diff --git a/include/ylt/struct_json/json_reader.h b/include/ylt/struct_json/json_reader.h index e285cb79a..2649e80c9 100644 --- a/include/ylt/struct_json/json_reader.h +++ b/include/ylt/struct_json/json_reader.h @@ -28,23 +28,23 @@ IGUANA_INLINE void from_json(T &value, It &&it, It &&end, iguana::from_json(value, it, end, ec); } -template +template IGUANA_INLINE void from_json(T &value, const View &view) { iguana::from_json(value, view); } -template +template IGUANA_INLINE void from_json(T &value, const View &view, std::error_code &ec) noexcept { iguana::from_json(value, view, ec); } -template +template IGUANA_INLINE void from_json(T &value, const Byte *data, size_t size) { iguana::from_json(value, data, size); } -template +template IGUANA_INLINE void from_json(T &value, const Byte *data, size_t size, std::error_code &ec) noexcept { iguana::from_json(value, data, size, ec); @@ -61,6 +61,8 @@ IGUANA_INLINE void from_json_file(T &value, const std::string &filename, iguana::from_json_file(value, filename, ec); } +using numeric_str = iguana::numeric_str; + // dom parse using jvalue = iguana::jvalue; using jarray = iguana::jarray; @@ -76,12 +78,12 @@ inline void parse(jvalue &result, It &&it, It &&end, std::error_code &ec) { iguana::parse(result, it, end, ec); } -template +template inline void parse(T &result, const View &view) { iguana::parse(result, view); } -template +template inline void parse(T &result, const View &view, std::error_code &ec) { iguana::parse(result, view, ec); } diff --git a/include/ylt/thirdparty/iguana/define.h b/include/ylt/thirdparty/iguana/define.h index bf42e71fe..1f908fde6 100644 --- a/include/ylt/thirdparty/iguana/define.h +++ b/include/ylt/thirdparty/iguana/define.h @@ -1,5 +1,12 @@ #pragma once +#include +#include +#include +#include +#include +#include + #if defined __clang__ #define IGUANA_INLINE __attribute__((always_inline)) inline #define IGUANA__INLINE_LAMBDA __attribute__((always_inline)) constexpr @@ -10,3 +17,85 @@ #define IGUANA_INLINE __attribute__((always_inline)) inline #define IGUANA__INLINE_LAMBDA constexpr __attribute__((always_inline)) #endif + +#if __has_cpp_attribute(likely) && __has_cpp_attribute(unlikely) +#define IGUANA_LIKELY [[likely]] +#define IGUANA_UNLIKELY [[unlikely]] +#else +#define IGUANA_LIKELY +#define IGUANA_UNLIKELY +#endif + +#ifdef _MSC_VER + +#if _MSC_VER >= 1920 && _MSVC_LANG >= 202002L +IGUANA_INLINE int countr_zero(unsigned long long x) { + return std::countr_zero(x); +} +template +constexpr inline bool contiguous_iterator = std::contiguous_iterator; + +#else +IGUANA_INLINE int countr_zero(unsigned long long x) { + // x will never be zero in iguana + unsigned long pos; + _BitScanForward64(&pos, x); + return pos; +} + +namespace std { +template +using remove_cvref_t = + typename remove_cv::type>::type; +} +template +constexpr inline bool contiguous_iterator = + std::is_same_v, + typename std::vector::iterator> || + std::is_same_v, + typename std::vector::const_iterator> || + std::is_same_v, typename std::string::iterator> || + std::is_same_v, + typename std::string::const_iterator> || + std::is_same_v, char *> || + std::is_same_v, + typename std::string_view::iterator> || + std::is_same_v, + typename std::string_view::const_iterator>; +#endif + +#else + +#if __cplusplus >= 202002L +IGUANA_INLINE int countr_zero(unsigned long long x) { + return std::countr_zero(x); +} +template +constexpr inline bool contiguous_iterator = std::contiguous_iterator; +#else +IGUANA_INLINE int countr_zero(unsigned long long x) { + // x will never be zero in iguana + return __builtin_ctzll(x); +} +namespace std { +template +using remove_cvref_t = + typename remove_cv::type>::type; +} +template +constexpr inline bool contiguous_iterator = + std::is_same_v, + typename std::vector::iterator> || + std::is_same_v, + typename std::vector::const_iterator> || + std::is_same_v, typename std::string::iterator> || + std::is_same_v, + typename std::string::const_iterator> || + std::is_same_v, char *> || + std::is_same_v, + typename std::string_view::iterator> || + std::is_same_v, + typename std::string_view::const_iterator>; +#endif + +#endif diff --git a/include/ylt/thirdparty/iguana/detail/traits.hpp b/include/ylt/thirdparty/iguana/detail/traits.hpp index 35f47c85d..5938f4a7f 100644 --- a/include/ylt/thirdparty/iguana/detail/traits.hpp +++ b/include/ylt/thirdparty/iguana/detail/traits.hpp @@ -12,7 +12,11 @@ #include #include #include + +#include "iguana/define.h" + namespace iguana { + template struct is_signed_intergral_like : std::integral_constant::value) && diff --git a/include/ylt/thirdparty/iguana/error_code.h b/include/ylt/thirdparty/iguana/error_code.h index d9c5b9419..3dd91ceec 100644 --- a/include/ylt/thirdparty/iguana/error_code.h +++ b/include/ylt/thirdparty/iguana/error_code.h @@ -7,7 +7,7 @@ namespace iguana { class iguana_category : public std::error_category { - public: +public: virtual const char *name() const noexcept override { return "iguana::category"; } @@ -24,8 +24,7 @@ class iguana_category : public std::error_category { int add_message(const std::string &msg) { if (auto it = err_map_.find(msg); it != err_map_.end()) { return it->second; - } - else { + } else { err_++; err_map_.emplace(msg, err_); return err_; @@ -55,27 +54,26 @@ enum class dom_errc { }; class iguana_dom_category : public std::error_category { - public: +public: virtual const char *name() const noexcept override { return "iguana::dom_category"; } virtual std::string message(int err_val) const override { switch (static_cast(err_val)) { - case dom_errc::ok: - return "ok"; - case dom_errc::wrong_type: { - auto it = detail_msg_map_.find(dom_errc::wrong_type); - if (it != detail_msg_map_.end()) { - return std::string("wrong type, ") - .append("real type is ") - .append(it->second); - } - else { - return "wrong type"; - } + case dom_errc::ok: + return "ok"; + case dom_errc::wrong_type: { + auto it = detail_msg_map_.find(dom_errc::wrong_type); + if (it != detail_msg_map_.end()) { + return std::string("wrong type, ") + .append("real type is ") + .append(it->second); + } else { + return "wrong type"; } - default: - return "(unrecognized error)"; + } + default: + return "(unrecognized error)"; } } @@ -97,4 +95,4 @@ inline std::error_code make_error_code(iguana::dom_errc err, return std::error_code((int)err, instance); } -} // namespace iguana +} // namespace iguana diff --git a/include/ylt/thirdparty/iguana/json_reader.hpp b/include/ylt/thirdparty/iguana/json_reader.hpp index bb6110e4e..86831b027 100644 --- a/include/ylt/thirdparty/iguana/json_reader.hpp +++ b/include/ylt/thirdparty/iguana/json_reader.hpp @@ -1,24 +1,27 @@ #pragma once -#include - -#include "detail/charconv.h" #include "detail/utf.hpp" #include "error_code.h" #include "json_util.hpp" namespace iguana { -template -void from_json(T &value, It &&it, It &&end); +template , int> = 0> +IGUANA_INLINE void from_json(T &value, It &&it, It &&end); namespace detail { -template +template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end); -template +template , int> = 0> +IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { + from_json(value, it, end); +} + +template , int> = 0> IGUANA_INLINE void parse_escape(U &value, It &&it, It &&end) { if (it == end) - throw std::runtime_error(R"(Expected ")"); + IGUANA_UNLIKELY { throw std::runtime_error(R"(Expected ")"); } if (*it == 'u') { ++it; if (std::distance(it, end) <= 4) @@ -52,89 +55,82 @@ IGUANA_INLINE void parse_escape(U &value, It &&it, It &&end) { } } -template -IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { - from_json(value, it, end); -} - -template +template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { skip_ws(it, end); - - using T = std::remove_reference_t; - - if constexpr (std::contiguous_iterator>) { - if constexpr (std::is_floating_point_v) { - const auto size = std::distance(it, end); - if (size == 0) [[unlikely]] - throw std::runtime_error("Failed to parse number"); - const auto start = &*it; - auto [p, ec] = detail::from_chars(start, start + size, value); - if (ec != std::errc{}) [[unlikely]] - throw std::runtime_error("Failed to parse number"); - it += (p - &*it); - } - else { - const auto size = std::distance(it, end); - const auto start = &*it; - auto [p, ec] = std::from_chars(start, start + size, value); - if (ec != std::errc{}) [[unlikely]] - throw std::runtime_error("Failed to parse number"); - it += (p - &*it); - } + if constexpr (contiguous_iterator>) { + const auto size = std::distance(it, end); + if (size == 0) + IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); } + const auto start = &*it; + auto [p, ec] = detail::from_chars(start, start + size, value); + if (ec != std::errc{}) + IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); } + it += (p - &*it); } else { char buffer[256]; size_t i{}; while (it != end && is_numeric(*it)) { - if (i > 254) [[unlikely]] - throw std::runtime_error("Number is too long"); + if (i > 254) + IGUANA_UNLIKELY { throw std::runtime_error("Number is too long"); } buffer[i] = *it++; ++i; } auto [p, ec] = detail::from_chars(buffer, buffer + i, value); - if (ec != std::errc{}) [[unlikely]] - throw std::runtime_error("Failed to parse number"); + if (ec != std::errc{}) + IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); } } } -template +template , int> = 0> +IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { + skip_ws(it, end); + auto start = it; + while (it != end && is_numeric(*it)) { + ++it; + } + value.value() = + std::string_view(&*start, static_cast(std::distance(start, it))); +} + +template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { using T = std::underlying_type_t>; parse_item(reinterpret_cast(value), it, end); } -template +template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { if constexpr (!skip) { skip_ws(it, end); match<'"'>(it, end); } - if (it == end) [[unlikely]] - throw std::runtime_error("Unxpected end of buffer"); - if (*it == '\\') [[unlikely]] { - if (++it == end) [[unlikely]] { - throw std::runtime_error("Unxpected end of buffer"); - } - else if (*it == 'n') { - value = '\n'; - } - else if (*it == 't') { - value = '\t'; - } - else if (*it == 'r') { - value = '\r'; - } - else if (*it == 'b') { - value = '\b'; - } - else if (*it == 'f') { - value = '\f'; - } - else [[unlikely]] { - value = *it; + if (it == end) + IGUANA_UNLIKELY { throw std::runtime_error("Unxpected end of buffer"); } + if (*it == '\\') + IGUANA_UNLIKELY { + if (++it == end) + IGUANA_UNLIKELY { throw std::runtime_error("Unxpected end of buffer"); } + else if (*it == 'n') { + value = '\n'; + } + else if (*it == 't') { + value = '\t'; + } + else if (*it == 'r') { + value = '\r'; + } + else if (*it == 'b') { + value = '\b'; + } + else if (*it == 'f') { + value = '\f'; + } + else + IGUANA_UNLIKELY { value = *it; } } - } else { value = *it; } @@ -144,41 +140,40 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { } } -template +template , int> = 0> IGUANA_INLINE void parse_item(U &&value, It &&it, It &&end) { skip_ws(it, end); - if (it < end) [[likely]] { - switch (*it) { - case 't': { - ++it; - match<"rue">(it, end); - value = true; - break; - } - case 'f': { - ++it; - match<"alse">(it, end); - value = false; - break; + if (it < end) + IGUANA_LIKELY { + switch (*it) { + case 't': + ++it; + match<'r', 'u', 'e'>(it, end); + value = true; + break; + case 'f': + ++it; + match<'a', 'l', 's', 'e'>(it, end); + value = false; + break; + IGUANA_UNLIKELY default + : throw std::runtime_error("Expected true or false"); } - [[unlikely]] default - : throw std::runtime_error("Expected true or false"); } - } - else [[unlikely]] { - throw std::runtime_error("Expected true or false"); - } + else + IGUANA_UNLIKELY { throw std::runtime_error("Expected true or false"); } } -template +template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { if constexpr (!skip) { skip_ws(it, end); match<'"'>(it, end); } value.clear(); - if constexpr (std::contiguous_iterator>) { + if constexpr (contiguous_iterator>) { auto start = it; while (it < end) { skip_till_escape_or_qoute(it, end); @@ -199,48 +194,42 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { else { while (it != end) { switch (*it) { - [[unlikely]] case '\\' : { - ++it; - parse_escape(value, it, end); - break; - } - [[unlikely]] case ']' : { return; } - [[unlikely]] case '"' : { - ++it; - return; - } - [[likely]] default : { - value.push_back(*it); - ++it; - } + IGUANA_UNLIKELY case '\\' : ++it; + parse_escape(value, it, end); + break; + IGUANA_UNLIKELY case ']' : return; + IGUANA_UNLIKELY case '"' : ++it; + return; + IGUANA_LIKELY default : value.push_back(*it); + ++it; } } } } -template +template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { - static_assert(std::contiguous_iterator>, - "must be contiguous"); + static_assert(contiguous_iterator>, "must be contiguous"); if constexpr (!skip) { skip_ws(it, end); match<'"'>(it, end); } using T = std::decay_t; auto start = it; - while (it < end) { - skip_till_escape_or_qoute(it, end); - if (*it == '"') { + while (it != end) { + skip_till_qoute(it, end); + if (*(it - 1) != '\\') { value = T(&*start, static_cast(std::distance(start, it))); ++it; return; } - it += 2; + ++it; } - throw std::runtime_error("Expected \""); // is needed? + throw std::runtime_error("Expected \""); } -template +template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { using T = std::remove_reference_t; constexpr auto n = sizeof(T) / sizeof(decltype(std::declval()[0])); @@ -252,9 +241,8 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { match<'"'>(it, end); auto value_it = std::begin(value); for (size_t i = 0; i < n; ++i) { - if (*it != '"') [[likely]] { - parse_item(*value_it++, it, end); - } + if (*it != '"') + IGUANA_LIKELY { parse_item(*value_it++, it, end); } } match<'"'>(it, end); return; @@ -262,14 +250,14 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { } match<'['>(it, end); skip_ws(it, end); - if (it == end) { - throw std::runtime_error("Unexpected end"); - } + if (it == end) + IGUANA_UNLIKELY { throw std::runtime_error("Unexpected end"); } - if (*it == ']') [[unlikely]] { - ++it; - return; - } + if (*it == ']') + IGUANA_UNLIKELY { + ++it; + return; + } auto value_it = std::begin(value); for (size_t i = 0; i < n; ++i) { parse_item(*value_it++, it, end); @@ -277,21 +265,22 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { if (it == end) { throw std::runtime_error("Unexpected end"); } - if (*it == ',') [[likely]] { - ++it; - skip_ws(it, end); - } + if (*it == ',') + IGUANA_LIKELY { + ++it; + skip_ws(it, end); + } else if (*it == ']') { ++it; return; } - else [[unlikely]] { - throw std::runtime_error("Expected ]"); - } + else + IGUANA_UNLIKELY { throw std::runtime_error("Expected ]"); } } } -template +template , int>> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { value.clear(); skip_ws(it, end); @@ -299,28 +288,57 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { match<'['>(it, end); skip_ws(it, end); for (size_t i = 0; it != end; ++i) { - if (*it == ']') [[unlikely]] { - ++it; - return; - } - if (i > 0) [[likely]] { - match<','>(it, end); - } - - using value_type = typename std::remove_cv_t::value_type; - if constexpr (refletable) { - from_json(value.emplace_back(), it, end); - } - else { - parse_item(value.emplace_back(), it, end); - } + if (*it == ']') + IGUANA_UNLIKELY { + ++it; + return; + } + if (i > 0) + IGUANA_LIKELY { match<','>(it, end); } + parse_item(value.emplace_back(), it, end); skip_ws(it, end); } throw std::runtime_error("Expected ]"); } -template +template +IGUANA_INLINE auto get_key(It &&it, It &&end) { + if constexpr (contiguous_iterator>) { + // skip white space and escape characters and find the string + skip_ws(it, end); + match<'"'>(it, end); + auto start = it; + skip_till_escape_or_qoute(it, end); + if (*it == '\\') + IGUANA_UNLIKELY { + // we dont' optimize this currently because it would increase binary + // size significantly with the complexity of generating escaped + // compile time versions of keys + it = start; + static thread_local std::string static_key{}; + detail::parse_item(static_key, it, end); + return std::string_view(static_key); + } + else + IGUANA_LIKELY { + auto key = std::string_view{ + &*start, static_cast(std::distance(start, it))}; + ++it; + if (key[0] == '@') + IGUANA_UNLIKELY { return key.substr(1); } + return key; + } + } + else { + static thread_local std::string static_key{}; + detail::parse_item(static_key, it, end); + return std::string_view(static_key); + } +} + +template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { using T = std::remove_reference_t; using key_type = typename T::key_type; @@ -330,23 +348,22 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { skip_ws(it, end); bool first = true; while (it != end) { - if (*it == '}') [[unlikely]] { - ++it; - return; - } - else if (first) [[unlikely]] - first = false; - else [[likely]] { - match<','>(it, end); - } + if (*it == '}') + IGUANA_UNLIKELY { + ++it; + return; + } + else if (first) + IGUANA_UNLIKELY { first = false; } + else + IGUANA_LIKELY { match<','>(it, end); } - static thread_local std::string_view key{}; - parse_item(key, it, end); + std::string_view key = get_key(it, end); skip_ws(it, end); match<':'>(it, end); - if constexpr (str_t || str_view_t) { + if constexpr (string_v || string_view_v) { parse_item(value[key_type(key)], it, end); } else { @@ -358,7 +375,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { } } -template +template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { skip_ws(it, end); match<'['>(it, end); @@ -380,19 +397,17 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { match<']'>(it, end); } -template +template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { skip_ws(it, end); - if (it < end && *it == '"') [[likely]] { - ++it; - } + if (it < end && *it == '"') + IGUANA_LIKELY { ++it; } using T = std::remove_reference_t; - if (it == end) [[unlikely]] { - throw std::runtime_error("Unexexpected eof"); - } + if (it == end) + IGUANA_UNLIKELY { throw std::runtime_error("Unexexpected eof"); } if (*it == 'n') { ++it; - match<"ull">(it, end); + match<'u', 'l', 'l'>(it, end); if constexpr (!std::is_pointer_v) { value.reset(); if (it < end && *it == '"') { @@ -403,7 +418,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { else { using value_type = typename T::value_type; value_type t; - if constexpr (str_t || str_view_t) { + if constexpr (string_v || string_view_v) { parse_item(t, it, end); } else { @@ -413,18 +428,16 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { } } -template +template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { skip_ws(it, end); - if (it < end && *it == '"') [[likely]] { - ++it; - } - if (it == end) [[unlikely]] { - throw std::runtime_error("Unexexpected eof"); - } + if (it < end && *it == '"') + IGUANA_LIKELY { ++it; } + if (it == end) + IGUANA_UNLIKELY { throw std::runtime_error("Unexexpected eof"); } if (*it == 'n') { ++it; - match<"ull">(it, end); + match<'u', 'l', 'l'>(it, end); } else { using value_type = typename std::remove_reference_t::element_type; @@ -433,7 +446,8 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { } } -IGUANA_INLINE void skip_object_value(auto &&it, auto &&end) { +template +IGUANA_INLINE void skip_object_value(It &&it, It &&end) { skip_ws(it, end); while (it != end) { switch (*it) { @@ -461,65 +475,55 @@ IGUANA_INLINE void skip_object_value(auto &&it, auto &&end) { break; } } + } // namespace detail -template +template , int>> IGUANA_INLINE void from_json(T &value, It &&it, It &&end) { skip_ws(it, end); - match<'{'>(it, end); + skip_ws(it, end); - bool first = true; - while (it != end) { - if (*it == '}') [[unlikely]] { + if (*it == '}') + IGUANA_UNLIKELY { ++it; return; } - else if (first) [[unlikely]] - first = false; - else [[likely]] { - match<','>(it, end); - } + std::string_view key = detail::get_key(it, end); +#ifdef SEQUENTIAL_PARSE + bool parse_done = false; + for_each(value, [&](const auto member_ptr, auto i) IGUANA__INLINE_LAMBDA { + constexpr auto mkey = iguana::get_name(); + constexpr std::string_view st_key(mkey.data(), mkey.size()); + if (parse_done || (key != st_key)) + IGUANA_UNLIKELY { return; } + skip_ws(it, end); + match<':'>(it, end); + detail::parse_item(value.*member_ptr, it, end); - if constexpr (refletable) { - std::string_view key; - if constexpr (std::contiguous_iterator>) { - // skip white space and escape characters and find the string - skip_ws(it, end); - match<'"'>(it, end); - auto start = it; - skip_till_escape_or_qoute(it, end); - if (*it == '\\') [[unlikely]] { - // we dont' optimize this currently because it would increase binary - // size significantly with the complexity of generating escaped - // compile time versions of keys - it = start; - static thread_local std::string static_key{}; - detail::parse_item(static_key, it, end); - key = static_key; - } - else [[likely]] { - key = std::string_view{&*start, - static_cast(std::distance(start, it))}; - if (key[0] == '@') [[unlikely]] { - key = key.substr(1); - } - ++it; - } - } - else { - static thread_local std::string static_key{}; - detail::parse_item(static_key, it, end); - key = static_key; + skip_ws(it, end); + if (*it == '}') + IGUANA_UNLIKELY { + ++it; + parse_done = true; + return; } - + else + IGUANA_LIKELY { match<','>(it, end); } + key = detail::get_key(it, end); + }); + if (parse_done) [[unlikely]] { + return; + } +#endif + while (it != end) { + static constexpr auto frozen_map = get_iguana_struct_map(); + if constexpr (frozen_map.size() > 0) { + const auto &member_it = frozen_map.find(key); skip_ws(it, end); match<':'>(it, end); - - static constexpr auto frozen_map = get_iguana_struct_map(); - if constexpr (frozen_map.size() > 0) { - const auto &member_it = frozen_map.find(key); - if (member_it != frozen_map.end()) { + if (member_it != frozen_map.end()) + IGUANA_LIKELY { std::visit( [&](auto &&member_ptr) IGUANA__INLINE_LAMBDA { using V = std::decay_t; @@ -532,20 +536,29 @@ IGUANA_INLINE void from_json(T &value, It &&it, It &&end) { }, member_it->second); } - else [[unlikely]] { + else + IGUANA_UNLIKELY { #ifdef THROW_UNKNOWN_KEY throw std::runtime_error("Unknown key: " + std::string(key)); #else detail::skip_object_value(it, end); #endif } - } } skip_ws(it, end); + if (*it == '}') + IGUANA_UNLIKELY { + ++it; + return; + } + else + IGUANA_LIKELY { match<','>(it, end); } + key = detail::get_key(it, end); } } -template +template , int> = 0> IGUANA_INLINE void from_json(T &value, It &&it, It &&end) { detail::parse_item(value, it, end); } @@ -561,12 +574,14 @@ IGUANA_INLINE void from_json(T &value, It &&it, It &&end, } } -template +template , int> = 0> IGUANA_INLINE void from_json(T &value, const View &view) { from_json(value, std::begin(view), std::end(view)); } -template +template , int> = 0> IGUANA_INLINE void from_json(T &value, const View &view, std::error_code &ec) noexcept { try { @@ -577,13 +592,15 @@ IGUANA_INLINE void from_json(T &value, const View &view, } } -template +template , int> = 0> IGUANA_INLINE void from_json(T &value, const Byte *data, size_t size) { std::string_view buffer(data, size); from_json(value, buffer); } -template +template , int> = 0> IGUANA_INLINE void from_json(T &value, const Byte *data, size_t size, std::error_code &ec) noexcept { try { @@ -601,10 +618,11 @@ template inline void parse_array(jarray &result, It &&it, It &&end) { skip_ws(it, end); match<'['>(it, end); - if (*it == ']') [[unlikely]] { - ++it; - return; - } + if (*it == ']') + IGUANA_UNLIKELY { + ++it; + return; + } while (true) { if (it == end) { break; @@ -613,10 +631,11 @@ inline void parse_array(jarray &result, It &&it, It &&end) { parse(result.back(), it, end); - if (*it == ']') [[unlikely]] { - ++it; - return; - } + if (*it == ']') + IGUANA_UNLIKELY { + ++it; + return; + } match<','>(it, end); } @@ -627,10 +646,11 @@ template inline void parse_object(jobject &result, It &&it, It &&end) { skip_ws(it, end); match<'{'>(it, end); - if (*it == '}') [[unlikely]] { - ++it; - return; - } + if (*it == '}') + IGUANA_UNLIKELY { + ++it; + return; + } skip_ws(it, end); @@ -649,10 +669,11 @@ inline void parse_object(jobject &result, It &&it, It &&end) { parse(emplaced.first->second, it, end); - if (*it == '}') [[unlikely]] { - ++it; - return; - } + if (*it == '}') + IGUANA_UNLIKELY { + ++it; + return; + } match<','>(it, end); } @@ -663,7 +684,8 @@ inline void parse(jvalue &result, It &&it, It &&end) { skip_ws(it, end); switch (*it) { case 'n': - match<"null">(it, end); + ++it; + match<'u', 'l', 'l'>(it, end); result.template emplace(); break; @@ -727,12 +749,14 @@ inline void parse(jvalue &result, It &&it, It &&end, std::error_code &ec) { } } -template +template , int> = 0> inline void parse(T &result, const View &view) { parse(result, std::begin(view), std::end(view)); } -template +template , int> = 0> inline void parse(T &result, const View &view, std::error_code &ec) noexcept { try { parse(result, view); @@ -742,14 +766,6 @@ inline void parse(T &result, const View &view, std::error_code &ec) noexcept { } } -template -IGUANA_INLINE void from_json(T &value, It &&it, It &&end) { - static_assert(!sizeof(T), - "The type is not support, please check if you have " - "defined REFLECTION for the type, otherwise the " - "type is not supported now!"); -} - IGUANA_INLINE std::string json_file_content(const std::string &filename) { std::error_code ec; uint64_t size = std::filesystem::file_size(filename, ec); diff --git a/include/ylt/thirdparty/iguana/json_util.hpp b/include/ylt/thirdparty/iguana/json_util.hpp index ab02133ab..bd28f3333 100644 --- a/include/ylt/thirdparty/iguana/json_util.hpp +++ b/include/ylt/thirdparty/iguana/json_util.hpp @@ -5,7 +5,6 @@ #include -#include #include #include #include @@ -15,152 +14,149 @@ #include #include "define.h" +#include "detail/charconv.h" #include "reflection.hpp" #include "value.hpp" namespace iguana { -template -concept char_t = std::same_as < std::decay_t, -char > || std::same_as, char16_t> || - std::same_as, char32_t> || - std::same_as, wchar_t>; - -template -concept bool_t = std::same_as < std::decay_t, -bool > || std::same_as, std::vector::reference>; - -template -concept int_t = - std::integral> && !char_t> && !bool_t; +template +inline constexpr bool char_v = std::is_same_v, char> || + std::is_same_v, char16_t> || + std::is_same_v, char32_t> || + std::is_same_v, wchar_t>; -template -concept float_t = std::floating_point>; +template +inline constexpr bool bool_v = + std::is_same_v, bool> || + std::is_same_v, std::vector::reference>; -template -concept num_t = std::floating_point> || int_t; +template +inline constexpr bool int_v = std::is_integral_v> && + !char_v> && !bool_v; -template -concept enum_t = std::is_enum_v>; +template +inline constexpr bool float_v = std::is_floating_point_v>; -template -concept arithmetic_t = std::is_arithmetic_v>; +template +inline constexpr bool num_v = float_v || int_v; template -constexpr inline bool is_basic_string_view = false; +inline constexpr bool enum_v = std::is_enum_v>; template -constexpr inline bool is_basic_string_view> = true; +constexpr inline bool optional_v = + is_template_instant_of>::value; -template -constexpr inline bool is_std_vector_v = false; +template +struct is_container : std::false_type {}; -template -constexpr inline bool is_std_vector_v> = true; +template +struct is_container< + T, std::void_t().size(), std::declval().begin(), + std::declval().end())>> : std::true_type {}; -template -concept vector_container = is_std_vector_v>; +template +struct is_map_container : std::false_type {}; -template -concept optional = requires(Type optional) { - optional.value(); - optional.has_value(); - optional.operator*(); - typename std::remove_cvref_t::value_type; -}; +template +struct is_map_container< + T, std::void_t())>> + : is_container {}; -template -concept container = requires(Type container) { - typename std::remove_cvref_t::value_type; - container.size(); - container.begin(); - container.end(); -}; +template +constexpr inline bool container_v = is_container>::value; -template -concept map_container = container && requires(Type container) { - typename std::remove_cvref_t::mapped_type; -}; +template +constexpr inline bool map_container_v = + is_map_container>::value; template -concept c_array = std::is_array_v> && - std::extent_v> > -0; +constexpr inline bool c_array_v = std::is_array_v> && + std::extent_v> > 0; -template -concept array = requires(Type arr) { - arr.size(); - std::tuple_size>{}; -}; - -template -concept fixed_array = c_array || array; +template +struct is_array : std::false_type {}; template -concept str_view_t = is_basic_string_view>; +struct is_array< + T, std::void_t().size()), + typename std::enable_if_t<(std::tuple_size::value != 0)>>> + : std::true_type {}; -// eliminate char a[] -template -concept str_t = - std::convertible_to, std::string_view> && !str_view_t && - !c_array; +template +constexpr inline bool array_v = is_array>::value; template -concept tuple = !array && requires(Type tuple) { - std::get<0>(tuple); - sizeof(std::tuple_size>); -}; +constexpr inline bool fixed_array_v = c_array_v || array_v; -template -concept json_view = requires(Type container) { - container.size(); - container.begin(); - container.end(); -}; +template +constexpr inline bool string_view_v = + is_template_instant_of>::value; template -concept json_byte = std::is_same_v || - std::is_same_v || std::is_same_v; +constexpr inline bool string_v = + is_template_instant_of>::value; -template -constexpr inline bool is_std_list_v = false; -template -constexpr inline bool is_std_list_v> = true; +// TODO: type must be char +template +constexpr inline bool json_view_v = container_v; -template -constexpr inline bool is_std_deque_v = false; -template -constexpr inline bool is_std_deque_v> = true; +template +constexpr inline bool json_byte_v = + std::is_same_v> || + std::is_same_v> || + std::is_same_v>; -template -concept sequence_container = is_std_list_v> || - is_std_vector_v> || - is_std_deque_v>; +template +constexpr inline bool sequence_container_v = + is_sequence_container>::value; -template -concept associat_container_t = - is_associat_container>::value; +template +constexpr inline bool tuple_v = is_tuple>::value; -template -concept sequence_container_t = - is_sequence_container>::value; +template +constexpr inline bool string_container_v = string_v || string_view_v; -template -concept tuple_t = is_tuple>::value; +template +constexpr inline bool unique_ptr_v = + is_template_instant_of>::value; template -concept string_container_t = str_t || str_view_t; +constexpr inline bool non_refletable_v = + container_v || c_array_v || tuple_v || optional_v || + unique_ptr_v || std::is_fundamental_v>; -template -concept unique_ptr_t = requires(Type ptr) { - ptr.operator*(); - typename std::remove_cvref_t::element_type; -} -&&!requires(Type ptr, Type ptr2) { ptr = ptr2; }; +template +constexpr inline bool refletable_v = is_reflection_v>; + +class numeric_str { + public: + std::string_view &value() { return val_; } + std::string_view value() const { return val_; } + template + T convert() { + static_assert(num_v, "T must be numeric type"); + if (val_.empty()) [[unlikely]] { + throw std::runtime_error("Failed to parse number"); + } + T res; + auto [_, ec] = + detail::from_chars(val_.data(), val_.data() + val_.size(), res); + if (ec != std::errc{}) [[unlikely]] { + throw std::runtime_error("Failed to parse number"); + } + return res; + } -template -concept non_refletable = container || c_array || tuple || - optional || unique_ptr_t || std::is_fundamental_v; + private: + std::string_view val_; +}; + +template +constexpr inline bool numeric_str_v = + std::is_same_v>; template struct string_literal { @@ -176,66 +172,56 @@ struct string_literal { constexpr const std::string_view sv() const noexcept { return {value, size}; } }; -template -IGUANA_INLINE void match(auto &&it, auto &&end) { - if (it == end || *it != c) [[unlikely]] { - static constexpr char b[] = {c, '\0'}; - // static constexpr auto error = concat_arrays("Expected:", b); - std::string error = std::string("Expected:").append(b); - throw std::runtime_error(error); - } - else [[likely]] { - ++it; - } -} - -template -IGUANA_INLINE void match(auto &&it, auto &&end) { +template +IGUANA_INLINE void match(It &&it, It &&end) { const auto n = static_cast(std::distance(it, end)); - if (n < str.size) [[unlikely]] { - // TODO: compile time generate this message, currently borken with - // MSVC - static constexpr auto error = "Unexpected end of buffer. Expected:"; - throw std::runtime_error(error); - } - size_t i{}; - // clang and gcc will vectorize this loop - for (auto *c = str.value; c < str.end(); ++it, ++c) { - i += *it != *c; - } - if (i != 0) [[unlikely]] { - // TODO: compile time generate this message, currently borken with - // MSVC - static constexpr auto error = "Expected: "; - throw std::runtime_error(error); - } + if (n < sizeof...(C)) + IGUANA_UNLIKELY { + // TODO: compile time generate this message, currently borken with + // MSVC + static constexpr auto error = "Unexpected end of buffer. Expected:"; + throw std::runtime_error(error); + } + if (((... || (*it++ != C)))) + IGUANA_UNLIKELY { + // TODO: compile time generate this message, currently borken with + // MSVC + static constexpr char b[] = {C..., '\0'}; + throw std::runtime_error(std::string("Expected these: ").append(b)); + } } -IGUANA_INLINE void skip_comment(auto &&it, auto &&end) { +template +IGUANA_INLINE void skip_comment(It &&it, It &&end) { ++it; - if (it == end) [[unlikely]] - throw std::runtime_error("Unexpected end, expected comment"); + if (it == end) + IGUANA_UNLIKELY { + throw std::runtime_error("Unexpected end, expected comment"); + } else if (*it == '/') { while (++it != end && *it != '\n') ; } else if (*it == '*') { while (++it != end) { - if (*it == '*') [[unlikely]] { - if (++it == end) [[unlikely]] - break; - else if (*it == '/') [[likely]] { - ++it; - break; + if (*it == '*') + IGUANA_UNLIKELY { + if (++it == end) + IGUANA_UNLIKELY { break; } + else if (*it == '/') + IGUANA_LIKELY { + ++it; + break; + } } - } } } - else [[unlikely]] - throw std::runtime_error("Expected / or * after /"); + else + IGUANA_UNLIKELY throw std::runtime_error("Expected / or * after /"); } -IGUANA_INLINE void skip_ws(auto &&it, auto &&end) { +template +IGUANA_INLINE void skip_ws(It &&it, It &&end) { while (it != end) { // assuming ascii if (static_cast(*it) < 33) { @@ -250,48 +236,49 @@ IGUANA_INLINE void skip_ws(auto &&it, auto &&end) { } } -IGUANA_INLINE void skip_ws_no_comments(auto &&it, auto &&end) { +template +IGUANA_INLINE void skip_ws_no_comments(It &&it, It &&end) { while (it != end) { // assuming ascii - if (static_cast(*it) < 33) [[likely]] { - ++it; - } + if (static_cast(*it) < 33) + IGUANA_LIKELY { ++it; } else { break; } } } -IGUANA_INLINE void skip_till_escape_or_qoute(auto &&it, auto &&end) { - static_assert(std::contiguous_iterator>); - - auto has_zero = [](uint64_t chunk) { - return (((chunk - 0x0101010101010101) & ~chunk) & 0x8080808080808080); - }; +inline constexpr auto has_zero = [](uint64_t chunk) IGUANA__INLINE_LAMBDA { + return (((chunk - 0x0101010101010101) & ~chunk) & 0x8080808080808080); +}; - auto has_qoute = [&](uint64_t chunk) IGUANA__INLINE_LAMBDA { - return has_zero( - chunk ^ - 0b0010001000100010001000100010001000100010001000100010001000100010); - }; +inline constexpr auto has_qoute = [](uint64_t chunk) IGUANA__INLINE_LAMBDA { + return has_zero( + chunk ^ + 0b0010001000100010001000100010001000100010001000100010001000100010); +}; - auto has_escape = [&](uint64_t chunk) IGUANA__INLINE_LAMBDA { - return has_zero( - chunk ^ - 0b0101110001011100010111000101110001011100010111000101110001011100); - }; +inline constexpr auto has_escape = [](uint64_t chunk) IGUANA__INLINE_LAMBDA { + return has_zero( + chunk ^ + 0b0101110001011100010111000101110001011100010111000101110001011100); +}; - if (std::distance(it, end) >= 7) [[likely]] { - const auto end_m7 = end - 7; - for (; it < end_m7; it += 8) { - const auto chunk = *reinterpret_cast(&*it); - uint64_t test = has_qoute(chunk) | has_escape(chunk); - if (test != 0) { - it += (std::countr_zero(test) >> 3); - return; +template +IGUANA_INLINE void skip_till_escape_or_qoute(It &&it, It &&end) { + static_assert(contiguous_iterator>); + if (std::distance(it, end) >= 7) + IGUANA_LIKELY { + const auto end_m7 = end - 7; + for (; it < end_m7; it += 8) { + const auto chunk = *reinterpret_cast(&*it); + uint64_t test = has_qoute(chunk) | has_escape(chunk); + if (test != 0) { + it += (countr_zero(test) >> 3); + return; + } } } - } // Tail end of buffer. Should be rare we even get here while (it < end) { @@ -305,21 +292,48 @@ IGUANA_INLINE void skip_till_escape_or_qoute(auto &&it, auto &&end) { throw std::runtime_error("Expected \""); } -IGUANA_INLINE void skip_string(auto &&it, auto &&end) noexcept { +template +IGUANA_INLINE void skip_till_qoute(It &&it, It &&end) { + static_assert(contiguous_iterator>); + if (std::distance(it, end) >= 7) + IGUANA_LIKELY { + const auto end_m7 = end - 7; + for (; it < end_m7; it += 8) { + const auto chunk = *reinterpret_cast(&*it); + uint64_t test = has_qoute(chunk); + if (test != 0) { + it += (countr_zero(test) >> 3); + return; + } + } + } + + // Tail end of buffer. Should be rare we even get here + while (it < end) { + if (*it == '"') + return; + ++it; + } + throw std::runtime_error("Expected \""); +} + +template +IGUANA_INLINE void skip_string(It &&it, It &&end) noexcept { ++it; while (it < end) { if (*it == '"') { ++it; break; } - else if (*it == '\\' && ++it == end) [[unlikely]] - break; + else if (*it == '\\' && ++it == end) + IGUANA_UNLIKELY + break; ++it; } } -template -IGUANA_INLINE void skip_until_closed(auto &&it, auto &&end) { +template +IGUANA_INLINE void skip_until_closed(It &&it, It &&end) { ++it; size_t open_count = 1; size_t close_count = 0; @@ -345,41 +359,27 @@ IGUANA_INLINE void skip_until_closed(auto &&it, auto &&end) { } } -IGUANA_INLINE constexpr bool is_numeric(const auto c) noexcept { - switch (c) { - case '0': - case '1': - case '2': - case '3': // - case '4': - case '5': - case '6': - case '7': // - case '8': - case '9': // - case '.': - case '+': - case '-': // - case 'e': - case 'E': // - return true; - } - return false; +IGUANA_INLINE bool is_numeric(char c) noexcept { + static constexpr int is_num[256] = { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 3 + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F + }; + return static_cast(is_num[static_cast(c)]); } -constexpr bool is_digit(char c) { return c <= '9' && c >= '0'; } - -constexpr size_t stoui(std::string_view s, size_t value = 0) { - if (s.empty()) { - return value; - } - - else if (is_digit(s[0])) { - return stoui(s.substr(1), (s[0] - '0') + value * 10); - } - - else { - throw std::runtime_error("not a digit"); - } -} } // namespace iguana diff --git a/include/ylt/thirdparty/iguana/json_writer.hpp b/include/ylt/thirdparty/iguana/json_writer.hpp index 38982e4ca..40f80de30 100644 --- a/include/ylt/thirdparty/iguana/json_writer.hpp +++ b/include/ylt/thirdparty/iguana/json_writer.hpp @@ -4,25 +4,27 @@ #ifndef SERIALIZE_JSON_HPP #define SERIALIZE_JSON_HPP -#include "define.h" -#include "detail/charconv.h" #include "json_util.hpp" namespace iguana { -template +template , int> = 0> IGUANA_INLINE void to_json(T &&t, Stream &s); -template +template , int> = 0> IGUANA_INLINE void render_json_value(Stream &ss, const T &v); -template +template , int> = 0> IGUANA_INLINE void render_json_value(Stream &ss, const T &v); -template +template , int> = 0> IGUANA_INLINE void render_json_value(Stream &ss, const T &o); -template +template , int> = 0> IGUANA_INLINE void render_json_value(Stream &ss, const T &v); template @@ -55,38 +57,47 @@ IGUANA_INLINE void render_json_value(Stream &ss, char value) { ss.append("\""); } -template +template , int> = 0> IGUANA_INLINE void render_json_value(Stream &ss, T value) { char temp[65]; auto p = detail::to_chars(temp, value); ss.append(temp, p - temp); } -template +template , int> = 0> +IGUANA_INLINE void render_json_value(Stream &ss, T v) { + ss.append(v.value().data(), v.value().size()); +} + +template , int> = 0> IGUANA_INLINE void render_json_value(Stream &ss, T &&t) { ss.push_back('"'); ss.append(t.data(), t.size()); ss.push_back('"'); } -template +template , int> = 0> IGUANA_INLINE void render_key(Stream &ss, T &t) { ss.push_back('"'); render_json_value(ss, t); ss.push_back('"'); } -template +template , int> = 0> IGUANA_INLINE void render_key(Stream &ss, T &&t) { render_json_value(ss, std::forward(t)); } -template +template , int> = 0> IGUANA_INLINE void render_json_value(Stream &ss, T &&t) { to_json(std::forward(t), ss); } -template +template , int> = 0> IGUANA_INLINE void render_json_value(Stream &ss, T val) { render_json_value(ss, static_cast>(val)); } @@ -111,7 +122,8 @@ IGUANA_INLINE void render_array(Stream &ss, const T &v) { ss.push_back(']'); } -template +template , int> = 0> IGUANA_INLINE void render_json_value(Stream &ss, const T &t) { if constexpr (std::is_same_v()[0])>>) { @@ -133,7 +145,8 @@ IGUANA_INLINE void render_json_value(Stream &ss, const T &t) { } } -template +template , int>> IGUANA_INLINE void render_json_value(Stream &ss, const T &o) { ss.push_back('{'); join(ss, o.cbegin(), o.cend(), ',', @@ -145,7 +158,8 @@ IGUANA_INLINE void render_json_value(Stream &ss, const T &o) { ss.push_back('}'); } -template +template , int>> IGUANA_INLINE void render_json_value(Stream &ss, const T &v) { ss.push_back('['); join(ss, v.cbegin(), v.cend(), ',', @@ -158,14 +172,13 @@ IGUANA_INLINE void render_json_value(Stream &ss, const T &v) { constexpr auto write_json_key = [](auto &s, auto i, auto &t) IGUANA__INLINE_LAMBDA { s.push_back('"'); - constexpr auto name = - get_name(); // will be replaced by string_view later + // will be replaced by string_view later + constexpr auto name = get_name(); s.append(name.data(), name.size()); s.push_back('"'); }; -template +template , int>> IGUANA_INLINE void render_json_value(Stream &ss, const T &v) { if (v) { render_json_value(ss, *v); @@ -175,7 +188,7 @@ IGUANA_INLINE void render_json_value(Stream &ss, const T &v) { } } -template +template , int> = 0> IGUANA_INLINE void render_json_value(Stream &s, T &&t) { using U = typename std::decay_t; s.push_back('['); @@ -184,13 +197,13 @@ IGUANA_INLINE void render_json_value(Stream &s, T &&t) { [&s, size](auto &v, auto i) IGUANA__INLINE_LAMBDA { render_json_value(s, v); - if (i != size - 1) [[likely]] - s.push_back(','); + if (i != size - 1) + IGUANA_LIKELY { s.push_back(','); } }); s.push_back(']'); } -template +template , int>> IGUANA_INLINE void to_json(T &&t, Stream &s) { s.push_back('{'); for_each(std::forward(t), @@ -216,7 +229,8 @@ IGUANA_INLINE void to_json(T &&t, Stream &s) { s.push_back('}'); } -template +template , int> = 0> IGUANA_INLINE void to_json(T &&t, Stream &s) { render_json_value(s, t); } diff --git a/include/ylt/thirdparty/iguana/prettify.hpp b/include/ylt/thirdparty/iguana/prettify.hpp index 384349daf..4998381e5 100644 --- a/include/ylt/thirdparty/iguana/prettify.hpp +++ b/include/ylt/thirdparty/iguana/prettify.hpp @@ -13,8 +13,9 @@ enum class general_state : uint32_t { BEFORE_FSLASH }; -inline void prettify_normal_state(const char c, auto &out, uint32_t &indent, - auto nl, general_state &state) noexcept { +template +inline void prettify_normal_state(const char c, Out &out, uint32_t &indent, + NL nl, general_state &state) noexcept { switch (c) { case ',': out += c; @@ -101,7 +102,8 @@ inline void prettify_other_states(const char c, general_state &state) noexcept { /// /// pretty print a JSON string /// -inline void prettify(const auto &in, auto &out, const bool tabs = false, +template +inline void prettify(const In &in, Out &out, const bool tabs = false, const uint32_t indent_size = 3) noexcept { out.reserve(in.size()); uint32_t indent{}; @@ -132,7 +134,8 @@ inline void prettify(const auto &in, auto &out, const bool tabs = false, /// /// allocating version of prettify /// -inline std::string prettify(const auto &in, const bool tabs = false, +template +inline std::string prettify(const In &in, const bool tabs = false, const uint32_t indent_size = 3) noexcept { std::string out{}; prettify(in, out, tabs, indent_size); diff --git a/include/ylt/thirdparty/iguana/reflection.hpp b/include/ylt/thirdparty/iguana/reflection.hpp index 5712c786b..ee626f307 100644 --- a/include/ylt/thirdparty/iguana/reflection.hpp +++ b/include/ylt/thirdparty/iguana/reflection.hpp @@ -551,28 +551,35 @@ namespace iguana::detail { #define MAKE_STR_LIST(...) \ MACRO_CONCAT(CON_STR, GET_ARG_COUNT(__VA_ARGS__))(__VA_ARGS__) -#define MAKE_META_DATA_IMPL(STRUCT_NAME, ...) \ - inline auto iguana_reflect_members(STRUCT_NAME const &) { \ - struct reflect_members { \ - constexpr decltype(auto) static apply_impl() { \ - return std::make_tuple(__VA_ARGS__); \ - } \ - using size_type = \ - std::integral_constant; \ - constexpr static std::string_view name() { \ - return std::string_view(#STRUCT_NAME, sizeof(#STRUCT_NAME) - 1); \ - } \ - constexpr static size_t value() { return size_type::value; } \ - constexpr static std::array arr() { \ - return arr_##STRUCT_NAME; \ - } \ - }; \ - return reflect_members{}; \ +#define MAKE_META_DATA_IMPL(STRUCT_NAME, ...) \ + inline auto iguana_reflect_members(STRUCT_NAME const &) { \ + struct reflect_members { \ + constexpr decltype(auto) static apply_impl() { \ + return std::make_tuple(__VA_ARGS__); \ + } \ + using size_type = \ + std::integral_constant; \ + constexpr static std::string_view name() { return name_##STRUCT_NAME; } \ + constexpr static std::string_view struct_name() { \ + return std::string_view(#STRUCT_NAME, sizeof(#STRUCT_NAME) - 1); \ + } \ + constexpr static std::string_view fields() { \ + return fields_##STRUCT_NAME; \ + } \ + constexpr static size_t value() { return size_type::value; } \ + constexpr static std::array arr() { \ + return arr_##STRUCT_NAME; \ + } \ + }; \ + return reflect_members{}; \ } -#define MAKE_META_DATA(STRUCT_NAME, N, ...) \ +#define MAKE_META_DATA(STRUCT_NAME, TABLE_NAME, N, ...) \ constexpr inline std::array arr_##STRUCT_NAME = { \ MARCO_EXPAND(MACRO_CONCAT(CON_STR, N)(__VA_ARGS__))}; \ + constexpr inline std::string_view fields_##STRUCT_NAME = { \ + MAKE_NAMES(__VA_ARGS__)}; \ + constexpr inline std::string_view name_##STRUCT_NAME = TABLE_NAME; \ MAKE_META_DATA_IMPL(STRUCT_NAME, \ MAKE_ARG_LIST(N, &STRUCT_NAME::FIELD, __VA_ARGS__)) @@ -623,6 +630,10 @@ inline constexpr auto get_iguana_struct_map_impl( } // namespace iguana::detail namespace iguana { +#define REFLECTION_WITH_NAME(STRUCT_NAME, TABLE_NAME, ...) \ + MAKE_META_DATA(STRUCT_NAME, TABLE_NAME, GET_ARG_COUNT(__VA_ARGS__), \ + __VA_ARGS__) + inline std::unordered_map< std::string_view, std::vector>> @@ -640,8 +651,9 @@ inline constexpr auto get_iguana_struct_map() { } } -#define REFLECTION(STRUCT_NAME, ...) \ - MAKE_META_DATA(STRUCT_NAME, GET_ARG_COUNT(__VA_ARGS__), __VA_ARGS__) +#define REFLECTION(STRUCT_NAME, ...) \ + MAKE_META_DATA(STRUCT_NAME, #STRUCT_NAME, GET_ARG_COUNT(__VA_ARGS__), \ + __VA_ARGS__) #define REFLECTION_EMPTY(STRUCT_NAME) MAKE_META_DATA_EMPTY(STRUCT_NAME) @@ -704,6 +716,12 @@ inline int add_custom_fields(std::string_view key, return 0; } +#ifdef _MSC_VER +#define IGUANA_UNIQUE_VARIABLE(str) MACRO_CONCAT(str, __COUNTER__) +#else +#define IGUANA_UNIQUE_VARIABLE(str) MACRO_CONCAT(str, __LINE__) +#endif + #define CUSTOM_FIELDS_IMPL(STRUCT_NAME, N, ...) \ inline auto IGUANA_UNIQUE_VARIABLE(STRUCT_NAME) = iguana::add_custom_fields( \ #STRUCT_NAME, {MARCO_EXPAND(MACRO_CONCAT(CON_STR, N)(__VA_ARGS__))}); @@ -748,11 +766,6 @@ constexpr int tuple_element_index() { return element_index_helper<0, Condition, Tuple, T>(); } -#if _MSC_VER || (__cplusplus >= 201402L) -template -concept refletable = is_reflection_v>; -#endif - template constexpr decltype(auto) get(T &&t) { using M = decltype(iguana_reflect_members(std::forward(t))); @@ -815,6 +828,12 @@ constexpr const std::string_view get_name() { return M::name(); } +template +constexpr const std::string_view get_fields() { + using M = Reflect_members; + return M::fields(); +} + template constexpr std::enable_if_t::value, size_t> get_value() { using M = decltype(iguana_reflect_members(std::declval())); @@ -912,4 +931,4 @@ constexpr std::enable_if_t>::value> for_each(T &&t, std::make_index_sequence{}); } } // namespace iguana -#endif // IGUANA_REFLECTION_HPP \ No newline at end of file +#endif // IGUANA_REFLECTION_HPP diff --git a/include/ylt/thirdparty/iguana/xml_reader.hpp b/include/ylt/thirdparty/iguana/xml_reader.hpp index a65e1f549..b4477d09a 100644 --- a/include/ylt/thirdparty/iguana/xml_reader.hpp +++ b/include/ylt/thirdparty/iguana/xml_reader.hpp @@ -297,7 +297,7 @@ IGUANA_INLINE void parse_item(T &value, It &&it, It &&end, if constexpr (cdata_t) { return; } - if (key != st_key) [[unlikely]] { + if (parse_done || key != st_key) [[unlikely]] { return; } if constexpr (!cdata_t) { diff --git a/include/ylt/thirdparty/iguana/xml_util.hpp b/include/ylt/thirdparty/iguana/xml_util.hpp index bff042ada..5b92af28b 100644 --- a/include/ylt/thirdparty/iguana/xml_util.hpp +++ b/include/ylt/thirdparty/iguana/xml_util.hpp @@ -63,6 +63,9 @@ template concept sequence_container_t = is_sequence_container>::value; +template +concept refletable = is_reflection_v>; + template concept unique_ptr_t = requires(Type ptr) { ptr.operator*(); diff --git a/include/ylt/thirdparty/iguana/yaml_util.hpp b/include/ylt/thirdparty/iguana/yaml_util.hpp index c55b59ede..5c43f47fa 100644 --- a/include/ylt/thirdparty/iguana/yaml_util.hpp +++ b/include/ylt/thirdparty/iguana/yaml_util.hpp @@ -112,6 +112,9 @@ concept tuple = !array && requires(Type tuple) { sizeof(std::tuple_size>); }; +template +concept refletable = is_reflection_v>; + // TODO: support c_array template concept non_refletable = container || c_array || tuple ||