diff --git a/doc/basics.md b/doc/basics.md index 3eefd2310..6627d9ff5 100644 --- a/doc/basics.md +++ b/doc/basics.md @@ -1346,6 +1346,34 @@ auto tag_invoke(deserialize_tag, simdjson_value &val, std::list& car) { With this code, deserializing an `std::list` instance would capture only the cars that are not made by Toyota. + + +For even more convenience, you can do it directly without a parser instance like so: + +```cpp +Car car = simdjson::from(json); +``` + +You can also use C++20 ranges to iterate over an array: + +```cpp + simdjson::padded_string json_cars = + R"( [ { "make": "Toyota", "model": "Camry", "year": 2018, + "tire_pressure": [ 40.1, 39.9 ] }, + { "make": "Kia", "model": "Soul", "year": 2012, + "tire_pressure": [ 30.1, 31.0 ] }, + { "make": "Toyota", "model": "Tercel", "year": 1999, + "tire_pressure": [ 29.8, 30.0 ] } + ])"_padded; + + for (Car car : simdjson::from(json_cars) | simdjson::as()) { + if (car.year < 1998) { + return false; + } + } +``` + + ### 3. Using static reflection (C++26) If you have a C++26 compatible compiler, you can compile @@ -1367,6 +1395,31 @@ simdjson::ondemand::document doc = parser.iterate(simdjson::pad(json)); Car c = doc.get(); ``` +Just like when using `tag_invoke` for custom types (but without the `tag_invoke` code), you can parse a class instance directly without a parser instance: + +```cpp +Car car = simdjson::from(json); +``` + +Similarly, you can also use C++20 ranges to iterate over an array: + +```cpp + simdjson::padded_string json_cars = + R"( [ { "make": "Toyota", "model": "Camry", "year": 2018, + "tire_pressure": [ 40.1, 39.9 ] }, + { "make": "Kia", "model": "Soul", "year": 2012, + "tire_pressure": [ 30.1, 31.0 ] }, + { "make": "Toyota", "model": "Tercel", "year": 1999, + "tire_pressure": [ 29.8, 30.0 ] } + ])"_padded; + + for (Car car : simdjson::from(json_cars) | simdjson::as()) { + if (car.year < 1998) { + return false; + } + } +``` + You can also automatically serialize the `Car` instance to a JSON string, see our [Builder documentation](builder.md). diff --git a/doc/builder.md b/doc/builder.md index 210ee236c..4a498e643 100644 --- a/doc/builder.md +++ b/doc/builder.md @@ -165,7 +165,7 @@ automatically. In most cases, it should work automatically: In some instances, you might want to create a string directly from your own data type. You can create a string directly, without an explicit `string_builder` instance -with the `simdjson::builder::to_json_string` function. +with the `simdjson::to_json` template function. (Under the hood a `string_builder` instance may still be created.) ```cpp @@ -178,12 +178,12 @@ with the `simdjson::builder::to_json_string` function. void f() { Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}}; - std::string json = simdjson::builder::to_json_string(c); + std::string json = simdjson::to_json(c); } ``` If you know the output size, in bytes, of your JSON string, you may -pass it as a second parameter (e.g., `simdjson::builder::to_json_string(c, 31123)`). +pass it as a second parameter (e.g., `simdjson::to_json(c, 31123)`). @@ -194,7 +194,7 @@ pattern: ```cpp std::string json; - if(simdjson::builder::to_json_string(c).get(json)) { + if(simdjson::to(c).get(json)) { // there was an error } else { // json contain the serialized JSON diff --git a/include/simdjson.h b/include/simdjson.h index 194435f52..7d70ea5dd 100644 --- a/include/simdjson.h +++ b/include/simdjson.h @@ -53,4 +53,5 @@ #include "simdjson/dom.h" #include "simdjson/ondemand.h" +#include "simdjson/convert.h" #endif // SIMDJSON_H diff --git a/include/simdjson/convert.h b/include/simdjson/convert.h new file mode 100644 index 000000000..652f14e9e --- /dev/null +++ b/include/simdjson/convert.h @@ -0,0 +1,307 @@ +#ifndef SIMDJSON_CONVERT_H +#define SIMDJSON_CONVERT_H +#if __cpp_concepts + +#include "simdjson/ondemand.h" +#include +#ifdef __cpp_lib_ranges +#include +#endif + +namespace simdjson { + +struct [[nodiscard]] auto_iterator_end {}; + +/** + * A Wrapper for simdjson_result in order to make it + * compatible with ranges (to satisfy std::ranges::input_range). + */ +struct [[nodiscard]] auto_iterator { + using iterator_category = std::forward_iterator_tag; + using type = simdjson_result; + using value_type = simdjson_result; // type::value_type + using reference = value_type &; + using const_reference = const value_type &; + using difference_type = std::ptrdiff_t; + + struct auto_iterator_storage { + type m_iter{}; + mutable value_type m_value{}; + }; + +private: + auto_iterator_storage *m_storage = nullptr; + +public: + constexpr auto_iterator() noexcept = default; + explicit auto_iterator(auto_iterator_storage &storage) noexcept + : m_storage{&storage} {}; + auto_iterator(auto_iterator const &) = default; + auto_iterator(auto_iterator &&) = default; + auto_iterator &operator=(auto_iterator const &) = default; + auto_iterator &operator=(auto_iterator &&) noexcept = default; + ~auto_iterator() = default; + + reference operator*() const noexcept { return m_storage->m_value; } + reference operator*() noexcept { return m_storage->m_value; } + + auto_iterator &operator++() noexcept { + ++m_storage->m_iter; + m_storage->m_value = + m_storage->m_iter.at_end() || m_storage->m_iter.error() != SUCCESS + ? value_type{} + : *m_storage->m_iter; + return *this; + } + auto_iterator operator++(int) noexcept { + auto_iterator const tmp = *this; + operator++(); + return tmp; + } + + [[nodiscard]] bool operator==(auto_iterator const &other) const noexcept { + return m_storage == other.m_storage && + m_storage->m_iter == other.m_storage->m_iter; + } + + [[nodiscard]] bool operator==(auto_iterator_end) const noexcept { + return m_storage != nullptr && m_storage->m_iter.at_end(); + } +}; + +template +struct [[nodiscard]] auto_parser +#if __cpp_lib_ranges + : std::ranges::view_interface> +#endif +{ + using value_type = simdjson_result; + using size_type = size_t; + using difference_type = std::ptrdiff_t; + using pointer = value_type *; + using const_pointer = const value_type *; + using reference = value_type &; + using const_reference = const value_type &; + using iterator = auto_iterator; + using const_iterator = auto_iterator; // auto_iterator is already const + +private: + ParserType m_parser; + ondemand::document m_doc; + error_code m_error{SUCCESS}; + + // Caching the iterator here: + iterator::auto_iterator_storage iter_storage{}; + + template + static constexpr bool is_nothrow_gettable = requires(ondemand::document doc) { + { doc.get() } noexcept; + }; + +public: + // non-pointer constructors: + explicit auto_parser(ParserType &&parser, ondemand::document &&doc) noexcept + requires(!std::is_pointer_v) + : m_parser{std::move(parser)}, m_doc{std::move(doc)} {} + + explicit auto_parser(ParserType &&parser, + padded_string_view const str) noexcept + requires(!std::is_pointer_v) + : m_parser{std::move(parser)}, m_doc{}, m_error{SUCCESS} { + m_error = m_parser.iterate(str).get(m_doc); + } + + explicit auto_parser(padded_string_view const str) noexcept + requires(!std::is_pointer_v) + : auto_parser{ParserType{}, str} {} + + // pointer constructors: + explicit auto_parser(std::remove_pointer_t &parser, + ondemand::document &&doc) noexcept + requires(std::is_pointer_v) + : m_parser{&parser}, m_doc{std::move(doc)} {} + + explicit auto_parser(std::remove_pointer_t &parser, + padded_string_view const str) noexcept + requires(std::is_pointer_v) + : m_parser{&parser}, m_doc{}, m_error{SUCCESS} { + m_error = m_parser->iterate(str).get(m_doc); + } + + explicit auto_parser(ParserType parser, ondemand::document &&doc) noexcept + requires(std::is_pointer_v) + : auto_parser{*parser, std::move(doc)} {} + + auto_parser(auto_parser const &) = delete; + auto_parser &operator=(auto_parser const &) = delete; + auto_parser(auto_parser &&) noexcept = default; + auto_parser &operator=(auto_parser &&) noexcept = default; + ~auto_parser() = default; + + /// Get the parser + [[nodiscard]] std::remove_pointer_t &parser() noexcept { + if constexpr (std::is_pointer_v) { + return *m_parser; + } else { + return m_parser; + } + } + + template + [[nodiscard]] simdjson_inline simdjson_result + result() noexcept(is_nothrow_gettable) { + if (m_error != SUCCESS) { + return m_error; + } + // For array and object types, we need to be at the start of the document + return m_doc.get(); + } + + [[nodiscard]] simdjson_inline simdjson_result + array() noexcept { + return result(); + } + + [[nodiscard]] simdjson_inline simdjson_result + object() noexcept { + return result(); + } + + [[nodiscard]] simdjson_inline simdjson_result + number() noexcept { + return result(); + } + + template + [[nodiscard]] simdjson_inline explicit(false) + operator simdjson_result() noexcept(is_nothrow_gettable) { + return result(); + } + + template + [[nodiscard]] simdjson_inline explicit(false) operator T() noexcept(false) { + if (m_error != SUCCESS) { + throw simdjson_error(m_error); + } + return m_doc.get(); + } + + // We can't have "operator std::optional" because it would create an + // ambiguity for the compiler. + // We also cannot have "operator T*" without manual memory management. + // We also cannot have "operator T&" without manual memory management either. + + template + [[nodiscard]] simdjson_inline std::optional + optional() noexcept(is_nothrow_gettable) { + if (m_error != SUCCESS) { + return std::nullopt; + } + T value; + // For std::optional + if (m_doc.get().get(value)) [[unlikely]] { + return std::nullopt; + } + return {std::move(value)}; + } + + simdjson_inline auto_iterator begin() noexcept { + if (m_error != SUCCESS) { + // Create an iterator with the error + iter_storage.m_iter = iterator::type(m_error); + iter_storage.m_value = value_type{}; + return auto_iterator{iter_storage}; + } + if (iter_storage.m_iter.error() != SUCCESS && + !iter_storage.m_iter.at_end()) { + // Try to get the document as an array + ondemand::array arr; + if(auto error = m_doc.get_array().get(arr); error == SUCCESS) { + iter_storage = {.m_iter = iterator::type{arr.begin()}, + .m_value = iterator::value_type{ + iter_storage.m_iter.at_end() || + iter_storage.m_iter.error() != SUCCESS + ? value_type{} + : *iter_storage.m_iter}}; + } else { + // If it's not an array, create an error iterator + iter_storage.m_iter = iterator::type(error); + iter_storage.m_value = value_type{}; + } + } + return auto_iterator{iter_storage}; + } + simdjson_inline auto_iterator_end end() noexcept { return {}; } +}; + +#ifdef __cpp_lib_ranges + +// For C++20, we implement our own pipe operator since range_adaptor_closure is C++23 +static constexpr struct [[nodiscard]] no_errors_adaptor { + + [[nodiscard]] bool + operator()(simdjson_result const &val) const noexcept { + return val.error() == SUCCESS; + } + + template + auto operator()(Range &&rng) const noexcept { + return std::forward(rng) | std::views::filter(*this); + } +} no_errors; + +template +struct [[nodiscard]] to_adaptor { + + /// Convert to T + [[nodiscard]] T + operator()(simdjson_result &val) const noexcept { + return val.get(); + } + + /// Make it an adaptor + template + auto operator()(Range &&rng) const noexcept { + return std::forward(rng) | no_errors | std::views::transform(*this); + } + + /** + * Parse input string into any object if possible. + */ + auto operator()(padded_string_view const str) const noexcept { + return auto_parser{str}; + } + + /** + * Parse the input using the specified parser into any object if possible. + */ + auto operator()(ondemand::parser &parser, + padded_string_view const str) const noexcept { + return auto_parser{parser, str}; + } +}; + +template static constexpr to_adaptor to{}; + +static constexpr to_adaptor<> from{}; + +template +using as = to_adaptor; + +// For C++20 ranges without range_adaptor_closure, we need to define pipe operators +template +inline auto operator|(Range&& range, const no_errors_adaptor& adaptor) { + return adaptor(std::forward(range)); +} + +template +inline auto operator|(Range&& range, const to_adaptor& adaptor) { + return adaptor(std::forward(range)); +} + +#endif // __cpp_lib_ranges + +} // namespace simdjson + +#endif // __cpp_concepts +#endif // SIMDJSON_CONVERT_H diff --git a/include/simdjson/generic/implementation_simdjson_result_base.h b/include/simdjson/generic/implementation_simdjson_result_base.h index aaf2bce12..72b02f549 100644 --- a/include/simdjson/generic/implementation_simdjson_result_base.h +++ b/include/simdjson/generic/implementation_simdjson_result_base.h @@ -122,6 +122,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ diff --git a/include/simdjson/generic/ondemand/array_iterator-inl.h b/include/simdjson/generic/ondemand/array_iterator-inl.h index 6e4ba8140..d667fa6c0 100644 --- a/include/simdjson/generic/ondemand/array_iterator-inl.h +++ b/include/simdjson/generic/ondemand/array_iterator-inl.h @@ -36,6 +36,9 @@ simdjson_inline array_iterator &array_iterator::operator++() noexcept { return *this; } +simdjson_inline bool array_iterator::at_end() const noexcept { + return iter.at_end(); +} } // namespace ondemand } // namespace SIMDJSON_IMPLEMENTATION } // namespace simdjson @@ -72,7 +75,9 @@ simdjson_inline simdjson_result::at_end() const noexcept { + return !first.iter.is_valid() || first.at_end(); +} } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H \ No newline at end of file diff --git a/include/simdjson/generic/ondemand/array_iterator.h b/include/simdjson/generic/ondemand/array_iterator.h index 0957be9c7..4a0bbb84a 100644 --- a/include/simdjson/generic/ondemand/array_iterator.h +++ b/include/simdjson/generic/ondemand/array_iterator.h @@ -34,7 +34,8 @@ public: * * Part of the std::iterator interface. */ - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result + operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. /** * Check if we are at the end of the JSON. * @@ -58,6 +59,11 @@ public: */ simdjson_inline array_iterator &operator++() noexcept; + /** + * Check if the array is at the end. + */ + [[nodiscard]] simdjson_inline bool at_end() const noexcept; + private: value_iterator iter{}; @@ -76,7 +82,6 @@ namespace simdjson { template<> struct simdjson_result : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base { -public: simdjson_inline simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::array_iterator &&value) noexcept; ///< @private simdjson_inline simdjson_result(error_code error) noexcept; ///< @private simdjson_inline simdjson_result() noexcept = default; @@ -89,6 +94,8 @@ public: simdjson_inline bool operator==(const simdjson_result &) const noexcept; simdjson_inline bool operator!=(const simdjson_result &) const noexcept; simdjson_inline simdjson_result &operator++() noexcept; + + [[nodiscard]] simdjson_inline bool at_end() const noexcept; }; } // namespace simdjson diff --git a/include/simdjson/generic/ondemand/json_builder.h b/include/simdjson/generic/ondemand/json_builder.h index 5298b5ca3..437844817 100644 --- a/include/simdjson/generic/ondemand/json_builder.h +++ b/include/simdjson/generic/ondemand/json_builder.h @@ -296,6 +296,11 @@ string_builder& operator<<(string_builder& b, const Z& z) { } } // namespace builder } // namespace SIMDJSON_IMPLEMENTATION +// Alias the function template to 'to' in the global namespace +template +simdjson_result to_json(const Z &z, size_t initial_capacity = 1024) { + return SIMDJSON_IMPLEMENTATION::builder::to_json_string(z, initial_capacity); +} } // namespace simdjson #endif // SIMDJSON_STATIC_REFLECTION diff --git a/include/simdjson/generic/ondemand/json_iterator-inl.h b/include/simdjson/generic/ondemand/json_iterator-inl.h index 6a054af81..dc8c4bce1 100644 --- a/include/simdjson/generic/ondemand/json_iterator-inl.h +++ b/include/simdjson/generic/ondemand/json_iterator-inl.h @@ -218,6 +218,8 @@ simdjson_inline void json_iterator::assert_valid_position(token_position positio #ifndef SIMDJSON_CLANG_VISUAL_STUDIO SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] ); SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] ); +#else + (void)position; // Suppress unused parameter warning #endif } diff --git a/include/simdjson/generic/ondemand/std_deserialize.h b/include/simdjson/generic/ondemand/std_deserialize.h index 7c8f518af..049b7aef9 100644 --- a/include/simdjson/generic/ondemand/std_deserialize.h +++ b/include/simdjson/generic/ondemand/std_deserialize.h @@ -11,7 +11,7 @@ #include #include #if SIMDJSON_STATIC_REFLECTION -#include +#include // #include // for std::define_static_string - header not available yet #endif diff --git a/p2996/Dockerfile b/p2996/Dockerfile index dd7b58891..79f368c01 100644 --- a/p2996/Dockerfile +++ b/p2996/Dockerfile @@ -8,7 +8,11 @@ RUN apt-get update && \ apt-get install -y --no-install-recommends ca-certificates gnupg \ build-essential cmake make python3 zlib1g wget subversion unzip ninja-build git linux-perf && \ rm -rf /var/lib/apt/lists/* -RUN git clone --depth=1 --branch p2996 https://github.com/bloomberg/clang-p2996.git /tmp/clang-source +ARG CLANG_COMMIT=d77eff1cbd78fd065668acf93b1f5f400d39134d +RUN git clone --depth=1 https://github.com/bloomberg/clang-p2996.git /tmp/clang-source && \ + cd /tmp/clang-source && \ + git fetch origin $CLANG_COMMIT --depth=1 && \ + git checkout $CLANG_COMMIT RUN cmake -S /tmp/clang-source/llvm -B /tmp/clang-source/build-llvm -DCMAKE_BUILD_TYPE=Release \ -DLLVM_ENABLE_ASSERTIONS=ON \ -DLLVM_UNREACHABLE_OPTIMIZE=ON \ diff --git a/p2996/README.md b/p2996/README.md index 48f1cdfd0..efb829b1a 100644 --- a/p2996/README.md +++ b/p2996/README.md @@ -64,6 +64,7 @@ cmake --build buildreflect --target benchmark_serialization_citm_catalog benchma 6. Run the tests... ```bash +cmake --build buildreflect ctest --test-dir buildreflect --output-on-failure ``` diff --git a/singleheader/simdjson.cpp b/singleheader/simdjson.cpp index 7724757b3..05540f5ee 100644 --- a/singleheader/simdjson.cpp +++ b/singleheader/simdjson.cpp @@ -1,4 +1,4 @@ -/* auto-generated on 2025-07-14 15:43:52 -0400. Do not edit! */ +/* auto-generated on 2025-08-05 16:29:59 +0000. version 4.0.0 Do not edit! */ /* including simdjson.cpp: */ /* begin file simdjson.cpp */ #define SIMDJSON_SRC_SIMDJSON_CPP @@ -577,17 +577,6 @@ double from_chars(const char *first, const char* end) noexcept; // We assume by default static linkage #define SIMDJSON_DLLIMPORTEXPORT #endif - -/** - * Workaround for the vcpkg package manager. Only vcpkg should - * ever touch the next line. The SIMDJSON_USING_LIBRARY macro is otherwise unused. - */ -#if SIMDJSON_USING_LIBRARY -#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport) -#endif -/** - * End of workaround for the vcpkg package manager. - */ #else #define SIMDJSON_DLLIMPORTEXPORT #endif @@ -2444,6 +2433,18 @@ namespace std { #define SIMDJSON_AVX512_ALLOWED 1 #endif + +#ifndef __has_cpp_attribute +#define simdjson_lifetime_bound +#elif __has_cpp_attribute(msvc::lifetimebound) +#define simdjson_lifetime_bound [[msvc::lifetimebound]] +#elif __has_cpp_attribute(clang::lifetimebound) +#define simdjson_lifetime_bound [[clang::lifetimebound]] +#elif __has_cpp_attribute(lifetimebound) +#define simdjson_lifetime_bound [[lifetimebound]] +#else +#define simdjson_lifetime_bound +#endif #endif // SIMDJSON_COMMON_DEFS_H /* end file simdjson/common_defs.h */ /* skipped duplicate #include "simdjson/compiler_check.h" */ @@ -2908,7 +2909,6 @@ concept optional_type = requires(std::remove_cvref_t obj) { { obj.value() } -> std::same_as::value_type&>; requires requires(typename std::remove_cvref_t::value_type &&val) { obj.emplace(std::move(val)); - obj = std::move(val); { obj.value_or(val) } -> std::convertible_to::value_type>; @@ -9170,6 +9170,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -15609,6 +15610,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -21903,6 +21905,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -28354,6 +28357,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -35164,6 +35168,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -41796,6 +41801,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -47874,6 +47880,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -53544,6 +53551,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -56568,10 +56576,78 @@ simdjson_inline void validate_utf8_character() { idx += 4; } +static const uint8_t CHAR_TYPE_SPACE = 1 << 0; +static const uint8_t CHAR_TYPE_OPERATOR = 1 << 1; +static const uint8_t CHAR_TYPE_ESC_ASCII = 1 << 2; +static const uint8_t CHAR_TYPE_NON_ASCII = 1 << 3; + +const uint8_t char_table[256] = { + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x04, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 +}; + +simdjson_inline bool char_is_type(uint8_t c, uint8_t type) { + return (char_table[c] & type); +} + +simdjson_inline bool char_is_space(uint8_t c) { + return char_is_type(c, CHAR_TYPE_SPACE); +} + +simdjson_inline bool char_is_operator(uint8_t c) { + return char_is_type(c, CHAR_TYPE_OPERATOR); +} + +simdjson_inline bool char_is_space_or_operator(uint8_t c) { + return char_is_type(c, CHAR_TYPE_SPACE | CHAR_TYPE_OPERATOR); +} + +simdjson_inline bool char_is_ascii_stop(uint8_t c) { + return char_is_type(c, CHAR_TYPE_ESC_ASCII | CHAR_TYPE_NON_ASCII); +} + // Returns true if the string is unclosed. simdjson_inline bool validate_string() { idx++; // skip first quote - while (idx < len && buf[idx] != '"') { + while (idx < len) { + do { + if (char_is_ascii_stop(buf[idx])) { break; } + idx++; + } while (idx < len); + if (idx >= len) { return true; } + if (buf[idx] == '"') { + return false; + } if (buf[idx] == '\\') { idx += 2; } else if (simdjson_unlikely(buf[idx] & 0x80)) { @@ -56585,43 +56661,31 @@ simdjson_inline bool validate_string() { return false; } -simdjson_inline bool is_whitespace_or_operator(uint8_t c) { - switch (c) { - case '{': case '}': case '[': case ']': case ',': case ':': - case ' ': case '\r': case '\n': case '\t': - return true; - default: - return false; - } -} - // // Parse the entire input in STEP_SIZE-byte chunks. // simdjson_inline error_code scan() { bool unclosed_string = false; for (;idx= len) { break; } + // String + if (buf[idx] == '"') { + add_structural(); + unclosed_string |= validate_string(); + // Operator + } else if (char_is_operator(buf[idx])) { + add_structural(); + // Primitive or invalid character (invalid characters will be checked in stage 2) + } else { + // Anything else, add the structural and go until we find the next one + add_structural(); + while (idx+1 obj) { { obj.value() } -> std::same_as::value_type&>; requires requires(typename std::remove_cvref_t::value_type &&val) { obj.emplace(std::move(val)); - obj = std::move(val); { obj.value_or(val) } -> std::convertible_to::value_type>; @@ -4280,9 +4280,9 @@ inline const char *padded_string::data() const noexcept { return data_ptr; } inline char *padded_string::data() noexcept { return data_ptr; } -inline padded_string::operator std::string_view() const { return std::string_view(data(), length()); } +inline padded_string::operator std::string_view() const simdjson_lifetime_bound { return std::string_view(data(), length()); } -inline padded_string::operator padded_string_view() const noexcept { +inline padded_string::operator padded_string_view() const noexcept simdjson_lifetime_bound { return padded_string_view(data(), length(), length() + SIMDJSON_PADDING); } @@ -6743,6 +6743,9 @@ protected: * by a "formatter" which handles the details. Thus * the string_builder template could support both minification * and prettification, and various other tradeoffs. + * + * This is not to be confused with the simdjson::builder::string_builder + * which is a different class. */ template class string_builder { @@ -11584,6 +11587,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -13725,6 +13729,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -16365,6 +16370,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -19005,6 +19011,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -21760,6 +21767,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -24831,6 +24839,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -27379,6 +27388,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -29940,6 +29950,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -32440,7 +32451,7 @@ template concept custom_deserializable = tag_invocable; template -concept deserializable = custom_deserializable || is_builtin_deserializable_v; +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; template concept nothrow_custom_deserializable = nothrow_tag_invocable; @@ -33079,6 +33090,19 @@ public: #if SIMDJSON_SUPPORTS_DESERIALIZATION if constexpr (custom_deserializable) { return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_t::value_type; + + // Check if the value is null + if (is_null()) { + out.reset(); // Set to nullopt + return SUCCESS; + } + + if (!out) { + out.emplace(); + } + return get(out.value()); } else { static_assert(!sizeof(T), "The get method with type T is not implemented by the simdjson library. " "And you do not seem to have added support for it. Indeed, we have that " @@ -35480,7 +35504,8 @@ public: * * Part of the std::iterator interface. */ - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result + operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. /** * Check if we are at the end of the JSON. * @@ -35504,6 +35529,11 @@ public: */ simdjson_inline array_iterator &operator++() noexcept; + /** + * Check if the array is at the end. + */ + [[nodiscard]] simdjson_inline bool at_end() const noexcept; + private: value_iterator iter{}; @@ -35522,7 +35552,6 @@ namespace simdjson { template<> struct simdjson_result : public arm64::implementation_simdjson_result_base { -public: simdjson_inline simdjson_result(arm64::ondemand::array_iterator &&value) noexcept; ///< @private simdjson_inline simdjson_result(error_code error) noexcept; ///< @private simdjson_inline simdjson_result() noexcept = default; @@ -35535,6 +35564,8 @@ public: simdjson_inline bool operator==(const simdjson_result &) const noexcept; simdjson_inline bool operator!=(const simdjson_result &) const noexcept; simdjson_inline simdjson_result &operator++() noexcept; + + [[nodiscard]] simdjson_inline bool at_end() const noexcept; }; } // namespace simdjson @@ -35867,7 +35898,7 @@ public: * time it parses a document or when it is destroyed. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator std::string_view() noexcept(false); + simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a raw_json_string. * @@ -35876,7 +35907,7 @@ public: * @returns A pointer to the raw JSON for the given string. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator raw_json_string() noexcept(false); + simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a bool. * @@ -37483,6 +37514,20 @@ inline simdjson_result to_json_string(simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); + +#if SIMDJSON_STATIC_REFLECTION +/** + * Create a JSON string from any user-defined type using static reflection. + * Only available when SIMDJSON_STATIC_REFLECTION is enabled. + */ +template + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +inline std::string to_json_string(const T& obj); +#endif + } // namespace simdjson /** @@ -37804,17 +37849,16 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser /** * This CPO (Customization Point Object) will help deserialize into optional types. */ -template +template requires(!require_custom_serialization) -error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { using value_type = typename std::remove_cvref_t::value_type; - static_assert( - deserializable, - "The specified type inside the unique_ptr must itself be deserializable"); - static_assert( - std::is_default_constructible_v, - "The specified type inside the unique_ptr must default constructible."); + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullopt + return SUCCESS; + } if (!out) { out.emplace(); @@ -37853,7 +37897,7 @@ template consteval auto expand(R range) { std::vector args; for (auto r : range) { - args.push_back(reflect_value(r)); + args.push_back(reflect_constant(r)); } return substitute(^^__impl::replicator, args); } @@ -37873,17 +37917,42 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { [: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)) { constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); - static_assert( - deserializable, - "The specified type inside the class must itself be deserializable"); + // Note: removed static assert as optional types are now handled generically // as long we are succesful or the field is not found, we continue if(e == simdjson::SUCCESS || e == simdjson::NO_SUCH_FIELD) { - obj[key].get(out.[:mem:]); + e = obj[key].get(out.[:mem:]); } } }; return e; } + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +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)) { + out = [:enum_val:]; + found = true; + } + }; + + return found ? SUCCESS : INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + template requires(user_defined_type>) error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { @@ -37922,6 +37991,10 @@ error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr & // Unique pointers //////////////////////////////////////// error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -37931,6 +38004,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) no } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -37940,6 +38017,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -37949,6 +38030,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -37958,6 +38043,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -37971,6 +38060,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -37980,6 +38073,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) no } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -37989,6 +38086,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -37998,6 +38099,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -38007,6 +38112,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -38016,6 +38125,61 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_shared(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(temp); + return SUCCESS; +} + } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H @@ -38303,6 +38467,9 @@ simdjson_inline array_iterator &array_iterator::operator++() noexcept { return *this; } +simdjson_inline bool array_iterator::at_end() const noexcept { + return iter.at_end(); +} } // namespace ondemand } // namespace arm64 } // namespace simdjson @@ -38339,7 +38506,9 @@ simdjson_inline simdjson_result &simdjson_resul ++(first); return *this; } - +simdjson_inline bool simdjson_result::at_end() const noexcept { + return !first.iter.is_valid() || first.at_end(); +} } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H @@ -39097,8 +39266,8 @@ simdjson_inline document::operator object() & noexcept(false) { return get_objec simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); } simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } simdjson_inline document::operator value() noexcept(false) { return get_value(); } @@ -40655,6 +40824,8 @@ simdjson_inline void json_iterator::assert_valid_position(token_position positio #ifndef SIMDJSON_CLANG_VISUAL_STUDIO SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] ); SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] ); +#else + (void)position; // Suppress unused parameter warning #endif } @@ -42079,6 +42250,9 @@ simdjson_inline simdjson_warn_unused simdjson_result simdjson_ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/serialization.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_builder.h" */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -43525,7 +43699,7 @@ namespace builder { * supports atomic types (Booleans, strings), it does not support composed * types (arrays and objects). * - * Ultimately, this class should support kernel-specific optimizations. E.g., + * Ultimately, this class can support kernel-specific optimizations. E.g., * it may make use of SIMD instructions to escape strings faster. */ class string_builder { @@ -43652,7 +43826,7 @@ public: * The result may not be valid UTF-8 if some of your content was not valid UTF-8. * Use validate_unicode() to check the content if needed. */ - simdjson_inline operator std::string_view() const noexcept(false); + simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound; #endif /** @@ -44027,9 +44201,9 @@ simdjson_inline void string_builder::clear() noexcept { namespace internal { // We could specialize further for 32-bit integers. -int int_log2(uint32_t x) { return (63 - leading_zeroes(x | 1)); } +simdjson_really_inline int int_log2(uint32_t x) { return (63 - leading_zeroes(x | 1)); } -int fast_digit_count(uint32_t x) { +simdjson_really_inline int fast_digit_count(uint32_t x) { static uint64_t table[] = { 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, @@ -44041,9 +44215,9 @@ int fast_digit_count(uint32_t x) { return uint32_t((x + table[int_log2(x)]) >> 32); } -int int_log2(uint64_t x) { return 63 - leading_zeroes(x | 1); } +simdjson_really_inline int int_log2(uint64_t x) { return 63 - leading_zeroes(x | 1); } -int fast_digit_count(uint64_t x) { +simdjson_really_inline int fast_digit_count(uint64_t x) { static uint64_t table[] = {9, 99, 999, @@ -44070,7 +44244,7 @@ int fast_digit_count(uint64_t x) { template ::value>::type> -simdjson_inline size_t digit_count(number_type v) noexcept { +simdjson_really_inline size_t digit_count(number_type v) noexcept { static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || sizeof(number_type) == 2 || sizeof(number_type) == 1, "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); @@ -44240,7 +44414,7 @@ simdjson_inline string_builder::operator std::string() const noexcept(false) { } simdjson_inline string_builder::operator std::string_view() const - noexcept(false) { + noexcept(false) simdjson_lifetime_bound { return view(); } #endif @@ -44358,6 +44532,8 @@ simdjson_inline void string_builder::append_key_value(key_type key, value_type v #include #include #include +#include +#include #include #include #include @@ -44438,8 +44614,13 @@ consteval std::string consteval_to_quoted_escaped(std::string_view input); template requires(std::is_class_v && !container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && !std::is_same_v && - !std::is_same_v) + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) constexpr void atom(string_builder &b, const T &t) { int i = 0; b.append('{'); @@ -44455,8 +44636,127 @@ constexpr void atom(string_builder &b, const T &t) { b.append('}'); } +// Support for optional types (std::optional, etc.) +template +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +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)):] >> [&]{ + if (e == [:enum_val:]) { + result = std::meta::identifier_of(enum_val); + } + }; + + 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)); + } +#else + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); +#endif +} + +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); + } + first = false; + atom(b, item); + } + b.append(']'); +} + +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + // works for struct -template void append(string_builder &b, const Z &z) { +template + requires(std::is_class_v && !container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) +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())):] >> [&]() { @@ -44489,8 +44789,8 @@ void append(string_builder &b, const Z &z) { } template -simdjson_result to_json_string(const Z &z) { - string_builder b; +simdjson_result to_json_string(const Z &z, size_t initial_capacity = 1024) { + string_builder b(initial_capacity); append(b, z); std::string_view s; if(auto e = b.view().get(s); e) { return e; } @@ -44506,7 +44806,13 @@ simdjson_error to_json(const Z &z, std::string &s) { s.assign(view); return SUCCESS; } -} // namespace json_builder + +template +string_builder& operator<<(string_builder& b, const Z& z) { + append(b, z); + return b; +} +} // namespace builder } // namespace arm64 } // namespace simdjson #endif // SIMDJSON_STATIC_REFLECTION @@ -44896,7 +45202,7 @@ template concept custom_deserializable = tag_invocable; template -concept deserializable = custom_deserializable || is_builtin_deserializable_v; +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; template concept nothrow_custom_deserializable = nothrow_tag_invocable; @@ -45535,6 +45841,19 @@ public: #if SIMDJSON_SUPPORTS_DESERIALIZATION if constexpr (custom_deserializable) { return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_t::value_type; + + // Check if the value is null + if (is_null()) { + out.reset(); // Set to nullopt + return SUCCESS; + } + + if (!out) { + out.emplace(); + } + return get(out.value()); } else { static_assert(!sizeof(T), "The get method with type T is not implemented by the simdjson library. " "And you do not seem to have added support for it. Indeed, we have that " @@ -47936,7 +48255,8 @@ public: * * Part of the std::iterator interface. */ - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result + operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. /** * Check if we are at the end of the JSON. * @@ -47960,6 +48280,11 @@ public: */ simdjson_inline array_iterator &operator++() noexcept; + /** + * Check if the array is at the end. + */ + [[nodiscard]] simdjson_inline bool at_end() const noexcept; + private: value_iterator iter{}; @@ -47978,7 +48303,6 @@ namespace simdjson { template<> struct simdjson_result : public fallback::implementation_simdjson_result_base { -public: simdjson_inline simdjson_result(fallback::ondemand::array_iterator &&value) noexcept; ///< @private simdjson_inline simdjson_result(error_code error) noexcept; ///< @private simdjson_inline simdjson_result() noexcept = default; @@ -47991,6 +48315,8 @@ public: simdjson_inline bool operator==(const simdjson_result &) const noexcept; simdjson_inline bool operator!=(const simdjson_result &) const noexcept; simdjson_inline simdjson_result &operator++() noexcept; + + [[nodiscard]] simdjson_inline bool at_end() const noexcept; }; } // namespace simdjson @@ -48323,7 +48649,7 @@ public: * time it parses a document or when it is destroyed. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator std::string_view() noexcept(false); + simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a raw_json_string. * @@ -48332,7 +48658,7 @@ public: * @returns A pointer to the raw JSON for the given string. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator raw_json_string() noexcept(false); + simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a bool. * @@ -49939,6 +50265,20 @@ inline simdjson_result to_json_string(simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); + +#if SIMDJSON_STATIC_REFLECTION +/** + * Create a JSON string from any user-defined type using static reflection. + * Only available when SIMDJSON_STATIC_REFLECTION is enabled. + */ +template + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +inline std::string to_json_string(const T& obj); +#endif + } // namespace simdjson /** @@ -50260,17 +50600,16 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser /** * This CPO (Customization Point Object) will help deserialize into optional types. */ -template +template requires(!require_custom_serialization) -error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { using value_type = typename std::remove_cvref_t::value_type; - static_assert( - deserializable, - "The specified type inside the unique_ptr must itself be deserializable"); - static_assert( - std::is_default_constructible_v, - "The specified type inside the unique_ptr must default constructible."); + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullopt + return SUCCESS; + } if (!out) { out.emplace(); @@ -50309,7 +50648,7 @@ template consteval auto expand(R range) { std::vector args; for (auto r : range) { - args.push_back(reflect_value(r)); + args.push_back(reflect_constant(r)); } return substitute(^^__impl::replicator, args); } @@ -50329,17 +50668,42 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { [: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)) { constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); - static_assert( - deserializable, - "The specified type inside the class must itself be deserializable"); + // Note: removed static assert as optional types are now handled generically // as long we are succesful or the field is not found, we continue if(e == simdjson::SUCCESS || e == simdjson::NO_SUCH_FIELD) { - obj[key].get(out.[:mem:]); + e = obj[key].get(out.[:mem:]); } } }; return e; } + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +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)) { + out = [:enum_val:]; + found = true; + } + }; + + return found ? SUCCESS : INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + template requires(user_defined_type>) error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { @@ -50378,6 +50742,10 @@ error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr & // Unique pointers //////////////////////////////////////// error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -50387,6 +50755,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) no } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -50396,6 +50768,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -50405,6 +50781,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -50414,6 +50794,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -50427,6 +50811,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -50436,6 +50824,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) no } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -50445,6 +50837,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -50454,6 +50850,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -50463,6 +50863,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -50472,6 +50876,61 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_shared(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(temp); + return SUCCESS; +} + } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H @@ -50759,6 +51218,9 @@ simdjson_inline array_iterator &array_iterator::operator++() noexcept { return *this; } +simdjson_inline bool array_iterator::at_end() const noexcept { + return iter.at_end(); +} } // namespace ondemand } // namespace fallback } // namespace simdjson @@ -50795,7 +51257,9 @@ simdjson_inline simdjson_result &simdjson_re ++(first); return *this; } - +simdjson_inline bool simdjson_result::at_end() const noexcept { + return !first.iter.is_valid() || first.at_end(); +} } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H @@ -51553,8 +52017,8 @@ simdjson_inline document::operator object() & noexcept(false) { return get_objec simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); } simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } simdjson_inline document::operator value() noexcept(false) { return get_value(); } @@ -53111,6 +53575,8 @@ simdjson_inline void json_iterator::assert_valid_position(token_position positio #ifndef SIMDJSON_CLANG_VISUAL_STUDIO SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] ); SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] ); +#else + (void)position; // Suppress unused parameter warning #endif } @@ -54535,6 +55001,9 @@ simdjson_inline simdjson_warn_unused simdjson_result simdjson_ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/serialization.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_builder.h" */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -55981,7 +56450,7 @@ namespace builder { * supports atomic types (Booleans, strings), it does not support composed * types (arrays and objects). * - * Ultimately, this class should support kernel-specific optimizations. E.g., + * Ultimately, this class can support kernel-specific optimizations. E.g., * it may make use of SIMD instructions to escape strings faster. */ class string_builder { @@ -56108,7 +56577,7 @@ public: * The result may not be valid UTF-8 if some of your content was not valid UTF-8. * Use validate_unicode() to check the content if needed. */ - simdjson_inline operator std::string_view() const noexcept(false); + simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound; #endif /** @@ -56483,9 +56952,9 @@ simdjson_inline void string_builder::clear() noexcept { namespace internal { // We could specialize further for 32-bit integers. -int int_log2(uint32_t x) { return (63 - leading_zeroes(x | 1)); } +simdjson_really_inline int int_log2(uint32_t x) { return (63 - leading_zeroes(x | 1)); } -int fast_digit_count(uint32_t x) { +simdjson_really_inline int fast_digit_count(uint32_t x) { static uint64_t table[] = { 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, @@ -56497,9 +56966,9 @@ int fast_digit_count(uint32_t x) { return uint32_t((x + table[int_log2(x)]) >> 32); } -int int_log2(uint64_t x) { return 63 - leading_zeroes(x | 1); } +simdjson_really_inline int int_log2(uint64_t x) { return 63 - leading_zeroes(x | 1); } -int fast_digit_count(uint64_t x) { +simdjson_really_inline int fast_digit_count(uint64_t x) { static uint64_t table[] = {9, 99, 999, @@ -56526,7 +56995,7 @@ int fast_digit_count(uint64_t x) { template ::value>::type> -simdjson_inline size_t digit_count(number_type v) noexcept { +simdjson_really_inline size_t digit_count(number_type v) noexcept { static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || sizeof(number_type) == 2 || sizeof(number_type) == 1, "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); @@ -56696,7 +57165,7 @@ simdjson_inline string_builder::operator std::string() const noexcept(false) { } simdjson_inline string_builder::operator std::string_view() const - noexcept(false) { + noexcept(false) simdjson_lifetime_bound { return view(); } #endif @@ -56814,6 +57283,8 @@ simdjson_inline void string_builder::append_key_value(key_type key, value_type v #include #include #include +#include +#include #include #include #include @@ -56894,8 +57365,13 @@ consteval std::string consteval_to_quoted_escaped(std::string_view input); template requires(std::is_class_v && !container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && !std::is_same_v && - !std::is_same_v) + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) constexpr void atom(string_builder &b, const T &t) { int i = 0; b.append('{'); @@ -56911,8 +57387,127 @@ constexpr void atom(string_builder &b, const T &t) { b.append('}'); } +// Support for optional types (std::optional, etc.) +template +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +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)):] >> [&]{ + if (e == [:enum_val:]) { + result = std::meta::identifier_of(enum_val); + } + }; + + 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)); + } +#else + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); +#endif +} + +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); + } + first = false; + atom(b, item); + } + b.append(']'); +} + +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + // works for struct -template void append(string_builder &b, const Z &z) { +template + requires(std::is_class_v && !container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) +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())):] >> [&]() { @@ -56945,8 +57540,8 @@ void append(string_builder &b, const Z &z) { } template -simdjson_result to_json_string(const Z &z) { - string_builder b; +simdjson_result to_json_string(const Z &z, size_t initial_capacity = 1024) { + string_builder b(initial_capacity); append(b, z); std::string_view s; if(auto e = b.view().get(s); e) { return e; } @@ -56962,7 +57557,13 @@ simdjson_error to_json(const Z &z, std::string &s) { s.assign(view); return SUCCESS; } -} // namespace json_builder + +template +string_builder& operator<<(string_builder& b, const Z& z) { + append(b, z); + return b; +} +} // namespace builder } // namespace fallback } // namespace simdjson #endif // SIMDJSON_STATIC_REFLECTION @@ -57851,7 +58452,7 @@ template concept custom_deserializable = tag_invocable; template -concept deserializable = custom_deserializable || is_builtin_deserializable_v; +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; template concept nothrow_custom_deserializable = nothrow_tag_invocable; @@ -58490,6 +59091,19 @@ public: #if SIMDJSON_SUPPORTS_DESERIALIZATION if constexpr (custom_deserializable) { return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_t::value_type; + + // Check if the value is null + if (is_null()) { + out.reset(); // Set to nullopt + return SUCCESS; + } + + if (!out) { + out.emplace(); + } + return get(out.value()); } else { static_assert(!sizeof(T), "The get method with type T is not implemented by the simdjson library. " "And you do not seem to have added support for it. Indeed, we have that " @@ -60891,7 +61505,8 @@ public: * * Part of the std::iterator interface. */ - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result + operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. /** * Check if we are at the end of the JSON. * @@ -60915,6 +61530,11 @@ public: */ simdjson_inline array_iterator &operator++() noexcept; + /** + * Check if the array is at the end. + */ + [[nodiscard]] simdjson_inline bool at_end() const noexcept; + private: value_iterator iter{}; @@ -60933,7 +61553,6 @@ namespace simdjson { template<> struct simdjson_result : public haswell::implementation_simdjson_result_base { -public: simdjson_inline simdjson_result(haswell::ondemand::array_iterator &&value) noexcept; ///< @private simdjson_inline simdjson_result(error_code error) noexcept; ///< @private simdjson_inline simdjson_result() noexcept = default; @@ -60946,6 +61565,8 @@ public: simdjson_inline bool operator==(const simdjson_result &) const noexcept; simdjson_inline bool operator!=(const simdjson_result &) const noexcept; simdjson_inline simdjson_result &operator++() noexcept; + + [[nodiscard]] simdjson_inline bool at_end() const noexcept; }; } // namespace simdjson @@ -61278,7 +61899,7 @@ public: * time it parses a document or when it is destroyed. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator std::string_view() noexcept(false); + simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a raw_json_string. * @@ -61287,7 +61908,7 @@ public: * @returns A pointer to the raw JSON for the given string. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator raw_json_string() noexcept(false); + simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a bool. * @@ -62894,6 +63515,20 @@ inline simdjson_result to_json_string(simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); + +#if SIMDJSON_STATIC_REFLECTION +/** + * Create a JSON string from any user-defined type using static reflection. + * Only available when SIMDJSON_STATIC_REFLECTION is enabled. + */ +template + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +inline std::string to_json_string(const T& obj); +#endif + } // namespace simdjson /** @@ -63215,17 +63850,16 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser /** * This CPO (Customization Point Object) will help deserialize into optional types. */ -template +template requires(!require_custom_serialization) -error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { using value_type = typename std::remove_cvref_t::value_type; - static_assert( - deserializable, - "The specified type inside the unique_ptr must itself be deserializable"); - static_assert( - std::is_default_constructible_v, - "The specified type inside the unique_ptr must default constructible."); + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullopt + return SUCCESS; + } if (!out) { out.emplace(); @@ -63264,7 +63898,7 @@ template consteval auto expand(R range) { std::vector args; for (auto r : range) { - args.push_back(reflect_value(r)); + args.push_back(reflect_constant(r)); } return substitute(^^__impl::replicator, args); } @@ -63284,17 +63918,42 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { [: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)) { constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); - static_assert( - deserializable, - "The specified type inside the class must itself be deserializable"); + // Note: removed static assert as optional types are now handled generically // as long we are succesful or the field is not found, we continue if(e == simdjson::SUCCESS || e == simdjson::NO_SUCH_FIELD) { - obj[key].get(out.[:mem:]); + e = obj[key].get(out.[:mem:]); } } }; return e; } + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +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)) { + out = [:enum_val:]; + found = true; + } + }; + + return found ? SUCCESS : INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + template requires(user_defined_type>) error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { @@ -63333,6 +63992,10 @@ error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr & // Unique pointers //////////////////////////////////////// error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -63342,6 +64005,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) no } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -63351,6 +64018,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -63360,6 +64031,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -63369,6 +64044,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -63382,6 +64061,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -63391,6 +64074,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) no } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -63400,6 +64087,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -63409,6 +64100,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -63418,6 +64113,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -63427,6 +64126,61 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_shared(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(temp); + return SUCCESS; +} + } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H @@ -63714,6 +64468,9 @@ simdjson_inline array_iterator &array_iterator::operator++() noexcept { return *this; } +simdjson_inline bool array_iterator::at_end() const noexcept { + return iter.at_end(); +} } // namespace ondemand } // namespace haswell } // namespace simdjson @@ -63750,7 +64507,9 @@ simdjson_inline simdjson_result &simdjson_res ++(first); return *this; } - +simdjson_inline bool simdjson_result::at_end() const noexcept { + return !first.iter.is_valid() || first.at_end(); +} } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H @@ -64508,8 +65267,8 @@ simdjson_inline document::operator object() & noexcept(false) { return get_objec simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); } simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } simdjson_inline document::operator value() noexcept(false) { return get_value(); } @@ -66066,6 +66825,8 @@ simdjson_inline void json_iterator::assert_valid_position(token_position positio #ifndef SIMDJSON_CLANG_VISUAL_STUDIO SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] ); SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] ); +#else + (void)position; // Suppress unused parameter warning #endif } @@ -67490,6 +68251,9 @@ simdjson_inline simdjson_warn_unused simdjson_result simdjson_ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/serialization.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_builder.h" */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -68936,7 +69700,7 @@ namespace builder { * supports atomic types (Booleans, strings), it does not support composed * types (arrays and objects). * - * Ultimately, this class should support kernel-specific optimizations. E.g., + * Ultimately, this class can support kernel-specific optimizations. E.g., * it may make use of SIMD instructions to escape strings faster. */ class string_builder { @@ -69063,7 +69827,7 @@ public: * The result may not be valid UTF-8 if some of your content was not valid UTF-8. * Use validate_unicode() to check the content if needed. */ - simdjson_inline operator std::string_view() const noexcept(false); + simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound; #endif /** @@ -69438,9 +70202,9 @@ simdjson_inline void string_builder::clear() noexcept { namespace internal { // We could specialize further for 32-bit integers. -int int_log2(uint32_t x) { return (63 - leading_zeroes(x | 1)); } +simdjson_really_inline int int_log2(uint32_t x) { return (63 - leading_zeroes(x | 1)); } -int fast_digit_count(uint32_t x) { +simdjson_really_inline int fast_digit_count(uint32_t x) { static uint64_t table[] = { 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, @@ -69452,9 +70216,9 @@ int fast_digit_count(uint32_t x) { return uint32_t((x + table[int_log2(x)]) >> 32); } -int int_log2(uint64_t x) { return 63 - leading_zeroes(x | 1); } +simdjson_really_inline int int_log2(uint64_t x) { return 63 - leading_zeroes(x | 1); } -int fast_digit_count(uint64_t x) { +simdjson_really_inline int fast_digit_count(uint64_t x) { static uint64_t table[] = {9, 99, 999, @@ -69481,7 +70245,7 @@ int fast_digit_count(uint64_t x) { template ::value>::type> -simdjson_inline size_t digit_count(number_type v) noexcept { +simdjson_really_inline size_t digit_count(number_type v) noexcept { static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || sizeof(number_type) == 2 || sizeof(number_type) == 1, "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); @@ -69651,7 +70415,7 @@ simdjson_inline string_builder::operator std::string() const noexcept(false) { } simdjson_inline string_builder::operator std::string_view() const - noexcept(false) { + noexcept(false) simdjson_lifetime_bound { return view(); } #endif @@ -69769,6 +70533,8 @@ simdjson_inline void string_builder::append_key_value(key_type key, value_type v #include #include #include +#include +#include #include #include #include @@ -69849,8 +70615,13 @@ consteval std::string consteval_to_quoted_escaped(std::string_view input); template requires(std::is_class_v && !container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && !std::is_same_v && - !std::is_same_v) + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) constexpr void atom(string_builder &b, const T &t) { int i = 0; b.append('{'); @@ -69866,8 +70637,127 @@ constexpr void atom(string_builder &b, const T &t) { b.append('}'); } +// Support for optional types (std::optional, etc.) +template +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +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)):] >> [&]{ + if (e == [:enum_val:]) { + result = std::meta::identifier_of(enum_val); + } + }; + + 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)); + } +#else + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); +#endif +} + +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); + } + first = false; + atom(b, item); + } + b.append(']'); +} + +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + // works for struct -template void append(string_builder &b, const Z &z) { +template + requires(std::is_class_v && !container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) +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())):] >> [&]() { @@ -69900,8 +70790,8 @@ void append(string_builder &b, const Z &z) { } template -simdjson_result to_json_string(const Z &z) { - string_builder b; +simdjson_result to_json_string(const Z &z, size_t initial_capacity = 1024) { + string_builder b(initial_capacity); append(b, z); std::string_view s; if(auto e = b.view().get(s); e) { return e; } @@ -69917,7 +70807,13 @@ simdjson_error to_json(const Z &z, std::string &s) { s.assign(view); return SUCCESS; } -} // namespace json_builder + +template +string_builder& operator<<(string_builder& b, const Z& z) { + append(b, z); + return b; +} +} // namespace builder } // namespace haswell } // namespace simdjson #endif // SIMDJSON_STATIC_REFLECTION @@ -70806,7 +71702,7 @@ template concept custom_deserializable = tag_invocable; template -concept deserializable = custom_deserializable || is_builtin_deserializable_v; +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; template concept nothrow_custom_deserializable = nothrow_tag_invocable; @@ -71445,6 +72341,19 @@ public: #if SIMDJSON_SUPPORTS_DESERIALIZATION if constexpr (custom_deserializable) { return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_t::value_type; + + // Check if the value is null + if (is_null()) { + out.reset(); // Set to nullopt + return SUCCESS; + } + + if (!out) { + out.emplace(); + } + return get(out.value()); } else { static_assert(!sizeof(T), "The get method with type T is not implemented by the simdjson library. " "And you do not seem to have added support for it. Indeed, we have that " @@ -73846,7 +74755,8 @@ public: * * Part of the std::iterator interface. */ - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result + operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. /** * Check if we are at the end of the JSON. * @@ -73870,6 +74780,11 @@ public: */ simdjson_inline array_iterator &operator++() noexcept; + /** + * Check if the array is at the end. + */ + [[nodiscard]] simdjson_inline bool at_end() const noexcept; + private: value_iterator iter{}; @@ -73888,7 +74803,6 @@ namespace simdjson { template<> struct simdjson_result : public icelake::implementation_simdjson_result_base { -public: simdjson_inline simdjson_result(icelake::ondemand::array_iterator &&value) noexcept; ///< @private simdjson_inline simdjson_result(error_code error) noexcept; ///< @private simdjson_inline simdjson_result() noexcept = default; @@ -73901,6 +74815,8 @@ public: simdjson_inline bool operator==(const simdjson_result &) const noexcept; simdjson_inline bool operator!=(const simdjson_result &) const noexcept; simdjson_inline simdjson_result &operator++() noexcept; + + [[nodiscard]] simdjson_inline bool at_end() const noexcept; }; } // namespace simdjson @@ -74233,7 +75149,7 @@ public: * time it parses a document or when it is destroyed. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator std::string_view() noexcept(false); + simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a raw_json_string. * @@ -74242,7 +75158,7 @@ public: * @returns A pointer to the raw JSON for the given string. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator raw_json_string() noexcept(false); + simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a bool. * @@ -75849,6 +76765,20 @@ inline simdjson_result to_json_string(simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); + +#if SIMDJSON_STATIC_REFLECTION +/** + * Create a JSON string from any user-defined type using static reflection. + * Only available when SIMDJSON_STATIC_REFLECTION is enabled. + */ +template + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +inline std::string to_json_string(const T& obj); +#endif + } // namespace simdjson /** @@ -76170,17 +77100,16 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser /** * This CPO (Customization Point Object) will help deserialize into optional types. */ -template +template requires(!require_custom_serialization) -error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { using value_type = typename std::remove_cvref_t::value_type; - static_assert( - deserializable, - "The specified type inside the unique_ptr must itself be deserializable"); - static_assert( - std::is_default_constructible_v, - "The specified type inside the unique_ptr must default constructible."); + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullopt + return SUCCESS; + } if (!out) { out.emplace(); @@ -76219,7 +77148,7 @@ template consteval auto expand(R range) { std::vector args; for (auto r : range) { - args.push_back(reflect_value(r)); + args.push_back(reflect_constant(r)); } return substitute(^^__impl::replicator, args); } @@ -76239,17 +77168,42 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { [: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)) { constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); - static_assert( - deserializable, - "The specified type inside the class must itself be deserializable"); + // Note: removed static assert as optional types are now handled generically // as long we are succesful or the field is not found, we continue if(e == simdjson::SUCCESS || e == simdjson::NO_SUCH_FIELD) { - obj[key].get(out.[:mem:]); + e = obj[key].get(out.[:mem:]); } } }; return e; } + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +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)) { + out = [:enum_val:]; + found = true; + } + }; + + return found ? SUCCESS : INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + template requires(user_defined_type>) error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { @@ -76288,6 +77242,10 @@ error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr & // Unique pointers //////////////////////////////////////// error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -76297,6 +77255,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) no } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -76306,6 +77268,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -76315,6 +77281,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -76324,6 +77294,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -76337,6 +77311,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -76346,6 +77324,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) no } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -76355,6 +77337,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -76364,6 +77350,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -76373,6 +77363,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -76382,6 +77376,61 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_shared(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(temp); + return SUCCESS; +} + } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H @@ -76669,6 +77718,9 @@ simdjson_inline array_iterator &array_iterator::operator++() noexcept { return *this; } +simdjson_inline bool array_iterator::at_end() const noexcept { + return iter.at_end(); +} } // namespace ondemand } // namespace icelake } // namespace simdjson @@ -76705,7 +77757,9 @@ simdjson_inline simdjson_result &simdjson_res ++(first); return *this; } - +simdjson_inline bool simdjson_result::at_end() const noexcept { + return !first.iter.is_valid() || first.at_end(); +} } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H @@ -77463,8 +78517,8 @@ simdjson_inline document::operator object() & noexcept(false) { return get_objec simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); } simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } simdjson_inline document::operator value() noexcept(false) { return get_value(); } @@ -79021,6 +80075,8 @@ simdjson_inline void json_iterator::assert_valid_position(token_position positio #ifndef SIMDJSON_CLANG_VISUAL_STUDIO SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] ); SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] ); +#else + (void)position; // Suppress unused parameter warning #endif } @@ -80445,6 +81501,9 @@ simdjson_inline simdjson_warn_unused simdjson_result simdjson_ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/serialization.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_builder.h" */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -81891,7 +82950,7 @@ namespace builder { * supports atomic types (Booleans, strings), it does not support composed * types (arrays and objects). * - * Ultimately, this class should support kernel-specific optimizations. E.g., + * Ultimately, this class can support kernel-specific optimizations. E.g., * it may make use of SIMD instructions to escape strings faster. */ class string_builder { @@ -82018,7 +83077,7 @@ public: * The result may not be valid UTF-8 if some of your content was not valid UTF-8. * Use validate_unicode() to check the content if needed. */ - simdjson_inline operator std::string_view() const noexcept(false); + simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound; #endif /** @@ -82393,9 +83452,9 @@ simdjson_inline void string_builder::clear() noexcept { namespace internal { // We could specialize further for 32-bit integers. -int int_log2(uint32_t x) { return (63 - leading_zeroes(x | 1)); } +simdjson_really_inline int int_log2(uint32_t x) { return (63 - leading_zeroes(x | 1)); } -int fast_digit_count(uint32_t x) { +simdjson_really_inline int fast_digit_count(uint32_t x) { static uint64_t table[] = { 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, @@ -82407,9 +83466,9 @@ int fast_digit_count(uint32_t x) { return uint32_t((x + table[int_log2(x)]) >> 32); } -int int_log2(uint64_t x) { return 63 - leading_zeroes(x | 1); } +simdjson_really_inline int int_log2(uint64_t x) { return 63 - leading_zeroes(x | 1); } -int fast_digit_count(uint64_t x) { +simdjson_really_inline int fast_digit_count(uint64_t x) { static uint64_t table[] = {9, 99, 999, @@ -82436,7 +83495,7 @@ int fast_digit_count(uint64_t x) { template ::value>::type> -simdjson_inline size_t digit_count(number_type v) noexcept { +simdjson_really_inline size_t digit_count(number_type v) noexcept { static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || sizeof(number_type) == 2 || sizeof(number_type) == 1, "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); @@ -82606,7 +83665,7 @@ simdjson_inline string_builder::operator std::string() const noexcept(false) { } simdjson_inline string_builder::operator std::string_view() const - noexcept(false) { + noexcept(false) simdjson_lifetime_bound { return view(); } #endif @@ -82724,6 +83783,8 @@ simdjson_inline void string_builder::append_key_value(key_type key, value_type v #include #include #include +#include +#include #include #include #include @@ -82804,8 +83865,13 @@ consteval std::string consteval_to_quoted_escaped(std::string_view input); template requires(std::is_class_v && !container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && !std::is_same_v && - !std::is_same_v) + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) constexpr void atom(string_builder &b, const T &t) { int i = 0; b.append('{'); @@ -82821,8 +83887,127 @@ constexpr void atom(string_builder &b, const T &t) { b.append('}'); } +// Support for optional types (std::optional, etc.) +template +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +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)):] >> [&]{ + if (e == [:enum_val:]) { + result = std::meta::identifier_of(enum_val); + } + }; + + 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)); + } +#else + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); +#endif +} + +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); + } + first = false; + atom(b, item); + } + b.append(']'); +} + +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + // works for struct -template void append(string_builder &b, const Z &z) { +template + requires(std::is_class_v && !container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) +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())):] >> [&]() { @@ -82855,8 +84040,8 @@ void append(string_builder &b, const Z &z) { } template -simdjson_result to_json_string(const Z &z) { - string_builder b; +simdjson_result to_json_string(const Z &z, size_t initial_capacity = 1024) { + string_builder b(initial_capacity); append(b, z); std::string_view s; if(auto e = b.view().get(s); e) { return e; } @@ -82872,7 +84057,13 @@ simdjson_error to_json(const Z &z, std::string &s) { s.assign(view); return SUCCESS; } -} // namespace json_builder + +template +string_builder& operator<<(string_builder& b, const Z& z) { + append(b, z); + return b; +} +} // namespace builder } // namespace icelake } // namespace simdjson #endif // SIMDJSON_STATIC_REFLECTION @@ -83876,7 +85067,7 @@ template concept custom_deserializable = tag_invocable; template -concept deserializable = custom_deserializable || is_builtin_deserializable_v; +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; template concept nothrow_custom_deserializable = nothrow_tag_invocable; @@ -84515,6 +85706,19 @@ public: #if SIMDJSON_SUPPORTS_DESERIALIZATION if constexpr (custom_deserializable) { return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_t::value_type; + + // Check if the value is null + if (is_null()) { + out.reset(); // Set to nullopt + return SUCCESS; + } + + if (!out) { + out.emplace(); + } + return get(out.value()); } else { static_assert(!sizeof(T), "The get method with type T is not implemented by the simdjson library. " "And you do not seem to have added support for it. Indeed, we have that " @@ -86916,7 +88120,8 @@ public: * * Part of the std::iterator interface. */ - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result + operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. /** * Check if we are at the end of the JSON. * @@ -86940,6 +88145,11 @@ public: */ simdjson_inline array_iterator &operator++() noexcept; + /** + * Check if the array is at the end. + */ + [[nodiscard]] simdjson_inline bool at_end() const noexcept; + private: value_iterator iter{}; @@ -86958,7 +88168,6 @@ namespace simdjson { template<> struct simdjson_result : public ppc64::implementation_simdjson_result_base { -public: simdjson_inline simdjson_result(ppc64::ondemand::array_iterator &&value) noexcept; ///< @private simdjson_inline simdjson_result(error_code error) noexcept; ///< @private simdjson_inline simdjson_result() noexcept = default; @@ -86971,6 +88180,8 @@ public: simdjson_inline bool operator==(const simdjson_result &) const noexcept; simdjson_inline bool operator!=(const simdjson_result &) const noexcept; simdjson_inline simdjson_result &operator++() noexcept; + + [[nodiscard]] simdjson_inline bool at_end() const noexcept; }; } // namespace simdjson @@ -87303,7 +88514,7 @@ public: * time it parses a document or when it is destroyed. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator std::string_view() noexcept(false); + simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a raw_json_string. * @@ -87312,7 +88523,7 @@ public: * @returns A pointer to the raw JSON for the given string. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator raw_json_string() noexcept(false); + simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a bool. * @@ -88919,6 +90130,20 @@ inline simdjson_result to_json_string(simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); + +#if SIMDJSON_STATIC_REFLECTION +/** + * Create a JSON string from any user-defined type using static reflection. + * Only available when SIMDJSON_STATIC_REFLECTION is enabled. + */ +template + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +inline std::string to_json_string(const T& obj); +#endif + } // namespace simdjson /** @@ -89240,17 +90465,16 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser /** * This CPO (Customization Point Object) will help deserialize into optional types. */ -template +template requires(!require_custom_serialization) -error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { using value_type = typename std::remove_cvref_t::value_type; - static_assert( - deserializable, - "The specified type inside the unique_ptr must itself be deserializable"); - static_assert( - std::is_default_constructible_v, - "The specified type inside the unique_ptr must default constructible."); + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullopt + return SUCCESS; + } if (!out) { out.emplace(); @@ -89289,7 +90513,7 @@ template consteval auto expand(R range) { std::vector args; for (auto r : range) { - args.push_back(reflect_value(r)); + args.push_back(reflect_constant(r)); } return substitute(^^__impl::replicator, args); } @@ -89309,17 +90533,42 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { [: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)) { constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); - static_assert( - deserializable, - "The specified type inside the class must itself be deserializable"); + // Note: removed static assert as optional types are now handled generically // as long we are succesful or the field is not found, we continue if(e == simdjson::SUCCESS || e == simdjson::NO_SUCH_FIELD) { - obj[key].get(out.[:mem:]); + e = obj[key].get(out.[:mem:]); } } }; return e; } + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +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)) { + out = [:enum_val:]; + found = true; + } + }; + + return found ? SUCCESS : INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + template requires(user_defined_type>) error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { @@ -89358,6 +90607,10 @@ error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr & // Unique pointers //////////////////////////////////////// error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -89367,6 +90620,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) no } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -89376,6 +90633,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -89385,6 +90646,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -89394,6 +90659,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -89407,6 +90676,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -89416,6 +90689,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) no } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -89425,6 +90702,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -89434,6 +90715,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -89443,6 +90728,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -89452,6 +90741,61 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_shared(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(temp); + return SUCCESS; +} + } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H @@ -89739,6 +91083,9 @@ simdjson_inline array_iterator &array_iterator::operator++() noexcept { return *this; } +simdjson_inline bool array_iterator::at_end() const noexcept { + return iter.at_end(); +} } // namespace ondemand } // namespace ppc64 } // namespace simdjson @@ -89775,7 +91122,9 @@ simdjson_inline simdjson_result &simdjson_resul ++(first); return *this; } - +simdjson_inline bool simdjson_result::at_end() const noexcept { + return !first.iter.is_valid() || first.at_end(); +} } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H @@ -90533,8 +91882,8 @@ simdjson_inline document::operator object() & noexcept(false) { return get_objec simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); } simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } simdjson_inline document::operator value() noexcept(false) { return get_value(); } @@ -92091,6 +93440,8 @@ simdjson_inline void json_iterator::assert_valid_position(token_position positio #ifndef SIMDJSON_CLANG_VISUAL_STUDIO SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] ); SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] ); +#else + (void)position; // Suppress unused parameter warning #endif } @@ -93515,6 +94866,9 @@ simdjson_inline simdjson_warn_unused simdjson_result simdjson_ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/serialization.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_builder.h" */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -94961,7 +96315,7 @@ namespace builder { * supports atomic types (Booleans, strings), it does not support composed * types (arrays and objects). * - * Ultimately, this class should support kernel-specific optimizations. E.g., + * Ultimately, this class can support kernel-specific optimizations. E.g., * it may make use of SIMD instructions to escape strings faster. */ class string_builder { @@ -95088,7 +96442,7 @@ public: * The result may not be valid UTF-8 if some of your content was not valid UTF-8. * Use validate_unicode() to check the content if needed. */ - simdjson_inline operator std::string_view() const noexcept(false); + simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound; #endif /** @@ -95463,9 +96817,9 @@ simdjson_inline void string_builder::clear() noexcept { namespace internal { // We could specialize further for 32-bit integers. -int int_log2(uint32_t x) { return (63 - leading_zeroes(x | 1)); } +simdjson_really_inline int int_log2(uint32_t x) { return (63 - leading_zeroes(x | 1)); } -int fast_digit_count(uint32_t x) { +simdjson_really_inline int fast_digit_count(uint32_t x) { static uint64_t table[] = { 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, @@ -95477,9 +96831,9 @@ int fast_digit_count(uint32_t x) { return uint32_t((x + table[int_log2(x)]) >> 32); } -int int_log2(uint64_t x) { return 63 - leading_zeroes(x | 1); } +simdjson_really_inline int int_log2(uint64_t x) { return 63 - leading_zeroes(x | 1); } -int fast_digit_count(uint64_t x) { +simdjson_really_inline int fast_digit_count(uint64_t x) { static uint64_t table[] = {9, 99, 999, @@ -95506,7 +96860,7 @@ int fast_digit_count(uint64_t x) { template ::value>::type> -simdjson_inline size_t digit_count(number_type v) noexcept { +simdjson_really_inline size_t digit_count(number_type v) noexcept { static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || sizeof(number_type) == 2 || sizeof(number_type) == 1, "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); @@ -95676,7 +97030,7 @@ simdjson_inline string_builder::operator std::string() const noexcept(false) { } simdjson_inline string_builder::operator std::string_view() const - noexcept(false) { + noexcept(false) simdjson_lifetime_bound { return view(); } #endif @@ -95794,6 +97148,8 @@ simdjson_inline void string_builder::append_key_value(key_type key, value_type v #include #include #include +#include +#include #include #include #include @@ -95874,8 +97230,13 @@ consteval std::string consteval_to_quoted_escaped(std::string_view input); template requires(std::is_class_v && !container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && !std::is_same_v && - !std::is_same_v) + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) constexpr void atom(string_builder &b, const T &t) { int i = 0; b.append('{'); @@ -95891,8 +97252,127 @@ constexpr void atom(string_builder &b, const T &t) { b.append('}'); } +// Support for optional types (std::optional, etc.) +template +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +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)):] >> [&]{ + if (e == [:enum_val:]) { + result = std::meta::identifier_of(enum_val); + } + }; + + 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)); + } +#else + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); +#endif +} + +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); + } + first = false; + atom(b, item); + } + b.append(']'); +} + +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + // works for struct -template void append(string_builder &b, const Z &z) { +template + requires(std::is_class_v && !container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) +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())):] >> [&]() { @@ -95925,8 +97405,8 @@ void append(string_builder &b, const Z &z) { } template -simdjson_result to_json_string(const Z &z) { - string_builder b; +simdjson_result to_json_string(const Z &z, size_t initial_capacity = 1024) { + string_builder b(initial_capacity); append(b, z); std::string_view s; if(auto e = b.view().get(s); e) { return e; } @@ -95942,7 +97422,13 @@ simdjson_error to_json(const Z &z, std::string &s) { s.assign(view); return SUCCESS; } -} // namespace json_builder + +template +string_builder& operator<<(string_builder& b, const Z& z) { + append(b, z); + return b; +} +} // namespace builder } // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_STATIC_REFLECTION @@ -97262,7 +98748,7 @@ template concept custom_deserializable = tag_invocable; template -concept deserializable = custom_deserializable || is_builtin_deserializable_v; +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; template concept nothrow_custom_deserializable = nothrow_tag_invocable; @@ -97901,6 +99387,19 @@ public: #if SIMDJSON_SUPPORTS_DESERIALIZATION if constexpr (custom_deserializable) { return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_t::value_type; + + // Check if the value is null + if (is_null()) { + out.reset(); // Set to nullopt + return SUCCESS; + } + + if (!out) { + out.emplace(); + } + return get(out.value()); } else { static_assert(!sizeof(T), "The get method with type T is not implemented by the simdjson library. " "And you do not seem to have added support for it. Indeed, we have that " @@ -100302,7 +101801,8 @@ public: * * Part of the std::iterator interface. */ - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result + operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. /** * Check if we are at the end of the JSON. * @@ -100326,6 +101826,11 @@ public: */ simdjson_inline array_iterator &operator++() noexcept; + /** + * Check if the array is at the end. + */ + [[nodiscard]] simdjson_inline bool at_end() const noexcept; + private: value_iterator iter{}; @@ -100344,7 +101849,6 @@ namespace simdjson { template<> struct simdjson_result : public westmere::implementation_simdjson_result_base { -public: simdjson_inline simdjson_result(westmere::ondemand::array_iterator &&value) noexcept; ///< @private simdjson_inline simdjson_result(error_code error) noexcept; ///< @private simdjson_inline simdjson_result() noexcept = default; @@ -100357,6 +101861,8 @@ public: simdjson_inline bool operator==(const simdjson_result &) const noexcept; simdjson_inline bool operator!=(const simdjson_result &) const noexcept; simdjson_inline simdjson_result &operator++() noexcept; + + [[nodiscard]] simdjson_inline bool at_end() const noexcept; }; } // namespace simdjson @@ -100689,7 +102195,7 @@ public: * time it parses a document or when it is destroyed. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator std::string_view() noexcept(false); + simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a raw_json_string. * @@ -100698,7 +102204,7 @@ public: * @returns A pointer to the raw JSON for the given string. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator raw_json_string() noexcept(false); + simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a bool. * @@ -102305,6 +103811,20 @@ inline simdjson_result to_json_string(simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); + +#if SIMDJSON_STATIC_REFLECTION +/** + * Create a JSON string from any user-defined type using static reflection. + * Only available when SIMDJSON_STATIC_REFLECTION is enabled. + */ +template + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +inline std::string to_json_string(const T& obj); +#endif + } // namespace simdjson /** @@ -102626,17 +104146,16 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser /** * This CPO (Customization Point Object) will help deserialize into optional types. */ -template +template requires(!require_custom_serialization) -error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { using value_type = typename std::remove_cvref_t::value_type; - static_assert( - deserializable, - "The specified type inside the unique_ptr must itself be deserializable"); - static_assert( - std::is_default_constructible_v, - "The specified type inside the unique_ptr must default constructible."); + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullopt + return SUCCESS; + } if (!out) { out.emplace(); @@ -102675,7 +104194,7 @@ template consteval auto expand(R range) { std::vector args; for (auto r : range) { - args.push_back(reflect_value(r)); + args.push_back(reflect_constant(r)); } return substitute(^^__impl::replicator, args); } @@ -102695,17 +104214,42 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { [: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)) { constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); - static_assert( - deserializable, - "The specified type inside the class must itself be deserializable"); + // Note: removed static assert as optional types are now handled generically // as long we are succesful or the field is not found, we continue if(e == simdjson::SUCCESS || e == simdjson::NO_SUCH_FIELD) { - obj[key].get(out.[:mem:]); + e = obj[key].get(out.[:mem:]); } } }; return e; } + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +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)) { + out = [:enum_val:]; + found = true; + } + }; + + return found ? SUCCESS : INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + template requires(user_defined_type>) error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { @@ -102744,6 +104288,10 @@ error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr & // Unique pointers //////////////////////////////////////// error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -102753,6 +104301,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) no } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -102762,6 +104314,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -102771,6 +104327,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -102780,6 +104340,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -102793,6 +104357,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -102802,6 +104370,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) no } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -102811,6 +104383,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -102820,6 +104396,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -102829,6 +104409,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -102838,6 +104422,61 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_shared(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(temp); + return SUCCESS; +} + } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H @@ -103125,6 +104764,9 @@ simdjson_inline array_iterator &array_iterator::operator++() noexcept { return *this; } +simdjson_inline bool array_iterator::at_end() const noexcept { + return iter.at_end(); +} } // namespace ondemand } // namespace westmere } // namespace simdjson @@ -103161,7 +104803,9 @@ simdjson_inline simdjson_result &simdjson_re ++(first); return *this; } - +simdjson_inline bool simdjson_result::at_end() const noexcept { + return !first.iter.is_valid() || first.at_end(); +} } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H @@ -103919,8 +105563,8 @@ simdjson_inline document::operator object() & noexcept(false) { return get_objec simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); } simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } simdjson_inline document::operator value() noexcept(false) { return get_value(); } @@ -105477,6 +107121,8 @@ simdjson_inline void json_iterator::assert_valid_position(token_position positio #ifndef SIMDJSON_CLANG_VISUAL_STUDIO SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] ); SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] ); +#else + (void)position; // Suppress unused parameter warning #endif } @@ -106901,6 +108547,9 @@ simdjson_inline simdjson_warn_unused simdjson_result simdjson_ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/serialization.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_builder.h" */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -108347,7 +109996,7 @@ namespace builder { * supports atomic types (Booleans, strings), it does not support composed * types (arrays and objects). * - * Ultimately, this class should support kernel-specific optimizations. E.g., + * Ultimately, this class can support kernel-specific optimizations. E.g., * it may make use of SIMD instructions to escape strings faster. */ class string_builder { @@ -108474,7 +110123,7 @@ public: * The result may not be valid UTF-8 if some of your content was not valid UTF-8. * Use validate_unicode() to check the content if needed. */ - simdjson_inline operator std::string_view() const noexcept(false); + simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound; #endif /** @@ -108849,9 +110498,9 @@ simdjson_inline void string_builder::clear() noexcept { namespace internal { // We could specialize further for 32-bit integers. -int int_log2(uint32_t x) { return (63 - leading_zeroes(x | 1)); } +simdjson_really_inline int int_log2(uint32_t x) { return (63 - leading_zeroes(x | 1)); } -int fast_digit_count(uint32_t x) { +simdjson_really_inline int fast_digit_count(uint32_t x) { static uint64_t table[] = { 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, @@ -108863,9 +110512,9 @@ int fast_digit_count(uint32_t x) { return uint32_t((x + table[int_log2(x)]) >> 32); } -int int_log2(uint64_t x) { return 63 - leading_zeroes(x | 1); } +simdjson_really_inline int int_log2(uint64_t x) { return 63 - leading_zeroes(x | 1); } -int fast_digit_count(uint64_t x) { +simdjson_really_inline int fast_digit_count(uint64_t x) { static uint64_t table[] = {9, 99, 999, @@ -108892,7 +110541,7 @@ int fast_digit_count(uint64_t x) { template ::value>::type> -simdjson_inline size_t digit_count(number_type v) noexcept { +simdjson_really_inline size_t digit_count(number_type v) noexcept { static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || sizeof(number_type) == 2 || sizeof(number_type) == 1, "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); @@ -109062,7 +110711,7 @@ simdjson_inline string_builder::operator std::string() const noexcept(false) { } simdjson_inline string_builder::operator std::string_view() const - noexcept(false) { + noexcept(false) simdjson_lifetime_bound { return view(); } #endif @@ -109180,6 +110829,8 @@ simdjson_inline void string_builder::append_key_value(key_type key, value_type v #include #include #include +#include +#include #include #include #include @@ -109260,8 +110911,13 @@ consteval std::string consteval_to_quoted_escaped(std::string_view input); template requires(std::is_class_v && !container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && !std::is_same_v && - !std::is_same_v) + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) constexpr void atom(string_builder &b, const T &t) { int i = 0; b.append('{'); @@ -109277,8 +110933,127 @@ constexpr void atom(string_builder &b, const T &t) { b.append('}'); } +// Support for optional types (std::optional, etc.) +template +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +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)):] >> [&]{ + if (e == [:enum_val:]) { + result = std::meta::identifier_of(enum_val); + } + }; + + 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)); + } +#else + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); +#endif +} + +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); + } + first = false; + atom(b, item); + } + b.append(']'); +} + +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + // works for struct -template void append(string_builder &b, const Z &z) { +template + requires(std::is_class_v && !container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) +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())):] >> [&]() { @@ -109311,8 +111086,8 @@ void append(string_builder &b, const Z &z) { } template -simdjson_result to_json_string(const Z &z) { - string_builder b; +simdjson_result to_json_string(const Z &z, size_t initial_capacity = 1024) { + string_builder b(initial_capacity); append(b, z); std::string_view s; if(auto e = b.view().get(s); e) { return e; } @@ -109328,7 +111103,13 @@ simdjson_error to_json(const Z &z, std::string &s) { s.assign(view); return SUCCESS; } -} // namespace json_builder + +template +string_builder& operator<<(string_builder& b, const Z& z) { + append(b, z); + return b; +} +} // namespace builder } // namespace westmere } // namespace simdjson #endif // SIMDJSON_STATIC_REFLECTION @@ -110125,7 +111906,7 @@ template concept custom_deserializable = tag_invocable; template -concept deserializable = custom_deserializable || is_builtin_deserializable_v; +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; template concept nothrow_custom_deserializable = nothrow_tag_invocable; @@ -110764,6 +112545,19 @@ public: #if SIMDJSON_SUPPORTS_DESERIALIZATION if constexpr (custom_deserializable) { return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_t::value_type; + + // Check if the value is null + if (is_null()) { + out.reset(); // Set to nullopt + return SUCCESS; + } + + if (!out) { + out.emplace(); + } + return get(out.value()); } else { static_assert(!sizeof(T), "The get method with type T is not implemented by the simdjson library. " "And you do not seem to have added support for it. Indeed, we have that " @@ -113165,7 +114959,8 @@ public: * * Part of the std::iterator interface. */ - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result + operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. /** * Check if we are at the end of the JSON. * @@ -113189,6 +114984,11 @@ public: */ simdjson_inline array_iterator &operator++() noexcept; + /** + * Check if the array is at the end. + */ + [[nodiscard]] simdjson_inline bool at_end() const noexcept; + private: value_iterator iter{}; @@ -113207,7 +115007,6 @@ namespace simdjson { template<> struct simdjson_result : public lsx::implementation_simdjson_result_base { -public: simdjson_inline simdjson_result(lsx::ondemand::array_iterator &&value) noexcept; ///< @private simdjson_inline simdjson_result(error_code error) noexcept; ///< @private simdjson_inline simdjson_result() noexcept = default; @@ -113220,6 +115019,8 @@ public: simdjson_inline bool operator==(const simdjson_result &) const noexcept; simdjson_inline bool operator!=(const simdjson_result &) const noexcept; simdjson_inline simdjson_result &operator++() noexcept; + + [[nodiscard]] simdjson_inline bool at_end() const noexcept; }; } // namespace simdjson @@ -113552,7 +115353,7 @@ public: * time it parses a document or when it is destroyed. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator std::string_view() noexcept(false); + simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a raw_json_string. * @@ -113561,7 +115362,7 @@ public: * @returns A pointer to the raw JSON for the given string. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator raw_json_string() noexcept(false); + simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a bool. * @@ -115168,6 +116969,20 @@ inline simdjson_result to_json_string(simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); + +#if SIMDJSON_STATIC_REFLECTION +/** + * Create a JSON string from any user-defined type using static reflection. + * Only available when SIMDJSON_STATIC_REFLECTION is enabled. + */ +template + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +inline std::string to_json_string(const T& obj); +#endif + } // namespace simdjson /** @@ -115489,17 +117304,16 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser /** * This CPO (Customization Point Object) will help deserialize into optional types. */ -template +template requires(!require_custom_serialization) -error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { using value_type = typename std::remove_cvref_t::value_type; - static_assert( - deserializable, - "The specified type inside the unique_ptr must itself be deserializable"); - static_assert( - std::is_default_constructible_v, - "The specified type inside the unique_ptr must default constructible."); + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullopt + return SUCCESS; + } if (!out) { out.emplace(); @@ -115538,7 +117352,7 @@ template consteval auto expand(R range) { std::vector args; for (auto r : range) { - args.push_back(reflect_value(r)); + args.push_back(reflect_constant(r)); } return substitute(^^__impl::replicator, args); } @@ -115558,17 +117372,42 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { [: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)) { constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); - static_assert( - deserializable, - "The specified type inside the class must itself be deserializable"); + // Note: removed static assert as optional types are now handled generically // as long we are succesful or the field is not found, we continue if(e == simdjson::SUCCESS || e == simdjson::NO_SUCH_FIELD) { - obj[key].get(out.[:mem:]); + e = obj[key].get(out.[:mem:]); } } }; return e; } + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +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)) { + out = [:enum_val:]; + found = true; + } + }; + + return found ? SUCCESS : INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + template requires(user_defined_type>) error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { @@ -115607,6 +117446,10 @@ error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr & // Unique pointers //////////////////////////////////////// error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -115616,6 +117459,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) no } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -115625,6 +117472,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -115634,6 +117485,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -115643,6 +117498,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -115656,6 +117515,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -115665,6 +117528,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) no } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -115674,6 +117541,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -115683,6 +117554,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -115692,6 +117567,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -115701,6 +117580,61 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_shared(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(temp); + return SUCCESS; +} + } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H @@ -115988,6 +117922,9 @@ simdjson_inline array_iterator &array_iterator::operator++() noexcept { return *this; } +simdjson_inline bool array_iterator::at_end() const noexcept { + return iter.at_end(); +} } // namespace ondemand } // namespace lsx } // namespace simdjson @@ -116024,7 +117961,9 @@ simdjson_inline simdjson_result &simdjson_result< ++(first); return *this; } - +simdjson_inline bool simdjson_result::at_end() const noexcept { + return !first.iter.is_valid() || first.at_end(); +} } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H @@ -116782,8 +118721,8 @@ simdjson_inline document::operator object() & noexcept(false) { return get_objec simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); } simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } simdjson_inline document::operator value() noexcept(false) { return get_value(); } @@ -118340,6 +120279,8 @@ simdjson_inline void json_iterator::assert_valid_position(token_position positio #ifndef SIMDJSON_CLANG_VISUAL_STUDIO SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] ); SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] ); +#else + (void)position; // Suppress unused parameter warning #endif } @@ -119764,6 +121705,9 @@ simdjson_inline simdjson_warn_unused simdjson_result simdjson_ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/serialization.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_builder.h" */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -121210,7 +123154,7 @@ namespace builder { * supports atomic types (Booleans, strings), it does not support composed * types (arrays and objects). * - * Ultimately, this class should support kernel-specific optimizations. E.g., + * Ultimately, this class can support kernel-specific optimizations. E.g., * it may make use of SIMD instructions to escape strings faster. */ class string_builder { @@ -121337,7 +123281,7 @@ public: * The result may not be valid UTF-8 if some of your content was not valid UTF-8. * Use validate_unicode() to check the content if needed. */ - simdjson_inline operator std::string_view() const noexcept(false); + simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound; #endif /** @@ -121712,9 +123656,9 @@ simdjson_inline void string_builder::clear() noexcept { namespace internal { // We could specialize further for 32-bit integers. -int int_log2(uint32_t x) { return (63 - leading_zeroes(x | 1)); } +simdjson_really_inline int int_log2(uint32_t x) { return (63 - leading_zeroes(x | 1)); } -int fast_digit_count(uint32_t x) { +simdjson_really_inline int fast_digit_count(uint32_t x) { static uint64_t table[] = { 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, @@ -121726,9 +123670,9 @@ int fast_digit_count(uint32_t x) { return uint32_t((x + table[int_log2(x)]) >> 32); } -int int_log2(uint64_t x) { return 63 - leading_zeroes(x | 1); } +simdjson_really_inline int int_log2(uint64_t x) { return 63 - leading_zeroes(x | 1); } -int fast_digit_count(uint64_t x) { +simdjson_really_inline int fast_digit_count(uint64_t x) { static uint64_t table[] = {9, 99, 999, @@ -121755,7 +123699,7 @@ int fast_digit_count(uint64_t x) { template ::value>::type> -simdjson_inline size_t digit_count(number_type v) noexcept { +simdjson_really_inline size_t digit_count(number_type v) noexcept { static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || sizeof(number_type) == 2 || sizeof(number_type) == 1, "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); @@ -121925,7 +123869,7 @@ simdjson_inline string_builder::operator std::string() const noexcept(false) { } simdjson_inline string_builder::operator std::string_view() const - noexcept(false) { + noexcept(false) simdjson_lifetime_bound { return view(); } #endif @@ -122043,6 +123987,8 @@ simdjson_inline void string_builder::append_key_value(key_type key, value_type v #include #include #include +#include +#include #include #include #include @@ -122123,8 +124069,13 @@ consteval std::string consteval_to_quoted_escaped(std::string_view input); template requires(std::is_class_v && !container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && !std::is_same_v && - !std::is_same_v) + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) constexpr void atom(string_builder &b, const T &t) { int i = 0; b.append('{'); @@ -122140,8 +124091,127 @@ constexpr void atom(string_builder &b, const T &t) { b.append('}'); } +// Support for optional types (std::optional, etc.) +template +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +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)):] >> [&]{ + if (e == [:enum_val:]) { + result = std::meta::identifier_of(enum_val); + } + }; + + 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)); + } +#else + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); +#endif +} + +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); + } + first = false; + atom(b, item); + } + b.append(']'); +} + +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + // works for struct -template void append(string_builder &b, const Z &z) { +template + requires(std::is_class_v && !container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) +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())):] >> [&]() { @@ -122174,8 +124244,8 @@ void append(string_builder &b, const Z &z) { } template -simdjson_result to_json_string(const Z &z) { - string_builder b; +simdjson_result to_json_string(const Z &z, size_t initial_capacity = 1024) { + string_builder b(initial_capacity); append(b, z); std::string_view s; if(auto e = b.view().get(s); e) { return e; } @@ -122191,7 +124261,13 @@ simdjson_error to_json(const Z &z, std::string &s) { s.assign(view); return SUCCESS; } -} // namespace json_builder + +template +string_builder& operator<<(string_builder& b, const Z& z) { + append(b, z); + return b; +} +} // namespace builder } // namespace lsx } // namespace simdjson #endif // SIMDJSON_STATIC_REFLECTION @@ -123001,7 +125077,7 @@ template concept custom_deserializable = tag_invocable; template -concept deserializable = custom_deserializable || is_builtin_deserializable_v; +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; template concept nothrow_custom_deserializable = nothrow_tag_invocable; @@ -123640,6 +125716,19 @@ public: #if SIMDJSON_SUPPORTS_DESERIALIZATION if constexpr (custom_deserializable) { return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_t::value_type; + + // Check if the value is null + if (is_null()) { + out.reset(); // Set to nullopt + return SUCCESS; + } + + if (!out) { + out.emplace(); + } + return get(out.value()); } else { static_assert(!sizeof(T), "The get method with type T is not implemented by the simdjson library. " "And you do not seem to have added support for it. Indeed, we have that " @@ -126041,7 +128130,8 @@ public: * * Part of the std::iterator interface. */ - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result + operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. /** * Check if we are at the end of the JSON. * @@ -126065,6 +128155,11 @@ public: */ simdjson_inline array_iterator &operator++() noexcept; + /** + * Check if the array is at the end. + */ + [[nodiscard]] simdjson_inline bool at_end() const noexcept; + private: value_iterator iter{}; @@ -126083,7 +128178,6 @@ namespace simdjson { template<> struct simdjson_result : public lasx::implementation_simdjson_result_base { -public: simdjson_inline simdjson_result(lasx::ondemand::array_iterator &&value) noexcept; ///< @private simdjson_inline simdjson_result(error_code error) noexcept; ///< @private simdjson_inline simdjson_result() noexcept = default; @@ -126096,6 +128190,8 @@ public: simdjson_inline bool operator==(const simdjson_result &) const noexcept; simdjson_inline bool operator!=(const simdjson_result &) const noexcept; simdjson_inline simdjson_result &operator++() noexcept; + + [[nodiscard]] simdjson_inline bool at_end() const noexcept; }; } // namespace simdjson @@ -126428,7 +128524,7 @@ public: * time it parses a document or when it is destroyed. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator std::string_view() noexcept(false); + simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a raw_json_string. * @@ -126437,7 +128533,7 @@ public: * @returns A pointer to the raw JSON for the given string. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator raw_json_string() noexcept(false); + simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a bool. * @@ -128044,6 +130140,20 @@ inline simdjson_result to_json_string(simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); + +#if SIMDJSON_STATIC_REFLECTION +/** + * Create a JSON string from any user-defined type using static reflection. + * Only available when SIMDJSON_STATIC_REFLECTION is enabled. + */ +template + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +inline std::string to_json_string(const T& obj); +#endif + } // namespace simdjson /** @@ -128365,17 +130475,16 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser /** * This CPO (Customization Point Object) will help deserialize into optional types. */ -template +template requires(!require_custom_serialization) -error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { using value_type = typename std::remove_cvref_t::value_type; - static_assert( - deserializable, - "The specified type inside the unique_ptr must itself be deserializable"); - static_assert( - std::is_default_constructible_v, - "The specified type inside the unique_ptr must default constructible."); + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullopt + return SUCCESS; + } if (!out) { out.emplace(); @@ -128414,7 +130523,7 @@ template consteval auto expand(R range) { std::vector args; for (auto r : range) { - args.push_back(reflect_value(r)); + args.push_back(reflect_constant(r)); } return substitute(^^__impl::replicator, args); } @@ -128434,17 +130543,42 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { [: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)) { constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); - static_assert( - deserializable, - "The specified type inside the class must itself be deserializable"); + // Note: removed static assert as optional types are now handled generically // as long we are succesful or the field is not found, we continue if(e == simdjson::SUCCESS || e == simdjson::NO_SUCH_FIELD) { - obj[key].get(out.[:mem:]); + e = obj[key].get(out.[:mem:]); } } }; return e; } + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +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)) { + out = [:enum_val:]; + found = true; + } + }; + + return found ? SUCCESS : INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + template requires(user_defined_type>) error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { @@ -128483,6 +130617,10 @@ error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr & // Unique pointers //////////////////////////////////////// error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -128492,6 +130630,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) no } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -128501,6 +130643,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -128510,6 +130656,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -128519,6 +130669,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_unique(); if (!out) { return MEMALLOC; } @@ -128532,6 +130686,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -128541,6 +130699,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) no } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -128550,6 +130712,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -128559,6 +130725,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -128568,6 +130738,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) } error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + if (val.is_null()) { + out.reset(); + return SUCCESS; + } if (!out) { out = std::make_shared(); if (!out) { return MEMALLOC; } @@ -128577,6 +130751,61 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_shared(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + if (val.is_null()) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(temp); + return SUCCESS; +} + } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H @@ -128864,6 +131093,9 @@ simdjson_inline array_iterator &array_iterator::operator++() noexcept { return *this; } +simdjson_inline bool array_iterator::at_end() const noexcept { + return iter.at_end(); +} } // namespace ondemand } // namespace lasx } // namespace simdjson @@ -128900,7 +131132,9 @@ simdjson_inline simdjson_result &simdjson_result ++(first); return *this; } - +simdjson_inline bool simdjson_result::at_end() const noexcept { + return !first.iter.is_valid() || first.at_end(); +} } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H @@ -129658,8 +131892,8 @@ simdjson_inline document::operator object() & noexcept(false) { return get_objec simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); } simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } simdjson_inline document::operator value() noexcept(false) { return get_value(); } @@ -131216,6 +133450,8 @@ simdjson_inline void json_iterator::assert_valid_position(token_position positio #ifndef SIMDJSON_CLANG_VISUAL_STUDIO SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] ); SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] ); +#else + (void)position; // Suppress unused parameter warning #endif } @@ -132640,6 +134876,9 @@ simdjson_inline simdjson_warn_unused simdjson_result simdjson_ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/serialization.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_builder.h" */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -134086,7 +136325,7 @@ namespace builder { * supports atomic types (Booleans, strings), it does not support composed * types (arrays and objects). * - * Ultimately, this class should support kernel-specific optimizations. E.g., + * Ultimately, this class can support kernel-specific optimizations. E.g., * it may make use of SIMD instructions to escape strings faster. */ class string_builder { @@ -134213,7 +136452,7 @@ public: * The result may not be valid UTF-8 if some of your content was not valid UTF-8. * Use validate_unicode() to check the content if needed. */ - simdjson_inline operator std::string_view() const noexcept(false); + simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound; #endif /** @@ -134588,9 +136827,9 @@ simdjson_inline void string_builder::clear() noexcept { namespace internal { // We could specialize further for 32-bit integers. -int int_log2(uint32_t x) { return (63 - leading_zeroes(x | 1)); } +simdjson_really_inline int int_log2(uint32_t x) { return (63 - leading_zeroes(x | 1)); } -int fast_digit_count(uint32_t x) { +simdjson_really_inline int fast_digit_count(uint32_t x) { static uint64_t table[] = { 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, @@ -134602,9 +136841,9 @@ int fast_digit_count(uint32_t x) { return uint32_t((x + table[int_log2(x)]) >> 32); } -int int_log2(uint64_t x) { return 63 - leading_zeroes(x | 1); } +simdjson_really_inline int int_log2(uint64_t x) { return 63 - leading_zeroes(x | 1); } -int fast_digit_count(uint64_t x) { +simdjson_really_inline int fast_digit_count(uint64_t x) { static uint64_t table[] = {9, 99, 999, @@ -134631,7 +136870,7 @@ int fast_digit_count(uint64_t x) { template ::value>::type> -simdjson_inline size_t digit_count(number_type v) noexcept { +simdjson_really_inline size_t digit_count(number_type v) noexcept { static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || sizeof(number_type) == 2 || sizeof(number_type) == 1, "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); @@ -134801,7 +137040,7 @@ simdjson_inline string_builder::operator std::string() const noexcept(false) { } simdjson_inline string_builder::operator std::string_view() const - noexcept(false) { + noexcept(false) simdjson_lifetime_bound { return view(); } #endif @@ -134919,6 +137158,8 @@ simdjson_inline void string_builder::append_key_value(key_type key, value_type v #include #include #include +#include +#include #include #include #include @@ -134999,8 +137240,13 @@ consteval std::string consteval_to_quoted_escaped(std::string_view input); template requires(std::is_class_v && !container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && !std::is_same_v && - !std::is_same_v) + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) constexpr void atom(string_builder &b, const T &t) { int i = 0; b.append('{'); @@ -135016,8 +137262,127 @@ constexpr void atom(string_builder &b, const T &t) { b.append('}'); } +// Support for optional types (std::optional, etc.) +template +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +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)):] >> [&]{ + if (e == [:enum_val:]) { + result = std::meta::identifier_of(enum_val); + } + }; + + 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)); + } +#else + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); +#endif +} + +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); + } + first = false; + atom(b, item); + } + b.append(']'); +} + +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template +void append(string_builder &b, const T &t) { + atom(b, t); +} + // works for struct -template void append(string_builder &b, const Z &z) { +template + requires(std::is_class_v && !container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) +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())):] >> [&]() { @@ -135050,8 +137415,8 @@ void append(string_builder &b, const Z &z) { } template -simdjson_result to_json_string(const Z &z) { - string_builder b; +simdjson_result to_json_string(const Z &z, size_t initial_capacity = 1024) { + string_builder b(initial_capacity); append(b, z); std::string_view s; if(auto e = b.view().get(s); e) { return e; } @@ -135067,7 +137432,13 @@ simdjson_error to_json(const Z &z, std::string &s) { s.assign(view); return SUCCESS; } -} // namespace json_builder + +template +string_builder& operator<<(string_builder& b, const Z& z) { + append(b, z); + return b; +} +} // namespace builder } // namespace lasx } // namespace simdjson #endif // SIMDJSON_STATIC_REFLECTION @@ -135115,9 +137486,366 @@ namespace simdjson { * @copydoc simdjson::builtin::builder */ namespace builder = builtin::builder; + +#if SIMDJSON_STATIC_REFLECTION + /** + * Create a JSON string from any user-defined type using static reflection. + * Only available when SIMDJSON_STATIC_REFLECTION is enabled. + */ + template + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) + inline std::string to_json_string(const T& obj) { + builder::string_builder str_builder; + append(str_builder, obj); + std::string_view view; + if (str_builder.view().get(view) == SUCCESS) { + return std::string(view); + } + return ""; + } +#endif + } // namespace simdjson #endif // SIMDJSON_ONDEMAND_H /* end file simdjson/ondemand.h */ +/* including simdjson/convert.h: #include "simdjson/convert.h" */ +/* begin file simdjson/convert.h */ +#ifndef SIMDJSON_CONVERT_H +#define SIMDJSON_CONVERT_H +#if __cpp_concepts + +/* skipped duplicate #include "simdjson/ondemand.h" */ +#include +#ifdef __cpp_lib_ranges +#include +#endif + +namespace simdjson { + +struct [[nodiscard]] auto_iterator_end {}; + +/** + * A Wrapper for simdjson_result in order to make it + * compatible with ranges (to satisfy std::ranges::input_range). + */ +struct [[nodiscard]] auto_iterator { + using iterator_category = std::forward_iterator_tag; + using type = simdjson_result; + using value_type = simdjson_result; // type::value_type + using reference = value_type &; + using const_reference = const value_type &; + using difference_type = std::ptrdiff_t; + + struct auto_iterator_storage { + type m_iter{}; + mutable value_type m_value{}; + }; + +private: + auto_iterator_storage *m_storage = nullptr; + +public: + constexpr auto_iterator() noexcept = default; + explicit auto_iterator(auto_iterator_storage &storage) noexcept + : m_storage{&storage} {}; + auto_iterator(auto_iterator const &) = default; + auto_iterator(auto_iterator &&) = default; + auto_iterator &operator=(auto_iterator const &) = default; + auto_iterator &operator=(auto_iterator &&) noexcept = default; + ~auto_iterator() = default; + + reference operator*() const noexcept { return m_storage->m_value; } + reference operator*() noexcept { return m_storage->m_value; } + + auto_iterator &operator++() noexcept { + ++m_storage->m_iter; + m_storage->m_value = + m_storage->m_iter.at_end() || m_storage->m_iter.error() != SUCCESS + ? value_type{} + : *m_storage->m_iter; + return *this; + } + auto_iterator operator++(int) noexcept { + auto_iterator const tmp = *this; + operator++(); + return tmp; + } + + [[nodiscard]] bool operator==(auto_iterator const &other) const noexcept { + return m_storage == other.m_storage && + m_storage->m_iter == other.m_storage->m_iter; + } + + [[nodiscard]] bool operator==(auto_iterator_end) const noexcept { + return m_storage != nullptr && m_storage->m_iter.at_end(); + } +}; + +template +struct [[nodiscard]] auto_parser +#if __cpp_lib_ranges + : std::ranges::view_interface> +#endif +{ + using value_type = simdjson_result; + using size_type = size_t; + using difference_type = std::ptrdiff_t; + using pointer = value_type *; + using const_pointer = const value_type *; + using reference = value_type &; + using const_reference = const value_type &; + using iterator = auto_iterator; + using const_iterator = auto_iterator; // auto_iterator is already const + +private: + ParserType m_parser; + ondemand::document m_doc; + error_code m_error{SUCCESS}; + + // Caching the iterator here: + iterator::auto_iterator_storage iter_storage{}; + + template + static constexpr bool is_nothrow_gettable = requires(ondemand::document doc) { + { doc.get() } noexcept; + }; + +public: + // non-pointer constructors: + explicit auto_parser(ParserType &&parser, ondemand::document &&doc) noexcept + requires(!std::is_pointer_v) + : m_parser{std::move(parser)}, m_doc{std::move(doc)} {} + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Weffc++" +#endif + + explicit auto_parser(ParserType &&parser, + padded_string_view const str) noexcept + requires(!std::is_pointer_v) + : m_parser{std::move(parser)}, m_doc{}, m_error{SUCCESS} { + // Initialize m_doc after m_parser to avoid potential issues + auto doc_result = m_parser.iterate(str); + m_error = doc_result.error(); + if (m_error == SUCCESS) { + m_doc = std::move(doc_result.value_unsafe()); + } + } + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + + explicit auto_parser(padded_string_view const str) noexcept + requires(!std::is_pointer_v) + : auto_parser{ParserType{}, str} {} + + // pointer constructors: + explicit auto_parser(std::remove_pointer_t &parser, + ondemand::document &&doc) noexcept + requires(std::is_pointer_v) + : m_parser{&parser}, m_doc{std::move(doc)} {} + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Weffc++" +#endif + + explicit auto_parser(std::remove_pointer_t &parser, + padded_string_view const str) noexcept + requires(std::is_pointer_v) + : m_parser{&parser}, m_doc{}, m_error{SUCCESS} { + // Initialize m_doc after m_parser to avoid potential issues + auto doc_result = parser.iterate(str); + m_error = doc_result.error(); + if (m_error == SUCCESS) { + m_doc = std::move(doc_result.value_unsafe()); + } + } + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + + explicit auto_parser(ParserType parser, ondemand::document &&doc) noexcept + requires(std::is_pointer_v) + : auto_parser{*parser, std::move(doc)} {} + + auto_parser(auto_parser const &) = delete; + auto_parser &operator=(auto_parser const &) = delete; + auto_parser(auto_parser &&) noexcept = default; + auto_parser &operator=(auto_parser &&) noexcept = default; + ~auto_parser() = default; + + /// Get the parser + [[nodiscard]] std::remove_pointer_t &parser() noexcept { + if constexpr (std::is_pointer_v) { + return *m_parser; + } else { + return m_parser; + } + } + + template + [[nodiscard]] simdjson_inline simdjson_result + result() noexcept(is_nothrow_gettable) { + if (m_error != SUCCESS) { + return m_error; + } + // For array and object types, we need to be at the start of the document + return m_doc.get(); + } + + [[nodiscard]] simdjson_inline simdjson_result + array() noexcept { + return result(); + } + + [[nodiscard]] simdjson_inline simdjson_result + object() noexcept { + return result(); + } + + [[nodiscard]] simdjson_inline simdjson_result + number() noexcept { + return result(); + } + + template + [[nodiscard]] simdjson_inline explicit(false) + operator simdjson_result() noexcept(is_nothrow_gettable) { + return result(); + } + + template + [[nodiscard]] simdjson_inline explicit(false) operator T() noexcept(false) { + if (m_error != SUCCESS) { + throw simdjson_error(m_error); + } + return m_doc.get(); + } + + // We can't have "operator std::optional" because it would create an + // ambiguity for the compiler. + // We also cannot have "operator T*" without manual memory management. + // We also cannot have "operator T&" without manual memory management either. + + template + [[nodiscard]] simdjson_inline std::optional + optional() noexcept(is_nothrow_gettable) { + if (m_error != SUCCESS) { + return std::nullopt; + } + // For std::optional + auto res = m_doc.get(); + if (res.error()) [[unlikely]] { + return std::nullopt; + } + return {res.value()}; + } + + simdjson_inline auto_iterator begin() noexcept { + if (m_error != SUCCESS) { + // Create an iterator with the error + iter_storage.m_iter = iterator::type(m_error); + iter_storage.m_value = value_type{}; + return auto_iterator{iter_storage}; + } + if (iter_storage.m_iter.error() != SUCCESS && + !iter_storage.m_iter.at_end()) { + // Try to get the document as an array + auto array_result = m_doc.get_array(); + if (array_result.error() == SUCCESS) { + iter_storage = {.m_iter = iterator::type{array_result.value_unsafe().begin()}, + .m_value = iterator::value_type{ + iter_storage.m_iter.at_end() || + iter_storage.m_iter.error() != SUCCESS + ? value_type{} + : *iter_storage.m_iter}}; + } else { + // If it's not an array, create an error iterator + iter_storage.m_iter = iterator::type(array_result.error()); + iter_storage.m_value = value_type{}; + } + } + return auto_iterator{iter_storage}; + } + simdjson_inline auto_iterator_end end() noexcept { return {}; } +}; + +#ifdef __cpp_lib_ranges + +// For C++20, we implement our own pipe operator since range_adaptor_closure is C++23 +static constexpr struct [[nodiscard]] no_errors_adaptor { + + [[nodiscard]] bool + operator()(simdjson_result const &val) const noexcept { + return val.error() == SUCCESS; + } + + template + auto operator()(Range &&rng) const noexcept { + return std::forward(rng) | std::views::filter(*this); + } +} no_errors; + +template +struct [[nodiscard]] to_adaptor { + + /// Convert to T + [[nodiscard]] T + operator()(simdjson_result &val) const noexcept { + return val.get(); + } + + /// Make it an adaptor + template + auto operator()(Range &&rng) const noexcept { + return std::forward(rng) | no_errors | std::views::transform(*this); + } + + /** + * Parse input string into any object if possible. + */ + auto operator()(padded_string_view const str) const noexcept { + return auto_parser{str}; + } + + /** + * Parse the input using the specified parser into any object if possible. + */ + auto operator()(ondemand::parser &parser, + padded_string_view const str) const noexcept { + return auto_parser{parser, str}; + } +}; + +template static constexpr to_adaptor to{}; + +static constexpr to_adaptor<> from{}; + +// For C++20 ranges without range_adaptor_closure, we need to define pipe operators +template +inline auto operator|(Range&& range, const no_errors_adaptor& adaptor) { + return adaptor(std::forward(range)); +} + +template +inline auto operator|(Range&& range, const to_adaptor& adaptor) { + return adaptor(std::forward(range)); +} + +#endif // __cpp_lib_ranges + +} // namespace simdjson + +#endif // __cpp_concepts +#endif // SIMDJSON_CONVERT_H +/* end file simdjson/convert.h */ #endif // SIMDJSON_H /* end file simdjson.h */ diff --git a/singleheader/singleheader.zip b/singleheader/singleheader.zip index f6c110e28..14b0ab204 100644 Binary files a/singleheader/singleheader.zip and b/singleheader/singleheader.zip differ diff --git a/tests/builder/static_reflection_builder_tests.cpp b/tests/builder/static_reflection_builder_tests.cpp index 5034090a4..3a09cc4f9 100644 --- a/tests/builder/static_reflection_builder_tests.cpp +++ b/tests/builder/static_reflection_builder_tests.cpp @@ -77,6 +77,7 @@ namespace builder_tests { Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}}; append(sb, c); std::string_view p{sb}; + (void)p; // to avoid unused variable warning TEST_SUCCEED(); } bool car_test_exception2() { @@ -85,12 +86,18 @@ namespace builder_tests { Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}}; sb << c; std::string_view p{sb}; + (void)p; // to avoid unused variable warning TEST_SUCCEED(); } - void car_test_to_json_exception() { + bool car_test_to_json_exception() { TEST_START(); Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}}; - std::string json = simdjson::builder::to_json_string(c); + std::string json = simdjson::to_json(c); + TEST_SUCCEED(); + } + bool car_test_to_json_exception_value() { + TEST_START(); + std::string json = simdjson::to_json(Car{"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}}); TEST_SUCCEED(); } #endif // SIMDJSON_EXCEPTIONS @@ -165,6 +172,7 @@ bool serialize_deserialize_x_y_z() { car_test_exception() && car_test_exception2() && car_test_to_json_exception() && + car_test_to_json_exception_value() && #endif // SIMDJSON_EXCEPTIONS car_test() && serialize_deserialize_kid() && diff --git a/tests/dom/CMakeLists.txt b/tests/dom/CMakeLists.txt index 98442b4ed..05b9e7406 100644 --- a/tests/dom/CMakeLists.txt +++ b/tests/dom/CMakeLists.txt @@ -132,7 +132,9 @@ if( ) message(STATUS "compiler id: ${CMAKE_CXX_COMPILER_ID} version: ${CMAKE_CXX_COMPILER_VERSION}") add_cpp_test(ranges_test LABELS dom acceptance per_implementation) - set_target_properties(ranges_test PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) + if(NOT SIMDJSON_STATIC_REFLECTION) + set_target_properties(ranges_test PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) + endif() endif() if(WIN32 AND BUILD_SHARED_LIBS) diff --git a/tests/ondemand/CMakeLists.txt b/tests/ondemand/CMakeLists.txt index 0ec01233e..989323107 100644 --- a/tests/ondemand/CMakeLists.txt +++ b/tests/ondemand/CMakeLists.txt @@ -33,6 +33,7 @@ add_cpp_test(ondemand_iterate_many_csv LABELS ondemand acceptance add_cpp_test(ondemand_custom_types_tests LABELS ondemand acceptance per_implementation) add_cpp_test(ondemand_custom_types_document_tests LABELS ondemand acceptance per_implementation) add_cpp_test(ondemand_stl_types_tests LABELS ondemand acceptance per_implementation) +add_cpp_test(ondemand_convert_tests LABELS ondemand acceptance per_implementation) if(NOT SIMDJSON_SANITIZE) add_cpp_test(ondemand_cacheline LABELS ondemand acceptance per_implementation) endif() diff --git a/tests/ondemand/ondemand_convert_tests.cpp b/tests/ondemand/ondemand_convert_tests.cpp new file mode 100644 index 000000000..a181e72bd --- /dev/null +++ b/tests/ondemand/ondemand_convert_tests.cpp @@ -0,0 +1,336 @@ +#include "simdjson.h" +#include "simdjson/convert.h" +#include "test_ondemand.h" + +#include +#include +#include + +#ifdef __cpp_lib_ranges + +namespace convert_tests { +#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_DESERIALIZATION +struct Car { + std::string make{}; + std::string model{}; + int year{}; + std::vector tire_pressure{}; + + friend simdjson::error_code tag_invoke(simdjson::deserialize_tag, auto &val, + Car &car) { + simdjson::ondemand::object obj; + auto error = val.get_object().get(obj); + if (error) { + return error; + } + // Instead of repeatedly obj["something"], we iterate through the object + // which we expect to be faster. + for (auto field : obj) { + simdjson::ondemand::raw_json_string key; + error = field.key().get(key); + if (error) { + return error; + } + if (key == "make") { + error = field.value().get_string(car.make); + if (error) { + return error; + } + } else if (key == "model") { + error = field.value().get_string(car.model); + if (error) { + return error; + } + } else if (key == "year") { + error = field.value().get(car.year); + if (error) { + return error; + } + } else if (key == "tire_pressure") { + error = field.value().get(car.tire_pressure); + if (error) { + return error; + } + } + } + return simdjson::SUCCESS; + } +}; + +static_assert(simdjson::custom_deserializable>, + "It should be deserializable"); + +static_assert(std::input_or_output_iterator, + "Must be a valid input iterator"); +static_assert(std::semiregular, + "Should be kinda regular"); +// static_assert(std::ranges::__access::__member_end>, +// "Must be a valid input iterator"); +// static_assert(std::ranges::views::__adaptor::__is_range_adaptor_closure< +// simdjson::auto_parser<>>, +// "Parser need to be range adaptor closure."); +// static_assert(std::ranges::views::__adaptor::__adaptor_invocable< +// decltype(simdjson::to()), +// simdjson::auto_parser<>>, +// "I don't even know!"); +static_assert(std::ranges::range>, + "Parser need to be a range."); +static_assert(std::ranges::forward_range>, + "Parser need to be an input range."); +static_assert( + requires(simdjson::auto_parser<> &parser) { + { parser.begin() } -> std::input_or_output_iterator; + }, "Must be valid iterator."); + +simdjson::padded_string json_car = + R"( { + "make": "Toyota", + "model": "Camry", + "year": 2018, + "tire_pressure": [ 40.1, 39.9 ] + } )"_padded; +simdjson::padded_string json_cars = + R"( [ { "make": "Toyota", "model": "Camry", "year": 2018, + "tire_pressure": [ 40.1, 39.9 ] }, + { "make": "Kia", "model": "Soul", "year": 2012, + "tire_pressure": [ 30.1, 31.0 ] }, + { "make": "Toyota", "model": "Tercel", "year": 1999, + "tire_pressure": [ 29.8, 30.0 ] } +])"_padded; + +bool simple() { + TEST_START(); + Car car = simdjson::from(json_car); + if (car.make != "Toyota" || car.model != "Camry" || car.year != 2018) { + return false; + } + TEST_SUCCEED(); +} + +bool broken() { + TEST_START(); + simdjson::padded_string short_json_cars = R"( { "make )"_padded; + try { + Car car = simdjson::from(json_cars); + TEST_FAIL("Should not have succeeded"); + } catch (...) { + TEST_SUCCEED(); + } + TEST_SUCCEED(); +} + +bool simple_optional() { + TEST_START(); + auto car = simdjson::from(json_car).optional(); + if (!car.has_value() || car->make != "Toyota" || car->model != "Camry" || + car->year != 2018) { + return false; + } + TEST_SUCCEED(); +} + +bool with_parser() { + TEST_START(); + simdjson::ondemand::parser parser; + Car car = simdjson::from(parser, json_car); + if (car.make != "Toyota" || car.model != "Camry" || car.year != 2018) { + return false; + } + TEST_SUCCEED(); +} + +bool to_array() { + TEST_START(); + auto parser = simdjson::from(json_cars); + for (auto val : parser.array()) { + Car car{}; + if (auto const error = val.get(car)) { + std::cerr << simdjson::error_message(error) << std::endl; + return false; + } + if (car.year < 1998) { + std::cerr << car.make << " " << car.model << " " << car.year << std::endl; + return false; + } + } + TEST_SUCCEED(); +} + +bool to_array_shortcut() { + TEST_START(); + simdjson::ondemand::parser parser; + for (auto val : simdjson::from(parser, json_cars)) { + Car car{}; + if (auto const error = val.get(car)) { + std::cerr << simdjson::error_message(error) << std::endl; + return false; + } + if (car.year < 1998) { + std::cerr << car.make << " " << car.model << " " << car.year << std::endl; + return false; + } + } + TEST_SUCCEED(); +} + +bool to_bad_array() { + TEST_START(); + auto parser = simdjson::from(json_car); + try { + auto array_result = parser.array(); + // Check if array_result has an error + if (array_result.error() != simdjson::SUCCESS) { + // This is expected - trying to get array from an object should fail + if (array_result.error() != simdjson::INCORRECT_TYPE) { + std::cerr << "Expected INCORRECT_TYPE but got: " << array_result.error() + << " (" << simdjson::error_message(array_result.error()) << ")" << std::endl; + return false; + } + // Got expected error, test passes + TEST_SUCCEED(); + } + + // If we get here without error, try to iterate + // This might throw when we try to use the array + for (auto val : array_result) { + static_cast(val); + // Should not reach here - the JSON is an object, not an array + std::cerr << "Unexpectedly succeeded in iterating over non-array JSON" << std::endl; + return false; + } + // Also should not reach here + std::cerr << "array() succeeded on object JSON without throwing" << std::endl; + return false; + } catch (simdjson::simdjson_error &e) { + if (e.error() != simdjson::INCORRECT_TYPE) { + std::cerr << "Expected INCORRECT_TYPE but got: " << e.error() << " (" << simdjson::error_message(e.error()) << ")" << std::endl; + return false; + } + // Got expected exception, test passes + } catch (...) { + std::cerr << "Unexpected exception type" << std::endl; + return false; + } + TEST_SUCCEED(); +} + +bool test_basic_adaptor() { + TEST_START(); + for (Car car : simdjson::from(json_cars) | simdjson::as()) { + if (car.year < 1998) { + return false; + } + } + TEST_SUCCEED(); +} + +bool test_no_errors() { + TEST_START(); + auto cars = simdjson::from(json_cars) | simdjson::no_errors; + for (auto val : cars) { + Car car = val.get(); + if (car.year < 1998) { + return false; + } + } + TEST_SUCCEED(); +} + +bool to_clean_array() { + TEST_START(); + for (auto val : simdjson::from(json_cars) | simdjson::no_errors) { + Car car = val.get(); + if (car.year < 1998) { + std::cerr << car.make << " " << car.model << " " << car.year << std::endl; + return false; + } + } + TEST_SUCCEED(); +} + +bool test_to_adaptor_basic() { + TEST_START(); + // Test 1: Basic usage of to with a value reference + simdjson::ondemand::parser parser; + auto doc_result = parser.iterate(json_car); + if (doc_result.error()) { + return false; + } + simdjson::ondemand::document doc = std::move(doc_result.value()); + simdjson::simdjson_result val = doc.get_value(); + + // to converts a simdjson_result& to T + Car car = simdjson::to(val); + if (car.make != "Toyota" || car.model != "Camry" || car.year != 2018) { + return false; + } + TEST_SUCCEED(); +} + +bool test_to_adaptor_with_single_value() { + TEST_START(); + // Test 2: Using to to convert individual values + simdjson::ondemand::parser parser; + auto doc_result = parser.iterate(json_car); + if (doc_result.error()) { + return false; + } + simdjson::ondemand::document doc = std::move(doc_result.value()); + + // Get individual field and convert it + auto obj_result = doc.get_object(); + if (obj_result.error()) { + return false; + } + simdjson::ondemand::object obj = std::move(obj_result.value()); + + auto year_val = obj["year"]; + int64_t year = simdjson::to(year_val); + if (year != 2018) { + return false; + } + + TEST_SUCCEED(); +} + +bool test_to_vs_from_equivalence() { + TEST_START(); + // Test 3: Verify that simdjson::to<> and simdjson::from behave equivalently + // Both are instances of to_adaptor - from is just to + + // These should produce identical auto_parser objects + auto parser1 = simdjson::from(json_car); + // simdjson::from is an alias for simdjson::to + auto parser2 = simdjson::from(json_car); // Same as parser1 + + // Both should parse the same way + Car car1 = parser1; + Car car2 = parser2; + + if (car1.make != car2.make || car1.model != car2.model || car1.year != car2.year) { + return false; + } + + TEST_SUCCEED(); +} + +#endif // SIMDJSON_EXCEPTIONS +bool run() { + return +#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_DESERIALIZATION + test_basic_adaptor() && broken() && simple() && simple_optional() && with_parser() && to_array() && + to_array_shortcut() && to_bad_array() && test_no_errors() && + to_clean_array() && test_to_adaptor_basic() && + test_to_adaptor_with_single_value() && test_to_vs_from_equivalence() && +#endif // SIMDJSON_EXCEPTIONS + true; +} + +} // namespace convert_tests + +int main(int argc, char *argv[]) { + return test_main(argc, argv, convert_tests::run); +} +#else +int main() { return 0; } +#endif \ No newline at end of file