From 18c280c19c14b423e112f57109a6c0862271a2f5 Mon Sep 17 00:00:00 2001 From: Grigorii Sokolov Date: Fri, 14 Feb 2025 17:58:36 +0200 Subject: [PATCH] [MED] support for Conditional/Setter flds of med::set - Added support for the cases like this: struct MSG_SET_FUNC : med::set< M< T16<0x0b>, FLD_UC >, // M< T16<0x0d>, FLD_FLAGS, FLD_FLAGS::setter >, O< T16<0x0e>, FLD_QTY, FLD_FLAGS::has_bits > >{}; - This is quite an exotic case, since, strictly speaking, presence of conditional fields in a med::set is not required for successful decoding - Please note, that it is support for single-instance fields only. Also, there is yet no support for Counter fields in a med::set --- med/mandatory.hpp | 13 ++++ med/optional.hpp | 13 ++++ med/set.hpp | 66 +++++++++++++--- ut/set.cpp | 191 ++++++++++++++++++++++++++++++++++++++++++++++ ut/ut_proto.hpp | 19 ++++- 5 files changed, 290 insertions(+), 12 deletions(-) diff --git a/med/mandatory.hpp b/med/mandatory.hpp index 8030a7e..5033e88 100644 --- a/med/mandatory.hpp +++ b/med/mandatory.hpp @@ -246,6 +246,19 @@ struct mandatory< { }; +//M +template requires ASetter +struct mandatory< + TAG, + FIELD, + SETTER, + min<1>, + max<1> +> : field_t> +{ + using setter_type = SETTER; +}; + //M template struct mandatory< diff --git a/med/optional.hpp b/med/optional.hpp index 2257b87..6899fb1 100644 --- a/med/optional.hpp +++ b/med/optional.hpp @@ -274,6 +274,19 @@ struct optional< { }; +//single-instance field as a part of compound +template +struct optional< + TAG, + FIELD, + CONDITION, + min<1>, + max<1> +> : field_t>, optional_t +{ + using condition = CONDITION; +}; + //multi-instance field w/ tag template struct optional< diff --git a/med/set.hpp b/med/set.hpp index f915511..367cdcd 100644 --- a/med/set.hpp +++ b/med/set.hpp @@ -23,6 +23,32 @@ namespace med { namespace sl { +template +inline constexpr void encode_single(FUNC& func, IE const& ie) +{ + if (ie.is_set()) + { + using mi = meta::produce_info_t; + constexpr bool explicit_meta = explicit_meta_in>(); + + CODEC_TRACE("[%s]%s: %s", name(), class_name(), class_name()); + if constexpr (explicit_meta) + { + using ctx = type_context>; + sl::ie_encode(func, ie); + } + else + { + using ctx = type_context; + sl::ie_encode(func, ie); + } + } + else if constexpr (!AOptional) + { + MED_THROW_EXCEPTION(missing_ie, name(), 1, 0) + } +} + struct set_name { template @@ -51,12 +77,13 @@ struct set_enc template static constexpr void apply(TO const& to, ENCODER& encoder) { - using mi = meta::produce_info_t; IE const& ie = to; - constexpr bool explicit_meta = explicit_meta_in>(); if constexpr (AMultiField) { + using mi = meta::produce_info_t; + constexpr bool explicit_meta = explicit_meta_in>(); + CODEC_TRACE("[%s]*%zu: %s", name(), ie.count(), class_name()); check_arity(encoder, ie); @@ -79,23 +106,29 @@ struct set_enc } else //single-instance field { - if (ie.is_set()) + if constexpr (AHasSetterType) //with setter { - CODEC_TRACE("[%s]%s: %s", name(), class_name(), class_name()); - if constexpr (explicit_meta) + CODEC_TRACE("[%s] with setter from %s", name(), name()); + IE ie; + ie.copy(static_cast(to), encoder); + + typename IE::setter_type setter; + if constexpr (std::is_same_v) { - using ctx = type_context>; - sl::ie_encode(encoder, ie); + if (not setter(ie, to)) + { + MED_THROW_EXCEPTION(invalid_value, name(), ie.get()) + } } else { - using ctx = type_context; - sl::ie_encode(encoder, ie); + setter(ie, to); } + encode_single(encoder, ie); } - else if constexpr (!AOptional) + else { - MED_THROW_EXCEPTION(missing_ie, name(), 1, 0) + encode_single(encoder, ie); } } } @@ -166,6 +199,17 @@ struct set_check MED_THROW_EXCEPTION(missing_ie, name(), 1, 0) } } + + if constexpr (AHasCondition) // conditional - quite an exotic case, since a med::set usually does not require conditional fields + { + bool const should_be_set = typename IE::condition{}(to); + if (ie.is_set() != should_be_set) + { + CODEC_TRACE("%cC[%s] %s be set in %s", ie.is_set() ? '+' : '-', name(), should_be_set ? "MUST" : "must NOT", name()); + if (should_be_set) { MED_THROW_EXCEPTION(missing_ie, name(), 1, 0); } + else { MED_THROW_EXCEPTION(extra_ie, name(), 0, 1); } + } + } } template diff --git a/ut/set.cpp b/ut/set.cpp index b4a1673..46eec84 100644 --- a/ut/set.cpp +++ b/ut/set.cpp @@ -192,6 +192,102 @@ TEST(encode, mset_fail_arity) EXPECT_THROW(encode(med::octet_encoder{ctx}, proto), med::missing_ie); } +TEST(encode, set_func_ok) +{ + PROTO proto; + + MSG_SET_FUNC& msg = proto.ref(); + + //one mandatory, one conditional + setter, one optional + msg.ref().set(0x11); + msg.ref().set(3); + msg.ref().set(1); + + uint8_t buffer[1024]; + med::encoder_context ctx{buffer}; + + EXPECT_NO_THROW(encode(med::octet_encoder{ctx}, proto)); + + // M< T16<0x0b>, FLD_UC >, // + // M< T16<0x0d>, FLD_FLAGS, FLD_FLAGS::setter >, + // O< T16<0x21>, FLD_U16, FLD_FLAGS::has_bits >, + // O< T16<0x89>, FLD_IP > + uint8_t const encoded1[] = { 0x24 + , 0, 0x0b, 0x11 + , 0, 0x0d, 0b0000'0101 + , 0, 0x21, 0x00, 0x03 + , 0, 0x89, 0x00, 0x00, 0x00, 0x01 + }; + EXPECT_EQ(sizeof(encoded1), ctx.buffer().get_offset()); + EXPECT_TRUE(Matches(encoded1, buffer)); + + //one mandatory + setter + ctx.reset(); + msg.clear(); + msg.ref().set(0x11); + EXPECT_NO_THROW(encode(med::octet_encoder{ctx}, proto)); + + // M< T16<0x0b>, FLD_UC >, // + // M< T16<0x0d>, FLD_FLAGS, FLD_FLAGS::setter >, + uint8_t const encoded2[] = { 0x24 + , 0, 0x0b, 0x11 + , 0, 0x0d, 0b0000'0000 + }; + EXPECT_EQ(sizeof(encoded2), ctx.buffer().get_offset()); + EXPECT_TRUE(Matches(encoded2, buffer)); + + //full msg + ctx.reset(); + msg.clear(); + msg.ref().set(0x11); + msg.ref().set(3); + msg.ref().set(1); + msg.ref().set(2); + msg.ref().set(4); + msg.ref().set(5); + + // try to set wrong flags + msg.ref().set(255); + + EXPECT_NO_THROW(encode(med::octet_encoder{ctx}, proto)); + + // M< T16<0x0b>, FLD_UC >, // + // M< T16<0x0d>, FLD_FLAGS, FLD_FLAGS::setter >, + // O< T16<0x0e>, FLD_QTY, FLD_FLAGS::has_bits >, + // O< T16<0x0c>, FLD_U8 >, // + // O< T16<0x21>, FLD_U16, FLD_FLAGS::has_bits >, + // O< T16<0x49>, FLD_U24, FLD_FLAGS::has_bits >, + // O< T16<0x89>, FLD_IP > + uint8_t const encoded3[] = { 0x24 + , 0, 0x0b, 0x11 + , 0, 0x0d, 0b0100'0111 + , 0, 0x0e, 0x05 + , 0, 0x0c, 0x04 + , 0, 0x21, 0x00, 0x03 + , 0, 0x49, 0x00, 0x00, 0x02 + , 0, 0x89, 0x00, 0x00, 0x00, 0x01 + }; + + EXPECT_EQ(sizeof(encoded3), ctx.buffer().get_offset()); + EXPECT_TRUE(Matches(encoded3, buffer)); +} + +TEST(encode, set_func_fail) +{ + PROTO proto; + + MSG_SET_FUNC& msg = proto.ref(); + + //NO mandatory, one conditional + setter, one optional + msg.ref().set(3); + msg.ref().set(1); + + uint8_t buffer[1024]; + med::encoder_context ctx{buffer}; + + EXPECT_THROW(encode(med::octet_encoder{ctx}, proto), med::missing_ie); +} + TEST(decode, set_ok) { PROTO proto; @@ -413,3 +509,98 @@ TEST(decode, mset_fail_arity) ctx.reset(mandatory_overflow, sizeof(mandatory_overflow)); EXPECT_THROW(decode(med::octet_decoder{ctx}, proto), med::extra_ie); } + +TEST(decode, set_func_ok) +{ + PROTO proto; + + //mandatory fields only + uint8_t const encoded1[] = { 0x24 + , 0, 0x0b, 0x11 + , 0, 0x0d, 0x00, + }; + med::decoder_context ctx{encoded1}; + EXPECT_NO_THROW(decode(med::octet_decoder{ctx}, proto)); + { + MSG_SET_FUNC const* msg = proto.get(); + ASSERT_NE(nullptr, msg); + + ASSERT_EQ(0x11, msg->get().get()); + ASSERT_EQ(0x00, msg->get().get()); + FLD_U24 const* fld1 = msg->get(); + FLD_U16 const* fld2 = msg->get(); + FLD_U8 const* fld3 = msg->get(); + FLD_IP const* fld4 = msg->get(); + FLD_QTY const* fld5 = msg->get(); + ASSERT_EQ(nullptr, fld1); + ASSERT_EQ(nullptr, fld2); + ASSERT_EQ(nullptr, fld3); + ASSERT_EQ(nullptr, fld4); + ASSERT_EQ(nullptr, fld5); + } + //all fields but in reverse order + uint8_t const encoded2[] = { 0x24 + , 0, 0x89, 0x00, 0x00, 0x00, 0x01 + , 0, 0x49, 0x00, 0x00, 0x02 + , 0, 0x21, 0x00, 0x03 + , 0, 0x0c, 0x04 + , 0, 0x0e, 0x05 + , 0, 0x0b, 0x11 + , 0, 0x0d, 0b0100'0111, + }; + ctx.reset(encoded2); + EXPECT_NO_THROW(decode(med::octet_decoder{ctx}, proto)); + { + MSG_SET_FUNC const* msg = proto.get(); + ASSERT_NE(nullptr, msg); + + ASSERT_EQ(0x11, msg->get().get()); + FLD_U24 const* fld1 = msg->get(); + FLD_U16 const* fld2 = msg->get(); + FLD_U8 const* fld3 = msg->get(); + FLD_IP const* fld4 = msg->get(); + FLD_QTY const* fld5 = msg->get(); + ASSERT_NE(nullptr, fld1); + ASSERT_NE(nullptr, fld2); + ASSERT_NE(nullptr, fld3); + ASSERT_NE(nullptr, fld4); + ASSERT_NE(nullptr, fld5); + ASSERT_EQ(2, fld1->get()); + ASSERT_EQ(3, fld2->get()); + ASSERT_EQ(4, fld3->get()); + ASSERT_EQ(1, fld4->get()); + ASSERT_EQ(5, fld5->get()); + // ASSERT_EQ(0b0010'1111, msg->get().get()); + ASSERT_TRUE(FLD_FLAGS::has_bits{}(msg->body())); + ASSERT_TRUE(FLD_FLAGS::has_bits{}(msg->body())); + ASSERT_TRUE(FLD_FLAGS::has_bits{}(msg->body())); + constexpr size_t u8_qty = 1; + ASSERT_TRUE(FLD_FLAGS::has_bits{}(msg->body())); + auto const uc_qty_minus_one = 1 - 1; + ASSERT_FALSE(FLD_FLAGS::has_bits{}(msg->body())); + } +} + +TEST(decode, set_func_fail) +{ + PROTO proto; + + //mandatory fields only, but some flags are set + uint8_t const encoded1[] = { 0x24 + , 0, 0x0b, 0x11 + , 0, 0x0d, 0b0100'0111 + }; + med::decoder_context ctx{encoded1}; + EXPECT_THROW(decode(med::octet_decoder{ctx}, proto), med::missing_ie); + + //some conditional fields, but flags are NOT set + uint8_t const encoded2[] = { 0x24 + , 0, 0x49, 0x00, 0x00, 0x02 + , 0, 0x21, 0x00, 0x03 + , 0, 0x0c, 0x04 + , 0, 0x0b, 0x11 + , 0, 0x0d, 0b0100'0000 + }; + ctx.reset(encoded2); + EXPECT_THROW(decode(med::octet_decoder{ctx}, proto), med::extra_ie); +} diff --git a/ut/ut_proto.hpp b/ut/ut_proto.hpp index eceaa4f..c81efb9 100644 --- a/ut/ut_proto.hpp +++ b/ut/ut_proto.hpp @@ -229,6 +229,7 @@ struct FLD_QTY : med::value } } }; + static constexpr char const* name() { return "Fld-Qty"; } }; struct FLD_FLAGS : med::value @@ -293,6 +294,7 @@ struct FLD_FLAGS : med::value //ies.template as().set(bits); } }; + static constexpr char const* name() { return "Fld-Flags"; } }; //optional fields with functors @@ -309,13 +311,28 @@ struct MSG_FUNC : med::sequence< static constexpr char const* name() { return "Msg-With-Functors"; } }; +//optional fields with functors +struct MSG_SET_FUNC : med::set< + M< T16<0x0b>, FLD_UC >, // + M< T16<0x0d>, FLD_FLAGS, FLD_FLAGS::setter >, + O< T16<0x0e>, FLD_QTY, FLD_FLAGS::has_bits >, + O< T16<0x0c>, FLD_U8 >, // + O< T16<0x21>, FLD_U16, FLD_FLAGS::has_bits >, + O< T16<0x49>, FLD_U24, FLD_FLAGS::has_bits >, + O< T16<0x89>, FLD_IP > +> +{ + static constexpr char const* name() { return "Msg-Set-With-Functors"; } + decltype(auto) body() const { return this->m_ies; } +}; struct PROTO : med::choice< M, MSG_SEQ>, M, MSG_MSEQ>, M, MSG_SET>, M, MSG_MSET>, - M, MSG_FUNC> + M, MSG_FUNC>, + M, MSG_SET_FUNC> > { };