#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 requires std::same_as, char>; { t.empty() } -> std::convertible_to; { t.byteSize() } -> std::convertible_to; { t.bytes() } -> adc_output_char_range; { t.byteView() } -> adc_range_of_view_char_range; { t.setFromBytes(std::input_iterator) } -> std::same_as; }; } // 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; } size_t byteSize() const { // return std::ranges::distance(_bytes.begin(), _bytes.end()); } // 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 (size_t i = _reservedNum; i < _bytes.size(); ++i) { const auto& el = _bytes[i]; if (std::ranges::distance(el.begin(), el.end())) { emp = false; break; } } return emp; } size_t byteSize() const { size_t sz = 0; for (const auto& el : _bytes | std::views::drop(_reservedNum)) { sz += std::ranges::distance(el.begin(), el.end()); } return sz; } // get a copy of message bytes template R bytes() const { R r; for (size_t i = _reservedNum; i < _bytes.size(); ++i) { std::ranges::for_each(storageViewByIndex(i), [&r](const auto& el) { std::ranges::copy(el, std::back_inserter(r)); }); } return r; } ByteStorageT bytes() const { // return bytes(); } // get a view of message bytes template R bytesView() const { R r; for (size_t i = _reservedNum; 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; size_t _reservedNum; AdcNetMessageSeqInterface(size_t reserved = 0) : _reservedNum(reserved), _bytes() { // reserve the "_reservedNum" first elements _bytes.resize(_reservedNum); } // 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::bytes; using base_t::bytesView; using base_t::empty; AdcTokenNetMessage() = default; template AdcTokenNetMessage(const T& v, const Ts&... vs) : AdcTokenNetMessage() // _reservedNum = 0 { 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 + this->_reservedNum) | 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 R tokensBytes(size_t start = 0, size_t N = std::numeric_limits::max()) const { R r; if (empty() || (start >= _tokenNumber) || !N) { return r; } utils::AdcJoinRange(this->_bytes | std::views::drop(start + this->_reservedNum) | std::views::take(N), tokenDelimiter, r); return r; } ByteStorageT tokensBytes(size_t start = 0, size_t N = std::numeric_limits::max()) const { return tokensBytes(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...); } _tokenNumber = this->_bytes.size() - this->_reservedNum; } template void setTokens(const T& v, const Ts&... vs) { this->_bytes.resize(this->_reservedNum); _tokenNumber = 0; 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())); ++_tokenNumber; 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.resize(this->_reservedNum); _tokenNumber = 0; appendFromBytes(begin, end); } protected: size_t _tokenNumber; 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) && (_tokenNumber > 1)) { // 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::appendFromBytes; // append only tokens not keyword using base_t::appendTokens; using base_t::bytes; using base_t::bytesView; using base_t::empty; using base_t::setTokens; using base_t::tokens; AdcKeyTokenNetMessage() { this->_reservedNum = 1; // reserve the first element for keyword this->_bytes.resize(this->_reservedNum); }; template AdcKeyTokenNetMessage(const KT& key, const T& tok, const Ts&... toks) : AdcKeyTokenNetMessage() { utils::convertToBytes(this->_bytes[0], key); setTokens(tok, toks...); } virtual ~AdcKeyTokenNetMessage() = default; size_t byteSize() const { size_t sz = base_t::template byteSize(); return sz + std::distance(this->_bytesbyte[0].begin(), this->_bytes[0].end()); } // get a copy of message bytes template R bytes() const { R r; std::ranges::copy(this->_bytes[0], std::back_inserter(r)); // keyword if (this->_tokenNumber) { std::ranges::copy(keytokenDelimiter, std::back_inserter(r)); // keyword-to-token delimiter } std::ranges::for_each(base_t::template bytesView(), [&r](const auto& el) { std::ranges::copy(el, std::back_inserter(r)); // token }); return r; } ByteStorageT bytes() const { // return bytes(); } // get a view of message bytes template R bytesView() const { R r; const auto& key = this->_bytes[0]; std::back_inserter(r) = {key.begin(), key.end()}; // keyword if (this->_tokenNumber) { std::back_inserter(r) = {keytokenDelimiter.begin(), keytokenDelimiter.end()}; // keyword-to-token delimiter } std::ranges::for_each(base_t::template bytesView(), [&r](const auto& el) { std::back_inserter(r) = {el.begin(), el.end()}; // token }); return r; } std::vector bytesView() const { // return bytesView>(); } template R keyBytes() const { R r; const auto& key = this->_bytes[0]; if (!std::distance(key.begin(), key.end())) { return r; } std::ranges::copy(key, std::back_inserter(r)); return r; } ByteStorageT keyBytes() const { // return keyBytes(); } template R keyView() const { const auto& key = this->_bytes[0]; if (!std::distance(key.begin(), key.end())) { return R(); } return R{this->_bytes[0].begin(), this->_bytes[0].end()}; } ByteViewT keyView() const { // return keyView(); } template void setKeyTokens(const KT& key, const T& tok, const Ts&... toks) { setTokens(tok, toks...); utils::convertToBytes(this->_bytes[0], key); } 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 } } }; } // namespace adc