From 7417ff130534226120a97bb7f4214f189e26b12f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Gonz=C3=A1lez?= Date: Wed, 14 Feb 2024 16:30:22 +0100 Subject: [PATCH] Support serializing a std::vector as an array. (#192) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refs #17138. Support serializing a std::vector as an array. Signed-off-by: Ricardo González Moreno * Refs #17138. Temporally solution Signed-off-by: Ricardo González Moreno * Refs #17138. Add more funcionality Signed-off-by: Ricardo González Moreno * Refs #17138. Add tests Signed-off-by: Ricardo González Moreno * Refs #17138. Apply suggestions Signed-off-by: Ricardo González Moreno --------- Signed-off-by: Ricardo González Moreno --- include/fastcdr/Cdr.h | 322 ++++++++++++++----- include/fastcdr/CdrSizeCalculator.hpp | 57 ++++ src/cpp/Cdr.cpp | 107 +++++++ src/cpp/CdrSizeCalculator.cpp | 8 + test/cdr/CMakeLists.txt | 8 + test/cdr/array_as_std_vector.cpp | 438 ++++++++++++++++++++++++++ 6 files changed, 863 insertions(+), 77 deletions(-) create mode 100644 test/cdr/array_as_std_vector.cpp diff --git a/include/fastcdr/Cdr.h b/include/fastcdr/Cdr.h index 33ebadbc..ba51fa2b 100644 --- a/include/fastcdr/Cdr.h +++ b/include/fastcdr/Cdr.h @@ -266,7 +266,7 @@ class Cdr Cdr_DllAPI size_t get_serialized_data_length() const; /*! - * @brief Get the number of bytes needed to align a position to certain data size. + * @brief Returns the number of bytes needed to align a position to certain data size. * @param current_alignment Position to be aligned. * @param data_size Size of next data to process (should be power of two). * @return Number of required alignment bytes. @@ -748,26 +748,17 @@ class Cdr Cdr& serialize( const std::array<_T, _Size>& array_t) { - Cdr::state dheader_state(*this); - - if (CdrVersion::XCDRv2 == cdr_version_ && !is_multi_array_primitive(&array_t)) + if (!is_multi_array_primitive(&array_t)) { - // Serialize DHEADER - uint32_t dheader {0}; - serialize(dheader); - } + Cdr::state dheader_state {allocate_xcdrv2_dheader()}; - serialize_array(array_t.data(), array_t.size()); + serialize_array(array_t.data(), array_t.size()); - if (CdrVersion::XCDRv2 == cdr_version_ && !is_multi_array_primitive(&array_t)) + set_xcdrv2_dheader(dheader_state); + } + else { - auto offset = offset_; - Cdr::state state_after(*this); - set_state(dheader_state); - size_t dheader = offset - offset_ - (4 + alignment(sizeof(uint32_t)));/* DHEADER */ - serialize(static_cast(dheader)); - set_state(state_after); - serialized_member_size_ = SERIALIZED_MEMBER_SIZE; + serialize_array(array_t.data(), array_t.size()); } return *this; @@ -784,14 +775,7 @@ class Cdr Cdr& serialize( const std::vector<_T>& vector_t) { - Cdr::state dheader_state(*this); - - if (CdrVersion::XCDRv2 == cdr_version_) - { - // Serialize DHEADER - uint32_t dheader {0}; - serialize(dheader); - } + Cdr::state dheader_state {allocate_xcdrv2_dheader()}; serialize(static_cast(vector_t.size())); @@ -805,16 +789,7 @@ class Cdr ex.raise(); } - if (CdrVersion::XCDRv2 == cdr_version_) - { - auto offset = offset_; - Cdr::state state_after(*this); - set_state(dheader_state); - size_t dheader = offset - offset_ - (4 + alignment(sizeof(uint32_t)));/* DHEADER */ - serialize(static_cast(dheader)); - set_state(state_after); - serialized_member_size_ = SERIALIZED_MEMBER_SIZE; - } + set_xcdrv2_dheader(dheader_state); return *this; } @@ -876,14 +851,7 @@ class Cdr Cdr& serialize( const std::map<_K, _T>& map_t) { - Cdr::state dheader_state(*this); - - if (CdrVersion::XCDRv2 == cdr_version_) - { - // Serialize DHEADER - uint32_t dheader {0}; - serialize(dheader); - } + Cdr::state dheader_state {allocate_xcdrv2_dheader()}; serialize(static_cast(map_t.size())); @@ -901,16 +869,7 @@ class Cdr ex.raise(); } - if (CdrVersion::XCDRv2 == cdr_version_) - { - auto offset = offset_; - Cdr::state state_after(*this); - set_state(dheader_state); - size_t dheader = offset - offset_ - (4 + alignment(sizeof(uint32_t)));/* DHEADER */ - serialize(static_cast(dheader)); - set_state(state_after); - serialized_member_size_ = SERIALIZED_MEMBER_SIZE; - } + set_xcdrv2_dheader(dheader_state); return *this; } @@ -1271,6 +1230,91 @@ class Cdr return *this; } + /*! + * @brief Encodes an std::vector of primitives as an array. + * @param[in] value Reference to a std::vector. + * @return Reference to the eprosima::fastcdr::Cdr object. + * @exception exception::NotEnoughMemoryException This exception is thrown when trying to encode into a buffer + * position that exceeds the internal memory size. + */ + template::value || + std::is_arithmetic<_T>::value>::type* = nullptr> + Cdr& serialize_array( + const std::vector<_T>& value) + { + serialize_array(value.data(), value.size()); + + return *this; + } + + /*! + * @brief Encodes an std::vector of non-primitives as an array. + * @param[in] value Reference to a std::vector. + * @return Reference to the eprosima::fastcdr::Cdr object. + * @exception exception::NotEnoughMemoryException This exception is thrown when trying to encode into a buffer + * position that exceeds the internal memory size. + */ + template::value && + !std::is_arithmetic<_T>::value>::type* = nullptr> + Cdr& serialize_array( + const std::vector<_T>& value) + { + Cdr::state dheader_state {allocate_xcdrv2_dheader()}; + + serialize_array(value.data(), value.size()); + + set_xcdrv2_dheader(dheader_state); + + return *this; + } + + /*! + * @brief Encodes an std::vector as an array with a different endianness. + * @param[in] value Reference to a std::vector. + * @param[in] endianness Endianness that will be used in the serialization of this value. + * @return Reference to the eprosima::fastcdr::Cdr object. + * @exception exception::NotEnoughMemoryException This exception is thrown when trying to encode into a buffer + * position that exceeds the internal memory size. + */ + template + Cdr& serialize_array( + const std::vector<_T>& value, + Endianness endianness) + { + bool aux_swap = swap_bytes_; + swap_bytes_ = (swap_bytes_ && (static_cast(endianness_) == endianness)) || + (!swap_bytes_ && (static_cast(endianness_) != endianness)); + + try + { + serialize_array(value); + swap_bytes_ = aux_swap; + } + catch (exception::Exception& ex) + { + swap_bytes_ = aux_swap; + ex.raise(); + } + + return *this; + } + + /*! + * @brief Encodes an std::vector of booleans as an array. + * @param[in] value Reference to a std::vector. + * @return Reference to the eprosima::fastcdr::Cdr object. + * @exception exception::NotEnoughMemoryException This exception is thrown when trying to encode into a buffer + * position that exceeds the internal memory size. + */ + TEMPLATE_SPEC + Cdr& serialize_array( + const std::vector& value) + { + serialize_bool_array(value); + + return *this; + } + /*! * @brief This function template serializes a raw sequence of non-primitives * @param sequence_t Pointer to the sequence that will be serialized in the buffer. @@ -1284,14 +1328,7 @@ class Cdr const _T* sequence_t, size_t num_elements) { - Cdr::state dheader_state(*this); - - if (CdrVersion::XCDRv2 == cdr_version_) - { - // Serialize DHEADER - uint32_t dheader {0}; - serialize(dheader); - } + Cdr::state dheader_state {allocate_xcdrv2_dheader()}; serialize(static_cast(num_elements)); @@ -1305,16 +1342,7 @@ class Cdr ex.raise(); } - if (CdrVersion::XCDRv2 == cdr_version_) - { - auto offset = offset_; - Cdr::state state_after(*this); - set_state(dheader_state); - size_t dheader = offset - offset_ - (4 + alignment(sizeof(uint32_t)));/* DHEADER */ - serialize(static_cast(dheader)); - set_state(state_after); - serialized_member_size_ = SERIALIZED_MEMBER_SIZE; - } + set_xcdrv2_dheader(dheader_state); return *this; } @@ -2330,6 +2358,120 @@ class Cdr bool* bool_t, size_t num_elements); + /*! + * @brief Decodes an array of primitives on a std::vector. + * + * std::vector must have allocated the number of element of the array. + * + * @param[out] value Reference to the std::vector where the array will be stored after decoding from the buffer. + * @return Reference to the eprosima::fastcdr::Cdr object. + * @exception exception::NotEnoughMemoryException This exception is thrown when trying to decode from a buffer + * position that exceeds the internal memory size. + */ + template::value || + std::is_arithmetic<_T>::value>::type* = nullptr> + Cdr& deserialize_array( + std::vector<_T>& value) + { + deserialize_array(value.data(), value.size()); + + return *this; + } + + /*! + * @brief Decodes an array of non-primitives on a std::vector. + * + * std::vector must have allocated the number of element of the array. + * + * @param[out] value Reference to the std::vector where the array will be stored after decoding from the buffer. + * @return Reference to the eprosima::fastcdr::Cdr object. + * @exception exception::NotEnoughMemoryException This exception is thrown when trying to decode from a buffer + * position that exceeds the internal memory size. + */ + template::value && + !std::is_arithmetic<_T>::value>::type* = nullptr> + Cdr& deserialize_array( + std::vector<_T>& value) + { + if (CdrVersion::XCDRv2 == cdr_version_) + { + uint32_t dheader {0}; + deserialize(dheader); + + uint32_t count {0}; + auto offset = offset_; + while (offset_ - offset < dheader && count < value.size()) + { + deserialize_array(&value.data()[count], 1); + ++count; + } + + if (offset_ - offset != dheader) + { + throw exception::BadParamException("Member size greater than size specified by DHEADER"); + } + } + else + { + return deserialize_array(value.data(), value.size()); + } + + return *this; + } + + /*! + * @brief Decodes an array of non-primitives on a std::vector with a different endianness. + * + * std::vector must have allocated the number of element of the array. + * + * @param[out] value Reference to the std::vector where the array will be stored after decoding from the buffer. + * @param[in] endianness Endianness that will be used in the serialization of this value. + * @return Reference to the eprosima::fastcdr::Cdr object. + * @exception exception::NotEnoughMemoryException This exception is thrown when trying to decode from a buffer + * position that exceeds the internal memory size. + */ + template + Cdr& deserialize_array( + std::vector<_T>& value, + Endianness endianness) + { + bool aux_swap = swap_bytes_; + swap_bytes_ = (swap_bytes_ && (static_cast(endianness_) == endianness)) || + (!swap_bytes_ && (static_cast(endianness_) != endianness)); + + try + { + deserialize_array(value); + swap_bytes_ = aux_swap; + } + catch (exception::Exception& ex) + { + swap_bytes_ = aux_swap; + ex.raise(); + } + + return *this; + } + + /*! + * @brief Decodes an array of booleans on a std::vector. + * + * std::vector must have allocated the number of element of the array. + * + * @param[out] value Reference to the std::vector where the array will be stored after decoding from the buffer. + * @return Reference to the eprosima::fastcdr::Cdr object. + * @exception exception::NotEnoughMemoryException This exception is thrown when trying to encode into a buffer + * position that exceeds the internal memory size. + */ + TEMPLATE_SPEC + Cdr& deserialize_array( + std::vector& value) + { + deserialize_bool_array(value); + + return *this; + } + /*! * @brief This function template deserializes a raw sequence of non-primitives. * This function allocates memory to store the sequence. The user pointer will be set to point this allocated memory. @@ -2797,6 +2939,22 @@ class Cdr return *this; } + /*! + * @brief Encodes an empty DHEADER if the encoding version is XCDRv2. + * After serializing the members's type, @ref set_xcdrv2_dheader must be called to set the correct DHEADER value + * using the @ref state returned by this function. + */ + Cdr_DllAPI state allocate_xcdrv2_dheader(); + + /*! + * @brief Uses the @ref state to calculate the member's type size and serialize the value in the previous allocated + * DHEADER. + * + * @param[in] state @ref state used to calculate the member's type size. + */ + Cdr_DllAPI void set_xcdrv2_dheader( + const state& state); + private: Cdr( @@ -2805,9 +2963,15 @@ class Cdr Cdr& operator =( const Cdr&) = delete; + Cdr_DllAPI Cdr& serialize_bool_array( + const std::vector& vector_t); + Cdr_DllAPI Cdr& serialize_bool_sequence( const std::vector& vector_t); + Cdr_DllAPI Cdr& deserialize_bool_array( + std::vector& vector_t); + Cdr_DllAPI Cdr& deserialize_bool_sequence( std::vector& vector_t); @@ -2867,7 +3031,8 @@ class Cdr } /*! - * @brief This function returns the extra bytes regarding the allignment. + * @brief Returns the number of bytes needed to align the current position (having as reference the origin) to + * certain data size. * @param data_size The size of the data that will be serialized. * @return The size needed for the alignment. */ @@ -3367,13 +3532,16 @@ class Cdr //! Align for types equal or greater than 64bits. size_t align64_ {4}; - + /*! + * When serializing a member's type using XCDRv2, this enumerator is used to inform the type was serialized with a + * DHEADER and the algorithm could optimize the XCDRv2 member header. + */ enum SerializedMemberSizeForNextInt { - NO_SERIALIZED_MEMBER_SIZE, - SERIALIZED_MEMBER_SIZE, - SERIALIZED_MEMBER_SIZE_4, - SERIALIZED_MEMBER_SIZE_8 + NO_SERIALIZED_MEMBER_SIZE, //! Default. No serialized member size in a DHEADER. + SERIALIZED_MEMBER_SIZE, //! Serialized member size in a DHEADER. + SERIALIZED_MEMBER_SIZE_4, //! Serialized member size (which is a multiple of 4) in a DHEADER. + SERIALIZED_MEMBER_SIZE_8 //! Serialized member size (which is a multiple of 8) in a DHEADER. } //! Specifies if a DHEADER was serialized. Used to optimize XCDRv2 member headers. serialized_member_size_ {NO_SERIALIZED_MEMBER_SIZE}; diff --git a/include/fastcdr/CdrSizeCalculator.hpp b/include/fastcdr/CdrSizeCalculator.hpp index 423b3233..280357a8 100644 --- a/include/fastcdr/CdrSizeCalculator.hpp +++ b/include/fastcdr/CdrSizeCalculator.hpp @@ -60,6 +60,16 @@ class CdrSizeCalculator Cdr_DllAPI CdrSizeCalculator( CdrVersion cdr_version); + /*! + * @brief Constructor. + * @param[in] cdr_version Represents the version of the encoding algorithm that will be used for the encoding. + * The default value is CdrVersion::XCDRv2. + * @param[in] encoding Represents the initial encoding. + */ + Cdr_DllAPI CdrSizeCalculator( + CdrVersion cdr_version, + EncodingAlgorithmFlag encoding); + /*! * @brief Retrieves the version of the encoding algorithm used by the instance. * @return Configured CdrVersion. @@ -1057,6 +1067,53 @@ class CdrSizeCalculator return calculate_array_serialized_size(data->data(), num_elements * data->size(), current_alignment); } + /*! + * @brief Specific template which calculates the encoded size of an std::vector of primitives as an array. + * @param[in] data Reference to the instance. + * @param[inout] current_alignment Current alignment in the encoding. + * @return Encoded size of the instance. + */ + template::value || + std::is_arithmetic<_T>::value>::type* = nullptr> + size_t calculate_array_serialized_size( + const std::vector<_T>& data, + size_t& current_alignment) + { + return calculate_array_serialized_size(data.data(), data.size(), current_alignment); + } + + /*! + * @brief Specific template which calculates the encoded size of an std::vector of non-primitives as an array. + * @param[in] data Reference to the instance. + * @param[inout] current_alignment Current alignment in the encoding. + * @return Encoded size of the instance. + */ + template::value && + !std::is_arithmetic<_T>::value>::type* = nullptr> + size_t calculate_array_serialized_size( + const std::vector<_T>& data, + size_t& current_alignment) + { + size_t initial_alignment {current_alignment}; + + if (CdrVersion::XCDRv2 == cdr_version_) + { + // DHEADER + current_alignment += 4 + alignment(current_alignment, 4); + } + + size_t calculated_size {current_alignment - initial_alignment}; + calculated_size += calculate_array_serialized_size(data.data(), data.size(), current_alignment); + + if (CdrVersion::XCDRv2 == cdr_version_) + { + // Inform DHEADER can be joined with NEXTINT + serialized_member_size_ = SERIALIZED_MEMBER_SIZE; + } + + return calculated_size; + } + /*! * @brief Generic template which calculates the encoded size of the constructed type's member of a unknown type. * @tparam _T Member's type. diff --git a/src/cpp/Cdr.cpp b/src/cpp/Cdr.cpp index 34e2ebe8..fcb7b41e 100644 --- a/src/cpp/Cdr.cpp +++ b/src/cpp/Cdr.cpp @@ -2156,6 +2156,44 @@ Cdr& Cdr::deserialize_array( throw NotEnoughMemoryException(NotEnoughMemoryException::NOT_ENOUGH_MEMORY_MESSAGE_DEFAULT); } +Cdr& Cdr::serialize_bool_array( + const std::vector& vector_t) +{ + state state_before_error(*this); + + size_t total_size = vector_t.size() * sizeof(bool); + + if (((end_ - offset_) >= total_size) || resize(total_size)) + { + // Save last datasize. + last_data_size_ = sizeof(bool); + + for (size_t count = 0; count < vector_t.size(); ++count) + { + uint8_t value = 0; + std::vector::const_reference ref = vector_t[count]; + + if (ref) + { + value = 1; + } + offset_++ << value; + } + } + else + { + set_state(state_before_error); + throw NotEnoughMemoryException(NotEnoughMemoryException::NOT_ENOUGH_MEMORY_MESSAGE_DEFAULT); + } + + if (CdrVersion::XCDRv2 == cdr_version_) + { + serialized_member_size_ = SERIALIZED_MEMBER_SIZE; + } + + return *this; +} + Cdr& Cdr::serialize_bool_sequence( const std::vector& vector_t) { @@ -2196,6 +2234,46 @@ Cdr& Cdr::serialize_bool_sequence( return *this; } +Cdr& Cdr::deserialize_bool_array( + std::vector& vector_t) +{ + state state_before_error(*this); + + size_t total_size = vector_t.size() * sizeof(bool); + + if ((end_ - offset_) >= total_size) + { + // Save last datasize. + last_data_size_ = sizeof(bool); + + for (uint32_t count = 0; count < vector_t.size(); ++count) + { + uint8_t value = 0; + offset_++ >> value; + + if (value == 1) + { + vector_t[count] = true; + } + else if (value == 0) + { + vector_t[count] = false; + } + else + { + throw BadParamException("Unexpected byte value in Cdr::deserialize_bool_sequence, expected 0 or 1"); + } + } + } + else + { + set_state(state_before_error); + throw NotEnoughMemoryException(NotEnoughMemoryException::NOT_ENOUGH_MEMORY_MESSAGE_DEFAULT); + } + + return *this; +} + Cdr& Cdr::deserialize_bool_sequence( std::vector& vector_t) { @@ -3304,5 +3382,34 @@ Cdr& Cdr::cdr_deserialize_type( return *this; } +Cdr::state Cdr::allocate_xcdrv2_dheader() +{ + Cdr::state dheader_state(*this); + + if (CdrVersion::XCDRv2 == cdr_version_) + { + // Serialize DHEADER + uint32_t dheader {0}; + serialize(dheader); + } + + return dheader_state; +} + +void Cdr::set_xcdrv2_dheader( + const Cdr::state& dheader_state) +{ + if (CdrVersion::XCDRv2 == cdr_version_) + { + auto offset = offset_; + Cdr::state state_after(*this); + set_state(dheader_state); + size_t dheader = offset - offset_ - (4 + alignment(sizeof(uint32_t))); /* DHEADER */ + serialize(static_cast(dheader)); + set_state(state_after); + serialized_member_size_ = SERIALIZED_MEMBER_SIZE; + } +} + } // namespace fastcdr } // namespace eprosima diff --git a/src/cpp/CdrSizeCalculator.cpp b/src/cpp/CdrSizeCalculator.cpp index c642de47..6462422b 100644 --- a/src/cpp/CdrSizeCalculator.cpp +++ b/src/cpp/CdrSizeCalculator.cpp @@ -32,6 +32,14 @@ CdrSizeCalculator::CdrSizeCalculator( } } +CdrSizeCalculator::CdrSizeCalculator( + CdrVersion cdr_version, + EncodingAlgorithmFlag encoding) + : cdr_version_(cdr_version) + , current_encoding_(encoding) +{ +} + CdrVersion CdrSizeCalculator::get_cdr_version() const { return cdr_version_; diff --git a/test/cdr/CMakeLists.txt b/test/cdr/CMakeLists.txt index 94776449..5d57f840 100644 --- a/test/cdr/CMakeLists.txt +++ b/test/cdr/CMakeLists.txt @@ -20,6 +20,14 @@ set_common_compile_options(FixedSizeStringTests) target_link_libraries(FixedSizeStringTests fastcdr GTest::gtest_main) gtest_discover_tests(FixedSizeStringTests) +############################################################################### +# array_as_std_vector tests +############################################################################### +add_executable(ArrayAsSTDVectorTests array_as_std_vector.cpp) +set_common_compile_options(ArrayAsSTDVectorTests) +target_link_libraries(ArrayAsSTDVectorTests fastcdr GTest::gtest_main) +gtest_discover_tests(ArrayAsSTDVectorTests) + ############################################################################### # Old cdr unit tests ############################################################################### diff --git a/test/cdr/array_as_std_vector.cpp b/test/cdr/array_as_std_vector.cpp new file mode 100644 index 00000000..2436cc29 --- /dev/null +++ b/test/cdr/array_as_std_vector.cpp @@ -0,0 +1,438 @@ +// Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include + +#include "../xcdr/utility.hpp" + +using namespace eprosima::fastcdr; + +using XCdrStreamValues = + std::array, + 1 + EncodingAlgorithmFlag::PL_CDR2 + Cdr::Endianness::LITTLE_ENDIANNESS>; + +class CdrArrayAsSTDVectorTest : public ::testing::TestWithParam< std::tuple> +{ +}; + +struct InnerBasicTypesShortStruct +{ + InnerBasicTypesShortStruct() = default; + + InnerBasicTypesShortStruct( + uint16_t v) + { + value1 = v; + value2 = v; + } + + bool operator ==( + const InnerBasicTypesShortStruct& other) const + { + return value1 == other.value1 && value2 == other.value2; + } + + uint16_t value1 {0}; + + uint16_t value2 {0}; +}; + +namespace eprosima { +namespace fastcdr { + +template<> +size_t calculate_serialized_size( + eprosima::fastcdr::CdrSizeCalculator& calculator, + const InnerBasicTypesShortStruct& data, + size_t& current_alignment) +{ + eprosima::fastcdr::EncodingAlgorithmFlag previous_encoding = calculator.get_encoding(); + size_t calculated_size {calculator.begin_calculate_type_serialized_size(previous_encoding, current_alignment)}; + + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId( + 3), data.value1, current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId( + 0x3FFF), data.value2, current_alignment); + + + calculated_size += calculator.end_calculate_type_serialized_size(previous_encoding, current_alignment); + + return calculated_size; +} + +template<> +void serialize( + Cdr& cdr, + const InnerBasicTypesShortStruct& data) + +{ + Cdr::state current_status(cdr); + cdr.begin_serialize_type(current_status, cdr.get_encoding_flag()); + cdr << MemberId(3) << data.value1; + cdr << MemberId(0x3FFF) << data.value2; + cdr.end_serialize_type(current_status); +} + +template<> +void deserialize( + Cdr& cdr, + InnerBasicTypesShortStruct& data) +{ + cdr.deserialize_type(cdr.get_encoding_flag(), [&data](Cdr& cdr_inner, const MemberId& mid) -> bool + { + bool ret_value = true; + if (EncodingAlgorithmFlag::PL_CDR == cdr_inner.get_encoding_flag() || + EncodingAlgorithmFlag::PL_CDR2 == cdr_inner.get_encoding_flag()) + { + switch (mid.id) + { + case 3: + cdr_inner >> data.value1; + break; + case 0x3FFF: + cdr_inner >> data.value2; + break; + default: + ret_value = false; + break; + } + + } + else + { + switch (mid.id) + { + case 0: + cdr_inner >> data.value1; + break; + case 1: + cdr_inner >> data.value2; + break; + default: + ret_value = false; + break; + } + } + + return ret_value; + }); +} + +} // namespace fastcdr +} // namespace eprosima + +template +void serialize_array( + const XCdrStreamValues& expected_streams, + EncodingAlgorithmFlag encoding, + Cdr::Endianness endianness, + _T value) +{ + //{ Calculate encoded size. + CdrSizeCalculator calculator(get_version_from_algorithm(encoding), encoding); + size_t current_alignment {0}; + size_t calculated_size {calculator.calculate_array_serialized_size(value, current_alignment)}; + calculated_size += 4; // Encapsulation + //} + + //{ Prepare buffer + uint8_t tested_stream = 0 + encoding + endianness; + auto buffer = + std::unique_ptr{reinterpret_cast(calloc(expected_streams[tested_stream].size(), sizeof(char))), free}; + FastBuffer fast_buffer(buffer.get(), expected_streams[tested_stream].size()); + Cdr cdr(fast_buffer, endianness, get_version_from_algorithm(encoding)); + //} + + //{ Encode the value. + cdr.set_encoding_flag(encoding); + cdr.serialize_encapsulation(); + Cdr::state enc_state(cdr); + cdr.serialize_array(value); + Cdr::state enc_state_end(cdr); + //} + + //{ Test encoded content + ASSERT_EQ(cdr.get_serialized_data_length(), expected_streams[tested_stream].size()); + ASSERT_EQ(cdr.get_serialized_data_length(), calculated_size); + ASSERT_EQ(0, memcmp(buffer.get(), expected_streams[tested_stream].data(), expected_streams[tested_stream].size())); + //} + + //{ Decoding the value. + _T dvalue(value.size()); + cdr.reset(); + cdr.read_encapsulation(); + ASSERT_EQ(cdr.get_encoding_flag(), encoding); + ASSERT_EQ(cdr.endianness(), endianness); + cdr.deserialize_array(dvalue); + ASSERT_EQ(value, dvalue); + Cdr::state dec_state_end(cdr); + ASSERT_EQ(enc_state_end, dec_state_end); + //} +} + +/*! + * @test Test an array of unsigned long type stored in a std::vector. + */ +TEST_P(CdrArrayAsSTDVectorTest, array_ulong_as_std_vector) +{ + const std::vector array_value {0xCDCDCDDC, 0xCDCDCDDC}; + constexpr uint8_t ival {0xCD}; + constexpr uint8_t fval {0xDC}; + + //{ Defining expected XCDR streams + XCdrStreamValues expected_streams; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x00, 0x00, 0x00, // Encapsulation + ival, ival, ival, fval, // ULong + ival, ival, ival, fval // ULong + }; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x01, 0x00, 0x00, // Encapsulation + fval, ival, ival, ival, // ULong + fval, ival, ival, ival // ULong + }; + expected_streams[0 + EncodingAlgorithmFlag::PL_CDR + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x02, 0x00, 0x00, // Encapsulation + ival, ival, ival, fval, // ULong + ival, ival, ival, fval, // ULong + }; + expected_streams[0 + EncodingAlgorithmFlag::PL_CDR + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x03, 0x00, 0x00, // Encapsulation + fval, ival, ival, ival, // ULong + fval, ival, ival, ival, // ULong + }; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR2 + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x06, 0x00, 0x00, // Encapsulation + ival, ival, ival, fval, // ULong + ival, ival, ival, fval // ULong + }; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR2 + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x07, 0x00, 0x00, // Encapsulation + fval, ival, ival, ival, // ULong + fval, ival, ival, ival // ULong + }; + expected_streams[0 + EncodingAlgorithmFlag::DELIMIT_CDR2 + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x08, 0x00, 0x00, // Encapsulation + ival, ival, ival, fval, // ULong + ival, ival, ival, fval // ULong + }; + expected_streams[0 + EncodingAlgorithmFlag::DELIMIT_CDR2 + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x09, 0x00, 0x00, // Encapsulation + fval, ival, ival, ival, // ULong + fval, ival, ival, ival // ULong + }; + expected_streams[0 + EncodingAlgorithmFlag::PL_CDR2 + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x0A, 0x00, 0x00, // Encapsulation + ival, ival, ival, fval, // ULong + ival, ival, ival, fval // ULong + }; + expected_streams[0 + EncodingAlgorithmFlag::PL_CDR2 + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x0B, 0x00, 0x00, // Encapsulation + fval, ival, ival, ival, // ULong + fval, ival, ival, ival // ULong + }; + //} + + EncodingAlgorithmFlag encoding = std::get<0>(GetParam()); + Cdr::Endianness endianness = std::get<1>(GetParam()); + + serialize_array(expected_streams, encoding, endianness, array_value); +} + +/*! + * @test Test an array of struct type stored in a std::vector. + * @code{.idl} + * struct InnerBasicTypesShortStruct + * { + * @id(3) + * unsigned short value1; + * @id(16383) + * unsigned short value2; + * }; + * + * InnerBasicTypesShortStruct var_structarray[2]; + * + * @endcode + */ +TEST_P(CdrArrayAsSTDVectorTest, array_struct) +{ + const std::vector array_value {{{0xCDDC}, {0xDCCD}}}; + constexpr uint8_t ival {0xCD}; + constexpr uint8_t fval {0xDC}; + + //{ Defining expected XCDR streams + XCdrStreamValues expected_streams; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x00, 0x00, 0x00, // Encapsulation + ival, fval, ival, fval, // Array element 0 + fval, ival, fval, ival // Array element 1 + }; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x01, 0x00, 0x00, // Encapsulation + fval, ival, fval, ival, // Array element 0 + ival, fval, ival, fval // Array element 1 + }; + expected_streams[0 + EncodingAlgorithmFlag::PL_CDR + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x02, 0x00, 0x00, // Encapsulation + 0x00, 0x03, 0x00, 0x02, // ShortMemberHeader + ival, fval, + 0x00, 0x00, // Alignment + 0x3F, 0x01, 0x00, 0x08, // LongMemberHeader + 0x00, 0x00, 0x3F, 0xFF, // LongMemberHeader + 0x00, 0x00, 0x00, 0x02, // LongMemberHeader + ival, fval, + 0x00, 0x00, // Alignment + 0x3F, 0x02, 0x00, 0x00, // Sentinel + 0x00, 0x03, 0x00, 0x02, // ShortMemberHeader + fval, ival, + 0x00, 0x00, // Alignment + 0x3F, 0x01, 0x00, 0x08, // LongMemberHeader + 0x00, 0x00, 0x3F, 0xFF, // LongMemberHeader + 0x00, 0x00, 0x00, 0x02, // LongMemberHeader + fval, ival, + 0x00, 0x00, // Alignment + 0x3F, 0x02, 0x00, 0x00, // Sentinel + }; + expected_streams[0 + EncodingAlgorithmFlag::PL_CDR + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x03, 0x00, 0x00, // Encapsulation + 0x03, 0x00, 0x02, 0x00, // ShortMemberHeader + fval, ival, + 0x00, 0x00, // Alignment + 0x01, 0x3F, 0x08, 0x00, // LongMemberHeader + 0xFF, 0x3F, 0x00, 0x00, // LongMemberHeader + 0x02, 0x00, 0x00, 0x00, // LongMemberHeader + fval, ival, + 0x00, 0x00, // Alignment + 0x02, 0x3F, 0x00, 0x00, // Sentinel + 0x03, 0x00, 0x02, 0x00, // ShortMemberHeader + ival, fval, + 0x00, 0x00, // Alignment + 0x01, 0x3F, 0x08, 0x00, // LongMemberHeader + 0xFF, 0x3F, 0x00, 0x00, // LongMemberHeader + 0x02, 0x00, 0x00, 0x00, // LongMemberHeader + ival, fval, + 0x00, 0x00, // Alignment + 0x02, 0x3F, 0x00, 0x00, // Sentinel + }; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR2 + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x06, 0x00, 0x00, // Encapsulation + 0x00, 0x00, 0x00, 0x08, // DHEADER + ival, fval, ival, fval, // Array element 0 + fval, ival, fval, ival // Array element 1 + }; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR2 + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x07, 0x00, 0x00, // Encapsulation + 0x08, 0x00, 0x00, 0x00, // DHEADER + fval, ival, fval, ival, // Array element 0 + ival, fval, ival, fval // Array element 1 + }; + expected_streams[0 + EncodingAlgorithmFlag::DELIMIT_CDR2 + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x08, 0x00, 0x00, // Encapsulation + 0x00, 0x00, 0x00, 0x10, // DHEADER + 0x00, 0x00, 0x00, 0x04, // DHEADER + ival, fval, ival, fval, // Array element 0 + 0x00, 0x00, 0x00, 0x04, // DHEADER + fval, ival, fval, ival // Array element 1 + }; + expected_streams[0 + EncodingAlgorithmFlag::DELIMIT_CDR2 + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x09, 0x00, 0x00, // Encapsulation + 0x10, 0x00, 0x00, 0x00, // DHEADER + 0x04, 0x00, 0x00, 0x00, // DHEADER + fval, ival, fval, ival, // Array element 0 + 0x04, 0x00, 0x00, 0x00, // DHEADER + ival, fval, ival, fval // Array element 1 + }; + expected_streams[0 + EncodingAlgorithmFlag::PL_CDR2 + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x0A, 0x00, 0x00, // Encapsulation + 0x00, 0x00, 0x00, 0x26, // DHEADER + 0x00, 0x00, 0x00, 0x0E, // DHEADER + 0x10, 0x00, 0x00, 0x03, // EMHEADER1(M) without NEXTINT + ival, fval, + 0x00, 0x00, // Alignment + 0x10, 0x00, 0x3F, 0xFF, // EMHEADER1(M) without NEXTINT + ival, fval, + 0x00, 0x00, // Alignment + 0x00, 0x00, 0x00, 0x0E, // DHEADER + 0x10, 0x00, 0x00, 0x03, // EMHEADER1(M) without NEXTINT + fval, ival, + 0x00, 0x00, // Alignment + 0x10, 0x00, 0x3F, 0xFF, // EMHEADER1(M) without NEXTINT + fval, ival + }; + expected_streams[0 + EncodingAlgorithmFlag::PL_CDR2 + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x0B, 0x00, 0x00, // Encapsulation + 0x26, 0x00, 0x00, 0x00, // DHEADER + 0x0E, 0x00, 0x00, 0x00, // DHEADER + 0x03, 0x00, 0x00, 0x10, // EMHEADER1(M) without NEXTINT + fval, ival, + 0x00, 0x00, // Alignment + 0xFF, 0x3F, 0x00, 0x10, // EMHEADER1(M) without NEXTINT + fval, ival, + 0x00, 0x00, // Alignment + 0x0E, 0x00, 0x00, 0x00, // DHEADER + 0x03, 0x00, 0x00, 0x10, // EMHEADER1(M) without NEXTINT + ival, fval, + 0x00, 0x00, // Alignment + 0xFF, 0x3F, 0x00, 0x10, // EMHEADER1(M) without NEXTINT + ival, fval + }; + //} + + EncodingAlgorithmFlag encoding = std::get<0>(GetParam()); + Cdr::Endianness endianness = std::get<1>(GetParam()); + + serialize_array(expected_streams, encoding, endianness, array_value); +} + +INSTANTIATE_TEST_SUITE_P( + CdrTest, + CdrArrayAsSTDVectorTest, + ::testing::Values( + std::make_tuple(EncodingAlgorithmFlag::PLAIN_CDR, Cdr::Endianness::BIG_ENDIANNESS), + std::make_tuple(EncodingAlgorithmFlag::PLAIN_CDR, Cdr::Endianness::LITTLE_ENDIANNESS), + std::make_tuple(EncodingAlgorithmFlag::PL_CDR, Cdr::Endianness::BIG_ENDIANNESS), + std::make_tuple(EncodingAlgorithmFlag::PL_CDR, Cdr::Endianness::LITTLE_ENDIANNESS), + std::make_tuple(EncodingAlgorithmFlag::PLAIN_CDR2, Cdr::Endianness::BIG_ENDIANNESS), + std::make_tuple(EncodingAlgorithmFlag::PLAIN_CDR2, Cdr::Endianness::LITTLE_ENDIANNESS), + std::make_tuple(EncodingAlgorithmFlag::DELIMIT_CDR2, Cdr::Endianness::BIG_ENDIANNESS), + std::make_tuple(EncodingAlgorithmFlag::DELIMIT_CDR2, Cdr::Endianness::LITTLE_ENDIANNESS), + std::make_tuple(EncodingAlgorithmFlag::PL_CDR2, Cdr::Endianness::BIG_ENDIANNESS), + std::make_tuple(EncodingAlgorithmFlag::PL_CDR2, Cdr::Endianness::LITTLE_ENDIANNESS) + ));