mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 55f698c97b | |||
| 7e2a1d7651 | |||
| 3e00a431a0 | |||
| d3626c6a37 | |||
| 686a1869c8 | |||
| 4c0f86db44 | |||
| c4058d9d22 | |||
| 6c4c934457 | |||
| fcae795042 | |||
| b7c51156b9 | |||
| 662e3d96c7 | |||
| 64009f7063 | |||
| 74fb3ecac7 | |||
| 26abf1d180 | |||
| e878dc6be3 | |||
| efa03f5733 | |||
| 8519623257 | |||
| 484a092c31 | |||
| 54af3feca2 | |||
| 8bb520adbf | |||
| ec8e6a6758 | |||
| 769528b6c0 | |||
| 9fbc577be7 | |||
| a7c95e9cc8 | |||
| 248d4eb2aa | |||
| 559493e2bf | |||
| 75acae5c46 | |||
| bd761ef573 | |||
| 76ed73f07f | |||
| 1b59b38de8 | |||
| 4794b5d936 | |||
| 114f14924c | |||
| e94825c9a8 | |||
| 333fd72f98 | |||
| dd4d02617f | |||
| e28d63334e | |||
| 3d1ab87ecb | |||
| d365cfb4a1 | |||
| b9e308a727 | |||
| aa6817d5aa | |||
| 90e4a66c93 | |||
| e6240f18c4 | |||
| d43fb6ff84 | |||
| 5eec29a6db | |||
| 228501f786 | |||
| 9d9f2427c5 | |||
| 8d53840253 | |||
| 0a82fb110f | |||
| 11f273c580 | |||
| 31662531ac | |||
| 97eb557388 | |||
| 41ced28821 | |||
| 1078eb4034 | |||
| 0cd774097f | |||
| b5e27af4da |
+23
-2
@@ -38,7 +38,7 @@ cmake-build-release/
|
||||
.history/
|
||||
|
||||
# Visual Studio artifacts
|
||||
/VS/
|
||||
/.vs/
|
||||
|
||||
# C/C++ build outputs
|
||||
.build/
|
||||
@@ -49,6 +49,27 @@ objs
|
||||
|
||||
# C++ ignore from https://github.com/github/gitignore/blob/master/C%2B%2B.gitignore
|
||||
|
||||
# CMake build artifacts
|
||||
CMakeCache.txt
|
||||
CMakeFiles/
|
||||
CPackConfig.cmake
|
||||
CPackSourceConfig.cmake
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
simdjson-config*.cmake
|
||||
simdjson-props.cmake
|
||||
simdjson.pc
|
||||
|
||||
# Build directories
|
||||
deps/
|
||||
examples/build_*/
|
||||
examples/*_demo
|
||||
examples/*_benchmark
|
||||
|
||||
# Temporary files
|
||||
examples/CMakeLists_demo.txt
|
||||
examples/simple_http.h
|
||||
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
@@ -106,4 +127,4 @@ objs
|
||||
!.vscode/extensions.json
|
||||
|
||||
# clangd
|
||||
.cache
|
||||
.cache
|
||||
|
||||
+3
-5
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
+99
-42
@@ -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.
|
||||
|
||||
@@ -1343,6 +1346,34 @@ auto tag_invoke(deserialize_tag, simdjson_value &val, std::list<Car>& car) {
|
||||
With this code, deserializing an `std::list<Car>` instance would capture only the cars
|
||||
that are not made by Toyota.
|
||||
|
||||
|
||||
|
||||
For even more convenience, you can do it directly without a parser instance like so:
|
||||
|
||||
```cpp
|
||||
Car car = simdjson::from(json);
|
||||
```
|
||||
|
||||
You can also use C++20 ranges to iterate over an array:
|
||||
|
||||
```cpp
|
||||
simdjson::padded_string json_cars =
|
||||
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||
"tire_pressure": [ 40.1, 39.9 ] },
|
||||
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||
"tire_pressure": [ 30.1, 31.0 ] },
|
||||
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||
"tire_pressure": [ 29.8, 30.0 ] }
|
||||
])"_padded;
|
||||
|
||||
for (Car car : simdjson::from(json_cars) | simdjson::as<Car>()) {
|
||||
if (car.year < 1998) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 3. Using static reflection (C++26)
|
||||
|
||||
If you have a C++26 compatible compiler, you can compile
|
||||
@@ -1355,14 +1386,40 @@ 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<Car>();
|
||||
```
|
||||
|
||||
Just like when using `tag_invoke` for custom types (but without the `tag_invoke` code), you can parse a class instance directly without a parser instance:
|
||||
|
||||
```cpp
|
||||
Car car = simdjson::from(json);
|
||||
```
|
||||
|
||||
Similarly, you can also use C++20 ranges to iterate over an array:
|
||||
|
||||
```cpp
|
||||
simdjson::padded_string json_cars =
|
||||
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||
"tire_pressure": [ 40.1, 39.9 ] },
|
||||
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||
"tire_pressure": [ 30.1, 31.0 ] },
|
||||
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||
"tire_pressure": [ 29.8, 30.0 ] }
|
||||
])"_padded;
|
||||
|
||||
for (Car car : simdjson::from(json_cars) | simdjson::as<Car>()) {
|
||||
if (car.year < 1998) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also automatically serialize the `Car` instance to a JSON string, see
|
||||
our [Builder documentation](builder.md).
|
||||
|
||||
|
||||
+115
-45
@@ -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<double> 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,25 +142,59 @@ 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<double> 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::to_json` template 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<double> tire_pressure;
|
||||
};
|
||||
|
||||
void f() {
|
||||
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
|
||||
std::string json = simdjson::to_json(c);
|
||||
}
|
||||
```
|
||||
|
||||
If you know the output size, in bytes, of your JSON string, you may
|
||||
pass it as a second parameter (e.g., `simdjson::to_json(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;
|
||||
if(simdjson::builder::to_json_string(c).get(json)) {
|
||||
if(simdjson::to(c).get(json)) {
|
||||
// there was an error
|
||||
} else {
|
||||
// json contain the serialized JSON
|
||||
|
||||
+8
-1
@@ -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
|
||||
----------------------------------------------
|
||||
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
# 🛠️ Build Instructions for simdjson One-Liner Demo
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### For Both Examples
|
||||
- **cpr library** (for HTTP requests) - See [INSTALL_CPR.md](INSTALL_CPR.md) for installation
|
||||
- **curl library** (cpr dependency) - Usually pre-installed on most systems
|
||||
- **simdjson library** - Included in this repository
|
||||
|
||||
### For Legacy Example (github_legacy.cpp)
|
||||
- Any C++20 compatible compiler (GCC 10+, Clang 10+, MSVC 2019+)
|
||||
|
||||
### For Modern Example (github_modern.cpp)
|
||||
- Bloomberg Clang fork with C++26 reflection support
|
||||
- Get it from: https://github.com/bloomberg/clang-p2996
|
||||
|
||||
## 🔨 Compilation Commands
|
||||
|
||||
### Quick Setup (Recommended)
|
||||
|
||||
1. **Build cpr from source** (if not installed system-wide):
|
||||
```bash
|
||||
# From the examples directory
|
||||
mkdir -p ../deps && cd ../deps
|
||||
git clone https://github.com/libcpr/cpr.git
|
||||
cd cpr && git checkout 1.10.5
|
||||
mkdir build && cd build
|
||||
cmake .. -DCMAKE_CXX_COMPILER=clang++ -DCPR_USE_SYSTEM_CURL=ON -DBUILD_SHARED_LIBS=OFF -DCPR_ENABLE_SSL=OFF
|
||||
make -j4
|
||||
cd ../../../examples
|
||||
```
|
||||
|
||||
2. **Compile the examples**:
|
||||
```bash
|
||||
# Legacy approach (any C++20 compiler)
|
||||
clang++ -std=c++20 \
|
||||
-I../deps/cpr/include \
|
||||
-I../deps/cpr/build/cpr_generated_includes \
|
||||
-I../include -I.. \
|
||||
github_legacy.cpp \
|
||||
../singleheader/simdjson.cpp \
|
||||
../deps/cpr/build/lib/libcpr.a \
|
||||
-lcurl -pthread \
|
||||
-o github_legacy_demo
|
||||
|
||||
# Modern approach (Bloomberg clang with reflection)
|
||||
clang++ -std=c++26 -freflection \
|
||||
-DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
-I../deps/cpr/include \
|
||||
-I../deps/cpr/build/cpr_generated_includes \
|
||||
-I../include -I.. \
|
||||
github_modern.cpp \
|
||||
../singleheader/simdjson.cpp \
|
||||
../deps/cpr/build/lib/libcpr.a \
|
||||
-lcurl -pthread \
|
||||
-o github_modern_demo
|
||||
```
|
||||
|
||||
### Using System-Installed cpr
|
||||
|
||||
If cpr is installed system-wide (see [INSTALL_CPR.md](INSTALL_CPR.md)):
|
||||
|
||||
```bash
|
||||
# Legacy approach
|
||||
clang++ -std=c++20 \
|
||||
-I../include -I.. \
|
||||
github_legacy.cpp \
|
||||
-lcpr -lcurl \
|
||||
-o github_legacy_demo
|
||||
|
||||
# Modern approach
|
||||
clang++ -std=c++26 -freflection \
|
||||
-DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
-I../include -I.. \
|
||||
github_modern.cpp \
|
||||
-lcpr -lcurl \
|
||||
-o github_modern_demo
|
||||
```
|
||||
|
||||
### Using simdjson Single Header
|
||||
|
||||
```bash
|
||||
# Legacy approach
|
||||
clang++ -std=c++20 \
|
||||
-I../singleheader \
|
||||
github_legacy.cpp \
|
||||
../singleheader/simdjson.cpp \
|
||||
-lcpr -lcurl \
|
||||
-o github_legacy_demo
|
||||
|
||||
# Modern approach
|
||||
clang++ -std=c++26 -freflection \
|
||||
-DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
-I../singleheader -I../include \
|
||||
github_modern.cpp \
|
||||
../singleheader/simdjson.cpp \
|
||||
-lcpr -lcurl \
|
||||
-o github_modern_demo
|
||||
```
|
||||
|
||||
### Using CMake (Recommended)
|
||||
|
||||
```bash
|
||||
# Create build directory
|
||||
mkdir build && cd build
|
||||
|
||||
# Configure with CMake
|
||||
cmake .. -DCMAKE_CXX_COMPILER=clang++ \
|
||||
-DCMAKE_CXX_STANDARD=26 \
|
||||
-DCMAKE_CXX_FLAGS="-freflection -DSIMDJSON_STATIC_REFLECTION=1"
|
||||
|
||||
# Build both examples
|
||||
make github_legacy github_modern
|
||||
```
|
||||
|
||||
### Quick Build with simdjson Single Header
|
||||
|
||||
```bash
|
||||
# Legacy approach
|
||||
clang++ -std=c++20 \
|
||||
-I../singleheader \
|
||||
github_legacy.cpp \
|
||||
../singleheader/simdjson.cpp \
|
||||
-lcpr -lcurl \
|
||||
-o github_legacy_demo
|
||||
|
||||
# Modern approach
|
||||
clang++ -std=c++26 -freflection \
|
||||
-DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
-I../singleheader \
|
||||
github_modern.cpp \
|
||||
../singleheader/simdjson.cpp \
|
||||
-lcpr -lcurl \
|
||||
-o github_modern_demo
|
||||
```
|
||||
|
||||
## 🏃 Running the Examples
|
||||
|
||||
```bash
|
||||
# Run legacy version
|
||||
./github_legacy_demo
|
||||
|
||||
# Run modern version
|
||||
./github_modern_demo
|
||||
```
|
||||
|
||||
## 🎯 Platform-Specific Notes
|
||||
|
||||
### Linux (x86_64)
|
||||
```bash
|
||||
-DSIMDJSON_IMPLEMENTATION_HASWELL=1
|
||||
```
|
||||
|
||||
### Linux (ARM64)
|
||||
```bash
|
||||
-DSIMDJSON_IMPLEMENTATION_ARM64=1
|
||||
```
|
||||
|
||||
### macOS (Apple Silicon)
|
||||
```bash
|
||||
-DSIMDJSON_IMPLEMENTATION_ARM64=1
|
||||
```
|
||||
|
||||
### macOS (Intel)
|
||||
```bash
|
||||
-DSIMDJSON_IMPLEMENTATION_HASWELL=1
|
||||
```
|
||||
|
||||
### Windows (MSVC)
|
||||
```cmd
|
||||
cl /std:c++20 /I..\include /I.. github_legacy.cpp /Fe:github_legacy_demo.exe
|
||||
```
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### "reflection feature not available"
|
||||
- Ensure you're using the Bloomberg clang fork
|
||||
- Check version: `clang++ --version` should show bloomberg/clang-p2996
|
||||
|
||||
### "SIMDJSON_STATIC_REFLECTION not working"
|
||||
- Make sure to define it before including headers:
|
||||
```cpp
|
||||
#define SIMDJSON_STATIC_REFLECTION 1
|
||||
#include <simdjson.h>
|
||||
```
|
||||
|
||||
### Linking errors
|
||||
- Use single-header approach for simplicity
|
||||
- Or ensure simdjson library is properly built and linked
|
||||
|
||||
### Performance issues
|
||||
- Add optimization flags: `-O3 -march=native`
|
||||
- Enable LTO: `-flto`
|
||||
|
||||
## 📦 Creating a Portable Demo
|
||||
|
||||
For conferences, prepare the demo environment:
|
||||
|
||||
```bash
|
||||
# Install cpr library (if not available)
|
||||
# Ubuntu/Debian:
|
||||
sudo apt-get install libcpr-dev
|
||||
|
||||
# macOS:
|
||||
brew install cpr
|
||||
|
||||
# Or build from source:
|
||||
git clone https://github.com/libcpr/cpr.git
|
||||
cd cpr && mkdir build && cd build
|
||||
cmake .. && make && sudo make install
|
||||
|
||||
# Create demo directory
|
||||
mkdir simdjson_oneliner_demo
|
||||
cd simdjson_oneliner_demo
|
||||
|
||||
# Copy necessary files
|
||||
cp path/to/github_legacy.cpp .
|
||||
cp path/to/github_modern.cpp .
|
||||
cp -r path/to/simdjson/include .
|
||||
cp -r path/to/simdjson/singleheader .
|
||||
|
||||
# Create build script
|
||||
cat > build_demo.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
echo "🔨 Building Legacy Example..."
|
||||
clang++ -std=c++20 -O3 -I./include -I. \
|
||||
github_legacy.cpp -lcpr -lcurl -o legacy_demo
|
||||
|
||||
echo "🔨 Building Modern Example (C++26)..."
|
||||
clang++ -std=c++26 -freflection -DSIMDJSON_STATIC_REFLECTION=1 -O3 \
|
||||
-I./include -I. github_modern.cpp -lcpr -lcurl -o modern_demo
|
||||
|
||||
echo "✅ Build complete!"
|
||||
echo "Run: ./legacy_demo or ./modern_demo"
|
||||
EOF
|
||||
|
||||
chmod +x build_demo.sh
|
||||
```
|
||||
|
||||
## 🎪 Conference Checklist
|
||||
|
||||
- [ ] Bloomberg clang installed on demo machine
|
||||
- [ ] Both examples compile cleanly
|
||||
- [ ] Internet connection for live API calls (or use standalone)
|
||||
- [ ] Backup: pre-recorded video of compilation and execution
|
||||
- [ ] Slides with code snippets
|
||||
- [ ] QR code for GitHub repo
|
||||
|
||||
## 🔗 Quick Links
|
||||
|
||||
- simdjson: https://github.com/simdjson/simdjson
|
||||
- Bloomberg Clang: https://github.com/bloomberg/clang-p2996
|
||||
- P2996 Reflection Proposal: https://wg21.link/p2996
|
||||
- Talk Resources: [Your GitHub repo with examples]
|
||||
@@ -1 +1,23 @@
|
||||
add_subdirectory(quickstart)
|
||||
|
||||
# Add simdjson one-liner demo examples
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/github_legacy.cpp AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/github_modern.cpp)
|
||||
# FetchContent to download cpr
|
||||
include(FetchContent)
|
||||
set(CPR_ENABLE_SSL OFF CACHE BOOL "")
|
||||
FetchContent_Declare(
|
||||
cpr
|
||||
GIT_REPOSITORY https://github.com/libcpr/cpr.git
|
||||
GIT_TAG 1.10.5
|
||||
)
|
||||
FetchContent_MakeAvailable(cpr)
|
||||
|
||||
# Build the examples
|
||||
add_executable(github_legacy github_legacy.cpp)
|
||||
target_link_libraries(github_legacy simdjson cpr::cpr)
|
||||
|
||||
add_executable(github_modern github_modern.cpp)
|
||||
target_link_libraries(github_modern simdjson cpr::cpr)
|
||||
target_compile_options(github_modern PRIVATE -std=c++26 -freflection)
|
||||
target_compile_definitions(github_modern PRIVATE SIMDJSON_STATIC_REFLECTION=1)
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
# 🚀 From Boilerplate to One-Liner: The simdjson Revolution
|
||||
|
||||
## Conference Talk: JSON Parsing in Modern C++
|
||||
|
||||
### 📋 Talk Abstract
|
||||
Discover how simdjson's new API combined with C++26 reflection transforms JSON parsing from a tedious, error-prone task into a single line of code. This talk showcases the dramatic evolution from manual parsing to automatic struct deserialization.
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Key Talking Points
|
||||
|
||||
### 1. **The Problem** (2 minutes)
|
||||
- JSON is everywhere: APIs, configs, data exchange
|
||||
- C++ historically makes JSON parsing verbose
|
||||
- Show other languages: `user = json.loads(data)` in Python
|
||||
- "Why can't C++ be this simple?"
|
||||
|
||||
### 2. **The Legacy Approach** (5 minutes)
|
||||
- Live demo: `github_legacy.cpp`
|
||||
- Walk through the boilerplate:
|
||||
```cpp
|
||||
// 😓 Manual field extraction
|
||||
user.login = std::string(doc["login"].get_string().value());
|
||||
user.id = doc["id"].get_int64().value();
|
||||
|
||||
// 😰 Handle optional fields
|
||||
auto company_result = doc["company"];
|
||||
if (!company_result.is_null()) {
|
||||
user.company = std::string(company_result.get_string().value());
|
||||
}
|
||||
```
|
||||
- Count the lines: ~30 lines just for parsing!
|
||||
- Error-prone: typos, missing fields, type mismatches
|
||||
|
||||
### 3. **The Magic Moment** (3 minutes)
|
||||
- "What if I told you it could be just ONE line?"
|
||||
- Show `github_modern.cpp`
|
||||
- The magic line:
|
||||
```cpp
|
||||
GitHubUser user = simdjson::from(simdjson::padded_string(json_data));
|
||||
```
|
||||
- Audience reaction: 🤯
|
||||
|
||||
### 4. **How It Works** (5 minutes)
|
||||
- C++26 static reflection
|
||||
- Compile-time struct introspection
|
||||
- simdjson generates parsing code automatically
|
||||
- Zero runtime overhead - it's all compile-time!
|
||||
|
||||
### 5. **Live Demo** (5 minutes)
|
||||
- Compile both examples
|
||||
- Run them side-by-side
|
||||
- Show identical output
|
||||
- Highlight the code difference
|
||||
- Add a new field to the struct - watch it "just work"
|
||||
|
||||
### 6. **Deserialization Performance** (3 minutes)
|
||||
- "But is deserialization fast?"
|
||||
- Live benchmark demonstration
|
||||
- Both approaches achieve ~2.7 GB/s deserialization speed on real GitHub API data
|
||||
- Reflection adds ZERO runtime overhead to deserialization
|
||||
- "You get simplicity WITHOUT sacrificing speed!"
|
||||
- Note: We're measuring JSON → struct deserialization performance
|
||||
- Note: Performance scales with larger documents (up to 3+ GB/s)
|
||||
|
||||
### 7. **The Future is Now** (2 minutes)
|
||||
- Bloomberg clang fork available today
|
||||
- C++26 coming soon
|
||||
- Start preparing your codebases
|
||||
- simdjson ready for the future
|
||||
|
||||
---
|
||||
|
||||
## 💻 Live Coding Demo Script
|
||||
|
||||
### Setup
|
||||
```bash
|
||||
# Show the two files
|
||||
ls -la github_*.cpp
|
||||
|
||||
# Show line count difference
|
||||
wc -l github_legacy.cpp github_modern.cpp
|
||||
```
|
||||
|
||||
### Demo 1: The Pain of Legacy
|
||||
```bash
|
||||
# Open github_legacy.cpp in editor
|
||||
# Highlight the parse_github_user function
|
||||
# Point out each manual field extraction
|
||||
# "Look at all this code just to parse 7 fields!"
|
||||
```
|
||||
|
||||
### Demo 2: The Modern Magic
|
||||
```bash
|
||||
# Open github_modern.cpp
|
||||
# Show the struct - "Just a plain struct!"
|
||||
# Show the one-liner
|
||||
# "That's it. That's the entire parsing code."
|
||||
```
|
||||
|
||||
### Demo 3: Compilation and Execution
|
||||
|
||||
**Option 1: Use the demo script (Recommended)**
|
||||
```bash
|
||||
# The script handles everything - building cpr, compiling, and running
|
||||
./conference_demo.sh
|
||||
|
||||
# For the extended demo with serialization:
|
||||
./conference_demo_serialization.sh
|
||||
```
|
||||
|
||||
**Option 2: Manual compilation**
|
||||
```bash
|
||||
# Build cpr first (one-time setup)
|
||||
mkdir -p ../deps && cd ../deps
|
||||
git clone https://github.com/libcpr/cpr.git
|
||||
cd cpr && git checkout 1.10.5
|
||||
mkdir build && cd build
|
||||
cmake .. -DCMAKE_CXX_COMPILER=clang++ -DCPR_USE_SYSTEM_CURL=ON \
|
||||
-DBUILD_SHARED_LIBS=OFF -DCPR_ENABLE_SSL=OFF
|
||||
make -j4
|
||||
cd ../../../examples
|
||||
|
||||
# Compile legacy
|
||||
clang++ -std=c++20 \
|
||||
-I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes \
|
||||
-I../include -I.. github_legacy.cpp ../singleheader/simdjson.cpp \
|
||||
../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o legacy_demo
|
||||
|
||||
# Compile modern (with reflection)
|
||||
clang++ -std=c++26 -freflection -DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
-I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes \
|
||||
-I../include -I.. github_modern.cpp ../singleheader/simdjson.cpp \
|
||||
../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o modern_demo
|
||||
|
||||
# Run both - they fetch real GitHub data!
|
||||
./legacy_demo
|
||||
./modern_demo
|
||||
```
|
||||
|
||||
### Demo 4: Adding a New Field (Crowd Pleaser!)
|
||||
```cpp
|
||||
// Add to struct in both files:
|
||||
std::string twitter_username;
|
||||
|
||||
// Legacy: Must add parsing code
|
||||
else if (key == "twitter_username")
|
||||
user.twitter_username = field.value().get_string().value();
|
||||
|
||||
// Modern: Nothing to add! It just works!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎤 Speaker Notes
|
||||
|
||||
### Opening Hook
|
||||
"How many of you have written JSON parsing code in C++? Keep your hands up if you enjoyed it... Yeah, I thought so."
|
||||
|
||||
### Transition to Modern
|
||||
"But what if I told you that in 2024, with simdjson and C++26, parsing JSON can be as simple as Python?"
|
||||
|
||||
### After showing the one-liner
|
||||
"No, this isn't pseudocode. This is real, working C++ code. Let me prove it to you."
|
||||
|
||||
### Addressing Skeptics
|
||||
- "It's not magic, it's metaprogramming"
|
||||
- "No runtime cost - it's all compile-time"
|
||||
- "Yes, it handles errors properly"
|
||||
- "Yes, it's production-ready"
|
||||
|
||||
### Closing
|
||||
"The future of C++ is here. It's fast, it's simple, and it's beautiful. Stop writing boilerplate. Start writing the code that matters."
|
||||
|
||||
---
|
||||
|
||||
## 📊 Slide Suggestions
|
||||
|
||||
### Slide 1: Title
|
||||
**From 50 Lines to 1: The simdjson Revolution**
|
||||
*Your Name - Conference 2024*
|
||||
|
||||
### Slide 2: The Problem
|
||||
```python
|
||||
# Python
|
||||
user = json.loads(data)
|
||||
|
||||
# JavaScript
|
||||
const user = JSON.parse(data);
|
||||
|
||||
# C++ ???
|
||||
// 😭 50+ lines of boilerplate
|
||||
```
|
||||
|
||||
### Slide 3: The Solution
|
||||
```cpp
|
||||
// C++26 with simdjson
|
||||
GitHubUser user = simdjson::from(simdjson::padded_string(data));
|
||||
```
|
||||
|
||||
### Slide 4: Performance Graph
|
||||
- Bar chart showing simdjson vs other parsers
|
||||
- "Fast AND Simple"
|
||||
|
||||
### Slide 5: Timeline
|
||||
- 2018: simdjson introduced (fast but verbose)
|
||||
- 2024: New API design
|
||||
- 2024: C++26 reflection support
|
||||
- Future: It's here!
|
||||
|
||||
### Slide 6: Call to Action
|
||||
- Try it today: github.com/simdjson/simdjson
|
||||
- Bloomberg clang: github.com/bloomberg/clang-p2996
|
||||
- Join the revolution!
|
||||
|
||||
---
|
||||
|
||||
## 🔥 Audience Engagement
|
||||
|
||||
### Interactive Elements
|
||||
1. **Live Poll**: "How many lines of code for parsing JSON?"
|
||||
2. **Challenge**: "Spot the bug in this manual parsing code"
|
||||
3. **Q&A Focus**: Performance, error handling, compatibility
|
||||
|
||||
### Memorable Moments
|
||||
- The reveal of the one-liner
|
||||
- Live compilation with reflection
|
||||
- Adding a field without changing parsing code
|
||||
- Performance numbers
|
||||
|
||||
### Takeaway Message
|
||||
"C++ doesn't have to be painful. With modern tools and modern standards, C++ can be as elegant as any language - and faster than all of them."
|
||||
@@ -0,0 +1,72 @@
|
||||
# Installing CPR Library
|
||||
|
||||
The examples require the CPR library for making HTTP requests to the GitHub API.
|
||||
|
||||
## Option 1: Install via Package Manager (Recommended)
|
||||
|
||||
### Ubuntu/Debian
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install libcpr-dev
|
||||
```
|
||||
|
||||
### macOS (Homebrew)
|
||||
```bash
|
||||
brew install cpr
|
||||
```
|
||||
|
||||
### Arch Linux
|
||||
```bash
|
||||
sudo pacman -S cpr
|
||||
```
|
||||
|
||||
## Option 2: Build from Source
|
||||
|
||||
```bash
|
||||
# Clone cpr
|
||||
git clone https://github.com/libcpr/cpr.git
|
||||
cd cpr
|
||||
|
||||
# Build and install
|
||||
mkdir build && cd build
|
||||
cmake .. -DCPR_USE_SYSTEM_CURL=ON
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
## Option 3: Using vcpkg
|
||||
|
||||
```bash
|
||||
vcpkg install cpr
|
||||
```
|
||||
|
||||
## Option 4: Using Conan
|
||||
|
||||
```bash
|
||||
conan install cpr/1.10.5@
|
||||
```
|
||||
|
||||
## Compilation
|
||||
|
||||
Once cpr is installed, compile the examples:
|
||||
|
||||
```bash
|
||||
# Legacy example
|
||||
clang++ -std=c++20 -I../include -I.. github_legacy.cpp -lcpr -lcurl -o github_legacy_demo
|
||||
|
||||
# Modern example (requires Bloomberg clang)
|
||||
clang++ -std=c++26 -freflection -DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
-I../include -I.. github_modern.cpp -lcpr -lcurl -o github_modern_demo
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you get "cpr/cpr.h not found", check:
|
||||
- `pkg-config --cflags --libs cpr`
|
||||
- Add include path: `-I/usr/local/include`
|
||||
- Add library path: `-L/usr/local/lib`
|
||||
|
||||
For SSL issues, ensure OpenSSL is installed:
|
||||
- Ubuntu/Debian: `sudo apt-get install libssl-dev`
|
||||
- macOS: `brew install openssl`
|
||||
- Link OpenSSL: `-lssl -lcrypto`
|
||||
@@ -0,0 +1,74 @@
|
||||
# 🚀 simdjson One-Liner Demo: From Boilerplate to Magic
|
||||
|
||||
This demo showcases the dramatic simplification of JSON parsing in C++ using simdjson's new API combined with C++26 reflection.
|
||||
|
||||
## Files
|
||||
|
||||
- `github_legacy.cpp` - Traditional manual JSON parsing approach (~30 lines of parsing code)
|
||||
- `github_modern.cpp` - Modern C++26 reflection approach (1 line of parsing code!)
|
||||
- `CONFERENCE_TALK.md` - Complete conference presentation guide
|
||||
- `BUILD_INSTRUCTIONS.md` - Detailed compilation instructions
|
||||
- `conference_demo.sh` - Interactive demo script for presentations
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Prerequisites
|
||||
- Bloomberg clang for C++26: https://github.com/bloomberg/clang-p2996
|
||||
- curl library (usually pre-installed)
|
||||
- cpr library (built automatically by demo script)
|
||||
|
||||
### Easy Demo
|
||||
```bash
|
||||
# Just run the demo script - it handles everything!
|
||||
./conference_demo.sh
|
||||
```
|
||||
|
||||
The script will:
|
||||
1. Build cpr if needed
|
||||
2. Compile both examples
|
||||
3. Run interactive presentation
|
||||
|
||||
### Manual Compilation
|
||||
|
||||
If you want to compile manually:
|
||||
|
||||
```bash
|
||||
# Build cpr first (if not installed)
|
||||
mkdir -p ../deps && cd ../deps
|
||||
git clone https://github.com/libcpr/cpr.git
|
||||
cd cpr && git checkout 1.10.5
|
||||
mkdir build && cd build
|
||||
cmake .. -DCMAKE_CXX_COMPILER=clang++ -DCPR_USE_SYSTEM_CURL=ON -DBUILD_SHARED_LIBS=OFF -DCPR_ENABLE_SSL=OFF
|
||||
make -j4
|
||||
cd ../../../examples
|
||||
|
||||
# Compile examples
|
||||
# Legacy
|
||||
clang++ -std=c++20 -I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes \
|
||||
-I../include -I.. github_legacy.cpp ../singleheader/simdjson.cpp \
|
||||
../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o legacy_demo
|
||||
|
||||
# Modern
|
||||
clang++ -std=c++26 -freflection -DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
-I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes \
|
||||
-I../include -I.. github_modern.cpp ../singleheader/simdjson.cpp \
|
||||
../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o modern_demo
|
||||
```
|
||||
|
||||
## The Magic ✨
|
||||
|
||||
**Before (Legacy):**
|
||||
```cpp
|
||||
// 30+ lines of manual parsing...
|
||||
user.login = std::string(doc["login"].get_string().value());
|
||||
user.id = doc["id"].get_int64().value();
|
||||
// ... etc for each field
|
||||
```
|
||||
|
||||
**After (Modern):**
|
||||
```cpp
|
||||
// Just ONE line!
|
||||
GitHubUser user = simdjson::from(simdjson::padded_string(response.text));
|
||||
```
|
||||
|
||||
Both examples fetch real data from GitHub API to demonstrate real-world usage!
|
||||
@@ -0,0 +1,53 @@
|
||||
# simdjson Serialization with C++26 Reflection
|
||||
|
||||
This directory now includes examples demonstrating **both** deserialization and serialization using C++26 reflection features.
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Basic Round-Trip Demo
|
||||
```bash
|
||||
# Compile and run the simple round-trip demo
|
||||
clang++ -std=c++26 -freflection -DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
reflection_roundtrip_demo.cpp ../singleheader/simdjson.cpp \
|
||||
-o reflection_roundtrip_demo
|
||||
./reflection_roundtrip_demo
|
||||
```
|
||||
|
||||
### Conference Demo with Serialization
|
||||
```bash
|
||||
# Run the extended conference demo that includes serialization
|
||||
./conference_demo_serialization.sh
|
||||
```
|
||||
|
||||
## 📝 What's New?
|
||||
|
||||
The experimental `simdjson::builder::to_json_string()` function enables one-line serialization:
|
||||
|
||||
```cpp
|
||||
// Deserialize JSON → Struct
|
||||
MyStruct obj = simdjson::from(json);
|
||||
|
||||
// Serialize Struct → JSON (NEW!)
|
||||
std::string json = simdjson::builder::to_json_string(obj);
|
||||
```
|
||||
|
||||
## 🎯 Features
|
||||
|
||||
- **Zero boilerplate**: No need to write serialization code
|
||||
- **Automatic handling**: Optional fields, containers, nested structures
|
||||
- **Type-safe**: Compile-time checking with reflection
|
||||
- **Performant**: Optimized string building
|
||||
|
||||
## ⚠️ Requirements
|
||||
|
||||
- Bloomberg's clang fork (clang-p2996) with C++26 reflection support
|
||||
- Build with `-DSIMDJSON_STATIC_REFLECTION=1`
|
||||
- Include `simdjson/builder/json_builder.h`
|
||||
|
||||
## 📊 Performance
|
||||
|
||||
While serialization is generally slower than deserialization (due to string building vs parsing), the reflection-based approach is still highly optimized and significantly faster than manual string concatenation approaches.
|
||||
|
||||
## 🔧 Status
|
||||
|
||||
This serialization support is **experimental** and part of the builder API. The API may change as C++26 reflection features evolve.
|
||||
Executable
+167
@@ -0,0 +1,167 @@
|
||||
#!/bin/bash
|
||||
# Conference Demo Script - simdjson One-Liner Magic
|
||||
# Run this during your talk for a smooth demo experience
|
||||
|
||||
# Check if cpr is built
|
||||
if [ ! -f "../deps/cpr/build/lib/libcpr.a" ]; then
|
||||
echo "⚠️ CPR library not found. Building it first..."
|
||||
echo ""
|
||||
if [ ! -d "../deps/cpr" ]; then
|
||||
mkdir -p ../deps
|
||||
cd ../deps
|
||||
git clone https://github.com/libcpr/cpr.git
|
||||
cd cpr && git checkout 1.10.5
|
||||
cd ../..
|
||||
fi
|
||||
cd ../deps/cpr
|
||||
mkdir -p build && cd build
|
||||
cmake .. -DCMAKE_CXX_COMPILER=clang++ -DCPR_USE_SYSTEM_CURL=ON -DBUILD_SHARED_LIBS=OFF -DCPR_ENABLE_SSL=OFF
|
||||
make -j4
|
||||
cd ../../../examples
|
||||
echo "✅ CPR built successfully!"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
clear
|
||||
echo "🚀 simdjson: From Boilerplate to One-Liner"
|
||||
echo "==========================================="
|
||||
echo ""
|
||||
echo "Press Enter to continue..."
|
||||
read
|
||||
|
||||
# Show the legacy approach
|
||||
echo "📚 First, let's look at the LEGACY approach..."
|
||||
echo ""
|
||||
echo "Opening github_legacy.cpp..."
|
||||
sleep 1
|
||||
echo ""
|
||||
echo "Key points:"
|
||||
echo " • Manual parse_github_user() function"
|
||||
echo " • Extract each field individually"
|
||||
echo " • Handle optional fields explicitly"
|
||||
echo " • ~30 lines of parsing code"
|
||||
echo ""
|
||||
echo "Press Enter to see the code..."
|
||||
read
|
||||
|
||||
# Display key parts of legacy code
|
||||
cat github_legacy.cpp | grep -A 20 "parse_github_user" | head -25
|
||||
|
||||
echo ""
|
||||
echo "😓 That's a lot of boilerplate!"
|
||||
echo ""
|
||||
echo "Press Enter to compile and run..."
|
||||
read
|
||||
|
||||
# Compile legacy
|
||||
echo "🔨 Compiling legacy version..."
|
||||
echo "Command: clang++ -std=c++20 -I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes -I../include -I.. github_legacy.cpp ../singleheader/simdjson.cpp ../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o legacy_demo"
|
||||
clang++ -std=c++20 -I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes -I../include -I.. github_legacy.cpp ../singleheader/simdjson.cpp ../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o legacy_demo
|
||||
echo "✅ Compiled!"
|
||||
echo ""
|
||||
echo "🏃 Running legacy demo (fetching real GitHub data)..."
|
||||
echo ""
|
||||
./legacy_demo
|
||||
|
||||
echo ""
|
||||
echo "Press Enter to see the MODERN approach..."
|
||||
read
|
||||
|
||||
clear
|
||||
echo "✨ Now, let's look at the MODERN approach with C++26 reflection..."
|
||||
echo ""
|
||||
echo "Opening github_modern.cpp..."
|
||||
sleep 1
|
||||
echo ""
|
||||
echo "Key points:"
|
||||
echo " • Just declare your struct"
|
||||
echo " • ONE line of parsing code"
|
||||
echo " • C++26 reflection handles everything"
|
||||
echo " • No manual field extraction!"
|
||||
echo ""
|
||||
echo "Press Enter to see the magic..."
|
||||
read
|
||||
|
||||
# Show the struct and the one-liner
|
||||
echo "The struct (just a plain struct!):"
|
||||
echo ""
|
||||
cat github_modern.cpp | grep -A 10 "struct GitHubUser" | head -12
|
||||
echo ""
|
||||
echo "The parsing code (ONE LINE!):"
|
||||
echo ""
|
||||
echo " GitHubUser user = simdjson::from(simdjson::padded_string(response.text));"
|
||||
echo ""
|
||||
echo "🤯 That's it! That's all the parsing code!"
|
||||
echo ""
|
||||
echo "Press Enter to compile with C++26 reflection..."
|
||||
read
|
||||
|
||||
# Compile modern
|
||||
echo "🔨 Compiling modern version with Bloomberg clang..."
|
||||
echo "Command: clang++ -std=c++26 -freflection -DSIMDJSON_STATIC_REFLECTION=1 -I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes -I../include -I.. github_modern.cpp ../singleheader/simdjson.cpp ../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o modern_demo"
|
||||
clang++ -std=c++26 -freflection -DSIMDJSON_STATIC_REFLECTION=1 -I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes -I../include -I.. github_modern.cpp ../singleheader/simdjson.cpp ../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o modern_demo
|
||||
echo "✅ Compiled!"
|
||||
echo ""
|
||||
echo "🏃 Running modern demo (fetching real GitHub data)..."
|
||||
echo ""
|
||||
./modern_demo
|
||||
|
||||
echo ""
|
||||
echo "Press Enter to see side-by-side comparison..."
|
||||
read
|
||||
|
||||
clear
|
||||
echo "📊 SIDE-BY-SIDE COMPARISON"
|
||||
echo "========================="
|
||||
echo ""
|
||||
echo "Legacy Approach: Modern Approach (C++26):"
|
||||
echo "---------------- ------------------------"
|
||||
echo "❌ 30+ lines of parsing code ✅ 1 line of parsing code"
|
||||
echo "❌ Manual field extraction ✅ Automatic with reflection"
|
||||
echo "❌ Error-prone ✅ Type-safe"
|
||||
echo "❌ Hard to maintain ✅ Just update the struct"
|
||||
echo "❌ Boilerplate for each type ✅ Works for any struct"
|
||||
echo ""
|
||||
echo ""
|
||||
echo "Press Enter to run PERFORMANCE BENCHMARKS..."
|
||||
read
|
||||
|
||||
clear
|
||||
echo "⚡ DESERIALIZATION PERFORMANCE BENCHMARKS"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
echo "Let's measure the actual JSON → struct deserialization speed..."
|
||||
echo ""
|
||||
|
||||
# Compile benchmarks if not exist
|
||||
if [ ! -f "./legacy_benchmark" ] || [ ! -f "./modern_benchmark" ]; then
|
||||
echo "🔨 Compiling benchmark versions..."
|
||||
|
||||
clang++ -std=c++20 -O3 -march=native \
|
||||
-I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes \
|
||||
-I../include -I.. github_legacy_benchmark.cpp ../singleheader/simdjson.cpp \
|
||||
../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o legacy_benchmark 2>/dev/null
|
||||
|
||||
clang++ -std=c++26 -freflection -O3 -march=native \
|
||||
-DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
-I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes \
|
||||
-I../include -I.. github_modern_benchmark.cpp ../singleheader/simdjson.cpp \
|
||||
../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o modern_benchmark 2>/dev/null
|
||||
fi
|
||||
|
||||
echo "🏃 Running legacy benchmark..."
|
||||
echo ""
|
||||
./legacy_benchmark
|
||||
|
||||
echo ""
|
||||
echo "Press Enter to run modern benchmark..."
|
||||
read
|
||||
|
||||
echo "🏃 Running modern benchmark..."
|
||||
echo ""
|
||||
./modern_benchmark
|
||||
|
||||
echo ""
|
||||
echo "🎉 The future of C++ is here!"
|
||||
echo ""
|
||||
echo "Questions?"
|
||||
Executable
+322
@@ -0,0 +1,322 @@
|
||||
#!/bin/bash
|
||||
# Conference Demo Script - simdjson One-Liner Magic (WITH SERIALIZATION!)
|
||||
# Extended demo showcasing both deserialization AND serialization with C++26 reflection
|
||||
|
||||
# Check if cpr is built
|
||||
if [ ! -f "../deps/cpr/build/lib/libcpr.a" ]; then
|
||||
echo "⚠️ CPR library not found. Building it first..."
|
||||
echo ""
|
||||
if [ ! -d "../deps/cpr" ]; then
|
||||
mkdir -p ../deps
|
||||
cd ../deps
|
||||
git clone https://github.com/libcpr/cpr.git
|
||||
cd cpr && git checkout 1.10.5
|
||||
cd ../..
|
||||
fi
|
||||
cd ../deps/cpr
|
||||
mkdir -p build && cd build
|
||||
cmake .. -DCMAKE_CXX_COMPILER=clang++ -DCPR_USE_SYSTEM_CURL=ON -DBUILD_SHARED_LIBS=OFF -DCPR_ENABLE_SSL=OFF
|
||||
make -j4
|
||||
cd ../../../examples
|
||||
echo "✅ CPR built successfully!"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
clear
|
||||
echo "🚀 simdjson: Complete JSON Round-Trip with C++26 Reflection"
|
||||
echo "============================================================"
|
||||
echo ""
|
||||
echo "Today we'll showcase BOTH deserialization AND serialization!"
|
||||
echo ""
|
||||
echo "Press Enter to continue..."
|
||||
read
|
||||
|
||||
# Create a demo program that shows serialization
|
||||
cat > github_serialization_demo.cpp << 'EOF'
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include "simdjson.h"
|
||||
#include <cpr/cpr.h>
|
||||
|
||||
struct GitHubUser {
|
||||
std::string login;
|
||||
std::optional<std::string> name;
|
||||
std::optional<std::string> company;
|
||||
std::optional<std::string> blog;
|
||||
std::optional<std::string> location;
|
||||
std::optional<std::string> email;
|
||||
std::optional<std::string> bio;
|
||||
int64_t public_repos;
|
||||
int64_t followers;
|
||||
int64_t following;
|
||||
};
|
||||
|
||||
int main() {
|
||||
std::cout << "🔄 C++26 Reflection: Complete JSON Round-Trip Demo\n";
|
||||
std::cout << "================================================\n\n";
|
||||
|
||||
// Step 1: Fetch real data from GitHub
|
||||
std::cout << "📡 Fetching GitHub user data...\n";
|
||||
auto response = cpr::Get(cpr::Url{"https://api.github.com/users/simdjson"});
|
||||
|
||||
if (response.status_code != 200) {
|
||||
std::cerr << "Error: Failed to fetch data\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "✅ Received " << response.text.length() << " bytes of JSON\n\n";
|
||||
|
||||
// Step 2: Deserialize with ONE LINE
|
||||
std::cout << "📥 DESERIALIZATION (JSON → Struct)\n";
|
||||
std::cout << "Code: GitHubUser user = simdjson::from(simdjson::padded_string(response.text));\n\n";
|
||||
|
||||
GitHubUser user = simdjson::from(simdjson::padded_string(response.text));
|
||||
|
||||
// Show the data we parsed
|
||||
std::cout << "Parsed data:\n";
|
||||
std::cout << " • Login: " << user.login << "\n";
|
||||
std::cout << " • Name: " << (user.name.has_value() ? *user.name : "<not set>") << "\n";
|
||||
std::cout << " • Company: " << (user.company.has_value() ? *user.company : "<not set>") << "\n";
|
||||
std::cout << " • Location: " << (user.location.has_value() ? *user.location : "<not set>") << "\n";
|
||||
std::cout << " • Bio: " << (user.bio.has_value() ? *user.bio : "<not set>") << "\n";
|
||||
std::cout << " • Repos: " << user.public_repos << "\n";
|
||||
std::cout << " • Followers: " << user.followers << "\n\n";
|
||||
|
||||
// Step 3: Modify the data
|
||||
std::cout << "✏️ Modifying data...\n";
|
||||
user.followers += 1000; // Wishful thinking!
|
||||
user.name = "simdjson - JSON at the speed of light"; // Set a name
|
||||
user.bio = user.bio.value_or("") + " (Now with C++26 reflection!)";
|
||||
std::cout << " • Added 1000 followers (we can dream!)\n";
|
||||
std::cout << " • Set organization name\n";
|
||||
std::cout << " • Updated bio\n\n";
|
||||
|
||||
// Step 4: Serialize back to JSON with ONE LINE
|
||||
std::cout << "📤 SERIALIZATION (Struct → JSON)\n";
|
||||
std::cout << "Code: std::string json = simdjson::builder::to_json_string(user);\n\n";
|
||||
|
||||
auto json_result = simdjson::builder::to_json_string(user);
|
||||
if (json_result.error()) {
|
||||
std::cerr << "Serialization error!\n";
|
||||
return 1;
|
||||
}
|
||||
std::string json = json_result.value();
|
||||
|
||||
std::cout << "Generated JSON (" << json.length() << " bytes):\n";
|
||||
// Pretty print first 200 chars
|
||||
if (json.length() > 200) {
|
||||
std::cout << json.substr(0, 200) << "...\n\n";
|
||||
} else {
|
||||
std::cout << json << "\n\n";
|
||||
}
|
||||
|
||||
// Step 5: Verify round-trip
|
||||
std::cout << "🔄 ROUND-TRIP VERIFICATION\n";
|
||||
std::cout << "Parsing our generated JSON back...\n";
|
||||
|
||||
GitHubUser user2 = simdjson::from(simdjson::padded_string(json));
|
||||
|
||||
std::cout << "✅ Round-trip successful!\n";
|
||||
std::cout << " • Original followers: " << user.followers << "\n";
|
||||
std::cout << " • Parsed followers: " << user2.followers << "\n";
|
||||
std::cout << " • Name set correctly: " << (user2.name.has_value() && *user2.name == *user.name ? "YES" : "NO") << "\n";
|
||||
std::cout << " • Bio preserved: " << (user2.bio.has_value() ? "YES" : "NO") << "\n\n";
|
||||
|
||||
std::cout << "🎉 That's it! Two lines of code for complete JSON handling:\n";
|
||||
std::cout << " • Deserialization: simdjson::from(...)\n";
|
||||
std::cout << " • Serialization: simdjson::builder::to_json_string(...)\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
# Create a benchmark that includes serialization
|
||||
cat > serialization_benchmark.cpp << 'EOF'
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
#include "simdjson.h"
|
||||
#include <map>
|
||||
|
||||
struct TestData {
|
||||
std::string name;
|
||||
std::vector<int> numbers;
|
||||
std::map<std::string, double> metrics;
|
||||
bool active;
|
||||
std::optional<std::string> description;
|
||||
};
|
||||
|
||||
int main() {
|
||||
std::cout << "⚡ Serialization Performance Benchmark\n";
|
||||
std::cout << "=====================================\n\n";
|
||||
|
||||
// Create test data
|
||||
TestData data{
|
||||
.name = "Performance Test",
|
||||
.numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
||||
.metrics = {{"latency", 1.23}, {"throughput", 456.78}, {"cpu", 89.01}},
|
||||
.active = true,
|
||||
.description = "Testing reflection-based serialization performance"
|
||||
};
|
||||
|
||||
const int iterations = 100000;
|
||||
|
||||
// Benchmark serialization
|
||||
std::cout << "📤 Benchmarking serialization (" << iterations << " iterations)...\n";
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
std::string json;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
auto result = simdjson::builder::to_json_string(data);
|
||||
if (i == 0 && !result.error()) {
|
||||
json = result.value();
|
||||
}
|
||||
}
|
||||
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
|
||||
std::cout << "\nResults:\n";
|
||||
std::cout << " • Generated JSON size: " << json.length() << " bytes\n";
|
||||
std::cout << " • Total time: " << duration.count() / 1000.0 << " ms\n";
|
||||
|
||||
double time_per_iteration_us = duration.count() / double(iterations);
|
||||
double time_per_iteration_s = time_per_iteration_us / 1000000.0;
|
||||
double bytes_per_second = json.length() / time_per_iteration_s;
|
||||
double mb_per_second = bytes_per_second / (1024.0 * 1024.0);
|
||||
|
||||
std::cout << " • Time per serialization: " << std::fixed << std::setprecision(2) << time_per_iteration_us << " μs\n";
|
||||
std::cout << " • Throughput: " << std::fixed << std::setprecision(2) << mb_per_second << " MB/s\n";
|
||||
|
||||
std::cout << "\nGenerated JSON:\n" << json << "\n\n";
|
||||
|
||||
// Benchmark deserialization for comparison
|
||||
std::cout << "📥 Benchmarking deserialization (for comparison)...\n";
|
||||
|
||||
simdjson::padded_string padded_json(json);
|
||||
start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
TestData parsed = simdjson::from(padded_json);
|
||||
}
|
||||
|
||||
end = std::chrono::high_resolution_clock::now();
|
||||
duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
|
||||
std::cout << "\nResults:\n";
|
||||
time_per_iteration_us = duration.count() / double(iterations);
|
||||
time_per_iteration_s = time_per_iteration_us / 1000000.0;
|
||||
bytes_per_second = json.length() / time_per_iteration_s;
|
||||
mb_per_second = bytes_per_second / (1024.0 * 1024.0);
|
||||
|
||||
std::cout << " • Time per deserialization: " << std::fixed << std::setprecision(2) << time_per_iteration_us << " μs\n";
|
||||
std::cout << " • Throughput: " << std::fixed << std::setprecision(2) << mb_per_second << " MB/s\n";
|
||||
|
||||
std::cout << "\n✅ Both serialization and deserialization work with reflection!\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "📝 First, let's see the traditional approach to serialization..."
|
||||
echo ""
|
||||
echo "Press Enter to see manual serialization code..."
|
||||
read
|
||||
|
||||
cat > manual_serialization_example.cpp << 'EOF'
|
||||
// The OLD way - Manual serialization
|
||||
std::string serialize_github_user(const GitHubUser& user) {
|
||||
std::string json = "{";
|
||||
json += "\"login\":\"" + user.login + "\",";
|
||||
json += "\"name\":\"" + user.name + "\",";
|
||||
json += "\"company\":\"" + user.company + "\",";
|
||||
json += "\"blog\":\"" + user.blog + "\",";
|
||||
json += "\"location\":\"" + user.location + "\",";
|
||||
|
||||
if (user.email.has_value()) {
|
||||
json += "\"email\":\"" + *user.email + "\",";
|
||||
}
|
||||
if (user.bio.has_value()) {
|
||||
json += "\"bio\":\"" + *user.bio + "\",";
|
||||
}
|
||||
|
||||
json += "\"public_repos\":" + std::to_string(user.public_repos) + ",";
|
||||
json += "\"followers\":" + std::to_string(user.followers) + ",";
|
||||
json += "\"following\":" + std::to_string(user.following);
|
||||
json += "}";
|
||||
|
||||
return json;
|
||||
}
|
||||
EOF
|
||||
|
||||
cat manual_serialization_example.cpp
|
||||
echo ""
|
||||
echo "😓 That's error-prone and doesn't handle escaping!"
|
||||
echo ""
|
||||
echo "Press Enter to see the MODERN approach..."
|
||||
read
|
||||
|
||||
clear
|
||||
echo "✨ The MODERN approach with C++26 reflection:"
|
||||
echo ""
|
||||
echo "Deserialization:"
|
||||
echo " GitHubUser user = simdjson::from(json);"
|
||||
echo ""
|
||||
echo "Serialization:"
|
||||
echo " std::string json = simdjson::builder::to_json_string(user);"
|
||||
echo ""
|
||||
echo "🤯 That's it! Bidirectional JSON handling in TWO lines!"
|
||||
echo ""
|
||||
echo "Press Enter to compile and run the complete demo..."
|
||||
read
|
||||
|
||||
# Compile and run serialization demo
|
||||
echo "🔨 Compiling serialization demo..."
|
||||
clang++ -std=c++26 -freflection -DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
-I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes \
|
||||
-I../include -I.. github_serialization_demo.cpp ../singleheader/simdjson.cpp \
|
||||
../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o serialization_demo
|
||||
|
||||
echo "✅ Compiled!"
|
||||
echo ""
|
||||
echo "🏃 Running complete round-trip demo..."
|
||||
echo ""
|
||||
./serialization_demo
|
||||
|
||||
echo ""
|
||||
echo "Press Enter to run performance benchmarks..."
|
||||
read
|
||||
|
||||
clear
|
||||
echo "⚡ PERFORMANCE BENCHMARKS"
|
||||
echo "========================"
|
||||
echo ""
|
||||
|
||||
# Compile benchmark
|
||||
echo "🔨 Compiling performance benchmark..."
|
||||
clang++ -std=c++26 -freflection -O3 -march=native \
|
||||
-DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
-I../include -I.. serialization_benchmark.cpp ../singleheader/simdjson.cpp \
|
||||
-pthread -o serialization_benchmark
|
||||
|
||||
echo "✅ Compiled!"
|
||||
echo ""
|
||||
echo "🏃 Running benchmark..."
|
||||
echo ""
|
||||
./serialization_benchmark
|
||||
|
||||
echo ""
|
||||
echo "🎉 Summary:"
|
||||
echo "==========="
|
||||
echo "• ONE line for deserialization"
|
||||
echo "• ONE line for serialization"
|
||||
echo "• Works with complex nested structures"
|
||||
echo "• Handles optional fields automatically"
|
||||
echo "• Type-safe and performant"
|
||||
echo "• No manual parsing/building code needed!"
|
||||
echo ""
|
||||
echo "The future of C++ JSON handling is here! 🚀"
|
||||
|
||||
# Cleanup
|
||||
rm -f manual_serialization_example.cpp
|
||||
@@ -0,0 +1,83 @@
|
||||
// Legacy approach - manual JSON parsing with simdjson
|
||||
#include <simdjson.h>
|
||||
#include <cpr/cpr.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
struct GitHubUser {
|
||||
std::string login;
|
||||
int64_t id;
|
||||
std::string name;
|
||||
std::optional<std::string> company;
|
||||
std::optional<std::string> location;
|
||||
int64_t public_repos;
|
||||
int64_t followers;
|
||||
};
|
||||
|
||||
// Legacy approach - manual parsing with lots of boilerplate
|
||||
GitHubUser parse_github_user(const std::string& json_str) {
|
||||
GitHubUser user;
|
||||
|
||||
simdjson::ondemand::parser parser;
|
||||
simdjson::padded_string json(json_str);
|
||||
simdjson::ondemand::document doc = parser.iterate(json);
|
||||
|
||||
// Manual field extraction with error checking
|
||||
user.login = std::string(doc["login"].get_string().value());
|
||||
user.id = doc["id"].get_int64().value();
|
||||
user.name = std::string(doc["name"].get_string().value());
|
||||
|
||||
// Handle optional fields
|
||||
auto company_result = doc["company"];
|
||||
if (!company_result.is_null()) {
|
||||
user.company = std::string(company_result.get_string().value());
|
||||
}
|
||||
|
||||
auto location_result = doc["location"];
|
||||
if (!location_result.is_null()) {
|
||||
user.location = std::string(location_result.get_string().value());
|
||||
}
|
||||
|
||||
user.public_repos = doc["public_repos"].get_int64().value();
|
||||
user.followers = doc["followers"].get_int64().value();
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "📚 Legacy Approach - Manual JSON Parsing\n";
|
||||
std::cout << "========================================\n\n";
|
||||
|
||||
// Fetch data from GitHub API
|
||||
auto response = cpr::Get(
|
||||
cpr::Url{"https://api.github.com/users/lemire"},
|
||||
cpr::Header{{"User-Agent", "simdjson-legacy-demo"}}
|
||||
);
|
||||
|
||||
if (response.status_code != 200) {
|
||||
std::cerr << "❌ Failed to fetch data: HTTP " << response.status_code << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
// 😓 The old way - manual parsing with lots of code
|
||||
GitHubUser user = parse_github_user(response.text);
|
||||
|
||||
// Display results
|
||||
std::cout << "GitHub User: " << user.name << " (@" << user.login << ")\n";
|
||||
std::cout << "ID: " << user.id << "\n";
|
||||
if (user.company) std::cout << "Company: " << *user.company << "\n";
|
||||
if (user.location) std::cout << "Location: " << *user.location << "\n";
|
||||
std::cout << "Public Repos: " << user.public_repos << "\n";
|
||||
std::cout << "Followers: " << user.followers << "\n";
|
||||
|
||||
std::cout << "\n⚠️ Notice all the manual parsing code required!\n";
|
||||
|
||||
} catch (const simdjson::simdjson_error& e) {
|
||||
std::cerr << "❌ Parsing error: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
// Legacy approach with performance measurement
|
||||
#include <simdjson.h>
|
||||
#include <cpr/cpr.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
struct GitHubUser {
|
||||
std::string login;
|
||||
int64_t id;
|
||||
std::string name;
|
||||
std::optional<std::string> company;
|
||||
std::optional<std::string> location;
|
||||
int64_t public_repos;
|
||||
int64_t followers;
|
||||
};
|
||||
|
||||
// Manual parsing function (the old way)
|
||||
GitHubUser parse_github_user(const std::string& json_str) {
|
||||
GitHubUser user;
|
||||
|
||||
simdjson::ondemand::parser parser;
|
||||
simdjson::padded_string json(json_str);
|
||||
simdjson::ondemand::document doc = parser.iterate(json);
|
||||
|
||||
// Manual field extraction with error checking
|
||||
user.login = std::string(doc["login"].get_string().value());
|
||||
user.id = doc["id"].get_int64().value();
|
||||
user.name = std::string(doc["name"].get_string().value());
|
||||
|
||||
// Handle optional fields
|
||||
auto company_result = doc["company"];
|
||||
if (!company_result.is_null()) {
|
||||
user.company = std::string(company_result.get_string().value());
|
||||
}
|
||||
|
||||
auto location_result = doc["location"];
|
||||
if (!location_result.is_null()) {
|
||||
user.location = std::string(location_result.get_string().value());
|
||||
}
|
||||
|
||||
user.public_repos = doc["public_repos"].get_int64().value();
|
||||
user.followers = doc["followers"].get_int64().value();
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
// Manual serialization function (the old way)
|
||||
std::string serialize_github_user(const GitHubUser& user) {
|
||||
std::ostringstream json;
|
||||
json << "{";
|
||||
json << "\"login\":\"" << user.login << "\",";
|
||||
json << "\"id\":" << user.id << ",";
|
||||
json << "\"name\":\"" << user.name << "\",";
|
||||
|
||||
if (user.company.has_value()) {
|
||||
json << "\"company\":\"" << *user.company << "\",";
|
||||
} else {
|
||||
json << "\"company\":null,";
|
||||
}
|
||||
|
||||
if (user.location.has_value()) {
|
||||
json << "\"location\":\"" << *user.location << "\",";
|
||||
} else {
|
||||
json << "\"location\":null,";
|
||||
}
|
||||
|
||||
json << "\"public_repos\":" << user.public_repos << ",";
|
||||
json << "\"followers\":" << user.followers;
|
||||
json << "}";
|
||||
|
||||
return json.str();
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "📚 Legacy Approach - Deserialization Performance Benchmark\n";
|
||||
std::cout << "=========================================================\n\n";
|
||||
|
||||
// Fetch data from GitHub API
|
||||
auto response = cpr::Get(
|
||||
cpr::Url{"https://api.github.com/users/lemire"},
|
||||
cpr::Header{{"User-Agent", "simdjson-benchmark"}}
|
||||
);
|
||||
|
||||
if (response.status_code != 200) {
|
||||
std::cerr << "❌ Failed to fetch data: HTTP " << response.status_code << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Warm up
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
auto user = parse_github_user(response.text);
|
||||
}
|
||||
|
||||
// Benchmark
|
||||
const int iterations = 10000;
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
for (int i = 0; i < iterations; ++i) {
|
||||
auto user = parse_github_user(response.text);
|
||||
// Prevent optimization
|
||||
if (i == 0) {
|
||||
std::cout << "Parsing: " << user.name << " (@" << user.login << ")\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
|
||||
// Calculate performance metrics
|
||||
double time_per_parse = duration.count() / static_cast<double>(iterations);
|
||||
double bytes_per_parse = response.text.size();
|
||||
double gb_per_second = (bytes_per_parse * iterations) / (duration.count() * 1000.0);
|
||||
|
||||
std::cout << "📊 Deserialization Performance Results:\n";
|
||||
std::cout << " • JSON size: " << bytes_per_parse << " bytes\n";
|
||||
std::cout << " • Iterations: " << iterations << "\n";
|
||||
std::cout << " • Total time: " << duration.count() / 1000.0 << " ms\n";
|
||||
std::cout << " • Time per deserialization: " << std::fixed << std::setprecision(2) << time_per_parse << " μs\n";
|
||||
std::cout << " • Deserialization speed: " << std::fixed << std::setprecision(2) << gb_per_second << " GB/s\n";
|
||||
|
||||
// Now benchmark serialization
|
||||
std::cout << "\n📝 Serialization Performance Benchmark\n";
|
||||
std::cout << "=====================================\n\n";
|
||||
|
||||
// Parse once to get a user object
|
||||
auto user = parse_github_user(response.text);
|
||||
|
||||
// Warm up serialization
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
auto json_str = serialize_github_user(user);
|
||||
}
|
||||
|
||||
// Benchmark serialization
|
||||
start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
for (int i = 0; i < iterations; ++i) {
|
||||
auto json_str = serialize_github_user(user);
|
||||
// Prevent optimization
|
||||
if (i == 0) {
|
||||
std::cout << "Serialized JSON size: " << json_str.length() << " bytes\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
end = std::chrono::high_resolution_clock::now();
|
||||
duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
|
||||
// Calculate serialization performance metrics
|
||||
time_per_parse = duration.count() / static_cast<double>(iterations);
|
||||
double serialized_size = serialize_github_user(user).length();
|
||||
gb_per_second = (serialized_size * iterations) / (duration.count() * 1000.0);
|
||||
|
||||
std::cout << "📊 Serialization Performance Results:\n";
|
||||
std::cout << " • JSON size: " << serialized_size << " bytes\n";
|
||||
std::cout << " • Iterations: " << iterations << "\n";
|
||||
std::cout << " • Total time: " << duration.count() / 1000.0 << " ms\n";
|
||||
std::cout << " • Time per serialization: " << std::fixed << std::setprecision(2) << time_per_parse << " μs\n";
|
||||
std::cout << " • Serialization speed: " << std::fixed << std::setprecision(2) << gb_per_second << " GB/s\n";
|
||||
std::cout << "\n⚠️ Note: Manual serialization with string concatenation\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// Modern approach - C++26 reflection with simdjson
|
||||
// Compile with Bloomberg clang fork:
|
||||
// clang++ -std=c++26 -freflection -DSIMDJSON_STATIC_REFLECTION=1 ...
|
||||
#include <simdjson.h>
|
||||
#include <simdjson/convert.h>
|
||||
#include <cpr/cpr.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
// 🎯 JUST DECLARE YOUR STRUCT - THAT'S IT!
|
||||
// No serialization code needed with C++26 reflection
|
||||
struct GitHubUser {
|
||||
std::string login;
|
||||
int64_t id;
|
||||
std::string name;
|
||||
std::optional<std::string> company;
|
||||
std::optional<std::string> location;
|
||||
int64_t public_repos;
|
||||
int64_t followers;
|
||||
};
|
||||
// NO BOILERPLATE CODE NEEDED! 🎉
|
||||
|
||||
int main() {
|
||||
std::cout << "✨ Modern Approach - C++26 Reflection\n";
|
||||
std::cout << "=====================================\n\n";
|
||||
|
||||
// Fetch data from GitHub API
|
||||
auto response = cpr::Get(
|
||||
cpr::Url{"https://api.github.com/users/lemire"},
|
||||
cpr::Header{{"User-Agent", "simdjson-modern-demo"}}
|
||||
);
|
||||
|
||||
if (response.status_code != 200) {
|
||||
std::cerr << "❌ Failed to fetch data: HTTP " << response.status_code << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
// ✨ THE MAGIC - Just one line, no boilerplate!
|
||||
// C++26 reflection handles everything automatically
|
||||
GitHubUser user = simdjson::from(simdjson::padded_string(response.text));
|
||||
|
||||
// Display results
|
||||
std::cout << "GitHub User: " << user.name << " (@" << user.login << ")\n";
|
||||
std::cout << "ID: " << user.id << "\n";
|
||||
if (user.company) std::cout << "Company: " << *user.company << "\n";
|
||||
if (user.location) std::cout << "Location: " << *user.location << "\n";
|
||||
std::cout << "Public Repos: " << user.public_repos << "\n";
|
||||
std::cout << "Followers: " << user.followers << "\n";
|
||||
|
||||
std::cout << "\n🚀 That's it! No manual parsing code needed!\n";
|
||||
std::cout << " C++26 reflection generates everything automatically.\n";
|
||||
|
||||
} catch (const simdjson::simdjson_error& e) {
|
||||
std::cerr << "❌ Parsing error: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// Modern approach with performance measurement - C++26 reflection
|
||||
#include <simdjson.h>
|
||||
#include <simdjson/convert.h>
|
||||
#include <cpr/cpr.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
// Just declare your struct - reflection handles the rest!
|
||||
struct GitHubUser {
|
||||
std::string login;
|
||||
int64_t id;
|
||||
std::string name;
|
||||
std::optional<std::string> company;
|
||||
std::optional<std::string> location;
|
||||
int64_t public_repos;
|
||||
int64_t followers;
|
||||
};
|
||||
|
||||
int main() {
|
||||
std::cout << "✨ Modern Approach - Deserialization Performance Benchmark (C++26)\n";
|
||||
std::cout << "================================================================\n\n";
|
||||
|
||||
// Fetch data from GitHub API
|
||||
auto response = cpr::Get(
|
||||
cpr::Url{"https://api.github.com/users/lemire"},
|
||||
cpr::Header{{"User-Agent", "simdjson-benchmark"}}
|
||||
);
|
||||
|
||||
if (response.status_code != 200) {
|
||||
std::cerr << "❌ Failed to fetch data: HTTP " << response.status_code << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Warm up
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
GitHubUser user = simdjson::from(simdjson::padded_string(response.text));
|
||||
}
|
||||
|
||||
// Benchmark
|
||||
const int iterations = 10000;
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
for (int i = 0; i < iterations; ++i) {
|
||||
GitHubUser user = simdjson::from(simdjson::padded_string(response.text));
|
||||
// Prevent optimization
|
||||
if (i == 0) {
|
||||
std::cout << "Parsing: " << user.name << " (@" << user.login << ")\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
|
||||
// Calculate performance metrics
|
||||
double time_per_parse = duration.count() / static_cast<double>(iterations);
|
||||
double bytes_per_parse = response.text.size();
|
||||
double gb_per_second = (bytes_per_parse * iterations) / (duration.count() * 1000.0);
|
||||
|
||||
std::cout << "📊 Deserialization Performance Results:\n";
|
||||
std::cout << " • JSON size: " << bytes_per_parse << " bytes\n";
|
||||
std::cout << " • Iterations: " << iterations << "\n";
|
||||
std::cout << " • Total time: " << duration.count() / 1000.0 << " ms\n";
|
||||
std::cout << " • Time per deserialization: " << std::fixed << std::setprecision(2) << time_per_parse << " μs\n";
|
||||
std::cout << " • Deserialization speed: " << std::fixed << std::setprecision(2) << gb_per_second << " GB/s\n";
|
||||
|
||||
std::cout << "\n🚀 Same performance, just ONE line of deserialization code!\n";
|
||||
|
||||
// Serialization note
|
||||
std::cout << "\n📝 Serialization with Reflection\n";
|
||||
std::cout << "================================\n\n";
|
||||
std::cout << "⚠️ NOTE: simdjson doesn't yet support reflection-based serialization.\n";
|
||||
std::cout << " The `simdjson::to` function is not yet implemented.\n";
|
||||
std::cout << " This is a future enhancement that would provide:\n";
|
||||
std::cout << " • One-line serialization: simdjson::to<std::string>(user)\n";
|
||||
std::cout << " • Automatic JSON generation from C++ structs\n";
|
||||
std::cout << " • No manual string building required\n";
|
||||
std::cout << "\n";
|
||||
std::cout << " For now, serialization still requires manual implementation,\n";
|
||||
std::cout << " but deserialization is fully automated with reflection! 🎉\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include "simdjson.h"
|
||||
#include <cpr/cpr.h>
|
||||
|
||||
struct GitHubUser {
|
||||
std::string login;
|
||||
std::optional<std::string> name;
|
||||
std::optional<std::string> company;
|
||||
std::optional<std::string> blog;
|
||||
std::optional<std::string> location;
|
||||
std::optional<std::string> email;
|
||||
std::optional<std::string> bio;
|
||||
int64_t public_repos;
|
||||
int64_t followers;
|
||||
int64_t following;
|
||||
};
|
||||
|
||||
int main() {
|
||||
std::cout << "🔄 C++26 Reflection: Complete JSON Round-Trip Demo\n";
|
||||
std::cout << "================================================\n\n";
|
||||
|
||||
// Step 1: Fetch real data from GitHub
|
||||
std::cout << "📡 Fetching GitHub user data...\n";
|
||||
auto response = cpr::Get(cpr::Url{"https://api.github.com/users/simdjson"});
|
||||
|
||||
if (response.status_code != 200) {
|
||||
std::cerr << "Error: Failed to fetch data\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "✅ Received " << response.text.length() << " bytes of JSON\n\n";
|
||||
|
||||
// Step 2: Deserialize with ONE LINE
|
||||
std::cout << "📥 DESERIALIZATION (JSON → Struct)\n";
|
||||
std::cout << "Code: GitHubUser user = simdjson::from(simdjson::padded_string(response.text));\n\n";
|
||||
|
||||
GitHubUser user = simdjson::from(simdjson::padded_string(response.text));
|
||||
|
||||
// Show the data we parsed
|
||||
std::cout << "Parsed data:\n";
|
||||
std::cout << " • Login: " << user.login << "\n";
|
||||
std::cout << " • Name: " << (user.name.has_value() ? *user.name : "<not set>") << "\n";
|
||||
std::cout << " • Company: " << (user.company.has_value() ? *user.company : "<not set>") << "\n";
|
||||
std::cout << " • Location: " << (user.location.has_value() ? *user.location : "<not set>") << "\n";
|
||||
std::cout << " • Bio: " << (user.bio.has_value() ? *user.bio : "<not set>") << "\n";
|
||||
std::cout << " • Repos: " << user.public_repos << "\n";
|
||||
std::cout << " • Followers: " << user.followers << "\n\n";
|
||||
|
||||
// Step 3: Modify the data
|
||||
std::cout << "✏️ Modifying data...\n";
|
||||
user.followers += 1000; // Wishful thinking!
|
||||
user.name = "simdjson - JSON at the speed of light"; // Set a name
|
||||
user.bio = user.bio.value_or("") + " (Now with C++26 reflection!)";
|
||||
std::cout << " • Added 1000 followers (we can dream!)\n";
|
||||
std::cout << " • Set organization name\n";
|
||||
std::cout << " • Updated bio\n\n";
|
||||
|
||||
// Step 4: Serialize back to JSON with ONE LINE
|
||||
std::cout << "📤 SERIALIZATION (Struct → JSON)\n";
|
||||
std::cout << "Code: std::string json = simdjson::builder::to_json_string(user);\n\n";
|
||||
|
||||
auto json_result = simdjson::builder::to_json_string(user);
|
||||
if (json_result.error()) {
|
||||
std::cerr << "Serialization error!\n";
|
||||
return 1;
|
||||
}
|
||||
std::string json = json_result.value();
|
||||
|
||||
std::cout << "Generated JSON (" << json.length() << " bytes):\n";
|
||||
// Pretty print first 200 chars
|
||||
if (json.length() > 200) {
|
||||
std::cout << json.substr(0, 200) << "...\n\n";
|
||||
} else {
|
||||
std::cout << json << "\n\n";
|
||||
}
|
||||
|
||||
// Step 5: Verify round-trip
|
||||
std::cout << "🔄 ROUND-TRIP VERIFICATION\n";
|
||||
std::cout << "Parsing our generated JSON back...\n";
|
||||
|
||||
GitHubUser user2 = simdjson::from(simdjson::padded_string(json));
|
||||
|
||||
std::cout << "✅ Round-trip successful!\n";
|
||||
std::cout << " • Original followers: " << user.followers << "\n";
|
||||
std::cout << " • Parsed followers: " << user2.followers << "\n";
|
||||
std::cout << " • Name set correctly: " << (user2.name.has_value() && *user2.name == *user.name ? "YES" : "NO") << "\n";
|
||||
std::cout << " • Bio preserved: " << (user2.bio.has_value() ? "YES" : "NO") << "\n\n";
|
||||
|
||||
std::cout << "🎉 That's it! Two lines of code for complete JSON handling:\n";
|
||||
std::cout << " • Deserialization: simdjson::from(...)\n";
|
||||
std::cout << " • Serialization: simdjson::builder::to_json_string(...)\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// Complete JSON Round-Trip Demo with C++26 Reflection
|
||||
// Compile with: clang++ -std=c++26 -freflection -DSIMDJSON_STATIC_REFLECTION=1 reflection_roundtrip_demo.cpp ../singleheader/simdjson.cpp -o reflection_roundtrip_demo
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include "simdjson.h"
|
||||
|
||||
// Define our data structure - just a plain struct!
|
||||
struct Product {
|
||||
std::string name;
|
||||
double price;
|
||||
std::vector<std::string> tags;
|
||||
std::optional<std::string> description;
|
||||
bool in_stock;
|
||||
};
|
||||
|
||||
int main() {
|
||||
std::cout << "🔄 simdjson C++26 Reflection Round-Trip Demo\n";
|
||||
std::cout << "============================================\n\n";
|
||||
|
||||
// Original JSON data
|
||||
const char* json_data = R"({
|
||||
"name": "High-Performance JSON Parser",
|
||||
"price": 0.0,
|
||||
"tags": ["C++", "performance", "json", "reflection"],
|
||||
"description": "The fastest JSON parser with C++26 reflection support",
|
||||
"in_stock": true
|
||||
})";
|
||||
|
||||
std::cout << "📄 Original JSON:\n" << json_data << "\n\n";
|
||||
|
||||
// Step 1: Deserialize JSON to struct with ONE line
|
||||
std::cout << "📥 Deserializing JSON → Struct...\n";
|
||||
Product product = simdjson::from(simdjson::padded_string(json_data));
|
||||
|
||||
std::cout << "✅ Deserialized successfully!\n";
|
||||
std::cout << " • Name: " << product.name << "\n";
|
||||
std::cout << " • Price: $" << product.price << "\n";
|
||||
std::cout << " • Tags: ";
|
||||
for (const auto& tag : product.tags) {
|
||||
std::cout << tag << " ";
|
||||
}
|
||||
std::cout << "\n • In stock: " << (product.in_stock ? "Yes" : "No") << "\n\n";
|
||||
|
||||
// Step 2: Modify the data
|
||||
std::cout << "✏️ Modifying data...\n";
|
||||
product.price = 99.99; // It's worth something now!
|
||||
product.tags.push_back("C++26");
|
||||
product.description = "Now with serialization support!";
|
||||
std::cout << " • Changed price to $99.99\n";
|
||||
std::cout << " • Added 'C++26' tag\n";
|
||||
std::cout << " • Updated description\n\n";
|
||||
|
||||
// Step 3: Serialize struct back to JSON with ONE line
|
||||
std::cout << "📤 Serializing Struct → JSON...\n";
|
||||
auto json_result = simdjson::builder::to_json_string(product);
|
||||
|
||||
if (json_result.error()) {
|
||||
std::cerr << "❌ Serialization failed!\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string new_json = json_result.value();
|
||||
std::cout << "✅ Serialized successfully!\n\n";
|
||||
|
||||
std::cout << "📄 Generated JSON:\n" << new_json << "\n\n";
|
||||
|
||||
// Step 4: Verify round-trip by parsing again
|
||||
std::cout << "🔍 Verifying round-trip...\n";
|
||||
Product product2 = simdjson::from(simdjson::padded_string(new_json));
|
||||
|
||||
std::cout << "✅ Round-trip successful!\n";
|
||||
std::cout << " • Price preserved: $" << product2.price << "\n";
|
||||
std::cout << " • New tag present: "
|
||||
<< (std::find(product2.tags.begin(), product2.tags.end(), "C++26") != product2.tags.end() ? "Yes" : "No")
|
||||
<< "\n";
|
||||
std::cout << " • Description updated: " << (product2.description.has_value() ? "Yes" : "No") << "\n\n";
|
||||
|
||||
std::cout << "🎉 That's it! Complete JSON handling in just 2 lines:\n";
|
||||
std::cout << " • Deserialize: auto obj = simdjson::from(json);\n";
|
||||
std::cout << " • Serialize: auto json = simdjson::builder::to_json_string(obj);\n\n";
|
||||
|
||||
std::cout << "⚡ No manual parsing, no boilerplate, just pure C++26 magic!\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
#include "simdjson.h"
|
||||
#include <map>
|
||||
|
||||
struct TestData {
|
||||
std::string name;
|
||||
std::vector<int> numbers;
|
||||
std::map<std::string, double> metrics;
|
||||
bool active;
|
||||
std::optional<std::string> description;
|
||||
};
|
||||
|
||||
int main() {
|
||||
std::cout << "⚡ Serialization Performance Benchmark\n";
|
||||
std::cout << "=====================================\n\n";
|
||||
|
||||
// Create test data
|
||||
TestData data{
|
||||
.name = "Performance Test",
|
||||
.numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
||||
.metrics = {{"latency", 1.23}, {"throughput", 456.78}, {"cpu", 89.01}},
|
||||
.active = true,
|
||||
.description = "Testing reflection-based serialization performance"
|
||||
};
|
||||
|
||||
const int iterations = 100000;
|
||||
|
||||
// Benchmark serialization
|
||||
std::cout << "📤 Benchmarking serialization (" << iterations << " iterations)...\n";
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
std::string json;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
auto result = simdjson::builder::to_json_string(data);
|
||||
if (i == 0 && !result.error()) {
|
||||
json = result.value();
|
||||
}
|
||||
}
|
||||
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
|
||||
std::cout << "\nResults:\n";
|
||||
std::cout << " • Generated JSON size: " << json.length() << " bytes\n";
|
||||
std::cout << " • Total time: " << duration.count() / 1000.0 << " ms\n";
|
||||
|
||||
double time_per_iteration_us = duration.count() / double(iterations);
|
||||
double time_per_iteration_s = time_per_iteration_us / 1000000.0;
|
||||
double bytes_per_second = json.length() / time_per_iteration_s;
|
||||
double mb_per_second = bytes_per_second / (1024.0 * 1024.0);
|
||||
|
||||
std::cout << " • Time per serialization: " << std::fixed << std::setprecision(2) << time_per_iteration_us << " μs\n";
|
||||
std::cout << " • Throughput: " << std::fixed << std::setprecision(2) << mb_per_second << " MB/s\n";
|
||||
|
||||
std::cout << "\nGenerated JSON:\n" << json << "\n\n";
|
||||
|
||||
// Benchmark deserialization for comparison
|
||||
std::cout << "📥 Benchmarking deserialization (for comparison)...\n";
|
||||
|
||||
simdjson::padded_string padded_json(json);
|
||||
start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
TestData parsed = simdjson::from(padded_json);
|
||||
}
|
||||
|
||||
end = std::chrono::high_resolution_clock::now();
|
||||
duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
|
||||
std::cout << "\nResults:\n";
|
||||
time_per_iteration_us = duration.count() / double(iterations);
|
||||
time_per_iteration_s = time_per_iteration_us / 1000000.0;
|
||||
bytes_per_second = json.length() / time_per_iteration_s;
|
||||
mb_per_second = bytes_per_second / (1024.0 * 1024.0);
|
||||
|
||||
std::cout << " • Time per deserialization: " << std::fixed << std::setprecision(2) << time_per_iteration_us << " μs\n";
|
||||
std::cout << " • Throughput: " << std::fixed << std::setprecision(2) << mb_per_second << " MB/s\n";
|
||||
|
||||
std::cout << "\n✅ Both serialization and deserialization work with reflection!\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -53,4 +53,5 @@
|
||||
|
||||
#include "simdjson/dom.h"
|
||||
#include "simdjson/ondemand.h"
|
||||
#include "simdjson/convert.h"
|
||||
#endif // SIMDJSON_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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -115,7 +115,6 @@ concept optional_type = requires(std::remove_cvref_t<T> obj) {
|
||||
{ obj.value() } -> std::same_as<typename std::remove_cvref_t<T>::value_type&>;
|
||||
requires requires(typename std::remove_cvref_t<T>::value_type &&val) {
|
||||
obj.emplace(std::move(val));
|
||||
obj = std::move(val);
|
||||
{
|
||||
obj.value_or(val)
|
||||
} -> std::convertible_to<typename std::remove_cvref_t<T>::value_type>;
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
#ifndef SIMDJSON_CONVERT_H
|
||||
#define SIMDJSON_CONVERT_H
|
||||
#if __cpp_concepts
|
||||
|
||||
#include "simdjson/ondemand.h"
|
||||
#include <optional>
|
||||
#ifdef __cpp_lib_ranges
|
||||
#include <ranges>
|
||||
#endif
|
||||
|
||||
namespace simdjson {
|
||||
|
||||
struct [[nodiscard]] auto_iterator_end {};
|
||||
|
||||
/**
|
||||
* A Wrapper for simdjson_result<ondemand::array_iterator> in order to make it
|
||||
* compatible with ranges (to satisfy std::ranges::input_range).
|
||||
*/
|
||||
struct [[nodiscard]] auto_iterator {
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using type = simdjson_result<ondemand::array_iterator>;
|
||||
using value_type = simdjson_result<ondemand::value>; // type::value_type
|
||||
using reference = value_type &;
|
||||
using const_reference = const value_type &;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
struct auto_iterator_storage {
|
||||
type m_iter{};
|
||||
mutable value_type m_value{};
|
||||
};
|
||||
|
||||
private:
|
||||
auto_iterator_storage *m_storage = nullptr;
|
||||
|
||||
public:
|
||||
constexpr auto_iterator() noexcept = default;
|
||||
explicit auto_iterator(auto_iterator_storage &storage) noexcept
|
||||
: m_storage{&storage} {};
|
||||
auto_iterator(auto_iterator const &) = default;
|
||||
auto_iterator(auto_iterator &&) = default;
|
||||
auto_iterator &operator=(auto_iterator const &) = default;
|
||||
auto_iterator &operator=(auto_iterator &&) noexcept = default;
|
||||
~auto_iterator() = default;
|
||||
|
||||
reference operator*() const noexcept { return m_storage->m_value; }
|
||||
reference operator*() noexcept { return m_storage->m_value; }
|
||||
|
||||
auto_iterator &operator++() noexcept {
|
||||
++m_storage->m_iter;
|
||||
m_storage->m_value =
|
||||
m_storage->m_iter.at_end() || m_storage->m_iter.error() != SUCCESS
|
||||
? value_type{}
|
||||
: *m_storage->m_iter;
|
||||
return *this;
|
||||
}
|
||||
auto_iterator operator++(int) noexcept {
|
||||
auto_iterator const tmp = *this;
|
||||
operator++();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(auto_iterator const &other) const noexcept {
|
||||
return m_storage == other.m_storage &&
|
||||
m_storage->m_iter == other.m_storage->m_iter;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(auto_iterator_end) const noexcept {
|
||||
return m_storage != nullptr && m_storage->m_iter.at_end();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ParserType = ondemand::parser>
|
||||
struct [[nodiscard]] auto_parser
|
||||
#if __cpp_lib_ranges
|
||||
: std::ranges::view_interface<auto_parser<ParserType>>
|
||||
#endif
|
||||
{
|
||||
using value_type = simdjson_result<ondemand::value>;
|
||||
using size_type = size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type *;
|
||||
using const_pointer = const value_type *;
|
||||
using reference = value_type &;
|
||||
using const_reference = const value_type &;
|
||||
using iterator = auto_iterator;
|
||||
using const_iterator = auto_iterator; // auto_iterator is already const
|
||||
|
||||
private:
|
||||
ParserType m_parser;
|
||||
ondemand::document m_doc;
|
||||
error_code m_error{SUCCESS};
|
||||
|
||||
// Caching the iterator here:
|
||||
iterator::auto_iterator_storage iter_storage{};
|
||||
|
||||
template <typename T>
|
||||
static constexpr bool is_nothrow_gettable = requires(ondemand::document doc) {
|
||||
{ doc.get<T>() } noexcept;
|
||||
};
|
||||
|
||||
public:
|
||||
// non-pointer constructors:
|
||||
explicit auto_parser(ParserType &&parser, ondemand::document &&doc) noexcept
|
||||
requires(!std::is_pointer_v<ParserType>)
|
||||
: m_parser{std::move(parser)}, m_doc{std::move(doc)} {}
|
||||
|
||||
explicit auto_parser(ParserType &&parser,
|
||||
padded_string_view const str) noexcept
|
||||
requires(!std::is_pointer_v<ParserType>)
|
||||
: m_parser{std::move(parser)}, m_doc{}, m_error{SUCCESS} {
|
||||
m_error = m_parser.iterate(str).get(m_doc);
|
||||
}
|
||||
|
||||
explicit auto_parser(padded_string_view const str) noexcept
|
||||
requires(!std::is_pointer_v<ParserType>)
|
||||
: auto_parser{ParserType{}, str} {}
|
||||
|
||||
// pointer constructors:
|
||||
explicit auto_parser(std::remove_pointer_t<ParserType> &parser,
|
||||
ondemand::document &&doc) noexcept
|
||||
requires(std::is_pointer_v<ParserType>)
|
||||
: m_parser{&parser}, m_doc{std::move(doc)} {}
|
||||
|
||||
explicit auto_parser(std::remove_pointer_t<ParserType> &parser,
|
||||
padded_string_view const str) noexcept
|
||||
requires(std::is_pointer_v<ParserType>)
|
||||
: m_parser{&parser}, m_doc{}, m_error{SUCCESS} {
|
||||
m_error = m_parser->iterate(str).get(m_doc);
|
||||
}
|
||||
|
||||
explicit auto_parser(ParserType parser, ondemand::document &&doc) noexcept
|
||||
requires(std::is_pointer_v<ParserType>)
|
||||
: auto_parser{*parser, std::move(doc)} {}
|
||||
|
||||
auto_parser(auto_parser const &) = delete;
|
||||
auto_parser &operator=(auto_parser const &) = delete;
|
||||
auto_parser(auto_parser &&) noexcept = default;
|
||||
auto_parser &operator=(auto_parser &&) noexcept = default;
|
||||
~auto_parser() = default;
|
||||
|
||||
/// Get the parser
|
||||
[[nodiscard]] std::remove_pointer_t<ParserType> &parser() noexcept {
|
||||
if constexpr (std::is_pointer_v<ParserType>) {
|
||||
return *m_parser;
|
||||
} else {
|
||||
return m_parser;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] simdjson_inline simdjson_result<T>
|
||||
result() noexcept(is_nothrow_gettable<T>) {
|
||||
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<T>();
|
||||
}
|
||||
|
||||
[[nodiscard]] simdjson_inline simdjson_result<ondemand::array>
|
||||
array() noexcept {
|
||||
return result<ondemand::array>();
|
||||
}
|
||||
|
||||
[[nodiscard]] simdjson_inline simdjson_result<ondemand::object>
|
||||
object() noexcept {
|
||||
return result<ondemand::object>();
|
||||
}
|
||||
|
||||
[[nodiscard]] simdjson_inline simdjson_result<ondemand::number>
|
||||
number() noexcept {
|
||||
return result<ondemand::number>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] simdjson_inline explicit(false)
|
||||
operator simdjson_result<T>() noexcept(is_nothrow_gettable<T>) {
|
||||
return result<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] simdjson_inline explicit(false) operator T() noexcept(false) {
|
||||
if (m_error != SUCCESS) {
|
||||
throw simdjson_error(m_error);
|
||||
}
|
||||
return m_doc.get<T>();
|
||||
}
|
||||
|
||||
// We can't have "operator std::optional<T>" because it would create an
|
||||
// ambiguity for the compiler.
|
||||
// We also cannot have "operator T*" without manual memory management.
|
||||
// We also cannot have "operator T&" without manual memory management either.
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] simdjson_inline std::optional<T>
|
||||
optional() noexcept(is_nothrow_gettable<T>) {
|
||||
if (m_error != SUCCESS) {
|
||||
return std::nullopt;
|
||||
}
|
||||
T value;
|
||||
// For std::optional<T>
|
||||
if (m_doc.get<T>().get(value)) [[unlikely]] {
|
||||
return std::nullopt;
|
||||
}
|
||||
return {std::move(value)};
|
||||
}
|
||||
|
||||
simdjson_inline auto_iterator begin() noexcept {
|
||||
if (m_error != SUCCESS) {
|
||||
// Create an iterator with the error
|
||||
iter_storage.m_iter = iterator::type(m_error);
|
||||
iter_storage.m_value = value_type{};
|
||||
return auto_iterator{iter_storage};
|
||||
}
|
||||
if (iter_storage.m_iter.error() != SUCCESS &&
|
||||
!iter_storage.m_iter.at_end()) {
|
||||
// Try to get the document as an array
|
||||
ondemand::array arr;
|
||||
if(auto error = m_doc.get_array().get(arr); error == SUCCESS) {
|
||||
iter_storage = {.m_iter = iterator::type{arr.begin()},
|
||||
.m_value = iterator::value_type{
|
||||
iter_storage.m_iter.at_end() ||
|
||||
iter_storage.m_iter.error() != SUCCESS
|
||||
? value_type{}
|
||||
: *iter_storage.m_iter}};
|
||||
} else {
|
||||
// If it's not an array, create an error iterator
|
||||
iter_storage.m_iter = iterator::type(error);
|
||||
iter_storage.m_value = value_type{};
|
||||
}
|
||||
}
|
||||
return auto_iterator{iter_storage};
|
||||
}
|
||||
simdjson_inline auto_iterator_end end() noexcept { return {}; }
|
||||
};
|
||||
|
||||
#ifdef __cpp_lib_ranges
|
||||
|
||||
// For C++20, we implement our own pipe operator since range_adaptor_closure is C++23
|
||||
static constexpr struct [[nodiscard]] no_errors_adaptor {
|
||||
|
||||
[[nodiscard]] bool
|
||||
operator()(simdjson_result<ondemand::value> const &val) const noexcept {
|
||||
return val.error() == SUCCESS;
|
||||
}
|
||||
|
||||
template <std::ranges::range Range>
|
||||
auto operator()(Range &&rng) const noexcept {
|
||||
return std::forward<Range>(rng) | std::views::filter(*this);
|
||||
}
|
||||
} no_errors;
|
||||
|
||||
template <typename T = void>
|
||||
struct [[nodiscard]] to_adaptor {
|
||||
|
||||
/// Convert to T
|
||||
[[nodiscard]] T
|
||||
operator()(simdjson_result<ondemand::value> &val) const noexcept {
|
||||
return val.get<T>();
|
||||
}
|
||||
|
||||
/// Make it an adaptor
|
||||
template <std::ranges::range Range>
|
||||
auto operator()(Range &&rng) const noexcept {
|
||||
return std::forward<Range>(rng) | no_errors | std::views::transform(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse input string into any object if possible.
|
||||
*/
|
||||
auto operator()(padded_string_view const str) const noexcept {
|
||||
return auto_parser{str};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the input using the specified parser into any object if possible.
|
||||
*/
|
||||
auto operator()(ondemand::parser &parser,
|
||||
padded_string_view const str) const noexcept {
|
||||
return auto_parser<ondemand::parser *>{parser, str};
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> static constexpr to_adaptor<T> to{};
|
||||
|
||||
static constexpr to_adaptor<> from{};
|
||||
|
||||
template <typename T = void>
|
||||
using as = to_adaptor<T>;
|
||||
|
||||
// For C++20 ranges without range_adaptor_closure, we need to define pipe operators
|
||||
template <std::ranges::range Range>
|
||||
inline auto operator|(Range&& range, const no_errors_adaptor& adaptor) {
|
||||
return adaptor(std::forward<Range>(range));
|
||||
}
|
||||
|
||||
template <std::ranges::range Range, typename T>
|
||||
inline auto operator|(Range&& range, const to_adaptor<T>& adaptor) {
|
||||
return adaptor(std::forward<Range>(range));
|
||||
}
|
||||
|
||||
#endif // __cpp_lib_ranges
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // __cpp_concepts
|
||||
#endif // SIMDJSON_CONVERT_H
|
||||
@@ -3,8 +3,8 @@
|
||||
#define SIMDJSON_SERIALIZATION_INL_H
|
||||
|
||||
#include "simdjson/dom/base.h"
|
||||
#include "simdjson/dom/serialization.h"
|
||||
#include "simdjson/dom/parser.h"
|
||||
#include "simdjson/dom/serialization.h"
|
||||
#include "simdjson/internal/tape_type.h"
|
||||
|
||||
#include "simdjson/dom/array-inl.h"
|
||||
@@ -16,7 +16,9 @@
|
||||
namespace simdjson {
|
||||
namespace dom {
|
||||
inline bool parser::print_json(std::ostream &os) const noexcept {
|
||||
if (!valid) { return false; }
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
simdjson::internal::string_builder<> sb;
|
||||
sb.append(doc.root());
|
||||
std::string_view answer = sb.str();
|
||||
@@ -24,37 +26,51 @@ inline bool parser::print_json(std::ostream &os) const noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::dom::element value) {
|
||||
simdjson::internal::string_builder<> sb;
|
||||
sb.append(value);
|
||||
return (out << sb.str());
|
||||
inline std::ostream &operator<<(std::ostream &out,
|
||||
simdjson::dom::element value) {
|
||||
simdjson::internal::string_builder<> sb;
|
||||
sb.append(value);
|
||||
return (out << sb.str());
|
||||
}
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::dom::element> x) {
|
||||
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
|
||||
return (out << x.value());
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream &out,
|
||||
simdjson::simdjson_result<simdjson::dom::element> x) {
|
||||
if (x.error()) {
|
||||
throw simdjson::simdjson_error(x.error());
|
||||
}
|
||||
return (out << x.value());
|
||||
}
|
||||
#endif
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::dom::array value) {
|
||||
simdjson::internal::string_builder<> sb;
|
||||
sb.append(value);
|
||||
return (out << sb.str());
|
||||
inline std::ostream &operator<<(std::ostream &out, simdjson::dom::array value) {
|
||||
simdjson::internal::string_builder<> sb;
|
||||
sb.append(value);
|
||||
return (out << sb.str());
|
||||
}
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::dom::array> x) {
|
||||
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
|
||||
return (out << x.value());
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream &out,
|
||||
simdjson::simdjson_result<simdjson::dom::array> x) {
|
||||
if (x.error()) {
|
||||
throw simdjson::simdjson_error(x.error());
|
||||
}
|
||||
return (out << x.value());
|
||||
}
|
||||
#endif
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::dom::object value) {
|
||||
simdjson::internal::string_builder<> sb;
|
||||
sb.append(value);
|
||||
return (out << sb.str());
|
||||
inline std::ostream &operator<<(std::ostream &out,
|
||||
simdjson::dom::object value) {
|
||||
simdjson::internal::string_builder<> sb;
|
||||
sb.append(value);
|
||||
return (out << sb.str());
|
||||
}
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::dom::object> x) {
|
||||
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
|
||||
return (out << x.value());
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream &out,
|
||||
simdjson::simdjson_result<simdjson::dom::object> x) {
|
||||
if (x.error()) {
|
||||
throw simdjson::simdjson_error(x.error());
|
||||
}
|
||||
return (out << x.value());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -69,8 +85,9 @@ namespace {
|
||||
* We expect that most compilers will use 8 bytes for this data structure.
|
||||
**/
|
||||
struct escape_sequence {
|
||||
uint8_t length;
|
||||
const char string[7]; // technically, we only ever need 6 characters, we pad to 8
|
||||
uint8_t length;
|
||||
const char
|
||||
string[7]; // technically, we only ever need 6 characters, we pad to 8
|
||||
};
|
||||
/**@private
|
||||
* This converts a signed integer into a character sequence.
|
||||
@@ -86,7 +103,7 @@ static char *fast_itoa(char *output, int64_t value) noexcept {
|
||||
char buffer[20];
|
||||
uint64_t value_positive;
|
||||
// In general, negating a signed integer is unsafe.
|
||||
if(value < 0) {
|
||||
if (value < 0) {
|
||||
*output++ = '-';
|
||||
// Doing value_positive = -value; while avoiding
|
||||
// undefined behavior warnings.
|
||||
@@ -105,7 +122,7 @@ static char *fast_itoa(char *output, int64_t value) noexcept {
|
||||
// A faster approach is possible if we expect large integers:
|
||||
// unroll the loop (work in 100s, 1000s) and use some kind of
|
||||
// memoization.
|
||||
while(value_positive >= 10) {
|
||||
while (value_positive >= 10) {
|
||||
*write_pointer-- = char('0' + (value_positive % 10));
|
||||
value_positive /= 10;
|
||||
}
|
||||
@@ -131,7 +148,7 @@ static char *fast_itoa(char *output, uint64_t value) noexcept {
|
||||
// A faster approach is possible if we expect large integers:
|
||||
// unroll the loop (work in 100s, 1000s) and use some kind of
|
||||
// memoization.
|
||||
while(value >= 10) {
|
||||
while (value >= 10) {
|
||||
*write_pointer-- = char('0' + (value % 10));
|
||||
value /= 10;
|
||||
};
|
||||
@@ -141,7 +158,6 @@ static char *fast_itoa(char *output, uint64_t value) noexcept {
|
||||
return output + len;
|
||||
}
|
||||
|
||||
|
||||
} // anonymous namespace
|
||||
namespace internal {
|
||||
|
||||
@@ -149,193 +165,208 @@ namespace internal {
|
||||
* Minifier/formatter code.
|
||||
**/
|
||||
|
||||
template<class formatter>
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::number(uint64_t x) {
|
||||
char number_buffer[24];
|
||||
char *newp = fast_itoa(number_buffer, x);
|
||||
buffer.insert(buffer.end(), number_buffer, newp);
|
||||
chars(number_buffer, newp);
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::number(int64_t x) {
|
||||
char number_buffer[24];
|
||||
char *newp = fast_itoa(number_buffer, x);
|
||||
buffer.insert(buffer.end(), number_buffer, newp);
|
||||
chars(number_buffer, newp);
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::number(double x) {
|
||||
char number_buffer[24];
|
||||
// Currently, passing the nullptr to the second argument is
|
||||
// safe because our implementation does not check the second
|
||||
// argument.
|
||||
char *newp = internal::to_chars(number_buffer, nullptr, x);
|
||||
buffer.insert(buffer.end(), number_buffer, newp);
|
||||
chars(number_buffer, newp);
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::start_array() { one_char('['); }
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::start_array() {
|
||||
one_char('[');
|
||||
}
|
||||
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::end_array() {
|
||||
one_char(']');
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::end_array() { one_char(']'); }
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::start_object() {
|
||||
one_char('{');
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::start_object() { one_char('{'); }
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::end_object() {
|
||||
one_char('}');
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::end_object() { one_char('}'); }
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::comma() {
|
||||
one_char(',');
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::comma() { one_char(','); }
|
||||
|
||||
template<class formatter>
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::true_atom() {
|
||||
const char * s = "true";
|
||||
buffer.insert(buffer.end(), s, s + 4);
|
||||
const char *s = "true";
|
||||
chars(s, s + 4);
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::false_atom() {
|
||||
const char * s = "false";
|
||||
buffer.insert(buffer.end(), s, s + 5);
|
||||
const char *s = "false";
|
||||
chars(s, s + 5);
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::null_atom() {
|
||||
const char * s = "null";
|
||||
buffer.insert(buffer.end(), s, s + 4);
|
||||
const char *s = "null";
|
||||
chars(s, s + 4);
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::one_char(char c) { buffer.push_back(c); }
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::one_char(char c) {
|
||||
buffer.push_back(c);
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::key(std::string_view unescaped) {
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::chars(const char *begin,
|
||||
const char *end) {
|
||||
buffer.append(begin, end);
|
||||
}
|
||||
|
||||
template <class formatter>
|
||||
simdjson_inline void
|
||||
base_formatter<formatter>::key(std::string_view unescaped) {
|
||||
string(unescaped);
|
||||
one_char(':');
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::string(std::string_view unescaped) {
|
||||
template <class formatter>
|
||||
simdjson_inline void
|
||||
base_formatter<formatter>::string(std::string_view unescaped) {
|
||||
one_char('\"');
|
||||
size_t i = 0;
|
||||
// Fast path for the case where we have no control character, no ", and no backslash.
|
||||
// This should include most keys.
|
||||
// Fast path for the case where we have no control character, no ", and no
|
||||
// backslash. This should include most keys.
|
||||
//
|
||||
// We would like to use 'bool' but some compilers take offense to bitwise operation
|
||||
// with bool types.
|
||||
constexpr static char needs_escaping[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
for(;i + 8 <= unescaped.length(); i += 8) {
|
||||
// We would like to use 'bool' but some compilers take offense to bitwise
|
||||
// operation with bool types.
|
||||
constexpr static char needs_escaping[] = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
for (; i + 8 <= unescaped.length(); i += 8) {
|
||||
// Poor's man vectorization. This could get much faster if we used SIMD.
|
||||
//
|
||||
// It is not the case that replacing '|' with '||' would be neutral performance-wise.
|
||||
if(needs_escaping[uint8_t(unescaped[i])] | needs_escaping[uint8_t(unescaped[i+1])]
|
||||
| needs_escaping[uint8_t(unescaped[i+2])] | needs_escaping[uint8_t(unescaped[i+3])]
|
||||
| needs_escaping[uint8_t(unescaped[i+4])] | needs_escaping[uint8_t(unescaped[i+5])]
|
||||
| needs_escaping[uint8_t(unescaped[i+6])] | needs_escaping[uint8_t(unescaped[i+7])]
|
||||
) { break; }
|
||||
// It is not the case that replacing '|' with '||' would be neutral
|
||||
// performance-wise.
|
||||
if (needs_escaping[uint8_t(unescaped[i])] |
|
||||
needs_escaping[uint8_t(unescaped[i + 1])] |
|
||||
needs_escaping[uint8_t(unescaped[i + 2])] |
|
||||
needs_escaping[uint8_t(unescaped[i + 3])] |
|
||||
needs_escaping[uint8_t(unescaped[i + 4])] |
|
||||
needs_escaping[uint8_t(unescaped[i + 5])] |
|
||||
needs_escaping[uint8_t(unescaped[i + 6])] |
|
||||
needs_escaping[uint8_t(unescaped[i + 7])]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(;i < unescaped.length(); i++) {
|
||||
if(needs_escaping[uint8_t(unescaped[i])]) { break; }
|
||||
for (; i < unescaped.length(); i++) {
|
||||
if (needs_escaping[uint8_t(unescaped[i])]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// The following is also possible and omits a 256-byte table, but it is slower:
|
||||
// for (; (i < unescaped.length()) && (uint8_t(unescaped[i]) > 0x1F)
|
||||
// The following is also possible and omits a 256-byte table, but it is
|
||||
// slower: for (; (i < unescaped.length()) && (uint8_t(unescaped[i]) > 0x1F)
|
||||
// && (unescaped[i] != '\"') && (unescaped[i] != '\\'); i++) {}
|
||||
|
||||
// At least for long strings, the following should be fast. We could
|
||||
// do better by integrating the checks and the insertion.
|
||||
buffer.insert(buffer.end(), unescaped.data(), unescaped.data() + i);
|
||||
chars(unescaped.data(), unescaped.data() + i);
|
||||
// We caught a control character if we enter this loop (slow).
|
||||
// Note that we are do not restart from the beginning, but rather we continue
|
||||
// from the point where we encountered something that requires escaping.
|
||||
for (; i < unescaped.length(); i++) {
|
||||
switch (unescaped[i]) {
|
||||
case '\"':
|
||||
{
|
||||
const char * s = "\\\"";
|
||||
buffer.insert(buffer.end(), s, s + 2);
|
||||
}
|
||||
break;
|
||||
case '\\':
|
||||
{
|
||||
const char * s = "\\\\";
|
||||
buffer.insert(buffer.end(), s, s + 2);
|
||||
}
|
||||
break;
|
||||
case '\"': {
|
||||
const char *s = "\\\"";
|
||||
chars(s, s + 2);
|
||||
} break;
|
||||
case '\\': {
|
||||
const char *s = "\\\\";
|
||||
chars(s, s + 2);
|
||||
} break;
|
||||
default:
|
||||
if (uint8_t(unescaped[i]) <= 0x1F) {
|
||||
// If packed, this uses 8 * 32 bytes.
|
||||
// Note that we expect most compilers to embed this code in the data
|
||||
// section.
|
||||
constexpr static escape_sequence escaped[32] = {
|
||||
{6, "\\u0000"}, {6, "\\u0001"}, {6, "\\u0002"}, {6, "\\u0003"},
|
||||
{6, "\\u0004"}, {6, "\\u0005"}, {6, "\\u0006"}, {6, "\\u0007"},
|
||||
{2, "\\b"}, {2, "\\t"}, {2, "\\n"}, {6, "\\u000b"},
|
||||
{2, "\\f"}, {2, "\\r"}, {6, "\\u000e"}, {6, "\\u000f"},
|
||||
{6, "\\u0010"}, {6, "\\u0011"}, {6, "\\u0012"}, {6, "\\u0013"},
|
||||
{6, "\\u0014"}, {6, "\\u0015"}, {6, "\\u0016"}, {6, "\\u0017"},
|
||||
{6, "\\u0018"}, {6, "\\u0019"}, {6, "\\u001a"}, {6, "\\u001b"},
|
||||
{6, "\\u001c"}, {6, "\\u001d"}, {6, "\\u001e"}, {6, "\\u001f"}};
|
||||
{6, "\\u0000"}, {6, "\\u0001"}, {6, "\\u0002"}, {6, "\\u0003"},
|
||||
{6, "\\u0004"}, {6, "\\u0005"}, {6, "\\u0006"}, {6, "\\u0007"},
|
||||
{2, "\\b"}, {2, "\\t"}, {2, "\\n"}, {6, "\\u000b"},
|
||||
{2, "\\f"}, {2, "\\r"}, {6, "\\u000e"}, {6, "\\u000f"},
|
||||
{6, "\\u0010"}, {6, "\\u0011"}, {6, "\\u0012"}, {6, "\\u0013"},
|
||||
{6, "\\u0014"}, {6, "\\u0015"}, {6, "\\u0016"}, {6, "\\u0017"},
|
||||
{6, "\\u0018"}, {6, "\\u0019"}, {6, "\\u001a"}, {6, "\\u001b"},
|
||||
{6, "\\u001c"}, {6, "\\u001d"}, {6, "\\u001e"}, {6, "\\u001f"}};
|
||||
auto u = escaped[uint8_t(unescaped[i])];
|
||||
buffer.insert(buffer.end(), u.string, u.string + u.length);
|
||||
chars(u.string, u.string + u.length);
|
||||
} else {
|
||||
one_char(unescaped[i]);
|
||||
}
|
||||
} // switch
|
||||
} // for
|
||||
} // for
|
||||
one_char('\"');
|
||||
}
|
||||
|
||||
|
||||
template<class formatter>
|
||||
inline void base_formatter<formatter>::clear() {
|
||||
template <class formatter> inline void base_formatter<formatter>::clear() {
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
template <class formatter>
|
||||
simdjson_inline std::string_view base_formatter<formatter>::str() const {
|
||||
return std::string_view(buffer.data(), buffer.size());
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
simdjson_inline void mini_formatter::print_newline() {
|
||||
return;
|
||||
}
|
||||
simdjson_inline void mini_formatter::print_newline() { return; }
|
||||
|
||||
simdjson_inline void mini_formatter::print_indents(size_t depth) {
|
||||
(void)depth;
|
||||
return;
|
||||
(void)depth;
|
||||
return;
|
||||
}
|
||||
|
||||
simdjson_inline void mini_formatter::print_space() {
|
||||
return;
|
||||
}
|
||||
simdjson_inline void mini_formatter::print_space() { return; }
|
||||
|
||||
simdjson_inline void pretty_formatter::print_newline() {
|
||||
one_char('\n');
|
||||
}
|
||||
simdjson_inline void pretty_formatter::print_newline() { one_char('\n'); }
|
||||
|
||||
simdjson_inline void pretty_formatter::print_indents(size_t depth) {
|
||||
if(this->indent_step <= 0) {
|
||||
return;
|
||||
}
|
||||
for(size_t i = 0; i < this->indent_step * depth; i++) {
|
||||
one_char(' ');
|
||||
}
|
||||
if (this->indent_step <= 0) {
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < this->indent_step * depth; i++) {
|
||||
one_char(' ');
|
||||
}
|
||||
}
|
||||
|
||||
simdjson_inline void pretty_formatter::print_space() {
|
||||
one_char(' ');
|
||||
}
|
||||
simdjson_inline void pretty_formatter::print_space() { one_char(' '); }
|
||||
|
||||
/***
|
||||
* String building code.
|
||||
@@ -514,7 +545,8 @@ inline void string_builder<serializer>::append(simdjson::dom::array value) {
|
||||
}
|
||||
|
||||
template <class serializer>
|
||||
simdjson_inline void string_builder<serializer>::append(simdjson::dom::key_value_pair kv) {
|
||||
simdjson_inline void
|
||||
string_builder<serializer>::append(simdjson::dom::key_value_pair kv) {
|
||||
format.key(kv.key);
|
||||
append(kv.value);
|
||||
}
|
||||
@@ -529,7 +561,6 @@ simdjson_inline std::string_view string_builder<serializer>::str() const {
|
||||
return format.str();
|
||||
}
|
||||
|
||||
|
||||
} // namespace internal
|
||||
} // namespace simdjson
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#include "simdjson/dom/element.h"
|
||||
#include "simdjson/dom/object.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace simdjson {
|
||||
|
||||
/**
|
||||
@@ -16,8 +14,7 @@ namespace simdjson {
|
||||
*/
|
||||
namespace internal {
|
||||
|
||||
template<class formatter>
|
||||
class base_formatter {
|
||||
template <class formatter> class base_formatter {
|
||||
public:
|
||||
/** Add a comma **/
|
||||
simdjson_inline void comma();
|
||||
@@ -56,24 +53,76 @@ public:
|
||||
/** Prints one character **/
|
||||
simdjson_inline void one_char(char c);
|
||||
|
||||
/** Prints characters in [begin, end) verbatim. **/
|
||||
simdjson_inline void chars(const char *begin, const char *end);
|
||||
|
||||
simdjson_inline void call_print_newline() {
|
||||
static_cast<formatter*>(this)->print_newline();
|
||||
static_cast<formatter *>(this)->print_newline();
|
||||
}
|
||||
|
||||
simdjson_inline void call_print_indents(size_t depth) {
|
||||
static_cast<formatter*>(this)->print_indents(depth);
|
||||
static_cast<formatter *>(this)->print_indents(depth);
|
||||
}
|
||||
|
||||
simdjson_inline void call_print_space() {
|
||||
static_cast<formatter*>(this)->print_space();
|
||||
static_cast<formatter *>(this)->print_space();
|
||||
}
|
||||
|
||||
protected:
|
||||
// implementation details (subject to change)
|
||||
/** Backing buffer **/
|
||||
std::vector<char> buffer{}; // not ideal!
|
||||
};
|
||||
struct vector_with_small_buffer {
|
||||
vector_with_small_buffer() = default;
|
||||
~vector_with_small_buffer() { free_buffer(); }
|
||||
|
||||
vector_with_small_buffer(const vector_with_small_buffer &) = delete;
|
||||
vector_with_small_buffer &
|
||||
operator=(const vector_with_small_buffer &) = delete;
|
||||
|
||||
void clear() {
|
||||
size = 0;
|
||||
capacity = StaticCapacity;
|
||||
free_buffer();
|
||||
buffer = array;
|
||||
}
|
||||
|
||||
simdjson_inline void push_back(char c) {
|
||||
if (capacity < size + 1)
|
||||
grow(capacity * 2);
|
||||
buffer[size++] = c;
|
||||
}
|
||||
|
||||
simdjson_inline void append(const char *begin, const char *end) {
|
||||
const size_t new_size = size + (end - begin);
|
||||
if (capacity < new_size)
|
||||
// std::max(new_size, capacity * 2); is broken in tests on Windows
|
||||
grow(new_size < capacity * 2 ? capacity * 2 : new_size);
|
||||
std::copy(begin, end, buffer + size);
|
||||
size = new_size;
|
||||
}
|
||||
|
||||
std::string_view str() const { return std::string_view(buffer, size); }
|
||||
|
||||
private:
|
||||
void free_buffer() {
|
||||
if (buffer != array)
|
||||
delete[] buffer;
|
||||
}
|
||||
void grow(size_t new_capacity) {
|
||||
auto new_buffer = new char[new_capacity];
|
||||
std::copy(buffer, buffer + size, new_buffer);
|
||||
free_buffer();
|
||||
buffer = new_buffer;
|
||||
capacity = new_capacity;
|
||||
}
|
||||
|
||||
static const size_t StaticCapacity = 64;
|
||||
char array[StaticCapacity];
|
||||
char *buffer = array;
|
||||
size_t size = 0;
|
||||
size_t capacity = StaticCapacity;
|
||||
} buffer{};
|
||||
};
|
||||
|
||||
/**
|
||||
* @private This is the class that we expect to use with the string_builder
|
||||
@@ -107,9 +156,11 @@ 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 formatter = mini_formatter>
|
||||
class string_builder {
|
||||
template <class formatter = mini_formatter> class string_builder {
|
||||
public:
|
||||
/** Construct an initially empty builder, would print the empty string **/
|
||||
string_builder() = default;
|
||||
@@ -131,11 +182,12 @@ public:
|
||||
simdjson_inline std::string_view str() const;
|
||||
/** Append a key_value_pair to the builder (to be printed) **/
|
||||
simdjson_inline void append(simdjson::dom::key_value_pair value);
|
||||
|
||||
private:
|
||||
formatter format{};
|
||||
};
|
||||
|
||||
} // internal
|
||||
} // namespace internal
|
||||
|
||||
namespace dom {
|
||||
|
||||
@@ -144,33 +196,43 @@ namespace dom {
|
||||
*
|
||||
* @param out The output stream.
|
||||
* @param value The element.
|
||||
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
|
||||
* @throw if there is an error with the underlying output stream. simdjson
|
||||
* itself will not throw.
|
||||
*/
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::dom::element value);
|
||||
inline std::ostream &operator<<(std::ostream &out,
|
||||
simdjson::dom::element value);
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::dom::element> x);
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream &out,
|
||||
simdjson::simdjson_result<simdjson::dom::element> x);
|
||||
#endif
|
||||
/**
|
||||
* Print JSON to an output stream.
|
||||
*
|
||||
* @param out The output stream.
|
||||
* @param value The array.
|
||||
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
|
||||
* @throw if there is an error with the underlying output stream. simdjson
|
||||
* itself will not throw.
|
||||
*/
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::dom::array value);
|
||||
inline std::ostream &operator<<(std::ostream &out, simdjson::dom::array value);
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::dom::array> x);
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream &out,
|
||||
simdjson::simdjson_result<simdjson::dom::array> x);
|
||||
#endif
|
||||
/**
|
||||
* Print JSON to an output stream.
|
||||
*
|
||||
* @param out The output stream.
|
||||
* @param value The object.
|
||||
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
|
||||
* @throw if there is an error with the underlying output stream. simdjson
|
||||
* itself will not throw.
|
||||
*/
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::dom::object value);
|
||||
inline std::ostream &operator<<(std::ostream &out, simdjson::dom::object value);
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::dom::object> x);
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream &out,
|
||||
simdjson::simdjson_result<simdjson::dom::object> x);
|
||||
#endif
|
||||
} // namespace dom
|
||||
|
||||
@@ -182,47 +244,47 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<si
|
||||
* cout << to_string(doc) << endl; // prints [1,2,3]
|
||||
*
|
||||
*/
|
||||
template <class T>
|
||||
std::string to_string(T x) {
|
||||
// in C++, to_string is standard: http://www.cplusplus.com/reference/string/to_string/
|
||||
// Currently minify and to_string are identical but in the future, they may
|
||||
// differ.
|
||||
simdjson::internal::string_builder<> sb;
|
||||
sb.append(x);
|
||||
std::string_view answer = sb.str();
|
||||
return std::string(answer.data(), answer.size());
|
||||
template <class T> std::string to_string(T x) {
|
||||
// in C++, to_string is standard:
|
||||
// http://www.cplusplus.com/reference/string/to_string/ Currently minify and
|
||||
// to_string are identical but in the future, they may differ.
|
||||
simdjson::internal::string_builder<> sb;
|
||||
sb.append(x);
|
||||
std::string_view answer = sb.str();
|
||||
return std::string(answer.data(), answer.size());
|
||||
}
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <class T>
|
||||
std::string to_string(simdjson_result<T> x) {
|
||||
if (x.error()) { throw simdjson_error(x.error()); }
|
||||
return to_string(x.value());
|
||||
template <class T> std::string to_string(simdjson_result<T> x) {
|
||||
if (x.error()) {
|
||||
throw simdjson_error(x.error());
|
||||
}
|
||||
return to_string(x.value());
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Minifies a JSON element or document, printing the smallest possible valid JSON.
|
||||
* Minifies a JSON element or document, printing the smallest possible valid
|
||||
* JSON.
|
||||
*
|
||||
* dom::parser parser;
|
||||
* element doc = parser.parse(" [ 1 , 2 , 3 ] "_padded);
|
||||
* cout << minify(doc) << endl; // prints [1,2,3]
|
||||
*
|
||||
*/
|
||||
template <class T>
|
||||
std::string minify(T x) {
|
||||
return to_string(x);
|
||||
}
|
||||
template <class T> std::string minify(T x) { return to_string(x); }
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <class T>
|
||||
std::string minify(simdjson_result<T> x) {
|
||||
if (x.error()) { throw simdjson_error(x.error()); }
|
||||
return to_string(x.value());
|
||||
template <class T> std::string minify(simdjson_result<T> x) {
|
||||
if (x.error()) {
|
||||
throw simdjson_error(x.error());
|
||||
}
|
||||
return to_string(x.value());
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Prettifies a JSON element or document, printing the valid JSON with indentation.
|
||||
* Prettifies a JSON element or document, printing the valid JSON with
|
||||
* indentation.
|
||||
*
|
||||
* dom::parser parser;
|
||||
* element doc = parser.parse(" [ 1 , 2 , 3 ] "_padded);
|
||||
@@ -238,23 +300,22 @@ std::string minify(simdjson_result<T> x) {
|
||||
* cout << prettify(doc) << endl;
|
||||
*
|
||||
*/
|
||||
template <class T>
|
||||
std::string prettify(T x) {
|
||||
simdjson::internal::string_builder<simdjson::internal::pretty_formatter> sb;
|
||||
sb.append(x);
|
||||
std::string_view answer = sb.str();
|
||||
return std::string(answer.data(), answer.size());
|
||||
template <class T> std::string prettify(T x) {
|
||||
simdjson::internal::string_builder<simdjson::internal::pretty_formatter> sb;
|
||||
sb.append(x);
|
||||
std::string_view answer = sb.str();
|
||||
return std::string(answer.data(), answer.size());
|
||||
}
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <class T>
|
||||
std::string prettify(simdjson_result<T> x) {
|
||||
if (x.error()) { throw simdjson_error(x.error()); }
|
||||
return to_string(x.value());
|
||||
template <class T> std::string prettify(simdjson_result<T> x) {
|
||||
if (x.error()) {
|
||||
throw simdjson_error(x.error());
|
||||
}
|
||||
return to_string(x.value());
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -122,6 +122,7 @@ struct implementation_simdjson_result_base {
|
||||
* the error() method returns a value that evaluates to false.
|
||||
*/
|
||||
simdjson_inline T&& value_unsafe() && noexcept;
|
||||
|
||||
protected:
|
||||
/** users should never directly access first and second. **/
|
||||
T first{}; /** Users should never directly access 'first'. **/
|
||||
|
||||
@@ -36,6 +36,9 @@ simdjson_inline array_iterator &array_iterator::operator++() noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
simdjson_inline bool array_iterator::at_end() const noexcept {
|
||||
return iter.at_end();
|
||||
}
|
||||
} // namespace ondemand
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
@@ -72,7 +75,9 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterato
|
||||
++(first);
|
||||
return *this;
|
||||
}
|
||||
|
||||
simdjson_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator>::at_end() const noexcept {
|
||||
return !first.iter.is_valid() || first.at_end();
|
||||
}
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H
|
||||
@@ -34,7 +34,8 @@ public:
|
||||
*
|
||||
* Part of the std::iterator interface.
|
||||
*/
|
||||
simdjson_inline simdjson_result<value> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
|
||||
simdjson_inline simdjson_result<value>
|
||||
operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
|
||||
/**
|
||||
* Check if we are at the end of the JSON.
|
||||
*
|
||||
@@ -58,6 +59,11 @@ public:
|
||||
*/
|
||||
simdjson_inline array_iterator &operator++() noexcept;
|
||||
|
||||
/**
|
||||
* Check if the array is at the end.
|
||||
*/
|
||||
[[nodiscard]] simdjson_inline bool at_end() const noexcept;
|
||||
|
||||
private:
|
||||
value_iterator iter{};
|
||||
|
||||
@@ -76,7 +82,6 @@ namespace simdjson {
|
||||
|
||||
template<>
|
||||
struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> {
|
||||
public:
|
||||
simdjson_inline simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::array_iterator &&value) noexcept; ///< @private
|
||||
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
|
||||
simdjson_inline simdjson_result() noexcept = default;
|
||||
@@ -89,6 +94,8 @@ public:
|
||||
simdjson_inline bool operator==(const simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> &) const noexcept;
|
||||
simdjson_inline bool operator!=(const simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> &) const noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> &operator++() noexcept;
|
||||
|
||||
[[nodiscard]] simdjson_inline bool at_end() const noexcept;
|
||||
};
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
@@ -77,7 +77,7 @@ template <typename T, typename ValT = SIMDJSON_IMPLEMENTATION::ondemand::value>
|
||||
concept custom_deserializable = tag_invocable<deserialize_tag, ValT&, T&>;
|
||||
|
||||
template <typename T, typename ValT = SIMDJSON_IMPLEMENTATION::ondemand::value>
|
||||
concept deserializable = custom_deserializable<T, ValT> || is_builtin_deserializable_v<T>;
|
||||
concept deserializable = custom_deserializable<T, ValT> || is_builtin_deserializable_v<T> || concepts::optional_type<T>;
|
||||
|
||||
template <typename T, typename ValT = SIMDJSON_IMPLEMENTATION::ondemand::value>
|
||||
concept nothrow_custom_deserializable = nothrow_tag_invocable<deserialize_tag, ValT&, T&>;
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <charconv>
|
||||
#include <cstring>
|
||||
#include <experimental/meta>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@@ -94,8 +96,13 @@ consteval std::string consteval_to_quoted_escaped(std::string_view input);
|
||||
template <class T>
|
||||
requires(std::is_class_v<T> && !container_but_not_string<T> &&
|
||||
!concepts::string_view_keyed_map<T> &&
|
||||
!concepts::optional_type<T> &&
|
||||
!concepts::smart_pointer<T> &&
|
||||
!concepts::appendable_containers<T> &&
|
||||
!std::is_same_v<T, std::string> &&
|
||||
!std::is_same_v<T, std::string_view>)
|
||||
!std::is_same_v<T, std::string_view> &&
|
||||
!std::is_same_v<T, const char*> &&
|
||||
!std::is_same_v<T, char>)
|
||||
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 <concepts::optional_type T>
|
||||
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 <concepts::smart_pointer T>
|
||||
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 <typename T>
|
||||
requires(std::is_enum_v<T>)
|
||||
void atom(string_builder &b, const T &e) {
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
std::string_view result = "<unnamed>";
|
||||
[:expand(std::meta::enumerators_of(^^T)):] >> [&]<auto enum_val>{
|
||||
if (e == [:enum_val:]) {
|
||||
result = std::meta::identifier_of(enum_val);
|
||||
}
|
||||
};
|
||||
|
||||
if (result != "<unnamed>") {
|
||||
b.append_raw("\"");
|
||||
b.append_raw(result);
|
||||
b.append_raw("\"");
|
||||
} else {
|
||||
// Fallback to integer if enum value not found
|
||||
atom(b, static_cast<std::underlying_type_t<T>>(e));
|
||||
}
|
||||
#else
|
||||
// Fallback: serialize as integer if reflection not available
|
||||
atom(b, static_cast<std::underlying_type_t<T>>(e));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Support for appendable containers that don't have operator[] (sets, etc.)
|
||||
template <concepts::appendable_containers T>
|
||||
requires(!container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
|
||||
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
|
||||
!std::is_same_v<T, std::string> &&
|
||||
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*>)
|
||||
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 <class T>
|
||||
requires(std::is_arithmetic_v<T> && !std::is_same_v<T, char>)
|
||||
void append(string_builder &b, const T &t) {
|
||||
atom(b, t);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
requires(std::is_same_v<T, std::string> ||
|
||||
std::is_same_v<T, std::string_view> ||
|
||||
std::is_same_v<T, const char *> ||
|
||||
std::is_same_v<T, char>)
|
||||
void append(string_builder &b, const T &t) {
|
||||
atom(b, t);
|
||||
}
|
||||
|
||||
template <concepts::optional_type T>
|
||||
void append(string_builder &b, const T &t) {
|
||||
atom(b, t);
|
||||
}
|
||||
|
||||
template <concepts::smart_pointer T>
|
||||
void append(string_builder &b, const T &t) {
|
||||
atom(b, t);
|
||||
}
|
||||
|
||||
template <concepts::appendable_containers T>
|
||||
requires(!container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
|
||||
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
|
||||
!std::is_same_v<T, std::string> &&
|
||||
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*>)
|
||||
void append(string_builder &b, const T &t) {
|
||||
atom(b, t);
|
||||
}
|
||||
|
||||
template <concepts::string_view_keyed_map T>
|
||||
void append(string_builder &b, const T &t) {
|
||||
atom(b, t);
|
||||
}
|
||||
|
||||
// works for struct
|
||||
template <class Z> void append(string_builder &b, const Z &z) {
|
||||
template <class Z>
|
||||
requires(std::is_class_v<Z> && !container_but_not_string<Z> &&
|
||||
!concepts::string_view_keyed_map<Z> &&
|
||||
!concepts::optional_type<Z> &&
|
||||
!concepts::smart_pointer<Z> &&
|
||||
!concepts::appendable_containers<Z> &&
|
||||
!std::is_same_v<Z, std::string> &&
|
||||
!std::is_same_v<Z, std::string_view> &&
|
||||
!std::is_same_v<Z, const char*> &&
|
||||
!std::is_same_v<Z, char>)
|
||||
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())):] >> [&]<auto dm>() {
|
||||
@@ -145,8 +271,8 @@ void append(string_builder &b, const Z &z) {
|
||||
}
|
||||
|
||||
template <class Z>
|
||||
simdjson_result<std::string> to_json_string(const Z &z) {
|
||||
string_builder b;
|
||||
simdjson_result<std::string> 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,8 +288,19 @@ simdjson_error to_json(const Z &z, std::string &s) {
|
||||
s.assign(view);
|
||||
return SUCCESS;
|
||||
}
|
||||
} // namespace json_builder
|
||||
|
||||
template <class Z>
|
||||
string_builder& operator<<(string_builder& b, const Z& z) {
|
||||
append(b, z);
|
||||
return b;
|
||||
}
|
||||
} // namespace builder
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
// Alias the function template to 'to' in the global namespace
|
||||
template <class Z>
|
||||
simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = 1024) {
|
||||
return SIMDJSON_IMPLEMENTATION::builder::to_json_string(z, initial_capacity);
|
||||
}
|
||||
} // namespace simdjson
|
||||
#endif // SIMDJSON_STATIC_REFLECTION
|
||||
|
||||
|
||||
@@ -218,6 +218,8 @@ simdjson_inline void json_iterator::assert_valid_position(token_position positio
|
||||
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
|
||||
SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] );
|
||||
SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] );
|
||||
#else
|
||||
(void)position; // Suppress unused parameter warning
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <typename number_type, typename = typename std::enable_if<
|
||||
std::is_unsigned<number_type>::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
|
||||
|
||||
@@ -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
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -35,6 +35,20 @@ inline simdjson_result<std::string_view> to_json_string(simdjson_result<SIMDJSON
|
||||
inline simdjson_result<std::string_view> to_json_string(simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> x);
|
||||
inline simdjson_result<std::string_view> to_json_string(simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object> x);
|
||||
inline simdjson_result<std::string_view> to_json_string(simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array> 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<typename T>
|
||||
requires(!std::same_as<T, SIMDJSON_IMPLEMENTATION::ondemand::document> &&
|
||||
!std::same_as<T, SIMDJSON_IMPLEMENTATION::ondemand::value> &&
|
||||
!std::same_as<T, SIMDJSON_IMPLEMENTATION::ondemand::object> &&
|
||||
!std::same_as<T, SIMDJSON_IMPLEMENTATION::ondemand::array>)
|
||||
inline std::string to_json_string(const T& obj);
|
||||
#endif
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <concepts>
|
||||
#include <limits>
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
#include <experimental/meta>
|
||||
#include <meta>
|
||||
// #include <static_reflection> // for std::define_static_string - header not available yet
|
||||
#endif
|
||||
|
||||
@@ -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 <concepts::optional_type T, typename ValT>
|
||||
template <concepts::optional_type T>
|
||||
requires(!require_custom_serialization<T>)
|
||||
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable<typename std::remove_cvref_t<T>::value_type, ValT>) {
|
||||
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable<typename std::remove_cvref_t<T>::value_type, decltype(val)>) {
|
||||
using value_type = typename std::remove_cvref_t<T>::value_type;
|
||||
|
||||
static_assert(
|
||||
deserializable<value_type, ValT>,
|
||||
"The specified type inside the unique_ptr must itself be deserializable");
|
||||
static_assert(
|
||||
std::is_default_constructible_v<value_type>,
|
||||
"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();
|
||||
@@ -297,7 +296,7 @@ template<typename R>
|
||||
consteval auto expand(R range) {
|
||||
std::vector<std::meta::info> args;
|
||||
for (auto r : range) {
|
||||
args.push_back(reflect_value(r));
|
||||
args.push_back(reflect_constant(r));
|
||||
}
|
||||
return substitute(^^__impl::replicator, args);
|
||||
}
|
||||
@@ -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())):] >> [&]<auto mem>() {
|
||||
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<decltype(out.[:mem:]), SIMDJSON_IMPLEMENTATION::ondemand::object>,
|
||||
"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 <typename T, typename ValT>
|
||||
requires(std::is_enum_v<T>)
|
||||
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)):] >> [&]<auto enum_val>{
|
||||
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<T> int_val;
|
||||
SIMDJSON_TRY(val.get(int_val));
|
||||
out = static_cast<T>(int_val);
|
||||
return SUCCESS;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename simdjson_value, typename T>
|
||||
requires(user_defined_type<std::remove_cvref_t<T>>)
|
||||
error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr<T> &out) noexcept {
|
||||
@@ -366,6 +390,10 @@ error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &
|
||||
// Unique pointers
|
||||
////////////////////////////////////////
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!out) {
|
||||
out = std::make_unique<bool>();
|
||||
if (!out) { return MEMALLOC; }
|
||||
@@ -375,6 +403,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) no
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!out) {
|
||||
out = std::make_unique<int64_t>();
|
||||
if (!out) { return MEMALLOC; }
|
||||
@@ -384,6 +416,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out)
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!out) {
|
||||
out = std::make_unique<uint64_t>();
|
||||
if (!out) { return MEMALLOC; }
|
||||
@@ -393,6 +429,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!out) {
|
||||
out = std::make_unique<double>();
|
||||
if (!out) { return MEMALLOC; }
|
||||
@@ -402,6 +442,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out)
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!out) {
|
||||
out = std::make_unique<std::string_view>();
|
||||
if (!out) { return MEMALLOC; }
|
||||
@@ -415,6 +459,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_vi
|
||||
// Shared pointers
|
||||
////////////////////////////////////////
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!out) {
|
||||
out = std::make_shared<bool>();
|
||||
if (!out) { return MEMALLOC; }
|
||||
@@ -424,6 +472,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) no
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!out) {
|
||||
out = std::make_shared<int64_t>();
|
||||
if (!out) { return MEMALLOC; }
|
||||
@@ -433,6 +485,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out)
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!out) {
|
||||
out = std::make_shared<uint64_t>();
|
||||
if (!out) { return MEMALLOC; }
|
||||
@@ -442,6 +498,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!out) {
|
||||
out = std::make_shared<double>();
|
||||
if (!out) { return MEMALLOC; }
|
||||
@@ -451,6 +511,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out)
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!out) {
|
||||
out = std::make_shared<std::string_view>();
|
||||
if (!out) { return MEMALLOC; }
|
||||
@@ -460,6 +524,61 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_vi
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////
|
||||
// Explicit optional specializations
|
||||
////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////
|
||||
// Explicit smart pointer specializations for string and int types
|
||||
////////////////////////////////////////
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &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>();
|
||||
}
|
||||
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<std::string> &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>();
|
||||
}
|
||||
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<int> &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<int>();
|
||||
}
|
||||
int64_t temp;
|
||||
SIMDJSON_TRY(val.get_int64().get(temp));
|
||||
*out = static_cast<int>(temp);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
|
||||
|
||||
@@ -75,6 +75,19 @@ public:
|
||||
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||
if constexpr (custom_deserializable<T, value>) {
|
||||
return deserialize(*this, out);
|
||||
} else if constexpr (concepts::optional_type<T>) {
|
||||
using value_type = typename std::remove_cvref_t<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<value_type>(out.value());
|
||||
} else {
|
||||
static_assert(!sizeof(T), "The get<T> 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 "
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<typename T>
|
||||
requires(!std::same_as<T, builtin::ondemand::document> &&
|
||||
!std::same_as<T, builtin::ondemand::value> &&
|
||||
!std::same_as<T, builtin::ondemand::object> &&
|
||||
!std::same_as<T, builtin::ondemand::array>)
|
||||
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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
+5
-1
@@ -8,7 +8,11 @@ RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends ca-certificates gnupg \
|
||||
build-essential cmake make python3 zlib1g wget subversion unzip ninja-build git linux-perf && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
RUN git clone --depth=1 --branch p2996 https://github.com/bloomberg/clang-p2996.git /tmp/clang-source
|
||||
ARG CLANG_COMMIT=d77eff1cbd78fd065668acf93b1f5f400d39134d
|
||||
RUN git clone --depth=1 https://github.com/bloomberg/clang-p2996.git /tmp/clang-source && \
|
||||
cd /tmp/clang-source && \
|
||||
git fetch origin $CLANG_COMMIT --depth=1 && \
|
||||
git checkout $CLANG_COMMIT
|
||||
RUN cmake -S /tmp/clang-source/llvm -B /tmp/clang-source/build-llvm -DCMAKE_BUILD_TYPE=Release \
|
||||
-DLLVM_ENABLE_ASSERTIONS=ON \
|
||||
-DLLVM_UNREACHABLE_OPTIMIZE=ON \
|
||||
|
||||
@@ -64,6 +64,7 @@ cmake --build buildreflect --target benchmark_serialization_citm_catalog benchma
|
||||
|
||||
6. Run the tests...
|
||||
```bash
|
||||
cmake --build buildreflect
|
||||
ctest --test-dir buildreflect --output-on-failure
|
||||
```
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
+590
-55
File diff suppressed because it is too large
Load Diff
+14512
-195
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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<const uint8_t *>(input.data()) + index, reinterpret_cast<uint8_t *>(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<const uint8_t *>(input.data()) + index, reinterpret_cast<uint8_t *>(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<uint8_t, 256> json_quotable_character =
|
||||
[]() SIMDJSON_CONSTEXPR_LAMBDA {
|
||||
std::array<uint8_t, 256> 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<uint8_t>(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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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() &&
|
||||
|
||||
@@ -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,45 @@ 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};
|
||||
(void)p; // to avoid unused variable warning
|
||||
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};
|
||||
(void)p; // to avoid unused variable warning
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool car_test_to_json_exception() {
|
||||
TEST_START();
|
||||
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
|
||||
std::string json = simdjson::to_json(c);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool car_test_to_json_exception_value() {
|
||||
TEST_START();
|
||||
std::string json = simdjson::to_json(Car{"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}});
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
|
||||
|
||||
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 +120,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 +133,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 +151,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 +161,24 @@ bool serialize_deserialize_x_y_z() {
|
||||
X s2;
|
||||
ASSERT_SUCCESS(doc.get<X>().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() &&
|
||||
car_test_to_json_exception_value() &&
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
car_test() &&
|
||||
serialize_deserialize_kid() &&
|
||||
serialize_deserialize_x_y_z() &&
|
||||
true;
|
||||
}
|
||||
|
||||
} // namespace builder_tests
|
||||
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
#include "simdjson.h"
|
||||
#include "test_builder.h"
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
|
||||
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<PrimitiveTypes>();
|
||||
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<StringTypes>();
|
||||
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<int> opt_int_with_value;
|
||||
std::optional<std::string> opt_string_with_value;
|
||||
std::optional<int> opt_int_null;
|
||||
std::optional<std::string> 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<OptionalTypes>();
|
||||
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<int> unique_int_with_value;
|
||||
std::shared_ptr<std::string> shared_string_with_value;
|
||||
std::unique_ptr<bool> unique_bool_with_value;
|
||||
std::unique_ptr<int> unique_int_null;
|
||||
std::shared_ptr<std::string> shared_string_null;
|
||||
};
|
||||
|
||||
SmartPointerTypes test;
|
||||
test.unique_int_with_value = std::make_unique<int>(123);
|
||||
test.shared_string_with_value = std::make_shared<std::string>("shared_test");
|
||||
test.unique_bool_with_value = std::make_unique<bool>(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<SmartPointerTypes>();
|
||||
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> int_vector;
|
||||
std::set<std::string> string_set;
|
||||
std::map<std::string, int> 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<ContainerTypes>();
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
#include "simdjson.h"
|
||||
#include "test_builder.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <limits>
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
namespace builder_tests {
|
||||
|
||||
bool test_empty_values() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
struct EmptyValues {
|
||||
std::string empty_string;
|
||||
std::vector<int> empty_vector;
|
||||
std::optional<int> null_optional;
|
||||
std::unique_ptr<int> null_unique_ptr;
|
||||
std::shared_ptr<std::string> 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<EmptyValues>();
|
||||
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<SpecialCharsNoNull>();
|
||||
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<int>::max();
|
||||
test.min_int = std::numeric_limits<int>::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<NumericLimits>();
|
||||
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> inner_vector;
|
||||
std::optional<Inner> optional_inner;
|
||||
std::unique_ptr<Inner> 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>(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<Outer>();
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
#include "simdjson.h"
|
||||
#include "test_builder.h"
|
||||
#include <string>
|
||||
|
||||
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<StatusStruct>();
|
||||
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<StatusStruct>();
|
||||
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<StatusStruct>();
|
||||
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<Task>();
|
||||
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<Response>();
|
||||
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<Date>();
|
||||
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);
|
||||
}
|
||||
@@ -132,7 +132,9 @@ if(
|
||||
)
|
||||
message(STATUS "compiler id: ${CMAKE_CXX_COMPILER_ID} version: ${CMAKE_CXX_COMPILER_VERSION}")
|
||||
add_cpp_test(ranges_test LABELS dom acceptance per_implementation)
|
||||
set_target_properties(ranges_test PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF)
|
||||
if(NOT SIMDJSON_STATIC_REFLECTION)
|
||||
set_target_properties(ranges_test PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32 AND BUILD_SHARED_LIBS)
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
#include "simdjson.h"
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "simdjson.h"
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ add_cpp_test(ondemand_iterate_many_csv LABELS ondemand acceptance
|
||||
add_cpp_test(ondemand_custom_types_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_custom_types_document_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_stl_types_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_convert_tests LABELS ondemand acceptance per_implementation)
|
||||
if(NOT SIMDJSON_SANITIZE)
|
||||
add_cpp_test(ondemand_cacheline LABELS ondemand acceptance per_implementation)
|
||||
endif()
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#endif
|
||||
#include "simdjson.h"
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
// Returns the default size of the page in bytes on this system.
|
||||
long page_size() {
|
||||
#ifdef _WIN32
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
#include "simdjson.h"
|
||||
#include "simdjson/convert.h"
|
||||
#include "test_ondemand.h"
|
||||
|
||||
#include <ranges>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __cpp_lib_ranges
|
||||
|
||||
namespace convert_tests {
|
||||
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||
struct Car {
|
||||
std::string make{};
|
||||
std::string model{};
|
||||
int year{};
|
||||
std::vector<double> tire_pressure{};
|
||||
|
||||
friend simdjson::error_code tag_invoke(simdjson::deserialize_tag, auto &val,
|
||||
Car &car) {
|
||||
simdjson::ondemand::object obj;
|
||||
auto error = val.get_object().get(obj);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
// Instead of repeatedly obj["something"], we iterate through the object
|
||||
// which we expect to be faster.
|
||||
for (auto field : obj) {
|
||||
simdjson::ondemand::raw_json_string key;
|
||||
error = field.key().get(key);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
if (key == "make") {
|
||||
error = field.value().get_string(car.make);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
} else if (key == "model") {
|
||||
error = field.value().get_string(car.model);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
} else if (key == "year") {
|
||||
error = field.value().get(car.year);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
} else if (key == "tire_pressure") {
|
||||
error = field.value().get(car.tire_pressure);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
}
|
||||
return simdjson::SUCCESS;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(simdjson::custom_deserializable<std::unique_ptr<Car>>,
|
||||
"It should be deserializable");
|
||||
|
||||
static_assert(std::input_or_output_iterator<simdjson::auto_iterator>,
|
||||
"Must be a valid input iterator");
|
||||
static_assert(std::semiregular<simdjson::auto_iterator>,
|
||||
"Should be kinda regular");
|
||||
// static_assert(std::ranges::__access::__member_end<simdjson::auto_parser<>>,
|
||||
// "Must be a valid input iterator");
|
||||
// static_assert(std::ranges::views::__adaptor::__is_range_adaptor_closure<
|
||||
// simdjson::auto_parser<>>,
|
||||
// "Parser need to be range adaptor closure.");
|
||||
// static_assert(std::ranges::views::__adaptor::__adaptor_invocable<
|
||||
// decltype(simdjson::to<Car>()),
|
||||
// simdjson::auto_parser<>>,
|
||||
// "I don't even know!");
|
||||
static_assert(std::ranges::range<simdjson::auto_parser<>>,
|
||||
"Parser need to be a range.");
|
||||
static_assert(std::ranges::forward_range<simdjson::auto_parser<>>,
|
||||
"Parser need to be an input range.");
|
||||
static_assert(
|
||||
requires(simdjson::auto_parser<> &parser) {
|
||||
{ parser.begin() } -> std::input_or_output_iterator;
|
||||
}, "Must be valid iterator.");
|
||||
|
||||
simdjson::padded_string json_car =
|
||||
R"( {
|
||||
"make": "Toyota",
|
||||
"model": "Camry",
|
||||
"year": 2018,
|
||||
"tire_pressure": [ 40.1, 39.9 ]
|
||||
} )"_padded;
|
||||
simdjson::padded_string json_cars =
|
||||
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||
"tire_pressure": [ 40.1, 39.9 ] },
|
||||
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||
"tire_pressure": [ 30.1, 31.0 ] },
|
||||
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||
"tire_pressure": [ 29.8, 30.0 ] }
|
||||
])"_padded;
|
||||
|
||||
bool simple() {
|
||||
TEST_START();
|
||||
Car car = simdjson::from(json_car);
|
||||
if (car.make != "Toyota" || car.model != "Camry" || car.year != 2018) {
|
||||
return false;
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool broken() {
|
||||
TEST_START();
|
||||
simdjson::padded_string short_json_cars = R"( { "make )"_padded;
|
||||
try {
|
||||
Car car = simdjson::from(json_cars);
|
||||
TEST_FAIL("Should not have succeeded");
|
||||
} catch (...) {
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool simple_optional() {
|
||||
TEST_START();
|
||||
auto car = simdjson::from(json_car).optional<Car>();
|
||||
if (!car.has_value() || car->make != "Toyota" || car->model != "Camry" ||
|
||||
car->year != 2018) {
|
||||
return false;
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool with_parser() {
|
||||
TEST_START();
|
||||
simdjson::ondemand::parser parser;
|
||||
Car car = simdjson::from(parser, json_car);
|
||||
if (car.make != "Toyota" || car.model != "Camry" || car.year != 2018) {
|
||||
return false;
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool to_array() {
|
||||
TEST_START();
|
||||
auto parser = simdjson::from(json_cars);
|
||||
for (auto val : parser.array()) {
|
||||
Car car{};
|
||||
if (auto const error = val.get(car)) {
|
||||
std::cerr << simdjson::error_message(error) << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (car.year < 1998) {
|
||||
std::cerr << car.make << " " << car.model << " " << car.year << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool to_array_shortcut() {
|
||||
TEST_START();
|
||||
simdjson::ondemand::parser parser;
|
||||
for (auto val : simdjson::from(parser, json_cars)) {
|
||||
Car car{};
|
||||
if (auto const error = val.get(car)) {
|
||||
std::cerr << simdjson::error_message(error) << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (car.year < 1998) {
|
||||
std::cerr << car.make << " " << car.model << " " << car.year << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool to_bad_array() {
|
||||
TEST_START();
|
||||
auto parser = simdjson::from(json_car);
|
||||
try {
|
||||
auto array_result = parser.array();
|
||||
// Check if array_result has an error
|
||||
if (array_result.error() != simdjson::SUCCESS) {
|
||||
// This is expected - trying to get array from an object should fail
|
||||
if (array_result.error() != simdjson::INCORRECT_TYPE) {
|
||||
std::cerr << "Expected INCORRECT_TYPE but got: " << array_result.error()
|
||||
<< " (" << simdjson::error_message(array_result.error()) << ")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
// Got expected error, test passes
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
// If we get here without error, try to iterate
|
||||
// This might throw when we try to use the array
|
||||
for (auto val : array_result) {
|
||||
static_cast<void>(val);
|
||||
// Should not reach here - the JSON is an object, not an array
|
||||
std::cerr << "Unexpectedly succeeded in iterating over non-array JSON" << std::endl;
|
||||
return false;
|
||||
}
|
||||
// Also should not reach here
|
||||
std::cerr << "array() succeeded on object JSON without throwing" << std::endl;
|
||||
return false;
|
||||
} catch (simdjson::simdjson_error &e) {
|
||||
if (e.error() != simdjson::INCORRECT_TYPE) {
|
||||
std::cerr << "Expected INCORRECT_TYPE but got: " << e.error() << " (" << simdjson::error_message(e.error()) << ")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
// Got expected exception, test passes
|
||||
} catch (...) {
|
||||
std::cerr << "Unexpected exception type" << std::endl;
|
||||
return false;
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_basic_adaptor() {
|
||||
TEST_START();
|
||||
for (Car car : simdjson::from(json_cars) | simdjson::as<Car>()) {
|
||||
if (car.year < 1998) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_no_errors() {
|
||||
TEST_START();
|
||||
auto cars = simdjson::from(json_cars) | simdjson::no_errors;
|
||||
for (auto val : cars) {
|
||||
Car car = val.get<Car>();
|
||||
if (car.year < 1998) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool to_clean_array() {
|
||||
TEST_START();
|
||||
for (auto val : simdjson::from(json_cars) | simdjson::no_errors) {
|
||||
Car car = val.get<Car>();
|
||||
if (car.year < 1998) {
|
||||
std::cerr << car.make << " " << car.model << " " << car.year << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_to_adaptor_basic() {
|
||||
TEST_START();
|
||||
// Test 1: Basic usage of to<T> with a value reference
|
||||
simdjson::ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(json_car);
|
||||
if (doc_result.error()) {
|
||||
return false;
|
||||
}
|
||||
simdjson::ondemand::document doc = std::move(doc_result.value());
|
||||
simdjson::simdjson_result<simdjson::ondemand::value> val = doc.get_value();
|
||||
|
||||
// to<T> converts a simdjson_result<value>& to T
|
||||
Car car = simdjson::to<Car>(val);
|
||||
if (car.make != "Toyota" || car.model != "Camry" || car.year != 2018) {
|
||||
return false;
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_to_adaptor_with_single_value() {
|
||||
TEST_START();
|
||||
// Test 2: Using to<T> to convert individual values
|
||||
simdjson::ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(json_car);
|
||||
if (doc_result.error()) {
|
||||
return false;
|
||||
}
|
||||
simdjson::ondemand::document doc = std::move(doc_result.value());
|
||||
|
||||
// Get individual field and convert it
|
||||
auto obj_result = doc.get_object();
|
||||
if (obj_result.error()) {
|
||||
return false;
|
||||
}
|
||||
simdjson::ondemand::object obj = std::move(obj_result.value());
|
||||
|
||||
auto year_val = obj["year"];
|
||||
int64_t year = simdjson::to<int64_t>(year_val);
|
||||
if (year != 2018) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_to_vs_from_equivalence() {
|
||||
TEST_START();
|
||||
// Test 3: Verify that simdjson::to<> and simdjson::from behave equivalently
|
||||
// Both are instances of to_adaptor - from is just to<void>
|
||||
|
||||
// These should produce identical auto_parser objects
|
||||
auto parser1 = simdjson::from(json_car);
|
||||
// simdjson::from is an alias for simdjson::to<void>
|
||||
auto parser2 = simdjson::from(json_car); // Same as parser1
|
||||
|
||||
// Both should parse the same way
|
||||
Car car1 = parser1;
|
||||
Car car2 = parser2;
|
||||
|
||||
if (car1.make != car2.make || car1.model != car2.model || car1.year != car2.year) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
bool run() {
|
||||
return
|
||||
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||
test_basic_adaptor() && broken() && simple() && simple_optional() && with_parser() && to_array() &&
|
||||
to_array_shortcut() && to_bad_array() && test_no_errors() &&
|
||||
to_clean_array() && test_to_adaptor_basic() &&
|
||||
test_to_adaptor_with_single_value() && test_to_vs_from_equivalence() &&
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
true;
|
||||
}
|
||||
|
||||
} // namespace convert_tests
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return test_main(argc, argv, convert_tests::run);
|
||||
}
|
||||
#else
|
||||
int main() { return 0; }
|
||||
#endif
|
||||
@@ -2,6 +2,7 @@
|
||||
#define TEST_MACROS_H
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#ifndef SIMDJSON_BENCHMARK_DATA_DIR
|
||||
#define SIMDJSON_BENCHMARK_DATA_DIR "jsonexamples/"
|
||||
|
||||
Reference in New Issue
Block a user