diff --git a/.gitignore b/.gitignore index 0608e31b2..3709099fb 100644 --- a/.gitignore +++ b/.gitignore @@ -38,7 +38,7 @@ cmake-build-release/ .history/ # Visual Studio artifacts -/VS/ +/.vs/ # C/C++ build outputs .build/ @@ -106,4 +106,4 @@ objs !.vscode/extensions.json # clangd -.cache \ No newline at end of file +.cache diff --git a/CMakeLists.txt b/CMakeLists.txt index af1e25d97..1b2f2fcfa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,9 @@ cmake_minimum_required(VERSION 3.14) -cmake_policy(VERSION 3.5) # For doctest - project( simdjson # The version number is modified by tools/release.py - VERSION 3.13.0 + VERSION 4.0.0 DESCRIPTION "Parsing gigabytes of JSON per second" HOMEPAGE_URL "https://simdjson.org/" LANGUAGES CXX C @@ -22,8 +20,8 @@ string( # ---- Options, variables ---- # These version numbers are modified by tools/release.py -set(SIMDJSON_LIB_VERSION "26.0.0" CACHE STRING "simdjson library version") -set(SIMDJSON_LIB_SOVERSION "26" CACHE STRING "simdjson library soversion") +set(SIMDJSON_LIB_VERSION "28.0.0" CACHE STRING "simdjson library version") +set(SIMDJSON_LIB_SOVERSION "28" CACHE STRING "simdjson library soversion") option(SIMDJSON_BUILD_STATIC_LIB "Build simdjson_static library along with simdjson (only makes sense if BUILD_SHARED_LIBS=ON)" OFF) if(SIMDJSON_BUILD_STATIC_LIB AND NOT BUILD_SHARED_LIBS) diff --git a/Doxyfile b/Doxyfile index e76055f67..676ca1015 100644 --- a/Doxyfile +++ b/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = simdjson # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = "3.13.0" +PROJECT_NUMBER = "4.0.0" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/doc/basics.md b/doc/basics.md index 52cab16ba..3eefd2310 100644 --- a/doc/basics.md +++ b/doc/basics.md @@ -1,47 +1,50 @@ The Basics ========== -An overview of what you need to know to use simdjson, with examples. +An overview of what you need to know to use simdjson to parse JSON documents, with examples. +[Our documentation regarding the generation (serialization) of JSON documents is in a +separate document](https://github.com/simdjson/simdjson/blob/master/doc/builder.md). - [The Basics](#the-basics) - - [Requirements](#requirements) - - [Including simdjson](#including-simdjson) - - [Using simdjson with package managers](#using-simdjson-with-package-managers) - - [Using simdjson as a CMake dependency](#using-simdjson-as-a-cmake-dependency) - - [Versions](#versions) - - [The basics: loading and parsing JSON documents](#the-basics-loading-and-parsing-json-documents) - - [Documents are iterators](#documents-are-iterators) - - [Parser, document and JSON scope](#parser-document-and-json-scope) - - [string_view](#string_view) - - [Avoiding pitfalls: enable development checks](#avoiding-pitfalls-enable-development-checks) - - [Using the parsed JSON](#using-the-parsed-json) - - [Using the parsed JSON: additional examples](#using-the-parsed-json-additional-examples) - - [Adding support for custom types](#adding-support-for-custom-types) - - [1. Specialize `simdjson::ondemand::value::get` to get custom types (pre-C++20)](#1-specialize-simdjsonondemandvalueget-to-get-custom-types-pre-c20) - - [2. Use `tag_invoke` for custom types (C++20)](#2-use-tag_invoke-for-custom-types-c20) - - [Minifying JSON strings without parsing](#minifying-json-strings-without-parsing) - - [UTF-8 validation (alone)](#utf-8-validation-alone) - - [JSON Pointer](#json-pointer) - - [JSONPath](#jsonpath) - - [Error handling](#error-handling) - - [Error handling examples without exceptions](#error-handling-examples-without-exceptions) - - [Disabling exceptions](#disabling-exceptions) - - [Exceptions](#exceptions) - - [Current location in document](#current-location-in-document) - - [Checking for trailing content](#checking-for-trailing-content) - - [Rewinding](#rewinding) - - [Newline-Delimited JSON (ndjson) and JSON lines](#newline-delimited-json-ndjson-and-json-lines) - - [Parsing numbers inside strings](#parsing-numbers-inside-strings) - - [Dynamic Number Types](#dynamic-number-types) - - [Raw strings from keys](#raw-strings-from-keys) - - [General direct access to the raw JSON string](#general-direct-access-to-the-raw-json-string) - - [Storing directly into an existing string instance](#storing-directly-into-an-existing-string-instance) - - [Thread safety](#thread-safety) - - [Standard compliance](#standard-compliance) - - [Backwards compatibility](#backwards-compatibility) - - [Examples](#examples) - - [Performance tips](#performance-tips) - - [Further reading](#further-reading) + * [Requirements](#requirements) + * [Including simdjson](#including-simdjson) + * [Using simdjson with package managers](#using-simdjson-with-package-managers) + * [Using simdjson as a CMake dependency](#using-simdjson-as-a-cmake-dependency) + * [Versions](#versions) + * [The basics: loading and parsing JSON documents](#the-basics--loading-and-parsing-json-documents) + * [Documents are iterators](#documents-are-iterators) + + [Parser, document and JSON scope](#parser--document-and-json-scope) + * [string_view](#string-view) + * [Avoiding pitfalls: enable development checks](#avoiding-pitfalls--enable-development-checks) + * [Using the parsed JSON](#using-the-parsed-json) + + [Using the parsed JSON: additional examples](#using-the-parsed-json--additional-examples) + * [Adding support for custom types](#adding-support-for-custom-types) + + [1. Specialize `simdjson::ondemand::value::get` to get custom types (pre-C++20)](#1-specialize--simdjson--ondemand--value--get--to-get-custom-types--pre-c--20-) + + [2. Use `tag_invoke` for custom types (C++20)](#2-use--tag-invoke--for-custom-types--c--20-) + + [3. Using static reflection (C++26)](#3-using-static-reflection--c--26-) + * [Minifying JSON strings without parsing](#minifying-json-strings-without-parsing) + * [UTF-8 validation (alone)](#utf-8-validation--alone-) + * [JSON Pointer](#json-pointer) + * [JSONPath](#jsonpath) + * [Error handling](#error-handling) + + [Error handling examples without exceptions](#error-handling-examples-without-exceptions) + + [Disabling exceptions](#disabling-exceptions) + + [Exceptions](#exceptions) + + [Current location in document](#current-location-in-document) + + [Checking for trailing content](#checking-for-trailing-content) + * [Rewinding](#rewinding) + * [Newline-Delimited JSON (ndjson) and JSON lines](#newline-delimited-json--ndjson--and-json-lines) + * [Parsing numbers inside strings](#parsing-numbers-inside-strings) + * [Dynamic Number Types](#dynamic-number-types) + * [Raw strings from keys](#raw-strings-from-keys) + * [General direct access to the raw JSON string](#general-direct-access-to-the-raw-json-string) + * [Storing directly into an existing string instance](#storing-directly-into-an-existing-string-instance) + * [Thread safety](#thread-safety) + * [Standard compliance](#standard-compliance) + * [Backwards compatibility](#backwards-compatibility) + * [Examples](#examples) + * [Performance tips](#performance-tips) + * [Further reading](#further-reading) Requirements @@ -826,7 +829,7 @@ There are 3 main ways provided by simdjson to deserialize a value into a custom 1. Specialize `simdjson::ondemand::document::get` for the whole document 2. Specialize `simdjson::ondemand::value::get` for each value 2. Using `tag_invoke` *(the recommended way if your system supports C++20 or better)* -3. Using static reflectioin (requires C++26 or better) +3. Using static reflection (requires C++26 or better) We describe all of them in the following sections. Most users who have systems compatible with C++20 or better should skip ahead to [using `tag_invoke` for custom types (C++20)](#2-use-tag_invoke-for-custom-types-c20) as it is more powerful and simpler. @@ -1140,7 +1143,7 @@ struct Car { }; ``` -Observe how we defined the class to use types that simdjson does not directly support (`float`, `int`). +Observe how we define the class to use types that simdjson does not directly support (`float`, `int`). With C++20 support, the library grabs from the JSON the generic type (`double`, `int`) and then it casts it automatically. @@ -1355,11 +1358,12 @@ your code with the `SIMDJSON_STATIC_REFLECTION` macro set: ``` Then you can deserialize a type such as `Car` automatically: + ```cpp std::string json = R"( { "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9 ] } )"; simdjson::ondemand::parser parser; -simdjson::ondemand::document doc = parser.iterate(simdjson::pad(json)).get(doc); +simdjson::ondemand::document doc = parser.iterate(simdjson::pad(json)); Car c = doc.get(); ``` diff --git a/doc/builder.md b/doc/builder.md index 739cb9cc0..210ee236c 100644 --- a/doc/builder.md +++ b/doc/builder.md @@ -4,16 +4,29 @@ Builder Sometimes you want to generate JSON string outputs efficiently. The simdjson library provides high-performance low-level facilities. When using these low-level functionalities, you are responsible to -define the structure of your JSON document. However, string escaping -and UTF-8 validation is automated. +define the structure of your JSON document. Our more advanced interface +automates the process using C++26 static reflection: you get both high +speed and high convenience. + +- [Builder](#builder) + * [Overview: string_builder](#overview--string-builder) + * [Example: string_builder](#example--string-builder) + * [C++26 static reflection](#c--26-static-reflection) + + [Without `string_buffer` instance](#without--string-buffer--instance) + + [Without `string_buffer` instance but with explicit error handling](#without--string-buffer--instance-but-with-explicit-error-handling) Overview: string_builder --------------------------- The string_builder class is a low-level utility for constructing JSON strings representing documents. It is optimized for performance, potentially leveraging kernel-specific features like SIMD instructions for tasks such as string escaping. This class supports atomic types (e.g., booleans, numbers, strings) but does not handle composed types directly (like arrays or objects). +Note that JSON strings are always encoded as UTF-8. An `string_builder` is created with an initial buffer capacity (e.g., 1kB). The memory -is reallocated when needed. It has the following methods to add content to the string: +is reallocated when needed. +The efficiency of `string_builder` stems from its internal use of a resizable array or buffer. When you append data, it adds the characters to this buffer, resizing it only when necessary, typically in a way that minimizes reallocations. This approach contrasts with regular string concatenation, where each operation creates a new string, copying all previous content, leading to quadratic time complexity for repeated concatenations. + + +It has the following methods to add content to the string: - `append(number_type v)`: Appends a number (including booleans) to the JSON buffer. Booleans are converted to the strings "false" or "true". Numbers are formatted according to the JSON standard, with floating-point numbers using the shortest representation that accurately reflects the value. @@ -32,6 +45,9 @@ After writting the content, if you have reasons to believe that the content migh - `validate_unicode()`: Checks if the content in the JSON buffer is valid UTF-8. Returns: true if the content is valid UTF-8, false otherwise. +You might need to do unicode validation if you have strings in your data structures containing +malformed UTF-8. + Once you are satisfied, you can recover the string as follows: - `operator std::string()`: Converts the JSON buffer to an std::string. (Might throw if an error occurred.) @@ -44,55 +60,75 @@ Example: string_builder --------------------------- ```C++ +struct Car { + std::string make; + std::string model; + int64_t year; + std::vector tire_pressure; +}; - void serialize_car(const Car& car, simdjson::builder::string_builder& builder) { - // start of JSON - builder.start_object(); +void serialize_car(const Car& car, simdjson::builder::string_builder& builder) { + // start of JSON + builder.start_object(); - // "make" - builder.append_key_value("make", car.make); - builder.append_comma(); + // "make" + builder.append_key_value("make", car.make); + builder.append_comma(); - // "model" - builder.append_key_value("model", car.model); - builder.append_comma(); + // "model" + builder.append_key_value("model", car.model); + builder.append_comma(); - // "year" - builder.append_key_value("year", car.year); - builder.append_comma(); + // "year" + builder.append_key_value("year", car.year); + builder.append_comma(); - // "tire_pressure" - builder.escape_and_append_with_quotes("tire_pressure"); - builder.append_colon(); - builder.start_array(); - // vector tire_pressure - for (size_t i = 0; i < car.tire_pressure.size(); ++i) { - builder.append(car.tire_pressure[i]); - if (i < car.tire_pressure.size() - 1) { - builder.append_comma(); - } + // "tire_pressure" + builder.escape_and_append_with_quotes("tire_pressure"); + builder.append_colon(); + builder.start_array(); + // vector tire_pressure + for (size_t i = 0; i < car.tire_pressure.size(); ++i) { + builder.append(car.tire_pressure[i]); + if (i < car.tire_pressure.size() - 1) { + builder.append_comma(); } - builder.end_array(); - builder.end_object(); } + builder.end_array(); + builder.end_object(); +} - bool car_test() { - simdjson::builder::string_builder sb; - Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}}; - serialize_car(c, sb); - std::string_view p; - if(sb.view().get(p)) { - return false; // there was an error - } - // p holds the JSON: - // "{\"make\":\"Toyota\",\"model\":\"Corolla\",\"year\":2017,\"tire_pressure\":[30.0,30.2,30.513,30.79]}" - return true; +bool car_test() { + simdjson::builder::string_builder sb; + Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}}; + serialize_car(c, sb); + std::string_view p{sb}; + // p holds the JSON: + // "{\"make\":\"Toyota\",\"model\":\"Corolla\",\"year\":2017,\"tire_pressure\":[30.0,30.2,30.513,30.79]}" + return true; +} +``` + +The `string_builder` constructor takes an optional parameter which specifies the initial +memory allocation in byte. If you know approximately the size of your JSON output, you can +pass this value as a parameter (e.g., `simdjson::builder::string_builder sb{1233213}`). + +The `string_builder` might throw an exception in case of error when you cast it result to `std::string_view`. If you wish to avoid exceptions, you can use the following programming pattern: + +```cpp + std::string_view p; + if(sb.view().get(p)) { + return false; // there was an error } ``` +In all cases, the `std::string_view` instance depends the corresponding `string_builder` instance. + C++26 static reflection ------------------------ +Static reflection (or compile-time reflection) in C++26 introduces a powerful compile-time mechanism that allows a program to inspect and manipulate its own structure, such as types, variables, functions, and other program elements, during compilation. Unlike runtime reflection in languages like Java or Python, C++26’s static reflection operates entirely at compile time, aligning with C++’s emphasis on zero-overhead abstractions and high performance. It means +that you can delegate much of the work to the library. If you have a compiler with support C++26 static reflection, you can compile your code with the `SIMDJSON_STATIC_REFLECTION` macro set: @@ -106,21 +142,55 @@ And then you can append your data structures to a `string_builder` instance automatically. In most cases, it should work automatically: ```cpp + struct Car { + std::string make; + std::string model; + int64_t year; + std::vector tire_pressure; + }; + bool car_test() { simdjson::builder::string_builder sb; Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}}; - append(sb, c); - std::string_view p; - if(sb.view().get(p)) { - return false; // there was an error - } + sb << c; + std::string_view p{sb}; // p holds the JSON: // "{\"make\":\"Toyota\",\"model\":\"Corolla\",\"year\":2017,\"tire_pressure\":[30.0,30.2,30.513,30.79]}" return true; } ``` -If you prefer, you can also create a string directly: + +### Without `string_buffer` instance + +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. +(Under the hood a `string_builder` instance may still be created.) + +```cpp + struct Car { + std::string make; + std::string model; + int64_t year; + std::vector tire_pressure; + }; + + void f() { + Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}}; + std::string json = simdjson::builder::to_json_string(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)`). + + + +### Without `string_buffer` instance but with explicit error handling + +If prefer a version without exceptions and explicit error handling, you can use the following +pattern: ```cpp std::string json; diff --git a/doc/dom.md b/doc/dom.md index 54ad79519..2c25fa404 100644 --- a/doc/dom.md +++ b/doc/dom.md @@ -1,7 +1,10 @@ The Document-Object-Model (DOM) front-end ========== -An overview of what you need to know to use simdjson, with examples. +An overview of what you need to know to use simdjson to parse JSON documents with +our DOM API, with examples. [Our documentation regarding the generation (serialization) of JSON documents is in a +separate document](https://github.com/simdjson/simdjson/blob/master/doc/builder.md). + * [DOM vs On-Demand](#dom-vs-on-demand) * [The Basics: Loading and Parsing JSON Documents](#the-basics-loading-and-parsing-json-documents-using-the-dom-front-end) @@ -28,6 +31,10 @@ a conventional Document-Object-Model (DOM) front-end. In such a scenario, the JS entirely parsed, validated and materialized in memory as the first step. The programmer may then access the parsed data using this in-memory model. +On-Demand is a different model where you parse just what you need, directly into your own +data structure. The On-Demand approach, when well tuned, can provide superior performance. +[We refer you to the On-Demand documentation for further details](https://github.com/simdjson/simdjson/blob/master/doc/basics.md). + The Basics: Loading and Parsing JSON Documents using the DOM front-end ---------------------------------------------- diff --git a/include/simdjson/arm64/implementation.h b/include/simdjson/arm64/implementation.h index 00c5bf566..c9b7dd753 100644 --- a/include/simdjson/arm64/implementation.h +++ b/include/simdjson/arm64/implementation.h @@ -23,7 +23,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace arm64 diff --git a/include/simdjson/common_defs.h b/include/simdjson/common_defs.h index ce3c48616..2cd772e88 100644 --- a/include/simdjson/common_defs.h +++ b/include/simdjson/common_defs.h @@ -199,17 +199,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 @@ -356,4 +345,16 @@ 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 diff --git a/include/simdjson/concepts.h b/include/simdjson/concepts.h index ffe344c07..371ad223f 100644 --- a/include/simdjson/concepts.h +++ b/include/simdjson/concepts.h @@ -115,7 +115,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>; diff --git a/include/simdjson/dom/serialization.h b/include/simdjson/dom/serialization.h index 87c735bbb..fc92e05bb 100644 --- a/include/simdjson/dom/serialization.h +++ b/include/simdjson/dom/serialization.h @@ -107,6 +107,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 { diff --git a/include/simdjson/fallback/implementation.h b/include/simdjson/fallback/implementation.h index e607253a0..523f06d2e 100644 --- a/include/simdjson/fallback/implementation.h +++ b/include/simdjson/fallback/implementation.h @@ -26,7 +26,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace fallback diff --git a/include/simdjson/generic/ondemand/deserialize.h b/include/simdjson/generic/ondemand/deserialize.h index 8bad5699b..1e310fba6 100644 --- a/include/simdjson/generic/ondemand/deserialize.h +++ b/include/simdjson/generic/ondemand/deserialize.h @@ -77,7 +77,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; diff --git a/include/simdjson/generic/ondemand/document-inl.h b/include/simdjson/generic/ondemand/document-inl.h index ec889d35b..39a2f6f7b 100644 --- a/include/simdjson/generic/ondemand/document-inl.h +++ b/include/simdjson/generic/ondemand/document-inl.h @@ -195,8 +195,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(); } diff --git a/include/simdjson/generic/ondemand/document.h b/include/simdjson/generic/ondemand/document.h index ef174fad8..a642248ea 100644 --- a/include/simdjson/generic/ondemand/document.h +++ b/include/simdjson/generic/ondemand/document.h @@ -322,7 +322,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. * @@ -331,7 +331,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. * diff --git a/include/simdjson/generic/ondemand/json_builder.h b/include/simdjson/generic/ondemand/json_builder.h index 14fb24699..5298b5ca3 100644 --- a/include/simdjson/generic/ondemand/json_builder.h +++ b/include/simdjson/generic/ondemand/json_builder.h @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -94,8 +96,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('{'); @@ -111,8 +118,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())):] >> [&]() { @@ -145,8 +271,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; } @@ -162,7 +288,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 SIMDJSON_IMPLEMENTATION } // namespace simdjson #endif // SIMDJSON_STATIC_REFLECTION diff --git a/include/simdjson/generic/ondemand/json_string_builder-inl.h b/include/simdjson/generic/ondemand/json_string_builder-inl.h index 42e42f7da..ae7c8a0b5 100644 --- a/include/simdjson/generic/ondemand/json_string_builder-inl.h +++ b/include/simdjson/generic/ondemand/json_string_builder-inl.h @@ -303,9 +303,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, @@ -317,9 +317,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, @@ -346,7 +346,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"); @@ -516,7 +516,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 diff --git a/include/simdjson/generic/ondemand/json_string_builder.h b/include/simdjson/generic/ondemand/json_string_builder.h index 2d1208abc..a685d8dce 100644 --- a/include/simdjson/generic/ondemand/json_string_builder.h +++ b/include/simdjson/generic/ondemand/json_string_builder.h @@ -19,7 +19,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 { @@ -146,7 +146,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 /** diff --git a/include/simdjson/generic/ondemand/serialization-inl.h b/include/simdjson/generic/ondemand/serialization-inl.h index 77be39a11..37a501789 100644 --- a/include/simdjson/generic/ondemand/serialization-inl.h +++ b/include/simdjson/generic/ondemand/serialization-inl.h @@ -9,6 +9,9 @@ #include "simdjson/generic/ondemand/object.h" #include "simdjson/generic/ondemand/serialization.h" #include "simdjson/generic/ondemand/value.h" +#if SIMDJSON_STATIC_REFLECTION +#include "simdjson/generic/ondemand/json_builder.h" +#endif #endif // SIMDJSON_CONDITIONAL_INCLUDE namespace simdjson { diff --git a/include/simdjson/generic/ondemand/serialization.h b/include/simdjson/generic/ondemand/serialization.h index 048c73cda..3d2c8ed85 100644 --- a/include/simdjson/generic/ondemand/serialization.h +++ b/include/simdjson/generic/ondemand/serialization.h @@ -35,6 +35,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 /** diff --git a/include/simdjson/generic/ondemand/std_deserialize.h b/include/simdjson/generic/ondemand/std_deserialize.h index 308d4b4c7..7c8f518af 100644 --- a/include/simdjson/generic/ondemand/std_deserialize.h +++ b/include/simdjson/generic/ondemand/std_deserialize.h @@ -248,17 +248,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(); @@ -317,17 +316,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 { @@ -366,6 +390,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; } @@ -375,6 +403,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; } @@ -384,6 +416,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; } @@ -393,6 +429,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; } @@ -402,6 +442,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; } @@ -415,6 +459,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; } @@ -424,6 +472,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; } @@ -433,6 +485,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; } @@ -442,6 +498,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; } @@ -451,6 +511,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; } @@ -460,6 +524,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 diff --git a/include/simdjson/generic/ondemand/value.h b/include/simdjson/generic/ondemand/value.h index dc175bab4..6d3c8d279 100644 --- a/include/simdjson/generic/ondemand/value.h +++ b/include/simdjson/generic/ondemand/value.h @@ -75,6 +75,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 " diff --git a/include/simdjson/haswell/implementation.h b/include/simdjson/haswell/implementation.h index 984d9f7ed..6861e4298 100644 --- a/include/simdjson/haswell/implementation.h +++ b/include/simdjson/haswell/implementation.h @@ -28,7 +28,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace haswell diff --git a/include/simdjson/icelake/implementation.h b/include/simdjson/icelake/implementation.h index d197b4309..940c5f992 100644 --- a/include/simdjson/icelake/implementation.h +++ b/include/simdjson/icelake/implementation.h @@ -28,7 +28,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace icelake diff --git a/include/simdjson/implementation.h b/include/simdjson/implementation.h index 366d29119..19cb37162 100644 --- a/include/simdjson/implementation.h +++ b/include/simdjson/implementation.h @@ -26,15 +26,6 @@ simdjson_inline simdjson_warn_unused bool validate_utf8(const std::string_view s return validate_utf8(sv.data(), sv.size()); } -/** - * Write the string to the output buffer while escaping double-quote, backlash and ascii control characters. - * - * @param input the string_view to escape - * @param out output buffer (for escaped string): to be safe, it should have 6 * input.size() allocated bytes. - * @return number of bytes written - */ -simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) noexcept; - /** * Validate the UTF-8 string. * @@ -136,14 +127,6 @@ public: */ simdjson_warn_unused virtual bool validate_utf8(const char *buf, size_t len) const noexcept = 0; - /** - * Write the string to the output buffer while escaping double-quote, backlash and ascii control characters. - * - * @param input the string_view to escape - * @param out output buffer (for escaped string): to be safe, it should have 6 * input.size() allocated bytes. - * @return number of bytes written - */ - simdjson_warn_unused virtual size_t write_string_escaped(const std::string_view input, char *out) const noexcept = 0; protected: /** @private Construct an implementation with the given name and description. For subclasses. */ simdjson_inline implementation( diff --git a/include/simdjson/lasx/implementation.h b/include/simdjson/lasx/implementation.h index d64ec3e49..8aafbb8b8 100644 --- a/include/simdjson/lasx/implementation.h +++ b/include/simdjson/lasx/implementation.h @@ -23,7 +23,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace lasx diff --git a/include/simdjson/lsx/implementation.h b/include/simdjson/lsx/implementation.h index a17fb5532..14468777d 100644 --- a/include/simdjson/lsx/implementation.h +++ b/include/simdjson/lsx/implementation.h @@ -23,7 +23,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace lsx diff --git a/include/simdjson/ondemand.h b/include/simdjson/ondemand.h index 3cfa69ba7..6394fa3fe 100644 --- a/include/simdjson/ondemand.h +++ b/include/simdjson/ondemand.h @@ -12,6 +12,28 @@ 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 diff --git a/include/simdjson/padded_string-inl.h b/include/simdjson/padded_string-inl.h index 34cc76cb9..7c0440ff4 100644 --- a/include/simdjson/padded_string-inl.h +++ b/include/simdjson/padded_string-inl.h @@ -125,9 +125,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); } diff --git a/include/simdjson/ppc64/implementation.h b/include/simdjson/ppc64/implementation.h index b3445af26..33436f534 100644 --- a/include/simdjson/ppc64/implementation.h +++ b/include/simdjson/ppc64/implementation.h @@ -32,7 +32,6 @@ public: size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace ppc64 diff --git a/include/simdjson/simdjson_version.h b/include/simdjson/simdjson_version.h index f06aa81f9..add79802f 100644 --- a/include/simdjson/simdjson_version.h +++ b/include/simdjson/simdjson_version.h @@ -4,18 +4,18 @@ #define SIMDJSON_SIMDJSON_VERSION_H /** The version of simdjson being used (major.minor.revision) */ -#define SIMDJSON_VERSION "3.13.0" +#define SIMDJSON_VERSION "4.0.0" namespace simdjson { enum { /** * The major version (MAJOR.minor.revision) of simdjson being used. */ - SIMDJSON_VERSION_MAJOR = 3, + SIMDJSON_VERSION_MAJOR = 4, /** * The minor version (major.MINOR.revision) of simdjson being used. */ - SIMDJSON_VERSION_MINOR = 13, + SIMDJSON_VERSION_MINOR = 0, /** * The revision (major.minor.REVISION) of simdjson being used. */ diff --git a/include/simdjson/westmere/implementation.h b/include/simdjson/westmere/implementation.h index c7e655b5e..37392be2a 100644 --- a/include/simdjson/westmere/implementation.h +++ b/include/simdjson/westmere/implementation.h @@ -24,7 +24,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace westmere diff --git a/singleheader/amalgamate.py b/singleheader/amalgamate.py index d6590f28a..6b556a783 100755 --- a/singleheader/amalgamate.py +++ b/singleheader/amalgamate.py @@ -296,10 +296,10 @@ class SimdjsonRepository: class Amalgamator: @classmethod - def amalgamate(cls, output_path: str, filename: str, roots: List[RelativeRoot], timestamp: str): + def amalgamate(cls, output_path: str, filename: str, roots: List[RelativeRoot], timestamp: str, version: str): print(f"Creating {output_path}") fid = open(output_path, 'w') - print(f"/* auto-generated on {timestamp}. Do not edit! */", file=fid) + print(f"/* auto-generated on {timestamp}. version {version} Do not edit! */", file=fid) amalgamator = cls(fid, SimdjsonRepository(PROJECTPATH, roots)) file = amalgamator.repository[filename] assert file, f"{filename} not found in {[os.path.join(PROJECTPATH, root) for root in roots]}!" @@ -480,8 +480,13 @@ AMAL_C = os.path.join(AMALGAMATE_OUTPUT_PATH, "simdjson.cpp") DEMOCPP = os.path.join(AMALGAMATE_OUTPUT_PATH, "amalgamate_demo.cpp") README = os.path.join(AMALGAMATE_OUTPUT_PATH, "README.md") -Amalgamator.amalgamate(AMAL_H, "simdjson.h", ['include'], timestamp).validate_all_files_used('include') -Amalgamator.amalgamate(AMAL_C, "simdjson.cpp", ['src', 'include'], timestamp).validate_all_files_used('src') +def read_version(): + with open(os.path.join(PROJECTPATH, 'include/simdjson/simdjson_version.h')) as f: + return re.search(r'\d+\.\d+\.\d+', f.read()).group(0) + +version = read_version() +Amalgamator.amalgamate(AMAL_H, "simdjson.h", ['include'], timestamp, version).validate_all_files_used('include') +Amalgamator.amalgamate(AMAL_C, "simdjson.cpp", ['src', 'include'], timestamp, version).validate_all_files_used('src') # copy the README and DEMOCPP if SCRIPTPATH != AMALGAMATE_OUTPUT_PATH: diff --git a/singleheader/simdjson.cpp b/singleheader/simdjson.cpp index 25ea0da3b..5f15de3f9 100644 --- a/singleheader/simdjson.cpp +++ b/singleheader/simdjson.cpp @@ -1,4 +1,4 @@ -/* auto-generated on 2025-08-03 18:36:14 +0000. Do not edit! */ +/* auto-generated on 2025-08-03 19:26:58 +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>; @@ -6419,15 +6419,6 @@ simdjson_inline simdjson_warn_unused bool validate_utf8(const std::string_view s return validate_utf8(sv.data(), sv.size()); } -/** - * Write the string to the output buffer while escaping double-quote, backlash and ascii control characters. - * - * @param input the string_view to escape - * @param out output buffer (for escaped string): to be safe, it should have 6 * input.size() allocated bytes. - * @return number of bytes written - */ -simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) noexcept; - /** * Validate the UTF-8 string. * @@ -6529,14 +6520,6 @@ public: */ simdjson_warn_unused virtual bool validate_utf8(const char *buf, size_t len) const noexcept = 0; - /** - * Write the string to the output buffer while escaping double-quote, backlash and ascii control characters. - * - * @param input the string_view to escape - * @param out output buffer (for escaped string): to be safe, it should have 6 * input.size() allocated bytes. - * @return number of bytes written - */ - simdjson_warn_unused virtual size_t write_string_escaped(const std::string_view input, char *out) const noexcept = 0; protected: /** @private Construct an implementation with the given name and description. For subclasses. */ simdjson_inline implementation( @@ -7262,7 +7245,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace arm64 @@ -7311,7 +7293,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace fallback @@ -7363,7 +7344,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace haswell @@ -7414,7 +7394,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace icelake @@ -7469,7 +7448,6 @@ public: size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace ppc64 @@ -7516,7 +7494,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace westmere @@ -7562,7 +7539,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace lsx @@ -7608,7 +7584,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace lasx @@ -7695,9 +7670,6 @@ public: simdjson_warn_unused bool validate_utf8(const char * buf, size_t len) const noexcept final override { return set_best()->validate_utf8(buf, len); } - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final { - return set_best()->write_string_escaped(input, out); - } simdjson_inline detect_best_supported_implementation_on_first_use() noexcept : implementation("best_supported_detector", "Detects the best supported implementation and sets it", 0) {} private: const implementation *set_best() const noexcept; @@ -7748,9 +7720,6 @@ public: simdjson_warn_unused error_code minify(const uint8_t *, size_t, uint8_t *, size_t &) const noexcept final override { return UNSUPPORTED_ARCHITECTURE; } - simdjson_warn_unused size_t write_string_escaped(const std::string_view, char *) const noexcept final override { - return 0; // TODO: Evaluate whether this is the right thing to do for unsupported architecture. - } simdjson_warn_unused bool validate_utf8(const char *, size_t) const noexcept final override { return false; // Just refuse to validate. Given that we have a fallback implementation // it seems unlikely that unsupported_implementation will ever be used. If it is used, @@ -7834,9 +7803,6 @@ simdjson_warn_unused error_code minify(const char *buf, size_t len, char *dst, s simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) noexcept { return get_active_implementation()->validate_utf8(buf, len); } -simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) noexcept { - return get_active_implementation()->write_string_escaped(input, out); -} const implementation * builtin_implementation() { static const implementation * builtin_impl = get_available_implementations()[SIMDJSON_STRINGIFY(SIMDJSON_BUILTIN_IMPLEMENTATION)]; assert(builtin_impl); @@ -10678,7 +10644,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace arm64 @@ -13895,104 +13860,6 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t } } -///////////// -/// TODO: This function is not used in the codebase. It is not clear if it is needed. -///////////// -simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) noexcept { - // We are making the following assumption: most strings will either be very short or they will not - // need escaping. - size_t i = 0; - size_t pos = 0; - /*if(input.size() >= escaping::BYTES_PROCESSED) { - auto vec_processing = [input,out]() -> size_t { - size_t index = 0; - size_t position = 0; - for(;input.size() - index >= escaping::BYTES_PROCESSED; index += escaping::BYTES_PROCESSED) { - escaping vinput = escaping::copy_and_find(reinterpret_cast(input.data()) + index, reinterpret_cast(out) + position); - if(vinput.has_escape()) { - return index + vinput.escape_index(); // We have a character that needs escaping - } - position += escaping::BYTES_PROCESSED; - } - if(index == input.size()) { return input.size(); } - // We virtually backtrack so we can load a full vector register - index = input.size() - escaping::BYTES_PROCESSED; - position = index; - escaping vinput = escaping::copy_and_find(reinterpret_cast(input.data()) + index, reinterpret_cast(out) + position); - if(vinput.has_escape()) { - return index + vinput.escape_index(); // We have a character that needs escaping - } - return input.size(); - }; - i = vec_processing(); - pos = i; - if(i == input.size()) { return pos; } - // Here we only continue if there was a character that needed escaping. - }*/ - static std::string_view control_chars[] = { - "\\x0000", "\\x0001", "\\x0002", "\\x0003", "\\x0004", "\\x0005", "\\x0006", - "\\x0007", "\\x0008", "\\t", "\\n", "\\x000b", "\\f", "\\r", - "\\x000e", "\\x000f", "\\x0010", "\\x0011", "\\x0012", "\\x0013", "\\x0014", - "\\x0015", "\\x0016", "\\x0017", "\\x0018", "\\x0019", "\\x001a", "\\x001b", - "\\x001c", "\\x001d", "\\x001e", "\\x001f"}; - static std::array json_quotable_character = - []() SIMDJSON_CONSTEXPR_LAMBDA { - std::array result{}; - for (int index = 0; index < 32; index++) { - result[index] = 1; - } - for (int index : {'"', '\\'}) { - result[index] = 1; - } - return result; - }(); - // The rest could possibly be vectorized, but consider that we expect most strings - // to be short or not to require escaping. - for (; i < input.size(); i++) { - uint8_t c = static_cast(input[i]); - if(json_quotable_character[c]) { - switch (c) { - case '"': - out[pos++] = '\\'; - out[pos++] = '"'; - break; - case '\\': - out[pos++] = '\\'; - out[pos++] = '\\'; - break; - case '\b': - out[pos++] = '\\'; - out[pos++] = 'b'; - break; - case '\f': - out[pos++] = '\\'; - out[pos++] = 'f'; - break; - case '\n': - out[pos++] = '\\'; - out[pos++] = 'n'; - break; - case '\r': - out[pos++] = '\\'; - out[pos++] = 'r'; - break; - case '\t': - out[pos++] = '\\'; - out[pos++] = 't'; - break; - default: - control_chars[c].copy(out + pos, 6); - pos += 6; - } - } else { - out[pos++] = c; - } - } - return pos; -} - - - } // namespace stringparsing } // unnamed namespace @@ -14522,10 +14389,6 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t * return stage2(_doc); } -simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept { - return arm64::stringparsing::write_string_escaped(input, out); -} - } // namespace arm64 } // namespace simdjson @@ -17229,7 +17092,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace haswell @@ -20297,104 +20159,6 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t } } -///////////// -/// TODO: This function is not used in the codebase. It is not clear if it is needed. -///////////// -simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) noexcept { - // We are making the following assumption: most strings will either be very short or they will not - // need escaping. - size_t i = 0; - size_t pos = 0; - /*if(input.size() >= escaping::BYTES_PROCESSED) { - auto vec_processing = [input,out]() -> size_t { - size_t index = 0; - size_t position = 0; - for(;input.size() - index >= escaping::BYTES_PROCESSED; index += escaping::BYTES_PROCESSED) { - escaping vinput = escaping::copy_and_find(reinterpret_cast(input.data()) + index, reinterpret_cast(out) + position); - if(vinput.has_escape()) { - return index + vinput.escape_index(); // We have a character that needs escaping - } - position += escaping::BYTES_PROCESSED; - } - if(index == input.size()) { return input.size(); } - // We virtually backtrack so we can load a full vector register - index = input.size() - escaping::BYTES_PROCESSED; - position = index; - escaping vinput = escaping::copy_and_find(reinterpret_cast(input.data()) + index, reinterpret_cast(out) + position); - if(vinput.has_escape()) { - return index + vinput.escape_index(); // We have a character that needs escaping - } - return input.size(); - }; - i = vec_processing(); - pos = i; - if(i == input.size()) { return pos; } - // Here we only continue if there was a character that needed escaping. - }*/ - static std::string_view control_chars[] = { - "\\x0000", "\\x0001", "\\x0002", "\\x0003", "\\x0004", "\\x0005", "\\x0006", - "\\x0007", "\\x0008", "\\t", "\\n", "\\x000b", "\\f", "\\r", - "\\x000e", "\\x000f", "\\x0010", "\\x0011", "\\x0012", "\\x0013", "\\x0014", - "\\x0015", "\\x0016", "\\x0017", "\\x0018", "\\x0019", "\\x001a", "\\x001b", - "\\x001c", "\\x001d", "\\x001e", "\\x001f"}; - static std::array json_quotable_character = - []() SIMDJSON_CONSTEXPR_LAMBDA { - std::array result{}; - for (int index = 0; index < 32; index++) { - result[index] = 1; - } - for (int index : {'"', '\\'}) { - result[index] = 1; - } - return result; - }(); - // The rest could possibly be vectorized, but consider that we expect most strings - // to be short or not to require escaping. - for (; i < input.size(); i++) { - uint8_t c = static_cast(input[i]); - if(json_quotable_character[c]) { - switch (c) { - case '"': - out[pos++] = '\\'; - out[pos++] = '"'; - break; - case '\\': - out[pos++] = '\\'; - out[pos++] = '\\'; - break; - case '\b': - out[pos++] = '\\'; - out[pos++] = 'b'; - break; - case '\f': - out[pos++] = '\\'; - out[pos++] = 'f'; - break; - case '\n': - out[pos++] = '\\'; - out[pos++] = 'n'; - break; - case '\r': - out[pos++] = '\\'; - out[pos++] = 'r'; - break; - case '\t': - out[pos++] = '\\'; - out[pos++] = 't'; - break; - default: - control_chars[c].copy(out + pos, 6); - pos += 6; - } - } else { - out[pos++] = c; - } - } - return pos; -} - - - } // namespace stringparsing } // unnamed namespace @@ -20921,10 +20685,6 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t * return stage2(_doc); } -simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept { - return haswell::stringparsing::write_string_escaped(input, out); -} - } // namespace haswell } // namespace simdjson @@ -23627,7 +23387,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace icelake @@ -26694,104 +26453,6 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t } } -///////////// -/// TODO: This function is not used in the codebase. It is not clear if it is needed. -///////////// -simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) noexcept { - // We are making the following assumption: most strings will either be very short or they will not - // need escaping. - size_t i = 0; - size_t pos = 0; - /*if(input.size() >= escaping::BYTES_PROCESSED) { - auto vec_processing = [input,out]() -> size_t { - size_t index = 0; - size_t position = 0; - for(;input.size() - index >= escaping::BYTES_PROCESSED; index += escaping::BYTES_PROCESSED) { - escaping vinput = escaping::copy_and_find(reinterpret_cast(input.data()) + index, reinterpret_cast(out) + position); - if(vinput.has_escape()) { - return index + vinput.escape_index(); // We have a character that needs escaping - } - position += escaping::BYTES_PROCESSED; - } - if(index == input.size()) { return input.size(); } - // We virtually backtrack so we can load a full vector register - index = input.size() - escaping::BYTES_PROCESSED; - position = index; - escaping vinput = escaping::copy_and_find(reinterpret_cast(input.data()) + index, reinterpret_cast(out) + position); - if(vinput.has_escape()) { - return index + vinput.escape_index(); // We have a character that needs escaping - } - return input.size(); - }; - i = vec_processing(); - pos = i; - if(i == input.size()) { return pos; } - // Here we only continue if there was a character that needed escaping. - }*/ - static std::string_view control_chars[] = { - "\\x0000", "\\x0001", "\\x0002", "\\x0003", "\\x0004", "\\x0005", "\\x0006", - "\\x0007", "\\x0008", "\\t", "\\n", "\\x000b", "\\f", "\\r", - "\\x000e", "\\x000f", "\\x0010", "\\x0011", "\\x0012", "\\x0013", "\\x0014", - "\\x0015", "\\x0016", "\\x0017", "\\x0018", "\\x0019", "\\x001a", "\\x001b", - "\\x001c", "\\x001d", "\\x001e", "\\x001f"}; - static std::array json_quotable_character = - []() SIMDJSON_CONSTEXPR_LAMBDA { - std::array result{}; - for (int index = 0; index < 32; index++) { - result[index] = 1; - } - for (int index : {'"', '\\'}) { - result[index] = 1; - } - return result; - }(); - // The rest could possibly be vectorized, but consider that we expect most strings - // to be short or not to require escaping. - for (; i < input.size(); i++) { - uint8_t c = static_cast(input[i]); - if(json_quotable_character[c]) { - switch (c) { - case '"': - out[pos++] = '\\'; - out[pos++] = '"'; - break; - case '\\': - out[pos++] = '\\'; - out[pos++] = '\\'; - break; - case '\b': - out[pos++] = '\\'; - out[pos++] = 'b'; - break; - case '\f': - out[pos++] = '\\'; - out[pos++] = 'f'; - break; - case '\n': - out[pos++] = '\\'; - out[pos++] = 'n'; - break; - case '\r': - out[pos++] = '\\'; - out[pos++] = 'r'; - break; - case '\t': - out[pos++] = '\\'; - out[pos++] = 't'; - break; - default: - control_chars[c].copy(out + pos, 6); - pos += 6; - } - } else { - out[pos++] = c; - } - } - return pos; -} - - - } // namespace stringparsing } // unnamed namespace @@ -27361,10 +27022,6 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t * return stage2(_doc); } -simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept { - return icelake::stringparsing::write_string_escaped(input, out); -} - } // namespace icelake } // namespace simdjson @@ -30183,7 +29840,6 @@ public: size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace ppc64 @@ -33362,104 +33018,6 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t } } -///////////// -/// TODO: This function is not used in the codebase. It is not clear if it is needed. -///////////// -simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) noexcept { - // We are making the following assumption: most strings will either be very short or they will not - // need escaping. - size_t i = 0; - size_t pos = 0; - /*if(input.size() >= escaping::BYTES_PROCESSED) { - auto vec_processing = [input,out]() -> size_t { - size_t index = 0; - size_t position = 0; - for(;input.size() - index >= escaping::BYTES_PROCESSED; index += escaping::BYTES_PROCESSED) { - escaping vinput = escaping::copy_and_find(reinterpret_cast(input.data()) + index, reinterpret_cast(out) + position); - if(vinput.has_escape()) { - return index + vinput.escape_index(); // We have a character that needs escaping - } - position += escaping::BYTES_PROCESSED; - } - if(index == input.size()) { return input.size(); } - // We virtually backtrack so we can load a full vector register - index = input.size() - escaping::BYTES_PROCESSED; - position = index; - escaping vinput = escaping::copy_and_find(reinterpret_cast(input.data()) + index, reinterpret_cast(out) + position); - if(vinput.has_escape()) { - return index + vinput.escape_index(); // We have a character that needs escaping - } - return input.size(); - }; - i = vec_processing(); - pos = i; - if(i == input.size()) { return pos; } - // Here we only continue if there was a character that needed escaping. - }*/ - static std::string_view control_chars[] = { - "\\x0000", "\\x0001", "\\x0002", "\\x0003", "\\x0004", "\\x0005", "\\x0006", - "\\x0007", "\\x0008", "\\t", "\\n", "\\x000b", "\\f", "\\r", - "\\x000e", "\\x000f", "\\x0010", "\\x0011", "\\x0012", "\\x0013", "\\x0014", - "\\x0015", "\\x0016", "\\x0017", "\\x0018", "\\x0019", "\\x001a", "\\x001b", - "\\x001c", "\\x001d", "\\x001e", "\\x001f"}; - static std::array json_quotable_character = - []() SIMDJSON_CONSTEXPR_LAMBDA { - std::array result{}; - for (int index = 0; index < 32; index++) { - result[index] = 1; - } - for (int index : {'"', '\\'}) { - result[index] = 1; - } - return result; - }(); - // The rest could possibly be vectorized, but consider that we expect most strings - // to be short or not to require escaping. - for (; i < input.size(); i++) { - uint8_t c = static_cast(input[i]); - if(json_quotable_character[c]) { - switch (c) { - case '"': - out[pos++] = '\\'; - out[pos++] = '"'; - break; - case '\\': - out[pos++] = '\\'; - out[pos++] = '\\'; - break; - case '\b': - out[pos++] = '\\'; - out[pos++] = 'b'; - break; - case '\f': - out[pos++] = '\\'; - out[pos++] = 'f'; - break; - case '\n': - out[pos++] = '\\'; - out[pos++] = 'n'; - break; - case '\r': - out[pos++] = '\\'; - out[pos++] = 'r'; - break; - case '\t': - out[pos++] = '\\'; - out[pos++] = 't'; - break; - default: - control_chars[c].copy(out + pos, 6); - pos += 6; - } - } else { - out[pos++] = c; - } - } - return pos; -} - - - } // namespace stringparsing } // unnamed namespace @@ -33959,10 +33517,6 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t * return stage2(_doc); } -simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept { - return ppc64::stringparsing::write_string_escaped(input, out); -} - } // namespace ppc64 } // namespace simdjson @@ -37092,7 +36646,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace westmere @@ -40590,104 +40143,6 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t } } -///////////// -/// TODO: This function is not used in the codebase. It is not clear if it is needed. -///////////// -simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) noexcept { - // We are making the following assumption: most strings will either be very short or they will not - // need escaping. - size_t i = 0; - size_t pos = 0; - /*if(input.size() >= escaping::BYTES_PROCESSED) { - auto vec_processing = [input,out]() -> size_t { - size_t index = 0; - size_t position = 0; - for(;input.size() - index >= escaping::BYTES_PROCESSED; index += escaping::BYTES_PROCESSED) { - escaping vinput = escaping::copy_and_find(reinterpret_cast(input.data()) + index, reinterpret_cast(out) + position); - if(vinput.has_escape()) { - return index + vinput.escape_index(); // We have a character that needs escaping - } - position += escaping::BYTES_PROCESSED; - } - if(index == input.size()) { return input.size(); } - // We virtually backtrack so we can load a full vector register - index = input.size() - escaping::BYTES_PROCESSED; - position = index; - escaping vinput = escaping::copy_and_find(reinterpret_cast(input.data()) + index, reinterpret_cast(out) + position); - if(vinput.has_escape()) { - return index + vinput.escape_index(); // We have a character that needs escaping - } - return input.size(); - }; - i = vec_processing(); - pos = i; - if(i == input.size()) { return pos; } - // Here we only continue if there was a character that needed escaping. - }*/ - static std::string_view control_chars[] = { - "\\x0000", "\\x0001", "\\x0002", "\\x0003", "\\x0004", "\\x0005", "\\x0006", - "\\x0007", "\\x0008", "\\t", "\\n", "\\x000b", "\\f", "\\r", - "\\x000e", "\\x000f", "\\x0010", "\\x0011", "\\x0012", "\\x0013", "\\x0014", - "\\x0015", "\\x0016", "\\x0017", "\\x0018", "\\x0019", "\\x001a", "\\x001b", - "\\x001c", "\\x001d", "\\x001e", "\\x001f"}; - static std::array json_quotable_character = - []() SIMDJSON_CONSTEXPR_LAMBDA { - std::array result{}; - for (int index = 0; index < 32; index++) { - result[index] = 1; - } - for (int index : {'"', '\\'}) { - result[index] = 1; - } - return result; - }(); - // The rest could possibly be vectorized, but consider that we expect most strings - // to be short or not to require escaping. - for (; i < input.size(); i++) { - uint8_t c = static_cast(input[i]); - if(json_quotable_character[c]) { - switch (c) { - case '"': - out[pos++] = '\\'; - out[pos++] = '"'; - break; - case '\\': - out[pos++] = '\\'; - out[pos++] = '\\'; - break; - case '\b': - out[pos++] = '\\'; - out[pos++] = 'b'; - break; - case '\f': - out[pos++] = '\\'; - out[pos++] = 'f'; - break; - case '\n': - out[pos++] = '\\'; - out[pos++] = 'n'; - break; - case '\r': - out[pos++] = '\\'; - out[pos++] = 'r'; - break; - case '\t': - out[pos++] = '\\'; - out[pos++] = 't'; - break; - default: - control_chars[c].copy(out + pos, 6); - pos += 6; - } - } else { - out[pos++] = c; - } - } - return pos; -} - - - } // namespace stringparsing } // unnamed namespace @@ -41218,11 +40673,6 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t * if (error) { return error; } return stage2(_doc); } - -simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept { - return westmere::stringparsing::write_string_escaped(input, out); -} - } // namespace westmere } // namespace simdjson @@ -43825,7 +43275,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace lsx @@ -46797,104 +46246,6 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t } } -///////////// -/// TODO: This function is not used in the codebase. It is not clear if it is needed. -///////////// -simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) noexcept { - // We are making the following assumption: most strings will either be very short or they will not - // need escaping. - size_t i = 0; - size_t pos = 0; - /*if(input.size() >= escaping::BYTES_PROCESSED) { - auto vec_processing = [input,out]() -> size_t { - size_t index = 0; - size_t position = 0; - for(;input.size() - index >= escaping::BYTES_PROCESSED; index += escaping::BYTES_PROCESSED) { - escaping vinput = escaping::copy_and_find(reinterpret_cast(input.data()) + index, reinterpret_cast(out) + position); - if(vinput.has_escape()) { - return index + vinput.escape_index(); // We have a character that needs escaping - } - position += escaping::BYTES_PROCESSED; - } - if(index == input.size()) { return input.size(); } - // We virtually backtrack so we can load a full vector register - index = input.size() - escaping::BYTES_PROCESSED; - position = index; - escaping vinput = escaping::copy_and_find(reinterpret_cast(input.data()) + index, reinterpret_cast(out) + position); - if(vinput.has_escape()) { - return index + vinput.escape_index(); // We have a character that needs escaping - } - return input.size(); - }; - i = vec_processing(); - pos = i; - if(i == input.size()) { return pos; } - // Here we only continue if there was a character that needed escaping. - }*/ - static std::string_view control_chars[] = { - "\\x0000", "\\x0001", "\\x0002", "\\x0003", "\\x0004", "\\x0005", "\\x0006", - "\\x0007", "\\x0008", "\\t", "\\n", "\\x000b", "\\f", "\\r", - "\\x000e", "\\x000f", "\\x0010", "\\x0011", "\\x0012", "\\x0013", "\\x0014", - "\\x0015", "\\x0016", "\\x0017", "\\x0018", "\\x0019", "\\x001a", "\\x001b", - "\\x001c", "\\x001d", "\\x001e", "\\x001f"}; - static std::array json_quotable_character = - []() SIMDJSON_CONSTEXPR_LAMBDA { - std::array result{}; - for (int index = 0; index < 32; index++) { - result[index] = 1; - } - for (int index : {'"', '\\'}) { - result[index] = 1; - } - return result; - }(); - // The rest could possibly be vectorized, but consider that we expect most strings - // to be short or not to require escaping. - for (; i < input.size(); i++) { - uint8_t c = static_cast(input[i]); - if(json_quotable_character[c]) { - switch (c) { - case '"': - out[pos++] = '\\'; - out[pos++] = '"'; - break; - case '\\': - out[pos++] = '\\'; - out[pos++] = '\\'; - break; - case '\b': - out[pos++] = '\\'; - out[pos++] = 'b'; - break; - case '\f': - out[pos++] = '\\'; - out[pos++] = 'f'; - break; - case '\n': - out[pos++] = '\\'; - out[pos++] = 'n'; - break; - case '\r': - out[pos++] = '\\'; - out[pos++] = 'r'; - break; - case '\t': - out[pos++] = '\\'; - out[pos++] = 't'; - break; - default: - control_chars[c].copy(out + pos, 6); - pos += 6; - } - } else { - out[pos++] = c; - } - } - return pos; -} - - - } // namespace stringparsing } // unnamed namespace @@ -47388,10 +46739,6 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t * return stage2(_doc); } -simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept { - return lsx::stringparsing::write_string_escaped(input, out); -} - } // namespace lsx } // namespace simdjson @@ -50007,7 +49354,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace lasx @@ -52995,104 +52341,6 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t } } -///////////// -/// TODO: This function is not used in the codebase. It is not clear if it is needed. -///////////// -simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) noexcept { - // We are making the following assumption: most strings will either be very short or they will not - // need escaping. - size_t i = 0; - size_t pos = 0; - /*if(input.size() >= escaping::BYTES_PROCESSED) { - auto vec_processing = [input,out]() -> size_t { - size_t index = 0; - size_t position = 0; - for(;input.size() - index >= escaping::BYTES_PROCESSED; index += escaping::BYTES_PROCESSED) { - escaping vinput = escaping::copy_and_find(reinterpret_cast(input.data()) + index, reinterpret_cast(out) + position); - if(vinput.has_escape()) { - return index + vinput.escape_index(); // We have a character that needs escaping - } - position += escaping::BYTES_PROCESSED; - } - if(index == input.size()) { return input.size(); } - // We virtually backtrack so we can load a full vector register - index = input.size() - escaping::BYTES_PROCESSED; - position = index; - escaping vinput = escaping::copy_and_find(reinterpret_cast(input.data()) + index, reinterpret_cast(out) + position); - if(vinput.has_escape()) { - return index + vinput.escape_index(); // We have a character that needs escaping - } - return input.size(); - }; - i = vec_processing(); - pos = i; - if(i == input.size()) { return pos; } - // Here we only continue if there was a character that needed escaping. - }*/ - static std::string_view control_chars[] = { - "\\x0000", "\\x0001", "\\x0002", "\\x0003", "\\x0004", "\\x0005", "\\x0006", - "\\x0007", "\\x0008", "\\t", "\\n", "\\x000b", "\\f", "\\r", - "\\x000e", "\\x000f", "\\x0010", "\\x0011", "\\x0012", "\\x0013", "\\x0014", - "\\x0015", "\\x0016", "\\x0017", "\\x0018", "\\x0019", "\\x001a", "\\x001b", - "\\x001c", "\\x001d", "\\x001e", "\\x001f"}; - static std::array json_quotable_character = - []() SIMDJSON_CONSTEXPR_LAMBDA { - std::array result{}; - for (int index = 0; index < 32; index++) { - result[index] = 1; - } - for (int index : {'"', '\\'}) { - result[index] = 1; - } - return result; - }(); - // The rest could possibly be vectorized, but consider that we expect most strings - // to be short or not to require escaping. - for (; i < input.size(); i++) { - uint8_t c = static_cast(input[i]); - if(json_quotable_character[c]) { - switch (c) { - case '"': - out[pos++] = '\\'; - out[pos++] = '"'; - break; - case '\\': - out[pos++] = '\\'; - out[pos++] = '\\'; - break; - case '\b': - out[pos++] = '\\'; - out[pos++] = 'b'; - break; - case '\f': - out[pos++] = '\\'; - out[pos++] = 'f'; - break; - case '\n': - out[pos++] = '\\'; - out[pos++] = 'n'; - break; - case '\r': - out[pos++] = '\\'; - out[pos++] = 'r'; - break; - case '\t': - out[pos++] = '\\'; - out[pos++] = 't'; - break; - default: - control_chars[c].copy(out + pos, 6); - pos += 6; - } - } else { - out[pos++] = c; - } - } - return pos; -} - - - } // namespace stringparsing } // unnamed namespace @@ -53582,10 +52830,6 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t * return stage2(_doc); } -simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept { - return lasx::stringparsing::write_string_escaped(input, out); -} - } // namespace lasx } // namespace simdjson @@ -55783,7 +55027,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace fallback @@ -56365,104 +55608,6 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t } } -///////////// -/// TODO: This function is not used in the codebase. It is not clear if it is needed. -///////////// -simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) noexcept { - // We are making the following assumption: most strings will either be very short or they will not - // need escaping. - size_t i = 0; - size_t pos = 0; - /*if(input.size() >= escaping::BYTES_PROCESSED) { - auto vec_processing = [input,out]() -> size_t { - size_t index = 0; - size_t position = 0; - for(;input.size() - index >= escaping::BYTES_PROCESSED; index += escaping::BYTES_PROCESSED) { - escaping vinput = escaping::copy_and_find(reinterpret_cast(input.data()) + index, reinterpret_cast(out) + position); - if(vinput.has_escape()) { - return index + vinput.escape_index(); // We have a character that needs escaping - } - position += escaping::BYTES_PROCESSED; - } - if(index == input.size()) { return input.size(); } - // We virtually backtrack so we can load a full vector register - index = input.size() - escaping::BYTES_PROCESSED; - position = index; - escaping vinput = escaping::copy_and_find(reinterpret_cast(input.data()) + index, reinterpret_cast(out) + position); - if(vinput.has_escape()) { - return index + vinput.escape_index(); // We have a character that needs escaping - } - return input.size(); - }; - i = vec_processing(); - pos = i; - if(i == input.size()) { return pos; } - // Here we only continue if there was a character that needed escaping. - }*/ - static std::string_view control_chars[] = { - "\\x0000", "\\x0001", "\\x0002", "\\x0003", "\\x0004", "\\x0005", "\\x0006", - "\\x0007", "\\x0008", "\\t", "\\n", "\\x000b", "\\f", "\\r", - "\\x000e", "\\x000f", "\\x0010", "\\x0011", "\\x0012", "\\x0013", "\\x0014", - "\\x0015", "\\x0016", "\\x0017", "\\x0018", "\\x0019", "\\x001a", "\\x001b", - "\\x001c", "\\x001d", "\\x001e", "\\x001f"}; - static std::array json_quotable_character = - []() SIMDJSON_CONSTEXPR_LAMBDA { - std::array result{}; - for (int index = 0; index < 32; index++) { - result[index] = 1; - } - for (int index : {'"', '\\'}) { - result[index] = 1; - } - return result; - }(); - // The rest could possibly be vectorized, but consider that we expect most strings - // to be short or not to require escaping. - for (; i < input.size(); i++) { - uint8_t c = static_cast(input[i]); - if(json_quotable_character[c]) { - switch (c) { - case '"': - out[pos++] = '\\'; - out[pos++] = '"'; - break; - case '\\': - out[pos++] = '\\'; - out[pos++] = '\\'; - break; - case '\b': - out[pos++] = '\\'; - out[pos++] = 'b'; - break; - case '\f': - out[pos++] = '\\'; - out[pos++] = 'f'; - break; - case '\n': - out[pos++] = '\\'; - out[pos++] = 'n'; - break; - case '\r': - out[pos++] = '\\'; - out[pos++] = 'r'; - break; - case '\t': - out[pos++] = '\\'; - out[pos++] = 't'; - break; - default: - control_chars[c].copy(out + pos, 6); - pos += 6; - } - } else { - out[pos++] = c; - } - } - return pos; -} - - - } // namespace stringparsing } // unnamed namespace @@ -57769,10 +56914,6 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t * return stage2(_doc); } -simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept { - return fallback::stringparsing::write_string_escaped(input, out); -} - } // namespace fallback } // namespace simdjson diff --git a/singleheader/simdjson.h b/singleheader/simdjson.h index 1d90229be..9fb3b5ed0 100644 --- a/singleheader/simdjson.h +++ b/singleheader/simdjson.h @@ -1,4 +1,4 @@ -/* auto-generated on 2025-08-03 18:36:14 +0000. Do not edit! */ +/* auto-generated on 2025-08-03 19:26:58 +0000. version 4.0.0 Do not edit! */ /* including simdjson.h: */ /* begin file simdjson.h */ #ifndef SIMDJSON_H @@ -597,17 +597,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 @@ -2464,6 +2453,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 */ @@ -2477,18 +2478,18 @@ namespace std { #define SIMDJSON_SIMDJSON_VERSION_H /** The version of simdjson being used (major.minor.revision) */ -#define SIMDJSON_VERSION "3.13.0" +#define SIMDJSON_VERSION "4.0.0" namespace simdjson { enum { /** * The major version (MAJOR.minor.revision) of simdjson being used. */ - SIMDJSON_VERSION_MAJOR = 3, + SIMDJSON_VERSION_MAJOR = 4, /** * The minor version (major.MINOR.revision) of simdjson being used. */ - SIMDJSON_VERSION_MINOR = 13, + SIMDJSON_VERSION_MINOR = 0, /** * The revision (major.minor.REVISION) of simdjson being used. */ @@ -2971,7 +2972,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>; @@ -3564,15 +3564,6 @@ simdjson_inline simdjson_warn_unused bool validate_utf8(const std::string_view s return validate_utf8(sv.data(), sv.size()); } -/** - * Write the string to the output buffer while escaping double-quote, backlash and ascii control characters. - * - * @param input the string_view to escape - * @param out output buffer (for escaped string): to be safe, it should have 6 * input.size() allocated bytes. - * @return number of bytes written - */ -simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) noexcept; - /** * Validate the UTF-8 string. * @@ -3674,14 +3665,6 @@ public: */ simdjson_warn_unused virtual bool validate_utf8(const char *buf, size_t len) const noexcept = 0; - /** - * Write the string to the output buffer while escaping double-quote, backlash and ascii control characters. - * - * @param input the string_view to escape - * @param out output buffer (for escaped string): to be safe, it should have 6 * input.size() allocated bytes. - * @return number of bytes written - */ - simdjson_warn_unused virtual size_t write_string_escaped(const std::string_view input, char *out) const noexcept = 0; protected: /** @private Construct an implementation with the given name and description. For subclasses. */ simdjson_inline implementation( @@ -4297,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); } @@ -6760,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 { @@ -9957,7 +9943,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace arm64 @@ -9996,7 +9981,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace fallback @@ -10037,7 +10021,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace haswell @@ -10078,7 +10061,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace icelake @@ -10123,7 +10105,6 @@ public: size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace ppc64 @@ -10160,7 +10141,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace westmere @@ -10196,7 +10176,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace lsx @@ -10232,7 +10211,6 @@ public: ) const noexcept final; simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final; }; } // namespace lasx @@ -32473,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; @@ -33112,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 " @@ -35907,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. * @@ -35916,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. * @@ -37523,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 /** @@ -37844,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(); @@ -37913,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 { @@ -37962,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; } @@ -37971,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; } @@ -37980,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; } @@ -37989,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; } @@ -37998,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; } @@ -38011,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; } @@ -38020,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; } @@ -38029,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; } @@ -38038,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; } @@ -38047,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; } @@ -38056,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 @@ -39142,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(); } @@ -42124,6 +42248,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 { @@ -43570,7 +43697,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 { @@ -43697,7 +43824,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 /** @@ -44072,9 +44199,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, @@ -44086,9 +44213,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, @@ -44115,7 +44242,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"); @@ -44285,7 +44412,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 @@ -44403,6 +44530,8 @@ simdjson_inline void string_builder::append_key_value(key_type key, value_type v #include #include #include +#include +#include #include #include #include @@ -44483,8 +44612,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('{'); @@ -44500,8 +44634,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())):] >> [&]() { @@ -44534,8 +44787,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; } @@ -44551,7 +44804,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 @@ -44941,7 +45200,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; @@ -45580,6 +45839,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 " @@ -48375,7 +48647,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. * @@ -48384,7 +48656,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. * @@ -49991,6 +50263,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 /** @@ -50312,17 +50598,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(); @@ -50381,17 +50666,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 { @@ -50430,6 +50740,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; } @@ -50439,6 +50753,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; } @@ -50448,6 +50766,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; } @@ -50457,6 +50779,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; } @@ -50466,6 +50792,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; } @@ -50479,6 +50809,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; } @@ -50488,6 +50822,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; } @@ -50497,6 +50835,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; } @@ -50506,6 +50848,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; } @@ -50515,6 +50861,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; } @@ -50524,6 +50874,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 @@ -51610,8 +52015,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(); } @@ -54592,6 +54997,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 { @@ -56038,7 +56446,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 { @@ -56165,7 +56573,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 /** @@ -56540,9 +56948,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, @@ -56554,9 +56962,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, @@ -56583,7 +56991,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"); @@ -56753,7 +57161,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 @@ -56871,6 +57279,8 @@ simdjson_inline void string_builder::append_key_value(key_type key, value_type v #include #include #include +#include +#include #include #include #include @@ -56951,8 +57361,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('{'); @@ -56968,8 +57383,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())):] >> [&]() { @@ -57002,8 +57536,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; } @@ -57019,7 +57553,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 @@ -57908,7 +58448,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; @@ -58547,6 +59087,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 " @@ -61342,7 +61895,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. * @@ -61351,7 +61904,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. * @@ -62958,6 +63511,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 /** @@ -63279,17 +63846,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(); @@ -63348,17 +63914,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 { @@ -63397,6 +63988,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; } @@ -63406,6 +64001,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; } @@ -63415,6 +64014,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; } @@ -63424,6 +64027,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; } @@ -63433,6 +64040,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; } @@ -63446,6 +64057,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; } @@ -63455,6 +64070,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; } @@ -63464,6 +64083,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; } @@ -63473,6 +64096,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; } @@ -63482,6 +64109,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; } @@ -63491,6 +64122,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 @@ -64577,8 +65263,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(); } @@ -67559,6 +68245,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 { @@ -69005,7 +69694,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 { @@ -69132,7 +69821,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 /** @@ -69507,9 +70196,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, @@ -69521,9 +70210,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, @@ -69550,7 +70239,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"); @@ -69720,7 +70409,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 @@ -69838,6 +70527,8 @@ simdjson_inline void string_builder::append_key_value(key_type key, value_type v #include #include #include +#include +#include #include #include #include @@ -69918,8 +70609,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('{'); @@ -69935,8 +70631,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())):] >> [&]() { @@ -69969,8 +70784,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; } @@ -69986,7 +70801,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 @@ -70875,7 +71696,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; @@ -71514,6 +72335,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 " @@ -74309,7 +75143,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. * @@ -74318,7 +75152,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. * @@ -75925,6 +76759,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 /** @@ -76246,17 +77094,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(); @@ -76315,17 +77162,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 { @@ -76364,6 +77236,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; } @@ -76373,6 +77249,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; } @@ -76382,6 +77262,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; } @@ -76391,6 +77275,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; } @@ -76400,6 +77288,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; } @@ -76413,6 +77305,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; } @@ -76422,6 +77318,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; } @@ -76431,6 +77331,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; } @@ -76440,6 +77344,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; } @@ -76449,6 +77357,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; } @@ -76458,6 +77370,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 @@ -77544,8 +78511,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(); } @@ -80526,6 +81493,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 { @@ -81972,7 +82942,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 { @@ -82099,7 +83069,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 /** @@ -82474,9 +83444,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, @@ -82488,9 +83458,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, @@ -82517,7 +83487,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"); @@ -82687,7 +83657,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 @@ -82805,6 +83775,8 @@ simdjson_inline void string_builder::append_key_value(key_type key, value_type v #include #include #include +#include +#include #include #include #include @@ -82885,8 +83857,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('{'); @@ -82902,8 +83879,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())):] >> [&]() { @@ -82936,8 +84032,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; } @@ -82953,7 +84049,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 @@ -83957,7 +85059,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; @@ -84596,6 +85698,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 " @@ -87391,7 +88506,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. * @@ -87400,7 +88515,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. * @@ -89007,6 +90122,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 /** @@ -89328,17 +90457,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(); @@ -89397,17 +90525,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 { @@ -89446,6 +90599,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; } @@ -89455,6 +90612,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; } @@ -89464,6 +90625,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; } @@ -89473,6 +90638,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; } @@ -89482,6 +90651,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; } @@ -89495,6 +90668,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; } @@ -89504,6 +90681,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; } @@ -89513,6 +90694,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; } @@ -89522,6 +90707,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; } @@ -89531,6 +90720,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; } @@ -89540,6 +90733,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 @@ -90626,8 +91874,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(); } @@ -93608,6 +94856,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 { @@ -95054,7 +96305,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 { @@ -95181,7 +96432,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 /** @@ -95556,9 +96807,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, @@ -95570,9 +96821,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, @@ -95599,7 +96850,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"); @@ -95769,7 +97020,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 @@ -95887,6 +97138,8 @@ simdjson_inline void string_builder::append_key_value(key_type key, value_type v #include #include #include +#include +#include #include #include #include @@ -95967,8 +97220,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('{'); @@ -95984,8 +97242,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())):] >> [&]() { @@ -96018,8 +97395,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; } @@ -96035,7 +97412,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 @@ -97355,7 +98738,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; @@ -97994,6 +99377,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 " @@ -100789,7 +102185,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. * @@ -100798,7 +102194,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. * @@ -102405,6 +103801,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 /** @@ -102726,17 +104136,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(); @@ -102795,17 +104204,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 { @@ -102844,6 +104278,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; } @@ -102853,6 +104291,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; } @@ -102862,6 +104304,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; } @@ -102871,6 +104317,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; } @@ -102880,6 +104330,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; } @@ -102893,6 +104347,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; } @@ -102902,6 +104360,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; } @@ -102911,6 +104373,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; } @@ -102920,6 +104386,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; } @@ -102929,6 +104399,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; } @@ -102938,6 +104412,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 @@ -104024,8 +105553,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(); } @@ -107006,6 +108535,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 { @@ -108452,7 +109984,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 { @@ -108579,7 +110111,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 /** @@ -108954,9 +110486,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, @@ -108968,9 +110500,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, @@ -108997,7 +110529,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"); @@ -109167,7 +110699,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 @@ -109285,6 +110817,8 @@ simdjson_inline void string_builder::append_key_value(key_type key, value_type v #include #include #include +#include +#include #include #include #include @@ -109365,8 +110899,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('{'); @@ -109382,8 +110921,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())):] >> [&]() { @@ -109416,8 +111074,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; } @@ -109433,7 +111091,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 @@ -110230,7 +111894,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; @@ -110869,6 +112533,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 " @@ -113664,7 +115341,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. * @@ -113673,7 +115350,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. * @@ -115280,6 +116957,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 /** @@ -115601,17 +117292,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(); @@ -115670,17 +117360,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 { @@ -115719,6 +117434,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; } @@ -115728,6 +117447,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; } @@ -115737,6 +117460,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; } @@ -115746,6 +117473,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; } @@ -115755,6 +117486,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; } @@ -115768,6 +117503,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; } @@ -115777,6 +117516,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; } @@ -115786,6 +117529,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; } @@ -115795,6 +117542,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; } @@ -115804,6 +117555,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; } @@ -115813,6 +117568,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 @@ -116899,8 +118709,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(); } @@ -119881,6 +121691,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 { @@ -121327,7 +123140,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 { @@ -121454,7 +123267,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 /** @@ -121829,9 +123642,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, @@ -121843,9 +123656,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, @@ -121872,7 +123685,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"); @@ -122042,7 +123855,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 @@ -122160,6 +123973,8 @@ simdjson_inline void string_builder::append_key_value(key_type key, value_type v #include #include #include +#include +#include #include #include #include @@ -122240,8 +124055,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('{'); @@ -122257,8 +124077,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())):] >> [&]() { @@ -122291,8 +124230,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; } @@ -122308,7 +124247,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 @@ -123118,7 +125063,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; @@ -123757,6 +125702,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 " @@ -126552,7 +128510,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. * @@ -126561,7 +128519,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. * @@ -128168,6 +130126,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 /** @@ -128489,17 +130461,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(); @@ -128558,17 +130529,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 { @@ -128607,6 +130603,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; } @@ -128616,6 +130616,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; } @@ -128625,6 +130629,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; } @@ -128634,6 +130642,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; } @@ -128643,6 +130655,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; } @@ -128656,6 +130672,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; } @@ -128665,6 +130685,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; } @@ -128674,6 +130698,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; } @@ -128683,6 +130711,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; } @@ -128692,6 +130724,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; } @@ -128701,6 +130737,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 @@ -129787,8 +131878,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(); } @@ -132769,6 +134860,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 { @@ -134215,7 +136309,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 { @@ -134342,7 +136436,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 /** @@ -134717,9 +136811,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, @@ -134731,9 +136825,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, @@ -134760,7 +136854,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"); @@ -134930,7 +137024,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 @@ -135048,6 +137142,8 @@ simdjson_inline void string_builder::append_key_value(key_type key, value_type v #include #include #include +#include +#include #include #include #include @@ -135128,8 +137224,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('{'); @@ -135145,8 +137246,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())):] >> [&]() { @@ -135179,8 +137399,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; } @@ -135196,7 +137416,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 @@ -135244,6 +137470,28 @@ 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 @@ -135338,8 +137586,9 @@ struct [[nodiscard]] auto_parser using const_iterator = auto_iterator; // auto_iterator is already const private: - ondemand::document m_doc; ParserType m_parser; + ondemand::document m_doc; + error_code m_error{SUCCESS}; // Caching the iterator here: iterator::auto_iterator_storage iter_storage{}; @@ -135353,13 +137602,28 @@ public: // non-pointer constructors: explicit auto_parser(ParserType &&parser, ondemand::document &&doc) noexcept requires(!std::is_pointer_v) - : m_doc{std::move(doc)}, m_parser{std::move(parser)} {} + : 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) - // Note: order matters! We need to call iterate BEFORE moving the parser - : m_doc(parser.iterate(str)), m_parser{std::move(parser)} {} + : 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) @@ -135369,12 +137633,28 @@ public: explicit auto_parser(std::remove_pointer_t &parser, ondemand::document &&doc) noexcept requires(std::is_pointer_v) - : m_doc{std::move(doc)}, m_parser{&parser} {} + : 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_doc(parser.iterate(str)), m_parser{&parser} {} + : 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) @@ -135398,6 +137678,9 @@ public: 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(); } @@ -135425,6 +137708,9 @@ public: template [[nodiscard]] simdjson_inline explicit(false) operator T() noexcept(false) { + if (m_error != SUCCESS) { + throw simdjson_error(m_error); + } return m_doc.get(); } @@ -135436,6 +137722,9 @@ public: 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]] { @@ -135445,14 +137734,28 @@ public: } 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()) { - iter_storage = {.m_iter = iterator::type{m_doc.begin()}, - .m_value = iterator::value_type{ - iter_storage.m_iter.at_end() || - iter_storage.m_iter.error() != SUCCESS - ? value_type{} - : *iter_storage.m_iter}}; + // 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}; } diff --git a/singleheader/singleheader.zip b/singleheader/singleheader.zip index 93e47f70e..adf332bc0 100644 Binary files a/singleheader/singleheader.zip and b/singleheader/singleheader.zip differ diff --git a/src/arm64.cpp b/src/arm64.cpp index 7e3a74878..e017ce016 100644 --- a/src/arm64.cpp +++ b/src/arm64.cpp @@ -165,10 +165,6 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t * return stage2(_doc); } -simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept { - return arm64::stringparsing::write_string_escaped(input, out); -} - } // namespace arm64 } // namespace simdjson diff --git a/src/fallback.cpp b/src/fallback.cpp index 0f8865de0..e3b23ac0c 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -459,10 +459,6 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t * return stage2(_doc); } -simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept { - return fallback::stringparsing::write_string_escaped(input, out); -} - } // namespace fallback } // namespace simdjson diff --git a/src/generic/stage2/stringparsing.h b/src/generic/stage2/stringparsing.h index cd229b0cb..93850a8db 100644 --- a/src/generic/stage2/stringparsing.h +++ b/src/generic/stage2/stringparsing.h @@ -237,104 +237,6 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t } } -///////////// -/// TODO: This function is not used in the codebase. It is not clear if it is needed. -///////////// -simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) noexcept { - // We are making the following assumption: most strings will either be very short or they will not - // need escaping. - size_t i = 0; - size_t pos = 0; - /*if(input.size() >= escaping::BYTES_PROCESSED) { - auto vec_processing = [input,out]() -> size_t { - size_t index = 0; - size_t position = 0; - for(;input.size() - index >= escaping::BYTES_PROCESSED; index += escaping::BYTES_PROCESSED) { - escaping vinput = escaping::copy_and_find(reinterpret_cast(input.data()) + index, reinterpret_cast(out) + position); - if(vinput.has_escape()) { - return index + vinput.escape_index(); // We have a character that needs escaping - } - position += escaping::BYTES_PROCESSED; - } - if(index == input.size()) { return input.size(); } - // We virtually backtrack so we can load a full vector register - index = input.size() - escaping::BYTES_PROCESSED; - position = index; - escaping vinput = escaping::copy_and_find(reinterpret_cast(input.data()) + index, reinterpret_cast(out) + position); - if(vinput.has_escape()) { - return index + vinput.escape_index(); // We have a character that needs escaping - } - return input.size(); - }; - i = vec_processing(); - pos = i; - if(i == input.size()) { return pos; } - // Here we only continue if there was a character that needed escaping. - }*/ - static std::string_view control_chars[] = { - "\\x0000", "\\x0001", "\\x0002", "\\x0003", "\\x0004", "\\x0005", "\\x0006", - "\\x0007", "\\x0008", "\\t", "\\n", "\\x000b", "\\f", "\\r", - "\\x000e", "\\x000f", "\\x0010", "\\x0011", "\\x0012", "\\x0013", "\\x0014", - "\\x0015", "\\x0016", "\\x0017", "\\x0018", "\\x0019", "\\x001a", "\\x001b", - "\\x001c", "\\x001d", "\\x001e", "\\x001f"}; - static std::array json_quotable_character = - []() SIMDJSON_CONSTEXPR_LAMBDA { - std::array result{}; - for (int index = 0; index < 32; index++) { - result[index] = 1; - } - for (int index : {'"', '\\'}) { - result[index] = 1; - } - return result; - }(); - // The rest could possibly be vectorized, but consider that we expect most strings - // to be short or not to require escaping. - for (; i < input.size(); i++) { - uint8_t c = static_cast(input[i]); - if(json_quotable_character[c]) { - switch (c) { - case '"': - out[pos++] = '\\'; - out[pos++] = '"'; - break; - case '\\': - out[pos++] = '\\'; - out[pos++] = '\\'; - break; - case '\b': - out[pos++] = '\\'; - out[pos++] = 'b'; - break; - case '\f': - out[pos++] = '\\'; - out[pos++] = 'f'; - break; - case '\n': - out[pos++] = '\\'; - out[pos++] = 'n'; - break; - case '\r': - out[pos++] = '\\'; - out[pos++] = 'r'; - break; - case '\t': - out[pos++] = '\\'; - out[pos++] = 't'; - break; - default: - control_chars[c].copy(out + pos, 6); - pos += 6; - } - } else { - out[pos++] = c; - } - } - return pos; -} - - - } // namespace stringparsing } // unnamed namespace diff --git a/src/haswell.cpp b/src/haswell.cpp index 1eac5f91d..d369a11fa 100644 --- a/src/haswell.cpp +++ b/src/haswell.cpp @@ -162,10 +162,6 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t * return stage2(_doc); } -simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept { - return haswell::stringparsing::write_string_escaped(input, out); -} - } // namespace SIMDJSON_IMPLEMENTATION } // namespace simdjson diff --git a/src/icelake.cpp b/src/icelake.cpp index 2febab916..c057d4278 100644 --- a/src/icelake.cpp +++ b/src/icelake.cpp @@ -208,10 +208,6 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t * return stage2(_doc); } -simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept { - return icelake::stringparsing::write_string_escaped(input, out); -} - } // namespace icelake } // namespace simdjson diff --git a/src/implementation.cpp b/src/implementation.cpp index 32d1f43de..3dba4b605 100644 --- a/src/implementation.cpp +++ b/src/implementation.cpp @@ -186,9 +186,6 @@ public: simdjson_warn_unused bool validate_utf8(const char * buf, size_t len) const noexcept final override { return set_best()->validate_utf8(buf, len); } - simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final { - return set_best()->write_string_escaped(input, out); - } simdjson_inline detect_best_supported_implementation_on_first_use() noexcept : implementation("best_supported_detector", "Detects the best supported implementation and sets it", 0) {} private: const implementation *set_best() const noexcept; @@ -239,9 +236,6 @@ public: simdjson_warn_unused error_code minify(const uint8_t *, size_t, uint8_t *, size_t &) const noexcept final override { return UNSUPPORTED_ARCHITECTURE; } - simdjson_warn_unused size_t write_string_escaped(const std::string_view, char *) const noexcept final override { - return 0; // TODO: Evaluate whether this is the right thing to do for unsupported architecture. - } simdjson_warn_unused bool validate_utf8(const char *, size_t) const noexcept final override { return false; // Just refuse to validate. Given that we have a fallback implementation // it seems unlikely that unsupported_implementation will ever be used. If it is used, @@ -325,9 +319,6 @@ simdjson_warn_unused error_code minify(const char *buf, size_t len, char *dst, s simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) noexcept { return get_active_implementation()->validate_utf8(buf, len); } -simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) noexcept { - return get_active_implementation()->write_string_escaped(input, out); -} const implementation * builtin_implementation() { static const implementation * builtin_impl = get_available_implementations()[SIMDJSON_STRINGIFY(SIMDJSON_BUILTIN_IMPLEMENTATION)]; assert(builtin_impl); diff --git a/src/lasx.cpp b/src/lasx.cpp index b77d87694..09b945f59 100644 --- a/src/lasx.cpp +++ b/src/lasx.cpp @@ -125,10 +125,6 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t * return stage2(_doc); } -simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept { - return lasx::stringparsing::write_string_escaped(input, out); -} - } // namespace lasx } // namespace simdjson diff --git a/src/lsx.cpp b/src/lsx.cpp index e283faf79..e68aada87 100644 --- a/src/lsx.cpp +++ b/src/lsx.cpp @@ -129,10 +129,6 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t * return stage2(_doc); } -simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept { - return lsx::stringparsing::write_string_escaped(input, out); -} - } // namespace lsx } // namespace simdjson diff --git a/src/ppc64.cpp b/src/ppc64.cpp index a54f0f231..f7dee707e 100644 --- a/src/ppc64.cpp +++ b/src/ppc64.cpp @@ -135,10 +135,6 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t * return stage2(_doc); } -simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept { - return ppc64::stringparsing::write_string_escaped(input, out); -} - } // namespace ppc64 } // namespace simdjson diff --git a/src/westmere.cpp b/src/westmere.cpp index 2acfaecac..7157ac491 100644 --- a/src/westmere.cpp +++ b/src/westmere.cpp @@ -166,11 +166,6 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t * if (error) { return error; } return stage2(_doc); } - -simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept { - return westmere::stringparsing::write_string_escaped(input, out); -} - } // namespace westmere } // namespace simdjson diff --git a/tests/builder/CMakeLists.txt b/tests/builder/CMakeLists.txt index b8985fa0d..2bf59484d 100644 --- a/tests/builder/CMakeLists.txt +++ b/tests/builder/CMakeLists.txt @@ -3,6 +3,9 @@ include_directories(..) add_cpp_test(builder_string_builder_tests LABELS ondemand acceptance per_implementation) if(SIMDJSON_STATIC_REFLECTION) add_cpp_test(static_reflection_builder_tests LABELS ondemand acceptance per_implementation) + add_cpp_test(static_reflection_comprehensive_tests LABELS ondemand acceptance per_implementation) + add_cpp_test(static_reflection_edge_cases_tests LABELS ondemand acceptance per_implementation) + add_cpp_test(static_reflection_enum_tests LABELS ondemand acceptance per_implementation) endif(SIMDJSON_STATIC_REFLECTION) # Copy the simdjson dll into the tests directory if(MSVC AND BUILD_SHARED_LIBS) diff --git a/tests/builder/builder_string_builder_tests.cpp b/tests/builder/builder_string_builder_tests.cpp index 72524fac0..bafc05cf5 100644 --- a/tests/builder/builder_string_builder_tests.cpp +++ b/tests/builder/builder_string_builder_tests.cpp @@ -270,6 +270,7 @@ namespace builder_tests { } + void serialize_car(const Car& car, simdjson::builder::string_builder& builder) { // start of JSON builder.start_object(); @@ -318,6 +319,17 @@ namespace builder_tests { TEST_SUCCEED(); } + #if SIMDJSON_EXCEPTIONS + bool car_test_exception() { + TEST_START(); + simdjson::builder::string_builder sb; + Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}}; + serialize_car_long(c, sb); + std::string_view p{sb}; + ASSERT_EQUAL(p, "{\"make\":\"Toyota\",\"model\":\"Corolla\",\"year\":2017,\"tire_pressure\":[30.0,30.2,30.513,30.79]}"); + TEST_SUCCEED(); + } + #endif bool run() { return various_integers() && @@ -325,6 +337,7 @@ namespace builder_tests { car_test_long() && car_test() && #if SIMDJSON_EXCEPTIONS + car_test_exception() && string_convertion_except() && #endif append_char() && diff --git a/tests/builder/static_reflection_builder_tests.cpp b/tests/builder/static_reflection_builder_tests.cpp index d48086fd2..5034090a4 100644 --- a/tests/builder/static_reflection_builder_tests.cpp +++ b/tests/builder/static_reflection_builder_tests.cpp @@ -48,14 +48,11 @@ namespace builder_tests { bool car_test() { TEST_START(); - simdjson::builder::string_builder sb; +#if SIMDJSON_STATIC_REFLECTION Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}}; - append(sb, c); - std::string_view p; - auto result = sb.view().get(p); + auto result = builder::to_json_string(c); ASSERT_SUCCESS(result); - ASSERT_EQUAL(p, "{\"make\":\"Toyota\",\"model\":\"Corolla\",\"year\":2017,\"tire_pressure\":[30.0,30.2,30.513,30.79]}"); - std::string pstr(p.begin(), p.end()); + std::string pstr = result.value(); ASSERT_EQUAL(pstr, "{\"make\":\"Toyota\",\"model\":\"Corolla\",\"year\":2017,\"tire_pressure\":[30.0,30.2,30.513,30.79]}"); simdjson::ondemand::parser parser; simdjson::ondemand::document doc; @@ -70,12 +67,38 @@ namespace builder_tests { ASSERT_EQUAL(c2.tire_pressure[1], 30.2); ASSERT_EQUAL(c2.tire_pressure[2], 30.513); ASSERT_EQUAL(c2.tire_pressure[3], 30.79); +#endif TEST_SUCCEED(); } + #if SIMDJSON_EXCEPTIONS + bool car_test_exception() { + TEST_START(); + simdjson::builder::string_builder sb; + Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}}; + append(sb, c); + std::string_view p{sb}; + TEST_SUCCEED(); + } + bool car_test_exception2() { + TEST_START(); + simdjson::builder::string_builder sb; + Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}}; + sb << c; + std::string_view p{sb}; + TEST_SUCCEED(); + } + void 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); + TEST_SUCCEED(); + } + #endif // SIMDJSON_EXCEPTIONS bool serialize_deserialize_kid() { TEST_START(); +#if SIMDJSON_STATIC_REFLECTION simdjson::padded_string json_str = R"({"age": 12, "name": "John", "toys": ["car", "ball"]})"_padded; simdjson::ondemand::parser parser; @@ -90,7 +113,7 @@ bool serialize_deserialize_kid() { ASSERT_EQUAL(k.toys[1], "ball"); // Now, go the other direction: std::string json; - ASSERT_SUCCESS(simdjson::builder::to_json_string(k).get(json)); + ASSERT_SUCCESS(builder::to_json_string(k).get(json)); std::cout << json << std::endl; // Now we parse it back: simdjson::ondemand::parser parser2; @@ -103,11 +126,13 @@ bool serialize_deserialize_kid() { ASSERT_EQUAL(k2.toys.size(), 2); ASSERT_EQUAL(k2.toys[0], "car"); ASSERT_EQUAL(k2.toys[1], "ball"); +#endif TEST_SUCCEED(); } bool serialize_deserialize_x_y_z() { TEST_START(); +#if SIMDJSON_STATIC_REFLECTION X s1 = {.a = '1', .b = 10, .c = 0, @@ -119,7 +144,7 @@ bool serialize_deserialize_x_y_z() { .i = {1, 2, 3}, .z = {.x = 1000}}}; std::string pstr; - ASSERT_SUCCESS(simdjson::builder::to_json_string(s1).get(pstr)); + ASSERT_SUCCESS(builder::to_json_string(s1).get(pstr)); ASSERT_EQUAL( pstr, R"({"a":"1","b":10,"c":0,"d":"test string\n\r\"","e":[1,2,3],"f":["ab","cd","fg"],"y":{"g":100,"h":"test string\n\r\"","i":[1,2,3],"z":{"x":1000}}})"); @@ -129,12 +154,23 @@ bool serialize_deserialize_x_y_z() { X s2; ASSERT_SUCCESS(doc.get().get(s2)); ASSERT_TRUE(s1 == s2); +#endif TEST_SUCCEED(); } -bool run() { - return car_test() && serialize_deserialize_kid() && serialize_deserialize_x_y_z() && true; -} + bool run() { + return + + #if SIMDJSON_EXCEPTIONS + car_test_exception() && + car_test_exception2() && + car_test_to_json_exception() && + #endif // SIMDJSON_EXCEPTIONS + car_test() && + serialize_deserialize_kid() && + serialize_deserialize_x_y_z() && + true; + } } // namespace builder_tests diff --git a/tests/builder/static_reflection_comprehensive_tests.cpp b/tests/builder/static_reflection_comprehensive_tests.cpp new file mode 100644 index 000000000..426dfc5e1 --- /dev/null +++ b/tests/builder/static_reflection_comprehensive_tests.cpp @@ -0,0 +1,231 @@ +#include "simdjson.h" +#include "test_builder.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace simdjson; + +namespace builder_tests { + + bool test_primitive_types() { + TEST_START(); +#if SIMDJSON_STATIC_REFLECTION + struct PrimitiveTypes { + bool bool_val; + char char_val; + int int_val; + double double_val; + float float_val; + }; + + PrimitiveTypes test{true, 'X', 42, 3.14159, 2.71f}; + + auto result = builder::to_json_string(test); + ASSERT_SUCCESS(result); + + std::string json = result.value(); + ASSERT_TRUE(json.find("\"bool_val\":true") != std::string::npos); + ASSERT_TRUE(json.find("\"char_val\":\"X\"") != std::string::npos); + ASSERT_TRUE(json.find("\"int_val\":42") != std::string::npos); + ASSERT_TRUE(json.find("\"double_val\":3.14159") != std::string::npos); + + // Test round-trip + ondemand::parser parser; + auto doc_result = parser.iterate(pad(json)); + ASSERT_SUCCESS(doc_result); + + auto get_result = doc_result.value().get(); + ASSERT_SUCCESS(get_result); + + PrimitiveTypes deserialized = std::move(get_result.value()); + ASSERT_EQUAL(deserialized.bool_val, test.bool_val); + ASSERT_EQUAL(deserialized.char_val, test.char_val); + ASSERT_EQUAL(deserialized.int_val, test.int_val); + ASSERT_EQUAL(deserialized.double_val, test.double_val); +#endif + TEST_SUCCEED(); + } + + bool test_string_types() { + TEST_START(); +#if SIMDJSON_STATIC_REFLECTION + struct StringTypes { + std::string string_val; + std::string_view string_view_val; + }; + + StringTypes test{"hello world", "test_view"}; + + auto result = builder::to_json_string(test); + ASSERT_SUCCESS(result); + + std::string json = result.value(); + ASSERT_TRUE(json.find("\"string_val\":\"hello world\"") != std::string::npos); + ASSERT_TRUE(json.find("\"string_view_val\":\"test_view\"") != std::string::npos); + + // Test round-trip + ondemand::parser parser; + auto doc_result = parser.iterate(pad(json)); + ASSERT_SUCCESS(doc_result); + + auto get_result = doc_result.value().get(); + ASSERT_SUCCESS(get_result); + + StringTypes deserialized = std::move(get_result.value()); + ASSERT_EQUAL(deserialized.string_val, test.string_val); +#endif + TEST_SUCCEED(); + } + + bool test_optional_types() { + TEST_START(); +#if SIMDJSON_STATIC_REFLECTION + struct OptionalTypes { + std::optional opt_int_with_value; + std::optional opt_string_with_value; + std::optional opt_int_null; + std::optional opt_string_null; + }; + + OptionalTypes test; + test.opt_int_with_value = 42; + test.opt_string_with_value = "optional_test"; + test.opt_int_null = std::nullopt; + test.opt_string_null = std::nullopt; + + auto result = builder::to_json_string(test); + ASSERT_SUCCESS(result); + + std::string json = result.value(); + ASSERT_TRUE(json.find("\"opt_int_with_value\":42") != std::string::npos); + ASSERT_TRUE(json.find("\"opt_string_with_value\":\"optional_test\"") != std::string::npos); + ASSERT_TRUE(json.find("\"opt_int_null\":null") != std::string::npos); + ASSERT_TRUE(json.find("\"opt_string_null\":null") != std::string::npos); + + // Test round-trip + ondemand::parser parser; + auto doc_result = parser.iterate(pad(json)); + ASSERT_SUCCESS(doc_result); + + auto get_result = doc_result.value().get(); + ASSERT_SUCCESS(get_result); + + OptionalTypes deserialized = std::move(get_result.value()); + ASSERT_TRUE(deserialized.opt_int_with_value.has_value()); + ASSERT_EQUAL(*deserialized.opt_int_with_value, 42); + ASSERT_TRUE(deserialized.opt_string_with_value.has_value()); + ASSERT_EQUAL(*deserialized.opt_string_with_value, "optional_test"); + ASSERT_FALSE(deserialized.opt_int_null.has_value()); + ASSERT_FALSE(deserialized.opt_string_null.has_value()); +#endif + TEST_SUCCEED(); + } + + bool test_smart_pointer_types() { + TEST_START(); +#if SIMDJSON_STATIC_REFLECTION + struct SmartPointerTypes { + std::unique_ptr unique_int_with_value; + std::shared_ptr shared_string_with_value; + std::unique_ptr unique_bool_with_value; + std::unique_ptr unique_int_null; + std::shared_ptr shared_string_null; + }; + + SmartPointerTypes test; + test.unique_int_with_value = std::make_unique(123); + test.shared_string_with_value = std::make_shared("shared_test"); + test.unique_bool_with_value = std::make_unique(true); + test.unique_int_null = nullptr; + test.shared_string_null = nullptr; + + auto result = builder::to_json_string(test); + ASSERT_SUCCESS(result); + + std::string json = result.value(); + ASSERT_TRUE(json.find("\"unique_int_with_value\":123") != std::string::npos); + ASSERT_TRUE(json.find("\"shared_string_with_value\":\"shared_test\"") != std::string::npos); + ASSERT_TRUE(json.find("\"unique_bool_with_value\":true") != std::string::npos); + ASSERT_TRUE(json.find("\"unique_int_null\":null") != std::string::npos); + ASSERT_TRUE(json.find("\"shared_string_null\":null") != std::string::npos); + + // Test round-trip + ondemand::parser parser; + auto doc_result = parser.iterate(pad(json)); + ASSERT_SUCCESS(doc_result); + + auto get_result = doc_result.value().get(); + ASSERT_SUCCESS(get_result); + + SmartPointerTypes deserialized = std::move(get_result.value()); + ASSERT_TRUE(deserialized.unique_int_with_value != nullptr); + ASSERT_EQUAL(*deserialized.unique_int_with_value, 123); + ASSERT_TRUE(deserialized.shared_string_with_value != nullptr); + ASSERT_EQUAL(*deserialized.shared_string_with_value, "shared_test"); + ASSERT_TRUE(deserialized.unique_bool_with_value != nullptr); + ASSERT_EQUAL(*deserialized.unique_bool_with_value, true); + ASSERT_TRUE(deserialized.unique_int_null == nullptr); + ASSERT_TRUE(deserialized.shared_string_null == nullptr); +#endif + TEST_SUCCEED(); + } + + bool test_container_types() { + TEST_START(); +#if SIMDJSON_STATIC_REFLECTION + struct ContainerTypes { + std::vector int_vector; + std::set string_set; + std::map string_map; + }; + + ContainerTypes test; + test.int_vector = {1, 2, 3, 4, 5}; + test.string_set = {"apple", "banana", "cherry"}; + test.string_map = {{"key1", 10}, {"key2", 20}, {"key3", 30}}; + + auto result = builder::to_json_string(test); + ASSERT_SUCCESS(result); + + std::string json = result.value(); + ASSERT_TRUE(json.find("\"int_vector\":[1,2,3,4,5]") != std::string::npos); + ASSERT_TRUE(json.find("\"string_set\":[") != std::string::npos); + ASSERT_TRUE(json.find("\"string_map\":{") != std::string::npos); + + // Test round-trip + ondemand::parser parser; + auto doc_result = parser.iterate(pad(json)); + ASSERT_SUCCESS(doc_result); + + auto get_result = doc_result.value().get(); + ASSERT_SUCCESS(get_result); + + ContainerTypes deserialized = std::move(get_result.value()); + ASSERT_EQUAL(deserialized.int_vector.size(), 5); + ASSERT_EQUAL(deserialized.string_set.size(), 3); + ASSERT_EQUAL(deserialized.string_map.size(), 3); + ASSERT_EQUAL(deserialized.int_vector[0], 1); + ASSERT_EQUAL(deserialized.int_vector[4], 5); +#endif + TEST_SUCCEED(); + } + + + bool run() { + return test_primitive_types() && + test_string_types() && + test_optional_types() && + test_smart_pointer_types() && + test_container_types(); + } + +} // namespace builder_tests + +int main(int argc, char *argv[]) { + return test_main(argc, argv, builder_tests::run); +} \ No newline at end of file diff --git a/tests/builder/static_reflection_edge_cases_tests.cpp b/tests/builder/static_reflection_edge_cases_tests.cpp new file mode 100644 index 000000000..3037414a4 --- /dev/null +++ b/tests/builder/static_reflection_edge_cases_tests.cpp @@ -0,0 +1,230 @@ +#include "simdjson.h" +#include "test_builder.h" +#include +#include +#include +#include +#include + +using namespace simdjson; + +namespace builder_tests { + + bool test_empty_values() { + TEST_START(); +#if SIMDJSON_STATIC_REFLECTION + struct EmptyValues { + std::string empty_string; + std::vector empty_vector; + std::optional null_optional; + std::unique_ptr null_unique_ptr; + std::shared_ptr null_shared_ptr; + }; + + EmptyValues test; + test.empty_string = ""; + // empty_vector is already empty by default + test.null_optional = std::nullopt; + test.null_unique_ptr = nullptr; + test.null_shared_ptr = nullptr; + + auto result = builder::to_json_string(test); + ASSERT_SUCCESS(result); + + std::string json = result.value(); + ASSERT_TRUE(json.find("\"empty_string\":\"\"") != std::string::npos); + ASSERT_TRUE(json.find("\"empty_vector\":[]") != std::string::npos); + ASSERT_TRUE(json.find("\"null_optional\":null") != std::string::npos); + ASSERT_TRUE(json.find("\"null_unique_ptr\":null") != std::string::npos); + ASSERT_TRUE(json.find("\"null_shared_ptr\":null") != std::string::npos); + + // Test round-trip + ondemand::parser parser; + auto doc_result = parser.iterate(pad(json)); + ASSERT_SUCCESS(doc_result); + + auto get_result = doc_result.value().get(); + ASSERT_SUCCESS(get_result); + + EmptyValues deserialized = std::move(get_result.value()); + ASSERT_EQUAL(deserialized.empty_string, ""); + ASSERT_EQUAL(deserialized.empty_vector.size(), 0); + ASSERT_FALSE(deserialized.null_optional.has_value()); + ASSERT_TRUE(deserialized.null_unique_ptr == nullptr); + ASSERT_TRUE(deserialized.null_shared_ptr == nullptr); +#endif + TEST_SUCCEED(); + } + + bool test_special_characters() { + TEST_START(); +#if SIMDJSON_STATIC_REFLECTION + struct SpecialChars { + std::string quotes; + std::string backslashes; + std::string newlines; + std::string unicode; + char null_char; + }; + + SpecialChars test; + test.quotes = "He said \"Hello\""; + test.backslashes = "Path\\to\\file"; + test.newlines = "Line1\nLine2\tTabbed"; + test.unicode = "Café résumé"; + test.null_char = '\0'; + + auto result = builder::to_json_string(test); + ASSERT_SUCCESS(result); + + std::string json = result.value(); + // Test that quotes are properly escaped + ASSERT_TRUE(json.find("\\\"Hello\\\"") != std::string::npos); + // Test that backslashes are properly escaped + ASSERT_TRUE(json.find("\\\\to\\\\") != std::string::npos); + // Test that newlines are properly escaped + ASSERT_TRUE(json.find("\\n") != std::string::npos); + ASSERT_TRUE(json.find("\\t") != std::string::npos); + + // Test round-trip (excluding null char which has special handling) + struct SpecialCharsNoNull { + std::string quotes; + std::string backslashes; + std::string newlines; + std::string unicode; + }; + + SpecialCharsNoNull test_no_null; + test_no_null.quotes = test.quotes; + test_no_null.backslashes = test.backslashes; + test_no_null.newlines = test.newlines; + test_no_null.unicode = test.unicode; + + auto result_no_null = builder::to_json_string(test_no_null); + ASSERT_SUCCESS(result_no_null); + + ondemand::parser parser; + auto doc_result = parser.iterate(pad(result_no_null.value())); + ASSERT_SUCCESS(doc_result); + + auto get_result = doc_result.value().get(); + ASSERT_SUCCESS(get_result); + + SpecialCharsNoNull deserialized = std::move(get_result.value()); + ASSERT_EQUAL(deserialized.quotes, test.quotes); + ASSERT_EQUAL(deserialized.backslashes, test.backslashes); + ASSERT_EQUAL(deserialized.newlines, test.newlines); + ASSERT_EQUAL(deserialized.unicode, test.unicode); +#endif + TEST_SUCCEED(); + } + + bool test_numeric_limits() { + TEST_START(); +#if SIMDJSON_STATIC_REFLECTION + struct NumericLimits { + int max_int; + int min_int; + double max_double; + double min_double; + bool true_val; + bool false_val; + }; + + NumericLimits test; + test.max_int = std::numeric_limits::max(); + test.min_int = std::numeric_limits::min(); + test.max_double = 1e100; // Large but safe double value + test.min_double = -1e100; // Large negative but safe double value + test.true_val = true; + test.false_val = false; + + auto result = builder::to_json_string(test); + ASSERT_SUCCESS(result); + + std::string json = result.value(); + ASSERT_TRUE(json.find("\"true_val\":true") != std::string::npos); + ASSERT_TRUE(json.find("\"false_val\":false") != std::string::npos); + + // Test round-trip + ondemand::parser parser; + auto doc_result = parser.iterate(pad(json)); + ASSERT_SUCCESS(doc_result); + + auto get_result = doc_result.value().get(); + ASSERT_SUCCESS(get_result); + + NumericLimits deserialized = std::move(get_result.value()); + ASSERT_EQUAL(deserialized.max_int, test.max_int); + ASSERT_EQUAL(deserialized.min_int, test.min_int); + ASSERT_EQUAL(deserialized.true_val, true); + ASSERT_EQUAL(deserialized.false_val, false); +#endif + TEST_SUCCEED(); + } + + bool test_nested_structures() { + TEST_START(); +#if SIMDJSON_STATIC_REFLECTION + struct Inner { + int value; + std::string name; + }; + + struct Outer { + Inner inner_obj; + std::vector inner_vector; + std::optional optional_inner; + std::unique_ptr unique_inner; + }; + + Outer test; + test.inner_obj = {42, "inner"}; + test.inner_vector = {{1, "first"}, {2, "second"}}; + test.optional_inner = Inner{99, "optional"}; + test.unique_inner = std::make_unique(Inner{123, "unique"}); + + auto result = builder::to_json_string(test); + ASSERT_SUCCESS(result); + + std::string json = result.value(); + ASSERT_TRUE(json.find("\"inner_obj\":{") != std::string::npos); + ASSERT_TRUE(json.find("\"inner_vector\":[") != std::string::npos); + ASSERT_TRUE(json.find("\"optional_inner\":{") != std::string::npos); + ASSERT_TRUE(json.find("\"unique_inner\":{") != std::string::npos); + + // Test round-trip + ondemand::parser parser; + auto doc_result = parser.iterate(pad(json)); + ASSERT_SUCCESS(doc_result); + + auto get_result = doc_result.value().get(); + ASSERT_SUCCESS(get_result); + + Outer deserialized = std::move(get_result.value()); + ASSERT_EQUAL(deserialized.inner_obj.value, 42); + ASSERT_EQUAL(deserialized.inner_obj.name, "inner"); + ASSERT_EQUAL(deserialized.inner_vector.size(), 2); + ASSERT_EQUAL(deserialized.inner_vector[0].value, 1); + ASSERT_EQUAL(deserialized.inner_vector[1].name, "second"); + ASSERT_TRUE(deserialized.optional_inner.has_value()); + ASSERT_EQUAL(deserialized.optional_inner->value, 99); + ASSERT_TRUE(deserialized.unique_inner != nullptr); + ASSERT_EQUAL(deserialized.unique_inner->value, 123); +#endif + TEST_SUCCEED(); + } + + + bool run() { + return test_empty_values() && + test_special_characters() && + test_numeric_limits() && + test_nested_structures(); + } + +} // namespace builder_tests + +int main(int argc, char *argv[]) { + return test_main(argc, argv, builder_tests::run); +} \ No newline at end of file diff --git a/tests/builder/static_reflection_enum_tests.cpp b/tests/builder/static_reflection_enum_tests.cpp new file mode 100644 index 000000000..22ceb37c7 --- /dev/null +++ b/tests/builder/static_reflection_enum_tests.cpp @@ -0,0 +1,258 @@ +#include "simdjson.h" +#include "test_builder.h" +#include + +using namespace simdjson; + +namespace builder_tests { + + bool test_enum_serialization() { + TEST_START(); +#if SIMDJSON_STATIC_REFLECTION + enum class Color { + Red, + Green, + Blue + }; + + struct EnumStruct { + Color color; + int value; + }; + + EnumStruct test{Color::Red, 42}; + + auto result = builder::to_json_string(test); + ASSERT_SUCCESS(result); + + std::string json = result.value(); + // Enum should be serialized as string (Red) + ASSERT_TRUE(json.find("\"color\":\"Red\"") != std::string::npos); + ASSERT_TRUE(json.find("\"value\":42") != std::string::npos); + + // Test different enum values + test.color = Color::Green; + auto result2 = builder::to_json_string(test); + ASSERT_SUCCESS(result2); + std::string json2 = result2.value(); + ASSERT_TRUE(json2.find("\"color\":\"Green\"") != std::string::npos); + + test.color = Color::Blue; + auto result3 = builder::to_json_string(test); + ASSERT_SUCCESS(result3); + std::string json3 = result3.value(); + ASSERT_TRUE(json3.find("\"color\":\"Blue\"") != std::string::npos); +#endif + TEST_SUCCEED(); + } + + bool test_enum_deserialization() { + TEST_START(); +#if SIMDJSON_STATIC_REFLECTION + enum class Status { + Active, + Inactive, + Pending + }; + + struct StatusStruct { + Status status; + std::string name; + }; + + // Test deserialization of different enum values with string representation + std::string json1 = "{\"status\":\"Active\",\"name\":\"test1\"}"; + ondemand::parser parser1; + auto doc_result1 = parser1.iterate(pad(json1)); + ASSERT_SUCCESS(doc_result1); + + auto get_result1 = doc_result1.value().get(); + ASSERT_SUCCESS(get_result1); + + StatusStruct deserialized1 = std::move(get_result1.value()); + ASSERT_TRUE(deserialized1.status == Status::Active); + ASSERT_EQUAL(deserialized1.name, "test1"); + + // Test Status::Inactive + std::string json2 = "{\"status\":\"Inactive\",\"name\":\"test2\"}"; + ondemand::parser parser2; + auto doc_result2 = parser2.iterate(pad(json2)); + ASSERT_SUCCESS(doc_result2); + + auto get_result2 = doc_result2.value().get(); + ASSERT_SUCCESS(get_result2); + + StatusStruct deserialized2 = std::move(get_result2.value()); + ASSERT_TRUE(deserialized2.status == Status::Inactive); + ASSERT_EQUAL(deserialized2.name, "test2"); + + // Test Status::Pending + std::string json3 = "{\"status\":\"Pending\",\"name\":\"test3\"}"; + ondemand::parser parser3; + auto doc_result3 = parser3.iterate(pad(json3)); + ASSERT_SUCCESS(doc_result3); + + auto get_result3 = doc_result3.value().get(); + ASSERT_SUCCESS(get_result3); + + StatusStruct deserialized3 = std::move(get_result3.value()); + ASSERT_TRUE(deserialized3.status == Status::Pending); + ASSERT_EQUAL(deserialized3.name, "test3"); +#endif + TEST_SUCCEED(); + } + + bool test_enum_round_trip() { + TEST_START(); +#if SIMDJSON_STATIC_REFLECTION + enum class Priority { + Low, + Medium, + High, + Critical + }; + + struct Task { + Priority priority; + std::string description; + int id; + }; + + Task original{Priority::High, "Important task", 123}; + + // Serialize + auto serialize_result = builder::to_json_string(original); + ASSERT_SUCCESS(serialize_result); + + std::string json = serialize_result.value(); + ASSERT_TRUE(json.find("\"priority\":\"High\"") != std::string::npos); // High as string + ASSERT_TRUE(json.find("\"description\":\"Important task\"") != std::string::npos); + ASSERT_TRUE(json.find("\"id\":123") != std::string::npos); + + // Deserialize + ondemand::parser parser; + auto doc_result = parser.iterate(pad(json)); + ASSERT_SUCCESS(doc_result); + + auto get_result = doc_result.value().get(); + ASSERT_SUCCESS(get_result); + + Task deserialized = std::move(get_result.value()); + ASSERT_TRUE(deserialized.priority == Priority::High); + ASSERT_EQUAL(deserialized.description, "Important task"); + ASSERT_EQUAL(deserialized.id, 123); +#endif + TEST_SUCCEED(); + } + + bool test_enum_with_underlying_type() { + TEST_START(); +#if SIMDJSON_STATIC_REFLECTION + enum class ErrorCode : int { + Success = 0, + NotFound = 404, + ServerError = 500 + }; + + struct Response { + ErrorCode error; + std::string message; + }; + + Response test{ErrorCode::NotFound, "Resource not found"}; + + auto result = builder::to_json_string(test); + ASSERT_SUCCESS(result); + + std::string json = result.value(); + ASSERT_TRUE(json.find("\"error\":\"NotFound\"") != std::string::npos); + ASSERT_TRUE(json.find("\"message\":\"Resource not found\"") != std::string::npos); + + // Test round-trip + ondemand::parser parser; + auto doc_result = parser.iterate(pad(json)); + ASSERT_SUCCESS(doc_result); + + auto get_result = doc_result.value().get(); + ASSERT_SUCCESS(get_result); + + Response deserialized = std::move(get_result.value()); + ASSERT_TRUE(deserialized.error == ErrorCode::NotFound); + ASSERT_EQUAL(deserialized.message, "Resource not found"); +#endif + TEST_SUCCEED(); + } + + bool test_multiple_enums() { + TEST_START(); +#if SIMDJSON_STATIC_REFLECTION + enum class Day { + Monday, + Tuesday, + Wednesday, + Thursday, + Friday, + Saturday, + Sunday + }; + + enum class Month { + January, + February, + March, + April, + May, + June, + July, + August, + September, + October, + November, + December + }; + + struct Date { + Day day; + Month month; + int year; + }; + + Date test{Day::Friday, Month::July, 2024}; + + auto result = builder::to_json_string(test); + ASSERT_SUCCESS(result); + + std::string json = result.value(); + ASSERT_TRUE(json.find("\"day\":\"Friday\"") != std::string::npos); // Friday as string + ASSERT_TRUE(json.find("\"month\":\"July\"") != std::string::npos); // July as string + ASSERT_TRUE(json.find("\"year\":2024") != std::string::npos); + + // Test round-trip + ondemand::parser parser; + auto doc_result = parser.iterate(pad(json)); + ASSERT_SUCCESS(doc_result); + + auto get_result = doc_result.value().get(); + ASSERT_SUCCESS(get_result); + + Date deserialized = std::move(get_result.value()); + ASSERT_TRUE(deserialized.day == Day::Friday); + ASSERT_TRUE(deserialized.month == Month::July); + ASSERT_EQUAL(deserialized.year, 2024); +#endif + TEST_SUCCEED(); + } + + bool run() { + return test_enum_serialization() && + test_enum_deserialization() && + test_enum_round_trip() && + test_enum_with_underlying_type() && + test_multiple_enums(); + } + +} // namespace builder_tests + +int main(int argc, char *argv[]) { + return test_main(argc, argv, builder_tests::run); +} \ No newline at end of file