From ab25f341177f28ae00e70e5d629438a70e0e9693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Gonz=C3=A1lez=20Moreno?= Date: Tue, 28 Apr 2026 09:01:34 +0200 Subject: [PATCH 1/3] Add regression test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ricardo González Moreno --- test/xcdr/optional.cpp | 139 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 138 insertions(+), 1 deletion(-) diff --git a/test/xcdr/optional.cpp b/test/xcdr/optional.cpp index 286adde8..558acefa 100644 --- a/test/xcdr/optional.cpp +++ b/test/xcdr/optional.cpp @@ -31,7 +31,7 @@ using XCdrStreamValues = 1 + EncodingAlgorithmFlag::PL_CDR2 + Cdr::Endianness::LITTLE_ENDIANNESS>; -class XCdrOptionalTest : public ::testing::TestWithParam< std::tuple> +class XCdrOptionalTest : public ::testing::TestWithParam> { }; @@ -7913,6 +7913,143 @@ TEST_P(XCdrOptionalTest, two_inner_short_optional) } } +/*! + * @test Test encoding of an empty optional field of octet type + * @code{.idl} + * struct NullOptional + * { + * @optional + * octet var_octet; + * }; + * @endcode + */ +TEST(XCdrOptionalTest, plaincdr_regression_test) +{ + //{ Defining expected XCDR streams + XCdrStreamValues expected_streams; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR + Cdr::Endianness::BIG_ENDIANNESS] = + { + 0x00, 0x00, 0x00, 0x00, // Encapsulation + 0x00, 0x01, 0x00, 0x00 // ShortMemberHeader + }; + expected_streams[0 + EncodingAlgorithmFlag::PLAIN_CDR + Cdr::Endianness::LITTLE_ENDIANNESS] = + { + 0x00, 0x01, 0x00, 0x00, // Encapsulation + 0x01, 0x00, 0x00, 0x00 // ShortMemberHeader + }; + //} + + optional opt_value; + + for (uint8_t tested_stream {0}; tested_stream < 2; ++tested_stream) + { + + //{ Calculate encoded size. + CdrSizeCalculator calculator(get_version_from_algorithm(EncodingAlgorithmFlag::PLAIN_CDR)); + size_t current_alignment {0}; + size_t calculated_size {calculator.begin_calculate_type_serialized_size(EncodingAlgorithmFlag::PLAIN_CDR, + current_alignment)}; + calculated_size += calculator.calculate_member_serialized_size(MemberId(1), opt_value, current_alignment); + calculated_size += calculator.end_calculate_type_serialized_size(EncodingAlgorithmFlag::PLAIN_CDR, + current_alignment); + calculated_size += 4; // Encapsulation + //} + + { + + //{ Prepare buffer + 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, + 0 == tested_stream ? Cdr::Endianness::BIG_ENDIANNESS : Cdr::Endianness::LITTLE_ENDIANNESS, + get_version_from_algorithm(EncodingAlgorithmFlag::PLAIN_CDR)); + //} + + //{ Encode optional not present. + cdr.set_encoding_flag(EncodingAlgorithmFlag::PLAIN_CDR); + cdr.serialize_encapsulation(); + Cdr::state enc_state(cdr); + cdr.begin_serialize_type(enc_state, EncodingAlgorithmFlag::PLAIN_CDR); + cdr.serialize_member(MemberId(1), opt_value); + cdr.end_serialize_type(enc_state); + cdr.set_dds_cdr_options({0, 0}); + 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 optional not present + optional dopt_value {3}; + cdr.reset(); + cdr.read_encapsulation(); + ASSERT_EQ(cdr.get_encoding_flag(), EncodingAlgorithmFlag::PLAIN_CDR); + ASSERT_EQ(cdr.endianness(), + 0 == tested_stream ? Cdr::Endianness::BIG_ENDIANNESS : Cdr::Endianness::LITTLE_ENDIANNESS); + cdr.deserialize_type(EncodingAlgorithmFlag::PLAIN_CDR, [&](Cdr& cdr_inner, const MemberId&)->bool + { + cdr_inner.deserialize_member(dopt_value); + + return false; + }); + ASSERT_FALSE(dopt_value.has_value()); + //} + } + + { + //{ Prepare buffer + 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, + 0 == tested_stream ? Cdr::Endianness::BIG_ENDIANNESS : Cdr::Endianness::LITTLE_ENDIANNESS, + get_version_from_algorithm(EncodingAlgorithmFlag::PLAIN_CDR)); + //} + + //{ Encode optional not present. + cdr.set_encoding_flag(EncodingAlgorithmFlag::PLAIN_CDR); + cdr.serialize_encapsulation(); + Cdr::state enc_state(cdr); + cdr.begin_serialize_type(enc_state, EncodingAlgorithmFlag::PLAIN_CDR); + cdr << MemberId(1) << opt_value; + cdr.end_serialize_type(enc_state); + cdr.set_dds_cdr_options({0, 0}); + 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 optional not present + optional dopt_value {3}; + cdr.reset(); + cdr.read_encapsulation(); + ASSERT_EQ(cdr.get_encoding_flag(), EncodingAlgorithmFlag::PLAIN_CDR); + ASSERT_EQ(cdr.endianness(), + 0 == tested_stream ? Cdr::Endianness::BIG_ENDIANNESS : Cdr::Endianness::LITTLE_ENDIANNESS); + cdr.deserialize_type(EncodingAlgorithmFlag::PLAIN_CDR, [&](Cdr& cdr_inner, const MemberId&)->bool + { + cdr_inner >> dopt_value; + + return false; + }); + ASSERT_FALSE(dopt_value.has_value()); + //} + } + } +} + INSTANTIATE_TEST_SUITE_P( XCdrTest, XCdrOptionalTest, From b7e590c3ecb2d8277a7c859dc5766d0ed199b36d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Gonz=C3=A1lez=20Moreno?= Date: Tue, 28 Apr 2026 09:02:01 +0200 Subject: [PATCH 2/3] Fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ricardo González Moreno --- include/fastcdr/Cdr.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/fastcdr/Cdr.h b/include/fastcdr/Cdr.h index a582e6a2..cd576077 100644 --- a/include/fastcdr/Cdr.h +++ b/include/fastcdr/Cdr.h @@ -720,7 +720,7 @@ class Cdr * @exception exception::NotEnoughMemoryException This exception is thrown when trying to encode into a buffer * position that exceeds the internal memory size. */ - template + template Cdr& serialize( const fixed_string& value) { @@ -1787,7 +1787,7 @@ class Cdr * @exception exception::NotEnoughMemoryException This exception is thrown when trying to decode from a buffer * position that exceeds the internal memory size. */ - template + template Cdr& deserialize( fixed_string& value) { @@ -2727,6 +2727,7 @@ class Cdr MemberId member_id; xcdr1_deserialize_member_header(member_id, current_state); auto prev_offset = offset_; + member_value.reset(0 < current_state.member_size_); if (0 < current_state.member_size_) { deserialize(member_value); From f8838c724930e6885dd7058c7882e3cc02c21ee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Gonz=C3=A1lez=20Moreno?= Date: Tue, 28 Apr 2026 10:54:35 +0200 Subject: [PATCH 3/3] Fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ricardo González Moreno --- include/fastcdr/Cdr.h | 45 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/include/fastcdr/Cdr.h b/include/fastcdr/Cdr.h index cd576077..85f7ec45 100644 --- a/include/fastcdr/Cdr.h +++ b/include/fastcdr/Cdr.h @@ -2750,6 +2750,51 @@ class Cdr return *this; } + /*! + * @brief Decodes an optional member of an external according to the encoding algorithm used. + * @param[out] member_value A reference of the variable where the optional member value will be stored. + * @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_member( + optional>& member_value) + { + if (member_value.has_value() && member_value.value().is_locked()) + { + throw exception::BadParamException("External member is locked"); + } + + if (EncodingAlgorithmFlag::PLAIN_CDR == current_encoding_) + { + Cdr::state current_state(*this); + MemberId member_id; + xcdr1_deserialize_member_header(member_id, current_state); + auto prev_offset = offset_; + member_value.reset(0 < current_state.member_size_); + if (0 < current_state.member_size_) + { + deserialize(member_value); + } + size_t member_size {current_state.member_size_}; + size_t diff {offset_ - prev_offset}; + if (member_size < diff) + { + throw exception::BadParamException( + "Member size provided by member header is lower than real decoded member size"); + } + + // Skip unused bytes + offset_ += (member_size - diff); + } + else + { + deserialize(member_value); + } + return *this; + } + /*! * @brief Tells to the encoder a new type and its members starts to be encoded. * @param[in,out] current_state State of the encoder previous of calling this function.