From e9bcbf7a3c081d887c75aca28cc5c2254263c3bc Mon Sep 17 00:00:00 2001 From: cppden Date: Sat, 24 Aug 2024 13:21:32 +0300 Subject: [PATCH] fix BCD + simplified buffer and contexts --- med/buffer.hpp | 53 +++++++++--------- med/debug.hpp | 2 +- med/decoder_context.hpp | 38 +++++-------- med/encoder_context.hpp | 35 +++++------- med/length.hpp | 3 +- med/value.hpp | 16 +++--- ut/choice.cpp | 115 +++++++++++++--------------------------- ut/ut.hpp | 4 +- 8 files changed, 97 insertions(+), 169 deletions(-) diff --git a/med/buffer.hpp b/med/buffer.hpp index b411841..4e9518f 100644 --- a/med/buffer.hpp +++ b/med/buffer.hpp @@ -21,15 +21,16 @@ Distributed under the MIT License namespace med { -template +template requires (sizeof(T) == 1 && std::is_integral_v) class buffer { using eob_index_t = uint8_t; static constexpr eob_index_t MAX_EOB_INDEX = std::numeric_limits::max(); public: - using pointer = PTR; - using value_type = std::remove_const_t>; + using pointer = T*; + using value_type = std::remove_const_t; + static constexpr auto is_const_v = std::is_const_v; constexpr buffer() noexcept = default; @@ -129,24 +130,20 @@ class buffer } } - constexpr void reset() { m_state.reset(get_start()); } - - constexpr void reset(std::span chunk) - { - m_start = chunk.data(); - m_state.reset(m_start); - end(m_start + chunk.size()); - } - - constexpr void reset(pointer p, std::size_t s) + constexpr void reset() noexcept { m_state.reset(get_start()); } + constexpr void reset(void const* p, std::size_t s) noexcept { - m_start = p; + m_start = static_cast(const_cast(p)); m_state.reset(m_start); end(m_start + s); } + template requires (std::is_integral_v) + constexpr void reset(std::span p) noexcept { reset(p.data(), p.size_bytes()); } + template + constexpr void reset(U (&p)[SZ]) noexcept { reset(p, sizeof(p)); } - constexpr state_type get_state() const { return m_state; } - constexpr void set_state(state_type const& st) { m_state = st; } + constexpr state_type get_state() const noexcept { return m_state; } + constexpr void set_state(state_type const& st) noexcept { m_state = st; } constexpr bool push_state() { @@ -172,20 +169,20 @@ class buffer } } - constexpr pointer get_start() const { return m_start; } - constexpr std::size_t get_offset() const { return begin() - get_start(); } - constexpr std::span used() const { return {get_start(), get_offset()}; } - constexpr std::size_t size() const { return end() - begin(); } - constexpr bool empty() const { return begin() >= end(); } - explicit constexpr operator bool() const { return !empty(); } + constexpr pointer get_start() const noexcept { return m_start; } + constexpr size_t get_offset() const noexcept { return begin() - get_start(); } + constexpr std::span used() const noexcept { return {get_start(), get_offset()}; } + constexpr size_t size() const noexcept { return end() - begin(); } + constexpr bool empty() const noexcept { return begin() >= end(); } + explicit constexpr operator bool() const noexcept { return !empty(); } - template constexpr void push(value_type v) + template constexpr void push(value_type v) requires (!is_const_v) { if (not empty()) { *m_state.cursor++ = v; } else { MED_THROW_EXCEPTION(overflow, name(), sizeof(value_type), *this) } } - template constexpr value_type pop() + template constexpr value_type pop() requires (is_const_v) { if (not empty()) { return *m_state.cursor++; } else { MED_THROW_EXCEPTION(overflow, name(), sizeof(value_type), *this) } @@ -229,9 +226,9 @@ class buffer } /// similar to advance but no bounds check - constexpr void offset(int delta) { m_state.cursor += delta; } + constexpr void offset(int delta) noexcept { m_state.cursor += delta; } - template constexpr void fill(std::size_t count, uint8_t value) + template constexpr void fill(std::size_t count, uint8_t value) requires (!is_const_v) { //CODEC_TRACE("padding %zu bytes=%u", count, value); if (size() >= count) @@ -244,8 +241,8 @@ class buffer } } - constexpr pointer begin() const { return m_state.cursor; } - constexpr pointer end() const { return m_end; } + constexpr pointer begin() const noexcept { return m_state.cursor; } + constexpr pointer end() const noexcept { return m_end; } /** * Adjusts the end of buffer pointer diff --git a/med/debug.hpp b/med/debug.hpp index 579c199..92fa8da 100644 --- a/med/debug.hpp +++ b/med/debug.hpp @@ -11,7 +11,7 @@ Distributed under the MIT License #include #include -#define CODEC_TRACE_ENABLE +//#define CODEC_TRACE_ENABLE #define CODEC_TRACE(FMT, ...) CODEC_TRACE_FL(__FILE__, __LINE__, FMT, __VA_ARGS__) diff --git a/med/decoder_context.hpp b/med/decoder_context.hpp index e174780..24de1de 100644 --- a/med/decoder_context.hpp +++ b/med/decoder_context.hpp @@ -17,7 +17,7 @@ namespace med { template < class ALLOCATOR = const null_allocator, - class BUFFER = buffer + class BUFFER = buffer > class decoder_context : public detail::allocator_holder { @@ -26,34 +26,20 @@ class decoder_context : public detail::allocator_holder using buffer_type = BUFFER; using state_t = typename buffer_type::state_type; - decoder_context(void const* data, std::size_t size, allocator_type* alloc = nullptr) - : detail::allocator_holder{alloc} - { - reset(data, size); - } - - decoder_context() - : decoder_context(nullptr, 0) - { - } - - template - decoder_context(T const (&data)[SIZE], allocator_type* alloc = nullptr) - : decoder_context(data, sizeof(data), alloc) - { - } - + decoder_context() noexcept : decoder_context(nullptr, 0){} + decoder_context(void const* p, size_t s, allocator_type* a = nullptr) noexcept + : detail::allocator_holder{a} { reset(p, s); } + template + decoder_context(T const (&p)[SIZE], allocator_type* a = nullptr) noexcept + : decoder_context(p, sizeof(p), a) {} + template + decoder_context(std::span p, allocator_type* a = nullptr) noexcept + : decoder_context(p.data(), p.size_bytes(), a) {} buffer_type& buffer() noexcept { return m_buffer; } - void reset(void const* data, std::size_t size) - { - buffer().reset(static_cast(data), size); - } - template - void reset(T const (&data)[SIZE]) { reset(data, sizeof(data)/sizeof(T)); } - - void reset() { buffer().reset(); } + template + constexpr void reset(Ts&&... args)noexcept{ buffer().reset(std::forward(args)...); } private: decoder_context(decoder_context const&) = delete; diff --git a/med/encoder_context.hpp b/med/encoder_context.hpp index c05fdf0..ec38059 100644 --- a/med/encoder_context.hpp +++ b/med/encoder_context.hpp @@ -19,7 +19,7 @@ namespace med { template < class ALLOCATOR = const null_allocator, - class BUFFER = buffer + class BUFFER = buffer > class encoder_context : public detail::allocator_holder { @@ -40,31 +40,20 @@ class encoder_context : public detail::allocator_holder encoder_context(encoder_context const&) = delete; encoder_context& operator=(encoder_context const&) = delete; - constexpr encoder_context(void* data, std::size_t size, allocator_type* alloc = nullptr) - : detail::allocator_holder{alloc} - { - reset(data, size); - } + constexpr encoder_context(void* p, size_t s, allocator_type* a = nullptr) noexcept + : detail::allocator_holder{a} { reset(p, s); } - template - explicit constexpr encoder_context(T (&buf)[SIZE], allocator_type* alloc = nullptr) - : encoder_context(buf, sizeof(buf), alloc) {} + template + explicit constexpr encoder_context(T (&p)[SIZE], allocator_type* a = nullptr) noexcept + : encoder_context(p, sizeof(p), a) {} constexpr buffer_type& buffer() noexcept { return m_buffer; } constexpr buffer_type const& buffer()const noexcept { return m_buffer; } - template - constexpr void reset(T (&data)[SIZE]) noexcept { reset(data, SIZE * sizeof(T)); } - - constexpr void reset(void* data, std::size_t size) noexcept - { - m_buffer.reset({static_cast(data), size}); - m_snapshot = nullptr; - } - - constexpr void reset() + template + constexpr void reset(Ts... args) noexcept { - m_buffer.reset(); + buffer().reset(args...); m_snapshot = nullptr; } @@ -86,7 +75,7 @@ class encoder_context : public detail::allocator_holder class snap_s : public state_t { public: - constexpr bool validate_length(std::size_t s) const + constexpr bool validate_length(size_t s) const { CODEC_TRACE("%s(%zu ? %zu)=%d", __FUNCTION__, m_length, s, m_length == s); return m_length == s; @@ -94,10 +83,10 @@ class encoder_context : public detail::allocator_holder private: friend class encoder_context; - constexpr snap_s(state_t const& st, std::size_t len) : state_t{st}, m_length{len} {} + constexpr snap_s(state_t const& st, size_t len) : state_t{st}, m_length{len} {} constexpr snap_s() = default; - std::size_t m_length{}; + size_t m_length{}; }; /** diff --git a/med/length.hpp b/med/length.hpp index 34ceff0..aff7ea4 100644 --- a/med/length.hpp +++ b/med/length.hpp @@ -94,11 +94,10 @@ constexpr std::size_t ie_length(IE const& ie, ENCODER& encoder) noexcept else //data itself { CODEC_TRACE("%s[%s]<%s:%s> - DATA", __FUNCTION__, name(), name(), name()); - using ie_type = typename IE::ie_type; if constexpr (AContainer) { using ctx = type_context, EXP_TAG, EXP_LEN>; - CODEC_TRACE("%s[%.30s]%s<%s:%s>: %s", __FUNCTION__, name(), AMultiField?"*":"", name(), name(), name()); + CODEC_TRACE("%s[%.30s]%s<%s:%s>: %s", __FUNCTION__, name(), AMultiField?"*":"", name(), name(), name()); len += ie.template calc_length(encoder); CODEC_TRACE("%s[%s] : len(SEQ) = %zu", __FUNCTION__, name(), len); } diff --git a/med/value.hpp b/med/value.hpp index 42394cb..1e6df93 100644 --- a/med/value.hpp +++ b/med/value.hpp @@ -48,18 +48,18 @@ struct numeric_value : IE using value_type = typename traits::value_type; using base_t = numeric_value; - value_type get() const { return get_encoded(); } - auto set(value_type v) { return set_encoded(v); } + value_type get() const noexcept { return get_encoded(); } + auto set(value_type v) noexcept { return set_encoded(v); } //NOTE: do not override! static constexpr bool is_defined = false; - value_type get_encoded() const { return m_value; } - void set_encoded(value_type v) { m_value = v; m_set = true; } - void clear() { m_set = false; } - bool is_set() const { return m_set; } - explicit operator bool() const { return is_set(); } + value_type get_encoded() const noexcept { return m_value; } + void set_encoded(value_type v) noexcept { m_value = v; m_set = true; } + void clear() noexcept { m_set = false; } + bool is_set() const noexcept { return m_set; } + explicit operator bool() const noexcept { return is_set(); } template - void copy(base_t const& from, ARGS&&...) { m_value = from.m_value; m_set = from.m_set; } + void copy(base_t const& from, ARGS&&...)noexcept{ m_value = from.m_value; m_set = from.m_set; } #if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ < 9) #pragma GCC diagnostic push diff --git a/ut/choice.cpp b/ut/choice.cpp index 828149f..9784b49 100644 --- a/ut/choice.cpp +++ b/ut/choice.cpp @@ -5,26 +5,38 @@ namespace cho { //low nibble selector template -struct LT : med::value>> {}; +struct LT : med::value>> {}; struct HI : med::value> {}; struct LO : med::value> {}; -struct HiLo : med::sequence< +struct HL : med::sequence< M< HI >, M< LO > > {}; +/* Binary Coded Decimal, i.e. each nibble is 0..9 +xxxx.xxxx +TAG 1 +xxxx.xxxx|xxxx.xxxx +TAG 1 2 F +xxxx.xxxx|xxxx.xxxx +TAG 1 2 3 +xxxx.xxxx|xxxx.xxxx|xxxx.xxxx +TAG 1 2 3 4 F +xxxx.xxxx|xxxx.xxxx|xxxx.xxxx +TAG 1 2 3 4 5 +*/ template struct BCD : med::sequence< - M< LT >, // explicit TAG M< LO >, - O< HiLo, med::max<2> > + O< HL, med::max<2> > >, med::add_meta_info< med::add_tag> > { static_assert(0 == (TAG & 0xF0), "LOW NIBBLE TAG EXPECTED"); + static constexpr uint8_t END = 0xF; - bool set(std::span data) // nibble per byte + bool set(std::span data) { if (1 <= data.size() && data.size() <= 5) { @@ -32,81 +44,26 @@ struct BCD : med::sequence< this->template ref().set(*it++); while (it != ite) { - auto* p = this->template ref().push_back(); + auto* p = this->template ref().push_back(); p->template ref().set(*it++); - if (it != ite) - { - p->template ref().set(*it++); - } - else - { - p->template ref().set(0xF); - } + p->template ref().set(it != ite ? *it++ : END); } return true; } return false; } -}; - -#if 0 -//NOTE: low nibble of 1st octet is a tag -//binary coded decimal: 0x21,0x43 is 1,2,3,4 -template -struct BCD : med::octet_string, med::min<1>> - , med::add_meta_info< med::add_tag< LT > > -{ - static_assert(0 == (TAG & 0xF0), "LOW NIBBLE TAG EXPECTED"); - static constexpr bool match(uint8_t v) { return TAG == (v & 0xF); } - - bool set(std::size_t len, void const* data) - { - //need additional nibble for the tag - std::size_t const num_octets = (len + 1); - if (num_octets >= traits::min_octets && num_octets <= traits::max_octets) - { - m_value.resize(num_octets); - uint8_t* p = m_value.data(); - uint8_t const* in = static_cast(data); - - *p++ = (*in & 0xF0) | TAG; - uint8_t o = (*in++ << 4); - for (; len > 1; --len) - { - *p++ = o | (*in >> 4); - o = *in++ << 4; - } - *p++ = o | 0xF; - return true; - } - return false; - } - template - void print(char (&sz)[N]) const + size_t size() const noexcept { - char* psz = sz; - - auto to_char = [&psz](uint8_t nibble) - { - if (nibble != 0xF) - { - *psz++ = static_cast(nibble > 9 ? (nibble+0x57) : (nibble+0x30)); - } - }; - - bool b1st = true; - for (uint8_t digit : *this) + size_t res = this->template get().is_set() ? 1 : 0; + if (auto* p = this->template get()) { - to_char(uint8_t(digit >> 4)); - //not 1st octet - print both nibbles - if (not b1st) to_char(digit & 0xF); - b1st = false; + res += 2 * p->count(); + res -= ((*p->last() & END) == END); } - *psz = 0; + return res; } }; -#endif struct BCD_1 : BCD<1> { @@ -172,7 +129,7 @@ using PLAIN = M; using namespace std::string_view_literals; using namespace cho; -#if 0 +#if 1 TEST(choice, plain) { uint8_t buffer[32]; @@ -230,31 +187,31 @@ TEST(choice, any) } #endif #if 1 -TEST(choice, explicit_tag) +TEST(choice, nibble_tag) { uint8_t buffer[32]; med::encoder_context<> ctx{ buffer }; med::octet_encoder encoder{ctx}; FLD_NSCHO msg; - uint8_t const bcd[] = {0x34, 0x56}; - msg.ref().set(bcd); + uint8_t const bcd[] = {3, 4, 5, 6}; + auto& c = msg.ref(); + //c.ref>().set(3); + //EXPECT_EQ(1, c.ref>().get()); + c.set(bcd); encode(encoder, msg); - EXPECT_STRCASEEQ("31 45 6F ", as_string(ctx.buffer())); - -#if 0 + EXPECT_STRCASEEQ("13 45 6F ", as_string(ctx.buffer())); decltype(msg) dmsg; - med::decoder_context<> dctx; - dctx.reset(ctx.buffer().used()); + med::decoder_context<> dctx{ctx.buffer().used()}; decode(med::octet_decoder{dctx}, dmsg); auto* pf = dmsg.get(); ASSERT_NE(nullptr, pf); - EXPECT_EQ(msg.get()->size(), dmsg.get()->size()); + //EXPECT_EQ(msg.get()->size(), pf->size()); + EXPECT_TRUE(msg == dmsg); EXPECT_TRUE(pf->is_set()); dmsg.clear(); EXPECT_FALSE(pf->is_set()); -#endif } #endif //NOTE: choice compound is tested in length.cpp ppp::proto diff --git a/ut/ut.hpp b/ut/ut.hpp index dfae440..5e39bf1 100644 --- a/ut/ut.hpp +++ b/ut/ut.hpp @@ -369,7 +369,7 @@ void check_decode(MSG const& msg, RANGE const& binary) template void check_octet_encode(MSG const& msg, std::initializer_list binary , bool incomplete = false - , std::source_location const& loc = std::source_location::current()) + , [[maybe_unused]] std::source_location const& loc = std::source_location::current()) { uint8_t buffer[1024] = {}; ASSERT_LE(binary.size(), sizeof(buffer)); @@ -383,7 +383,7 @@ void check_octet_encode(MSG const& msg, std::initializer_list binary template void check_octet_decode(MSG const& msg, std::initializer_list binary - , std::source_location const& loc = std::source_location::current()) + , [[maybe_unused]] std::source_location const& loc = std::source_location::current()) { MSG decoded_msg; med::decoder_context ctx{(void const*)std::data(binary), binary.size()};