Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions med/mandatory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,19 @@ struct mandatory<
{
};

//M<TAG, FIELD, SETTER>
template <ATag TAG, AField FIELD, class SETTER> requires ASetter<FIELD, SETTER>
struct mandatory<
TAG,
FIELD,
SETTER,
min<1>,
max<1>
> : field_t<FIELD, add_tag<TAG>>
{
using setter_type = SETTER;
};

//M<TAG, FIELD, arity<NUM>
template <ATag TAG, AField FIELD, std::size_t NUM>
struct mandatory<
Expand Down
13 changes: 13 additions & 0 deletions med/optional.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,19 @@ struct optional<
{
};

//single-instance field as a part of compound
template <ATag TAG, AField FIELD, ACondition CONDITION>
struct optional<
TAG,
FIELD,
CONDITION,
min<1>,
max<1>
> : field_t<FIELD, add_tag<TAG>>, optional_t
{
using condition = CONDITION;
};

//multi-instance field w/ tag
template <ATag TAG, AField FIELD, std::size_t MAX>
struct optional<
Expand Down
66 changes: 55 additions & 11 deletions med/set.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,32 @@ namespace med {

namespace sl {

template <class FUNC, class IE>
inline constexpr void encode_single(FUNC& func, IE const& ie)
{
if (ie.is_set())
{
using mi = meta::produce_info_t<FUNC, IE>;
constexpr bool explicit_meta = explicit_meta_in<mi, get_field_type_t<IE>>();

CODEC_TRACE("[%s]%s: %s", name<IE>(), class_name<IE>(), class_name<mi>());
if constexpr (explicit_meta)
{
using ctx = type_context<IE_SET, meta::list_rest_t<mi>>;
sl::ie_encode<ctx>(func, ie);
}
else
{
using ctx = type_context<IE_SET, mi>;
sl::ie_encode<ctx>(func, ie);
}
}
else if constexpr (!AOptional<IE>)
{
MED_THROW_EXCEPTION(missing_ie, name<IE>(), 1, 0)
}
}

struct set_name
{
template <class IE, typename TAG, class CODEC>
Expand Down Expand Up @@ -51,12 +77,13 @@ struct set_enc
template <class CTX, class PREV_IE, class IE, class TO, class ENCODER>
static constexpr void apply(TO const& to, ENCODER& encoder)
{
using mi = meta::produce_info_t<ENCODER, IE>;
IE const& ie = to;
constexpr bool explicit_meta = explicit_meta_in<mi, get_field_type_t<IE>>();

if constexpr (AMultiField<IE>)
{
using mi = meta::produce_info_t<ENCODER, IE>;
constexpr bool explicit_meta = explicit_meta_in<mi, get_field_type_t<IE>>();

CODEC_TRACE("[%s]*%zu: %s", name<IE>(), ie.count(), class_name<mi>());
check_arity(encoder, ie);

Expand All @@ -79,23 +106,29 @@ struct set_enc
}
else //single-instance field
{
if (ie.is_set())
if constexpr (AHasSetterType<IE>) //with setter
{
CODEC_TRACE("[%s]%s: %s", name<IE>(), class_name<IE>(), class_name<mi>());
if constexpr (explicit_meta)
CODEC_TRACE("[%s] with setter from %s", name<IE>(), name<TO>());
IE ie;
ie.copy(static_cast<IE const&>(to), encoder);

typename IE::setter_type setter;
if constexpr (std::is_same_v<bool, decltype(setter(ie, to))>)
{
using ctx = type_context<IE_SET, meta::list_rest_t<mi>>;
sl::ie_encode<ctx>(encoder, ie);
if (not setter(ie, to))
{
MED_THROW_EXCEPTION(invalid_value, name<IE>(), ie.get())
}
}
else
{
using ctx = type_context<IE_SET, mi>;
sl::ie_encode<ctx>(encoder, ie);
setter(ie, to);
}
encode_single(encoder, ie);
}
else if constexpr (!AOptional<IE>)
else
{
MED_THROW_EXCEPTION(missing_ie, name<IE>(), 1, 0)
encode_single(encoder, ie);
}
}
}
Expand Down Expand Up @@ -166,6 +199,17 @@ struct set_check
MED_THROW_EXCEPTION(missing_ie, name<IE>(), 1, 0)
}
}

if constexpr (AHasCondition<IE>) // 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<IE>(), should_be_set ? "MUST" : "must NOT", name<TO>());
if (should_be_set) { MED_THROW_EXCEPTION(missing_ie, name<IE>(), 1, 0); }
else { MED_THROW_EXCEPTION(extra_ie, name<IE>(), 0, 1); }
}
}
}

template <class TO, class DECODER>
Expand Down
191 changes: 191 additions & 0 deletions ut/set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<MSG_SET_FUNC>();

//one mandatory, one conditional + setter, one optional
msg.ref<FLD_UC>().set(0x11);
msg.ref<FLD_U16>().set(3);
msg.ref<FLD_IP>().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 >, //<TV>
// M< T16<0x0d>, FLD_FLAGS, FLD_FLAGS::setter >,
// O< T16<0x21>, FLD_U16, FLD_FLAGS::has_bits<FLD_FLAGS::U16> >,
// 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<FLD_UC>().set(0x11);
EXPECT_NO_THROW(encode(med::octet_encoder{ctx}, proto));

// M< T16<0x0b>, FLD_UC >, //<TV>
// 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<FLD_UC>().set(0x11);
msg.ref<FLD_U16>().set(3);
msg.ref<FLD_IP>().set(1);
msg.ref<FLD_U24>().set(2);
msg.ref<FLD_U8>().set(4);
msg.ref<FLD_QTY>().set(5);

// try to set wrong flags
msg.ref<FLD_FLAGS>().set(255);

EXPECT_NO_THROW(encode(med::octet_encoder{ctx}, proto));

// M< T16<0x0b>, FLD_UC >, //<TV>
// M< T16<0x0d>, FLD_FLAGS, FLD_FLAGS::setter >,
// O< T16<0x0e>, FLD_QTY, FLD_FLAGS::has_bits<FLD_FLAGS::QTY> >,
// O< T16<0x0c>, FLD_U8 >, //<TV>
// O< T16<0x21>, FLD_U16, FLD_FLAGS::has_bits<FLD_FLAGS::U16> >,
// O< T16<0x49>, FLD_U24, FLD_FLAGS::has_bits<FLD_FLAGS::U24> >,
// 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<MSG_SET_FUNC>();

//NO mandatory, one conditional + setter, one optional
msg.ref<FLD_U16>().set(3);
msg.ref<FLD_IP>().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;
Expand Down Expand Up @@ -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<MSG_SET_FUNC>();
ASSERT_NE(nullptr, msg);

ASSERT_EQ(0x11, msg->get<FLD_UC>().get());
ASSERT_EQ(0x00, msg->get<FLD_FLAGS>().get());
FLD_U24 const* fld1 = msg->get<FLD_U24>();
FLD_U16 const* fld2 = msg->get<FLD_U16>();
FLD_U8 const* fld3 = msg->get<FLD_U8>();
FLD_IP const* fld4 = msg->get<FLD_IP>();
FLD_QTY const* fld5 = msg->get<FLD_QTY>();
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<MSG_SET_FUNC>();
ASSERT_NE(nullptr, msg);

ASSERT_EQ(0x11, msg->get<FLD_UC>().get());
FLD_U24 const* fld1 = msg->get<FLD_U24>();
FLD_U16 const* fld2 = msg->get<FLD_U16>();
FLD_U8 const* fld3 = msg->get<FLD_U8>();
FLD_IP const* fld4 = msg->get<FLD_IP>();
FLD_QTY const* fld5 = msg->get<FLD_QTY>();
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<FLD_FLAGS>().get());
ASSERT_TRUE(FLD_FLAGS::has_bits<FLD_FLAGS::U16>{}(msg->body()));
ASSERT_TRUE(FLD_FLAGS::has_bits<FLD_FLAGS::U24>{}(msg->body()));
ASSERT_TRUE(FLD_FLAGS::has_bits<FLD_FLAGS::QTY>{}(msg->body()));
constexpr size_t u8_qty = 1;
ASSERT_TRUE(FLD_FLAGS::has_bits<u8_qty << FLD_FLAGS::U8_QTY>{}(msg->body()));
auto const uc_qty_minus_one = 1 - 1;
ASSERT_FALSE(FLD_FLAGS::has_bits<uc_qty_minus_one << FLD_FLAGS::UC_QTY>{}(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);
}
19 changes: 18 additions & 1 deletion ut/ut_proto.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ struct FLD_QTY : med::value<uint8_t>
}
}
};
static constexpr char const* name() { return "Fld-Qty"; }
};

struct FLD_FLAGS : med::value<uint8_t>
Expand Down Expand Up @@ -293,6 +294,7 @@ struct FLD_FLAGS : med::value<uint8_t>
//ies.template as<FLD_FLAGS>().set(bits);
}
};
static constexpr char const* name() { return "Fld-Flags"; }
};

//optional fields with functors
Expand All @@ -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 >, //<TV>
M< T16<0x0d>, FLD_FLAGS, FLD_FLAGS::setter >,
O< T16<0x0e>, FLD_QTY, FLD_FLAGS::has_bits<FLD_FLAGS::QTY> >,
O< T16<0x0c>, FLD_U8 >, //<TV>
O< T16<0x21>, FLD_U16, FLD_FLAGS::has_bits<FLD_FLAGS::U16> >,
O< T16<0x49>, FLD_U24, FLD_FLAGS::has_bits<FLD_FLAGS::U24> >,
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<C<0x01>, MSG_SEQ>,
M<C<0x11>, MSG_MSEQ>,
M<C<0x04>, MSG_SET>,
M<C<0x14>, MSG_MSET>,
M<C<0xFF>, MSG_FUNC>
M<C<0xFF>, MSG_FUNC>,
M<C<0x24>, MSG_SET_FUNC>
>
{
};