#pragma once #include #include #include #include "../common/adc_traits.h" #include "../common/adc_utils.h" namespace adc { 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 R bytes() const { R r; std::ranges::copy(_bytes, std::back_inserter(r)); return r; } virtual ByteStorageT bytes() const { // return bytes(); } // get a view of message bytes template R bytesView() const { R r; r.emplace_back(_bytes.begin(), _bytes.end()); return r; } std::vector bytesView() const { // return bytesView>(); } protected: ByteStorageT _bytes; AdcNetMessageTrivialInterface() = default; }; /* interface for more complex messages: byte storage: std::vector of char-range (sequence of buffers) */ template class AdcNetMessageSeqInterface { public: virtual ~AdcNetMessageSeqInterface() = default; bool empty() const { if (_bytes.empty()) { return true; } bool emp = true; for (const auto &el : _bytes) { if (std::ranges::distance(el.begin(), el.end())) { emp = false; break; } } 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 AdcNetMessageTrivialInterface { using base_t = AdcNetMessageTrivialInterface; public: using base_t::base_t; using base_t::bytes; 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) { utils::convertToBytes(this->_bytes, v, vs...); } template void setBytes(const T& v, const Ts&... vs) { this->_bytes = ByteStorageT(); appendBytes(v, vs...); } template void appendFromBytes(IT begin, IT end) requires std::same_as, char> { std::copy(begin, end, std::back_inserter(this->_bytes)); } template void setFromBytes(IT begin, IT end) requires std::same_as, char> { this->_bytes = ByteStorageT(); appendFromBytes(begin, end); } }; namespace constants { static constexpr char ADC_DEFAULT_TOKEN_DELIMITER[] = " "; static constexpr char ADC_DEFAULT_KEY_TOKEN_DELIMITER[] = " "; } // namespace constants template class AdcTokenNetMessage : public AdcNetMessageSeqInterface { // 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; template AdcTokenNetMessage(const T& v, const Ts&... vs) : AdcTokenNetMessage() { setTokens(v, vs...); } virtual ~AdcTokenNetMessage() = default; template R> R tokens(size_t start = 0, size_t N = std::numeric_limits::max()) const { R r; if (empty()) { return r; } std::ranges::copy(this->_bytes | std::views::drop(start) | std::views::take(N), std::back_inserter(r)); return r; } std::vector tokens(size_t start = 0, size_t N = std::numeric_limits::max()) const { // return tokens>(start, N); } template void appendTokens(const T& v, const Ts&... vs) { if constexpr (traits::adc_input_char_range || traits::formattable) { 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, "INVALID TYPE OF INPUT TOKENS!!!"); if (std::ranges::distance(v.begin(), v.end())) { for (const auto& el : v) { utils::convertToBytes(this->_bytes.emplace_back(), 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) { this->_bytes.clear(); appendTokens(v, vs...); } template void appendFromBytes(IT begin, IT end) requires std::same_as, char> { auto it = begin, start_it = begin; do { it = std::search(start_it, end, tokenDelimiter.begin(), tokenDelimiter.end()); 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); } template void setFromBytes(IT begin, IT end) requires std::same_as, char> { this->_bytes.clear(); appendFromBytes(begin, end); } protected: 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; } }; // special type of tokenized message: // the first token is keyword // the other - just tokens // delimiter between keyword and other tokens may differ from token-to-token one template class AdcKeyTokenNetMessage : public AdcTokenNetMessage { using base_t = AdcTokenNetMessage; public: 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; AdcKeyTokenNetMessage() = default; template AdcKeyTokenNetMessage(const KT &key, const T &tok, const Ts &...toks) { utils::convertToBytes(this->_bytes.emplace_back(), key); setTokens(tok, toks...); } virtual ~AdcKeyTokenNetMessage() = default; template R keyBytes() const { R r; if (empty()) { return r; } std::ranges::copy(this->_bytes[0], r); return r; } ByteStorageT keyBytes() const { if (empty()) { return ByteStorageT(); } return this->_bytes[0]; } template R keyView() const { 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 tokens>(start, N); } template void setKeyTokens(const KT &key, const T &tok, const Ts &...toks) { this->_bytes.clear(); utils::convertToBytes(this->_bytes.emplace_back(), key); appendTokens(tok, toks...); } template void setTokens(const T& v, const Ts&... vs) { this->_bytes.resize(1); // the first element is keyword 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 } } protected: 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