764 lines
20 KiB
C++
764 lines
20 KiB
C++
#pragma once
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
|
|
#include "../common/adc_traits.h"
|
|
#include "../common/adc_utils.h"
|
|
|
|
namespace adc
|
|
{
|
|
|
|
namespace traits
|
|
{
|
|
|
|
template <typename T>
|
|
concept adc_has_clear_c = requires(T t) { t.clear(); };
|
|
|
|
} // namespace traits
|
|
|
|
|
|
namespace utils
|
|
{
|
|
|
|
|
|
template <typename ByteStorageT, typename T, typename... Ts>
|
|
static 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...);
|
|
}
|
|
}
|
|
|
|
|
|
template <traits::adc_output_char_range R>
|
|
struct AdcTokenManip {
|
|
AdcTokenManip(R& byte_seq) : _byteSequence(byte_seq) {}
|
|
|
|
template <traits::adc_input_char_range DR>
|
|
auto tokens(const DR& delimiter, size_t start = 0, size_t num = std::numeric_limits<size_t>::max())
|
|
{
|
|
std::span<const char> dl;
|
|
|
|
if constexpr (std::is_array_v<DR>) {
|
|
dl = std::span(delimiter, sizeof(delimiter) - 1); // remove trailing '\0'
|
|
} else {
|
|
dl = std::span(delimiter);
|
|
}
|
|
|
|
return std::views::split(_byteSequence, dl) | std::views::drop(start) | std::views::take(num);
|
|
}
|
|
|
|
template <traits::adc_input_char_range DR, typename T, typename... Ts>
|
|
AdcTokenManip& addTokens(const DR& delimiter, T&& v, Ts&&... vs)
|
|
{
|
|
std::span<const char> dl;
|
|
|
|
if constexpr (std::is_array_v<DR>) {
|
|
dl = std::span(delimiter, sizeof(delimiter) - 1); // remove trailing '\0'
|
|
} else {
|
|
dl = std::span(delimiter);
|
|
}
|
|
|
|
if (std::ranges::size(_byteSequence)) {
|
|
std::ranges::copy(dl, std::back_inserter(_byteSequence));
|
|
}
|
|
|
|
convertToBytes(_byteSequence, std::forward<T>(v));
|
|
|
|
if constexpr (sizeof...(Ts)) {
|
|
addTokens(dl, std::forward<Ts>(vs)...);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
template <traits::adc_input_char_range DR, typename T, typename... Ts>
|
|
AdcTokenManip& setTokens(const DR& delimiter, T&& v, Ts&&... vs)
|
|
{
|
|
if (std::ranges::size(_byteSequence)) {
|
|
if constexpr (traits::adc_has_clear_c<R>) {
|
|
_byteSequence.clear();
|
|
} else {
|
|
_byteSequence = R();
|
|
}
|
|
}
|
|
|
|
return addTokens(delimiter, std::forward<T>(v), std::forward<Ts>(vs)...);
|
|
}
|
|
|
|
|
|
private:
|
|
R& _byteSequence;
|
|
};
|
|
|
|
|
|
} // namespace utils
|
|
|
|
|
|
|
|
/*
|
|
*/
|
|
template <traits::adc_output_char_range ByteStorageT = std::string, traits::adc_char_view ByteViewT = std::string_view>
|
|
class AdcNetMessageInterface
|
|
{
|
|
public:
|
|
typedef ByteStorageT byte_storage_t;
|
|
typedef ByteViewT byte_view_t;
|
|
|
|
virtual ~AdcNetMessageInterface() = 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 <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; // sequence of byte buffers
|
|
size_t _reservedNum;
|
|
|
|
|
|
AdcNetMessageInterface(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 AdcNetMessageInterface<ByteStorageT, ByteViewT>
|
|
{
|
|
using base_t = AdcNetMessageInterface<ByteStorageT, ByteViewT>;
|
|
|
|
public:
|
|
using typename base_t::byte_storage_t;
|
|
using typename base_t::byte_view_t;
|
|
|
|
using base_t::base_t;
|
|
using base_t::bytes;
|
|
using base_t::bytesView;
|
|
using base_t::empty;
|
|
|
|
AdcGenericNetMessage() : base_t()
|
|
{
|
|
// just the single buffer
|
|
this->_bytes.resize(1);
|
|
}
|
|
|
|
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[0], v, vs...);
|
|
}
|
|
|
|
template <typename T, typename... Ts>
|
|
void setBytes(const T& v, const Ts&... vs)
|
|
{
|
|
this->_bytes[0] = 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[0]));
|
|
}
|
|
|
|
|
|
template <std::input_iterator IT>
|
|
void setFromBytes(IT begin, IT end)
|
|
requires std::same_as<std::iter_value_t<IT>, char>
|
|
{
|
|
this->_bytes[0] = ByteStorageT();
|
|
|
|
appendFromBytes(begin, end);
|
|
}
|
|
};
|
|
|
|
|
|
namespace constants
|
|
{
|
|
static constexpr char ADC_DEFAULT_TOKEN_DELIMITER[] = " ";
|
|
static constexpr char ADC_DEFAULT_KEY_TOKEN_DELIMITER[] = " ";
|
|
static constexpr char ADC_DEFAULT_KEY_VALUE_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 AdcNetMessageInterface<ByteStorageT, ByteViewT>
|
|
{
|
|
// AdcNetMessageSeqInterface<ByteStorageT, ByteViewT>::_bytes - tokens
|
|
using base_t = AdcNetMessageInterface<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() : base_t()
|
|
{
|
|
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;
|
|
|
|
|
|
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 <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
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|
|
template <traits::adc_output_char_range R, const char* DELIM = constants::ADC_DEFAULT_TOKEN_DELIMITER>
|
|
class AdcTokenMessage : utils::AdcTokenManip<R>
|
|
{
|
|
using base_t = utils::AdcTokenManip<R>;
|
|
|
|
public:
|
|
static constexpr std::span tokenDelimiter{DELIM, sizeof(DELIM) - 1}; // "-1" to remove trailing '\0'
|
|
|
|
AdcTokenMessage(R& byte_seq) : base_t(byte_seq) {}
|
|
|
|
// auto tokens(size_t start = 0, size_t num = std::numeric_limits<size_t>::max())
|
|
// {
|
|
// std::vector<std::span<const char>> res;
|
|
// std::ranges::for_each(base_t::tokens(tokenDelimiter, start, num),
|
|
// [&res](const auto& el) { res.emplace_back(el.begin(), el.end()); });
|
|
|
|
// return res;
|
|
// }
|
|
|
|
template <traits::adc_range_of_view_char_range RT>
|
|
auto tokens(RT& res, size_t start = 0, size_t num = std::numeric_limits<size_t>::max())
|
|
{
|
|
std::ranges::for_each(base_t::tokens(tokenDelimiter, start, num),
|
|
[&res](const auto& el) { res.emplace_back(el.begin(), el.end()); });
|
|
|
|
return &res;
|
|
}
|
|
|
|
template <typename TokT, typename... TokTs>
|
|
AdcTokenMessage& addTokens(TokT&& token, TokTs&&... tokens)
|
|
{
|
|
base_t::addTokens(tokenDelimiter, std::forward<TokT>(token), std::forward<TokTs>(tokens)...);
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <typename TokT, typename... TokTs>
|
|
AdcTokenMessage& setTokens(TokT&& token, TokTs&&... tokens)
|
|
{
|
|
base_t::setTokens(tokenDelimiter, std::forward<TokT>(token), std::forward<TokTs>(tokens)...);
|
|
return *this;
|
|
}
|
|
|
|
protected:
|
|
R& _byteSequence;
|
|
};
|
|
|
|
|
|
template <traits::adc_output_char_range R, const char* KEY_VALUE_DELIM = constants::ADC_DEFAULT_KEY_VALUE_DELIMITER>
|
|
class AdcKeyValueMessage
|
|
{
|
|
public:
|
|
static constexpr std::span keyvalueDelimiter{KEY_VALUE_DELIM, sizeof(KEY_VALUE_DELIM) - 1};
|
|
|
|
AdcKeyValueMessage(R& byte_seq) : _byteSequence(byte_seq) {}
|
|
|
|
template <traits::adc_char_view VT>
|
|
auto key() const
|
|
{
|
|
auto r = std::ranges::search(_byteSequence, keyvalueDelimiter);
|
|
if (r.empty()) {
|
|
return VT();
|
|
}
|
|
|
|
return VT(_byteSequence.begin(), r.begin());
|
|
}
|
|
|
|
auto key() const
|
|
{
|
|
return key<std::string_view>();
|
|
}
|
|
|
|
template <traits::adc_char_view VT>
|
|
auto value() const
|
|
{
|
|
auto r = std::ranges::search(_byteSequence, keyvalueDelimiter);
|
|
if (r.empty()) {
|
|
return VT();
|
|
}
|
|
|
|
return VT(r.end(), _byteSequence.end());
|
|
}
|
|
|
|
|
|
auto value() const
|
|
{
|
|
return value<std::string_view>();
|
|
}
|
|
|
|
|
|
template <typename KeyT, typename ValueT>
|
|
AdcKeyValueMessage& setKeyValue(KeyT&& key, ValueT&& value)
|
|
{
|
|
if (std::ranges::size(_byteSequence)) {
|
|
if constexpr (traits::adc_has_clear_c<R>) {
|
|
_byteSequence.clear();
|
|
} else {
|
|
_byteSequence = R();
|
|
}
|
|
}
|
|
|
|
utils::convertToBytes(_byteSequence, std::forward<KeyT>(key));
|
|
std::ranges::copy(keyvalueDelimiter, std::back_inserter(_byteSequence));
|
|
utils::convertToBytes(_byteSequence, std::forward<ValueT>(value));
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <typename ValueT>
|
|
AdcKeyValueMessage& setValue(ValueT&& value)
|
|
{
|
|
if (std::ranges::size(_byteSequence)) {
|
|
if constexpr (traits::adc_has_clear_c<R>) {
|
|
_byteSequence.clear();
|
|
} else {
|
|
_byteSequence = R();
|
|
}
|
|
}
|
|
|
|
std::vector<char> kw;
|
|
std::ranges::copy(key<std::span<const char>>(), std::back_inserter(kw));
|
|
|
|
return setKeyValue(kw, std::forward<ValueT>(value));
|
|
}
|
|
|
|
protected:
|
|
R& _byteSequence;
|
|
};
|
|
|
|
|
|
namespace traits
|
|
{
|
|
template <typename T>
|
|
concept adc_netmessage_c = requires {
|
|
typename T::byte_storage_t;
|
|
typename T::byte_view_t;
|
|
requires std::derived_from<T, AdcNetMessageInterface<typename T::byte_storage_t, typename T::byte_view_t>>;
|
|
};
|
|
// template <typename T, typename IT>
|
|
// concept adc_netmessage_c = requires(const T t) { // const methods
|
|
// requires std::same_as<std::iter_value_t<IT>, char>;
|
|
// { t.empty() } -> std::convertible_to<bool>;
|
|
// { t.byteSize() } -> std::convertible_to<size_t>;
|
|
// { t.bytes() } -> adc_output_char_range;
|
|
// { t.byteView() } -> adc_range_of_view_char_range;
|
|
// { t.setFromBytes(std::input_iterator<IT>) } -> std::same_as<void>;
|
|
// };
|
|
|
|
} // namespace traits
|
|
|
|
|
|
|
|
} // namespace adc
|