Program Listing for File MessageSerialization.h

Return to documentation for file (src/desert_classes/MessageSerialization.h)

/****************************************************************************
 * Copyright (C) 2024 Davide Costa                                          *
 *                                                                          *
 * This file is part of RMW desert.                                         *
 *                                                                          *
 *   RMW desert is free software: you can redistribute it and/or modify it  *
 *   under the terms of the GNU General Public License as published by the  *
 *   Free Software Foundation, either version 3 of the License, or any      *
 *   later version.                                                         *
 *                                                                          *
 *   RMW desert is distributed in the hope that it will be useful,          *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of         *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
 *   GNU General Public License for more details.                           *
 *                                                                          *
 *   You should have received a copy of the GNU General Public License      *
 *   along with RMW desert.  If not, see <http://www.gnu.org/licenses/>.    *
 ****************************************************************************/

#ifndef MESSAGE_SERIALIZATION_H_
#define MESSAGE_SERIALIZATION_H_

#include "CBorStream.h"
#include "CStringHelper.h"
#include "macros.h"

#include <vector>
#include <string>

#define INTROSPECTION_C_MEMBER rosidl_typesupport_introspection_c__MessageMember
#define INTROSPECTION_CPP_MEMBER rosidl_typesupport_introspection_cpp::MessageMember

#define INTROSPECTION_C_MEMBERS rosidl_typesupport_introspection_c__MessageMembers
#define INTROSPECTION_CPP_MEMBERS rosidl_typesupport_introspection_cpp::MessageMembers

#define INTROSPECTION_C_SERVICE_MEMBERS rosidl_typesupport_introspection_c__ServiceMembers
#define INTROSPECTION_CPP_SERVICE_MEMBERS rosidl_typesupport_introspection_cpp::ServiceMembers

namespace MessageSerialization
{

  template<typename T>
  void serialize_field(const INTROSPECTION_CPP_MEMBER * member, void * field, cbor::TxStream & stream)
  {
    if (!member->is_array_)
    {
      stream << * static_cast<T *>(field);
    }
    else if (member->array_size_ && !member->is_upper_bound_)
    {
      stream.serialize_sequence(static_cast<T *>(field), member->array_size_);
    }
    else
    {
      std::vector<T> & data = *reinterpret_cast<std::vector<T> *>(field);
      stream << data;
    }
  }

  template<typename T>
  void serialize_field(const INTROSPECTION_C_MEMBER * member, void * field, cbor::TxStream & stream)
  {
    // String specific implementation
    if constexpr(std::is_same_v<T, std::string>)
    {
      if (!member->is_array_)
      {
        stream << CStringHelper::convert_to_std_string(field);
      }
      else if (member->array_size_ && !member->is_upper_bound_)
      {
        stream << CStringHelper::convert_to_std_vector_string(field, member->array_size_);
      }
      else
      {
        printf("WARNING: non-fixed size sequences are currently sperimental\n");
        stream << CStringHelper::convert_sequence_to_std_vector_string(field);
      }
    }
    // U16string specific implementation
    else if constexpr(std::is_same_v<T, std::u16string>)
    {
      if (!member->is_array_)
      {
        stream << CStringHelper::convert_to_std_u16string(field);
      }
      else if (member->array_size_ && !member->is_upper_bound_)
      {
        stream << CStringHelper::convert_to_std_vector_u16string(field, member->array_size_);
      }
      else
      {
        printf("WARNING: non-fixed size sequences are currently sperimental\n");
        stream << CStringHelper::convert_sequence_to_std_vector_u16string(field);
      }
    }
    // Generic implementation
    else
    {
      if (!member->is_array_)
      {
        stream << * static_cast<T *>(field);
      }
      else if (member->array_size_ && !member->is_upper_bound_)
      {
        stream.serialize_sequence(static_cast<T *>(field), member->array_size_);
      }
      else
      {
        printf("WARNING: non-fixed size sequences are currently sperimental\n");
        auto & data = *reinterpret_cast<typename GenericCSequence<T>::type *>(field);

        // Serialize length
        stream << (uint32_t)data.size;

        stream.serialize_sequence(reinterpret_cast<T *>(data.data), data.size);
      }
    }
  }

  template<typename MembersType>
  void serialize(const void * msg, const MembersType * casted_members, cbor::TxStream & stream)
  {
    for (uint32_t i = 0; i < casted_members->member_count_; ++i) {
      const auto member = casted_members->members_ + i;
      void * field = const_cast<char *>(static_cast<const char *>(msg)) + member->offset_;
      switch (member->type_id_) {
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_MESSAGE:
          {
            auto sub_members = static_cast<const MembersType *>(member->members_->data);
            if (!member->is_array_) {
              serialize(field, sub_members, stream);
            }
            else if (member->array_size_ && !member->is_upper_bound_)
            {
              for (size_t index = 0; index < member->array_size_; ++index) {
                serialize(member->get_function(field, index), sub_members, stream);
              }
            }
            else
            {
              size_t array_size = member->size_function(field);

              if (member->is_upper_bound_ && array_size > member->array_size_)
              {
                throw std::runtime_error("Sequence overcomes the maximum length");
              }

              // Serialize length
              stream << (uint32_t)array_size;

              for (size_t index = 0; index < array_size; ++index) {
                serialize(member->get_function(field, index), sub_members, stream);
              }
            }
          }
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_BOOLEAN:
          if (!member->is_array_)
          {
            // Don't cast to bool here because if the bool is uninitialized the random value can't be deserialized
            stream << (*static_cast<uint8_t *>(field) ? true : false);
          }
          else
          {
            serialize_field<bool>(member, field, stream);
          }
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_OCTET:
          //throw std::runtime_error("OCTET type unsupported");
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_UINT8:
          serialize_field<uint8_t>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_CHAR:
          serialize_field<char>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_INT8:
          serialize_field<int8_t>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_FLOAT:
          serialize_field<float>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_DOUBLE:
          serialize_field<double>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_INT16:
          serialize_field<int16_t>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_UINT16:
          serialize_field<uint16_t>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_INT32:
          serialize_field<int32_t>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_UINT32:
          serialize_field<uint32_t>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_INT64:
          serialize_field<int64_t>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_UINT64:
          serialize_field<uint64_t>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_STRING:
          serialize_field<std::string>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_WSTRING:
          serialize_field<std::u16string>(member, field, stream);
          break;
        default:
          throw std::runtime_error("unknown type");
      }
    }
  }

  template<typename T>
  void deserialize_field(const INTROSPECTION_CPP_MEMBER * member, void * field, cbor::RxStream & stream)
  {
    if (!member->is_array_) {
      stream >> *static_cast<T *>(field);
    }
    else if (member->array_size_ && !member->is_upper_bound_)
    {
      stream.deserialize_sequence(static_cast<T *>(field), member->array_size_);
    }
    else
    {
      auto & vector = *reinterpret_cast<std::vector<T> *>(field);
      new(&vector) std::vector<T>;
      stream >> vector;
    }
  }

  template<typename T>
  void deserialize_field(const INTROSPECTION_C_MEMBER * member, void * field, cbor::RxStream & stream)
  {
    // String specific implementation
    if constexpr(std::is_same_v<T, std::string>)
    {
      if (!member->is_array_)
      {
        std::string str;
        stream >> str;
        CStringHelper::assign_string(str, field);
      }
      else if (member->array_size_ && !member->is_upper_bound_)
      {
        std::vector<std::string> cpp_string_vector;
        stream >> cpp_string_vector;

        CStringHelper::assign_vector_string(cpp_string_vector, field, member->array_size_);
      }
      else
      {
        printf("WARNING: non-fixed size sequences are currently sperimental\n");
        std::vector<std::string> cpp_string_vector;
        stream >> cpp_string_vector;

        CStringHelper::assign_vector_string_to_sequence(cpp_string_vector, field);
      }
    }
    // U16string specific implementation
    else if constexpr(std::is_same_v<T, std::u16string>)
    {
      if (!member->is_array_)
      {
        std::u16string str;
        stream >> str;
        CStringHelper::assign_u16string(str, field);
      }
      else if (member->array_size_ && !member->is_upper_bound_)
      {
        std::vector<std::u16string> cpp_string_vector;
        stream >> cpp_string_vector;

        CStringHelper::assign_vector_u16string(cpp_string_vector, field, member->array_size_);
      }
      else
      {
        printf("WARNING: non-fixed size sequences are currently sperimental\n");
        std::vector<std::u16string> cpp_string_vector;
        stream >> cpp_string_vector;

        CStringHelper::assign_vector_u16string_to_sequence(cpp_string_vector, field);
      }
    }
    // Generic implementation
    else
    {
      if (!member->is_array_)
      {
        stream >> * static_cast<T *>(field);
      }
      else if (member->array_size_ && !member->is_upper_bound_)
      {
        stream.deserialize_sequence(static_cast<T *>(field), member->array_size_);
      }
      else
      {
        printf("WARNING: non-fixed size sequences are currently sperimental\n");
        auto & data = *reinterpret_cast<typename GenericCSequence<T>::type *>(field);
        uint32_t size = 0;
        stream >> size;
        size_t dsize = static_cast<size_t>(size);

        if (!GenericCSequence<T>::init(&data, dsize))
        {
          throw std::runtime_error("unable to initialize GenericCSequence");
        }

        stream.deserialize_sequence(reinterpret_cast<T *>(data.data), dsize);
      }
    }
  }

  template<typename MembersType>
  void deserialize(void * msg, const MembersType * casted_members, cbor::RxStream & stream)
  {
    for (uint32_t i = 0; i < casted_members->member_count_; ++i) {
      const auto member = casted_members->members_ + i;
      void * field = static_cast<char *>(msg) + member->offset_;
      switch (member->type_id_) {
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_MESSAGE:
          {
            auto sub_members = static_cast<const MembersType *>(member->members_->data);
            if (!member->is_array_) {
              deserialize(field, sub_members, stream);
            }
            else if (member->array_size_ && !member->is_upper_bound_)
            {
              for (size_t index = 0; index < member->array_size_; ++index) {
                deserialize(member->get_function(field, index), sub_members, stream);
              }
            }
            else
            {
              // Deserialize length
              uint32_t array_size = 0;
              stream >> array_size;

              auto vector = reinterpret_cast<std::vector<unsigned char> *>(field);
              new(vector) std::vector<unsigned char>;
              member->resize_function(field, array_size);

              for (size_t index = 0; index < array_size; ++index) {
                deserialize(member->get_function(field, index), sub_members, stream);
              }
            }
          }
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_BOOLEAN:
          deserialize_field<bool>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_OCTET:
          //throw std::runtime_error("OCTET type unsupported");
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_UINT8:
          deserialize_field<uint8_t>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_CHAR:
          deserialize_field<char>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_INT8:
          deserialize_field<int8_t>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_FLOAT:
          deserialize_field<float>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_DOUBLE:
          deserialize_field<double>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_INT16:
          deserialize_field<int16_t>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_UINT16:
          deserialize_field<uint16_t>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_INT32:
          deserialize_field<int32_t>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_UINT32:
          deserialize_field<uint32_t>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_INT64:
          deserialize_field<int64_t>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_UINT64:
          deserialize_field<uint64_t>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_STRING:
          deserialize_field<std::string>(member, field, stream);
          break;
        case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_WSTRING:
          deserialize_field<std::u16string>(member, field, stream);
          break;
        default:
          throw std::runtime_error("unknown type");
      }
    }
  }

}


#endif