text: // Copyright 2021 The Chromium OS Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #ifndef TRUNKS_CSME_PINWEAVER_CLIENT_UTILS_H_
  #define TRUNKS_CSME_PINWEAVER_CLIENT_UTILS_H_
  
  #include <string.h>
  #include <string>
  
  #include "trunks/csme/pinweaver_csme_types.h"
  
  #include <base/logging.h>
  
  // TODO(b/190621192): Support variable length request and response, and further
  // refactor all the consumers into one single template.
  namespace trunks {
  namespace csme {
  
  template <typename Type>
  std::string SerializeToString(const Type& t) {
    const char* buffer = reinterpret_cast<const char*>(&t);
    return std::string(buffer, buffer + sizeof(t));
  }
  
  // Declared as inline to temporarily avoid duplicated definition.
  inline bool CheckResponse(const pw_heci_header_req& req_header,
                            const pw_heci_header_res& resp_header) {
    if (req_header.pw_heci_seq != resp_header.pw_heci_seq) {
      LOG(ERROR) << __func__ << ": Mismatched sequence: expected "
                 << req_header.pw_heci_seq << " got " << resp_header.pw_heci_seq;
      return false;
    }
    if (resp_header.pw_heci_rc) {
      LOG(ERROR) << __func__
                 << ": CSME returns error: " << resp_header.pw_heci_rc;
      return false;
    }
    if (req_header.pw_heci_cmd != resp_header.pw_heci_cmd) {
      LOG(ERROR) << __func__ << ": Mismatched command: expected "
                 << req_header.pw_heci_cmd << " got " << resp_header.pw_heci_cmd;
      return false;
    }
    return true;
  }
  
  // Implementation of deserialization of the packed data from CSME at recursion.
  template <typename... OutputTypes>
  class UnpackImpl;
  
  // Unpacks the first data from the serialized payload, and invokes recursion.
  template <typename FirstOutputType, typename... OutputTypes>
  class UnpackImpl<FirstOutputType, OutputTypes...> {
   public:
    UnpackImpl() = default;
    bool Unpack(const std::string& serialized,
                FirstOutputType* first,
                OutputTypes*... outputs) {
      if (serialized.size() < sizeof(*first)) {
        LOG(ERROR) << __func__ << ": Serialized data too short; expected >= "
                   << sizeof(*first) << "; got " << serialized.size();
        return false;
      }
      memcpy(first, serialized.data(), sizeof(*first));
      return UnpackImpl<OutputTypes...>().Unpack(
          serialized.substr(sizeof(*first)), outputs...);
    }
  };
  
  // Unpacks the first data from the serialized payload, and invokes recursion.
  #if 1
  template <typename... OutputTypes>
  class UnpackImpl<std::string, OutputTypes...> {
   public:
    UnpackImpl() = default;
    bool Unpack(const std::string& serialized,
                std::string* output_str,
                OutputTypes*... outputs) {
      // Report error as we don't have use case for empty string output.
      if (serialized.empty()) {
        LOG(ERROR) << __func__ << ": Expected empty string payload.";
        return false;
      }
      *output_str = serialized;
      // There isn't supposed to be any output parameters left; the recursive call
      // will verify.
      return UnpackImpl<OutputTypes...>().Unpack("", outputs...);
    }
  };
  #endif
  
  // Special handling for cases that all the output data are unpacked.
  template <>
  class UnpackImpl<> {
   public:
    UnpackImpl() = default;
    bool Unpack(const std::string& serialized) {
      if (!serialized.empty()) {
        LOG(ERROR) << __func__ << ": Execessively long data; reminaing size="
                   << serialized.size();
        return false;
      }
      return true;
    }
  };
  
  // Deserializes the response from CSME, including the integrity check against
  // the CSME command.
  template <typename... OutputTypes>
  bool UnpackFromResponse(const pw_heci_header_req& req_header,
                          const std::string& response,
                          OutputTypes*... outputs) {
    if (response.size() < sizeof(pw_heci_header_res)) {
      LOG(ERROR) << __func__ << ": response too short; size=" << response.size();
      return false;
    }
    const pw_heci_header_res* resp_header =
        reinterpret_cast<const pw_heci_header_res*>(response.data());
  
    if (!CheckResponse(req_header, *resp_header)) {
      LOG(ERROR) << __func__ << ": Failed to vlaidate response header.";
      return false;
    }
  
    const std::string serialized_outputs =
        response.substr(sizeof(pw_heci_header_res));
    if (resp_header->total_length != serialized_outputs.size()) {
      LOG(ERROR) << __func__ << ": Unexpected payload length; specified: "
                 << resp_header->total_length << " actual "
                 << serialized_outputs.size();
      return false;
    }
    if (!UnpackImpl<OutputTypes...>().Unpack(serialized_outputs, outputs...)) {
      LOG(ERROR) << __func__ << ": Unpacking error.";
      return false;
    }
    return true;
  }
  
  template <typename Type>
  void BuildFixedSizedRequest(int cmd, int seq, Type* req) {
    req->header.pw_heci_cmd = cmd;
    req->header.pw_heci_seq = seq;
    req->header.total_length = sizeof(Type) - sizeof(req->header);
  }
  
  }  // namespace csme
  }  // namespace trunks
  
  #endif  // TRUNKS_CSME_PINWEAVER_CLIENT_UTILS_H_
  