diff --git a/cmake/subdir.cmake b/cmake/subdir.cmake index bb3785289..234a25fcb 100644 --- a/cmake/subdir.cmake +++ b/cmake/subdir.cmake @@ -12,6 +12,9 @@ endforeach() if (NOT ENABLE_CPP_20) Set(BUILD_STRUCT_PACK ON) + Set(BUILD_STRUCT_JSON ON) + Set(BUILD_STRUCT_XML ON) + Set(BUILD_STRUCT_YAML ON) endif() foreach(child ${children}) diff --git a/include/ylt/struct_yaml/yaml_reader.h b/include/ylt/struct_yaml/yaml_reader.h index f38c4c77e..ca8b80bea 100644 --- a/include/ylt/struct_yaml/yaml_reader.h +++ b/include/ylt/struct_yaml/yaml_reader.h @@ -18,12 +18,12 @@ namespace struct_yaml { -template +template inline void from_yaml(T &value, const View &view) { iguana::from_yaml(value, view); } -template +template inline void from_yaml(T &value, const View &view, std::error_code &ec) { iguana::from_yaml(value, view, ec); } diff --git a/include/ylt/thirdparty/iguana/define.h b/include/ylt/thirdparty/iguana/define.h index 1f908fde6..ee0e35962 100644 --- a/include/ylt/thirdparty/iguana/define.h +++ b/include/ylt/thirdparty/iguana/define.h @@ -1,7 +1,9 @@ #pragma once #include +#if __cplusplus >= 202002L #include +#endif #include #include #include @@ -36,11 +38,23 @@ template constexpr inline bool contiguous_iterator = std::contiguous_iterator; #else -IGUANA_INLINE int countr_zero(unsigned long long x) { +IGUANA_INLINE int countr_zero(unsigned long long n) { // x will never be zero in iguana - unsigned long pos; - _BitScanForward64(&pos, x); - return pos; +#if defined(_MSC_VER) && defined(_M_X64) + unsigned long c; + _BitScanForward64(&c, n); + return static_cast(c); +#elif defined(_MSC_VER) && defined(_M_IX86) + unsigned long c; + if (static_cast(n) != 0) { + _BitScanForward(&c, static_cast(n)); + return static_cast(c); + } + else { + _BitScanForward(&c, static_cast(n >> 32)); + return static_cast(c) + 32; + } +#endif } namespace std { diff --git a/include/ylt/thirdparty/iguana/enum_reflection.hpp b/include/ylt/thirdparty/iguana/enum_reflection.hpp new file mode 100644 index 000000000..1e0d2ebe6 --- /dev/null +++ b/include/ylt/thirdparty/iguana/enum_reflection.hpp @@ -0,0 +1,172 @@ +#pragma once +#include +#include + +#include "frozen/string.h" +#include "frozen/unordered_map.h" + +namespace iguana { +template +constexpr std::string_view get_raw_name() { +#ifdef _MSC_VER + return __FUNCSIG__; +#else + return __PRETTY_FUNCTION__; +#endif +} + +template +constexpr std::string_view get_raw_name() { +#ifdef _MSC_VER + return __FUNCSIG__; +#else + return __PRETTY_FUNCTION__; +#endif +} + +template +inline constexpr std::string_view type_string() { + constexpr std::string_view sample = get_raw_name(); + constexpr size_t pos = sample.find("int"); + constexpr std::string_view str = get_raw_name(); + constexpr auto next1 = str.rfind(sample[pos + 3]); +#if defined(_MSC_VER) + constexpr auto s1 = str.substr(pos + 6, next1 - pos - 6); +#else + constexpr auto s1 = str.substr(pos, next1 - pos); +#endif + return s1; +} + +template +inline constexpr std::string_view enum_string() { + constexpr std::string_view sample = get_raw_name(); + constexpr size_t pos = sample.find("int"); + constexpr std::string_view str = get_raw_name(); + constexpr auto next1 = str.rfind(sample[pos + 3]); +#if defined(__clang__) || defined(_MSC_VER) + constexpr auto s1 = str.substr(pos, next1 - pos); +#else + constexpr auto s1 = str.substr(pos + 5, next1 - pos - 5); +#endif + return s1; +} + +#if defined(__clang__) && (__clang_major__ >= 17) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wenum-constexpr-conversion" +#endif + +template +constexpr std::string_view get_raw_name_with_v() { +#ifdef _MSC_VER + return __FUNCSIG__; +#else + return __PRETTY_FUNCTION__; +#endif +} + +// True means the V is a valid enum value, and the second of the result is the +// name +template +constexpr std::pair try_get_enum_name() { + constexpr std::string_view sample_raw_name = get_raw_name_with_v(); + constexpr size_t pos = sample_raw_name.find("5"); + constexpr std::string_view enum_raw_name = get_raw_name_with_v(); + constexpr auto enum_end = enum_raw_name.rfind(&sample_raw_name[pos + 1]); +#ifdef _MSC_VER + constexpr auto enum_begin = enum_raw_name.rfind(',', enum_end) + 1; +#else + constexpr auto enum_begin = enum_raw_name.rfind(' ', enum_end) + 1; +#endif + constexpr std::string_view res = + enum_raw_name.substr(enum_begin, enum_end - enum_begin); + + constexpr size_t pos_brackets = res.find(')'); + + size_t pos_colon = res.find("::"); + return {pos_brackets == std::string_view::npos, + res.substr(pos_colon == std::string_view::npos ? 0 : pos_colon + 2)}; +} + +// Enumerate the numbers in a integer sequence to see if they are legal enum +// value +template +constexpr inline auto get_enum_arr( + const std::integer_sequence &) { + constexpr std::size_t N = sizeof...(Is); + std::array enum_names = {}; + std::array enum_values = {}; + std::size_t num = 0; + (([&]() { + constexpr auto res = try_get_enum_name(Is)>(); + if constexpr (res.first) { + // the Is is a valid enum value + enum_names[num] = res.second; + enum_values[num] = static_cast(Is); + ++num; + } + })(), + ...); + return std::make_tuple(num, enum_values, enum_names); +} + +template &arr, size_t... Is> +constexpr auto array_to_seq(const std::index_sequence &) { + return std::integer_sequence(); +} + +// convert array to map +template +constexpr inline auto get_enum_to_str_map( + const std::array &enum_names, + const std::array &enum_values, const std::index_sequence &) { + return frozen::unordered_map{ + {enum_values[Is], enum_names[Is]}...}; +} + +template +constexpr inline auto get_str_to_enum_map( + const std::array &enum_names, + const std::array &enum_values, const std::index_sequence &) { + return frozen::unordered_map{ + {enum_names[Is], enum_values[Is]}...}; +} + +// the default generic enum_value +// if the user has not defined a specialization template, this will be called +template +struct enum_value { + constexpr static std::array value = {}; +}; + +template +constexpr inline auto get_enum_map() { + constexpr auto &arr = enum_value::value; + constexpr auto arr_size = arr.size(); + if constexpr (arr_size > 0) { + // the user has defined a specialization template + constexpr auto arr_seq = + array_to_seq(std::make_index_sequence()); + constexpr auto t = get_enum_arr(arr_seq); + if constexpr (str_to_enum) { + return get_str_to_enum_map( + std::get<2>(t), std::get<1>(t), + std::make_index_sequence(t)>{}); + } + else { + return get_enum_to_str_map( + std::get<2>(t), std::get<1>(t), + std::make_index_sequence(t)>{}); + } + } + else { + return false; + } +} + +#if defined(__clang__) && (__clang_major__ >= 17) +#pragma clang diagnostic pop +#endif + +} // namespace iguana \ No newline at end of file diff --git a/include/ylt/thirdparty/iguana/json_reader.hpp b/include/ylt/thirdparty/iguana/json_reader.hpp index 86831b027..16f34a802 100644 --- a/include/ylt/thirdparty/iguana/json_reader.hpp +++ b/include/ylt/thirdparty/iguana/json_reader.hpp @@ -25,7 +25,9 @@ IGUANA_INLINE void parse_escape(U &value, It &&it, It &&end) { if (*it == 'u') { ++it; if (std::distance(it, end) <= 4) - throw std::runtime_error(R"(Expected 4 hexadecimal digits)"); + IGUANA_UNLIKELY { + throw std::runtime_error(R"(Expected 4 hexadecimal digits)"); + } auto code_point = parse_unicode_hex4(it); encode_utf8(value, code_point); } @@ -94,12 +96,6 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { 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 , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { @@ -197,7 +193,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { IGUANA_UNLIKELY case '\\' : ++it; parse_escape(value, it, end); break; - IGUANA_UNLIKELY case ']' : return; + // IGUANA_UNLIKELY case ']' : return; IGUANA_UNLIKELY case '"' : ++it; return; IGUANA_LIKELY default : value.push_back(*it); @@ -229,6 +225,27 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { throw std::runtime_error("Expected \""); } +template , int> = 0> +IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { + static constexpr auto str_to_enum = get_enum_map>(); + if constexpr (bool_v) { + // not defined a specialization template + using T = std::underlying_type_t>; + parse_item(reinterpret_cast(value), it, end); + } + else { + std::string_view enum_names; + parse_item(enum_names, it, end); + auto it = str_to_enum.find(enum_names); + if (it != str_to_enum.end()) + IGUANA_LIKELY { value = it->second; } + else { + throw std::runtime_error(std::string(enum_names) + + " missing corresponding value in enum_value"); + } + } +} + template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { using T = std::remove_reference_t; @@ -655,9 +672,8 @@ inline void parse_object(jobject &result, It &&it, It &&end) { skip_ws(it, end); while (true) { - if (it == end) { - break; - } + if (it == end) + IGUANA_UNLIKELY { throw std::runtime_error("Expected }"); } std::string key; detail::parse_item(key, it, end); diff --git a/include/ylt/thirdparty/iguana/json_util.hpp b/include/ylt/thirdparty/iguana/json_util.hpp index bd28f3333..986040efd 100644 --- a/include/ylt/thirdparty/iguana/json_util.hpp +++ b/include/ylt/thirdparty/iguana/json_util.hpp @@ -3,134 +3,11 @@ #pragma once -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "define.h" -#include "detail/charconv.h" -#include "reflection.hpp" +#include "util.hpp" #include "value.hpp" namespace iguana { -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 -inline constexpr bool bool_v = - std::is_same_v, bool> || - std::is_same_v, std::vector::reference>; - -template -inline constexpr bool int_v = std::is_integral_v> && - !char_v> && !bool_v; - -template -inline constexpr bool float_v = std::is_floating_point_v>; - -template -inline constexpr bool num_v = float_v || int_v; - -template -inline constexpr bool enum_v = std::is_enum_v>; - -template -constexpr inline bool optional_v = - is_template_instant_of>::value; - -template -struct is_container : std::false_type {}; - -template -struct is_container< - T, std::void_t().size(), std::declval().begin(), - std::declval().end())>> : std::true_type {}; - -template -struct is_map_container : std::false_type {}; - -template -struct is_map_container< - T, std::void_t())>> - : is_container {}; - -template -constexpr inline bool container_v = is_container>::value; - -template -constexpr inline bool map_container_v = - is_map_container>::value; - -template -constexpr inline bool c_array_v = std::is_array_v> && - std::extent_v> > 0; - -template -struct is_array : std::false_type {}; - -template -struct is_array< - T, std::void_t().size()), - typename std::enable_if_t<(std::tuple_size::value != 0)>>> - : std::true_type {}; - -template -constexpr inline bool array_v = is_array>::value; - -template -constexpr inline bool fixed_array_v = c_array_v || array_v; - -template -constexpr inline bool string_view_v = - is_template_instant_of>::value; - -template -constexpr inline bool string_v = - is_template_instant_of>::value; - -// TODO: type must be char -template -constexpr inline bool json_view_v = container_v; - -template -constexpr inline bool json_byte_v = - std::is_same_v> || - std::is_same_v> || - std::is_same_v>; - -template -constexpr inline bool sequence_container_v = - is_sequence_container>::value; - -template -constexpr inline bool tuple_v = is_tuple>::value; - -template -constexpr inline bool string_container_v = string_v || string_view_v; - -template -constexpr inline bool unique_ptr_v = - is_template_instant_of>::value; - -template -constexpr inline bool non_refletable_v = - container_v || c_array_v || tuple_v || optional_v || - unique_ptr_v || std::is_fundamental_v>; - -template -constexpr inline bool refletable_v = is_reflection_v>; - class numeric_str { public: std::string_view &value() { return val_; } @@ -138,15 +15,13 @@ class numeric_str { template T convert() { static_assert(num_v, "T must be numeric type"); - if (val_.empty()) [[unlikely]] { - throw std::runtime_error("Failed to parse number"); - } + if (val_.empty()) + IGUANA_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"); - } + if (ec != std::errc{}) + IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); } return res; } @@ -158,39 +33,6 @@ template constexpr inline bool numeric_str_v = std::is_same_v>; -template -struct string_literal { - static constexpr size_t size = (N > 0) ? (N - 1) : 0; - - constexpr string_literal() = default; - - constexpr string_literal(const char (&str)[N]) { std::copy_n(str, N, value); } - - char value[N]; - constexpr const char *end() const noexcept { return value + size; } - - constexpr const std::string_view sv() const noexcept { return {value, size}; } -}; - -template -IGUANA_INLINE void match(It &&it, It &&end) { - const auto n = static_cast(std::distance(it, end)); - 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)); - } -} - template IGUANA_INLINE void skip_comment(It &&it, It &&end) { ++it; diff --git a/include/ylt/thirdparty/iguana/json_writer.hpp b/include/ylt/thirdparty/iguana/json_writer.hpp index 40f80de30..64758ece2 100644 --- a/include/ylt/thirdparty/iguana/json_writer.hpp +++ b/include/ylt/thirdparty/iguana/json_writer.hpp @@ -12,6 +12,13 @@ template , int> = 0> IGUANA_INLINE void to_json(T &&t, Stream &s); +template +IGUANA_INLINE void render_json_value(Stream &ss, std::optional &val); + +template , int> = 0> +IGUANA_INLINE void render_json_value(Stream &ss, const T &t); + template , int> = 0> IGUANA_INLINE void render_json_value(Stream &ss, const T &v); @@ -25,7 +32,10 @@ template , int> = 0> -IGUANA_INLINE void render_json_value(Stream &ss, const T &v); +IGUANA_INLINE void render_json_value(Stream &s, T &&t); + +template , int> = 0> +IGUANA_INLINE void render_json_value(Stream &s, T &&t); template IGUANA_INLINE void join(Stream &ss, InputIt first, InputIt last, const T &delim, @@ -99,7 +109,23 @@ IGUANA_INLINE void render_json_value(Stream &ss, T &&t) { template , int> = 0> IGUANA_INLINE void render_json_value(Stream &ss, T val) { - render_json_value(ss, static_cast>(val)); + static constexpr auto enum_to_str = get_enum_map>(); + if constexpr (bool_v) { + render_json_value(ss, static_cast>(val)); + } + else { + auto it = enum_to_str.find(val); + if (it != enum_to_str.end()) + IGUANA_LIKELY { + auto str = it->second; + render_json_value(ss, std::string_view(str.data(), str.size())); + } + else { + throw std::runtime_error( + std::to_string(static_cast>(val)) + + " is a missing value in enum_value"); + } + } } template @@ -122,8 +148,7 @@ IGUANA_INLINE void render_array(Stream &ss, const T &v) { ss.push_back(']'); } -template , int> = 0> +template , int>> IGUANA_INLINE void render_json_value(Stream &ss, const T &t) { if constexpr (std::is_same_v()[0])>>) { @@ -188,7 +213,7 @@ IGUANA_INLINE void render_json_value(Stream &ss, const T &v) { } } -template , int> = 0> +template , int>> IGUANA_INLINE void render_json_value(Stream &s, T &&t) { using U = typename std::decay_t; s.push_back('['); @@ -203,6 +228,15 @@ IGUANA_INLINE void render_json_value(Stream &s, T &&t) { s.push_back(']'); } +template , int>> +IGUANA_INLINE void render_json_value(Stream &s, T &&t) { + std::visit( + [&s](auto value) { + render_json_value(s, value); + }, + t); +} + template , int>> IGUANA_INLINE void to_json(T &&t, Stream &s) { s.push_back('{'); @@ -215,16 +249,9 @@ IGUANA_INLINE void to_json(T &&t, Stream &s) { write_json_key(s, i, t); s.push_back(':'); - - if constexpr (!is_reflection::value) { - render_json_value(s, t.*v); - } - else { - to_json(t.*v, s); - } - + render_json_value(s, t.*v); if (Idx < Count - 1) - s.push_back(','); + IGUANA_LIKELY { s.push_back(','); } }); s.push_back('}'); } diff --git a/include/ylt/thirdparty/iguana/util.hpp b/include/ylt/thirdparty/iguana/util.hpp index b0850cbcb..79ebe8bf1 100644 --- a/include/ylt/thirdparty/iguana/util.hpp +++ b/include/ylt/thirdparty/iguana/util.hpp @@ -1,50 +1,166 @@ #pragma once + +#include + +#include +#include +#include +#include +#include +#include #include +#include + +#include "define.h" +#include "detail/charconv.h" +#include "enum_reflection.hpp" +#include "error_code.h" +#include "reflection.hpp" namespace iguana { + template -constexpr std::string_view get_raw_name() { -#ifdef _MSC_VER - return __FUNCSIG__; -#else - return __PRETTY_FUNCTION__; -#endif -} +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 -constexpr std::string_view get_raw_name() { -#ifdef _MSC_VER - return __FUNCSIG__; -#else - return __PRETTY_FUNCTION__; -#endif -} +template +inline constexpr bool bool_v = + std::is_same_v, bool> || + std::is_same_v, std::vector::reference>; template -inline constexpr std::string_view type_string() { - constexpr std::string_view sample = get_raw_name(); - constexpr size_t pos = sample.find("int"); - constexpr std::string_view str = get_raw_name(); - constexpr auto next1 = str.rfind(sample[pos + 3]); -#if defined(_MSC_VER) - constexpr auto s1 = str.substr(pos + 6, next1 - pos - 6); -#else - constexpr auto s1 = str.substr(pos, next1 - pos); -#endif - return s1; -} +inline constexpr bool int_v = std::is_integral_v> && + !char_v> && !bool_v; + +template +inline constexpr bool float_v = std::is_floating_point_v>; + +template +inline constexpr bool num_v = float_v || int_v; + +template +inline constexpr bool enum_v = std::is_enum_v>; + +template +constexpr inline bool optional_v = + is_template_instant_of>::value; + +template +struct is_container : std::false_type {}; + +template +struct is_container< + T, std::void_t().size(), std::declval().begin(), + std::declval().end())>> : std::true_type {}; + +template +struct is_map_container : std::false_type {}; + +template +struct is_map_container< + T, std::void_t())>> + : is_container {}; + +template +constexpr inline bool container_v = is_container>::value; + +template +constexpr inline bool map_container_v = + is_map_container>::value; + +template +constexpr inline bool c_array_v = std::is_array_v> && + std::extent_v> > 0; + +template +struct is_array : std::false_type {}; + +template +struct is_array< + T, std::void_t().size()), + typename std::enable_if_t<(std::tuple_size::value != 0)>>> + : std::true_type {}; + +template +constexpr inline bool array_v = is_array>::value; + +template +constexpr inline bool fixed_array_v = c_array_v || array_v; + +template +constexpr inline bool string_view_v = + is_template_instant_of>::value; + +template +constexpr inline bool string_v = + is_template_instant_of>::value; + +// TODO: type must be char +template +constexpr inline bool json_view_v = container_v; -template -inline constexpr std::string_view enum_string() { - constexpr std::string_view sample = get_raw_name(); - constexpr size_t pos = sample.find("int"); - constexpr std::string_view str = get_raw_name(); - constexpr auto next1 = str.rfind(sample[pos + 3]); -#if defined(__clang__) || defined(_MSC_VER) - constexpr auto s1 = str.substr(pos, next1 - pos); -#else - constexpr auto s1 = str.substr(pos + 5, next1 - pos - 5); -#endif - return s1; +template +constexpr inline bool json_byte_v = + std::is_same_v> || + std::is_same_v> || + std::is_same_v>; + +template +constexpr inline bool sequence_container_v = + is_sequence_container>::value; + +template +constexpr inline bool tuple_v = is_tuple>::value; + +template +constexpr inline bool string_container_v = string_v || string_view_v; + +template +constexpr inline bool unique_ptr_v = + is_template_instant_of>::value; + +template +struct is_variant : std::false_type {}; + +template +struct is_variant> : std::true_type {}; + +template +constexpr inline bool variant_v = is_variant>::value; + +template +constexpr inline bool non_refletable_v = + container_v || c_array_v || tuple_v || optional_v || + unique_ptr_v || std::is_fundamental_v> || + variant_v; + +template +constexpr inline bool refletable_v = is_reflection_v>; + +template +constexpr inline bool plain_v = + string_container_v || num_v || char_v || bool_v || enum_v; + +template +IGUANA_INLINE void match(It &&it, It &&end) { + const auto n = static_cast(std::distance(it, end)); + 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)); + } } -} // namespace iguana \ No newline at end of file + +} // namespace iguana diff --git a/include/ylt/thirdparty/iguana/xml_reader.hpp b/include/ylt/thirdparty/iguana/xml_reader.hpp index b4477d09a..f6cae0be0 100644 --- a/include/ylt/thirdparty/iguana/xml_reader.hpp +++ b/include/ylt/thirdparty/iguana/xml_reader.hpp @@ -8,34 +8,42 @@ namespace iguana { namespace detail { -template -IGUANA_INLINE void parse_item(T &value, It &&it, It &&end, +template , int> = 0> +IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, + std::string_view name); + +template , int> = 0> +IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, + std::string_view name); + +template , int> = 0> +IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, std::string_view name); -template +template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, std::string_view name); -template +template , int> = 0> IGUANA_INLINE void parse_value(U &&value, It &&begin, It &&end) { using T = std::decay_t; - if constexpr (string_t) { + if constexpr (string_container_v) { value = T(&*begin, static_cast(std::distance(begin, end))); } - else if constexpr (num_t) { + else if constexpr (num_v) { auto size = std::distance(begin, end); const auto start = &*begin; auto [p, ec] = detail::from_chars(start, start + size, 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"); } - else if constexpr (char_t) { - if (static_cast(std::distance(begin, end)) != 1) [[unlikely]] { - throw std::runtime_error("Expected one character"); - } + else if constexpr (char_v) { + if (static_cast(std::distance(begin, end)) != 1) + IGUANA_UNLIKELY { throw std::runtime_error("Expected one character"); } value = *begin; } - else if constexpr (bool_t) { + else if constexpr (bool_v) { auto bool_v = std::string_view( &*begin, static_cast(std::distance(begin, end))); if (bool_v == "true") { @@ -44,25 +52,38 @@ IGUANA_INLINE void parse_value(U &&value, It &&begin, It &&end) { else if (bool_v == "false") { value = false; } - else [[unlikely]] { - throw std::runtime_error("Expected true or false"); - } + else + IGUANA_UNLIKELY { throw std::runtime_error("Expected true or false"); } } - else if constexpr (enum_t) { - using T = std::underlying_type_t>; - parse_value(reinterpret_cast(value), begin, end); + else if constexpr (enum_v) { + static constexpr auto str_to_enum = get_enum_map(); + if constexpr (bool_v) { + // not defined a specialization template + parse_value(reinterpret_cast &>(value), begin, + end); + } + else { + auto enum_names = std::string_view( + &*begin, static_cast(std::distance(begin, end))); + auto it = str_to_enum.find(enum_names); + if (it != str_to_enum.end()) + IGUANA_LIKELY { value = it->second; } + else { + throw std::runtime_error(std::string(enum_names) + + " missing corresponding value in enum_value"); + } + } } } - -template +template , int> = 0> IGUANA_INLINE void parse_attr(U &&value, It &&it, It &&end) { using key_type = typename std::remove_cvref_t::key_type; using value_type = typename std::remove_cvref_t::mapped_type; while (it != end) { skip_sapces_and_newline(it, end); - if (*it == '>' || *it == '/') [[unlikely]] { - return; - } + if (*it == '>' || *it == '/') + IGUANA_UNLIKELY { return; } auto key_begin = it; auto key_end = skip_pass<'='>(it, end); key_type key; @@ -78,7 +99,7 @@ IGUANA_INLINE void parse_attr(U &&value, It &&it, It &&end) { } } -template +template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, std::string_view name) { skip_till<'>'>(it, end); @@ -90,50 +111,54 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, match_close_tag(it, end, name); } -template +template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, std::string_view name) { parse_item(value.emplace_back(), it, end, name); skip_sapces_and_newline(it, end); while (it != end) { match<'<'>(it, end); - if (*it == '?' || *it == '!') [[unlikely]] { - // skip '>(it, end); - ++it; - skip_sapces_and_newline(it, end); - continue; + if (*it == '?' || *it == '!') + IGUANA_UNLIKELY { + // skip '>(it, end); + ++it; + skip_sapces_and_newline(it, end); + continue; + } } - } auto start = it; skip_till_greater_or_space(it, end); std::string_view key = std::string_view{ &*start, static_cast(std::distance(start, it))}; - if (key != name) [[unlikely]] { - it = start - 1; - return; - } + if (key != name) + IGUANA_UNLIKELY { + it = start - 1; + return; + } parse_item(value.emplace_back(), it, end, name); skip_sapces_and_newline(it, end); } } -template +template , int>> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, std::string_view name) { using value_type = typename std::remove_reference_t::value_type; skip_till<'>'>(it, end); - if (*(it - 1) == '/') [[likely]] { - ++it; - return; - } + if (*(it - 1) == '/') + IGUANA_LIKELY { + ++it; + return; + } - if constexpr (plain_t) { + if constexpr (plain_v) { // The following code is for option not to be emplaced // when parse "...> <..." skip_till<'>'>(it, end); @@ -153,14 +178,14 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, } } -template +template , int>> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, std::string_view name) { value = std::make_unique::element_type>(); parse_item(*value, it, end, name); } -template +template , int>> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, std::string_view name) { parse_attr(value.attr(), it, end); @@ -193,65 +218,69 @@ IGUANA_INLINE void skip_object_value(It &&it, It &&end, std::string_view name) { } // return true means reach the close tag -template +template , int> = 0> IGUANA_INLINE auto skip_till_key(T &value, It &&it, It &&end) { skip_sapces_and_newline(it, end); while (true) { match<'<'>(it, end); - if (*it == '/') [[unlikely]] { - // - return true; // reach the close tag - } - else if (*it == '?') [[unlikely]] { - // - skip_till<'>'>(it, end); - ++it; - skip_sapces_and_newline(it, end); - continue; - } - else if (*it == '!') [[unlikely]] { - ++it; - if (*it == '[') { - // >()) { - ++it; - skip_till<']'>(it, end); - ++it; - match<"]>">(it, end); - skip_sapces_and_newline(it, end); - continue; - } - else { - // if parse cdata - ++it; - match<"CDATA[">(it, end); - skip_sapces_and_newline(it, end); - auto &cdata_value = get(value); - using VT = typename std::decay_t::value_type; - auto vb = it; - auto ve = skip_pass<']'>(it, end); - if constexpr (str_view_t) { - cdata_value.value() = - VT(&*vb, static_cast(std::distance(vb, ve))); + if (*it == '/') + IGUANA_UNLIKELY { + // + return true; // reach the close tag + } + else if (*it == '?') + IGUANA_UNLIKELY { + // + skip_till<'>'>(it, end); + ++it; + skip_sapces_and_newline(it, end); + continue; + } + else if (*it == '!') + IGUANA_UNLIKELY { + ++it; + if (*it == '[') { + // >()) { + ++it; + skip_till<']'>(it, end); + ++it; + match<']', '>'>(it, end); + skip_sapces_and_newline(it, end); + continue; } else { - cdata_value.value().append( - &*vb, static_cast(std::distance(vb, ve))); + // if parse cdata + ++it; + match<'C', 'D', 'A', 'T', 'A', '['>(it, end); + skip_sapces_and_newline(it, end); + auto &cdata_value = get(value); + using VT = typename std::decay_t::value_type; + auto vb = it; + auto ve = skip_pass<']'>(it, end); + if constexpr (string_view_v) { + cdata_value.value() = + VT(&*vb, static_cast(std::distance(vb, ve))); + } + else { + cdata_value.value().append( + &*vb, static_cast(std::distance(vb, ve))); + } + match<']', '>'>(it, end); + skip_sapces_and_newline(it, end); + continue; } - match<"]>">(it, end); + } + else { + // + // '>(it, end); + ++it; skip_sapces_and_newline(it, end); continue; } } - else { - // - // '>(it, end); - ++it; - skip_sapces_and_newline(it, end); - continue; - } - } return false; } } @@ -271,7 +300,7 @@ IGUANA_INLINE void check_required(std::string_view key_set) { } } -template +template , int>> IGUANA_INLINE void parse_item(T &value, It &&it, It &&end, std::string_view name) { using U = std::decay_t; @@ -291,62 +320,69 @@ IGUANA_INLINE void parse_item(T &value, It &&it, It &&end, bool parse_done = false; // sequential parse for_each(value, [&](const auto member_ptr, auto i) IGUANA__INLINE_LAMBDA { +#if defined(_MSC_VER) && _MSVC_LANG < 202002L + // seems MVSC can't pass a constexpr value to lambda + constexpr auto cdata_idx = get_type_index(); +#endif using item_type = std::remove_reference_t; constexpr auto mkey = iguana::get_name(); constexpr std::string_view st_key(mkey.data(), mkey.size()); - if constexpr (cdata_t) { - return; - } - if (parse_done || key != st_key) [[unlikely]] { + if constexpr (cdata_v) { return; } - if constexpr (!cdata_t) { + if (parse_done || key != st_key) + IGUANA_UNLIKELY { return; } + if constexpr (!cdata_v) { parse_item(value.*member_ptr, it, end, key); if constexpr (iguana::has_iguana_required_arr_v) { key_set.append(key).append(", "); } } - if (skip_till_key(value, it, end)) [[unlikely]] { - match_close_tag(it, end, name); - parse_done = true; - return; - } + if (skip_till_key(value, it, end)) + IGUANA_UNLIKELY { + match_close_tag(it, end, name); + parse_done = true; + return; + } start = it; skip_till_greater_or_space(it, end); key = std::string_view{&*start, static_cast(std::distance(start, it))}; }); - if (parse_done) [[unlikely]] { - check_required(key_set); - return; - } + if (parse_done) + IGUANA_UNLIKELY { + check_required(key_set); + return; + } // map parse while (true) { static constexpr auto frozen_map = get_iguana_struct_map(); const auto &member_it = frozen_map.find(key); - if (member_it != frozen_map.end()) [[likely]] { - std::visit( - [&](auto &&member_ptr) IGUANA__INLINE_LAMBDA { - static_assert( - std::is_member_pointer_v>, - "type must be memberptr"); - using V = std::remove_reference_t; - if constexpr (!cdata_t) { - parse_item(value.*member_ptr, it, end, key); - if constexpr (iguana::has_iguana_required_arr_v) { - key_set.append(key).append(", "); + if (member_it != frozen_map.end()) + IGUANA_LIKELY { + std::visit( + [&](auto &&member_ptr) IGUANA__INLINE_LAMBDA { + static_assert( + std::is_member_pointer_v>, + "type must be memberptr"); + using V = std::remove_reference_t; + if constexpr (!cdata_v) { + parse_item(value.*member_ptr, it, end, key); + if constexpr (iguana::has_iguana_required_arr_v) { + key_set.append(key).append(", "); + } } - } - }, - member_it->second); - } - else [[unlikely]] { + }, + member_it->second); + } + else + IGUANA_UNLIKELY { #ifdef THROW_UNKNOWN_KEY - throw std::runtime_error("Unknown key: " + std::string(key)); + throw std::runtime_error("Unknown key: " + std::string(key)); #else - skip_object_value(it, end, key); + skip_object_value(it, end, key); #endif - } + } if (skip_till_key(value, it, end)) { match_close_tag(it, end, name); check_required(key_set); @@ -361,7 +397,7 @@ IGUANA_INLINE void parse_item(T &value, It &&it, It &&end, } // namespace detail -template +template , int> = 0> IGUANA_INLINE void from_xml(U &value, It &&it, It &&end) { while (it != end) { skip_sapces_and_newline(it, end); @@ -381,7 +417,8 @@ IGUANA_INLINE void from_xml(U &value, It &&it, It &&end) { detail::parse_attr(value.attr(), it, end); detail::parse_item(value.value(), it, end, key); } -template + +template , int> = 0> IGUANA_INLINE void from_xml(U &value, It &&it, It &&end) { while (it != end) { skip_sapces_and_newline(it, end); @@ -401,12 +438,12 @@ IGUANA_INLINE void from_xml(U &value, It &&it, It &&end) { detail::parse_item(value, it, end, key); } -template +template , int> = 0> IGUANA_INLINE void from_xml(U &value, const View &view) { from_xml(value, std::begin(view), std::end(view)); } -template +template , int> = 0> IGUANA_INLINE Num get_number(std::string_view str) { Num num; detail::parse_value(num, str.begin(), str.end()); diff --git a/include/ylt/thirdparty/iguana/xml_util.hpp b/include/ylt/thirdparty/iguana/xml_util.hpp index 5b92af28b..be28c4d27 100644 --- a/include/ylt/thirdparty/iguana/xml_util.hpp +++ b/include/ylt/thirdparty/iguana/xml_util.hpp @@ -1,123 +1,7 @@ #pragma once - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "define.h" -#include "reflection.hpp" +#include "util.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 -concept float_t = std::floating_point>; - -template -concept num_t = std::floating_point> || int_t; - -template -concept enum_t = std::is_enum_v>; - -template -constexpr inline bool is_basic_string_view = false; - -template -constexpr inline bool is_basic_string_view> = true; - -template -concept str_view_t = is_basic_string_view>; - -template -concept str_t = - std::convertible_to, std::string_view> && !str_view_t; - -template -concept string_t = std::convertible_to, std::string_view>; - -template -concept tuple_t = is_tuple>::value; - -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*(); - typename std::remove_cvref_t::element_type; -} -&&!requires(Type ptr, Type ptr2) { ptr = ptr2; }; - -template -concept optional_t = requires(Type optional) { - optional.value(); - optional.has_value(); - optional.operator*(); - typename std::remove_cvref_t::value_type; -}; - -template -concept container = requires(Type container) { - typename std::remove_cvref_t::value_type; - container.size(); - container.begin(); - container.end(); -}; -template -concept map_container = container && requires(Type container) { - typename std::remove_cvref_t::mapped_type; -}; - -template -concept plain_t = - string_t || num_t || char_t || bool_t || enum_t; - -template -concept c_array = std::is_array_v> && - std::extent_v> > -0; - -template -concept array = requires(Type arr) { - arr.size(); - std::tuple_size>{}; -}; - -template -concept tuple = !array && requires(Type tuple) { - std::get<0>(tuple); - sizeof(std::tuple_size>); -}; - -template -concept non_refletable = container || c_array || tuple || - optional_t || unique_ptr_t || std::is_fundamental_v; - template > class xml_attr_t { @@ -133,7 +17,8 @@ class xml_attr_t { map_type attr_; }; -template +template , int> = 0> class xml_cdata_t { public: T &value() { return val_; } @@ -151,7 +36,7 @@ template constexpr inline bool is_attr_t_v> = true; template -concept attr_t = is_attr_t_v>; +constexpr inline bool attr_v = is_attr_t_v>; template struct is_cdata_t : std::false_type {}; @@ -160,21 +45,7 @@ template struct is_cdata_t> : std::true_type {}; template -concept cdata_t = is_cdata_t>::value; - -template -struct string_literal { - static constexpr size_t size = (N > 0) ? (N - 1) : 0; - - constexpr string_literal() = default; - - constexpr string_literal(const char (&str)[N]) { std::copy_n(str, N, value); } - - char value[N]; - constexpr const char *end() const noexcept { return value + size; } - - constexpr const std::string_view sv() const noexcept { return {value, size}; } -}; +constexpr inline bool cdata_v = is_cdata_t>::value; inline constexpr auto has_zero = [](uint64_t chunk) IGUANA__INLINE_LAMBDA { return (((chunk - 0x0101010101010101) & ~chunk) & 0x8080808080808080); @@ -217,57 +88,25 @@ inline constexpr auto has_qoute = [](uint64_t chunk) IGUANA__INLINE_LAMBDA { 0b0010001000100010001000100010001000100010001000100010001000100010); }; -IGUANA_INLINE void skip_sapces_and_newline(auto &&it, auto &&end) { +template +IGUANA_INLINE void skip_sapces_and_newline(It &&it, It &&end) { while (it != end && (*it < 33)) { ++it; } } -// match c and skip -template -IGUANA_INLINE void match(auto &&it, auto &&end) { - if (it == end || *it != c) [[unlikely]] { - static constexpr char b[] = {c, '\0'}; - 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) { - 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); - } -} - -IGUANA_INLINE void match_close_tag(auto &&it, auto &&end, - std::string_view key) { - if (it == end || (*it++) != '/') [[unlikely]] { - throw std::runtime_error("unclosed tag: " + std::string(key)); - } +template +IGUANA_INLINE void match_close_tag(It &&it, It &&end, std::string_view key) { + if (it == end || (*it++) != '/') + IGUANA_UNLIKELY { + throw std::runtime_error("unclosed tag: " + std::string(key)); + } size_t size = key.size(); if (static_cast(std::distance(it, end)) <= size || - std::string_view{&*it, size} != key) [[unlikely]] { - throw std::runtime_error("unclosed tag: " + std::string(key)); - } + std::string_view{&*it, size} != key) + IGUANA_UNLIKELY { + throw std::runtime_error("unclosed tag: " + std::string(key)); + } it += size; match<'>'>(it, end); @@ -275,35 +114,36 @@ IGUANA_INLINE void match_close_tag(auto &&it, auto &&end, // ++it; } -template -IGUANA_INLINE void skip_till(auto &&it, auto &&end) { - static_assert(std::contiguous_iterator>); - - 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; - if constexpr (c == '>') - test = has_greater(chunk); - else if constexpr (c == '<') - test = has_smaller(chunk); - else if constexpr (c == '"') - test = has_qoute(chunk); - else if constexpr (c == ' ') - test = has_space(chunk); - else if constexpr (c == ']') - test = has_square_bracket(chunk); - else if constexpr (c == '=') - test = has_equal(chunk); - else - static_assert(!c, "not support this character"); - if (test != 0) { - it += (std::countr_zero(test) >> 3); - return; +template +IGUANA_INLINE void skip_till(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; + if constexpr (c == '>') + test = has_greater(chunk); + else if constexpr (c == '<') + test = has_smaller(chunk); + else if constexpr (c == '"') + test = has_qoute(chunk); + else if constexpr (c == ' ') + test = has_space(chunk); + else if constexpr (c == ']') + test = has_square_bracket(chunk); + else if constexpr (c == '=') + test = has_equal(chunk); + else + static_assert(!c, "not support this character"); + if (test != 0) { + it += (countr_zero(test) >> 3); + return; + } } } - } // Tail end of buffer. Should be rare we even get here while (it < end) { @@ -317,20 +157,22 @@ IGUANA_INLINE void skip_till(auto &&it, auto &&end) { } // skip_till<'>', '<'>(it, end); -IGUANA_INLINE void skip_till_greater_or_space(auto &&it, auto &&end) { - static_assert(std::contiguous_iterator>); - - 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_greater(chunk) | has_space(chunk); - if (test != 0) { - it += (std::countr_zero(test) >> 3); - return; +template +IGUANA_INLINE void skip_till_greater_or_space(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_greater(chunk) | has_space(chunk); + if (test != 0) { + it += (countr_zero(test) >> 3); + return; + } } } - } // Tail end of buffer. Should be rare we even get here while (it < end) { @@ -344,8 +186,8 @@ IGUANA_INLINE void skip_till_greater_or_space(auto &&it, auto &&end) { throw std::runtime_error("Expected > or space"); } -template -IGUANA_INLINE auto skip_pass(auto &&it, auto &&end) { +template +IGUANA_INLINE auto skip_pass(It &&it, It &&end) { skip_till(it, end); auto res = it++ - 1; while (*res == ' ') { diff --git a/include/ylt/thirdparty/iguana/xml_writer.hpp b/include/ylt/thirdparty/iguana/xml_writer.hpp index e5bb10c67..800ea6077 100644 --- a/include/ylt/thirdparty/iguana/xml_writer.hpp +++ b/include/ylt/thirdparty/iguana/xml_writer.hpp @@ -6,7 +6,13 @@ namespace iguana { -template +template , int> = 0> +IGUANA_INLINE void render_xml_value(Stream &ss, const T &value, + std::string_view name); + +template , int> = 0> IGUANA_INLINE void render_xml_value(Stream &ss, T &&t, std::string_view name); template @@ -24,7 +30,7 @@ IGUANA_INLINE void render_tail(Stream &ss, std::string_view str) { } template -inline void render_head(Stream &ss, std::string_view str) { +IGUANA_INLINE void render_head(Stream &ss, std::string_view str) { if constexpr (pretty) { ss.append(spaces, '\t'); } @@ -33,31 +39,48 @@ inline void render_head(Stream &ss, std::string_view str) { ss.push_back('>'); } -template +template , int> = 0> IGUANA_INLINE void render_value(Stream &ss, const T &value) { - if constexpr (string_t) { + if constexpr (string_container_v) { ss.append(value.data(), value.size()); } - else if constexpr (num_t) { + else if constexpr (num_v) { char temp[65]; auto p = detail::to_chars(temp, value); ss.append(temp, p - temp); } - else if constexpr (char_t) { + else if constexpr (char_v) { ss.push_back(value); } - else if constexpr (bool_t) { + else if constexpr (bool_v) { ss.append(value ? "true" : "false"); } - else if constexpr (enum_t) { - render_value(ss, static_cast>(value)); + else if constexpr (enum_v) { + static constexpr auto enum_to_str = get_enum_map>(); + if constexpr (bool_v) { + render_value(ss, static_cast>(value)); + } + else { + auto it = enum_to_str.find(value); + if (it != enum_to_str.end()) + IGUANA_LIKELY { + auto str = it->second; + render_value(ss, std::string_view(str.data(), str.size())); + } + else { + throw std::runtime_error( + std::to_string(static_cast>(value)) + + " is a missing value in enum_value"); + } + } } else { static_assert(!sizeof(T), "type is not supported"); } } -template +template , int> = 0> inline void render_xml_attr(Stream &ss, const T &value, std::string_view name) { if constexpr (pretty) { ss.append(spaces, '\t'); @@ -75,14 +98,16 @@ inline void render_xml_attr(Stream &ss, const T &value, std::string_view name) { ss.push_back('>'); } -template +template , int> = 0> IGUANA_INLINE void render_xml_value(Stream &ss, const T &value, std::string_view name) { render_value(ss, value); render_tail(ss, name); } -template +template , int> = 0> IGUANA_INLINE void render_xml_value(Stream &ss, const T &value, std::string_view name) { if (value) { @@ -93,7 +118,8 @@ IGUANA_INLINE void render_xml_value(Stream &ss, const T &value, } } -template +template , int> = 0> IGUANA_INLINE void render_xml_value(Stream &ss, const T &value, std::string_view name) { if (value) { @@ -104,19 +130,21 @@ IGUANA_INLINE void render_xml_value(Stream &ss, const T &value, } } -template +template , int> = 0> IGUANA_INLINE void render_xml_value(Stream &ss, const T &value, std::string_view name) { render_xml_attr(ss, value.attr(), name); render_xml_value(ss, value.value(), name); } -template +template , int>> IGUANA_INLINE void render_xml_value(Stream &ss, const T &value, std::string_view name) { using value_type = typename std::remove_cvref_t::value_type; for (const auto &v : value) { - if constexpr (attr_t) { + if constexpr (attr_v) { render_xml_value(ss, v, name); } else { @@ -126,7 +154,8 @@ IGUANA_INLINE void render_xml_value(Stream &ss, const T &value, } } -template +template , int>> IGUANA_INLINE void render_xml_value(Stream &ss, T &&t, std::string_view name) { if constexpr (pretty) { ss.push_back('\n'); @@ -141,13 +170,13 @@ IGUANA_INLINE void render_xml_value(Stream &ss, T &&t, std::string_view name) { std::string_view(get_name, Idx>().data(), get_name, Idx>().size()); static_assert(Idx < Count); - if constexpr (sequence_container_t) { + if constexpr (sequence_container_v) { render_xml_value(ss, t.*v, tag_name); } - else if constexpr (attr_t) { + else if constexpr (attr_v) { render_xml_value(ss, t.*v, tag_name); } - else if constexpr (cdata_t) { + else if constexpr (cdata_v) { if constexpr (pretty) { ss.append(spaces + 1, '\t'); ss.append("\n"); @@ -164,16 +193,18 @@ IGUANA_INLINE void render_xml_value(Stream &ss, T &&t, std::string_view name) { render_tail(ss, name); } -template +template , int> = 0> IGUANA_INLINE void to_xml(T &&t, Stream &s) { using value_type = typename std::decay_t::value_type; - static_assert(refletable, "value_type must be refletable"); + static_assert(refletable_v, "value_type must be refletable"); constexpr std::string_view root_name = std::string_view( get_name().data(), get_name().size()); render_xml_value(s, std::forward(t), root_name); } -template +template , int> = 0> IGUANA_INLINE void to_xml(T &&t, Stream &s) { constexpr std::string_view root_name = std::string_view( get_name>().data(), get_name>().size()); diff --git a/include/ylt/thirdparty/iguana/yaml_reader.hpp b/include/ylt/thirdparty/iguana/yaml_reader.hpp index 1704b09a8..ea0f31213 100644 --- a/include/ylt/thirdparty/iguana/yaml_reader.hpp +++ b/include/ylt/thirdparty/iguana/yaml_reader.hpp @@ -8,87 +8,92 @@ namespace iguana { -template +template , int> = 0> IGUANA_INLINE void from_yaml(T &value, It &&it, It &&end, size_t min_spaces = 0); namespace detail { -template +template , int> = 0> IGUANA_INLINE void parse_escape_str(U &value, It &&it, It &&end) { auto start = it; while (it != end) { - if (*(it++) == '\\') [[unlikely]] { - if (*it == 'u') { - value.append(&*start, - static_cast(std::distance(start, it) - 1)); - ++it; - if (std::distance(it, end) < 4) [[unlikely]] + if (*(it++) == '\\') + IGUANA_UNLIKELY { + if (*it == 'u') { + value.append(&*start, + static_cast(std::distance(start, it) - 1)); + ++it; + if (std::distance(it, end) < 4) + IGUANA_UNLIKELY throw std::runtime_error(R"(Expected 4 hexadecimal digits)"); - auto code_point = parse_unicode_hex4(it); - encode_utf8(value, code_point); - start = it; - } - else if (*it == 'n') { - value.append(&*start, - static_cast(std::distance(start, it) - 1)); - start = ++it; - value.push_back('\n'); - } - else if (*it == 't') { - value.append(&*start, - static_cast(std::distance(start, it) - 1)); - start = ++it; - value.push_back('\t'); - } - else if (*it == 'r') { - value.append(&*start, - static_cast(std::distance(start, it) - 1)); - start = ++it; - value.push_back('\r'); - } - else if (*it == 'b') { - value.append(&*start, - static_cast(std::distance(start, it) - 1)); - start = ++it; - value.push_back('\b'); - } - else if (*it == 'f') { - value.append(&*start, - static_cast(std::distance(start, it) - 1)); - start = ++it; - value.push_back('\f'); + auto code_point = parse_unicode_hex4(it); + encode_utf8(value, code_point); + start = it; + } + else if (*it == 'n') { + value.append(&*start, + static_cast(std::distance(start, it) - 1)); + start = ++it; + value.push_back('\n'); + } + else if (*it == 't') { + value.append(&*start, + static_cast(std::distance(start, it) - 1)); + start = ++it; + value.push_back('\t'); + } + else if (*it == 'r') { + value.append(&*start, + static_cast(std::distance(start, it) - 1)); + start = ++it; + value.push_back('\r'); + } + else if (*it == 'b') { + value.append(&*start, + static_cast(std::distance(start, it) - 1)); + start = ++it; + value.push_back('\b'); + } + else if (*it == 'f') { + value.append(&*start, + static_cast(std::distance(start, it) - 1)); + start = ++it; + value.push_back('\f'); + } } - } } value.append(&*start, static_cast(std::distance(start, end))); } // use '-' here to simply represent '>-' -template +template , int> = 0> IGUANA_INLINE void parse_block_str(U &value, It &&it, It &&end, size_t min_spaces) { auto spaces = skip_space_and_lines(it, end, min_spaces); - if (spaces < min_spaces) [[unlikely]] { - // back to the end of the previous line - if constexpr (block_type == '|' || block_type == '>') { - value.push_back('\n'); + if (spaces < min_spaces) + IGUANA_UNLIKELY { + // back to the end of the previous line + if constexpr (block_type == '|' || block_type == '>') { + value.push_back('\n'); + } + it -= spaces + 1; + return; } - it -= spaces + 1; - return; - } while (it != end) { auto start = it; auto value_end = skip_till_newline(it, end); value.append(&*start, static_cast(std::distance(start, value_end))); spaces = skip_space_and_lines(it, end, min_spaces); - if ((spaces < min_spaces) || (it == end)) [[unlikely]] { - if constexpr (block_type == '|' || block_type == '>') { - value.push_back('\n'); + if ((spaces < min_spaces) || (it == end)) + IGUANA_UNLIKELY { + if constexpr (block_type == '|' || block_type == '>') { + value.push_back('\n'); + } + it -= spaces + 1; + return; } - it -= spaces + 1; - return; - } if constexpr (block_type == '|') { value.push_back('\n'); } @@ -98,23 +103,25 @@ IGUANA_INLINE void parse_block_str(U &value, It &&it, It &&end, } } -template +template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces); -template +template , int> = 0> IGUANA_INLINE void parse_value(U &value, It &&value_begin, It &&value_end) { - if (value_begin == value_end) [[unlikely]] { - return; - } + if (value_begin == value_end) + IGUANA_UNLIKELY { return; } auto size = std::distance(value_begin, value_end); const auto start = &*value_begin; auto [p, ec] = detail::from_chars(start, start + size, 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"); } // string_view should be used for string with ' " ? -template +template , int> = 0> IGUANA_INLINE void parse_value(U &value, It &&value_begin, It &&value_end) { using T = std::decay_t; auto start = value_begin; @@ -122,10 +129,18 @@ IGUANA_INLINE void parse_value(U &value, It &&value_begin, It &&value_end) { if (*value_begin == '"') { ++start; --end; - if (*end != '"') [[unlikely]] { - throw std::runtime_error(R"(Expected ")"); - } - if constexpr (str_t) { + if (*end != '"') + IGUANA_UNLIKELY { + // TODO: improve + auto it = start; + while (*it != '"' && it != end) { + ++it; + } + if (it == end || (*(it + 1) != '#')) + IGUANA_UNLIKELY { throw std::runtime_error(R"(Expected ")"); } + end = it; + } + if constexpr (string_v) { parse_escape_str(value, start, end); return; } @@ -133,29 +148,42 @@ IGUANA_INLINE void parse_value(U &value, It &&value_begin, It &&value_end) { else if (*value_begin == '\'') { ++start; --end; - if (*end != '\'') [[unlikely]] { - throw std::runtime_error(R"(Expected ')"); - } + if (*end != '\'') + IGUANA_UNLIKELY { throw std::runtime_error(R"(Expected ')"); } } value = T(&*start, static_cast(std::distance(start, end))); } -template +template , int> = 0> IGUANA_INLINE void parse_value(U &value, It &&value_begin, It &&value_end) { if (static_cast(std::distance(value_begin, value_end)) != 1) - [[unlikely]] { - throw std::runtime_error("Expected one character"); - } + IGUANA_UNLIKELY { throw std::runtime_error("Expected one character"); } value = *value_begin; } -template +template , int> = 0> IGUANA_INLINE void parse_value(U &value, It &&value_begin, It &&value_end) { - using T = std::underlying_type_t>; - parse_value(reinterpret_cast(value), value_begin, value_end); + static constexpr auto str_to_enum = get_enum_map>(); + if constexpr (bool_v) { + // not defined a specialization template + using T = std::underlying_type_t>; + parse_value(reinterpret_cast(value), value_begin, value_end); + } + else { + auto enum_names = std::string_view( + &*value_begin, + static_cast(std::distance(value_begin, value_end))); + auto it = str_to_enum.find(enum_names); + if (it != str_to_enum.end()) + IGUANA_LIKELY { value = it->second; } + else { + throw std::runtime_error(std::string(enum_names) + + " missing corresponding value in enum_value"); + } + } } -template +template , int> = 0> IGUANA_INLINE void parse_value(U &&value, It &&value_begin, It &&value_end) { auto bool_v = std::string_view( &*value_begin, @@ -166,23 +194,21 @@ IGUANA_INLINE void parse_value(U &&value, It &&value_begin, It &&value_end) { else if (bool_v == "false") { value = 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, size_t min_spaces) { from_yaml(value, it, end, min_spaces); } -template +template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces) { using T = std::decay_t; - if constexpr (str_t) { - if (skip_space_till_end(it, end)) [[unlikely]] { - return; - } + if constexpr (string_v) { + if (skip_space_till_end(it, end)) + IGUANA_UNLIKELY { return; } if (*it == '|') { ++it; parse_block_str<'|'>(value, it, end, min_spaces); @@ -212,14 +238,15 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces) { } } -template +template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces); -template +template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces); // minspaces : The minimum indentation -template +template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces) { value.clear(); auto spaces = skip_space_and_lines(it, end, min_spaces); @@ -228,24 +255,23 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces) { ++it; skip_space_and_lines(it, end, min_spaces); while (it != end) { - if (*it == ']') [[unlikely]] { - ++it; - return; - } - if constexpr (plain_t) { + if (*it == ']') + IGUANA_UNLIKELY { + ++it; + return; + } + if constexpr (plain_v) { auto start = it; auto value_end = skip_till<',', ']'>(it, end); parse_value(value.emplace_back(), start, value_end); - if (*(it - 1) == ']') [[unlikely]] { - return; - } + if (*(it - 1) == ']') + IGUANA_UNLIKELY { return; } } else { parse_item(value.emplace_back(), it, end, spaces + 1); skip_space_and_lines(it, end, min_spaces); - if (*it == ',') [[likely]] { - ++it; - } + if (*it == ',') + IGUANA_LIKELY { ++it; } } skip_space_and_lines(it, end, min_spaces); } @@ -253,20 +279,22 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces) { else if (*it == '-') { while (it != end) { auto subspaces = skip_space_and_lines(it, end, spaces); - if (subspaces < spaces) [[unlikely]] { - it -= subspaces + 1; - return; - } - if (it == end) [[unlikely]] - return; + if (subspaces < spaces) + IGUANA_UNLIKELY { + it -= subspaces + 1; + return; + } + if (it == end) + IGUANA_UNLIKELY + return; match<'-'>(it, end); // space + 1 because of the skipped '-' subspaces = skip_space_and_lines(it, end, spaces + 1); - if constexpr (str_t) { - // except str_t because of scalar blocks + if constexpr (string_v) { + // except string_v because of scalar blocks parse_item(value.emplace_back(), it, end, spaces + 1); } - else if constexpr (plain_t) { + else if constexpr (plain_v) { auto start = it; auto value_end = skip_till_newline(it, end); parse_value(value.emplace_back(), start, value_end); @@ -276,12 +304,11 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces) { } } } - else [[unlikely]] { - throw std::runtime_error("Expected ']' or '-'"); - } + else + IGUANA_UNLIKELY { throw std::runtime_error("Expected ']' or '-'"); } } -template +template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces) { auto spaces = skip_space_and_lines(it, end, min_spaces); if (*it == '[') { @@ -290,7 +317,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces) { for_each(value, [&](auto &v, auto i) IGUANA__INLINE_LAMBDA { using value_type = std::decay_t; skip_space_and_lines(it, end, min_spaces); - if constexpr (plain_t) { + if constexpr (plain_v) { auto start = it; auto value_end = skip_till<',', ']'>(it, end); parse_value(v, start, value_end); @@ -311,10 +338,10 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces) { skip_space_and_lines(it, end, spaces); match<'-'>(it, end); auto subspaces = skip_space_and_lines(it, end, spaces + 1); - if constexpr (str_t) { + if constexpr (string_v) { parse_item(v, it, end, spaces + 1); } - else if constexpr (plain_t) { + else if constexpr (plain_v) { auto start = it; auto value_end = skip_till_newline(it, end); parse_value(v, start, value_end); @@ -329,7 +356,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces) { } } -template +template , int>> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces) { using T = std::remove_reference_t; using key_type = typename T::key_type; @@ -339,29 +366,28 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces) { ++it; auto subspaces = skip_space_and_lines(it, end, min_spaces); while (it != end) { - if (*it == '}') [[unlikely]] { - ++it; - return; - } + if (*it == '}') + IGUANA_UNLIKELY { + ++it; + return; + } auto start = it; auto value_end = skip_till<':'>(it, end); key_type key; parse_value(key, start, value_end); subspaces = skip_space_and_lines(it, end, min_spaces); - if constexpr (plain_t) { + if constexpr (plain_v) { start = it; value_end = skip_till<',', '}'>(it, end); parse_value(value[key], start, value_end); - if (*(it - 1) == '}') [[unlikely]] { - return; - } + if (*(it - 1) == '}') + IGUANA_UNLIKELY { return; } } else { parse_item(value[key], it, end, min_spaces); subspaces = skip_space_and_lines(it, end, min_spaces); - if (*it == ',') [[likely]] { - ++it; - } + if (*it == ',') + IGUANA_LIKELY { ++it; } } subspaces = skip_space_and_lines(it, end, min_spaces); } @@ -369,16 +395,17 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces) { else { auto subspaces = skip_space_and_lines(it, end, min_spaces); while (it != end) { - if (subspaces < min_spaces) [[unlikely]] { - it -= subspaces + 1; - return; - } + if (subspaces < min_spaces) + IGUANA_UNLIKELY { + it -= subspaces + 1; + return; + } auto start = it; auto value_end = skip_till<':'>(it, end); key_type key; parse_value(key, start, value_end); subspaces = skip_space_and_lines(it, end, min_spaces); - if constexpr (plain_t && !str_t) { + if constexpr (plain_v && !string_v) { start = it; value_end = skip_till_newline(it, end); parse_value(value[key], start, value_end); @@ -391,27 +418,27 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces) { } } -template +template , int>> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces) { using T = std::remove_reference_t; value = std::make_unique(); - static_assert(!str_t, + static_assert(!string_v, "unique_ptr is not allowed"); parse_item(*value, it, end, min_spaces); } -template +template , int>> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces) { using T = std::remove_reference_t; using value_type = typename T::value_type; auto spaces = skip_space_and_lines(it, end, min_spaces); - if (it == end) [[unlikely]] { - return; - } - if (spaces < min_spaces) [[unlikely]] { - it -= spaces + 1; - return; - } + if (it == end) + IGUANA_UNLIKELY { return; } + if (spaces < min_spaces) + IGUANA_UNLIKELY { + it -= spaces + 1; + return; + } auto start = it; std::decay_t value_end = end; if (*it == 'n') { @@ -422,20 +449,22 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces) { return; } } - if constexpr (str_t || !plain_t) { + if constexpr (string_v || !plain_v) { it = start; parse_item(value.emplace(), it, end, min_spaces); } else { - if (value_end == end) [[likely]] { - // if value_end isn't touched - value_end = skip_till_newline(it, end); - } + if (value_end == end) + IGUANA_LIKELY { + // if value_end isn't touched + value_end = skip_till_newline(it, end); + } parse_value(value.emplace(), start, value_end); } } -IGUANA_INLINE void skip_object_value(auto &&it, auto &&end, size_t min_spaces) { +template +IGUANA_INLINE void skip_object_value(It &&it, It &&end, size_t min_spaces) { int subspace = min_spaces; while (it != end) { while (it != end && *it != '\n') { @@ -443,9 +472,8 @@ IGUANA_INLINE void skip_object_value(auto &&it, auto &&end, size_t min_spaces) { } // here : it == '\n' subspace = 0; - if (it != end) [[likely]] { - ++it; - } + if (it != end) + IGUANA_LIKELY { ++it; } while (it != end && *(it++) == ' ') { ++subspace; } @@ -458,7 +486,7 @@ IGUANA_INLINE void skip_object_value(auto &&it, auto &&end, size_t min_spaces) { } // namespace detail -template +template , int>> IGUANA_INLINE void from_yaml(T &value, It &&it, It &&end, size_t min_spaces) { auto spaces = skip_space_and_lines(it, end, min_spaces); while (it != end) { @@ -469,36 +497,40 @@ IGUANA_INLINE void from_yaml(T &value, It &&it, It &&end, size_t min_spaces) { 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()) [[likely]] { - std::visit( - [&](auto &&member_ptr) IGUANA__INLINE_LAMBDA { - using V = std::decay_t; - if constexpr (std::is_member_pointer_v) { - detail::parse_item(value.*member_ptr, it, end, spaces + 1); - } - else { - static_assert(!sizeof(V), "type not supported"); - } - }, - member_it->second); - } - else [[unlikely]] { + if (member_it != frozen_map.end()) + IGUANA_LIKELY { + std::visit( + [&](auto &&member_ptr) IGUANA__INLINE_LAMBDA { + using V = std::decay_t; + if constexpr (std::is_member_pointer_v) { + detail::parse_item(value.*member_ptr, it, end, spaces + 1); + } + else { + static_assert(!sizeof(V), "type not supported"); + } + }, + member_it->second); + } + else + IGUANA_UNLIKELY { #ifdef THROW_UNKNOWN_KEY - throw std::runtime_error("Unknown key: " + std::string(key)); + throw std::runtime_error("Unknown key: " + std::string(key)); #else - detail::skip_object_value(it, end, spaces + 1); + detail::skip_object_value(it, end, spaces + 1); #endif - } + } } auto subspaces = skip_space_and_lines(it, end, min_spaces); - if (subspaces < min_spaces) [[unlikely]] { - it -= subspaces + 1; - return; - } + if (subspaces < min_spaces) + IGUANA_UNLIKELY { + it -= subspaces + 1; + return; + } } } -template +template , int> = 0> IGUANA_INLINE void from_yaml(T &value, It &&it, It &&end) { detail::parse_item(value, it, end, 0); } @@ -514,12 +546,14 @@ IGUANA_INLINE void from_yaml(T &value, It &&it, It &&end, } } -template +template , int> = 0> IGUANA_INLINE void from_yaml(T &value, const View &view) { from_yaml(value, std::begin(view), std::end(view)); } -template +template , int> = 0> IGUANA_INLINE void from_yaml(T &value, const View &view, std::error_code &ec) noexcept { try { diff --git a/include/ylt/thirdparty/iguana/yaml_util.hpp b/include/ylt/thirdparty/iguana/yaml_util.hpp index 5c43f47fa..00e444614 100644 --- a/include/ylt/thirdparty/iguana/yaml_util.hpp +++ b/include/ylt/thirdparty/iguana/yaml_util.hpp @@ -1,209 +1,80 @@ #pragma once -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "define.h" -#include "reflection.hpp" -#include "value.hpp" +#include "util.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 -concept float_t = std::floating_point>; - -template -concept num_t = std::floating_point> || int_t; - -template -concept enum_t = std::is_enum_v>; - -template -constexpr inline bool is_basic_string_view = false; - -template -constexpr inline bool is_basic_string_view> = true; - -template -concept str_view_t = is_basic_string_view>; - -template -concept str_t = - std::convertible_to, std::string_view> && !str_view_t; - -template -concept string_t = std::convertible_to, std::string_view>; - -template -concept tuple_t = is_tuple>::value; - -template -concept sequence_container_t = - is_sequence_container>::value; - -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 -concept optional_t = requires(Type optional) { - optional.value(); - optional.has_value(); - optional.operator*(); - typename std::remove_cvref_t::value_type; -}; - -template -concept container = requires(Type container) { - typename std::remove_cvref_t::value_type; - container.size(); - container.begin(); - container.end(); -}; -template -concept map_container = container && requires(Type container) { - typename std::remove_cvref_t::mapped_type; -}; - -template -concept plain_t = - string_t || num_t || char_t || bool_t || enum_t; - -template -concept c_array = std::is_array_v> && - std::extent_v> > -0; - -template -concept array = requires(Type arr) { - arr.size(); - std::tuple_size>{}; -}; - -template -concept tuple = !array && requires(Type tuple) { - std::get<0>(tuple); - sizeof(std::tuple_size>); -}; - -template -concept refletable = is_reflection_v>; - -// TODO: support c_array -template -concept non_refletable = container || c_array || tuple || - optional_t || unique_ptr_t || std::is_fundamental_v; - -IGUANA_INLINE void skip_yaml_comment(auto &&it, auto &&end) { - while (++it != end && *it != '\n') - ; -} - -// match c and skip -template -IGUANA_INLINE void match(auto &&it, auto &&end) { - if (it == end || *it != c) [[unlikely]] { - static constexpr char b[] = {c, '\0'}; - std::string error = std::string("Expected:").append(b); - throw std::runtime_error(error); - } - else [[likely]] { - ++it; - } -} - // return true when it==end -IGUANA_INLINE bool skip_space_till_end(auto &&it, auto &&end) { - while (it != end && *it == ' ') ++it; +template +IGUANA_INLINE bool skip_space_till_end(It &&it, It &&end) { + while (it != end && *it < 33) ++it; return it == end; } // will not skip '\n' -IGUANA_INLINE auto skip_till_newline(auto &&it, auto &&end) { - if (it == end) [[unlikely]] { - return it; - } +template +IGUANA_INLINE auto skip_till_newline(It &&it, It &&end) { + if (it == end) + IGUANA_UNLIKELY { return it; } std::decay_t res = it; while ((it != end) && (*it != '\n')) { - if (*it == ' ') [[unlikely]] { - res = it; - while (it != end && *it == ' ') ++it; - } - else if (*it == '#') [[unlikely]] { - if (*(it - 1) == ' ') [[unlikely]] { - // it - 1 should be legal because this func is for parse value - while ((it != end) && *it != '\n') { + if (*it == ' ') + IGUANA_UNLIKELY { + res = it; + while (it != end && *it == ' ') ++it; + } + else if (*it == '#') + IGUANA_UNLIKELY { + if (*(it - 1) == ' ') { + // it - 1 should be legal because this func is for parse value + while ((it != end) && *it != '\n') { + ++it; + } + return res; + } + else { ++it; } - return res; } - } - else [[likely]] { - ++it; - } + else + IGUANA_LIKELY { ++it; } } return (*(it - 1) == ' ') ? res : it; } -template -IGUANA_INLINE auto skip_till(auto &&it, auto &&end) { - if (it == end) [[unlikely]] { - return it; - } +template +IGUANA_INLINE auto skip_till(It &&it, It &&end) { + if (it == end) + IGUANA_UNLIKELY { return it; } std::decay_t res = it; while ((it != end) && (!((... || (*it == C))))) { - if (*it == '\n') [[unlikely]] { - throw std::runtime_error("\\n is not expected"); - } - else if (*it == ' ') [[unlikely]] { - res = it; - while (it != end && *it == ' ') ++it; - } - else if (*it == '#') [[unlikely]] { - if (*(it - 1) == ' ') [[unlikely]] { - // it - 1 may be illegal - while ((it != end) && *it != '\n') { - ++it; - } - return res; + if (*it == '\n') + IGUANA_UNLIKELY { throw std::runtime_error("\\n is not expected"); } + else if (*it == ' ') + IGUANA_UNLIKELY { + res = it; + while (it != end && *it == ' ') ++it; } - } - else [[likely]] { - ++it; - } + else if (*it == '#') + IGUANA_UNLIKELY { + if (*(it - 1) == ' ') + IGUANA_UNLIKELY { + // it - 1 may be illegal + while ((it != end) && *it != '\n') { + ++it; + } + return res; + } + } + else + IGUANA_LIKELY { ++it; } } - if (it == end) [[unlikely]] { - static constexpr char b[] = {C..., '\0'}; - std::string error = std::string("Expected one of these: ").append(b); - throw std::runtime_error(error); - } + if (it == end) + IGUANA_UNLIKELY { + static constexpr char b[] = {C..., '\0'}; + std::string error = std::string("Expected one of these: ").append(b); + throw std::runtime_error(error); + } ++it; // skip return (*(it - 2) == ' ') ? res : it - 1; } @@ -211,9 +82,8 @@ IGUANA_INLINE auto skip_till(auto &&it, auto &&end) { // If there are '\n' , return indentation // If not, return minspaces + space // If Throw == true, check res < minspaces -template -IGUANA_INLINE size_t skip_space_and_lines(auto &&it, auto &&end, - size_t minspaces) { +template +IGUANA_INLINE size_t skip_space_and_lines(It &&it, It &&end, size_t minspaces) { size_t res = minspaces; while (it != end) { if (*it == '\n') { @@ -221,16 +91,17 @@ IGUANA_INLINE size_t skip_space_and_lines(auto &&it, auto &&end, res = 0; auto start = it; // skip the --- line - if ((it != end) && (*it == '-')) [[unlikely]] { - auto line_end = skip_till(it, end); - auto line = std::string_view( - &*start, static_cast(std::distance(start, line_end))); - if (line != "---") { - it = start; + if ((it != end) && (*it == '-')) + IGUANA_UNLIKELY { + auto line_end = skip_till(it, end); + auto line = std::string_view( + &*start, static_cast(std::distance(start, line_end))); + if (line != "---") { + it = start; + } } - } } - else if (*it == ' ') { + else if (*it == ' ' || *it == '\t') { ++it; ++res; } @@ -242,9 +113,8 @@ IGUANA_INLINE size_t skip_space_and_lines(auto &&it, auto &&end, } else { if constexpr (Throw) { - if (res < minspaces) [[unlikely]] { - throw std::runtime_error("Indentation problem"); - } + if (res < minspaces) + IGUANA_UNLIKELY { throw std::runtime_error("Indentation problem"); } } return res; } diff --git a/include/ylt/thirdparty/iguana/yaml_writer.hpp b/include/ylt/thirdparty/iguana/yaml_writer.hpp index 1a6117205..781f25bf4 100644 --- a/include/ylt/thirdparty/iguana/yaml_writer.hpp +++ b/include/ylt/thirdparty/iguana/yaml_writer.hpp @@ -5,24 +5,28 @@ namespace iguana { -template +template , int> = 0> IGUANA_INLINE void to_yaml(T &&t, Stream &s, size_t min_spaces = 0); -template +template , int> = 0> IGUANA_INLINE void render_yaml_value(Stream &ss, T &&t, size_t min_spaces) { ss.push_back('\n'); to_yaml(std::forward(t), ss, min_spaces); } // TODO: support more string style, support escape -template +template , int> = 0> IGUANA_INLINE void render_yaml_value(Stream &ss, T &&t, size_t min_spaces) { ss.append(t.data(), t.size()); if constexpr (appendLf) ss.push_back('\n'); } -template +template , int> = 0> IGUANA_INLINE void render_yaml_value(Stream &ss, T value, size_t min_spaces) { char temp[65]; auto p = detail::to_chars(temp, value); @@ -47,21 +51,41 @@ IGUANA_INLINE void render_yaml_value(Stream &ss, bool value, ss.push_back('\n'); } -template +template , int> = 0> IGUANA_INLINE void render_yaml_value(Stream &ss, T value, size_t min_spaces) { - render_yaml_value(ss, static_cast>(value), - min_spaces); + static constexpr auto enum_to_str = get_enum_map>(); + if constexpr (bool_v) { + render_yaml_value(ss, static_cast>(value), + min_spaces); + } + else { + auto it = enum_to_str.find(value); + if (it != enum_to_str.end()) + IGUANA_LIKELY { + auto str = it->second; + render_yaml_value(ss, std::string_view(str.data(), str.size()), + min_spaces); + } + else { + throw std::runtime_error( + std::to_string(static_cast>(value)) + + " is a missing value in enum_value"); + } + } } -template +template , int> = 0> IGUANA_INLINE void render_yaml_value(Stream &ss, const T &val, size_t min_spaces); -template +template , int> = 0> IGUANA_INLINE void render_yaml_value(Stream &ss, const T &val, size_t min_spaces); -template +template , int> = 0> IGUANA_INLINE void render_yaml_value(Stream &ss, const T &t, size_t min_spaces) { ss.push_back('\n'); @@ -72,7 +96,7 @@ IGUANA_INLINE void render_yaml_value(Stream &ss, const T &t, } } -template +template , int> = 0> IGUANA_INLINE void render_yaml_value(Stream &ss, T &&t, size_t min_spaces) { ss.push_back('\n'); for_each(std::forward(t), @@ -83,7 +107,8 @@ IGUANA_INLINE void render_yaml_value(Stream &ss, T &&t, size_t min_spaces) { }); } -template +template , int> = 0> IGUANA_INLINE void render_yaml_value(Stream &ss, const T &t, size_t min_spaces) { ss.push_back('\n'); @@ -95,7 +120,7 @@ IGUANA_INLINE void render_yaml_value(Stream &ss, const T &t, } } -template +template , int>> IGUANA_INLINE void render_yaml_value(Stream &ss, const T &val, size_t min_spaces) { if (!val) { @@ -107,7 +132,7 @@ IGUANA_INLINE void render_yaml_value(Stream &ss, const T &val, } } -template +template , int>> IGUANA_INLINE void render_yaml_value(Stream &ss, const T &val, size_t min_spaces) { if (!val) { @@ -124,7 +149,7 @@ constexpr auto write_yaml_key = [](auto &s, auto i, s.append(name.data(), name.size()); }; -template +template , int>> IGUANA_INLINE void to_yaml(T &&t, Stream &s, size_t min_spaces) { for_each(std::forward(t), [&t, &s, min_spaces](const auto &v, auto i) IGUANA__INLINE_LAMBDA { @@ -145,10 +170,11 @@ IGUANA_INLINE void to_yaml(T &&t, Stream &s, size_t min_spaces) { }); } -template +template , int> = 0> IGUANA_INLINE void to_yaml(T &&t, Stream &s) { - if constexpr (tuple_t || map_container || sequence_container_t || - optional_t || unique_ptr_t) + if constexpr (tuple_v || map_container_v || sequence_container_v || + optional_v || unique_ptr_v) render_yaml_value(s, std::forward(t), 0); else static_assert(!sizeof(T), "don't suppport this type"); diff --git a/src/struct_json/examples/main.cpp b/src/struct_json/examples/main.cpp index 3943116ab..2018d3df0 100644 --- a/src/struct_json/examples/main.cpp +++ b/src/struct_json/examples/main.cpp @@ -16,7 +16,7 @@ bool operator==(const person& a, const person& b) { REFLECTION(person, name, age); int main() { - person p{.name = "tom", .age = 20}; + person p{"tom", 20}; std::string str; // struct to json