From 6bd447c45870470dd1fc93aad6bd0f33dbadb087 Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Tue, 8 Oct 2024 01:50:40 +0300 Subject: [PATCH] ... --- common/adc_traits.h | 5 + common/adc_utils.h | 39 +++- net/adc_device_netmsg.h | 465 ++++++++++++++++++++++++++++++++------ tests/adc_netmsg_test.cpp | 82 ++++--- 4 files changed, 478 insertions(+), 113 deletions(-) diff --git a/common/adc_traits.h b/common/adc_traits.h index ff03280..52dec2d 100644 --- a/common/adc_traits.h +++ b/common/adc_traits.h @@ -59,6 +59,11 @@ concept adc_range_of_input_char_range = std::ranges::range && traits::adc_input_char_range>; +template +concept adc_range_of_output_char_range = + std::ranges::range && traits::adc_output_char_range>; + + template concept adc_range_of_view_or_output_char_range = adc_range_of_view_char_range || adc_output_char_range; diff --git a/common/adc_utils.h b/common/adc_utils.h index d3832ca..19d6b92 100644 --- a/common/adc_utils.h +++ b/common/adc_utils.h @@ -453,15 +453,17 @@ template class AdcDefaultValueConverter { public: + static constexpr std::span compositeValueDelimiter{DELIMITER, AdcCharArrSize(DELIMITER)}; + template static SerializedT serialize(const ValueT& value) { SerializedT res; if constexpr (traits::adc_is_tuple_v) { - AdcCharRangeFromTuple(res, value, DELIMITER); - } else if constexpr (std::ranges::range) { - AdcCharRangeFromValueRange(res, value, DELIMITER); + AdcCharRangeFromTuple(res, value, compositeValueDelimiter); + } else if constexpr (std::ranges::range && !traits::adc_input_char_range) { + AdcCharRangeFromValueRange(res, value, compositeValueDelimiter); } else { res = AdcTrivialSerializer(value); } @@ -480,9 +482,9 @@ public: ValueT res; if constexpr (traits::adc_is_tuple_v) { - AdcTupleFromCharRange(res, svalue, DELIMITER); - } else if constexpr (traits::adc_output_char_range) { - AdcValueRangeFromCharRange(res, svalue, DELIMITER); + AdcTupleFromCharRange(res, svalue, compositeValueDelimiter); + } else if constexpr (std::ranges::range && !traits::adc_output_char_range) { + AdcValueRangeFromCharRange(res, svalue, compositeValueDelimiter); } else { res = AdcTrivialDeserializer(svalue); } @@ -623,4 +625,29 @@ static auto AdcSplitCharRange(R&& r, DR&& delim, size_t start = 0, size_t num = return res; } + +// FVN-1a hash function +template +static constexpr size_t AdcFNV1aHash(const R& r) +{ + static_assert(sizeof(size_t) == 8 || sizeof(size_t) == 4, "ONLY FOR 32 or 64-bit size_t!!!"); + + size_t hash = 0, prime = 0; + if constexpr (sizeof(size_t) == 8) { // 64-bit + prime = 1099511628211UL; + hash = 14695981039346656037UL; + } else if constexpr (sizeof(size_t) == 4) { // 32-bit + prime = 16777619; + hash = 2166136261; + } + + std::ranges::for_each(r, [&hash, &prime](const char& ch) { + hash ^= ch; + hash *= prime; + }); + + return hash; +} + + } // namespace adc::utils diff --git a/net/adc_device_netmsg.h b/net/adc_device_netmsg.h index 68ff144..72dbe74 100644 --- a/net/adc_device_netmsg.h +++ b/net/adc_device_netmsg.h @@ -15,7 +15,9 @@ namespace constants static constexpr char ADC_DEFAULT_KEY_VALUE_DELIMITER1[] = " "; static constexpr char ADC_DEFAULT_KEY_PARAM_DELIMITER[] = " "; static constexpr char ADC_DEFAULT_PARAM_PARAM_DELIMITER[] = " "; +static constexpr char ADC_DEFAULT_VALUE_DELIMITER[] = " "; static constexpr char ADC_DEFAULT_COMPOSITE_PARAM_DELIMITER[] = ","; +static constexpr char ADC_DEFAULT_COMPOSITE_VALUE_DELIMITER[] = ","; } // namespace constants @@ -23,16 +25,32 @@ static constexpr char ADC_DEFAULT_COMPOSITE_PARAM_DELIMITER[] = ","; template -class AdcKeyValueMessage1 + const char VALUE_DELIM[] = constants::ADC_DEFAULT_VALUE_DELIMITER, + const char COMPOSITE_VALUE_DELIM[] = constants::ADC_DEFAULT_COMPOSITE_VALUE_DELIMITER> +class AdcKeyValueMessage { public: - static constexpr std::span keyValueDelimiter{KEY_VALUE_DELIM, utils::AdcCharArrSize(KEY_VALUE_DELIM)}; - static constexpr std::span valueDelimiter{VALUE_DELIM, utils::AdcCharArrSize(VALUE_DELIM)}; + static constexpr size_t keyValueDelimiterSize = utils::AdcCharArrSize(KEY_VALUE_DELIM); + static constexpr std::span keyValueDelimiter{KEY_VALUE_DELIM, keyValueDelimiterSize}; - AdcKeyValueMessage1(ByteSeqT& byte_seq) : _byteSequence(byte_seq) {} + static constexpr size_t valueDelimiterSize = utils::AdcCharArrSize(VALUE_DELIM); + static constexpr std::span valueDelimiter{VALUE_DELIM, valueDelimiterSize}; - virtual ~AdcKeyValueMessage1() = default; + static constexpr size_t compValueDelimiterSize = utils::AdcCharArrSize(COMPOSITE_VALUE_DELIM); + static constexpr std::span compValueDelimiter{COMPOSITE_VALUE_DELIM, compValueDelimiterSize}; + + static_assert(keyValueDelimiterSize && valueDelimiterSize && compValueDelimiterSize, + "KEY-VALUE, VALUE-PARTS AND COMPOSITE-VALUE DELIMITERS MUST NOT BE AN EMPTY ARRAY!!!"); + + AdcKeyValueMessage(ByteSeqT& byte_seq) : _byteSequence(byte_seq) {} + + virtual ~AdcKeyValueMessage() = default; + + + bool empty() const + { + return std::ranges::size(_byteSequence) == 0; + } template requires(traits::adc_char_view && std::ranges::contiguous_range) || @@ -111,39 +129,132 @@ public: } - template - requires traits::adc_output_char_range - AdcKeyValueMessage1& setKeyValue(KeyT&& key, ValueT&& value) + template + requires(std::ranges::contiguous_range && traits::adc_char_view>) || + traits::adc_range_of_output_char_range + auto valueParts(size_t start = 0, size_t N = std::numeric_limits::max()) const { - if (std::ranges::size(_byteSequence)) { - _byteSequence = _byteSequence(); + auto val = value>(); + + return utils::AdcSplitCharRange(val, valueDelimiter, start, N); + } + + auto valueParts(size_t start = 0, size_t N = std::numeric_limits::max()) const + { + if constexpr (std::ranges::contiguous_range) { + return valueParts>(start, N); + } else { + return valueParts>(start, N); + } + } + + + template + requires traits::adc_output_char_range || traits::adc_char_view + auto joinValueParts(size_t start = 0, size_t N = std::numeric_limits::max()) const + { + T res; + + if constexpr (std::ranges::contiguous_range) { + auto pp = valueParts>>(start, N); + if (pp.size()) { + res = T{pp.front().begin(), pp.back().end()}; + } + } else { + utils::AdcJoinRange(valueParts>>(start, N), valueDelimiter, res); } - std::ranges::copy( - utils::AdcDefaultValueConverter::template serialize(std::forward(key)), - std::back_inserter(_byteSequence)); + return res; + } - std::ranges::copy(keyValueDelimiter, std::back_inserter(_byteSequence)); - std::ranges::copy( - utils::AdcDefaultValueConverter::template serialize(std::forward(value)), - std::back_inserter(_byteSequence)); + auto joinValueParts(size_t start = 0, size_t N = std::numeric_limits::max()) const + { + if constexpr (std::ranges::contiguous_range) { + return joinValueParts(start, N); + } else { + return joinValueParts(start, N); + } + } + + // template + // requires traits::adc_output_char_range + // AdcKeyValueMessage& setKeyValue(KeyT&& key, ValueT&& value) + // { + // if (std::ranges::size(_byteSequence)) { + // _byteSequence = ByteSeqT(); + // } + + // std::ranges::copy( + // utils::AdcDefaultValueConverter::template serialize(std::forward(key)), + // std::back_inserter(_byteSequence)); + + // std::ranges::copy(keyValueDelimiter, std::back_inserter(_byteSequence)); + + // std::ranges::copy( + // utils::AdcDefaultValueConverter::template serialize(std::forward(value)), + // std::back_inserter(_byteSequence)); + + // return *this; + // } + + + // template + // AdcKeyValueMessage& setValue(ValueT&& value) + // requires traits::adc_output_char_range + // { + // std::vector kw; + // std::ranges::copy(key>(), std::back_inserter(kw)); + + // return setKeyValue(kw, std::forward(value)); + // } + + + template + requires traits::adc_output_char_range + AdcKeyValueMessage& setKeyValue(KeyT&& key, ValuePartTs&&... values) + { + if (std::ranges::size(_byteSequence)) { + _byteSequence = ByteSeqT(); + } + + std::ranges::copy(utils::AdcDefaultValueConverter::template serialize( + std::forward(key)), + std::back_inserter(_byteSequence)); + + if constexpr (sizeof...(ValuePartTs)) { + std::ranges::copy(keyValueDelimiter, std::back_inserter(_byteSequence)); + setValueHelper(std::forward(values)...); + } return *this; } - - template - AdcKeyValueMessage1& setValue(ValueT&& value) + template + requires traits::adc_output_char_range + AdcKeyValueMessage& setValue(ValuePartTs&&... values) { std::vector kw; std::ranges::copy(key>(), std::back_inserter(kw)); - return setKeyValue(kw, std::forward(value)); + return setValue(kw, std::forward(values)...); } protected: ByteSeqT& _byteSequence; + + template + void setValueHelper(ValuePartT&& value, ValuePartTs&&... values) + { + std::ranges::copy(utils::AdcDefaultValueConverter::template serialize( + std::forward(value)), + std::back_inserter(_byteSequence)); + + if constexpr (sizeof...(ValuePartTs)) { + std::ranges::copy(valueDelimiter, std::back_inserter(_byteSequence)); + setValueHelper(std::forward(values)...); + } + } }; @@ -152,9 +263,9 @@ template -class AdcDeviceNetMessage : public AdcKeyValueMessage1 +class AdcDeviceNetMessage : public AdcKeyValueMessage { - using base_t = AdcKeyValueMessage1; + using base_t = AdcKeyValueMessage; public: static constexpr std::span keyParamDelimiter{KEY_PARAM_DELIM, utils::AdcCharArrSize(KEY_PARAM_DELIM)}; @@ -166,63 +277,275 @@ public: AdcDeviceNetMessage(ByteSeqT& byte_seq) : base_t(byte_seq) {} - template - VT keyword() const - { - std::span key; - // first, remove spaces at the beginning and end of byte sequence - auto bs = utils::AdcTrimSpacesView>(this->_byteSequence); + virtual ~AdcDeviceNetMessage() = default; - auto found = std::ranges::search(bs, keyParamDelimiter); - if (found.empty()) { // only keyword - return VT(bs.begin(), bs.end()); + template + requires(std::ranges::contiguous_range && traits::adc_char_view>) || + traits::adc_range_of_output_char_range + auto params(size_t start = 0, size_t N = std::numeric_limits::max()) const + { + auto val = base_t::template value>(); + + return utils::AdcSplitCharRange(val, paramParamDelimiter, start, N); + } + + auto params(size_t start = 0, size_t N = std::numeric_limits::max()) const + { + if constexpr (std::ranges::contiguous_range) { + return params>(start, N); + } else { + return params>(start, N); } - - key = std::span(bs.begin(), found.begin()); - - - return utils::AdcTrimSpacesView(key); - } - - auto keyword() const - { - return keyword(); } - template - auto params(size_t start = 0, size_t N = std::numeric_limits::max()) const + template + requires traits::adc_output_char_range || traits::adc_char_view + auto joinParams(size_t start = 0, size_t N = std::numeric_limits::max()) const { - auto val = base_t::template value>(); + T res; - // R res; - // std::ranges::for_each(std::views::split(val, keyParamDelimiter) | std::views::drop(start) | - // std::views::take(N), - // [&res](const auto& el) { - // // remove spaces - // std::back_inserter(res) = - // utils::AdcTrimSpacesView>(el); - // // std::ranges::copy(utils::AdcTrimSpaces(el), std::back_inserter(res)); - // }); - - // return res; - return utils::AdcSplitCharRange(val, paramParamDelimiter, start, N); - } - - auto params(size_t start = 0, size_t N = std::numeric_limits::max()) const - { - return params>(start, N); - } - - - template - auto joinParams(size_t start = 0, size_t N = std::numeric_limits::max()) - { - R res; - utils::AdcJoinRange(params>>(start, N), paramParamDelimiter, res); + if constexpr (std::ranges::contiguous_range) { + auto pp = params>>(start, N); + if (pp.size()) { + res = T{pp.front().begin(), pp.back().end()}; + } + } else { + utils::AdcJoinRange(params>>(start, N), paramParamDelimiter, res); + } return res; } + + auto joinParams(size_t start = 0, size_t N = std::numeric_limits::max()) const + { + if constexpr (std::ranges::contiguous_range) { + return joinParams(start, N); + } else { + return joinParams(start, N); + } + } + + + template + requires traits::adc_output_char_range + AdcDeviceNetMessage& setKeyParams(KeyT&& key, ParamTs&&... params) + { + this->setKeyValue(std::forward(key), std::string_view()); + + if constexpr (sizeof...(ParamTs)) { + setParamsHelper(std::forward(params)...); + } + + return *this; + } + +protected: + template + void setParamsHelper(ParamT&& param, ParamTs&&... params) + { + std::ranges::copy(utils::AdcDefaultValueConverter::template serialize( + std::forward(param)), + std::back_inserter(this->_byteSequence)); + + if constexpr (sizeof...(ParamTs)) { + std::ranges::copy(paramParamDelimiter, std::back_inserter(this->_byteSequence)); + setParamsHelper(std::forward(params)...); + } + } }; + +template +class AdcDeviceProtoMessage + : protected AdcKeyValueMessage +{ + using base_t = AdcKeyValueMessage; + +public: + static constexpr std::string_view ACK_KEY{"ACK"}; + static constexpr std::string_view SET_KEY{"SET"}; + static constexpr std::string_view GET_KEY{"GET"}; + static constexpr std::string_view CMD_KEY{"CMD"}; + static constexpr std::string_view ERR_KEY{"ERR"}; + static constexpr std::string_view HELLO_KEY{"HELLO"}; + static constexpr std::string_view DEVICE_KEY{"DEVICE"}; + static constexpr std::string_view NAMES_KEY{"NAMES"}; + + static constexpr std::array VALID_KEY{ACK_KEY, SET_KEY, GET_KEY, CMD_KEY, + ERR_KEY, HELLO_KEY, DEVICE_KEY, NAMES_KEY}; + + + typedef std::array keyword_hash_array_t; + + enum KEY_IDX : size_t { + ACK_KEY_IDX, + SET_KEY_IDX, + GET_KEY_IDX, + CMD_KEY_IDX, + ERR_KEY_IDX, + HELLO_KEY_IDX, + DEVICE_KEY_IDX, + NAMES_KEY_IDX + }; + +private: // include here to allow clang compilation + size_t _keyHash = 0; + + template + static constexpr auto computeKeywordHashesImpl(std::index_sequence) + { + return keyword_hash_array_t{utils::AdcFNV1aHash(VALID_KEY[I])...}; + } + template > + static constexpr auto computeKeywordHashes() + { + return computeKeywordHashesImpl(Indices{}); + } + + static constexpr keyword_hash_array_t KEY_HASHES = computeKeywordHashes(); + + auto keyHash() + { + return _keyHash = utils::AdcFNV1aHash(key()); + } + + bool isKey(size_t idx) const + { + return _keyHash == KEY_HASHES[idx]; + } + +public: + AdcDeviceProtoMessage(ByteSeqT& byte_seq) : base_t(byte_seq) + { + keyHash(); + } + + bool isValid() const + { + return std::ranges::any_of(KEY_HASHES, [this](size_t h) { return _keyHash == h; }); + } + + bool isACK() const + { + return isKey(ACK_KEY_IDX); + } + + bool isSET() const + { + return isKey(SET_KEY_IDX); + } + + bool isGET() const + { + return isKey(GET_KEY_IDX); + } + + bool isCMD() const + { + return isKey(CMD_KEY_IDX); + } + + bool isERR() const + { + return isKey(ERR_KEY_IDX); + } + + bool isHELLO() const + { + return isKey(HELLO_KEY_IDX); + } + + bool isDEVICE() const + { + return isKey(DEVICE_KEY_IDX); + } + + bool isNAMES() const + { + return isKey(NAMES_KEY_IDX); + } + + + + template + requires(traits::adc_char_view && std::ranges::contiguous_range) || + traits::adc_output_char_range + auto key() const + { + return base_t::template key(); + } + + + auto key() const + { + return base_t::key(); + } + + + void ack() + { + base_t::setKeyValue(ACK_KEY); + + keyHash(); + } + + template + void set(AttrNameT&& attr_name, ValueT&& value, ValueTs&&... values) + { + base_t::setKeyValue(SET_KEY, std::forward(attr_name), std::forward(value), + std::forward(values)...); + + keyHash(); + } + + template + void get(AttrNameT&& attr_name) + { + base_t::setKeyValue(GET_KEY, std::forward(attr_name)); + + keyHash(); + } + + template + void cmd(CmdNameT&& cmd_name) + { + base_t::setKeyValue(CMD_KEY, std::forward(cmd_name)); + + keyHash(); + } + + void err(const std::error_code& ec) + { + base_t::setKeyValue(ERR_KEY, ec.value(), ec.category(), ec.message()); + + keyHash(); + } + + template + void hello(SenderNameT&& name, ParamTs&&... params) + { + base_t::setKeyValue(HELLO_KEY, std::forward(name), std::forward(params)...); + + keyHash(); + } + + template + void device(DevNameT&& dev_name) + { + base_t::setKeyValue(DEVICE_KEY, std::forward(dev_name)); + + keyHash(); + } + + void names() + { + base_t::setKeyValue(NAMES_KEY); + + keyHash(); + } +}; } // namespace adc diff --git a/tests/adc_netmsg_test.cpp b/tests/adc_netmsg_test.cpp index f780265..1eeeafa 100644 --- a/tests/adc_netmsg_test.cpp +++ b/tests/adc_netmsg_test.cpp @@ -1,4 +1,3 @@ -#include #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include #include @@ -6,9 +5,11 @@ // #include "../common/adc_utils.h" // #include "../net/adc_netmessage.h" +#include + #include "../net/adc_device_netmsg.h" #include "../net/adc_endpoint.h" -#include "../net/adc_netmsg.h" +// #include "../net/adc_netmsg.h" using namespace adc; @@ -18,59 +19,59 @@ static constexpr char TD[] = "-"; TEST_CASE("[ADC NET MESSAGE]") { - AdcKeyTokenNetMessage
msg; + // AdcKeyTokenNetMessage
msg; - std::string_view bytes{"SET=POS 1 2 3 4 5"}; + // std::string_view bytes{"SET=POS 1 2 3 4 5"}; - std::cout << "INPUT BYTES: [" << bytes << "]\n"; + // std::cout << "INPUT BYTES: [" << bytes << "]\n"; - msg.setFromBytes(bytes.begin(), bytes.end()); + // msg.setFromBytes(bytes.begin(), bytes.end()); - auto keyv = msg.keyView(); - auto keyb = msg.keyBytes(); + // auto keyv = msg.keyView(); + // auto keyb = msg.keyBytes(); - std::cout << "KEY VIEW: [" << keyv << "]\n"; - std::cout << "KEY BYTE: [" << keyb << "]\n"; + // 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 tks = msg.tokens(1, 3); + // for (auto& el : tks) { + // std::cout << "TOKEN: [" << el << "]\n"; + // } - auto t2 = msg.bytesView(); - std::cout << "BYTES VIEW: "; - for (auto& el : t2) { - std::cout << "[" << el << "]"; - } - std::cout << "\n"; + // auto t2 = msg.bytesView(); + // std::cout << "BYTES VIEW: "; + // for (auto& el : t2) { + // std::cout << "[" << el << "]"; + // } + // std::cout << "\n"; - std::cout << "\n\n---------\n\n"; + // std::cout << "\n\n---------\n\n"; - msg.setTokens("FILTER", "A1", "B3"); - std::cout << "BYTES: " << msg.bytes() << "\n"; + // msg.setTokens("FILTER", "A1", "B3"); + // std::cout << "BYTES: " << msg.bytes() << "\n"; - std::cout << "TOK BYTES: [" << msg.tokensBytes(1, 1) << "]\n"; + // std::cout << "TOK BYTES: [" << msg.tokensBytes(1, 1) << "]\n"; - std::cout << "\n\n---------\n\n"; + // std::cout << "\n\n---------\n\n"; - AdcTokenNetMessage<> tmsg; - std::list bb{'1', ' ', '2', ' ', '3', ' ', '4', ' ', '5'}; + // AdcTokenNetMessage<> tmsg; + // std::list bb{'1', ' ', '2', ' ', '3', ' ', '4', ' ', '5'}; - tmsg.setFromBytes(bb.begin(), bb.end()); + // tmsg.setFromBytes(bb.begin(), bb.end()); - auto t1 = tmsg.bytesView(); - std::cout << "TOKENS VIEW: "; - for (auto& el : t1) { - std::cout << "[" << el << "]"; - } - std::cout << "\n"; + // auto t1 = tmsg.bytesView(); + // std::cout << "TOKENS VIEW: "; + // for (auto& el : t1) { + // std::cout << "[" << el << "]"; + // } + // std::cout << "\n"; - std::cout << "\n\n\n"; + // std::cout << "\n\n\n"; AdcEndpoint ept(AdcEndpoint::PROTO_ID_LOCAL, "SOCK"); @@ -88,12 +89,21 @@ TEST_CASE("[ADC NET MESSAGE]") AdcDeviceNetMessage md(bs); - std::cout << "KEY = {" << md.keyword() << "}\n"; std::cout << "KEY = {" << md.key() << "}\n"; std::cout << "VALUE = {" << md.value() << "}\n"; for (auto& pp : md.params(2, 2)) { std::cout << "PAR: {" << pp << "}\n"; } + + auto st = md.joinParams(0, 3); + std::cout << "JOIN: {" << st << "}\n"; + + // md.setKeyValue(11, std::make_tuple("1.1", 33.77)); + md.setKeyValue(std::string_view("SET"), std::make_tuple("1.1", 33.77), "OPER", 1.0, 33.44); + std::cout << "BS: " << bs << "\n"; + + md.setKeyParams(std::string_view("SET_ATTR"), std::make_tuple("1.1", 33.77), 77.33, "DEDE"); + std::cout << "BS: " << bs << "\n"; } /*