ADC/net/adc_netmessage.h
Timur A. Fatkhullin 2acb97e973 ...
2024-05-28 23:41:36 +03:00

462 lines
12 KiB
C++

#pragma once
#include <algorithm>
#include <ranges>
#include <vector>
#include "../common/adc_traits.h"
/*
ABSTRACT DEVICE COMPONENTS LIBRARY
*/
/* GENERIC NETWORK MESSAGE CLASS */
namespace adc
{
namespace traits
{
template <typename T>
concept adc_output_range_of_char_range_c = requires {
requires std::ranges::output_range<T, std::ranges::range_value_t<T>>;
requires std::ranges::output_range<std::ranges::range_value_t<T>, char>;
};
} // namespace traits
namespace utils
{
template <traits::adc_output_range_of_char_range_c OutR,
traits::adc_input_char_range InR,
traits::adc_input_char_range DelimT = std::string_view>
size_t AdcSplitCharRangeToTokens(OutR& result, const InR& input, const DelimT& delim = std::string_view(" "))
{
decltype(std::ranges::split_view(input, delim)) vv;
if constexpr (std::is_array_v<DelimT>) {
vv = input | std::views::split(std::string_view(delim));
} else {
vv = input | std::views::split(delim);
}
std::ranges::transform(vv, std::back_inserter(result),
[](const auto& el) { return std::string(el.begin(), el.end()); });
// return number of tokens
return std::ranges::distance(vv.begin(), vv.end());
}
} // namespace utils
class AdcNetMessageInterface
{
public:
typedef std::vector<std::string_view> output_seq_t;
bool messageEmpty() const { return _outputSequence.empty(); }
template <typename T, typename... Ts>
void appendBytes(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
appendBytes(std::string_view(v), vs...);
} else {
if constexpr (traits::adc_input_char_range<T>) {
std::ranges::copy(v, std::back_inserter(_storageSequence.emplace_back()));
} else if constexpr (traits::formattable<T>) {
std::format_to(std::back_inserter(_storageSequence.emplace_back()), v);
} else {
static_assert(false, "UNSUPPORTED TYPE!!!");
}
updateState();
}
if constexpr (sizeof...(Ts)) {
appendBytes(vs...);
}
}
template <typename T, typename... Ts>
void setBytes(const T& v, const Ts&... vs)
{
_storageSequence.clear();
_outputSequence.clear();
appendBytes(v, vs...);
}
template <std::input_iterator IterT>
void appendBytes(IterT begin, IterT end)
{
static_assert(std::is_same_v<std::iter_value_t<IterT>, char>, "INVALID ITERATOR VALUE TYPE!!!");
std::ranges::copy(begin, end, std::back_inserter(_storageSequence.emplace_back()));
updateState();
}
template <std::input_iterator IterT>
void setBytes(IterT begin, IterT end)
{
_storageSequence.clear();
_outputSequence.clear();
appendBytes(begin, end);
}
virtual size_t byteSize() const
{
if (_outputSequence.empty()) {
return 0;
}
auto v = _outputSequence | std::views::join;
return std::ranges::distance(v.begin(), v.end());
}
template <traits::adc_output_char_range R>
size_t bytes(R& r)
{
if (_outputSequence.empty()) {
return 0;
}
auto v = _outputSequence | std::views::join;
std::ranges::copy(v, std::back_inserter(r));
return std::ranges::distance(v.begin(), v.end());
};
virtual const output_seq_t& storageSeq() = 0;
protected:
std::vector<std::string> _storageSequence;
output_seq_t _outputSequence;
AdcNetMessageInterface() : _storageSequence(), _outputSequence() {};
template <typename T, typename... Ts>
AdcNetMessageInterface(const T& v, const Ts&... vs) : AdcNetMessageInterface()
{
appendBytes(v, vs...);
}
virtual ~AdcNetMessageInterface() = default;
AdcNetMessageInterface(const AdcNetMessageInterface&) = default;
AdcNetMessageInterface(AdcNetMessageInterface&&) = default;
AdcNetMessageInterface& operator=(const AdcNetMessageInterface&) = default;
AdcNetMessageInterface& operator=(AdcNetMessageInterface&&) = default;
virtual bool byteStorageEmpty() const
{
for (const auto& el : _storageSequence) {
if (!el.empty()) {
return false;
};
}
return true;
}
virtual void updateState()
{
// just add view to byte data
_outputSequence.emplace_back(_storageSequence.back());
};
};
/* GENERIC (NOTHING SPECIAL) NETWORK MESSAGE */
class AdcGenericNetMessage : public AdcNetMessageInterface
{
public:
using AdcNetMessageInterface::AdcNetMessageInterface;
virtual ~AdcGenericNetMessage() = default;
const output_seq_t& storageSeq() override { return _outputSequence; };
};
namespace constants
{
static constexpr char ADC_DEFAULT_TOKEN_DELIMITER[] = " ";
static constexpr char ADC_DEFAULT_KEY_PARAM_DELIMITER[] = " ";
static constexpr char ADC_DEFAULT_PARAM_PARAM_DELIMITER[] = " ";
} // namespace constants
template <const char* TOKEN_DELIM = constants::ADC_DEFAULT_TOKEN_DELIMITER>
class AdcTokenNetMessage : protected AdcNetMessageInterface // (hide setBytes/appendBytes)
{
using base_t = AdcNetMessageInterface;
public:
static constexpr std::string_view tokenDelimiter{TOKEN_DELIM};
// back to public access
using base_t::bytes;
using base_t::byteSize;
using base_t::messageEmpty;
AdcTokenNetMessage() = default;
template <typename T, typename... Ts>
AdcTokenNetMessage(const T& v, const Ts&... vs) : AdcTokenNetMessage()
{
appendTokens(v, vs...);
}
virtual ~AdcTokenNetMessage() = default;
template <typename T, typename... Ts>
void appendTokens(const T& v, const Ts&... vs)
{
if constexpr (traits::adc_input_char_range<T> || traits::formattable<T>) {
this->appendBytes(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) {
this->appendBytes(el);
}
}
} else {
static_assert(false, "INVALID TYPE OF INPUT TOKENS!!!");
}
if constexpr (sizeof...(Ts)) {
appendTokens(vs...);
}
}
template <typename T, typename... Ts>
void setTokens(const T& v, const Ts&... vs)
{
_storageSequence.clear();
_outputSequence.clear();
appendTokens(v, vs...);
}
template <traits::adc_output_range_of_char_range_c R>
void tokens(R& r, size_t start = 0, size_t N = std::numeric_limits<size_t>::max()) const
{
if (byteStorageEmpty()) {
return;
}
if ((start >= _storageSequence.size()) || !N) {
return;
}
std::ranges::copy(_storageSequence | std::views ::drop(start) | std::views::take(N), std::back_inserter(r));
}
template <traits::adc_output_char_range R>
void tokensBytes(R& r, size_t start = 0, size_t N = std::numeric_limits<size_t>::max()) const
{
if (byteStorageEmpty()) {
return;
}
if ((start >= _storageSequence.size()) || !N) {
return;
}
auto max_el = _storageSequence.size() - start;
N = N >= max_el ? max_el * 2 - 1 : N * 2 - 1;
std::ranges::copy(_outputSequence | std::views::drop(start * 2) | std::views::take(N) | std::views::join,
std::back_inserter(r));
}
const output_seq_t& storageSeq() override
{
// nothing special, just returns the reference
return _outputSequence;
}
template <std::input_iterator IterT>
void appendFromBytes(IterT begin, IterT end)
{
static_assert(std::is_same_v<std::iter_value_t<IterT>, char>, "INVALID ITERATOR VALUE TYPE!!!");
auto curr_n_toks = _storageSequence.size();
auto n_toks = utils::AdcSplitCharRangeToTokens(_storageSequence, std::span(begin, end), tokenDelimiter);
if (n_toks) {
n_toks += curr_n_toks;
if (curr_n_toks) {
_outputSequence.emplace_back(tokenDelimiter); // add delimiter in output sequence
}
_outputSequence.emplace_back(_storageSequence[curr_n_toks]);
for (auto i = curr_n_toks + 1; i < n_toks; ++i) {
_outputSequence.emplace_back(tokenDelimiter); // add delimiter in output sequence
_outputSequence.emplace_back(_storageSequence[i]);
}
}
}
template <std::input_iterator IterT>
void setFromBytes(IterT begin, IterT end)
{
_storageSequence.clear();
_outputSequence.clear();
appendFromBytes(begin, end);
}
protected:
void updateState() override
{
if (_outputSequence.size()) {
_outputSequence.emplace_back(tokenDelimiter); // add delimiter in output sequence
}
_outputSequence.emplace_back(_storageSequence.back());
}
};
template <const char* KEY_PARAM_DELIM = constants::ADC_DEFAULT_KEY_PARAM_DELIMITER,
const char* PARAM_PARAM_DELIM = constants::ADC_DEFAULT_PARAM_PARAM_DELIMITER>
class AdcKeyParamNetMessage : protected AdcTokenNetMessage<PARAM_PARAM_DELIM>
{
using base_t = AdcTokenNetMessage<PARAM_PARAM_DELIM>;
public:
static constexpr std::string_view keyparamDelimiter{KEY_PARAM_DELIM};
static constexpr std::string_view paramparamDelimiter{PARAM_PARAM_DELIM};
// back to public access
using base_t::bytes;
using base_t::byteSize;
using base_t::messageEmpty;
using base_t::storageSeq;
AdcKeyParamNetMessage() = default;
template <typename KeyT, typename... ParamTs>
AdcKeyParamNetMessage(const KeyT& key, const ParamTs&... params) : AdcKeyParamNetMessage()
{
setKeyParam(key, params...);
}
virtual ~AdcKeyParamNetMessage() = default;
// key and params
template <typename KeyT, typename... ParamTs>
void setKeyParam(const KeyT& key, const ParamTs&... params)
{
_key.clear();
this->_storageSequence.clear();
this->_outputSequence.clear();
// TODO: what if key is an empty range?!!!
if constexpr (traits::adc_input_char_range<KeyT> || traits::formattable<KeyT>) {
std::ranges::copy(key, std::back_inserter(_key));
} else if constexpr (traits::formattable<KeyT>) {
std::format_to(std::back_inserter(_key), "{}", key);
} else {
static_assert(false, "INVALID KEY-VALUE TYPE!!!");
}
this->_outputSequence.emplace_back(_key);
if constexpr (sizeof...(ParamTs)) {
this->setTokens(params...);
}
}
template <traits::adc_output_char_range R>
void key(R& r) const
{
if constexpr (std::is_convertible_v<std::string, R>) {
r = _key;
return;
}
std::ranges::copy(_key, std::back_inserter(r));
}
template <traits::adc_output_range_of_char_range_c R>
void params(R& r, size_t start = 0, size_t N = std::numeric_limits<size_t>::max()) const
{
this->tokens(r, start, N);
}
template <traits::adc_output_char_range R>
void paramsBytes(R& r, size_t start = 0, size_t N = std::numeric_limits<size_t>::max()) const
{
// '+1' since the first 2 elements in _outputSequence are key and key-param delimiter
this->tokensBytes(r, start + 1, N);
}
template <std::input_iterator IterT>
void setFromBytes(IterT begin, IterT end)
{
auto sp = std::span(begin, end);
auto found = std::ranges::search(sp, keyparamDelimiter);
if (found.empty()) { // no parameters
setKeyParam(sp);
} else {
auto N = std::ranges::distance(sp.begin(), found.begin());
setKeyParam(sp.first(N));
this->_outputSequence.emplace_back(keyparamDelimiter); // add key-param delimiter in output sequence
base_t::appendFromBytes(found.begin() + keyparamDelimiter.size(), sp.end());
}
}
protected:
std::string _key;
// _outputSequence will store parameters
};
} // namespace adc