#pragma once #include "../common/adc_traits.h" namespace adc { namespace traits { template concept adc_netproto_c = adc_range_of_view_char_range && requires(T t, const R& r) { { t.toLowLevel(r) } -> std::same_as; { t.fromLowLevel(r) } -> std::same_as; }; } // namespace traits namespace constants { static constexpr char ADC_DEFAULT_NETPROTO_STOPSEQ[] = "\n"; static constexpr char ADC_DEFAULT_NETPROTO_STARTMARK[] = "\n\t\r\v\r\t\n"; } // namespace constants template struct AdcStopSeqSessionProto { static constexpr std::string_view STOP_SEQ{STOPSEQ}; static constexpr size_t STOP_SEQ_SIZE = STOP_SEQ.size(); static_assert(STOP_SEQ_SIZE, "STOP BYTE SEQUENCE MUST NOT BE AN EMPTY ONE!!!"); template auto search(const R& r) { std::tuple, std::ranges::iterator_t, bool> res{r.begin(), r.end(), false}; std::get<1>(res) = std::search(r.begin(), r.end(), STOP_SEQ.begin(), STOP_SEQ.end()); if (std::get<1>(res) != r.end()) { // move iterator to the one-past-the-end position std::get<1>(res) = std::advance(std::get<1>(res), STOP_SEQ_SIZE); std::get<2>(res) = true; } return res; } template auto toProto(const R& r) { // return 2-element array with the first element as a view of the input range and // the second one - a view of the stop sequence if constexpr (std::ranges::viewable_range) { auto res = std::array{std::ranges::subrange(r.begin(), r.end()), std::ranges::subrange(STOP_SEQ.begin(), STOP_SEQ.end())}; return res; } else { // return a copy of input range with appended stop sequence auto res = r; std::ranges::copy(STOP_SEQ, std::back_inserter(res)); return res; } } template auto fromProto(const R& r) { auto N = std::distance(r.begin(), r.end()); if (N < STOP_SEQ_SIZE) { // one must ensure for input range size correctness return std::span>(); } if constexpr (std::ranges::viewable_range) { return std::ranges::subrange(r.begin(), r.end() - STOP_SEQ_SIZE); } else { R res; std::ranges::copy(r | std::views::take(N - STOP_SEQ_SIZE), std::back_inserter(res)); return res; } } }; template struct AdcNetProtoStopSeq : InetT { static constexpr std::string_view stopSeq{STOPSEQ}; static constexpr size_t stopSeqSize = stopSeq.size(); static_assert(stopSeqSize, "STOP BYTE SEQUENCE MUST NOT BE AN EMPTY ONE!!!"); using socket_t = typename InetT::socket; template std::pair matchCondition(IT begin, IT end) { auto res = std::make_pair(begin, false); if (begin == end) { return res; } res.first = std::search(begin, end, stopSeq.begin(), stopSeq.end()); if (res.first != end) { std::advance(res.first, stopSeqSize); // move iterator to the one-past-the-end position res.second = true; } else { // may be only a part of valid byte sequence was received, // so start next matching from previous begin-iterator res.first = begin; } return res; } /* range of char range's views */ template R toLowLevel(const R& r) { R res = r; // just add to the back of the range "stop sequence" element std::back_inserter(res) = {stopSeq.begin(), stopSeq.end()}; return res; } template R fromLowLevel(const R& r) { R res; auto N = std::distance(r.begin(), r.end()); if (!N) { return res; } // just remove the last element (it is assumed the last element is the stop sequence) std::ranges::copy(r | std::views::take(N - 1), std::back_inserter(res)); return res; } /* range of char */ template R toLowLevel(const R& r) { R res = r; // just add to the back of the "stop sequence" std::ranges::copy(stopSeq, std::back_inserter(res)); return res; } template R fromLowLevel(const R& r) { R res; auto N = std::distance(r.begin(), r.end()); if (N <= stopSeq) { // !!!!!!!!!!! return res; } // remove stopSeqSize bytes from the back of the input range std::ranges::copy(r | std::views::take(N - stopSeqSize), std::back_inserter(res)); return res; } /* from iterators to the input range */ template std::pair fromLowLevel(IT begin, IT end) { auto N = std::distance(begin, end); if (N <= stopSeqSize) { // !!!!!!!! return std::make_pair(end, end); } std::advance(end, -stopSeqSize); return std::make_pair(begin, end); } }; template struct AdcNetProtoSizedBlob { static constexpr std::string_view startMark{START_MARK}; static constexpr size_t sizeFieldLen = SIZE_FIELD_LEN; template std::pair matchCondition(IT begin, IT end) { auto res = std::make_pair(begin, false); if (std::distance(begin, end) <= (startMark.size() + sizeFieldLen)) { // still unusefull part return res; } res.first = std::search(begin, end, startMark.begin(), startMark.end()); if (res.first != end) { std::advance(res.first, startMark); auto it = res.first; std::advance(it, sizeFieldLen); std::string sz_str{res.first, it}; size_t blob_size = 0; auto end_ptr = sz_str.data() + sizeFieldLen; auto [ptr, ec] = std::from_chars(sz_str.data(), end_ptr, blob_size, 16); // heximal!!! if ((ec != std::errc()) || (ptr != end_ptr)) { return {begin, false}; } std::advance(res.first, sizeFieldLen); if (std::distance(res.first, end) >= blob_size) { // still partially received message?! std::advance(res.first, blob_size); // move output iterator to the one-past-the-end position res.second = true; } else { // still partially received message?! res.first = begin; } } else { // may be only a part of message was received, // so start next matching from previous begin-iterator res.first = begin; } return res; } }; } // namespace adc