mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Compare commits
76 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d326f2ce9f | |||
| ca42a49fba | |||
| a7f8fb71c5 | |||
| a553db4c67 | |||
| 1fa1af8c15 | |||
| 5ed1044056 | |||
| 62913867ff | |||
| 6700d48b57 | |||
| b5577d5e85 | |||
| b84a4ec2b9 | |||
| 1638a185f7 | |||
| 6fe450f5ce | |||
| c7b70de070 | |||
| 3279fbd55b | |||
| 66e64e0e5f | |||
| 03f81e66af | |||
| e3b7eddb37 | |||
| 99c4ba6e8f | |||
| 6aa7eea334 | |||
| 6a47cda07f | |||
| 617c69e104 | |||
| 625adceb24 | |||
| bd0e9c1336 | |||
| 7bf82b02d5 | |||
| 88a1b3e83b | |||
| c72954eade | |||
| 4456a10469 | |||
| d8ed2417ad | |||
| 5517df7aee | |||
| 6e618b0805 | |||
| bde288a623 | |||
| b2932d1b8f | |||
| a7811090ef | |||
| 3320885fac | |||
| 786c68b158 | |||
| 703ef54bd9 | |||
| 2526068e2f | |||
| ddc7b8c7dd | |||
| d8f90bdd14 | |||
| e38a4923e5 | |||
| e6dfa2e0ed | |||
| 1f369ef210 | |||
| 22dcdc9f1e | |||
| dda2dafa30 | |||
| 72e9d44e10 | |||
| 8aae14931d | |||
| ef3d1ac25f | |||
| 9292480a9b | |||
| 611ea97dbd | |||
| f249e7e128 | |||
| 44fddaa807 | |||
| be16e1ae04 | |||
| 124a33c160 | |||
| a0b870e9c2 | |||
| 68699eb73c | |||
| 06453f1637 | |||
| eb828d97ba | |||
| 00f84fb448 | |||
| 5bf0954c6e | |||
| 3056d54fa3 | |||
| 241b9f6ea7 | |||
| 015daad6a9 | |||
| fc3b7766c0 | |||
| b5c78288e5 | |||
| bd86bb0cfb | |||
| 4840a0347c | |||
| 7f68baec1e | |||
| e2ea5fb8de | |||
| 7dfd165ecd | |||
| 4dfa0a2407 | |||
| e252a21f65 | |||
| 89f2b634df | |||
| addca203d5 | |||
| 1825d7e88c | |||
| f87d6af3e9 | |||
| 8fff57578c |
@@ -1,8 +1,53 @@
|
||||
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.
|
||||
|
||||
|
||||
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.
|
||||
Please read before contributing:
|
||||
- CONTRIBUTING: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md
|
||||
- HACKING: https://github.com/simdjson/simdjson/blob/master/HACKING.md
|
||||
|
||||
If you plan to contribute to simdjson, please read our
|
||||
|
||||
CONTRIBUTING guide: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md and our
|
||||
HACKING guide: https://github.com/simdjson/simdjson/blob/master/HACKING.md
|
||||
|
||||
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!
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Ubuntu ppc64le (GCC 11)
|
||||
name: Ubuntu aarch64 (GCC 13)
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
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)
|
||||
@@ -0,0 +1,29 @@
|
||||
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)
|
||||
@@ -0,0 +1,29 @@
|
||||
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)
|
||||
@@ -0,0 +1,22 @@
|
||||
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
|
||||
@@ -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.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 .
|
||||
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 .
|
||||
|
||||
@@ -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.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 .
|
||||
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 .
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
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
|
||||
Vendored
+3
@@ -3,6 +3,9 @@
|
||||
{"column": 95 },
|
||||
{"column": 120 }
|
||||
],
|
||||
"cmake.configureArgs": [
|
||||
"-DSIMDJSON_DEVELOPER_MODE=ON"
|
||||
],
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"files.associations": {
|
||||
".clangd": "yaml",
|
||||
|
||||
+3
-3
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14)
|
||||
project(
|
||||
simdjson
|
||||
# The version number is modified by tools/release.py
|
||||
VERSION 4.0.0
|
||||
VERSION 4.0.7
|
||||
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 "28.0.0" CACHE STRING "simdjson library version")
|
||||
set(SIMDJSON_LIB_SOVERSION "28" CACHE STRING "simdjson library soversion")
|
||||
set(SIMDJSON_LIB_VERSION "27.0.0" CACHE STRING "simdjson library version")
|
||||
set(SIMDJSON_LIB_SOVERSION "27" CACHE STRING "simdjson library soversion")
|
||||
|
||||
option(SIMDJSON_BUILD_STATIC_LIB "Build simdjson_static library along with simdjson (only makes sense if BUILD_SHARED_LIBS=ON)" OFF)
|
||||
if(SIMDJSON_BUILD_STATIC_LIB AND NOT BUILD_SHARED_LIBS)
|
||||
|
||||
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = "4.0.0"
|
||||
PROJECT_NUMBER = "4.0.7"
|
||||
|
||||
# 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
|
||||
|
||||
+46
-8
@@ -20,13 +20,54 @@ If you plan to contribute to simdjson, please read our [CONTRIBUTING](https://gi
|
||||
Build Quickstart
|
||||
------------------------------
|
||||
|
||||
For non-Windows system,
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -D SIMDJSON_DEVELOPER_MODE=ON ..
|
||||
cmake --build .
|
||||
cmake -B -D SIMDJSON_DEVELOPER_MODE=ON ..
|
||||
cmake --build build
|
||||
ctest --test-dir 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
|
||||
------------------------------
|
||||
|
||||
@@ -103,12 +144,9 @@ 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).
|
||||
* **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).
|
||||
* **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:
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
|
||||
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:simdjson)
|
||||
[![][license img]][license] [![][licensemit img]][licensemit]
|
||||
|
||||
|
||||
@@ -64,6 +62,7 @@ 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.
|
||||
@@ -119,6 +118,7 @@ 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)
|
||||
|
||||
|
||||
@@ -35,13 +35,16 @@ if (TARGET benchmark::benchmark)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(SIMDJSON_STATIC_REFLECTION)
|
||||
add_subdirectory(static_reflect)
|
||||
endif(SIMDJSON_STATIC_REFLECTION)
|
||||
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
check_cxx_compiler_flag("-std=c++20" SIMDJSON_COMPILER_SUPPORTS_CXX20)
|
||||
if(SIMDJSON_EXCEPTIONS AND SIMDJSON_COMPILER_SUPPORTS_CXX20)
|
||||
add_subdirectory(from)
|
||||
endif()
|
||||
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)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# 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}/..)
|
||||
@@ -0,0 +1,127 @@
|
||||
#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;
|
||||
}
|
||||
@@ -44,7 +44,10 @@ struct yyjson_base {
|
||||
|
||||
struct yyjson : yyjson_base {
|
||||
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
|
||||
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), result);
|
||||
yyjson_doc *doc = yyjson_read(json.data(), json.size(), 0);
|
||||
bool b = yyjson_base::run(doc, result);
|
||||
yyjson_doc_free(doc);
|
||||
return b;
|
||||
}
|
||||
};
|
||||
BENCHMARK_TEMPLATE(distinct_user_id, yyjson)->UseManualTime();
|
||||
@@ -52,11 +55,15 @@ 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) {
|
||||
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), 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;
|
||||
}
|
||||
};
|
||||
BENCHMARK_TEMPLATE(distinct_user_id, yyjson_insitu)->UseManualTime();
|
||||
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||
|
||||
} // namespace distinct_user_id
|
||||
|
||||
#endif // SIMDJSON_COMPETITION_YYJSON
|
||||
|
||||
@@ -36,18 +36,26 @@ struct yyjson_base {
|
||||
|
||||
struct yyjson : yyjson_base {
|
||||
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
|
||||
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), find_id, 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;
|
||||
}
|
||||
};
|
||||
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) {
|
||||
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), find_id, 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;
|
||||
}
|
||||
};
|
||||
BENCHMARK_TEMPLATE(find_tweet, yyjson_insitu)->UseManualTime();
|
||||
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||
|
||||
} // namespace find_tweet
|
||||
|
||||
#endif // SIMDJSON_COMPETITION_YYJSON
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
add_executable(from_benchmark from_benchmark.cpp)
|
||||
|
||||
# Compile for C++20.
|
||||
target_compile_features(from_benchmark PRIVATE cxx_std_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)
|
||||
|
||||
@@ -100,6 +100,7 @@ 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;
|
||||
}
|
||||
};
|
||||
@@ -113,6 +114,7 @@ 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;
|
||||
}
|
||||
};
|
||||
@@ -120,4 +122,4 @@ BENCHMARK_TEMPLATE(json2msgpack, yyjson_insitu)->UseManualTime();
|
||||
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||
} // namespace json2msgpack
|
||||
|
||||
#endif // SIMDJSON_COMPETITION_YYJSON
|
||||
#endif // SIMDJSON_COMPETITION_YYJSON
|
||||
|
||||
@@ -49,18 +49,26 @@ struct yyjson_base {
|
||||
|
||||
struct yyjson : yyjson_base {
|
||||
bool run(simdjson::padded_string &json, std::vector<point> &result) {
|
||||
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), result);
|
||||
yyjson_doc *doc = yyjson_read(json.data(), json.size(), 0);
|
||||
bool b = yyjson_base::run(doc, result);
|
||||
yyjson_doc_free(doc);
|
||||
return b;
|
||||
}
|
||||
};
|
||||
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) {
|
||||
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), 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;
|
||||
}
|
||||
};
|
||||
BENCHMARK_TEMPLATE(kostya, yyjson_insitu)->UseManualTime();
|
||||
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||
|
||||
} // namespace kostya
|
||||
|
||||
#endif // SIMDJSON_COMPETITION_YYJSON
|
||||
|
||||
@@ -47,18 +47,26 @@ struct yyjson_base {
|
||||
|
||||
struct yyjson : yyjson_base {
|
||||
bool run(simdjson::padded_string &json, std::vector<point> &result) {
|
||||
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), result);
|
||||
yyjson_doc *doc = yyjson_read(json.data(), json.size(), 0);
|
||||
bool b = yyjson_base::run(doc, result);
|
||||
yyjson_doc_free(doc);
|
||||
return b;
|
||||
}
|
||||
};
|
||||
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) {
|
||||
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), 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;
|
||||
}
|
||||
};
|
||||
BENCHMARK_TEMPLATE(large_random, yyjson_insitu)->UseManualTime();
|
||||
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||
|
||||
} // namespace large_random
|
||||
|
||||
#endif // SIMDJSON_COMPETITION_YYJSON
|
||||
|
||||
@@ -62,19 +62,26 @@ struct yyjson_base {
|
||||
|
||||
struct yyjson : yyjson_base {
|
||||
bool run(simdjson::padded_string &json, std::vector<tweet<std::string_view>> &result) {
|
||||
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), result);
|
||||
yyjson_doc *doc = yyjson_read(json.data(), json.size(), 0);
|
||||
bool b = yyjson_base::run(doc, result);
|
||||
yyjson_doc_free(doc);
|
||||
return b;
|
||||
}
|
||||
};
|
||||
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) {
|
||||
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), 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;
|
||||
}
|
||||
};
|
||||
BENCHMARK_TEMPLATE(partial_tweets, yyjson_insitu)->UseManualTime();
|
||||
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||
|
||||
} // namespace partial_tweets
|
||||
|
||||
#endif // SIMDJSON_COMPETITION_YYJSON
|
||||
|
||||
|
||||
@@ -6,38 +6,42 @@ CPMAddPackage(
|
||||
EXCLUDE_FROM_ALL YES
|
||||
)
|
||||
|
||||
option(SIMDJSON_USE_RUST "Build the static_reflect benchmark" OFF)
|
||||
|
||||
|
||||
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")
|
||||
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()
|
||||
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)
|
||||
message(STATUS "We will not benchmark serde-benchmark." )
|
||||
endif(SIMDJSON_USER_RUST)
|
||||
|
||||
# Add the benchmark executable targets
|
||||
add_subdirectory(twitter_benchmark)
|
||||
|
||||
@@ -51,18 +51,26 @@ struct yyjson_base {
|
||||
|
||||
struct yyjson : yyjson_base {
|
||||
bool run(simdjson::padded_string &json, int64_t max_retweet_count, top_tweet_result<StringType> &result) {
|
||||
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), max_retweet_count, 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;
|
||||
}
|
||||
};
|
||||
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) {
|
||||
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), max_retweet_count, 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;
|
||||
}
|
||||
};
|
||||
BENCHMARK_TEMPLATE(top_tweet, yyjson_insitu)->UseManualTime();
|
||||
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||
|
||||
} // namespace top_tweet
|
||||
|
||||
#endif // SIMDJSON_COMPETITION_YYJSON
|
||||
|
||||
@@ -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 POSITION_INDEPENDENT_CODE ON)
|
||||
set_target_properties(simdjson-internal-flags PROPERTIES INTERFACE_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,6 +32,9 @@ 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
|
||||
@@ -171,6 +174,10 @@ 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)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR riscv64)
|
||||
|
||||
set(CMAKE_CROSSCOMPILING_EMULATOR "qemu-riscv64-static")
|
||||
+176
-21
@@ -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 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 against 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 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).
|
||||
|
||||
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 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)).
|
||||
- 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)).
|
||||
- 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 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).
|
||||
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).
|
||||
|
||||
|
||||
|
||||
@@ -320,7 +320,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 which only supports C++11. When we detect that `string_view` is natively
|
||||
on compilers that 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 +343,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 otimizations, you can set
|
||||
because you may want the checks while building with optimizations, 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,9 +371,8 @@ 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 the
|
||||
value (`json_type::array`, `json_type::object`, `json_type::number`, `json_type::string`,
|
||||
`json_type::boolean`, `json_type::null`). 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 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`)
|
||||
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.
|
||||
@@ -406,7 +405,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: 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
|
||||
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
|
||||
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
|
||||
@@ -453,7 +452,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. 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
|
||||
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
|
||||
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"]`.
|
||||
@@ -516,7 +515,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 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).
|
||||
* **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).
|
||||
> ```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;
|
||||
@@ -1376,7 +1375,16 @@ your code with the `SIMDJSON_STATIC_REFLECTION` macro set:
|
||||
|
||||
Then you can deserialize a type such as `Car` automatically:
|
||||
|
||||
```cpp
|
||||
|
||||
|
||||
```C++
|
||||
struct Car {
|
||||
std::string make;
|
||||
std::string model;
|
||||
int year;
|
||||
std::vector<float> tire_pressure;
|
||||
};
|
||||
|
||||
std::string json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||
"tire_pressure": [ 40.1, 39.9 ] } )";
|
||||
simdjson::ondemand::parser parser;
|
||||
@@ -1385,6 +1393,111 @@ 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).
|
||||
|
||||
@@ -1400,6 +1513,12 @@ type without a document instance like so:
|
||||
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.
|
||||
|
||||
```cpp
|
||||
@@ -1460,9 +1579,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:
|
||||
|
||||
@@ -1482,7 +1601,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"( [
|
||||
@@ -1637,7 +1756,7 @@ x = obj.at_path("$.d.foo2.a.2"); // 30
|
||||
Error handling
|
||||
--------------
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
The entire simdjson API is usable with and without exceptions. All simdjson APIs that can fail return `simdjson_result<T>`, which is a <value, error_code>
|
||||
pair. You can retrieve the value with .get() without generating an exception, like so:
|
||||
@@ -2688,15 +2807,16 @@ a parameter a reference to a `std::string`.
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc = parser.iterate(json);
|
||||
std::string name;
|
||||
doc["name"].get_string(name);
|
||||
auto error = doc["name"].get_string(name);
|
||||
if(error) { /* handle error */ }
|
||||
```
|
||||
|
||||
The same routine can be written without exceptions handling:
|
||||
|
||||
```C++
|
||||
std::string name;
|
||||
auto err = doc["name"].get_string(name);
|
||||
if (err) { /* handle error */ }
|
||||
auto error = doc["name"].get_string(name);
|
||||
if (error) { /* handle error */ }
|
||||
```
|
||||
|
||||
The `std::string` instance, once created, is independent. Unlike our `std::string_view` instances,
|
||||
@@ -3099,10 +3219,45 @@ 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 `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 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 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.
|
||||
- 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 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"];
|
||||
|
||||
+123
-13
@@ -34,27 +34,28 @@ 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.
|
||||
Parameters:
|
||||
- `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(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 writting the content, if you have reasons to believe that the content might violate UTF-8 conventions, you can check it as follows:
|
||||
After writing 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.
|
||||
malformed UTF-8. Note that we do not automatically call `validate_unicode()`.
|
||||
|
||||
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>`.
|
||||
- `view()`: Returns a view of the written JSON buffer as a `simdjson_result<std::string_view>` (C++20).
|
||||
|
||||
The later method (`view()`) is recommended.
|
||||
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()`).
|
||||
|
||||
Example: string_builder
|
||||
---------------------------
|
||||
@@ -131,20 +132,20 @@ In all cases, the `std::string_view` instance depends the corresponding `string_
|
||||
|
||||
|
||||
|
||||
If you have C++20, you can simplify the code, as the `std::vector<double>` is automatically
|
||||
supported.
|
||||
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_key_value<"make">(c.make);
|
||||
sb.append_comma();
|
||||
sb.append_key_value("model", c.model);
|
||||
sb.append_key_value<"model">(c.model);
|
||||
sb.append_comma();
|
||||
sb.append_key_value("year", c.year);
|
||||
sb.append_key_value<"year">(c.year);
|
||||
sb.append_comma();
|
||||
sb.append_key_value("tire_pressure", c.tire_pressure);
|
||||
sb.append_key_value<"tire_pressure">(c.tire_pressure);
|
||||
sb.end_object();
|
||||
std::string_view p = sb.view();
|
||||
```
|
||||
@@ -175,9 +176,52 @@ 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
|
||||
------------------------
|
||||
|
||||
@@ -239,7 +283,38 @@ 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
|
||||
|
||||
@@ -248,9 +323,44 @@ pattern:
|
||||
|
||||
```cpp
|
||||
std::string json;
|
||||
if(simdjson::to(c).get(json)) {
|
||||
if(simdjson::to_json(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
-1
@@ -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 Pytho or C++. If you set the macro
|
||||
By default, the string `-0` is parsed as the integer 0 as in Python 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`.
|
||||
|
||||
+1
-1
@@ -253,7 +253,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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ using namespace simd;
|
||||
struct backslash_and_quote {
|
||||
public:
|
||||
static constexpr uint32_t BYTES_PROCESSED = 32;
|
||||
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
simdjson_inline 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; }
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#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.
|
||||
|
||||
@@ -122,11 +122,46 @@ 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_CONCEPTS_H
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
#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
|
||||
@@ -58,6 +58,12 @@ inline simdjson_result<T> auto_parser<parser_type>::result() noexcept(is_nothrow
|
||||
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>();
|
||||
@@ -73,12 +79,7 @@ inline simdjson_result<ondemand::number> auto_parser<parser_type>::number() noex
|
||||
return result<ondemand::number>();
|
||||
}
|
||||
|
||||
template <typename parser_type>
|
||||
template <typename T>
|
||||
inline auto_parser<parser_type>::operator simdjson_result<T>() noexcept(is_nothrow_gettable<T>) {
|
||||
return result<T>();
|
||||
}
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <typename parser_type>
|
||||
template <typename T>
|
||||
inline auto_parser<parser_type>::operator T() noexcept(false) {
|
||||
@@ -87,6 +88,7 @@ inline auto_parser<parser_type>::operator T() noexcept(false) {
|
||||
}
|
||||
return m_doc.get<T>();
|
||||
}
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
|
||||
template <typename parser_type>
|
||||
template <typename T>
|
||||
@@ -109,13 +111,23 @@ inline T to_adaptor<T>::operator()(simdjson_result<ondemand::value> &val) const
|
||||
|
||||
template <typename T>
|
||||
inline auto to_adaptor<T>::operator()(padded_string_view const str) const noexcept {
|
||||
return auto_parser{str};
|
||||
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
|
||||
|
||||
@@ -46,16 +46,19 @@ public:
|
||||
|
||||
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>);
|
||||
|
||||
|
||||
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;
|
||||
|
||||
template <typename T>
|
||||
simdjson_warn_unused simdjson_inline explicit(false) operator simdjson_result<T>() noexcept(is_nothrow_gettable<T>);
|
||||
|
||||
#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>);
|
||||
@@ -72,7 +75,14 @@ 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
|
||||
|
||||
|
||||
@@ -67,8 +67,38 @@ 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()); }
|
||||
@@ -93,6 +123,7 @@ 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;
|
||||
|
||||
@@ -108,6 +108,10 @@ 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
|
||||
@@ -168,8 +172,27 @@ 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.
|
||||
*
|
||||
@@ -253,6 +276,7 @@ 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.
|
||||
*/
|
||||
@@ -306,8 +330,11 @@ struct simdjson_result : public internal::simdjson_result_base<T> {
|
||||
*/
|
||||
simdjson_inline error_code error() const noexcept;
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
|
||||
|
||||
#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 static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
simdjson_inline 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,12 +29,41 @@ simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_b
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
simdjson_inline error_code implementation_simdjson_result_base<T>::error() const noexcept {
|
||||
simdjson_warn_unused 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,12 +67,17 @@ struct implementation_simdjson_result_base {
|
||||
*
|
||||
* @param value The variable to assign the value to. May not be set if there is an error.
|
||||
*/
|
||||
simdjson_inline error_code get(T &value) && noexcept;
|
||||
simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept;
|
||||
|
||||
/**
|
||||
* The error.
|
||||
*/
|
||||
simdjson_inline error_code error() const noexcept;
|
||||
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;
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
|
||||
@@ -81,6 +86,16 @@ 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);
|
||||
|
||||
/**
|
||||
|
||||
@@ -361,7 +361,7 @@ simdjson_inline bool is_digit(const uint8_t c) {
|
||||
return static_cast<uint8_t>(c - '0') <= 9;
|
||||
}
|
||||
|
||||
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_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) {
|
||||
// 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_inline error_code parse_decimal_after_separator(simdjson_unused const u
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) {
|
||||
simdjson_warn_unused 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_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_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) {
|
||||
// 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_inline error_code write_float(const uint8_t *const src, bool negative,
|
||||
//
|
||||
// Our objective is accurate parsing (ULP of 0) at high speed.
|
||||
template<typename W>
|
||||
simdjson_inline error_code parse_number(const uint8_t *const src, W &writer);
|
||||
simdjson_warn_unused 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_inline error_code parse_number(const uint8_t *const, W &writer) {
|
||||
simdjson_warn_unused 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_inline error_code parse_number(const uint8_t *const src, W &writer) {
|
||||
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
|
||||
//
|
||||
// Check for minus sign
|
||||
//
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
#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"
|
||||
@@ -45,9 +48,11 @@
|
||||
#include "simdjson/generic/ondemand/token_iterator-inl.h"
|
||||
#include "simdjson/generic/ondemand/value_iterator-inl.h"
|
||||
|
||||
// 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"
|
||||
// JSON builder inline definitions
|
||||
#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_inline error_code array::consume() noexcept {
|
||||
simdjson_warn_unused simdjson_warn_unused simdjson_inline error_code array::consume() noexcept {
|
||||
auto error = iter.json_iter().skip_child(iter.depth()-1);
|
||||
if(error) { iter.abandon(); }
|
||||
return error;
|
||||
|
||||
@@ -141,7 +141,7 @@ public:
|
||||
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
|
||||
*/
|
||||
template <typename T>
|
||||
simdjson_inline error_code get(T &out)
|
||||
simdjson_warn_unused 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);
|
||||
@@ -166,7 +166,7 @@ protected:
|
||||
/**
|
||||
* Go to the end of the array, no matter where you are right now.
|
||||
*/
|
||||
simdjson_inline error_code consume() noexcept;
|
||||
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
|
||||
|
||||
/**
|
||||
* Begin array iteration.
|
||||
@@ -252,7 +252,7 @@ public:
|
||||
return first.get<T>();
|
||||
}
|
||||
template<typename T>
|
||||
simdjson_inline error_code get(T& out) noexcept {
|
||||
simdjson_warn_unused 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;
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* @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
|
||||
@@ -0,0 +1,464 @@
|
||||
/**
|
||||
* @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
|
||||
@@ -7,55 +7,8 @@
|
||||
#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
|
||||
|
||||
@@ -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_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept {
|
||||
simdjson_warn_unused 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_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_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_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_inline error_code document::consume() noexcept {
|
||||
simdjson_warn_unused simdjson_inline error_code document::consume() noexcept {
|
||||
bool scalar = false;
|
||||
auto error = is_scalar().get(scalar);
|
||||
if(error) { return error; }
|
||||
@@ -347,6 +347,54 @@ 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
|
||||
@@ -454,7 +502,7 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
|
||||
return first.get_string(allow_replacement);
|
||||
}
|
||||
template <typename string_type>
|
||||
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_string(string_type& receiver, bool allow_replacement) noexcept {
|
||||
simdjson_warn_unused 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);
|
||||
}
|
||||
@@ -490,12 +538,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_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get(T &out) & noexcept {
|
||||
simdjson_warn_unused 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_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get(T &out) && noexcept {
|
||||
simdjson_warn_unused 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);
|
||||
}
|
||||
@@ -505,8 +553,8 @@ template<> simdjson_deprecated simdjson_inline simdjson_result<SIMDJSON_IMPLEMEN
|
||||
if (error()) { return error(); }
|
||||
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document>(first);
|
||||
}
|
||||
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 {
|
||||
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 {
|
||||
if (error()) { return error(); }
|
||||
out = std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document>(first);
|
||||
return SUCCESS;
|
||||
@@ -624,6 +672,15 @@ 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
|
||||
|
||||
|
||||
@@ -658,7 +715,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_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_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 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); }
|
||||
@@ -711,7 +768,13 @@ 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
|
||||
@@ -808,7 +871,7 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
|
||||
return first.get_string(allow_replacement);
|
||||
}
|
||||
template <typename string_type>
|
||||
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_string(string_type& receiver, bool allow_replacement) noexcept {
|
||||
simdjson_warn_unused 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);
|
||||
}
|
||||
@@ -843,12 +906,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_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(T &out) & noexcept {
|
||||
simdjson_warn_unused 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_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(T &out) && noexcept {
|
||||
simdjson_warn_unused 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);
|
||||
}
|
||||
@@ -865,13 +928,13 @@ simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::o
|
||||
return first.is_string();
|
||||
}
|
||||
template <>
|
||||
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(SIMDJSON_IMPLEMENTATION::ondemand::document_reference &out) & noexcept {
|
||||
simdjson_warn_unused 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_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(SIMDJSON_IMPLEMENTATION::ondemand::document_reference &out) && noexcept {
|
||||
simdjson_warn_unused 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;
|
||||
@@ -959,7 +1022,14 @@ 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
|
||||
|
||||
@@ -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_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
/**
|
||||
* Cast this JSON value to a string.
|
||||
*
|
||||
@@ -228,7 +228,7 @@ public:
|
||||
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
|
||||
*/
|
||||
template<typename T>
|
||||
simdjson_inline error_code get(T &out) &
|
||||
simdjson_warn_unused simdjson_inline error_code get(T &out) &
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document> : true)
|
||||
#else
|
||||
@@ -725,11 +725,41 @@ 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_inline error_code consume() noexcept;
|
||||
simdjson_warn_unused 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;
|
||||
@@ -782,7 +812,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_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
simdjson_warn_unused 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;
|
||||
@@ -826,7 +856,7 @@ public:
|
||||
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
|
||||
*/
|
||||
template<typename T>
|
||||
simdjson_inline error_code get(T &out) &
|
||||
simdjson_warn_unused simdjson_inline error_code get(T &out) &
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document_reference> : true)
|
||||
#else
|
||||
@@ -861,6 +891,11 @@ public:
|
||||
/** @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>
|
||||
@@ -929,7 +964,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_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
simdjson_warn_unused 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;
|
||||
@@ -942,6 +977,9 @@ 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);
|
||||
@@ -981,6 +1019,11 @@ 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
|
||||
};
|
||||
|
||||
|
||||
@@ -1007,7 +1050,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_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
simdjson_warn_unused 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;
|
||||
@@ -1058,6 +1101,11 @@ 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
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -312,7 +312,10 @@ 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) { doc.iter.consume_character(','); }
|
||||
if (allow_comma_separated) {
|
||||
error_code ignored = doc.iter.consume_character(',');
|
||||
static_cast<void>(ignored); // ignored on purpose
|
||||
}
|
||||
// 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();
|
||||
|
||||
@@ -114,7 +114,7 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
|
||||
}
|
||||
|
||||
template<typename string_type>
|
||||
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::field>::unescaped_key(string_type &receiver, bool allow_replacement) noexcept {
|
||||
simdjson_warn_unused 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);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
#include <charconv>
|
||||
#include <cstring>
|
||||
#include <experimental/meta>
|
||||
#include <meta>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
@@ -25,30 +25,21 @@ 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(container_but_not_string<T>)
|
||||
requires(concepts::container_but_not_string<T> && !require_custom_serialization<T>)
|
||||
constexpr void atom(string_builder &b, const T &t) {
|
||||
if (t.size() == 0) {
|
||||
auto it = t.begin();
|
||||
auto end = t.end();
|
||||
if (it == end) {
|
||||
b.append_raw("[]");
|
||||
return;
|
||||
}
|
||||
b.append('[');
|
||||
atom(b, t[0]);
|
||||
for (size_t i = 1; i < t.size(); ++i) {
|
||||
atom(b, *it);
|
||||
++it;
|
||||
for (; it != end; ++it) {
|
||||
b.append(',');
|
||||
atom(b, t[i]);
|
||||
atom(b, *it);
|
||||
}
|
||||
b.append(']');
|
||||
}
|
||||
@@ -63,6 +54,7 @@ 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("{}");
|
||||
@@ -89,12 +81,9 @@ 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> && !container_but_not_string<T> &&
|
||||
requires(std::is_class_v<T> && !concepts::container_but_not_string<T> &&
|
||||
!concepts::string_view_keyed_map<T> &&
|
||||
!concepts::optional_type<T> &&
|
||||
!concepts::smart_pointer<T> &&
|
||||
@@ -102,14 +91,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>)
|
||||
!std::is_same_v<T, char> && !require_custom_serialization<T>)
|
||||
constexpr void atom(string_builder &b, const T &t) {
|
||||
int i = 0;
|
||||
b.append('{');
|
||||
[:expand(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked())):] >> [&]<auto dm>() {
|
||||
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
|
||||
if (i != 0)
|
||||
b.append(',');
|
||||
constexpr auto key = std::define_static_string(consteval_to_quoted_escaped(std::meta::identifier_of(dm)));
|
||||
constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm)));
|
||||
b.append_raw(key);
|
||||
b.append(':');
|
||||
atom(b, t.[:dm:]);
|
||||
@@ -120,6 +109,7 @@ 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());
|
||||
@@ -130,6 +120,7 @@ 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);
|
||||
@@ -140,24 +131,19 @@ 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>)
|
||||
requires(std::is_enum_v<T> && !require_custom_serialization<T>)
|
||||
void atom(string_builder &b, const T &e) {
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
std::string_view result = "<unnamed>";
|
||||
[:expand(std::meta::enumerators_of(^^T)):] >> [&]<auto enum_val>{
|
||||
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)));
|
||||
if (e == [:enum_val:]) {
|
||||
result = std::meta::identifier_of(enum_val);
|
||||
b.append_raw(enum_str);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
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));
|
||||
}
|
||||
// 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));
|
||||
@@ -166,10 +152,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(!container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
|
||||
requires(!concepts::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*>)
|
||||
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
|
||||
constexpr void atom(string_builder &b, const T &container) {
|
||||
if (container.empty()) {
|
||||
b.append_raw("[]");
|
||||
@@ -204,32 +190,35 @@ 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(!container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
|
||||
requires(!concepts::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*>)
|
||||
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
|
||||
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> && !container_but_not_string<Z> &&
|
||||
requires(std::is_class_v<Z> && !concepts::container_but_not_string<Z> &&
|
||||
!concepts::string_view_keyed_map<Z> &&
|
||||
!concepts::optional_type<Z> &&
|
||||
!concepts::smart_pointer<Z> &&
|
||||
@@ -237,14 +226,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>)
|
||||
!std::is_same_v<Z, char> && !require_custom_serialization<Z>)
|
||||
void append(string_builder &b, const Z &z) {
|
||||
int i = 0;
|
||||
b.append('{');
|
||||
[:expand(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked())):] >> [&]<auto dm>() {
|
||||
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) {
|
||||
if (i != 0)
|
||||
b.append(',');
|
||||
constexpr auto key = std::define_static_string(consteval_to_quoted_escaped(std::meta::identifier_of(dm)));
|
||||
constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm)));
|
||||
b.append_raw(key);
|
||||
b.append(':');
|
||||
atom(b, z.[:dm:]);
|
||||
@@ -253,25 +242,35 @@ void append(string_builder &b, const Z &z) {
|
||||
b.append('}');
|
||||
}
|
||||
|
||||
// works for container
|
||||
// works for container that have begin() and end() iterators
|
||||
template <class Z>
|
||||
requires(container_but_not_string<Z>)
|
||||
requires(concepts::container_but_not_string<Z> && !require_custom_serialization<Z>)
|
||||
void append(string_builder &b, const Z &z) {
|
||||
if (z.size() == 0) {
|
||||
auto it = z.begin();
|
||||
auto end = z.end();
|
||||
if (it == end) {
|
||||
b.append_raw("[]");
|
||||
return;
|
||||
}
|
||||
b.append('[');
|
||||
atom(b, z[0]);
|
||||
for (size_t i = 1; i < z.size(); ++i) {
|
||||
atom(b, *it);
|
||||
++it;
|
||||
for (; it != end; ++it) {
|
||||
b.append(',');
|
||||
atom(b, z[i]);
|
||||
atom(b, *it);
|
||||
}
|
||||
b.append(']');
|
||||
}
|
||||
|
||||
template <class Z>
|
||||
simdjson_result<std::string> to_json_string(const Z &z, size_t initial_capacity = 1024) {
|
||||
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) {
|
||||
string_builder b(initial_capacity);
|
||||
append(b, z);
|
||||
std::string_view s;
|
||||
@@ -280,8 +279,8 @@ simdjson_result<std::string> to_json_string(const Z &z, size_t initial_capacity
|
||||
}
|
||||
|
||||
template <class Z>
|
||||
simdjson_error to_json(const Z &z, std::string &s) {
|
||||
string_builder b;
|
||||
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);
|
||||
append(b, z);
|
||||
std::string_view view;
|
||||
if(auto e = b.view().get(view); e) { return e; }
|
||||
@@ -294,13 +293,88 @@ 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_result<std::string> to_json(const Z &z, size_t initial_capacity = 1024) {
|
||||
return SIMDJSON_IMPLEMENTATION::builder::to_json_string(z, initial_capacity);
|
||||
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);
|
||||
}
|
||||
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
|
||||
|
||||
@@ -344,7 +344,7 @@ simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept {
|
||||
return _string_buf_loc;
|
||||
}
|
||||
|
||||
simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept {
|
||||
simdjson_warn_unused 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_inline error_code json_iterator::consume_character(char c) noexcept {
|
||||
simdjson_warn_unused 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_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept {
|
||||
simdjson_warn_unused 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_inline error_code report_error(error_code error, const char *message) noexcept;
|
||||
simdjson_warn_unused 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_inline error_code optional_error(error_code error, const char *message) noexcept;
|
||||
simdjson_warn_unused 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_inline error_code consume_character(char c) noexcept;
|
||||
simdjson_warn_unused 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,24 +42,25 @@ 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;
|
||||
@@ -76,7 +77,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;
|
||||
}
|
||||
}
|
||||
@@ -117,7 +118,8 @@ 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(
|
||||
@@ -125,8 +127,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(
|
||||
@@ -141,7 +143,6 @@ 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 {
|
||||
@@ -155,12 +156,17 @@ find_next_json_quotable_character(const std::string_view view,
|
||||
}
|
||||
|
||||
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
|
||||
"\\x0000", "\\x0001", "\\x0002", "\\x0003", "\\x0004", "\\x0005", "\\x0006",
|
||||
"\\x0007", "\\x0008", "\\t", "\\n", "\\x000b", "\\f", "\\r",
|
||||
"\\x000e", "\\x000f", "\\x0010", "\\x0011", "\\x0012", "\\x0013", "\\x0014",
|
||||
"\\x0015", "\\x0016", "\\x0017", "\\x0018", "\\x0019", "\\x001a", "\\x001b",
|
||||
"\\x001c", "\\x001d", "\\x001e", "\\x001f"};
|
||||
"\\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"};
|
||||
|
||||
// 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);
|
||||
@@ -201,29 +207,6 @@ 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),
|
||||
@@ -302,10 +285,13 @@ simdjson_inline void string_builder::clear() noexcept {
|
||||
|
||||
namespace internal {
|
||||
|
||||
// We could specialize further for 32-bit integers.
|
||||
simdjson_really_inline int int_log2(uint32_t x) { return (63 - leading_zeroes(x | 1)); }
|
||||
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);
|
||||
}
|
||||
|
||||
simdjson_really_inline int fast_digit_count(uint32_t x) {
|
||||
simdjson_really_inline int fast_digit_count_32(uint32_t x) {
|
||||
static uint64_t table[] = {
|
||||
4294967296, 8589934582, 8589934582, 8589934582, 12884901788,
|
||||
12884901788, 12884901788, 17179868184, 17179868184, 17179868184,
|
||||
@@ -317,9 +303,7 @@ simdjson_really_inline int fast_digit_count(uint32_t x) {
|
||||
return uint32_t((x + table[int_log2(x)]) >> 32);
|
||||
}
|
||||
|
||||
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) {
|
||||
simdjson_really_inline int fast_digit_count_64(uint64_t x) {
|
||||
static uint64_t table[] = {9,
|
||||
99,
|
||||
999,
|
||||
@@ -350,26 +334,31 @@ 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");
|
||||
return fast_digit_count(v);
|
||||
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));
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
@@ -405,7 +394,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;
|
||||
}
|
||||
@@ -432,7 +421,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;
|
||||
}
|
||||
@@ -484,10 +473,17 @@ 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);
|
||||
@@ -512,6 +508,7 @@ simdjson_inline void string_builder::append_raw(const char *str,
|
||||
#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);
|
||||
@@ -519,18 +516,25 @@ simdjson_inline void string_builder::append(const T &opt) {
|
||||
append_null();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires(std::is_convertible<T, std::string_view>::value ||
|
||||
std::is_same<T, const char*>::value )
|
||||
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.)
|
||||
// Support for range-based appending (std::ranges::view, etc.)
|
||||
template <std::ranges::range R>
|
||||
requires (!std::is_convertible<R, std::string_view>::value)
|
||||
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);
|
||||
@@ -547,8 +551,8 @@ simdjson_inline void string_builder::append(const R &range) noexcept {
|
||||
|
||||
// Append remaining items with preceding commas
|
||||
for (; it != end; ++it) {
|
||||
append_comma();
|
||||
append_key_value(it->first, it->second);
|
||||
append_comma();
|
||||
append_key_value(it->first, it->second);
|
||||
}
|
||||
end_object();
|
||||
} else {
|
||||
@@ -564,11 +568,10 @@ simdjson_inline void string_builder::append(const R &range) noexcept {
|
||||
|
||||
// Append remaining items with preceding commas
|
||||
for (; it != end; ++it) {
|
||||
append_comma();
|
||||
append(*it);
|
||||
append_comma();
|
||||
append(*it);
|
||||
}
|
||||
end_array();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -576,7 +579,7 @@ simdjson_inline void string_builder::append(const R &range) noexcept {
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
simdjson_inline string_builder::operator std::string() const noexcept(false) {
|
||||
return std::string(std::string_view());
|
||||
return std::string(operator std::string_view());
|
||||
}
|
||||
|
||||
simdjson_inline string_builder::operator std::string_view() const
|
||||
@@ -605,70 +608,95 @@ 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_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,9 +10,40 @@
|
||||
#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
|
||||
@@ -24,7 +55,9 @@ namespace builder {
|
||||
*/
|
||||
class string_builder {
|
||||
public:
|
||||
simdjson_inline string_builder(size_t initial_capacity = 1024);
|
||||
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
|
||||
|
||||
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
|
||||
|
||||
/**
|
||||
* Append number (includes Booleans). Booleans are mapped to the strings
|
||||
@@ -33,7 +66,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;
|
||||
|
||||
/**
|
||||
@@ -62,7 +95,10 @@ 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.
|
||||
@@ -116,13 +152,21 @@ 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 ||
|
||||
@@ -232,7 +276,7 @@ private:
|
||||
#if !SIMDJSON_STATIC_REFLECTION
|
||||
// fallback implementation until we have static reflection
|
||||
template <class Z>
|
||||
simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = 1024) {
|
||||
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;
|
||||
@@ -240,8 +284,20 @@ simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = 1024)
|
||||
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
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
#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 {
|
||||
@@ -66,7 +70,7 @@ simdjson_inline simdjson_result<object> object::start_root(value_iterator &iter)
|
||||
SIMDJSON_TRY( iter.start_root_object().error() );
|
||||
return object(iter);
|
||||
}
|
||||
simdjson_inline error_code object::consume() noexcept {
|
||||
simdjson_warn_unused simdjson_inline error_code object::consume() noexcept {
|
||||
if(iter.is_at_key()) {
|
||||
/**
|
||||
* whenever you are pointing at a key, calling skip_child() is
|
||||
@@ -195,6 +199,52 @@ 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
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
#include "simdjson/generic/ondemand/base.h"
|
||||
#include "simdjson/generic/implementation_simdjson_result_base.h"
|
||||
#include "simdjson/generic/ondemand/value_iterator.h"
|
||||
#if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS
|
||||
#include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string
|
||||
#endif
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
namespace simdjson {
|
||||
@@ -211,7 +214,7 @@ public:
|
||||
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
|
||||
*/
|
||||
template <typename T>
|
||||
simdjson_inline error_code get(T &out)
|
||||
simdjson_warn_unused simdjson_inline error_code get(T &out)
|
||||
noexcept(custom_deserializable<T, object> ? nothrow_custom_deserializable<T, object> : true) {
|
||||
static_assert(custom_deserializable<T, object>);
|
||||
return deserialize(*this, out);
|
||||
@@ -231,12 +234,42 @@ public:
|
||||
SIMDJSON_TRY(get<T>(out));
|
||||
return out;
|
||||
}
|
||||
|
||||
#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;
|
||||
* object.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
|
||||
#endif // SIMDJSON_SUPPORTS_CONCEPTS
|
||||
protected:
|
||||
/**
|
||||
* Go to the end of the object, no matter where you are right now.
|
||||
*/
|
||||
simdjson_inline error_code consume() noexcept;
|
||||
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
|
||||
static simdjson_inline simdjson_result<object> start(value_iterator &iter) noexcept;
|
||||
static simdjson_inline simdjson_result<object> start_root(value_iterator &iter) noexcept;
|
||||
static simdjson_inline simdjson_result<object> started(value_iterator &iter) noexcept;
|
||||
@@ -291,7 +324,7 @@ public:
|
||||
return first.get<T>();
|
||||
}
|
||||
template<typename T>
|
||||
simdjson_inline error_code get(T& out) noexcept {
|
||||
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept {
|
||||
if (error()) { return error(); }
|
||||
if constexpr (std::is_same_v<T, SIMDJSON_IMPLEMENTATION::ondemand::object>) {
|
||||
out = first;
|
||||
@@ -300,6 +333,16 @@ public:
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
// TODO: move this code into object-inl.h
|
||||
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 {
|
||||
if (error()) { return error(); }
|
||||
return first.extract_into<FieldNames...>(out);
|
||||
}
|
||||
#endif // SIMDJSON_STATIC_REFLECTION
|
||||
#endif // SIMDJSON_SUPPORTS_CONCEPTS
|
||||
};
|
||||
|
||||
|
||||
@@ -15,8 +15,13 @@ namespace ondemand {
|
||||
|
||||
simdjson_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {}
|
||||
|
||||
simdjson_inline const char * raw_json_string::raw() const noexcept { return reinterpret_cast<const char *>(buf); }
|
||||
simdjson_inline const char * raw_json_string::raw() const noexcept {
|
||||
return reinterpret_cast<const char *>(buf);
|
||||
}
|
||||
|
||||
simdjson_inline char raw_json_string::operator[](size_t i) const noexcept {
|
||||
return reinterpret_cast<const char *>(buf)[i];
|
||||
}
|
||||
|
||||
simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept {
|
||||
size_t pos{0};
|
||||
@@ -193,6 +198,10 @@ simdjson_inline simdjson_result<const char *> simdjson_result<SIMDJSON_IMPLEMENT
|
||||
if (error()) { return error(); }
|
||||
return first.raw();
|
||||
}
|
||||
simdjson_inline char simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string>::operator[](size_t i) const noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first[i];
|
||||
}
|
||||
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string>::unescape(SIMDJSON_IMPLEMENTATION::ondemand::json_iterator &iter, bool allow_replacement) const noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.unescape(iter, allow_replacement);
|
||||
|
||||
@@ -58,6 +58,12 @@ public:
|
||||
*/
|
||||
simdjson_inline const char * raw() const noexcept;
|
||||
|
||||
/**
|
||||
* Get the character at index i. This is unchecked.
|
||||
* [0] when the string is of length 0 returns the final quote (").
|
||||
*/
|
||||
simdjson_inline char operator[](size_t i) const noexcept;
|
||||
|
||||
/**
|
||||
* This compares the current instance to the std::string_view target: returns true if
|
||||
* they are byte-by-byte equal (no escaping is done) on target.size() characters,
|
||||
@@ -197,10 +203,10 @@ public:
|
||||
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
|
||||
|
||||
simdjson_inline simdjson_result<const char *> raw() const noexcept;
|
||||
simdjson_inline char operator[](size_t) const noexcept;
|
||||
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(SIMDJSON_IMPLEMENTATION::ondemand::json_iterator &iter, bool allow_replacement) const noexcept;
|
||||
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape_wobbly(SIMDJSON_IMPLEMENTATION::ondemand::json_iterator &iter) const noexcept;
|
||||
};
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H
|
||||
@@ -16,15 +16,12 @@
|
||||
#endif
|
||||
|
||||
namespace simdjson {
|
||||
template <typename T>
|
||||
constexpr bool require_custom_serialization = false;
|
||||
|
||||
//////////////////////////////
|
||||
// Number deserialization
|
||||
//////////////////////////////
|
||||
|
||||
template <std::unsigned_integral T>
|
||||
requires(!require_custom_serialization<T>)
|
||||
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
||||
using limits = std::numeric_limits<T>;
|
||||
|
||||
@@ -38,7 +35,6 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
||||
}
|
||||
|
||||
template <std::floating_point T>
|
||||
requires(!require_custom_serialization<T>)
|
||||
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
||||
double x;
|
||||
SIMDJSON_TRY(val.get_double().get(x));
|
||||
@@ -47,7 +43,6 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
||||
}
|
||||
|
||||
template <std::signed_integral T>
|
||||
requires(!require_custom_serialization<T>)
|
||||
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
||||
using limits = std::numeric_limits<T>;
|
||||
|
||||
@@ -77,7 +72,6 @@ error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept {
|
||||
|
||||
// any string-like type (can be constructed from std::string_view)
|
||||
template <concepts::constructible_from_string_view T, typename ValT>
|
||||
requires(!require_custom_serialization<T>)
|
||||
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::string_view>) {
|
||||
std::string_view str;
|
||||
SIMDJSON_TRY(val.get_string().get(str));
|
||||
@@ -94,7 +88,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothr
|
||||
* doc.get<std::vector<int>>().
|
||||
*/
|
||||
template <concepts::appendable_containers T, typename ValT>
|
||||
requires(!require_custom_serialization<T>)
|
||||
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
|
||||
using value_type = typename std::remove_cvref_t<T>::value_type;
|
||||
static_assert(
|
||||
@@ -140,7 +133,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
|
||||
* string-keyed types.
|
||||
*/
|
||||
template <concepts::string_view_keyed_map T, typename ValT>
|
||||
requires(!require_custom_serialization<T>)
|
||||
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
|
||||
using value_type = typename std::remove_cvref_t<T>::mapped_type;
|
||||
static_assert(
|
||||
@@ -222,7 +214,6 @@ error_code tag_invoke(deserialize_tag, SIMDJSON_IMPLEMENTATION::ondemand::docume
|
||||
* @return status of the conversion
|
||||
*/
|
||||
template <concepts::smart_pointer T, typename ValT>
|
||||
requires(!require_custom_serialization<T>)
|
||||
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable<typename std::remove_cvref_t<T>::element_type, ValT>) {
|
||||
using element_type = typename std::remove_cvref_t<T>::element_type;
|
||||
|
||||
@@ -248,12 +239,13 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser
|
||||
* This CPO (Customization Point Object) will help deserialize into optional types.
|
||||
*/
|
||||
template <concepts::optional_type T>
|
||||
requires(!require_custom_serialization<T>)
|
||||
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable<typename std::remove_cvref_t<T>::value_type, decltype(val)>) {
|
||||
using value_type = typename std::remove_cvref_t<T>::value_type;
|
||||
|
||||
// Check if the value is null
|
||||
if (val.is_null()) {
|
||||
bool is_null_value;
|
||||
SIMDJSON_TRY( val.is_null().get(is_null_value) );
|
||||
if (is_null_value) {
|
||||
out.reset(); // Set to nullopt
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -272,35 +264,9 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deser
|
||||
template <typename T>
|
||||
constexpr bool user_defined_type = (std::is_class_v<T>
|
||||
&& !std::is_same_v<T, std::string> && !std::is_same_v<T, std::string_view> && !concepts::optional_type<T> &&
|
||||
!concepts::appendable_containers<T> && !require_custom_serialization<T>);
|
||||
!concepts::appendable_containers<T>);
|
||||
|
||||
|
||||
// workaround from
|
||||
// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2996r10.html#back-and-forth
|
||||
// for missing expansion statements
|
||||
namespace __impl {
|
||||
template<auto... vals>
|
||||
struct replicator_type {
|
||||
template<typename F>
|
||||
constexpr void operator>>(F body) const {
|
||||
(body.template operator()<vals>(), ...);
|
||||
}
|
||||
};
|
||||
|
||||
template<auto... vals>
|
||||
replicator_type<vals...> replicator = {};
|
||||
}
|
||||
|
||||
template<typename R>
|
||||
consteval auto expand(R range) {
|
||||
std::vector<std::meta::info> args;
|
||||
for (auto r : range) {
|
||||
args.push_back(reflect_constant(r));
|
||||
}
|
||||
return substitute(^^__impl::replicator, args);
|
||||
}
|
||||
// end of workaround
|
||||
|
||||
template <typename T, typename ValT>
|
||||
requires(user_defined_type<T> && std::is_class_v<T>)
|
||||
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
|
||||
@@ -310,19 +276,26 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
|
||||
} else {
|
||||
SIMDJSON_TRY(val.get_object().get(obj));
|
||||
}
|
||||
error_code e = simdjson::SUCCESS;
|
||||
|
||||
[:expand(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked())):] >> [&]<auto mem>() {
|
||||
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));
|
||||
// Note: removed static assert as optional types are now handled generically
|
||||
// as long we are succesful or the field is not found, we continue
|
||||
if(e == simdjson::SUCCESS || e == simdjson::NO_SUCH_FIELD) {
|
||||
e = obj[key].get(out.[:mem:]);
|
||||
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
|
||||
// for optional members, it's ok if the key is missing
|
||||
auto error = obj[key].get(out.[:mem:]);
|
||||
if (error && error != NO_SUCH_FIELD) {
|
||||
if(error == NO_SUCH_FIELD) {
|
||||
out.[:mem:].reset();
|
||||
continue;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
} else {
|
||||
// for non-optional members, the key must be present
|
||||
SIMDJSON_TRY(obj[key].get(out.[:mem:]));
|
||||
}
|
||||
}
|
||||
};
|
||||
return e;
|
||||
return simdjson::SUCCESS;
|
||||
}
|
||||
|
||||
// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12
|
||||
@@ -332,16 +305,15 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
std::string_view str;
|
||||
SIMDJSON_TRY(val.get_string().get(str));
|
||||
|
||||
bool found = false;
|
||||
[:expand(std::meta::enumerators_of(^^T)):] >> [&]<auto enum_val>{
|
||||
if (!found && str == std::meta::identifier_of(enum_val)) {
|
||||
constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
|
||||
template for (constexpr auto enum_val : enumerators) {
|
||||
if (str == std::meta::identifier_of(enum_val)) {
|
||||
out = [:enum_val:];
|
||||
found = true;
|
||||
return SUCCESS;
|
||||
}
|
||||
};
|
||||
|
||||
return found ? SUCCESS : INCORRECT_TYPE;
|
||||
return INCORRECT_TYPE;
|
||||
#else
|
||||
// Fallback: deserialize as integer if reflection not available
|
||||
std::underlying_type_t<T> int_val;
|
||||
@@ -389,7 +361,9 @@ error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &
|
||||
// Unique pointers
|
||||
////////////////////////////////////////
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
bool is_null_value;
|
||||
SIMDJSON_TRY( val.is_null().get(is_null_value) );
|
||||
if (is_null_value) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -402,7 +376,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) no
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
bool is_null_value;
|
||||
SIMDJSON_TRY( val.is_null().get(is_null_value) );
|
||||
if (is_null_value) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -415,7 +391,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out)
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
bool is_null_value;
|
||||
SIMDJSON_TRY( val.is_null().get(is_null_value) );
|
||||
if (is_null_value) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -428,7 +406,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
bool is_null_value;
|
||||
SIMDJSON_TRY( val.is_null().get(is_null_value) );
|
||||
if (is_null_value) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -441,7 +421,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out)
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
bool is_null_value;
|
||||
SIMDJSON_TRY( val.is_null().get(is_null_value) );
|
||||
if (is_null_value) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -458,7 +440,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_vi
|
||||
// Shared pointers
|
||||
////////////////////////////////////////
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
bool is_null_value;
|
||||
SIMDJSON_TRY( val.is_null().get(is_null_value) );
|
||||
if (is_null_value) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -471,7 +455,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) no
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
bool is_null_value;
|
||||
SIMDJSON_TRY( val.is_null().get(is_null_value) );
|
||||
if (is_null_value) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -484,7 +470,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out)
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
bool is_null_value;
|
||||
SIMDJSON_TRY( val.is_null().get(is_null_value) );
|
||||
if (is_null_value) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -497,7 +485,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
bool is_null_value;
|
||||
SIMDJSON_TRY( val.is_null().get(is_null_value) );
|
||||
if (is_null_value) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -510,7 +500,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out)
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
bool is_null_value;
|
||||
SIMDJSON_TRY( val.is_null().get(is_null_value) );
|
||||
if (is_null_value) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -532,7 +524,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_vi
|
||||
////////////////////////////////////////
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
|
||||
// Check if the value is null
|
||||
if (val.is_null()) {
|
||||
bool is_null_value;
|
||||
SIMDJSON_TRY( val.is_null().get(is_null_value) );
|
||||
if (is_null_value) {
|
||||
out.reset(); // Set to nullptr
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -548,7 +542,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
|
||||
// Check if the value is null
|
||||
if (val.is_null()) {
|
||||
bool is_null_value;
|
||||
SIMDJSON_TRY( val.is_null().get(is_null_value) );
|
||||
if (is_null_value) {
|
||||
out.reset(); // Set to nullptr
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -564,7 +560,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
|
||||
// Check if the value is null
|
||||
if (val.is_null()) {
|
||||
bool is_null_value;
|
||||
SIMDJSON_TRY( val.is_null().get(is_null_value) );
|
||||
if (is_null_value) {
|
||||
out.reset(); // Set to nullptr
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ simdjson_inline simdjson_result<std::string_view> value::get_string(bool allow_r
|
||||
return iter.get_string(allow_replacement);
|
||||
}
|
||||
template <typename string_type>
|
||||
simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept {
|
||||
simdjson_warn_unused simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept {
|
||||
return iter.get_string(receiver, allow_replacement);
|
||||
}
|
||||
simdjson_inline simdjson_result<std::string_view> value::get_wobbly_string() noexcept {
|
||||
@@ -90,15 +90,15 @@ template<> simdjson_inline simdjson_result<int64_t> value::get() noexcept { retu
|
||||
template<> simdjson_inline simdjson_result<bool> value::get() noexcept { return get_bool(); }
|
||||
|
||||
|
||||
template<> simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); }
|
||||
template<> simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); }
|
||||
template<> simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); }
|
||||
template<> simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); }
|
||||
template<> simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); }
|
||||
template<> simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); }
|
||||
template<> simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); }
|
||||
template<> simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); }
|
||||
template<> simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); }
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <class T>
|
||||
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
|
||||
*/
|
||||
template <typename T>
|
||||
simdjson_inline error_code get(T &out)
|
||||
simdjson_warn_unused simdjson_inline error_code get(T &out)
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
noexcept(custom_deserializable<T, value> ? nothrow_custom_deserializable<T, value> : true)
|
||||
#else
|
||||
@@ -79,7 +79,9 @@ public:
|
||||
using value_type = typename std::remove_cvref_t<T>::value_type;
|
||||
|
||||
// Check if the value is null
|
||||
if (is_null()) {
|
||||
bool is_null_value;
|
||||
SIMDJSON_TRY( is_null().get(is_null_value) );
|
||||
if (is_null_value) {
|
||||
out.reset(); // Set to nullopt
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -215,7 +217,7 @@ public:
|
||||
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
|
||||
*/
|
||||
template <typename string_type>
|
||||
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
|
||||
/**
|
||||
* Cast this JSON value to a "wobbly" string.
|
||||
@@ -736,7 +738,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_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
simdjson_warn_unused 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;
|
||||
|
||||
@@ -41,7 +41,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start
|
||||
if (*_json_iter->peek() == '}') {
|
||||
logger::log_value(*_json_iter, "empty object");
|
||||
_json_iter->return_current_and_advance();
|
||||
end_container();
|
||||
SIMDJSON_TRY(end_container());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -895,7 +895,7 @@ simdjson_inline void value_iterator::advance_scalar(const char *type) noexcept {
|
||||
_json_iter->ascend_to(depth()-1);
|
||||
}
|
||||
|
||||
simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept {
|
||||
simdjson_warn_unused simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept {
|
||||
logger::log_start_value(*_json_iter, start_position(), depth(), type);
|
||||
// If we're not at the position anymore, we don't want to advance the cursor.
|
||||
const uint8_t *json;
|
||||
|
||||
@@ -335,7 +335,7 @@ public:
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<number> get_root_number(bool check_trailing) noexcept;
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_null(bool check_trailing) noexcept;
|
||||
|
||||
simdjson_inline error_code error() const noexcept;
|
||||
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
|
||||
simdjson_inline uint8_t *&string_buf_loc() noexcept;
|
||||
simdjson_inline const json_iterator &json_iter() const noexcept;
|
||||
simdjson_inline json_iterator &json_iter() noexcept;
|
||||
@@ -419,8 +419,8 @@ protected:
|
||||
simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept;
|
||||
|
||||
|
||||
simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
|
||||
simdjson_inline error_code end_container() noexcept;
|
||||
simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
|
||||
simdjson_warn_unused simdjson_inline error_code end_container() noexcept;
|
||||
|
||||
/**
|
||||
* Advance to a place expecting a value (increasing depth).
|
||||
@@ -430,8 +430,8 @@ protected:
|
||||
*/
|
||||
simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
|
||||
|
||||
simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
|
||||
simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
|
||||
simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
|
||||
simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
|
||||
|
||||
simdjson_inline bool is_at_start() const noexcept;
|
||||
/**
|
||||
@@ -468,7 +468,7 @@ protected:
|
||||
/** @copydoc error_code json_iterator::end_position() const noexcept; */
|
||||
simdjson_inline token_position end_position() const noexcept;
|
||||
/** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */
|
||||
simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
|
||||
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
|
||||
|
||||
friend class document;
|
||||
friend class object;
|
||||
|
||||
@@ -17,7 +17,7 @@ using namespace simd;
|
||||
struct backslash_and_quote {
|
||||
public:
|
||||
static constexpr uint32_t BYTES_PROCESSED = 32;
|
||||
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
simdjson_inline 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 ((quote_bits - 1) & bs_bits) != 0; }
|
||||
|
||||
@@ -17,7 +17,7 @@ using namespace simd;
|
||||
struct backslash_and_quote {
|
||||
public:
|
||||
static constexpr uint32_t BYTES_PROCESSED = 64;
|
||||
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
simdjson_inline 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 ((quote_bits - 1) & bs_bits) != 0; }
|
||||
|
||||
@@ -17,7 +17,7 @@ using namespace simd;
|
||||
struct backslash_and_quote {
|
||||
public:
|
||||
static constexpr uint32_t BYTES_PROCESSED = 32;
|
||||
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
simdjson_inline 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; }
|
||||
|
||||
@@ -17,7 +17,7 @@ using namespace simd;
|
||||
struct backslash_and_quote {
|
||||
public:
|
||||
static constexpr uint32_t BYTES_PROCESSED = 32;
|
||||
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
simdjson_inline 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; }
|
||||
@@ -67,7 +67,7 @@ simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *ds
|
||||
simd8<bool> is_backslash = (v == '\\');
|
||||
simd8<bool> is_control = (v < 32);
|
||||
return {
|
||||
(is_backslash | is_quote | is_control).to_bitmask()
|
||||
static_cast<uint64_t>((is_backslash | is_quote | is_control).to_bitmask())
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,22 @@ using std::size_t;
|
||||
#define SIMDJSON_IS_ARM64 1
|
||||
#elif defined(__riscv) && __riscv_xlen == 64
|
||||
#define SIMDJSON_IS_RISCV64 1
|
||||
#if __riscv_v_intrinsic >= 11000
|
||||
#define SIMDJSON_HAS_RVV_INTRINSICS 1
|
||||
#endif
|
||||
|
||||
#define SIMDJSON_HAS_ZVBB_INTRINSICS \
|
||||
0 // there is currently no way to detect this
|
||||
|
||||
#if SIMDJSON_HAS_RVV_INTRINSICS && __riscv_vector && \
|
||||
__riscv_v_min_vlen >= 128 && __riscv_v_elen >= 64
|
||||
// RISC-V V extension
|
||||
#define SIMDJSON_IS_RVV 1
|
||||
#if SIMDJSON_HAS_ZVBB_INTRINSICS && __riscv_zvbb >= 1000000
|
||||
// RISC-V Vector Basic Bit-manipulation
|
||||
#define SIMDJSON_IS_ZVBB 1
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(__loongarch_lp64)
|
||||
#define SIMDJSON_IS_LOONGARCH64 1
|
||||
#elif defined(__PPC64__) || defined(_M_PPC64)
|
||||
|
||||
@@ -17,7 +17,7 @@ using namespace simd;
|
||||
struct backslash_and_quote {
|
||||
public:
|
||||
static constexpr uint32_t BYTES_PROCESSED = 32;
|
||||
simdjson_inline static backslash_and_quote
|
||||
simdjson_inline backslash_and_quote
|
||||
copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
|
||||
simdjson_inline bool has_quote_first() {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#define SIMDJSON_SIMDJSON_VERSION_H
|
||||
|
||||
/** The version of simdjson being used (major.minor.revision) */
|
||||
#define SIMDJSON_VERSION "4.0.0"
|
||||
#define SIMDJSON_VERSION "4.0.7"
|
||||
|
||||
namespace simdjson {
|
||||
enum {
|
||||
@@ -19,7 +19,7 @@ enum {
|
||||
/**
|
||||
* The revision (major.minor.REVISION) of simdjson being used.
|
||||
*/
|
||||
SIMDJSON_VERSION_REVISION = 0
|
||||
SIMDJSON_VERSION_REVISION = 7
|
||||
};
|
||||
} // namespace simdjson
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ using namespace simd;
|
||||
struct backslash_and_quote {
|
||||
public:
|
||||
static constexpr uint32_t BYTES_PROCESSED = 32;
|
||||
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
simdjson_inline 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; }
|
||||
|
||||
@@ -78,3 +78,36 @@ You can modify the source code with your favorite editor and run again steps 5 (
|
||||
|
||||
You can create a new docker shell at any time by running step 3 (bash script).
|
||||
|
||||
## Using static reflection in your own projects
|
||||
|
||||
You can import simdjson in your own CMake project. You can configure your project like so:
|
||||
|
||||
```
|
||||
# Fetch simdjson from GitHub
|
||||
# Replace GIT_TAG by the commit you require.
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
simdjson
|
||||
GIT_REPOSITORY https://github.com/simdjson/simdjson.git
|
||||
GIT_TAG 015daad6a95a4f67c08ed5980d24b57be221c38f
|
||||
CMAKE_ARGS -DSIMDJSON_STATIC_REFLECTION=ON
|
||||
)
|
||||
FetchContent_MakeAvailable(simdjson)
|
||||
|
||||
##########
|
||||
# You may also use CPM.
|
||||
# https://github.com/cpm-cmake/CPM.cmake
|
||||
# CPMAddPackage(
|
||||
# NAME simdjson
|
||||
# GITHUB_REPOSITORY simdjson/simdjson
|
||||
# GIT_TAG 015daad6a95a4f67c08ed5980d24b57be221c38f
|
||||
# OPTIONS "SIMDJSON_STATIC_REFLECTION ON"
|
||||
# )
|
||||
target_link_libraries(webservice PRIVATE simdjson::simdjson)
|
||||
```
|
||||
|
||||
Replace the `GIT_TAG` by the appropriate value.
|
||||
|
||||
Once C++26 support will be officially available in mainstream compilers,
|
||||
we will simplify these instructions and it will no longer be needed
|
||||
to specify `SIMDJSON_STATIC_REFLECTION`.
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
var fs = require('fs');
|
||||
var fs = require('node:fs');
|
||||
|
||||
var faker = require('faker');
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ rules = """
|
||||
|
||||
We refer your to the HACKING.md file for more information on how the project is organized.
|
||||
|
||||
If you are trying to add a new implementation, you need to edit the amalgamate.py script
|
||||
to add your implementation to the IMPLEMENTATIONS list.
|
||||
|
||||
To help understand the error, here are the rules for including files in simdjson:
|
||||
|
||||
All implementation-specific files, including arm64.h, arm64/implementation.h and
|
||||
@@ -480,11 +483,42 @@ AMAL_C = os.path.join(AMALGAMATE_OUTPUT_PATH, "simdjson.cpp")
|
||||
DEMOCPP = os.path.join(AMALGAMATE_OUTPUT_PATH, "amalgamate_demo.cpp")
|
||||
README = os.path.join(AMALGAMATE_OUTPUT_PATH, "README.md")
|
||||
|
||||
def validate_implementations():
|
||||
"""Scan include/simdjson/*/implementation.h and compare with IMPLEMENTATIONS list."""
|
||||
include_simdjson_path = os.path.join(PROJECTPATH, 'include', 'simdjson')
|
||||
found_implementations = set()
|
||||
|
||||
if os.path.exists(include_simdjson_path):
|
||||
for item in os.listdir(include_simdjson_path):
|
||||
item_path = os.path.join(include_simdjson_path, item)
|
||||
if os.path.isdir(item_path):
|
||||
impl_h_path = os.path.join(item_path, 'implementation.h')
|
||||
if os.path.exists(impl_h_path) and item != 'builtin': # Ignore builtin
|
||||
found_implementations.add(item)
|
||||
|
||||
expected_implementations = set(IMPLEMENTATIONS)
|
||||
if found_implementations != expected_implementations:
|
||||
missing = expected_implementations - found_implementations
|
||||
extra = found_implementations - expected_implementations
|
||||
warning = "Warning: IMPLEMENTATIONS list does not match found implementations.\n"
|
||||
if missing:
|
||||
warning += f"Missing in filesystem: {sorted(missing)}\n"
|
||||
if extra:
|
||||
warning += f"Extra in filesystem: {sorted(extra)}\n"
|
||||
print(warning)
|
||||
return False
|
||||
return True
|
||||
|
||||
def read_version():
|
||||
with open(os.path.join(PROJECTPATH, 'include/simdjson/simdjson_version.h')) as f:
|
||||
return re.search(r'\d+\.\d+\.\d+', f.read()).group(0)
|
||||
|
||||
version = read_version()
|
||||
if not validate_implementations():
|
||||
print("Validation failed. Please update IMPLEMENTATIONS list in amalgamate.py.")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("implementation validated")
|
||||
Amalgamator.amalgamate(AMAL_H, "simdjson.h", ['include'], timestamp, version).validate_all_files_used('include')
|
||||
Amalgamator.amalgamate(AMAL_C, "simdjson.cpp", ['src', 'include'], timestamp, version).validate_all_files_used('src')
|
||||
|
||||
|
||||
+706
-151
File diff suppressed because it is too large
Load Diff
+8184
-4895
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+1
-1
@@ -209,7 +209,7 @@ simdjson_inline bool validate_string() {
|
||||
//
|
||||
// Parse the entire input in STEP_SIZE-byte chunks.
|
||||
//
|
||||
simdjson_inline error_code scan() {
|
||||
simdjson_warn_unused simdjson_inline error_code scan() {
|
||||
bool unclosed_string = false;
|
||||
for (;idx<len;idx++) {
|
||||
do {
|
||||
|
||||
@@ -29,7 +29,7 @@ private:
|
||||
template<size_t STEP_SIZE>
|
||||
simdjson_inline void step(const uint8_t *block_buf, buf_block_reader<STEP_SIZE> &reader) noexcept;
|
||||
simdjson_inline void next(const simd::simd8x64<uint8_t>& in, const json_block& block);
|
||||
simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len);
|
||||
simdjson_warn_unused simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len);
|
||||
json_scanner scanner{};
|
||||
uint8_t *dst;
|
||||
};
|
||||
@@ -39,7 +39,7 @@ simdjson_inline void json_minifier::next(const simd::simd8x64<uint8_t>& in, cons
|
||||
dst += in.compress(mask, dst);
|
||||
}
|
||||
|
||||
simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) {
|
||||
simdjson_warn_unused simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) {
|
||||
error_code error = scanner.finish();
|
||||
if (error) { dst_len = 0; return error; }
|
||||
dst_len = dst - dst_start;
|
||||
|
||||
@@ -108,7 +108,7 @@ public:
|
||||
json_scanner() = default;
|
||||
simdjson_inline json_block next(const simd::simd8x64<uint8_t>& in);
|
||||
// Returns either UNCLOSED_STRING or SUCCESS
|
||||
simdjson_inline error_code finish();
|
||||
simdjson_warn_unused simdjson_inline error_code finish();
|
||||
|
||||
private:
|
||||
// Whether the last character of the previous iteration is part of a scalar token
|
||||
@@ -156,7 +156,7 @@ simdjson_inline json_block json_scanner::next(const simd::simd8x64<uint8_t>& in)
|
||||
);
|
||||
}
|
||||
|
||||
simdjson_inline error_code json_scanner::finish() {
|
||||
simdjson_warn_unused simdjson_inline error_code json_scanner::finish() {
|
||||
return string_scanner.finish();
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ private:
|
||||
template<size_t STEP_SIZE>
|
||||
simdjson_inline void step(const uint8_t *block, buf_block_reader<STEP_SIZE> &reader) noexcept;
|
||||
simdjson_inline void next(const simd::simd8x64<uint8_t>& in, const json_block& block, size_t idx);
|
||||
simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial);
|
||||
simdjson_warn_unused simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial);
|
||||
|
||||
json_scanner scanner{};
|
||||
utf8_checker checker{};
|
||||
|
||||
@@ -195,7 +195,7 @@ using namespace simd;
|
||||
}
|
||||
}
|
||||
// do not forget to call check_eof!
|
||||
simdjson_inline error_code errors() {
|
||||
simdjson_warn_unused simdjson_inline error_code errors() {
|
||||
return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -151,7 +151,8 @@ simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr,
|
||||
simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) {
|
||||
while (1) {
|
||||
// Copy the next n bytes, and find the backslash and quote in them.
|
||||
auto bs_quote = backslash_and_quote::copy_and_find(src, dst);
|
||||
auto b = backslash_and_quote{};
|
||||
auto bs_quote = b.copy_and_find(src, dst);
|
||||
// If the next thing is the end quote, copy and return
|
||||
if (bs_quote.has_quote_first()) {
|
||||
// we encountered quotes first. Move dst to point to quotes and exit
|
||||
@@ -196,7 +197,8 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t
|
||||
// It is not ideal that this function is nearly identical to parse_string.
|
||||
while (1) {
|
||||
// Copy the next n bytes, and find the backslash and quote in them.
|
||||
auto bs_quote = backslash_and_quote::copy_and_find(src, dst);
|
||||
auto b = backslash_and_quote{};
|
||||
auto bs_quote = b.copy_and_find(src, dst);
|
||||
// If the next thing is the end quote, copy and return
|
||||
if (bs_quote.has_quote_first()) {
|
||||
// we encountered quotes first. Move dst to point to quotes and exit
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# All remaining tests link with simdjson proper
|
||||
include_directories(..)
|
||||
add_cpp_test(builder_string_builder_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(builder_string_builder_tests LABELS ondemand acceptance per_implementation)
|
||||
if(SIMDJSON_STATIC_REFLECTION)
|
||||
add_cpp_test(static_reflection_custom_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(static_reflection_builder_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(static_reflection_comprehensive_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(static_reflection_edge_cases_tests LABELS ondemand acceptance per_implementation)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -50,9 +50,8 @@ namespace builder_tests {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
|
||||
auto result = builder::to_json_string(c);
|
||||
ASSERT_SUCCESS(result);
|
||||
std::string pstr = result.value();
|
||||
std::string pstr;
|
||||
ASSERT_SUCCESS(builder::to_json_string(c).get(pstr));
|
||||
ASSERT_EQUAL(pstr, "{\"make\":\"Toyota\",\"model\":\"Corolla\",\"year\":2017,\"tire_pressure\":[30.0,30.2,30.513,30.79]}");
|
||||
simdjson::ondemand::parser parser;
|
||||
simdjson::ondemand::document doc;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
@@ -12,6 +13,84 @@ using namespace simdjson;
|
||||
|
||||
namespace builder_tests {
|
||||
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
// Custom type for testing tag_invoke with extract_into
|
||||
struct Price {
|
||||
double amount;
|
||||
std::string currency;
|
||||
|
||||
// Custom deserializer that applies currency conversion
|
||||
friend error_code tag_invoke(deserialize_tag,
|
||||
ondemand::value& val,
|
||||
Price& price) noexcept {
|
||||
ondemand::object obj;
|
||||
auto error = val.get_object().get(obj);
|
||||
if (error) return error;
|
||||
|
||||
// Get the raw amount
|
||||
error = obj["amount"].get(price.amount);
|
||||
if (error) return error;
|
||||
|
||||
// Get the currency
|
||||
std::string_view currency_sv;
|
||||
error = obj["currency"].get(currency_sv);
|
||||
if (error) return error;
|
||||
price.currency = std::string(currency_sv);
|
||||
|
||||
// Custom logic: Convert EUR to USD for consistency
|
||||
if (price.currency == "EUR") {
|
||||
price.amount = price.amount * 1.1; // Simplified conversion
|
||||
price.currency = "USD";
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
};
|
||||
|
||||
struct Product {
|
||||
std::string name;
|
||||
Price price; // Custom deserializable type
|
||||
int stock;
|
||||
};
|
||||
|
||||
// Another custom type for testing
|
||||
struct Dimensions {
|
||||
double value;
|
||||
std::string unit;
|
||||
|
||||
// Custom deserializer that converts to metric
|
||||
friend error_code tag_invoke(deserialize_tag,
|
||||
ondemand::value& val,
|
||||
Dimensions& dim) noexcept {
|
||||
ondemand::object obj;
|
||||
auto error = val.get_object().get(obj);
|
||||
if (error) return error;
|
||||
|
||||
error = obj["value"].get(dim.value);
|
||||
if (error) return error;
|
||||
|
||||
std::string_view unit_sv;
|
||||
error = obj["unit"].get(unit_sv);
|
||||
if (error) return error;
|
||||
dim.unit = std::string(unit_sv);
|
||||
|
||||
// Convert inches to cm
|
||||
if (dim.unit == "inches") {
|
||||
dim.value = dim.value * 2.54;
|
||||
dim.unit = "cm";
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
};
|
||||
|
||||
struct Package {
|
||||
std::string id;
|
||||
Dimensions weight;
|
||||
Dimensions length;
|
||||
};
|
||||
#endif
|
||||
|
||||
bool test_primitive_types() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
@@ -25,10 +104,9 @@ namespace builder_tests {
|
||||
|
||||
PrimitiveTypes test{true, 'X', 42, 3.14159, 2.71f};
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"bool_val\":true") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"char_val\":\"X\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"int_val\":42") != std::string::npos);
|
||||
@@ -39,10 +117,9 @@ namespace builder_tests {
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<PrimitiveTypes>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
PrimitiveTypes deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<PrimitiveTypes>().get(deserialized));
|
||||
|
||||
PrimitiveTypes deserialized = std::move(get_result.value());
|
||||
ASSERT_EQUAL(deserialized.bool_val, test.bool_val);
|
||||
ASSERT_EQUAL(deserialized.char_val, test.char_val);
|
||||
ASSERT_EQUAL(deserialized.int_val, test.int_val);
|
||||
@@ -60,11 +137,9 @@ namespace builder_tests {
|
||||
};
|
||||
|
||||
StringTypes test{"hello world", "test_view"};
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"string_val\":\"hello world\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"string_view_val\":\"test_view\"") != std::string::npos);
|
||||
|
||||
@@ -72,11 +147,9 @@ namespace builder_tests {
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
StringTypes deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<StringTypes>().get(deserialized));
|
||||
|
||||
auto get_result = doc_result.value().get<StringTypes>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
StringTypes deserialized = std::move(get_result.value());
|
||||
ASSERT_EQUAL(deserialized.string_val, test.string_val);
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
@@ -98,10 +171,8 @@ namespace builder_tests {
|
||||
test.opt_int_null = std::nullopt;
|
||||
test.opt_string_null = std::nullopt;
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
ASSERT_TRUE(json.find("\"opt_int_with_value\":42") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"opt_string_with_value\":\"optional_test\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"opt_int_null\":null") != std::string::npos);
|
||||
@@ -112,10 +183,9 @@ namespace builder_tests {
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<OptionalTypes>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
OptionalTypes deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<OptionalTypes>().get(deserialized));
|
||||
|
||||
OptionalTypes deserialized = std::move(get_result.value());
|
||||
ASSERT_TRUE(deserialized.opt_int_with_value.has_value());
|
||||
ASSERT_EQUAL(*deserialized.opt_int_with_value, 42);
|
||||
ASSERT_TRUE(deserialized.opt_string_with_value.has_value());
|
||||
@@ -144,10 +214,8 @@ namespace builder_tests {
|
||||
test.unique_int_null = nullptr;
|
||||
test.shared_string_null = nullptr;
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
ASSERT_TRUE(json.find("\"unique_int_with_value\":123") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"shared_string_with_value\":\"shared_test\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"unique_bool_with_value\":true") != std::string::npos);
|
||||
@@ -157,12 +225,9 @@ namespace builder_tests {
|
||||
// Test round-trip
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<SmartPointerTypes>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
SmartPointerTypes deserialized = std::move(get_result.value());
|
||||
SmartPointerTypes deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<SmartPointerTypes>().get(deserialized));
|
||||
ASSERT_TRUE(deserialized.unique_int_with_value != nullptr);
|
||||
ASSERT_EQUAL(*deserialized.unique_int_with_value, 123);
|
||||
ASSERT_TRUE(deserialized.shared_string_with_value != nullptr);
|
||||
@@ -178,6 +243,7 @@ namespace builder_tests {
|
||||
bool test_container_types() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
// Test basic container types
|
||||
struct ContainerTypes {
|
||||
std::vector<int> int_vector;
|
||||
std::set<std::string> string_set;
|
||||
@@ -192,7 +258,8 @@ namespace builder_tests {
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
ASSERT_TRUE(json.find("\"int_vector\":[1,2,3,4,5]") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"string_set\":[") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"string_map\":{") != std::string::npos);
|
||||
@@ -201,27 +268,447 @@ namespace builder_tests {
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<ContainerTypes>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
ContainerTypes deserialized = std::move(get_result.value());
|
||||
ContainerTypes deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<ContainerTypes>().get(deserialized));
|
||||
ASSERT_EQUAL(deserialized.int_vector.size(), 5);
|
||||
ASSERT_EQUAL(deserialized.string_set.size(), 3);
|
||||
ASSERT_EQUAL(deserialized.string_map.size(), 3);
|
||||
ASSERT_EQUAL(deserialized.int_vector[0], 1);
|
||||
ASSERT_EQUAL(deserialized.int_vector[4], 5);
|
||||
|
||||
// Test std::list with iterator-based serialization
|
||||
struct ListContainer {
|
||||
std::list<int> int_list;
|
||||
std::list<std::string> string_list;
|
||||
};
|
||||
|
||||
ListContainer list_test;
|
||||
list_test.int_list = {10, 20, 30, 40, 50};
|
||||
list_test.string_list = {"first", "second", "third"};
|
||||
|
||||
std::string list_result;
|
||||
ASSERT_SUCCESS( builder::to_json_string(list_test).get(list_result));
|
||||
|
||||
// Check that list serialization produces correct JSON array format
|
||||
ASSERT_TRUE(list_result.find("\"int_list\":[10,20,30,40,50]") != std::string::npos);
|
||||
ASSERT_TRUE(list_result.find("\"string_list\":[\"first\",\"second\",\"third\"]") != std::string::npos);
|
||||
|
||||
// Test list round-trip
|
||||
auto list_doc_result = parser.iterate(pad(list_result));
|
||||
ASSERT_SUCCESS(list_doc_result);
|
||||
|
||||
ListContainer list_deserialized;
|
||||
ASSERT_SUCCESS(list_doc_result.get<ListContainer>().get(list_deserialized));
|
||||
ASSERT_EQUAL(list_deserialized.int_list.size(), 5);
|
||||
ASSERT_EQUAL(list_deserialized.string_list.size(), 3);
|
||||
|
||||
// Check list values
|
||||
auto it = list_deserialized.int_list.begin();
|
||||
ASSERT_EQUAL(*it, 10);
|
||||
std::advance(it, 4);
|
||||
ASSERT_EQUAL(*it, 50);
|
||||
|
||||
auto str_it = list_deserialized.string_list.begin();
|
||||
ASSERT_EQUAL(*str_it, "first");
|
||||
std::advance(str_it, 2);
|
||||
ASSERT_EQUAL(*str_it, "third");
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_extract_into() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
struct Car {
|
||||
std::string make;
|
||||
std::string model;
|
||||
int year;
|
||||
double price;
|
||||
std::optional<std::string> color;
|
||||
};
|
||||
|
||||
ondemand::parser parser;
|
||||
|
||||
// Test 1: Extract only specific fields
|
||||
{
|
||||
auto padded = R"({
|
||||
"make": "Toyota",
|
||||
"model": "Camry",
|
||||
"year": 2024,
|
||||
"price": 28999.99,
|
||||
"color": "Blue",
|
||||
"engine": "V6",
|
||||
"transmission": "Automatic"
|
||||
})"_padded;
|
||||
|
||||
Car car{};
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS( parser.iterate(padded).get(doc) );
|
||||
|
||||
ondemand::object obj;
|
||||
ASSERT_SUCCESS(doc.get_object().get(obj));
|
||||
|
||||
// Extract only 'make' and 'model'
|
||||
ASSERT_SUCCESS(obj.extract_into<"make","model">(car));
|
||||
|
||||
ASSERT_EQUAL(car.make, "Toyota");
|
||||
ASSERT_EQUAL(car.model, "Camry");
|
||||
ASSERT_EQUAL(car.year, 0); // Not extracted
|
||||
ASSERT_EQUAL(car.price, 0); // Not extracted
|
||||
|
||||
ASSERT_SUCCESS(parser.iterate(padded).get(doc));
|
||||
|
||||
// Extract only 'make' and 'model'
|
||||
ASSERT_SUCCESS(doc.extract_into<"make", "model">(car));
|
||||
|
||||
ASSERT_EQUAL(car.make, "Toyota");
|
||||
ASSERT_EQUAL(car.model, "Camry");
|
||||
ASSERT_EQUAL(car.year, 0); // Not extracted
|
||||
ASSERT_EQUAL(car.price, 0); // Not extracted
|
||||
}
|
||||
|
||||
// Test 2: Extract with optional field
|
||||
{
|
||||
auto padded = R"({
|
||||
"make": "Honda",
|
||||
"model": "Accord",
|
||||
"year": 2023,
|
||||
"price": 26999.99,
|
||||
"color": "Red"
|
||||
})"_padded;
|
||||
|
||||
Car car{};
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(padded).get(doc));
|
||||
|
||||
ondemand::object obj;
|
||||
ASSERT_SUCCESS(doc.get_object().get(obj));
|
||||
|
||||
// Extract including optional 'color'
|
||||
ASSERT_SUCCESS(obj.extract_into<"make", "model", "color">(car));
|
||||
|
||||
ASSERT_EQUAL(car.make, "Honda");
|
||||
ASSERT_EQUAL(car.model, "Accord");
|
||||
ASSERT_TRUE(car.color.has_value());
|
||||
ASSERT_EQUAL(*car.color, "Red");
|
||||
}
|
||||
|
||||
// Test 3: Extract with missing optional field
|
||||
{
|
||||
auto padded = R"({
|
||||
"make": "Ford",
|
||||
"model": "F-150",
|
||||
"year": 2024,
|
||||
"price": 35999.99
|
||||
})"_padded;
|
||||
|
||||
Car car{};
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(padded).get(doc));
|
||||
|
||||
ondemand::object obj;
|
||||
ASSERT_SUCCESS(doc.get_object().get(obj));
|
||||
|
||||
// Try to extract including optional 'color' which doesn't exist
|
||||
ASSERT_SUCCESS(obj.extract_into<"make", "model", "color">(car));
|
||||
|
||||
ASSERT_EQUAL(car.make, "Ford");
|
||||
ASSERT_EQUAL(car.model, "F-150");
|
||||
ASSERT_FALSE(car.color.has_value()); // Should be empty
|
||||
}
|
||||
|
||||
// Test 4: Extract with custom deserializable type using tag_invoke
|
||||
{
|
||||
// Test using the Price struct defined at namespace level
|
||||
|
||||
auto padded = R"({
|
||||
"name": "Laptop",
|
||||
"price": {
|
||||
"amount": 1000,
|
||||
"currency": "EUR"
|
||||
},
|
||||
"stock": 15,
|
||||
"description": "High-end gaming laptop"
|
||||
})"_padded;
|
||||
|
||||
Product product{};
|
||||
auto doc = parser.iterate(padded);
|
||||
ASSERT_SUCCESS(doc);
|
||||
|
||||
ondemand::object obj;
|
||||
auto obj_result = doc.get_object().get(obj);
|
||||
ASSERT_SUCCESS(obj_result);
|
||||
|
||||
// Extract including price field with custom deserializer
|
||||
ASSERT_SUCCESS(obj.extract_into<"name", "price">(product));
|
||||
|
||||
ASSERT_EQUAL(product.name, "Laptop");
|
||||
// Verify custom deserializer was invoked: EUR should be converted to USD
|
||||
ASSERT_EQUAL(product.price.currency, "USD"); // Should be converted from EUR
|
||||
// Verify custom deserializer was invoked: amount should be 1100 (1000 * 1.1)
|
||||
ASSERT_EQUAL(product.price.amount, 1100); // Should be exactly 1100 after conversion
|
||||
ASSERT_EQUAL(product.stock, 0); // Not extracted
|
||||
}
|
||||
|
||||
// Test 5: Extract with nested custom deserializable types
|
||||
{
|
||||
// Test using the Dimensions struct defined at namespace level
|
||||
|
||||
auto padded = R"({
|
||||
"id": "PKG123",
|
||||
"weight": {
|
||||
"value": 10,
|
||||
"unit": "pounds"
|
||||
},
|
||||
"length": {
|
||||
"value": 12,
|
||||
"unit": "inches"
|
||||
},
|
||||
"fragile": true
|
||||
})"_padded;
|
||||
|
||||
Package pkg{};
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(padded).get(doc));
|
||||
|
||||
ondemand::object obj;
|
||||
ASSERT_SUCCESS(doc.get_object().get(obj));
|
||||
|
||||
// Extract only length (with custom deserializer), skip weight
|
||||
ASSERT_SUCCESS(obj.extract_into<"id", "length">(pkg));
|
||||
|
||||
ASSERT_EQUAL(pkg.id, "PKG123");
|
||||
// Verify custom deserializer was invoked: inches should be converted to cm
|
||||
ASSERT_EQUAL(pkg.length.unit, "cm"); // Should be converted from inches
|
||||
// Verify exact conversion: 12 inches * 2.54 = 30.48 cm
|
||||
ASSERT_EQUAL(pkg.length.value, 30.48); // Should be exactly 30.48 after conversion
|
||||
ASSERT_EQUAL(pkg.weight.unit, ""); // Weight not extracted
|
||||
ASSERT_EQUAL(pkg.weight.value, 0); // Weight value should remain at default
|
||||
}
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
bool test_extract_from() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
// Test 1: Extract specific fields from Car struct
|
||||
{
|
||||
struct Car {
|
||||
std::string make;
|
||||
std::string model;
|
||||
int year;
|
||||
double price;
|
||||
bool electric;
|
||||
};
|
||||
|
||||
Car car{"Tesla", "Model 3", 2023, 42000.0, true};
|
||||
|
||||
// Extract only make and model
|
||||
std::string json;
|
||||
ASSERT_SUCCESS((extract_from<"make", "model">(car).get(json)));
|
||||
|
||||
// Parse back to verify correctness
|
||||
auto padded = pad(json);
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(padded);
|
||||
ASSERT_SUCCESS(doc);
|
||||
|
||||
std::string_view make;
|
||||
std::string_view model;
|
||||
ASSERT_SUCCESS(doc["make"].get(make));
|
||||
ASSERT_SUCCESS(doc["model"].get(model));
|
||||
ASSERT_EQUAL(make, "Tesla");
|
||||
ASSERT_EQUAL(model, "Model 3");
|
||||
|
||||
// Verify excluded fields are not present
|
||||
auto year_result = doc["year"];
|
||||
ASSERT_ERROR(year_result.error(), NO_SUCH_FIELD);
|
||||
auto price_result = doc["price"];
|
||||
ASSERT_ERROR(price_result.error(), NO_SUCH_FIELD);
|
||||
auto electric_result = doc["electric"];
|
||||
ASSERT_ERROR(electric_result.error(), NO_SUCH_FIELD);
|
||||
}
|
||||
|
||||
// Test 2: Extract different field combination
|
||||
{
|
||||
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;
|
||||
ASSERT_SUCCESS((extract_from<"year", "price">(car).get(json)));
|
||||
|
||||
auto padded = pad(json);
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(padded);
|
||||
ASSERT_SUCCESS(doc);
|
||||
|
||||
int64_t year;
|
||||
double price;
|
||||
ASSERT_SUCCESS(doc["year"].get(year));
|
||||
ASSERT_SUCCESS(doc["price"].get(price));
|
||||
ASSERT_EQUAL(year, 2024);
|
||||
ASSERT_EQUAL(price, 55000.0);
|
||||
|
||||
// Verify excluded fields
|
||||
auto make_result = doc["make"];
|
||||
ASSERT_ERROR(make_result.error(), NO_SUCH_FIELD);
|
||||
}
|
||||
|
||||
// Test 3: Extract from struct with optional fields
|
||||
{
|
||||
struct Person {
|
||||
std::string name;
|
||||
int age;
|
||||
std::optional<std::string> email;
|
||||
std::optional<std::string> phone;
|
||||
};
|
||||
|
||||
Person person{"John Doe", 30, "john@example.com", std::nullopt};
|
||||
|
||||
// Extract name and email
|
||||
std::string json;
|
||||
ASSERT_SUCCESS((extract_from<"name", "email">(person).get(json)));
|
||||
|
||||
auto padded = pad(json);
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(padded);
|
||||
ASSERT_SUCCESS(doc);
|
||||
|
||||
std::string_view name;
|
||||
std::string_view email;
|
||||
ASSERT_SUCCESS(doc["name"].get(name));
|
||||
ASSERT_SUCCESS(doc["email"].get(email));
|
||||
ASSERT_EQUAL(name, "John Doe");
|
||||
ASSERT_EQUAL(email, "john@example.com");
|
||||
}
|
||||
|
||||
// Test 4: Extract with optional that has value
|
||||
{
|
||||
struct Person {
|
||||
std::string name;
|
||||
int age;
|
||||
std::optional<std::string> email;
|
||||
std::optional<std::string> phone;
|
||||
};
|
||||
|
||||
Person person{"Jane Smith", 25, "jane@example.com", "555-1234"};
|
||||
|
||||
// Extract name, age, and phone
|
||||
std::string json;
|
||||
ASSERT_SUCCESS((extract_from<"name", "age", "phone">(person).get(json)));
|
||||
|
||||
auto padded = pad(json);
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(padded);
|
||||
ASSERT_SUCCESS(doc);
|
||||
|
||||
std::string_view name;
|
||||
int64_t age;
|
||||
std::string_view phone;
|
||||
ASSERT_SUCCESS(doc["name"].get(name));
|
||||
ASSERT_SUCCESS(doc["age"].get(age));
|
||||
ASSERT_SUCCESS(doc["phone"].get(phone));
|
||||
ASSERT_EQUAL(name, "Jane Smith");
|
||||
ASSERT_EQUAL(age, 25);
|
||||
ASSERT_EQUAL(phone, "555-1234");
|
||||
|
||||
// Email should not be present
|
||||
auto email_result = doc["email"];
|
||||
ASSERT_ERROR(email_result.error(), NO_SUCH_FIELD);
|
||||
}
|
||||
|
||||
// Test 5: Round-trip test - serialize with extract_from, deserialize with extract_into
|
||||
{
|
||||
struct Product {
|
||||
std::string id;
|
||||
std::string name;
|
||||
double price;
|
||||
int stock;
|
||||
};
|
||||
|
||||
Product original{"P123", "Widget", 19.99, 100};
|
||||
|
||||
// Extract specific fields to JSON
|
||||
std::string json;
|
||||
ASSERT_SUCCESS((extract_from<"id", "name", "price">(original).get(json)));
|
||||
|
||||
// Parse and extract back
|
||||
Product restored{"", "", 0.0, 0};
|
||||
auto padded = pad(json);
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(padded);
|
||||
ASSERT_SUCCESS(doc);
|
||||
|
||||
ondemand::object obj;
|
||||
ASSERT_SUCCESS(doc.get_object().get(obj));
|
||||
auto extract_result = obj.extract_into<"id", "name", "price">(restored);
|
||||
ASSERT_SUCCESS(extract_result);
|
||||
|
||||
// Verify fields match
|
||||
ASSERT_EQUAL(restored.id, original.id);
|
||||
ASSERT_EQUAL(restored.name, original.name);
|
||||
ASSERT_EQUAL(restored.price, original.price);
|
||||
ASSERT_EQUAL(restored.stock, 0); // Stock should remain at default
|
||||
}
|
||||
|
||||
// Test 6: Extract from nested structs
|
||||
{
|
||||
struct Address {
|
||||
std::string street;
|
||||
std::string city;
|
||||
std::string zip;
|
||||
};
|
||||
|
||||
struct Company {
|
||||
std::string name;
|
||||
Address headquarters;
|
||||
int employees;
|
||||
};
|
||||
|
||||
Company company{"TechCorp", {"123 Main St", "San Francisco", "94105"}, 500};
|
||||
|
||||
// Extract name and employees only
|
||||
std::string json;
|
||||
ASSERT_SUCCESS((extract_from<"name", "employees">(company).get(json)));
|
||||
|
||||
auto padded = pad(json);
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(padded);
|
||||
ASSERT_SUCCESS(doc);
|
||||
|
||||
std::string_view name;
|
||||
int64_t employees;
|
||||
ASSERT_SUCCESS(doc["name"].get(name));
|
||||
ASSERT_SUCCESS(doc["employees"].get(employees));
|
||||
ASSERT_EQUAL(name, "TechCorp");
|
||||
ASSERT_EQUAL(employees, 500);
|
||||
|
||||
// headquarters should not be present
|
||||
auto hq_result = doc["headquarters"];
|
||||
ASSERT_ERROR(hq_result.error(), NO_SUCH_FIELD);
|
||||
}
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool run() {
|
||||
return test_primitive_types() &&
|
||||
test_string_types() &&
|
||||
test_optional_types() &&
|
||||
test_smart_pointer_types() &&
|
||||
test_container_types();
|
||||
test_container_types() &&
|
||||
test_extract_into() &&
|
||||
test_extract_from();
|
||||
}
|
||||
|
||||
} // namespace builder_tests
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
|
||||
|
||||
|
||||
#include "simdjson.h"
|
||||
#include "test_builder.h"
|
||||
#include <charconv>
|
||||
#include <string>
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
// Suppose that we want to serialize/deserialize Car using
|
||||
// strings for the year
|
||||
struct Car {
|
||||
std::string make;
|
||||
std::string model;
|
||||
int64_t year;
|
||||
std::vector<float> tire_pressure;
|
||||
};
|
||||
|
||||
|
||||
|
||||
namespace simdjson {
|
||||
// This tag_invoke MUST be inside simdjson namespace
|
||||
template <typename simdjson_value>
|
||||
auto tag_invoke(deserialize_tag, simdjson_value &val, Car& car) {
|
||||
ondemand::object obj;
|
||||
auto error = val.get_object().get(obj);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
if ((error = obj["make"].get_string(car.make))) {
|
||||
return error;
|
||||
}
|
||||
if ((error = obj["model"].get_string(car.model))) {
|
||||
return error;
|
||||
}
|
||||
std::string_view year_str;
|
||||
if ((error = obj["year"].get(year_str))) {
|
||||
return error;
|
||||
}
|
||||
int64_t year_value;
|
||||
auto [ptr, ec] = std::from_chars(year_str.data(), year_str.data() + year_str.size(), year_value);
|
||||
if (ec != std::errc{}) {
|
||||
return INCORRECT_TYPE;
|
||||
}
|
||||
car.year = year_value;
|
||||
if ((error = obj["tire_pressure"].get<std::vector<float>>().get(
|
||||
car.tire_pressure))) {
|
||||
return error;
|
||||
}
|
||||
return simdjson::SUCCESS;
|
||||
}
|
||||
|
||||
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
|
||||
namespace custom_tests {
|
||||
|
||||
bool test_car_deserialization() {
|
||||
TEST_START();
|
||||
auto json = R"({"make":"Toyota","model":"Camry","year":"2018","tire_pressure":[30.5,30.6,30.7,30.8]})"_padded;
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(json);
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
Car car;
|
||||
ASSERT_SUCCESS(doc_result.get(car));
|
||||
if (car.make != "Toyota" || car.model != "Camry" || std::to_string(car.year) != "2018" || car.tire_pressure.size() != 4 || car.tire_pressure[0] != 30.5f) {
|
||||
return false;
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool test_car_serialization() {
|
||||
TEST_START();
|
||||
Car car{"Toyota", "Camry", 2018, {30.5f,30.6f,30.7f,30.8f}};
|
||||
simdjson::builder::string_builder builder;
|
||||
builder.append(car);
|
||||
std::string_view json;
|
||||
ASSERT_SUCCESS(builder.view().get(json));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool test_car_roundtrip() {
|
||||
TEST_START();
|
||||
Car car{"Toyota", "Camry", 2018, {30.5f,30.6f,30.7f,30.8f}};
|
||||
simdjson::builder::string_builder builder;
|
||||
builder.append(car);
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder.view().get(json));
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(json);
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
Car carback;
|
||||
ASSERT_SUCCESS(doc_result.get(carback));
|
||||
|
||||
if (carback.make != car.make || carback.model != car.model || carback.year != car.year || carback.tire_pressure != car.tire_pressure) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_car_roundtrip_to_json() {
|
||||
TEST_START();
|
||||
Car car{"Toyota", "Camry", 2018, {30.5f,30.6f,30.7f,30.8f}};
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(simdjson::to_json(car).get(json));
|
||||
simdjson::builder::string_builder builder;
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(json);
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
Car carback;
|
||||
ASSERT_SUCCESS(doc_result.get(carback));
|
||||
|
||||
if (carback.make != car.make || carback.model != car.model || carback.year != car.year || carback.tire_pressure != car.tire_pressure) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool run() {
|
||||
return test_car_deserialization() && test_car_serialization() && test_car_roundtrip() && test_car_roundtrip_to_json();
|
||||
}
|
||||
|
||||
} // namespace builder_tests
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return test_main(argc, argv, custom_tests::run);
|
||||
}
|
||||
@@ -28,10 +28,8 @@ namespace builder_tests {
|
||||
test.null_unique_ptr = nullptr;
|
||||
test.null_shared_ptr = nullptr;
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
ASSERT_TRUE(json.find("\"empty_string\":\"\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"empty_vector\":[]") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"null_optional\":null") != std::string::npos);
|
||||
@@ -43,10 +41,8 @@ namespace builder_tests {
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<EmptyValues>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
EmptyValues deserialized = std::move(get_result.value());
|
||||
EmptyValues deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<EmptyValues>().get(deserialized));
|
||||
ASSERT_EQUAL(deserialized.empty_string, "");
|
||||
ASSERT_EQUAL(deserialized.empty_vector.size(), 0);
|
||||
ASSERT_FALSE(deserialized.null_optional.has_value());
|
||||
@@ -74,10 +70,8 @@ namespace builder_tests {
|
||||
test.unicode = "Café résumé";
|
||||
test.null_char = '\0';
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
// Test that quotes are properly escaped
|
||||
ASSERT_TRUE(json.find("\\\"Hello\\\"") != std::string::npos);
|
||||
// Test that backslashes are properly escaped
|
||||
@@ -100,17 +94,17 @@ namespace builder_tests {
|
||||
test_no_null.newlines = test.newlines;
|
||||
test_no_null.unicode = test.unicode;
|
||||
|
||||
auto result_no_null = builder::to_json_string(test_no_null);
|
||||
ASSERT_SUCCESS(result_no_null);
|
||||
std::string result_no_null;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test_no_null).get(result_no_null));
|
||||
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(result_no_null.value()));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
ondemand::document doc_result;
|
||||
|
||||
auto get_result = doc_result.value().get<SpecialCharsNoNull>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
ASSERT_SUCCESS(parser.iterate(pad(result_no_null)).get(doc_result));
|
||||
|
||||
SpecialCharsNoNull deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<SpecialCharsNoNull>().get(deserialized));
|
||||
|
||||
SpecialCharsNoNull deserialized = std::move(get_result.value());
|
||||
ASSERT_EQUAL(deserialized.quotes, test.quotes);
|
||||
ASSERT_EQUAL(deserialized.backslashes, test.backslashes);
|
||||
ASSERT_EQUAL(deserialized.newlines, test.newlines);
|
||||
@@ -139,22 +133,18 @@ namespace builder_tests {
|
||||
test.true_val = true;
|
||||
test.false_val = false;
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
ASSERT_TRUE(json.find("\"true_val\":true") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"false_val\":false") != std::string::npos);
|
||||
|
||||
// Test round-trip
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
ondemand::document doc_result;
|
||||
|
||||
auto get_result = doc_result.value().get<NumericLimits>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
NumericLimits deserialized = std::move(get_result.value());
|
||||
ASSERT_SUCCESS(parser.iterate(pad(json)).get(doc_result));
|
||||
NumericLimits deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<NumericLimits>().get(deserialized));
|
||||
ASSERT_EQUAL(deserialized.max_int, test.max_int);
|
||||
ASSERT_EQUAL(deserialized.min_int, test.min_int);
|
||||
ASSERT_EQUAL(deserialized.true_val, true);
|
||||
@@ -184,10 +174,8 @@ namespace builder_tests {
|
||||
test.optional_inner = Inner{99, "optional"};
|
||||
test.unique_inner = std::make_unique<Inner>(Inner{123, "unique"});
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
ASSERT_TRUE(json.find("\"inner_obj\":{") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"inner_vector\":[") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"optional_inner\":{") != std::string::npos);
|
||||
@@ -198,10 +186,8 @@ namespace builder_tests {
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<Outer>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
Outer deserialized = std::move(get_result.value());
|
||||
Outer deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<Outer>().get(deserialized));
|
||||
ASSERT_EQUAL(deserialized.inner_obj.value, 42);
|
||||
ASSERT_EQUAL(deserialized.inner_obj.name, "inner");
|
||||
ASSERT_EQUAL(deserialized.inner_vector.size(), 2);
|
||||
|
||||
@@ -21,26 +21,21 @@ namespace builder_tests {
|
||||
};
|
||||
|
||||
EnumStruct test{Color::Red, 42};
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
// Enum should be serialized as string (Red)
|
||||
ASSERT_TRUE(json.find("\"color\":\"Red\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"value\":42") != std::string::npos);
|
||||
|
||||
// Test different enum values
|
||||
test.color = Color::Green;
|
||||
auto result2 = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result2);
|
||||
std::string json2 = result2.value();
|
||||
std::string json2;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json2));
|
||||
ASSERT_TRUE(json2.find("\"color\":\"Green\"") != std::string::npos);
|
||||
|
||||
test.color = Color::Blue;
|
||||
auto result3 = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result3);
|
||||
std::string json3 = result3.value();
|
||||
std::string json3;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json3));
|
||||
ASSERT_TRUE(json3.find("\"color\":\"Blue\"") != std::string::npos);
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
@@ -61,41 +56,37 @@ namespace builder_tests {
|
||||
};
|
||||
|
||||
// Test deserialization of different enum values with string representation
|
||||
std::string json1 = "{\"status\":\"Active\",\"name\":\"test1\"}";
|
||||
auto json1 = R"({"status":"Active","name":"test1"})"_padded;
|
||||
ondemand::parser parser1;
|
||||
auto doc_result1 = parser1.iterate(pad(json1));
|
||||
ASSERT_SUCCESS(doc_result1);
|
||||
ondemand::document doc_result1;
|
||||
ASSERT_SUCCESS(parser1.iterate(json1).get(doc_result1));
|
||||
StatusStruct deserialized1;
|
||||
ASSERT_SUCCESS(doc_result1.get<StatusStruct>().get(deserialized1));
|
||||
|
||||
auto get_result1 = doc_result1.value().get<StatusStruct>();
|
||||
ASSERT_SUCCESS(get_result1);
|
||||
|
||||
StatusStruct deserialized1 = std::move(get_result1.value());
|
||||
ASSERT_TRUE(deserialized1.status == Status::Active);
|
||||
ASSERT_EQUAL(deserialized1.name, "test1");
|
||||
|
||||
// Test Status::Inactive
|
||||
std::string json2 = "{\"status\":\"Inactive\",\"name\":\"test2\"}";
|
||||
auto json2 = R"({"status":"Inactive","name":"test2"})"_padded;
|
||||
ondemand::parser parser2;
|
||||
auto doc_result2 = parser2.iterate(pad(json2));
|
||||
ASSERT_SUCCESS(doc_result2);
|
||||
ondemand::document doc_result2;
|
||||
ASSERT_SUCCESS(parser2.iterate(json2).get(doc_result2));
|
||||
|
||||
auto get_result2 = doc_result2.value().get<StatusStruct>();
|
||||
ASSERT_SUCCESS(get_result2);
|
||||
StatusStruct deserialized2;
|
||||
ASSERT_SUCCESS(doc_result2.get<StatusStruct>().get(deserialized2));
|
||||
|
||||
StatusStruct deserialized2 = std::move(get_result2.value());
|
||||
ASSERT_TRUE(deserialized2.status == Status::Inactive);
|
||||
ASSERT_EQUAL(deserialized2.name, "test2");
|
||||
|
||||
// Test Status::Pending
|
||||
std::string json3 = "{\"status\":\"Pending\",\"name\":\"test3\"}";
|
||||
ondemand::parser parser3;
|
||||
auto doc_result3 = parser3.iterate(pad(json3));
|
||||
ASSERT_SUCCESS(doc_result3);
|
||||
ondemand::document doc_result3;
|
||||
ASSERT_SUCCESS(parser3.iterate(pad(json3)).get(doc_result3));
|
||||
|
||||
auto get_result3 = doc_result3.value().get<StatusStruct>();
|
||||
ASSERT_SUCCESS(get_result3);
|
||||
StatusStruct deserialized3;
|
||||
ASSERT_SUCCESS(doc_result3.get<StatusStruct>().get(deserialized3));
|
||||
|
||||
StatusStruct deserialized3 = std::move(get_result3.value());
|
||||
ASSERT_TRUE(deserialized3.status == Status::Pending);
|
||||
ASSERT_EQUAL(deserialized3.name, "test3");
|
||||
#endif
|
||||
@@ -121,23 +112,21 @@ namespace builder_tests {
|
||||
Task original{Priority::High, "Important task", 123};
|
||||
|
||||
// Serialize
|
||||
auto serialize_result = builder::to_json_string(original);
|
||||
ASSERT_SUCCESS(serialize_result);
|
||||
|
||||
std::string json = serialize_result.value();
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(original).get(json));
|
||||
ASSERT_TRUE(json.find("\"priority\":\"High\"") != std::string::npos); // High as string
|
||||
ASSERT_TRUE(json.find("\"description\":\"Important task\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"id\":123") != std::string::npos);
|
||||
|
||||
// Deserialize
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
std::cout << json << std::endl;
|
||||
ondemand::document doc_result;
|
||||
ASSERT_SUCCESS(parser.iterate(pad(json)).get(doc_result));
|
||||
|
||||
auto get_result = doc_result.value().get<Task>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
Task deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<Task>().get(deserialized));
|
||||
|
||||
Task deserialized = std::move(get_result.value());
|
||||
ASSERT_TRUE(deserialized.priority == Priority::High);
|
||||
ASSERT_EQUAL(deserialized.description, "Important task");
|
||||
ASSERT_EQUAL(deserialized.id, 123);
|
||||
@@ -161,22 +150,18 @@ namespace builder_tests {
|
||||
|
||||
Response test{ErrorCode::NotFound, "Resource not found"};
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
ASSERT_TRUE(json.find("\"error\":\"NotFound\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"message\":\"Resource not found\"") != std::string::npos);
|
||||
|
||||
// Test round-trip
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
ondemand::document doc_result;
|
||||
ASSERT_SUCCESS(parser.iterate(pad(json)).get(doc_result));
|
||||
Response deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<Response>().get(deserialized));
|
||||
|
||||
auto get_result = doc_result.value().get<Response>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
Response deserialized = std::move(get_result.value());
|
||||
ASSERT_TRUE(deserialized.error == ErrorCode::NotFound);
|
||||
ASSERT_EQUAL(deserialized.message, "Resource not found");
|
||||
#endif
|
||||
@@ -219,10 +204,8 @@ namespace builder_tests {
|
||||
|
||||
Date test{Day::Friday, Month::July, 2024};
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
ASSERT_TRUE(json.find("\"day\":\"Friday\"") != std::string::npos); // Friday as string
|
||||
ASSERT_TRUE(json.find("\"month\":\"July\"") != std::string::npos); // July as string
|
||||
ASSERT_TRUE(json.find("\"year\":2024") != std::string::npos);
|
||||
@@ -231,11 +214,8 @@ namespace builder_tests {
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<Date>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
Date deserialized = std::move(get_result.value());
|
||||
Date deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<Date>().get(deserialized));
|
||||
ASSERT_TRUE(deserialized.day == Day::Friday);
|
||||
ASSERT_TRUE(deserialized.month == Month::July);
|
||||
ASSERT_EQUAL(deserialized.year, 2024);
|
||||
|
||||
@@ -123,7 +123,7 @@ endif()
|
||||
|
||||
# Add the tests if we're on:
|
||||
# 1. Visual Studio 2022 v17.6 or later
|
||||
# 2. GCC v14.0.0 or later (GCC v13.0.0 cannot handle pipe oprator of lambda)
|
||||
# 2. GCC v14.0.0 or later (GCC v13.0.0 cannot handle pipe operator of lambda)
|
||||
# 3. Clang v15.0.0 or later (certain version C++ headers occur error when compiling)
|
||||
if(
|
||||
(MSVC AND MSVC_VERSION LESS 1930) OR
|
||||
|
||||
@@ -1795,6 +1795,18 @@ namespace validate_tests {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool shall_not_parse() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
auto test = "{\"joe\":\"\xf0\x8f\xbf\xbf\"}"_padded;
|
||||
simdjson::dom::parser parser;
|
||||
simdjson::dom::element doc;
|
||||
auto error = parser.parse(test).get(doc);
|
||||
if(error) {
|
||||
return true; // expected
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool test_validate() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
const std::string test = R"({ "foo" : 1, "bar" : [ 1, 2, 3 ], "baz": { "a": 1, "b": 2, "c": 3 } })";
|
||||
@@ -1860,6 +1872,7 @@ namespace validate_tests {
|
||||
}
|
||||
bool run() {
|
||||
return issue1187() &&
|
||||
shall_not_parse() &&
|
||||
test_range() &&
|
||||
test_issue1169_long() &&
|
||||
test_issue1169() &&
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
using namespace simdjson;
|
||||
namespace ranges_test {
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
bool printout() {
|
||||
TEST_START();
|
||||
auto cars_json = R"( [
|
||||
@@ -43,6 +43,7 @@ namespace ranges_test {
|
||||
ASSERT_TRUE(std::ranges::equal(cars, expected));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
|
||||
bool run() {
|
||||
return
|
||||
|
||||
@@ -0,0 +1,418 @@
|
||||
/**
|
||||
* @file compile_time_json_tests.cpp
|
||||
* @brief Comprehensive tests for compile-time JSON parsing using C++26 P2996 reflection
|
||||
*/
|
||||
|
||||
#include "simdjson.h"
|
||||
#include "test_ondemand.h"
|
||||
|
||||
using namespace simdjson;
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
namespace compile_time_json_tests {
|
||||
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
using namespace arm64::compile_time;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Test 1: Basic object with primitives
|
||||
*/
|
||||
bool test_basic_object() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto config = parse_json<R"({
|
||||
"port": 8080,
|
||||
"host": "localhost",
|
||||
"debug": true,
|
||||
"timeout": 30.5
|
||||
})">();
|
||||
|
||||
static_assert(config.port == 8080);
|
||||
static_assert(std::string_view(config.host) == "localhost");
|
||||
static_assert(config.debug == true);
|
||||
static_assert(config.timeout == 30.5);
|
||||
|
||||
ASSERT_EQUAL(config.port, 8080);
|
||||
ASSERT_EQUAL(std::string_view(config.host), "localhost"sv);
|
||||
ASSERT_TRUE(config.debug);
|
||||
ASSERT_EQUAL(config.timeout, 30.5);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 2: Nested objects
|
||||
*/
|
||||
bool test_nested_objects() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto config = parse_json<R"({
|
||||
"server_port": 3000,
|
||||
"enable_ssl": true,
|
||||
"database": {
|
||||
"host": "db.example.com",
|
||||
"port": 5432,
|
||||
"timeout_sec": 30.0
|
||||
}
|
||||
})">();
|
||||
|
||||
static_assert(config.server_port == 3000);
|
||||
static_assert(config.enable_ssl == true);
|
||||
static_assert(std::string_view(config.database.host) == "db.example.com");
|
||||
static_assert(config.database.port == 5432);
|
||||
static_assert(config.database.timeout_sec == 30.0);
|
||||
|
||||
ASSERT_EQUAL(config.server_port, 3000);
|
||||
ASSERT_TRUE(config.enable_ssl);
|
||||
ASSERT_EQUAL(std::string_view(config.database.host), "db.example.com"sv);
|
||||
ASSERT_EQUAL(config.database.port, 5432);
|
||||
ASSERT_EQUAL(config.database.timeout_sec, 30.0);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 3: Deeply nested objects (3+ levels)
|
||||
*/
|
||||
bool test_deeply_nested_objects() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto config = parse_json<R"({
|
||||
"app_name": "MyApp",
|
||||
"version": 1.5,
|
||||
"server": {
|
||||
"host": "api.example.com",
|
||||
"port": 443,
|
||||
"tls": {
|
||||
"enabled": true,
|
||||
"cert_path": "/etc/ssl/cert.pem",
|
||||
"min_version": 1.3
|
||||
}
|
||||
}
|
||||
})">();
|
||||
|
||||
static_assert(std::string_view(config.app_name) == "MyApp");
|
||||
static_assert(config.version == 1.5);
|
||||
static_assert(std::string_view(config.server.host) == "api.example.com");
|
||||
static_assert(config.server.port == 443);
|
||||
static_assert(config.server.tls.enabled == true);
|
||||
static_assert(std::string_view(config.server.tls.cert_path) == "/etc/ssl/cert.pem");
|
||||
static_assert(config.server.tls.min_version == 1.3);
|
||||
|
||||
ASSERT_EQUAL(std::string_view(config.app_name), "MyApp"sv);
|
||||
ASSERT_EQUAL(config.server.tls.enabled, true);
|
||||
ASSERT_EQUAL(config.server.tls.min_version, 1.3);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 4: Empty object
|
||||
*/
|
||||
bool test_empty_object() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto config = parse_json<"{}">();
|
||||
(void)config; // Suppress unused warning
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 5: Negative numbers
|
||||
*/
|
||||
bool test_negative_numbers() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto data = parse_json<R"({
|
||||
"temperature": -273.15,
|
||||
"count": -42
|
||||
})">();
|
||||
|
||||
static_assert(data.temperature == -273.15);
|
||||
static_assert(data.count == -42);
|
||||
|
||||
ASSERT_EQUAL(data.temperature, -273.15);
|
||||
ASSERT_EQUAL(data.count, -42);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 6: Whitespace handling
|
||||
*/
|
||||
bool test_whitespace() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto data = parse_json<R"(
|
||||
{
|
||||
"key1" : "value1" ,
|
||||
"key2" : 42
|
||||
}
|
||||
)">();
|
||||
|
||||
static_assert(std::string_view(data.key1) == "value1");
|
||||
static_assert(data.key2 == 42);
|
||||
|
||||
ASSERT_EQUAL(std::string_view(data.key1), "value1"sv);
|
||||
ASSERT_EQUAL(data.key2, 42);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 7: Real-time system configuration
|
||||
*/
|
||||
bool test_realtime_config() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto config = parse_json<R"({
|
||||
"control_loop_hz": 1000,
|
||||
"max_acceleration": 9.8,
|
||||
"min_velocity": -50.0,
|
||||
"max_velocity": 50.0,
|
||||
"enable_safety_checks": true,
|
||||
"log_level": "INFO"
|
||||
})">();
|
||||
|
||||
static_assert(config.control_loop_hz == 1000);
|
||||
static_assert(config.max_acceleration == 9.8);
|
||||
static_assert(config.enable_safety_checks == true);
|
||||
|
||||
ASSERT_EQUAL(config.control_loop_hz, 1000);
|
||||
ASSERT_EQUAL(config.max_acceleration, 9.8);
|
||||
ASSERT_EQUAL(config.min_velocity, -50.0);
|
||||
ASSERT_EQUAL(config.max_velocity, 50.0);
|
||||
ASSERT_TRUE(config.enable_safety_checks);
|
||||
ASSERT_EQUAL(std::string_view(config.log_level), "INFO"sv);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 8: External JSON file (future #embed support)
|
||||
*/
|
||||
bool test_external_json_embed() {
|
||||
TEST_START();
|
||||
|
||||
// Future C++26 with #embed:
|
||||
// constexpr auto config = parse_json<#embed "test_config.json">();
|
||||
|
||||
// Current workaround - inline the JSON from test_config.json
|
||||
constexpr auto config = parse_json<R"({
|
||||
"system_name": "RealTimeController",
|
||||
"version": "2.1.0",
|
||||
"control_loop_hz": 1000,
|
||||
"max_latency_us": 500,
|
||||
"enable_diagnostics": true,
|
||||
"log_level": "INFO"
|
||||
})">();
|
||||
|
||||
static_assert(std::string_view(config.system_name) == "RealTimeController");
|
||||
static_assert(std::string_view(config.version) == "2.1.0");
|
||||
static_assert(config.control_loop_hz == 1000);
|
||||
static_assert(config.max_latency_us == 500);
|
||||
static_assert(config.enable_diagnostics == true);
|
||||
static_assert(std::string_view(config.log_level) == "INFO");
|
||||
|
||||
ASSERT_EQUAL(std::string_view(config.system_name), "RealTimeController"sv);
|
||||
ASSERT_EQUAL(std::string_view(config.version), "2.1.0"sv);
|
||||
ASSERT_EQUAL(config.control_loop_hz, 1000);
|
||||
ASSERT_EQUAL(config.max_latency_us, 500);
|
||||
ASSERT_TRUE(config.enable_diagnostics);
|
||||
ASSERT_EQUAL(std::string_view(config.log_level), "INFO"sv);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 9: JSON validation
|
||||
*/
|
||||
bool test_json_validation() {
|
||||
TEST_START();
|
||||
|
||||
static_assert(validate_json<R"({"valid": true})">());
|
||||
static_assert(validate_json<R"({"nested": {"deep": 42}})">());
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 10: Null values
|
||||
*/
|
||||
bool test_null_values() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto data = parse_json<R"({
|
||||
"nullable_field": null,
|
||||
"number": 42
|
||||
})">();
|
||||
|
||||
static_assert(data.nullable_field == nullptr);
|
||||
static_assert(data.number == 42);
|
||||
|
||||
ASSERT_EQUAL(data.nullable_field, nullptr);
|
||||
ASSERT_EQUAL(data.number, 42);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 11: Arrays of primitives
|
||||
*/
|
||||
bool test_arrays_primitives() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto data = parse_json<R"({
|
||||
"values": [1, 2, 3, 4, 5],
|
||||
"flags": [true, false, true]
|
||||
})">();
|
||||
|
||||
static_assert(data.values.size() == 5);
|
||||
static_assert(data.values[0] == 1);
|
||||
static_assert(data.values[4] == 5);
|
||||
static_assert(data.flags.size() == 3);
|
||||
static_assert(data.flags[0] == true);
|
||||
static_assert(data.flags[1] == false);
|
||||
|
||||
ASSERT_EQUAL(data.values.size(), 5);
|
||||
ASSERT_EQUAL(data.values[0], 1);
|
||||
ASSERT_EQUAL(data.values[4], 5);
|
||||
ASSERT_EQUAL(data.flags.size(), 3);
|
||||
ASSERT_TRUE(data.flags[0]);
|
||||
ASSERT_FALSE(data.flags[1]);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 12: Arrays of objects
|
||||
*/
|
||||
bool test_arrays_of_objects() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto data = parse_json<R"({
|
||||
"users": [
|
||||
{"name": "Alice", "age": 30},
|
||||
{"name": "Bob", "age": 25}
|
||||
]
|
||||
})">();
|
||||
|
||||
static_assert(data.users.size() == 2);
|
||||
static_assert(std::string_view(data.users[0].name) == "Alice");
|
||||
static_assert(data.users[0].age == 30);
|
||||
static_assert(std::string_view(data.users[1].name) == "Bob");
|
||||
static_assert(data.users[1].age == 25);
|
||||
|
||||
ASSERT_EQUAL(data.users.size(), 2);
|
||||
ASSERT_EQUAL(std::string_view(data.users[0].name), "Alice"sv);
|
||||
ASSERT_EQUAL(data.users[0].age, 30);
|
||||
ASSERT_EQUAL(std::string_view(data.users[1].name), "Bob"sv);
|
||||
ASSERT_EQUAL(data.users[1].age, 25);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 13: Nested arrays in objects
|
||||
*/
|
||||
bool test_nested_arrays() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto data = parse_json<R"({
|
||||
"config": {
|
||||
"ports": [8080, 8081, 8082]
|
||||
}
|
||||
})">();
|
||||
|
||||
static_assert(data.config.ports.size() == 3);
|
||||
static_assert(data.config.ports[0] == 8080);
|
||||
static_assert(data.config.ports[2] == 8082);
|
||||
|
||||
ASSERT_EQUAL(data.config.ports.size(), 3);
|
||||
ASSERT_EQUAL(data.config.ports[0], 8080);
|
||||
ASSERT_EQUAL(data.config.ports[2], 8082);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 14: Complex mixed structure with arrays and nested objects
|
||||
*/
|
||||
bool test_complex_mixed() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto config = parse_json<R"({
|
||||
"app": "myapp",
|
||||
"version": 1.0,
|
||||
"config": {
|
||||
"ports": [8080, 8081, 8082],
|
||||
"enabled": true
|
||||
},
|
||||
"servers": [
|
||||
{"host": "server1", "port": 3000},
|
||||
{"host": "server2", "port": 3001}
|
||||
]
|
||||
})">();
|
||||
|
||||
static_assert(std::string_view(config.app) == "myapp");
|
||||
static_assert(config.version == 1.0);
|
||||
static_assert(config.config.ports[0] == 8080);
|
||||
static_assert(config.config.enabled == true);
|
||||
static_assert(std::string_view(config.servers[0].host) == "server1");
|
||||
static_assert(config.servers[1].port == 3001);
|
||||
|
||||
ASSERT_EQUAL(std::string_view(config.app), "myapp"sv);
|
||||
ASSERT_EQUAL(config.config.ports[0], 8080);
|
||||
ASSERT_EQUAL(std::string_view(config.servers[0].host), "server1"sv);
|
||||
ASSERT_EQUAL(config.servers[1].port, 3001);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 15: Empty arrays
|
||||
*/
|
||||
bool test_empty_arrays() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto data = parse_json<R"({
|
||||
"empty": []
|
||||
})">();
|
||||
|
||||
static_assert(data.empty.size() == 0);
|
||||
|
||||
ASSERT_EQUAL(data.empty.size(), 0);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool run() {
|
||||
return test_basic_object() &&
|
||||
test_nested_objects() &&
|
||||
test_deeply_nested_objects() &&
|
||||
test_empty_object() &&
|
||||
test_negative_numbers() &&
|
||||
test_whitespace() &&
|
||||
test_realtime_config() &&
|
||||
test_external_json_embed() &&
|
||||
test_json_validation() &&
|
||||
test_null_values() &&
|
||||
test_arrays_primitives() &&
|
||||
test_arrays_of_objects() &&
|
||||
test_nested_arrays() &&
|
||||
test_complex_mixed() &&
|
||||
test_empty_arrays();
|
||||
}
|
||||
|
||||
} // namespace compile_time_json_tests
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
return test_main(argc, argv, compile_time_json_tests::run);
|
||||
#else
|
||||
std::cout << "Compile-time JSON tests require SIMDJSON_STATIC_REFLECTION=ON" << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
#endif
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user