From 2f662b0d796ede920b44da7f078f26f632d091b5 Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Tue, 4 Jun 2024 18:18:05 +0300 Subject: [PATCH] .... --- net/adc_netmsg.h | 403 ++++++++++++++++++++++++++------------ tests/adc_netmsg_test.cpp | 58 +++++- 2 files changed, 329 insertions(+), 132 deletions(-) diff --git a/net/adc_netmsg.h b/net/adc_netmsg.h index 64a3ec9..8acd495 100644 --- a/net/adc_netmsg.h +++ b/net/adc_netmsg.h @@ -13,14 +13,62 @@ namespace adc { -template -class AdcNetMessageCommonInterface -{ - virtual bool empty() const { return std::ranges::distance(_bytes.begin(), _bytes.end()) == 0; } +namespace utils { +template +void convertToBytes(ByteStorageT &res, const T &v, const Ts &...vs) +{ + if constexpr (std::is_array_v>) { // to handle with trailing '\0' in plain char array + convertToBytes(res, std::string_view(v), vs...); + } else { + if constexpr (traits::adc_input_char_range) { + std::ranges::copy(v, std::back_inserter(res)); + } else if constexpr (std::convertible_to) { + convertToBytes(res, static_cast(v), vs...); + } else if constexpr (traits::formattable) { + std::format_to(std::back_inserter(res), "{}", v); + } else { + static_assert(false, "UNSUPPORTED TYPE!!!"); + } + } + + if constexpr (sizeof...(Ts)) { + convertToBytes(res, vs...); + } +} + + +} // namespace utils + + +namespace traits { + +template +concept adc_netmessage_c = requires(const T t) { // const methods + { t.empty() } -> std::convertible_to; + { t.bytes() } -> adc_output_char_range; + { t.byteView() } -> adc_range_of_view_char_range; +}; + +} // namespace traits + + +/* + Trivial message interface: + byte storage: just a char-range +*/ +template +class AdcNetMessageTrivialInterface +{ +public: + + virtual ~AdcNetMessageTrivialInterface() = default; + + bool empty() const { return std::ranges::distance(_bytes.begin(), _bytes.end()) == 0; } + // get a copy of message bytes - template + template R bytes() const { R r; @@ -30,10 +78,14 @@ class AdcNetMessageCommonInterface } - virtual ByteStorageT bytes() const { return bytes(); } + virtual ByteStorageT bytes() const + { + // + return bytes(); + } // get a view of message bytes - template + template R bytesView() const { R r; @@ -43,63 +95,128 @@ class AdcNetMessageCommonInterface return r; } - virtual std::vector bytesView() const { return bytesView>(); } + std::vector bytesView() const + { + // + return bytesView>(); + } protected: ByteStorageT _bytes; + AdcNetMessageTrivialInterface() = default; +}; - AdcNetMessageCommonInterface() = default; - virtual ~AdcNetMessageCommonInterface() = default; +/* + interface for more complex messages: + byte storage: std::vector of char-range (sequence of buffers) +*/ +template +class AdcNetMessageSeqInterface +{ +public: + virtual ~AdcNetMessageSeqInterface() = default; - AdcNetMessageCommonInterface(const AdcNetMessageCommonInterface&) = default; - AdcNetMessageCommonInterface(AdcNetMessageCommonInterface&&) = default; - - AdcNetMessageCommonInterface& operator=(const AdcNetMessageCommonInterface&) = default; - AdcNetMessageCommonInterface& operator=(AdcNetMessageCommonInterface&&) = default; - - - template - void convertToBytes(ByteViewT& res, const T& v, const Ts&... vs) + bool empty() const { - if constexpr (std::is_array_v>) { // to handle with trailing '\0' in plain char array - convertToBytes(std::string_view(v), vs...); - } else { - if constexpr (traits::adc_input_char_range) { - std::ranges::copy(v, std::back_inserter(res)); - } else if constexpr (std::convertible_to) { - convertToBytes(static_cast(v), vs...); - } else if constexpr (traits::formattable) { - std::format_to(std::back_inserter(res), "{}", v); - } else { - static_assert(false, "UNSUPPORTED TYPE!!!"); + if (_bytes.empty()) { + return true; + } + + bool emp = true; + for (const auto &el : _bytes) { + if (std::ranges::distance(el.begin(), el.end())) { + emp = false; + break; } } - if constexpr (sizeof...(Ts)) { - convertToBytes(vs...); - } + return emp; } + + // get a copy of message bytes + template + R bytes() const + { + R r; + + for (size_t i = 0; i < _bytes.size(); ++i) { + std::ranges::for_each(storageViewByIndex(i), + [&r](const auto &el) { std::ranges::copy(el, std::back_inserter(r)); }); + } + + return r; + } + + + virtual ByteStorageT bytes() const + { + // + return bytes(); + } + + // get a view of message bytes + template + R bytesView() const + { + R r; + + + for (size_t i = 0; i < _bytes.size(); ++i) { + std::ranges::for_each(storageViewByIndex(i), [&r](const auto &el) { r.emplace_back(el.begin(), el.end()); }); + } + + return r; + } + + std::vector bytesView() const + { + // + return bytesView>(); + } + +protected: + std::vector _bytes; + + + AdcNetMessageSeqInterface() = default; + + // return a sequence of storage element view and possible additional + // byte views (e.g. elements delimiter) + virtual std::vector storageViewByIndex(size_t) const = 0; }; + + + + // Generic message class template -class AdcGenericNetMessage : public AdcNetMessageCommonInterface +class AdcGenericNetMessage : public AdcNetMessageTrivialInterface { - using base_t = AdcNetMessageCommonInterface; + using base_t = AdcNetMessageTrivialInterface; public: using base_t::base_t; using base_t::bytes; - using base_t::byteView; + using base_t::bytesView; + using base_t::empty; + + + template + AdcGenericNetMessage(const T &v, const Ts &...vs) : AdcGenericNetMessage() + { + appendBytes(v, vs...); + } + template void appendBytes(const T& v, const Ts&... vs) { - this->convertToBytes(this->_bytes, v, vs...); + utils::convertToBytes(this->_bytes, v, vs...); } template @@ -141,13 +258,17 @@ static constexpr char ADC_DEFAULT_KEY_TOKEN_DELIMITER[] = " "; template -class AdcTokenNetMessage : public AdcNetMessageCommonInterface +class AdcTokenNetMessage : public AdcNetMessageSeqInterface { - using base_t = AdcNetMessageCommonInterface; + // AdcNetMessageSeqInterface::_bytes - tokens + using base_t = AdcNetMessageSeqInterface; public: static constexpr std::string_view tokenDelimiter{TOKEN_DELIM}; + using base_t::bytesView; + using base_t::bytes; + using base_t::empty; AdcTokenNetMessage() = default; @@ -160,61 +281,24 @@ public: virtual ~AdcTokenNetMessage() = default; - virtual bool empty() const - { - // - return _tokens.empty(); - } - - template - R bytes() const - { - R r; - - if (_tokens.empty()) { - return r; - } - - - utils::AdcJoinRange(_tokens, tokenDelimiter, r); - - return r; - } - - - template - R bytesView() const - { - R r; - - if (_tokens.empty()) { - return r; - } - - utils::AdcReturnRangeElementsView(_tokens, tokenDelimiter, r); - - return r; - } - - template R> - R tokens() const + R tokens(size_t start = 0, size_t N = std::numeric_limits::max()) const { R r; - if (_tokens.empty()) { + if (empty()) { return r; } - std::ranges::copy(_tokens, std::back_inserter(r)); + std::ranges::copy(this->_bytes | std::views::drop(start) | std::views::take(N), std::back_inserter(r)); return r; } - std::vector tokens() const + std::vector tokens(size_t start = 0, size_t N = std::numeric_limits::max()) const { // - return tokens>(); + return tokens>(start, N); } @@ -222,7 +306,7 @@ public: void appendTokens(const T& v, const Ts&... vs) { if constexpr (traits::adc_input_char_range || traits::formattable) { - this->convertToBytes(_tokens.emplace_back(), v); + utils::convertToBytes(this->_bytes.emplace_back(), v); } else if constexpr (std::ranges::input_range) { using el_t = std::ranges::range_value_t; static_assert(traits::adc_input_char_range || traits::formattable, @@ -230,7 +314,7 @@ public: if (std::ranges::distance(v.begin(), v.end())) { for (const auto& el : v) { - this->convertToBytes(_tokens.emplace_back(), el); + utils::convertToBytes(this->_bytes.emplace_back(), el); } } @@ -247,7 +331,7 @@ public: template void setTokens(const T& v, const Ts&... vs) { - _tokens.clear(); + this->_bytes.clear(); appendTokens(v, vs...); } @@ -261,8 +345,9 @@ public: do { it = std::search(start_it, end, tokenDelimiter.begin(), tokenDelimiter.end()); - std::copy(start_it, it, std::back_inserter(_tokens.emplace_back())); - start_it = it + tokenDelimiter.size(); + std::copy(start_it, it, std::back_inserter(this->_bytes.emplace_back())); + start_it = it; + std::advance(start_it, tokenDelimiter.size()); // to support not only random-access iterators } while (it != end); } @@ -271,13 +356,24 @@ public: void setFromBytes(IT begin, IT end) requires std::same_as, char> { - _tokens.clear(); + this->_bytes.clear(); appendFromBytes(begin, end); } protected: - std::vector _tokens; + virtual std::vector storageViewByIndex(size_t idx) const override + { + const ByteStorageT &el = this->_bytes[idx]; + std::vector vw{{el.begin(), el.end()}}; + + auto last_idx = this->_bytes.size() - 1; + if (idx < last_idx) { // add delimiter + vw.emplace_back(tokenDelimiter.begin(), tokenDelimiter.end()); + } + + return vw; + } }; @@ -294,26 +390,40 @@ class AdcKeyTokenNetMessage : public AdcTokenNetMessage; public: - static constexpr std::string_view keytokenDelim{KEY_TOKEN_DELIM}; + static constexpr std::string_view keytokenDelimiter{KEY_TOKEN_DELIM}; using base_t::appendTokens; using base_t::setTokens; using base_t::tokens; + using base_t::appendFromBytes; // append only tokens not keyword + using base_t::bytesView; + using base_t::bytes; + using base_t::empty; - virtual bool empty() const + AdcKeyTokenNetMessage() = default; + + + template + AdcKeyTokenNetMessage(const KT &key, const T &tok, const Ts &...toks) { - auto N = std::distance(this->_bytes.begin(), this->_bytes.end()); - - return (N == 0) && this->_tokens.empty(); + utils::convertToBytes(this->_bytes.emplace_back(), key); + setTokens(tok, toks...); } + + virtual ~AdcKeyTokenNetMessage() = default; + template R keyBytes() const { R r; - std::ranges::copy(this->_bytes, r); + if (empty()) { + return r; + } + + std::ranges::copy(this->_bytes[0], r); return r; } @@ -321,67 +431,100 @@ public: ByteStorageT keyBytes() const { - // - return this->_bytes; + if (empty()) { + return ByteStorageT(); + } + + return this->_bytes[0]; } template R keyView() const { - // - return R{this->_bytes.begin(), this->_bytes.end()}; + if (empty()) { + return R(); + } + + return R{this->_bytes[0].begin(), this->_bytes[0].end()}; } ByteViewT keyView() const + { + if (empty()) { + return ByteViewT(); + } + + return ByteViewT{this->_bytes[0].begin(), this->_bytes[0].end()}; + } + + template R> + R tokens(size_t start = 0, size_t N = std::numeric_limits::max()) const + { + // "+1" since the first element is keyword + return base_t::template tokens(start + 1, N); + } + + std::vector tokens(size_t start = 0, size_t N = std::numeric_limits::max()) const { // - return ByteViewT{this->_bytes.begin(), this->_bytes.end()}; + return tokens>(start, N); } - template - R bytes() const + template + void setKeyTokens(const KT &key, const T &tok, const Ts &...toks) { - R r; + this->_bytes.clear(); - if (this->_bytes.empty() && this->_tokens.empty()) { - return r; - } - - std::ranges::copy(this->_bytes, std::back_inserter(r)); // keyword - - if (!this->_tokens.empty()) { - std::ranges::copy(keytokenDelim, std::back_inserter(r)); // keyword-to-tokens delimiter - utils::AdcJoinRange(this->_tokens, base_t::tokenDelimiter, r); - } - - return r; + utils::convertToBytes(this->_bytes.emplace_back(), key); + appendTokens(tok, toks...); } - template - R bytesView() const + + template + void setTokens(const T& v, const Ts&... vs) { - R r; + this->_bytes.resize(1); // the first element is keyword - if (this->_bytes.empty() && this->_tokens.empty()) { - return r; + appendTokens(v, vs...); + } + + + template + void setFromBytes(IT begin, IT end) + requires std::same_as, char> + { + this->_bytes.clear(); + + auto it = std::search(begin, end, keytokenDelimiter.begin(), keytokenDelimiter.end()); + + std::copy(begin, it, std::back_inserter(this->_bytes.emplace_back())); // keyword + + if (it != end) { + std::advance(it, keytokenDelimiter.size()); + appendFromBytes(it, end); // tokens } - - std::back_inserter(r) = {this->_bytes.begin(), this->_bytes.end()}; // keyword - - if (!this->_tokens.empty()) { - std::back_inserter(r) = {keytokenDelim.begin(), keytokenDelim.end()}; // keyword-to-tokens delimiter - utils::AdcReturnRangeElementsView(this->_tokens, base_t::tokenDelimiter, r); - } - - return r; } protected: - // _bytes: keyword - // _tokens: tokens + + virtual std::vector storageViewByIndex(size_t idx) const override + { + if (idx == 0) { // the first element is keyword + const ByteStorageT &el = this->_bytes[idx]; + std::vector vw{{el.begin(), el.end()}}; + + if (this->_bytes.size() > 1) { // there are tokens, so add keyword-to-tokens delimiter + vw.emplace_back(keytokenDelimiter.begin(), keytokenDelimiter.end()); + } + + return vw; + } else { + return base_t::storageViewByIndex(idx); + } + } }; } // namespace adc diff --git a/tests/adc_netmsg_test.cpp b/tests/adc_netmsg_test.cpp index d6e36ea..610e583 100644 --- a/tests/adc_netmsg_test.cpp +++ b/tests/adc_netmsg_test.cpp @@ -3,13 +3,65 @@ #include #include -#include "../common/adc_utils.h" -#include "../net/adc_netmessage.h" +// #include "../common/adc_utils.h" +// #include "../net/adc_netmessage.h" + +#include "../net/adc_netmsg.h" using namespace adc; static constexpr char DD[] = "="; +static constexpr char TD[] = "-"; + +TEST_CASE("[ADC NET MESSAGE]") +{ + AdcKeyTokenNetMessage
msg; + + std::string_view bytes{"SET=POS 1 2 3 4 5"}; + + std::cout << "INPUT BYTES: [" << bytes << "]\n"; + + msg.setFromBytes(bytes.begin(), bytes.end()); + + auto keyv = msg.keyView(); + auto keyb = msg.keyBytes(); + + std::cout << "KEY VIEW: [" << keyv << "]\n"; + std::cout << "KEY BYTE: [" << keyb << "]\n"; + + auto tks = msg.tokens(1, 3); + for (auto& el : tks) { + std::cout << "TOKEN: [" << el << "]\n"; + } + + auto t2 = msg.bytesView(); + for (auto& el : t2) { + std::cout << "BYTES VIEW: [" << el << "]\n"; + } + + + std::cout << "\n\n---------\n\n"; + + + msg.setTokens("FILTER", "A1", "B3"); + std::cout << "BYTES: " << msg.bytes() << "\n"; + + std::cout << "\n\n---------\n\n"; + + + AdcTokenNetMessage<> tmsg; + std::list bb{'1', ' ', '2', ' ', '3', ' ', '4', ' ', '5'}; + + tmsg.setFromBytes(bb.begin(), bb.end()); + + auto t1 = tmsg.bytesView(); + for (auto& el : t1) { + std::cout << "TOKEN VIEW: [" << el << "]\n"; + } +} + +/* TEST_CASE("[ADC NET MESSAGE]") { AdcKeyParamNetMessage
msg; @@ -73,3 +125,5 @@ TEST_CASE("[ADC NET MESSAGE]") std::cout << "\n\nN = " << N << "\n"; std::cout << sr << "\n"; } + +*/