diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index 8854ffbe5..eb7ddd22a 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -35,14 +35,16 @@ if (TARGET benchmark::benchmark) endif() endif() -if(SIMDJSON_STATIC_REFLECTION) - add_subdirectory(static_reflect) -endif(SIMDJSON_STATIC_REFLECTION) - include(CheckCXXCompilerFlag) check_cxx_compiler_flag("-std=c++20" SIMDJSON_COMPILER_SUPPORTS_CXX20) -if(SIMDJSON_EXCEPTIONS AND SIMDJSON_COMPILER_SUPPORTS_CXX20) - add_subdirectory(from) - add_subdirectory(car_builder) -endif() \ No newline at end of file +if(SIMDJSON_STATIC_REFLECTION) + add_subdirectory(static_reflect) +else() + if(SIMDJSON_EXCEPTIONS AND SIMDJSON_COMPILER_SUPPORTS_CXX20) + add_subdirectory(from) + add_subdirectory(car_builder) + endif() +endif(SIMDJSON_STATIC_REFLECTION) + + diff --git a/include/simdjson/base.h b/include/simdjson/base.h index e6e2ab9bf..a3cd949f0 100644 --- a/include/simdjson/base.h +++ b/include/simdjson/base.h @@ -10,6 +10,7 @@ #include "simdjson/error.h" #include "simdjson/portability.h" #include "simdjson/concepts.h" +#include "simdjson/constevalutil.h" /** * @brief The top level simdjson namespace, containing everything the library provides. diff --git a/include/simdjson/constevalutil.h b/include/simdjson/constevalutil.h new file mode 100644 index 000000000..84efc5187 --- /dev/null +++ b/include/simdjson/constevalutil.h @@ -0,0 +1,53 @@ +#ifndef SIMDJSON_CONSTEVALUTIL_H +#define SIMDJSON_CONSTEVALUTIL_H + +#include +#include +#include + +#if SIMDJSON_CONSTEVAL +namespace simdjson { +namespace constevalutil { + +constexpr static std::array json_quotable_character = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 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, 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, 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, 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, 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, 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, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +constexpr static std::array control_chars = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", + "\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", + "\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", + "\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b", + "\\u001c", "\\u001d", "\\u001e", "\\u001f"}; +// unoptimized, meant for compile-time execution +consteval std::string consteval_to_quoted_escaped(std::string_view input) { + std::string out = "\""; + for (char c : input) { + if (json_quotable_character[uint8_t(c)]) { + if (c == '"') { + out.append("\\\""); + } else if (c == '\\') { + out.append("\\\\"); + } else { + std::string_view v = control_chars[uint8_t(c)]; + out.append(v); + } + } else { + out.push_back(c); + } + } + out.push_back('"'); + return out; +} +} // namespace constevalutil +} // namespace simdjson +#endif // SIMDJSON_CONSTEVAL +#endif // SIMDJSON_CONSTEVALUTIL_H \ No newline at end of file diff --git a/include/simdjson/generic/ondemand/json_builder.h b/include/simdjson/generic/ondemand/json_builder.h index e773674b3..424d3f1d7 100644 --- a/include/simdjson/generic/ondemand/json_builder.h +++ b/include/simdjson/generic/ondemand/json_builder.h @@ -89,9 +89,6 @@ template requires(std::is_class_v && !container_but_not_string && @@ -106,10 +103,10 @@ template constexpr void atom(string_builder &b, const T &t) { int i = 0; b.append('{'); - [:expand(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked())):] >> [&]() { + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { if (i != 0) b.append(','); - constexpr auto key = std::define_static_string(consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); b.append_raw(key); b.append(':'); atom(b, t.[:dm:]); @@ -143,21 +140,16 @@ template requires(std::is_enum_v) void atom(string_builder &b, const T &e) { #if SIMDJSON_STATIC_REFLECTION - std::string_view result = ""; - [:expand(std::meta::enumerators_of(^^T)):] >> [&]{ + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(enum_val))); if (e == [:enum_val:]) { - result = std::meta::identifier_of(enum_val); + b.append_raw(enum_str); + return; } }; - - if (result != "") { - b.append_raw("\""); - b.append_raw(result); - b.append_raw("\""); - } else { - // Fallback to integer if enum value not found - atom(b, static_cast>(e)); - } + // Fallback to integer if enum value not found + atom(b, static_cast>(e)); #else // Fallback: serialize as integer if reflection not available atom(b, static_cast>(e)); @@ -241,10 +233,10 @@ template void append(string_builder &b, const Z &z) { int i = 0; b.append('{'); - [:expand(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked())):] >> [&]() { + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) { if (i != 0) b.append(','); - constexpr auto key = std::define_static_string(consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); b.append_raw(key); b.append(':'); atom(b, z.[:dm:]); diff --git a/include/simdjson/generic/ondemand/json_string_builder-inl.h b/include/simdjson/generic/ondemand/json_string_builder-inl.h index 25617f4c6..ed74bbdd5 100644 --- a/include/simdjson/generic/ondemand/json_string_builder-inl.h +++ b/include/simdjson/generic/ondemand/json_string_builder-inl.h @@ -206,29 +206,6 @@ inline size_t write_string_escaped(const std::string_view input, char *out) { return out - initout; } -#if SIMDJSON_CONSTEVAL -// unoptimized, meant for compile-time execution -consteval std::string consteval_to_quoted_escaped(std::string_view input) { - std::string out = "\""; - for (char c : input) { - if (json_quotable_character[uint8_t(c)]) { - if (c == '"') { - out.append("\\\""); - } else if (c == '\\') { - out.append("\\\\"); - } else { - std::string_view v = control_chars[uint8_t(c)]; - out.append(v); - } - } else { - out.push_back(c); - } - } - out.push_back('"'); - return out; -} -#endif // SIMDJSON_CONSTEVAL - simdjson_inline string_builder::string_builder(size_t initial_capacity) : buffer(new(std::nothrow) char[initial_capacity]), position(0), capacity(buffer.get() != nullptr ? initial_capacity : 0), diff --git a/include/simdjson/generic/ondemand/std_deserialize.h b/include/simdjson/generic/ondemand/std_deserialize.h index 827284000..96720ff59 100644 --- a/include/simdjson/generic/ondemand/std_deserialize.h +++ b/include/simdjson/generic/ondemand/std_deserialize.h @@ -275,32 +275,6 @@ constexpr bool user_defined_type = (std::is_class_v !concepts::appendable_containers && !require_custom_serialization); -// workaround from -// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2996r10.html#back-and-forth -// for missing expansion statements -namespace __impl { - template - struct replicator_type { - template - constexpr void operator>>(F body) const { - (body.template operator()(), ...); - } - }; - - template - replicator_type replicator = {}; -} - -template -consteval auto expand(R range) { - std::vector args; - for (auto r : range) { - args.push_back(reflect_constant(r)); - } - return substitute(^^__impl::replicator, args); -} -// end of workaround - template requires(user_defined_type && std::is_class_v) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { @@ -311,9 +285,8 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { SIMDJSON_TRY(val.get_object().get(obj)); } error_code e = simdjson::SUCCESS; - - [:expand(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked())):] >> [&]() { - if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + template for (constexpr auto mem : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); // Note: removed static assert as optional types are now handled generically // as long we are succesful or the field is not found, we continue @@ -332,16 +305,15 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { #if SIMDJSON_STATIC_REFLECTION std::string_view str; SIMDJSON_TRY(val.get_string().get(str)); - - bool found = false; - [:expand(std::meta::enumerators_of(^^T)):] >> [&]{ - if (!found && str == std::meta::identifier_of(enum_val)) { + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + if (str == std::meta::identifier_of(enum_val)) { out = [:enum_val:]; - found = true; + return SUCCESS; } }; - return found ? SUCCESS : INCORRECT_TYPE; + return INCORRECT_TYPE; #else // Fallback: deserialize as integer if reflection not available std::underlying_type_t int_val; diff --git a/tests/builder/static_reflection_enum_tests.cpp b/tests/builder/static_reflection_enum_tests.cpp index 22ceb37c7..9ea2411af 100644 --- a/tests/builder/static_reflection_enum_tests.cpp +++ b/tests/builder/static_reflection_enum_tests.cpp @@ -131,6 +131,7 @@ namespace builder_tests { // Deserialize ondemand::parser parser; + std::cout << json << std::endl; auto doc_result = parser.iterate(pad(json)); ASSERT_SUCCESS(doc_result);