diff --git a/include/fastcdr/CdrSizeCalculator.hpp b/include/fastcdr/CdrSizeCalculator.hpp index 67860e2a..1caab9ef 100644 --- a/include/fastcdr/CdrSizeCalculator.hpp +++ b/include/fastcdr/CdrSizeCalculator.hpp @@ -1166,7 +1166,9 @@ class CdrSizeCalculator 0 < calculated_size) { - if (8 < calculated_size) + if (8 < calculated_size || + (1 != calculated_size && 2 != calculated_size && 4 != calculated_size && + 8 != calculated_size)) { extra_size = 8; // Long EMHEADER. if (NO_SERIALIZED_MEMBER_SIZE != serialized_member_size_) diff --git a/src/cpp/Cdr.cpp b/src/cpp/Cdr.cpp index 189ee21d..1b31547e 100644 --- a/src/cpp/Cdr.cpp +++ b/src/cpp/Cdr.cpp @@ -50,18 +50,18 @@ inline size_t alignment_on_state( inline uint32_t Cdr::get_long_lc( SerializedMemberSizeForNextInt serialized_member_size) { - uint32_t lc = 0x40000000; + uint32_t lc {0x40000000}; switch (serialized_member_size) { case SERIALIZED_MEMBER_SIZE_8: - lc = 0x70000000; + lc = 0x70000000u; break; case SERIALIZED_MEMBER_SIZE_4: - lc = 0x60000000; + lc = 0x60000000u; break; case SERIALIZED_MEMBER_SIZE: - lc = 0x50000000; + lc = 0x50000000u; break; default: break; @@ -73,17 +73,20 @@ inline uint32_t Cdr::get_long_lc( inline uint32_t Cdr::get_short_lc( size_t member_serialized_size) { - uint32_t lc = 0x0; + uint32_t lc {0xFFFFFFFFu}; switch (member_serialized_size) { + case 1: + lc = 0x00000000u; + break; case 2: - lc = 0x10000000; + lc = 0x10000000u; break; case 4: - lc = 0x20000000; + lc = 0x20000000u; break; case 8: - lc = 0x30000000; + lc = 0x30000000u; break; default: break; @@ -2986,7 +2989,7 @@ Cdr& Cdr::xcdr2_end_serialize_member( { const size_t member_serialized_size = last_offset - offset_ - (current_state.header_serialized_ == XCdrHeaderSelection::SHORT_HEADER ? 4 : 8); - if (8 < member_serialized_size) + if (8 < member_serialized_size || 0xFFFFFFFFu == get_short_lc(member_serialized_size)) { switch (current_state.header_serialized_) { @@ -3196,8 +3199,9 @@ Cdr& Cdr::xcdr1_deserialize_type( assert(EncodingAlgorithmFlag::PLAIN_CDR == type_encoding || EncodingAlgorithmFlag::PL_CDR == type_encoding); Cdr::state current_state(*this); + current_encoding_ = type_encoding; - if (EncodingAlgorithmFlag::PL_CDR == type_encoding) + if (EncodingAlgorithmFlag::PL_CDR == current_encoding_) { while (xcdr1_deserialize_member_header(next_member_id_, current_state)) { @@ -3234,6 +3238,7 @@ Cdr& Cdr::xcdr1_deserialize_type( } next_member_id_ = current_state.next_member_id_; + current_encoding_ = current_state.previous_encoding_; return *this; } @@ -3253,8 +3258,9 @@ Cdr& Cdr::xcdr2_deserialize_type( deserialize(dheader); Cdr::state current_state(*this); + current_encoding_ = type_encoding; - if (EncodingAlgorithmFlag::PL_CDR2 == type_encoding) + if (EncodingAlgorithmFlag::PL_CDR2 == current_encoding_) { while (offset_ - current_state.offset_ != dheader) { @@ -3306,10 +3312,13 @@ Cdr& Cdr::xcdr2_deserialize_type( next_member_id_ = current_state.next_member_id_; } + + current_encoding_ = current_state.previous_encoding_; } else { Cdr::state current_state(*this); + current_encoding_ = type_encoding; next_member_id_ = MemberId(0); while (offset_ != end_ && functor(*this, next_member_id_)) @@ -3318,6 +3327,7 @@ Cdr& Cdr::xcdr2_deserialize_type( } next_member_id_ = current_state.next_member_id_; + current_encoding_ = current_state.previous_encoding_; } return *this; diff --git a/test/xcdr/CMakeLists.txt b/test/xcdr/CMakeLists.txt index 6a6c2d70..09861750 100644 --- a/test/xcdr/CMakeLists.txt +++ b/test/xcdr/CMakeLists.txt @@ -19,6 +19,7 @@ set(XCDR_TEST_SOURCE appendable.cpp basic_types.cpp external.cpp + final.cpp mutable.cpp optional.cpp xcdrv1.cpp diff --git a/test/xcdr/appendable.cpp b/test/xcdr/appendable.cpp index 8ab748eb..0b125332 100644 --- a/test/xcdr/appendable.cpp +++ b/test/xcdr/appendable.cpp @@ -256,6 +256,99 @@ void deserialize( } // namespace fastcdr } // namespace eprosima +struct ApInnerStructure +{ +public: + + ApInnerStructure() = default; + + ApInnerStructure( + eprosima::fastcdr::EncodingAlgorithmFlag e1, + eprosima::fastcdr::EncodingAlgorithmFlag e2 + ) + : enc_xcdrv1(e1) + , enc_xcdrv2(e2) + { + } + + ApInnerStructure( + eprosima::fastcdr::EncodingAlgorithmFlag e1, + eprosima::fastcdr::EncodingAlgorithmFlag e2, + uint8_t value + ) + : value1(value) + , enc_xcdrv1(e1) + , enc_xcdrv2(e2) + { + } + + bool operator ==( + const ApInnerStructure& other) const + { + return value1 == other.value1 && + value2.has_value() == other.value2.has_value() && + (!value2.has_value() || value2.value() == other.value2.value()); + } + + //! First being serialized. + uint32_t value1 {0}; + + //! Second being serialized. + eprosima::fastcdr::optional value2; + + eprosima::fastcdr::EncodingAlgorithmFlag enc_xcdrv1 {eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR}; + + eprosima::fastcdr::EncodingAlgorithmFlag enc_xcdrv2 {eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR2}; +}; + +namespace eprosima { +namespace fastcdr { + +template<> +void serialize( + Cdr& cdr, + const ApInnerStructure& data) +{ + Cdr::state current_status(cdr); + cdr.begin_serialize_type(current_status, cdr.get_cdr_version() == eprosima::fastcdr::CdrVersion::XCDRv1 + ? data.enc_xcdrv1 + : data.enc_xcdrv2); + cdr << MemberId(0) << data.value1; + cdr << MemberId(1) << data.value2; + cdr.end_serialize_type(current_status); +} + +template<> +void deserialize( + Cdr& cdr, + ApInnerStructure& data) +{ + cdr.deserialize_type(cdr.get_cdr_version() == eprosima::fastcdr::CdrVersion::XCDRv1 + ? data.enc_xcdrv1 + : data.enc_xcdrv2, + [&data](Cdr& cdr_inner, const MemberId& mid) -> bool + { + bool ret_value {true}; + 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 + /*! * @test Test an appendable structure where the encoded version has more members that the decoded one. * @code{.idl} @@ -958,6 +1051,262 @@ TEST_P(XCdrAppendableTest, inner_mutable) //} } +/*! + * @test Test an inner final structure inside a appendable structure. + * @code{.idl} + * @final + * struct InnerFinalStructure + * { + * @id(0) + * unsigned long value1; + * @id(1) @optional + * unsigned long value2; + * }; + * + * @appendable + * struct AppendableWithInnerFinalStruct + * { + * @id(1) + * unsigned long value1; + * @id(2) + * InnerFinalStructure value2; + * }; + * @endcode + */ +TEST_P(XCdrAppendableTest, inner_final_structure) +{ + constexpr uint8_t ival {0xCD}; + + //{ Defining expected XCDR streams + XCdrStreamValues expected_streams; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x00, 0x00, 0x00, // Encapsulation + 0x00, 0x00, 0x00, ival, // ULong + 0x00, 0x00, 0x00, ival, // ULong + 0x00, 0x01, 0x00, 0x00, // ShortMemberHeader (optional) + }; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x01, 0x00, 0x00, // Encapsulation + ival, 0x00, 0x00, 0x00, // ULong + ival, 0x00, 0x00, 0x00, // ULong + 0x01, 0x00, 0x00, 0x00, // ShortMemberHeader (optional) + }; + expected_streams[0 + EncodingAlgorithmFlag::DELIMIT_CDR2 + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x08, 0x00, 0x00, // Encapsulation + 0x00, 0x00, 0x00, 0x09, // DHEADER + 0x00, 0x00, 0x00, ival, // ULong + 0x00, 0x00, 0x00, ival, // ULong + 0x00, // Optional not present + }; + expected_streams[0 + EncodingAlgorithmFlag::DELIMIT_CDR2 + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x09, 0x00, 0x00, // Encapsulation + 0x09, 0x00, 0x00, 0x00, // DHEADER + ival, 0x00, 0x00, 0x00, // ULong + ival, 0x00, 0x00, 0x00, // ULong + 0x00, // Optional not present + }; + //} + + EncodingAlgorithmFlag encoding = std::get<0>(GetParam()); + Cdr::Endianness endianness = std::get<1>(GetParam()); + + //{ 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 + uint32_t value1 {ival}; + ApInnerStructure value2 {eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR2, ival}; + cdr.set_encoding_flag(encoding); + cdr.serialize_encapsulation(); + Cdr::state enc_state(cdr); + cdr.begin_serialize_type(enc_state, encoding); + cdr << MemberId(1) << value1; + cdr << MemberId(2) << value2; + cdr.end_serialize_type(enc_state); + Cdr::state enc_state_end(cdr); + //} + + //{ Test encoded content + ASSERT_EQ(cdr.get_serialized_data_length(), expected_streams[tested_stream].size()); + ASSERT_EQ(0, memcmp(buffer.get(), expected_streams[tested_stream].data(), + expected_streams[tested_stream].size())); + //} + + //{ Decoding + uint32_t dvalue1 {0}; + ApInnerStructure dvalue2 {eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR2, ival}; + cdr.reset(); + cdr.read_encapsulation(); + ASSERT_EQ(cdr.get_encoding_flag(), encoding); + ASSERT_EQ(cdr.endianness(), endianness); + cdr.deserialize_type(encoding, [&](Cdr& cdr_inner, const MemberId& mid)->bool + { + bool ret_value {true}; + + switch (mid.id) + { + case 0: + cdr_inner >> dvalue1; + break; + case 1: + cdr_inner >> dvalue2; + break; + default: + ret_value = false; + break; + } + + return ret_value; + }); + ASSERT_EQ(value1, dvalue1); + ASSERT_EQ(value2, dvalue2); + Cdr::state dec_state_end(cdr); + ASSERT_EQ(enc_state_end, dec_state_end); + //} +} + +/*! + * @test Test an inner mutable structure inside a appendable structure. + * @code{.idl} + * @mutable + * struct InnerMutableStructure + * { + * @id(0) + * unsigned long value1; + * @id(1) @optional + * unsigned long value2; + * }; + * + * @appendable + * struct AppendableWithInnerMutableStruct + * { + * @id(1) + * unsigned long value1; + * @id(2) + * InnerMutableStructure value2; + * }; + * @endcode + */ +TEST_P(XCdrAppendableTest, inner_mutable_structure) +{ + constexpr uint8_t ival {0xCD}; + + //{ Defining expected XCDR streams + XCdrStreamValues expected_streams; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x00, 0x00, 0x00, // Encapsulation + 0x00, 0x00, 0x00, ival, // ULong + 0x00, 0x00, 0x00, 0x04, // ShortMemberHeader + 0x00, 0x00, 0x00, ival, // ULong + 0x3F, 0x02, 0x00, 0x00, // Sentinel + }; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x01, 0x00, 0x00, // Encapsulation + ival, 0x00, 0x00, 0x00, // ULong + 0x00, 0x00, 0x04, 0x00, // ShortMemberHeader + ival, 0x00, 0x00, 0x00, // ULong + 0x02, 0x3F, 0x00, 0x00, // Sentinel + }; + expected_streams[0 + EncodingAlgorithmFlag::DELIMIT_CDR2 + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x08, 0x00, 0x00, // Encapsulation + 0x00, 0x00, 0x00, 0x10, // DHEADER + 0x00, 0x00, 0x00, ival, // ULong + 0x00, 0x00, 0x00, 0x08, // DHEADER + 0x20, 0x00, 0x00, 0x00, // EMHEADER1(M) without NEXTINT + 0x00, 0x00, 0x00, ival, // ULong + }; + expected_streams[0 + EncodingAlgorithmFlag::DELIMIT_CDR2 + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x09, 0x00, 0x00, // Encapsulation + 0x10, 0x00, 0x00, 0x00, // DHEADER + ival, 0x00, 0x00, 0x00, // ULong + 0x08, 0x00, 0x00, 0x00, // DHEADER + 0x00, 0x00, 0x00, 0x20, // EMHEADER1(M) without NEXTINT + ival, 0x00, 0x00, 0x00, // ULong + }; + //} + + EncodingAlgorithmFlag encoding = std::get<0>(GetParam()); + Cdr::Endianness endianness = std::get<1>(GetParam()); + + //{ 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 + uint32_t value1 {ival}; + ApInnerStructure value2 {eprosima::fastcdr::EncodingAlgorithmFlag::PL_CDR, + eprosima::fastcdr::EncodingAlgorithmFlag::PL_CDR2, ival}; + cdr.set_encoding_flag(encoding); + cdr.serialize_encapsulation(); + Cdr::state enc_state(cdr); + cdr.begin_serialize_type(enc_state, encoding); + cdr << MemberId(1) << value1; + cdr << MemberId(2) << value2; + cdr.end_serialize_type(enc_state); + Cdr::state enc_state_end(cdr); + //} + + //{ Test encoded content + ASSERT_EQ(cdr.get_serialized_data_length(), expected_streams[tested_stream].size()); + ASSERT_EQ(0, memcmp(buffer.get(), expected_streams[tested_stream].data(), + expected_streams[tested_stream].size())); + //} + + //{ Decoding + uint32_t dvalue1 {0}; + ApInnerStructure dvalue2 {eprosima::fastcdr::EncodingAlgorithmFlag::PL_CDR, + eprosima::fastcdr::EncodingAlgorithmFlag::PL_CDR2, ival}; + cdr.reset(); + cdr.read_encapsulation(); + ASSERT_EQ(cdr.get_encoding_flag(), encoding); + ASSERT_EQ(cdr.endianness(), endianness); + cdr.deserialize_type(encoding, [&](Cdr& cdr_inner, const MemberId& mid)->bool + { + bool ret_value {true}; + + switch (mid.id) + { + case 0: + cdr_inner >> dvalue1; + break; + case 1: + cdr_inner >> dvalue2; + break; + default: + ret_value = false; + break; + } + + return ret_value; + }); + ASSERT_EQ(value1, dvalue1); + ASSERT_EQ(value2, dvalue2); + Cdr::state dec_state_end(cdr); + ASSERT_EQ(enc_state_end, dec_state_end); + //} +} + INSTANTIATE_TEST_SUITE_P( XCdrTest, XCdrAppendableTest, diff --git a/test/xcdr/final.cpp b/test/xcdr/final.cpp new file mode 100644 index 00000000..8283e3a3 --- /dev/null +++ b/test/xcdr/final.cpp @@ -0,0 +1,392 @@ +// 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 + +#include + +#include +#include "utility.hpp" + +using namespace eprosima::fastcdr; + +using XCdrStreamValues = + std::array, + 1 + EncodingAlgorithmFlag::PL_CDR2 + Cdr::Endianness::LITTLE_ENDIANNESS>; + + +class XCdrFinalTest : public ::testing::TestWithParam< std::tuple> +{ +}; + +struct FiInnerStructure +{ +public: + + FiInnerStructure() = default; + + FiInnerStructure( + eprosima::fastcdr::EncodingAlgorithmFlag e1, + eprosima::fastcdr::EncodingAlgorithmFlag e2 + ) + : enc_xcdrv1(e1) + , enc_xcdrv2(e2) + { + } + + FiInnerStructure( + eprosima::fastcdr::EncodingAlgorithmFlag e1, + eprosima::fastcdr::EncodingAlgorithmFlag e2, + uint8_t value + ) + : value1(value) + , enc_xcdrv1(e1) + , enc_xcdrv2(e2) + { + } + + bool operator ==( + const FiInnerStructure& other) const + { + return value1 == other.value1 && + value2.has_value() == other.value2.has_value() && + (!value2.has_value() || value2.value() == other.value2.value()); + } + + //! First being serialized. + uint32_t value1 {0}; + + //! Second being serialized. + eprosima::fastcdr::optional value2; + + eprosima::fastcdr::EncodingAlgorithmFlag enc_xcdrv1 {eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR}; + + eprosima::fastcdr::EncodingAlgorithmFlag enc_xcdrv2 {eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR2}; +}; + +namespace eprosima { +namespace fastcdr { + +template<> +void serialize( + Cdr& cdr, + const FiInnerStructure& data) +{ + Cdr::state current_status(cdr); + cdr.begin_serialize_type(current_status, cdr.get_cdr_version() == eprosima::fastcdr::CdrVersion::XCDRv1 + ? data.enc_xcdrv1 + : data.enc_xcdrv2); + cdr << MemberId(0) << data.value1; + cdr << MemberId(1) << data.value2; + cdr.end_serialize_type(current_status); +} + +template<> +void deserialize( + Cdr& cdr, + FiInnerStructure& data) +{ + cdr.deserialize_type(cdr.get_cdr_version() == eprosima::fastcdr::CdrVersion::XCDRv1 + ? data.enc_xcdrv1 + : data.enc_xcdrv2, + [&data](Cdr& cdr_inner, const MemberId& mid) -> bool + { + bool ret_value {true}; + 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 + +/*! + * @test Test an inner appendable structure inside a final structure. + * @code{.idl} + * @appendable + * struct InnerAppendableStructure + * { + * @id(0) + * unsigned long value1; + * @id(1) @optional + * unsigned long value2; + * }; + * + * @final + * struct FinalWithInnerAppendableStruct + * { + * @id(1) + * unsigned long value1; + * @id(2) + * InnerAppendableStructure value2; + * }; + * @endcode + */ +TEST_P(XCdrFinalTest, inner_appendable_structure) +{ + constexpr uint8_t ival {0xCD}; + + //{ Defining expected XCDR streams + XCdrStreamValues expected_streams; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x00, 0x00, 0x00, // Encapsulation + 0x00, 0x00, 0x00, ival, // ULong + 0x00, 0x00, 0x00, ival, // ULong + 0x00, 0x01, 0x00, 0x00, // ShortMemberHeader (optional) + }; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x01, 0x00, 0x00, // Encapsulation + ival, 0x00, 0x00, 0x00, // ULong + ival, 0x00, 0x00, 0x00, // ULong + 0x01, 0x00, 0x00, 0x00, // ShortMemberHeader (optional) + }; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR2 + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x06, 0x00, 0x00, // Encapsulation + 0x00, 0x00, 0x00, ival, // ULong + 0x00, 0x00, 0x00, 0x05, // DHEADER + 0x00, 0x00, 0x00, ival, // ULong + 0x00, // Optional not present + }; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR2 + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x07, 0x00, 0x00, // Encapsulation + ival, 0x00, 0x00, 0x00, // ULong + 0x05, 0x00, 0x00, 0x00, // DHEADER + ival, 0x00, 0x00, 0x00, // ULong + 0x00, // Optional not present + }; + //} + + EncodingAlgorithmFlag encoding = std::get<0>(GetParam()); + Cdr::Endianness endianness = std::get<1>(GetParam()); + + //{ 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 + uint32_t value1 {ival}; + FiInnerStructure value2 {eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2, ival}; + cdr.set_encoding_flag(encoding); + cdr.serialize_encapsulation(); + Cdr::state enc_state(cdr); + cdr.begin_serialize_type(enc_state, encoding); + cdr << MemberId(1) << value1; + cdr << MemberId(2) << value2; + cdr.end_serialize_type(enc_state); + Cdr::state enc_state_end(cdr); + //} + + //{ Test encoded content + ASSERT_EQ(cdr.get_serialized_data_length(), expected_streams[tested_stream].size()); + ASSERT_EQ(0, memcmp(buffer.get(), expected_streams[tested_stream].data(), + expected_streams[tested_stream].size())); + //} + + //{ Decoding + uint32_t dvalue1 {0}; + FiInnerStructure dvalue2 {eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2, ival}; + cdr.reset(); + cdr.read_encapsulation(); + ASSERT_EQ(cdr.get_encoding_flag(), encoding); + ASSERT_EQ(cdr.endianness(), endianness); + cdr.deserialize_type(encoding, [&](Cdr& cdr_inner, const MemberId& mid)->bool + { + bool ret_value {true}; + + switch (mid.id) + { + case 0: + cdr_inner >> dvalue1; + break; + case 1: + cdr_inner >> dvalue2; + break; + default: + ret_value = false; + break; + } + + return ret_value; + }); + ASSERT_EQ(value1, dvalue1); + ASSERT_EQ(value2, dvalue2); + Cdr::state dec_state_end(cdr); + ASSERT_EQ(enc_state_end, dec_state_end); + //} +} + +/*! + * @test Test an inner mutable structure inside a final structure. + * @code{.idl} + * @mutable + * struct InnerMutableStructure + * { + * @id(0) + * unsigned long value1; + * @id(1) @optional + * unsigned long value2; + * }; + * + * @final + * struct FinalWithInnerMutableStruct + * { + * @id(1) + * unsigned long value1; + * @id(2) + * InnerMutableStructure value2; + * }; + * @endcode + */ +TEST_P(XCdrFinalTest, inner_mutable_structure) +{ + constexpr uint8_t ival {0xCD}; + + //{ Defining expected XCDR streams + XCdrStreamValues expected_streams; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x00, 0x00, 0x00, // Encapsulation + 0x00, 0x00, 0x00, ival, // ULong + 0x00, 0x00, 0x00, 0x04, // ShortMemberHeader + 0x00, 0x00, 0x00, ival, // ULong + 0x3F, 0x02, 0x00, 0x00, // Sentinel + }; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x01, 0x00, 0x00, // Encapsulation + ival, 0x00, 0x00, 0x00, // ULong + 0x00, 0x00, 0x04, 0x00, // ShortMemberHeader + ival, 0x00, 0x00, 0x00, // ULong + 0x02, 0x3F, 0x00, 0x00, // Sentinel + }; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR2 + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x06, 0x00, 0x00, // Encapsulation + 0x00, 0x00, 0x00, ival, // ULong + 0x00, 0x00, 0x00, 0x08, // DHEADER + 0x20, 0x00, 0x00, 0x00, // EMHEADER1(M) without NEXTINT + 0x00, 0x00, 0x00, ival, // ULong + }; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR2 + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x07, 0x00, 0x00, // Encapsulation + ival, 0x00, 0x00, 0x00, // ULong + 0x08, 0x00, 0x00, 0x00, // DHEADER + 0x00, 0x00, 0x00, 0x20, // EMHEADER1(M) without NEXTINT + ival, 0x00, 0x00, 0x00, // ULong + }; + //} + + EncodingAlgorithmFlag encoding = std::get<0>(GetParam()); + Cdr::Endianness endianness = std::get<1>(GetParam()); + + //{ 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 + uint32_t value1 {ival}; + FiInnerStructure value2 {eprosima::fastcdr::EncodingAlgorithmFlag::PL_CDR, + eprosima::fastcdr::EncodingAlgorithmFlag::PL_CDR2, ival}; + cdr.set_encoding_flag(encoding); + cdr.serialize_encapsulation(); + Cdr::state enc_state(cdr); + cdr.begin_serialize_type(enc_state, encoding); + cdr << MemberId(1) << value1; + cdr << MemberId(2) << value2; + cdr.end_serialize_type(enc_state); + Cdr::state enc_state_end(cdr); + //} + + //{ Test encoded content + ASSERT_EQ(cdr.get_serialized_data_length(), expected_streams[tested_stream].size()); + ASSERT_EQ(0, memcmp(buffer.get(), expected_streams[tested_stream].data(), + expected_streams[tested_stream].size())); + //} + + //{ Decoding + uint32_t dvalue1 {0}; + FiInnerStructure dvalue2 {eprosima::fastcdr::EncodingAlgorithmFlag::PL_CDR, + eprosima::fastcdr::EncodingAlgorithmFlag::PL_CDR2, ival}; + cdr.reset(); + cdr.read_encapsulation(); + ASSERT_EQ(cdr.get_encoding_flag(), encoding); + ASSERT_EQ(cdr.endianness(), endianness); + cdr.deserialize_type(encoding, [&](Cdr& cdr_inner, const MemberId& mid)->bool + { + bool ret_value {true}; + + switch (mid.id) + { + case 0: + cdr_inner >> dvalue1; + break; + case 1: + cdr_inner >> dvalue2; + break; + default: + ret_value = false; + break; + } + + return ret_value; + }); + ASSERT_EQ(value1, dvalue1); + ASSERT_EQ(value2, dvalue2); + Cdr::state dec_state_end(cdr); + ASSERT_EQ(enc_state_end, dec_state_end); + //} +} + +INSTANTIATE_TEST_SUITE_P( + XCdrTest, + XCdrFinalTest, + ::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::PLAIN_CDR2, Cdr::Endianness::BIG_ENDIANNESS), + std::make_tuple(EncodingAlgorithmFlag::PLAIN_CDR2, Cdr::Endianness::LITTLE_ENDIANNESS) + )); + diff --git a/test/xcdr/mutable.cpp b/test/xcdr/mutable.cpp index e09c3961..ba88bf12 100644 --- a/test/xcdr/mutable.cpp +++ b/test/xcdr/mutable.cpp @@ -183,6 +183,99 @@ void deserialize( } // namespace fastcdr } // namespace eprosima +struct MuInnerStructure +{ +public: + + MuInnerStructure() = default; + + MuInnerStructure( + eprosima::fastcdr::EncodingAlgorithmFlag e1, + eprosima::fastcdr::EncodingAlgorithmFlag e2 + ) + : enc_xcdrv1(e1) + , enc_xcdrv2(e2) + { + } + + MuInnerStructure( + eprosima::fastcdr::EncodingAlgorithmFlag e1, + eprosima::fastcdr::EncodingAlgorithmFlag e2, + uint8_t value + ) + : value1(value) + , enc_xcdrv1(e1) + , enc_xcdrv2(e2) + { + } + + bool operator ==( + const MuInnerStructure& other) const + { + return value1 == other.value1 && + value2.has_value() == other.value2.has_value() && + (!value2.has_value() || value2.value() == other.value2.value()); + } + + //! First being serialized. + uint32_t value1 {0}; + + //! Second being serialized. + eprosima::fastcdr::optional value2; + + eprosima::fastcdr::EncodingAlgorithmFlag enc_xcdrv1 {eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR}; + + eprosima::fastcdr::EncodingAlgorithmFlag enc_xcdrv2 {eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR2}; +}; + +namespace eprosima { +namespace fastcdr { + +template<> +void serialize( + Cdr& cdr, + const MuInnerStructure& data) +{ + Cdr::state current_status(cdr); + cdr.begin_serialize_type(current_status, cdr.get_cdr_version() == eprosima::fastcdr::CdrVersion::XCDRv1 + ? data.enc_xcdrv1 + : data.enc_xcdrv2); + cdr << MemberId(3) << data.value1; + cdr << MemberId(16) << data.value2; + cdr.end_serialize_type(current_status); +} + +template<> +void deserialize( + Cdr& cdr, + MuInnerStructure& data) +{ + cdr.deserialize_type(cdr.get_cdr_version() == eprosima::fastcdr::CdrVersion::XCDRv1 + ? data.enc_xcdrv1 + : data.enc_xcdrv2, + [&data](Cdr& cdr_inner, const MemberId& mid) -> bool + { + bool ret_value {true}; + 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 + /*! * @test Test a mutable structure where the encoded version has more members that the decoded one. * @code{.idl} @@ -861,6 +954,282 @@ TEST_P(XCdrMutableTest, inner_unordered_and_less_serialized_elements) //} } +/*! + * @test Test an inner final structure inside a mutable structure. + * @code{.idl} + * @final + * struct InnerFinalStructure + * { + * @id(3) + * unsigned long value1; + * @id(16) @optional + * unsigned long value2; + * }; + * + * @mutable + * struct MutableWithInnerFinalStruct + * { + * @id(1) + * unsigned long value1; + * @id(2) + * InnerFinalStructure value2; + * }; + * @endcode + */ +TEST_P(XCdrMutableTest, inner_final_structure) +{ + constexpr uint8_t ival {0xCD}; + + //{ Defining expected XCDR streams + XCdrStreamValues expected_streams; + expected_streams[0 + EncodingAlgorithmFlag::PL_CDR + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x02, 0x00, 0x00, // Encapsulation + 0x00, 0x01, 0x00, 0x04, // ShortMemberHeader + 0x00, 0x00, 0x00, ival, // ULong + 0x00, 0x02, 0x00, 0x08, // ShortMemberHeader + 0x00, 0x00, 0x00, ival, // ULong + 0x00, 0x10, 0x00, 0x00, // ShortMemberHeader (optional) + 0x3F, 0x02, 0x00, 0x00, // Sentinel + }; + expected_streams[0 + EncodingAlgorithmFlag::PL_CDR + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x03, 0x00, 0x00, // Encapsulation + 0x01, 0x00, 0x04, 0x00, // ShortMemberHeader + ival, 0x00, 0x00, 0x00, // ULong + 0x02, 0x00, 0x08, 0x00, // ShortMemberHeader + ival, 0x00, 0x00, 0x00, // ULong + 0x10, 0x00, 0x00, 0x00, // ShortMemberHeader + 0x02, 0x3F, 0x00, 0x00, // Sentinel + }; + expected_streams[0 + EncodingAlgorithmFlag::PL_CDR2 + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x0A, 0x00, 0x00, // Encapsulation + 0x00, 0x00, 0x00, 0x15, // DHEADER + 0x20, 0x00, 0x00, 0x01, // EMHEADER1(M) without NEXTINT + 0x00, 0x00, 0x00, ival, // ULong + 0x40, 0x00, 0x00, 0x02, // EMHEADER1(M) with NEXTINT + 0x00, 0x00, 0x00, 0x05, // NEXTINT + 0x00, 0x00, 0x00, ival, // ULong + 0x00, // Optional not present + }; + expected_streams[0 + EncodingAlgorithmFlag::PL_CDR2 + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x0B, 0x00, 0x00, // Encapsulation + 0x15, 0x00, 0x00, 0x00, // DHEADER + 0x01, 0x00, 0x00, 0x20, // EMHEADER1(M) without NEXTINT + ival, 0x00, 0x00, 0x00, // ULong + 0x02, 0x00, 0x00, 0x40, // EMHEADER1(M) with NEXTINT + 0x05, 0x00, 0x00, 0x00, // NEXTINT + ival, 0x00, 0x00, 0x00, // ULong + 0x00, // Optional not present + }; + //} + + EncodingAlgorithmFlag encoding = std::get<0>(GetParam()); + Cdr::Endianness endianness = std::get<1>(GetParam()); + + //{ 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 + uint32_t value1 {ival}; + MuInnerStructure value2 {eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR2, ival}; + cdr.set_encoding_flag(encoding); + cdr.serialize_encapsulation(); + Cdr::state enc_state(cdr); + cdr.begin_serialize_type(enc_state, encoding); + cdr << MemberId(1) << value1; + cdr << MemberId(2) << value2; + cdr.end_serialize_type(enc_state); + Cdr::state enc_state_end(cdr); + //} + + //{ Test encoded content + ASSERT_EQ(cdr.get_serialized_data_length(), expected_streams[tested_stream].size()); + ASSERT_EQ(0, memcmp(buffer.get(), expected_streams[tested_stream].data(), + expected_streams[tested_stream].size())); + //} + + //{ Decoding + uint32_t dvalue1 {0}; + MuInnerStructure dvalue2 {eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR2, ival}; + cdr.reset(); + cdr.read_encapsulation(); + ASSERT_EQ(cdr.get_encoding_flag(), encoding); + ASSERT_EQ(cdr.endianness(), endianness); + cdr.deserialize_type(encoding, [&](Cdr& cdr_inner, const MemberId& mid)->bool + { + bool ret_value {true}; + + switch (mid.id) + { + case 1: + cdr_inner >> dvalue1; + break; + case 2: + cdr_inner >> dvalue2; + break; + default: + ret_value = false; + break; + } + + return ret_value; + }); + ASSERT_EQ(value1, dvalue1); + ASSERT_EQ(value2, dvalue2); + Cdr::state dec_state_end(cdr); + ASSERT_EQ(enc_state_end, dec_state_end); + //} +} + +/*! + * @test Test an inner appendable structure inside a mutable structure. + * @code{.idl} + * @appendable + * struct InnerAppendableStructure + * { + * @id(3) + * unsigned long value1; + * @id(16) @optional + * unsigned long value2; + * }; + * + * @mutable + * struct MutableWithInnerAppendableStruct + * { + * @id(1) + * unsigned long value1; + * @id(2) + * InnerAppendableStructure value2; + * }; + * @endcode + */ +TEST_P(XCdrMutableTest, inner_appendable_structure) +{ + constexpr uint8_t ival {0xCD}; + + //{ Defining expected XCDR streams + XCdrStreamValues expected_streams; + expected_streams[0 + EncodingAlgorithmFlag::PL_CDR + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x02, 0x00, 0x00, // Encapsulation + 0x00, 0x01, 0x00, 0x04, // ShortMemberHeader + 0x00, 0x00, 0x00, ival, // ULong + 0x00, 0x02, 0x00, 0x08, // ShortMemberHeader + 0x00, 0x00, 0x00, ival, // ULong + 0x00, 0x10, 0x00, 0x00, // ShortMemberHeader (optional) + 0x3F, 0x02, 0x00, 0x00, // Sentinel + }; + expected_streams[0 + EncodingAlgorithmFlag::PL_CDR + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x03, 0x00, 0x00, // Encapsulation + 0x01, 0x00, 0x04, 0x00, // ShortMemberHeader + ival, 0x00, 0x00, 0x00, // ULong + 0x02, 0x00, 0x08, 0x00, // ShortMemberHeader + ival, 0x00, 0x00, 0x00, // ULong + 0x10, 0x00, 0x00, 0x00, // ShortMemberHeader + 0x02, 0x3F, 0x00, 0x00, // Sentinel + }; + expected_streams[0 + EncodingAlgorithmFlag::PL_CDR2 + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x0A, 0x00, 0x00, // Encapsulation + 0x00, 0x00, 0x00, 0x15, // DHEADER + 0x20, 0x00, 0x00, 0x01, // EMHEADER1(M) without NEXTINT + 0x00, 0x00, 0x00, ival, // ULong + 0x50, 0x00, 0x00, 0x02, // EMHEADER1(M) with NEXTINT + 0x00, 0x00, 0x00, 0x05, // NEXTINT + DHEADER + 0x00, 0x00, 0x00, ival, // ULong + 0x00, // Optional not present + }; + expected_streams[0 + EncodingAlgorithmFlag::PL_CDR2 + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x0B, 0x00, 0x00, // Encapsulation + 0x15, 0x00, 0x00, 0x00, // DHEADER + 0x01, 0x00, 0x00, 0x20, // EMHEADER1(M) without NEXTINT + ival, 0x00, 0x00, 0x00, // ULong + 0x02, 0x00, 0x00, 0x50, // EMHEADER1(M) with NEXTINT + 0x05, 0x00, 0x00, 0x00, // NEXTINT + DHEADER + ival, 0x00, 0x00, 0x00, // ULong + 0x00, // Optional not present + }; + //} + + EncodingAlgorithmFlag encoding = std::get<0>(GetParam()); + Cdr::Endianness endianness = std::get<1>(GetParam()); + + //{ 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 + uint32_t value1 {ival}; + MuInnerStructure value2 {eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2, ival}; + cdr.set_encoding_flag(encoding); + cdr.serialize_encapsulation(); + Cdr::state enc_state(cdr); + cdr.begin_serialize_type(enc_state, encoding); + cdr << MemberId(1) << value1; + cdr << MemberId(2) << value2; + cdr.end_serialize_type(enc_state); + Cdr::state enc_state_end(cdr); + //} + + //{ Test encoded content + ASSERT_EQ(cdr.get_serialized_data_length(), expected_streams[tested_stream].size()); + ASSERT_EQ(0, memcmp(buffer.get(), expected_streams[tested_stream].data(), + expected_streams[tested_stream].size())); + //} + + //{ Decoding + uint32_t dvalue1 {0}; + MuInnerStructure dvalue2 {eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2, ival}; + cdr.reset(); + cdr.read_encapsulation(); + ASSERT_EQ(cdr.get_encoding_flag(), encoding); + ASSERT_EQ(cdr.endianness(), endianness); + cdr.deserialize_type(encoding, [&](Cdr& cdr_inner, const MemberId& mid)->bool + { + bool ret_value {true}; + + switch (mid.id) + { + case 1: + cdr_inner >> dvalue1; + break; + case 2: + cdr_inner >> dvalue2; + break; + default: + ret_value = false; + break; + } + + return ret_value; + }); + ASSERT_EQ(value1, dvalue1); + ASSERT_EQ(value2, dvalue2); + Cdr::state dec_state_end(cdr); + ASSERT_EQ(enc_state_end, dec_state_end); + //} +} + INSTANTIATE_TEST_SUITE_P( XCdrTest, XCdrMutableTest,