679 lines
21 KiB
C++
679 lines
21 KiB
C++
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <charconv>
|
|
#include <limits>
|
|
#include <ranges>
|
|
#include <regex>
|
|
#include <utility>
|
|
|
|
#include "../common/adc_traits.h"
|
|
|
|
|
|
namespace adc::utils
|
|
{
|
|
|
|
// compile-time size of zero-terminated char array
|
|
static consteval size_t AdcCharArrSize(const char* arr)
|
|
{
|
|
return *arr ? 1 + AdcCharArrSize(arr + 1) : 0;
|
|
}
|
|
|
|
static bool AdcIsSpace(char in) noexcept
|
|
{
|
|
static constexpr auto ws = {' ', '\t', '\n', '\v', '\r', '\f'};
|
|
return std::ranges::any_of(ws, [in](auto p) { return p == in; });
|
|
};
|
|
|
|
|
|
template <traits::adc_char_range R>
|
|
static auto AdcTrimSpaces(R&& r)
|
|
{
|
|
return r | std::views::drop_while(AdcIsSpace) | std::views::reverse | std::views::drop_while(AdcIsSpace) |
|
|
std::views::reverse;
|
|
}
|
|
|
|
|
|
enum class AdcTrimType { TRIM_LEFT, TRIM_RIGHT, TRIM_BOTH };
|
|
|
|
template <traits::adc_char_view VR, traits::adc_char_range R>
|
|
requires std::ranges::contiguous_range<R>
|
|
static auto AdcTrimSpacesView(R&& r, AdcTrimType type = AdcTrimType::TRIM_BOTH)
|
|
{
|
|
auto is_space = [](const auto& ch) { return ch == ' '; };
|
|
|
|
auto end = std::forward<R>(r).end();
|
|
|
|
auto f1 = std::forward<R>(r).begin();
|
|
|
|
if (type != AdcTrimType::TRIM_RIGHT) {
|
|
// look for the first non-space symbol
|
|
f1 = std::ranges::find_if_not(std::forward<R>(r), is_space);
|
|
if (f1 == end) { // all are spaces!
|
|
return VR();
|
|
}
|
|
}
|
|
|
|
auto f2 = end;
|
|
|
|
if (type != AdcTrimType::TRIM_LEFT) {
|
|
auto f3 = f1;
|
|
|
|
do {
|
|
f2 = std::ranges::find_if(++f3, end, is_space);
|
|
if (f2 == end)
|
|
break;
|
|
f3 = std::ranges::find_if_not(f2 + 1, end, is_space);
|
|
} while (f3 != end);
|
|
}
|
|
|
|
return VR(f1, f2);
|
|
}
|
|
|
|
template <traits::adc_char_range R>
|
|
static auto AdcTrimSpacesView(R&& r, AdcTrimType type = AdcTrimType::TRIM_BOTH)
|
|
{
|
|
return AdcTrimSpacesView<std::string_view>(std::forward<R>(r), type);
|
|
}
|
|
|
|
|
|
template <typename ValueT, traits::adc_char_range R>
|
|
static ValueT AdcFromChars(R&& range)
|
|
{
|
|
using v_t = std::remove_cv_t<ValueT>;
|
|
|
|
|
|
#ifdef _LIBCPP_VERSION // clang's libc++ does not have floating-point overloads for std::from_chars
|
|
if constexpr (traits::adc_is_any_v<v_t, char, int, long, long long, __int128>) {
|
|
#else
|
|
if constexpr (traits::adc_is_any_of_v<v_t, unsigned char, char, unsigned short, short, unsigned long, long,
|
|
unsigned long long, long long, unsigned int, int, unsigned __int128, __int128,
|
|
float, double, long double>) {
|
|
#endif
|
|
v_t v;
|
|
|
|
auto rr = AdcTrimSpaces(range);
|
|
|
|
std::from_chars_result res;
|
|
const char* end_ptr;
|
|
|
|
// std::from_chars needs random-access container!!!
|
|
if constexpr (std::ranges::random_access_range<R>) {
|
|
res = std::from_chars(&*rr.begin(), &*rr.end(), v);
|
|
end_ptr = &*rr.end();
|
|
} else {
|
|
std::string s(rr.begin(), rr.end());
|
|
end_ptr = s.data() + s.size();
|
|
res = std::from_chars(s.data(), s.data() + s.size(), v);
|
|
}
|
|
|
|
if (res.ec == std::errc()) {
|
|
if (res.ptr != end_ptr) {
|
|
throw std::invalid_argument(
|
|
"AdcFromChars: cannot convert char-range to user-type value (invalid argument)");
|
|
}
|
|
|
|
return v;
|
|
} else if (res.ec == std::errc::invalid_argument) {
|
|
throw std::invalid_argument(
|
|
"AdcFromChars: cannot convert char-range to user-type value (invalid argument)");
|
|
} else if (res.ec == std::errc::result_out_of_range) {
|
|
throw std::invalid_argument(
|
|
"AdcFromChars: cannot convert char-range to user-type value (result out of range)");
|
|
}
|
|
#ifdef _LIBCPP_VERSION // clang's libc++ does not have floating-point overloads for std::from_chars
|
|
} else if constexpr (std::is_floating_point_v<v_t>) {
|
|
v_t v;
|
|
|
|
auto rr = AdcTrimSpaces(range);
|
|
|
|
std::string s(rr.begin(), rr.end());
|
|
size_t pos;
|
|
try {
|
|
if constexpr (std::is_same_v<v_t, float>) {
|
|
v = std::stof(s, &pos);
|
|
} else if constexpr (std::is_same_v<v_t, double>) {
|
|
v = std::stod(s, &pos);
|
|
} else if constexpr (std::is_same_v<v_t, long double>) {
|
|
v = std::stold(s, &pos);
|
|
}
|
|
} catch (const std::invalid_argument&) {
|
|
throw std::invalid_argument(
|
|
"AdcFromChars: cannot convert char-range to user-type value (invalid argument)");
|
|
} catch (const std::out_of_range&) {
|
|
throw std::invalid_argument(
|
|
"AdcFromChars: cannot convert char-range to user-type value (result out of range)");
|
|
}
|
|
|
|
if (pos != s.size()) {
|
|
throw std::invalid_argument(
|
|
"AdcFromChars: cannot convert char-range to user-type value (invalid argument)");
|
|
}
|
|
|
|
return v;
|
|
#endif
|
|
} else if constexpr (std::is_constructible_v<ValueT, std::ranges::iterator_t<R>, std::ranges::iterator_t<R>>) {
|
|
return ValueT(range.begin(), range.end());
|
|
} else {
|
|
static_assert(false,
|
|
"AdcFromChars: CANNOT CONVERT CHAR-RANGE TO USER-TYPE VALUE (UNSUPPORTED USER-VALUE TYPE)");
|
|
// throw std::invalid_argument(
|
|
// "AdcFromChars: cannot convert char-range to user-type value (unsupported user-value type)");
|
|
}
|
|
|
|
return ValueT();
|
|
}
|
|
|
|
|
|
/* TRIVIAL SERIALIZER/DESERIALIZER */
|
|
|
|
static const std::regex AdcIntegerRegex("^ *[+-]?\\d+\\d* *$", std::regex::ECMAScript);
|
|
static const std::regex AdcRealRegex("^ *[-+]?\\d+\\d*\\.?\\d*([Ee][-+]?\\d+)? *$", std::regex::ECMAScript);
|
|
|
|
// template <typename SerializedT, traits::formattable VT>
|
|
template <typename SerializedT, typename VT>
|
|
static SerializedT AdcTrivialSerializer(VT&& value)
|
|
{
|
|
using v_t = std::decay_t<VT>;
|
|
|
|
if constexpr (std::is_convertible_v<v_t, SerializedT>) {
|
|
return static_cast<SerializedT>(std::forward<VT>(value));
|
|
} else if constexpr (traits::adc_output_char_range<SerializedT> && traits::adc_input_char_range<v_t>) {
|
|
SerializedT s_val;
|
|
std::ranges::copy(std::forward<VT>(value), std::back_inserter(s_val));
|
|
return s_val;
|
|
} else if constexpr (traits::adc_output_char_range<SerializedT> && traits::formattable<v_t>) {
|
|
SerializedT s_val;
|
|
std::format_to(std::back_inserter(s_val), "{}", std::forward<VT>(value));
|
|
return s_val;
|
|
} else {
|
|
// throw std::invalid_argument("trivial serializer: cannot serialize value");
|
|
static_assert(false, "TRIVIAL SERIALIZER: UNSUPPORTED SERIALIZING TYPE!!!");
|
|
}
|
|
}
|
|
|
|
|
|
template <typename VT, typename SerializedT = std::string>
|
|
static VT AdcTrivialDeserializer(SerializedT&& s_value)
|
|
{
|
|
if constexpr (std::is_convertible_v<SerializedT, VT>) {
|
|
return static_cast<VT>(std::forward<SerializedT>(s_value));
|
|
} else if constexpr (traits::adc_input_char_range<SerializedT>) {
|
|
return AdcFromChars<VT>(std::forward<SerializedT>(s_value));
|
|
} else {
|
|
static_assert(false, "TRIVIAL DESERIALIZER: UNSUPPORTED SERIALIZING TYPE!!!");
|
|
}
|
|
|
|
return VT();
|
|
}
|
|
|
|
|
|
template <std::ranges::range OutputR, traits::adc_input_char_range InputR, traits::adc_input_char_range DelimR>
|
|
static auto AdcValueRangeFromCharRange(OutputR& res,
|
|
InputR&& input_r,
|
|
DelimR&& delim,
|
|
size_t max_len = std::numeric_limits<size_t>::max())
|
|
{
|
|
if (max_len == 0) {
|
|
return res.begin();
|
|
}
|
|
|
|
using value_t = std::ranges::range_value_t<OutputR>;
|
|
|
|
if constexpr (std::is_array_v<std::remove_cvref_t<DelimR>>) {
|
|
std::ranges::copy(std::views::split(std::forward<InputR>(input_r), std::string_view(delim)) |
|
|
std::views::filter([](const auto& r) { return !std::ranges::empty(r); }) |
|
|
std::views::transform([](auto vl) { return AdcTrivialDeserializer<value_t>(vl); }) |
|
|
std::views::take(max_len),
|
|
std::back_inserter(res));
|
|
} else {
|
|
std::ranges::copy(std::views::split(std::forward<InputR>(input_r), std::forward<DelimR>(delim)) |
|
|
std::views::filter([](const auto& r) { return !std::ranges::empty(r); }) |
|
|
std::views::transform([](auto vl) { return AdcTrivialDeserializer<value_t>(vl); }) |
|
|
std::views::take(max_len),
|
|
std::back_inserter(res));
|
|
}
|
|
|
|
return res.begin();
|
|
}
|
|
|
|
|
|
template <traits::adc_output_char_range OutputR, std::ranges::range InputR, traits::adc_input_char_range DelimR>
|
|
static auto AdcCharRangeFromValueRange(OutputR& res,
|
|
InputR&& input_r,
|
|
DelimR&& delim,
|
|
size_t max_len = std::numeric_limits<size_t>::max())
|
|
{
|
|
max_len = max_len < std::ranges::size(input_r) ? max_len : std::ranges::size(input_r);
|
|
|
|
if (max_len == 0) {
|
|
return res.begin();
|
|
}
|
|
|
|
--max_len;
|
|
|
|
if (max_len) {
|
|
auto view = input_r | std::views::take(max_len) | std::views::transform([&delim](auto v) {
|
|
auto r = AdcTrivialSerializer<OutputR>(v);
|
|
std::ranges::copy(delim, std::back_inserter(r));
|
|
return r;
|
|
}) |
|
|
std::views::join;
|
|
|
|
std::ranges::copy(view, std::back_inserter(res));
|
|
|
|
auto view1 = input_r | std::views::drop(max_len) | std::views::take(1) | std::views::transform([](auto v) {
|
|
auto r = AdcTrivialSerializer<OutputR>(v);
|
|
return r;
|
|
}) |
|
|
std::views::join;
|
|
std::ranges::copy(view1, std::back_inserter(res));
|
|
} else {
|
|
res = AdcTrivialSerializer<OutputR>(*std::forward<InputR>(input_r).begin());
|
|
}
|
|
|
|
return res.begin();
|
|
}
|
|
|
|
|
|
namespace details
|
|
{
|
|
|
|
template <size_t I = 0, typename... Ts>
|
|
void setTupleElements(std::tuple<Ts...>& tp, auto&& r)
|
|
{
|
|
if (I < std::ranges::distance(r)) {
|
|
if constexpr (I < sizeof...(Ts)) {
|
|
std::get<I>(tp) =
|
|
AdcFromChars<std::tuple_element_t<I, std::tuple<Ts...>>>(*(r | std::views::drop(I)).begin());
|
|
|
|
setTupleElements<I + 1>(tp, std::forward<decltype(r)>(r));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
template <size_t I = 0, typename... Ts>
|
|
void fromTupleElements(const std::tuple<Ts...>& tp, auto& r, const auto& delim)
|
|
{
|
|
using r_t = std::decay_t<decltype(r)>;
|
|
|
|
if constexpr (I < (sizeof...(Ts) - 1)) {
|
|
auto el = AdcTrivialSerializer<r_t>(std::get<I>(tp));
|
|
std::ranges::copy(el, std::back_inserter(r));
|
|
std::ranges::copy(delim, std::back_inserter(r));
|
|
|
|
fromTupleElements<I + 1>(tp, r, delim);
|
|
} else if constexpr (I == (sizeof...(Ts) - 1)) {
|
|
auto el = AdcTrivialSerializer<r_t>(std::get<I>(tp));
|
|
std::ranges::copy(el, std::back_inserter(r));
|
|
}
|
|
}
|
|
|
|
} // namespace details
|
|
|
|
|
|
template <traits::adc_input_char_range InputR, traits::adc_input_char_range DelimR, typename... Ts>
|
|
static void AdcTupleFromCharRange(std::tuple<Ts...>& tp, InputR&& input_r, DelimR&& delim)
|
|
{
|
|
if constexpr (std::is_array_v<std::remove_cvref_t<DelimR>>) {
|
|
auto r = std::views::split(std::forward<InputR>(input_r), std::string_view(delim)) |
|
|
std::views::filter([](const auto& r) { return !std::ranges::empty(r); });
|
|
details::setTupleElements(tp, r);
|
|
} else {
|
|
auto r = std::views::split(std::forward<InputR>(input_r), std::forward<DelimR>(delim)) |
|
|
std::views::filter([](const auto& r) { return !std::ranges::empty(r); });
|
|
details::setTupleElements(tp, r);
|
|
}
|
|
}
|
|
|
|
|
|
template <traits::adc_output_char_range OutputR, traits::adc_input_char_range DelimR, typename... Ts>
|
|
static auto AdcCharRangeFromTuple(OutputR& res, const std::tuple<Ts...>& tp, DelimR&& delim)
|
|
{
|
|
if constexpr (sizeof...(Ts) > 1) {
|
|
details::fromTupleElements(tp, res, delim);
|
|
} else {
|
|
res = AdcTrivialSerializer<OutputR>(std::get<0>(tp));
|
|
}
|
|
|
|
return res.begin();
|
|
}
|
|
|
|
|
|
namespace details
|
|
{
|
|
|
|
template <typename DerivedT>
|
|
class AdcDelimiter
|
|
{
|
|
public:
|
|
template <traits::adc_input_char_range DT = std::string_view>
|
|
auto& setDelimiter(const DT& delimiter)
|
|
{
|
|
_delimiter = std::string{delimiter.begin(), delimiter.end()};
|
|
|
|
return static_cast<DerivedT&>(*this);
|
|
}
|
|
|
|
auto& setDelimiter(const char* delimiter)
|
|
{
|
|
_delimiter = std::string{delimiter};
|
|
|
|
return static_cast<DerivedT&>(*this);
|
|
}
|
|
|
|
protected:
|
|
template <traits::adc_input_char_range DT = std::string_view>
|
|
AdcDelimiter(const DT& delimiter = ",") : _delimiter(delimiter.begin(), delimiter.end())
|
|
{
|
|
}
|
|
|
|
AdcDelimiter(const char* delimiter = ",") : _delimiter(delimiter) {}
|
|
|
|
virtual ~AdcDelimiter() = default;
|
|
|
|
std::string _delimiter;
|
|
};
|
|
|
|
} // namespace details
|
|
|
|
/*
|
|
*/
|
|
template <traits::adc_output_char_range SerializedT = std::string>
|
|
class AdcDefaultSerializer : public details::AdcDelimiter<AdcDefaultSerializer<SerializedT>>
|
|
{
|
|
std::string _delimiter;
|
|
|
|
using base_t = details::AdcDelimiter<AdcDefaultSerializer<SerializedT>>;
|
|
|
|
public:
|
|
template <typename DT = std::string_view>
|
|
AdcDefaultSerializer(const DT& delimiter = ",") : base_t(delimiter)
|
|
{
|
|
}
|
|
|
|
virtual ~AdcDefaultSerializer() = default;
|
|
|
|
template <typename T>
|
|
SerializedT operator()(const T& value)
|
|
{
|
|
SerializedT res;
|
|
|
|
if constexpr (traits::adc_is_tuple_v<T>) {
|
|
AdcCharRangeFromTuple(res, value, _delimiter);
|
|
} else if constexpr (traits::adc_input_char_range<T>) {
|
|
AdcCharRangeFromValueRange(res, value, _delimiter);
|
|
} else {
|
|
res = AdcTrivialSerializer<SerializedT>(value);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
*/
|
|
template <traits::adc_input_char_range SerializedT = std::string>
|
|
class AdcDefaultDeserializer : public details::AdcDelimiter<AdcDefaultDeserializer<SerializedT>>
|
|
{
|
|
std::string _delimiter;
|
|
|
|
using base_t = details::AdcDelimiter<AdcDefaultSerializer<SerializedT>>;
|
|
|
|
public:
|
|
template <typename DT = std::string_view>
|
|
AdcDefaultDeserializer(const DT& delimiter = ",") : base_t(delimiter)
|
|
{
|
|
}
|
|
|
|
virtual ~AdcDefaultDeserializer() = default;
|
|
|
|
template <typename T>
|
|
T deserialization(const SerializedT& value)
|
|
{
|
|
T res;
|
|
|
|
if constexpr (traits::adc_is_tuple_v<T>) {
|
|
AdcTupleFromCharRange(res, value, _delimiter);
|
|
} else if constexpr (traits::adc_output_char_range<T>) {
|
|
AdcValueRangeFromCharRange(res, value, _delimiter);
|
|
} else {
|
|
res = AdcTrivialDeserializer<T>(value);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
};
|
|
|
|
namespace constants
|
|
{
|
|
|
|
static constexpr char DEFAULT_CONVERTER_DELIMITER[] = " ";
|
|
|
|
} // namespace constants
|
|
|
|
template <const char* DELIMITER = constants::DEFAULT_CONVERTER_DELIMITER>
|
|
class AdcDefaultValueConverter
|
|
{
|
|
public:
|
|
static constexpr std::span compositeValueDelimiter{DELIMITER, AdcCharArrSize(DELIMITER)};
|
|
|
|
template <typename SerializedT, typename ValueT>
|
|
static SerializedT serialize(const ValueT& value)
|
|
{
|
|
SerializedT res;
|
|
|
|
if constexpr (traits::adc_is_tuple_v<ValueT>) {
|
|
AdcCharRangeFromTuple(res, value, compositeValueDelimiter);
|
|
} else if constexpr (std::ranges::range<ValueT> && !traits::adc_input_char_range<ValueT>) {
|
|
AdcCharRangeFromValueRange(res, value, compositeValueDelimiter);
|
|
} else {
|
|
res = AdcTrivialSerializer<SerializedT>(value);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
template <typename ValueT, typename SerializedT>
|
|
static ValueT deserialize(const SerializedT& svalue)
|
|
{
|
|
if constexpr (std::is_void_v<ValueT>) {
|
|
return;
|
|
}
|
|
|
|
ValueT res;
|
|
|
|
if constexpr (traits::adc_is_tuple_v<ValueT>) {
|
|
AdcTupleFromCharRange(res, svalue, compositeValueDelimiter);
|
|
} else if constexpr (std::ranges::range<ValueT> && !traits::adc_output_char_range<ValueT>) {
|
|
AdcValueRangeFromCharRange(res, svalue, compositeValueDelimiter);
|
|
} else {
|
|
res = AdcTrivialDeserializer<ValueT>(svalue);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
};
|
|
|
|
|
|
/* join range elements using delimiter between its elements */
|
|
|
|
// NOTE: C++23 has std::views::join_with adapter but here I use upto C++20!!!
|
|
template <std::ranges::input_range R,
|
|
std::ranges::input_range RD,
|
|
std::ranges::output_range<std::ranges::range_value_t<RD>> RO>
|
|
static size_t AdcJoinRange(const R& r, const RD& delim, RO& ro)
|
|
requires std::ranges::range<std::ranges::range_value_t<R>> && // input R is range of ranges
|
|
std::same_as<std::ranges::range_value_t<std::ranges::range_value_t<R>>, std::ranges::range_value_t<RD>>
|
|
{
|
|
auto N = std::ranges::distance(r.begin(), r.end());
|
|
|
|
if (!N) {
|
|
return 0;
|
|
}
|
|
|
|
size_t i = 0;
|
|
|
|
std::ranges::for_each(r, [&](const auto& el) {
|
|
std::ranges::copy(el, std::back_inserter(ro));
|
|
if (++i < N) {
|
|
std::ranges::copy(delim, std::back_inserter(ro));
|
|
}
|
|
});
|
|
|
|
|
|
return N;
|
|
}
|
|
|
|
|
|
// create a range with views of elements of input range and insert a view of input delimiter between them
|
|
|
|
template <std::ranges::input_range R, std::ranges::input_range RD, std::ranges::range RO>
|
|
static size_t AdcReturnRangeElementsView(const R& r, const RD& delim, RO& ro)
|
|
requires std::ranges::range<std::ranges::range_value_t<R>> && // input R is range of ranges
|
|
std::same_as<std::ranges::range_value_t<std::ranges::range_value_t<R>>, std::ranges::range_value_t<RD>> &&
|
|
std::ranges::view<std::ranges::range_value_t<RO>> && // output RO is range of views
|
|
std::same_as<std::ranges::range_value_t<std::ranges::range_value_t<RO>>, std::ranges::range_value_t<RD>>
|
|
{
|
|
auto N = std::ranges::distance(r.begin(), r.end());
|
|
|
|
if (!N) {
|
|
return 0;
|
|
}
|
|
|
|
size_t i = 0;
|
|
|
|
std::ranges::for_each(r, [&](const auto& el) {
|
|
std::back_inserter(ro) = {el.begin(), el.end()};
|
|
if (++i < N) {
|
|
std::back_inserter(ro) = {delim.begin(), delim.end()};
|
|
}
|
|
});
|
|
|
|
return N;
|
|
}
|
|
|
|
|
|
template <std::ranges::range ResT, traits::adc_char_range R, traits::adc_input_char_range DR>
|
|
requires(std::ranges::contiguous_range<R> && traits::adc_char_view<std::ranges::range_value_t<ResT>>) ||
|
|
traits::adc_output_char_range<std::ranges::range_value_t<ResT>>
|
|
static auto AdcSplitCharRange(R&& r, DR&& delim, size_t start = 0, size_t num = std::numeric_limits<size_t>::max())
|
|
{
|
|
ResT res;
|
|
using el_t = std::ranges::range_value_t<ResT>;
|
|
|
|
if (num == 0) {
|
|
return res;
|
|
}
|
|
|
|
|
|
size_t last_el = num;
|
|
if (start) { // to prevent overflowing
|
|
if (num < (std::numeric_limits<size_t>::max() - start + 1)) {
|
|
last_el = start + num - 1;
|
|
}
|
|
} else {
|
|
if (num < (std::numeric_limits<size_t>::max())) {
|
|
last_el = num - 1;
|
|
}
|
|
}
|
|
|
|
auto begin = std::forward<R>(r).begin();
|
|
auto end = std::forward<R>(r).end();
|
|
|
|
auto it_el = begin;
|
|
auto it_next = begin;
|
|
|
|
size_t i_el = 0;
|
|
|
|
auto prev = std::ranges::search(std::forward<R>(r), std::forward<DR>(delim));
|
|
|
|
do {
|
|
if (prev.begin() != begin) {
|
|
if (i_el >= start) {
|
|
std::back_inserter(res) = el_t(it_el, prev.begin());
|
|
}
|
|
|
|
++i_el;
|
|
if (i_el > last_el) {
|
|
break;
|
|
}
|
|
|
|
it_el = prev.end();
|
|
}
|
|
|
|
if (prev.end() == end) {
|
|
break;
|
|
}
|
|
|
|
it_next = prev.end();
|
|
|
|
auto next = std::ranges::search(it_next, end, std::forward<DR>(delim).begin(), std::forward<DR>(delim).end());
|
|
|
|
while (it_next == next.begin()) {
|
|
it_next = next.end();
|
|
|
|
next = std::ranges::search(it_next, end, std::forward<DR>(delim).begin(), std::forward<DR>(delim).end());
|
|
|
|
if (next.empty()) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
prev = next;
|
|
|
|
} while (true);
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
static constexpr bool AdcCharRangeCompare(const traits::adc_char_view auto& what,
|
|
const traits::adc_char_view auto& where,
|
|
bool case_insensitive = false)
|
|
{
|
|
if (std::ranges::size(what) == std::ranges::size(where)) {
|
|
if (case_insensitive) {
|
|
auto f = std::ranges::search(where,
|
|
std::views::transform(what, [](const char& ch) { return std::tolower(ch); }));
|
|
return !f.empty();
|
|
} else {
|
|
auto f = std::ranges::search(where, what);
|
|
return !f.empty();
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
// FVN-1a hash function
|
|
template <traits::adc_input_char_range R>
|
|
static constexpr size_t AdcFNV1aHash(const R& r)
|
|
{
|
|
static_assert(sizeof(size_t) == 8 || sizeof(size_t) == 4, "ONLY FOR 32 or 64-bit size_t!!!");
|
|
|
|
size_t hash = 0, prime = 0;
|
|
if constexpr (sizeof(size_t) == 8) { // 64-bit
|
|
prime = 1099511628211UL;
|
|
hash = 14695981039346656037UL;
|
|
} else if constexpr (sizeof(size_t) == 4) { // 32-bit
|
|
prime = 16777619;
|
|
hash = 2166136261;
|
|
}
|
|
|
|
std::ranges::for_each(r, [&hash, &prime](const char& ch) {
|
|
hash ^= ch;
|
|
hash *= prime;
|
|
});
|
|
|
|
return hash;
|
|
}
|
|
|
|
|
|
} // namespace adc::utils
|