#pragma once #include #include #include #include "../common/adc_traits.h" /* ABSTRACT DEVICE COMPONENTS LIBRARY */ /* GENERIC NETWORK MESSAGE CLASS */ namespace adc { namespace traits { template concept adc_output_range_of_char_range_c = requires { requires std::ranges::output_range>; requires std::ranges::output_range, char>; }; } // namespace traits namespace utils { template size_t AdcSplitCharRangeToTokens(OutR& result, const InR& input, const DelimT& delim = std::string_view(" ")) { decltype(std::ranges::split_view(input, delim)) vv; if constexpr (std::is_array_v) { vv = input | std::views::split(std::string_view(delim)); } else { vv = input | std::views::split(delim); } std::ranges::transform(vv, std::back_inserter(result), [](const auto& el) { return std::string(el.begin(), el.end()); }); // return number of tokens return std::ranges::distance(vv.begin(), vv.end()); } } // namespace utils class AdcNetMessageInterface { public: typedef std::vector output_seq_t; bool messageEmpty() const { return _outputSequence.empty(); } template void appendBytes(const T& v, const Ts&... vs) { if constexpr (std::is_array_v>) { // to handle with trailing '\0' in plain char array appendBytes(std::string_view(v), vs...); } else { if constexpr (traits::adc_input_char_range) { std::ranges::copy(v, std::back_inserter(_storageSequence.emplace_back())); } else if constexpr (traits::formattable) { std::format_to(std::back_inserter(_storageSequence.emplace_back()), "{}", v); } else { static_assert(false, "UNSUPPORTED TYPE!!!"); } updateState(); } if constexpr (sizeof...(Ts)) { appendBytes(vs...); } } template void setBytes(const T& v, const Ts&... vs) { _storageSequence.clear(); _outputSequence.clear(); appendBytes(v, vs...); } template void appendBytes(IterT begin, IterT end) { static_assert(std::is_same_v, char>, "INVALID ITERATOR VALUE TYPE!!!"); std::ranges::copy(begin, end, std::back_inserter(_storageSequence.emplace_back())); updateState(); } template void setBytes(IterT begin, IterT end) { _storageSequence.clear(); _outputSequence.clear(); appendBytes(begin, end); } virtual size_t byteSize() const { if (_outputSequence.empty()) { return 0; } auto v = _outputSequence | std::views::join; return std::ranges::distance(v.begin(), v.end()); } template R> size_t bytes(R& r) { if (_outputSequence.empty()) { return 0; } auto v = _outputSequence | std::views::join; std::ranges::copy(v, std::back_inserter(r)); return std::ranges::distance(v.begin(), v.end()); }; virtual const output_seq_t& storageSeq() = 0; protected: std::vector _storageSequence; output_seq_t _outputSequence; AdcNetMessageInterface() : _storageSequence(), _outputSequence(){}; template AdcNetMessageInterface(const T& v, const Ts&... vs) : AdcNetMessageInterface() { appendBytes(v, vs...); } virtual ~AdcNetMessageInterface() = default; AdcNetMessageInterface(const AdcNetMessageInterface&) = default; AdcNetMessageInterface(AdcNetMessageInterface&&) = default; AdcNetMessageInterface& operator=(const AdcNetMessageInterface&) = default; AdcNetMessageInterface& operator=(AdcNetMessageInterface&&) = default; virtual bool byteStorageEmpty() const { for (const auto& el : _storageSequence) { if (!el.empty()) { return false; }; } return true; } virtual void updateState() { // just add view to byte data _outputSequence.emplace_back(_storageSequence.back()); }; }; /* GENERIC (NOTHING SPECIAL) NETWORK MESSAGE */ class AdcGenericNetMessage : public AdcNetMessageInterface { public: using AdcNetMessageInterface::AdcNetMessageInterface; virtual ~AdcGenericNetMessage() = default; const output_seq_t& storageSeq() override { return _outputSequence; }; }; namespace constants { static constexpr char ADC_DEFAULT_TOKEN_DELIMITER[] = " "; static constexpr char ADC_DEFAULT_KEY_PARAM_DELIMITER[] = " "; static constexpr char ADC_DEFAULT_PARAM_PARAM_DELIMITER[] = " "; } // namespace constants template class AdcTokenNetMessage : protected AdcNetMessageInterface // (hide setBytes/appendBytes) { using base_t = AdcNetMessageInterface; public: static constexpr std::string_view tokenDelimiter{TOKEN_DELIM}; // back to public access using base_t::bytes; using base_t::byteSize; using base_t::messageEmpty; AdcTokenNetMessage() = default; template AdcTokenNetMessage(const T& v, const Ts&... vs) : AdcTokenNetMessage() { appendTokens(v, vs...); } virtual ~AdcTokenNetMessage() = default; template void appendTokens(const T& v, const Ts&... vs) { if (_storageSequence.size()) { // some tokens are already in storage _outputSequence.emplace_back(tokenDelimiter); // add delimiter in output sequence } if constexpr (traits::adc_input_char_range || traits::formattable) { this->appendBytes(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, "INVALID TYPE OF INPUT TOKENS!!!"); if (std::ranges::distance(v.begin(), v.end())) { for (const auto& el : v) { this->appendBytes(el); } } } else { static_assert(false, "UNSUPPORTED (CANNOT BE SERIALIZED TO BYTES) TYPE OF INPUT TOKENS!!!"); } if constexpr (sizeof...(Ts)) { appendTokens(vs...); } } template void setTokens(const T& v, const Ts&... vs) { _storageSequence.clear(); _outputSequence.clear(); appendTokens(v, vs...); } template void tokens(R& r, size_t start = 0, size_t N = std::numeric_limits::max()) const { if (byteStorageEmpty()) { return; } if ((start >= _storageSequence.size()) || !N) { return; } std::ranges::copy(_storageSequence | std::views ::drop(start) | std::views::take(N), std::back_inserter(r)); } template void tokensBytes(R& r, size_t start = 0, size_t N = std::numeric_limits::max()) const { if (byteStorageEmpty()) { return; } if ((start >= _storageSequence.size()) || !N) { return; } auto max_el = _storageSequence.size() - start; N = N >= max_el ? max_el * 2 - 1 : N * 2 - 1; std::ranges::copy(_outputSequence | std::views::drop(start * 2) | std::views::take(N) | std::views::join, std::back_inserter(r)); } const output_seq_t& storageSeq() override { // nothing special, just returns the reference return _outputSequence; } template void appendFromBytes(IterT begin, IterT end) { static_assert(std::is_same_v, char>, "INVALID ITERATOR VALUE TYPE!!!"); auto curr_n_toks = _storageSequence.size(); auto n_toks = utils::AdcSplitCharRangeToTokens(_storageSequence, std::span(begin, end), tokenDelimiter); if (n_toks) { n_toks += curr_n_toks; if (curr_n_toks) { _outputSequence.emplace_back(tokenDelimiter); // add delimiter in output sequence } _outputSequence.emplace_back(_storageSequence[curr_n_toks]); for (auto i = curr_n_toks + 1; i < n_toks; ++i) { _outputSequence.emplace_back(tokenDelimiter); // add delimiter in output sequence _outputSequence.emplace_back(_storageSequence[i]); } } } template void setFromBytes(IterT begin, IterT end) { _storageSequence.clear(); _outputSequence.clear(); appendFromBytes(begin, end); } protected: // void updateState() override // { // if (_outputSequence.size()) { // some tokens are already in storage // _outputSequence.emplace_back(tokenDelimiter); // add delimiter in output sequence // } // _outputSequence.emplace_back(_storageSequence.back()); // } }; template class AdcKeyParamNetMessage : protected AdcTokenNetMessage { using base_t = AdcTokenNetMessage; public: static constexpr std::string_view keyparamDelimiter{KEY_PARAM_DELIM}; static constexpr std::string_view paramparamDelimiter{PARAM_PARAM_DELIM}; // back to public access using base_t::bytes; using base_t::byteSize; using base_t::messageEmpty; using base_t::storageSeq; AdcKeyParamNetMessage() = default; template AdcKeyParamNetMessage(const KeyT& key, const ParamTs&... params) : AdcKeyParamNetMessage() { setKeyParam(key, params...); } virtual ~AdcKeyParamNetMessage() = default; // key and params template void setKeyParam(const KeyT& key, const ParamTs&... params) { _key.clear(); this->_storageSequence.clear(); this->_outputSequence.clear(); // TODO: what if key is an empty range?!!! if constexpr (traits::adc_input_char_range || traits::formattable) { std::ranges::copy(key, std::back_inserter(_key)); } else if constexpr (traits::formattable) { std::format_to(std::back_inserter(_key), "{}", key); } else { static_assert(false, "INVALID KEY-VALUE TYPE!!!"); } this->_outputSequence.emplace_back(_key); if constexpr (sizeof...(ParamTs)) { this->_outputSequence.emplace_back(keyparamDelimiter); // add key-param delimiter in output sequence this->appendTokens(params...); } } template void key(R& r) const { if constexpr (std::is_convertible_v) { r = _key; return; } std::ranges::copy(_key, std::back_inserter(r)); } template void params(R& r, size_t start = 0, size_t N = std::numeric_limits::max()) const { this->tokens(r, start, N); } template void paramsBytes(R& r, size_t start = 0, size_t N = std::numeric_limits::max()) const { // '+1' since the first 2 elements in _outputSequence are key and key-param delimiter this->tokensBytes(r, start + 1, N); } template void setFromBytes(IterT begin, IterT end) { auto sp = std::span(begin, end); auto found = std::ranges::search(sp, keyparamDelimiter); if (found.empty()) { // no parameters setKeyParam(sp); } else { auto N = std::ranges::distance(sp.begin(), found.begin()); setKeyParam(sp.first(N)); this->_outputSequence.emplace_back(keyparamDelimiter); // add key-param delimiter in output sequence base_t::appendFromBytes(found.begin() + keyparamDelimiter.size(), sp.end()); } } protected: std::string _key; // _outputSequence will store parameters }; } // namespace adc