305 lines
9.1 KiB
C++
305 lines
9.1 KiB
C++
#pragma once
|
|
|
|
#include "../common/adc_traits.h"
|
|
|
|
namespace adc
|
|
{
|
|
|
|
namespace traits
|
|
{
|
|
|
|
template <typename T, typename R>
|
|
concept adc_netproto_c = adc_range_of_view_char_range<R> && requires(T t, const R& r) {
|
|
{ t.toLowLevel(r) } -> std::same_as<R>;
|
|
{ t.fromLowLevel(r) } -> std::same_as<R>;
|
|
};
|
|
|
|
} // 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
|
|
|
|
|
|
/* SESSION-LEVEL PROTOCOLS */
|
|
|
|
template <const char* STOPSEQ = constants::ADC_DEFAULT_NETPROTO_STOPSEQ>
|
|
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!!!");
|
|
|
|
typedef std::string proto_ident_t;
|
|
|
|
proto_ident_t ident() const
|
|
{
|
|
return "STOP SEQUENCE PROTO";
|
|
}
|
|
|
|
// template <traits::adc_input_char_range R>
|
|
// auto search(const R& r)
|
|
// {
|
|
// std::tuple<std::ranges::iterator_t<R>, std::ranges::iterator_t<R>, bool> res{r.begin(), r.end(), false};
|
|
|
|
// if (!r.size()) {
|
|
// return res;
|
|
// }
|
|
|
|
// // std::get<1>(res) = std::search(r.begin(), r.end(), STOP_SEQ.begin(), STOP_SEQ.end());
|
|
// auto found = std::ranges::search(r, STOP_SEQ);
|
|
// // if (std::get<1>(res) != r.end()) { // move iterator to the one-past-the-end position
|
|
// if (!found.empty()) { // move iterator to the one-past-the-end position
|
|
// // std::advance(std::get<1>(res), STOP_SEQ_SIZE);
|
|
// // std::get<1>(res) = found.begin() + STOP_SEQ_SIZE;
|
|
// std::get<1>(res) = found.end();
|
|
// std::get<2>(res) = true;
|
|
// }
|
|
|
|
// return res;
|
|
// }
|
|
|
|
template <traits::adc_input_char_range R>
|
|
auto search(const R& r)
|
|
{
|
|
auto found = std::ranges::search(r, STOP_SEQ);
|
|
|
|
if constexpr (std::ranges::viewable_range<R>) {
|
|
return found.empty() ? std::span(r.begin(), r.begin()) : std::span(r.begin(), found.end());
|
|
} else {
|
|
std::vector<char> res; // to guaranty adc_output_char_range
|
|
if (!found.empty()) {
|
|
std::copy(r.begin(), found.end(), std::back_inserter(res));
|
|
}
|
|
return res;
|
|
}
|
|
}
|
|
|
|
// template <std::input_iterator IT>
|
|
// auto search(IT begin, IT end)
|
|
// {
|
|
// std::tuple<IT, IT, bool> res{begin, end, false};
|
|
|
|
// if (begin == end) {
|
|
// return res;
|
|
// }
|
|
|
|
// auto it = std::search(begin, end, STOP_SEQ.begin(), STOP_SEQ.end());
|
|
// if (it != end) {
|
|
// // std::advance(it, STOP_SEQ_SIZE); // move iterator to the one-past-the-end position
|
|
// std::get<1>(res) = it + STOP_SEQ_SIZE;
|
|
// std::get<2>(res) = true;
|
|
// } else {
|
|
// // may be only a part of valid byte sequence was received,
|
|
// // so start next matching from previous begin-iterator
|
|
// std::get<1>(res) = begin;
|
|
// }
|
|
|
|
// return res;
|
|
// }
|
|
|
|
template <traits::adc_input_char_range R>
|
|
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<R>) {
|
|
auto res = std::array{std::span(r.begin(), r.end()), std::span(STOP_SEQ.begin(), STOP_SEQ.end())};
|
|
// 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 <traits::adc_input_char_range R>
|
|
auto fromProto(const R& r)
|
|
{
|
|
size_t N = std::distance(r.begin(), r.end());
|
|
|
|
if (N < STOP_SEQ_SIZE) { // one must ensure for input range size correctness
|
|
// return std::span<std::ranges::range_value_t<R>>();
|
|
return std::ranges::subrange(r.begin(), r.begin());
|
|
}
|
|
|
|
if constexpr (std::ranges::viewable_range<R>) {
|
|
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 <typename InetT, const char* STOPSEQ = constants::ADC_DEFAULT_NETPROTO_STOPSEQ>
|
|
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::input_iterator IT>
|
|
std::pair<IT, bool> 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 <traits::adc_range_of_view_char_range R>
|
|
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 <traits::adc_range_of_view_char_range R>
|
|
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 <traits::adc_output_char_range R>
|
|
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 <traits::adc_output_char_range R>
|
|
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::input_iterator IT>
|
|
std::pair<IT, IT> 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 <typename InetT, const char* START_MARK = constants::ADC_DEFAULT_NETPROTO_STARTMARK, size_t SIZE_FIELD_LEN = 8>
|
|
struct AdcNetProtoSizedBlob {
|
|
static constexpr std::string_view startMark{START_MARK};
|
|
static constexpr size_t sizeFieldLen = SIZE_FIELD_LEN;
|
|
|
|
template <std::input_iterator IT>
|
|
std::pair<IT, bool> 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
|