#pragma once #include #include #include #include #include #include #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 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 requires std::ranges::contiguous_range static auto AdcTrimSpacesView(R&& r, AdcTrimType type = AdcTrimType::TRIM_BOTH) { auto is_space = [](const auto& ch) { return ch == ' '; }; auto end = std::forward(r).end(); auto f1 = std::forward(r).begin(); if (type != AdcTrimType::TRIM_RIGHT) { // look for the first non-space symbol f1 = std::ranges::find_if_not(std::forward(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 static auto AdcTrimSpacesView(R&& r, AdcTrimType type = AdcTrimType::TRIM_BOTH) { return AdcTrimSpacesView(std::forward(r), type); } template static ValueT AdcFromChars(R&& range) { using v_t = std::remove_cv_t; #ifdef _LIBCPP_VERSION // clang's libc++ does not have floating-point overloads for std::from_chars if constexpr (traits::adc_is_any_v) { #else if constexpr (traits::adc_is_any_of_v) { #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) { 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; auto rr = AdcTrimSpaces(range); std::string s(rr.begin(), rr.end()); size_t pos; try { if constexpr (std::is_same_v) { v = std::stof(s, &pos); } else if constexpr (std::is_same_v) { v = std::stod(s, &pos); } else if constexpr (std::is_same_v) { 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, std::ranges::iterator_t>) { 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 template static SerializedT AdcTrivialSerializer(VT&& value) { using v_t = std::decay_t; if constexpr (std::is_convertible_v) { return static_cast(std::forward(value)); } else if constexpr (traits::adc_output_char_range && traits::adc_input_char_range) { SerializedT s_val; std::ranges::copy(std::forward(value), std::back_inserter(s_val)); return s_val; } else if constexpr (traits::adc_output_char_range && traits::formattable) { SerializedT s_val; std::format_to(std::back_inserter(s_val), "{}", std::forward(value)); return s_val; } else { // throw std::invalid_argument("trivial serializer: cannot serialize value"); static_assert(false, "TRIVIAL SERIALIZER: UNSUPPORTED SERIALIZING TYPE!!!"); } } template static VT AdcTrivialDeserializer(SerializedT&& s_value) { if constexpr (std::is_convertible_v) { return static_cast(std::forward(s_value)); } else if constexpr (traits::adc_input_char_range) { return AdcFromChars(std::forward(s_value)); } else { static_assert(false, "TRIVIAL DESERIALIZER: UNSUPPORTED SERIALIZING TYPE!!!"); } return VT(); } template static auto AdcValueRangeFromCharRange(OutputR& res, InputR&& input_r, DelimR&& delim, size_t max_len = std::numeric_limits::max()) { if (max_len == 0) { return res.begin(); } using value_t = std::ranges::range_value_t; if constexpr (std::is_array_v>) { std::ranges::copy(std::views::split(std::forward(input_r), std::string_view(delim)) | std::views::filter([](const auto& r) { return !std::ranges::empty(r); }) | std::views::transform([](auto vl) { return AdcTrivialDeserializer(vl); }) | std::views::take(max_len), std::back_inserter(res)); } else { std::ranges::copy(std::views::split(std::forward(input_r), std::forward(delim)) | std::views::filter([](const auto& r) { return !std::ranges::empty(r); }) | std::views::transform([](auto vl) { return AdcTrivialDeserializer(vl); }) | std::views::take(max_len), std::back_inserter(res)); } return res.begin(); } template static auto AdcCharRangeFromValueRange(OutputR& res, InputR&& input_r, DelimR&& delim, size_t max_len = std::numeric_limits::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(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(v); return r; }) | std::views::join; std::ranges::copy(view1, std::back_inserter(res)); } else { res = AdcTrivialSerializer(*std::forward(input_r).begin()); } return res.begin(); } namespace details { template void setTupleElements(std::tuple& tp, auto&& r) { if (I < std::ranges::distance(r)) { if constexpr (I < sizeof...(Ts)) { std::get(tp) = AdcFromChars>>(*(r | std::views::drop(I)).begin()); setTupleElements(tp, std::forward(r)); } } } template void fromTupleElements(const std::tuple& tp, auto& r, const auto& delim) { using r_t = std::decay_t; if constexpr (I < (sizeof...(Ts) - 1)) { auto el = AdcTrivialSerializer(std::get(tp)); std::ranges::copy(el, std::back_inserter(r)); std::ranges::copy(delim, std::back_inserter(r)); fromTupleElements(tp, r, delim); } else if constexpr (I == (sizeof...(Ts) - 1)) { auto el = AdcTrivialSerializer(std::get(tp)); std::ranges::copy(el, std::back_inserter(r)); } } } // namespace details template static void AdcTupleFromCharRange(std::tuple& tp, InputR&& input_r, DelimR&& delim) { if constexpr (std::is_array_v>) { auto r = std::views::split(std::forward(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(input_r), std::forward(delim)) | std::views::filter([](const auto& r) { return !std::ranges::empty(r); }); details::setTupleElements(tp, r); } } template static auto AdcCharRangeFromTuple(OutputR& res, const std::tuple& tp, DelimR&& delim) { if constexpr (sizeof...(Ts) > 1) { details::fromTupleElements(tp, res, delim); } else { res = AdcTrivialSerializer(std::get<0>(tp)); } return res.begin(); } namespace details { template class AdcDelimiter { public: template auto& setDelimiter(const DT& delimiter) { _delimiter = std::string{delimiter.begin(), delimiter.end()}; return static_cast(*this); } auto& setDelimiter(const char* delimiter) { _delimiter = std::string{delimiter}; return static_cast(*this); } protected: template AdcDelimiter(const DT& delimiter = ",") : _delimiter(delimiter.begin(), delimiter.end()) { } AdcDelimiter(const char* delimiter = ",") : _delimiter(delimiter) {} virtual ~AdcDelimiter() = default; std::string _delimiter; }; } // namespace details /* */ template class AdcDefaultSerializer : public details::AdcDelimiter> { std::string _delimiter; using base_t = details::AdcDelimiter>; public: template AdcDefaultSerializer(const DT& delimiter = ",") : base_t(delimiter) { } virtual ~AdcDefaultSerializer() = default; template SerializedT operator()(const T& value) { SerializedT res; if constexpr (traits::adc_is_tuple_v) { AdcCharRangeFromTuple(res, value, _delimiter); } else if constexpr (traits::adc_input_char_range) { AdcCharRangeFromValueRange(res, value, _delimiter); } else { res = AdcTrivialSerializer(value); } return res; } }; /* */ template class AdcDefaultDeserializer : public details::AdcDelimiter> { std::string _delimiter; using base_t = details::AdcDelimiter>; public: template AdcDefaultDeserializer(const DT& delimiter = ",") : base_t(delimiter) { } virtual ~AdcDefaultDeserializer() = default; template T deserialization(const SerializedT& value) { T res; if constexpr (traits::adc_is_tuple_v) { AdcTupleFromCharRange(res, value, _delimiter); } else if constexpr (traits::adc_output_char_range) { AdcValueRangeFromCharRange(res, value, _delimiter); } else { res = AdcTrivialDeserializer(value); } return res; } }; namespace constants { static constexpr char DEFAULT_CONVERTER_DELIMITER[] = " "; } // namespace constants template class AdcDefaultValueConverter { public: static constexpr std::span compositeValueDelimiter{DELIMITER, AdcCharArrSize(DELIMITER)}; template static SerializedT serialize(const ValueT& value) { SerializedT res; if constexpr (traits::adc_is_tuple_v) { AdcCharRangeFromTuple(res, value, compositeValueDelimiter); } else if constexpr (std::ranges::range && !traits::adc_input_char_range) { AdcCharRangeFromValueRange(res, value, compositeValueDelimiter); } else { res = AdcTrivialSerializer(value); } return res; } template static ValueT deserialize(const SerializedT& svalue) { if constexpr (std::is_void_v) { return; } ValueT res; if constexpr (traits::adc_is_tuple_v) { AdcTupleFromCharRange(res, svalue, compositeValueDelimiter); } else if constexpr (std::ranges::range && !traits::adc_output_char_range) { AdcValueRangeFromCharRange(res, svalue, compositeValueDelimiter); } else { res = AdcTrivialDeserializer(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 > RO> static size_t AdcJoinRange(const R& r, const RD& delim, RO& ro) requires std::ranges::range> && // input R is range of ranges std::same_as>, std::ranges::range_value_t> { 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 static size_t AdcReturnRangeElementsView(const R& r, const RD& delim, RO& ro) requires std::ranges::range> && // input R is range of ranges std::same_as>, std::ranges::range_value_t> && std::ranges::view> && // output RO is range of views std::same_as>, std::ranges::range_value_t> { 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 requires(std::ranges::contiguous_range && traits::adc_char_view>) || traits::adc_output_char_range> static auto AdcSplitCharRange(R&& r, DR&& delim, size_t start = 0, size_t num = std::numeric_limits::max()) { ResT res; using el_t = std::ranges::range_value_t; if (num == 0) { return res; } size_t last_el = num; if (start) { // to prevent overflowing if (num < (std::numeric_limits::max() - start + 1)) { last_el = start + num - 1; } } else { if (num < (std::numeric_limits::max())) { last_el = num - 1; } } auto begin = std::forward(r).begin(); auto end = std::forward(r).end(); auto it_el = begin; auto it_next = begin; size_t i_el = 0; auto prev = std::ranges::search(std::forward(r), std::forward(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(delim).begin(), std::forward(delim).end()); while (it_next == next.begin()) { it_next = next.end(); next = std::ranges::search(it_next, end, std::forward(delim).begin(), std::forward(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 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