Compare commits

..

4 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 c4058d9d22 Adding scripts with both serialization and deserialization 2025-08-08 02:24:12 +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
162 changed files with 9313 additions and 18342 deletions
+1 -4
View File
@@ -9,8 +9,7 @@ assignees: ''
Before submitting an issue, please ensure that you have read the documentation:
* Basics is an overview of how to use simdjson and its APIs to parse JSON: https://github.com/simdjson/simdjson/blob/master/doc/basics.md
* Builder is an overview of how to use simdjson to generate JSON: https://github.com/simdjson/simdjson/blob/master/doc/builder.md
* Basics is an overview of how to use simdjson and its APIs: https://github.com/simdjson/simdjson/blob/master/doc/basics.md
* Performance shows some more advanced scenarios and how to tune for them: https://github.com/simdjson/simdjson/blob/master/doc/performance.md
* Contributing: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md
* We follow the [JSON specification as described by RFC 8259](https://www.rfc-editor.org/rfc/rfc8259.txt) (T. Bray, 2017). If you wish to support features that are not part of RFC 8259, then you should not refer to your issue as a bug.
@@ -56,8 +55,6 @@ We support up-to-date 64-bit ARM and x64 FreeBSD, macOS, Windows and Linux syste
pre-release version of a compiler, do not report it as a bug to simdjson. However, we always
invite contributions either in the form an analysis or of a code contribution.
Under Windows, we support Visual Studio (both with LLVM and without). We do not support MinGW and other alternate compiler systems. Windows users should be aware that there [is a long-running bug with GCC under Windows](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412).
**Indicate whether you are willing or able to provide a bug fix as a pull request**
If you plan to contribute to simdjson, please read our guide:
+1 -2
View File
@@ -9,8 +9,7 @@ assignees: ''
Before submitting an issue, please ensure that you have read the documentation:
* Basics is an overview of how to use simdjson and its APIs to parse JSON: https://github.com/simdjson/simdjson/blob/master/doc/basics.md
* Builder is an overview of how to use simdjson to generate JSON: https://github.com/simdjson/simdjson/blob/master/doc/builder.md
* Basics is an overview of how to use simdjson and its APIs: https://github.com/simdjson/simdjson/blob/master/doc/basics.md
* Performance shows some more advanced scenarios and how to tune for them: https://github.com/simdjson/simdjson/blob/master/doc/performance.md
* Contributing: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md
* We follow the [JSON specification as described by RFC 8259](https://www.rfc-editor.org/rfc/rfc8259.txt) (T. Bray, 2017).
@@ -9,8 +9,7 @@ assignees: ''
Before submitting an issue, please ensure that you have read the documentation:
* Basics is an overview of how to use simdjson and its APIs to parse JSON: https://github.com/simdjson/simdjson/blob/master/doc/basics.md
* Builder is an overview of how to use simdjson to generate JSON: https://github.com/simdjson/simdjson/blob/master/doc/builder.md
* Basics is an overview of how to use simdjson and its APIs: https://github.com/simdjson/simdjson/blob/master/doc/basics.md
* Performance shows some more advanced scenarios and how to tune for them: https://github.com/simdjson/simdjson/blob/master/doc/performance.md
* Contributing: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md
* We follow the [JSON specification as described by RFC 8259](https://www.rfc-editor.org/rfc/rfc8259.txt) (T. Bray, 2017).
+4 -49
View File
@@ -1,53 +1,8 @@
Short title (summary):
Description
- What did you change and why? (1-3 sentences)
- Issue reproduced / related issue: link the issue if relevant (e.g. #123)
Type of change
- [ ] Bug fix
- [ ] New feature
- [ ] Refactor / cleanup
- [ ] Documentation / tests
- [ ] Other (please describe):
How to verify / test
- Add additional tests to verify bugs or new features.
- If you claim performance gains, you should provide benchmark numbers using high quality benchmarking code.
Please read before contributing:
- CONTRIBUTING: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md
- HACKING: https://github.com/simdjson/simdjson/blob/master/HACKING.md
Our tests check whether you have introduced trailing white space. If such a test fails, please check the "artifacts button" above, which if you click it gives a link to a downloadable file to help you identify the issue. You can also run scripts/remove_trailing_whitespace.sh locally if you have a bash shell and the sed command available on your system.
If you plan to contribute to simdjson, please read our
If you can, we recommend running our tests with the sanitizers turned on.
For non-Visual Studio users, it is as easy as doing:
```bash
cmake -B build -D SIMDJSON_SANITIZE=ON -D SIMDJSON_DEVELOPER_MODE=ON
cmake --build build
ctest --test-dir build
```
Our CI checks, among other things, for trailing whitespace. If a test fails for that reason,
use the "artifacts" button to download the artifact and inspect the problematic lines,
or run `scripts/remove_trailing_whitespace.sh` locally if you have a bash shell and `sed`.
Checklist before submitting
- [ ] I added/updated tests covering my change (if applicable)
- [ ] Code builds locally and passes my check
- [ ] Documentation / README updated if needed
- [ ] Commits are atomic and messages are clear
- [ ] I linked the related issue (if applicable)
Final notes
- For large PRs, prefer smaller incremental PRs or request staged review.
Thanks for the contribution!
CONTRIBUTING guide: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md and our
HACKING guide: https://github.com/simdjson/simdjson/blob/master/HACKING.md
+1 -1
View File
@@ -1,4 +1,4 @@
name: Ubuntu aarch64 (GCC 13)
name: Ubuntu ppc64le (GCC 11)
on:
push:
-29
View File
@@ -1,29 +0,0 @@
name: Ubuntu rvv VLEN=1024 (clang 18)
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Install packages
run: |
sudo apt-get update -q -y
sudo apt-get install -y cmake make g++-riscv64-linux-gnu qemu-user-static clang-18
- name: Build
run: |
CXX=clang++-18 CXXFLAGS="--target=riscv64-linux-gnu -march=rv64gcv_zvbb" \
cmake --toolchain=cmake/toolchains-ci/riscv64-linux-gnu.cmake -DCMAKE_BUILD_TYPE=Release -B build
cmake --build build/ -j$(nproc)
- name: Test VLEN=1024
run: |
export QEMU_LD_PREFIX="/usr/riscv64-linux-gnu"
export QEMU_CPU="rv64,v=on,zvbb=on,vlen=1024,rvv_ta_all_1s=on,rvv_ma_all_1s=on"
ctest --timeout 1800 --output-on-failure --test-dir build -j $(nproc)
-29
View File
@@ -1,29 +0,0 @@
name: Ubuntu rvv VLEN=128 (clang 17)
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Install packages
run: |
sudo apt-get update -q -y
sudo apt-get install -y cmake make g++-riscv64-linux-gnu qemu-user-static clang-17
- name: Build
run: |
CXX=clang++-17 CXXFLAGS="--target=riscv64-linux-gnu -march=rv64gcv" \
cmake --toolchain=cmake/toolchains-ci/riscv64-linux-gnu.cmake -DCMAKE_BUILD_TYPE=Release -B build
cmake --build build/ -j$(nproc)
- name: Test VLEN=128
run: |
export QEMU_LD_PREFIX="/usr/riscv64-linux-gnu"
export QEMU_CPU="rv64,v=on,vlen=128,rvv_ta_all_1s=on,rvv_ma_all_1s=on"
ctest --timeout 1800 --output-on-failure --test-dir build -j $(nproc)
-29
View File
@@ -1,29 +0,0 @@
name: Ubuntu rvv VLEN=256 (gcc 14)
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Install packages
run: |
sudo apt-get update -q -y
sudo apt-get install -y cmake make g++-14-riscv64-linux-gnu qemu-user-static
- name: Build
run: |
CXX=riscv64-linux-gnu-g++-14 CXXFLAGS=-march=rv64gcv \
cmake --toolchain=cmake/toolchains-ci/riscv64-linux-gnu.cmake -DCMAKE_BUILD_TYPE=Release -B build
cmake --build build/ -j$(nproc)
- name: Test VLEN=256
run: |
export QEMU_LD_PREFIX="/usr/riscv64-linux-gnu"
export QEMU_CPU="rv64,v=on,zvbb=on,vlen=256,rvv_ta_all_1s=on,rvv_ma_all_1s=on"
ctest --timeout 1800 --output-on-failure --test-dir build -j $(nproc)
+16 -48
View File
@@ -14,52 +14,20 @@ jobs:
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Configure Debug Build
- name: Use cmake
run: |
CXX=g++-12 cmake -DSIMDJSON_CXX_STANDARD=20 -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -B builddebug
- name: Compile Debug Build
run: |
cmake --build builddebug
- name: Test Debug Build
run: |
ctest --output-on-failure -LE explicitonly -j --test-dir builddebug
- name: Configure Release Build
run: |
CXX=g++-12 cmake -DSIMDJSON_CXX_STANDARD=20 -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination -B build
- name: Compile Release Build
run: |
cmake --build build
- name: Test Release Build
run: |
ctest --output-on-failure -LE explicitonly -j --test-dir build
- name: Install Release Build
run: |
cmake --install build
- name: Generate Example Code
run: |
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp
- name: Compile Example Code
run: |
c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson
- name: Run Example Code
run: |
./linkandrun jsonexamples/twitter.json
- name: Configure Find Tests
run: |
cd tests/installation_tests/find && \
cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../destination -B build
- name: Compile Find Tests
run: |
cd tests/installation_tests/find && cmake --build build
mkdir builddebug &&
cd builddebug &&
CXX=g++-12 cmake -DSIMDJSON_CXX_STANDARD=20 -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF .. &&
cmake --build . &&
ctest --output-on-failure -LE explicitonly -j &&
cd .. &&
mkdir build &&
cd build &&
CXX=g++-12 cmake -DSIMDJSON_CXX_STANDARD=20 -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest --output-on-failure -LE explicitonly -j &&
cmake --install . &&
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
cd ../tests/installation_tests/find &&
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .
@@ -1,22 +0,0 @@
name: Ubuntu 24.04 CI (CXX 20, noexcept)
on: [push, pull_request]
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-24.04
strategy:
matrix:
cxx: [g++-13, clang++-16]
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Prepare
run: cmake -DSIMDJSON_CXX_STANDARD=20 -DSIMDJSON_EXCEPTIONS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -B build
env:
CXX: ${{matrix.cxx}}
- name: Build
run: cmake --build build -j=2
- name: Test
run: ctest --output-on-failure --test-dir build
+1 -1
View File
@@ -31,4 +31,4 @@ jobs:
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
mkdir testfindpackage &&
cd testfindpackage &&
echo -e 'cmake_minimum_required(VERSION 3.14)\nproject(simdjsontester)\nset(CMAKE_CXX_STANDARD 17)\nfind_package(simdjson REQUIRED)'> CMakeLists.txt && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../destination .. && cmake --build .
echo -e 'cmake_minimum_required(VERSION 3.1)\nproject(simdjsontester)\nset(CMAKE_CXX_STANDARD 17)\nfind_package(simdjson REQUIRED)'> CMakeLists.txt && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../destination .. && cmake --build .
+1 -1
View File
@@ -31,4 +31,4 @@ jobs:
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
mkdir testfindpackage &&
cd testfindpackage &&
echo -e 'cmake_minimum_required(VERSION 3.14)\nproject(simdjsontester)\nset(CMAKE_CXX_STANDARD 17)\nfind_package(simdjson REQUIRED)'> CMakeLists.txt && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../destination .. && cmake --build .
echo -e 'cmake_minimum_required(VERSION 3.1)\nproject(simdjsontester)\nset(CMAKE_CXX_STANDARD 17)\nfind_package(simdjson REQUIRED)'> CMakeLists.txt && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../destination .. && cmake --build .
-30
View File
@@ -1,30 +0,0 @@
name: VS17-CI-SANITIZE
on: [push, pull_request]
jobs:
ci:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
name: windows-vs17
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Debug}
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Release}
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: RelWithDebInfo}
steps:
- name: checkout
uses: actions/checkout@v4
- name: Configure
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSANITIZE=ON -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -DBUILD_SHARED_LIBS=${{matrix.shared}} -B build
- name: Build
run: cmake --build build --config ${{matrix.build_type}} --verbose
- name: Run tests
run: |
cd build
ctest -C ${{matrix.build_type}} -LE explicitonly --output-on-failure
+21
View File
@@ -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
-3
View File
@@ -3,9 +3,6 @@
{"column": 95 },
{"column": 120 }
],
"cmake.configureArgs": [
"-DSIMDJSON_DEVELOPER_MODE=ON"
],
"files.trimTrailingWhitespace": true,
"files.associations": {
".clangd": "yaml",
+3 -3
View File
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14)
project(
simdjson
# The version number is modified by tools/release.py
VERSION 4.0.7
VERSION 4.0.0
DESCRIPTION "Parsing gigabytes of JSON per second"
HOMEPAGE_URL "https://simdjson.org/"
LANGUAGES CXX C
@@ -20,8 +20,8 @@ string(
# ---- Options, variables ----
# These version numbers are modified by tools/release.py
set(SIMDJSON_LIB_VERSION "27.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "27" 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 = "4.0.7"
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
+8 -46
View File
@@ -20,54 +20,13 @@ If you plan to contribute to simdjson, please read our [CONTRIBUTING](https://gi
Build Quickstart
------------------------------
For non-Windows system,
```bash
cmake -B -D SIMDJSON_DEVELOPER_MODE=ON ..
cmake --build build
ctest --test-dir build
mkdir build
cd build
cmake -D SIMDJSON_DEVELOPER_MODE=ON ..
cmake --build .
```
It is similar for Visual Studio users, please see the CMake or Visual Studio documentation.
By default the library is built in Release mode.
Assertions and development checks
------------------------------
We do not use conventional `assert` in simdjson. Instead we use the macro
`SIMDJSON_ASSUME`:
```cpp
SIMDJSON_ASSUME(something_that_is_true());
```
Sometimes, you need to do a bit more work that a simple check.
The `SIMDJSON_DEVELOPMENT_CHECKS` macro is true only in Debug mode unless manually set.
It is acceptable to add checks that you would not do in Release mode as long as
they are guarded:
```cpp
#if SIMDJSON_DEVELOPMENT_CHECKS
// do sanity checks here
```
Working with sanitizers
------------------------------
The simdjson library must be memory-safe. We cannot allow buffer overruns.
During development, if you system supports it, we recommend configuring
the project with `-D SIMDJSON_SANITIZE=ON`.
```bash
cmake -B -D SIMDJSON_SANITIZE=ON -D SIMDJSON_DEVELOPER_MODE=ON ..
cmake --build build
ctest --test-dir build
```
Design notes
------------------------------
@@ -144,9 +103,12 @@ simdjson's source structure, from the top level, looks like this:
* generic/stage2/*.h: `simdjson::<implementation>::stage2` namespace. Generic implementation of the tape creator, which consumes the index from stage 1 and actually parses numbers and string and such. Used for the DOM interface.
Other important files and directories:
* **.drone.yml:** Definitions for Drone CI.
* **.appveyor.yml:** Definitions for Appveyor CI (Windows).
* **.circleci:** Definitions for Circle CI.
* **.github/workflows:** Definitions for GitHub Actions (CI).
* **singleheader:** Contains generated `simdjson.h` and `simdjson.cpp` that we release. The files `singleheader/simdjson.h` and `singleheader/simdjson.cpp` should never be edited by hand.
* **singleheader/amalgamate.py:** Generates `singleheader/simdjson.h` and `singleheader/simdjson.cpp` for release (python script). If you add a new implementation (e.g., rvv), you need to edit this file (IMPLEMENTATIONS).
* **singleheader/amalgamate.py:** Generates `singleheader/simdjson.h` and `singleheader/simdjson.cpp` for release (python script).
* **benchmark:** This is where we do benchmarking. Benchmarking is core to every change we make; the
cardinal rule is don't regress performance without knowing exactly why, and what you're trading
for it. Many of our benchmarks are microbenchmarks. We are effectively doing controlled scientific experiments for the purpose of understanding what affects our performance. So we simplify as much as possible. We try to avoid irrelevant factors such as page faults, interrupts, unnecessary system calls. We recommend checking the performance as follows:
+2 -2
View File
@@ -1,3 +1,5 @@
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/simdjson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:simdjson)
[![][license img]][license] [![][licensemit img]][licensemit]
@@ -62,7 +64,6 @@ Real-world usage
- [WasmEdge](https://wasmedge.org)
- [RonDB](https://github.com/logicalclocks/rondb)
- [GreptimeDB](https://github.com/GreptimeTeam/greptimedb)
- [mamba](https://github.com/mamba-org/mamba)
If you are planning to use simdjson in a product, please work from one of our releases.
@@ -118,7 +119,6 @@ Godbolt
-------------
Some users may want to browse code along with the compiled assembly. You want to check out the following lists of examples:
* [C++26 reflection example](https://godbolt.org/z/K3Px64TqK)
* [simdjson examples with errors handled through exceptions](https://godbolt.org/z/7G5qE4sr9)
* [simdjson examples with errors without exceptions](https://godbolt.org/z/e9dWb9E4v)
+1 -11
View File
@@ -35,16 +35,6 @@ if (TARGET benchmark::benchmark)
endif()
endif()
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("-std=c++20" SIMDJSON_COMPILER_SUPPORTS_CXX20)
if(SIMDJSON_STATIC_REFLECTION)
add_subdirectory(static_reflect)
else()
if(SIMDJSON_EXCEPTIONS AND SIMDJSON_COMPILER_SUPPORTS_CXX20)
add_subdirectory(from)
add_subdirectory(car_builder)
endif()
endif(SIMDJSON_STATIC_REFLECTION)
endif(SIMDJSON_STATIC_REFLECTION)
-14
View File
@@ -1,14 +0,0 @@
# Executable
add_executable(benchmark_car_builder benchmark_car_builder.cpp)
# Compile for C++20.
target_compile_features(benchmark_car_builder PRIVATE cxx_std_20)
# Check if -march=native is supported
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("-march=native" SIMDJSON_SUPPORTS_MARCH_NATIVE)
if(SIMDJSON_SUPPORTS_MARCH_NATIVE)
target_compile_options(benchmark_car_builder PRIVATE -march=native)
endif()
target_include_directories(benchmark_car_builder PRIVATE ${CMAKE_CURRENT_LIST_DIR}/..)
@@ -1,127 +0,0 @@
#include "event_counter.h"
#include <random>
#include <vector>
#include <simdjson.h>
event_collector collector;
struct Car {
std::string make;
std::string model;
int64_t year; // We deliberately do not include the tire pressure.
};
std::vector<Car> generate_random_cars(size_t count) {
static const std::vector<std::string> makes = {"Toyota", "Honda", "Ford",
"BMW", "Mazda"};
static const std::vector<std::string> models = {"Camry", "Civic", "Focus",
"320i", "3"};
static thread_local std::mt19937 rng{std::random_device{}()};
std::uniform_int_distribution<int> make_dist(0, makes.size() - 1);
std::uniform_int_distribution<int> model_dist(0, models.size() - 1);
std::uniform_int_distribution<int64_t> year_dist(2000, 2025);
std::uniform_real_distribution<double> pressure_dist(30.0, 45.0);
std::vector<Car> cars;
cars.reserve(count);
for (size_t i = 0; i < count; ++i) {
Car car;
car.make = makes[make_dist(rng)];
car.model = models[model_dist(rng)];
car.year = year_dist(rng);
cars.push_back(std::move(car));
}
return cars;
}
std::string_view serialize(simdjson::builder::string_builder &sb,
const std::vector<Car> &cars) {
sb.clear();
sb.start_array();
for (const auto &car : cars) {
sb.start_object();
sb.append_key_value("make", car.make);
sb.append_comma();
sb.append_key_value("model", car.model);
sb.append_comma();
sb.append_key_value("year", car.year);
sb.end_object();
}
sb.end_array();
std::string_view result;
if (sb.view().get(result)) {
return ""; // unexpected (error)
}
return result;
}
double pretty_print(const std::string &name, size_t num_chars,
std::pair<event_aggregate, size_t> result) {
const auto &agg = result.first;
size_t N = result.second;
num_chars *= N;
printf("%-40s : %8.2f ns %8.2f GB/s", name.c_str(),
agg.elapsed_ns() / num_chars, num_chars / agg.elapsed_ns());
if (collector.has_events()) {
printf(" %8.2f GHz %8.2f cycles/char %8.2f ins./char %8.2f i/c",
agg.cycles() / agg.elapsed_ns(), agg.cycles() / num_chars,
agg.instructions() / num_chars, agg.instructions() / agg.cycles());
}
printf("\n");
return num_chars / agg.elapsed_ns();
}
template <class function_type>
std::pair<event_aggregate, size_t>
bench(const function_type &&function, size_t min_repeat = 100,
size_t min_time_ns = 40'000'000, size_t max_repeat = 10000000) {
size_t N = min_repeat;
if (N == 0) {
N = 1;
}
event_aggregate warm_aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
function();
std::atomic_thread_fence(std::memory_order_release);
event_count allocate_count = collector.end();
warm_aggregate << allocate_count;
if ((i + 1 == N) && (warm_aggregate.total_elapsed_ns() < min_time_ns) &&
(N < max_repeat)) {
N *= 10;
}
}
event_aggregate aggregate{};
for (size_t i = 0; i < 10; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (size_t i = 0; i < N; i++) {
function();
}
std::atomic_thread_fence(std::memory_order_release);
event_count allocate_count = collector.end();
aggregate << allocate_count;
}
return {aggregate, N};
}
void run_benchmarks() {
std::vector<Car> source = generate_random_cars(100000);
simdjson::builder::string_builder sb;
size_t volume = serialize(sb, source).size();
pretty_print("string_builder", volume, bench([&source, &sb]() -> size_t {
return serialize(sb, source).size();
}));
}
int main() {
for (size_t trial = 0; trial < 3; trial++) {
printf("Trial %zu:\n", trial + 1);
run_benchmarks();
printf("\n");
}
return EXIT_SUCCESS;
}
+2 -9
View File
@@ -44,10 +44,7 @@ struct yyjson_base {
struct yyjson : yyjson_base {
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
yyjson_doc *doc = yyjson_read(json.data(), json.size(), 0);
bool b = yyjson_base::run(doc, result);
yyjson_doc_free(doc);
return b;
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), result);
}
};
BENCHMARK_TEMPLATE(distinct_user_id, yyjson)->UseManualTime();
@@ -55,15 +52,11 @@ BENCHMARK_TEMPLATE(distinct_user_id, yyjson)->UseManualTime();
#if SIMDJSON_COMPETITION_ONDEMAND_INSITU
struct yyjson_insitu : yyjson_base {
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
yyjson_doc *doc = yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0);
bool b = yyjson_base::run(doc, result);
yyjson_doc_free(doc);
return b;
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), result);
}
};
BENCHMARK_TEMPLATE(distinct_user_id, yyjson_insitu)->UseManualTime();
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
} // namespace distinct_user_id
#endif // SIMDJSON_COMPETITION_YYJSON
+3 -2
View File
@@ -25,11 +25,12 @@ int main(int argc, char *argv[]) {
exit(1);
}
const char *filename = argv[1];
simdjson::padded_string p;
if (simdjson::padded_string::load(filename).get(p)) {
auto v = simdjson::padded_string::load(filename);
if (v.error()) {
std::cerr << "Could not load the file " << filename << std::endl;
return EXIT_FAILURE;
}
const simdjson::padded_string& p = v.value_unsafe();
if (test_baseline) {
std::wclog << "Baseline: Getline + normal parse... " << std::endl;
std::cout << "Gigabytes/second\t"
+2 -10
View File
@@ -36,26 +36,18 @@ struct yyjson_base {
struct yyjson : yyjson_base {
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
yyjson_doc *doc = yyjson_read(json.data(), json.size(), 0);
bool b = yyjson_base::run(doc, find_id, result);
yyjson_doc_free(doc);
return b;
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), find_id, result);
}
};
BENCHMARK_TEMPLATE(find_tweet, yyjson)->UseManualTime();
#if SIMDJSON_COMPETITION_ONDEMAND_INSITU
struct yyjson_insitu : yyjson_base {
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
yyjson_doc *doc = yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0);
bool b = yyjson_base::run(doc, find_id, result);
yyjson_doc_free(doc);
return b;
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), find_id, result);
}
};
BENCHMARK_TEMPLATE(find_tweet, yyjson_insitu)->UseManualTime();
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
} // namespace find_tweet
#endif // SIMDJSON_COMPETITION_YYJSON
-15
View File
@@ -1,15 +0,0 @@
# Executable
add_executable(from_benchmark from_benchmark.cpp)
# Compile for C++20.
if(CMAKE_CXX_STANDARD LESS 20)
target_compile_features(from_benchmark PRIVATE cxx_std_20)
endif()
# Check if -march=native is supported
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("-march=native" SIMDJSON_SUPPORTS_MARCH_NATIVE)
if(SIMDJSON_SUPPORTS_MARCH_NATIVE)
target_compile_options(from_benchmark PRIVATE -march=native)
endif()
target_include_directories(from_benchmark PRIVATE ${CMAKE_CURRENT_LIST_DIR}/..)
-77
View File
@@ -1,77 +0,0 @@
#ifndef BENCHMARK_HELPERS_H
#define BENCHMARK_HELPERS_H
#include "event_counter.h"
#include <atomic>
event_collector collector;
template <class function_type>
std::pair<event_aggregate, size_t>
bench(const function_type &&function, size_t min_repeat = 10,
size_t min_time_ns = 40'000'000, size_t max_repeat = 10000000) {
size_t N = min_repeat;
if (N == 0) {
N = 1;
}
event_aggregate warm_aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
function();
std::atomic_thread_fence(std::memory_order_release);
event_count allocate_count = collector.end();
warm_aggregate << allocate_count;
if ((i + 1 == N) && (warm_aggregate.total_elapsed_ns() < min_time_ns) &&
(N < max_repeat)) {
N *= 10;
}
}
event_aggregate aggregate{};
for (size_t i = 0; i < 10; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (size_t i = 0; i < N; i++) {
function();
}
std::atomic_thread_fence(std::memory_order_release);
event_count allocate_count = collector.end();
aggregate << allocate_count;
}
return {aggregate, N};
}
double pretty_print(const std::string &name, size_t num_chars,
std::pair<event_aggregate, size_t> result) {
const auto &agg = result.first;
size_t N = result.second;
num_chars *= N;
printf("%-40s : %8.2f ns %8.2f GB/s", name.c_str(),
agg.elapsed_ns() / num_chars, num_chars / agg.elapsed_ns());
if (collector.has_events()) {
printf(" %8.2f GHz %8.2f cycles/char %8.2f ins./char %8.2f i/c",
agg.cycles() / agg.elapsed_ns(), agg.cycles() / num_chars,
agg.instructions() / num_chars, agg.instructions() / agg.cycles());
}
printf("\n");
return num_chars / agg.elapsed_ns();
}
double pretty_print_array(const std::string &name, size_t num_chars, size_t num_elements,
std::pair<event_aggregate, size_t> result) {
const auto &agg = result.first;
size_t N = result.second;
num_chars *= N;
printf("%-40s : %8.2f ns/char %8.2f ns/value %8.2f GB/s", name.c_str(),
agg.elapsed_ns() / num_chars, agg.elapsed_ns() / num_elements, num_chars / agg.elapsed_ns());
if (collector.has_events()) {
printf(" %8.2f GHz %8.2f cycles/char %8.2f ins./char %8.2f ins./value %8.2f i/c",
agg.cycles() / agg.elapsed_ns(), agg.cycles() / num_chars,
agg.instructions() / num_chars, agg.instructions() / num_elements, agg.instructions() / agg.cycles());
}
printf("\n");
return num_chars / agg.elapsed_ns();
}
#endif
-146
View File
@@ -1,146 +0,0 @@
#ifndef CAR_HELPERS_H
#define CAR_HELPERS_H
#include <iomanip>
#include <random>
#include <sstream>
#include <vector>
#include <simdjson.h>
struct Car {
std::string make;
std::string model;
int64_t year; // We deliberately do not include the tire pressure.
};
using namespace simdjson;
std::string random_car_json() {
static const std::vector<std::string> makes = {"Toyota", "Honda", "Ford",
"BMW", "Mazda"};
static const std::vector<std::string> models = {"Camry", "Civic", "Focus",
"320i", "3"};
static thread_local std::mt19937 rng{std::random_device{}()};
std::uniform_int_distribution<int> make_dist(0, makes.size() - 1);
std::uniform_int_distribution<int> model_dist(0, models.size() - 1);
std::uniform_int_distribution<int> year_dist(2000, 2025);
std::uniform_real_distribution<double> pressure_dist(30.0, 45.0);
std::ostringstream oss;
oss << R"({ "make": ")" << makes[make_dist(rng)] << R"(", "model": ")"
<< models[model_dist(rng)] << R"(", "year": )" << year_dist(rng)
<< R"(, "tire_pressure": [ )" << std::fixed << std::setprecision(1)
<< pressure_dist(rng) << ", " << pressure_dist(rng) << R"( ] })";
return oss.str();
}
std::string generate_car_json_stream(size_t N) {
std::ostringstream oss;
for (size_t i = 0; i < N; ++i) {
oss << random_car_json();
oss << "\n";
}
return oss.str();
}
std::string generate_car_json_array(size_t N) {
std::ostringstream oss;
oss << "[\n";
for (size_t i = 0; i < N; ++i) {
oss << random_car_json();
if (i < N - 1) {
oss << ",";
}
oss << "\n";
}
oss << "]";
return oss.str();
}
// We want to maximize C++ portability, but this is not needed with static
// reflection (C++26).
template <>
simdjson_inline simdjson_result<Car>
simdjson::ondemand::document::get() & noexcept {
ondemand::object obj;
auto error = get_object().get(obj);
if (error) {
return error;
}
Car car;
if ((error = obj["make"].get_string(car.make))) {
return error;
}
if ((error = obj["model"].get_string(car.model))) {
return error;
}
if ((error = obj["year"].get_int64().get(car.year))) {
return error;
}
return car;
}
template <>
simdjson_inline simdjson_result<Car> simdjson::ondemand::value::get() noexcept {
ondemand::object obj;
auto error = get_object().get(obj);
if (error) {
return error;
}
Car car;
if ((error = obj["make"].get_string(car.make))) {
return error;
}
if ((error = obj["model"].get_string(car.model))) {
return error;
}
if ((error = obj["year"].get_int64().get(car.year))) {
return error;
}
return car;
}
template <>
simdjson_inline simdjson_result<Car>
simdjson::ondemand::document_reference::get() & noexcept {
ondemand::object obj;
auto error = get_object().get(obj);
if (error) {
return error;
}
Car car;
if ((error = obj["make"].get_string(car.make))) {
return error;
}
if ((error = obj["model"].get_string(car.model))) {
return error;
}
if ((error = obj["year"].get_int64().get(car.year))) {
return error;
}
return car;
}
template <>
simdjson_inline simdjson_result<Car>
simdjson::ondemand::document_reference::get() && noexcept {
ondemand::object obj;
auto error = get_object().get(obj);
if (error) {
return error;
}
Car car;
if ((error = obj["make"].get_string(car.make))) {
return error;
}
if ((error = obj["model"].get_string(car.model))) {
return error;
}
if ((error = obj["year"].get_int64().get(car.year))) {
return error;
}
return car;
}
#endif
-132
View File
@@ -1,132 +0,0 @@
// We are going to assume C++20.
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <simdjson.h>
#include <string>
#include "benchmark_helpers.h"
#include "car_helpers.h"
using namespace simdjson;
void run_benchmarks() {
printf("Running benchmarks on tiny input...\n");
simdjson::padded_string json =
R"({ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9 ] })"_padded;
volatile size_t dummy = 0;
ondemand::parser parser;
// Classic On-Demand
pretty_print("simdjson classic", json.size(),
bench([&json, &dummy, &parser]() {
ondemand::document doc = parser.iterate(json);
Car car = doc.get<Car>();
dummy = dummy + car.year;
}));
// Benchmark simdjson::from() without parser
pretty_print("simdjson::from<Car>() (no parser)", json.size(),
bench([&json, &dummy]() {
Car car = simdjson::from(json);
dummy = dummy + car.year;
}));
// Benchmark simdjson::from() with parser
pretty_print("simdjson::from<Car>() (with parser)", json.size(),
bench([&json, &dummy, &parser]() {
Car car = simdjson::from(parser, json);
dummy = dummy + car.year;
}));
}
void run_array_benchmarks() {
printf("Running benchmarks on large array...\n");
size_t N = 1'000'000;
simdjson::padded_string json = generate_car_json_array(N);
volatile size_t dummy = 0;
ondemand::parser parser;
// Classic On-Demand
pretty_print_array("simdjson classic", json.size(), N,
bench([&json, &dummy, &parser]() {
dummy = 0;
ondemand::document doc = parser.iterate(json);
ondemand::array array = doc.get_array();
for (auto value : array) {
Car car = value.get<Car>();
dummy = dummy + car.year;
}
}));
// from with array
pretty_print_array("simdjson::from(json).array()", json.size(), N,
bench([&json, &dummy, &parser]() {
dummy = 0;
for (auto value : simdjson::from(json).array()) {
Car car = value.get<Car>();
dummy = dummy + car.year;
}
}));
#if SIMDJSON_SUPPORTS_RANGES_FROM
// Benchmark simdjson::from() without parser (EXPERIMENTAL)
pretty_print_array("simdjson::from<Car>() (experimental, no parser)",
json.size(), N, bench([&json, &dummy]() {
dummy = 0;
for (Car car :
simdjson::from(json) | simdjson::as<Car>()) {
dummy = dummy + car.year;
}
}));
// Benchmark simdjson::from() with parser (EXPERIMENTAL)
pretty_print_array("simdjson::from<Car>() (experimental, with parser)",
json.size(), N, bench([&json, &dummy, &parser]() {
dummy = 0;
for (Car car : simdjson::from(parser, json) |
simdjson::as<Car>()) {
dummy = dummy + car.year;
}
}));
#endif // SIMDJSON_SUPPORTS_RANGES_FROM
}
void run_stream_benchmarks() {
printf("Running stream benchmarks...\n");
simdjson::padded_string json = generate_car_json_stream(1'000'000);
volatile size_t dummy = 0;
ondemand::parser parser;
// Classic On-Demand
pretty_print("simdjson classic", json.size(),
bench([&json, &dummy, &parser]() {
ondemand::document_stream stream = parser.iterate_many(json);
for (auto doc : stream) {
Car car = doc.get<Car>();
dummy = dummy + car.year;
}
}));
// from_many
pretty_print("simdjson with thread local", json.size(),
bench([&json, &dummy, &parser]() {
ondemand::document_stream stream =
ondemand::parser::get_parser().iterate_many(json);
for (auto doc : stream) {
Car car = doc.get<Car>();
dummy = dummy + car.year;
}
}));
}
int main() {
for (size_t trial = 0; trial < 3; trial++) {
printf("Trial %zu:\n", trial + 1);
run_array_benchmarks();
run_stream_benchmarks();
run_benchmarks();
printf("\n");
}
return EXIT_SUCCESS;
}
+1 -5
View File
@@ -9,11 +9,7 @@ simdjson_never_inline
double bench(std::string filename, simdjson::padded_string& p) {
std::chrono::time_point<std::chrono::steady_clock> start_clock =
std::chrono::steady_clock::now();
auto error = simdjson::padded_string::load(filename).get(p);
if(error) {
std::cerr << simdjson::error_message(error) << std::endl;
std::abort();
}
simdjson::padded_string::load(filename).value_unsafe().swap(p);
std::chrono::time_point<std::chrono::steady_clock> end_clock =
std::chrono::steady_clock::now();
std::chrono::duration<double> elapsed = end_clock - start_clock;
+1 -3
View File
@@ -100,7 +100,6 @@ struct yyjson : yyjson2msgpack {
std::string_view &result) {
yyjson_doc *doc = yyjson_read(json.data(), json.size(), 0);
result = to_msgpack(doc, reinterpret_cast<uint8_t*>(buffer));
yyjson_doc_free(doc);
return true;
}
};
@@ -114,7 +113,6 @@ struct yyjson_insitu : yyjson2msgpack {
yyjson_doc *doc =
yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0);
result = to_msgpack(doc, reinterpret_cast<uint8_t*>(buffer));
yyjson_doc_free(doc);
return true;
}
};
@@ -122,4 +120,4 @@ BENCHMARK_TEMPLATE(json2msgpack, yyjson_insitu)->UseManualTime();
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
} // namespace json2msgpack
#endif // SIMDJSON_COMPETITION_YYJSON
#endif // SIMDJSON_COMPETITION_YYJSON
+2 -10
View File
@@ -49,26 +49,18 @@ struct yyjson_base {
struct yyjson : yyjson_base {
bool run(simdjson::padded_string &json, std::vector<point> &result) {
yyjson_doc *doc = yyjson_read(json.data(), json.size(), 0);
bool b = yyjson_base::run(doc, result);
yyjson_doc_free(doc);
return b;
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), result);
}
};
BENCHMARK_TEMPLATE(kostya, yyjson)->UseManualTime();
#if SIMDJSON_COMPETITION_ONDEMAND_INSITU
struct yyjson_insitu : yyjson_base {
bool run(simdjson::padded_string &json, std::vector<point> &result) {
yyjson_doc *doc = yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0);
bool b = yyjson_base::run(doc, result);
yyjson_doc_free(doc);
return b;
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), result);
}
};
BENCHMARK_TEMPLATE(kostya, yyjson_insitu)->UseManualTime();
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
} // namespace kostya
#endif // SIMDJSON_COMPETITION_YYJSON
+2 -10
View File
@@ -47,26 +47,18 @@ struct yyjson_base {
struct yyjson : yyjson_base {
bool run(simdjson::padded_string &json, std::vector<point> &result) {
yyjson_doc *doc = yyjson_read(json.data(), json.size(), 0);
bool b = yyjson_base::run(doc, result);
yyjson_doc_free(doc);
return b;
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), result);
}
};
BENCHMARK_TEMPLATE(large_random, yyjson)->UseManualTime();
#if SIMDJSON_COMPETITION_ONDEMAND_INSITU
struct yyjson_insitu : yyjson_base {
bool run(simdjson::padded_string &json, std::vector<point> &result) {
yyjson_doc *doc = yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0);
bool b = yyjson_base::run(doc, result);
yyjson_doc_free(doc);
return b;
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), result);
}
};
BENCHMARK_TEMPLATE(large_random, yyjson_insitu)->UseManualTime();
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
} // namespace large_random
#endif // SIMDJSON_COMPETITION_YYJSON
+3 -10
View File
@@ -62,26 +62,19 @@ struct yyjson_base {
struct yyjson : yyjson_base {
bool run(simdjson::padded_string &json, std::vector<tweet<std::string_view>> &result) {
yyjson_doc *doc = yyjson_read(json.data(), json.size(), 0);
bool b = yyjson_base::run(doc, result);
yyjson_doc_free(doc);
return b;
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), result);
}
};
BENCHMARK_TEMPLATE(partial_tweets, yyjson)->UseManualTime();
#if SIMDJSON_COMPETITION_ONDEMAND_INSITU
struct yyjson_insitu : yyjson_base {
bool run(simdjson::padded_string &json, std::vector<tweet<std::string_view>> &result) {
yyjson_doc *doc = yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0);
bool b = yyjson_base::run(doc, result);
yyjson_doc_free(doc);
return b;
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), result);
}
};
BENCHMARK_TEMPLATE(partial_tweets, yyjson_insitu)->UseManualTime();
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
} // namespace partial_tweets
#endif // SIMDJSON_COMPETITION_YYJSON
+29 -33
View File
@@ -6,42 +6,38 @@ CPMAddPackage(
EXCLUDE_FROM_ALL YES
)
option(SIMDJSON_USE_RUST "Build the static_reflect benchmark" OFF)
if(SIMDJSON_USER_RUST)
if(NOT WIN32)
# We want the check whether Rust is available before trying to build a crate.
CPMAddPackage(
NAME corrosion
GITHUB_REPOSITORY corrosion-rs/corrosion
VERSION 0.4.4
DOWNLOAD_ONLY ON
OPTIONS "Rust_FIND_QUIETLY OFF"
)
include("${corrosion_SOURCE_DIR}/cmake/FindRust.cmake")
endif()
if(RUST_FOUND)
message(STATUS "Rust found: " ${Rust_VERSION} )
add_subdirectory("${corrosion_SOURCE_DIR}" "${PROJECT_BINARY_DIR}/_deps/corrosion" EXCLUDE_FROM_ALL)
# Important: we want to build in release mode!
corrosion_import_crate(MANIFEST_PATH "serde-benchmark/Cargo.toml" NO_LINKER_OVERRIDE PROFILE release)
else()
message(STATUS "Rust/Cargo is unavailable." )
message(STATUS "We will not benchmark serde-benchmark." )
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
message(STATUS "Under macOS, you may be able to install rust with")
message(STATUS "curl https://sh.rustup.rs -sSf | sh")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
message(STATUS "Under Linux, you may be able to install rust with a command such as")
message(STATUS "apt-get install cargo" )
message(STATUS "or" )
message(STATUS "curl https://sh.rustup.rs -sSf | sh")
endif()
endif()
else(SIMDJSON_USER_RUST)
if(NOT WIN32)
# We want the check whether Rust is available before trying to build a crate.
CPMAddPackage(
NAME corrosion
GITHUB_REPOSITORY corrosion-rs/corrosion
VERSION 0.4.4
DOWNLOAD_ONLY ON
OPTIONS "Rust_FIND_QUIETLY OFF"
)
include("${corrosion_SOURCE_DIR}/cmake/FindRust.cmake")
endif()
if(RUST_FOUND)
message(STATUS "Rust found: " ${Rust_VERSION} )
add_subdirectory("${corrosion_SOURCE_DIR}" "${PROJECT_BINARY_DIR}/_deps/corrosion" EXCLUDE_FROM_ALL)
# Important: we want to build in release mode!
corrosion_import_crate(MANIFEST_PATH "serde-benchmark/Cargo.toml" NO_LINKER_OVERRIDE PROFILE release)
else()
message(STATUS "Rust/Cargo is unavailable." )
message(STATUS "We will not benchmark serde-benchmark." )
endif(SIMDJSON_USER_RUST)
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
message(STATUS "Under macOS, you may be able to install rust with")
message(STATUS "curl https://sh.rustup.rs -sSf | sh")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
message(STATUS "Under Linux, you may be able to install rust with a command such as")
message(STATUS "apt-get install cargo" )
message(STATUS "or" )
message(STATUS "curl https://sh.rustup.rs -sSf | sh")
endif()
endif()
# Add the benchmark executable targets
add_subdirectory(twitter_benchmark)
+2 -10
View File
@@ -51,26 +51,18 @@ struct yyjson_base {
struct yyjson : yyjson_base {
bool run(simdjson::padded_string &json, int64_t max_retweet_count, top_tweet_result<StringType> &result) {
yyjson_doc *doc = yyjson_read(json.data(), json.size(), 0);
bool b = yyjson_base::run(doc, max_retweet_count, result);
yyjson_doc_free(doc);
return b;
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), max_retweet_count, result);
}
};
BENCHMARK_TEMPLATE(top_tweet, yyjson)->UseManualTime();
#if SIMDJSON_COMPETITION_ONDEMAND_INSITU
struct yyjson_insitu : yyjson_base {
bool run(simdjson::padded_string &json, int64_t max_retweet_count, top_tweet_result<StringType> &result) {
yyjson_doc *doc = yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0);
bool b = yyjson_base::run(doc, max_retweet_count, result);
yyjson_doc_free(doc);
return b;
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), max_retweet_count, result);
}
};
BENCHMARK_TEMPLATE(top_tweet, yyjson_insitu)->UseManualTime();
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
} // namespace top_tweet
#endif // SIMDJSON_COMPETITION_YYJSON
+1 -8
View File
@@ -4,7 +4,7 @@
add_library(simdjson-internal-flags INTERFACE)
if(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
# We default to ON for all targets, so that we can use the library in shared libraries.
set_target_properties(simdjson-internal-flags PROPERTIES INTERFACE_POSITION_INDEPENDENT_CODE ON)
set_target_properties(simdjson-internal-flags PROPERTIES POSITION_INDEPENDENT_CODE ON)
endif(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
option(SIMDJSON_CHECK_EOF "Check for the end of the input buffer. The setting is unnecessary since we require padding of the inputs. You should expect tests to fail with this option turned on." OFF)
@@ -32,9 +32,6 @@ undefined behavior.")
link_libraries(
-fsanitize=address -fno-omit-frame-pointer -fno-sanitize-recover=all
)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
add_compile_options(-fsanitize=address)
link_libraries(-fsanitize=address)
else()
message(
STATUS
@@ -174,10 +171,6 @@ else()
-Werror -Wall -Wextra -Weffc++ -Wsign-compare -Wshadow -Wwrite-strings
-Wpointer-arith -Winit-self -Wconversion -Wno-sign-conversion
)
if(CMAKE_CXX_STANDARD VERSION_GREATER_EQUAL 20)
target_compile_options(simdjson-internal-flags INTERFACE -Wctad-maybe-unsupported)
endif()
endif()
option(SIMDJSON_GLIBCXX_ASSERTIONS "Set _GLIBCXX_ASSERTIONS" OFF)
@@ -1,4 +0,0 @@
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR riscv64)
set(CMAKE_CROSSCOMPILING_EMULATOR "qemu-riscv64-static")
+113 -290
View File
@@ -5,47 +5,47 @@ An overview of what you need to know to use simdjson to parse JSON documents, wi
[Our documentation regarding the generation (serialization) of JSON documents is in a
separate document](https://github.com/simdjson/simdjson/blob/master/doc/builder.md).
- [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)
* [3. Using static reflection (C++26)](#3-using-static-reflection-c26)
* [The simdjson::from shortcut (experimental, C++20)](#the-simdjsonfrom-shortcut-experimental-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)
* [Raw JSON string for objects and arrays](#raw-json-string-for-objects-and-arrays)
- [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)
- [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--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
------------------
@@ -54,7 +54,7 @@ The simdjson library is widely deployed in popular systems such as the Node.js r
environment.
- A recent compiler (LLVM clang 6 or better, GNU GCC 7.4 or better, Xcode 11 or better) on POSIX systems such as macOS, FreeBSD or Linux. We require that the compiler supports the C++11 standard or better. We test the library on a big-endian system (IBM s390x with Linux).
- Visual Studio 2017 or better. We support the LLVM clang compiler under Visual Studio (clang-cl) as well as the regular Visual Studio compiler. For better release performance (both compile time and execution time), we recommend Visual Studio users adopt LLVM (clang-cl). We discourage using GCC under Windows: there [is a long-running bug with GCC under Windows](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412).
- Visual Studio 2017 or better. We support the LLVM clang compiler under Visual Studio (clang-cl) as well as as the regular Visual Studio compiler. For better release performance (both compile time and execution time), we recommend Visual Studio users adopt LLVM (clang-cl). We also support MinGW 64-bit under Windows.
Support for AVX-512 require a processor with AVX512-VBMI2 support (Ice Lake or better, AMD Zen 4 or better) under a 64-bit system and a recent compiler (LLVM clang 6 or better, GCC 8 or better, Visual Studio 2019 or better). You need a correspondingly recent assembler such as gas (2.30+) or nasm (2.14+): recent compilers usually come with recent assemblers. If you mix a recent compiler with an incompatible/old assembler (e.g., when using a recent compiler with an old Linux distribution), you may get errors at build time because the compiler produces instructions that the assembler does not recognize: you should update your assembler to match your compiler (e.g., upgrade binutils to version 2.30 or better under Linux) or use an older compiler matching the capabilities of your assembler.
@@ -78,14 +78,14 @@ c++ myproject.cpp simdjson.cpp
```
Note:
- We recommend that you use simdjson by copying the single-header `simdjson.h` file along with the source file `simdjson.cpp` directly into your project, as they are part of [every release](https://github.com/simdjson/simdjson/releases) as assets. In this manner, you only have to compile `simdjson.cpp` as any other source file: it works well in every development environment. However, you may also use simdjson as a git submodule ([example](https://github.com/simdjson/cmakedemo)), using FetchContent ([example](https://github.com/simdjson/cmake_demo_single_file)), with ExternalProject_Add ([example](https://github.com/simdjson/cmakedemo_externalproject)) or with CPM ([example](https://github.com/cpm-cmake/CPM.cmake/tree/master/examples/simdjson)).
- We recommend that you use simdjson by copying the single-header `simdjson.h` file along with the source file `simdjson.cpp` directly in your project, as they are part of [every release](https://github.com/simdjson/simdjson/releases) as assets. In this manner, you only have to compile `simdjson.cpp` as any other source file: it works well in every development environment. However, you may also use simdjson as a git submodule ([example](https://github.com/simdjson/cmakedemo)), using FetchContent ([example](https://github.com/simdjson/cmake_demo_single_file)), with ExternalProject_Add ([example](https://github.com/simdjson/cmakedemo_externalproject)) or with CPM ([example](https://github.com/cpm-cmake/CPM.cmake/tree/master/examples/simdjson)).
- Users on macOS and other platforms where default compilers do not provide C++11 compliant by default should request it with the appropriate flag (e.g., `c++ -std=c++11 myproject.cpp simdjson.cpp`).
- The library relies on [runtime CPU detection](implementation-selection.md): avoid specifying an architecture at compile time (e.g., `-march-native`) if you want your binaries to run everywhere.
Using simdjson with package managers
------------------
You can install the simdjson library on your system or in your project using multiple package managers such as MSYS2, the conan package manager, vcpkg, brew, the apt package manager (debian-based Linux systems), the FreeBSD package manager (FreeBSD), and so on. E.g., [we provide a complete example with vcpkg](https://github.com/simdjson/simdjson-vcpkg) that works under Windows. [Visit our wiki for more details](https://github.com/simdjson/simdjson/wiki/Installing-simdjson-with-a-package-manager).
You can install the simdjson library on your system or in your project using multiple package managers such as MSYS2, the conan package manager, vcpkg, brew, the apt package manager (debian-based Linux systems), the FreeBSD package manager (FreeBSD), and so on. E.g., [we provide an complete example with vcpkg](https://github.com/simdjson/simdjson-vcpkg) that works under Windows. [Visit our wiki for more details](https://github.com/simdjson/simdjson/wiki/Installing-simdjson-with-a-package-manager).
@@ -180,18 +180,6 @@ auto json = padded_string::load("twitter.json"); // load JSON file 'twitter.json
ondemand::document doc = parser.iterate(json); // position a pointer at the beginning of the JSON data
```
If you prefer not to create your own `ondemand::parser` instance, you can access
a thread-local version by calling `ondemand::parser.get_parser()`.
```c++
ondemand::document doc = ondemand::parser.get_parser().iterate(json);
```
However, you should be careful because a parser instance can only be used for one
document at a time, thus it is only applicable when you are only parsing one
document per thread at any one time.
You can also create a padded string---and call `iterate()`:
```c++
@@ -251,6 +239,7 @@ Consider reusing the same buffers and limiting memory allocations.
By default, the simdjson library throws exceptions (`simdjson_error`) on errors. We omit `try`-`catch` clauses from our illustrating examples: if you omit `try`-`catch` in your code, an uncaught exception will halt your program. It is also possible to use simdjson without generating exceptions, and you may even build the library without exception support at all. See [Error handling](#error-handling) for details.
Some users may want to browse code along with the compiled assembly. You want to check out the following lists of examples:
* [simdjson examples with errors handled through exceptions](https://godbolt.org/z/98Kx9Kqjn)
@@ -320,7 +309,7 @@ At the cost of some memory allocation, you may convert your `std::string_view` i
For convenience, we also allow [storing an escaped string directly into an existing string instance](#storing-directly-into-an-existing-string-instance).
The `std::string_view` class has become standard as part of C++17 but it is not always available
on compilers that only supports C++11. When we detect that `string_view` is natively
on compilers which only supports C++11. When we detect that `string_view` is natively
available, we define the macro `SIMDJSON_HAS_STRING_VIEW`.
When we detect that it is unavailable,
@@ -343,7 +332,7 @@ We recommend that you first compile and run your code in debug mode:
The simdjson code will set `SIMDJSON_DEVELOPMENT_CHECKS=1` in debug mode. Because
the C++ standard does not provide a direct way of checking for a debug build, and
because you may want the checks while building with optimizations, you can set
because you may want the checks while building with otimizations, you can set
the macro `SIMDJSON_DEVELOPMENT_CHECKS` to 1 prior to including
the `simdjson.h` header to enable these additional checks: just make sure you remove the
definition once your code has been tested. When `SIMDJSON_DEVELOPMENT_CHECKS` is set to 1, the
@@ -371,8 +360,9 @@ and arrays (`simdjson::ondemand::array`).
We also have a generic ephemeral type (`simdjson::ondemand::value`) which represents a potential
array or object, or scalar type (`double`, `uint64_t`, `int64_t`, `bool`, `null`, string) inside
an array or an object. Both generic types (`simdjson::ondemand::document` and
`simdjson::ondemand::value`) have a `type()` method returning a `json_type` value describing indicating the type (`json_type::array`, `json_type::object`, `json_type::number`, `json_type::string`,
`json_type::boolean`, `json_type::null`, and `json_type::unknown` for unrecognized types). The `type()` method does not consume nor validate the value: e.g., you must still call `is_null()` to check that the value is a `null` even if `json_type::null` is returned. Starting with simdjson 4.0, we return `json_type::unknown` for bad tokens such as the `NaN` token in `{"key":NaN}`. A `json_type::unknown` type value indicates an error in the JSON document but you might still be able to proceed, see [General direct access to the raw JSON string](#general-direct-access-to-the-raw-json-string). A generic value (`simdjson::ondemand::value`)
`simdjson::ondemand::value`) have a `type()` method returning a `json_type` value describing the
value (`json_type::array`, `json_type::object`, `json_type::number`, `json_type::string`,
`json_type::boolean`, `json_type::null`). A generic value (`simdjson::ondemand::value`)
is only valid temporarily, as soon as you access other values, other keys in objects, etc.
it becomes invalid: you should therefore consume the value immediately by converting it to a
scalar type, an array or an object.
@@ -405,7 +395,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
ondemand::object and ondemand::array. We also have explicit methods such as `get_string()`, `get_double()`,
`get_uint64()`, `get_int64()`, `get_bool()`, `get_object()` and `get_array()`. After a cast or an explicit method,
the number, string or boolean will be parsed, or the initial `{` or `[` will be verified for `ondemand::object` and `ondemand::array`. An exception may be thrown if
the cast is not possible: the error code is `simdjson::INCORRECT_TYPE` (see [Error handling](#error-handling)). Importantly, when getting an ondemand::object or ondemand::array instance, its content is
the cast is not possible: there error code is `simdjson::INCORRECT_TYPE` (see [Error handling](#error-handling)). Importantly, when getting an ondemand::object or ondemand::array instance, its content is
not validated: you are only guaranteed that the corresponding initial character (`{` or `[`) is present. Thus,
for example, you could have an ondemand::object instance pointing at the invalid JSON `{ "this is not a valid object" }`: the validation occurs as you access the content.
The `get_string()` returns a valid UTF-8 string, after
@@ -452,7 +442,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`. This will
scan through the object looking for the field with the matching string, doing a character-by-character
comparison. It may generate the error `simdjson::NO_SUCH_FIELD` if there is no such key in the object, it may throw an exception (see [Error handling](#error-handling)). For efficiency reason, you should avoid looking up the same field repeatedly: e.g., do
not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. Generally, you should not mix and match iterating through an object (`for(auto field : object) {...}`) and key accesses (`object["foo"]`): if you need to iterate through an object after a key access, you need to call `reset()` on the object. Whenever you call `reset()`, you need to keep in mind that though you can iterate over the array repeatedly, values should be consumed only once (e.g., repeatedly calling `unescaped_key()` on the same key is forbidden). Keep in mind that On-Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if (!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsibility as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an
not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. For best performance, you should try to query the keys in the same order they appear in the document. If you need several keys and you cannot predict the order they will appear in, it is recommended to iterate through all keys `for(auto field : object) {...}`. Generally, you should not mix and match iterating through an object (`for(auto field : object) {...}`) and key accesses (`object["foo"]`): if you need to iterate through an object after a key access, you need to call `reset()` on the object. Whenever you call `reset()`, you need to keep in mind that though you can iterate over the array repeatedly, values should be consumedonly once (e.g., repeatedly calling `unescaped_key()` on the same key is forbidden). Keep in mind that On-Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if (!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsibility as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an
object twice: `std::string_view(object["foo"]` followed by `std::string_view(object["foo"]` then your code
is in error. Furthermore, you can only consume one field at a time, on the same object. The
value instance you get from `content["bids"]` becomes invalid when you call `content["asks"]`.
@@ -515,7 +505,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
> double y = doc["y"]; // The cursor is now after the 2 (at })
> double x = doc["x"]; // Success: [] loops back around to find "x"
> ```
* **Output to strings:** Given a document, a value, an array or an object in a JSON document, you can output a JSON string version suitable to be parsed again as JSON content: `simdjson::to_json_string(element)`. A call to `to_json_string` consumes fully the element: if you apply it on a document, the internal pointer is advanced to the end of the document. The `simdjson::to_json_string` does not allocate memory. The `to_json_string` function should not be confused with retrieving the value of a string instance which are escaped and represented using a lightweight `std::string_view` instance pointing at an internal string buffer inside the parser instance. To illustrate, the first of the following two code segments will print the unescaped string `"test"` complete with the quote whereas the second one will print the escaped content of the string (without the quotes).
* **Output to strings:** Given a document, a value, an array or an object in a JSON document, you can output a JSON string version suitable to be parsed again as JSON content: `simdjson::to_json_string(element)`. A call to `to_json_string` consumes fully the element: if you apply it on a document, the JSON pointer is advanced to the end of the document. The `simdjson::to_json_string` does not allocate memory. The `to_json_string` function should not be confused with retrieving the value of a string instance which are escaped and represented using a lightweight `std::string_view` instance pointing at an internal string buffer inside the parser instance. To illustrate, the first of the following two code segments will print the unescaped string `"test"` complete with the quote whereas the second one will print the escaped content of the string (without the quotes).
> ```C++
> // serialize a JSON to an escaped std::string instance so that it can be parsed again as JSON
> auto silly_json = R"( { "test": "result" } )"_padded;
@@ -698,9 +688,6 @@ support for users who avoid exceptions. See [the simdjson error handling documen
cout << "null";
}
break;
case ondemand::json_type::unknown:
cout << "unknown"; // indicates an error
break;
}
}
void basics_treewalk() {
@@ -884,13 +871,13 @@ type:
We may do so by providing additional template definitions to the `ondemand::value` type.
We may start by providing a definition for `std::vector<double>` as follows. Observe
how we guard the code with `#if !SIMDJSON_SUPPORTS_CONCEPTS`: that is because the necessary code
how we guard the code with `#if !SIMDJSON_SUPPORTS_DESERIALIZATION`: that is because the necessary code
is automatically provided by simdjson if C++20 (and concepts) are available.
See [Use `tag_invoke` for custom types](#2-use-tag_invoke-for-custom-types-c20) if you have
C++20 support.
```c++
#if !SIMDJSON_SUPPORTS_CONCEPTS
#if !SIMDJSON_SUPPORTS_DESERIALIZATION
// The code is unnecessary with C++20:
template <>
simdjson_inline simdjson_result<std::vector<double>>
@@ -946,7 +933,7 @@ struct Car {
std::vector<double> tire_pressure;
};
#if !SIMDJSON_SUPPORTS_CONCEPTS
#if !SIMDJSON_SUPPORTS_DESERIALIZATION
// This code is not necessary if you have a C++20 compliant system:
template <>
simdjson_inline simdjson_result<std::vector<double>>
@@ -1047,7 +1034,7 @@ struct Car {
std::vector<double> tire_pressure;
};
#if !SIMDJSON_SUPPORTS_CONCEPTS
#if !SIMDJSON_SUPPORTS_DESERIALIZATION
// This code is not necessary if you have a C++20 compliant system:
template <>
simdjson_inline simdjson_result<std::vector<double>>
@@ -1142,7 +1129,7 @@ The first argument is usually a tag type (often an empty struct) that uniquely i
If your system supports C++20, we recommend that you adopt the `tag_invoke` approach
instead to deserialize custom types. It may prove to be considerably simpler. When
simdjson detects the necessary support, it sets the `SIMDJSON_SUPPORTS_CONCEPTS` macro
simdjson detects the necessary support, it sets the `SIMDJSON_SUPPORTS_DESERIALIZATION` macro
to 1, otherwise it is set to 0.
Consider a custom class `Car`:
@@ -1361,6 +1348,31 @@ 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)
@@ -1375,16 +1387,7 @@ your code with the `SIMDJSON_STATIC_REFLECTION` macro set:
Then you can deserialize a type such as `Car` automatically:
```C++
struct Car {
std::string make;
std::string model;
int year;
std::vector<float> tire_pressure;
};
```cpp
std::string json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
"tire_pressure": [ 40.1, 39.9 ] } )";
simdjson::ondemand::parser parser;
@@ -1392,152 +1395,35 @@ simdjson::ondemand::document doc = parser.iterate(simdjson::pad(json));
Car c = doc.get<Car>();
```
We try to automate the parsing of any given structure or class
by looking at its non-static public members. At compile-time,
the library looks at a simple structure like `Car` and
maps it to parsing code. We call the default constructor,
and then assign values to the public members.
If a key is missing in the JSON document, an error is generated (`NO_SUCH_FIELD`),
except if the attribute is of a type like `std::optional` (`simdjson::concepts::optional_type`).
Sometimes you might want to only extract some attributes from the JSON. You can
achieve this result with the `extract_into` method supported by both `object` and
`document` instances. It returns an error code that evaluates to false when there
is no error.
Consider the following example where you only want to parse the make and the model
from the JSON:
```cpp
struct car_type {
std::string make;
std::string model;
uint64_t year;
std::vector<double> tire_pressure;
};
void f() {
auto json = R"( {
"make": "Toyota",
"model": "Camry",
"year": 2024,
"tire_pressure": [ 40.1, 39.9 ]
} )"_padded;
ondemand::parser parser;
ondemand::document doc = parser.iterate(json);
Car car{};
auto error = doc.extract_into<"make","model">(car);
if(error) { /** error handling */ }
// only car.make and car.
}
```
#### Special cases
However, there are instances where the construction cannot
be easily automated. Let us consider a class without any
public member.
```cpp
class MyDate {
public:
void assign(std::string_view str) {
date_str = str;
}
const std::string& to_string() const {
return date_str;
}
private:
std::string date_str;
};
```
This class has a default constructor, but it must be initialized
with the `assign` method. We need to help the library with
a `tag_invoke` function (just as in the C++20 case).
```cpp
namespace simdjson {
template <typename simdjson_value>
auto tag_invoke(deserialize_tag, simdjson_value &val, MyDate& date) {
std::string_view str;
auto error = val.get_string().get(str);
if(error) { return error; }
date.assign(str);
return simdjson::SUCCESS;
}
} // namespace simdjson
```
Once this is done, we can now automatically parse a custom type
like `complicated_weather_data` containing `MyDate` values.
```cpp
struct complicated_weather_data {
std::vector<MyDate> time;
std::vector<float> temperature;
};
```
The code might be as simple as the following.
```cpp
auto padded = R"({"time":["2023-03-15T12:00:00Z"],"temperature":[42]})"_padded;
simdjson::ondemand::parser parser;
simdjson::ondemand::document doc = parser.iterate(padded);
complicated_weather_data p = doc.get<complicated_weather_data>();
```
Thus you can combine C++26 static reflection with custom deserialization
functions.
You can also automatically serialize the `Car` instance to a JSON string, see
our [Builder documentation](builder.md).
### The simdjson::from shortcut (experimental, C++20)
For even more convenience, you can parse a JSON document directly to a supported
type without a document instance like so:
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);
```
You can also use the `simdjson::from` syntax without exceptions, like so:
```cpp
Car car;
simdjson::error_code err = simdjson::from(json_car).get(car);
```
You can also use the `simdjson::from` syntax to iterate over an array.
Similarly, you can also use C++20 ranges to iterate over an array:
```cpp
for(auto val : simdjson::from(json).array()) {
Car c = val.get<Car>(); // ...
}
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;
}
}
```
Standard STL types are supported:
You can also automatically serialize the `Car` instance to a JSON string, see
our [Builder documentation](builder.md).
```cpp
std::map<std::string, std::string> obj =
simdjson::from(R"({"key": "value"})"_padded);
```
The `simdjson::from` construction is EXPERIMENTAL and subject to changes.
Minifying JSON strings without parsing
----------------------
@@ -1579,9 +1465,9 @@ If you find yourself needing only fast Unicode functions, consider using the sim
JSON Pointer
------------
The simdjson library also supports [JSON pointer](https://tools.ietf.org/html/rfc6901) through the `at_pointer()` method, letting you reach further down into the document in a single call. JSON Pointer is supported by both the [DOM approach](https://github.com/simdjson/simdjson/blob/master/doc/dom.md#json-pointer) as well as the On-Demand approach.
The simdjson library also supports [JSON pointer](https://tools.ietf.org/html/rfc6901) through the `at_pointer()` method, letting you reach further down into the document in a single call. JSON pointer is supported by both the [DOM approach](https://github.com/simdjson/simdjson/blob/master/doc/dom.md#json-pointer) as well as the On-Demand approach.
**Note:** The On-Demand implementation of JSON Pointer relies on `find_field` which implies that it does not unescape keys when matching.
**Note:** The On-Demand implementation of JSON pointer relies on `find_field` which implies that it does not unescape keys when matching.
Consider the following example:
@@ -1601,7 +1487,7 @@ index allows you to select the indexed node. Within objects, the string value of
select the value. If your keys contain the characters '/' or '~', they must be escaped as '~1' and
'~0' respectively. An empty JSON Pointer Path refers to the whole document.
For multiple JSON Pointer queries on a document, one can call `at_pointer` multiple times.
For multiple JSON pointer queries on a document, one can call `at_pointer` multiple times.
```c++
auto cars_json = R"( [
@@ -1756,7 +1642,7 @@ x = obj.at_path("$.d.foo2.a.2"); // 30
Error handling
--------------
Error handling with exception and a single try/catch clause makes the code simple, but it gives you little control over errors. For easier debugging or more robust error handling, you may want to consider our exception-free approach.
Error handing with exception and a single try/catch clause makes the code simple, but it gives you little control over errors. For easier debugging or more robust error handling, you may want to consider our exception-free approach.
The entire simdjson API is usable with and without exceptions. All simdjson APIs that can fail return `simdjson_result<T>`, which is a &lt;value, error_code&gt;
pair. You can retrieve the value with .get() without generating an exception, like so:
@@ -2350,8 +2236,7 @@ The simdjson library supports parsing valid numbers inside strings which makes i
three methods: `get_double_in_string`, `get_int64_in_string` and `get_uint64_in_string`. However, it is important to note that these methods are not substitute to the regular
`get_double`, `get_int64` and `get_uint64`. The usage of the `get_*_in_string` methods is solely to parse valid JSON numbers inside strings, and so we expect users to call these
methods appropriately. In particular, a valid JSON number has no leading and no trailing whitespaces, and the strings `"nan"`, `"1e"` and `"infinity"` will not be accepted as valid
numbers (although you have access to the raw string with the `raw_json_token()` method, see [General direct access to the raw JSON string](#general-direct-access-to-the-raw-json-string)
). As an example, suppose we have the following JSON text:
numbers. As an example, suppose we have the following JSON text:
```c++
auto json =
@@ -2670,36 +2555,10 @@ The `raw_json_token()` should be fast and free of allocation.
Given a quote-deliminated string, you find the string sequence inside the quote with a
single line of code:
```cpp
```C++
std::string_view noquote(std::string_view v) { return {v.data()+1, v.find_last_of('"')-1}; }
```
The `raw_json_token()` method can enable you to provide fallbacks when parsing fails.
Consider the following example.
```cpp
padded_string json = "{\"key\": NaN}"_padded;
simdjson::ondemand::parser parser;
simdjson::ondemand::document doc = parser.iterate(json);
simdjson::ondemand::object object = doc.get_object();
simdjson::ondemand::value val = object["key"];
simdjson::ondemand::json_type type = val.type();
// type == simdjson::ondemand::json_type::unknown
try {
double num = val.get_double();
} catch (const simdjson::simdjson_error& e) {
// e == simdjson::error_code::INCORRECT_TYPE
std::string_view str = val.raw_json_token();
// str == "NaN"
}
```
The NaN is not supported in JSON. However, in the On-Demand API, you can check
the string corresponding to the JSON token and determine how to handle it.
### Raw JSON string for objects and arrays
If your value is an array or an object, `raw_json_token()` returns effectively a single
character (`[`) or (`}`) which is not very useful. For arrays and objects, we have another
method called `raw_json()` which consumes (traverses) the array or the object.
@@ -2807,16 +2666,15 @@ a parameter a reference to a `std::string`.
ondemand::parser parser;
ondemand::document doc = parser.iterate(json);
std::string name;
auto error = doc["name"].get_string(name);
if(error) { /* handle error */ }
doc["name"].get_string(name);
```
The same routine can be written without exceptions handling:
```C++
std::string name;
auto error = doc["name"].get_string(name);
if (error) { /* handle error */ }
auto err = doc["name"].get_string(name);
if (err) { /* handle error */ }
```
The `std::string` instance, once created, is independent. Unlike our `std::string_view` instances,
@@ -3219,45 +3077,10 @@ Performance tips
- The On-Demand front-end works best when doing a single pass over the input: avoid calling `count_elements`, `rewind`, `reset` and similar methods.
- If you are familiar with assembly language, you may use the online tool godbolt to explore the compiled code. The following example may work: [https://godbolt.org/z/xE4GWs573](https://godbolt.org/z/xE4GWs573).
- Given a field `field` in an object, calling `field.key()` is often faster than `field.unescaped_key()` so if you do not need an unescaped `std::string_view` instance, prefer `field.key()`. Similarly, we expect `field.escaped_key()` to be faster than `field.unescaped_key()` even though both return a `std::string_view` instance.
- For release builds, we recommend setting the `NDEBUG` pre-processor directive when compiling the `simdjson` library. Importantly, using the optimization flags `-O2` or `-O3` under GCC and LLVM clang does not set the `NDEBUG` directive, you must set it manually (e.g., `-DNDEBUG`).
- For release builds, we recommend setting `NDEBUG` pre-processor directive when compiling the `simdjson` library. Importantly, using the optimization flags `-O2` or `-O3` under GCC and LLVM clang does not set the `NDEBUG` directive, you must set it manually (e.g., `-DNDEBUG`).
- For long streams of JSON documents, consider [`iterate_many`](iterate_many.md) and [`parse_many`](parse_many.md) for better performance.
- Never seek to access a field twice (e.g., o["data"] and later again o["data"]). Instead capture once an ondemand::value and reuse it.
- If you must access several different keys in an object, it might be preferable to iterate through all the fields in the object instead, and branch on the field keys. Consider this example.
```cpp
auto json = R"({"price": 123.456789, "volume": 9999,
"timestamp": "2025-09-04T09:45:00Z",
"symbol": "XYZ", "currency": "USD", "change": 1.23,
"isActive": true})"_padded;
simdjson::ondemand::parser parser;
simdjson::ondemand::document doc = parser.iterate(json);
for(auto keyvalue : doc.get_object()) {
simdjson::ondemand::raw_json_string key = keyvalue.key();
switch(key[0]) {
case 'p': // price
if (key == "price") {
std::string_view price_str = keyvalue.value().raw_json();
std::cout << "Price: " << price_str << std::endl;
}
break;
case 'v': // volume
if (key == "volume") {
std::string_view volume_str = keyvalue.value().raw_json();
std::cout << "Volume: " << volume_str << std::endl;
}
break;
case 't': // timestamp
if (key == "timestamp") {
std::string_view timestamp = keyvalue.value();
std::cout << "Timestamp: " << timestamp << std::endl;
}
break;
default: break;
}
}
```
- If you must access several different keys in an object, it might be preferable to iterate through all the fields in the object instead, and branch on the field keys.
- If possible, refer to each object and array in your code once. For example, the following code repeatedly refers to the `"data"` key to create an object...
```C++
std::string_view make = o["data"]["make"];
+7 -171
View File
@@ -34,28 +34,27 @@ It has the following methods to add content to the string:
- `append_null()`: Appends the string "null" to the JSON buffer.
- `clear()`: Clears the contents of the JSON buffer, resetting the position to 0 while retaining the allocated capacity.
- `escape_and_append(std::string_view input)`: Appends a string view to the JSON buffer after escaping special characters (e.g., quotes, backslashes) as required by JSON.
- `escape_and_append_with_quotes(std::string_view input)` Appends a string view surrounded by double quotes (e.g., "input") to the JSON buffer after escaping special characters. For constant strings, you may also do `escape_and_append_with_quotes<"mystring">()`.
- `escape_and_append_with_quotes(std::string_view input)` Appends a string view surrounded by double quotes (e.g., "input") to the JSON buffer after escaping special characters.
Parameters:
- `escape_and_append_with_quotes(char input)`: Appends a single character surrounded by double quotes (e.g., "c") to the JSON buffer after escaping it if necessary.
- `append_raw(const char *c)`: Appends a null-terminated C string directly to the JSON buffer without escaping.
- `append_raw(std::string_view input)`: Appends a string view directly to the JSON buffer without escaping.
- `append_raw(const char *str, size_t len)`: Appends a specified number of characters from a C string directly to the JSON
- `append_key_value(key,value)`: Appends a key and a value (`"json":somevalue`)
- `append_key_value<"mykey">(value)`: Appends a key and a value (`"json":somevalue`), useful when the key is a compile-time constant (C++20).
After writing the content, if you have reasons to believe that the content might violate UTF-8 conventions, you can check it as follows:
After writting the content, if you have reasons to believe that the content might violate UTF-8 conventions, you can check it as follows:
- `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. Note that we do not automatically call `validate_unicode()`.
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.)
- `operator std::string_view()`: Converts the JSON buffer to an std::string_view. (Might throw if an error occurred.)
- `view()`: Returns a view of the written JSON buffer as a `simdjson_result<std::string_view>` (C++20).
- `view()`: Returns a view of the written JSON buffer as a `simdjson_result<std::string_view>`.
The later method (`view()`) is recommended. For performance reasons, we expect you to explicitly call `validate_unicode()` as needed (e.g., prior to calling `view()`).
The later method (`view()`) is recommended.
Example: string_builder
---------------------------
@@ -110,9 +109,6 @@ bool car_test() {
}
```
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}`).
@@ -128,100 +124,6 @@ The `string_builder` might throw an exception in case of error when you cast it
In all cases, the `std::string_view` instance depends the corresponding `string_builder` instance.
### C++20
If you have C++20, you can simplify the code, as the `std::vector<double>` is automatically supported. Further, we can pass the keys (which are compile-time
constant) as template parameter (for improved performance).
```cpp
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
simdjson::builder::string_builder sb;
sb.start_object();
sb.append_key_value<"make">(c.make);
sb.append_comma();
sb.append_key_value<"model">(c.model);
sb.append_comma();
sb.append_key_value<"year">(c.year);
sb.append_comma();
sb.append_key_value<"tire_pressure">(c.tire_pressure);
sb.end_object();
std::string_view p = sb.view();
```
With C++20, you can similarly handle standard containers transparently.
For example, you can serialize `std::map<std::string,T>` types.
```cpp
std::map<std::string,double> c = {{"key1", 1}, {"key2", 1}};
simdjson::builder::string_builder sb;
sb.append(c);
std::string_view p = sb.view();
```
You can also serialize `std::vector<T>` types.
```cpp
std::vector<std::vector<double>> c = {{1.0, 2.0}, {3.0, 4.0}};
simdjson::builder::string_builder sb;
sb.append(c);
std::string_view p = sb.view();
```
You can also skip the creation for the `string_builder` instance in such simple cases.
```cpp
std::vector<std::vector<double>> c = {{1.0, 2.0}, {3.0, 4.0}};
std::string json = simdjson::to_json(c);
```
We also have an overload for when you want to reuse the same `std::string` instance:
```cpp
std::vector<std::vector<double>> c = {{1.0, 2.0}, {3.0, 4.0}};
std::string json;
auto error = simdjson::to_json(c, json);
if(error) { /* there was an error */ }
```
We do recommend that you create and reuse the `string_builder` instance for performance
reasons.
You can also add custom serialization functions using a `tag_invoke` function.
For example, the following
function will allow you to serialize instances of the type `Car`.
```cpp
#include <simdjson>
struct Car {
std::string make;
std::string model;
int64_t year;
std::vector<float> tire_pressure;
};
namespace simdjson {
template <typename builder_type>
void tag_invoke(serialize_tag, builder_type &builder, const Car& car) {
builder.start_object();
builder.append_key_value("make", car.make);
builder.append_comma();
builder.append_key_value("model", car.model);
builder.append_comma();
builder.append_key_value("year", car.year);
builder.append_comma();
builder.append_key_value("tire_pressure", car.tire_pressure);
builder.end_object();
}
} // namespace simdjson
```
C++26 static reflection
------------------------
@@ -283,38 +185,7 @@ with the `simdjson::to_json` template function.
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)`).
Sometimes you may want to reuse the same `std::string` instance. We
have an overload for this purpose:
```cpp
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
std::string s;
auto error = simdjson::to_json(c, s);
if(error) { /* there was an error */ }
```
You can then also add a third parameter for the expected output size in bytes.
### Extracting just some fields
In some instances, your class might have many fields that you do not want to serialize.
You can achieve this result with the `simdjson::extract_from` template. In the following
example, we serialize only the `year` and `price` fields on the `Car` instance.
```cpp
struct Car {
std::string make;
std::string model;
int year;
double price;
bool electric;
};
Car car{"Ford", "F-150", 2024, 55000.0, false};
// Extract year and price
std::string json_result = simdjson::extract_from<"year", "price">(car);
// Alternatively:
// std::string json_result;
// auto error = extract_from<"year", "price">(car).get(json_result);
// if(error) { /* error handling */ }
```
### Without `string_buffer` instance but with explicit error handling
@@ -323,44 +194,9 @@ pattern:
```cpp
std::string json;
if(simdjson::to_json(c).get(json)) {
if(simdjson::to(c).get(json)) {
// there was an error
} else {
// json contain the serialized JSON
}
```
### Customization
If you want to serialize a value in a custome way, you can do it with a
`tag_invoke` specialization like the following example which will map
the year attribute to a string.
```cpp
#include <simdjson>
struct Car {
std::string make;
std::string model;
int64_t year;
std::vector<float> tire_pressure;
};
namespace simdjson {
template <typename builder_type>
void tag_invoke(serialize_tag, builder_type &builder, const Car& car) {
builder.start_object();
builder.append_key_value("make", car.make);
builder.append_comma();
builder.append_key_value("model", car.model);
builder.append_comma();
builder.append_key_value("year", std::to_string(car.year));
builder.append_comma();
builder.append_key_value("tire_pressure", car.tire_pressure);
builder.end_object();
}
} // namespace simdjson
```
+1 -69
View File
@@ -129,7 +129,7 @@ Once you have an element, you can navigate it with idiomatic C++ iterators, oper
The strings contain unescaped valid UTF-8 strings: no unmatched surrogate is allowed.
Internally, numbers are stored as either 64-bit integers or 64-bit floating-point numbers.
Thus it is possible to get the full 64-bit integer range (either signed or unsigned).
By default, the string `-0` is parsed as the integer 0 as in Python or C++. If you set the macro
By default, the string `-0` is parsed as the integer 0 as in Pytho or C++. If you set the macro
`SIMDJSON_MINUS_ZERO_AS_FLOAT` to `1` when building simdjson, you can get that `-0` is mapped to `-0.0`
as in JavaScript. You can get the desired effect by building simdjson with cmake setting the
`SIMDJSON_MINUS_ZERO_AS_FLOAT` to on: `cmake -B build -D SIMDJSON_MINUS_ZERO_AS_FLOAT=ON`.
@@ -354,74 +354,6 @@ if(error) { /*won't happen*/ }
```
## Using `at_path_with_wildcard` for JSONPath Queries
The `at_path_with_wildcard` function in simdjson extends the JSONPath querying capabilities by supporting wildcard expressions (`*`) in JSON paths. This allows users to retrieve multiple elements from a JSON document in a single query. For example, you can use `$.address.*` to fetch all fields within the `address` object or `$.phoneNumbers[*].numbers[*]` to retrieve all phone numbers across multiple objects in an array.
The `*` wildcard matches all elements at a specific level. For instance, `$.address.*` retrieves all key-value pairs in the `address` object, while `$.*.streetAddress` fetches all `streetAddress` fields across objects at the root level. You can combine wildcards with array indexing. For example, `$.phoneNumbers[*].numbers[1]` retrieves the second number from each `numbers` array in the `phoneNumbers` array. If no elements match the wildcard query, the function returns an empty result. For instance, querying `$.empty_object.*` or `$.empty_array.*` will yield an empty set.
### Example Usage
Here is an example demonstrating the use of `at_path_with_wildcard`:
```cpp
simdjson::padded_string json_string = R"(
{
"firstName": "John",
"lastName": "doe",
"age": 26,
"address": {
"streetAddress": "naist street",
"city": "Nara",
"postalCode": "630-0192"
},
"phoneNumbers": [
{
"type": "iPhone",
"numbers": ["0123-4567-8888", "0123-4567-8788"]
},
{
"type": "home",
"numbers": ["0123-4567-8910"]
}
]
})"_padded;
dom::parser parser;
dom::element parsed_json = parser.parse(json_string);
std::vector<dom::element> values;
// Fetch all fields in the address object
auto error = parsed_json.at_path_with_wildcard("$.address.*").get(values);
if(error) {
// do something
}
for (auto &value : values) {
std::string_view field;
error = value.get(field);
if(error) {
// do something
}
std::cout << field << std::endl;
}
// Fetch all phone numbers
error = parsed_json.at_path_with_wildcard("$.phoneNumbers[*].numbers[*]").get(values);
if(error) {
// do something
}
for (auto &value : values) {
std::string_view number;
error = value.get(number);
if(error) {
// do something
}
std::cout << number << std::endl;
}
```
This function is particularly useful for extracting data from complex JSON structures with nested arrays and objects. By leveraging wildcards, you can simplify your queries and reduce the need for multiple iterations.
Error Handling
--------------
+1 -1
View File
@@ -309,7 +309,7 @@ The first argument is usually a tag type (often an empty struct) that uniquely i
You can deserialize you own data structures conveniently if your system supports C++20.
When it is the case, the macro `SIMDJSON_SUPPORTS_CONCEPTS` will be set to 1 by
When it is the case, the macro `SIMDJSON_SUPPORTS_DESERIALIZATION` will be set to 1 by
the simdjson library.
Consider a custom class `Car`:
+3 -1
View File
@@ -173,6 +173,8 @@ Recent versions of Microsoft Visual Studio on Windows provides support for the L
We recommend Visual Studio users prefer LLVM (clang-cl). It compiles to faster release binaries. Furthermore, it compilers faster in release mode.
Under Windows, we also support the GNU GCC compiler via MSYS2. The performance of 64-bit MSYS2 under Windows is excellent (on par with Linux).
Power Usage and Downclocking
--------------
@@ -253,7 +255,7 @@ long page_size() {
// page boundary.
bool need_allocation(const char *buf, size_t len) {
return ((reinterpret_cast<uintptr_t>(buf + len - 1) % page_size())
+ simdjson::SIMDJSON_PADDING >= static_cast<uintptr_t>(page_size()));
+ simdjson::SIMDJSON_PADDING > static_cast<uintptr_t>(page_size()));
}
simdjson::padded_string_view
+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 -1
View File
@@ -18,7 +18,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
auto error = parser.parse(str).get(elem);
if (error) { return 0; }
std::string minified = simdjson::minify(elem);
std::string minified=simdjson::minify(elem);
(void)minified;
return 0;
}
-1
View File
@@ -54,5 +54,4 @@
#include "simdjson/dom.h"
#include "simdjson/ondemand.h"
#include "simdjson/convert.h"
#include "simdjson/convert-inl.h"
#endif // SIMDJSON_H
+1 -1
View File
@@ -17,7 +17,7 @@ using namespace simd;
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return bs_bits != 0; }
-1
View File
@@ -10,7 +10,6 @@
#include "simdjson/error.h"
#include "simdjson/portability.h"
#include "simdjson/concepts.h"
#include "simdjson/constevalutil.h"
/**
* @brief The top level simdjson namespace, containing everything the library provides.
+5 -19
View File
@@ -81,38 +81,24 @@
#endif
#endif
#if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 201911L
#include <ranges>
#define SIMDJSON_SUPPORTS_RANGES 1
#else
#define SIMDJSON_SUPPORTS_RANGES 0
#endif
#if defined(__cpp_concepts) && !defined(SIMDJSON_CONCEPT_DISABLED)
#if __cpp_concepts >= 201907L
#include <utility>
#define SIMDJSON_SUPPORTS_CONCEPTS 1
#else
#define SIMDJSON_SUPPORTS_CONCEPTS 0
#endif
#else // defined(__cpp_concepts) && !defined(SIMDJSON_CONCEPT_DISABLED)
#define SIMDJSON_SUPPORTS_CONCEPTS 0
#endif // defined(__cpp_concepts) && !defined(SIMDJSON_CONCEPT_DISABLED)
// copy SIMDJSON_SUPPORTS_CONCEPTS to SIMDJSON_SUPPORTS_DESERIALIZATION.
#if SIMDJSON_SUPPORTS_CONCEPTS
#define SIMDJSON_SUPPORTS_DESERIALIZATION 1
#else
#define SIMDJSON_SUPPORTS_DESERIALIZATION 0
#endif
#else // defined(__cpp_concepts) && !defined(SIMDJSON_CONCEPT_DISABLED)
#define SIMDJSON_SUPPORTS_DESERIALIZATION 0
#endif // defined(__cpp_concepts) && !defined(SIMDJSON_CONCEPT_DISABLED)
#if !defined(SIMDJSON_CONSTEVAL)
#if defined(__cpp_consteval) && __cpp_consteval >= 201811L && defined(__cpp_lib_constexpr_string) && __cpp_lib_constexpr_string >= 201907L
#if defined(__cpp_consteval) && __cpp_consteval >= 201811L
#define SIMDJSON_CONSTEVAL 1
#else
#define SIMDJSON_CONSTEVAL 0
#endif // defined(__cpp_consteval) && __cpp_consteval >= 201811L && defined(__cpp_lib_constexpr_string) && __cpp_lib_constexpr_string >= 201907L
#endif // defined(__cpp_consteval) && __cpp_consteval >= 201811L
#endif // !defined(SIMDJSON_CONSTEVAL)
#endif // SIMDJSON_COMPILER_CHECK_H
+3 -40
View File
@@ -1,6 +1,6 @@
#ifndef SIMDJSON_CONCEPTS_H
#define SIMDJSON_CONCEPTS_H
#if SIMDJSON_SUPPORTS_CONCEPTS
#if SIMDJSON_SUPPORTS_DESERIALIZATION
#include <concepts>
#include <type_traits>
@@ -32,9 +32,7 @@ SIMDJSON_IMPL_CONCEPT(op_append, operator+=)
#undef SIMDJSON_IMPL_CONCEPT
} // namespace details
template <typename T>
concept is_pair = requires { typename T::first_type; typename T::second_type; } &&
std::same_as<T, std::pair<typename T::first_type, typename T::second_type>>;
template <typename T>
concept string_view_like = std::is_convertible_v<T, std::string_view> &&
!std::is_convertible_v<T, const char*>;
@@ -122,46 +120,11 @@ concept optional_type = requires(std::remove_cvref_t<T> obj) {
} -> std::convertible_to<typename std::remove_cvref_t<T>::value_type>;
};
{ static_cast<bool>(obj) } -> std::same_as<bool>; // convertible to bool
{ obj.reset() } noexcept -> std::same_as<void>;
};
// Types we serialize as JSON strings (not as containers)
template <typename T>
concept string_like =
std::is_same_v<std::remove_cvref_t<T>, std::string> ||
std::is_same_v<std::remove_cvref_t<T>, std::string_view> ||
std::is_same_v<std::remove_cvref_t<T>, const char*> ||
std::is_same_v<std::remove_cvref_t<T>, char*>;
// Concept that checks if a type is a container but not a string (because
// strings handling must be handled differently)
// Now uses iterator-based approach for broader container support
template <typename T>
concept container_but_not_string =
std::ranges::input_range<T> && !string_like<T> && !concepts::string_view_keyed_map<T>;
} // namespace concepts
/**
* We use tag_invoke as our customization point mechanism.
*/
template <typename Tag, typename... Args>
concept tag_invocable = requires(Tag tag, Args... args) {
tag_invoke(std::forward<Tag>(tag), std::forward<Args>(args)...);
};
template <typename Tag, typename... Args>
concept nothrow_tag_invocable =
tag_invocable<Tag, Args...> && requires(Tag tag, Args... args) {
{
tag_invoke(std::forward<Tag>(tag), std::forward<Args>(args)...)
} noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
#endif // SIMDJSON_CONCEPTS_H
-76
View File
@@ -1,76 +0,0 @@
#ifndef SIMDJSON_CONSTEVALUTIL_H
#define SIMDJSON_CONSTEVALUTIL_H
#include <string>
#include <string_view>
#include <array>
namespace simdjson {
namespace constevalutil {
#if SIMDJSON_CONSTEVAL
constexpr static std::array<uint8_t, 256> json_quotable_character = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
constexpr static std::array<std::string_view, 32> control_chars = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
"\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r",
"\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014",
"\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b",
"\\u001c", "\\u001d", "\\u001e", "\\u001f"};
// unoptimized, meant for compile-time execution
consteval std::string consteval_to_quoted_escaped(std::string_view input) {
std::string out = "\"";
for (char c : input) {
if (json_quotable_character[uint8_t(c)]) {
if (c == '"') {
out.append("\\\"");
} else if (c == '\\') {
out.append("\\\\");
} else {
std::string_view v = control_chars[uint8_t(c)];
out.append(v);
}
} else {
out.push_back(c);
}
}
out.push_back('"');
return out;
}
#endif // SIMDJSON_CONSTEVAL
#if SIMDJSON_SUPPORTS_CONCEPTS
template <std::size_t N>
struct fixed_string {
constexpr fixed_string() : data{} {} // Default constructor for buffers
constexpr fixed_string(const char (&str)[N]) {
for (std::size_t i = 0; i < N; ++i) {
data[i] = str[i];
}
}
char data[N];
constexpr std::string_view view() const { return {data, N - 1}; }
};
template <std::size_t N>
fixed_string(const char (&)[N]) -> fixed_string<N>;
template <fixed_string str>
struct string_constant {
static constexpr std::string_view value = str.view();
};
#endif // SIMDJSON_SUPPORTS_CONCEPTS
} // namespace constevalutil
} // namespace simdjson
#endif // SIMDJSON_CONSTEVALUTIL_H
-135
View File
@@ -1,135 +0,0 @@
#ifndef SIMDJSON_CONVERT_INL_H
#define SIMDJSON_CONVERT_INL_H
#include "simdjson/convert.h"
#if SIMDJSON_SUPPORTS_CONCEPTS
namespace simdjson {
namespace convert {
namespace internal {
// auto_parser method definitions
template <typename parser_type>
inline auto_parser<parser_type>::auto_parser(parser_type &&parser, ondemand::document &&doc) noexcept requires(!std::is_pointer_v<parser_type>)
: m_parser{std::move(parser)}, m_doc{std::move(doc)} {}
template <typename parser_type>
inline auto_parser<parser_type>::auto_parser(parser_type &&parser, padded_string_view const str) noexcept requires(!std::is_pointer_v<parser_type>)
: m_parser{std::move(parser)}, m_doc{}, m_error{SUCCESS} {
m_error = m_parser.iterate(str).get(m_doc);
}
template <typename parser_type>
inline auto_parser<parser_type>::auto_parser(std::remove_pointer_t<parser_type> &parser, ondemand::document &&doc) noexcept requires(std::is_pointer_v<parser_type>)
: m_parser{&parser}, m_doc{std::move(doc)} {}
template <typename parser_type>
inline auto_parser<parser_type>::auto_parser(std::remove_pointer_t<parser_type> &parser, padded_string_view const str) noexcept requires(std::is_pointer_v<parser_type>)
: m_parser{&parser}, m_doc{}, m_error{SUCCESS} {
m_error = m_parser->iterate(str).get(m_doc);
}
template <typename parser_type>
inline auto_parser<parser_type>::auto_parser(padded_string_view const str) noexcept requires(std::is_pointer_v<parser_type>)
: auto_parser{ondemand::parser::get_parser(), str} {}
template <typename parser_type>
inline auto_parser<parser_type>::auto_parser(parser_type parser, ondemand::document &&doc) noexcept requires(std::is_pointer_v<parser_type>)
: auto_parser{*parser, std::move(doc)} {}
template <typename parser_type>
inline std::remove_pointer_t<parser_type> &auto_parser<parser_type>::parser() noexcept {
if constexpr (std::is_pointer_v<parser_type>) {
return *m_parser;
} else {
return m_parser;
}
}
template <typename parser_type>
template <typename T>
inline simdjson_result<T> auto_parser<parser_type>::result() noexcept(is_nothrow_gettable<T>) {
if (m_error != SUCCESS) {
return m_error;
}
return m_doc.get<T>();
}
template <typename parser_type>
template <typename T>
simdjson_warn_unused simdjson_inline error_code auto_parser<parser_type>::get(T &value) && noexcept(is_nothrow_gettable<T>) {
return result<T>().get(value);
}
template <typename parser_type>
inline simdjson_result<ondemand::array> auto_parser<parser_type>::array() noexcept {
return result<ondemand::array>();
}
template <typename parser_type>
inline simdjson_result<ondemand::object> auto_parser<parser_type>::object() noexcept {
return result<ondemand::object>();
}
template <typename parser_type>
inline simdjson_result<ondemand::number> auto_parser<parser_type>::number() noexcept {
return result<ondemand::number>();
}
#if SIMDJSON_EXCEPTIONS
template <typename parser_type>
template <typename T>
inline auto_parser<parser_type>::operator T() noexcept(false) {
if (m_error != SUCCESS) {
throw simdjson_error(m_error);
}
return m_doc.get<T>();
}
#endif // SIMDJSON_EXCEPTIONS
template <typename parser_type>
template <typename T>
inline std::optional<T> auto_parser<parser_type>::optional() noexcept(is_nothrow_gettable<T>) {
if (m_error != SUCCESS) {
return std::nullopt;
}
T value;
if (m_doc.get<T>().get(value)) [[unlikely]] {
return std::nullopt;
}
return {std::move(value)};
}
// to_adaptor method definitions
template <typename T>
inline T to_adaptor<T>::operator()(simdjson_result<ondemand::value> &val) const noexcept {
return val.get<T>();
}
template <typename T>
inline auto to_adaptor<T>::operator()(padded_string_view const str) const noexcept {
return auto_parser<ondemand::parser *>{str};
}
template <typename T>
inline auto to_adaptor<T>::operator()(ondemand::parser &parser, padded_string_view const str) const noexcept {
return auto_parser<ondemand::parser *>{parser, str};
}
template <typename T>
inline auto to_adaptor<T>::operator()(std::string str) const noexcept {
return auto_parser<ondemand::parser *>{pad_with_reserve(str)};
}
template <typename T>
inline auto to_adaptor<T>::operator()(ondemand::parser &parser, std::string str) const noexcept {
return auto_parser<ondemand::parser *>{parser, pad_with_reserve(str)};
}
} // namespace internal
} // namespace convert
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_CONVERT_INL_H
+282 -96
View File
@@ -1,121 +1,307 @@
#ifndef SIMDJSON_CONVERT_H
#define SIMDJSON_CONVERT_H
#if __cpp_concepts
#include "simdjson/ondemand.h"
#include <optional>
#if SIMDJSON_SUPPORTS_CONCEPTS
#ifdef __cpp_lib_ranges
#include <ranges>
#endif
namespace simdjson {
namespace convert {
namespace internal {
struct [[nodiscard]] auto_iterator_end {};
/**
* A utility class for automatically parsing JSON documents.
* This template is NOT part of our public API.
* It is subject to changes.
* @private
* A Wrapper for simdjson_result<ondemand::array_iterator> in order to make it
* compatible with ranges (to satisfy std::ranges::input_range).
*/
template <typename parser_type = ondemand::parser*>
struct auto_parser {
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:
parser_type m_parser;
ondemand::document m_doc;
error_code m_error{SUCCESS};
auto_iterator_storage *m_storage = nullptr;
template <typename T>
static constexpr bool is_nothrow_gettable = requires(ondemand::document doc) {
{ doc.get<T>() } noexcept;
};
public:
explicit auto_parser(parser_type &&parser, ondemand::document &&doc) noexcept requires(!std::is_pointer_v<parser_type>);
explicit auto_parser(parser_type &&parser, padded_string_view const str) noexcept requires(!std::is_pointer_v<parser_type>);
explicit auto_parser(std::remove_pointer_t<parser_type> &parser, ondemand::document &&doc) noexcept requires(std::is_pointer_v<parser_type>);
explicit auto_parser(std::remove_pointer_t<parser_type> &parser, padded_string_view const str) noexcept requires(std::is_pointer_v<parser_type>);
explicit auto_parser(padded_string_view const str) noexcept requires(std::is_pointer_v<parser_type>);
explicit auto_parser(parser_type parser, ondemand::document &&doc) noexcept requires(std::is_pointer_v<parser_type>);
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;
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;
simdjson_warn_unused std::remove_pointer_t<parser_type> &parser() noexcept;
reference operator*() const noexcept { return m_storage->m_value; }
reference operator*() noexcept { return m_storage->m_value; }
template <typename T>
simdjson_warn_unused simdjson_inline simdjson_result<T> result() noexcept(is_nothrow_gettable<T>);
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept(is_nothrow_gettable<T>);
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;
}
simdjson_warn_unused simdjson_inline simdjson_result<ondemand::array> array() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<ondemand::object> object() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<ondemand::number> number() noexcept;
#if SIMDJSON_EXCEPTIONS
template <typename T>
simdjson_warn_unused simdjson_inline explicit(false) operator T() noexcept(false);
#endif // SIMDJSON_EXCEPTIONS
template <typename T>
simdjson_warn_unused simdjson_inline std::optional<T> optional() noexcept(is_nothrow_gettable<T>);
[[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;
/**
* A utility class for adapting values for the `auto_parser`.
* This template is not part of our public API. It is subject to changes.
* @private
*/
template <typename T = void>
struct to_adaptor {
T operator()(simdjson_result<ondemand::value> &val) const noexcept;
auto operator()(padded_string_view const str) const noexcept;
auto operator()(ondemand::parser &parser, padded_string_view const str) const noexcept;
// The std::string is padded with reserve to ensure there is enough space for padding.
// Some sanitizers may not like this, so you can use simdjson::pad instead.
// simdjson::from(simdjson::pad(str))
auto operator()(std::string str) const noexcept;
auto operator()(ondemand::parser &parser, std::string str) const noexcept;
};
// deduction guide
auto_parser(padded_string_view const str) -> auto_parser<ondemand::parser*>;
} // namespace internal
} // namespace convert
struct [[nodiscard]] to_adaptor {
/**
* The simdjson::from instance is EXPERIMENTAL AND SUBJECT TO CHANGES.
*
* The `from` instance is a utility adaptor for parsing JSON strings into objects.
* It provides a convenient way to convert JSON data into C++ objects using the `auto_parser`.
*
* Example usage:
*
* ```cpp
* std::map<std::string, std::string> obj =
* simdjson::from(R"({"key": "value"})"_padded);
* ```
*
* This will parse the JSON string and return an object representation. By default, we
* use the simdjson::ondemand::parser::get_parser() instance. A parser instance should
* be used for just one document at a time.
*
* You can also pass you own parser instance:
* ```cpp
* simdjson::ondemand::parser parser;
* std::map<std::string, std::string> obj =
* simdjson::from(parser, R"({"key": "value"})"_padded);
* ```
* The parser instance can be reused.
*
* This functionality requires C++20 or better.
*/
static constexpr convert::internal::to_adaptor<> from{};
/// 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 // SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_CONVERT_H
#endif // __cpp_concepts
#endif // SIMDJSON_CONVERT_H
+2 -109
View File
@@ -52,22 +52,11 @@ inline simdjson_result<dom::element> simdjson_result<dom::array>::at_pointer(std
return at_pointer(json_pointer);
}
inline simdjson_result<std::vector<dom::element>> simdjson_result<dom::array>::at_path_with_wildcard(std::string_view json_path) const noexcept {
if (error()) {
return error();
}
return first.at_path_with_wildcard(json_path);
}
inline simdjson_result<dom::element> simdjson_result<dom::array>::at(size_t index) const noexcept {
if (error()) { return error(); }
return first.at(index);
}
inline std::vector<dom::element>& simdjson_result<dom::array>::get_values(std::vector<dom::element>& out) const noexcept {
return first.get_values(out);
}
namespace dom {
//
@@ -138,93 +127,6 @@ inline simdjson_result<element> array::at_path(std::string_view json_path) const
return at_pointer(json_pointer);
}
inline void array::process_json_path_of_child_elements(std::vector<element>::iterator& current, std::vector<element>::iterator& end, const std::string_view& path_suffix, std::vector<element>& accumulator) const noexcept {
if (current == end) {
return;
}
simdjson_result<std::vector<element>> result;
for (auto it = current; it != end; ++it) {
std::vector<element> child_result;
auto error = it->at_path_with_wildcard(path_suffix).get(child_result);
if(error) {
continue;
}
accumulator.reserve(accumulator.size() + child_result.size());
accumulator.insert(accumulator.end(),
std::make_move_iterator(child_result.begin()),
std::make_move_iterator(child_result.end()));
}
}
inline simdjson_result<std::vector<element>> array::at_path_with_wildcard(std::string_view json_path) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
size_t i = 0;
// json_path.starts_with('$') requires C++20.
if (!json_path.empty() && json_path.front() == '$') {
i = 1;
}
if (i >= json_path.size() || (json_path[i] != '.' && json_path[i] != '[')) {
return INVALID_JSON_POINTER;
}
if (json_path.find("*") != std::string::npos) {
std::vector<element> child_values;
if (
(json_path.compare(i, 3, "[*]") == 0 && json_path.size() == i + 3) ||
(json_path.compare(i, 2,".*") == 0 && json_path.size() == i + 2)
) {
get_values(child_values);
return child_values;
}
std::pair<std::string_view, std::string_view> key_and_json_path = get_next_key_and_json_path(json_path);
std::string_view key = key_and_json_path.first;
json_path = key_and_json_path.second;
if (key.size() > 0) {
if (key == "*") {
get_values(child_values);
} else {
element pointer_result;
std::string json_pointer = std::string("/") + std::string(key);
auto error = at_pointer(json_pointer).get(pointer_result);
if (!error) {
child_values.emplace_back(pointer_result);
}
}
std::vector<element> result = {};
if (child_values.size() > 0) {
std::vector<element>::iterator child_values_begin = child_values.begin();
std::vector<element>::iterator child_values_end = child_values.end();
process_json_path_of_child_elements(child_values_begin, child_values_end, json_path, result);
}
return result;
} else {
return INVALID_JSON_POINTER;
}
} else {
element result;
auto error = at_path(json_path).get(result);
if (error) {
return error;
}
return std::vector<element>{std::move(result)};
}
}
inline simdjson_result<element> array::at(size_t index) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
size_t i=0;
@@ -235,15 +137,6 @@ inline simdjson_result<element> array::at(size_t index) const noexcept {
return INDEX_OUT_OF_BOUNDS;
}
inline std::vector<element>& array::get_values(std::vector<element>& out) const noexcept {
out.reserve(this->size());
for (auto element : *this) {
out.emplace_back(element);
}
return out;
}
inline array::operator element() const noexcept {
return element(tape);
}
@@ -290,13 +183,13 @@ inline bool array::iterator::operator>(const array::iterator& other) const noexc
#include "simdjson/dom/element-inl.h"
#if SIMDJSON_SUPPORTS_RANGES
#if defined(__cpp_lib_ranges)
static_assert(std::ranges::view<simdjson::dom::array>);
static_assert(std::ranges::sized_range<simdjson::dom::array>);
#if SIMDJSON_EXCEPTIONS
static_assert(std::ranges::view<simdjson::simdjson_result<simdjson::dom::array>>);
static_assert(std::ranges::sized_range<simdjson::simdjson_result<simdjson::dom::array>>);
#endif // SIMDJSON_EXCEPTIONS
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // defined(__cpp_lib_ranges)
#endif // SIMDJSON_ARRAY_INL_H
+4 -27
View File
@@ -1,8 +1,6 @@
#ifndef SIMDJSON_DOM_ARRAY_H
#define SIMDJSON_DOM_ARRAY_H
#include <vector>
#include "simdjson/dom/base.h"
#include "simdjson/internal/tape_ref.h"
@@ -110,17 +108,6 @@ public:
*/
inline simdjson_result<element> at_pointer(std::string_view json_pointer) const noexcept;
/**
* Recursive function which processes the json path of each child element
*/
inline void process_json_path_of_child_elements(std::vector<element>::iterator& current, std::vector<element>::iterator& end, const std::string_view& path_suffix, std::vector<element>& accumulator) const noexcept;
/**
* Adds support for JSONPath expression with wildcards '*'
*/
inline simdjson_result<std::vector<element>> at_path_with_wildcard(std::string_view json_path) const noexcept;
/**
* Get the value associated with the given JSONPath expression. We only support
* JSONPath queries that trivially convertible to JSON Pointer queries: key
@@ -154,15 +141,6 @@ public:
*/
inline simdjson_result<element> at(size_t index) const noexcept;
/**
* Gets the values of items in an array element
* This function has linear-time complexity: the values are checked one by one.
*
* @return The child elements of an array
*/
inline std::vector<element>& get_values(std::vector<element>& out) const noexcept;
/**
* Implicitly convert object to element
*/
@@ -189,11 +167,8 @@ public:
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
inline simdjson_result<dom::element> at_pointer(std::string_view json_pointer) const noexcept;
inline void process_json_path_of_child_elements(std::vector<dom::element>::iterator& current, std::vector<dom::element>::iterator& end, const std::string_view& path_suffix, std::vector<dom::element>& accumulator) const noexcept;
inline simdjson_result<std::vector<dom::element>> at_path_with_wildcard(std::string_view json_path) const noexcept;
inline simdjson_result<dom::element> at_path(std::string_view json_path) const noexcept;
inline simdjson_result<dom::element> at(size_t index) const noexcept;
inline std::vector<dom::element>& get_values(std::vector<dom::element>& out) const noexcept;
#if SIMDJSON_EXCEPTIONS
inline dom::array::iterator begin() const noexcept(false);
@@ -206,7 +181,9 @@ public:
} // namespace simdjson
#if SIMDJSON_SUPPORTS_RANGES
#if defined(__cpp_lib_ranges)
#include <ranges>
namespace std {
namespace ranges {
template<>
@@ -217,6 +194,6 @@ inline constexpr bool enable_view<simdjson::simdjson_result<simdjson::dom::array
#endif // SIMDJSON_EXCEPTIONS
} // namespace ranges
} // namespace std
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // defined(__cpp_lib_ranges)
#endif // SIMDJSON_DOM_ARRAY_H
-20
View File
@@ -128,12 +128,6 @@ simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::at_
if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
return at_pointer(json_pointer);
}
simdjson_inline simdjson_result<std::vector<dom::element>> simdjson_result<dom::element>::at_path_with_wildcard(const std::string_view json_path) const noexcept {
if (error()) { return error(); }
return first.at_path_with_wildcard(json_path);
}
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
[[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]]
simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::at(const std::string_view json_pointer) const noexcept {
@@ -424,20 +418,6 @@ inline simdjson_result<element> element::at_pointer(std::string_view json_pointe
}
}
}
inline simdjson_result<std::vector<element>> element::at_path_with_wildcard(std::string_view json_path) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
switch (tape.tape_ref_type()) {
case internal::tape_type::START_OBJECT:
return object(tape).at_path_with_wildcard(json_path);
case internal::tape_type::START_ARRAY:
return array(tape).at_path_with_wildcard(json_path);
default:
return std::vector<element>{};
}
}
inline simdjson_result<element> element::at_path(std::string_view json_path) const noexcept {
auto json_pointer = json_path_to_pointer_conversion(json_path);
if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
-5
View File
@@ -1,8 +1,6 @@
#ifndef SIMDJSON_DOM_ELEMENT_H
#define SIMDJSON_DOM_ELEMENT_H
#include <vector>
#include "simdjson/dom/base.h"
#include "simdjson/dom/array.h"
@@ -401,8 +399,6 @@ public:
*/
inline simdjson_result<element> at_pointer(const std::string_view json_pointer) const noexcept;
inline simdjson_result<std::vector<element>> at_path_with_wildcard(const std::string_view json_path) const noexcept;
/**
* Get the value associated with the given JSONPath expression. We only support
* JSONPath queries that trivially convertible to JSON Pointer queries: key
@@ -548,7 +544,6 @@ public:
simdjson_inline simdjson_result<dom::element> operator[](const char *key) const noexcept;
simdjson_result<dom::element> operator[](int) const noexcept = delete;
simdjson_inline simdjson_result<dom::element> at_pointer(const std::string_view json_pointer) const noexcept;
simdjson_inline simdjson_result<std::vector<dom::element>> at_path_with_wildcard(const std::string_view json_path) const noexcept;
simdjson_inline simdjson_result<dom::element> at_path(const std::string_view json_path) const noexcept;
[[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]]
simdjson_inline simdjson_result<dom::element> at(const std::string_view json_pointer) const noexcept;
+2 -114
View File
@@ -40,19 +40,10 @@ inline simdjson_result<dom::element> simdjson_result<dom::object>::at_path(std::
if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
return at_pointer(json_pointer);
}
inline simdjson_result<std::vector<dom::element>> simdjson_result<dom::object>::at_path_with_wildcard(std::string_view json_path) const noexcept {
if (error()) {
return error();
}
return first.at_path_with_wildcard(json_path);
}
inline simdjson_result<dom::element> simdjson_result<dom::object>::at_key(std::string_view key) const noexcept {
if (error()) { return error(); }
return first.at_key(key);
}
inline std::vector<dom::element>& simdjson_result<dom::object>::get_values(std::vector<dom::element>& out) const noexcept {
return first.get_values(out);
}
inline simdjson_result<dom::element> simdjson_result<dom::object>::at_key_case_insensitive(std::string_view key) const noexcept {
if (error()) { return error(); }
return first.at_key_case_insensitive(key);
@@ -152,97 +143,6 @@ inline simdjson_result<element> object::at_path(std::string_view json_path) cons
return at_pointer(json_pointer);
}
inline void object::process_json_path_of_child_elements(std::vector<element>::iterator& current, std::vector<element>::iterator& end, const std::string_view& path_suffix, std::vector<element>& accumulator) const noexcept {
if (current == end) {
return;
}
simdjson_result<std::vector<element>> result;
for (auto it = current; it != end; ++it) {
std::vector<element> child_result;
auto error = it->at_path_with_wildcard(path_suffix).get(child_result);
if(error) {
continue;
}
accumulator.reserve(accumulator.size() + child_result.size());
accumulator.insert(accumulator.end(),
std::make_move_iterator(child_result.begin()),
std::make_move_iterator(child_result.end()));
}
}
inline simdjson_result<std::vector<element>> object::at_path_with_wildcard(std::string_view json_path) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
size_t i = 0;
if (json_path.empty()) {
return INVALID_JSON_POINTER;
}
// if JSONPath starts with $, skip it
// json_path.starts_with('$') requires C++20.
if (json_path.front() == '$') {
i = 1;
}
if (i >= json_path.size() || (json_path[i] != '.' && json_path[i] != '[')) {
// expect json path to always start with $ but this isn't currently
// expected in jsonpathutil.h.
return INVALID_JSON_POINTER;
}
if (json_path.find("*") != std::string::npos) {
std::vector<element> child_values;
if (
(json_path.compare(i, 3, "[*]") == 0 && json_path.size() == i + 3) ||
(json_path.compare(i, 2,".*") == 0 && json_path.size() == i + 2)
) {
get_values(child_values);
return child_values;
}
std::pair<std::string_view, std::string_view> key_and_json_path = get_next_key_and_json_path(json_path);
std::string_view key = key_and_json_path.first;
json_path = key_and_json_path.second;
if (key.size() > 0) {
if (key == "*") {
get_values(child_values);
} else {
element pointer_result;
auto error = at_pointer(std::string("/") + std::string(key)).get(pointer_result);
if (!error) {
child_values.emplace_back(pointer_result);
}
}
std::vector<element> result = {};
if (child_values.size() > 0) {
std::vector<element>::iterator child_values_begin = child_values.begin();
std::vector<element>::iterator child_values_end = child_values.end();
process_json_path_of_child_elements(child_values_begin, child_values_end, json_path, result);
}
return result;
} else {
return INVALID_JSON_POINTER;
}
} else {
element result;
auto error = this->at_path(json_path).get(result);
if (error) {
return error;
}
return std::vector<element>{std::move(result)};
}
}
inline simdjson_result<element> object::at_key(std::string_view key) const noexcept {
iterator end_field = end();
for (iterator field = begin(); field != end_field; ++field) {
@@ -252,18 +152,6 @@ inline simdjson_result<element> object::at_key(std::string_view key) const noexc
}
return NO_SUCH_FIELD;
}
inline std::vector<element>& object::get_values(std::vector<element>& out) const noexcept {
iterator end_field = end();
iterator begin_field = begin();
out.reserve(std::distance(begin_field, end_field));
for (iterator field = begin_field; field != end_field; ++field) {
out.emplace_back(field.value());
}
return out;
}
// In case you wonder why we need this, please see
// https://github.com/simdjson/simdjson/issues/323
// People do seek keys in a case-insensitive manner.
@@ -375,13 +263,13 @@ inline key_value_pair::key_value_pair(std::string_view _key, element _value) noe
} // namespace simdjson
#if SIMDJSON_SUPPORTS_RANGES
#if defined(__cpp_lib_ranges)
static_assert(std::ranges::view<simdjson::dom::object>);
static_assert(std::ranges::sized_range<simdjson::dom::object>);
#if SIMDJSON_EXCEPTIONS
static_assert(std::ranges::view<simdjson::simdjson_result<simdjson::dom::object>>);
static_assert(std::ranges::sized_range<simdjson::simdjson_result<simdjson::dom::object>>);
#endif // SIMDJSON_EXCEPTIONS
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // defined(__cpp_lib_ranges)
#endif // SIMDJSON_OBJECT_INL_H
+4 -25
View File
@@ -1,8 +1,6 @@
#ifndef SIMDJSON_DOM_OBJECT_H
#define SIMDJSON_DOM_OBJECT_H
#include <vector>
#include "simdjson/dom/base.h"
#include "simdjson/dom/element.h"
#include "simdjson/internal/tape_ref.h"
@@ -174,16 +172,6 @@ public:
*/
inline simdjson_result<element> at_pointer(std::string_view json_pointer) const noexcept;
/**
* Recursive function which processes the json path of each child element
*/
inline void process_json_path_of_child_elements(std::vector<element>::iterator& current, std::vector<element>::iterator& end, const std::string_view& path_suffix, std::vector<element>& accumulator) const noexcept;
/**
* Adds support for JSONPath expression with wildcards '*'
*/
inline simdjson_result<std::vector<element>> at_path_with_wildcard(std::string_view json_path) const noexcept;
/**
* Get the value associated with the given JSONPath expression. We only support
* JSONPath queries that trivially convertible to JSON Pointer queries: key
@@ -215,14 +203,6 @@ public:
*/
inline simdjson_result<element> at_key(std::string_view key) const noexcept;
/**
* Gets the values associated with keys of an object
* This function has linear-time complexity: the keys are checked one by one.
*
* @return the values associated with each key of an object
*/
inline std::vector<element>& get_values(std::vector<element>& out) const noexcept;
/**
* Get the value associated with the given key in a case-insensitive manner.
* It is only guaranteed to work over ASCII inputs.
@@ -281,11 +261,8 @@ public:
inline simdjson_result<dom::element> operator[](const char *key) const noexcept;
simdjson_result<dom::element> operator[](int) const noexcept = delete;
inline simdjson_result<dom::element> at_pointer(std::string_view json_pointer) const noexcept;
inline void process_json_path_of_child_elements(std::vector<dom::element>::iterator& current, std::vector<dom::element>::iterator& end, const std::string_view& path_suffix, std::vector<dom::element>& accumulator) const noexcept;
inline simdjson_result<std::vector<dom::element>> at_path_with_wildcard(std::string_view json_path_new) const noexcept;
inline simdjson_result<dom::element> at_path(std::string_view json_path) const noexcept;
inline simdjson_result<dom::element> at_key(std::string_view key) const noexcept;
inline std::vector<dom::element>& get_values(std::vector<dom::element>& out) const noexcept;
inline simdjson_result<dom::element> at_key_case_insensitive(std::string_view key) const noexcept;
#if SIMDJSON_EXCEPTIONS
@@ -297,7 +274,9 @@ public:
} // namespace simdjson
#if SIMDJSON_SUPPORTS_RANGES
#if defined(__cpp_lib_ranges)
#include <ranges>
namespace std {
namespace ranges {
template<>
@@ -308,6 +287,6 @@ inline constexpr bool enable_view<simdjson::simdjson_result<simdjson::dom::objec
#endif // SIMDJSON_EXCEPTIONS
} // namespace ranges
} // namespace std
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // defined(__cpp_lib_ranges)
#endif // SIMDJSON_DOM_OBJECT_H
+5 -5
View File
@@ -35,11 +35,11 @@ inline bool parser::dump_raw_tape(std::ostream &os) const noexcept {
return valid ? doc.dump_raw_tape(os) : false;
}
inline simdjson_result<size_t> parser::read_file(std::string_view path) noexcept {
inline simdjson_result<size_t> parser::read_file(const std::string &path) noexcept {
// Open the file
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
std::FILE *fp = std::fopen(path.data(), "rb");
std::FILE *fp = std::fopen(path.c_str(), "rb");
SIMDJSON_POP_DISABLE_WARNINGS
if (fp == nullptr) {
@@ -91,18 +91,18 @@ inline simdjson_result<size_t> parser::read_file(std::string_view path) noexcept
return bytes_read;
}
inline simdjson_result<element> parser::load(std::string_view path) & noexcept {
inline simdjson_result<element> parser::load(const std::string &path) & noexcept {
return load_into_document(doc, path);
}
inline simdjson_result<element> parser::load_into_document(document& provided_doc, std::string_view path) & noexcept {
inline simdjson_result<element> parser::load_into_document(document& provided_doc, const std::string &path) & noexcept {
size_t len;
auto _error = read_file(path).get(len);
if (_error) { return _error; }
return parse_into_document(provided_doc, loaded_bytes.get(), len, false);
}
inline simdjson_result<document_stream> parser::load_many(std::string_view path, size_t batch_size) noexcept {
inline simdjson_result<document_stream> parser::load_many(const std::string &path, size_t batch_size) noexcept {
size_t len;
auto _error = read_file(path).get(len);
if (_error) { return _error; }
+6 -6
View File
@@ -102,8 +102,8 @@ public:
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
*/
inline simdjson_result<element> load(std::string_view path) & noexcept;
inline simdjson_result<element> load(std::string_view path) && = delete ;
inline simdjson_result<element> load(const std::string &path) & noexcept;
inline simdjson_result<element> load(const std::string &path) && = delete ;
/**
* Load a JSON document from a file into a provide document instance and return a temporary reference to it.
@@ -148,8 +148,8 @@ public:
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
*/
inline simdjson_result<element> load_into_document(document& doc, std::string_view path) & noexcept;
inline simdjson_result<element> load_into_document(document& doc, std::string_view path) && =delete;
inline simdjson_result<element> load_into_document(document& doc, const std::string &path) & noexcept;
inline simdjson_result<element> load_into_document(document& doc, const std::string &path) && =delete;
/**
* Parse a JSON document and return a temporary reference to it.
@@ -386,7 +386,7 @@ public:
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
*/
inline simdjson_result<document_stream> load_many(std::string_view path, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept;
inline simdjson_result<document_stream> load_many(const std::string &path, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept;
/**
* Parse a buffer containing many JSON documents.
@@ -657,7 +657,7 @@ private:
inline error_code ensure_capacity(document& doc, size_t desired_capacity) noexcept;
/** Read the file into loaded_bytes */
inline simdjson_result<size_t> read_file(std::string_view path) noexcept;
inline simdjson_result<size_t> read_file(const std::string &path) noexcept;
friend class parser::Iterator;
friend class document_stream;
+18 -33
View File
@@ -67,38 +67,8 @@ simdjson_inline error_code simdjson_result_base<T>::error() const noexcept {
return this->second;
}
template<typename T>
simdjson_inline bool simdjson_result_base<T>::has_value() const noexcept {
return this->error() == SUCCESS;
}
#if SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline T& simdjson_result_base<T>::operator*() & noexcept(false) {
return this->value();
}
template<typename T>
simdjson_inline T&& simdjson_result_base<T>::operator*() && noexcept(false) {
return std::forward<internal::simdjson_result_base<T>>(*this).value();
}
template<typename T>
simdjson_inline T* simdjson_result_base<T>::operator->() noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline const T* simdjson_result_base<T>::operator->() const noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline T& simdjson_result_base<T>::value() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
@@ -123,7 +93,6 @@ simdjson_inline simdjson_result_base<T>::operator T&&() && noexcept(false) {
#endif // SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline const T& simdjson_result_base<T>::value_unsafe() const& noexcept {
return this->first;
@@ -159,11 +128,27 @@ simdjson_inline void simdjson_result<T>::tie(T &value, error_code &error) && noe
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code
simdjson_result<T>::get(T &value) && noexcept {
simdjson_warn_unused simdjson_inline error_code simdjson_result<T>::get(T &value) && noexcept {
return std::forward<internal::simdjson_result_base<T>>(*this).get(value);
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code
simdjson_result<T>::get(std::string &value) && noexcept
#if SIMDJSON_SUPPORTS_DESERIALIZATION
requires (!std::is_same_v<T, std::string>)
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
{
// SFINAE : n'active que pour T = std::string_view
static_assert(std::is_same<T, std::string_view>::value, "simdjson_result<T>::get(std::string&) n'est disponible que pour T = std::string_view");
std::string_view v;
error_code error = std::forward<simdjson_result<T>>(*this).get(v);
if (!error) {
value.assign(v.data(), v.size());
}
return error;
}
template<typename T>
simdjson_inline error_code simdjson_result<T>::error() const noexcept {
return internal::simdjson_result_base<T>::error();
+7 -70
View File
@@ -108,10 +108,6 @@ namespace internal {
/**
* The result of a simdjson operation that could fail.
*
* IMPORTANT: For the ondemand API, we use implementation_simdjson_result_base<T> as a base class
* to avoid some compilation issue. Thus, if you modify this class, please ensure that the ondemand
* implementation_simdjson_result_base<T> is also modified.
*
* Gives the option of reading error codes, or throwing an exception by casting to the desired result.
*
* This is a base class for implementations that want to add functions to the result type for
@@ -172,27 +168,8 @@ struct simdjson_result_base : protected std::pair<T, error_code> {
*/
simdjson_inline error_code error() const noexcept;
/**
* Whether there is a value.
*/
simdjson_inline bool has_value() const noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Dereference operator to access the contained value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T& operator*() & noexcept(false);
simdjson_inline T&& operator*() && noexcept(false);
/**
* Arrow operator to access members of the contained value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T* operator->() noexcept(false);
simdjson_inline const T* operator->() const noexcept(false);
/**
* Get the result value.
*
@@ -226,42 +203,12 @@ struct simdjson_result_base : protected std::pair<T, error_code> {
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evaluates to false.
* We discourage the use of value_unsafe().
*
* The recommended pattern is:
*
* T value; // where T is the type
* auto error = result.get(value);
* if (error) {
* // handle error
* }
*
* Or you may call 'value()' which will raise an exception
* in case of error:
*
* T value = result.value();
*/
simdjson_inline const T& value_unsafe() const& noexcept;
/**
* Take the result value (move it). This function is safe if and only
* the error() method returns a value that evaluates to false.
* We discourage the use of value_unsafe().
*
* The recommended pattern is:
*
* T value; // where T is the type
* auto error = result.get(value);
* if (error) {
* // handle error, return, exit, abort
* } else {
* // use value here.
* }
*
* Or you may call 'value()' which will raise an exception
* in case of error:
*
* T value = result.value();
*/
simdjson_inline T&& value_unsafe() && noexcept;
@@ -276,7 +223,6 @@ struct simdjson_result_base : protected std::pair<T, error_code> {
*/
template<typename T>
struct simdjson_result : public internal::simdjson_result_base<T> {
/**
* @private Create a new empty result with error = UNINITIALIZED.
*/
@@ -308,33 +254,24 @@ struct simdjson_result : public internal::simdjson_result_base<T> {
* @param value The variable to assign the value to. May not be set if there is an error.
*/
simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept;
//
/**
* Copy the value to a provided std::string, only enabled for std::string_view.
*
* @param value The variable to assign the value to. May not be set if there is an error.
*/
template <typename U = T>
simdjson_warn_unused simdjson_inline error_code get(std::string &value) && noexcept {
static_assert(std::is_same<U, std::string_view>::value, "SFINAE");
std::string_view v;
error_code error = std::forward<simdjson_result<T>>(*this).get(v);
if (!error) {
value.assign(v.data(), v.size());
}
return error;
}
simdjson_warn_unused simdjson_inline error_code get(std::string &value) && noexcept
#if SIMDJSON_SUPPORTS_DESERIALIZATION
requires (!std::is_same_v<T, std::string>)
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
;
/**
* The error.
*/
simdjson_inline error_code error() const noexcept;
#if SIMDJSON_EXCEPTIONS
using internal::simdjson_result_base<T>::operator*;
using internal::simdjson_result_base<T>::operator->;
/**
* Get the result value.
*
@@ -13,7 +13,7 @@ namespace {
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 1;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return c == '"'; }
simdjson_inline bool has_backslash() { return c == '\\'; }
@@ -29,41 +29,12 @@ simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_b
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base<T>::error() const noexcept {
simdjson_inline error_code implementation_simdjson_result_base<T>::error() const noexcept {
return this->second;
}
template<typename T>
simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base<T>::has_value() const noexcept {
return this->error() == SUCCESS;
}
#if SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::operator*() & noexcept(false) {
return this->value();
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::operator*() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).value();
}
template<typename T>
simdjson_inline T* implementation_simdjson_result_base<T>::operator->() noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline const T* implementation_simdjson_result_base<T>::operator->() const noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::value() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
@@ -67,17 +67,12 @@ struct implementation_simdjson_result_base {
*
* @param value The variable to assign the value to. May not be set if there is an error.
*/
simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept;
simdjson_inline error_code get(T &value) && noexcept;
/**
* The error.
*/
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
/**
* Whether there is a value.
*/
simdjson_warn_unused simdjson_inline bool has_value() const noexcept;
simdjson_inline error_code error() const noexcept;
#if SIMDJSON_EXCEPTIONS
@@ -86,16 +81,6 @@ struct implementation_simdjson_result_base {
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T& operator*() & noexcept(false);
simdjson_inline T&& operator*() && noexcept(false);
/**
* Arrow operator to access members of the contained value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T* operator->() noexcept(false);
simdjson_inline const T* operator->() const noexcept(false);
simdjson_inline T& value() & noexcept(false);
/**
+6 -6
View File
@@ -361,7 +361,7 @@ simdjson_inline bool is_digit(const uint8_t c) {
return static_cast<uint8_t>(c - '0') <= 9;
}
simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) {
simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) {
// we continue with the fiction that we have an integer. If the
// floating point number is representable as x * 10^z for some integer
// z that fits in 53 bits, then we will be able to convert back the
@@ -389,7 +389,7 @@ simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(si
return SUCCESS;
}
simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) {
simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) {
// Exp Sign: -123.456e[-]78
bool neg_exp = ('-' == *p);
if (neg_exp || '+' == *p) { p++; } // Skip + as well
@@ -478,7 +478,7 @@ static error_code slow_float_parsing(simdjson_unused const uint8_t * src, double
/** @private */
template<typename W>
simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) {
simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) {
// If we frequently had to deal with long strings of digits,
// we could extend our code by using a 128-bit integer instead
// of a 64-bit integer. However, this is uncommon in practice.
@@ -541,13 +541,13 @@ simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const
//
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer);
simdjson_inline error_code parse_number(const uint8_t *const src, W &writer);
// for performance analysis, it is sometimes useful to skip parsing
#ifdef SIMDJSON_SKIPNUMBERPARSING
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) {
simdjson_inline error_code parse_number(const uint8_t *const, W &writer) {
writer.append_s64(0); // always write zero
return SUCCESS; // always succeeds
}
@@ -573,7 +573,7 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
//
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
//
// Check for minus sign
//
@@ -14,9 +14,6 @@
#include "simdjson/generic/ondemand/raw_json_string.h"
#include "simdjson/generic/ondemand/parser.h"
// JSON builder - needed for extract_into functionality
#include "simdjson/generic/ondemand/json_string_builder.h"
// All other declarations
#include "simdjson/generic/ondemand/array.h"
#include "simdjson/generic/ondemand/array_iterator.h"
@@ -48,11 +45,9 @@
#include "simdjson/generic/ondemand/token_iterator-inl.h"
#include "simdjson/generic/ondemand/value_iterator-inl.h"
// JSON builder inline definitions
// JSON builder, ideally they should not be part of the ondemand directory
// but it is convenient for now to have them here.
#include "simdjson/generic/ondemand/json_string_builder.h"
#include "simdjson/generic/ondemand/json_string_builder-inl.h"
#include "simdjson/generic/ondemand/json_builder.h"
// Compile-time JSON parsing (C++26 P2996 reflection)
#include "simdjson/generic/ondemand/compile_time_json.h"
#include "simdjson/generic/ondemand/compile_time_json-inl.h"
@@ -85,7 +85,7 @@ simdjson_inline simdjson_result<array_iterator> array::begin() noexcept {
simdjson_inline simdjson_result<array_iterator> array::end() noexcept {
return array_iterator(iter);
}
simdjson_warn_unused simdjson_warn_unused simdjson_inline error_code array::consume() noexcept {
simdjson_inline error_code array::consume() noexcept {
auto error = iter.json_iter().skip_child(iter.depth()-1);
if(error) { iter.abandon(); }
return error;
+7 -7
View File
@@ -132,7 +132,7 @@ public:
*/
simdjson_inline simdjson_result<value> at(size_t index) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
#if SIMDJSON_SUPPORTS_DESERIALIZATION
/**
* Get this array as the given type.
*
@@ -141,7 +141,7 @@ public:
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
simdjson_inline error_code get(T &out)
noexcept(custom_deserializable<T, array> ? nothrow_custom_deserializable<T, array> : true) {
static_assert(custom_deserializable<T, array>);
return deserialize(*this, out);
@@ -161,12 +161,12 @@ public:
SIMDJSON_TRY(get<T>(out));
return out;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
protected:
/**
* Go to the end of the array, no matter where you are right now.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
simdjson_inline error_code consume() noexcept;
/**
* Begin array iteration.
@@ -240,7 +240,7 @@ public:
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
#if SIMDJSON_SUPPORTS_DESERIALIZATION
// TODO: move this code into object-inl.h
template<typename T>
@@ -252,7 +252,7 @@ public:
return first.get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept {
simdjson_inline error_code get(T& out) noexcept {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, SIMDJSON_IMPLEMENTATION::ondemand::array>) {
out = first;
@@ -261,7 +261,7 @@ public:
}
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
};
} // namespace simdjson
@@ -62,7 +62,7 @@ public:
/**
* Check if the array is at the end.
*/
simdjson_warn_unused simdjson_inline bool at_end() const noexcept;
[[nodiscard]] simdjson_inline bool at_end() const noexcept;
private:
value_iterator iter{};
@@ -95,7 +95,7 @@ struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> : publ
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;
simdjson_warn_unused simdjson_inline bool at_end() const noexcept;
[[nodiscard]] simdjson_inline bool at_end() const noexcept;
};
} // namespace simdjson
@@ -1,98 +0,0 @@
/**
* @file compile_time_json-inl.h
* @brief Implementation details for compile-time JSON parsing
*
* This file contains inline implementations and helper utilities for compile-time
* JSON parsing. Currently, the main implementation is self-contained in the header.
*/
#ifndef SIMDJSON_GENERIC_COMPILE_TIME_JSON_INL_H
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#define SIMDJSON_GENERIC_COMPILE_TIME_JSON_INL_H
#include "simdjson/generic/ondemand/compile_time_json.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
#if SIMDJSON_STATIC_REFLECTION
#include <meta>
#include <array>
#include <string_view>
#include <cstdint>
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace compile_time {
/**
* @brief Optimized constexpr string to integer conversion
*
* This can be used for more efficient integer parsing in the future.
* Currently, we parse all numbers as doubles for simplicity.
*/
constexpr int64_t parse_int_fast(std::string_view str) {
int64_t result = 0;
bool negative = false;
std::size_t i = 0;
if (i < str.size() && str[i] == '-') {
negative = true;
++i;
}
while (i < str.size() && str[i] >= '0' && str[i] <= '9') {
result = result * 10 + (str[i] - '0');
++i;
}
return negative ? -result : result;
}
/**
* @brief Unescape JSON string at compile-time
*
* Currently, strings are returned as views into the original JSON.
* This function can be used in the future for proper escape handling.
*/
template<std::size_t MaxLen = 1024>
constexpr auto unescape_json_string(std::string_view escaped) {
std::array<char, MaxLen> result{};
std::size_t out_pos = 0;
std::size_t i = 0;
while (i < escaped.size() && out_pos < MaxLen) {
if (escaped[i] == '\\' && i + 1 < escaped.size()) {
++i;
switch (escaped[i]) {
case '"': result[out_pos++] = '"'; break;
case '\\': result[out_pos++] = '\\'; break;
case '/': result[out_pos++] = '/'; break;
case 'b': result[out_pos++] = '\b'; break;
case 'f': result[out_pos++] = '\f'; break;
case 'n': result[out_pos++] = '\n'; break;
case 'r': result[out_pos++] = '\r'; break;
case 't': result[out_pos++] = '\t'; break;
case 'u':
// Unicode escape - would need proper implementation
// For now, skip the escape sequence
i += 4; // Skip 4 hex digits
break;
default:
result[out_pos++] = escaped[i];
}
++i;
} else {
result[out_pos++] = escaped[i];
++i;
}
}
return std::pair{result, out_pos};
}
} // namespace compile_time
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_GENERIC_COMPILE_TIME_JSON_INL_H
@@ -1,464 +0,0 @@
/**
* @file compile_time_json.h
* @brief Compile-time JSON parsing using C++26 reflection with std::meta::substitute()
*
* Based on the godbolt example: https://godbolt.org/z/Kn5b46T8j
* Uses the Outer<Ms...>::Inner + substitute() pattern for recursive type generation.
*/
#ifndef SIMDJSON_GENERIC_COMPILE_TIME_JSON_H
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#define SIMDJSON_GENERIC_COMPILE_TIME_JSON_H
#include "simdjson/generic/ondemand/base.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
#if SIMDJSON_STATIC_REFLECTION
#include <meta>
#include <string_view>
#include <array>
#include <vector>
#include <cstdint>
#include <string>
#include <algorithm>
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace compile_time {
/**
* @brief Helper struct for substitute() pattern
* The consteval block can use define_aggregate because it's in a template context
*/
template <std::meta::info ...Ms>
struct Outer {
struct Inner;
consteval {
std::meta::define_aggregate(^^Inner, {Ms...});
}
};
/**
* @brief Type alias for the generated struct
*/
template <std::meta::info ...Ms>
using Cls = Outer<Ms...>::Inner;
/**
* @brief Variable template for constructing instances with values
*/
template <typename T, auto ... Vs>
constexpr auto construct_from = T{Vs...};
// Forward declaration
consteval std::meta::info parse_json_impl(std::string_view json);
/**
* @brief Parse JSON array and return std::meta::info for the generated array
*/
consteval std::meta::info parse_json_array_impl(std::string_view json) {
auto cursor = json.begin();
auto end = json.end();
auto is_whitespace = [](char c) {
return c == ' ' || c == '\n' || c == '\t' || c == '\r';
};
auto skip_whitespace = [&]() -> void {
while (cursor != end && is_whitespace(*cursor)) cursor++;
};
auto expect_consume = [&](char c) -> void {
skip_whitespace();
if (cursor == end || *(cursor++) != c) throw "unexpected character";
};
auto parse_value = [&](std::string &out) -> void {
skip_whitespace();
bool quoted = false;
unsigned depth = 0;
while (true) {
if (cursor == end) throw "unexpected end of stream";
if (is_whitespace(*cursor) && !quoted && depth == 0)
break;
if (depth == 0 && (*cursor == ',' || *cursor == ']'))
break;
out += *(cursor++);
if (out.back() == '{')
++depth;
else if (out.back() == '}')
--depth;
else if (out.back() == '[')
++depth;
else if (out.back() == ']')
--depth;
else if (out.back() == '"') {
if (quoted && depth == 0)
break;
quoted = true;
}
}
};
skip_whitespace();
expect_consume('[');
std::vector<std::meta::info> values = {^^void};
std::meta::info element_type = ^^void;
bool first = true;
using std::meta::reflect_constant, std::meta::reflect_constant_string;
skip_whitespace();
if (cursor != end && *cursor == ']') {
expect_consume(']');
// Empty array - use int as placeholder type since void doesn't work
auto array_type = std::meta::substitute(^^std::array, {^^int, reflect_constant(0uz)});
values[0] = array_type;
return std::meta::substitute(^^construct_from, values);
}
while (cursor != end && *cursor != ']') {
std::string value;
parse_value(value);
if (value.empty()) throw "expected value";
if (value[0] == '"') {
if (value.back() != '"') throw "expected end of string";
std::string_view contents(&value[1], value.size() - 2);
if (first) element_type = ^^char const*;
values.push_back(reflect_constant_string(contents));
} else if (value == "true") {
if (first) element_type = ^^bool;
values.push_back(reflect_constant(true));
} else if (value == "false") {
if (first) element_type = ^^bool;
values.push_back(reflect_constant(false));
} else if (value == "null") {
if (first) element_type = ^^std::nullptr_t;
values.push_back(reflect_constant(nullptr));
} else if ((value[0] >= '0' && value[0] <= '9') || value[0] == '-') {
// Try to parse as integer first
bool is_int = true;
for (char c : value) {
if (c == '.' || c == 'e' || c == 'E') {
is_int = false;
break;
}
}
if (is_int) {
int contents = [](std::string_view in) {
int out = 0;
bool negative = false;
std::size_t i = 0;
if (in[0] == '-') {
negative = true;
i = 1;
}
for (; i < in.size(); ++i) {
out = out * 10 + (in[i] - '0');
}
return negative ? -out : out;
}(value);
if (first) element_type = ^^int;
values.push_back(reflect_constant(contents));
} else {
// Parse as double
double contents = [](std::string_view in) {
double result = 0.0;
double sign = 1.0;
std::size_t i = 0;
if (in[0] == '-') {
sign = -1.0;
i = 1;
}
while (i < in.size() && in[i] >= '0' && in[i] <= '9') {
result = result * 10.0 + (in[i] - '0');
++i;
}
if (i < in.size() && in[i] == '.') {
++i;
double fraction = 0.0;
double divisor = 1.0;
while (i < in.size() && in[i] >= '0' && in[i] <= '9') {
fraction = fraction * 10.0 + (in[i] - '0');
divisor *= 10.0;
++i;
}
result += fraction / divisor;
}
return result * sign;
}(value);
if (first) element_type = ^^double;
values.push_back(reflect_constant(contents));
}
} else if (value[0] == '{') {
// Nested object in array
std::meta::info parsed = parse_json_impl(value);
if (first) element_type = std::meta::type_of(parsed);
values.push_back(parsed);
} else if (value[0] == '[') {
// Nested array
std::meta::info parsed = parse_json_array_impl(value);
if (first) element_type = std::meta::type_of(parsed);
values.push_back(parsed);
}
first = false;
skip_whitespace();
if (cursor != end && *cursor == ',')
++cursor;
}
if (cursor == end) throw "unexpected end";
expect_consume(']');
// Create std::array<ElementType, Count> type
std::size_t count = values.size() - 1; // -1 because first element is ^^void placeholder
auto array_type = std::meta::substitute(^^std::array, {element_type, reflect_constant(count)});
// Create array instance with values
values[0] = array_type;
return std::meta::substitute(^^construct_from, values);
}
/**
* @brief Parse JSON and return std::meta::info for the generated type + instance
*/
consteval std::meta::info parse_json_impl(std::string_view json) {
auto cursor = json.begin();
auto end = json.end();
auto is_whitespace = [](char c) {
return c == ' ' || c == '\n' || c == '\t' || c == '\r';
};
auto skip_whitespace = [&]() -> void {
while (cursor != end && is_whitespace(*cursor)) cursor++;
};
auto expect_consume = [&](char c) -> void {
skip_whitespace();
if (cursor == end || *(cursor++) != c) throw "unexpected character";
};
auto parse_until = [&](std::vector<char> delims, std::string &out) -> void {
skip_whitespace();
while (cursor != end &&
!std::ranges::any_of(delims, [&](char c) { return c == *cursor; }))
out += *(cursor++);
};
auto parse_delimited = [&](char lhs, std::string &out, char rhs) -> void {
skip_whitespace();
expect_consume(lhs);
parse_until({rhs}, out);
expect_consume(rhs);
};
auto parse_value = [&](std::string &out) -> void {
skip_whitespace();
bool quoted = false;
unsigned depth = 0;
bool in_array = false;
while (true) {
if (cursor == end) throw "unexpected end of stream";
if (is_whitespace(*cursor) && !quoted && depth == 0)
break;
if (depth == 0 && (*cursor == ',' || *cursor == '}' || *cursor == ']'))
break;
out += *(cursor++);
if (out.back() == '{')
++depth;
else if (out.back() == '}')
--depth;
else if (out.back() == '[') {
in_array = true;
++depth;
} else if (out.back() == ']')
--depth;
else if (out.back() == '"') {
if (quoted && depth == 0)
break;
quoted = true;
}
}
};
skip_whitespace();
expect_consume('{');
std::vector<std::meta::info> members;
std::vector<std::meta::info> values = {^^void};
using std::meta::reflect_constant, std::meta::reflect_constant_string;
while (cursor != end && *cursor != '}') {
std::string field_name;
std::string value;
parse_delimited('"', field_name, '"');
expect_consume(':');
parse_value(value);
if (value.empty()) throw "expected value";
if (cursor == end) throw "unexpected end of stream";
if (value[0] == '"') {
if (value.back() != '"') throw "expected end of string";
std::string_view contents(&value[1], value.size() - 2);
auto dms = std::meta::data_member_spec(^^char const*, {.name=field_name});
members.push_back(reflect_constant(dms));
values.push_back(reflect_constant_string(contents));
} else if (value == "true") {
auto dms = std::meta::data_member_spec(^^bool, {.name=field_name});
members.push_back(reflect_constant(dms));
values.push_back(reflect_constant(true));
} else if (value == "false") {
auto dms = std::meta::data_member_spec(^^bool, {.name=field_name});
members.push_back(reflect_constant(dms));
values.push_back(reflect_constant(false));
} else if (value == "null") {
auto dms = std::meta::data_member_spec(^^std::nullptr_t, {.name=field_name});
members.push_back(reflect_constant(dms));
values.push_back(reflect_constant(nullptr));
} else if ((value[0] >= '0' && value[0] <= '9') || value[0] == '-') {
// Try to parse as integer first
bool is_int = true;
for (char c : value) {
if (c == '.' || c == 'e' || c == 'E') {
is_int = false;
break;
}
}
if (is_int) {
int contents = [](std::string_view in) {
int out = 0;
bool negative = false;
std::size_t i = 0;
if (in[0] == '-') {
negative = true;
i = 1;
}
for (; i < in.size(); ++i) {
out = out * 10 + (in[i] - '0');
}
return negative ? -out : out;
}(value);
auto dms = std::meta::data_member_spec(^^int, {.name=field_name});
members.push_back(reflect_constant(dms));
values.push_back(reflect_constant(contents));
} else {
// Parse as double
double contents = [](std::string_view in) {
double result = 0.0;
double sign = 1.0;
std::size_t i = 0;
if (in[0] == '-') {
sign = -1.0;
i = 1;
}
while (i < in.size() && in[i] >= '0' && in[i] <= '9') {
result = result * 10.0 + (in[i] - '0');
++i;
}
if (i < in.size() && in[i] == '.') {
++i;
double fraction = 0.0;
double divisor = 1.0;
while (i < in.size() && in[i] >= '0' && in[i] <= '9') {
fraction = fraction * 10.0 + (in[i] - '0');
divisor *= 10.0;
++i;
}
result += fraction / divisor;
}
return result * sign;
}(value);
auto dms = std::meta::data_member_spec(^^double, {.name=field_name});
members.push_back(reflect_constant(dms));
values.push_back(reflect_constant(contents));
}
} else if (value[0] == '{') {
// Nested object
std::meta::info parsed = parse_json_impl(value);
auto dms = std::meta::data_member_spec(std::meta::type_of(parsed), {.name=field_name});
members.push_back(reflect_constant(dms));
values.push_back(parsed);
} else if (value[0] == '[') {
// Array
std::meta::info parsed = parse_json_array_impl(value);
auto dms = std::meta::data_member_spec(std::meta::type_of(parsed), {.name=field_name});
members.push_back(reflect_constant(dms));
values.push_back(parsed);
}
skip_whitespace();
if (cursor != end && *cursor == ',')
++cursor;
}
if (cursor == end) throw "unexpected end";
expect_consume('}');
// The substitute() trick:
// 1. Create the type: Cls<member_specs...>
values[0] = std::meta::substitute(^^Cls, members);
// 2. Create instance: construct_from<Type, values...>
return std::meta::substitute(^^construct_from, values);
}
/**
* @brief Main parse_json function - template wrapper
*/
template<constevalutil::fixed_string json_str>
consteval auto parse_json() {
constexpr std::meta::info result = parse_json_impl(json_str.view());
return [:result:];
}
/**
* @brief JSON validation
*/
template<constevalutil::fixed_string json_str>
consteval bool validate_json() {
try {
parse_json_impl(json_str.view());
return true;
} catch (...) {
return false;
}
}
} // namespace compile_time
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_GENERIC_COMPILE_TIME_JSON_H
@@ -1,4 +1,4 @@
#if SIMDJSON_SUPPORTS_CONCEPTS
#if SIMDJSON_SUPPORTS_DESERIALIZATION
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
@@ -7,8 +7,55 @@
#include "simdjson/generic/ondemand/array.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
#include <concepts>
namespace simdjson {
namespace tag_invoke_fn_ns {
void tag_invoke();
struct tag_invoke_fn {
template <typename Tag, typename... Args>
requires requires(Tag tag, Args &&...args) {
tag_invoke(std::forward<Tag>(tag), std::forward<Args>(args)...);
}
constexpr auto operator()(Tag tag, Args &&...args) const
noexcept(noexcept(tag_invoke(std::forward<Tag>(tag),
std::forward<Args>(args)...)))
-> decltype(tag_invoke(std::forward<Tag>(tag),
std::forward<Args>(args)...)) {
return tag_invoke(std::forward<Tag>(tag), std::forward<Args>(args)...);
}
};
} // namespace tag_invoke_fn_ns
inline namespace tag_invoke_ns {
inline constexpr tag_invoke_fn_ns::tag_invoke_fn tag_invoke = {};
} // namespace tag_invoke_ns
template <typename Tag, typename... Args>
concept tag_invocable = requires(Tag tag, Args... args) {
tag_invoke(std::forward<Tag>(tag), std::forward<Args>(args)...);
};
template <typename Tag, typename... Args>
concept nothrow_tag_invocable =
tag_invocable<Tag, Args...> && requires(Tag tag, Args... args) {
{
tag_invoke(std::forward<Tag>(tag), std::forward<Args>(args)...)
} noexcept;
};
template <typename Tag, typename... Args>
using tag_invoke_result =
std::invoke_result<decltype(tag_invoke), Tag, Args...>;
template <typename Tag, typename... Args>
using tag_invoke_result_t =
std::invoke_result_t<decltype(tag_invoke), Tag, Args...>;
template <auto &Tag> using tag_t = std::decay_t<decltype(Tag)>;
struct deserialize_tag;
/// These types are deserializable in a built-in way
@@ -50,35 +97,35 @@ inline constexpr struct deserialize_tag {
// Customization Point for array
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
[[nodiscard]] constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for object
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
[[nodiscard]] constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for value
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
[[nodiscard]] constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for document
template <typename T>
requires custom_deserializable<T, document_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_type>) {
[[nodiscard]] constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for document reference
template <typename T>
requires custom_deserializable<T, document_reference_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_reference_type>) {
[[nodiscard]] constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_reference_type>) {
return tag_invoke(*this, object, output);
}
@@ -88,5 +135,5 @@ inline constexpr struct deserialize_tag {
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
#endif // SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
@@ -141,7 +141,7 @@ simdjson_inline simdjson_result<std::string_view> document::get_string(bool allo
return get_root_value_iterator().get_root_string(true, allow_replacement);
}
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept {
simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(receiver, true, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> document::get_wobbly_string() noexcept {
@@ -167,15 +167,15 @@ template<> simdjson_inline simdjson_result<int64_t> document::get() & noexcept {
template<> simdjson_inline simdjson_result<bool> document::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document::get() & noexcept { return get_value(); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); }
template<> simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); }
template<> simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); }
template<> simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); }
template<> simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); }
template<> simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); }
template<> simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); }
template<> simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); }
template<> simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); }
template<> simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); }
template<> simdjson_deprecated simdjson_inline simdjson_result<raw_json_string> document::get() && noexcept { return get_raw_json_string(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<std::string_view> document::get() && noexcept { return get_string(false); }
@@ -245,7 +245,7 @@ simdjson_inline simdjson_result<value> document::operator[](const char *key) & n
return start_or_resume_object()[key];
}
simdjson_warn_unused simdjson_inline error_code document::consume() noexcept {
simdjson_inline error_code document::consume() noexcept {
bool scalar = false;
auto error = is_scalar().get(scalar);
if(error) { return error; }
@@ -347,54 +347,6 @@ simdjson_inline simdjson_result<value> document::at_path(std::string_view json_p
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code document::extract_into(T& out) & noexcept {
// Helper to check if a field name matches any of the requested fields
auto should_extract = [](std::string_view field_name) constexpr -> bool {
return ((FieldNames.view() == field_name) || ...);
};
// Iterate through all members of T using reflection
template for (constexpr auto mem : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only extract this field if it's in our list of requested fields
if constexpr (should_extract(key)) {
// Try to find and extract the field
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
// For optional fields, it's ok if they're missing
auto field_result = find_field_unordered(key);
if (!field_result.error()) {
auto error = field_result.get(out.[:mem:]);
if (error && error != NO_SUCH_FIELD) {
return error;
}
} else if (field_result.error() != NO_SUCH_FIELD) {
return field_result.error();
} else {
out.[:mem:].reset();
}
} else {
// For required fields (in the requested list), fail if missing
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
}
}
}
};
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
@@ -502,7 +454,7 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
return first.get_string(allow_replacement);
}
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_string(string_type& receiver, bool allow_replacement) noexcept {
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
@@ -538,12 +490,12 @@ simdjson_deprecated simdjson_inline simdjson_result<T> simdjson_result<SIMDJSON_
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document>(first).get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get(T &out) & noexcept {
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get(T &out) & noexcept {
if (error()) { return error(); }
return first.get<T>(out);
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get(T &out) && noexcept {
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get(T &out) && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document>(first).get<T>(out);
}
@@ -553,8 +505,8 @@ template<> simdjson_deprecated simdjson_inline simdjson_result<SIMDJSON_IMPLEMEN
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document>(first);
}
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get<SIMDJSON_IMPLEMENTATION::ondemand::document>(SIMDJSON_IMPLEMENTATION::ondemand::document &out) & noexcept = delete;
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get<SIMDJSON_IMPLEMENTATION::ondemand::document>(SIMDJSON_IMPLEMENTATION::ondemand::document &out) && noexcept {
template<> simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get<SIMDJSON_IMPLEMENTATION::ondemand::document>(SIMDJSON_IMPLEMENTATION::ondemand::document &out) & noexcept = delete;
template<> simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get<SIMDJSON_IMPLEMENTATION::ondemand::document>(SIMDJSON_IMPLEMENTATION::ondemand::document &out) && noexcept {
if (error()) { return error(); }
out = std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document>(first);
return SUCCESS;
@@ -672,15 +624,6 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
return first.at_path(json_path);
}
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::extract_into(T& out) & noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
} // namespace simdjson
@@ -715,7 +658,7 @@ simdjson_inline simdjson_result<double> document_reference::get_double() noexcep
simdjson_inline simdjson_result<double> document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
simdjson_inline simdjson_result<std::string_view> document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); }
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); }
simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); }
simdjson_inline simdjson_result<std::string_view> document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); }
simdjson_inline simdjson_result<raw_json_string> document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); }
simdjson_inline simdjson_result<bool> document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); }
@@ -768,13 +711,7 @@ simdjson_inline simdjson_result<value> document_reference::at_pointer(std::strin
simdjson_inline simdjson_result<value> document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json() noexcept { return doc->raw_json();}
simdjson_inline document_reference::operator document&() const noexcept { return *doc; }
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept {
return doc->extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
@@ -871,7 +808,7 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
return first.get_string(allow_replacement);
}
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_string(string_type& receiver, bool allow_replacement) noexcept {
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
@@ -906,12 +843,12 @@ simdjson_inline simdjson_result<T> simdjson_result<SIMDJSON_IMPLEMENTATION::onde
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>(first).get<T>();
}
template <class T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(T &out) & noexcept {
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(T &out) & noexcept {
if (error()) { return error(); }
return first.get<T>(out);
}
template <class T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(T &out) && noexcept {
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(T &out) && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>(first).get<T>(out);
}
@@ -928,13 +865,13 @@ simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::o
return first.is_string();
}
template <>
simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(SIMDJSON_IMPLEMENTATION::ondemand::document_reference &out) & noexcept {
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(SIMDJSON_IMPLEMENTATION::ondemand::document_reference &out) & noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
template <>
simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(SIMDJSON_IMPLEMENTATION::ondemand::document_reference &out) && noexcept {
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(SIMDJSON_IMPLEMENTATION::ondemand::document_reference &out) && noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
@@ -1022,14 +959,7 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
}
return first.at_path(json_path);
}
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::extract_into(T& out) & noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
+20 -84
View File
@@ -117,7 +117,7 @@ public:
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a string.
*
@@ -183,7 +183,7 @@ public:
*/
template <typename T>
simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
#if SIMDJSON_SUPPORTS_DESERIALIZATION
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document> : true)
#else
noexcept
@@ -206,7 +206,7 @@ public:
*/
template<typename T>
simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
#if SIMDJSON_SUPPORTS_DESERIALIZATION
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document> : true)
#else
noexcept
@@ -228,14 +228,14 @@ public:
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_DESERIALIZATION
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document> : true)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
#if SIMDJSON_SUPPORTS_DESERIALIZATION
if constexpr (custom_deserializable<T, document>) {
return deserialize(*this, out);
} else {
@@ -247,7 +247,7 @@ public:
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
#else // SIMDJSON_SUPPORTS_DESERIALIZATION
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
@@ -257,7 +257,7 @@ public:
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
@@ -481,27 +481,11 @@ public:
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* The answer can be one of
* simdjson::ondemand::json_type::object,
* simdjson::ondemand::json_type::array,
* simdjson::ondemand::json_type::string,
* simdjson::ondemand::json_type::number,
* simdjson::ondemand::json_type::boolean,
* simdjson::ondemand::json_type::null.
*
* Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown
* given a bad token.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* Prior to simdjson 4.0, this function would return an error given a bad token.
* Starting with simdjson 4.0, it will return simdjson::ondemand::json_type::unknown.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() noexcept;
@@ -725,41 +709,11 @@ public:
* the JSON document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_STATIC_REFLECTION
/**
* Extract only specific fields from the JSON object into a struct.
*
* This allows selective deserialization of only the fields you need,
* potentially improving performance by skipping unwanted fields.
*
* Example:
* ```c++
* struct Car {
* std::string make;
* std::string model;
* int year;
* double price;
* };
*
* Car car;
* doc.extract_into<"make", "model">(car);
* // Only 'make' and 'model' fields are extracted from JSON
* ```
*
* @tparam FieldNames Compile-time string literals specifying which fields to extract
* @param out The output struct to populate with selected fields
* @returns SUCCESS on success, or an error code if a required field is missing or has wrong type
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
protected:
/**
* Consumes the document.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
simdjson_inline error_code consume() noexcept;
simdjson_inline document(ondemand::json_iterator &&iter) noexcept;
simdjson_inline const uint8_t *text(uint32_t idx) const noexcept;
@@ -812,7 +766,7 @@ public:
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
@@ -821,7 +775,7 @@ public:
simdjson_inline simdjson_result<bool> is_null() noexcept;
template <typename T>
simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
#if SIMDJSON_SUPPORTS_DESERIALIZATION
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document> : true)
#else
noexcept
@@ -834,7 +788,7 @@ public:
}
template<typename T>
simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
#if SIMDJSON_SUPPORTS_DESERIALIZATION
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document> : true)
#else
noexcept
@@ -856,14 +810,14 @@ public:
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_DESERIALIZATION
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document_reference> : true)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
#if SIMDJSON_SUPPORTS_DESERIALIZATION
if constexpr (custom_deserializable<T, document_reference>) {
return deserialize(*this, out);
} else {
@@ -875,7 +829,7 @@ public:
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
#else // SIMDJSON_SUPPORTS_DESERIALIZATION
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
@@ -885,17 +839,12 @@ public:
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_inline error_code get(T &out) && noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
simdjson_inline operator document&() const noexcept;
#if SIMDJSON_EXCEPTIONS
template <class T>
@@ -964,7 +913,7 @@ public:
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
@@ -977,9 +926,6 @@ public:
template<typename T> simdjson_inline error_code get(T &out) & noexcept;
template<typename T> simdjson_inline error_code get(T &out) && noexcept;
#if SIMDJSON_EXCEPTIONS
using SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator*;
using SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator->;
template <class T, typename std::enable_if<std::is_same<T, SIMDJSON_IMPLEMENTATION::ondemand::document>::value == false>::type>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
@@ -1019,11 +965,6 @@ public:
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
};
@@ -1050,7 +991,7 @@ public:
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
@@ -1101,11 +1042,6 @@ public:
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
};
@@ -182,19 +182,10 @@ simdjson_inline document_stream::iterator& document_stream::iterator::operator++
return *this;
}
simdjson_inline bool document_stream::iterator::at_end() const noexcept {
return finished;
}
simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept {
return finished != other.finished;
}
simdjson_inline bool document_stream::iterator::operator==(const document_stream::iterator &other) const noexcept {
return finished == other.finished;
}
simdjson_inline document_stream::iterator document_stream::begin() noexcept {
start();
// If there are no documents, we're finished.
@@ -312,10 +303,7 @@ inline void document_stream::next_document() noexcept {
// Always set depth=1 at the start of document
doc.iter._depth = 1;
// consume comma if comma separated is allowed
if (allow_comma_separated) {
error_code ignored = doc.iter.consume_character(',');
static_cast<void>(ignored); // ignored on purpose
}
if (allow_comma_separated) { doc.iter.consume_character(','); }
// Resets the string buffer at the beginning, thus invalidating the strings.
doc.iter._string_buf_loc = parser->string_buf.get();
doc.iter._root = doc.iter.position();
@@ -131,7 +131,6 @@ public:
* Default constructor.
*/
simdjson_inline iterator() noexcept;
simdjson_inline iterator(const iterator &other) noexcept = default;
/**
* Get the current document (or error).
*/
@@ -145,7 +144,6 @@ public:
* @param other the end iterator to compare to.
*/
simdjson_inline bool operator!=(const iterator &other) const noexcept;
simdjson_inline bool operator==(const iterator &other) const noexcept;
/**
* @private
*
@@ -189,11 +187,6 @@ public:
*/
inline error_code error() const noexcept;
/**
* Returns whether the iterator is at the end.
*/
inline bool at_end() const noexcept;
private:
simdjson_inline iterator(document_stream *s, bool finished) noexcept;
/** The document_stream we're iterating through. */
@@ -205,7 +198,6 @@ public:
friend class document_stream;
friend class json_iterator;
};
using iterator = document_stream::iterator;
/**
* Start iterating the documents in the stream.
@@ -114,7 +114,7 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
}
template<typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::field>::unescaped_key(string_type &receiver, bool allow_replacement) noexcept {
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::field>::unescaped_key(string_type &receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(receiver, allow_replacement);
}
+57 -132
View File
@@ -13,7 +13,7 @@
#include <charconv>
#include <cstring>
#include <meta>
#include <experimental/meta>
#include <memory>
#include <optional>
#include <string_view>
@@ -25,21 +25,30 @@ namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace builder {
// Concept that checks if a type is a container but not a string (because
// strings handling must be handled differently)
template <typename T>
concept container_but_not_string =
requires(T a) {
{ a.size() } -> std::convertible_to<std::size_t>;
{
a[std::declval<std::size_t>()]
}; // check if elements are accessible for the subscript operator
} && !std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char *>;
template <class T>
requires(concepts::container_but_not_string<T> && !require_custom_serialization<T>)
requires(container_but_not_string<T>)
constexpr void atom(string_builder &b, const T &t) {
auto it = t.begin();
auto end = t.end();
if (it == end) {
if (t.size() == 0) {
b.append_raw("[]");
return;
}
b.append('[');
atom(b, *it);
++it;
for (; it != end; ++it) {
atom(b, t[0]);
for (size_t i = 1; i < t.size(); ++i) {
b.append(',');
atom(b, *it);
atom(b, t[i]);
}
b.append(']');
}
@@ -54,7 +63,6 @@ constexpr void atom(string_builder &b, const T &t) {
}
template <concepts::string_view_keyed_map T>
requires(!require_custom_serialization<T>)
constexpr void atom(string_builder &b, const T &m) {
if (m.empty()) {
b.append_raw("{}");
@@ -81,9 +89,12 @@ template<typename number_type,
constexpr void atom(string_builder &b, const number_type t) {
b.append(t);
}
#if SIMDJSON_CONSTEVAL
consteval std::string consteval_to_quoted_escaped(std::string_view input);
#endif
template <class T>
requires(std::is_class_v<T> && !concepts::container_but_not_string<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> &&
@@ -91,14 +102,14 @@ template <class T>
!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> && !require_custom_serialization<T>)
!std::is_same_v<T, char>)
constexpr void atom(string_builder &b, const T &t) {
int i = 0;
b.append('{');
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
[:expand(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked())):] >> [&]<auto dm>() {
if (i != 0)
b.append(',');
constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm)));
constexpr auto key = std::define_static_string(consteval_to_quoted_escaped(std::meta::identifier_of(dm)));
b.append_raw(key);
b.append(':');
atom(b, t.[:dm:]);
@@ -109,7 +120,6 @@ constexpr void atom(string_builder &b, const T &t) {
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
constexpr void atom(string_builder &b, const T &opt) {
if (opt) {
atom(b, opt.value());
@@ -120,7 +130,6 @@ constexpr void atom(string_builder &b, const T &opt) {
// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.)
template <concepts::smart_pointer T>
requires(!require_custom_serialization<T>)
constexpr void atom(string_builder &b, const T &ptr) {
if (ptr) {
atom(b, *ptr);
@@ -131,19 +140,24 @@ constexpr void atom(string_builder &b, const T &ptr) {
// Support for enums - serialize as string representation using expand approach from P2996R12
template <typename T>
requires(std::is_enum_v<T> && !require_custom_serialization<T>)
requires(std::is_enum_v<T>)
void atom(string_builder &b, const T &e) {
#if SIMDJSON_STATIC_REFLECTION
constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
template for (constexpr auto enum_val : enumerators) {
constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(enum_val)));
std::string_view result = "<unnamed>";
[:expand(std::meta::enumerators_of(^^T)):] >> [&]<auto enum_val>{
if (e == [:enum_val:]) {
b.append_raw(enum_str);
return;
result = std::meta::identifier_of(enum_val);
}
};
// Fallback to integer if enum value not found
atom(b, static_cast<std::underlying_type_t<T>>(e));
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));
@@ -152,10 +166,10 @@ void atom(string_builder &b, const T &e) {
// Support for appendable containers that don't have operator[] (sets, etc.)
template <concepts::appendable_containers T>
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<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*> && !require_custom_serialization<T>)
!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("[]");
@@ -190,35 +204,32 @@ void append(string_builder &b, const T &t) {
}
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
void append(string_builder &b, const T &t) {
atom(b, t);
}
template <concepts::smart_pointer T>
requires(!require_custom_serialization<T>)
void append(string_builder &b, const T &t) {
atom(b, t);
}
template <concepts::appendable_containers T>
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<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*> && !require_custom_serialization<T>)
!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>
requires(!require_custom_serialization<T>)
void append(string_builder &b, const T &t) {
atom(b, t);
}
// works for struct
template <class Z>
requires(std::is_class_v<Z> && !concepts::container_but_not_string<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> &&
@@ -226,14 +237,14 @@ template <class 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> && !require_custom_serialization<Z>)
!std::is_same_v<Z, char>)
void append(string_builder &b, const Z &z) {
int i = 0;
b.append('{');
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) {
[:expand(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked())):] >> [&]<auto dm>() {
if (i != 0)
b.append(',');
constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm)));
constexpr auto key = std::define_static_string(consteval_to_quoted_escaped(std::meta::identifier_of(dm)));
b.append_raw(key);
b.append(':');
atom(b, z.[:dm:]);
@@ -242,35 +253,25 @@ void append(string_builder &b, const Z &z) {
b.append('}');
}
// works for container that have begin() and end() iterators
// works for container
template <class Z>
requires(concepts::container_but_not_string<Z> && !require_custom_serialization<Z>)
requires(container_but_not_string<Z>)
void append(string_builder &b, const Z &z) {
auto it = z.begin();
auto end = z.end();
if (it == end) {
if (z.size() == 0) {
b.append_raw("[]");
return;
}
b.append('[');
atom(b, *it);
++it;
for (; it != end; ++it) {
atom(b, z[0]);
for (size_t i = 1; i < z.size(); ++i) {
b.append(',');
atom(b, *it);
atom(b, z[i]);
}
b.append(']');
}
template <class Z>
requires (require_custom_serialization<Z>)
void append(string_builder &b, const Z &z) {
b.append(z);
}
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
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;
@@ -279,8 +280,8 @@ simdjson_warn_unused simdjson_result<std::string> to_json_string(const Z &z, siz
}
template <class Z>
simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
string_builder b(initial_capacity);
simdjson_error to_json(const Z &z, std::string &s) {
string_builder b;
append(b, z);
std::string_view view;
if(auto e = b.view().get(view); e) { return e; }
@@ -293,90 +294,14 @@ string_builder& operator<<(string_builder& b, const Z& z) {
append(b, z);
return b;
}
// extract_from: Serialize only specific fields from a struct to JSON
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
void extract_from(string_builder &b, const T &obj) {
// Helper to check if a field name matches any of the requested fields
auto should_extract = [](std::string_view field_name) constexpr -> bool {
return ((FieldNames.view() == field_name) || ...);
};
b.append('{');
bool first = true;
// Iterate through all members of T using reflection
template for (constexpr auto mem : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only serialize this field if it's in our list of requested fields
if constexpr (should_extract(key)) {
if (!first) {
b.append(',');
}
first = false;
// Serialize the key
constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)));
b.append_raw(quoted_key);
b.append(':');
// Serialize the value
atom(b, obj.[:mem:]);
}
}
};
b.append('}');
}
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
string_builder b(initial_capacity);
extract_from<FieldNames...>(b, obj);
std::string_view s;
if(auto e = b.view().get(s); e) { return e; }
return std::string(s);
}
} // namespace builder
} // namespace SIMDJSON_IMPLEMENTATION
// Alias the function template to 'to' in the global namespace
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = SIMDJSON_IMPLEMENTATION::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
SIMDJSON_IMPLEMENTATION::builder::string_builder b(initial_capacity);
SIMDJSON_IMPLEMENTATION::builder::append(b, z);
std::string_view s;
if(auto e = b.view().get(s); e) { return e; }
return std::string(s);
simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = 1024) {
return SIMDJSON_IMPLEMENTATION::builder::to_json_string(z, initial_capacity);
}
template <class Z>
simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = SIMDJSON_IMPLEMENTATION::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
SIMDJSON_IMPLEMENTATION::builder::string_builder b(initial_capacity);
SIMDJSON_IMPLEMENTATION::builder::append(b, z);
std::string_view view;
if(auto e = b.view().get(view); e) { return e; }
s.assign(view);
return SUCCESS;
}
// Global namespace function for extract_from
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = SIMDJSON_IMPLEMENTATION::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
SIMDJSON_IMPLEMENTATION::builder::string_builder b(initial_capacity);
SIMDJSON_IMPLEMENTATION::builder::extract_from<FieldNames...>(b, obj);
std::string_view s;
if(auto e = b.view().get(s); e) { return e; }
return std::string(s);
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif
@@ -344,7 +344,7 @@ simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept {
return _string_buf_loc;
}
simdjson_warn_unused simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept {
simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD);
logger::log_error(*this, message);
error = _error;
@@ -388,7 +388,7 @@ simdjson_inline void json_iterator::reenter_child(token_position position, depth
_depth = child_depth;
}
simdjson_warn_unused simdjson_inline error_code json_iterator::consume_character(char c) noexcept {
simdjson_inline error_code json_iterator::consume_character(char c) noexcept {
if (*peek() == c) {
return_current_and_advance();
return SUCCESS;
@@ -411,7 +411,7 @@ simdjson_inline void json_iterator::set_start_position(depth_t depth, token_posi
#endif
simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept {
simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD);
logger::log_error(*this, message);
return _error;
@@ -238,14 +238,14 @@ public:
* @param error The error to report. Must not be SUCCESS, UNINITIALIZED, INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
/**
* Log error, but don't stop iteration.
* @param error The error to report. Must be INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_warn_unused simdjson_inline error_code optional_error(error_code error, const char *message) noexcept;
simdjson_inline error_code optional_error(error_code error, const char *message) noexcept;
/**
* Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with
@@ -265,7 +265,7 @@ public:
simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept;
simdjson_warn_unused simdjson_inline error_code consume_character(char c) noexcept;
simdjson_inline error_code consume_character(char c) noexcept;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position start_position(depth_t depth) const noexcept;
simdjson_inline void set_start_position(depth_t depth, token_position position) noexcept;
@@ -42,25 +42,24 @@ namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace builder {
static SIMDJSON_CONSTEXPR_LAMBDA std::array<uint8_t, 256>
json_quotable_character = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
static SIMDJSON_CONSTEXPR_LAMBDA std::array<uint8_t, 256> json_quotable_character = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/**
A possible SWAR implementation of has_json_escapable_byte. It is not used
because it is slower than the current implementation. It is kept here for
reference (to show that we tried it).
A possible SWAR implementation of has_json_escapable_byte. It is not used because
it is slower than the current implementation. It is kept here for reference (to show
that we tried it).
inline bool has_json_escapable_byte(uint64_t x) {
uint64_t is_ascii = 0x8080808080808080ULL & ~x;
@@ -77,7 +76,7 @@ SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline bool
simple_needs_escaping(std::string_view v) {
for (char c : v) {
// a table lookup is faster than a series of comparisons
if (json_quotable_character[static_cast<uint8_t>(c)]) {
if(json_quotable_character[static_cast<uint8_t>(c)]) {
return true;
}
}
@@ -118,8 +117,7 @@ simdjson_inline bool fast_needs_escaping(std::string_view view) {
__m128i running = _mm_setzero_si128();
for (; i + 15 < view.size(); i += 16) {
__m128i word =
_mm_loadu_si128(reinterpret_cast<const __m128i *>(view.data() + i));
__m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(view.data() + i));
running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34)));
running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92)));
running = _mm_or_si128(
@@ -127,8 +125,8 @@ simdjson_inline bool fast_needs_escaping(std::string_view view) {
_mm_setzero_si128()));
}
if (i < view.size()) {
__m128i word = _mm_loadu_si128(
reinterpret_cast<const __m128i *>(view.data() + view.length() - 16));
__m128i word =
_mm_loadu_si128(reinterpret_cast<const __m128i *>(view.data() + view.length() - 16));
running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34)));
running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92)));
running = _mm_or_si128(
@@ -143,6 +141,7 @@ simdjson_inline bool fast_needs_escaping(std::string_view view) {
}
#endif
SIMDJSON_CONSTEXPR_LAMBDA inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
@@ -156,17 +155,12 @@ find_next_json_quotable_character(const std::string_view view,
}
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
"\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r",
"\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014",
"\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b",
"\\u001c", "\\u001d", "\\u001e", "\\u001f"};
"\\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"};
// All Unicode characters may be placed within the quotation marks, except for
// the characters that MUST be escaped: quotation mark, reverse solidus, and the
// control characters (U+0000 through U+001F). There are two-character sequence
// escape representations of some popular characters:
// \", \\, \b, \f, \n, \r, \t.
SIMDJSON_CONSTEXPR_LAMBDA void escape_json_char(char c, char *&out) {
if (c == '"') {
memcpy(out, "\\\"", 2);
@@ -207,6 +201,29 @@ inline size_t write_string_escaped(const std::string_view input, char *out) {
return out - initout;
}
#if SIMDJSON_CONSTEVAL
// unoptimized, meant for compile-time execution
consteval std::string consteval_to_quoted_escaped(std::string_view input) {
std::string out = "\"";
for (char c : input) {
if (json_quotable_character[uint8_t(c)]) {
if (c == '"') {
out.append("\\\"");
} else if (c == '\\') {
out.append("\\\\");
} else {
std::string_view v = control_chars[uint8_t(c)];
out.append(v);
}
} else {
out.push_back(c);
}
}
out.push_back('"');
return out;
}
#endif // SIMDJSON_CONSTEVAL
simdjson_inline string_builder::string_builder(size_t initial_capacity)
: buffer(new(std::nothrow) char[initial_capacity]), position(0),
capacity(buffer.get() != nullptr ? initial_capacity : 0),
@@ -285,13 +302,10 @@ simdjson_inline void string_builder::clear() noexcept {
namespace internal {
template <typename number_type, typename = typename std::enable_if<
std::is_unsigned<number_type>::value>::type>
simdjson_really_inline int int_log2(number_type x) {
return 63 - leading_zeroes(uint64_t(x) | 1);
}
// We could specialize further for 32-bit integers.
simdjson_really_inline int int_log2(uint32_t x) { return (63 - leading_zeroes(x | 1)); }
simdjson_really_inline int fast_digit_count_32(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,
@@ -303,7 +317,9 @@ simdjson_really_inline int fast_digit_count_32(uint32_t x) {
return uint32_t((x + table[int_log2(x)]) >> 32);
}
simdjson_really_inline int fast_digit_count_64(uint64_t x) {
simdjson_really_inline int int_log2(uint64_t x) { return 63 - leading_zeroes(x | 1); }
simdjson_really_inline int fast_digit_count(uint64_t x) {
static uint64_t table[] = {9,
99,
999,
@@ -334,31 +350,26 @@ 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");
SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) {
return fast_digit_count_32(static_cast<uint32_t>(v));
}
else {
return fast_digit_count_64(static_cast<uint64_t>(v));
}
return fast_digit_count(v);
}
static const char decimal_table[200] = {
0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35,
0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31,
0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37,
0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33,
0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39,
0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35,
0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31,
0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37,
0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33,
0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39,
0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35,
0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31,
0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37,
0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33,
0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39,
0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35,
0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39,
0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35,
0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31,
0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37,
0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33,
0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39,
0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35,
0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31,
0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37,
0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33,
0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39,
0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35,
0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31,
0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37,
0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33,
0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39,
0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35,
0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39,
};
} // namespace internal
@@ -394,7 +405,7 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
size_t dc = internal::digit_count(pv);
char *write_pointer = buffer.get() + position + dc - 1;
while (pv >= 100) {
memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2);
memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100)*2], 2);
write_pointer -= 2;
pv /= 100;
}
@@ -421,7 +432,7 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
}
char *write_pointer = buffer.get() + position + dc - 1;
while (pv >= 100) {
memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2);
memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100)*2], 2);
write_pointer -= 2;
pv /= 100;
}
@@ -473,17 +484,10 @@ string_builder::escape_and_append_with_quotes(char input) noexcept {
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(const char *input) noexcept {
simdjson_inline void string_builder::escape_and_append_with_quotes(const char* input) noexcept {
std::string_view cinput(input);
escape_and_append_with_quotes(cinput);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <constevalutil::fixed_string key>
simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept {
escape_and_append_with_quotes(constevalutil::string_constant<key>::value);
}
#endif
simdjson_inline void string_builder::append_raw(const char *c) noexcept {
size_t len = std::strlen(c);
@@ -505,81 +509,10 @@ simdjson_inline void string_builder::append_raw(const char *str,
position += len;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void string_builder::append(const T &opt) {
if (opt) {
append(*opt);
} else {
append_null();
}
}
template <typename T>
requires(require_custom_serialization<T>)
simdjson_inline void string_builder::append(const T &val) {
serialize(*this, val);
}
template <typename T>
requires(std::is_convertible<T, std::string_view>::value ||
std::is_same<T, const char *>::value)
simdjson_inline void string_builder::append(const T &value) {
escape_and_append_with_quotes(value);
}
#endif
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
// Support for range-based appending (std::ranges::view, etc.)
template <std::ranges::range R>
requires(!std::is_convertible<R, std::string_view>::value)
simdjson_inline void string_builder::append(const R &range) noexcept {
auto it = std::ranges::begin(range);
auto end = std::ranges::end(range);
if constexpr (concepts::is_pair<typename R::value_type>) {
start_object();
if (it == end) {
end_object();
return; // Handle empty range
}
// Append first item without leading comma
append_key_value(it->first, it->second);
++it;
// Append remaining items with preceding commas
for (; it != end; ++it) {
append_comma();
append_key_value(it->first, it->second);
}
end_object();
} else {
start_array();
if (it == end) {
end_array();
return; // Handle empty range
}
// Append first item without leading comma
append(*it);
++it;
// Append remaining items with preceding commas
for (; it != end; ++it) {
append_comma();
append(*it);
}
end_array();
}
}
#endif
#if SIMDJSON_EXCEPTIONS
simdjson_inline string_builder::operator std::string() const noexcept(false) {
return std::string(operator std::string_view());
return std::string(std::string_view());
}
simdjson_inline string_builder::operator std::string_view() const
@@ -608,95 +541,77 @@ simdjson_inline bool string_builder::validate_unicode() const noexcept {
return simdjson::validate_utf8(buffer.get(), position);
}
simdjson_inline void string_builder::start_object() noexcept {
simdjson_inline void string_builder::start_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '{';
}
}
simdjson_inline void string_builder::end_object() noexcept {
simdjson_inline void string_builder::end_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '}';
}
}
simdjson_inline void string_builder::start_array() noexcept {
simdjson_inline void string_builder::start_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '[';
}
}
simdjson_inline void string_builder::end_array() noexcept {
simdjson_inline void string_builder::end_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ']';
}
}
simdjson_inline void string_builder::append_comma() noexcept {
simdjson_inline void string_builder::append_comma() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ',';
}
}
simdjson_inline void string_builder::append_colon() noexcept {
simdjson_inline void string_builder::append_colon() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ':';
}
}
template <typename key_type, typename value_type>
simdjson_inline void
string_builder::append_key_value(key_type key, value_type value) noexcept {
static_assert(std::is_same<key_type, const char *>::value ||
std::is_convertible<key_type, std::string_view>::value,
"Unsupported key type");
template<typename key_type, typename value_type>
simdjson_inline void string_builder::append_key_value(key_type key, value_type value) noexcept {
static_assert(
std::is_arithmetic<value_type>::value ||
std::is_same<value_type, char>::value ||
std::is_same<value_type, const char*>::value ||
std::is_convertible<value_type, std::string_view>::value ||
std::is_same<value_type, std::nullptr_t>::value,
"Unsupported value type");
static_assert(
std::is_same<key_type, const char*>::value ||
std::is_convertible<key_type, std::string_view>::value,
"Unsupported key type");
escape_and_append_with_quotes(key);
append_colon();
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
append_null();
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
} else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(
std::is_convertible<value_type, std::string_view>::value) {
} else SIMDJSON_IF_CONSTEXPR(std::is_convertible<value_type, std::string_view>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
} else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char*>::value) {
escape_and_append_with_quotes(value);
}
else {
} else {
append(value);
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <constevalutil::fixed_string key, typename value_type>
simdjson_inline void
string_builder::append_key_value(value_type value) noexcept {
escape_and_append_with_quotes<key>();
append_colon();
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
append_null();
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(
std::is_convertible<value_type, std::string_view>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
escape_and_append_with_quotes(value);
}
else {
append(value);
}
}
#endif
} // namespace builder
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H
#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H
@@ -10,40 +10,9 @@
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
#if SIMDJSON_SUPPORTS_CONCEPTS
namespace SIMDJSON_IMPLEMENTATION {
namespace builder {
class string_builder;
}}
template <typename T, typename = void>
struct has_custom_serialization : std::false_type {};
inline constexpr struct serialize_tag {
template <typename T>
requires custom_deserializable<T>
constexpr void operator()(SIMDJSON_IMPLEMENTATION::builder::string_builder& b, T& obj) const{
return tag_invoke(*this, b, obj);
}
} serialize{};
template <typename T>
struct has_custom_serialization<T, std::void_t<
decltype(tag_invoke(serialize, std::declval<SIMDJSON_IMPLEMENTATION::builder::string_builder&>(), std::declval<T&>()))
>> : std::true_type {};
template <typename T>
constexpr bool require_custom_serialization = has_custom_serialization<T>::value;
#else
struct has_custom_serialization : std::false_type {};
#endif // SIMDJSON_SUPPORTS_CONCEPTS
namespace SIMDJSON_IMPLEMENTATION {
namespace builder {
/**
* A builder for JSON strings representing documents. This is a low-level
* builder that is not meant to be used directly by end-users. Though it
@@ -55,9 +24,7 @@ namespace builder {
*/
class string_builder {
public:
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
simdjson_inline string_builder(size_t initial_capacity = 1024);
/**
* Append number (includes Booleans). Booleans are mapped to the strings
@@ -66,7 +33,7 @@ public:
* represents the number.
*/
template<typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
simdjson_inline void append(number_type v) noexcept;
/**
@@ -95,10 +62,7 @@ public:
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(std::string_view input) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template<constevalutil::fixed_string key>
simdjson_inline void escape_and_append_with_quotes() noexcept;
#endif
/**
* Append the character surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
@@ -152,33 +116,8 @@ public:
* The key is escaped and surrounded by double quotes.
* The value is escaped if it is a string.
*/
template<typename key_type, typename value_type>
template<typename key_type, typename value_type>
simdjson_inline void append_key_value(key_type key, value_type value) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template<constevalutil::fixed_string key, typename value_type>
simdjson_inline void append_key_value(value_type value) noexcept;
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(const T &opt);
template <typename T>
requires(require_custom_serialization<T>)
simdjson_inline void append(const T &val);
// Support for string-like types
template <typename T>
requires(std::is_convertible<T, std::string_view>::value ||
std::is_same<T, const char*>::value )
simdjson_inline void append(const T &value);
#endif
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
// Support for range-based appending (std::ranges::view, etc.)
template <std::ranges::range R>
requires (!std::is_convertible<R, std::string_view>::value)
simdjson_inline void append(const R &range) noexcept;
#endif
/**
* Append the std::string_view directly, without escaping.
* There is no UTF-8 validation.
@@ -271,34 +210,6 @@ private:
}
}
#if !SIMDJSON_STATIC_REFLECTION
// fallback implementation until we have static reflection
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = simdjson::SIMDJSON_IMPLEMENTATION::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::SIMDJSON_IMPLEMENTATION::builder::string_builder b(initial_capacity);
b.append(z);
std::string_view s;
auto e = b.view().get(s);
if(e) { return e; }
return std::string(s);
}
template <class Z>
simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::SIMDJSON_IMPLEMENTATION::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::SIMDJSON_IMPLEMENTATION::builder::string_builder b(initial_capacity);
b.append(z);
std::string_view sv;
auto e = b.view().get(sv);
if(e) { return e; }
s.assign(sv.data(), sv.size());
return simdjson::SUCCESS;
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_SUPPORTS_CONCEPTS
} // namespace simdjson
#endif // SIMDJSON_GENERIC_STRING_BUILDER_H
@@ -15,7 +15,6 @@ namespace ondemand {
* The type of a JSON value.
*/
enum class json_type {
unknown=0,
// Start at 1 to catch uninitialized / default values more easily
array=1, ///< A JSON array ( [ 1, 2, 3 ... ] )
object, ///< A JSON object ( { "a": 1, "b" 2, ... } )
+1 -51
View File
@@ -9,10 +9,6 @@
#include "simdjson/generic/ondemand/raw_json_string.h"
#include "simdjson/generic/ondemand/json_iterator.h"
#include "simdjson/generic/ondemand/value-inl.h"
#if SIMDJSON_STATIC_REFLECTION
#include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string
#include <meta>
#endif
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
@@ -70,7 +66,7 @@ simdjson_inline simdjson_result<object> object::start_root(value_iterator &iter)
SIMDJSON_TRY( iter.start_root_object().error() );
return object(iter);
}
simdjson_warn_unused simdjson_inline error_code object::consume() noexcept {
simdjson_inline error_code object::consume() noexcept {
if(iter.is_at_key()) {
/**
* whenever you are pointing at a key, calling skip_child() is
@@ -199,52 +195,6 @@ simdjson_inline simdjson_result<bool> object::reset() & noexcept {
return iter.reset_object();
}
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code object::extract_into(T& out) & noexcept {
// Helper to check if a field name matches any of the requested fields
auto should_extract = [](std::string_view field_name) constexpr -> bool {
return ((FieldNames.view() == field_name) || ...);
};
// Iterate through all members of T using reflection
template for (constexpr auto mem : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only extract this field if it's in our list of requested fields
if constexpr (should_extract(key)) {
// Try to find and extract the field
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
// For optional fields, it's ok if they're missing
auto field_result = find_field_unordered(key);
if (!field_result.error()) {
auto error = field_result.get(out.[:mem:]);
if (error && error != NO_SUCH_FIELD) {
return error;
}
} else if (field_result.error() != NO_SUCH_FIELD) {
return field_result.error();
} else {
out.[:mem:].reset();
}
} else {
// For required fields (in the requested list), fail if missing
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
}
}
}
};
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson

Some files were not shown because too many files have changed in this diff Show More