Compare commits

...

55 Commits

Author SHA1 Message Date
Francisco Geiman Thiesen 55f698c97b Merge branch 'master' into francisco/toy_example 2025-08-09 03:26:37 +00:00
Francisco Geiman Thiesen 7e2a1d7651 Pushing a few readmes that can be useful for talk preparation 2025-08-09 03:25:54 +00:00
Francisco Geiman Thiesen 3e00a431a0 Merge pull request #2406 from simdjson/fix-convert-ci-failures
Introducing simplified api with from/to adapters (thanks to @the-moisrex for driving this) and fixing all the CI errors.
2025-08-08 10:42:32 -07:00
Pavel Novikov d3626c6a37 removed now unnecessary <utility> header, (#2411)
disabled copy ctor and assignment for `vector_with_small_buffer`
2025-08-08 08:39:28 -04:00
Daniel Lemire 686a1869c8 updating commit 2025-08-08 08:38:41 -04:00
Daniel Lemire 4c0f86db44 minor fixes 2025-08-07 23:59:30 -04:00
Francisco Geiman Thiesen c4058d9d22 Adding scripts with both serialization and deserialization 2025-08-08 02:24:12 +00:00
Daniel Lemire 6c4c934457 added a new test. 2025-08-07 18:11:44 -04:00
Daniel Lemire fcae795042 minor fix 2025-08-07 16:05:24 -04:00
Daniel Lemire b7c51156b9 Sped up serialization fix (#2409)
* clang format

* added `chars()` method

* implemented vector with small buffer instead of `std::vector`

* added missing <utility> header

* minor fixes

---------

Co-authored-by: Pavel Novikov <dev-ape@yandex.ru>
Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2025-08-07 16:04:20 -04:00
Pavel Novikov 662e3d96c7 Sped up serialization by 10..70%-ish (#2408)
* clang format

* added `chars()` method

* implemented vector with small buffer instead of `std::vector`

* added missing <utility> header
2025-08-07 16:03:39 -04:00
Daniel Lemire 64009f7063 more code simplification. 2025-08-07 13:25:18 -04:00
Daniel Lemire 74fb3ecac7 removing another pragma and some code simplification. 2025-08-07 13:23:21 -04:00
Daniel Lemire 26abf1d180 removing macros 2025-08-07 11:39:51 -04:00
Francisco Geiman Thiesen e878dc6be3 Addressing @the-moisrex review. 2025-08-06 03:05:32 +00:00
Francisco Geiman Thiesen efa03f5733 Fix to_bad_array test to handle both exception and non-exception error cases
The test was failing in CI with g++-13 because it only handled the
exception case. However, the array() method is marked noexcept and
returns a simdjson_result that may contain an error code instead of
throwing an exception.

This fix checks for both cases:
1. If array_result.error() is not SUCCESS, verify it's INCORRECT_TYPE
2. If no error is returned initially, the exception may be thrown when
   iterating over the result

This ensures the test passes regardless of whether the error is
reported via error code or exception.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-05 20:51:17 +00:00
Francisco Geiman Thiesen 8519623257 Fix unused parameter warning in json_iterator::assert_valid_position for SIMDJSON_CLANG_VISUAL_STUDIO
Added (void)position; to suppress unused parameter warning when compiling with SIMDJSON_CLANG_VISUAL_STUDIO defined, where the position parameter isn't used in the SIMDJSON_ASSUME statements.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-05 19:27:01 +00:00
Francisco Geiman Thiesen 484a092c31 Adding a few tests for the simdjson::to adapter. 2025-08-05 16:29:59 +00:00
Francisco Geiman Thiesen 54af3feca2 Adding initial toy_example that shows why reflection is almost frictionless to use. 2025-08-05 15:17:08 +00:00
Francisco Geiman Thiesen 8bb520adbf Merge master and regenerate amalgamated files
Resolved conflicts by regenerating the amalgamated single-header
files (simdjson.h, simdjson.cpp, and singleheader.zip) using the
amalgamate.py script after merging latest changes from master.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-03 20:16:47 +00:00
Francisco Geiman Thiesen ec8e6a6758 Fix -Werror=effc++ warnings and test issues in convert.h
This commit addresses multiple issues:

1. Fixed -Werror=effc++ warnings by using #pragma to disable the
   warning for constructors that cannot initialize all members in
   the member initialization list due to error handling requirements.

2. Added proper error tracking (m_error member) to handle cases where
   document initialization fails, preventing segfaults when using
   invalid documents.

3. Fixed lifetime issues in tests where temporary auto_parser objects
   were being used, causing dangling references. Tests now properly
   store the parser object before using it.

4. Simplified range adaptor tests that were expecting features not
   yet implemented in simdjson's ondemand API.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-03 19:26:58 +00:00
Francisco Geiman Thiesen 769528b6c0 Fix segmentation fault in auto_parser constructor for C++20
The issue was that we were trying to initialize ondemand::document
directly from simdjson_result<ondemand::document> in the member
initializer list. This caused a segfault in C++20 builds.

The fix explicitly handles the simdjson_result in the constructor
body, checking for errors and using value_unsafe() to extract the
document. This avoids potential issues with implicit conversions
and ensures proper error handling.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-03 19:01:42 +00:00
Francisco Geiman Thiesen 9fbc577be7 Fix member initialization order warning in auto_parser
The compiler was warning about member initialization order mismatch.
C++ initializes members in the order they are declared in the class,
not the order they appear in the initializer list.

Fixed by reordering member declarations to match the initialization
order needed: m_doc must be initialized before m_parser since we
need to call parser.iterate() before moving the parser.

This fixes the -Werror=reorder compilation error in CI.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-03 18:48:07 +00:00
Francisco Geiman Thiesen a7c95e9cc8 Fix initialization order in auto_parser constructor
The issue was that we were calling m_parser.iterate() after moving
the parser, which could leave it in an invalid state. In C++20,
this might behave differently than C++17.

Fixed by reordering the member initializer list to call
parser.iterate() BEFORE moving the parser into m_parser.

This ensures the document is created while the parser is still valid.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-03 18:36:14 +00:00
Francisco Geiman Thiesen 248d4eb2aa Try using parentheses instead of braces for document initialization
The issue might be related to how brace initialization vs parentheses
initialization handles implicit conversion from simdjson_result<document>
to document. This could be compiler-specific behavior.

Using parentheses initialization to ensure the conversion operator
is called properly.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-03 18:12:50 +00:00
Francisco Geiman Thiesen 559493e2bf Fix auto_parser constructor to handle document initialization correctly
The issue was that the auto_parser constructor was using implicit
conversion from simdjson_result<document> to document, which could
cause issues with certain implementations (particularly fallback).

Changed to use value_unsafe() to explicitly extract the document
after the parser is fully initialized. This ensures the document
is in a valid state for subsequent operations.

This fixes the ondemand_convert_tests failure in CI with clang++-16.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-03 15:28:40 +00:00
Francisco Geiman Thiesen 75acae5c46 Fix C++20 compatibility issues in convert.h
- Remove constexpr from functions that call non-constexpr methods
- The no_errors and to<T> adaptors were marked constexpr but call
  simdjson_result methods that are not constexpr in C++20
- This was causing compilation failures in CI for C++20 builds
- Tests now compile and pass with both C++17 and C++20

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-03 06:01:50 +00:00
Francisco Geiman Thiesen bd761ef573 Fix ranges support for C++20 compatibility
Instead of disabling the feature, provide C++20-compatible implementation
of the pipe operators for ranges support. The range_adaptor_closure is
C++23-only, so we implement our own pipe operators for C++20.

This preserves the core functionality of the PR while ensuring
compatibility across different compiler versions.
2025-08-03 05:39:10 +00:00
Francisco Geiman Thiesen 76ed73f07f Disable ranges-dependent tests to fix compilation
The test_no_errors() and to_clean_array() tests depend on the C++23
ranges features that we disabled. This commit conditionally compiles
these tests out when ranges support is disabled.
2025-08-03 05:34:34 +00:00
Francisco Geiman Thiesen 1b59b38de8 Disable C++23 ranges features to fix CI compatibility
The ranges features were causing compatibility issues across different
compilers and platforms. Disabling them for now until C++23 support
is more widespread.

This should fix the remaining Ubuntu and Windows CI failures.
2025-08-03 05:11:31 +00:00
Francisco Geiman Thiesen 4794b5d936 Update amalgamated files with convert.h fixes
Regenerate singleheader/simdjson.cpp and singleheader/simdjson.h
to include all the fixes for CI compatibility issues.
2025-08-02 17:06:45 +00:00
Francisco Geiman Thiesen 114f14924c Simplify ranges feature detection for C++23
Only enable range_adaptor_closure features when compiling with C++23
or later, as this feature is not available in C++20 implementations.
2025-08-02 16:04:58 +00:00
Francisco Geiman Thiesen e94825c9a8 Fix preprocessor check for __cpp_lib_ranges_zip
Add defined() check before comparing the value to avoid preprocessor
errors in compilers where this macro doesn't exist (like g++-13
with certain configurations).
2025-08-02 15:43:31 +00:00
Francisco Geiman Thiesen 333fd72f98 Fix CI failures in convert.h implementation
- Fix deprecated reflect_value warning by using reflect_constant
- Fix std::const_iterator C++23 requirement by using auto_iterator
- Fix C++23 std::ranges::range_adaptor_closure availability check
- Add convert.h to main simdjson.h includes

These changes ensure compatibility across different C++ standards
and compiler versions, fixing the Ubuntu CI failures.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-02 15:04:25 +00:00
Daniel Lemire dd4d02617f removing unnecessary hack 2025-07-31 18:40:54 -04:00
Daniel Lemire e28d63334e fix 2025-07-31 13:07:29 -04:00
Daniel Lemire 3d1ab87ecb Merge branch 'master' into release_candidate_4_0_0 2025-07-31 10:24:54 -04:00
Borislav Stanimirov d365cfb4a1 remove cmake_policy (#2404)
* properly gitignore Visual Studio artifacts

* remove cmake_policy
2025-07-31 10:18:28 -04:00
Daniel Lemire b9e308a727 marking them as 'really inline' 2025-07-28 11:47:09 -04:00
Brad Bramble aa6817d5aa Fix linker errors for downstream users caused by non-inline symbols in header (#2403) 2025-07-28 11:44:32 -04:00
Francisco Geiman Thiesen 90e4a66c93 Bringing a bit more use-cases for reflection based serializations (optional). Also adding string-based enum handling as requested on X. (#2395)
* Adding type validation, enhancing optional type support and adding test a few more tests.

* Adding support for string-based enum serlalization and deserialization.

* Removing unintentional endline.

* Removing trailing whitespace.

* Adding simpler api as suggested by moisrex.

* Removing explicit optiona<int> and optional<std::string> references and using concepts instead! Credit goes to Lemire for pointing this out and suggesting a concepts based approach here.

* Removing tests that are not relevant for this branch.

* Removing api related changes. That will be done by moisrex.

* Removing unnecessary new endlines.

* Removing tests related to api changes and cleaning-up irrelevant tests.

* removing broken reference

* Removing trailing whitespace
2025-07-23 09:24:05 +02:00
evbse e6240f18c4 Add version to amalgamated files (#2400) 2025-07-23 09:20:06 +02:00
M. Bahoosh d43fb6ff84 test for no_errors 2025-07-21 05:24:12 -10:00
M. Bahoosh 5eec29a6db Moving iterator's storage to auto_parser 2025-07-21 05:18:13 -10:00
M. Bahoosh 228501f786 From/To adaptors 2025-07-21 03:22:20 -10:00
M. Bahoosh 9d9f2427c5 Make auto_parser and auto_iterator comply with ranges. 2025-07-20 03:22:07 -10:00
M. Bahoosh 8d53840253 Removing unneeded code 2025-07-19 08:37:04 -10:00
M. Bahoosh 0a82fb110f Auto Iterator 2025-07-19 08:35:49 -10:00
M. Bahoosh 11f273c580 Moving ondemand::document into auto_parser 2025-07-19 04:37:13 -10:00
M. Bahoosh 31662531ac Basic Auto Parser 2025-07-19 03:58:21 -10:00
Daniel Lemire 97eb557388 Merge branch 'release_candidate_4_0_0' of github.com:simdjson/simdjson into release_candidate_4_0_0 2025-07-16 12:08:03 -04:00
Daniel Lemire 41ced28821 adding a space 2025-07-16 12:07:49 -04:00
Daniel Lemire 1078eb4034 minor update to the release candidate (#2394) 2025-07-16 12:05:09 -04:00
Daniel Lemire 0cd774097f [skip ci] doc fixes 2025-07-14 23:46:30 -04:00
Daniel Lemire b5e27af4da release candidate 4.0.0 2025-07-14 15:55:00 -04:00
82 changed files with 19324 additions and 771 deletions
+23 -2
View File
@@ -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
View File
@@ -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)
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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++26s 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
View File
@@ -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
----------------------------------------------
+254
View File
@@ -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]
+22
View File
@@ -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()
+232
View File
@@ -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."
+72
View File
@@ -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`
+74
View File
@@ -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!
+53
View File
@@ -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.
+167
View File
@@ -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?"
+322
View File
@@ -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
+83
View File
@@ -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;
}
+165
View File
@@ -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;
}
+61
View File
@@ -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;
}
+86
View File
@@ -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;
}
+95
View File
@@ -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;
}
+86
View File
@@ -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;
}
+85
View File
@@ -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;
}
+1
View File
@@ -53,4 +53,5 @@
#include "simdjson/dom.h"
#include "simdjson/ondemand.h"
#include "simdjson/convert.h"
#endif // SIMDJSON_H
-1
View File
@@ -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
+12 -11
View File
@@ -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
-1
View File
@@ -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>;
+307
View File
@@ -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
+167 -136
View File
@@ -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
+116 -55
View File
@@ -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(); }
+2 -2
View File
@@ -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
+13
View File
@@ -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
-17
View File
@@ -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(
-1
View File
@@ -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
-1
View File
@@ -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
+22
View File
@@ -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
+2 -2
View File
@@ -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);
}
-1
View File
@@ -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
+3 -3
View File
@@ -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
View File
@@ -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 \
+1
View File
@@ -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
```
+9 -4
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+14512 -195
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
-4
View File
@@ -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
-4
View File
@@ -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
-98
View File
@@ -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
-4
View File
@@ -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
-4
View File
@@ -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
-9
View File
@@ -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);
-4
View File
@@ -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
-4
View File
@@ -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
-4
View File
@@ -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
-5
View File
@@ -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
View File
@@ -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);
}
+3 -1
View File
@@ -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)
+2
View File
@@ -4,6 +4,8 @@
#include <iostream>
#include <string>
#include <vector>
using namespace std::string_literals;
#include "simdjson.h"
+1
View File
@@ -11,6 +11,7 @@
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <vector>
#include "simdjson.h"
+1
View File
@@ -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()
+1
View File
@@ -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
+336
View File
@@ -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
+1
View File
@@ -2,6 +2,7 @@
#define TEST_MACROS_H
#include <iostream>
#include <vector>
#ifndef SIMDJSON_BENCHMARK_DATA_DIR
#define SIMDJSON_BENCHMARK_DATA_DIR "jsonexamples/"