ADC/net/adc_netmsg.h

577 lines
15 KiB
C++

#pragma once
#include <algorithm>
#include <string>
#include <vector>
#include "../common/adc_traits.h"
#include "../common/adc_utils.h"
namespace adc
{
namespace utils
{
template <typename ByteStorageT, typename T, typename... Ts>
void convertToBytes(ByteStorageT& res, const T& v, const Ts&... vs)
{
if constexpr (std::is_array_v<std::remove_cvref_t<T>>) { // to handle with trailing '\0' in plain char array
convertToBytes(res, std::string_view(v), vs...);
} else {
if constexpr (traits::adc_input_char_range<T>) {
std::ranges::copy(v, std::back_inserter(res));
} else if constexpr (std::convertible_to<T, ByteStorageT>) {
convertToBytes(res, static_cast<ByteStorageT>(v), vs...);
} else if constexpr (traits::formattable<T>) {
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 <typename T>
concept adc_netmessage_c = requires(const T t) { // const methods
{ t.empty() } -> std::convertible_to<bool>;
{ 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 <traits::adc_output_char_range ByteStorageT = std::string, traits::adc_char_view ByteViewT = std::string_view>
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 <traits::adc_output_char_range R>
R bytes() const
{
R r;
std::ranges::copy(_bytes, std::back_inserter(r));
return r;
}
virtual ByteStorageT bytes() const
{
//
return bytes<ByteStorageT>();
}
// get a view of message bytes
template <traits::adc_range_of_view_char_range R>
R bytesView() const
{
R r;
r.emplace_back(_bytes.begin(), _bytes.end());
return r;
}
std::vector<ByteViewT> bytesView() const
{
//
return bytesView<std::vector<ByteViewT>>();
}
protected:
ByteStorageT _bytes;
AdcNetMessageTrivialInterface() = default;
};
/*
interface for more complex messages:
byte storage: std::vector of char-range (sequence of buffers)
*/
template <traits::adc_output_char_range ByteStorageT = std::string, traits::adc_char_view ByteViewT = std::string_view>
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;
}
// get a copy of message bytes
template <traits::adc_output_char_range R>
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<ByteStorageT>();
}
// get a view of message bytes
template <traits::adc_range_of_view_char_range R>
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<ByteViewT> bytesView() const
{
//
return bytesView<std::vector<ByteViewT>>();
}
protected:
std::vector<ByteStorageT> _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<ByteViewT> storageViewByIndex(size_t) const = 0;
};
// Generic message class
template <traits::adc_output_char_range ByteStorageT = std::string, traits::adc_char_view ByteViewT = std::string_view>
class AdcGenericNetMessage : public AdcNetMessageTrivialInterface<ByteStorageT, ByteViewT>
{
using base_t = AdcNetMessageTrivialInterface<ByteStorageT, ByteViewT>;
public:
using base_t::base_t;
using base_t::bytes;
using base_t::bytesView;
using base_t::empty;
template <typename T, typename... Ts>
AdcGenericNetMessage(const T& v, const Ts&... vs) : AdcGenericNetMessage()
{
appendBytes(v, vs...);
}
template <typename T, typename... Ts>
void appendBytes(const T& v, const Ts&... vs)
{
utils::convertToBytes(this->_bytes, v, vs...);
}
template <typename T, typename... Ts>
void setBytes(const T& v, const Ts&... vs)
{
this->_bytes = ByteStorageT();
appendBytes(v, vs...);
}
template <std::input_iterator IT>
void appendFromBytes(IT begin, IT end)
requires std::same_as<std::iter_value_t<IT>, char>
{
std::copy(begin, end, std::back_inserter(this->_bytes));
}
template <std::input_iterator IT>
void setFromBytes(IT begin, IT end)
requires std::same_as<std::iter_value_t<IT>, 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 <const char* TOKEN_DELIM = constants::ADC_DEFAULT_TOKEN_DELIMITER,
traits::adc_output_char_range ByteStorageT = std::string,
traits::adc_char_view ByteViewT = std::string_view>
class AdcTokenNetMessage : public AdcNetMessageSeqInterface<ByteStorageT, ByteViewT>
{
// AdcNetMessageSeqInterface<ByteStorageT, ByteViewT>::_bytes - tokens
using base_t = AdcNetMessageSeqInterface<ByteStorageT, ByteViewT>;
public:
static constexpr std::string_view tokenDelimiter{TOKEN_DELIM};
using base_t::bytes;
using base_t::bytesView;
using base_t::empty;
AdcTokenNetMessage() = default;
template <typename T, typename... Ts>
AdcTokenNetMessage(const T& v, const Ts&... vs) : AdcTokenNetMessage() // _reservedNum = 0
{
setTokens(v, vs...);
}
virtual ~AdcTokenNetMessage() = default;
template <std::ranges::output_range<ByteStorageT> R>
R tokens(size_t start = 0, size_t N = std::numeric_limits<size_t>::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<ByteStorageT> tokens(size_t start = 0, size_t N = std::numeric_limits<size_t>::max()) const
{
//
return tokens<std::vector<ByteStorageT>>(start, N);
}
template <traits::adc_output_char_range R>
R tokensBytes(size_t start = 0, size_t N = std::numeric_limits<size_t>::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<size_t>::max()) const
{
return tokensBytes<ByteStorageT>(start, N);
}
template <typename T, typename... Ts>
void appendTokens(const T& v, const Ts&... vs)
{
if constexpr (traits::adc_input_char_range<T> || traits::formattable<T>) {
utils::convertToBytes(this->_bytes.emplace_back(), v);
} else if constexpr (std::ranges::input_range<T>) {
using el_t = std::ranges::range_value_t<T>;
static_assert(traits::adc_input_char_range<el_t> || traits::formattable<el_t>,
"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 <typename T, typename... Ts>
void setTokens(const T& v, const Ts&... vs)
{
this->_bytes.resize(this->_reservedNum);
_tokenNumber = 0;
appendTokens(v, vs...);
}
template <std::input_iterator IT>
void appendFromBytes(IT begin, IT end)
requires std::same_as<std::iter_value_t<IT>, 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 <std::input_iterator IT>
void setFromBytes(IT begin, IT end)
requires std::same_as<std::iter_value_t<IT>, char>
{
this->_bytes.resize(this->_reservedNum);
_tokenNumber = 0;
appendFromBytes(begin, end);
}
protected:
size_t _tokenNumber;
virtual std::vector<ByteViewT> storageViewByIndex(size_t idx) const override
{
const ByteStorageT& el = this->_bytes[idx];
std::vector<ByteViewT> 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 <const char* KEY_TOKEN_DELIM = constants::ADC_DEFAULT_KEY_TOKEN_DELIMITER,
const char* TOKEN_DELIM = constants::ADC_DEFAULT_TOKEN_DELIMITER,
traits::adc_output_char_range ByteStorageT = std::string,
traits::adc_char_view ByteViewT = std::string_view>
class AdcKeyTokenNetMessage : public AdcTokenNetMessage<TOKEN_DELIM, ByteStorageT, ByteViewT>
{
using base_t = AdcTokenNetMessage<TOKEN_DELIM, ByteStorageT, ByteViewT>;
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 <typename KT, typename T, typename... Ts>
AdcKeyTokenNetMessage(const KT& key, const T& tok, const Ts&... toks) : AdcKeyTokenNetMessage()
{
utils::convertToBytes(this->_bytes[0], key);
setTokens(tok, toks...);
}
virtual ~AdcKeyTokenNetMessage() = default;
// get a copy of message bytes
template <traits::adc_output_char_range R>
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<ByteStorageT>();
}
// get a view of message bytes
template <traits::adc_range_of_view_char_range R>
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<ByteViewT> bytesView() const
{
//
return bytesView<std::vector<ByteViewT>>();
}
template <traits::adc_output_char_range R>
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<ByteStorageT>();
}
template <traits::adc_char_view R>
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<ByteViewT>();
}
template <typename KT, typename T, typename... Ts>
void setKeyTokens(const KT& key, const T& tok, const Ts&... toks)
{
setTokens(tok, toks...);
utils::convertToBytes(this->_bytes[0], key);
}
template <std::input_iterator IT>
void setFromBytes(IT begin, IT end)
requires std::same_as<std::iter_value_t<IT>, 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