mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 22773f3c70 | |||
| 249eb2c28a | |||
| 94c429aa70 | |||
| 72e51a9a81 | |||
| 1a57afec1f | |||
| 4de7426b9f | |||
| 85cadf4074 | |||
| bc48e72070 | |||
| 8955c2c6d5 | |||
| 73e69a5e84 | |||
| fb83b114ef | |||
| 542cd7af71 | |||
| 769364abb2 | |||
| f25d5f9fc2 | |||
| 12fb30ae9c | |||
| c0a952687d | |||
| 307ddd4415 | |||
| 21657455ed | |||
| afb1c9f4b1 | |||
| 9973f7b3a3 | |||
| 18d612df15 | |||
| bddb23177e | |||
| 2c5de6c6ed | |||
| 844e5eac23 | |||
| 45caa861cd | |||
| 50bf372d3f | |||
| b4a0fd3ff4 | |||
| a1ff05a5e8 | |||
| 5395aacb8b | |||
| 781ea4b495 | |||
| 03e8d14eee | |||
| be94694290 | |||
| 6065b3b424 | |||
| 019bf1dfe0 | |||
| 2922822622 | |||
| 262ddad037 | |||
| ba007b0cb9 | |||
| ec675d9c61 | |||
| 7dcc196f66 | |||
| 7ec4572d33 | |||
| 5891d4b64b | |||
| 3cd9875f1f | |||
| 6cf1bdba43 | |||
| 817f10dd7d | |||
| e829566589 | |||
| dbe0c1543e | |||
| 06c774c655 | |||
| 95d8c81810 | |||
| af2a43610e | |||
| e362b18499 | |||
| 6e60ed8338 | |||
| e2ca00c841 | |||
| b7124f7c64 | |||
| e6c6db6493 | |||
| 6fffc99dac | |||
| 8c5cc8c443 | |||
| d8c49a8f25 | |||
| cda98064b8 | |||
| e532d61e16 | |||
| db93de2a21 |
@@ -29,6 +29,9 @@ We accept the identification of an issue by a sanitizer or some checker tool (e.
|
|||||||
|
|
||||||
We recommend that you run your tests using different optimization levels. In particular, we recommend your run tests with the simdjson library and you code compiled in debug mode. The simdjson then sets the SIMDJSON_DEVELOPMENT_CHECKS macro to 1, and this triggers additional checks on your code and on the internals of the library. If possible, we recommend that you run tests with sanitizers (e.g., see [No more leaks with sanitize flags in gcc and clang](https://lemire.me/blog/2016/04/20/no-more-leaks-with-sanitize-flags-in-gcc-and-clang/)). You can compile the library with sanitizers for debugging purposes (e.g., set SIMDJSON_SANITIZE to ON using CMake), but you should also turn on sanitizers on your own code. You may also use tools like valgrind or the commercial equivalent.
|
We recommend that you run your tests using different optimization levels. In particular, we recommend your run tests with the simdjson library and you code compiled in debug mode. The simdjson then sets the SIMDJSON_DEVELOPMENT_CHECKS macro to 1, and this triggers additional checks on your code and on the internals of the library. If possible, we recommend that you run tests with sanitizers (e.g., see [No more leaks with sanitize flags in gcc and clang](https://lemire.me/blog/2016/04/20/no-more-leaks-with-sanitize-flags-in-gcc-and-clang/)). You can compile the library with sanitizers for debugging purposes (e.g., set SIMDJSON_SANITIZE to ON using CMake), but you should also turn on sanitizers on your own code. You may also use tools like valgrind or the commercial equivalent.
|
||||||
|
|
||||||
|
Mixing debug and release simdjson code is unsafe: you either build all your code using simdjson in
|
||||||
|
release mode or all of it in debug mode.
|
||||||
|
|
||||||
Before reporting a bug, please ensure that you have read our documentation.
|
Before reporting a bug, please ensure that you have read our documentation.
|
||||||
|
|
||||||
**To Reproduce**
|
**To Reproduce**
|
||||||
@@ -55,6 +58,7 @@ We support up-to-date 64-bit ARM and x64 FreeBSD, macOS, Windows and Linux syste
|
|||||||
* We do not support unreleased or experimental compilers. If you encounter an issue with a
|
* We do not support unreleased or experimental compilers. If you encounter an issue with a
|
||||||
pre-release version of a compiler, do not report it as a bug to simdjson. However, we always
|
pre-release version of a compiler, do not report it as a bug to simdjson. However, we always
|
||||||
invite contributions either in the form an analysis or of a code contribution.
|
invite contributions either in the form an analysis or of a code contribution.
|
||||||
|
* Vendors (e.g., Apple and Microsoft) stop supporting old systems. Once a compiler system is no longer supported by its vendor, we no longer support it. We will gladly accept code contributions, but we do not consider it a *bug* if you have issues with an obsolete compiler systems. This policy extends to obsolete standard libraries, linkers and other build tools. Please do not report it as an issue. If you cannot resolve the issue yourself, we encourage you to reach out to the vendor for legacy support.
|
||||||
|
|
||||||
Under Windows, we support Visual Studio (both with LLVM and without). We do not support MinGW and other alternate compiler systems. Windows users should be aware that there [is a long-running bug with GCC under Windows](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412).
|
Under Windows, we support Visual Studio (both with LLVM and without). We do not support MinGW and other alternate compiler systems. Windows users should be aware that there [is a long-running bug with GCC under Windows](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412).
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ How to verify / test
|
|||||||
Please read before contributing:
|
Please read before contributing:
|
||||||
- CONTRIBUTING: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md
|
- CONTRIBUTING: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md
|
||||||
- HACKING: https://github.com/simdjson/simdjson/blob/master/HACKING.md
|
- HACKING: https://github.com/simdjson/simdjson/blob/master/HACKING.md
|
||||||
|
- AI Usage Policy: https://github.com/simdjson/simdjson/blob/master/AI_USAGE_POLICY.md
|
||||||
|
|
||||||
|
|
||||||
If you can, we recommend running our tests with the sanitizers turned on.
|
If you can, we recommend running our tests with the sanitizers turned on.
|
||||||
|
|||||||
@@ -19,11 +19,11 @@ jobs:
|
|||||||
sudo apt-get install -y cmake make g++-riscv64-linux-gnu qemu-user-static clang-18
|
sudo apt-get install -y cmake make g++-riscv64-linux-gnu qemu-user-static clang-18
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
CXX=clang++-18 CXXFLAGS="--target=riscv64-linux-gnu -march=rv64gcv_zvbb" \
|
CC=clang-18 CXX=clang++-18 CFLAGS="--target=riscv64-linux-gnu -march=rv64gcv_zvbb" CXXFLAGS="${CFLAGS}" \
|
||||||
cmake --toolchain=cmake/toolchains-ci/riscv64-linux-gnu.cmake -DCMAKE_BUILD_TYPE=Release -B build
|
cmake --toolchain=cmake/toolchains-ci/riscv64-linux-gnu.cmake -DSIMDJSON_DEVELOPER_MODE=ON -B build
|
||||||
cmake --build build/ -j$(nproc)
|
cmake --build build/ -j$(nproc) --config Release
|
||||||
- name: Test VLEN=1024
|
- name: Test VLEN=1024
|
||||||
run: |
|
run: |
|
||||||
export QEMU_LD_PREFIX="/usr/riscv64-linux-gnu"
|
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"
|
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)
|
ctest --timeout 1800 --output-on-failure --test-dir build -j $(nproc)
|
||||||
|
|||||||
@@ -19,11 +19,6 @@ jobs:
|
|||||||
sudo apt-get install -y cmake make g++-riscv64-linux-gnu qemu-user-static clang-17
|
sudo apt-get install -y cmake make g++-riscv64-linux-gnu qemu-user-static clang-17
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
CXX=clang++-17 CXXFLAGS="--target=riscv64-linux-gnu -march=rv64gcv" \
|
CC=clang-17 CXX=clang++-17 CFLAGS="--target=riscv64-linux-gnu -march=rv64gcv" CXXFLAGS="${CFLAGS}" \
|
||||||
cmake --toolchain=cmake/toolchains-ci/riscv64-linux-gnu.cmake -DCMAKE_BUILD_TYPE=Release -B build
|
cmake --toolchain=cmake/toolchains-ci/riscv64-linux-gnu.cmake -DSIMDJSON_DEVELOPER_MODE=ON -B build
|
||||||
cmake --build build/ -j$(nproc)
|
cmake --build build/ -j$(nproc) --config Release
|
||||||
- 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,39 @@
|
|||||||
|
name: Ubuntu rvv VLEN=128 (clang 20)
|
||||||
|
|
||||||
|
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-20
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
CC=clang-20 CXX=clang++-20 CFLAGS="--target=riscv64-linux-gnu -march=rv64gcv" CXXFLAGS="${CFLAGS}" \
|
||||||
|
cmake --toolchain=cmake/toolchains-ci/riscv64-linux-gnu.cmake -DSIMDJSON_DEVELOPER_MODE=ON -B build
|
||||||
|
cmake --build build/ -j$(nproc) --config Release
|
||||||
|
- name: Test VLEN=128
|
||||||
|
run: |
|
||||||
|
QEMU_LD_PREFIX="/usr/riscv64-linux-gnu" \
|
||||||
|
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)
|
||||||
|
- name: Build VLS
|
||||||
|
run: |
|
||||||
|
CC=clang-20 CXX=clang++-20 CFLAGS="--target=riscv64-linux-gnu -march=rv64gcv_zvl128b_zba_zbb_zbc -mrvv-vector-bits=zvl" CXXFLAGS="${CFLAGS}" \
|
||||||
|
cmake --toolchain=cmake/toolchains-ci/riscv64-linux-gnu.cmake -DSIMDJSON_DEVELOPER_MODE=ON -B build-vls
|
||||||
|
cmake --build build-vls/ -j$(nproc) --config Release
|
||||||
|
- name: Test VLEN=128 VLS
|
||||||
|
run: |
|
||||||
|
QEMU_LD_PREFIX="/usr/riscv64-linux-gnu" \
|
||||||
|
QEMU_CPU="rv64,v=on,zba=on,zbb=on,zbc=on,vlen=128,rvv_ta_all_1s=on,rvv_ma_all_1s=on" \
|
||||||
|
ctest --timeout 1800 --output-on-failure --test-dir build-vls -j $(nproc)
|
||||||
@@ -19,11 +19,21 @@ jobs:
|
|||||||
sudo apt-get install -y cmake make g++-14-riscv64-linux-gnu qemu-user-static
|
sudo apt-get install -y cmake make g++-14-riscv64-linux-gnu qemu-user-static
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
CXX=riscv64-linux-gnu-g++-14 CXXFLAGS=-march=rv64gcv \
|
CC=riscv64-linux-gnu-gcc-14 CXX=riscv64-linux-gnu-g++-14 CFLAGS=-march=rv64gcv CXXFLAGS="${CFLAGS}" \
|
||||||
cmake --toolchain=cmake/toolchains-ci/riscv64-linux-gnu.cmake -DCMAKE_BUILD_TYPE=Release -B build
|
cmake --toolchain=cmake/toolchains-ci/riscv64-linux-gnu.cmake -DSIMDJSON_DEVELOPER_MODE=ON -B build
|
||||||
cmake --build build/ -j$(nproc)
|
cmake --build build/ -j$(nproc) --config Release
|
||||||
- name: Test VLEN=256
|
- name: Test VLEN=256
|
||||||
run: |
|
run: |
|
||||||
export QEMU_LD_PREFIX="/usr/riscv64-linux-gnu"
|
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"
|
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)
|
ctest --timeout 1800 --output-on-failure --test-dir build -j $(nproc)
|
||||||
|
- name: Build VLS
|
||||||
|
run: |
|
||||||
|
CC=riscv64-linux-gnu-gcc-14 CXX=riscv64-linux-gnu-g++-14 CFLAGS="-march=rv64gcv_zvl256b -mrvv-vector-bits=zvl" CXXFLAGS="${CFLAGS}" \
|
||||||
|
cmake --toolchain=cmake/toolchains-ci/riscv64-linux-gnu.cmake -DSIMDJSON_DEVELOPER_MODE=ON -B build-vls
|
||||||
|
cmake --build build-vls/ -j$(nproc) --config Release
|
||||||
|
- name: Test VLEN=256 VLS
|
||||||
|
run: |
|
||||||
|
QEMU_LD_PREFIX="/usr/riscv64-linux-gnu" \
|
||||||
|
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-vls -j $(nproc)
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
name: Ubuntu rvv VLEN=512 (clang 19)
|
||||||
|
|
||||||
|
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-19
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
CC=clang-19 CXX=clang++-19 CFLAGS="--target=riscv64-linux-gnu -march=rv64gcv" CXXFLAGS="${CFLAGS}" \
|
||||||
|
cmake --toolchain=cmake/toolchains-ci/riscv64-linux-gnu.cmake -DSIMDJSON_DEVELOPER_MODE=ON -B build
|
||||||
|
cmake --build build/ -j$(nproc) --config Release
|
||||||
|
- name: Test VLEN=512
|
||||||
|
run: |
|
||||||
|
QEMU_LD_PREFIX="/usr/riscv64-linux-gnu" \
|
||||||
|
QEMU_CPU="rv64,v=on,vlen=512,rvv_ta_all_1s=on,rvv_ma_all_1s=on" \
|
||||||
|
ctest --timeout 1800 --output-on-failure --test-dir build -j $(nproc)
|
||||||
|
- name: Build VLS
|
||||||
|
run: |
|
||||||
|
CC=clang-19 CXX=clang++-19 CFLAGS="--target=riscv64-linux-gnu -march=rv64gcv_zvl512b_zba_zbb_zbc -mrvv-vector-bits=zvl" CXXFLAGS="${CFLAGS}" \
|
||||||
|
cmake --toolchain=cmake/toolchains-ci/riscv64-linux-gnu.cmake -DSIMDJSON_DEVELOPER_MODE=ON -B build-vls
|
||||||
|
cmake --build build-vls/ -j$(nproc) --config Release
|
||||||
|
- name: Test VLEN=512 VLS
|
||||||
|
run: |
|
||||||
|
QEMU_LD_PREFIX="/usr/riscv64-linux-gnu" \
|
||||||
|
QEMU_CPU="rv64,v=on,zba=on,zbb=on,zbc=on,vlen=512,rvv_ta_all_1s=on,rvv_ma_all_1s=on" \
|
||||||
|
ctest --timeout 1800 --output-on-failure --test-dir build-vls -j $(nproc)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Performance check on Ubuntu 20.04 CI (GCC 9)
|
name: Performance check on Ubuntu 24.04 CI (GCC 13)
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Ubuntu 20.04 CI (GCC 9) without exceptions
|
name: Ubuntu 24.04 CI (GCC 13) without exceptions
|
||||||
|
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
cd .. &&
|
cd .. &&
|
||||||
mkdir build &&
|
mkdir build &&
|
||||||
cd build &&
|
cd build &&
|
||||||
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_EXCEPTIONS=OFF -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
|
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_EXCEPTIONS=OFF -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
|
||||||
cmake --build . &&
|
cmake --build . &&
|
||||||
ctest --output-on-failure -LE explicitonly -j &&
|
ctest --output-on-failure -LE explicitonly -j &&
|
||||||
make install &&
|
make install &&
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Ubuntu 20.04 CI (GCC 9) Without Threads
|
name: Ubuntu 24.04 CI (GCC 13) Without Threads
|
||||||
|
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
name: Ubuntu 20.04 CI (GCC 9) With Memory Sanitizer
|
name: Ubuntu 24.04 CI (GCC 13) With Memory Sanitizer
|
||||||
|
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ubuntu-build-address-sanitizier:
|
ubuntu-build-address-sanitizer:
|
||||||
if: >-
|
if: >-
|
||||||
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# AI Usage Policy
|
||||||
|
|
||||||
|
Contributors can use whatever tools they would like to
|
||||||
|
craft their contributions, but there must be a **human in the loop**.
|
||||||
|
**Contributors must read and review all LLM-generated code or text before they
|
||||||
|
ask other project members to review it.** The contributor is always the author
|
||||||
|
and is fully accountable for their contributions. Contributors should be
|
||||||
|
sufficiently confident that the contribution is high enough quality that asking
|
||||||
|
for a review is a good use of scarce maintainer time, and they should be **able
|
||||||
|
to answer questions about their work** during review.
|
||||||
|
|
||||||
|
We expect that new contributors will be less confident in their contributions,
|
||||||
|
and our guidance to them is to **start with small contributions** that they can
|
||||||
|
fully understand to build confidence. We aspire to be a welcoming community
|
||||||
|
that helps new contributors grow their expertise, but learning involves taking
|
||||||
|
small steps, getting feedback, and iterating. Passing maintainer feedback to an
|
||||||
|
LLM doesn't help anyone grow, and does not sustain our community.
|
||||||
|
|
||||||
|
This policy includes, but is not limited to, the following kinds of
|
||||||
|
contributions:
|
||||||
|
|
||||||
|
- Code, usually in the form of a pull request
|
||||||
|
- Issues or security vulnerabilities
|
||||||
|
- Comments and feedback on pull requests
|
||||||
|
|
||||||
|
## Extractive Contributions
|
||||||
|
|
||||||
|
The reason for our "human-in-the-loop" contribution policy is that processing
|
||||||
|
patches, PRs, RFCs, and comments is not free -- it takes a lot of
|
||||||
|
maintainer time and energy to review those contributions! Sending the
|
||||||
|
unreviewed output of an LLM to open source project maintainers *extracts* work
|
||||||
|
from them in the form of design and code review, so we call this kind of
|
||||||
|
contribution an "extractive contribution".
|
||||||
|
|
||||||
|
## Transparency
|
||||||
|
|
||||||
|
For contributions involving significant AI assistance, we encourage you to disclose
|
||||||
|
its use and explain your process. If a submission appears to rely heavily on AI
|
||||||
|
without disclosure, we may doubt that the **human-in-the-loop** requirement has
|
||||||
|
been met. Please show awareness of your use of AI.
|
||||||
|
|
||||||
|
## Copyright
|
||||||
|
|
||||||
|
Artificial intelligence systems raise many questions around copyright that have
|
||||||
|
yet to be answered. Our policy on AI tools is similar to our copyright policy:
|
||||||
|
Contributors are responsible for ensuring that they have the right to
|
||||||
|
contribute code under the terms of our license, typically meaning that either
|
||||||
|
they, their employer, or their collaborators hold the copyright. Using AI tools
|
||||||
|
to regenerate copyrighted material does not remove the copyright, and
|
||||||
|
contributors are responsible for ensuring that such material does not appear in
|
||||||
|
their contributions. Contributions found to violate this policy will be removed
|
||||||
|
just like any other offending contribution.
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
|
||||||
|
- [LLVM AI Tool Use Policy](https://discourse.llvm.org/t/rfc-llvm-ai-tool-policy-human-in-the-loop/89159)
|
||||||
+135
-90
@@ -12,7 +12,7 @@ endif()
|
|||||||
project(
|
project(
|
||||||
simdjson
|
simdjson
|
||||||
# The version number is modified by tools/release.py
|
# The version number is modified by tools/release.py
|
||||||
VERSION 4.2.4
|
VERSION 4.6.1
|
||||||
DESCRIPTION "Parsing gigabytes of JSON per second"
|
DESCRIPTION "Parsing gigabytes of JSON per second"
|
||||||
HOMEPAGE_URL "https://simdjson.org/"
|
HOMEPAGE_URL "https://simdjson.org/"
|
||||||
LANGUAGES CXX C
|
LANGUAGES CXX C
|
||||||
@@ -29,8 +29,8 @@ string(
|
|||||||
# ---- Options, variables ----
|
# ---- Options, variables ----
|
||||||
|
|
||||||
# These version numbers are modified by tools/release.py
|
# These version numbers are modified by tools/release.py
|
||||||
set(SIMDJSON_LIB_VERSION "29.0.0" CACHE STRING "simdjson library version")
|
set(SIMDJSON_LIB_VERSION "33.0.0" CACHE STRING "simdjson library version")
|
||||||
set(SIMDJSON_LIB_SOVERSION "29" CACHE STRING "simdjson library soversion")
|
set(SIMDJSON_LIB_SOVERSION "33" 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)
|
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)
|
if(SIMDJSON_BUILD_STATIC_LIB AND NOT BUILD_SHARED_LIBS)
|
||||||
@@ -76,9 +76,12 @@ if(SIMDJSON_DEVELOPMENT_CHECKS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(is_top_project)
|
if(is_top_project)
|
||||||
|
option(SIMDJSON_INSTALL "Enable target install" ON)
|
||||||
option(SIMDJSON_DEVELOPER_MODE "Enable targets for developing simdjson" OFF)
|
option(SIMDJSON_DEVELOPER_MODE "Enable targets for developing simdjson" OFF)
|
||||||
option(BUILD_SHARED_LIBS "Build simdjson as a shared library" OFF)
|
option(BUILD_SHARED_LIBS "Build simdjson as a shared library" OFF)
|
||||||
option(SIMDJSON_SINGLEHEADER "Disable singleheader generation" ON)
|
option(SIMDJSON_SINGLEHEADER "Disable singleheader generation" ON)
|
||||||
|
else()
|
||||||
|
option(SIMDJSON_INSTALL "Enable target install" ${BUILD_SHARED_LIBS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
include(cmake/handle-deprecations.cmake)
|
include(cmake/handle-deprecations.cmake)
|
||||||
@@ -92,13 +95,16 @@ add_library(simdjson ${SIMDJSON_SOURCES})
|
|||||||
add_library(simdjson::simdjson ALIAS simdjson)
|
add_library(simdjson::simdjson ALIAS simdjson)
|
||||||
set(SIMDJSON_LIBRARIES simdjson)
|
set(SIMDJSON_LIBRARIES simdjson)
|
||||||
|
|
||||||
|
# Check for <bit> header compatibility
|
||||||
|
include(CheckIncludeFileCXX)
|
||||||
|
check_include_file_cxx(bit SIMDJSON_HAS_BIT_HEADER)
|
||||||
|
|
||||||
# Enable precompiled headers for faster builds
|
# Enable precompiled headers for faster builds
|
||||||
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.16")
|
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.16")
|
||||||
target_precompile_headers(simdjson PRIVATE
|
set(SIMDJSON_PRECOMPILE_HEADERS
|
||||||
<algorithm>
|
<algorithm>
|
||||||
<array>
|
<array>
|
||||||
<atomic>
|
<atomic>
|
||||||
<bit>
|
|
||||||
<cassert>
|
<cassert>
|
||||||
<cctype>
|
<cctype>
|
||||||
<cerrno>
|
<cerrno>
|
||||||
@@ -111,6 +117,12 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.16")
|
|||||||
<utility>
|
<utility>
|
||||||
<vector>
|
<vector>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(SIMDJSON_HAS_BIT_HEADER)
|
||||||
|
list(APPEND SIMDJSON_PRECOMPILE_HEADERS <bit>)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_precompile_headers(simdjson PRIVATE ${SIMDJSON_PRECOMPILE_HEADERS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(SIMDJSON_BUILD_STATIC_LIB)
|
if(SIMDJSON_BUILD_STATIC_LIB)
|
||||||
@@ -155,13 +167,37 @@ if(MSVC)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if(SIMDJSON_STATIC_REFLECTION)
|
if(SIMDJSON_STATIC_REFLECTION)
|
||||||
|
|
||||||
|
|
||||||
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION MATCHES "^21")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_CXX_COMPILER} --version
|
||||||
|
OUTPUT_VARIABLE CLANG_VERSION_OUTPUT
|
||||||
|
ERROR_VARIABLE CLANG_VERSION_ERROR
|
||||||
|
RESULT_VARIABLE CLANG_VERSION_RESULT
|
||||||
|
)
|
||||||
|
if(CLANG_VERSION_RESULT EQUAL 0 AND CLANG_VERSION_OUTPUT MATCHES "https://github.com/bloomberg/clang-p2996.git")
|
||||||
|
set(IS_BLOOMBERG_P2996_CLANG ON)
|
||||||
|
message(STATUS "Using Bloomberg P2996 Clang fork")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
# We would like to require C++26, but no compiler supports that!
|
# We would like to require C++26, but no compiler supports that!
|
||||||
# This is a hack:
|
# This is a hack:
|
||||||
|
if(IS_BLOOMBERG_P2996_CLANG)
|
||||||
simdjson_add_props(
|
simdjson_add_props(
|
||||||
target_compile_options PUBLIC
|
target_compile_options PUBLIC
|
||||||
-freflection -fexpansion-statements -stdlib=libc++ -std=c++26
|
-freflection -fexpansion-statements -stdlib=libc++ -std=c++26
|
||||||
)
|
)
|
||||||
|
else()
|
||||||
|
simdjson_add_props(
|
||||||
|
target_compile_options PUBLIC
|
||||||
|
-freflection -std=c++26
|
||||||
|
)
|
||||||
|
endif()
|
||||||
else()
|
else()
|
||||||
simdjson_add_props(target_compile_features PUBLIC cxx_std_11)
|
simdjson_add_props(target_compile_features PUBLIC cxx_std_11)
|
||||||
endif()
|
endif()
|
||||||
@@ -216,87 +252,89 @@ endif()
|
|||||||
|
|
||||||
# ---- Install rules ----
|
# ---- Install rules ----
|
||||||
|
|
||||||
include(CMakePackageConfigHelpers)
|
if(SIMDJSON_INSTALL)
|
||||||
include(GNUInstallDirs)
|
include(CMakePackageConfigHelpers)
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
if(SIMDJSON_SINGLEHEADER)
|
if(SIMDJSON_SINGLEHEADER)
|
||||||
install(
|
install(
|
||||||
FILES singleheader/simdjson.h
|
FILES singleheader/simdjson.h
|
||||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||||
COMPONENT simdjson_Development
|
COMPONENT simdjson_Development
|
||||||
)
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
install(
|
||||||
|
TARGETS simdjson
|
||||||
|
EXPORT simdjsonTargets
|
||||||
|
RUNTIME COMPONENT simdjson_Runtime
|
||||||
|
LIBRARY COMPONENT simdjson_Runtime
|
||||||
|
NAMELINK_COMPONENT simdjson_Development
|
||||||
|
ARCHIVE COMPONENT simdjson_Development
|
||||||
|
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||||
|
)
|
||||||
|
configure_file(cmake/simdjson-config.cmake.in simdjson-config.cmake @ONLY)
|
||||||
|
|
||||||
|
write_basic_package_version_file(
|
||||||
|
simdjson-config-version.cmake
|
||||||
|
COMPATIBILITY SameMinorVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
set(
|
||||||
|
SIMDJSON_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/simdjson"
|
||||||
|
CACHE STRING "CMake package config location relative to the install prefix"
|
||||||
|
)
|
||||||
|
mark_as_advanced(SIMDJSON_INSTALL_CMAKEDIR)
|
||||||
|
|
||||||
|
install(
|
||||||
|
FILES
|
||||||
|
"${PROJECT_BINARY_DIR}/simdjson-config.cmake"
|
||||||
|
"${PROJECT_BINARY_DIR}/simdjson-config-version.cmake"
|
||||||
|
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
|
||||||
|
COMPONENT simdjson_Development
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
EXPORT simdjsonTargets
|
||||||
|
NAMESPACE simdjson::
|
||||||
|
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
|
||||||
|
COMPONENT simdjson_Development
|
||||||
|
)
|
||||||
|
|
||||||
|
if(SIMDJSON_BUILD_STATIC_LIB)
|
||||||
|
install(
|
||||||
|
TARGETS simdjson_static
|
||||||
|
EXPORT simdjson_staticTargets
|
||||||
|
ARCHIVE COMPONENT simdjson_Development
|
||||||
|
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
EXPORT simdjson_staticTargets
|
||||||
|
NAMESPACE simdjson::
|
||||||
|
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
|
||||||
|
COMPONENT simdjson_Development
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# pkg-config
|
||||||
|
include(cmake/JoinPaths.cmake)
|
||||||
|
join_paths(PKGCONFIG_INCLUDEDIR "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||||
|
join_paths(PKGCONFIG_LIBDIR "\${prefix}" "${CMAKE_INSTALL_LIBDIR}")
|
||||||
|
|
||||||
|
if(SIMDJSON_ENABLE_THREADS)
|
||||||
|
set(PKGCONFIG_CFLAGS "-DSIMDJSON_THREADS_ENABLED=1")
|
||||||
|
if(CMAKE_THREAD_LIBS_INIT)
|
||||||
|
set(PKGCONFIG_LIBS_PRIVATE "Libs.private: ${CMAKE_THREAD_LIBS_INIT}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
configure_file("simdjson.pc.in" "simdjson.pc" @ONLY)
|
||||||
|
install(
|
||||||
|
FILES "${CMAKE_CURRENT_BINARY_DIR}/simdjson.pc"
|
||||||
|
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
install(
|
|
||||||
TARGETS simdjson
|
|
||||||
EXPORT simdjsonTargets
|
|
||||||
RUNTIME COMPONENT simdjson_Runtime
|
|
||||||
LIBRARY COMPONENT simdjson_Runtime
|
|
||||||
NAMELINK_COMPONENT simdjson_Development
|
|
||||||
ARCHIVE COMPONENT simdjson_Development
|
|
||||||
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
|
||||||
)
|
|
||||||
configure_file(cmake/simdjson-config.cmake.in simdjson-config.cmake @ONLY)
|
|
||||||
|
|
||||||
write_basic_package_version_file(
|
|
||||||
simdjson-config-version.cmake
|
|
||||||
COMPATIBILITY SameMinorVersion
|
|
||||||
)
|
|
||||||
|
|
||||||
set(
|
|
||||||
SIMDJSON_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/simdjson"
|
|
||||||
CACHE STRING "CMake package config location relative to the install prefix"
|
|
||||||
)
|
|
||||||
mark_as_advanced(SIMDJSON_INSTALL_CMAKEDIR)
|
|
||||||
|
|
||||||
install(
|
|
||||||
FILES
|
|
||||||
"${PROJECT_BINARY_DIR}/simdjson-config.cmake"
|
|
||||||
"${PROJECT_BINARY_DIR}/simdjson-config-version.cmake"
|
|
||||||
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
|
|
||||||
COMPONENT simdjson_Development
|
|
||||||
)
|
|
||||||
|
|
||||||
install(
|
|
||||||
EXPORT simdjsonTargets
|
|
||||||
NAMESPACE simdjson::
|
|
||||||
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
|
|
||||||
COMPONENT simdjson_Development
|
|
||||||
)
|
|
||||||
|
|
||||||
if(SIMDJSON_BUILD_STATIC_LIB)
|
|
||||||
install(
|
|
||||||
TARGETS simdjson_static
|
|
||||||
EXPORT simdjson_staticTargets
|
|
||||||
ARCHIVE COMPONENT simdjson_Development
|
|
||||||
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
|
||||||
)
|
|
||||||
install(
|
|
||||||
EXPORT simdjson_staticTargets
|
|
||||||
NAMESPACE simdjson::
|
|
||||||
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
|
|
||||||
COMPONENT simdjson_Development
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# pkg-config
|
|
||||||
include(cmake/JoinPaths.cmake)
|
|
||||||
join_paths(PKGCONFIG_INCLUDEDIR "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
|
|
||||||
join_paths(PKGCONFIG_LIBDIR "\${prefix}" "${CMAKE_INSTALL_LIBDIR}")
|
|
||||||
|
|
||||||
if(SIMDJSON_ENABLE_THREADS)
|
|
||||||
set(PKGCONFIG_CFLAGS "-DSIMDJSON_THREADS_ENABLED=1")
|
|
||||||
if(CMAKE_THREAD_LIBS_INIT)
|
|
||||||
set(PKGCONFIG_LIBS_PRIVATE "Libs.private: ${CMAKE_THREAD_LIBS_INIT}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
configure_file("simdjson.pc.in" "simdjson.pc" @ONLY)
|
|
||||||
install(
|
|
||||||
FILES "${CMAKE_CURRENT_BINARY_DIR}/simdjson.pc"
|
|
||||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
|
|
||||||
)
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# CPack
|
# CPack
|
||||||
#
|
#
|
||||||
@@ -373,18 +411,25 @@ add_subdirectory(fuzz)
|
|||||||
#
|
#
|
||||||
# Source files should be just ASCII
|
# Source files should be just ASCII
|
||||||
#
|
#
|
||||||
find_program(FIND find)
|
find_program(FIND_CMD find)
|
||||||
find_program(FILE file)
|
find_program(FILE_CMD file)
|
||||||
find_program(GREP grep)
|
find_program(GREP_CMD grep)
|
||||||
if(FIND AND FILE AND GREP)
|
if(FIND_CMD AND FILE_CMD AND GREP_CMD)
|
||||||
add_test(
|
add_test(
|
||||||
NAME just_ascii
|
NAME just_ascii
|
||||||
COMMAND sh -c "\
|
COMMAND sh -c "\
|
||||||
${FIND} include src windows tools singleheader tests examples benchmark \
|
non_ascii=$(${FIND_CMD} include src windows tools singleheader tests examples benchmark \
|
||||||
-path benchmark/checkperf-reference -prune -name '*.h' -o -name '*.cpp' \
|
-path benchmark/checkperf-reference -prune -name '*.h' -o -name '*.cpp' \
|
||||||
-type f -exec ${FILE} '{}' \; | ${GREP} -qv ASCII || exit 0 && exit 1"
|
-type f -exec ${FILE_CMD} '{}' \; | ${GREP_CMD} -v ASCII); \
|
||||||
|
if [ -n \"$non_ascii\" ]; then \
|
||||||
|
echo 'The following files contain non-ASCII characters:'; \
|
||||||
|
echo \"$non_ascii\"; \
|
||||||
|
exit 1; \
|
||||||
|
fi"
|
||||||
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
|
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
|
||||||
)
|
)
|
||||||
|
else()
|
||||||
|
message(WARNING "just_ascii test disabled because required tools were not found: find='${FIND_CMD}', file='${FILE_CMD}', grep='${GREP_CMD}'")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -101,3 +101,10 @@ Getting Started Hacking
|
|||||||
|
|
||||||
An overview of simdjson's directory structure, with pointers to architecture and design
|
An overview of simdjson's directory structure, with pointers to architecture and design
|
||||||
considerations and other helpful notes, can be found at [HACKING.md](HACKING.md).
|
considerations and other helpful notes, can be found at [HACKING.md](HACKING.md).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AI Usage Policy
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Please also review our [AI Usage Policy](AI_USAGE_POLICY.md).
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
|
|||||||
# could be handy for archiving the generated documentation or if some version
|
# could be handy for archiving the generated documentation or if some version
|
||||||
# control system is used.
|
# control system is used.
|
||||||
|
|
||||||
PROJECT_NUMBER = "4.2.4"
|
PROJECT_NUMBER = "4.6.1"
|
||||||
|
|
||||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
# 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
|
# for a project that appears at the top of each page and should give viewer a
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ Real-world usage
|
|||||||
- [RonDB](https://github.com/logicalclocks/rondb)
|
- [RonDB](https://github.com/logicalclocks/rondb)
|
||||||
- [GreptimeDB](https://github.com/GreptimeTeam/greptimedb)
|
- [GreptimeDB](https://github.com/GreptimeTeam/greptimedb)
|
||||||
- [mamba](https://github.com/mamba-org/mamba)
|
- [mamba](https://github.com/mamba-org/mamba)
|
||||||
|
- [Ladybird Browser](https://ladybird.org)
|
||||||
|
- [SereneDB](https://github.com/serenedb/serenedb)
|
||||||
|
|
||||||
|
|
||||||
If you are planning to use simdjson in a product, please work from one of our releases.
|
If you are planning to use simdjson in a product, please work from one of our releases.
|
||||||
@@ -187,6 +189,7 @@ We distinguish between "bindings" (which just wrap the C++ code) and a port to a
|
|||||||
- [JSON::SIMD](https://metacpan.org/pod/JSON::SIMD): Perl bindings; fully-featured JSON module that uses simdjson for decoding.
|
- [JSON::SIMD](https://metacpan.org/pod/JSON::SIMD): Perl bindings; fully-featured JSON module that uses simdjson for decoding.
|
||||||
- [gemmaJSON](https://github.com/sainttttt/gemmaJSON): Nim JSON parser based on simdjson bindings.
|
- [gemmaJSON](https://github.com/sainttttt/gemmaJSON): Nim JSON parser based on simdjson bindings.
|
||||||
- [simdjson-java](https://github.com/simdjson/simdjson-java): Java port.
|
- [simdjson-java](https://github.com/simdjson/simdjson-java): Java port.
|
||||||
|
- [mruby-fast-json](https://github.com/Asmod4n/mruby-fast-json): mruby binding with high API coverage.
|
||||||
|
|
||||||
About simdjson
|
About simdjson
|
||||||
--------------
|
--------------
|
||||||
@@ -209,10 +212,16 @@ We have an in-depth paper focused on the UTF-8 validation:
|
|||||||
|
|
||||||
We also have an informal [blog post providing some background and context](https://branchfree.org/2019/02/25/paper-parsing-gigabytes-of-json-per-second/).
|
We also have an informal [blog post providing some background and context](https://branchfree.org/2019/02/25/paper-parsing-gigabytes-of-json-per-second/).
|
||||||
|
|
||||||
For the video inclined, <br />
|
For the video inclined, we had a talk at QCon San Francisco 2019<br />
|
||||||
[](http://www.youtube.com/watch?v=wlvKAT7SZIQ)<br />
|
[](http://www.youtube.com/watch?v=wlvKAT7SZIQ)<br />
|
||||||
(It was the best voted talk, we're kinda proud of it.)
|
(It was the best voted talk, we're kinda proud of it.)
|
||||||
|
|
||||||
|
We also had a CppCon 2025 talk. We show how C++26 reflection allows for one-line serialization (to_json(player)) or deserialization—without invasive macros or manual mapping—using nothing but the C++ standard library. Whether you’re a performance junkie or simply interested in the roadmap for the next decade of C++ development, watch our full talk!
|
||||||
|
|
||||||
|
[](http://www.youtube.com/watch?v=Mcgk3CxHYMs)<br />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Citing this work
|
Citing this work
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ if (TARGET benchmark::benchmark)
|
|||||||
link_libraries(benchmark::benchmark)
|
link_libraries(benchmark::benchmark)
|
||||||
add_executable(bench_parse_call bench_parse_call.cpp)
|
add_executable(bench_parse_call bench_parse_call.cpp)
|
||||||
add_executable(bench_dom_api bench_dom_api.cpp)
|
add_executable(bench_dom_api bench_dom_api.cpp)
|
||||||
|
add_executable(bench_stream_formats bench_stream_formats.cpp)
|
||||||
if(SIMDJSON_EXCEPTIONS)
|
if(SIMDJSON_EXCEPTIONS)
|
||||||
add_executable(bench_ondemand bench_ondemand.cpp)
|
add_executable(bench_ondemand bench_ondemand.cpp)
|
||||||
if(TARGET yyjson)
|
if(TARGET yyjson)
|
||||||
|
|||||||
@@ -0,0 +1,216 @@
|
|||||||
|
#include <benchmark/benchmark.h>
|
||||||
|
#include <string>
|
||||||
|
#include "simdjson.h"
|
||||||
|
|
||||||
|
using namespace simdjson;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
enum class stream_case {
|
||||||
|
ndjson_small,
|
||||||
|
ndjson_large,
|
||||||
|
rfc7464_small,
|
||||||
|
rfc7464_large,
|
||||||
|
comma_delimited_small,
|
||||||
|
comma_delimited_large
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr size_t TARGET_BYTES = 128 * 1000 * 1000;
|
||||||
|
constexpr size_t SMALL_PAYLOAD = 16;
|
||||||
|
constexpr size_t LARGE_PAYLOAD = 4096;
|
||||||
|
constexpr size_t BATCH_SIZE = 1 << 20;
|
||||||
|
|
||||||
|
struct stream_dataset {
|
||||||
|
padded_string json;
|
||||||
|
size_t count{};
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string make_document(size_t id, size_t payload_size) {
|
||||||
|
return std::string{"{\"id\":"} + std::to_string(id) +
|
||||||
|
",\"name\":\"aaaaaaaa\",\"payload\":\"" +
|
||||||
|
std::string(payload_size, 'x') + "\",\"flag\":true}";
|
||||||
|
}
|
||||||
|
|
||||||
|
stream_dataset build_dataset(stream_case which) {
|
||||||
|
const bool small = which == stream_case::ndjson_small ||
|
||||||
|
which == stream_case::rfc7464_small ||
|
||||||
|
which == stream_case::comma_delimited_small;
|
||||||
|
const bool rfc = which == stream_case::rfc7464_small ||
|
||||||
|
which == stream_case::rfc7464_large;
|
||||||
|
const bool comma = which == stream_case::comma_delimited_small ||
|
||||||
|
which == stream_case::comma_delimited_large;
|
||||||
|
const size_t payload_size = small ? SMALL_PAYLOAD : LARGE_PAYLOAD;
|
||||||
|
const size_t count = TARGET_BYTES / (payload_size + 48);
|
||||||
|
std::string out;
|
||||||
|
out.reserve(count * (payload_size + 64));
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
if (rfc) {
|
||||||
|
out += char(0x1E);
|
||||||
|
}
|
||||||
|
if (comma && i > 0) {
|
||||||
|
out += ',';
|
||||||
|
}
|
||||||
|
out += make_document(i, payload_size);
|
||||||
|
if (!comma) {
|
||||||
|
out += '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {padded_string(out), count};
|
||||||
|
}
|
||||||
|
|
||||||
|
const stream_dataset &get_dataset(stream_case which) {
|
||||||
|
static const stream_dataset ndjson_small =
|
||||||
|
build_dataset(stream_case::ndjson_small);
|
||||||
|
static const stream_dataset ndjson_large =
|
||||||
|
build_dataset(stream_case::ndjson_large);
|
||||||
|
static const stream_dataset rfc_small =
|
||||||
|
build_dataset(stream_case::rfc7464_small);
|
||||||
|
static const stream_dataset rfc_large =
|
||||||
|
build_dataset(stream_case::rfc7464_large);
|
||||||
|
static const stream_dataset comma_small =
|
||||||
|
build_dataset(stream_case::comma_delimited_small);
|
||||||
|
static const stream_dataset comma_large =
|
||||||
|
build_dataset(stream_case::comma_delimited_large);
|
||||||
|
switch (which) {
|
||||||
|
case stream_case::ndjson_small:
|
||||||
|
return ndjson_small;
|
||||||
|
case stream_case::ndjson_large:
|
||||||
|
return ndjson_large;
|
||||||
|
case stream_case::rfc7464_small:
|
||||||
|
return rfc_small;
|
||||||
|
case stream_case::rfc7464_large:
|
||||||
|
return rfc_large;
|
||||||
|
case stream_case::comma_delimited_small:
|
||||||
|
return comma_small;
|
||||||
|
case stream_case::comma_delimited_large:
|
||||||
|
return comma_large;
|
||||||
|
}
|
||||||
|
return ndjson_small;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_counters(benchmark::State &state, const stream_dataset &dataset) {
|
||||||
|
state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(dataset.json.size()));
|
||||||
|
state.SetItemsProcessed(int64_t(state.iterations()) * int64_t(dataset.count));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <stream_case which, bool threaded = true>
|
||||||
|
static void bench_ondemand(benchmark::State &state) {
|
||||||
|
const auto &dataset = get_dataset(which);
|
||||||
|
ondemand::parser parser;
|
||||||
|
parser.threaded = threaded;
|
||||||
|
stream_format format = stream_format::whitespace_delimited;
|
||||||
|
if constexpr (which == stream_case::rfc7464_small ||
|
||||||
|
which == stream_case::rfc7464_large) {
|
||||||
|
format = stream_format::json_sequence;
|
||||||
|
} else if constexpr (which == stream_case::comma_delimited_small ||
|
||||||
|
which == stream_case::comma_delimited_large) {
|
||||||
|
format = stream_format::comma_delimited;
|
||||||
|
}
|
||||||
|
for (const auto _ : state) {
|
||||||
|
ondemand::document_stream docs;
|
||||||
|
auto error = parser.iterate_many(dataset.json, BATCH_SIZE, format).get(docs);
|
||||||
|
if (error) {
|
||||||
|
state.SkipWithError(error_message(error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint64_t sum = 0;
|
||||||
|
for (auto doc : docs) {
|
||||||
|
ondemand::object obj;
|
||||||
|
if ((error = doc.get_object().get(obj))) {
|
||||||
|
state.SkipWithError(error_message(error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint64_t id;
|
||||||
|
if ((error = obj["id"].get_uint64().get(id))) {
|
||||||
|
state.SkipWithError(error_message(error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sum += id;
|
||||||
|
}
|
||||||
|
benchmark::DoNotOptimize(sum);
|
||||||
|
}
|
||||||
|
set_counters(state, dataset);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <stream_case which>
|
||||||
|
static void bench_dom(benchmark::State &state) {
|
||||||
|
const auto &dataset = get_dataset(which);
|
||||||
|
dom::parser parser;
|
||||||
|
parser.threaded = true;
|
||||||
|
stream_format format = stream_format::whitespace_delimited;
|
||||||
|
if constexpr (which == stream_case::rfc7464_small ||
|
||||||
|
which == stream_case::rfc7464_large) {
|
||||||
|
format = stream_format::json_sequence;
|
||||||
|
} else if constexpr (which == stream_case::comma_delimited_small ||
|
||||||
|
which == stream_case::comma_delimited_large) {
|
||||||
|
format = stream_format::comma_delimited;
|
||||||
|
}
|
||||||
|
for (const auto _ : state) {
|
||||||
|
dom::document_stream docs;
|
||||||
|
auto error = parser.parse_many(dataset.json, BATCH_SIZE, format).get(docs);
|
||||||
|
if (error) {
|
||||||
|
state.SkipWithError(error_message(error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint64_t sum = 0;
|
||||||
|
for (auto doc : docs) {
|
||||||
|
uint64_t id;
|
||||||
|
if ((error = doc["id"].get(id))) {
|
||||||
|
state.SkipWithError(error_message(error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sum += id;
|
||||||
|
}
|
||||||
|
benchmark::DoNotOptimize(sum);
|
||||||
|
}
|
||||||
|
set_counters(state, dataset);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
BENCHMARK(bench_ondemand<stream_case::ndjson_small>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_ondemand<stream_case::ndjson_large>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_ondemand<stream_case::rfc7464_small>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_ondemand<stream_case::rfc7464_large>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_ondemand<stream_case::comma_delimited_small>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_ondemand<stream_case::comma_delimited_large>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
// Non-threaded comma_delimited for comparison
|
||||||
|
BENCHMARK(bench_ondemand<stream_case::comma_delimited_small, false>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_ondemand<stream_case::comma_delimited_large, false>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
|
||||||
|
BENCHMARK(bench_dom<stream_case::ndjson_small>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_dom<stream_case::ndjson_large>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_dom<stream_case::rfc7464_small>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_dom<stream_case::rfc7464_large>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_dom<stream_case::comma_delimited_small>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
BENCHMARK(bench_dom<stream_case::comma_delimited_large>)
|
||||||
|
->UseRealTime()
|
||||||
|
->DisplayAggregatesOnly(true);
|
||||||
|
|
||||||
|
BENCHMARK_MAIN();
|
||||||
@@ -10,7 +10,7 @@ namespace partial_tweets {
|
|||||||
// {
|
// {
|
||||||
// "created_at": "Sun Aug 31 00:29:15 +0000 2014",
|
// "created_at": "Sun Aug 31 00:29:15 +0000 2014",
|
||||||
// "id": 505874924095815700,
|
// "id": 505874924095815700,
|
||||||
// "text": "@aym0566x \n\n名前:前田あゆみ\n第一印象:なんか怖っ!\n今の印象:とりあえずキモい。噛み合わない\n好きなところ:ぶすでキモいとこ😋✨✨\n思い出:んーーー、ありすぎ😊❤️\nLINE交換できる?:あぁ……ごめん✋\nトプ画をみて:照れますがな😘✨\n一言:お前は一生もんのダチ💖",
|
// "text": "@aym0566x ...",
|
||||||
// "in_reply_to_status_id": null,
|
// "in_reply_to_status_id": null,
|
||||||
// "user": {
|
// "user": {
|
||||||
// "id": 1186275104,
|
// "id": 1186275104,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ CPMAddPackage(
|
|||||||
|
|
||||||
option(SIMDJSON_USE_RUST "Build the static_reflect benchmark" OFF)
|
option(SIMDJSON_USE_RUST "Build the static_reflect benchmark" OFF)
|
||||||
|
|
||||||
if(SIMDJSON_USER_RUST)
|
if(SIMDJSON_USE_RUST)
|
||||||
if(NOT WIN32)
|
if(NOT WIN32)
|
||||||
# We want the check whether Rust is available before trying to build a crate.
|
# We want the check whether Rust is available before trying to build a crate.
|
||||||
CPMAddPackage(
|
CPMAddPackage(
|
||||||
@@ -39,9 +39,9 @@ if(SIMDJSON_USER_RUST)
|
|||||||
message(STATUS "curl https://sh.rustup.rs -sSf | sh")
|
message(STATUS "curl https://sh.rustup.rs -sSf | sh")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
else(SIMDJSON_USER_RUST)
|
else(SIMDJSON_USE_RUST)
|
||||||
message(STATUS "We will not benchmark serde-benchmark." )
|
message(STATUS "We will not benchmark serde-benchmark." )
|
||||||
endif(SIMDJSON_USER_RUST)
|
endif(SIMDJSON_USE_RUST)
|
||||||
|
|
||||||
# Add the benchmark executable targets
|
# Add the benchmark executable targets
|
||||||
add_subdirectory(twitter_benchmark)
|
add_subdirectory(twitter_benchmark)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
add_executable(benchmark_serialization_citm_catalog benchmark_serialization_citm_catalog.cpp)
|
add_executable(benchmark_serialization_citm_catalog benchmark_serialization_citm_catalog.cpp)
|
||||||
|
add_executable(benchmark_parsing_citm benchmark_parsing_citm.cpp)
|
||||||
|
|
||||||
# Link with Rust benchmarking code if available
|
# Link with Rust benchmarking code if available
|
||||||
if(TARGET serde-benchmark)
|
if(TARGET serde-benchmark)
|
||||||
@@ -11,4 +12,29 @@ target_link_libraries(benchmark_serialization_citm_catalog PRIVATE simdjson::sim
|
|||||||
target_link_libraries(benchmark_serialization_citm_catalog PRIVATE reflectcpp)
|
target_link_libraries(benchmark_serialization_citm_catalog PRIVATE reflectcpp)
|
||||||
target_compile_definitions(benchmark_serialization_citm_catalog PRIVATE SIMDJSON_BENCH_CPP_REFLECT=1)
|
target_compile_definitions(benchmark_serialization_citm_catalog PRIVATE SIMDJSON_BENCH_CPP_REFLECT=1)
|
||||||
|
|
||||||
|
if(TARGET yyjson)
|
||||||
|
target_link_libraries(benchmark_serialization_citm_catalog PRIVATE yyjson)
|
||||||
|
target_compile_definitions(benchmark_serialization_citm_catalog PRIVATE SIMDJSON_COMPETITION_YYJSON)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_compile_definitions(benchmark_serialization_citm_catalog PRIVATE JSON_FILE="${BENCH_CITM_JSON}")
|
target_compile_definitions(benchmark_serialization_citm_catalog PRIVATE JSON_FILE="${BENCH_CITM_JSON}")
|
||||||
|
|
||||||
|
# Configuration for parsing benchmark
|
||||||
|
if(TARGET serde-benchmark)
|
||||||
|
target_link_libraries(benchmark_parsing_citm PRIVATE serde-benchmark)
|
||||||
|
target_compile_definitions(benchmark_parsing_citm PRIVATE SIMDJSON_RUST_VERSION="${Rust_VERSION}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(benchmark_parsing_citm PRIVATE simdjson::simdjson nlohmann_json)
|
||||||
|
|
||||||
|
if(TARGET rapidjson)
|
||||||
|
target_link_libraries(benchmark_parsing_citm PRIVATE rapidjson)
|
||||||
|
target_compile_definitions(benchmark_parsing_citm PRIVATE SIMDJSON_COMPETITION_RAPIDJSON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(TARGET yyjson)
|
||||||
|
target_link_libraries(benchmark_parsing_citm PRIVATE yyjson)
|
||||||
|
target_compile_definitions(benchmark_parsing_citm PRIVATE SIMDJSON_COMPETITION_YYJSON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_compile_definitions(benchmark_parsing_citm PRIVATE JSON_FILE="${BENCH_CITM_JSON}")
|
||||||
@@ -0,0 +1,563 @@
|
|||||||
|
#include <cassert>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <ctime>
|
||||||
|
#include <format>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <simdjson.h>
|
||||||
|
#include <string>
|
||||||
|
#include "citm_catalog_data.h"
|
||||||
|
#include "nlohmann_citm_catalog_data.h"
|
||||||
|
#include "../benchmark_utils/benchmark_helper.h"
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
|
||||||
|
#include "rapidjson_citm_catalog_data.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_YYJSON
|
||||||
|
#include "yyjson_citm_catalog_data.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_RUST_VERSION
|
||||||
|
#include "../serde-benchmark/serde_benchmark.h"
|
||||||
|
|
||||||
|
void bench_rust_parsing(const std::string &json_str) {
|
||||||
|
size_t input_volume = json_str.size();
|
||||||
|
printf("# input volume: %zu bytes\n", input_volume);
|
||||||
|
|
||||||
|
volatile bool result = true;
|
||||||
|
pretty_print(1, input_volume, "bench_rust_parsing",
|
||||||
|
bench([&json_str, &result]() {
|
||||||
|
serde_benchmark::CitmCatalog *catalog = serde_benchmark::citm_from_str(json_str.c_str(), json_str.size());
|
||||||
|
result = (catalog != nullptr);
|
||||||
|
if (catalog) {
|
||||||
|
serde_benchmark::free_citm(catalog);
|
||||||
|
}
|
||||||
|
if (!result) {
|
||||||
|
printf("parse error\n");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <class T> void bench_simdjson_static_reflection_parsing(const std::string &json_str) {
|
||||||
|
size_t input_volume = json_str.size();
|
||||||
|
printf("# input volume: %zu bytes\n", input_volume);
|
||||||
|
|
||||||
|
// Pre-allocate padded buffer outside the benchmark loop
|
||||||
|
std::string mutable_json = json_str;
|
||||||
|
simdjson::pad(mutable_json);
|
||||||
|
|
||||||
|
volatile bool result = true;
|
||||||
|
pretty_print(1, input_volume, "bench_simdjson_static_reflection_parsing",
|
||||||
|
bench([&mutable_json, &result]() {
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc;
|
||||||
|
if(parser.iterate(mutable_json).get(doc)) {
|
||||||
|
result = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
T my_struct;
|
||||||
|
if(doc.get<T>().get(my_struct)) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
if (!result) {
|
||||||
|
printf("parse error\n");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
|
template <class T> void bench_simdjson_from_parsing(const std::string &json_str) {
|
||||||
|
size_t input_volume = json_str.size();
|
||||||
|
printf("# input volume: %zu bytes\n", input_volume);
|
||||||
|
|
||||||
|
// Pre-allocate padded buffer outside the benchmark loop
|
||||||
|
simdjson::padded_string padded = simdjson::padded_string(json_str);
|
||||||
|
|
||||||
|
volatile bool result = true;
|
||||||
|
pretty_print(1, input_volume, "bench_simdjson_from_parsing",
|
||||||
|
bench([&padded, &result]() {
|
||||||
|
T my_struct;
|
||||||
|
auto err = simdjson::from(padded).get(my_struct);
|
||||||
|
if (err) {
|
||||||
|
result = false;
|
||||||
|
printf("parse error: %s\n", simdjson::error_message(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// nlohmann::json deserialization functions
|
||||||
|
void from_json(const nlohmann::json &j, CITMPrice &p) {
|
||||||
|
j.at("amount").get_to(p.amount);
|
||||||
|
j.at("audienceSubCategoryId").get_to(p.audienceSubCategoryId);
|
||||||
|
j.at("seatCategoryId").get_to(p.seatCategoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void from_json(const nlohmann::json &j, CITMArea &a) {
|
||||||
|
j.at("areaId").get_to(a.areaId);
|
||||||
|
j.at("blockIds").get_to(a.blockIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
void from_json(const nlohmann::json &j, CITMSeatCategory &s) {
|
||||||
|
j.at("areas").get_to(s.areas);
|
||||||
|
j.at("seatCategoryId").get_to(s.seatCategoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void from_json(const nlohmann::json &j, CITMPerformance &p) {
|
||||||
|
j.at("id").get_to(p.id);
|
||||||
|
j.at("eventId").get_to(p.eventId);
|
||||||
|
if (j.contains("logo") && !j["logo"].is_null()) {
|
||||||
|
p.logo = j["logo"].get<std::string>();
|
||||||
|
}
|
||||||
|
if (j.contains("name") && !j["name"].is_null()) {
|
||||||
|
p.name = j["name"].get<std::string>();
|
||||||
|
}
|
||||||
|
j.at("prices").get_to(p.prices);
|
||||||
|
j.at("seatCategories").get_to(p.seatCategories);
|
||||||
|
if (j.contains("seatMapImage") && !j["seatMapImage"].is_null()) {
|
||||||
|
p.seatMapImage = j["seatMapImage"].get<std::string>();
|
||||||
|
}
|
||||||
|
j.at("start").get_to(p.start);
|
||||||
|
j.at("venueCode").get_to(p.venueCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void from_json(const nlohmann::json &j, CITMEvent &e) {
|
||||||
|
j.at("id").get_to(e.id);
|
||||||
|
j.at("name").get_to(e.name);
|
||||||
|
if (j.contains("description") && !j["description"].is_null()) {
|
||||||
|
e.description = j["description"].get<std::string>();
|
||||||
|
}
|
||||||
|
if (j.contains("logo") && !j["logo"].is_null()) {
|
||||||
|
e.logo = j["logo"].get<std::string>();
|
||||||
|
}
|
||||||
|
j.at("subTopicIds").get_to(e.subTopicIds);
|
||||||
|
if (j.contains("subjectCode") && !j["subjectCode"].is_null()) {
|
||||||
|
e.subjectCode = j["subjectCode"].get<std::string>();
|
||||||
|
}
|
||||||
|
if (j.contains("subtitle") && !j["subtitle"].is_null()) {
|
||||||
|
e.subtitle = j["subtitle"].get<std::string>();
|
||||||
|
}
|
||||||
|
j.at("topicIds").get_to(e.topicIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
void from_json(const nlohmann::json &j, CitmCatalog &c) {
|
||||||
|
j.at("events").get_to(c.events);
|
||||||
|
j.at("performances").get_to(c.performances);
|
||||||
|
}
|
||||||
|
|
||||||
|
CitmCatalog nlohmann_deserialize(const std::string &json_str) {
|
||||||
|
nlohmann::json j = nlohmann::json::parse(json_str);
|
||||||
|
return j.get<CitmCatalog>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_nlohmann_parsing(const std::string &json_str) {
|
||||||
|
size_t input_volume = json_str.size();
|
||||||
|
printf("# input volume: %zu bytes\n", input_volume);
|
||||||
|
|
||||||
|
volatile bool result = true;
|
||||||
|
pretty_print(1, input_volume, "bench_nlohmann_parsing",
|
||||||
|
bench([&json_str, &result]() {
|
||||||
|
try {
|
||||||
|
CitmCatalog data = nlohmann_deserialize(json_str);
|
||||||
|
result = true;
|
||||||
|
} catch (...) {
|
||||||
|
result = false;
|
||||||
|
printf("parse error\n");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
|
||||||
|
CitmCatalog rapidjson_deserialize(const std::string &json_str) {
|
||||||
|
rapidjson::Document doc;
|
||||||
|
doc.Parse(json_str.c_str());
|
||||||
|
|
||||||
|
if (doc.HasParseError()) {
|
||||||
|
throw std::runtime_error("RapidJSON parse error");
|
||||||
|
}
|
||||||
|
|
||||||
|
CitmCatalog catalog;
|
||||||
|
|
||||||
|
// Parse events
|
||||||
|
if (doc.HasMember("events") && doc["events"].IsObject()) {
|
||||||
|
for (auto& m : doc["events"].GetObject()) {
|
||||||
|
CITMEvent event;
|
||||||
|
const auto& e = m.value;
|
||||||
|
|
||||||
|
event.id = e["id"].GetUint64();
|
||||||
|
event.name = e["name"].GetString();
|
||||||
|
if (e.HasMember("description") && !e["description"].IsNull()) {
|
||||||
|
event.description = e["description"].GetString();
|
||||||
|
}
|
||||||
|
if (e.HasMember("logo") && !e["logo"].IsNull()) {
|
||||||
|
event.logo = e["logo"].GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
event.subTopicIds.clear();
|
||||||
|
for (auto& id : e["subTopicIds"].GetArray()) {
|
||||||
|
event.subTopicIds.push_back(id.GetUint64());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.HasMember("subjectCode") && !e["subjectCode"].IsNull()) {
|
||||||
|
event.subjectCode = e["subjectCode"].GetString();
|
||||||
|
}
|
||||||
|
if (e.HasMember("subtitle") && !e["subtitle"].IsNull()) {
|
||||||
|
event.subtitle = e["subtitle"].GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
event.topicIds.clear();
|
||||||
|
for (auto& id : e["topicIds"].GetArray()) {
|
||||||
|
event.topicIds.push_back(id.GetUint64());
|
||||||
|
}
|
||||||
|
|
||||||
|
catalog.events[m.name.GetString()] = event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse performances
|
||||||
|
if (doc.HasMember("performances") && doc["performances"].IsArray()) {
|
||||||
|
for (auto& p : doc["performances"].GetArray()) {
|
||||||
|
CITMPerformance perf;
|
||||||
|
|
||||||
|
perf.id = p["id"].GetUint64();
|
||||||
|
perf.eventId = p["eventId"].GetUint64();
|
||||||
|
if (p.HasMember("logo") && !p["logo"].IsNull()) {
|
||||||
|
perf.logo = p["logo"].GetString();
|
||||||
|
}
|
||||||
|
if (p.HasMember("name") && !p["name"].IsNull()) {
|
||||||
|
perf.name = p["name"].GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse prices
|
||||||
|
for (auto& price : p["prices"].GetArray()) {
|
||||||
|
CITMPrice pr;
|
||||||
|
pr.amount = price["amount"].GetUint64();
|
||||||
|
pr.audienceSubCategoryId = price["audienceSubCategoryId"].GetUint64();
|
||||||
|
pr.seatCategoryId = price["seatCategoryId"].GetUint64();
|
||||||
|
perf.prices.push_back(pr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse seat categories
|
||||||
|
for (auto& sc : p["seatCategories"].GetArray()) {
|
||||||
|
CITMSeatCategory seatCat;
|
||||||
|
seatCat.seatCategoryId = sc["seatCategoryId"].GetUint64();
|
||||||
|
|
||||||
|
for (auto& area : sc["areas"].GetArray()) {
|
||||||
|
CITMArea ar;
|
||||||
|
ar.areaId = area["areaId"].GetUint64();
|
||||||
|
for (auto& block : area["blockIds"].GetArray()) {
|
||||||
|
ar.blockIds.push_back(block.GetUint64());
|
||||||
|
}
|
||||||
|
seatCat.areas.push_back(ar);
|
||||||
|
}
|
||||||
|
perf.seatCategories.push_back(seatCat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.HasMember("seatMapImage") && !p["seatMapImage"].IsNull()) {
|
||||||
|
perf.seatMapImage = p["seatMapImage"].GetString();
|
||||||
|
}
|
||||||
|
perf.start = p["start"].GetUint64();
|
||||||
|
perf.venueCode = p["venueCode"].GetString();
|
||||||
|
|
||||||
|
catalog.performances.push_back(perf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return catalog;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_rapidjson_parsing(const std::string &json_str) {
|
||||||
|
size_t input_volume = json_str.size();
|
||||||
|
printf("# input volume: %zu bytes\n", input_volume);
|
||||||
|
|
||||||
|
volatile bool result = true;
|
||||||
|
pretty_print(1, input_volume, "bench_rapidjson_parsing",
|
||||||
|
bench([&json_str, &result]() {
|
||||||
|
try {
|
||||||
|
CitmCatalog data = rapidjson_deserialize(json_str);
|
||||||
|
result = true;
|
||||||
|
} catch (...) {
|
||||||
|
result = false;
|
||||||
|
printf("parse error\n");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_YYJSON
|
||||||
|
CitmCatalog yyjson_deserialize(const std::string &json_str) {
|
||||||
|
yyjson_doc *doc = yyjson_read(json_str.c_str(), json_str.size(), 0);
|
||||||
|
if (!doc) {
|
||||||
|
throw std::runtime_error("YYJson parse error");
|
||||||
|
}
|
||||||
|
|
||||||
|
yyjson_val *root = yyjson_doc_get_root(doc);
|
||||||
|
CitmCatalog catalog;
|
||||||
|
|
||||||
|
// Parse events
|
||||||
|
yyjson_val *events = yyjson_obj_get(root, "events");
|
||||||
|
if (events) {
|
||||||
|
size_t idx, max;
|
||||||
|
yyjson_val *key, *val;
|
||||||
|
yyjson_obj_foreach(events, idx, max, key, val) {
|
||||||
|
CITMEvent event;
|
||||||
|
|
||||||
|
event.id = yyjson_get_uint(yyjson_obj_get(val, "id"));
|
||||||
|
const char* name = yyjson_get_str(yyjson_obj_get(val, "name"));
|
||||||
|
if (name) event.name = name;
|
||||||
|
|
||||||
|
yyjson_val *desc = yyjson_obj_get(val, "description");
|
||||||
|
if (desc && !yyjson_is_null(desc)) {
|
||||||
|
const char* str = yyjson_get_str(desc);
|
||||||
|
if (str) event.description = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
yyjson_val *logo = yyjson_obj_get(val, "logo");
|
||||||
|
if (logo && !yyjson_is_null(logo)) {
|
||||||
|
const char* str = yyjson_get_str(logo);
|
||||||
|
if (str) event.logo = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
yyjson_val *subTopics = yyjson_obj_get(val, "subTopicIds");
|
||||||
|
if (subTopics) {
|
||||||
|
size_t sidx, smax;
|
||||||
|
yyjson_val *sval;
|
||||||
|
yyjson_arr_foreach(subTopics, sidx, smax, sval) {
|
||||||
|
event.subTopicIds.push_back(yyjson_get_uint(sval));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yyjson_val *subjectCode = yyjson_obj_get(val, "subjectCode");
|
||||||
|
if (subjectCode && !yyjson_is_null(subjectCode)) {
|
||||||
|
const char* str = yyjson_get_str(subjectCode);
|
||||||
|
if (str) event.subjectCode = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
yyjson_val *subtitle = yyjson_obj_get(val, "subtitle");
|
||||||
|
if (subtitle && !yyjson_is_null(subtitle)) {
|
||||||
|
const char* str = yyjson_get_str(subtitle);
|
||||||
|
if (str) event.subtitle = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
yyjson_val *topics = yyjson_obj_get(val, "topicIds");
|
||||||
|
if (topics) {
|
||||||
|
size_t tidx, tmax;
|
||||||
|
yyjson_val *tval;
|
||||||
|
yyjson_arr_foreach(topics, tidx, tmax, tval) {
|
||||||
|
event.topicIds.push_back(yyjson_get_uint(tval));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* keyStr = yyjson_get_str(key);
|
||||||
|
if (keyStr) {
|
||||||
|
catalog.events[keyStr] = event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse performances
|
||||||
|
yyjson_val *performances = yyjson_obj_get(root, "performances");
|
||||||
|
if (performances) {
|
||||||
|
size_t idx, max;
|
||||||
|
yyjson_val *val;
|
||||||
|
yyjson_arr_foreach(performances, idx, max, val) {
|
||||||
|
CITMPerformance perf;
|
||||||
|
|
||||||
|
perf.id = yyjson_get_uint(yyjson_obj_get(val, "id"));
|
||||||
|
perf.eventId = yyjson_get_uint(yyjson_obj_get(val, "eventId"));
|
||||||
|
|
||||||
|
yyjson_val *logo = yyjson_obj_get(val, "logo");
|
||||||
|
if (logo && !yyjson_is_null(logo)) {
|
||||||
|
const char* str = yyjson_get_str(logo);
|
||||||
|
if (str) perf.logo = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
yyjson_val *name = yyjson_obj_get(val, "name");
|
||||||
|
if (name && !yyjson_is_null(name)) {
|
||||||
|
const char* str = yyjson_get_str(name);
|
||||||
|
if (str) perf.name = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse prices
|
||||||
|
yyjson_val *prices = yyjson_obj_get(val, "prices");
|
||||||
|
if (prices) {
|
||||||
|
size_t pidx, pmax;
|
||||||
|
yyjson_val *pval;
|
||||||
|
yyjson_arr_foreach(prices, pidx, pmax, pval) {
|
||||||
|
CITMPrice price;
|
||||||
|
price.amount = yyjson_get_uint(yyjson_obj_get(pval, "amount"));
|
||||||
|
price.audienceSubCategoryId = yyjson_get_uint(yyjson_obj_get(pval, "audienceSubCategoryId"));
|
||||||
|
price.seatCategoryId = yyjson_get_uint(yyjson_obj_get(pval, "seatCategoryId"));
|
||||||
|
perf.prices.push_back(price);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse seat categories
|
||||||
|
yyjson_val *seatCats = yyjson_obj_get(val, "seatCategories");
|
||||||
|
if (seatCats) {
|
||||||
|
size_t scidx, scmax;
|
||||||
|
yyjson_val *scval;
|
||||||
|
yyjson_arr_foreach(seatCats, scidx, scmax, scval) {
|
||||||
|
CITMSeatCategory seatCat;
|
||||||
|
seatCat.seatCategoryId = yyjson_get_uint(yyjson_obj_get(scval, "seatCategoryId"));
|
||||||
|
|
||||||
|
yyjson_val *areas = yyjson_obj_get(scval, "areas");
|
||||||
|
if (areas) {
|
||||||
|
size_t aidx, amax;
|
||||||
|
yyjson_val *aval;
|
||||||
|
yyjson_arr_foreach(areas, aidx, amax, aval) {
|
||||||
|
CITMArea area;
|
||||||
|
area.areaId = yyjson_get_uint(yyjson_obj_get(aval, "areaId"));
|
||||||
|
|
||||||
|
yyjson_val *blocks = yyjson_obj_get(aval, "blockIds");
|
||||||
|
if (blocks) {
|
||||||
|
size_t bidx, bmax;
|
||||||
|
yyjson_val *bval;
|
||||||
|
yyjson_arr_foreach(blocks, bidx, bmax, bval) {
|
||||||
|
area.blockIds.push_back(yyjson_get_uint(bval));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
seatCat.areas.push_back(area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
perf.seatCategories.push_back(seatCat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yyjson_val *seatMapImage = yyjson_obj_get(val, "seatMapImage");
|
||||||
|
if (seatMapImage && !yyjson_is_null(seatMapImage)) {
|
||||||
|
const char* str = yyjson_get_str(seatMapImage);
|
||||||
|
if (str) perf.seatMapImage = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
perf.start = yyjson_get_uint(yyjson_obj_get(val, "start"));
|
||||||
|
const char* venueCode = yyjson_get_str(yyjson_obj_get(val, "venueCode"));
|
||||||
|
if (venueCode) perf.venueCode = venueCode;
|
||||||
|
|
||||||
|
catalog.performances.push_back(perf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yyjson_doc_free(doc);
|
||||||
|
return catalog;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_yyjson_parsing(const std::string &json_str) {
|
||||||
|
size_t input_volume = json_str.size();
|
||||||
|
printf("# input volume: %zu bytes\n", input_volume);
|
||||||
|
|
||||||
|
volatile bool result = true;
|
||||||
|
pretty_print(1, input_volume, "bench_yyjson_parsing",
|
||||||
|
bench([&json_str, &result]() {
|
||||||
|
try {
|
||||||
|
CitmCatalog data = yyjson_deserialize(json_str);
|
||||||
|
result = true;
|
||||||
|
} catch (...) {
|
||||||
|
result = false;
|
||||||
|
printf("parse error\n");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::string read_file(std::string filename) {
|
||||||
|
printf("# Reading file %s\n", filename.c_str());
|
||||||
|
constexpr size_t read_size = 4096;
|
||||||
|
auto stream = std::ifstream(filename);
|
||||||
|
stream.exceptions(std::ios_base::badbit);
|
||||||
|
|
||||||
|
if (!stream) {
|
||||||
|
std::cerr << "Error: Failed to open file " << filename << std::endl;
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string out;
|
||||||
|
auto buf = std::string(read_size, '\0');
|
||||||
|
while (stream.read(&buf[0], read_size)) {
|
||||||
|
out.append(buf, 0, size_t(stream.gcount()));
|
||||||
|
}
|
||||||
|
out.append(buf, 0, size_t(stream.gcount()));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to check if benchmark name matches any of the comma-separated filters
|
||||||
|
bool matches_filter(const std::string& benchmark_name, const std::string& filter) {
|
||||||
|
if (filter.empty()) return true;
|
||||||
|
|
||||||
|
// Split filter by comma
|
||||||
|
size_t start = 0;
|
||||||
|
size_t end = filter.find(',');
|
||||||
|
while (end != std::string::npos) {
|
||||||
|
std::string token = filter.substr(start, end - start);
|
||||||
|
if (benchmark_name.find(token) != std::string::npos) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
start = end + 1;
|
||||||
|
end = filter.find(',', start);
|
||||||
|
}
|
||||||
|
// Check last token
|
||||||
|
std::string token = filter.substr(start);
|
||||||
|
return benchmark_name.find(token) != std::string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
// Get the JSON file path from preprocessor or use default
|
||||||
|
std::string filename;
|
||||||
|
#ifdef JSON_FILE
|
||||||
|
filename = JSON_FILE;
|
||||||
|
#else
|
||||||
|
filename = "jsonexamples/citm_catalog.json";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::string json_str = read_file(filename);
|
||||||
|
|
||||||
|
// Parse command-line arguments for filter
|
||||||
|
std::string filter;
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
std::string arg = argv[i];
|
||||||
|
if (arg == "-f" && i + 1 < argc) {
|
||||||
|
filter = argv[i + 1];
|
||||||
|
printf("# Filter: %s\n", filter.c_str());
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no filter provided, run all benchmarks
|
||||||
|
if (filter.empty()) {
|
||||||
|
printf("# Running all benchmarks (use -f <filter> to run specific ones)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmarking the parsing
|
||||||
|
if (matches_filter("nlohmann", filter)) {
|
||||||
|
bench_nlohmann_parsing(json_str);
|
||||||
|
}
|
||||||
|
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
|
||||||
|
if (matches_filter("rapidjson", filter)) {
|
||||||
|
bench_rapidjson_parsing(json_str);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef SIMDJSON_COMPETITION_YYJSON
|
||||||
|
if (matches_filter("yyjson", filter)) {
|
||||||
|
bench_yyjson_parsing(json_str);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (matches_filter("simdjson_static_reflection", filter)) {
|
||||||
|
bench_simdjson_static_reflection_parsing<CitmCatalog>(json_str);
|
||||||
|
}
|
||||||
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
|
if (matches_filter("simdjson_from", filter)) {
|
||||||
|
bench_simdjson_from_parsing<CitmCatalog>(json_str);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef SIMDJSON_RUST_VERSION
|
||||||
|
if (matches_filter("rust", filter)) {
|
||||||
|
printf("# Note: Rust/Serde parsing test\n");
|
||||||
|
bench_rust_parsing(json_str);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
+160
-22
@@ -11,6 +11,10 @@
|
|||||||
#include "nlohmann_citm_catalog_data.h"
|
#include "nlohmann_citm_catalog_data.h"
|
||||||
#include "../benchmark_utils/benchmark_helper.h"
|
#include "../benchmark_utils/benchmark_helper.h"
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_YYJSON
|
||||||
|
#include "yyjson_citm_catalog_data.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if SIMDJSON_BENCH_CPP_REFLECT
|
#if SIMDJSON_BENCH_CPP_REFLECT
|
||||||
#include <rfl.hpp>
|
#include <rfl.hpp>
|
||||||
#include <rfl/json.hpp>
|
#include <rfl/json.hpp>
|
||||||
@@ -35,20 +39,15 @@ void bench_reflect_cpp(CitmCatalog &data) {
|
|||||||
#include "../serde-benchmark/serde_benchmark.h"
|
#include "../serde-benchmark/serde_benchmark.h"
|
||||||
|
|
||||||
void bench_rust(serde_benchmark::CitmCatalog *data) {
|
void bench_rust(serde_benchmark::CitmCatalog *data) {
|
||||||
const char * output = serde_benchmark::str_from_citm(data);
|
serde_benchmark::set_citm_data(data);
|
||||||
size_t output_volume = strlen(output);
|
size_t output_volume = serde_benchmark::serialize_citm_to_string();
|
||||||
printf("# output volume: %zu bytes\n", output_volume);
|
printf("# output volume: %zu bytes\n", output_volume);
|
||||||
|
|
||||||
volatile size_t measured_volume = 0;
|
volatile size_t measured_volume = 0;
|
||||||
pretty_print(1, output_volume, "bench_rust",
|
pretty_print(1, output_volume, "bench_rust",
|
||||||
bench([&data, &measured_volume, &output_volume]() {
|
bench([&measured_volume, &output_volume]() {
|
||||||
const char * output = serde_benchmark::str_from_citm(data);
|
measured_volume = serde_benchmark::serialize_citm_to_string();
|
||||||
measured_volume = strlen(output);
|
|
||||||
if (measured_volume != output_volume) {
|
|
||||||
printf("mismatch\n");
|
|
||||||
}
|
|
||||||
serde_benchmark::free_str(const_cast<char*>(output));
|
|
||||||
}));
|
}));
|
||||||
serde_benchmark::free_str(const_cast<char*>(output));
|
|
||||||
}
|
}
|
||||||
#endif // SIMDJSON_RUST_VERSION
|
#endif // SIMDJSON_RUST_VERSION
|
||||||
|
|
||||||
@@ -68,7 +67,55 @@ void bench_nlohmann(CitmCatalog &data) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_YYJSON
|
||||||
|
void bench_yyjson(CitmCatalog &data) {
|
||||||
|
std::string output = yyjson_serialize_citm(data);
|
||||||
|
size_t output_volume = output.size();
|
||||||
|
printf("# output volume: %zu bytes\n", output_volume);
|
||||||
|
|
||||||
|
volatile size_t measured_volume = 0;
|
||||||
|
pretty_print(1, output_volume, "bench_yyjson",
|
||||||
|
bench([&data, &measured_volume, &output_volume]() {
|
||||||
|
std::string output = yyjson_serialize_citm(data);
|
||||||
|
measured_volume = output.size();
|
||||||
|
if (measured_volume != output_volume) {
|
||||||
|
printf("mismatch\n");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Fair allocation variant: allocates fresh buffer each iteration (matches other libraries)
|
||||||
void bench_simdjson_static_reflection(CitmCatalog &data) {
|
void bench_simdjson_static_reflection(CitmCatalog &data) {
|
||||||
|
// First run to determine expected size
|
||||||
|
simdjson::builder::string_builder sb_init;
|
||||||
|
simdjson::builder::append(sb_init, data);
|
||||||
|
std::string_view p_init;
|
||||||
|
if(sb_init.view().get(p_init)) {
|
||||||
|
std::cerr << "Error!" << std::endl;
|
||||||
|
}
|
||||||
|
size_t output_volume = p_init.size();
|
||||||
|
printf("# output volume: %zu bytes\n", output_volume);
|
||||||
|
|
||||||
|
volatile size_t measured_volume = 0;
|
||||||
|
pretty_print(sizeof(data), output_volume, "bench_simdjson_static_reflection",
|
||||||
|
bench([&data, &measured_volume, &output_volume]() {
|
||||||
|
// Fresh allocation each iteration - fair comparison
|
||||||
|
simdjson::builder::string_builder sb;
|
||||||
|
simdjson::builder::append(sb, data);
|
||||||
|
std::string_view p;
|
||||||
|
if(sb.view().get(p)) {
|
||||||
|
std::cerr << "Error!" << std::endl;
|
||||||
|
}
|
||||||
|
measured_volume = sb.size();
|
||||||
|
if (measured_volume != output_volume) {
|
||||||
|
printf("mismatch\n");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimized variant: reuses buffer across iterations (shows API potential)
|
||||||
|
void bench_simdjson_static_reflection_reuse(CitmCatalog &data) {
|
||||||
simdjson::builder::string_builder sb;
|
simdjson::builder::string_builder sb;
|
||||||
simdjson::builder::append(sb, data);
|
simdjson::builder::append(sb, data);
|
||||||
std::string_view p;
|
std::string_view p;
|
||||||
@@ -80,7 +127,7 @@ void bench_simdjson_static_reflection(CitmCatalog &data) {
|
|||||||
printf("# output volume: %zu bytes\n", output_volume);
|
printf("# output volume: %zu bytes\n", output_volume);
|
||||||
|
|
||||||
volatile size_t measured_volume = 0;
|
volatile size_t measured_volume = 0;
|
||||||
pretty_print(sizeof(data), output_volume, "bench_simdjson_static_reflection",
|
pretty_print(sizeof(data), output_volume, "bench_simdjson_reuse_buffer",
|
||||||
bench([&data, &measured_volume, &output_volume, &sb]() {
|
bench([&data, &measured_volume, &output_volume, &sb]() {
|
||||||
sb.clear();
|
sb.clear();
|
||||||
simdjson::builder::append(sb, data);
|
simdjson::builder::append(sb, data);
|
||||||
@@ -95,25 +142,97 @@ void bench_simdjson_static_reflection(CitmCatalog &data) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string read_file(const std::string &file_path, size_t read_size = 65536) {
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
|
// Fair allocation variant: allocates fresh string each iteration
|
||||||
|
void bench_simdjson_to(CitmCatalog &data) {
|
||||||
|
// First run to determine size
|
||||||
|
std::string output_init;
|
||||||
|
if (simdjson::error_code err = simdjson::builder::to_json(data, output_init); err) {
|
||||||
|
std::cerr << "Error in to_json initialization!" << simdjson::error_message(err) << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t output_volume = output_init.size();
|
||||||
|
printf("# output volume: %zu bytes\n", output_volume);
|
||||||
|
|
||||||
|
volatile size_t measured_volume = 0;
|
||||||
|
pretty_print(sizeof(data), output_volume, "bench_simdjson_to",
|
||||||
|
bench([&data, &measured_volume, &output_volume]() {
|
||||||
|
// Fresh allocation each iteration - fair comparison
|
||||||
|
std::string output;
|
||||||
|
if (simdjson::error_code err = simdjson::builder::to_json(data, output); err) {
|
||||||
|
std::cerr << "Error in to_json!" << simdjson::error_message(err) << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
measured_volume = output.size();
|
||||||
|
if (measured_volume != output_volume) {
|
||||||
|
printf("mismatch\n");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimized variant: reuses pre-allocated string
|
||||||
|
void bench_simdjson_to_reuse(CitmCatalog &data) {
|
||||||
|
std::string output;
|
||||||
|
if (simdjson::error_code err = simdjson::builder::to_json(data, output); err) {
|
||||||
|
std::cerr << "Error in to_json initialization!" << simdjson::error_message(err) << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t output_volume = output.size();
|
||||||
|
printf("# output volume: %zu bytes\n", output_volume);
|
||||||
|
|
||||||
|
// Pre-allocate string with sufficient capacity to avoid reallocation
|
||||||
|
output.reserve(output_volume * 2);
|
||||||
|
|
||||||
|
volatile size_t measured_volume = 0;
|
||||||
|
pretty_print(sizeof(data), output_volume, "bench_simdjson_to_reuse",
|
||||||
|
bench([&data, &measured_volume, &output_volume, &output]() {
|
||||||
|
// Reuse the pre-allocated string - avoids allocation
|
||||||
|
if (simdjson::error_code err = simdjson::builder::to_json(data, output); err) {
|
||||||
|
std::cerr << "Error in to_json!" << simdjson::error_message(err) << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
measured_volume = output.size();
|
||||||
|
if (measured_volume != output_volume) {
|
||||||
|
printf("mismatch\n");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
simdjson::padded_string read_file(const std::string &file_path, size_t read_size = 65536) {
|
||||||
std::ifstream stream(file_path, std::ios::binary);
|
std::ifstream stream(file_path, std::ios::binary);
|
||||||
if(!stream) {
|
if(!stream) {
|
||||||
std::cerr << "Could not open file '" << file_path << "'" << std::endl;
|
std::cerr << "Could not open file '" << file_path << "'" << std::endl;
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
stream.exceptions(std::ios_base::badbit);
|
stream.exceptions(std::ios_base::badbit);
|
||||||
std::string out;
|
simdjson::padded_string_builder builder;
|
||||||
std::string buf(read_size, '\0');
|
std::string buf(read_size, '\0');
|
||||||
while (stream.read(&buf[0], read_size)) {
|
while (stream.read(&buf[0], read_size)) {
|
||||||
out.append(buf, 0, size_t(stream.gcount()));
|
builder.append(buf.data(), size_t(stream.gcount()));
|
||||||
}
|
}
|
||||||
out.append(buf, 0, size_t(stream.gcount()));
|
builder.append(buf.data(), size_t(stream.gcount()));
|
||||||
return out;
|
return builder.convert();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to check if benchmark name contains filter substring
|
// Function to check if benchmark name matches any of the comma-separated filters
|
||||||
bool matches_filter(const std::string& benchmark_name, const std::string& filter) {
|
bool matches_filter(const std::string& benchmark_name, const std::string& filter) {
|
||||||
return filter.empty() || benchmark_name.find(filter) != std::string::npos;
|
if (filter.empty()) return true;
|
||||||
|
|
||||||
|
// Split filter by comma
|
||||||
|
size_t start = 0;
|
||||||
|
size_t end = filter.find(',');
|
||||||
|
while (end != std::string::npos) {
|
||||||
|
std::string token = filter.substr(start, end - start);
|
||||||
|
if (benchmark_name.find(token) != std::string::npos) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
start = end + 1;
|
||||||
|
end = filter.find(',', start);
|
||||||
|
}
|
||||||
|
// Check last token
|
||||||
|
std::string token = filter.substr(start);
|
||||||
|
return benchmark_name.find(token) != std::string::npos;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
@@ -131,12 +250,12 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Testing correctness of round-trip (serialization + deserialization)
|
// Testing correctness of round-trip (serialization + deserialization)
|
||||||
std::string json_str = read_file(JSON_FILE);
|
simdjson::padded_string json_str = read_file(JSON_FILE);
|
||||||
|
|
||||||
// Loading up the data into a structure.
|
// Loading up the data into a structure.
|
||||||
simdjson::ondemand::parser parser;
|
simdjson::ondemand::parser parser;
|
||||||
simdjson::ondemand::document doc;
|
simdjson::ondemand::document doc;
|
||||||
if(parser.iterate(simdjson::pad(json_str)).get(doc)) {
|
if(parser.iterate(json_str).get(doc)) {
|
||||||
std::cerr << "Error loading the document!" << std::endl;
|
std::cerr << "Error loading the document!" << std::endl;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
@@ -147,18 +266,37 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Benchmarking the serialization
|
// Benchmarking the serialization
|
||||||
|
// Note: simdjson benchmarks include both "fair" (fresh allocation) and "reuse" (buffer reuse) variants
|
||||||
|
// The "fair" variants allocate fresh memory each iteration, matching other libraries' behavior
|
||||||
|
// The "reuse" variants demonstrate the API's potential when buffer reuse is possible
|
||||||
|
|
||||||
if (matches_filter("nlohmann", filter)) {
|
if (matches_filter("nlohmann", filter)) {
|
||||||
bench_nlohmann(my_struct);
|
bench_nlohmann(my_struct);
|
||||||
}
|
}
|
||||||
|
#ifdef SIMDJSON_COMPETITION_YYJSON
|
||||||
|
if (matches_filter("yyjson", filter)) {
|
||||||
|
bench_yyjson(my_struct);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (matches_filter("simdjson_static_reflection", filter)) {
|
if (matches_filter("simdjson_static_reflection", filter)) {
|
||||||
bench_simdjson_static_reflection(my_struct);
|
bench_simdjson_static_reflection(my_struct);
|
||||||
}
|
}
|
||||||
|
if (matches_filter("simdjson_reuse", filter)) {
|
||||||
|
bench_simdjson_static_reflection_reuse(my_struct);
|
||||||
|
}
|
||||||
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
|
if (matches_filter("simdjson_to", filter)) {
|
||||||
|
bench_simdjson_to(my_struct);
|
||||||
|
}
|
||||||
|
if (matches_filter("simdjson_to_reuse", filter)) {
|
||||||
|
bench_simdjson_to_reuse(my_struct);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef SIMDJSON_RUST_VERSION
|
#ifdef SIMDJSON_RUST_VERSION
|
||||||
if (matches_filter("rust", filter)) {
|
if (matches_filter("rust", filter)) {
|
||||||
printf("# WARNING: The Rust benchmark may not be directly comparable since it does not use an equivalent data structure.\n");
|
|
||||||
// Create a Rust-compatible CitmCatalog structure from the JSON string
|
// Create a Rust-compatible CitmCatalog structure from the JSON string
|
||||||
serde_benchmark::CitmCatalog* rust_data =
|
serde_benchmark::CitmCatalog* rust_data =
|
||||||
serde_benchmark::citm_from_str(json_str.c_str(), json_str.size());
|
serde_benchmark::citm_from_str(json_str.data(), json_str.size());
|
||||||
|
|
||||||
if (rust_data == nullptr) {
|
if (rust_data == nullptr) {
|
||||||
printf("# Failed to initialize Rust data structure\n");
|
printf("# Failed to initialize Rust data structure\n");
|
||||||
|
|||||||
@@ -4,79 +4,65 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
struct Area {
|
// Price structure - field names must match JSON keys for reflection
|
||||||
int64_t id;
|
struct CITMPrice {
|
||||||
std::string name;
|
uint64_t amount;
|
||||||
int64_t parent;
|
uint64_t audienceSubCategoryId;
|
||||||
std::vector<int64_t> childAreas;
|
uint64_t seatCategoryId;
|
||||||
bool operator==(const Area &other) const = default;
|
bool operator==(const CITMPrice&) const = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AudienceSubCategory {
|
struct CITMArea {
|
||||||
int64_t id;
|
uint64_t areaId;
|
||||||
std::string name;
|
std::vector<uint64_t> blockIds;
|
||||||
int64_t parent;
|
bool operator==(const CITMArea&) const = default;
|
||||||
bool operator==(const AudienceSubCategory &other) const = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Event {
|
struct CITMSeatCategory {
|
||||||
int64_t id;
|
std::vector<CITMArea> areas;
|
||||||
std::string name;
|
uint64_t seatCategoryId;
|
||||||
std::string description;
|
bool operator==(const CITMSeatCategory&) const = default;
|
||||||
int64_t subTopic;
|
|
||||||
int64_t topic;
|
|
||||||
std::vector<int64_t> audience;
|
|
||||||
bool operator==(const Event &other) const = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Performance {
|
struct CITMPerformance {
|
||||||
int64_t id;
|
uint64_t id;
|
||||||
std::string name;
|
uint64_t eventId;
|
||||||
int64_t event;
|
std::optional<std::string> logo;
|
||||||
std::string start;
|
std::optional<std::string> name;
|
||||||
int64_t venueCode;
|
std::vector<CITMPrice> prices;
|
||||||
bool operator==(const Performance &other) const = default;
|
std::vector<CITMSeatCategory> seatCategories;
|
||||||
|
std::optional<std::string> seatMapImage;
|
||||||
|
uint64_t start;
|
||||||
|
std::string venueCode;
|
||||||
|
bool operator==(const CITMPerformance&) const = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SeatCategory {
|
struct CITMEvent {
|
||||||
int64_t id;
|
uint64_t id;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::vector<int64_t> areas;
|
std::optional<std::string> description;
|
||||||
bool operator==(const SeatCategory &other) const = default;
|
std::optional<std::string> logo;
|
||||||
};
|
std::vector<uint64_t> subTopicIds;
|
||||||
|
std::optional<std::string> subjectCode;
|
||||||
struct SubTopic {
|
std::optional<std::string> subtitle;
|
||||||
int64_t id;
|
std::vector<uint64_t> topicIds;
|
||||||
std::string name;
|
bool operator==(const CITMEvent&) const = default;
|
||||||
int64_t parent;
|
|
||||||
bool operator==(const SubTopic &other) const = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Topic {
|
|
||||||
int64_t id;
|
|
||||||
std::string name;
|
|
||||||
bool operator==(const Topic &other) const = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Venue {
|
|
||||||
int64_t id;
|
|
||||||
std::string name;
|
|
||||||
int64_t address;
|
|
||||||
bool operator==(const Venue &other) const = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CitmCatalog {
|
struct CitmCatalog {
|
||||||
std::map<std::string, Area> areas;
|
std::map<std::string, CITMEvent> events;
|
||||||
std::map<std::string, AudienceSubCategory> audienceSubCategory;
|
std::vector<CITMPerformance> performances;
|
||||||
std::map<std::string, Event> events;
|
bool operator==(const CitmCatalog&) const = default;
|
||||||
std::map<std::string, Performance> performances;
|
|
||||||
std::map<std::string, SeatCategory> seatCategory;
|
|
||||||
std::map<std::string, SubTopic> subTopic;
|
|
||||||
std::map<std::string, Topic> topic;
|
|
||||||
std::map<std::string, Venue> venue;
|
|
||||||
|
|
||||||
bool operator==(const CitmCatalog &other) const = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Type aliases
|
||||||
|
using Event = CITMEvent;
|
||||||
|
using Performance = CITMPerformance;
|
||||||
|
using Price = CITMPrice;
|
||||||
|
using SeatArea = CITMArea;
|
||||||
|
using SeatCategoryInfo = CITMSeatCategory;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -8,164 +8,72 @@
|
|||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
// ---- Area ----
|
// ---- CITMPrice ----
|
||||||
inline void to_json(json &j, const Area &a) {
|
inline void to_json(json &j, const CITMPrice &p) {
|
||||||
j = json{
|
j = json{
|
||||||
{"id", a.id},
|
{"amount", p.amount},
|
||||||
{"name", a.name},
|
{"audienceSubCategoryId", p.audienceSubCategoryId},
|
||||||
{"parent", a.parent},
|
{"seatCategoryId", p.seatCategoryId}
|
||||||
{"childAreas", a.childAreas}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
inline void from_json(const json &j, Area &a) {
|
|
||||||
j.at("id").get_to(a.id);
|
|
||||||
j.at("name").get_to(a.name);
|
|
||||||
j.at("parent").get_to(a.parent);
|
|
||||||
j.at("childAreas").get_to(a.childAreas);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- AudienceSubCategory ----
|
// ---- CITMArea ----
|
||||||
inline void to_json(json &j, const AudienceSubCategory &asc) {
|
inline void to_json(json &j, const CITMArea &a) {
|
||||||
j = json{
|
j = json{
|
||||||
{"id", asc.id},
|
{"areaId", a.areaId},
|
||||||
{"name", asc.name},
|
{"blockIds", a.blockIds}
|
||||||
{"parent", asc.parent}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
inline void from_json(const json &j, AudienceSubCategory &asc) {
|
|
||||||
j.at("id").get_to(asc.id);
|
|
||||||
j.at("name").get_to(asc.name);
|
|
||||||
j.at("parent").get_to(asc.parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Event ----
|
// ---- CITMSeatCategory ----
|
||||||
inline void to_json(json &j, const Event &e) {
|
inline void to_json(json &j, const CITMSeatCategory &s) {
|
||||||
j = json{
|
j = json{
|
||||||
{"id", e.id},
|
{"areas", s.areas},
|
||||||
{"name", e.name},
|
{"seatCategoryId", s.seatCategoryId}
|
||||||
{"description", e.description},
|
|
||||||
{"subTopic", e.subTopic},
|
|
||||||
{"topic", e.topic},
|
|
||||||
{"audience", e.audience}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
inline void from_json(const json &j, Event &e) {
|
|
||||||
j.at("id").get_to(e.id);
|
|
||||||
j.at("name").get_to(e.name);
|
|
||||||
j.at("description").get_to(e.description);
|
|
||||||
j.at("subTopic").get_to(e.subTopic);
|
|
||||||
j.at("topic").get_to(e.topic);
|
|
||||||
j.at("audience").get_to(e.audience);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Performance ----
|
// ---- CITMPerformance ----
|
||||||
inline void to_json(json &j, const Performance &p) {
|
inline void to_json(json &j, const CITMPerformance &p) {
|
||||||
j = json{
|
j = json{
|
||||||
{"id", p.id},
|
{"id", p.id},
|
||||||
|
{"eventId", p.eventId},
|
||||||
|
{"logo", p.logo},
|
||||||
{"name", p.name},
|
{"name", p.name},
|
||||||
{"event", p.event},
|
{"prices", p.prices},
|
||||||
|
{"seatCategories", p.seatCategories},
|
||||||
|
{"seatMapImage", p.seatMapImage},
|
||||||
{"start", p.start},
|
{"start", p.start},
|
||||||
{"venueCode", p.venueCode}
|
{"venueCode", p.venueCode}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
inline void from_json(const json &j, Performance &p) {
|
|
||||||
j.at("id").get_to(p.id);
|
|
||||||
j.at("name").get_to(p.name);
|
|
||||||
j.at("event").get_to(p.event);
|
|
||||||
j.at("start").get_to(p.start);
|
|
||||||
j.at("venueCode").get_to(p.venueCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- SeatCategory ----
|
// ---- CITMEvent ----
|
||||||
inline void to_json(json &j, const SeatCategory &sc) {
|
inline void to_json(json &j, const CITMEvent &e) {
|
||||||
j = json{
|
j = json{
|
||||||
{"id", sc.id},
|
{"id", e.id},
|
||||||
{"name", sc.name},
|
{"name", e.name},
|
||||||
{"areas", sc.areas}
|
{"description", e.description},
|
||||||
|
{"logo", e.logo},
|
||||||
|
{"subTopicIds", e.subTopicIds},
|
||||||
|
{"subjectCode", e.subjectCode},
|
||||||
|
{"subtitle", e.subtitle},
|
||||||
|
{"topicIds", e.topicIds}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
inline void from_json(const json &j, SeatCategory &sc) {
|
|
||||||
j.at("id").get_to(sc.id);
|
|
||||||
j.at("name").get_to(sc.name);
|
|
||||||
j.at("areas").get_to(sc.areas);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- SubTopic ----
|
|
||||||
inline void to_json(json &j, const SubTopic &st) {
|
|
||||||
j = json{
|
|
||||||
{"id", st.id},
|
|
||||||
{"name", st.name},
|
|
||||||
{"parent", st.parent}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
inline void from_json(const json &j, SubTopic &st) {
|
|
||||||
j.at("id").get_to(st.id);
|
|
||||||
j.at("name").get_to(st.name);
|
|
||||||
j.at("parent").get_to(st.parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Topic ----
|
|
||||||
inline void to_json(json &j, const Topic &t) {
|
|
||||||
j = json{
|
|
||||||
{"id", t.id},
|
|
||||||
{"name", t.name}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
inline void from_json(const json &j, Topic &t) {
|
|
||||||
j.at("id").get_to(t.id);
|
|
||||||
j.at("name").get_to(t.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Venue ----
|
|
||||||
inline void to_json(json &j, const Venue &v) {
|
|
||||||
j = json{
|
|
||||||
{"id", v.id},
|
|
||||||
{"name", v.name},
|
|
||||||
{"address", v.address}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
inline void from_json(const json &j, Venue &v) {
|
|
||||||
j.at("id").get_to(v.id);
|
|
||||||
j.at("name").get_to(v.name);
|
|
||||||
j.at("address").get_to(v.address);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- CitmCatalog ----
|
// ---- CitmCatalog ----
|
||||||
inline void to_json(json &j, const CitmCatalog &c) {
|
inline void to_json(json &j, const CitmCatalog &c) {
|
||||||
j = json{
|
j = json{
|
||||||
{"areas", c.areas},
|
|
||||||
{"audienceSubCategory", c.audienceSubCategory},
|
|
||||||
{"events", c.events},
|
{"events", c.events},
|
||||||
{"performances", c.performances},
|
{"performances", c.performances}
|
||||||
{"seatCategory", c.seatCategory},
|
|
||||||
{"subTopic", c.subTopic},
|
|
||||||
{"topic", c.topic},
|
|
||||||
{"venue", c.venue}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
inline void from_json(const json &j, CitmCatalog &c) {
|
|
||||||
j.at("areas").get_to(c.areas);
|
|
||||||
j.at("audienceSubCategory").get_to(c.audienceSubCategory);
|
|
||||||
j.at("events").get_to(c.events);
|
|
||||||
j.at("performances").get_to(c.performances);
|
|
||||||
j.at("seatCategory").get_to(c.seatCategory);
|
|
||||||
j.at("subTopic").get_to(c.subTopic);
|
|
||||||
j.at("topic").get_to(c.topic);
|
|
||||||
j.at("venue").get_to(c.venue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optional convenience functions for benchmarking
|
// Serialization function
|
||||||
inline std::string nlohmann_serialize(const CitmCatalog &catalog) {
|
inline std::string nlohmann_serialize(const CitmCatalog &catalog) {
|
||||||
json j = catalog;
|
json j = catalog;
|
||||||
return j.dump();
|
return j.dump();
|
||||||
}
|
}
|
||||||
inline bool nlohmann_deserialize(const std::string &json_in, CitmCatalog &catalog) {
|
|
||||||
try {
|
|
||||||
catalog = json::parse(json_in);
|
|
||||||
return false; // success
|
|
||||||
} catch(...) {
|
|
||||||
return true; // failure
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // NLOHMANN_CITM_CATALOG_DATA_H
|
#endif // NLOHMANN_CITM_CATALOG_DATA_H
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
#ifndef RAPIDJSON_CITM_CATALOG_DATA_H
|
||||||
|
#define RAPIDJSON_CITM_CATALOG_DATA_H
|
||||||
|
|
||||||
|
#include "citm_catalog_data.h"
|
||||||
|
#include <rapidjson/document.h>
|
||||||
|
#include <rapidjson/writer.h>
|
||||||
|
#include <rapidjson/stringbuffer.h>
|
||||||
|
#include <rapidjson/error/en.h>
|
||||||
|
|
||||||
|
using namespace rapidjson;
|
||||||
|
|
||||||
|
// RapidJSON deserialization for CITM Catalog data
|
||||||
|
CitmCatalog rapidjson_deserialize_citm(const std::string& json_str) {
|
||||||
|
Document doc;
|
||||||
|
doc.Parse(json_str.c_str());
|
||||||
|
|
||||||
|
if (doc.HasParseError()) {
|
||||||
|
throw std::runtime_error("RapidJSON parse error");
|
||||||
|
}
|
||||||
|
|
||||||
|
CitmCatalog catalog;
|
||||||
|
|
||||||
|
// Parse events
|
||||||
|
if (doc.HasMember("events") && doc["events"].IsObject()) {
|
||||||
|
const Value& events = doc["events"];
|
||||||
|
for (auto it = events.MemberBegin(); it != events.MemberEnd(); ++it) {
|
||||||
|
Event event;
|
||||||
|
const Value& ev = it->value;
|
||||||
|
|
||||||
|
if (ev.HasMember("description") && ev["description"].IsString())
|
||||||
|
event.description = ev["description"].GetString();
|
||||||
|
if (ev.HasMember("id") && ev["id"].IsUint64())
|
||||||
|
event.id = ev["id"].GetUint64();
|
||||||
|
if (ev.HasMember("logo") && ev["logo"].IsString())
|
||||||
|
event.logo = ev["logo"].GetString();
|
||||||
|
if (ev.HasMember("name") && ev["name"].IsString())
|
||||||
|
event.name = ev["name"].GetString();
|
||||||
|
if (ev.HasMember("subjectCode") && ev["subjectCode"].IsString())
|
||||||
|
event.subjectCode = ev["subjectCode"].GetString();
|
||||||
|
if (ev.HasMember("subtitle") && ev["subtitle"].IsString())
|
||||||
|
event.subtitle = ev["subtitle"].GetString();
|
||||||
|
|
||||||
|
if (ev.HasMember("topicIds") && ev["topicIds"].IsArray()) {
|
||||||
|
const Value& topics = ev["topicIds"];
|
||||||
|
for (SizeType j = 0; j < topics.Size(); j++) {
|
||||||
|
if (topics[j].IsUint64())
|
||||||
|
event.topicIds.push_back(topics[j].GetUint64());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev.HasMember("subTopicIds") && ev["subTopicIds"].IsArray()) {
|
||||||
|
const Value& subtopics = ev["subTopicIds"];
|
||||||
|
for (SizeType j = 0; j < subtopics.Size(); j++) {
|
||||||
|
if (subtopics[j].IsUint64())
|
||||||
|
event.subTopicIds.push_back(subtopics[j].GetUint64());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
catalog.events[it->name.GetString()] = event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse performances
|
||||||
|
if (doc.HasMember("performances") && doc["performances"].IsArray()) {
|
||||||
|
const Value& performances = doc["performances"];
|
||||||
|
for (SizeType i = 0; i < performances.Size(); i++) {
|
||||||
|
Performance perf;
|
||||||
|
const Value& p = performances[i];
|
||||||
|
|
||||||
|
if (p.HasMember("id") && p["id"].IsUint64())
|
||||||
|
perf.id = p["id"].GetUint64();
|
||||||
|
if (p.HasMember("eventId") && p["eventId"].IsUint64())
|
||||||
|
perf.eventId = p["eventId"].GetUint64();
|
||||||
|
if (p.HasMember("start") && p["start"].IsUint64())
|
||||||
|
perf.start = p["start"].GetUint64();
|
||||||
|
if (p.HasMember("venueCode") && p["venueCode"].IsString())
|
||||||
|
perf.venueCode = p["venueCode"].GetString();
|
||||||
|
if (p.HasMember("name") && p["name"].IsString())
|
||||||
|
perf.name = p["name"].GetString();
|
||||||
|
|
||||||
|
catalog.performances.push_back(perf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse other string maps
|
||||||
|
auto parseStringMap = [&doc](const char* key, std::map<std::string, std::string>& target) {
|
||||||
|
if (doc.HasMember(key) && doc[key].IsObject()) {
|
||||||
|
const Value& obj = doc[key];
|
||||||
|
for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) {
|
||||||
|
if (it->value.IsString()) {
|
||||||
|
target[it->name.GetString()] = it->value.GetString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return catalog;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RapidJSON serialization for CITM Catalog data
|
||||||
|
std::string rapidjson_serialize_citm(const CitmCatalog& catalog) {
|
||||||
|
Document doc;
|
||||||
|
doc.SetObject();
|
||||||
|
Document::AllocatorType& allocator = doc.GetAllocator();
|
||||||
|
|
||||||
|
// Serialize events
|
||||||
|
Value events_obj(kObjectType);
|
||||||
|
for (const auto& [key, event] : catalog.events) {
|
||||||
|
Value event_obj(kObjectType);
|
||||||
|
|
||||||
|
if (event.description) {
|
||||||
|
Value desc;
|
||||||
|
desc.SetString(event.description->c_str(), allocator);
|
||||||
|
event_obj.AddMember("description", desc, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
event_obj.AddMember("id", event.id, allocator);
|
||||||
|
|
||||||
|
if (event.logo) {
|
||||||
|
Value logo;
|
||||||
|
logo.SetString(event.logo->c_str(), allocator);
|
||||||
|
event_obj.AddMember("logo", logo, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value name;
|
||||||
|
name.SetString(event.name.c_str(), allocator);
|
||||||
|
event_obj.AddMember("name", name, allocator);
|
||||||
|
|
||||||
|
if (event.subjectCode) {
|
||||||
|
Value subject;
|
||||||
|
subject.SetString(event.subjectCode->c_str(), allocator);
|
||||||
|
event_obj.AddMember("subjectCode", subject, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.subtitle) {
|
||||||
|
Value subtitle;
|
||||||
|
subtitle.SetString(event.subtitle->c_str(), allocator);
|
||||||
|
event_obj.AddMember("subtitle", subtitle, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value topicIds(kArrayType);
|
||||||
|
for (uint64_t id : event.topicIds) {
|
||||||
|
topicIds.PushBack(id, allocator);
|
||||||
|
}
|
||||||
|
event_obj.AddMember("topicIds", topicIds, allocator);
|
||||||
|
|
||||||
|
Value subTopicIds(kArrayType);
|
||||||
|
for (uint64_t id : event.subTopicIds) {
|
||||||
|
subTopicIds.PushBack(id, allocator);
|
||||||
|
}
|
||||||
|
event_obj.AddMember("subTopicIds", subTopicIds, allocator);
|
||||||
|
|
||||||
|
Value key_val;
|
||||||
|
key_val.SetString(key.c_str(), allocator);
|
||||||
|
events_obj.AddMember(key_val, event_obj, allocator);
|
||||||
|
}
|
||||||
|
doc.AddMember("events", events_obj, allocator);
|
||||||
|
|
||||||
|
// Serialize performances
|
||||||
|
Value performances_array(kArrayType);
|
||||||
|
for (const auto& perf : catalog.performances) {
|
||||||
|
Value perf_obj(kObjectType);
|
||||||
|
perf_obj.AddMember("id", perf.id, allocator);
|
||||||
|
perf_obj.AddMember("eventId", perf.eventId, allocator);
|
||||||
|
perf_obj.AddMember("start", perf.start, allocator);
|
||||||
|
|
||||||
|
Value venue;
|
||||||
|
venue.SetString(perf.venueCode.c_str(), allocator);
|
||||||
|
perf_obj.AddMember("venueCode", venue, allocator);
|
||||||
|
|
||||||
|
if (perf.name) {
|
||||||
|
Value name;
|
||||||
|
name.SetString(perf.name->c_str(), allocator);
|
||||||
|
perf_obj.AddMember("name", name, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
performances_array.PushBack(perf_obj, allocator);
|
||||||
|
}
|
||||||
|
doc.AddMember("performances", performances_array, allocator);
|
||||||
|
|
||||||
|
StringBuffer buffer;
|
||||||
|
Writer<StringBuffer> writer(buffer);
|
||||||
|
doc.Accept(writer);
|
||||||
|
|
||||||
|
return buffer.GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // RAPIDJSON_CITM_CATALOG_DATA_H
|
||||||
@@ -0,0 +1,231 @@
|
|||||||
|
#ifndef YYJSON_CITM_CATALOG_DATA_H
|
||||||
|
#define YYJSON_CITM_CATALOG_DATA_H
|
||||||
|
|
||||||
|
#include "citm_catalog_data.h"
|
||||||
|
#include <yyjson.h>
|
||||||
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
// yyjson deserialization for CITM Catalog data
|
||||||
|
// Matches C++ CitmCatalog struct (only events + performances)
|
||||||
|
CitmCatalog yyjson_deserialize_citm(const std::string &json_str) {
|
||||||
|
CitmCatalog catalog;
|
||||||
|
|
||||||
|
yyjson_doc *doc = yyjson_read(json_str.c_str(), json_str.size(), 0);
|
||||||
|
if (!doc) {
|
||||||
|
throw std::runtime_error("yyjson parse error");
|
||||||
|
}
|
||||||
|
|
||||||
|
yyjson_val *root = yyjson_doc_get_root(doc);
|
||||||
|
if (!root) {
|
||||||
|
yyjson_doc_free(doc);
|
||||||
|
return catalog;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse events
|
||||||
|
yyjson_val *events_val = yyjson_obj_get(root, "events");
|
||||||
|
if (events_val && yyjson_is_obj(events_val)) {
|
||||||
|
size_t idx, max;
|
||||||
|
yyjson_val *key, *val;
|
||||||
|
yyjson_obj_foreach(events_val, idx, max, key, val) {
|
||||||
|
CITMEvent event;
|
||||||
|
|
||||||
|
yyjson_val *v;
|
||||||
|
v = yyjson_obj_get(val, "description");
|
||||||
|
if (v && yyjson_is_str(v)) event.description = yyjson_get_str(v);
|
||||||
|
|
||||||
|
v = yyjson_obj_get(val, "id");
|
||||||
|
if (v && yyjson_is_uint(v)) event.id = yyjson_get_uint(v);
|
||||||
|
|
||||||
|
v = yyjson_obj_get(val, "logo");
|
||||||
|
if (v && yyjson_is_str(v)) event.logo = yyjson_get_str(v);
|
||||||
|
|
||||||
|
v = yyjson_obj_get(val, "name");
|
||||||
|
if (v && yyjson_is_str(v)) event.name = yyjson_get_str(v);
|
||||||
|
|
||||||
|
v = yyjson_obj_get(val, "subjectCode");
|
||||||
|
if (v && yyjson_is_str(v)) event.subjectCode = yyjson_get_str(v);
|
||||||
|
|
||||||
|
v = yyjson_obj_get(val, "subtitle");
|
||||||
|
if (v && yyjson_is_str(v)) event.subtitle = yyjson_get_str(v);
|
||||||
|
|
||||||
|
// Parse topicIds array
|
||||||
|
v = yyjson_obj_get(val, "topicIds");
|
||||||
|
if (v && yyjson_is_arr(v)) {
|
||||||
|
size_t arr_idx, arr_max;
|
||||||
|
yyjson_val *arr_val;
|
||||||
|
yyjson_arr_foreach(v, arr_idx, arr_max, arr_val) {
|
||||||
|
if (yyjson_is_uint(arr_val))
|
||||||
|
event.topicIds.push_back(yyjson_get_uint(arr_val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse subTopicIds array
|
||||||
|
v = yyjson_obj_get(val, "subTopicIds");
|
||||||
|
if (v && yyjson_is_arr(v)) {
|
||||||
|
size_t arr_idx, arr_max;
|
||||||
|
yyjson_val *arr_val;
|
||||||
|
yyjson_arr_foreach(v, arr_idx, arr_max, arr_val) {
|
||||||
|
if (yyjson_is_uint(arr_val))
|
||||||
|
event.subTopicIds.push_back(yyjson_get_uint(arr_val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (yyjson_is_str(key))
|
||||||
|
catalog.events[yyjson_get_str(key)] = event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse performances (simplified - full parsing would need prices/seatCategories)
|
||||||
|
yyjson_val *performances_val = yyjson_obj_get(root, "performances");
|
||||||
|
if (performances_val && yyjson_is_arr(performances_val)) {
|
||||||
|
size_t idx, max;
|
||||||
|
yyjson_val *perf_val;
|
||||||
|
yyjson_arr_foreach(performances_val, idx, max, perf_val) {
|
||||||
|
CITMPerformance perf;
|
||||||
|
|
||||||
|
yyjson_val *v;
|
||||||
|
v = yyjson_obj_get(perf_val, "id");
|
||||||
|
if (v && yyjson_is_uint(v)) perf.id = yyjson_get_uint(v);
|
||||||
|
|
||||||
|
v = yyjson_obj_get(perf_val, "eventId");
|
||||||
|
if (v && yyjson_is_uint(v)) perf.eventId = yyjson_get_uint(v);
|
||||||
|
|
||||||
|
v = yyjson_obj_get(perf_val, "start");
|
||||||
|
if (v && yyjson_is_uint(v)) perf.start = yyjson_get_uint(v);
|
||||||
|
|
||||||
|
v = yyjson_obj_get(perf_val, "venueCode");
|
||||||
|
if (v && yyjson_is_str(v)) perf.venueCode = yyjson_get_str(v);
|
||||||
|
|
||||||
|
v = yyjson_obj_get(perf_val, "name");
|
||||||
|
if (v && yyjson_is_str(v)) perf.name = yyjson_get_str(v);
|
||||||
|
|
||||||
|
v = yyjson_obj_get(perf_val, "logo");
|
||||||
|
if (v && yyjson_is_str(v)) perf.logo = yyjson_get_str(v);
|
||||||
|
|
||||||
|
v = yyjson_obj_get(perf_val, "seatMapImage");
|
||||||
|
if (v && yyjson_is_str(v)) perf.seatMapImage = yyjson_get_str(v);
|
||||||
|
|
||||||
|
// Note: prices and seatCategories parsing omitted for brevity
|
||||||
|
// The serialization benchmark uses data loaded by simdjson
|
||||||
|
|
||||||
|
catalog.performances.push_back(perf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yyjson_doc_free(doc);
|
||||||
|
return catalog;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to add optional string field
|
||||||
|
static inline void yyjson_add_optional_str(yyjson_mut_doc *doc, yyjson_mut_val *obj,
|
||||||
|
const char *key, const std::optional<std::string> &val) {
|
||||||
|
if (val.has_value()) {
|
||||||
|
yyjson_mut_obj_add_str(doc, obj, key, val->c_str());
|
||||||
|
} else {
|
||||||
|
yyjson_mut_obj_add_null(doc, obj, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// yyjson serialization for CITM Catalog data
|
||||||
|
// Matches C++ CitmCatalog struct exactly (only events + performances)
|
||||||
|
std::string yyjson_serialize_citm(const CitmCatalog &catalog) {
|
||||||
|
yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
|
||||||
|
yyjson_mut_val *root = yyjson_mut_obj(doc);
|
||||||
|
yyjson_mut_doc_set_root(doc, root);
|
||||||
|
|
||||||
|
// Create events object
|
||||||
|
yyjson_mut_val *events_obj = yyjson_mut_obj(doc);
|
||||||
|
for (const auto& [key, event] : catalog.events) {
|
||||||
|
yyjson_mut_val *event_obj = yyjson_mut_obj(doc);
|
||||||
|
|
||||||
|
yyjson_add_optional_str(doc, event_obj, "description", event.description);
|
||||||
|
yyjson_mut_obj_add_uint(doc, event_obj, "id", event.id);
|
||||||
|
yyjson_add_optional_str(doc, event_obj, "logo", event.logo);
|
||||||
|
// name is not optional in CITMEvent
|
||||||
|
yyjson_mut_obj_add_str(doc, event_obj, "name", event.name.c_str());
|
||||||
|
|
||||||
|
// Add subTopicIds array
|
||||||
|
yyjson_mut_val *subtopic_ids = yyjson_mut_arr(doc);
|
||||||
|
for (uint64_t id : event.subTopicIds) {
|
||||||
|
yyjson_mut_arr_add_uint(doc, subtopic_ids, id);
|
||||||
|
}
|
||||||
|
yyjson_mut_obj_add_val(doc, event_obj, "subTopicIds", subtopic_ids);
|
||||||
|
|
||||||
|
yyjson_add_optional_str(doc, event_obj, "subjectCode", event.subjectCode);
|
||||||
|
yyjson_add_optional_str(doc, event_obj, "subtitle", event.subtitle);
|
||||||
|
|
||||||
|
// Add topicIds array
|
||||||
|
yyjson_mut_val *topic_ids = yyjson_mut_arr(doc);
|
||||||
|
for (uint64_t id : event.topicIds) {
|
||||||
|
yyjson_mut_arr_add_uint(doc, topic_ids, id);
|
||||||
|
}
|
||||||
|
yyjson_mut_obj_add_val(doc, event_obj, "topicIds", topic_ids);
|
||||||
|
|
||||||
|
yyjson_mut_obj_add_val(doc, events_obj, key.c_str(), event_obj);
|
||||||
|
}
|
||||||
|
yyjson_mut_obj_add_val(doc, root, "events", events_obj);
|
||||||
|
|
||||||
|
// Create performances array
|
||||||
|
yyjson_mut_val *performances_array = yyjson_mut_arr(doc);
|
||||||
|
for (const auto& perf : catalog.performances) {
|
||||||
|
yyjson_mut_val *perf_obj = yyjson_mut_obj(doc);
|
||||||
|
|
||||||
|
yyjson_mut_obj_add_uint(doc, perf_obj, "eventId", perf.eventId);
|
||||||
|
yyjson_mut_obj_add_uint(doc, perf_obj, "id", perf.id);
|
||||||
|
yyjson_add_optional_str(doc, perf_obj, "logo", perf.logo);
|
||||||
|
yyjson_add_optional_str(doc, perf_obj, "name", perf.name);
|
||||||
|
|
||||||
|
// Add prices array
|
||||||
|
yyjson_mut_val *prices_array = yyjson_mut_arr(doc);
|
||||||
|
for (const auto& price : perf.prices) {
|
||||||
|
yyjson_mut_val *price_obj = yyjson_mut_obj(doc);
|
||||||
|
yyjson_mut_obj_add_uint(doc, price_obj, "amount", price.amount);
|
||||||
|
yyjson_mut_obj_add_uint(doc, price_obj, "audienceSubCategoryId", price.audienceSubCategoryId);
|
||||||
|
yyjson_mut_obj_add_uint(doc, price_obj, "seatCategoryId", price.seatCategoryId);
|
||||||
|
yyjson_mut_arr_append(prices_array, price_obj);
|
||||||
|
}
|
||||||
|
yyjson_mut_obj_add_val(doc, perf_obj, "prices", prices_array);
|
||||||
|
|
||||||
|
// Add seatCategories array
|
||||||
|
yyjson_mut_val *seat_cats_array = yyjson_mut_arr(doc);
|
||||||
|
for (const auto& seatCat : perf.seatCategories) {
|
||||||
|
yyjson_mut_val *seat_cat_obj = yyjson_mut_obj(doc);
|
||||||
|
|
||||||
|
// Add areas array
|
||||||
|
yyjson_mut_val *areas_array = yyjson_mut_arr(doc);
|
||||||
|
for (const auto& area : seatCat.areas) {
|
||||||
|
yyjson_mut_val *area_obj = yyjson_mut_obj(doc);
|
||||||
|
yyjson_mut_obj_add_uint(doc, area_obj, "areaId", area.areaId);
|
||||||
|
|
||||||
|
yyjson_mut_val *block_ids = yyjson_mut_arr(doc);
|
||||||
|
for (uint64_t blockId : area.blockIds) {
|
||||||
|
yyjson_mut_arr_add_uint(doc, block_ids, blockId);
|
||||||
|
}
|
||||||
|
yyjson_mut_obj_add_val(doc, area_obj, "blockIds", block_ids);
|
||||||
|
yyjson_mut_arr_append(areas_array, area_obj);
|
||||||
|
}
|
||||||
|
yyjson_mut_obj_add_val(doc, seat_cat_obj, "areas", areas_array);
|
||||||
|
yyjson_mut_obj_add_uint(doc, seat_cat_obj, "seatCategoryId", seatCat.seatCategoryId);
|
||||||
|
yyjson_mut_arr_append(seat_cats_array, seat_cat_obj);
|
||||||
|
}
|
||||||
|
yyjson_mut_obj_add_val(doc, perf_obj, "seatCategories", seat_cats_array);
|
||||||
|
|
||||||
|
yyjson_add_optional_str(doc, perf_obj, "seatMapImage", perf.seatMapImage);
|
||||||
|
yyjson_mut_obj_add_uint(doc, perf_obj, "start", perf.start);
|
||||||
|
yyjson_mut_obj_add_str(doc, perf_obj, "venueCode", perf.venueCode.c_str());
|
||||||
|
|
||||||
|
yyjson_mut_arr_append(performances_array, perf_obj);
|
||||||
|
}
|
||||||
|
yyjson_mut_obj_add_val(doc, root, "performances", performances_array);
|
||||||
|
|
||||||
|
// Write to string
|
||||||
|
char *json_output = yyjson_mut_write(doc, 0, NULL);
|
||||||
|
std::string result(json_output);
|
||||||
|
free(json_output);
|
||||||
|
yyjson_mut_doc_free(doc);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // YYJSON_CITM_CATALOG_DATA_H
|
||||||
@@ -5,405 +5,165 @@ extern crate libc;
|
|||||||
use libc::{c_char, size_t};
|
use libc::{c_char, size_t};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use std::{collections::HashMap, ffi::CString, ptr, slice};
|
use std::{collections::HashMap, ffi::CString, ptr, slice};
|
||||||
use serde::de::{self, Deserializer};
|
|
||||||
/******************************************************/
|
|
||||||
/******************************************************/
|
|
||||||
/**
|
|
||||||
* Warning: the C++ code may not generate the same JSON.
|
|
||||||
*/
|
|
||||||
/******************************************************/
|
|
||||||
/******************************************************/
|
|
||||||
|
|
||||||
// This has no equivalent in C++:
|
//==============================================================================
|
||||||
#[derive(Serialize, Deserialize)]
|
// Twitter Benchmark Structures
|
||||||
pub struct Metadata {
|
// These match the C++ TwitterData structures exactly
|
||||||
result_type: String,
|
//==============================================================================
|
||||||
iso_language_code: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
id: i64,
|
id: u64,
|
||||||
id_str: String,
|
|
||||||
name: String,
|
name: String,
|
||||||
screen_name: String,
|
screen_name: String,
|
||||||
location: String,
|
location: String,
|
||||||
description: String,
|
description: String,
|
||||||
// C++ does not have those:
|
|
||||||
// url: Option<String>,
|
|
||||||
//protected: bool,
|
|
||||||
//listed_count: i64,
|
|
||||||
//created_at: String,
|
|
||||||
//favourites_count: i64,
|
|
||||||
//utc_offset: Option<i64>,
|
|
||||||
//time_zone: Option<String>,
|
|
||||||
//geo_enabled: bool,
|
|
||||||
verified: bool,
|
verified: bool,
|
||||||
followers_count: i64,
|
followers_count: u64,
|
||||||
friends_count: i64,
|
friends_count: u64,
|
||||||
statuses_count: i64,
|
statuses_count: u64,
|
||||||
// C++ does not have those:
|
|
||||||
//lang: String,
|
|
||||||
//profile_background_color: String,
|
|
||||||
//profile_background_image_url: String,
|
|
||||||
//profile_background_image_url_https: String,
|
|
||||||
//profile_background_tile: bool,
|
|
||||||
//profile_image_url: String,
|
|
||||||
//profile_image_url_https: String,
|
|
||||||
//profile_banner_url: Option<String>,
|
|
||||||
//profile_link_color: String,
|
|
||||||
//profile_sidebar_border_color: String,
|
|
||||||
//profile_sidebar_fill_color: String,
|
|
||||||
//profile_text_color: String,
|
|
||||||
//profile_use_background_image: bool,
|
|
||||||
//default_profile: bool,
|
|
||||||
//default_profile_image: bool,
|
|
||||||
//following: bool,
|
|
||||||
//follow_request_sent: bool,
|
|
||||||
//notifications: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct Hashtag {
|
|
||||||
text: String,
|
|
||||||
|
|
||||||
// C++ has those but D. Lemire does not know what they are, they don't appear in the JSON:
|
|
||||||
// int64_t indices_start;
|
|
||||||
// int64_t indices_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct Url {
|
|
||||||
url: String,
|
|
||||||
expanded_url: String,
|
|
||||||
display_url: String,
|
|
||||||
// C++ has those but D. Lemire does not know what they are, they don't appear in the JSON:
|
|
||||||
// int64_t indices_start;
|
|
||||||
// int64_t indices_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct UserMention {
|
|
||||||
id: i64,
|
|
||||||
name: String,
|
|
||||||
screen_name: String,
|
|
||||||
// Not in the C++ equivalent:
|
|
||||||
//id_str: String,
|
|
||||||
//indices: Vec<i64>,
|
|
||||||
// C++ has those but D. Lemire does not know what they are, they don't appear in the JSON:
|
|
||||||
// int64_t indices_start;
|
|
||||||
// int64_t indices_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct Entities {
|
|
||||||
hashtags: Vec<Hashtag>,
|
|
||||||
urls: Vec<Url>,
|
|
||||||
user_mentions: Vec<UserMention>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct Status {
|
pub struct Status {
|
||||||
created_at: String,
|
created_at: String,
|
||||||
id: i64,
|
id: u64,
|
||||||
text: String,
|
text: String,
|
||||||
user: User,
|
user: User,
|
||||||
entities: Entities,
|
retweet_count: u64,
|
||||||
retweet_count: i64,
|
favorite_count: u64,
|
||||||
favorite_count: i64,
|
|
||||||
favorited: bool,
|
|
||||||
retweeted: bool,
|
|
||||||
// None of these are in the C++ equivalent:
|
|
||||||
/*
|
|
||||||
metadata: Metadata,
|
|
||||||
id_str: String,
|
|
||||||
source: String,
|
|
||||||
truncated: bool,
|
|
||||||
in_reply_to_status_id: Option<i64>,
|
|
||||||
in_reply_to_status_id_str: Option<String>,
|
|
||||||
in_reply_to_user_id: Option<i64>,
|
|
||||||
in_reply_to_user_id_str: Option<String>,
|
|
||||||
in_reply_to_screen_name: Option<String>,
|
|
||||||
geo: Option<String>,
|
|
||||||
coordinates: Option<String>,
|
|
||||||
place: Option<String>,
|
|
||||||
contributors: Option<String>,
|
|
||||||
lang: String,
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct TwitterData {
|
pub struct TwitterData {
|
||||||
statuses: Vec<Status>,
|
statuses: Vec<Status>,
|
||||||
}
|
}
|
||||||
|
static mut TWITTER_DATA: *mut TwitterData = std::ptr::null_mut();
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn twitter_from_str(raw_input: *const c_char, raw_input_length: size_t) -> *mut TwitterData {
|
pub unsafe extern "C" fn twitter_from_str(raw_input: *const c_char, raw_input_length: size_t) -> *mut TwitterData {
|
||||||
let input = std::str::from_utf8_unchecked(slice::from_raw_parts(raw_input as *const u8, raw_input_length));
|
let input = std::str::from_utf8_unchecked(slice::from_raw_parts(raw_input as *const u8, raw_input_length));
|
||||||
match serde_json::from_str(&input) {
|
match serde_json::from_str(&input) {
|
||||||
Ok(result) => Box::into_raw(Box::new(result)),
|
Ok(result) => Box::into_raw(Box::new(result)),
|
||||||
Err(_) => std::ptr::null_mut(),
|
Err(_) => std::ptr::null_mut(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn str_from_twitter(raw: *mut TwitterData) -> *const c_char {
|
pub unsafe extern "C" fn set_twitter_data(raw: *mut TwitterData) {
|
||||||
let twitter_thing = { &*raw };
|
TWITTER_DATA = raw;
|
||||||
let serialized = serde_json::to_string(&twitter_thing).unwrap();
|
|
||||||
return std::ffi::CString::new(serialized.as_str()).unwrap().into_raw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn serialize_twitter_to_string() -> usize {
|
||||||
|
if TWITTER_DATA.is_null() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let data = &*TWITTER_DATA;
|
||||||
|
serde_json::to_string(data).unwrap().len()
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn free_twitter(raw: *mut TwitterData) {
|
pub unsafe extern "C" fn free_twitter(raw: *mut TwitterData) {
|
||||||
if raw.is_null() {
|
if raw.is_null() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
drop(Box::from_raw(raw))
|
||||||
drop(Box::from_raw(raw))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern fn free_string(ptr: *const c_char) {
|
pub unsafe extern fn free_string(ptr: *const c_char) {
|
||||||
let _ = std::ffi::CString::from_raw(ptr as *mut _);
|
let _ = std::ffi::CString::from_raw(ptr as *mut _);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Functions associated with the CitmCatalog benchmark
|
//==============================================================================
|
||||||
|
// CITM Catalog Benchmark Structures
|
||||||
|
// These match the C++ CitmCatalog structures EXACTLY for fair comparison
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
/// Matches C++ CITMPrice struct exactly
|
||||||
pub struct Area {
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub id: i64,
|
pub struct CITMPrice {
|
||||||
pub name: Option<String>, // Changed to Option
|
pub amount: u64,
|
||||||
pub parent: i64,
|
#[serde(rename = "audienceSubCategoryId")]
|
||||||
#[serde(rename = "childAreas")]
|
pub audience_sub_category_id: u64,
|
||||||
pub child_areas: Vec<i64>,
|
#[serde(rename = "seatCategoryId")]
|
||||||
|
pub seat_category_id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
/// Matches C++ CITMArea struct exactly
|
||||||
pub struct AudienceSubCategory {
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub id: i64,
|
pub struct CITMArea {
|
||||||
pub name: Option<String>, // Changed to Option
|
#[serde(rename = "areaId")]
|
||||||
pub parent: i64,
|
pub area_id: u64,
|
||||||
|
#[serde(rename = "blockIds")]
|
||||||
|
pub block_ids: Vec<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
/// Matches C++ CITMSeatCategory struct exactly
|
||||||
pub struct Event {
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
#[serde(default)]
|
pub struct CITMSeatCategory {
|
||||||
pub description: Option<String>,
|
pub areas: Vec<CITMArea>,
|
||||||
pub id: i64,
|
#[serde(rename = "seatCategoryId")]
|
||||||
|
pub seat_category_id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Matches C++ CITMPerformance struct exactly
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct CITMPerformance {
|
||||||
|
pub id: u64,
|
||||||
|
#[serde(rename = "eventId")]
|
||||||
|
pub event_id: u64,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub logo: Option<String>,
|
pub logo: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
|
pub prices: Vec<CITMPrice>,
|
||||||
|
#[serde(rename = "seatCategories")]
|
||||||
|
pub seat_categories: Vec<CITMSeatCategory>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub subTopicIds: Vec<i64>,
|
#[serde(rename = "seatMapImage")]
|
||||||
|
pub seat_map_image: Option<String>,
|
||||||
|
pub start: u64,
|
||||||
|
#[serde(rename = "venueCode")]
|
||||||
|
pub venue_code: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Matches C++ CITMEvent struct exactly
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct CITMEvent {
|
||||||
|
pub id: u64,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub subjectCode: Option<String>,
|
pub name: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub description: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub logo: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
#[serde(rename = "subTopicIds")]
|
||||||
|
pub sub_topic_ids: Vec<u64>,
|
||||||
|
#[serde(default)]
|
||||||
|
#[serde(rename = "subjectCode")]
|
||||||
|
pub subject_code: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub subtitle: Option<String>,
|
pub subtitle: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub topicIds: Vec<i64>,
|
#[serde(rename = "topicIds")]
|
||||||
// Add a catch-all for any other fields
|
pub topic_ids: Vec<u64>,
|
||||||
#[serde(flatten)]
|
|
||||||
pub extra: HashMap<String, serde_json::Value>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
pub struct Performance {
|
|
||||||
#[serde(default)]
|
|
||||||
pub id: i64,
|
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
pub name: Option<String>,
|
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
pub event: i64,
|
|
||||||
|
|
||||||
// This is the key fix - accept any JSON value type for timestamps
|
|
||||||
// This allows both string dates and integer timestamps (line 3511)
|
|
||||||
#[serde(default)]
|
|
||||||
pub start: serde_json::Value,
|
|
||||||
|
|
||||||
#[serde(rename = "venueCode")]
|
|
||||||
pub venue_code: String,
|
|
||||||
|
|
||||||
// Add a catch-all for any other fields
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub extra: HashMap<String, serde_json::Value>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct SeatCategory {
|
|
||||||
pub id: i64,
|
|
||||||
pub name: Option<String>, // Changed to Option
|
|
||||||
pub areas: Vec<i64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct SubTopic {
|
|
||||||
pub id: i64,
|
|
||||||
pub name: Option<String>, // Changed to Option
|
|
||||||
pub parent: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct Topic {
|
|
||||||
pub id: i64,
|
|
||||||
pub name: Option<String>, // Changed to Option
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct Venue {
|
|
||||||
pub id: i64,
|
|
||||||
pub name: Option<String>, // Changed to Option
|
|
||||||
pub address: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Custom deserializers
|
|
||||||
fn deserialize_string_to_area<'de, D>(deserializer: D) -> Result<HashMap<String, Area>, D::Error>
|
|
||||||
where D: Deserializer<'de> {
|
|
||||||
let string_map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
|
|
||||||
let mut result = HashMap::new();
|
|
||||||
|
|
||||||
for (id, name) in string_map {
|
|
||||||
let id_num = id.parse::<i64>().unwrap_or(0);
|
|
||||||
result.insert(id.clone(), Area {
|
|
||||||
id: id_num,
|
|
||||||
name: Some(name),
|
|
||||||
parent: 0,
|
|
||||||
child_areas: Vec::new(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_string_to_audience_subcategory<'de, D>(deserializer: D) -> Result<HashMap<String, AudienceSubCategory>, D::Error>
|
|
||||||
where D: Deserializer<'de> {
|
|
||||||
let string_map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
|
|
||||||
let mut result = HashMap::new();
|
|
||||||
|
|
||||||
for (id, name) in string_map {
|
|
||||||
let id_num = id.parse::<i64>().unwrap_or(0);
|
|
||||||
result.insert(id.clone(), AudienceSubCategory {
|
|
||||||
id: id_num,
|
|
||||||
name: Some(name),
|
|
||||||
parent: 0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_string_to_seat_category<'de, D>(deserializer: D) -> Result<HashMap<String, SeatCategory>, D::Error>
|
|
||||||
where D: Deserializer<'de> {
|
|
||||||
let string_map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
|
|
||||||
let mut result = HashMap::new();
|
|
||||||
|
|
||||||
for (id, name) in string_map {
|
|
||||||
let id_num = id.parse::<i64>().unwrap_or(0);
|
|
||||||
result.insert(id.clone(), SeatCategory {
|
|
||||||
id: id_num,
|
|
||||||
name: Some(name),
|
|
||||||
areas: Vec::new(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_string_to_subtopic<'de, D>(deserializer: D) -> Result<HashMap<String, SubTopic>, D::Error>
|
|
||||||
where D: Deserializer<'de> {
|
|
||||||
let string_map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
|
|
||||||
let mut result = HashMap::new();
|
|
||||||
|
|
||||||
for (id, name) in string_map {
|
|
||||||
let id_num = id.parse::<i64>().unwrap_or(0);
|
|
||||||
result.insert(id.clone(), SubTopic {
|
|
||||||
id: id_num,
|
|
||||||
name: Some(name),
|
|
||||||
parent: 0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_string_to_topic<'de, D>(deserializer: D) -> Result<HashMap<String, Topic>, D::Error>
|
|
||||||
where D: Deserializer<'de> {
|
|
||||||
let string_map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
|
|
||||||
let mut result = HashMap::new();
|
|
||||||
|
|
||||||
for (id, name) in string_map {
|
|
||||||
let id_num = id.parse::<i64>().unwrap_or(0);
|
|
||||||
result.insert(id.clone(), Topic {
|
|
||||||
id: id_num,
|
|
||||||
name: Some(name),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_string_to_venue<'de, D>(deserializer: D) -> Result<HashMap<String, Venue>, D::Error>
|
|
||||||
where D: Deserializer<'de> {
|
|
||||||
let string_map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
|
|
||||||
let mut result = HashMap::new();
|
|
||||||
|
|
||||||
for (id, name) in string_map {
|
|
||||||
result.insert(id.clone(), Venue {
|
|
||||||
id: 0,
|
|
||||||
name: Some(name),
|
|
||||||
address: 0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Matches C++ CitmCatalog struct exactly - ONLY events and performances
|
||||||
|
/// This is the key fix: we serialize only what C++ serializes
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct CitmCatalog {
|
pub struct CitmCatalog {
|
||||||
#[serde(rename = "areaNames")]
|
pub events: HashMap<String, CITMEvent>,
|
||||||
pub area_names: HashMap<String, String>,
|
pub performances: Vec<CITMPerformance>,
|
||||||
|
|
||||||
#[serde(rename = "audienceSubCategoryNames")]
|
|
||||||
pub audience_subcategory_names: HashMap<String, String>,
|
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
#[serde(rename = "blockNames")]
|
|
||||||
pub block_names: HashMap<String, String>,
|
|
||||||
|
|
||||||
pub events: HashMap<String, Event>,
|
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
pub performances: Vec<Performance>,
|
|
||||||
|
|
||||||
#[serde(rename = "seatCategoryNames")]
|
|
||||||
pub seat_category_names: HashMap<String, String>,
|
|
||||||
|
|
||||||
#[serde(rename = "subTopicNames")]
|
|
||||||
pub subtopic_names: HashMap<String, String>,
|
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
#[serde(rename = "subjectNames")]
|
|
||||||
pub subject_names: HashMap<String, String>,
|
|
||||||
|
|
||||||
#[serde(rename = "topicNames")]
|
|
||||||
pub topic_names: HashMap<String, String>,
|
|
||||||
|
|
||||||
#[serde(rename = "topicSubTopics")]
|
|
||||||
pub topic_subtopics: HashMap<String, Vec<i64>>,
|
|
||||||
|
|
||||||
#[serde(rename = "venueNames")]
|
|
||||||
pub venue_names: HashMap<String, String>,
|
|
||||||
|
|
||||||
// Catch-all for other fields
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub extra: HashMap<String, serde_json::Value>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static mut CITM_DATA: *mut CitmCatalog = std::ptr::null_mut();
|
||||||
|
|
||||||
/// Creates a CitmCatalog from a JSON string (UTF-8 encoded).
|
/// Creates a CitmCatalog from a JSON string (UTF-8 encoded).
|
||||||
|
/// Only extracts events and performances to match C++ behavior.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn citm_from_str(
|
pub unsafe extern "C" fn citm_from_str(
|
||||||
raw_input: *const c_char,
|
raw_input: *const c_char,
|
||||||
@@ -414,7 +174,6 @@ pub unsafe extern "C" fn citm_from_str(
|
|||||||
return ptr::null_mut();
|
return ptr::null_mut();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the raw pointer + length into a Rust slice
|
|
||||||
let bytes = slice::from_raw_parts(raw_input as *const u8, raw_input_length);
|
let bytes = slice::from_raw_parts(raw_input as *const u8, raw_input_length);
|
||||||
let input_str = match std::str::from_utf8(bytes) {
|
let input_str = match std::str::from_utf8(bytes) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
@@ -424,12 +183,23 @@ pub unsafe extern "C" fn citm_from_str(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Try deserializing the input string into CitmCatalog
|
// Parse the full JSON to extract only events and performances
|
||||||
match serde_json::from_str::<CitmCatalog>(input_str) {
|
match serde_json::from_str::<serde_json::Value>(input_str) {
|
||||||
Ok(catalog) => Box::into_raw(Box::new(catalog)),
|
Ok(full_json) => {
|
||||||
|
// Extract only the fields we need (matching C++ behavior)
|
||||||
|
let events: HashMap<String, CITMEvent> = full_json.get("events")
|
||||||
|
.and_then(|v| serde_json::from_value(v.clone()).ok())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let performances: Vec<CITMPerformance> = full_json.get("performances")
|
||||||
|
.and_then(|v| serde_json::from_value(v.clone()).ok())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let catalog = CitmCatalog { events, performances };
|
||||||
|
Box::into_raw(Box::new(catalog))
|
||||||
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Error deserializing JSON: {}", e);
|
eprintln!("Error deserializing JSON: {}", e);
|
||||||
eprintln!("JSON snippet (first 200 chars): {:.200}...", input_str);
|
|
||||||
ptr::null_mut()
|
ptr::null_mut()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -437,30 +207,17 @@ pub unsafe extern "C" fn citm_from_str(
|
|||||||
|
|
||||||
/// Serializes a CitmCatalog into a JSON string (UTF-8).
|
/// Serializes a CitmCatalog into a JSON string (UTF-8).
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn str_from_citm(raw_catalog: *mut CitmCatalog) -> *mut c_char {
|
pub unsafe extern "C" fn set_citm_data(raw: *mut CitmCatalog) {
|
||||||
if raw_catalog.is_null() {
|
CITM_DATA = raw;
|
||||||
eprintln!("Error: Catalog pointer is null");
|
}
|
||||||
return ptr::null_mut();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix: Actually serialize the catalog
|
#[no_mangle]
|
||||||
let catalog = &*raw_catalog;
|
pub unsafe extern "C" fn serialize_citm_to_string() -> usize {
|
||||||
|
if CITM_DATA.is_null() {
|
||||||
match serde_json::to_string(catalog) {
|
return 0;
|
||||||
Ok(serialized) => {
|
|
||||||
match CString::new(serialized) {
|
|
||||||
Ok(cstr) => cstr.into_raw(),
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Error creating CString: {}", e);
|
|
||||||
ptr::null_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Error serializing catalog to JSON: {}", e);
|
|
||||||
ptr::null_mut()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let data = &*CITM_DATA;
|
||||||
|
return serde_json::to_string(data).unwrap().len();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Frees the CitmCatalog pointer.
|
/// Frees the CitmCatalog pointer.
|
||||||
@@ -475,8 +232,120 @@ pub unsafe extern "C" fn free_citm(raw_catalog: *mut CitmCatalog) {
|
|||||||
pub extern "C" fn free_str(ptr: *mut c_char) {
|
pub extern "C" fn free_str(ptr: *mut c_char) {
|
||||||
if !ptr.is_null() {
|
if !ptr.is_null() {
|
||||||
unsafe {
|
unsafe {
|
||||||
// Convert back into a CString, which automatically frees the memory
|
|
||||||
let _ = CString::from_raw(ptr);
|
let _ = CString::from_raw(ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// FFI Overhead Measurement Functions
|
||||||
|
// These allow measuring the actual FFI overhead vs pure Rust serialization
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
/// Result structure for FFI overhead measurement
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct FfiOverheadResult {
|
||||||
|
/// Time in nanoseconds for pure serde_json::to_string() (no FFI overhead)
|
||||||
|
pub pure_serde_ns: u64,
|
||||||
|
/// Time in nanoseconds for serde + CString conversion
|
||||||
|
pub serde_plus_cstring_ns: u64,
|
||||||
|
/// Number of iterations performed
|
||||||
|
pub iterations: u64,
|
||||||
|
/// Output size in bytes (for verification)
|
||||||
|
pub output_size: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prevents compiler from optimizing away the value
|
||||||
|
/// Works on stable Rust (unlike std::hint::black_box which is unstable)
|
||||||
|
#[inline(never)]
|
||||||
|
fn black_box<T>(dummy: T) -> T {
|
||||||
|
unsafe {
|
||||||
|
let ret = std::ptr::read_volatile(&dummy);
|
||||||
|
std::mem::forget(dummy);
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Measures FFI overhead for Twitter serialization.
|
||||||
|
/// Performs `iterations` serializations entirely in Rust and returns timing data.
|
||||||
|
/// This allows comparing against per-call FFI overhead.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn measure_twitter_ffi_overhead(
|
||||||
|
raw: *mut TwitterData,
|
||||||
|
iterations: u64
|
||||||
|
) -> FfiOverheadResult {
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
let twitter_data = &*raw;
|
||||||
|
let output_size: u64;
|
||||||
|
|
||||||
|
// Warm-up run
|
||||||
|
let warmup = serde_json::to_string(&twitter_data).unwrap();
|
||||||
|
output_size = warmup.len() as u64;
|
||||||
|
|
||||||
|
// Measure pure serde_json::to_string() - no CString conversion
|
||||||
|
let start_pure = Instant::now();
|
||||||
|
for _ in 0..iterations {
|
||||||
|
let serialized = serde_json::to_string(&twitter_data).unwrap();
|
||||||
|
// Prevent optimization from eliminating the work
|
||||||
|
black_box(&serialized);
|
||||||
|
}
|
||||||
|
let pure_serde_ns = start_pure.elapsed().as_nanos() as u64;
|
||||||
|
|
||||||
|
// Measure serde + CString conversion (but not FFI return)
|
||||||
|
let start_cstring = Instant::now();
|
||||||
|
for _ in 0..iterations {
|
||||||
|
let serialized = serde_json::to_string(&twitter_data).unwrap();
|
||||||
|
let cstring = CString::new(serialized).unwrap();
|
||||||
|
// Prevent optimization from eliminating the work
|
||||||
|
black_box(&cstring);
|
||||||
|
}
|
||||||
|
let serde_plus_cstring_ns = start_cstring.elapsed().as_nanos() as u64;
|
||||||
|
|
||||||
|
FfiOverheadResult {
|
||||||
|
pure_serde_ns,
|
||||||
|
serde_plus_cstring_ns,
|
||||||
|
iterations,
|
||||||
|
output_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Measures FFI overhead for CITM serialization.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn measure_citm_ffi_overhead(
|
||||||
|
raw: *mut CitmCatalog,
|
||||||
|
iterations: u64
|
||||||
|
) -> FfiOverheadResult {
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
let catalog = &*raw;
|
||||||
|
let output_size: u64;
|
||||||
|
|
||||||
|
// Warm-up run
|
||||||
|
let warmup = serde_json::to_string(&catalog).unwrap();
|
||||||
|
output_size = warmup.len() as u64;
|
||||||
|
|
||||||
|
// Measure pure serde_json::to_string() - no CString conversion
|
||||||
|
let start_pure = Instant::now();
|
||||||
|
for _ in 0..iterations {
|
||||||
|
let serialized = serde_json::to_string(&catalog).unwrap();
|
||||||
|
black_box(&serialized);
|
||||||
|
}
|
||||||
|
let pure_serde_ns = start_pure.elapsed().as_nanos() as u64;
|
||||||
|
|
||||||
|
// Measure serde + CString conversion
|
||||||
|
let start_cstring = Instant::now();
|
||||||
|
for _ in 0..iterations {
|
||||||
|
let serialized = serde_json::to_string(&catalog).unwrap();
|
||||||
|
let cstring = CString::new(serialized).unwrap();
|
||||||
|
black_box(&cstring);
|
||||||
|
}
|
||||||
|
let serde_plus_cstring_ns = start_cstring.elapsed().as_nanos() as u64;
|
||||||
|
|
||||||
|
FfiOverheadResult {
|
||||||
|
pure_serde_ns,
|
||||||
|
serde_plus_cstring_ns,
|
||||||
|
iterations,
|
||||||
|
output_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
/* Generated with cbindgen:0.28.0 */
|
/* Generated with cbindgen:0.28.0 */
|
||||||
|
|
||||||
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
|
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
|
||||||
|
/* Note: FfiOverheadResult and measurement functions added manually */
|
||||||
|
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -17,11 +18,25 @@ struct CitmCatalog;
|
|||||||
|
|
||||||
struct TwitterData;
|
struct TwitterData;
|
||||||
|
|
||||||
|
/// Result structure for FFI overhead measurement
|
||||||
|
struct FfiOverheadResult {
|
||||||
|
/// Time in nanoseconds for pure serde_json::to_string() (no FFI overhead)
|
||||||
|
uint64_t pure_serde_ns;
|
||||||
|
/// Time in nanoseconds for serde + CString conversion
|
||||||
|
uint64_t serde_plus_cstring_ns;
|
||||||
|
/// Number of iterations performed
|
||||||
|
uint64_t iterations;
|
||||||
|
/// Output size in bytes (for verification)
|
||||||
|
uint64_t output_size;
|
||||||
|
};
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
TwitterData *twitter_from_str(const char *raw_input, size_t raw_input_length);
|
TwitterData *twitter_from_str(const char *raw_input, size_t raw_input_length);
|
||||||
|
|
||||||
const char *str_from_twitter(TwitterData *raw);
|
void set_twitter_data(TwitterData *raw);
|
||||||
|
|
||||||
|
size_t serialize_twitter_to_string();
|
||||||
|
|
||||||
void free_twitter(TwitterData *raw);
|
void free_twitter(TwitterData *raw);
|
||||||
|
|
||||||
@@ -30,14 +45,22 @@ void free_string(const char *ptr);
|
|||||||
/// Creates a CitmCatalog from a JSON string (UTF-8 encoded).
|
/// Creates a CitmCatalog from a JSON string (UTF-8 encoded).
|
||||||
CitmCatalog *citm_from_str(const char *raw_input, uintptr_t raw_input_length);
|
CitmCatalog *citm_from_str(const char *raw_input, uintptr_t raw_input_length);
|
||||||
|
|
||||||
/// Serializes a CitmCatalog into a JSON string (UTF-8).
|
void set_citm_data(CitmCatalog *raw);
|
||||||
char *str_from_citm(CitmCatalog *raw_catalog);
|
|
||||||
|
size_t serialize_citm_to_string();
|
||||||
|
|
||||||
/// Frees the CitmCatalog pointer.
|
/// Frees the CitmCatalog pointer.
|
||||||
void free_citm(CitmCatalog *raw_catalog);
|
void free_citm(CitmCatalog *raw_catalog);
|
||||||
|
|
||||||
void free_str(char *ptr);
|
void free_str(char *ptr);
|
||||||
|
|
||||||
|
/// Measures FFI overhead for Twitter serialization.
|
||||||
|
/// Performs `iterations` serializations entirely in Rust and returns timing data.
|
||||||
|
FfiOverheadResult measure_twitter_ffi_overhead(TwitterData *raw, uint64_t iterations);
|
||||||
|
|
||||||
|
/// Measures FFI overhead for CITM serialization.
|
||||||
|
FfiOverheadResult measure_citm_ffi_overhead(CitmCatalog *raw, uint64_t iterations);
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
||||||
} // namespace serde_benchmark
|
} // namespace serde_benchmark
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
use std::fs;
|
||||||
|
|
||||||
|
// Include the lib.rs content directly
|
||||||
|
include!("../lib.rs");
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Read the Twitter JSON file
|
||||||
|
let json_str = fs::read_to_string("/Users/random_person/Desktop/simdjson/build/jsonexamples/twitter.json")
|
||||||
|
.expect("Failed to read file");
|
||||||
|
|
||||||
|
// Parse it
|
||||||
|
let data: TwitterData = serde_json::from_str(&json_str)
|
||||||
|
.expect("Failed to parse JSON");
|
||||||
|
|
||||||
|
// Serialize it back
|
||||||
|
let output = serde_json::to_string(&data)
|
||||||
|
.expect("Failed to serialize");
|
||||||
|
|
||||||
|
// Write to file for comparison
|
||||||
|
fs::write("rust_output.json", &output)
|
||||||
|
.expect("Failed to write output");
|
||||||
|
|
||||||
|
println!("Output size: {} bytes", output.len());
|
||||||
|
println!("Written to rust_output.json");
|
||||||
|
|
||||||
|
// Also write pretty version for easier inspection
|
||||||
|
let pretty = serde_json::to_string_pretty(&data)
|
||||||
|
.expect("Failed to serialize pretty");
|
||||||
|
fs::write("rust_output_pretty.json", &pretty)
|
||||||
|
.expect("Failed to write pretty output");
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
use std::fs;
|
||||||
|
|
||||||
|
// Import from the parent lib.rs
|
||||||
|
include!("../lib.rs");
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Read the Twitter JSON file
|
||||||
|
let json_str = fs::read_to_string("/Users/random_person/Desktop/simdjson/build/jsonexamples/twitter.json")
|
||||||
|
.expect("Failed to read file");
|
||||||
|
|
||||||
|
// Parse it
|
||||||
|
let data: TwitterData = serde_json::from_str(&json_str)
|
||||||
|
.expect("Failed to parse JSON");
|
||||||
|
|
||||||
|
// Serialize it back (compact)
|
||||||
|
let output = serde_json::to_vec(&data)
|
||||||
|
.expect("Failed to serialize");
|
||||||
|
|
||||||
|
let output_str = String::from_utf8(output.clone()).unwrap();
|
||||||
|
|
||||||
|
// Write to file for comparison
|
||||||
|
fs::write("rust_output_test.json", &output)
|
||||||
|
.expect("Failed to write output");
|
||||||
|
|
||||||
|
println!("Output size: {} bytes", output.len());
|
||||||
|
|
||||||
|
// Count statuses
|
||||||
|
println!("Number of statuses: {}", data.statuses.len());
|
||||||
|
|
||||||
|
// Check what fields are in the first status
|
||||||
|
if let Some(first) = data.statuses.first() {
|
||||||
|
// Let's serialize just the first status to see what fields are included
|
||||||
|
let first_json = serde_json::to_string_pretty(first).unwrap();
|
||||||
|
println!("First status (pretty):\n{}", first_json);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,32 @@
|
|||||||
|
|
||||||
# Add executable targets
|
# Add executable targets
|
||||||
add_executable(benchmark_serialization_twitter benchmark_serialization_twitter.cpp)
|
add_executable(benchmark_serialization_twitter benchmark_serialization_twitter.cpp)
|
||||||
|
add_executable(benchmark_parsing_twitter benchmark_parsing_twitter.cpp)
|
||||||
|
|
||||||
if(TARGET serde-benchmark)
|
if(TARGET serde-benchmark)
|
||||||
message(STATUS "serde-benchmark target was created. Linking benchmarks and serde-benchmark.")
|
message(STATUS "serde-benchmark target was created. Linking benchmarks and serde-benchmark.")
|
||||||
target_link_libraries(benchmark_serialization_twitter PRIVATE serde-benchmark)
|
target_link_libraries(benchmark_serialization_twitter PRIVATE serde-benchmark)
|
||||||
|
target_link_libraries(benchmark_parsing_twitter PRIVATE serde-benchmark)
|
||||||
target_compile_definitions(benchmark_serialization_twitter PRIVATE SIMDJSON_RUST_VERSION="${Rust_VERSION}")
|
target_compile_definitions(benchmark_serialization_twitter PRIVATE SIMDJSON_RUST_VERSION="${Rust_VERSION}")
|
||||||
|
target_compile_definitions(benchmark_parsing_twitter PRIVATE SIMDJSON_RUST_VERSION="${Rust_VERSION}")
|
||||||
endif()
|
endif()
|
||||||
target_link_libraries(benchmark_serialization_twitter PRIVATE simdjson::simdjson nlohmann_json)
|
target_link_libraries(benchmark_serialization_twitter PRIVATE simdjson::simdjson nlohmann_json)
|
||||||
target_link_libraries(benchmark_serialization_twitter PRIVATE reflectcpp)
|
target_link_libraries(benchmark_serialization_twitter PRIVATE reflectcpp)
|
||||||
target_compile_definitions(benchmark_serialization_twitter PRIVATE SIMDJSON_BENCH_CPP_REFLECT=1)
|
target_compile_definitions(benchmark_serialization_twitter PRIVATE SIMDJSON_BENCH_CPP_REFLECT=1)
|
||||||
|
|
||||||
|
target_link_libraries(benchmark_parsing_twitter PRIVATE simdjson::simdjson nlohmann_json)
|
||||||
|
|
||||||
|
if(TARGET rapidjson)
|
||||||
|
target_link_libraries(benchmark_parsing_twitter PRIVATE rapidjson)
|
||||||
|
target_compile_definitions(benchmark_parsing_twitter PRIVATE SIMDJSON_COMPETITION_RAPIDJSON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(TARGET yyjson)
|
||||||
|
target_link_libraries(benchmark_parsing_twitter PRIVATE yyjson)
|
||||||
|
target_compile_definitions(benchmark_parsing_twitter PRIVATE SIMDJSON_COMPETITION_YYJSON)
|
||||||
|
target_link_libraries(benchmark_serialization_twitter PRIVATE yyjson)
|
||||||
|
target_compile_definitions(benchmark_serialization_twitter PRIVATE SIMDJSON_COMPETITION_YYJSON)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_compile_definitions(benchmark_serialization_twitter PRIVATE JSON_FILE="${EXAMPLE_JSON}")
|
target_compile_definitions(benchmark_serialization_twitter PRIVATE JSON_FILE="${EXAMPLE_JSON}")
|
||||||
|
target_compile_definitions(benchmark_parsing_twitter PRIVATE JSON_FILE="${EXAMPLE_JSON}")
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
#include <cassert>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <ctime>
|
||||||
|
#include <format>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <simdjson.h>
|
||||||
|
#include <string>
|
||||||
|
#include "twitter_data.h"
|
||||||
|
#include "nlohmann_twitter_data.h"
|
||||||
|
#include "../benchmark_utils/benchmark_helper.h"
|
||||||
|
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
|
||||||
|
#include "rapidjson_twitter_data.h"
|
||||||
|
#endif
|
||||||
|
#ifdef SIMDJSON_COMPETITION_YYJSON
|
||||||
|
#include "yyjson_twitter_data.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_RUST_VERSION
|
||||||
|
#include "../serde-benchmark/serde_benchmark.h"
|
||||||
|
|
||||||
|
void bench_rust_parsing(const std::string &json_str) {
|
||||||
|
size_t input_volume = json_str.size();
|
||||||
|
printf("# input volume: %zu bytes\n", input_volume);
|
||||||
|
|
||||||
|
volatile bool result = true;
|
||||||
|
pretty_print(1, input_volume, "bench_rust_parsing",
|
||||||
|
bench([&json_str, &result]() {
|
||||||
|
serde_benchmark::TwitterData *td = serde_benchmark::twitter_from_str(json_str.c_str(), json_str.size());
|
||||||
|
result = (td != nullptr);
|
||||||
|
if (td) {
|
||||||
|
serde_benchmark::free_twitter(td);
|
||||||
|
}
|
||||||
|
if (!result) {
|
||||||
|
printf("parse error\n");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// OPTIMIZED VERSION: Reuses parser across iterations
|
||||||
|
template <class T>
|
||||||
|
void bench_simdjson_static_reflection_parsing(const std::string &json_str) {
|
||||||
|
size_t input_volume = json_str.size();
|
||||||
|
printf("# input volume: %zu bytes\n", input_volume);
|
||||||
|
|
||||||
|
// Pre-allocate padded buffer outside the benchmark loop
|
||||||
|
simdjson::padded_string padded = simdjson::padded_string(json_str);
|
||||||
|
|
||||||
|
// CRITICAL: Create parser OUTSIDE the loop for reuse
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
|
||||||
|
volatile bool result = true;
|
||||||
|
pretty_print(1, input_volume, "bench_simdjson_static_reflection_parsing",
|
||||||
|
bench([&padded, &result, &parser]() {
|
||||||
|
// Reuse the same parser instance
|
||||||
|
simdjson::ondemand::document doc;
|
||||||
|
if(parser.iterate(padded).get(doc)) {
|
||||||
|
result = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
T my_struct;
|
||||||
|
if(doc.get<T>().get(my_struct)) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
if (!result) {
|
||||||
|
printf("parse error\n");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
|
template <class T>
|
||||||
|
void bench_simdjson_from_parsing(const std::string &json_str) {
|
||||||
|
size_t input_volume = json_str.size();
|
||||||
|
printf("# input volume: %zu bytes\n", input_volume);
|
||||||
|
|
||||||
|
// Pre-allocate padded buffer outside the benchmark loop
|
||||||
|
simdjson::padded_string padded = simdjson::padded_string(json_str);
|
||||||
|
|
||||||
|
volatile bool result = true;
|
||||||
|
pretty_print(1, input_volume, "bench_simdjson_from_parsing",
|
||||||
|
bench([&padded, &result]() {
|
||||||
|
T my_struct;
|
||||||
|
auto err = simdjson::from(padded).get(my_struct);
|
||||||
|
if (err) {
|
||||||
|
result = false;
|
||||||
|
printf("parse error: %s\n", simdjson::error_message(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void bench_nlohmann_parsing(const std::string &json_str) {
|
||||||
|
size_t input_volume = json_str.size();
|
||||||
|
printf("# input volume: %zu bytes\n", input_volume);
|
||||||
|
|
||||||
|
volatile bool result = true;
|
||||||
|
pretty_print(1, input_volume, "bench_nlohmann_parsing",
|
||||||
|
bench([&json_str, &result]() {
|
||||||
|
try {
|
||||||
|
TwitterData data = nlohmann_deserialize(json_str);
|
||||||
|
result = true;
|
||||||
|
} catch (...) {
|
||||||
|
result = false;
|
||||||
|
printf("parse error\n");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
|
||||||
|
void bench_rapidjson_parsing(const std::string &json_str) {
|
||||||
|
size_t input_volume = json_str.size();
|
||||||
|
printf("# input volume: %zu bytes\n", input_volume);
|
||||||
|
|
||||||
|
volatile bool result = true;
|
||||||
|
pretty_print(1, input_volume, "bench_rapidjson_parsing",
|
||||||
|
bench([&json_str, &result]() {
|
||||||
|
try {
|
||||||
|
TwitterData data = rapidjson_deserialize(json_str);
|
||||||
|
result = true;
|
||||||
|
} catch (...) {
|
||||||
|
result = false;
|
||||||
|
printf("parse error\n");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_YYJSON
|
||||||
|
void bench_yyjson_parsing(const std::string &json_str) {
|
||||||
|
size_t input_volume = json_str.size();
|
||||||
|
printf("# input volume: %zu bytes\n", input_volume);
|
||||||
|
|
||||||
|
volatile bool result = true;
|
||||||
|
pretty_print(1, input_volume, "bench_yyjson_parsing",
|
||||||
|
bench([&json_str, &result]() {
|
||||||
|
try {
|
||||||
|
TwitterData data = yyjson_deserialize(json_str);
|
||||||
|
result = true;
|
||||||
|
} catch (...) {
|
||||||
|
result = false;
|
||||||
|
printf("parse error\n");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::string read_file(std::string filename) {
|
||||||
|
printf("# Reading file %s\n", filename.c_str());
|
||||||
|
constexpr size_t read_size = 4096;
|
||||||
|
auto stream = std::ifstream(filename.c_str());
|
||||||
|
stream.exceptions(std::ios_base::badbit);
|
||||||
|
std::string out;
|
||||||
|
std::string buf(read_size, '\0');
|
||||||
|
while (stream.read(&buf[0], read_size)) {
|
||||||
|
out.append(buf, 0, size_t(stream.gcount()));
|
||||||
|
}
|
||||||
|
out.append(buf, 0, size_t(stream.gcount()));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to check if benchmark name matches any of the comma-separated filters
|
||||||
|
bool matches_filter(const std::string& benchmark_name, const std::string& filter) {
|
||||||
|
if (filter.empty()) return true;
|
||||||
|
|
||||||
|
// Split filter by comma
|
||||||
|
size_t start = 0;
|
||||||
|
size_t end = filter.find(',');
|
||||||
|
while (end != std::string::npos) {
|
||||||
|
std::string token = filter.substr(start, end - start);
|
||||||
|
if (benchmark_name.find(token) != std::string::npos) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
start = end + 1;
|
||||||
|
end = filter.find(',', start);
|
||||||
|
}
|
||||||
|
// Check last token
|
||||||
|
std::string token = filter.substr(start);
|
||||||
|
return benchmark_name.find(token) != std::string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
std::string filter;
|
||||||
|
|
||||||
|
// Parse command-line arguments
|
||||||
|
for (int i = 1; i < argc; ++i) {
|
||||||
|
if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--filter") == 0) {
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
filter = argv[++i];
|
||||||
|
} else {
|
||||||
|
std::cerr << "Error: -f/--filter requires an argument" << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the JSON data
|
||||||
|
std::string json_str = read_file(JSON_FILE);
|
||||||
|
|
||||||
|
// Benchmarking the parsing
|
||||||
|
if (matches_filter("nlohmann", filter)) {
|
||||||
|
bench_nlohmann_parsing(json_str);
|
||||||
|
}
|
||||||
|
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
|
||||||
|
if (matches_filter("rapidjson", filter)) {
|
||||||
|
bench_rapidjson_parsing(json_str);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef SIMDJSON_COMPETITION_YYJSON
|
||||||
|
if (matches_filter("yyjson", filter)) {
|
||||||
|
bench_yyjson_parsing(json_str);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (matches_filter("simdjson_static_reflection", filter)) {
|
||||||
|
bench_simdjson_static_reflection_parsing<TwitterData>(json_str);
|
||||||
|
}
|
||||||
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
|
if (matches_filter("simdjson_from", filter)) {
|
||||||
|
bench_simdjson_from_parsing<TwitterData>(json_str);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef SIMDJSON_RUST_VERSION
|
||||||
|
if (matches_filter("rust", filter)) {
|
||||||
|
printf("# Note: Rust/Serde parsing test\n");
|
||||||
|
bench_rust_parsing(json_str);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <chrono>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <format>
|
#include <format>
|
||||||
@@ -10,6 +11,9 @@
|
|||||||
#include "twitter_data.h"
|
#include "twitter_data.h"
|
||||||
#include "nlohmann_twitter_data.h"
|
#include "nlohmann_twitter_data.h"
|
||||||
#include "../benchmark_utils/benchmark_helper.h"
|
#include "../benchmark_utils/benchmark_helper.h"
|
||||||
|
#ifdef SIMDJSON_COMPETITION_YYJSON
|
||||||
|
#include "yyjson_twitter_data.h"
|
||||||
|
#endif
|
||||||
#if SIMDJSON_BENCH_CPP_REFLECT
|
#if SIMDJSON_BENCH_CPP_REFLECT
|
||||||
#include <rfl.hpp>
|
#include <rfl.hpp>
|
||||||
#include <rfl/json.hpp>
|
#include <rfl/json.hpp>
|
||||||
@@ -35,19 +39,49 @@ void bench_reflect_cpp(TwitterData &data) {
|
|||||||
|
|
||||||
|
|
||||||
void bench_rust(serde_benchmark::TwitterData *data) {
|
void bench_rust(serde_benchmark::TwitterData *data) {
|
||||||
const char * output = serde_benchmark::str_from_twitter(data);
|
serde_benchmark::set_twitter_data(data);
|
||||||
size_t output_volume = strlen(output);
|
size_t output_volume = serde_benchmark::serialize_twitter_to_string();
|
||||||
printf("# output volume: %zu bytes\n", output_volume);
|
printf("# output volume: %zu bytes\n", output_volume);
|
||||||
|
|
||||||
volatile size_t measured_volume = 0;
|
volatile size_t measured_volume = 0;
|
||||||
pretty_print(1, output_volume, "bench_rust",
|
pretty_print(1, output_volume, "bench_rust",
|
||||||
bench([&data, &measured_volume, &output_volume]() {
|
bench([&measured_volume, &output_volume]() {
|
||||||
const char * output = serde_benchmark::str_from_twitter(data);
|
measured_volume = serde_benchmark::serialize_twitter_to_string();
|
||||||
serde_benchmark::free_string(output);
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Fair allocation variant: allocates fresh buffer each iteration (matches other libraries)
|
||||||
template <class T> void bench_simdjson_static_reflection(T &data) {
|
template <class T> void bench_simdjson_static_reflection(T &data) {
|
||||||
|
// First run to determine expected size
|
||||||
|
simdjson::builder::string_builder sb_init;
|
||||||
|
simdjson::builder::append(sb_init, data);
|
||||||
|
std::string_view p_init;
|
||||||
|
if(sb_init.view().get(p_init)) {
|
||||||
|
std::cerr << "Error!" << std::endl;
|
||||||
|
}
|
||||||
|
size_t output_volume = p_init.size();
|
||||||
|
printf("# output volume: %zu bytes\n", output_volume);
|
||||||
|
|
||||||
|
volatile size_t measured_volume = 0;
|
||||||
|
pretty_print(sizeof(data), output_volume, "bench_simdjson_static_reflection",
|
||||||
|
bench([&data, &measured_volume, &output_volume]() {
|
||||||
|
// Fresh allocation each iteration - fair comparison
|
||||||
|
simdjson::builder::string_builder sb;
|
||||||
|
simdjson::builder::append(sb, data);
|
||||||
|
std::string_view p;
|
||||||
|
if(sb.view().get(p)) {
|
||||||
|
std::cerr << "Error!" << std::endl;
|
||||||
|
}
|
||||||
|
measured_volume = sb.size();
|
||||||
|
if (measured_volume != output_volume) {
|
||||||
|
printf("mismatch\n");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimized variant: reuses buffer across iterations (shows API potential)
|
||||||
|
template <class T> void bench_simdjson_static_reflection_reuse(T &data) {
|
||||||
simdjson::builder::string_builder sb;
|
simdjson::builder::string_builder sb;
|
||||||
simdjson::builder::append(sb, data);
|
simdjson::builder::append(sb, data);
|
||||||
std::string_view p;
|
std::string_view p;
|
||||||
@@ -59,7 +93,7 @@ template <class T> void bench_simdjson_static_reflection(T &data) {
|
|||||||
printf("# output volume: %zu bytes\n", output_volume);
|
printf("# output volume: %zu bytes\n", output_volume);
|
||||||
|
|
||||||
volatile size_t measured_volume = 0;
|
volatile size_t measured_volume = 0;
|
||||||
pretty_print(sizeof(data), output_volume, "bench_simdjson_static_reflection",
|
pretty_print(sizeof(data), output_volume, "bench_simdjson_reuse_buffer",
|
||||||
bench([&data, &measured_volume, &output_volume, &sb]() {
|
bench([&data, &measured_volume, &output_volume, &sb]() {
|
||||||
sb.clear();
|
sb.clear();
|
||||||
simdjson::builder::append(sb, data);
|
simdjson::builder::append(sb, data);
|
||||||
@@ -74,6 +108,63 @@ template <class T> void bench_simdjson_static_reflection(T &data) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
|
// Fair allocation variant: allocates fresh string each iteration
|
||||||
|
template <class T> void bench_simdjson_to(T &data) {
|
||||||
|
// First run to determine size
|
||||||
|
std::string output_init;
|
||||||
|
if (simdjson::error_code err = simdjson::builder::to_json(data, output_init); err) {
|
||||||
|
std::cerr << "Error in to_json initialization!" << simdjson::error_message(err) << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t output_volume = output_init.size();
|
||||||
|
printf("# output volume: %zu bytes\n", output_volume);
|
||||||
|
|
||||||
|
volatile size_t measured_volume = 0;
|
||||||
|
pretty_print(sizeof(data), output_volume, "bench_simdjson_to",
|
||||||
|
bench([&data, &measured_volume, &output_volume]() {
|
||||||
|
// Fresh allocation each iteration - fair comparison
|
||||||
|
std::string output;
|
||||||
|
if (simdjson::error_code err = simdjson::builder::to_json(data, output); err) {
|
||||||
|
std::cerr << "Error in to_json!" << simdjson::error_message(err) << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
measured_volume = output.size();
|
||||||
|
if (measured_volume != output_volume) {
|
||||||
|
printf("mismatch\n");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimized variant: reuses pre-allocated string
|
||||||
|
template <class T> void bench_simdjson_to_reuse(T &data) {
|
||||||
|
std::string output;
|
||||||
|
if (simdjson::error_code err = simdjson::builder::to_json(data, output); err) {
|
||||||
|
std::cerr << "Error in to_json initialization!" << simdjson::error_message(err) << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t output_volume = output.size();
|
||||||
|
printf("# output volume: %zu bytes\n", output_volume);
|
||||||
|
|
||||||
|
// Pre-allocate string with sufficient capacity to avoid reallocation
|
||||||
|
output.reserve(output_volume * 2);
|
||||||
|
|
||||||
|
volatile size_t measured_volume = 0;
|
||||||
|
pretty_print(sizeof(data), output_volume, "bench_simdjson_to_reuse",
|
||||||
|
bench([&data, &measured_volume, &output_volume, &output]() {
|
||||||
|
// Reuse the pre-allocated string - avoids allocation
|
||||||
|
if (simdjson::error_code err = simdjson::builder::to_json(data, output); err) {
|
||||||
|
std::cerr << "Error in to_json!" << simdjson::error_message(err) << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
measured_volume = output.size();
|
||||||
|
if (measured_volume != output_volume) {
|
||||||
|
printf("mismatch\n");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void bench_nlohmann(TwitterData &data) {
|
void bench_nlohmann(TwitterData &data) {
|
||||||
std::string output = nlohmann_serialize(data);
|
std::string output = nlohmann_serialize(data);
|
||||||
size_t output_volume = output.size();
|
size_t output_volume = output.size();
|
||||||
@@ -90,28 +181,61 @@ void bench_nlohmann(TwitterData &data) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_YYJSON
|
||||||
|
void bench_yyjson(TwitterData &data) {
|
||||||
|
std::string output = yyjson_serialize(data);
|
||||||
|
size_t output_volume = output.size();
|
||||||
|
printf("# output volume: %zu bytes\n", output_volume);
|
||||||
|
|
||||||
|
volatile size_t measured_volume = 0;
|
||||||
|
pretty_print(1, output_volume, "bench_yyjson",
|
||||||
|
bench([&data, &measured_volume, &output_volume]() {
|
||||||
|
std::string output = yyjson_serialize(data);
|
||||||
|
measured_volume = output.size();
|
||||||
|
if (measured_volume != output_volume) {
|
||||||
|
printf("mismatch\n");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) {
|
size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) {
|
||||||
((std::string *)userp)->append((char *)contents, size * nmemb);
|
((std::string *)userp)->append((char *)contents, size * nmemb);
|
||||||
return size * nmemb;
|
return size * nmemb;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string read_file(std::string filename) {
|
simdjson::padded_string read_file(std::string filename) {
|
||||||
printf("# Reading file %s\n", filename.c_str());
|
printf("# Reading file %s\n", filename.c_str());
|
||||||
constexpr size_t read_size = 4096;
|
constexpr size_t read_size = 4096;
|
||||||
auto stream = std::ifstream(filename.c_str());
|
auto stream = std::ifstream(filename.c_str());
|
||||||
stream.exceptions(std::ios_base::badbit);
|
stream.exceptions(std::ios_base::badbit);
|
||||||
std::string out;
|
simdjson::padded_string_builder builder;
|
||||||
std::string buf(read_size, '\0');
|
std::string buf(read_size, '\0');
|
||||||
while (stream.read(&buf[0], read_size)) {
|
while (stream.read(&buf[0], read_size)) {
|
||||||
out.append(buf, 0, size_t(stream.gcount()));
|
builder.append(buf.data(), size_t(stream.gcount()));
|
||||||
}
|
}
|
||||||
out.append(buf, 0, size_t(stream.gcount()));
|
builder.append(buf.data(), size_t(stream.gcount()));
|
||||||
return out;
|
return builder.convert();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to check if benchmark name contains filter substring
|
// Function to check if benchmark name matches any of the comma-separated filters
|
||||||
bool matches_filter(const std::string& benchmark_name, const std::string& filter) {
|
bool matches_filter(const std::string& benchmark_name, const std::string& filter) {
|
||||||
return filter.empty() || benchmark_name.find(filter) != std::string::npos;
|
if (filter.empty()) return true;
|
||||||
|
|
||||||
|
// Split filter by comma
|
||||||
|
size_t start = 0;
|
||||||
|
size_t end = filter.find(',');
|
||||||
|
while (end != std::string::npos) {
|
||||||
|
std::string token = filter.substr(start, end - start);
|
||||||
|
if (benchmark_name.find(token) != std::string::npos) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
start = end + 1;
|
||||||
|
end = filter.find(',', start);
|
||||||
|
}
|
||||||
|
// Check last token
|
||||||
|
std::string token = filter.substr(start);
|
||||||
|
return benchmark_name.find(token) != std::string::npos;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
@@ -129,12 +253,12 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Testing correctness of round-trip (serialization + deserialization)
|
// Testing correctness of round-trip (serialization + deserialization)
|
||||||
std::string json_str = read_file(JSON_FILE);
|
simdjson::padded_string json_str = read_file(JSON_FILE);
|
||||||
|
|
||||||
// Loading up the data into a structure.
|
// Loading up the data into a structure.
|
||||||
simdjson::ondemand::parser parser;
|
simdjson::ondemand::parser parser;
|
||||||
simdjson::ondemand::document doc;
|
simdjson::ondemand::document doc;
|
||||||
if(parser.iterate(simdjson::pad(json_str)).get(doc)) {
|
if(parser.iterate(json_str).get(doc)) {
|
||||||
std::cerr << "Error loading the document!" << std::endl;
|
std::cerr << "Error loading the document!" << std::endl;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
@@ -145,18 +269,41 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Benchmarking the serialization
|
// Benchmarking the serialization
|
||||||
|
// Note: simdjson benchmarks include both "fair" (fresh allocation) and "reuse" (buffer reuse) variants
|
||||||
|
// The "fair" variants allocate fresh memory each iteration, matching other libraries' behavior
|
||||||
|
// The "reuse" variants demonstrate the API's potential when buffer reuse is possible
|
||||||
|
|
||||||
if (matches_filter("nlohmann", filter)) {
|
if (matches_filter("nlohmann", filter)) {
|
||||||
bench_nlohmann(my_struct);
|
bench_nlohmann(my_struct);
|
||||||
}
|
}
|
||||||
|
#ifdef SIMDJSON_COMPETITION_YYJSON
|
||||||
|
if (matches_filter("yyjson", filter)) {
|
||||||
|
bench_yyjson(my_struct);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (matches_filter("simdjson_static_reflection", filter)) {
|
if (matches_filter("simdjson_static_reflection", filter)) {
|
||||||
bench_simdjson_static_reflection(my_struct);
|
bench_simdjson_static_reflection(my_struct);
|
||||||
}
|
}
|
||||||
|
if (matches_filter("simdjson_reuse", filter)) {
|
||||||
|
bench_simdjson_static_reflection_reuse(my_struct);
|
||||||
|
}
|
||||||
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
|
if (matches_filter("simdjson_to", filter)) {
|
||||||
|
bench_simdjson_to(my_struct);
|
||||||
|
}
|
||||||
|
if (matches_filter("simdjson_to_reuse", filter)) {
|
||||||
|
bench_simdjson_to_reuse(my_struct);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef SIMDJSON_RUST_VERSION
|
#ifdef SIMDJSON_RUST_VERSION
|
||||||
if (matches_filter("rust", filter)) {
|
if (matches_filter("rust", filter)) {
|
||||||
printf("# WARNING: The Rust benchmark may not be directly comparable since it does not use an equivalent data structure.\n");
|
serde_benchmark::TwitterData * td = serde_benchmark::twitter_from_str(json_str.data(), json_str.size());
|
||||||
serde_benchmark::TwitterData * td = serde_benchmark::twitter_from_str(json_str.c_str(), json_str.size());
|
if (td == nullptr) {
|
||||||
bench_rust(td);
|
printf("# Failed to parse Twitter data for Rust benchmark\n");
|
||||||
serde_benchmark::free_twitter(td);
|
} else {
|
||||||
|
bench_rust(td);
|
||||||
|
serde_benchmark::free_twitter(td);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if SIMDJSON_BENCH_CPP_REFLECT
|
#if SIMDJSON_BENCH_CPP_REFLECT
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "twitter_data.h"
|
#include "twitter_data.h"
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
// Serialization functions for nlohmann
|
||||||
void to_json(nlohmann::json &j, const User &u) {
|
void to_json(nlohmann::json &j, const User &u) {
|
||||||
j = nlohmann::json{{"id", u.id},
|
j = nlohmann::json{{"id", u.id},
|
||||||
{"name", u.name},
|
{"name", u.name},
|
||||||
@@ -16,101 +17,54 @@ void to_json(nlohmann::json &j, const User &u) {
|
|||||||
{"statuses_count", u.statuses_count}};
|
{"statuses_count", u.statuses_count}};
|
||||||
}
|
}
|
||||||
|
|
||||||
void to_json(nlohmann::json &j, const Hashtag &h) {
|
|
||||||
j = nlohmann::json{{"text", h.text},
|
|
||||||
{"indices_start", h.indices_start},
|
|
||||||
{"indices_end", h.indices_end}};
|
|
||||||
}
|
|
||||||
|
|
||||||
void to_json(nlohmann::json &j, const Url &u) {
|
|
||||||
j = nlohmann::json{{"url", u.url},
|
|
||||||
{"expanded_url", u.expanded_url},
|
|
||||||
{"display_url", u.display_url},
|
|
||||||
{"indices_start", u.indices_start},
|
|
||||||
{"indices_end", u.indices_end}};
|
|
||||||
}
|
|
||||||
|
|
||||||
void to_json(nlohmann::json &j, const UserMention &um) {
|
|
||||||
j = nlohmann::json{{"id", um.id},
|
|
||||||
{"name", um.name},
|
|
||||||
{"screen_name", um.screen_name},
|
|
||||||
{"indices_start", um.indices_start},
|
|
||||||
{"indices_end", um.indices_end}};
|
|
||||||
}
|
|
||||||
|
|
||||||
void to_json(nlohmann::json &j, const Entities &e) {
|
|
||||||
j = nlohmann::json{{"hashtags", e.hashtags},
|
|
||||||
{"urls", e.urls},
|
|
||||||
{"user_mentions", e.user_mentions}};
|
|
||||||
}
|
|
||||||
|
|
||||||
void to_json(nlohmann::json &j, const Status &s) {
|
void to_json(nlohmann::json &j, const Status &s) {
|
||||||
j = nlohmann::json{{"created_at", s.created_at},
|
j = nlohmann::json{{"created_at", s.created_at},
|
||||||
{"id", s.id},
|
{"id", s.id},
|
||||||
{"text", s.text},
|
{"text", s.text},
|
||||||
{"user", s.user},
|
{"user", s.user},
|
||||||
{"entities", s.entities},
|
|
||||||
{"retweet_count", s.retweet_count},
|
{"retweet_count", s.retweet_count},
|
||||||
{"favorite_count", s.favorite_count},
|
{"favorite_count", s.favorite_count}};
|
||||||
{"favorited", s.favorited},
|
|
||||||
{"retweeted", s.retweeted}};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::string nlohmann_serialize(const std::vector<Hashtag>& v) {
|
|
||||||
nlohmann::json a = nlohmann::json::array();
|
|
||||||
for(const Hashtag & h : v) {
|
|
||||||
a.push_back(nlohmann::json{{"text", h.text},
|
|
||||||
{"indices_start", h.indices_start},
|
|
||||||
{"indices_end", h.indices_end}});
|
|
||||||
}
|
|
||||||
return a.dump();
|
|
||||||
}
|
|
||||||
std::string nlohmann_serialize(const std::vector<Url>& v) {
|
|
||||||
nlohmann::json a = nlohmann::json::array();
|
|
||||||
for(const Url & u : v) {
|
|
||||||
a.push_back(nlohmann::json{{"url", u.url},
|
|
||||||
{"expanded_url", u.expanded_url},
|
|
||||||
{"display_url", u.display_url},
|
|
||||||
{"indices_start", u.indices_start},
|
|
||||||
{"indices_end", u.indices_end}});
|
|
||||||
}
|
|
||||||
return a.dump();
|
|
||||||
}
|
|
||||||
std::string nlohmann_serialize(const std::vector<UserMention>& v) {
|
|
||||||
nlohmann::json a = nlohmann::json::array();
|
|
||||||
for(const UserMention & um : v) {
|
|
||||||
a.push_back(nlohmann::json{{"id", um.id},
|
|
||||||
{"name", um.name},
|
|
||||||
{"screen_name", um.screen_name},
|
|
||||||
{"indices_start", um.indices_start},
|
|
||||||
{"indices_end", um.indices_end}});
|
|
||||||
}
|
|
||||||
return a.dump();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string nlohmann_serialize(const std::vector<Status>& v) {
|
|
||||||
nlohmann::json a = nlohmann::json::array();
|
|
||||||
for(const Status & s : v) {
|
|
||||||
a.push_back(nlohmann::json{{"created_at", s.created_at},
|
|
||||||
{"id", s.id},
|
|
||||||
{"text", s.text},
|
|
||||||
{"user", s.user},
|
|
||||||
{"entities", s.entities},
|
|
||||||
{"retweet_count", s.retweet_count},
|
|
||||||
{"favorite_count", s.favorite_count},
|
|
||||||
{"favorited", s.favorited},
|
|
||||||
{"retweeted", s.retweeted}});
|
|
||||||
}
|
|
||||||
return a.dump();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void to_json(nlohmann::json &j, const TwitterData &t) {
|
void to_json(nlohmann::json &j, const TwitterData &t) {
|
||||||
j = nlohmann::json{{"statuses", t.statuses}};
|
j = nlohmann::json{{"statuses", t.statuses}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deserialization functions for nlohmann
|
||||||
|
void from_json(const nlohmann::json &j, User &u) {
|
||||||
|
j.at("id").get_to(u.id);
|
||||||
|
j.at("name").get_to(u.name);
|
||||||
|
j.at("screen_name").get_to(u.screen_name);
|
||||||
|
j.at("location").get_to(u.location);
|
||||||
|
j.at("description").get_to(u.description);
|
||||||
|
j.at("verified").get_to(u.verified);
|
||||||
|
j.at("followers_count").get_to(u.followers_count);
|
||||||
|
j.at("friends_count").get_to(u.friends_count);
|
||||||
|
j.at("statuses_count").get_to(u.statuses_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void from_json(const nlohmann::json &j, Status &s) {
|
||||||
|
j.at("created_at").get_to(s.created_at);
|
||||||
|
j.at("id").get_to(s.id);
|
||||||
|
j.at("text").get_to(s.text);
|
||||||
|
j.at("user").get_to(s.user);
|
||||||
|
j.at("retweet_count").get_to(s.retweet_count);
|
||||||
|
j.at("favorite_count").get_to(s.favorite_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void from_json(const nlohmann::json &j, TwitterData &t) {
|
||||||
|
j.at("statuses").get_to(t.statuses);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper functions for benchmarking
|
||||||
std::string nlohmann_serialize(const TwitterData &data) {
|
std::string nlohmann_serialize(const TwitterData &data) {
|
||||||
return nlohmann_serialize(data.statuses);
|
nlohmann::json j = data;
|
||||||
|
return j.dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
TwitterData nlohmann_deserialize(const std::string &json_str) {
|
||||||
|
nlohmann::json j = nlohmann::json::parse(json_str);
|
||||||
|
return j.get<TwitterData>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // NLOHMANN_TWITTER_DATA_H
|
#endif // NLOHMANN_TWITTER_DATA_H
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
#ifndef RAPIDJSON_TWITTER_DATA_H
|
||||||
|
#define RAPIDJSON_TWITTER_DATA_H
|
||||||
|
|
||||||
|
#include "twitter_data.h"
|
||||||
|
#include <rapidjson/document.h>
|
||||||
|
#include <rapidjson/writer.h>
|
||||||
|
#include <rapidjson/stringbuffer.h>
|
||||||
|
#include <rapidjson/error/en.h>
|
||||||
|
|
||||||
|
using namespace rapidjson;
|
||||||
|
|
||||||
|
// RapidJSON deserialization for simplified Twitter data
|
||||||
|
TwitterData rapidjson_deserialize(const std::string& json_str) {
|
||||||
|
Document doc;
|
||||||
|
doc.Parse(json_str.c_str());
|
||||||
|
|
||||||
|
if (doc.HasParseError()) {
|
||||||
|
throw std::runtime_error("RapidJSON parse error");
|
||||||
|
}
|
||||||
|
|
||||||
|
TwitterData data;
|
||||||
|
|
||||||
|
if (!doc.HasMember("statuses") || !doc["statuses"].IsArray()) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Value& statuses = doc["statuses"];
|
||||||
|
data.statuses.reserve(statuses.Size());
|
||||||
|
|
||||||
|
for (SizeType i = 0; i < statuses.Size(); i++) {
|
||||||
|
const Value& status_json = statuses[i];
|
||||||
|
Status status;
|
||||||
|
|
||||||
|
// Parse status fields
|
||||||
|
if (status_json.HasMember("created_at") && status_json["created_at"].IsString())
|
||||||
|
status.created_at = status_json["created_at"].GetString();
|
||||||
|
if (status_json.HasMember("id") && status_json["id"].IsUint64())
|
||||||
|
status.id = status_json["id"].GetUint64();
|
||||||
|
if (status_json.HasMember("text") && status_json["text"].IsString())
|
||||||
|
status.text = status_json["text"].GetString();
|
||||||
|
if (status_json.HasMember("retweet_count") && status_json["retweet_count"].IsUint64())
|
||||||
|
status.retweet_count = status_json["retweet_count"].GetUint64();
|
||||||
|
if (status_json.HasMember("favorite_count") && status_json["favorite_count"].IsUint64())
|
||||||
|
status.favorite_count = status_json["favorite_count"].GetUint64();
|
||||||
|
|
||||||
|
// Parse user
|
||||||
|
if (status_json.HasMember("user") && status_json["user"].IsObject()) {
|
||||||
|
const Value& user_json = status_json["user"];
|
||||||
|
User user;
|
||||||
|
|
||||||
|
if (user_json.HasMember("id") && user_json["id"].IsUint64())
|
||||||
|
user.id = user_json["id"].GetUint64();
|
||||||
|
if (user_json.HasMember("name") && user_json["name"].IsString())
|
||||||
|
user.name = user_json["name"].GetString();
|
||||||
|
if (user_json.HasMember("screen_name") && user_json["screen_name"].IsString())
|
||||||
|
user.screen_name = user_json["screen_name"].GetString();
|
||||||
|
if (user_json.HasMember("location") && user_json["location"].IsString())
|
||||||
|
user.location = user_json["location"].GetString();
|
||||||
|
if (user_json.HasMember("description") && user_json["description"].IsString())
|
||||||
|
user.description = user_json["description"].GetString();
|
||||||
|
if (user_json.HasMember("verified") && user_json["verified"].IsBool())
|
||||||
|
user.verified = user_json["verified"].GetBool();
|
||||||
|
if (user_json.HasMember("followers_count") && user_json["followers_count"].IsUint64())
|
||||||
|
user.followers_count = user_json["followers_count"].GetUint64();
|
||||||
|
if (user_json.HasMember("friends_count") && user_json["friends_count"].IsUint64())
|
||||||
|
user.friends_count = user_json["friends_count"].GetUint64();
|
||||||
|
if (user_json.HasMember("statuses_count") && user_json["statuses_count"].IsUint64())
|
||||||
|
user.statuses_count = user_json["statuses_count"].GetUint64();
|
||||||
|
|
||||||
|
status.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.statuses.push_back(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RapidJSON serialization for simplified Twitter data
|
||||||
|
std::string rapidjson_serialize(const TwitterData& data) {
|
||||||
|
Document doc;
|
||||||
|
doc.SetObject();
|
||||||
|
Document::AllocatorType& allocator = doc.GetAllocator();
|
||||||
|
|
||||||
|
Value statuses_array(kArrayType);
|
||||||
|
|
||||||
|
for (const auto& status : data.statuses) {
|
||||||
|
Value status_obj(kObjectType);
|
||||||
|
|
||||||
|
Value created_at;
|
||||||
|
created_at.SetString(status.created_at.c_str(), allocator);
|
||||||
|
status_obj.AddMember("created_at", created_at, allocator);
|
||||||
|
|
||||||
|
status_obj.AddMember("id", status.id, allocator);
|
||||||
|
|
||||||
|
Value text;
|
||||||
|
text.SetString(status.text.c_str(), allocator);
|
||||||
|
status_obj.AddMember("text", text, allocator);
|
||||||
|
|
||||||
|
// Add user
|
||||||
|
Value user_obj(kObjectType);
|
||||||
|
user_obj.AddMember("id", status.user.id, allocator);
|
||||||
|
|
||||||
|
Value name;
|
||||||
|
name.SetString(status.user.name.c_str(), allocator);
|
||||||
|
user_obj.AddMember("name", name, allocator);
|
||||||
|
|
||||||
|
Value screen_name;
|
||||||
|
screen_name.SetString(status.user.screen_name.c_str(), allocator);
|
||||||
|
user_obj.AddMember("screen_name", screen_name, allocator);
|
||||||
|
|
||||||
|
Value location;
|
||||||
|
location.SetString(status.user.location.c_str(), allocator);
|
||||||
|
user_obj.AddMember("location", location, allocator);
|
||||||
|
|
||||||
|
Value description;
|
||||||
|
description.SetString(status.user.description.c_str(), allocator);
|
||||||
|
user_obj.AddMember("description", description, allocator);
|
||||||
|
|
||||||
|
user_obj.AddMember("verified", status.user.verified, allocator);
|
||||||
|
user_obj.AddMember("followers_count", status.user.followers_count, allocator);
|
||||||
|
user_obj.AddMember("friends_count", status.user.friends_count, allocator);
|
||||||
|
user_obj.AddMember("statuses_count", status.user.statuses_count, allocator);
|
||||||
|
|
||||||
|
status_obj.AddMember("user", user_obj, allocator);
|
||||||
|
|
||||||
|
status_obj.AddMember("retweet_count", status.retweet_count, allocator);
|
||||||
|
status_obj.AddMember("favorite_count", status.favorite_count, allocator);
|
||||||
|
|
||||||
|
statuses_array.PushBack(status_obj, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
doc.AddMember("statuses", statuses_array, allocator);
|
||||||
|
|
||||||
|
StringBuffer buffer;
|
||||||
|
Writer<StringBuffer> writer(buffer);
|
||||||
|
doc.Accept(writer);
|
||||||
|
|
||||||
|
return buffer.GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // RAPIDJSON_TWITTER_DATA_H
|
||||||
@@ -4,68 +4,31 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
// Simplified Twitter structures for benchmarking
|
||||||
|
|
||||||
struct User {
|
struct User {
|
||||||
int64_t id;
|
uint64_t id;
|
||||||
std::string id_str;
|
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string screen_name;
|
std::string screen_name;
|
||||||
std::string location;
|
std::string location;
|
||||||
std::string description;
|
std::string description;
|
||||||
bool verified;
|
bool verified;
|
||||||
int64_t followers_count;
|
uint64_t followers_count;
|
||||||
int64_t friends_count;
|
uint64_t friends_count;
|
||||||
int64_t statuses_count;
|
uint64_t statuses_count;
|
||||||
bool operator<=>(const User &other) const = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Hashtag {
|
|
||||||
std::string text;
|
|
||||||
int64_t indices_start;
|
|
||||||
int64_t indices_end;
|
|
||||||
bool operator<=>(const Hashtag &other) const = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Url {
|
|
||||||
std::string url;
|
|
||||||
std::string expanded_url;
|
|
||||||
std::string display_url;
|
|
||||||
int64_t indices_start;
|
|
||||||
int64_t indices_end;
|
|
||||||
bool operator<=>(const Url &other) const = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UserMention {
|
|
||||||
int64_t id;
|
|
||||||
std::string name;
|
|
||||||
std::string screen_name;
|
|
||||||
int64_t indices_start;
|
|
||||||
int64_t indices_end;
|
|
||||||
bool operator<=>(const UserMention &other) const = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Entities {
|
|
||||||
std::vector<Hashtag> hashtags;
|
|
||||||
std::vector<Url> urls;
|
|
||||||
std::vector<UserMention> user_mentions;
|
|
||||||
bool operator==(const Entities &other) const = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Status {
|
struct Status {
|
||||||
std::string created_at;
|
std::string created_at;
|
||||||
int64_t id;
|
uint64_t id;
|
||||||
std::string text;
|
std::string text;
|
||||||
User user;
|
User user;
|
||||||
Entities entities;
|
uint64_t retweet_count;
|
||||||
int64_t retweet_count;
|
uint64_t favorite_count;
|
||||||
int64_t favorite_count;
|
|
||||||
bool favorited;
|
|
||||||
bool retweeted;
|
|
||||||
bool operator==(const Status &other) const = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TwitterData {
|
struct TwitterData {
|
||||||
std::vector<Status> statuses;
|
std::vector<Status> statuses;
|
||||||
bool operator==(const TwitterData &other) const = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif // TWITTER_DATA_H
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
#ifndef YYJSON_TWITTER_DATA_H
|
||||||
|
#define YYJSON_TWITTER_DATA_H
|
||||||
|
|
||||||
|
#include "twitter_data.h"
|
||||||
|
#include <yyjson.h>
|
||||||
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
// yyjson deserialization for simplified Twitter data
|
||||||
|
TwitterData yyjson_deserialize(const std::string &json_str) {
|
||||||
|
TwitterData data;
|
||||||
|
|
||||||
|
yyjson_doc *doc = yyjson_read(json_str.c_str(), json_str.size(), 0);
|
||||||
|
if (!doc) {
|
||||||
|
throw std::runtime_error("yyjson parse error");
|
||||||
|
}
|
||||||
|
|
||||||
|
yyjson_val *root = yyjson_doc_get_root(doc);
|
||||||
|
if (!root) {
|
||||||
|
yyjson_doc_free(doc);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get statuses array
|
||||||
|
yyjson_val *statuses_val = yyjson_obj_get(root, "statuses");
|
||||||
|
if (!statuses_val || !yyjson_is_arr(statuses_val)) {
|
||||||
|
yyjson_doc_free(doc);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t idx, max;
|
||||||
|
yyjson_val *status_val;
|
||||||
|
yyjson_arr_foreach(statuses_val, idx, max, status_val) {
|
||||||
|
Status status;
|
||||||
|
|
||||||
|
// Parse status fields
|
||||||
|
yyjson_val *val;
|
||||||
|
|
||||||
|
val = yyjson_obj_get(status_val, "created_at");
|
||||||
|
if (val && yyjson_is_str(val)) status.created_at = yyjson_get_str(val);
|
||||||
|
|
||||||
|
val = yyjson_obj_get(status_val, "id");
|
||||||
|
if (val && yyjson_is_uint(val)) status.id = yyjson_get_uint(val);
|
||||||
|
|
||||||
|
val = yyjson_obj_get(status_val, "text");
|
||||||
|
if (val && yyjson_is_str(val)) status.text = yyjson_get_str(val);
|
||||||
|
|
||||||
|
val = yyjson_obj_get(status_val, "retweet_count");
|
||||||
|
if (val && yyjson_is_uint(val)) status.retweet_count = yyjson_get_uint(val);
|
||||||
|
|
||||||
|
val = yyjson_obj_get(status_val, "favorite_count");
|
||||||
|
if (val && yyjson_is_uint(val)) status.favorite_count = yyjson_get_uint(val);
|
||||||
|
|
||||||
|
// Parse user
|
||||||
|
yyjson_val *user_val = yyjson_obj_get(status_val, "user");
|
||||||
|
if (user_val && yyjson_is_obj(user_val)) {
|
||||||
|
User user;
|
||||||
|
|
||||||
|
val = yyjson_obj_get(user_val, "id");
|
||||||
|
if (val && yyjson_is_uint(val)) user.id = yyjson_get_uint(val);
|
||||||
|
|
||||||
|
val = yyjson_obj_get(user_val, "name");
|
||||||
|
if (val && yyjson_is_str(val)) user.name = yyjson_get_str(val);
|
||||||
|
|
||||||
|
val = yyjson_obj_get(user_val, "screen_name");
|
||||||
|
if (val && yyjson_is_str(val)) user.screen_name = yyjson_get_str(val);
|
||||||
|
|
||||||
|
val = yyjson_obj_get(user_val, "location");
|
||||||
|
if (val && yyjson_is_str(val)) user.location = yyjson_get_str(val);
|
||||||
|
|
||||||
|
val = yyjson_obj_get(user_val, "description");
|
||||||
|
if (val && yyjson_is_str(val)) user.description = yyjson_get_str(val);
|
||||||
|
|
||||||
|
val = yyjson_obj_get(user_val, "verified");
|
||||||
|
if (val && yyjson_is_bool(val)) user.verified = yyjson_get_bool(val);
|
||||||
|
|
||||||
|
val = yyjson_obj_get(user_val, "followers_count");
|
||||||
|
if (val && yyjson_is_uint(val)) user.followers_count = yyjson_get_uint(val);
|
||||||
|
|
||||||
|
val = yyjson_obj_get(user_val, "friends_count");
|
||||||
|
if (val && yyjson_is_uint(val)) user.friends_count = yyjson_get_uint(val);
|
||||||
|
|
||||||
|
val = yyjson_obj_get(user_val, "statuses_count");
|
||||||
|
if (val && yyjson_is_uint(val)) user.statuses_count = yyjson_get_uint(val);
|
||||||
|
|
||||||
|
status.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.statuses.push_back(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
yyjson_doc_free(doc);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// yyjson serialization for simplified Twitter data
|
||||||
|
std::string yyjson_serialize(const TwitterData &data) {
|
||||||
|
yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
|
||||||
|
yyjson_mut_val *root = yyjson_mut_obj(doc);
|
||||||
|
yyjson_mut_doc_set_root(doc, root);
|
||||||
|
|
||||||
|
// Create statuses array
|
||||||
|
yyjson_mut_val *statuses_array = yyjson_mut_arr(doc);
|
||||||
|
|
||||||
|
for (const auto& status : data.statuses) {
|
||||||
|
yyjson_mut_val *status_obj = yyjson_mut_obj(doc);
|
||||||
|
|
||||||
|
// Add status fields
|
||||||
|
yyjson_mut_obj_add_str(doc, status_obj, "created_at", status.created_at.c_str());
|
||||||
|
yyjson_mut_obj_add_uint(doc, status_obj, "id", status.id);
|
||||||
|
yyjson_mut_obj_add_str(doc, status_obj, "text", status.text.c_str());
|
||||||
|
|
||||||
|
// User object
|
||||||
|
yyjson_mut_val *user_obj = yyjson_mut_obj(doc);
|
||||||
|
yyjson_mut_obj_add_uint(doc, user_obj, "id", status.user.id);
|
||||||
|
yyjson_mut_obj_add_str(doc, user_obj, "name", status.user.name.c_str());
|
||||||
|
yyjson_mut_obj_add_str(doc, user_obj, "screen_name", status.user.screen_name.c_str());
|
||||||
|
yyjson_mut_obj_add_str(doc, user_obj, "location", status.user.location.c_str());
|
||||||
|
yyjson_mut_obj_add_str(doc, user_obj, "description", status.user.description.c_str());
|
||||||
|
yyjson_mut_obj_add_bool(doc, user_obj, "verified", status.user.verified);
|
||||||
|
yyjson_mut_obj_add_uint(doc, user_obj, "followers_count", status.user.followers_count);
|
||||||
|
yyjson_mut_obj_add_uint(doc, user_obj, "friends_count", status.user.friends_count);
|
||||||
|
yyjson_mut_obj_add_uint(doc, user_obj, "statuses_count", status.user.statuses_count);
|
||||||
|
yyjson_mut_obj_add_val(doc, status_obj, "user", user_obj);
|
||||||
|
|
||||||
|
// Other fields
|
||||||
|
yyjson_mut_obj_add_uint(doc, status_obj, "retweet_count", status.retweet_count);
|
||||||
|
yyjson_mut_obj_add_uint(doc, status_obj, "favorite_count", status.favorite_count);
|
||||||
|
|
||||||
|
yyjson_mut_arr_append(statuses_array, status_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add statuses array to root
|
||||||
|
yyjson_mut_obj_add_val(doc, root, "statuses", statuses_array);
|
||||||
|
|
||||||
|
// Write to string
|
||||||
|
char *json_output = yyjson_mut_write(doc, 0, NULL);
|
||||||
|
std::string result(json_output);
|
||||||
|
free(json_output);
|
||||||
|
yyjson_mut_doc_free(doc);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // YYJSON_TWITTER_DATA_H
|
||||||
Vendored
+1
-1
@@ -12,7 +12,7 @@ cmake_dependent_option(SIMDJSON_GOOGLE_BENCHMARKS "compile the Google Benchmark
|
|||||||
if(SIMDJSON_GOOGLE_BENCHMARKS)
|
if(SIMDJSON_GOOGLE_BENCHMARKS)
|
||||||
CPMAddPackage(
|
CPMAddPackage(
|
||||||
NAME google_benchmarks
|
NAME google_benchmarks
|
||||||
URL https://github.com/google/benchmark/archive/refs/tags/v1.9.4.zip
|
URL https://github.com/google/benchmark/archive/refs/tags/v1.9.5.zip
|
||||||
OPTIONS
|
OPTIONS
|
||||||
"BENCHMARK_ENABLE_TESTING OFF"
|
"BENCHMARK_ENABLE_TESTING OFF"
|
||||||
"BENCHMARK_ENABLE_INSTALL OFF"
|
"BENCHMARK_ENABLE_INSTALL OFF"
|
||||||
|
|||||||
+102
-65
@@ -161,54 +161,77 @@ The basics: loading and parsing JSON documents
|
|||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
|
|
||||||
The simdjson library allows you to navigate and validate JSON documents ([RFC 8259](https://www.tbray.org/ongoing/When/201x/2017/12/14/rfc8259.html)).
|
The simdjson library allows you to navigate and validate JSON documents ([RFC 8259](https://www.tbray.org/ongoing/When/201x/2017/12/14/rfc8259.html)).
|
||||||
As required by the standard, your JSON document should be in a Unicode (UTF-8) string. The whole
|
Your JSON document should be a valid Unicode (UTF-8) string.
|
||||||
string, from the beginning to the end, needs to be valid: we do not attempt to tolerate bad
|
|
||||||
inputs before or after a document.
|
|
||||||
|
|
||||||
For efficiency reasons, simdjson requires a string with a few bytes (`simdjson::SIMDJSON_PADDING`)
|
To parse JSON, create a `ondemand::parser` and call its `iterate()` method on a padded input.
|
||||||
at the end, these bytes may be read but their content does not affect the parsing. In practice,
|
The simplest way to load a JSON file is with `padded_string::load`:
|
||||||
it means that the JSON inputs should be stored in a memory region with `simdjson::SIMDJSON_PADDING`
|
|
||||||
extra bytes at the end. You do not have to set these bytes to specific values though you may
|
|
||||||
want to if you want to avoid runtime warnings with some sanitizers. We expect the user
|
|
||||||
of the library to load the data (from disk or from the network) into a padded buffer. To make
|
|
||||||
this easy, we provide the `padded_string::load` function which loads files from disk in a padded buffer.
|
|
||||||
[You can similarly fetch a file from a URL to a padded string](https://github.com/simdjson/curltostring) using our `simdjson::padded_string_builder`. Advanced users may want to read the section Free Padding in [our performance notes](performance.md).
|
|
||||||
|
|
||||||
The simdjson library offers a tree-like [API](https://en.wikipedia.org/wiki/API), which you can
|
|
||||||
access by creating a `ondemand::parser` and calling the `iterate()` method. The iterate method
|
|
||||||
quickly indexes the input string and may detect some errors. The following example illustrates
|
|
||||||
how to get started with an input JSON file (`"twitter.json"`):
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
auto json = padded_string::load("twitter.json"); // load JSON file 'twitter.json'.
|
auto json = padded_string::load("twitter.json");
|
||||||
ondemand::document doc = parser.iterate(json); // position a pointer at the beginning of the JSON data
|
ondemand::document doc = parser.iterate(json);
|
||||||
```
|
```
|
||||||
|
|
||||||
(Windows users compiling with C++17 or better may use `wchar_t` strings to support non-ASCII
|
For inline JSON strings, use the `_padded` suffix:
|
||||||
filenames: `padded_string::load(L"twitter.json")`.)
|
|
||||||
|
|
||||||
If you prefer not to create your own `ondemand::parser` instance, you can access
|
```cpp
|
||||||
a thread-local version by calling `ondemand::parser.get_parser()`.
|
ondemand::parser parser;
|
||||||
|
auto json = "[1,2,3]"_padded;
|
||||||
|
ondemand::document doc = parser.iterate(json);
|
||||||
|
```
|
||||||
|
|
||||||
|
If you are compiling with C++17 or better, you can use `simdjson::padded_input`
|
||||||
|
which accepts any string-like input and handles padding automatically:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
ondemand::parser parser;
|
||||||
|
std::string_view json = "[1,2,3]";
|
||||||
|
simdjson::padded_input input(json);
|
||||||
|
ondemand::document doc = parser.iterate(input);
|
||||||
|
|
||||||
|
// Also works with std::string, considering reserved capacity
|
||||||
|
std::string json_str = "[1,2,3]";
|
||||||
|
json_str.reserve(100); // Reserve extra space
|
||||||
|
simdjson::padded_input input2(json_str); // May avoid copying
|
||||||
|
ondemand::document doc2 = parser.iterate(input2);
|
||||||
|
```
|
||||||
|
|
||||||
|
The simdjson library also accepts `std::string` instances directly---if the provided
|
||||||
|
reference is non-const, it will allocate padding as needed:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
ondemand::parser parser;
|
||||||
|
std::string json = "[1,2,3]";
|
||||||
|
ondemand::document doc = parser.iterate(json);
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, the simdjson library throws exceptions (`simdjson_error`) on errors. We omit `try`-`catch` clauses from our illustrating examples: if you omit `try`-`catch` in your code, an uncaught exception will halt your program. It is also possible to use simdjson without generating exceptions, and you may even build the library without exception support at all. See [Error handling](#error-handling) for details.
|
||||||
|
|
||||||
|
|
||||||
|
### Advanced input options
|
||||||
|
|
||||||
|
This section covers additional ways to provide JSON input to simdjson, including
|
||||||
|
options for fine-grained control over padding and memory.
|
||||||
|
|
||||||
|
**Thread-local parser.** If you prefer not to create your own `ondemand::parser` instance, you can access
|
||||||
|
a thread-local version by calling `ondemand::parser.get_parser()`:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
ondemand::document doc = ondemand::parser.get_parser().iterate(json);
|
ondemand::document doc = ondemand::parser.get_parser().iterate(json);
|
||||||
```
|
```
|
||||||
|
|
||||||
However, you should be careful because a parser instance can only be used for one
|
A parser instance can only be used for one document at a time, so
|
||||||
document at a time, thus it is only applicable when you are only parsing one
|
the thread-local parser is only applicable when you parse one
|
||||||
document per thread at any one time.
|
document per thread at any one time.
|
||||||
|
|
||||||
You can also create a padded string---and call `iterate()`:
|
**`padded_input` details (C++17+).** The actual padding only occurs when the JSON string ends near the boundary of a memory page, which is
|
||||||
|
uncommon. Using a `simdjson::padded_input` is safe although sanitizers and tools like valgrind
|
||||||
|
might report illegal reads (which are safe in our case because they remain in the mapped page). You should avoid `simdjson::padded_input`
|
||||||
|
on systems without a page size of at least 4096: virtually all systems qualify except for
|
||||||
|
some niche embedded systems running custom operating systems. Standard Linux, Windows, macOS, Android, iOS, etc., are all fine. Note that, most times, a `simdjson::padded_input` instance will not copy the data and will only act
|
||||||
|
as a view (it does not own the memory).
|
||||||
|
|
||||||
```cpp
|
**User-managed buffers.** If you have a buffer of your own with enough padding already (`SIMDJSON_PADDING` extra bytes allocated), you can use `padded_string_view` to pass it in:
|
||||||
ondemand::parser parser;
|
|
||||||
auto json = "[1,2,3]"_padded; // The _padded suffix creates a simdjson::padded_string instance
|
|
||||||
ondemand::document doc = parser.iterate(json); // parse a string
|
|
||||||
```
|
|
||||||
|
|
||||||
If you have a buffer of your own with enough padding already (SIMDJSON_PADDING extra bytes allocated), you can use `padded_string_view` to pass it in:
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
@@ -217,60 +240,74 @@ strcpy(json, "[1]");
|
|||||||
ondemand::document doc = parser.iterate(json, strlen(json), sizeof(json));
|
ondemand::document doc = parser.iterate(json, strlen(json), sizeof(json));
|
||||||
```
|
```
|
||||||
|
|
||||||
The simdjson library will also accept `std::string` instances. If the provided
|
**Copying into a `padded_string`.** You can copy your data directly into a `simdjson::padded_string`:
|
||||||
reference is non-const, it will allocate padding as needed.
|
|
||||||
|
|
||||||
You can copy your data directly on a `simdjson::padded_string` as follows:
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
const char * data = "my data"; // 7 bytes
|
const char * data = "my data"; // 7 bytes
|
||||||
simdjson::padded_string my_padded_data(data, 7); // copies to a padded buffer
|
simdjson::padded_string my_padded_data(data, 7); // copies to a padded buffer
|
||||||
```
|
```
|
||||||
|
|
||||||
Or as follows...
|
Or from a `std::string`:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
std::string data = "my data";
|
std::string data = "my data";
|
||||||
simdjson::padded_string my_padded_data(data); // copies to a padded buffer
|
simdjson::padded_string my_padded_data(data); // copies to a padded buffer
|
||||||
```
|
```
|
||||||
|
|
||||||
You can then parse the JSON data from the `simdjson::padded_string` instance:
|
**`std::string` and sanitizer warnings.** Whenever you pass an `std::string` reference to `parser::iterate`,
|
||||||
|
the parser may access bytes beyond the end of
|
||||||
|
|
||||||
```cpp
|
|
||||||
ondemand::document doc = parser.iterate(my_padded_data);
|
|
||||||
```
|
|
||||||
|
|
||||||
Whenever you pass an `std::string` reference to `parser::iterate`,
|
|
||||||
the parser will access the bytes beyond the end of
|
|
||||||
the string but before the end of the allocated memory (`std::string::capacity()`).
|
the string but before the end of the allocated memory (`std::string::capacity()`).
|
||||||
If you are using a sanitizer that checks for reading uninitialized bytes or `std::string`'s
|
Sanitizers that check for reading uninitialized bytes may produce warnings.
|
||||||
container-overflow checks, you may encounter sanitizer warnings.
|
You can safely ignore these warnings, or call `simdjson::pad(std::string&)` to pad the
|
||||||
You can safely ignore these warnings. Or you can call `simdjson::pad(std::string&)` to pad the
|
string explicitly:
|
||||||
string with `SIMDJSON_PADDING` spaces: this function returns a `simdjson::padding_string_view` which can be be passed to the parser's iterator function:
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
std::string json = "[1]";
|
std::string json = "[1]";
|
||||||
ondemand::document doc = parser.iterate(simdjson::pad(json));
|
ondemand::document doc = parser.iterate(simdjson::pad(json));
|
||||||
```
|
```
|
||||||
|
|
||||||
We recommend against creating many `std::string` or many `std::padding_string` instances in your application to store your JSON data.
|
We recommend against creating many `std::string` or many `std::padded_string` instances in your application to store your JSON data.
|
||||||
Consider reusing the same buffers and limiting memory allocations.
|
Consider reusing the same buffers and limiting memory allocations.
|
||||||
|
|
||||||
By default, the simdjson library throws exceptions (`simdjson_error`) on errors. We omit `try`-`catch` clauses from our illustrating examples: if you omit `try`-`catch` in your code, an uncaught exception will halt your program. It is also possible to use simdjson without generating exceptions, and you may even build the library without exception support at all. See [Error handling](#error-handling) for details.
|
**Memory-file mapping (non-Windows).** You can use memory-file mapping to create a `simdjson::padded_string_view`
|
||||||
|
from a file on disk:
|
||||||
|
|
||||||
Some users may want to browse code along with the compiled assembly. You want to check out the following lists of examples:
|
```cpp
|
||||||
|
simdjson::padded_memory_map map(myfilename);
|
||||||
|
if (!map.is_valid()) { /* handle error */ }
|
||||||
|
simdjson::padded_string_view view = map.view();
|
||||||
|
ondemand::document doc = parser.iterate(view);
|
||||||
|
```
|
||||||
|
|
||||||
* [simdjson examples with errors handled through exceptions](https://godbolt.org/z/98Kx9Kqjn)
|
**Windows-specific notes.** Windows users compiling with C++17 or better may use `wchar_t` strings to support non-ASCII
|
||||||
* [simdjson examples with errors without exceptions](https://godbolt.org/z/PKG7GdbPo)
|
filenames: `padded_string::load(L"twitter.json")`. Windows users who need to read files with
|
||||||
|
|
||||||
*Windows-specific*: Windows users who need to read files with
|
|
||||||
non-ANSI characters in the name should set their code page to
|
non-ANSI characters in the name should set their code page to
|
||||||
UTF-8 (65001). This should be the default with Windows 11 and better.
|
UTF-8 (65001). This should be the default with Windows 11 and better.
|
||||||
Further, they may use the AreFileApisANSI function to determine whether
|
Further, they may use the AreFileApisANSI function to determine whether
|
||||||
the filename is interpreted using the ANSI or the system default OEM
|
the filename is interpreted using the ANSI or the system default OEM
|
||||||
codepage, and they may call SetFileApisToOEM accordingly.
|
codepage, and they may call SetFileApisToOEM accordingly.
|
||||||
|
|
||||||
|
Some users may want to browse code along with the compiled assembly:
|
||||||
|
|
||||||
|
* [simdjson examples with errors handled through exceptions](https://godbolt.org/z/98Kx9Kqjn)
|
||||||
|
* [simdjson examples with errors without exceptions](https://godbolt.org/z/PKG7GdbPo)
|
||||||
|
|
||||||
|
**Summary of input types:**
|
||||||
|
|
||||||
|
|
||||||
|
| Input Type / Method | Padding Requirement | How Padding is Handled | Ownership / Copying | Notes / Warnings |
|
||||||
|
|----------------------------------------------|-------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------|----------------------------------------------|----------------------------------------------------------------------------------|
|
||||||
|
| `padded_string::load("file.json")` | Automatic (SIMDJSON_PADDING extra bytes) | Library allocates padded buffer and loads file into it | Owned by `padded_string` | Recommended for files; safest and simplest. |
|
||||||
|
| `"...json..."_padded` literal | Automatic (built-in padding) | Creates `padded_string` with padding | Owned by `padded_string` | Convenient for small hardcoded JSON. |
|
||||||
|
| `simdjson::padded_input` (C++17+) | Automatic when needed | Adds padding **only** if the string ends near a memory page boundary. For `std::string`, considers `capacity()` | Usually a non-owning view (no copy most times) | Safe on standard OS (page size ≥ 4096). May trigger sanitizer/valgrind warnings (harmless). Avoid on niche embedded systems. |
|
||||||
|
| User buffer with explicit padding | Must have at least `SIMDJSON_PADDING` extra allocated bytes after JSON content | Pass via `iterate(ptr, json_length, total_allocated_size)` or `padded_string_view` | User-owned (no copy) | Use `char buf[len + SIMDJSON_PADDING]`. Library reads (but never writes) into padding. |
|
||||||
|
| `std::string` (non-const) | Library checks `capacity()` | If insufficient, library may allocate a padded copy | May copy (depends on capacity) | Can trigger sanitizer warnings on uninitialized bytes. Use `simdjson::pad(json)` to avoid. |
|
||||||
|
| `simdjson::pad(std::string&)` | Adds padding if needed | Returns `padded_string_view` pointing to the (possibly resized) string | References original string | Recommended to silence sanitizers when using `std::string`. |
|
||||||
|
| `padded_string(data, length)` or `padded_string(std::string)` | Automatic (copies into padded buffer) | Explicit copy into owned padded buffer | Owned by `padded_string` | Safe when you want full ownership and padding guaranteed. |
|
||||||
|
| `padded_string_view` (manual) | User guarantees `SIMDJSON_PADDING` extra bytes after the viewed length | User provides pointer + length + capacity | Non-owning view | Low-level; requires careful buffer management. |
|
||||||
|
| Memory-mapped file (`padded_memory_map`) | Automatic via mapping (non-Windows only) | Creates view with sufficient padding | Non-owning (tied to map lifetime) | Advanced; efficient for large files on Linux/macOS/etc. |
|
||||||
|
|
||||||
|
|
||||||
Documents are iterators
|
Documents are iterators
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
@@ -374,6 +411,10 @@ builds to disable additional runtime testing and get the best performance. We
|
|||||||
disable these checks on a best-effort basis but the C++ standard does not provide
|
disable these checks on a best-effort basis but the C++ standard does not provide
|
||||||
a direct way to check for a release build.
|
a direct way to check for a release build.
|
||||||
|
|
||||||
|
|
||||||
|
Warnign: Mixing debug and release simdjson code is unsafe: you either build all your code
|
||||||
|
using simdjson in release mode or all of it in debug mode.
|
||||||
|
|
||||||
Using the parsed JSON
|
Using the parsed JSON
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
@@ -417,7 +458,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
|||||||
* **Extracting Values:** You can cast a JSON element to a native type:
|
* **Extracting Values:** You can cast a JSON element to a native type:
|
||||||
`double(element)`. This works for `std::string_view`, double, uint64_t, int64_t, bool,
|
`double(element)`. This works for `std::string_view`, double, uint64_t, int64_t, bool,
|
||||||
ondemand::object and ondemand::array. We also have explicit methods such as `get_string()`, `get_double()`,
|
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,
|
`get_uint64()`, `get_int64()`, `get_uint32()`, `get_int32()`, `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 number, string or boolean will be parsed, or the initial `{` or `[` will be verified for `ondemand::object` and `ondemand::array`. An exception may be thrown if
|
||||||
the cast is not possible: the error code is `simdjson::INCORRECT_TYPE` (see [Error handling](#error-handling)). Importantly, when getting an ondemand::object or ondemand::array instance, its content is
|
the cast is not possible: 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,
|
not validated: you are only guaranteed that the corresponding initial character (`{` or `[`) is present. Thus,
|
||||||
@@ -427,8 +468,8 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
|||||||
pass `true` (`get_string(true)`) as a parameter to get replacement characters where errors
|
pass `true` (`get_string(true)`) as a parameter to get replacement characters where errors
|
||||||
occur. If you somehow need to access non-UTF-8 strings in a lossless manner
|
occur. If you somehow need to access non-UTF-8 strings in a lossless manner
|
||||||
(e.g., if you strings contain unpaired surrogates), you may use the `get_wobbly_string()` function to get a string in the [WTF-8 format](https://simonsapin.github.io/wtf-8).
|
(e.g., if you strings contain unpaired surrogates), you may use the `get_wobbly_string()` function to get a string in the [WTF-8 format](https://simonsapin.github.io/wtf-8).
|
||||||
When calling `get_uint64()` and `get_int64()`, if the number does not fit in a corresponding
|
When calling `get_uint64()`, `get_int64()`, `get_uint32()` or `get_int32()`, if the number does not fit in the
|
||||||
64-bit integer type, it is also considered an error. When parsing numbers or other scalar values, the library checks
|
corresponding integer type, it is also considered an error (`NUMBER_OUT_OF_RANGE`). When parsing numbers or other scalar values, the library checks
|
||||||
that the value is followed by an expected character, thus you *may* get a number parsing error when accessing the digits
|
that the value is followed by an expected character, thus you *may* get a number parsing error when accessing the digits
|
||||||
as an integer in the following strings: `{"number":12332a`, `{"number":12332\0`, `{"number":12332` (the digits appear at the end). We always abide by the [RFC 8259](https://www.tbray.org/ongoing/When/201x/2017/12/14/rfc8259.html) JSON specification so that, for example, numbers prefixed by the `+` sign are in error.
|
as an integer in the following strings: `{"number":12332a`, `{"number":12332\0`, `{"number":12332` (the digits appear at the end). We always abide by the [RFC 8259](https://www.tbray.org/ongoing/When/201x/2017/12/14/rfc8259.html) JSON specification so that, for example, numbers prefixed by the `+` sign are in error.
|
||||||
|
|
||||||
@@ -469,7 +510,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
|||||||
out of the array, you may use an array access (e.g., `array[1]`). You should never reset an array as you are iterating through it. The following is an anti-pattern: `for(auto value: myarray) {myarray.reset()}`.
|
out of the array, you may use an array access (e.g., `array[1]`). You should never reset an array as you are iterating through it. The following is an anti-pattern: `for(auto value: myarray) {myarray.reset()}`.
|
||||||
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`. This will
|
* **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
|
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
|
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)). The returned value is only valid so long as you do not access another field: normally, you should therefore grab the value right after accessing a key (i.e., convert it to number, string, object, array...). For efficiency reason, you should avoid looking up the same field repeatedly: e.g., do
|
||||||
not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. Generally, you should not mix and match iterating through an object (`for(auto field : object) {...}`) and key accesses (`object["foo"]`): if you need to iterate through an object after a key access, you need to call `reset()` on the object. Whenever you call `reset()`, you need to keep in mind that though you can iterate over the array repeatedly, values should be 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 consumedonly once (e.g., repeatedly calling `unescaped_key()` on the same key is forbidden). Keep in mind that On-Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if (!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsibility as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an
|
||||||
object twice: `std::string_view(object["foo"]` followed by `std::string_view(object["foo"]` then your code
|
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
|
is in error. Furthermore, you can only consume one field at a time, on the same object. The
|
||||||
@@ -1281,10 +1322,6 @@ You can also use the custom `Car` type as part of a template such as `std::vecto
|
|||||||
simdjson::ondemand::parser parser;
|
simdjson::ondemand::parser parser;
|
||||||
simdjson::ondemand::document doc = parser.iterate(json);
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
std::vector<Car> cars(doc);
|
std::vector<Car> cars(doc);
|
||||||
// visual studio users need an explicit call:
|
|
||||||
// std::vector<Car> cars = doc.get<std::vector<Car>>();
|
|
||||||
// because the compiler does not know whether to convert
|
|
||||||
// doc to an unsigned int or to a vector.
|
|
||||||
for(Car& c : cars) {
|
for(Car& c : cars) {
|
||||||
std::cout << c.year << std::endl;
|
std::cout << c.year << std::endl;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,21 @@ your code with the `SIMDJSON_STATIC_REFLECTION` macro set:
|
|||||||
The `simdjson::compile_time::parse_json` function parses a JSON document at **compile time** and returns a `constexpr` structure reflecting its content. We support the full range of JSON values, which are mapped to C++ types as in
|
The `simdjson::compile_time::parse_json` function parses a JSON document at **compile time** and returns a `constexpr` structure reflecting its content. We support the full range of JSON values, which are mapped to C++ types as in
|
||||||
the following table.
|
the following table.
|
||||||
|
|
||||||
|
For convenience, you can also use the `""_json` user-defined literal operator, which is available in the `simdjson::literals` namespace:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
using namespace simdjson::literals;
|
||||||
|
|
||||||
|
constexpr auto cfg = R"(
|
||||||
|
{
|
||||||
|
"port": 8080,
|
||||||
|
"host": "localhost"
|
||||||
|
}
|
||||||
|
)"_json;
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, you can use the qualified name `simdjson::literals::operator""_json`.
|
||||||
|
|
||||||
|
|
||||||
| JSON type | C++ type |
|
| JSON type | C++ type |
|
||||||
|----------------|----------------------------------|
|
|----------------|----------------------------------|
|
||||||
@@ -61,6 +76,8 @@ You can do so, at compile-time, as follows:
|
|||||||
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
using namespace simdjson::literals;
|
||||||
|
|
||||||
constexpr auto cfg = R"(
|
constexpr auto cfg = R"(
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -79,6 +96,8 @@ constexpr auto cfg = R"(
|
|||||||
You can nest objects and arrays:
|
You can nest objects and arrays:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
using namespace simdjson::literals;
|
||||||
|
|
||||||
constexpr auto data = R"(
|
constexpr auto data = R"(
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -98,6 +117,8 @@ constexpr auto data = R"(
|
|||||||
Top-level arrays are allowed:
|
Top-level arrays are allowed:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
using namespace simdjson::literals;
|
||||||
|
|
||||||
constexpr auto arr = R"(
|
constexpr auto arr = R"(
|
||||||
|
|
||||||
[1, 2, 3]
|
[1, 2, 3]
|
||||||
@@ -116,6 +137,8 @@ want to check that it conforms to your expectation. You can do so with concepts.
|
|||||||
Let us consider this example:
|
Let us consider this example:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
using namespace simdjson::literals;
|
||||||
|
|
||||||
constexpr auto config = R"(
|
constexpr auto config = R"(
|
||||||
|
|
||||||
[
|
[
|
||||||
|
|||||||
+47
-1
@@ -125,6 +125,22 @@ Further, they may use the AreFileApisANSI function to determine whether
|
|||||||
the filename is interpreted using the ANSI or the system default OEM
|
the filename is interpreted using the ANSI or the system default OEM
|
||||||
codepage, and they may call SetFileApisToOEM accordingly.
|
codepage, and they may call SetFileApisToOEM accordingly.
|
||||||
|
|
||||||
|
|
||||||
|
**Advanced feature:**
|
||||||
|
On non-Windows systems, you can use memory-file mapping to create a `simdjson::padded_string_view`
|
||||||
|
from a file on disk.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// if the macro _WIN32 is defined, this will not work since we do not support Windows
|
||||||
|
simdjson::padded_memory_map map(TWITTER_JSON);
|
||||||
|
if (!map.is_valid()) { /* handle error */ }
|
||||||
|
simdjson::padded_string_view view = map.view(); // view is usable while padded_memory_map is in scope
|
||||||
|
ondemand::document doc = parser.iterate(view); // parse the JSON
|
||||||
|
```
|
||||||
|
|
||||||
|
Using memory-file mapping requires some care. The file should not be modified while you are
|
||||||
|
accessing it.
|
||||||
|
|
||||||
Using the Parsed JSON
|
Using the Parsed JSON
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
@@ -152,6 +168,23 @@ Once you have an element, you can navigate it with idiomatic C++ iterators, oper
|
|||||||
`SIMDJSON_MINUS_ZERO_AS_FLOAT` to `1` when building simdjson, you can get that `-0` is mapped to `-0.0`
|
`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
|
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`.
|
`SIMDJSON_MINUS_ZERO_AS_FLOAT` to on: `cmake -B build -D SIMDJSON_MINUS_ZERO_AS_FLOAT=ON`.
|
||||||
|
* **Big Integer Support (opt-in):** By default, integers that exceed the 64-bit range cause parsing to fail with `BIGINT_ERROR`. You can opt in to big integer support so that these numbers are stored as raw digit strings on the tape instead:
|
||||||
|
```cpp
|
||||||
|
simdjson::dom::parser parser;
|
||||||
|
parser.number_as_string(true); // opt-in, default false
|
||||||
|
simdjson::dom::element doc;
|
||||||
|
auto error = parser.parse("[1, 123456789012345678901]"_padded).get(doc);
|
||||||
|
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||||
|
for (simdjson::dom::element elem : doc) {
|
||||||
|
if (elem.is_bigint()) {
|
||||||
|
std::string_view digits;
|
||||||
|
error = elem.get_bigint().get(digits);
|
||||||
|
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||||
|
std::cout << "big integer: " << digits << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
When enabled, big integers have type `element_type::BIGINT`. Calling `get_int64()`, `get_uint64()`, or `get_double()` on a big integer returns `INCORRECT_TYPE`. Normal numbers (int64, uint64, double) are unaffected.
|
||||||
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`.
|
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`.
|
||||||
* **Array Iteration:** To iterate through an array, use `for (auto value : array) { ... }`. If you
|
* **Array Iteration:** To iterate through an array, use `for (auto value : array) { ... }`. If you
|
||||||
know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`
|
know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`
|
||||||
@@ -164,7 +197,7 @@ Once you have an element, you can navigate it with idiomatic C++ iterators, oper
|
|||||||
* **Array and Object size** Given an array or an object, you can get its size (number of elements or keys)
|
* **Array and Object size** Given an array or an object, you can get its size (number of elements or keys)
|
||||||
with the `size()` method.
|
with the `size()` method.
|
||||||
* **Checking an Element Type:** You can check an element's type with `element.type()`. It
|
* **Checking an Element Type:** You can check an element's type with `element.type()`. It
|
||||||
returns an `element_type` with values such as `simdjson::dom::element_type::ARRAY`, `simdjson::dom::element_type::OBJECT`, `simdjson::dom::element_type::INT64`, `simdjson::dom::element_type::UINT64`,`simdjson::dom::element_type::DOUBLE`, `simdjson::dom::element_type::STRING`, `simdjson::dom::element_type::BOOL` or, `simdjson::dom::element_type::NULL_VALUE`.
|
returns an `element_type` with values such as `simdjson::dom::element_type::ARRAY`, `simdjson::dom::element_type::OBJECT`, `simdjson::dom::element_type::INT64`, `simdjson::dom::element_type::UINT64`,`simdjson::dom::element_type::DOUBLE`, `simdjson::dom::element_type::STRING`, `simdjson::dom::element_type::BOOL`, `simdjson::dom::element_type::NULL_VALUE` or, `simdjson::dom::element_type::BIGINT` (when big integer support is enabled).
|
||||||
* **Output to streams and strings:** Given a document or an element (or node) out of a JSON document, you can output a minified string version using the C++ stream idiom (`out << element`). You can also request the construction of a minified string version (`simdjson::minify(element)`) or a prettified string version (`simdjson::prettify(element)`). Numbers are serialized as 64-bit floating-point numbers (`double`).
|
* **Output to streams and strings:** Given a document or an element (or node) out of a JSON document, you can output a minified string version using the C++ stream idiom (`out << element`). You can also request the construction of a minified string version (`simdjson::minify(element)`) or a prettified string version (`simdjson::prettify(element)`). Numbers are serialized as 64-bit floating-point numbers (`double`).
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
@@ -849,6 +882,19 @@ simdjson::dom::element element = parser.parse(padded_json_copy.get(), json_len,
|
|||||||
|
|
||||||
Setting the `realloc_if_needed` parameter `false` in this manner may lead to better performance since copies are avoided, but it requires that the user takes more responsibilities: the simdjson library cannot verify that the input buffer was padded with SIMDJSON_PADDING extra bytes.
|
Setting the `realloc_if_needed` parameter `false` in this manner may lead to better performance since copies are avoided, but it requires that the user takes more responsibilities: the simdjson library cannot verify that the input buffer was padded with SIMDJSON_PADDING extra bytes.
|
||||||
|
|
||||||
|
If you are compiling your project with C++17 or better, you can use a `simdjson::padded_input`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
simdjson::dom::parser parser;
|
||||||
|
std::string_view json = "[1,2,3]";
|
||||||
|
simdjson::padded_input input(json); // Automatically pads if needed
|
||||||
|
simdjson::dom::element element = parser.parse(input);
|
||||||
|
```
|
||||||
|
|
||||||
|
The actual padding only occurs if the JSON string ends near the boundary of a memory page, which is uncommon. Using a `simdjson::padded_input` is safe although sanitizers and tools like valgrind might report illegal reads (which are safe in our case because they remain in the mapped page). You should avoid `simdjson::padded_input` on systems without a page size of at least 4096: virtually all systems qualify except for some niche embedded systems running custom operating systems. Standard Linux, Windows, macOS, Android, iOS, etc., are all fine. Note that, most times, an `simdjson::padded_input` instance will not copy the data and will only act
|
||||||
|
as a view (it does not own the memory).
|
||||||
|
|
||||||
|
|
||||||
Performance Tips
|
Performance Tips
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ compiles *all* the implementations into the executable. On Intel, it will includ
|
|||||||
(icelake, haswell, westmere and fallback), on 64-bit ARM it will include just one since running dispatching is unnecessary, and on PPC
|
(icelake, haswell, westmere and fallback), on 64-bit ARM it will include just one since running dispatching is unnecessary, and on PPC
|
||||||
it will include 2 (ppc64 and fallback).
|
it will include 2 (ppc64 and fallback).
|
||||||
|
|
||||||
|
On Loongson processors, LASX runtime dispatching is only enabled on GCC 15+, not on LLVM or older versions of GCC.
|
||||||
|
Thus unless you compile specifically for LASX or use GCC 15+, you will not benefit from LASX support.
|
||||||
|
|
||||||
If you know more about where you're going to run and want to save the space, you can disable any of
|
If you know more about where you're going to run and want to save the space, you can disable any of
|
||||||
these implementations at compile time with `-DSIMDJSON_IMPLEMENTATION_X=0` (where X is ICELAKE, HASWELL,
|
these implementations at compile time with `-DSIMDJSON_IMPLEMENTATION_X=0` (where X is ICELAKE, HASWELL,
|
||||||
WESTMERE, ARM64, PPC64, LSX, LASX and FALLBACK).
|
WESTMERE, ARM64, PPC64, LSX, LASX and FALLBACK).
|
||||||
|
|||||||
+287
-29
@@ -26,6 +26,7 @@ Contents
|
|||||||
- [Tracking your position](#tracking-your-position)
|
- [Tracking your position](#tracking-your-position)
|
||||||
- [Incomplete streams](#incomplete-streams)
|
- [Incomplete streams](#incomplete-streams)
|
||||||
- [C++20 features](#c20-features)
|
- [C++20 features](#c20-features)
|
||||||
|
- [C++26 features (static reflection)](#c26-features-static-reflection)
|
||||||
|
|
||||||
Motivation
|
Motivation
|
||||||
-----------
|
-----------
|
||||||
@@ -132,7 +133,7 @@ E.g., `[1,2]{"32":1}` is recognized as two documents.
|
|||||||
Some official formats **(non-exhaustive list)**:
|
Some official formats **(non-exhaustive list)**:
|
||||||
- [Newline-Delimited JSON (NDJSON)](https://github.com/ndjson/ndjson-spec/)
|
- [Newline-Delimited JSON (NDJSON)](https://github.com/ndjson/ndjson-spec/)
|
||||||
- [JSON lines (JSONL)](http://jsonlines.org/)
|
- [JSON lines (JSONL)](http://jsonlines.org/)
|
||||||
- [Record separator-delimited JSON (RFC 7464)](https://tools.ietf.org/html/rfc7464) <- Not supported by simdjson!
|
- [Record separator-delimited JSON (RFC 7464)](https://tools.ietf.org/html/rfc7464)
|
||||||
- [More on Wikipedia...](https://en.wikipedia.org/wiki/JSON_streaming)
|
- [More on Wikipedia...](https://en.wikipedia.org/wiki/JSON_streaming)
|
||||||
|
|
||||||
API
|
API
|
||||||
@@ -154,6 +155,20 @@ for (auto doc : docs) {
|
|||||||
|
|
||||||
See [basics.md](basics.md#newline-delimited-json-ndjson-and-json-lines) for an overview of the API.
|
See [basics.md](basics.md#newline-delimited-json-ndjson-and-json-lines) for an overview of the API.
|
||||||
|
|
||||||
|
|
||||||
|
**Advanced feature:**
|
||||||
|
On non-Windows systems, you can use memory-file mapping to create a `simdjson::padded_string_view`
|
||||||
|
from a file on disk.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// If the macro _WIN32 is defined, this will not work since we do not support memory-file mapping
|
||||||
|
// under Windows at this time.
|
||||||
|
simdjson::padded_memory_map map(myfilename);
|
||||||
|
if (!map.is_valid()) { /* handle error */ }
|
||||||
|
simdjson::padded_string_view view = map.view(); // view is usable while padded_memory_map is in scope
|
||||||
|
ondemand::document doc = parser.iterate(view); // parse the JSON
|
||||||
|
```
|
||||||
|
|
||||||
## Use cases
|
## Use cases
|
||||||
|
|
||||||
From [jsonlines.org](http://jsonlines.org/examples/):
|
From [jsonlines.org](http://jsonlines.org/examples/):
|
||||||
@@ -264,39 +279,131 @@ Importantly, you should only call `truncated_bytes()` after iterating through al
|
|||||||
Comma-separated documents
|
Comma-separated documents
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
We also support comma-separated documents, but with some performance limitations. The `iterate_many` function takes in an option to allow parsing of comma separated documents (which defaults on false). In this mode, the entire buffer is processed in one batch. Therefore, the total size of the document should not exceed the maximal capacity of the parser (4 GB). This mode also effectively disallow multithreading. It is therefore mostly suitable for not "very large" inputs. In this mode, the batch_size parameter
|
To parse comma-separated documents like `{"a":1},{"b":2},{"c":3}`, use the `stream_format::comma_delimited` parameter:
|
||||||
is effectively ignored, as it is set to at least the document size.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
auto json = R"( 1, 2, 3, 4, "a", "b", "c", {"hello": "world"} , [1, 2, 3])"_padded;
|
auto json = R"({"a":1},{"b":2},{"c":3})"_padded;
|
||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
ondemand::document_stream doc_stream;
|
ondemand::document_stream stream;
|
||||||
// We pass '32' as the batch size, but it is a bogus parameter because, since
|
auto error = parser.iterate_many(json, ondemand::DEFAULT_BATCH_SIZE,
|
||||||
// we pass 'true' to the allow_comma parameter, the batch size will be set to at least
|
simdjson::stream_format::comma_delimited).get(stream);
|
||||||
// the document size.
|
if (error) { std::cerr << error << std::endl; return; }
|
||||||
auto error = parser.iterate_many(json, 32, true).get(doc_stream);
|
for (auto doc : stream) {
|
||||||
if (error) { std::cerr << error << std::endl; return; }
|
std::cout << doc << std::endl;
|
||||||
for (auto doc : doc_stream) {
|
}
|
||||||
std::cout << doc.type() << std::endl;
|
// Prints: {"a":1}
|
||||||
}
|
// {"b":2}
|
||||||
```
|
// {"c":3}
|
||||||
|
|
||||||
This will print:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
number
|
|
||||||
number
|
Whitespace around the commas is allowed:
|
||||||
number
|
```cpp
|
||||||
number
|
auto json = R"({"a":1} , {"b":2} , {"c":3})"_padded; // Also works
|
||||||
string
|
|
||||||
string
|
|
||||||
string
|
|
||||||
object
|
|
||||||
array
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Nested commas inside objects and arrays are preserved:
|
||||||
|
```cpp
|
||||||
|
auto json = R"({"arr":[1,2,3]},{"obj":{"x":1,"y":2}})"_padded;
|
||||||
|
// Correctly parses as 2 documents, not 6
|
||||||
|
```
|
||||||
|
|
||||||
|
Mixed document types are supported:
|
||||||
|
```cpp
|
||||||
|
auto json = R"(1, 2, 3, 4, "a", "b", "c", {"hello": "world"}, [1, 2, 3])"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream doc_stream;
|
||||||
|
auto error = parser.iterate_many(json, ondemand::DEFAULT_BATCH_SIZE,
|
||||||
|
simdjson::stream_format::comma_delimited).get(doc_stream);
|
||||||
|
if (error) { std::cerr << error << std::endl; return; }
|
||||||
|
for (auto doc : doc_stream) {
|
||||||
|
std::cout << doc.type() << std::endl;
|
||||||
|
}
|
||||||
|
// Prints: number number number number string string string object array
|
||||||
|
```
|
||||||
|
|
||||||
|
Extra top-level separators are tolerated for compatibility with the legacy
|
||||||
|
`allow_comma_separated` behavior. For example, leading commas, trailing commas,
|
||||||
|
and repeated commas are treated as empty separators rather than documents.
|
||||||
|
|
||||||
|
### Legacy `allow_comma_separated` parameter (deprecated)
|
||||||
|
|
||||||
|
The `allow_comma_separated` boolean parameter is deprecated. When set to `true`, it now internally maps to `stream_format::comma_delimited`.
|
||||||
|
|
||||||
|
The old single-batch limitation no longer applies - comma-delimited parsing now supports multi-batch processing and threading for optimal performance on large files.
|
||||||
|
|
||||||
|
JSON Text Sequences (RFC 7464)
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
[RFC 7464](https://tools.ietf.org/html/rfc7464) defines a format for streaming JSON values using ASCII Record Separator (RS, 0x1E) as a delimiter. Each JSON text is preceded by RS and optionally followed by ASCII Line Feed (LF, 0x0A).
|
||||||
|
|
||||||
|
Example input:
|
||||||
|
```
|
||||||
|
<RS>{"name":"doc1"}<LF>
|
||||||
|
<RS>{"name":"doc2"}<LF>
|
||||||
|
<RS>{"name":"doc3"}<LF>
|
||||||
|
```
|
||||||
|
|
||||||
|
To parse JSON text sequences, use the `stream_format::json_sequence` parameter:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Build input with RS (0x1E) and LF (0x0A) delimiters
|
||||||
|
std::string input_str;
|
||||||
|
input_str += '\x1e'; input_str += "{\"a\":1}"; input_str += '\x0a';
|
||||||
|
input_str += '\x1e'; input_str += "{\"b\":2}"; input_str += '\x0a';
|
||||||
|
input_str += '\x1e'; input_str += "{\"c\":3}"; input_str += '\x0a';
|
||||||
|
simdjson::padded_string input(input_str);
|
||||||
|
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream stream;
|
||||||
|
auto error = parser.iterate_many(input, ondemand::DEFAULT_BATCH_SIZE,
|
||||||
|
simdjson::stream_format::json_sequence).get(stream);
|
||||||
|
if (error) { std::cerr << error << std::endl; return; }
|
||||||
|
for (auto doc : stream) {
|
||||||
|
std::cout << doc << std::endl;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `stream_format` enum has the following values:
|
||||||
|
- `stream_format::whitespace_delimited` (default): Standard NDJSON/JSON Lines format
|
||||||
|
- `stream_format::json_sequence`: RFC 7464 format with RS delimiters
|
||||||
|
- `stream_format::comma_delimited`: Comma-separated JSON documents
|
||||||
|
- `stream_format::comma_delimited_array`: A single JSON array whose elements are iterated as comma-delimited documents (see below)
|
||||||
|
|
||||||
|
The trailing LF after each JSON text is optional but recommended by the RFC for robustness.
|
||||||
|
|
||||||
|
JSON Array As A Document Stream
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
Sometimes an input is a single, well-formed JSON array — `[{"a":1},{"b":2},{"c":3}]` — but you want to iterate its elements one at a time without materializing the whole array. Use `stream_format::comma_delimited_array`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto json = R"([{"a":1},{"b":2},{"c":3}])"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream stream;
|
||||||
|
auto error = parser.iterate_many(json, ondemand::DEFAULT_BATCH_SIZE,
|
||||||
|
simdjson::stream_format::comma_delimited_array).get(stream);
|
||||||
|
if (error) { std::cerr << error << std::endl; return; }
|
||||||
|
for (auto doc : stream) {
|
||||||
|
std::cout << doc << std::endl;
|
||||||
|
}
|
||||||
|
// Prints: {"a":1}
|
||||||
|
// {"b":2}
|
||||||
|
// {"c":3}
|
||||||
|
```
|
||||||
|
|
||||||
|
The parser strips the outer `[` and `]` plus any surrounding JSON whitespace (space, tab, LF, CR) and then behaves exactly like `stream_format::comma_delimited` over the remaining bytes. All comma-delimited features are inherited: multi-batch processing, threading, mixed scalar types, and nested commas preserved inside inner objects and arrays.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// All of these work:
|
||||||
|
auto a = R"([1, "x", true, null, {"k":"v"}, [1,2]])"_padded; // mixed scalars
|
||||||
|
auto b = R"( [ 1, 2, 3 ] )"_padded; // whitespace
|
||||||
|
auto c = R"([])"_padded; // empty array → 0 docs
|
||||||
|
```
|
||||||
|
|
||||||
|
If the input is not a well-formed outer array (missing `[`, missing `]`, or empty / all-whitespace), `iterate_many` returns `TAPE_ERROR`. Content **inside** the array is not validated up front — individual document parse errors surface when you iterate, just like `comma_delimited`.
|
||||||
|
|
||||||
|
Positions reported via `current_index()` are relative to the **stripped** buffer (the bytes between `[` and `]`), not the original input, for consistency with the existing BOM-stripping behavior.
|
||||||
|
|
||||||
|
|
||||||
C++20 features
|
C++20 features
|
||||||
--------------------
|
--------------------
|
||||||
@@ -404,3 +511,154 @@ Otherwise you may use this longer version for explicit handling of errors:
|
|||||||
cars.push_back(c);
|
cars.push_back(c);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
C++26 features (static reflection)
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
If you have a C++26 compatible compiler with [P2996](https://wg21.link/P2996)
|
||||||
|
static reflection support, you can compile the simdjson library with the
|
||||||
|
`SIMDJSON_STATIC_REFLECTION` macro set to `1`. When this is the case, simdjson
|
||||||
|
can deserialize a stream of JSON documents directly into your own structures
|
||||||
|
**without** writing any `tag_invoke` function. The library inspects the
|
||||||
|
non-static public members of your type at compile time and produces the
|
||||||
|
parsing code automatically.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#define SIMDJSON_STATIC_REFLECTION 1
|
||||||
|
#include "simdjson.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
Consider the same `Car` structure used in the C++20 example, but **without**
|
||||||
|
any `tag_invoke` glue:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct Car {
|
||||||
|
std::string make;
|
||||||
|
std::string model;
|
||||||
|
int year;
|
||||||
|
std::vector<double> tire_pressure;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
With C++26 static reflection enabled, you can iterate a stream of cars and
|
||||||
|
push them into a `std::vector<Car>` directly:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] }
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] }
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] } )"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream stream;
|
||||||
|
auto error = parser.iterate_many(json).get(stream);
|
||||||
|
if (error) { /* handle error */ }
|
||||||
|
std::vector<Car> cars;
|
||||||
|
for (auto doc : stream) {
|
||||||
|
Car c;
|
||||||
|
if ((error = doc.get<Car>().get(c))) { /* handle error */ }
|
||||||
|
cars.push_back(c);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This works for every `stream_format` value supported by `iterate_many`. The
|
||||||
|
following examples each parse the same three cars, but laid out using a
|
||||||
|
different streaming convention.
|
||||||
|
|
||||||
|
### Whitespace-delimited (default, NDJSON / JSON Lines)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] }
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] }
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] } )"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream stream;
|
||||||
|
auto error = parser.iterate_many(json, ondemand::DEFAULT_BATCH_SIZE,
|
||||||
|
simdjson::stream_format::whitespace_delimited).get(stream);
|
||||||
|
if (error) { /* handle error */ }
|
||||||
|
std::vector<Car> cars;
|
||||||
|
for (auto doc : stream) {
|
||||||
|
cars.push_back((Car)doc); // throws on error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Comma-delimited documents
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] } )"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream stream;
|
||||||
|
auto error = parser.iterate_many(json, ondemand::DEFAULT_BATCH_SIZE,
|
||||||
|
simdjson::stream_format::comma_delimited).get(stream);
|
||||||
|
if (error) { /* handle error */ }
|
||||||
|
std::vector<Car> cars;
|
||||||
|
for (auto doc : stream) {
|
||||||
|
Car c;
|
||||||
|
if ((error = doc.get<Car>().get(c))) { /* handle error */ }
|
||||||
|
cars.push_back(c);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### A single JSON array as a stream of documents
|
||||||
|
|
||||||
|
When the input is a single JSON array, you can stream its elements one at a
|
||||||
|
time without materializing the entire array as a `std::vector` upfront:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto json = R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] } ] )"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream stream;
|
||||||
|
auto error = parser.iterate_many(json, ondemand::DEFAULT_BATCH_SIZE,
|
||||||
|
simdjson::stream_format::comma_delimited_array).get(stream);
|
||||||
|
if (error) { /* handle error */ }
|
||||||
|
std::vector<Car> cars;
|
||||||
|
for (auto doc : stream) {
|
||||||
|
Car c;
|
||||||
|
if ((error = doc.get<Car>().get(c))) { /* handle error */ }
|
||||||
|
cars.push_back(c);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### JSON Text Sequences (RFC 7464)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Build input with RS (0x1E) and LF (0x0A) delimiters
|
||||||
|
std::string input_str;
|
||||||
|
auto append = [&](std::string_view doc) {
|
||||||
|
input_str += '\x1e'; input_str += doc; input_str += '\x0a';
|
||||||
|
};
|
||||||
|
append(R"({ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9 ] })");
|
||||||
|
append(R"({ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0 ] })");
|
||||||
|
append(R"({ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0 ] })");
|
||||||
|
simdjson::padded_string input(input_str);
|
||||||
|
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream stream;
|
||||||
|
auto error = parser.iterate_many(input, ondemand::DEFAULT_BATCH_SIZE,
|
||||||
|
simdjson::stream_format::json_sequence).get(stream);
|
||||||
|
if (error) { /* handle error */ }
|
||||||
|
std::vector<Car> cars;
|
||||||
|
for (auto doc : stream) {
|
||||||
|
Car c;
|
||||||
|
if ((error = doc.get<Car>().get(c))) { /* handle error */ }
|
||||||
|
cars.push_back(c);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In every case, the user-defined type (`Car` here) does not need a hand-written
|
||||||
|
`tag_invoke` overload: the library generates the deserialization code from the
|
||||||
|
type's public data members at compile time.
|
||||||
|
|||||||
+127
-1
@@ -132,7 +132,7 @@ Whitespace Characters:
|
|||||||
Some official formats **(non-exhaustive list)**:
|
Some official formats **(non-exhaustive list)**:
|
||||||
- [Newline-Delimited JSON (NDJSON)](https://github.com/ndjson/ndjson-spec)
|
- [Newline-Delimited JSON (NDJSON)](https://github.com/ndjson/ndjson-spec)
|
||||||
- [JSON lines (JSONL)](http://jsonlines.org/)
|
- [JSON lines (JSONL)](http://jsonlines.org/)
|
||||||
- [Record separator-delimited JSON (RFC 7464)](https://tools.ietf.org/html/rfc7464) <- Not supported by simdjson!
|
- [Record separator-delimited JSON (RFC 7464)](https://tools.ietf.org/html/rfc7464)
|
||||||
- [More on Wikipedia...](https://en.wikipedia.org/wiki/JSON_streaming)
|
- [More on Wikipedia...](https://en.wikipedia.org/wiki/JSON_streaming)
|
||||||
|
|
||||||
API
|
API
|
||||||
@@ -217,6 +217,20 @@ got full document at 29
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**Advanced feature:**
|
||||||
|
On non-Windows systems, you can use memory-file mapping to create a `simdjson::padded_string_view`
|
||||||
|
from a file on disk.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// If the macro _WIN32 is defined, this will not work since we do not support memory-file mapping
|
||||||
|
// under Windows at this time.
|
||||||
|
simdjson::padded_memory_map map(myfilename);
|
||||||
|
if (!map.is_valid()) { /* handle error */ }
|
||||||
|
simdjson::padded_string_view view = map.view(); // view is usable while padded_memory_map is in scope
|
||||||
|
ondemand::document doc = parser.iterate(view); // parse the JSON
|
||||||
|
```
|
||||||
|
|
||||||
Incomplete streams
|
Incomplete streams
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
@@ -239,3 +253,115 @@ Consider the following example where a truncated document (`{"key":"intentionall
|
|||||||
|
|
||||||
|
|
||||||
Importantly, you should only call `truncated_bytes()` after iterating through all of the documents since the stream cannot tell whether there are truncated documents at the very end when it may not have accessed that part of the data yet.
|
Importantly, you should only call `truncated_bytes()` after iterating through all of the documents since the stream cannot tell whether there are truncated documents at the very end when it may not have accessed that part of the data yet.
|
||||||
|
|
||||||
|
JSON Text Sequences (RFC 7464)
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
[RFC 7464](https://tools.ietf.org/html/rfc7464) defines a format for streaming JSON values using ASCII Record Separator (RS, 0x1E) as a delimiter. Each JSON text is preceded by RS and optionally followed by ASCII Line Feed (LF, 0x0A).
|
||||||
|
|
||||||
|
Example input:
|
||||||
|
```
|
||||||
|
<RS>{"name":"doc1"}<LF>
|
||||||
|
<RS>{"name":"doc2"}<LF>
|
||||||
|
<RS>{"name":"doc3"}<LF>
|
||||||
|
```
|
||||||
|
|
||||||
|
To parse JSON text sequences, use the `stream_format::json_sequence` parameter:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Build input with RS (0x1E) and LF (0x0A) delimiters
|
||||||
|
std::string input_str;
|
||||||
|
input_str += '\x1e'; input_str += "{\"a\":1}"; input_str += '\x0a';
|
||||||
|
input_str += '\x1e'; input_str += "{\"b\":2}"; input_str += '\x0a';
|
||||||
|
input_str += '\x1e'; input_str += "{\"c\":3}"; input_str += '\x0a';
|
||||||
|
simdjson::padded_string input(input_str);
|
||||||
|
|
||||||
|
simdjson::dom::parser parser;
|
||||||
|
simdjson::dom::document_stream stream;
|
||||||
|
auto error = parser.parse_many(input, simdjson::dom::DEFAULT_BATCH_SIZE,
|
||||||
|
simdjson::stream_format::json_sequence).get(stream);
|
||||||
|
if (error) { std::cerr << error << std::endl; return; }
|
||||||
|
for (auto doc : stream) {
|
||||||
|
std::cout << doc << std::endl;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `stream_format` enum has the following values:
|
||||||
|
- `stream_format::whitespace_delimited` (default): Standard NDJSON/JSON Lines format
|
||||||
|
- `stream_format::json_sequence`: RFC 7464 format with RS delimiters
|
||||||
|
- `stream_format::comma_delimited`: Comma-separated JSON documents
|
||||||
|
- `stream_format::comma_delimited_array`: A single JSON array whose elements are iterated as comma-delimited documents (see below)
|
||||||
|
|
||||||
|
The trailing LF after each JSON text is optional but recommended by the RFC for robustness.
|
||||||
|
|
||||||
|
Comma-Separated Documents
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Some systems produce JSON documents separated by commas, like `{"a":1},{"b":2},{"c":3}`. This is common when extracting elements from a JSON array or when APIs return comma-separated results.
|
||||||
|
|
||||||
|
To parse comma-separated documents, use the `stream_format::comma_delimited` parameter:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto json = R"({"a":1},{"b":2},{"c":3})"_padded;
|
||||||
|
simdjson::dom::parser parser;
|
||||||
|
simdjson::dom::document_stream stream;
|
||||||
|
auto error = parser.parse_many(json, simdjson::dom::DEFAULT_BATCH_SIZE,
|
||||||
|
simdjson::stream_format::comma_delimited).get(stream);
|
||||||
|
if (error) { std::cerr << error << std::endl; return; }
|
||||||
|
for (auto doc : stream) {
|
||||||
|
std::cout << doc << std::endl;
|
||||||
|
}
|
||||||
|
// Prints: {"a":1}
|
||||||
|
// {"b":2}
|
||||||
|
// {"c":3}
|
||||||
|
```
|
||||||
|
|
||||||
|
Whitespace around the commas is allowed:
|
||||||
|
```cpp
|
||||||
|
auto json = R"({"a":1} , {"b":2} , {"c":3})"_padded; // Also works
|
||||||
|
```
|
||||||
|
|
||||||
|
Nested commas inside objects and arrays are preserved:
|
||||||
|
```cpp
|
||||||
|
auto json = R"({"arr":[1,2,3]},{"obj":{"x":1,"y":2}})"_padded;
|
||||||
|
// Correctly parses as 2 documents, not 6
|
||||||
|
```
|
||||||
|
|
||||||
|
Extra top-level separators are tolerated for compatibility with the legacy
|
||||||
|
On-Demand comma-separated mode. Leading commas, trailing commas, and repeated
|
||||||
|
commas are treated as empty separators rather than documents.
|
||||||
|
|
||||||
|
Unlike the legacy `allow_comma_separated` parameter, `stream_format::comma_delimited` supports multi-batch processing and threading for optimal performance on large files.
|
||||||
|
|
||||||
|
JSON Array As A Document Stream
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
Sometimes an input is a single, well-formed JSON array — `[{"a":1},{"b":2},{"c":3}]` — but you want to iterate its elements one at a time without materializing the whole array. Use `stream_format::comma_delimited_array`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto json = R"([{"a":1},{"b":2},{"c":3}])"_padded;
|
||||||
|
simdjson::dom::parser parser;
|
||||||
|
simdjson::dom::document_stream stream;
|
||||||
|
auto error = parser.parse_many(json, simdjson::dom::DEFAULT_BATCH_SIZE,
|
||||||
|
simdjson::stream_format::comma_delimited_array).get(stream);
|
||||||
|
if (error) { std::cerr << error << std::endl; return; }
|
||||||
|
for (auto doc : stream) {
|
||||||
|
std::cout << doc << std::endl;
|
||||||
|
}
|
||||||
|
// Prints: {"a":1}
|
||||||
|
// {"b":2}
|
||||||
|
// {"c":3}
|
||||||
|
```
|
||||||
|
|
||||||
|
The parser strips the outer `[` and `]` plus any surrounding JSON whitespace (space, tab, LF, CR) and then behaves exactly like `stream_format::comma_delimited` over the remaining bytes. All comma-delimited features are inherited: multi-batch processing, threading, mixed scalar types, and nested commas preserved inside inner objects and arrays.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// All of these work:
|
||||||
|
auto a = R"([1, "x", true, null, {"k":"v"}, [1,2]])"_padded; // mixed scalars
|
||||||
|
auto b = R"( [ 1, 2, 3 ] )"_padded; // whitespace
|
||||||
|
auto c = R"([])"_padded; // empty array → 0 docs
|
||||||
|
```
|
||||||
|
|
||||||
|
If the input is not a well-formed outer array (missing `[`, missing `]`, or empty / all-whitespace), `parse_many` returns `TAPE_ERROR`. Content **inside** the array is not validated up front — individual document parse errors surface when you iterate, just like `comma_delimited`.
|
||||||
|
|
||||||
|
Positions reported via `current_index()` are relative to the **stripped** buffer (the bytes between `[` and `]`), not the original input, for consistency with the existing BOM-stripping behavior.
|
||||||
|
|||||||
+20
-4
@@ -204,9 +204,25 @@ but can be significantly larger. E.g., Apple systems favour pages spanning 16 ki
|
|||||||
|
|
||||||
In effect, it means that you can almost always read a few bytes beyond your current buffer---without
|
In effect, it means that you can almost always read a few bytes beyond your current buffer---without
|
||||||
allocating extra memory. However, tools such as valgrind or memory sanitizers will flag such behavior as unsafe.
|
allocating extra memory. However, tools such as valgrind or memory sanitizers will flag such behavior as unsafe.
|
||||||
Nevertheless, you can still make sure of this capability in your code if you are an expert
|
You can still make sure of this capability in your code if you are an expert
|
||||||
programmer and you are willing to silence sanitizer warnings. The following code provides
|
programmer and you are willing to silence sanitizer warnings.
|
||||||
a portable example.
|
|
||||||
|
If you are building simdjson with C++17 or better, you can use `simdjson::padded_input`.
|
||||||
|
The `padded_input` struct automatically manages padding for you. It can be constructed from a `std::string_view` or a C-style string with length. If the input already has sufficient padding (up to the end of the memory page), it creates a view without copying. Otherwise, it copies the data into a `padded_string` with proper padding.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::string_view json = get_json_data();
|
||||||
|
simdjson::padded_input input(json); // Automatically pads if needed
|
||||||
|
auto result = parser.parse(input);
|
||||||
|
```
|
||||||
|
|
||||||
|
This simplifies padding management compared to manually checking and allocating.
|
||||||
|
|
||||||
|
|
||||||
|
More generally, the following code provides a portable example.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The conditional compilation checks for the `_MSC_VER` macro (indicating Microsoft Visual Studio)
|
The conditional compilation checks for the `_MSC_VER` macro (indicating Microsoft Visual Studio)
|
||||||
@@ -250,7 +266,7 @@ long page_size() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if the buffer + len + simdjson::SIMDJSON_PADDING crosses the
|
// Returns true if the buffer + len + simdjson::SIMDJSON_PADDING crosses the
|
||||||
// page boundary.
|
// page boundary. Assumes len != 0.
|
||||||
bool need_allocation(const char *buf, size_t len) {
|
bool need_allocation(const char *buf, size_t len) {
|
||||||
return ((reinterpret_cast<uintptr_t>(buf + len - 1) % page_size())
|
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()));
|
||||||
|
|||||||
@@ -93,6 +93,14 @@ Float values are represented as two 64-bit tape elements:
|
|||||||
|
|
||||||
Performance consideration: We store numbers of the main tape because we believe that locality of reference is helpful for performance.
|
Performance consideration: We store numbers of the main tape because we believe that locality of reference is helpful for performance.
|
||||||
|
|
||||||
|
## Big Integers
|
||||||
|
|
||||||
|
When a JSON integer exceeds the 64-bit range (both signed and unsigned), it is classified as a big integer. By default, parsing returns `BIGINT_ERROR`. When `parser.number_as_string(true)` is set, the raw digits are stored on the string tape using the same format as strings (4-byte little-endian length prefix, followed by the UTF-8 digit bytes, followed by a null terminator).
|
||||||
|
|
||||||
|
A big integer is represented on the main tape as the 64-bit tape element `('Z' << 56) + x` where the payload `x` is the location on the string tape of the null-terminated digit string.
|
||||||
|
|
||||||
|
For example, the JSON value `99999999999999999999` would be stored as the string `"99999999999999999999"` on the string tape, with a `'Z'` tag on the main tape. Negative big integers include the leading minus sign.
|
||||||
|
|
||||||
## Root node
|
## Root node
|
||||||
|
|
||||||
Each JSON document will have two special 64-bit tape elements representing a root node, one at the beginning and one at the end.
|
Each JSON document will have two special 64-bit tape elements representing a root node, one at the beginning and one at the end.
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ static void print_json(std::ostream& os, simdjson::dom::element element) noexcep
|
|||||||
case simdjson::dom::element_type::NULL_VALUE:
|
case simdjson::dom::element_type::NULL_VALUE:
|
||||||
os << "null" << endl;
|
os << "null" << endl;
|
||||||
break;
|
break;
|
||||||
|
case simdjson::dom::element_type::BIGINT:
|
||||||
|
os << element.get_bigint().value_unsafe() << endl;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||||
|
|||||||
@@ -428,6 +428,7 @@ namespace {
|
|||||||
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
|
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
|
||||||
static_assert(NUM_CHUNKS == 4, "ARM kernel should use four registers per 64-byte block.");
|
static_assert(NUM_CHUNKS == 4, "ARM kernel should use four registers per 64-byte block.");
|
||||||
const simd8<T> chunks[NUM_CHUNKS];
|
const simd8<T> chunks[NUM_CHUNKS];
|
||||||
|
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
|
||||||
|
|
||||||
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
|
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
|
||||||
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
|
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ using namespace simd;
|
|||||||
struct backslash_and_quote {
|
struct backslash_and_quote {
|
||||||
public:
|
public:
|
||||||
static constexpr uint32_t BYTES_PROCESSED = 32;
|
static constexpr uint32_t BYTES_PROCESSED = 32;
|
||||||
// We only copy if dst is non-null.
|
|
||||||
simdjson_inline 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_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
|
||||||
@@ -35,10 +34,8 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
|
|||||||
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
|
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
|
||||||
simd8<uint8_t> v0(src);
|
simd8<uint8_t> v0(src);
|
||||||
simd8<uint8_t> v1(src + sizeof(v0));
|
simd8<uint8_t> v1(src + sizeof(v0));
|
||||||
if(dst != nullptr) {
|
v0.store(dst);
|
||||||
v0.store(dst);
|
v1.store(dst + sizeof(v0));
|
||||||
v1.store(dst + sizeof(v0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getting a 64-bit bitmask is much cheaper than multiple 16-bit bitmasks on ARM; therefore, we
|
// Getting a 64-bit bitmask is much cheaper than multiple 16-bit bitmasks on ARM; therefore, we
|
||||||
// smash them together into a 64-byte mask and get the bitmask from there.
|
// smash them together into a 64-byte mask and get the bitmask from there.
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS
|
|||||||
|
|
||||||
/** The maximum document size supported by simdjson. */
|
/** The maximum document size supported by simdjson. */
|
||||||
constexpr size_t SIMDJSON_MAXSIZE_BYTES = 0xFFFFFFFF;
|
constexpr size_t SIMDJSON_MAXSIZE_BYTES = 0xFFFFFFFF;
|
||||||
|
/** The maximum depth of nested objects and arrays supported by simdjson.
|
||||||
|
A depth of SIMDJSON_MAXSIZE_BYTES/2 is not reasonable and would be
|
||||||
|
adversarial, but it serves as an upper bound for validation purposes. */
|
||||||
|
constexpr size_t SIMDJSON_MAX_DEPTH = SIMDJSON_MAXSIZE_BYTES/2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The amount of padding needed in a buffer to parse JSON.
|
* The amount of padding needed in a buffer to parse JSON.
|
||||||
@@ -46,6 +50,21 @@ struct padded_string;
|
|||||||
class padded_string_view;
|
class padded_string_view;
|
||||||
enum class stage1_mode;
|
enum class stage1_mode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream format for parse_many/iterate_many.
|
||||||
|
*/
|
||||||
|
enum class stream_format {
|
||||||
|
whitespace_delimited, ///< Whitespace-delimited JSON documents (default, includes NDJSON/JSONL)
|
||||||
|
json_sequence, ///< RFC 7464 JSON text sequences (RS-delimited)
|
||||||
|
comma_delimited, ///< Comma-separated JSON documents (e.g., `{...},{...},{...}`)
|
||||||
|
comma_delimited_array ///< A single JSON array whose elements are iterated as
|
||||||
|
///< comma-separated documents (e.g., `[{...},{...},{...}]`).
|
||||||
|
///< The parser strips the outer `[` / `]` plus any
|
||||||
|
///< surrounding JSON whitespace (space, tab, LF, CR)
|
||||||
|
///< and then behaves like `comma_delimited` over the
|
||||||
|
///< remaining bytes.
|
||||||
|
};
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
#include "simdjson/lasx.h"
|
#include "simdjson/lasx.h"
|
||||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lsx)
|
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lsx)
|
||||||
#include "simdjson/lsx.h"
|
#include "simdjson/lsx.h"
|
||||||
|
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(rvv_vls)
|
||||||
|
#include "simdjson/rvv-vls.h"
|
||||||
#else
|
#else
|
||||||
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ namespace simdjson {
|
|||||||
namespace lsx {}
|
namespace lsx {}
|
||||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
|
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
|
||||||
namespace lasx {}
|
namespace lasx {}
|
||||||
|
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(rvv_vls)
|
||||||
|
namespace rvv_vls {}
|
||||||
#else
|
#else
|
||||||
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
#include "simdjson/lsx/builder.h"
|
#include "simdjson/lsx/builder.h"
|
||||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
|
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
|
||||||
#include "simdjson/lasx/builder.h"
|
#include "simdjson/lasx/builder.h"
|
||||||
|
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(rvv_vls)
|
||||||
|
#include "simdjson/rvv-vls/builder.h"
|
||||||
#else
|
#else
|
||||||
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -23,6 +23,8 @@
|
|||||||
#include "simdjson/lsx/implementation.h"
|
#include "simdjson/lsx/implementation.h"
|
||||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
|
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
|
||||||
#include "simdjson/lasx/implementation.h"
|
#include "simdjson/lasx/implementation.h"
|
||||||
|
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(rvv_vls)
|
||||||
|
#include "simdjson/rvv-vls/implementation.h"
|
||||||
#else
|
#else
|
||||||
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
#include "simdjson/lsx/ondemand.h"
|
#include "simdjson/lsx/ondemand.h"
|
||||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
|
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
|
||||||
#include "simdjson/lasx/ondemand.h"
|
#include "simdjson/lasx/ondemand.h"
|
||||||
|
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(rvv_vls)
|
||||||
|
#include "simdjson/rvv-vls/ondemand.h"
|
||||||
#else
|
#else
|
||||||
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -266,6 +266,12 @@ consteval std::pair<double, size_t> parse_double(const char *src,
|
|||||||
}
|
}
|
||||||
} // namespace number_parsing
|
} // namespace number_parsing
|
||||||
|
|
||||||
|
consteval auto make_data_member_options(auto&& name_str) {
|
||||||
|
std::meta::data_member_options options{};
|
||||||
|
options.name = std::forward<decltype(name_str)>(name_str);
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
// JSON string may contain embedded nulls, and C++26 reflection does not yet
|
// JSON string may contain embedded nulls, and C++26 reflection does not yet
|
||||||
// support std::string_view as a data member type. As a workaround, we define
|
// support std::string_view as a data member type. As a workaround, we define
|
||||||
// a custom type that holds a const char* and a size.
|
// a custom type that holds a const char* and a size.
|
||||||
@@ -320,7 +326,8 @@ using class_type = type_builder<meta_info...>::constructed_type;
|
|||||||
/**
|
/**
|
||||||
* @brief Variable template for constructing instances with values
|
* @brief Variable template for constructing instances with values
|
||||||
*/
|
*/
|
||||||
template <typename T, auto... Vs> constexpr auto construct_from = T{Vs...};
|
template <typename T, auto... Vs> constexpr T construct_from = T{Vs...};
|
||||||
|
|
||||||
|
|
||||||
// in JSON, there are only a few whitespace characters that are allowed
|
// in JSON, there are only a few whitespace characters that are allowed
|
||||||
// outside of objects, arrays, strings, and numbers.
|
// outside of objects, arrays, strings, and numbers.
|
||||||
@@ -410,7 +417,7 @@ parse_number(std::string_view json,
|
|||||||
std::from_chars(json.data(), json.data() + json.size(), int_value);
|
std::from_chars(json.data(), json.data() + json.size(), int_value);
|
||||||
if (res.ec == std::errc()) {
|
if (res.ec == std::errc()) {
|
||||||
out = int_value;
|
out = int_value;
|
||||||
if ((res.ptr - json.data()) != scope) {
|
if (static_cast<std::size_t>(res.ptr - json.data()) != scope) {
|
||||||
simdjson_consteval_error(
|
simdjson_consteval_error(
|
||||||
"Internal error: cannot agree on the character range of the float");
|
"Internal error: cannot agree on the character range of the float");
|
||||||
}
|
}
|
||||||
@@ -424,7 +431,7 @@ parse_number(std::string_view json,
|
|||||||
std::from_chars(json.data(), json.data() + json.size(), uint_value);
|
std::from_chars(json.data(), json.data() + json.size(), uint_value);
|
||||||
if (res.ec == std::errc()) {
|
if (res.ec == std::errc()) {
|
||||||
out = uint_value;
|
out = uint_value;
|
||||||
if ((res.ptr - json.data()) != scope) {
|
if (static_cast<std::size_t>(res.ptr - json.data()) != scope) {
|
||||||
simdjson_consteval_error(
|
simdjson_consteval_error(
|
||||||
"Internal error: cannot agree on the character range of the float");
|
"Internal error: cannot agree on the character range of the float");
|
||||||
}
|
}
|
||||||
@@ -533,7 +540,7 @@ parse_string(std::string_view json) {
|
|||||||
// present, we have an error (isolated high surrogate), which we
|
// present, we have an error (isolated high surrogate), which we
|
||||||
// tolerate by substituting the substitution_code_point.
|
// tolerate by substituting the substitution_code_point.
|
||||||
if (end - cursor < 6 || *cursor != '\\' ||
|
if (end - cursor < 6 || *cursor != '\\' ||
|
||||||
*(cursor + 1) != 'u' > 0xFFFF) {
|
*(cursor + 1) != 'u') {
|
||||||
code_point = substitution_code_point;
|
code_point = substitution_code_point;
|
||||||
} else { // we have \u following the high surrogate
|
} else { // we have \u following the high surrogate
|
||||||
cursor += 2; // skip \u
|
cursor += 2; // skip \u
|
||||||
@@ -887,10 +894,19 @@ parse_json_array_impl(const std::string_view json) {
|
|||||||
std::size_t count = values.size() - 1;
|
std::size_t count = values.size() - 1;
|
||||||
// We assume all elements have the same type as the first element.
|
// We assume all elements have the same type as the first element.
|
||||||
// However, if the array is heterogeneous, we should use std::variant.
|
// However, if the array is heterogeneous, we should use std::variant.
|
||||||
|
auto elem_type = std::meta::type_of(values[1]);
|
||||||
|
// String literals reflected via reflect_constant_string have type const
|
||||||
|
// char[N], but when passed as template auto parameters they decay to
|
||||||
|
// const char*. Use const char* as the element type so that
|
||||||
|
// construct_from can aggregate-initialize the array.
|
||||||
|
if (std::meta::is_array_type(elem_type) &&
|
||||||
|
std::meta::remove_all_extents(elem_type) == ^^const char) {
|
||||||
|
elem_type = ^^const char *;
|
||||||
|
}
|
||||||
auto array_type = std::meta::substitute(
|
auto array_type = std::meta::substitute(
|
||||||
^^std::array,
|
^^std::array,
|
||||||
{
|
{
|
||||||
std::meta::type_of(values[1]), std::meta::reflect_constant(count)});
|
elem_type, std::meta::reflect_constant(count)});
|
||||||
|
|
||||||
// Create array instance with values
|
// Create array instance with values
|
||||||
values[0] = array_type;
|
values[0] = array_type;
|
||||||
@@ -957,8 +973,7 @@ parse_json_object_impl(std::string_view json) {
|
|||||||
simdjson_consteval_error("Expected '}'");
|
simdjson_consteval_error("Expected '}'");
|
||||||
}
|
}
|
||||||
cursor += object_size;
|
cursor += object_size;
|
||||||
auto dms = std::meta::data_member_spec(std::meta::type_of(parsed),
|
auto dms = std::meta::data_member_spec(std::meta::type_of(parsed), make_data_member_options(field_name));
|
||||||
{.name = field_name});
|
|
||||||
members.push_back(std::meta::reflect_constant(dms));
|
members.push_back(std::meta::reflect_constant(dms));
|
||||||
values.push_back(parsed);
|
values.push_back(parsed);
|
||||||
|
|
||||||
@@ -967,8 +982,7 @@ parse_json_object_impl(std::string_view json) {
|
|||||||
case '[': {
|
case '[': {
|
||||||
std::string_view value(cursor, end);
|
std::string_view value(cursor, end);
|
||||||
auto [parsed, array_size] = parse_json_array_impl(value);
|
auto [parsed, array_size] = parse_json_array_impl(value);
|
||||||
auto dms = std::meta::data_member_spec(std::meta::type_of(parsed),
|
auto dms = std::meta::data_member_spec(std::meta::type_of(parsed), make_data_member_options(field_name));
|
||||||
{.name = field_name});
|
|
||||||
members.push_back(std::meta::reflect_constant(dms));
|
members.push_back(std::meta::reflect_constant(dms));
|
||||||
values.push_back(parsed);
|
values.push_back(parsed);
|
||||||
if (*(cursor + array_size - 1) != ']') {
|
if (*(cursor + array_size - 1) != ']') {
|
||||||
@@ -989,8 +1003,7 @@ parse_json_object_impl(std::string_view json) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto dms =
|
auto dms =
|
||||||
std::meta::data_member_spec(^^const char *, {
|
std::meta::data_member_spec(^^const char *, make_data_member_options(field_name));
|
||||||
.name = field_name});
|
|
||||||
members.push_back(std::meta::reflect_constant(dms));
|
members.push_back(std::meta::reflect_constant(dms));
|
||||||
values.push_back(std::meta::reflect_constant_string(value));
|
values.push_back(std::meta::reflect_constant_string(value));
|
||||||
break;
|
break;
|
||||||
@@ -1001,8 +1014,7 @@ parse_json_object_impl(std::string_view json) {
|
|||||||
}
|
}
|
||||||
cursor += 4;
|
cursor += 4;
|
||||||
|
|
||||||
auto dms = std::meta::data_member_spec(^^bool, {
|
auto dms = std::meta::data_member_spec(^^bool, make_data_member_options(field_name));
|
||||||
.name = field_name});
|
|
||||||
members.push_back(std::meta::reflect_constant(dms));
|
members.push_back(std::meta::reflect_constant(dms));
|
||||||
values.push_back(std::meta::reflect_constant(true));
|
values.push_back(std::meta::reflect_constant(true));
|
||||||
break;
|
break;
|
||||||
@@ -1013,8 +1025,7 @@ parse_json_object_impl(std::string_view json) {
|
|||||||
}
|
}
|
||||||
cursor += 5;
|
cursor += 5;
|
||||||
|
|
||||||
auto dms = std::meta::data_member_spec(^^bool, {
|
auto dms = std::meta::data_member_spec(^^bool, make_data_member_options(field_name));
|
||||||
.name = field_name});
|
|
||||||
members.push_back(std::meta::reflect_constant(dms));
|
members.push_back(std::meta::reflect_constant(dms));
|
||||||
values.push_back(std::meta::reflect_constant(false));
|
values.push_back(std::meta::reflect_constant(false));
|
||||||
break;
|
break;
|
||||||
@@ -1025,9 +1036,7 @@ parse_json_object_impl(std::string_view json) {
|
|||||||
}
|
}
|
||||||
cursor += 4;
|
cursor += 4;
|
||||||
|
|
||||||
auto dms = std::meta::data_member_spec(^^std::nullptr_t,
|
auto dms = std::meta::data_member_spec(^^std::nullptr_t, make_data_member_options(field_name));
|
||||||
{
|
|
||||||
.name = field_name});
|
|
||||||
members.push_back(std::meta::reflect_constant(dms));
|
members.push_back(std::meta::reflect_constant(dms));
|
||||||
values.push_back(std::meta::reflect_constant(nullptr));
|
values.push_back(std::meta::reflect_constant(nullptr));
|
||||||
break;
|
break;
|
||||||
@@ -1051,22 +1060,19 @@ parse_json_object_impl(std::string_view json) {
|
|||||||
if (std::holds_alternative<int64_t>(out)) {
|
if (std::holds_alternative<int64_t>(out)) {
|
||||||
int64_t int_value = std::get<int64_t>(out);
|
int64_t int_value = std::get<int64_t>(out);
|
||||||
auto dms =
|
auto dms =
|
||||||
std::meta::data_member_spec(^^int64_t, {
|
std::meta::data_member_spec(^^int64_t, make_data_member_options(field_name));
|
||||||
.name = field_name});
|
|
||||||
members.push_back(std::meta::reflect_constant(dms));
|
members.push_back(std::meta::reflect_constant(dms));
|
||||||
values.push_back(std::meta::reflect_constant(int_value));
|
values.push_back(std::meta::reflect_constant(int_value));
|
||||||
} else if (std::holds_alternative<uint64_t>(out)) {
|
} else if (std::holds_alternative<uint64_t>(out)) {
|
||||||
uint64_t uint_value = std::get<uint64_t>(out);
|
uint64_t uint_value = std::get<uint64_t>(out);
|
||||||
auto dms =
|
auto dms =
|
||||||
std::meta::data_member_spec(^^uint64_t, {
|
std::meta::data_member_spec(^^uint64_t, make_data_member_options(field_name));
|
||||||
.name = field_name});
|
|
||||||
members.push_back(std::meta::reflect_constant(dms));
|
members.push_back(std::meta::reflect_constant(dms));
|
||||||
values.push_back(std::meta::reflect_constant(uint_value));
|
values.push_back(std::meta::reflect_constant(uint_value));
|
||||||
} else {
|
} else {
|
||||||
double float_value = std::get<double>(out);
|
double float_value = std::get<double>(out);
|
||||||
auto dms =
|
auto dms =
|
||||||
std::meta::data_member_spec(^^double, {
|
std::meta::data_member_spec(^^double, make_data_member_options(field_name));
|
||||||
.name = field_name});
|
|
||||||
members.push_back(std::meta::reflect_constant(dms));
|
members.push_back(std::meta::reflect_constant(dms));
|
||||||
values.push_back(std::meta::reflect_constant(float_value));
|
values.push_back(std::meta::reflect_constant(float_value));
|
||||||
}
|
}
|
||||||
@@ -1111,16 +1117,11 @@ template <constevalutil::fixed_string json_str> consteval auto parse_json() {
|
|||||||
"Only JSON objects and arrays are supported at the top level, this "
|
"Only JSON objects and arrays are supported at the top level, this "
|
||||||
"limitation will be lifted in the future.");*/
|
"limitation will be lifted in the future.");*/
|
||||||
|
|
||||||
constexpr auto result = json.front() == '['
|
if constexpr (json.front() == '[') {
|
||||||
? parse_json_array_impl(json)
|
return [: parse_json_array_impl(json).first :];
|
||||||
: parse_json_object_impl(json);
|
} else {
|
||||||
return [: result.first :];
|
return [: parse_json_object_impl(json).first :];
|
||||||
/*
|
}
|
||||||
if(json.front() == '[') {
|
|
||||||
return [:parse_json_array_impl(json).first:];
|
|
||||||
} else if(json.front() == '{') {
|
|
||||||
// return [:parse_json_object_impl(json).first:];
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace compile_time
|
} // namespace compile_time
|
||||||
|
|||||||
@@ -63,13 +63,16 @@ namespace compile_time {
|
|||||||
template <constevalutil::fixed_string json_str> consteval auto parse_json();
|
template <constevalutil::fixed_string json_str> consteval auto parse_json();
|
||||||
|
|
||||||
} // namespace compile_time
|
} // namespace compile_time
|
||||||
} // namespace simdjson
|
|
||||||
|
|
||||||
|
inline namespace literals {
|
||||||
|
|
||||||
template <simdjson::constevalutil::fixed_string str>
|
template <simdjson::constevalutil::fixed_string str>
|
||||||
consteval auto operator ""_json() {
|
consteval auto operator ""_json() {
|
||||||
return simdjson::compile_time::parse_json<str>();
|
return simdjson::compile_time::parse_json<str>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace literals
|
||||||
|
} // namespace simdjson
|
||||||
|
|
||||||
#endif // SIMDJSON_STATIC_REFLECTION
|
#endif // SIMDJSON_STATIC_REFLECTION
|
||||||
#endif // SIMDJSON_GENERIC_COMPILE_TIME_JSON_H
|
#endif // SIMDJSON_GENERIC_COMPILE_TIME_JSON_H
|
||||||
|
|||||||
@@ -119,5 +119,4 @@
|
|||||||
#define SIMDJSON_CONSTEVAL 0
|
#define SIMDJSON_CONSTEVAL 0
|
||||||
#endif // defined(__cpp_consteval) && __cpp_consteval >= 201811L && defined(__cpp_lib_constexpr_string) && __cpp_lib_constexpr_string >= 201907L
|
#endif // defined(__cpp_consteval) && __cpp_consteval >= 201811L && defined(__cpp_lib_constexpr_string) && __cpp_lib_constexpr_string >= 201907L
|
||||||
#endif // !defined(SIMDJSON_CONSTEVAL)
|
#endif // !defined(SIMDJSON_CONSTEVAL)
|
||||||
|
|
||||||
#endif // SIMDJSON_COMPILER_CHECK_H
|
#endif // SIMDJSON_COMPILER_CHECK_H
|
||||||
|
|||||||
@@ -58,6 +58,11 @@ struct fixed_string {
|
|||||||
data[i] = str[i];
|
data[i] = str[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
constexpr fixed_string(const unsigned char (&str)[N]) {
|
||||||
|
for (std::size_t i = 0; i < N; ++i) {
|
||||||
|
data[i] = static_cast<char>(str[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
char data[N];
|
char data[N];
|
||||||
constexpr std::string_view view() const { return {data, N - 1}; }
|
constexpr std::string_view view() const { return {data, N - 1}; }
|
||||||
constexpr size_t size() const { return N ; }
|
constexpr size_t size() const { return N ; }
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ inline error_code document::allocate(size_t capacity) noexcept {
|
|||||||
allocated_capacity = 0;
|
allocated_capacity = 0;
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
if (capacity > SIMDJSON_MAXSIZE_BYTES) {
|
||||||
|
return CAPACITY;
|
||||||
|
}
|
||||||
|
|
||||||
// a pathological input like "[[[[..." would generate capacity tape elements, so
|
// a pathological input like "[[[[..." would generate capacity tape elements, so
|
||||||
// need a capacity of at least capacity + 1, but it is also possible to do
|
// need a capacity of at least capacity + 1, but it is also possible to do
|
||||||
@@ -141,6 +144,15 @@ inline bool document::dump_raw_tape(std::ostream &os) const noexcept {
|
|||||||
case 'r': // we start and end with the root node
|
case 'r': // we start and end with the root node
|
||||||
// should we be hitting the root node?
|
// should we be hitting the root node?
|
||||||
return false;
|
return false;
|
||||||
|
case 'Z': // we have a big integer
|
||||||
|
os << "bigint ";
|
||||||
|
std::memcpy(&string_length, string_buf.get() + payload, sizeof(uint32_t));
|
||||||
|
os << std::string_view(
|
||||||
|
reinterpret_cast<const char *>(string_buf.get() + payload + sizeof(uint32_t)),
|
||||||
|
string_length
|
||||||
|
);
|
||||||
|
os << '\n';
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,12 +89,14 @@ simdjson_inline document_stream::document_stream(
|
|||||||
dom::parser &_parser,
|
dom::parser &_parser,
|
||||||
const uint8_t *_buf,
|
const uint8_t *_buf,
|
||||||
size_t _len,
|
size_t _len,
|
||||||
size_t _batch_size
|
size_t _batch_size,
|
||||||
|
stream_format _format
|
||||||
) noexcept
|
) noexcept
|
||||||
: parser{&_parser},
|
: parser{&_parser},
|
||||||
buf{_buf},
|
buf{_buf},
|
||||||
len{_len},
|
len{_len},
|
||||||
batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size},
|
batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size},
|
||||||
|
format{_format},
|
||||||
error{SUCCESS}
|
error{SUCCESS}
|
||||||
#ifdef SIMDJSON_THREADS_ENABLED
|
#ifdef SIMDJSON_THREADS_ENABLED
|
||||||
, use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change
|
, use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change
|
||||||
@@ -112,6 +114,7 @@ simdjson_inline document_stream::document_stream() noexcept
|
|||||||
buf{nullptr},
|
buf{nullptr},
|
||||||
len{0},
|
len{0},
|
||||||
batch_size{0},
|
batch_size{0},
|
||||||
|
format{stream_format::whitespace_delimited},
|
||||||
error{UNINITIALIZED}
|
error{UNINITIALIZED}
|
||||||
#ifdef SIMDJSON_THREADS_ENABLED
|
#ifdef SIMDJSON_THREADS_ENABLED
|
||||||
, use_thread(false)
|
, use_thread(false)
|
||||||
@@ -224,7 +227,14 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
|
|||||||
} else {
|
} else {
|
||||||
size_t next_doc_index = stream->batch_start + stream->parser->implementation->structural_indexes[stream->parser->implementation->next_structural_index];
|
size_t next_doc_index = stream->batch_start + stream->parser->implementation->structural_indexes[stream->parser->implementation->next_structural_index];
|
||||||
size_t svlen = next_doc_index - current_index();
|
size_t svlen = next_doc_index - current_index();
|
||||||
while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0')) {
|
// Trim trailing whitespace, NUL, and RS (0x1E). In RFC 7464 json_sequence
|
||||||
|
// mode the scanner classifies RS as a scalar character, so an RS-prefixed
|
||||||
|
// scalar document (number/true/false/null/string) has no closing structural
|
||||||
|
// index and the slice runs all the way up to the next document's RS. RS
|
||||||
|
// cannot legally appear in a JSON value at the source level (control
|
||||||
|
// characters in strings must be escaped as \u001E), so stripping it is
|
||||||
|
// safe in every stream_format.
|
||||||
|
while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0' || static_cast<uint8_t>(start[svlen-1]) == 0x1E)) {
|
||||||
svlen--;
|
svlen--;
|
||||||
}
|
}
|
||||||
return std::string_view(start, svlen);
|
return std::string_view(start, svlen);
|
||||||
@@ -274,10 +284,35 @@ inline size_t document_stream::next_batch_start() const noexcept {
|
|||||||
|
|
||||||
inline error_code document_stream::run_stage1(dom::parser &p, size_t _batch_start) noexcept {
|
inline error_code document_stream::run_stage1(dom::parser &p, size_t _batch_start) noexcept {
|
||||||
size_t remaining = len - _batch_start;
|
size_t remaining = len - _batch_start;
|
||||||
|
stage1_mode mode;
|
||||||
if (remaining <= batch_size) {
|
if (remaining <= batch_size) {
|
||||||
return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final);
|
// Final batch
|
||||||
|
switch (format) {
|
||||||
|
case stream_format::json_sequence:
|
||||||
|
mode = stage1_mode::json_sequence_final;
|
||||||
|
break;
|
||||||
|
case stream_format::comma_delimited:
|
||||||
|
mode = stage1_mode::comma_delimited_final;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mode = stage1_mode::streaming_final;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return p.implementation->stage1(&buf[_batch_start], remaining, mode);
|
||||||
} else {
|
} else {
|
||||||
return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial);
|
// Partial batch
|
||||||
|
switch (format) {
|
||||||
|
case stream_format::json_sequence:
|
||||||
|
mode = stage1_mode::json_sequence_partial;
|
||||||
|
break;
|
||||||
|
case stream_format::comma_delimited:
|
||||||
|
mode = stage1_mode::comma_delimited_partial;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mode = stage1_mode::streaming_partial;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return p.implementation->stage1(&buf[_batch_start], batch_size, mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -206,12 +206,14 @@ private:
|
|||||||
* @param buf is the raw byte buffer we need to process
|
* @param buf is the raw byte buffer we need to process
|
||||||
* @param len is the length of the raw byte buffer in bytes
|
* @param len is the length of the raw byte buffer in bytes
|
||||||
* @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document)
|
* @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document)
|
||||||
|
* @param format is the stream format
|
||||||
*/
|
*/
|
||||||
simdjson_inline document_stream(
|
simdjson_inline document_stream(
|
||||||
dom::parser &parser,
|
dom::parser &parser,
|
||||||
const uint8_t *buf,
|
const uint8_t *buf,
|
||||||
size_t len,
|
size_t len,
|
||||||
size_t batch_size
|
size_t batch_size,
|
||||||
|
stream_format format = stream_format::whitespace_delimited
|
||||||
) noexcept;
|
) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -261,6 +263,8 @@ private:
|
|||||||
const uint8_t *buf;
|
const uint8_t *buf;
|
||||||
size_t len;
|
size_t len;
|
||||||
size_t batch_size;
|
size_t batch_size;
|
||||||
|
/** The stream format. */
|
||||||
|
stream_format format;
|
||||||
/** The error (or lack thereof) from the current document. */
|
/** The error (or lack thereof) from the current document. */
|
||||||
error_code error;
|
error_code error;
|
||||||
size_t batch_start{0};
|
size_t batch_start{0};
|
||||||
|
|||||||
@@ -81,6 +81,10 @@ simdjson_inline simdjson_result<bool> simdjson_result<dom::element>::get_bool()
|
|||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.get_bool();
|
return first.get_bool();
|
||||||
}
|
}
|
||||||
|
simdjson_inline simdjson_result<std::string_view> simdjson_result<dom::element>::get_bigint() const noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return first.get_bigint();
|
||||||
|
}
|
||||||
|
|
||||||
simdjson_inline bool simdjson_result<dom::element>::is_array() const noexcept {
|
simdjson_inline bool simdjson_result<dom::element>::is_array() const noexcept {
|
||||||
return !error() && first.is_array();
|
return !error() && first.is_array();
|
||||||
@@ -110,6 +114,9 @@ simdjson_inline bool simdjson_result<dom::element>::is_bool() const noexcept {
|
|||||||
simdjson_inline bool simdjson_result<dom::element>::is_null() const noexcept {
|
simdjson_inline bool simdjson_result<dom::element>::is_null() const noexcept {
|
||||||
return !error() && first.is_null();
|
return !error() && first.is_null();
|
||||||
}
|
}
|
||||||
|
simdjson_inline bool simdjson_result<dom::element>::is_bigint() const noexcept {
|
||||||
|
return !error() && first.is_bigint();
|
||||||
|
}
|
||||||
|
|
||||||
simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::operator[](std::string_view key) const noexcept {
|
simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::operator[](std::string_view key) const noexcept {
|
||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
@@ -218,6 +225,15 @@ inline simdjson_result<bool> element::get_bool() const noexcept {
|
|||||||
}
|
}
|
||||||
return INCORRECT_TYPE;
|
return INCORRECT_TYPE;
|
||||||
}
|
}
|
||||||
|
inline simdjson_result<std::string_view> element::get_bigint() const noexcept {
|
||||||
|
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable());
|
||||||
|
switch (tape.tape_ref_type()) {
|
||||||
|
case internal::tape_type::BIGINT:
|
||||||
|
return tape.get_string_view();
|
||||||
|
default:
|
||||||
|
return INCORRECT_TYPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
inline simdjson_result<const char *> element::get_c_str() const noexcept {
|
inline simdjson_result<const char *> element::get_c_str() const noexcept {
|
||||||
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
|
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
|
||||||
switch (tape.tape_ref_type()) {
|
switch (tape.tape_ref_type()) {
|
||||||
@@ -360,6 +376,10 @@ inline bool element::is_null() const noexcept {
|
|||||||
return tape.is_null_on_tape();
|
return tape.is_null_on_tape();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool element::is_bigint() const noexcept {
|
||||||
|
return tape.tape_ref_type() == internal::tape_type::BIGINT;
|
||||||
|
}
|
||||||
|
|
||||||
#if SIMDJSON_EXCEPTIONS
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
|
||||||
inline element::operator bool() const noexcept(false) { return get<bool>(); }
|
inline element::operator bool() const noexcept(false) { return get<bool>(); }
|
||||||
@@ -492,6 +512,8 @@ inline std::ostream& operator<<(std::ostream& out, element_type type) {
|
|||||||
return out << "bool";
|
return out << "bool";
|
||||||
case element_type::NULL_VALUE:
|
case element_type::NULL_VALUE:
|
||||||
return out << "null";
|
return out << "null";
|
||||||
|
case element_type::BIGINT:
|
||||||
|
return out << "bigint";
|
||||||
default:
|
default:
|
||||||
return out << "unexpected content!!!"; // abort() usage is forbidden in the library
|
return out << "unexpected content!!!"; // abort() usage is forbidden in the library
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ enum class element_type {
|
|||||||
DOUBLE = 'd', ///< double: Any number with a "." or "e" that fits in double.
|
DOUBLE = 'd', ///< double: Any number with a "." or "e" that fits in double.
|
||||||
STRING = '"', ///< std::string_view
|
STRING = '"', ///< std::string_view
|
||||||
BOOL = 't', ///< bool
|
BOOL = 't', ///< bool
|
||||||
NULL_VALUE = 'n' ///< null
|
NULL_VALUE = 'n', ///< null
|
||||||
|
BIGINT = 'Z' ///< std::string_view: big integer stored as raw digit string
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -120,6 +121,14 @@ public:
|
|||||||
*/
|
*/
|
||||||
inline simdjson_result<bool> get_bool() const noexcept;
|
inline simdjson_result<bool> get_bool() const noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read this element as a big integer (raw digit string).
|
||||||
|
*
|
||||||
|
* @returns A string_view of the raw digits, or:
|
||||||
|
* INCORRECT_TYPE if the JSON element is not a big integer.
|
||||||
|
*/
|
||||||
|
inline simdjson_result<std::string_view> get_bigint() const noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether this element is a json array.
|
* Whether this element is a json array.
|
||||||
*
|
*
|
||||||
@@ -175,6 +184,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
inline bool is_null() const noexcept;
|
inline bool is_null() const noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this element is a big integer (number exceeding 64-bit range).
|
||||||
|
*/
|
||||||
|
inline bool is_bigint() const noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tell whether the value can be cast to provided type (T).
|
* Tell whether the value can be cast to provided type (T).
|
||||||
*
|
*
|
||||||
@@ -533,6 +547,7 @@ public:
|
|||||||
simdjson_inline simdjson_result<uint64_t> get_uint64() const noexcept;
|
simdjson_inline simdjson_result<uint64_t> get_uint64() const noexcept;
|
||||||
simdjson_inline simdjson_result<double> get_double() const noexcept;
|
simdjson_inline simdjson_result<double> get_double() const noexcept;
|
||||||
simdjson_inline simdjson_result<bool> get_bool() const noexcept;
|
simdjson_inline simdjson_result<bool> get_bool() const noexcept;
|
||||||
|
simdjson_inline simdjson_result<std::string_view> get_bigint() const noexcept;
|
||||||
|
|
||||||
simdjson_inline bool is_array() const noexcept;
|
simdjson_inline bool is_array() const noexcept;
|
||||||
simdjson_inline bool is_object() const noexcept;
|
simdjson_inline bool is_object() const noexcept;
|
||||||
@@ -543,6 +558,7 @@ public:
|
|||||||
simdjson_inline bool is_number() const noexcept;
|
simdjson_inline bool is_number() const noexcept;
|
||||||
simdjson_inline bool is_bool() const noexcept;
|
simdjson_inline bool is_bool() const noexcept;
|
||||||
simdjson_inline bool is_null() const noexcept;
|
simdjson_inline bool is_null() const noexcept;
|
||||||
|
simdjson_inline bool is_bigint() const noexcept;
|
||||||
|
|
||||||
simdjson_inline simdjson_result<dom::element> operator[](std::string_view key) const noexcept;
|
simdjson_inline simdjson_result<dom::element> operator[](std::string_view key) const noexcept;
|
||||||
simdjson_inline simdjson_result<dom::element> operator[](const char *key) const noexcept;
|
simdjson_inline simdjson_result<dom::element> operator[](const char *key) const noexcept;
|
||||||
|
|||||||
@@ -36,10 +36,11 @@ inline bool parser::dump_raw_tape(std::ostream &os) const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline simdjson_result<size_t> parser::read_file(std::string_view path) noexcept {
|
inline simdjson_result<size_t> parser::read_file(std::string_view path) noexcept {
|
||||||
|
const std::string path_copy(path);
|
||||||
// Open the file
|
// Open the file
|
||||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||||
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
|
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
|
||||||
std::FILE *fp = std::fopen(path.data(), "rb");
|
std::FILE *fp = std::fopen(path_copy.c_str(), "rb");
|
||||||
SIMDJSON_POP_DISABLE_WARNINGS
|
SIMDJSON_POP_DISABLE_WARNINGS
|
||||||
|
|
||||||
if (fp == nullptr) {
|
if (fp == nullptr) {
|
||||||
@@ -132,6 +133,7 @@ inline simdjson_result<element> parser::parse_into_document(document& provided_d
|
|||||||
buf += 3;
|
buf += 3;
|
||||||
len -= 3;
|
len -= 3;
|
||||||
}
|
}
|
||||||
|
implementation->_number_as_string = _number_as_string;
|
||||||
_error = implementation->parse(buf, len, provided_doc);
|
_error = implementation->parse(buf, len, provided_doc);
|
||||||
|
|
||||||
if (_error) { return _error; }
|
if (_error) { return _error; }
|
||||||
@@ -168,12 +170,7 @@ simdjson_inline simdjson_result<element> parser::parse(const padded_string_view
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline simdjson_result<document_stream> parser::parse_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept {
|
inline simdjson_result<document_stream> parser::parse_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept {
|
||||||
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
|
return parse_many(buf, len, batch_size, stream_format::whitespace_delimited);
|
||||||
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
|
|
||||||
buf += 3;
|
|
||||||
len -= 3;
|
|
||||||
}
|
|
||||||
return document_stream(*this, buf, len, batch_size);
|
|
||||||
}
|
}
|
||||||
inline simdjson_result<document_stream> parser::parse_many(const char *buf, size_t len, size_t batch_size) noexcept {
|
inline simdjson_result<document_stream> parser::parse_many(const char *buf, size_t len, size_t batch_size) noexcept {
|
||||||
return parse_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size);
|
return parse_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size);
|
||||||
@@ -185,6 +182,42 @@ inline simdjson_result<document_stream> parser::parse_many(const padded_string &
|
|||||||
return parse_many(s.data(), s.length(), batch_size);
|
return parse_many(s.data(), s.length(), batch_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline simdjson_result<document_stream> parser::parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept {
|
||||||
|
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
|
||||||
|
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
|
||||||
|
buf += 3;
|
||||||
|
len -= 3;
|
||||||
|
}
|
||||||
|
if (format == stream_format::comma_delimited_array) {
|
||||||
|
// Strip leading JSON whitespace.
|
||||||
|
while (len > 0 && (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n' || buf[0] == '\r')) {
|
||||||
|
buf++; len--;
|
||||||
|
}
|
||||||
|
// Expect the opening '['.
|
||||||
|
if (len == 0 || buf[0] != '[') { return TAPE_ERROR; }
|
||||||
|
buf++; len--;
|
||||||
|
// Strip trailing JSON whitespace.
|
||||||
|
while (len > 0 && (buf[len-1] == ' ' || buf[len-1] == '\t' || buf[len-1] == '\n' || buf[len-1] == '\r')) {
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
// Expect the closing ']'.
|
||||||
|
if (len == 0 || buf[len-1] != ']') { return TAPE_ERROR; }
|
||||||
|
len--;
|
||||||
|
// Fall through to comma_delimited over the array contents.
|
||||||
|
format = stream_format::comma_delimited;
|
||||||
|
}
|
||||||
|
return document_stream(*this, buf, len, batch_size, format);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::parse_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept {
|
||||||
|
return parse_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, format);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::parse_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
|
||||||
|
return parse_many(s.data(), s.length(), batch_size, format);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::parse_many(const padded_string &s, size_t batch_size, stream_format format) noexcept {
|
||||||
|
return parse_many(s.data(), s.length(), batch_size, format);
|
||||||
|
}
|
||||||
|
|
||||||
simdjson_inline size_t parser::capacity() const noexcept {
|
simdjson_inline size_t parser::capacity() const noexcept {
|
||||||
return implementation ? implementation->capacity() : 0;
|
return implementation ? implementation->capacity() : 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -494,6 +494,23 @@ public:
|
|||||||
/** @private We do not want to allow implicit conversion from C string to std::string. */
|
/** @private We do not want to allow implicit conversion from C string to std::string. */
|
||||||
simdjson_result<document_stream> parse_many(const char *buf, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept = delete;
|
simdjson_result<document_stream> parse_many(const char *buf, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept = delete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a stream of JSON documents with explicit format specification.
|
||||||
|
*
|
||||||
|
* @param buf The concatenated JSON documents.
|
||||||
|
* @param len The length of the buffer.
|
||||||
|
* @param batch_size The batch size to use.
|
||||||
|
* @param format The stream format.
|
||||||
|
* @return A stream of documents, or an error.
|
||||||
|
*/
|
||||||
|
inline simdjson_result<document_stream> parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept;
|
||||||
|
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||||
|
inline simdjson_result<document_stream> parse_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept;
|
||||||
|
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||||
|
inline simdjson_result<document_stream> parse_many(const std::string &s, size_t batch_size, stream_format format) noexcept;
|
||||||
|
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||||
|
inline simdjson_result<document_stream> parse_many(const padded_string &s, size_t batch_size, stream_format format) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
|
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
|
||||||
* and `max_depth` depth.
|
* and `max_depth` depth.
|
||||||
@@ -610,6 +627,13 @@ public:
|
|||||||
inline bool dump_raw_tape(std::ostream &os) const noexcept;
|
inline bool dump_raw_tape(std::ostream &os) const noexcept;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When enabled, big integers (exceeding uint64 range) are stored as strings
|
||||||
|
* in the tape instead of returning BIGINT_ERROR. Default: false.
|
||||||
|
*/
|
||||||
|
inline void number_as_string(bool enabled) noexcept { _number_as_string = enabled; }
|
||||||
|
inline bool number_as_string() const noexcept { return _number_as_string; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* The maximum document length this parser will automatically support.
|
* The maximum document length this parser will automatically support.
|
||||||
@@ -618,6 +642,9 @@ private:
|
|||||||
*/
|
*/
|
||||||
size_t _max_capacity;
|
size_t _max_capacity;
|
||||||
|
|
||||||
|
/** Whether to store big integers as strings instead of returning BIGINT_ERROR */
|
||||||
|
bool _number_as_string{false};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The loaded buffer (reused each time load() is called)
|
* The loaded buffer (reused each time load() is called)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -460,6 +460,12 @@ inline void string_builder<serializer>::append(simdjson::dom::element value) {
|
|||||||
case tape_type::STRING:
|
case tape_type::STRING:
|
||||||
format.string(iter.get_string_view());
|
format.string(iter.get_string_view());
|
||||||
break;
|
break;
|
||||||
|
case tape_type::BIGINT: {
|
||||||
|
// Big integer stored as string — output raw digits (no quotes)
|
||||||
|
auto sv = iter.get_string_view();
|
||||||
|
format.chars(sv.data(), sv.data() + sv.size());
|
||||||
|
break;
|
||||||
|
}
|
||||||
case tape_type::INT64:
|
case tape_type::INT64:
|
||||||
format.number(iter.next_tape_value<int64_t>());
|
format.number(iter.next_tape_value<int64_t>());
|
||||||
iter.json_index++; // numbers take up 2 spots, so we need to increment
|
iter.json_index++; // numbers take up 2 spots, so we need to increment
|
||||||
|
|||||||
@@ -41,6 +41,19 @@ simdjson_inline int leading_zeroes(uint64_t input_num) {
|
|||||||
#endif// _MSC_VER
|
#endif// _MSC_VER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
simdjson_inline int trailing_zeroes(uint64_t input_num) {
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
unsigned long trailing_zero = 0;
|
||||||
|
// Search the mask data from least significant bit (LSB)
|
||||||
|
// to most significant bit (MSB) for a set bit (1).
|
||||||
|
if (_BitScanForward64(&trailing_zero, input_num))
|
||||||
|
return (int)trailing_zero;
|
||||||
|
else return 64;
|
||||||
|
#else
|
||||||
|
return __builtin_ctzll(input_num);
|
||||||
|
#endif// _MSC_VER
|
||||||
|
}
|
||||||
|
|
||||||
} // unnamed namespace
|
} // unnamed namespace
|
||||||
} // namespace fallback
|
} // namespace fallback
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ namespace {
|
|||||||
struct backslash_and_quote {
|
struct backslash_and_quote {
|
||||||
public:
|
public:
|
||||||
static constexpr uint32_t BYTES_PROCESSED = 1;
|
static constexpr uint32_t BYTES_PROCESSED = 1;
|
||||||
// We only copy if dst is non-null.
|
|
||||||
simdjson_inline 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_quote_first() { return c == '"'; }
|
||||||
@@ -26,9 +25,7 @@ public:
|
|||||||
|
|
||||||
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
|
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
|
||||||
// store to dest unconditionally - we can overwrite the bits we don't like later
|
// store to dest unconditionally - we can overwrite the bits we don't like later
|
||||||
if(dst != nullptr) {
|
dst[0] = src[0];
|
||||||
dst[0] = src[0];
|
|
||||||
}
|
|
||||||
return { src[0] };
|
return { src[0] };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
#include "simdjson/lasx/begin.h"
|
#include "simdjson/lasx/begin.h"
|
||||||
#elif SIMDJSON_IMPLEMENTATION_LSX
|
#elif SIMDJSON_IMPLEMENTATION_LSX
|
||||||
#include "simdjson/lsx/begin.h"
|
#include "simdjson/lsx/begin.h"
|
||||||
|
#elif SIMDJSON_IMPLEMENTATION_RVV_VLS
|
||||||
|
#include "simdjson/rvv-vls/begin.h"
|
||||||
#elif SIMDJSON_IMPLEMENTATION_FALLBACK
|
#elif SIMDJSON_IMPLEMENTATION_FALLBACK
|
||||||
#include "simdjson/fallback/begin.h"
|
#include "simdjson/fallback/begin.h"
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace SIMDJSON_IMPLEMENTATION {
|
|||||||
namespace builder {
|
namespace builder {
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
requires(concepts::container_but_not_string<T> && !require_custom_serialization<T>)
|
requires(concepts::container_but_not_string<T> && ! concepts::optional_type<T> && !require_custom_serialization<T>)
|
||||||
constexpr void atom(string_builder &b, const T &t) {
|
constexpr void atom(string_builder &b, const T &t) {
|
||||||
auto it = t.begin();
|
auto it = t.begin();
|
||||||
auto end = t.end();
|
auto end = t.end();
|
||||||
@@ -130,7 +130,7 @@ template <typename T>
|
|||||||
requires(std::is_enum_v<T> && !require_custom_serialization<T>)
|
requires(std::is_enum_v<T> && !require_custom_serialization<T>)
|
||||||
void atom(string_builder &b, const T &e) {
|
void atom(string_builder &b, const T &e) {
|
||||||
#if SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
|
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
|
||||||
template for (constexpr auto enum_val : enumerators) {
|
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)));
|
constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(enum_val)));
|
||||||
if (e == [:enum_val:]) {
|
if (e == [:enum_val:]) {
|
||||||
@@ -275,7 +275,7 @@ simdjson_warn_unused simdjson_result<std::string> to_json_string(const Z &z, siz
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class Z>
|
template <class Z>
|
||||||
simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
|
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
|
||||||
string_builder b(initial_capacity);
|
string_builder b(initial_capacity);
|
||||||
append(b, z);
|
append(b, z);
|
||||||
std::string_view view;
|
std::string_view view;
|
||||||
@@ -294,30 +294,23 @@ string_builder& operator<<(string_builder& b, const Z& z) {
|
|||||||
template<constevalutil::fixed_string... FieldNames, typename T>
|
template<constevalutil::fixed_string... FieldNames, typename T>
|
||||||
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
|
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
|
||||||
void extract_from(string_builder &b, const T &obj) {
|
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('{');
|
b.append('{');
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
// Iterate through all members of T using reflection
|
// Iterate through all members of T using reflection
|
||||||
template for (constexpr auto mem : std::define_static_array(
|
static constexpr auto members = std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()));
|
||||||
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
|
template for (constexpr auto mem : members) {
|
||||||
|
|
||||||
if constexpr (std::meta::is_public(mem)) {
|
if constexpr (std::meta::is_public(mem)) {
|
||||||
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
|
static 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
|
// Only serialize this field if it's in our list of requested fields
|
||||||
if constexpr (should_extract(key)) {
|
if constexpr (((FieldNames.view() == key) || ...)) {
|
||||||
if (!first) {
|
if (!first) {
|
||||||
b.append(',');
|
b.append(',');
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
|
|
||||||
// Serialize the key
|
// Serialize the key
|
||||||
constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)));
|
static 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_raw(quoted_key);
|
||||||
b.append(':');
|
b.append(':');
|
||||||
|
|
||||||
@@ -352,7 +345,7 @@ simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t ini
|
|||||||
return std::string(s);
|
return std::string(s);
|
||||||
}
|
}
|
||||||
template <class Z>
|
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_warn_unused error_code 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::string_builder b(initial_capacity);
|
||||||
SIMDJSON_IMPLEMENTATION::builder::append(b, z);
|
SIMDJSON_IMPLEMENTATION::builder::append(b, z);
|
||||||
std::string_view view;
|
std::string_view view;
|
||||||
|
|||||||
@@ -27,12 +27,50 @@
|
|||||||
#define SIMDJSON_EXPERIMENTAL_HAS_NEON 1
|
#define SIMDJSON_EXPERIMENTAL_HAS_NEON 1
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(__loongarch_sx)
|
||||||
|
#ifndef SIMDJSON_EXPERIMENTAL_HAS_LSX
|
||||||
|
#define SIMDJSON_EXPERIMENTAL_HAS_LSX 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#if defined(__riscv_v_intrinsic) && __riscv_v_intrinsic >= 11000 && \
|
||||||
|
defined(__riscv_vector)
|
||||||
|
#ifndef SIMDJSON_EXPERIMENTAL_HAS_RVV
|
||||||
|
#define SIMDJSON_EXPERIMENTAL_HAS_RVV 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#if (defined(__PPC64__) || defined(_M_PPC64)) && defined(__ALTIVEC__)
|
||||||
|
#ifndef SIMDJSON_EXPERIMENTAL_HAS_PPC64
|
||||||
|
#define SIMDJSON_EXPERIMENTAL_HAS_PPC64 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
|
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
|
||||||
#include <arm_neon.h>
|
#include <arm_neon.h>
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <intrin.h>
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
|
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
|
||||||
#include <emmintrin.h>
|
#include <emmintrin.h>
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <intrin.h>
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
#if SIMDJSON_EXPERIMENTAL_HAS_LSX
|
||||||
|
#include <lsxintrin.h>
|
||||||
|
#endif
|
||||||
|
#if SIMDJSON_EXPERIMENTAL_HAS_RVV
|
||||||
|
#include <riscv_vector.h>
|
||||||
|
#endif
|
||||||
|
#if SIMDJSON_EXPERIMENTAL_HAS_PPC64
|
||||||
|
#include <altivec.h>
|
||||||
|
#ifdef bool
|
||||||
|
#undef bool
|
||||||
|
#endif
|
||||||
|
#ifdef vector
|
||||||
|
#undef vector
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
namespace simdjson {
|
namespace simdjson {
|
||||||
namespace SIMDJSON_IMPLEMENTATION {
|
namespace SIMDJSON_IMPLEMENTATION {
|
||||||
@@ -133,16 +171,45 @@ simdjson_inline bool fast_needs_escaping(std::string_view view) {
|
|||||||
}
|
}
|
||||||
return _mm_movemask_epi8(running) != 0;
|
return _mm_movemask_epi8(running) != 0;
|
||||||
}
|
}
|
||||||
|
#elif SIMDJSON_EXPERIMENTAL_HAS_PPC64
|
||||||
|
simdjson_inline bool fast_needs_escaping(std::string_view view) {
|
||||||
|
if (view.size() < 16) {
|
||||||
|
return simple_needs_escaping(view);
|
||||||
|
}
|
||||||
|
size_t i = 0;
|
||||||
|
__vector unsigned char running = vec_splats((unsigned char)0);
|
||||||
|
__vector unsigned char v34 = vec_splats((unsigned char)34);
|
||||||
|
__vector unsigned char v92 = vec_splats((unsigned char)92);
|
||||||
|
__vector unsigned char v32 = vec_splats((unsigned char)32);
|
||||||
|
|
||||||
|
for (; i + 15 < view.size(); i += 16) {
|
||||||
|
__vector unsigned char word =
|
||||||
|
vec_vsx_ld(0, reinterpret_cast<const unsigned char *>(view.data() + i));
|
||||||
|
running = vec_or(running, (__vector unsigned char)vec_cmpeq(word, v34));
|
||||||
|
running = vec_or(running, (__vector unsigned char)vec_cmpeq(word, v92));
|
||||||
|
running = vec_or(running,
|
||||||
|
(__vector unsigned char)vec_cmplt(word, v32));
|
||||||
|
}
|
||||||
|
if (i < view.size()) {
|
||||||
|
__vector unsigned char word = vec_vsx_ld(
|
||||||
|
0, reinterpret_cast<const unsigned char *>(view.data() + view.length() - 16));
|
||||||
|
running = vec_or(running, (__vector unsigned char)vec_cmpeq(word, v34));
|
||||||
|
running = vec_or(running, (__vector unsigned char)vec_cmpeq(word, v92));
|
||||||
|
running = vec_or(running,
|
||||||
|
(__vector unsigned char)vec_cmplt(word, v32));
|
||||||
|
}
|
||||||
|
return !vec_all_eq(running, vec_splats((unsigned char)0));
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
simdjson_inline bool fast_needs_escaping(std::string_view view) {
|
simdjson_inline bool fast_needs_escaping(std::string_view view) {
|
||||||
return simple_needs_escaping(view);
|
return simple_needs_escaping(view);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SIMDJSON_CONSTEXPR_LAMBDA inline size_t
|
// Scalar fallback for finding next quotable character
|
||||||
find_next_json_quotable_character(const std::string_view view,
|
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
|
||||||
size_t location) noexcept {
|
find_next_json_quotable_character_scalar(const std::string_view view,
|
||||||
|
size_t location) noexcept {
|
||||||
for (auto pos = view.begin() + location; pos != view.end(); ++pos) {
|
for (auto pos = view.begin() + location; pos != view.end(); ++pos) {
|
||||||
if (json_quotable_character[static_cast<uint8_t>(*pos)]) {
|
if (json_quotable_character[static_cast<uint8_t>(*pos)]) {
|
||||||
return pos - view.begin();
|
return pos - view.begin();
|
||||||
@@ -151,6 +218,213 @@ find_next_json_quotable_character(const std::string_view view,
|
|||||||
return size_t(view.size());
|
return size_t(view.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SIMD-accelerated position finding that directly locates the first quotable
|
||||||
|
// character, combining detection and position extraction in a single pass to
|
||||||
|
// minimize redundant work.
|
||||||
|
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
|
||||||
|
simdjson_inline size_t
|
||||||
|
find_next_json_quotable_character(const std::string_view view,
|
||||||
|
size_t location) noexcept {
|
||||||
|
const size_t len = view.size();
|
||||||
|
const uint8_t *ptr =
|
||||||
|
reinterpret_cast<const uint8_t *>(view.data()) + location;
|
||||||
|
size_t remaining = len - location;
|
||||||
|
|
||||||
|
// SIMD constants for characters requiring escape
|
||||||
|
uint8x16_t v34 = vdupq_n_u8(34); // '"'
|
||||||
|
uint8x16_t v92 = vdupq_n_u8(92); // '\\'
|
||||||
|
uint8x16_t v32 = vdupq_n_u8(32); // control char threshold
|
||||||
|
|
||||||
|
while (remaining >= 16) {
|
||||||
|
uint8x16_t word = vld1q_u8(ptr);
|
||||||
|
|
||||||
|
// Check for quotable characters: '"', '\\', or control chars (< 32)
|
||||||
|
uint8x16_t needs_escape = vceqq_u8(word, v34);
|
||||||
|
needs_escape = vorrq_u8(needs_escape, vceqq_u8(word, v92));
|
||||||
|
needs_escape = vorrq_u8(needs_escape, vcltq_u8(word, v32));
|
||||||
|
|
||||||
|
const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(needs_escape), 4);
|
||||||
|
const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
|
||||||
|
if(mask != 0) {
|
||||||
|
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
|
||||||
|
auto trailing_zero = trailing_zeroes(mask);
|
||||||
|
return offset + (trailing_zero >> 2);
|
||||||
|
}
|
||||||
|
ptr += 16;
|
||||||
|
remaining -= 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scalar fallback for remaining bytes
|
||||||
|
size_t current = len - remaining;
|
||||||
|
return find_next_json_quotable_character_scalar(view, current);
|
||||||
|
}
|
||||||
|
#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2
|
||||||
|
simdjson_inline size_t
|
||||||
|
find_next_json_quotable_character(const std::string_view view,
|
||||||
|
size_t location) noexcept {
|
||||||
|
const size_t len = view.size();
|
||||||
|
const uint8_t *ptr =
|
||||||
|
reinterpret_cast<const uint8_t *>(view.data()) + location;
|
||||||
|
size_t remaining = len - location;
|
||||||
|
|
||||||
|
// SIMD constants
|
||||||
|
__m128i v34 = _mm_set1_epi8(34); // '"'
|
||||||
|
__m128i v92 = _mm_set1_epi8(92); // '\\'
|
||||||
|
__m128i v31 = _mm_set1_epi8(31); // for control char detection
|
||||||
|
|
||||||
|
while (remaining >= 16) {
|
||||||
|
__m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
|
||||||
|
|
||||||
|
// Check for quotable characters
|
||||||
|
__m128i needs_escape = _mm_cmpeq_epi8(word, v34);
|
||||||
|
needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(word, v92));
|
||||||
|
needs_escape = _mm_or_si128(
|
||||||
|
needs_escape,
|
||||||
|
_mm_cmpeq_epi8(_mm_subs_epu8(word, v31), _mm_setzero_si128()));
|
||||||
|
|
||||||
|
int mask = _mm_movemask_epi8(needs_escape);
|
||||||
|
if (mask != 0) {
|
||||||
|
// Found quotable character - use trailing zero count to find position
|
||||||
|
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
|
||||||
|
return offset + trailing_zeroes(mask);
|
||||||
|
}
|
||||||
|
ptr += 16;
|
||||||
|
remaining -= 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scalar fallback for remaining bytes
|
||||||
|
size_t current = len - remaining;
|
||||||
|
return find_next_json_quotable_character_scalar(view, current);
|
||||||
|
}
|
||||||
|
#elif SIMDJSON_EXPERIMENTAL_HAS_LSX
|
||||||
|
simdjson_inline size_t
|
||||||
|
find_next_json_quotable_character(const std::string_view view,
|
||||||
|
size_t location) noexcept {
|
||||||
|
const size_t len = view.size();
|
||||||
|
const uint8_t *ptr =
|
||||||
|
reinterpret_cast<const uint8_t *>(view.data()) + location;
|
||||||
|
size_t remaining = len - location;
|
||||||
|
|
||||||
|
//SIMD constants for characters requiring escape
|
||||||
|
__m128i v34 = __lsx_vreplgr2vr_b(34); // '"'
|
||||||
|
__m128i v92 = __lsx_vreplgr2vr_b(92); // '\\'
|
||||||
|
__m128i v32 = __lsx_vreplgr2vr_b(32); // control char threshold
|
||||||
|
|
||||||
|
while (remaining >= 16){
|
||||||
|
__m128i word = __lsx_vld(ptr, 0);
|
||||||
|
|
||||||
|
//Check for the quotable characters: '"', '\\', or control char (<32)
|
||||||
|
__m128i needs_escape = __lsx_vseq_b(word, v34);
|
||||||
|
needs_escape = __lsx_vor_v(needs_escape, __lsx_vseq_b(word, v92));
|
||||||
|
needs_escape = __lsx_vor_v(needs_escape, __lsx_vslt_bu(word, v32));
|
||||||
|
|
||||||
|
if (!__lsx_bz_v(needs_escape)){
|
||||||
|
|
||||||
|
//Found quotable character - extract exact byte position
|
||||||
|
uint64_t lo = __lsx_vpickve2gr_du(needs_escape,0);
|
||||||
|
uint64_t hi = __lsx_vpickve2gr_du(needs_escape,1);
|
||||||
|
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
|
||||||
|
if ( lo != 0) {
|
||||||
|
return offset + trailing_zeroes(lo) / 8;
|
||||||
|
} else {
|
||||||
|
return offset + 8 + trailing_zeroes(hi) / 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ptr += 16;
|
||||||
|
remaining -= 16;
|
||||||
|
}
|
||||||
|
size_t current = len - remaining;
|
||||||
|
return find_next_json_quotable_character_scalar(view, current);
|
||||||
|
}
|
||||||
|
#elif SIMDJSON_EXPERIMENTAL_HAS_RVV
|
||||||
|
simdjson_inline size_t
|
||||||
|
find_next_json_quotable_character(const std::string_view view,
|
||||||
|
size_t location) noexcept {
|
||||||
|
const size_t len = view.size();
|
||||||
|
const uint8_t *ptr =
|
||||||
|
reinterpret_cast<const uint8_t *>(view.data()) + location;
|
||||||
|
size_t remaining = len - location;
|
||||||
|
|
||||||
|
while (remaining > 0) {
|
||||||
|
size_t vl = __riscv_vsetvl_e8m1(remaining);
|
||||||
|
vuint8m1_t word = __riscv_vle8_v_u8m1(ptr, vl);
|
||||||
|
|
||||||
|
// Check for quotable characters: '"', '\\', or control chars (< 32)
|
||||||
|
vbool8_t needs_escape = __riscv_vmseq(word, (uint8_t)34, vl);
|
||||||
|
needs_escape = __riscv_vmor(needs_escape,
|
||||||
|
__riscv_vmseq(word, (uint8_t)92, vl), vl);
|
||||||
|
needs_escape = __riscv_vmor(needs_escape,
|
||||||
|
__riscv_vmsltu(word, (uint8_t)32, vl), vl);
|
||||||
|
|
||||||
|
long first = __riscv_vfirst(needs_escape, vl);
|
||||||
|
if (first >= 0) {
|
||||||
|
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
|
||||||
|
return offset + first;
|
||||||
|
}
|
||||||
|
ptr += vl;
|
||||||
|
remaining -= vl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
#elif SIMDJSON_EXPERIMENTAL_HAS_PPC64
|
||||||
|
simdjson_inline size_t
|
||||||
|
find_next_json_quotable_character(const std::string_view view,
|
||||||
|
size_t location) noexcept {
|
||||||
|
const size_t len = view.size();
|
||||||
|
const uint8_t *ptr =
|
||||||
|
reinterpret_cast<const uint8_t *>(view.data()) + location;
|
||||||
|
size_t remaining = len - location;
|
||||||
|
|
||||||
|
// SIMD constants for characters requiring escape
|
||||||
|
__vector unsigned char v34 = vec_splats((unsigned char)34); // '"'
|
||||||
|
__vector unsigned char v92 = vec_splats((unsigned char)92); // '\\'
|
||||||
|
__vector unsigned char v32 = vec_splats((unsigned char)32); // control char threshold
|
||||||
|
|
||||||
|
// Bitmask for vec_vbpermq to extract one bit per byte
|
||||||
|
const __vector unsigned char perm_mask = {0x78, 0x70, 0x68, 0x60, 0x58, 0x50,
|
||||||
|
0x48, 0x40, 0x38, 0x30, 0x28, 0x20,
|
||||||
|
0x18, 0x10, 0x08, 0x00};
|
||||||
|
|
||||||
|
while (remaining >= 16) {
|
||||||
|
__vector unsigned char word =
|
||||||
|
vec_vsx_ld(0, reinterpret_cast<const unsigned char *>(ptr));
|
||||||
|
|
||||||
|
// Check for quotable characters: '"', '\\', or control chars (< 32)
|
||||||
|
__vector unsigned char needs_escape =
|
||||||
|
(__vector unsigned char)vec_cmpeq(word, v34);
|
||||||
|
needs_escape = vec_or(needs_escape,
|
||||||
|
(__vector unsigned char)vec_cmpeq(word, v92));
|
||||||
|
needs_escape = vec_or(needs_escape,
|
||||||
|
(__vector unsigned char)vec_cmplt(word, v32));
|
||||||
|
|
||||||
|
__vector unsigned long long result =
|
||||||
|
(__vector unsigned long long)vec_vbpermq(needs_escape, perm_mask);
|
||||||
|
#ifdef __LITTLE_ENDIAN__
|
||||||
|
unsigned int mask = static_cast<unsigned int>(result[1]);
|
||||||
|
#else
|
||||||
|
unsigned int mask = static_cast<unsigned int>(result[0]);
|
||||||
|
#endif
|
||||||
|
if (mask != 0) {
|
||||||
|
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
|
||||||
|
return offset + __builtin_ctz(mask);
|
||||||
|
}
|
||||||
|
ptr += 16;
|
||||||
|
remaining -= 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scalar fallback for remaining bytes
|
||||||
|
size_t current = len - remaining;
|
||||||
|
return find_next_json_quotable_character_scalar(view, current);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
|
||||||
|
find_next_json_quotable_character(const std::string_view view,
|
||||||
|
size_t location) noexcept {
|
||||||
|
return find_next_json_quotable_character_scalar(view, location);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
|
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
|
||||||
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
|
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
|
||||||
"\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r",
|
"\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r",
|
||||||
@@ -163,7 +437,7 @@ SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
|
|||||||
// control characters (U+0000 through U+001F). There are two-character sequence
|
// control characters (U+0000 through U+001F). There are two-character sequence
|
||||||
// escape representations of some popular characters:
|
// escape representations of some popular characters:
|
||||||
// \", \\, \b, \f, \n, \r, \t.
|
// \", \\, \b, \f, \n, \r, \t.
|
||||||
SIMDJSON_CONSTEXPR_LAMBDA void escape_json_char(char c, char *&out) {
|
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&out) {
|
||||||
if (c == '"') {
|
if (c == '"') {
|
||||||
memcpy(out, "\\\"", 2);
|
memcpy(out, "\\\"", 2);
|
||||||
out += 2;
|
out += 2;
|
||||||
@@ -177,14 +451,20 @@ SIMDJSON_CONSTEXPR_LAMBDA void escape_json_char(char c, char *&out) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Writes the escaped version of input to out, returning the number of bytes
|
||||||
|
// written. Uses SIMD position finding to locate quotable characters efficiently.
|
||||||
inline size_t write_string_escaped(const std::string_view input, char *out) {
|
inline size_t write_string_escaped(const std::string_view input, char *out) {
|
||||||
size_t mysize = input.size();
|
size_t mysize = input.size();
|
||||||
if (!fast_needs_escaping(input)) { // fast path!
|
|
||||||
|
// Use SIMD position finder directly - it returns mysize if no escape needed
|
||||||
|
size_t location = find_next_json_quotable_character(input, 0);
|
||||||
|
if (location == mysize) {
|
||||||
|
// Fast path: no escaping needed
|
||||||
memcpy(out, input.data(), input.size());
|
memcpy(out, input.data(), input.size());
|
||||||
return input.size();
|
return input.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *const initout = out;
|
const char *const initout = out;
|
||||||
size_t location = find_next_json_quotable_character(input, 0);
|
|
||||||
memcpy(out, input.data(), location);
|
memcpy(out, input.data(), location);
|
||||||
out += location;
|
out += location;
|
||||||
escape_json_char(input[location], out);
|
escape_json_char(input[location], out);
|
||||||
@@ -216,7 +496,7 @@ simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// check for overflow, most of the time there is no overflow
|
// check for overflow, most of the time there is no overflow
|
||||||
if (simdjson_likely(position + upcoming_bytes < position)) {
|
if (simdjson_unlikely(position + upcoming_bytes < position)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// We will rarely get here.
|
// We will rarely get here.
|
||||||
@@ -225,7 +505,7 @@ simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
|
|||||||
return is_valid;
|
return is_valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
simdjson_inline void string_builder::grow_buffer(size_t desired_capacity) {
|
inline void string_builder::grow_buffer(size_t desired_capacity) {
|
||||||
if (!is_valid) {
|
if (!is_valid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -383,12 +663,29 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else SIMDJSON_IF_CONSTEXPR(std::is_unsigned<number_type>::value) {
|
else SIMDJSON_IF_CONSTEXPR(std::is_unsigned<number_type>::value) {
|
||||||
|
// Process 4 digits at a time instead of 2, reducing store operations
|
||||||
|
// and divisions by approximately half for large numbers.
|
||||||
constexpr size_t max_number_size = 20;
|
constexpr size_t max_number_size = 20;
|
||||||
if (capacity_check(max_number_size)) {
|
if (capacity_check(max_number_size)) {
|
||||||
using unsigned_type = typename std::make_unsigned<number_type>::type;
|
using unsigned_type = typename std::make_unsigned<number_type>::type;
|
||||||
unsigned_type pv = static_cast<unsigned_type>(v);
|
unsigned_type pv = static_cast<unsigned_type>(v);
|
||||||
size_t dc = internal::digit_count(pv);
|
size_t dc = internal::digit_count(pv);
|
||||||
char *write_pointer = buffer.get() + position + dc - 1;
|
char *write_pointer = buffer.get() + position + dc - 1;
|
||||||
|
|
||||||
|
// Process 4 digits per iteration for large numbers
|
||||||
|
while (pv >= 10000) {
|
||||||
|
unsigned_type q = pv / 10000;
|
||||||
|
unsigned_type r = pv % 10000;
|
||||||
|
unsigned_type r_hi = r / 100; // High 2 digits of remainder
|
||||||
|
unsigned_type r_lo = r % 100; // Low 2 digits of remainder
|
||||||
|
// Write low 2 digits first (rightmost), then high 2 digits
|
||||||
|
memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2);
|
||||||
|
memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2);
|
||||||
|
write_pointer -= 4;
|
||||||
|
pv = q;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle remaining 1-4 digits with original 2-digit loop
|
||||||
while (pv >= 100) {
|
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;
|
write_pointer -= 2;
|
||||||
@@ -403,6 +700,7 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
|
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
|
||||||
|
// Same 4-digit batching as unsigned path for signed integers
|
||||||
constexpr size_t max_number_size = 20;
|
constexpr size_t max_number_size = 20;
|
||||||
if (capacity_check(max_number_size)) {
|
if (capacity_check(max_number_size)) {
|
||||||
using unsigned_type = typename std::make_unsigned<number_type>::type;
|
using unsigned_type = typename std::make_unsigned<number_type>::type;
|
||||||
@@ -416,6 +714,20 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
|
|||||||
buffer.get()[position] = '-';
|
buffer.get()[position] = '-';
|
||||||
position += negative ? 1 : 0;
|
position += negative ? 1 : 0;
|
||||||
char *write_pointer = buffer.get() + position + dc - 1;
|
char *write_pointer = buffer.get() + position + dc - 1;
|
||||||
|
|
||||||
|
// Process 4 digits per iteration for large numbers
|
||||||
|
while (pv >= 10000) {
|
||||||
|
unsigned_type q = pv / 10000;
|
||||||
|
unsigned_type r = pv % 10000;
|
||||||
|
unsigned_type r_hi = r / 100;
|
||||||
|
unsigned_type r_lo = r % 100;
|
||||||
|
memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2);
|
||||||
|
memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2);
|
||||||
|
write_pointer -= 4;
|
||||||
|
pv = q;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle remaining 1-4 digits
|
||||||
while (pv >= 100) {
|
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;
|
write_pointer -= 2;
|
||||||
@@ -530,7 +842,7 @@ simdjson_inline void string_builder::append(const T &value) {
|
|||||||
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
|
#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>
|
template <std::ranges::range R>
|
||||||
requires(!std::is_convertible<R, std::string_view>::value && !require_custom_serialization<R>)
|
requires(!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
|
||||||
simdjson_inline void string_builder::append(const R &range) noexcept {
|
simdjson_inline void string_builder::append(const R &range) noexcept {
|
||||||
auto it = std::ranges::begin(range);
|
auto it = std::ranges::begin(range);
|
||||||
auto end = std::ranges::end(range);
|
auto end = std::ranges::end(range);
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ public:
|
|||||||
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
|
#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>
|
template <std::ranges::range R>
|
||||||
requires (!std::is_convertible<R, std::string_view>::value && !require_custom_serialization<R>)
|
requires (!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
|
||||||
simdjson_inline void append(const R &range) noexcept;
|
simdjson_inline void append(const R &range) noexcept;
|
||||||
#endif
|
#endif
|
||||||
/**
|
/**
|
||||||
@@ -249,7 +249,7 @@ private:
|
|||||||
* If the allocation fails, is_valid is set to false. We expect
|
* If the allocation fails, is_valid is set to false. We expect
|
||||||
* that this function would not be repeatedly called.
|
* that this function would not be repeatedly called.
|
||||||
*/
|
*/
|
||||||
simdjson_inline void grow_buffer(size_t desired_capacity);
|
inline void grow_buffer(size_t desired_capacity);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We use this helper function to make sure that is_valid is kept consistent.
|
* We use this helper function to make sure that is_valid is kept consistent.
|
||||||
@@ -280,7 +280,7 @@ simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t ini
|
|||||||
return std::string(s);
|
return std::string(s);
|
||||||
}
|
}
|
||||||
template <class Z>
|
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_warn_unused error_code 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);
|
simdjson::SIMDJSON_IMPLEMENTATION::builder::string_builder b(initial_capacity);
|
||||||
b.append(z);
|
b.append(z);
|
||||||
std::string_view sv;
|
std::string_view sv;
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ public:
|
|||||||
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
|
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
|
||||||
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
|
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
|
||||||
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
|
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
|
||||||
simdjson_warn_unused std::pair<const uint8_t *,bool> parse_string_if_needed(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
|
|
||||||
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
|
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
|
||||||
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
|
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
|
||||||
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
|
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
|
||||||
@@ -75,6 +74,7 @@ inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(s
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept {
|
inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept {
|
||||||
|
if(max_depth > SIMDJSON_MAX_DEPTH) { return CAPACITY; }
|
||||||
// Stage 2 stacks
|
// Stage 2 stacks
|
||||||
open_containers.reset(new (std::nothrow) open_container[max_depth]);
|
open_containers.reset(new (std::nothrow) open_container[max_depth]);
|
||||||
is_array.reset(new (std::nothrow) bool[max_depth]);
|
is_array.reset(new (std::nothrow) bool[max_depth]);
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
#include "simdjson/generic/ondemand/deserialize.h"
|
#include "simdjson/generic/ondemand/deserialize.h"
|
||||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
namespace simdjson {
|
namespace simdjson {
|
||||||
namespace SIMDJSON_IMPLEMENTATION {
|
namespace SIMDJSON_IMPLEMENTATION {
|
||||||
namespace ondemand {
|
namespace ondemand {
|
||||||
@@ -131,6 +133,18 @@ simdjson_inline simdjson_result<int64_t> document::get_int64() noexcept {
|
|||||||
simdjson_inline simdjson_result<int64_t> document::get_int64_in_string() noexcept {
|
simdjson_inline simdjson_result<int64_t> document::get_int64_in_string() noexcept {
|
||||||
return get_root_value_iterator().get_root_int64_in_string(true);
|
return get_root_value_iterator().get_root_int64_in_string(true);
|
||||||
}
|
}
|
||||||
|
simdjson_inline simdjson_result<uint32_t> document::get_uint32() noexcept {
|
||||||
|
uint64_t result;
|
||||||
|
SIMDJSON_TRY(get_uint64().get(result));
|
||||||
|
if (result > (std::numeric_limits<uint32_t>::max)()) { return NUMBER_OUT_OF_RANGE; }
|
||||||
|
return static_cast<uint32_t>(result);
|
||||||
|
}
|
||||||
|
simdjson_inline simdjson_result<int32_t> document::get_int32() noexcept {
|
||||||
|
int64_t result;
|
||||||
|
SIMDJSON_TRY(get_int64().get(result));
|
||||||
|
if (result > (std::numeric_limits<int32_t>::max)() || result < (std::numeric_limits<int32_t>::min)()) { return NUMBER_OUT_OF_RANGE; }
|
||||||
|
return static_cast<int32_t>(result);
|
||||||
|
}
|
||||||
simdjson_inline simdjson_result<double> document::get_double() noexcept {
|
simdjson_inline simdjson_result<double> document::get_double() noexcept {
|
||||||
return get_root_value_iterator().get_root_double(true);
|
return get_root_value_iterator().get_root_double(true);
|
||||||
}
|
}
|
||||||
@@ -164,6 +178,8 @@ template<> simdjson_inline simdjson_result<std::string_view> document::get() & n
|
|||||||
template<> simdjson_inline simdjson_result<double> document::get() & noexcept { return get_double(); }
|
template<> simdjson_inline simdjson_result<double> document::get() & noexcept { return get_double(); }
|
||||||
template<> simdjson_inline simdjson_result<uint64_t> document::get() & noexcept { return get_uint64(); }
|
template<> simdjson_inline simdjson_result<uint64_t> document::get() & noexcept { return get_uint64(); }
|
||||||
template<> simdjson_inline simdjson_result<int64_t> document::get() & noexcept { return get_int64(); }
|
template<> simdjson_inline simdjson_result<int64_t> document::get() & noexcept { return get_int64(); }
|
||||||
|
template<> simdjson_inline simdjson_result<uint32_t> document::get() & noexcept { return get_uint32(); }
|
||||||
|
template<> simdjson_inline simdjson_result<int32_t> document::get() & noexcept { return get_int32(); }
|
||||||
template<> simdjson_inline simdjson_result<bool> document::get() & noexcept { return get_bool(); }
|
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 simdjson_result<value> document::get() & noexcept { return get_value(); }
|
||||||
|
|
||||||
@@ -174,6 +190,8 @@ template<> simdjson_warn_unused simdjson_inline error_code document::get(std::st
|
|||||||
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(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(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(int64_t& out) & noexcept { return get_int64().get(out); }
|
||||||
|
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint32_t& out) & noexcept { return get_uint32().get(out); }
|
||||||
|
template<> simdjson_warn_unused simdjson_inline error_code document::get(int32_t& out) & noexcept { return get_int32().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(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_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); }
|
||||||
|
|
||||||
@@ -504,6 +522,14 @@ simdjson_inline simdjson_result<int64_t> simdjson_result<SIMDJSON_IMPLEMENTATION
|
|||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.get_int64_in_string();
|
return first.get_int64_in_string();
|
||||||
}
|
}
|
||||||
|
simdjson_inline simdjson_result<uint32_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_uint32() noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return first.get_uint32();
|
||||||
|
}
|
||||||
|
simdjson_inline simdjson_result<int32_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_int32() noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return first.get_int32();
|
||||||
|
}
|
||||||
simdjson_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_double() noexcept {
|
simdjson_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_double() noexcept {
|
||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.get_double();
|
return first.get_double();
|
||||||
@@ -627,27 +653,27 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::op
|
|||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator uint64_t() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator uint64_t() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_uint64();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator int64_t() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator int64_t() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_int64();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator double() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator double() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_double();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator std::string_view() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator std::string_view() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_string();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_raw_json_string();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator bool() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator bool() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_bool();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator SIMDJSON_IMPLEMENTATION::ondemand::value() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator SIMDJSON_IMPLEMENTATION::ondemand::value() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
@@ -731,6 +757,18 @@ simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64() noexc
|
|||||||
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); }
|
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); }
|
||||||
simdjson_inline simdjson_result<int64_t> document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); }
|
simdjson_inline simdjson_result<int64_t> document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); }
|
||||||
simdjson_inline simdjson_result<int64_t> document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); }
|
simdjson_inline simdjson_result<int64_t> document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); }
|
||||||
|
simdjson_inline simdjson_result<uint32_t> document_reference::get_uint32() noexcept {
|
||||||
|
uint64_t result;
|
||||||
|
SIMDJSON_TRY(doc->get_root_value_iterator().get_root_uint64(false).get(result));
|
||||||
|
if (result > (std::numeric_limits<uint32_t>::max)()) { return NUMBER_OUT_OF_RANGE; }
|
||||||
|
return static_cast<uint32_t>(result);
|
||||||
|
}
|
||||||
|
simdjson_inline simdjson_result<int32_t> document_reference::get_int32() noexcept {
|
||||||
|
int64_t result;
|
||||||
|
SIMDJSON_TRY(doc->get_root_value_iterator().get_root_int64(false).get(result));
|
||||||
|
if (result > (std::numeric_limits<int32_t>::max)() || result < (std::numeric_limits<int32_t>::min)()) { return NUMBER_OUT_OF_RANGE; }
|
||||||
|
return static_cast<int32_t>(result);
|
||||||
|
}
|
||||||
simdjson_inline simdjson_result<double> document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
|
simdjson_inline simdjson_result<double> document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
|
||||||
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<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); }
|
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); }
|
||||||
@@ -748,6 +786,8 @@ template<> simdjson_inline simdjson_result<std::string_view> document_reference:
|
|||||||
template<> simdjson_inline simdjson_result<double> document_reference::get() & noexcept { return get_double(); }
|
template<> simdjson_inline simdjson_result<double> document_reference::get() & noexcept { return get_double(); }
|
||||||
template<> simdjson_inline simdjson_result<uint64_t> document_reference::get() & noexcept { return get_uint64(); }
|
template<> simdjson_inline simdjson_result<uint64_t> document_reference::get() & noexcept { return get_uint64(); }
|
||||||
template<> simdjson_inline simdjson_result<int64_t> document_reference::get() & noexcept { return get_int64(); }
|
template<> simdjson_inline simdjson_result<int64_t> document_reference::get() & noexcept { return get_int64(); }
|
||||||
|
template<> simdjson_inline simdjson_result<uint32_t> document_reference::get() & noexcept { return get_uint32(); }
|
||||||
|
template<> simdjson_inline simdjson_result<int32_t> document_reference::get() & noexcept { return get_int32(); }
|
||||||
template<> simdjson_inline simdjson_result<bool> document_reference::get() & noexcept { return get_bool(); }
|
template<> simdjson_inline simdjson_result<bool> document_reference::get() & noexcept { return get_bool(); }
|
||||||
template<> simdjson_inline simdjson_result<value> document_reference::get() & noexcept { return get_value(); }
|
template<> simdjson_inline simdjson_result<value> document_reference::get() & noexcept { return get_value(); }
|
||||||
#if SIMDJSON_EXCEPTIONS
|
#if SIMDJSON_EXCEPTIONS
|
||||||
@@ -879,6 +919,14 @@ simdjson_inline simdjson_result<int64_t> simdjson_result<SIMDJSON_IMPLEMENTATION
|
|||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.get_int64_in_string();
|
return first.get_int64_in_string();
|
||||||
}
|
}
|
||||||
|
simdjson_inline simdjson_result<uint32_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_uint32() noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return first.get_uint32();
|
||||||
|
}
|
||||||
|
simdjson_inline simdjson_result<int32_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_int32() noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return first.get_int32();
|
||||||
|
}
|
||||||
simdjson_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_double() noexcept {
|
simdjson_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_double() noexcept {
|
||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.get_double();
|
return first.get_double();
|
||||||
@@ -994,27 +1042,27 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_refe
|
|||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator uint64_t() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator uint64_t() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_uint64();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator int64_t() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator int64_t() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_int64();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator double() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator double() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_double();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator std::string_view() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator std::string_view() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_string();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_raw_json_string();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator bool() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator bool() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get_bool();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator SIMDJSON_IMPLEMENTATION::ondemand::value() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator SIMDJSON_IMPLEMENTATION::ondemand::value() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
|
|||||||
@@ -76,6 +76,26 @@ public:
|
|||||||
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
|
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
|
||||||
*/
|
*/
|
||||||
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
||||||
|
/**
|
||||||
|
* Cast this JSON value to a 32-bit unsigned integer.
|
||||||
|
*
|
||||||
|
* Calls get_uint64() and checks that the result fits in a uint32_t.
|
||||||
|
*
|
||||||
|
* @returns A 32-bit unsigned integer.
|
||||||
|
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
|
||||||
|
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
|
||||||
|
*/
|
||||||
|
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
|
||||||
|
/**
|
||||||
|
* Cast this JSON value to a 32-bit signed integer.
|
||||||
|
*
|
||||||
|
* Calls get_int64() and checks that the result fits in an int32_t.
|
||||||
|
*
|
||||||
|
* @returns A 32-bit signed integer.
|
||||||
|
* @returns INCORRECT_TYPE If the JSON value is not an integer.
|
||||||
|
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
|
||||||
|
*/
|
||||||
|
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
|
||||||
/**
|
/**
|
||||||
* Cast this JSON value to a double.
|
* Cast this JSON value to a double.
|
||||||
*
|
*
|
||||||
@@ -299,21 +319,21 @@ public:
|
|||||||
* @returns A signed 64-bit integer.
|
* @returns A signed 64-bit integer.
|
||||||
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
|
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
|
||||||
*/
|
*/
|
||||||
simdjson_inline operator uint64_t() noexcept(false);
|
explicit simdjson_inline operator uint64_t() noexcept(false);
|
||||||
/**
|
/**
|
||||||
* Cast this JSON value to a signed integer.
|
* Cast this JSON value to a signed integer.
|
||||||
*
|
*
|
||||||
* @returns A signed 64-bit integer.
|
* @returns A signed 64-bit integer.
|
||||||
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
|
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
|
||||||
*/
|
*/
|
||||||
simdjson_inline operator int64_t() noexcept(false);
|
explicit simdjson_inline operator int64_t() noexcept(false);
|
||||||
/**
|
/**
|
||||||
* Cast this JSON value to a double.
|
* Cast this JSON value to a double.
|
||||||
*
|
*
|
||||||
* @returns A double.
|
* @returns A double.
|
||||||
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
|
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
|
||||||
*/
|
*/
|
||||||
simdjson_inline operator double() noexcept(false);
|
explicit simdjson_inline operator double() noexcept(false);
|
||||||
/**
|
/**
|
||||||
* Cast this JSON value to a string.
|
* Cast this JSON value to a string.
|
||||||
*
|
*
|
||||||
@@ -323,7 +343,7 @@ public:
|
|||||||
* time it parses a document or when it is destroyed.
|
* time it parses a document or when it is destroyed.
|
||||||
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
|
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
|
||||||
*/
|
*/
|
||||||
simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound;
|
explicit simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound;
|
||||||
/**
|
/**
|
||||||
* Cast this JSON value to a raw_json_string.
|
* Cast this JSON value to a raw_json_string.
|
||||||
*
|
*
|
||||||
@@ -332,14 +352,14 @@ public:
|
|||||||
* @returns A pointer to the raw JSON for the given string.
|
* @returns A pointer to the raw JSON for the given string.
|
||||||
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
|
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
|
||||||
*/
|
*/
|
||||||
simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound;
|
explicit simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound;
|
||||||
/**
|
/**
|
||||||
* Cast this JSON value to a bool.
|
* Cast this JSON value to a bool.
|
||||||
*
|
*
|
||||||
* @returns A bool value.
|
* @returns A bool value.
|
||||||
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
|
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
|
||||||
*/
|
*/
|
||||||
simdjson_inline operator bool() noexcept(false);
|
explicit simdjson_inline operator bool() noexcept(false);
|
||||||
/**
|
/**
|
||||||
* Cast this JSON value to a value when the document is an object or an array.
|
* Cast this JSON value to a value when the document is an object or an array.
|
||||||
*
|
*
|
||||||
@@ -829,6 +849,8 @@ public:
|
|||||||
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
|
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
|
||||||
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
|
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
|
||||||
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
||||||
|
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
|
||||||
|
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
|
||||||
simdjson_inline simdjson_result<double> get_double() noexcept;
|
simdjson_inline simdjson_result<double> get_double() noexcept;
|
||||||
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
||||||
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
||||||
@@ -923,12 +945,12 @@ public:
|
|||||||
explicit simdjson_inline operator T() noexcept(false);
|
explicit simdjson_inline operator T() noexcept(false);
|
||||||
simdjson_inline operator array() & noexcept(false);
|
simdjson_inline operator array() & noexcept(false);
|
||||||
simdjson_inline operator object() & noexcept(false);
|
simdjson_inline operator object() & noexcept(false);
|
||||||
simdjson_inline operator uint64_t() noexcept(false);
|
explicit simdjson_inline operator uint64_t() noexcept(false);
|
||||||
simdjson_inline operator int64_t() noexcept(false);
|
explicit simdjson_inline operator int64_t() noexcept(false);
|
||||||
simdjson_inline operator double() noexcept(false);
|
explicit simdjson_inline operator double() noexcept(false);
|
||||||
simdjson_inline operator std::string_view() noexcept(false);
|
explicit simdjson_inline operator std::string_view() noexcept(false);
|
||||||
simdjson_inline operator raw_json_string() noexcept(false);
|
explicit simdjson_inline operator raw_json_string() noexcept(false);
|
||||||
simdjson_inline operator bool() noexcept(false);
|
explicit simdjson_inline operator bool() noexcept(false);
|
||||||
simdjson_inline operator value() noexcept(false);
|
simdjson_inline operator value() noexcept(false);
|
||||||
#endif
|
#endif
|
||||||
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
|
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
|
||||||
@@ -982,6 +1004,8 @@ public:
|
|||||||
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
|
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
|
||||||
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
|
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
|
||||||
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
||||||
|
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
|
||||||
|
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
|
||||||
simdjson_inline simdjson_result<double> get_double() noexcept;
|
simdjson_inline simdjson_result<double> get_double() noexcept;
|
||||||
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
||||||
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
||||||
@@ -1006,12 +1030,12 @@ public:
|
|||||||
explicit simdjson_inline operator T() noexcept(false);
|
explicit simdjson_inline operator T() noexcept(false);
|
||||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
|
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
|
||||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() & noexcept(false);
|
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() & noexcept(false);
|
||||||
simdjson_inline operator uint64_t() noexcept(false);
|
explicit simdjson_inline operator uint64_t() noexcept(false);
|
||||||
simdjson_inline operator int64_t() noexcept(false);
|
explicit simdjson_inline operator int64_t() noexcept(false);
|
||||||
simdjson_inline operator double() noexcept(false);
|
explicit simdjson_inline operator double() noexcept(false);
|
||||||
simdjson_inline operator std::string_view() noexcept(false);
|
explicit simdjson_inline operator std::string_view() noexcept(false);
|
||||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false);
|
explicit simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false);
|
||||||
simdjson_inline operator bool() noexcept(false);
|
explicit simdjson_inline operator bool() noexcept(false);
|
||||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::value() noexcept(false);
|
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::value() noexcept(false);
|
||||||
#endif
|
#endif
|
||||||
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
|
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
|
||||||
@@ -1069,6 +1093,8 @@ public:
|
|||||||
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
|
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
|
||||||
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
|
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
|
||||||
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
||||||
|
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
|
||||||
|
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
|
||||||
simdjson_inline simdjson_result<double> get_double() noexcept;
|
simdjson_inline simdjson_result<double> get_double() noexcept;
|
||||||
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
||||||
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
||||||
@@ -1090,12 +1116,12 @@ public:
|
|||||||
explicit simdjson_inline operator T() noexcept(false);
|
explicit simdjson_inline operator T() noexcept(false);
|
||||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
|
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
|
||||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() & noexcept(false);
|
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() & noexcept(false);
|
||||||
simdjson_inline operator uint64_t() noexcept(false);
|
explicit simdjson_inline operator uint64_t() noexcept(false);
|
||||||
simdjson_inline operator int64_t() noexcept(false);
|
explicit simdjson_inline operator int64_t() noexcept(false);
|
||||||
simdjson_inline operator double() noexcept(false);
|
explicit simdjson_inline operator double() noexcept(false);
|
||||||
simdjson_inline operator std::string_view() noexcept(false);
|
explicit simdjson_inline operator std::string_view() noexcept(false);
|
||||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false);
|
explicit simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false);
|
||||||
simdjson_inline operator bool() noexcept(false);
|
explicit simdjson_inline operator bool() noexcept(false);
|
||||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::value() noexcept(false);
|
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::value() noexcept(false);
|
||||||
#endif
|
#endif
|
||||||
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
|
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
|
||||||
|
|||||||
@@ -95,13 +95,15 @@ simdjson_inline document_stream::document_stream(
|
|||||||
const uint8_t *_buf,
|
const uint8_t *_buf,
|
||||||
size_t _len,
|
size_t _len,
|
||||||
size_t _batch_size,
|
size_t _batch_size,
|
||||||
bool _allow_comma_separated
|
bool _allow_comma_separated,
|
||||||
|
stream_format _format
|
||||||
) noexcept
|
) noexcept
|
||||||
: parser{&_parser},
|
: parser{&_parser},
|
||||||
buf{_buf},
|
buf{_buf},
|
||||||
len{_len},
|
len{_len},
|
||||||
batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size},
|
batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size},
|
||||||
allow_comma_separated{_allow_comma_separated},
|
allow_comma_separated{_allow_comma_separated},
|
||||||
|
format{_format},
|
||||||
error{SUCCESS}
|
error{SUCCESS}
|
||||||
#ifdef SIMDJSON_THREADS_ENABLED
|
#ifdef SIMDJSON_THREADS_ENABLED
|
||||||
, use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change
|
, use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change
|
||||||
@@ -120,6 +122,7 @@ simdjson_inline document_stream::document_stream() noexcept
|
|||||||
len{0},
|
len{0},
|
||||||
batch_size{0},
|
batch_size{0},
|
||||||
allow_comma_separated{false},
|
allow_comma_separated{false},
|
||||||
|
format{stream_format::whitespace_delimited},
|
||||||
error{UNINITIALIZED}
|
error{UNINITIALIZED}
|
||||||
#ifdef SIMDJSON_THREADS_ENABLED
|
#ifdef SIMDJSON_THREADS_ENABLED
|
||||||
, use_thread(false)
|
, use_thread(false)
|
||||||
@@ -219,7 +222,10 @@ inline void document_stream::start() noexcept {
|
|||||||
error = run_stage1(*parser, batch_start);
|
error = run_stage1(*parser, batch_start);
|
||||||
}
|
}
|
||||||
if (error) { return; }
|
if (error) { return; }
|
||||||
doc_index = batch_start;
|
// For json_sequence mode, structural_indexes[0] points to the actual JSON value
|
||||||
|
// after the RS delimiter and any following whitespace. For regular mode, it is
|
||||||
|
// the offset from batch_start to the first document in the batch.
|
||||||
|
doc_index = batch_start + parser->implementation->structural_indexes[0];
|
||||||
doc = document(json_iterator(&buf[batch_start], parser));
|
doc = document(json_iterator(&buf[batch_start], parser));
|
||||||
doc.iter._streaming = true;
|
doc.iter._streaming = true;
|
||||||
|
|
||||||
@@ -300,7 +306,7 @@ inline void document_stream::next() noexcept {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
if (error) { continue; } // If the error was EMPTY, we may want to load another batch.
|
if (error) { continue; } // If the error was EMPTY, we may want to load another batch.
|
||||||
doc_index = batch_start;
|
doc_index = batch_start + parser->implementation->structural_indexes[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -329,10 +335,35 @@ inline error_code document_stream::run_stage1(ondemand::parser &p, size_t _batch
|
|||||||
// This code only updates the structural index in the parser, it does not update any json_iterator
|
// This code only updates the structural index in the parser, it does not update any json_iterator
|
||||||
// instance.
|
// instance.
|
||||||
size_t remaining = len - _batch_start;
|
size_t remaining = len - _batch_start;
|
||||||
|
stage1_mode mode;
|
||||||
if (remaining <= batch_size) {
|
if (remaining <= batch_size) {
|
||||||
return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final);
|
// Final batch
|
||||||
|
switch (format) {
|
||||||
|
case stream_format::json_sequence:
|
||||||
|
mode = stage1_mode::json_sequence_final;
|
||||||
|
break;
|
||||||
|
case stream_format::comma_delimited:
|
||||||
|
mode = stage1_mode::comma_delimited_final;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mode = stage1_mode::streaming_final;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return p.implementation->stage1(&buf[_batch_start], remaining, mode);
|
||||||
} else {
|
} else {
|
||||||
return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial);
|
// Partial batch
|
||||||
|
switch (format) {
|
||||||
|
case stream_format::json_sequence:
|
||||||
|
mode = stage1_mode::json_sequence_partial;
|
||||||
|
break;
|
||||||
|
case stream_format::comma_delimited:
|
||||||
|
mode = stage1_mode::comma_delimited_partial;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mode = stage1_mode::streaming_partial;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return p.implementation->stage1(&buf[_batch_start], batch_size, mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,14 +384,21 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
|
|||||||
depth--;
|
depth--;
|
||||||
break;
|
break;
|
||||||
default: // Scalar value document
|
default: // Scalar value document
|
||||||
// TODO: We could remove trailing whitespaces
|
|
||||||
// This returns a string spanning from start of value to the beginning of the next document (excluded)
|
// This returns a string spanning from start of value to the beginning of the next document (excluded)
|
||||||
{
|
{
|
||||||
auto next_index = stream->parser->implementation->structural_indexes[++cur_struct_index];
|
auto next_index = stream->parser->implementation->structural_indexes[++cur_struct_index];
|
||||||
// normally the length would be next_index - current_index() - 1, except for the last document
|
// normally the length would be next_index - current_index() - 1, except for the last document
|
||||||
size_t svlen = next_index - current_index();
|
size_t svlen = next_index - current_index();
|
||||||
const char *start = reinterpret_cast<const char*>(stream->buf) + current_index();
|
const char *start = reinterpret_cast<const char*>(stream->buf) + current_index();
|
||||||
while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0')) {
|
// Trim trailing whitespace, NUL, and RS (0x1E). In RFC 7464
|
||||||
|
// json_sequence mode the scanner classifies RS as a scalar
|
||||||
|
// character, so an RS-prefixed scalar document (number / true /
|
||||||
|
// false / null / string) has no closing structural index and the
|
||||||
|
// slice runs all the way up to the next document's RS. RS cannot
|
||||||
|
// legally appear in a JSON value at the source level (control
|
||||||
|
// characters in strings must be escaped as \u001E), so stripping
|
||||||
|
// it is safe in every stream_format.
|
||||||
|
while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0' || static_cast<uint8_t>(start[svlen-1]) == 0x1E)) {
|
||||||
svlen--;
|
svlen--;
|
||||||
}
|
}
|
||||||
return std::string_view(start, svlen);
|
return std::string_view(start, svlen);
|
||||||
|
|||||||
@@ -229,13 +229,16 @@ private:
|
|||||||
* @param buf is the raw byte buffer we need to process
|
* @param buf is the raw byte buffer we need to process
|
||||||
* @param len is the length of the raw byte buffer in bytes
|
* @param len is the length of the raw byte buffer in bytes
|
||||||
* @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document)
|
* @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document)
|
||||||
|
* @param allow_comma_separated whether to allow comma-separated documents
|
||||||
|
* @param format the stream format
|
||||||
*/
|
*/
|
||||||
simdjson_inline document_stream(
|
simdjson_inline document_stream(
|
||||||
ondemand::parser &parser,
|
ondemand::parser &parser,
|
||||||
const uint8_t *buf,
|
const uint8_t *buf,
|
||||||
size_t len,
|
size_t len,
|
||||||
size_t batch_size,
|
size_t batch_size,
|
||||||
bool allow_comma_separated
|
bool allow_comma_separated,
|
||||||
|
stream_format format = stream_format::whitespace_delimited
|
||||||
) noexcept;
|
) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -284,6 +287,7 @@ private:
|
|||||||
size_t len;
|
size_t len;
|
||||||
size_t batch_size;
|
size_t batch_size;
|
||||||
bool allow_comma_separated;
|
bool allow_comma_separated;
|
||||||
|
stream_format format;
|
||||||
/**
|
/**
|
||||||
* We are going to use just one document instance. The document owns
|
* We are going to use just one document instance. The document owns
|
||||||
* the json_iterator. It implies that we only ever pass a reference
|
* the json_iterator. It implies that we only ever pass a reference
|
||||||
|
|||||||
@@ -364,7 +364,7 @@ simdjson_inline simdjson_result<std::string_view> json_iterator::unescape(raw_js
|
|||||||
#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
|
#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
|
||||||
return result;
|
return result;
|
||||||
#else
|
#else
|
||||||
return parser->unescape_maybe(in, _string_buf_loc, allow_replacement);
|
return parser->unescape(in, _string_buf_loc, allow_replacement);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -133,6 +133,34 @@ simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iter
|
|||||||
return json_iterator(reinterpret_cast<const uint8_t *>(json.data()), this);
|
return json_iterator(reinterpret_cast<const uint8_t *>(json.data()), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept {
|
||||||
|
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
|
||||||
|
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
|
||||||
|
buf += 3;
|
||||||
|
len -= 3;
|
||||||
|
}
|
||||||
|
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size) noexcept {
|
||||||
|
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size) noexcept {
|
||||||
|
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
|
||||||
|
return iterate_many(s.data(), s.length(), batch_size);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size) noexcept {
|
||||||
|
return iterate_many(padded_string_view(s), batch_size);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size) noexcept {
|
||||||
|
return iterate_many(padded_string_view(s), batch_size);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size) noexcept {
|
||||||
|
return iterate_many(pad(s), batch_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
|
||||||
|
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||||
|
SIMDJSON_DISABLE_DEPRECATED_WARNING
|
||||||
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
|
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
|
||||||
// Warning: no check is done on the buffer padding. We trust the user.
|
// Warning: no check is done on the buffer padding. We trust the user.
|
||||||
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
|
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
|
||||||
@@ -140,8 +168,11 @@ inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf,
|
|||||||
buf += 3;
|
buf += 3;
|
||||||
len -= 3;
|
len -= 3;
|
||||||
}
|
}
|
||||||
if(allow_comma_separated && batch_size < len) { batch_size = len; }
|
// Map allow_comma_separated to stream_format::comma_delimited
|
||||||
return document_stream(*this, buf, len, batch_size, allow_comma_separated);
|
if (allow_comma_separated) {
|
||||||
|
return document_stream(*this, buf, len, batch_size, false, stream_format::comma_delimited);
|
||||||
|
}
|
||||||
|
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
|
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
|
||||||
@@ -161,6 +192,48 @@ inline simdjson_result<document_stream> parser::iterate_many(const std::string &
|
|||||||
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
|
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
|
||||||
return iterate_many(pad(s), batch_size, allow_comma_separated);
|
return iterate_many(pad(s), batch_size, allow_comma_separated);
|
||||||
}
|
}
|
||||||
|
SIMDJSON_POP_DISABLE_WARNINGS
|
||||||
|
#endif // SIMDJSON_DISABLE_DEPRECATED_API
|
||||||
|
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept {
|
||||||
|
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
|
||||||
|
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
|
||||||
|
buf += 3;
|
||||||
|
len -= 3;
|
||||||
|
}
|
||||||
|
if (format == stream_format::comma_delimited_array) {
|
||||||
|
// Strip leading JSON whitespace.
|
||||||
|
while (len > 0 && (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n' || buf[0] == '\r')) {
|
||||||
|
buf++; len--;
|
||||||
|
}
|
||||||
|
// Expect the opening '['.
|
||||||
|
if (len == 0 || buf[0] != '[') { return TAPE_ERROR; }
|
||||||
|
buf++; len--;
|
||||||
|
// Strip trailing JSON whitespace.
|
||||||
|
while (len > 0 && (buf[len-1] == ' ' || buf[len-1] == '\t' || buf[len-1] == '\n' || buf[len-1] == '\r')) {
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
// Expect the closing ']'.
|
||||||
|
if (len == 0 || buf[len-1] != ']') { return TAPE_ERROR; }
|
||||||
|
len--;
|
||||||
|
// Fall through to comma_delimited over the array contents.
|
||||||
|
format = stream_format::comma_delimited;
|
||||||
|
}
|
||||||
|
return document_stream(*this, buf, len, batch_size, false, format);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept {
|
||||||
|
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, format);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept {
|
||||||
|
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
|
||||||
|
return iterate_many(s.data(), s.length(), batch_size, format);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
|
||||||
|
return iterate_many(padded_string_view(s), batch_size, format);
|
||||||
|
}
|
||||||
|
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept {
|
||||||
|
return iterate_many(padded_string_view(s), batch_size, format);
|
||||||
|
}
|
||||||
simdjson_pure simdjson_inline size_t parser::capacity() const noexcept {
|
simdjson_pure simdjson_inline size_t parser::capacity() const noexcept {
|
||||||
return _capacity;
|
return _capacity;
|
||||||
}
|
}
|
||||||
@@ -172,27 +245,13 @@ simdjson_pure simdjson_inline size_t parser::max_depth() const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
simdjson_inline void parser::set_max_capacity(size_t max_capacity) noexcept {
|
simdjson_inline void parser::set_max_capacity(size_t max_capacity) noexcept {
|
||||||
if(max_capacity < dom::MINIMAL_DOCUMENT_CAPACITY) {
|
if(max_capacity > dom::MINIMAL_DOCUMENT_CAPACITY) {
|
||||||
_max_capacity = max_capacity;
|
_max_capacity = max_capacity;
|
||||||
} else {
|
} else {
|
||||||
_max_capacity = dom::MINIMAL_DOCUMENT_CAPACITY;
|
_max_capacity = dom::MINIMAL_DOCUMENT_CAPACITY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape_maybe(raw_json_string in, uint8_t *&dst, bool allow_replacement) const noexcept {
|
|
||||||
std::pair<const uint8_t *, bool> result = implementation->parse_string_if_needed(in.buf, dst, allow_replacement);
|
|
||||||
const uint8_t *end = result.first;
|
|
||||||
bool copied = result.second;
|
|
||||||
if (!end) { return STRING_ERROR; }
|
|
||||||
if(copied) {
|
|
||||||
std::string_view strresult(reinterpret_cast<const char *>(dst), end-dst);
|
|
||||||
dst = const_cast<uint8_t *>(end);
|
|
||||||
return strresult;
|
|
||||||
}
|
|
||||||
// fast path, no copy was made!!!
|
|
||||||
return std::string_view(reinterpret_cast<const char *>(in.buf), end-in.buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement) const noexcept {
|
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement) const noexcept {
|
||||||
uint8_t *end = implementation->parse_string(in.buf, dst, allow_replacement);
|
uint8_t *end = implementation->parse_string(in.buf, dst, allow_replacement);
|
||||||
if (!end) { return STRING_ERROR; }
|
if (!end) { return STRING_ERROR; }
|
||||||
|
|||||||
@@ -244,32 +244,66 @@ public:
|
|||||||
* spot is cache-related: small enough to fit in cache, yet big enough to
|
* spot is cache-related: small enough to fit in cache, yet big enough to
|
||||||
* parse as many documents as possible in one tight loop.
|
* parse as many documents as possible in one tight loop.
|
||||||
* Defaults to 10MB, which has been a reasonable sweet spot in our tests.
|
* Defaults to 10MB, which has been a reasonable sweet spot in our tests.
|
||||||
* @param allow_comma_separated (defaults on false) This allows a mode where the documents are
|
* @param allow_comma_separated @deprecated Use stream_format::comma_delimited instead.
|
||||||
* separated by commas instead of whitespace. It comes with a performance
|
* When true, maps internally to stream_format::comma_delimited.
|
||||||
* penalty because the entire document is indexed at once (and the document must be
|
* Defaults to false.
|
||||||
* less than 4 GB), and there is no multithreading. In this mode, the batch_size parameter
|
|
||||||
* is effectively ignored, as it is set to at least the document size.
|
|
||||||
* @return The stream, or an error. An empty input will yield 0 documents rather than an EMPTY error. Errors:
|
* @return The stream, or an error. An empty input will yield 0 documents rather than an EMPTY error. Errors:
|
||||||
* - MEMALLOC if the parser does not have enough capacity and memory allocation fails
|
* - MEMALLOC if the parser does not have enough capacity and memory allocation fails
|
||||||
* - CAPACITY if the parser does not have enough capacity and batch_size > max_capacity.
|
* - CAPACITY if the parser does not have enough capacity and batch_size > max_capacity.
|
||||||
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
|
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
|
||||||
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
|
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
|
||||||
*/
|
*/
|
||||||
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept;
|
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
|
||||||
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
||||||
inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept;
|
inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
|
||||||
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
||||||
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept;
|
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
|
||||||
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
||||||
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept;
|
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
|
||||||
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size)
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size)
|
||||||
the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */
|
the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */
|
||||||
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept;
|
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
|
||||||
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
||||||
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept;
|
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
|
||||||
/** @private We do not want to allow implicit conversion from C string to std::string. */
|
/** @private We do not want to allow implicit conversion from C string to std::string. */
|
||||||
simdjson_result<document_stream> iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete;
|
simdjson_result<document_stream> iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete;
|
||||||
|
|
||||||
|
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
|
||||||
|
/**
|
||||||
|
* @deprecated Use iterate_many with stream_format::comma_delimited instead.
|
||||||
|
*/
|
||||||
|
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
|
||||||
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
|
||||||
|
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size, bool allow_comma_separated) noexcept;
|
||||||
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
|
||||||
|
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
|
||||||
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
|
||||||
|
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
|
||||||
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
|
||||||
|
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
|
||||||
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
|
||||||
|
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept;
|
||||||
|
#endif // SIMDJSON_DISABLE_DEPRECATED_API
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a stream of JSON documents with explicit format specification.
|
||||||
|
*
|
||||||
|
* @param buf The concatenated JSON documents.
|
||||||
|
* @param len The length of the buffer.
|
||||||
|
* @param batch_size The batch size to use.
|
||||||
|
* @param format The stream format (whitespace_delimited, json_sequence, or comma_delimited).
|
||||||
|
* @return A stream of documents, or an error.
|
||||||
|
*/
|
||||||
|
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept;
|
||||||
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||||
|
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept;
|
||||||
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||||
|
inline simdjson_result<document_stream> iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept;
|
||||||
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||||
|
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept;
|
||||||
|
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||||
|
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept;
|
||||||
|
|
||||||
/** The capacity of this parser (the largest document it can process). */
|
/** The capacity of this parser (the largest document it can process). */
|
||||||
simdjson_pure simdjson_inline size_t capacity() const noexcept;
|
simdjson_pure simdjson_inline size_t capacity() const noexcept;
|
||||||
/** The maximum capacity of this parser (the largest document it is allowed to process). */
|
/** The maximum capacity of this parser (the largest document it is allowed to process). */
|
||||||
@@ -334,32 +368,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement = false) const noexcept;
|
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement = false) const noexcept;
|
||||||
|
|
||||||
/**
|
|
||||||
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer if
|
|
||||||
* needed. If no escaping is done, the string is returned as is and dst is not not changed.
|
|
||||||
* The result must be valid UTF-8.
|
|
||||||
* The provided pointer is advanced to the end of the string by reference if a copy is needed,
|
|
||||||
* and a string_view instance
|
|
||||||
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
|
|
||||||
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
|
|
||||||
*
|
|
||||||
* This unescape_maybe function is a low-level function. If you want a more user-friendly approach, you should
|
|
||||||
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
|
|
||||||
* instead of get_raw_json_string()).
|
|
||||||
*
|
|
||||||
* ## IMPORTANT: string_view lifetime
|
|
||||||
*
|
|
||||||
* The string_view is only valid as long as the bytes in dst.
|
|
||||||
*
|
|
||||||
* @param raw_json_string input
|
|
||||||
* @param dst A pointer to a buffer at least large enough to write this string as well as
|
|
||||||
* an additional SIMDJSON_PADDING bytes.
|
|
||||||
* @param allow_replacement Whether we allow a replacement if the input string contains unmatched surrogate pairs.
|
|
||||||
* @return A string_view pointing at the unescaped string in dst
|
|
||||||
* @error STRING_ERROR if escapes are incorrect.
|
|
||||||
*/
|
|
||||||
simdjson_inline simdjson_result<std::string_view> unescape_maybe(raw_json_string in, uint8_t *&dst, bool allow_replacement = false) const noexcept;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
|
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
|
||||||
* The result may not be valid UTF-8. See https://simonsapin.github.io/wtf-8/
|
* The result may not be valid UTF-8. See https://simonsapin.github.io/wtf-8/
|
||||||
|
|||||||
@@ -305,7 +305,7 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
|
|||||||
#if SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
std::string_view str;
|
std::string_view str;
|
||||||
SIMDJSON_TRY(val.get_string().get(str));
|
SIMDJSON_TRY(val.get_string().get(str));
|
||||||
constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
|
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
|
||||||
template for (constexpr auto enum_val : enumerators) {
|
template for (constexpr auto enum_val : enumerators) {
|
||||||
if (str == std::meta::identifier_of(enum_val)) {
|
if (str == std::meta::identifier_of(enum_val)) {
|
||||||
out = [:enum_val:];
|
out = [:enum_val:];
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
#include "simdjson/generic/ondemand/value.h"
|
#include "simdjson/generic/ondemand/value.h"
|
||||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
namespace simdjson {
|
namespace simdjson {
|
||||||
namespace SIMDJSON_IMPLEMENTATION {
|
namespace SIMDJSON_IMPLEMENTATION {
|
||||||
namespace ondemand {
|
namespace ondemand {
|
||||||
@@ -72,6 +74,18 @@ simdjson_inline simdjson_result<int64_t> value::get_int64() noexcept {
|
|||||||
simdjson_inline simdjson_result<int64_t> value::get_int64_in_string() noexcept {
|
simdjson_inline simdjson_result<int64_t> value::get_int64_in_string() noexcept {
|
||||||
return iter.get_int64_in_string();
|
return iter.get_int64_in_string();
|
||||||
}
|
}
|
||||||
|
simdjson_inline simdjson_result<uint32_t> value::get_uint32() noexcept {
|
||||||
|
uint64_t result;
|
||||||
|
SIMDJSON_TRY(get_uint64().get(result));
|
||||||
|
if (result > (std::numeric_limits<uint32_t>::max)()) { return NUMBER_OUT_OF_RANGE; }
|
||||||
|
return static_cast<uint32_t>(result);
|
||||||
|
}
|
||||||
|
simdjson_inline simdjson_result<int32_t> value::get_int32() noexcept {
|
||||||
|
int64_t result;
|
||||||
|
SIMDJSON_TRY(get_int64().get(result));
|
||||||
|
if (result > (std::numeric_limits<int32_t>::max)() || result < (std::numeric_limits<int32_t>::min)()) { return NUMBER_OUT_OF_RANGE; }
|
||||||
|
return static_cast<int32_t>(result);
|
||||||
|
}
|
||||||
simdjson_inline simdjson_result<bool> value::get_bool() noexcept {
|
simdjson_inline simdjson_result<bool> value::get_bool() noexcept {
|
||||||
return iter.get_bool();
|
return iter.get_bool();
|
||||||
}
|
}
|
||||||
@@ -87,6 +101,8 @@ template<> simdjson_inline simdjson_result<number> value::get() noexcept { retur
|
|||||||
template<> simdjson_inline simdjson_result<double> value::get() noexcept { return get_double(); }
|
template<> simdjson_inline simdjson_result<double> value::get() noexcept { return get_double(); }
|
||||||
template<> simdjson_inline simdjson_result<uint64_t> value::get() noexcept { return get_uint64(); }
|
template<> simdjson_inline simdjson_result<uint64_t> value::get() noexcept { return get_uint64(); }
|
||||||
template<> simdjson_inline simdjson_result<int64_t> value::get() noexcept { return get_int64(); }
|
template<> simdjson_inline simdjson_result<int64_t> value::get() noexcept { return get_int64(); }
|
||||||
|
template<> simdjson_inline simdjson_result<uint32_t> value::get() noexcept { return get_uint32(); }
|
||||||
|
template<> simdjson_inline simdjson_result<int32_t> value::get() noexcept { return get_int32(); }
|
||||||
template<> simdjson_inline simdjson_result<bool> value::get() noexcept { return get_bool(); }
|
template<> simdjson_inline simdjson_result<bool> value::get() noexcept { return get_bool(); }
|
||||||
|
|
||||||
|
|
||||||
@@ -98,6 +114,8 @@ template<> simdjson_warn_unused simdjson_inline error_code value::get(number& ou
|
|||||||
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(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(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(int64_t& out) noexcept { return get_int64().get(out); }
|
||||||
|
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint32_t& out) noexcept { return get_uint32().get(out); }
|
||||||
|
template<> simdjson_warn_unused simdjson_inline error_code value::get(int32_t& out) noexcept { return get_int32().get(out); }
|
||||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); }
|
template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); }
|
||||||
|
|
||||||
#if SIMDJSON_EXCEPTIONS
|
#if SIMDJSON_EXCEPTIONS
|
||||||
@@ -401,6 +419,14 @@ simdjson_inline simdjson_result<int64_t> simdjson_result<SIMDJSON_IMPLEMENTATION
|
|||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.get_int64_in_string();
|
return first.get_int64_in_string();
|
||||||
}
|
}
|
||||||
|
simdjson_inline simdjson_result<uint32_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_uint32() noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return first.get_uint32();
|
||||||
|
}
|
||||||
|
simdjson_inline simdjson_result<int32_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_int32() noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return first.get_int32();
|
||||||
|
}
|
||||||
simdjson_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_double() noexcept {
|
simdjson_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_double() noexcept {
|
||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.get_double();
|
return first.get_double();
|
||||||
|
|||||||
@@ -161,6 +161,28 @@ public:
|
|||||||
*/
|
*/
|
||||||
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cast this JSON value to a 32-bit unsigned integer.
|
||||||
|
*
|
||||||
|
* Calls get_uint64() and checks that the result fits in a uint32_t.
|
||||||
|
*
|
||||||
|
* @returns A 32-bit unsigned integer.
|
||||||
|
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
|
||||||
|
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
|
||||||
|
*/
|
||||||
|
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cast this JSON value to a 32-bit signed integer.
|
||||||
|
*
|
||||||
|
* Calls get_int64() and checks that the result fits in an int32_t.
|
||||||
|
*
|
||||||
|
* @returns A 32-bit signed integer.
|
||||||
|
* @returns INCORRECT_TYPE If the JSON value is not an integer.
|
||||||
|
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
|
||||||
|
*/
|
||||||
|
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cast this JSON value to a double.
|
* Cast this JSON value to a double.
|
||||||
*
|
*
|
||||||
@@ -746,6 +768,8 @@ public:
|
|||||||
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
|
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
|
||||||
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
|
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
|
||||||
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
|
||||||
|
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
|
||||||
|
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
|
||||||
simdjson_inline simdjson_result<double> get_double() noexcept;
|
simdjson_inline simdjson_result<double> get_double() noexcept;
|
||||||
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
||||||
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
||||||
|
|||||||
@@ -515,9 +515,13 @@ simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_ite
|
|||||||
template <typename string_type>
|
template <typename string_type>
|
||||||
simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(string_type& receiver, bool allow_replacement) noexcept {
|
simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(string_type& receiver, bool allow_replacement) noexcept {
|
||||||
std::string_view content;
|
std::string_view content;
|
||||||
|
// Save the string buffer location so that we can restore it after get_string
|
||||||
|
auto saved_string_buf_loc = _json_iter->string_buf_loc();
|
||||||
auto err = get_string(allow_replacement).get(content);
|
auto err = get_string(allow_replacement).get(content);
|
||||||
if (err) { return err; }
|
if (err) { return err; }
|
||||||
receiver = content;
|
receiver = content;
|
||||||
|
// Restore the string buffer location, effectively discarding any temporary string storage
|
||||||
|
_json_iter->string_buf_loc() = saved_string_buf_loc;
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_wobbly_string() noexcept {
|
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_wobbly_string() noexcept {
|
||||||
@@ -657,9 +661,13 @@ simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_ite
|
|||||||
template <typename string_type>
|
template <typename string_type>
|
||||||
simdjson_warn_unused simdjson_inline error_code value_iterator::get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept {
|
simdjson_warn_unused simdjson_inline error_code value_iterator::get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept {
|
||||||
std::string_view content;
|
std::string_view content;
|
||||||
|
// Save the string buffer location so that we can restore it after get_string
|
||||||
|
auto saved_string_buf_loc = _json_iter->string_buf_loc();
|
||||||
auto err = get_root_string(check_trailing, allow_replacement).get(content);
|
auto err = get_root_string(check_trailing, allow_replacement).get(content);
|
||||||
if (err) { return err; }
|
if (err) { return err; }
|
||||||
receiver = content;
|
receiver = content;
|
||||||
|
// Restore the string buffer location, effectively discarding any temporary string storage
|
||||||
|
_json_iter->string_buf_loc() = saved_string_buf_loc;
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_wobbly_string(bool check_trailing) noexcept {
|
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_wobbly_string(bool check_trailing) noexcept {
|
||||||
|
|||||||
@@ -300,6 +300,7 @@ namespace simd {
|
|||||||
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
|
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
|
||||||
static_assert(NUM_CHUNKS == 2, "Haswell kernel should use two registers per 64-byte block.");
|
static_assert(NUM_CHUNKS == 2, "Haswell kernel should use two registers per 64-byte block.");
|
||||||
const simd8<T> chunks[NUM_CHUNKS];
|
const simd8<T> chunks[NUM_CHUNKS];
|
||||||
|
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
|
||||||
|
|
||||||
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
|
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
|
||||||
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
|
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ using namespace simd;
|
|||||||
struct backslash_and_quote {
|
struct backslash_and_quote {
|
||||||
public:
|
public:
|
||||||
static constexpr uint32_t BYTES_PROCESSED = 32;
|
static constexpr uint32_t BYTES_PROCESSED = 32;
|
||||||
// We only copy if dst is non-null.
|
|
||||||
simdjson_inline 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_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
|
||||||
@@ -35,9 +34,7 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
|
|||||||
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
|
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
|
||||||
simd8<uint8_t> v(src);
|
simd8<uint8_t> v(src);
|
||||||
// store to dest unconditionally - we can overwrite the bits we don't like later
|
// store to dest unconditionally - we can overwrite the bits we don't like later
|
||||||
if(dst != nullptr) {
|
v.store(dst);
|
||||||
v.store(dst);
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
static_cast<uint32_t>((v == '\\').to_bitmask()), // bs_bits
|
static_cast<uint32_t>((v == '\\').to_bitmask()), // bs_bits
|
||||||
static_cast<uint32_t>((v == '"').to_bitmask()), // quote_bits
|
static_cast<uint32_t>((v == '"').to_bitmask()), // quote_bits
|
||||||
|
|||||||
@@ -322,6 +322,7 @@ namespace simd {
|
|||||||
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
|
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
|
||||||
static_assert(NUM_CHUNKS == 1, "Icelake kernel should use one register per 64-byte block.");
|
static_assert(NUM_CHUNKS == 1, "Icelake kernel should use one register per 64-byte block.");
|
||||||
const simd8<T> chunks[NUM_CHUNKS];
|
const simd8<T> chunks[NUM_CHUNKS];
|
||||||
|
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
|
||||||
|
|
||||||
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
|
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
|
||||||
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
|
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ using namespace simd;
|
|||||||
struct backslash_and_quote {
|
struct backslash_and_quote {
|
||||||
public:
|
public:
|
||||||
static constexpr uint32_t BYTES_PROCESSED = 64;
|
static constexpr uint32_t BYTES_PROCESSED = 64;
|
||||||
// We only copy if dst is non-null.
|
|
||||||
simdjson_inline 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_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
|
||||||
@@ -35,9 +34,7 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
|
|||||||
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
|
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
|
||||||
simd8<uint8_t> v(src);
|
simd8<uint8_t> v(src);
|
||||||
// store to dest unconditionally - we can overwrite the bits we don't like later
|
// store to dest unconditionally - we can overwrite the bits we don't like later
|
||||||
if(dst != nullptr) {
|
v.store(dst);
|
||||||
v.store(dst);
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
static_cast<uint64_t>(v == '\\'), // bs_bits
|
static_cast<uint64_t>(v == '\\'), // bs_bits
|
||||||
static_cast<uint64_t>(v == '"'), // quote_bits
|
static_cast<uint64_t>(v == '"'), // quote_bits
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
#define SIMDJSON_IMPLEMENTATION_ID_westmere 6
|
#define SIMDJSON_IMPLEMENTATION_ID_westmere 6
|
||||||
#define SIMDJSON_IMPLEMENTATION_ID_lsx 7
|
#define SIMDJSON_IMPLEMENTATION_ID_lsx 7
|
||||||
#define SIMDJSON_IMPLEMENTATION_ID_lasx 8
|
#define SIMDJSON_IMPLEMENTATION_ID_lasx 8
|
||||||
|
//#define SIMDJSON_IMPLEMENTATION_ID_rvv 9
|
||||||
|
#define SIMDJSON_IMPLEMENTATION_ID_rvv_vls 10
|
||||||
|
|
||||||
#define SIMDJSON_IMPLEMENTATION_ID_FOR(IMPL) SIMDJSON_CAT(SIMDJSON_IMPLEMENTATION_ID_, IMPL)
|
#define SIMDJSON_IMPLEMENTATION_ID_FOR(IMPL) SIMDJSON_CAT(SIMDJSON_IMPLEMENTATION_ID_, IMPL)
|
||||||
#define SIMDJSON_IMPLEMENTATION_ID SIMDJSON_IMPLEMENTATION_ID_FOR(SIMDJSON_IMPLEMENTATION)
|
#define SIMDJSON_IMPLEMENTATION_ID SIMDJSON_IMPLEMENTATION_ID_FOR(SIMDJSON_IMPLEMENTATION)
|
||||||
@@ -126,9 +128,14 @@
|
|||||||
#endif
|
#endif
|
||||||
#define SIMDJSON_CAN_ALWAYS_RUN_LSX (SIMDJSON_IMPLEMENTATION_LSX)
|
#define SIMDJSON_CAN_ALWAYS_RUN_LSX (SIMDJSON_IMPLEMENTATION_LSX)
|
||||||
|
|
||||||
|
#define SIMDJSON_CAN_ALWAYS_RUN_RVV_VLS SIMDJSON_IS_RVV_VLS
|
||||||
|
#ifndef SIMDJSON_IMPLEMENTATION_RVV_VLS
|
||||||
|
#define SIMDJSON_IMPLEMENTATION_RVV_VLS SIMDJSON_CAN_ALWAYS_RUN_RVV_VLS
|
||||||
|
#endif
|
||||||
|
|
||||||
// Default Fallback to on unless a builtin implementation has already been selected.
|
// Default Fallback to on unless a builtin implementation has already been selected.
|
||||||
#ifndef SIMDJSON_IMPLEMENTATION_FALLBACK
|
#ifndef SIMDJSON_IMPLEMENTATION_FALLBACK
|
||||||
#if SIMDJSON_CAN_ALWAYS_RUN_ARM64 || SIMDJSON_CAN_ALWAYS_RUN_ICELAKE || SIMDJSON_CAN_ALWAYS_RUN_HASWELL || SIMDJSON_CAN_ALWAYS_RUN_WESTMERE || SIMDJSON_CAN_ALWAYS_RUN_PPC64 || SIMDJSON_CAN_ALWAYS_RUN_LSX || SIMDJSON_CAN_ALWAYS_RUN_LASX
|
#if SIMDJSON_CAN_ALWAYS_RUN_ARM64 || SIMDJSON_CAN_ALWAYS_RUN_ICELAKE || SIMDJSON_CAN_ALWAYS_RUN_HASWELL || SIMDJSON_CAN_ALWAYS_RUN_WESTMERE || SIMDJSON_CAN_ALWAYS_RUN_PPC64 || SIMDJSON_CAN_ALWAYS_RUN_LSX || SIMDJSON_CAN_ALWAYS_RUN_LASX || SIMDJSON_CAN_ALWAYS_RUN_RVV_VLS
|
||||||
// if anything at all except fallback can always run, then disable fallback.
|
// if anything at all except fallback can always run, then disable fallback.
|
||||||
#define SIMDJSON_IMPLEMENTATION_FALLBACK 0
|
#define SIMDJSON_IMPLEMENTATION_FALLBACK 0
|
||||||
#else
|
#else
|
||||||
@@ -154,6 +161,8 @@
|
|||||||
#define SIMDJSON_BUILTIN_IMPLEMENTATION lsx
|
#define SIMDJSON_BUILTIN_IMPLEMENTATION lsx
|
||||||
#elif SIMDJSON_CAN_ALWAYS_RUN_LASX
|
#elif SIMDJSON_CAN_ALWAYS_RUN_LASX
|
||||||
#define SIMDJSON_BUILTIN_IMPLEMENTATION lasx
|
#define SIMDJSON_BUILTIN_IMPLEMENTATION lasx
|
||||||
|
#elif SIMDJSON_CAN_ALWAYS_RUN_RVV_VLS
|
||||||
|
#define SIMDJSON_BUILTIN_IMPLEMENTATION rvv_vls
|
||||||
#elif SIMDJSON_CAN_ALWAYS_RUN_FALLBACK
|
#elif SIMDJSON_CAN_ALWAYS_RUN_FALLBACK
|
||||||
#define SIMDJSON_BUILTIN_IMPLEMENTATION fallback
|
#define SIMDJSON_BUILTIN_IMPLEMENTATION fallback
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#include "simdjson/base.h"
|
#include "simdjson/base.h"
|
||||||
#include "simdjson/error.h"
|
#include "simdjson/error.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace simdjson {
|
namespace simdjson {
|
||||||
|
|
||||||
@@ -20,7 +19,12 @@ class document;
|
|||||||
* 3) The stream_final mode allows us to truncate final
|
* 3) The stream_final mode allows us to truncate final
|
||||||
* unterminated strings. It is useful in conjunction with streaming_partial.
|
* unterminated strings. It is useful in conjunction with streaming_partial.
|
||||||
*/
|
*/
|
||||||
enum class stage1_mode { regular, streaming_partial, streaming_final};
|
enum class stage1_mode {
|
||||||
|
regular,
|
||||||
|
streaming_partial, streaming_final,
|
||||||
|
json_sequence_partial, json_sequence_final,
|
||||||
|
comma_delimited_partial, comma_delimited_final
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if mode == streaming_partial or mode == streaming_final
|
* Returns true if mode == streaming_partial or mode == streaming_final
|
||||||
@@ -32,7 +36,6 @@ inline bool is_streaming(stage1_mode mode) {
|
|||||||
// return (mode == stage1_mode::streaming_partial || mode == stage1_mode::streaming_final);
|
// return (mode == stage1_mode::streaming_partial || mode == stage1_mode::streaming_final);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
|
|
||||||
@@ -103,24 +106,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
simdjson_warn_unused virtual error_code stage2_next(dom::document &doc) noexcept = 0;
|
simdjson_warn_unused virtual error_code stage2_next(dom::document &doc) noexcept = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
|
||||||
* must be an unescaped quote terminating the string. It returns the final output
|
|
||||||
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
|
||||||
* then null_ptr is returned. If no escaping was required, then no copy is made.
|
|
||||||
* It is assumed that the output buffer is large
|
|
||||||
* enough to store the unescapedstring + SIMDJSON_PADDING bytes.
|
|
||||||
*
|
|
||||||
* Overridden by each implementation.
|
|
||||||
*
|
|
||||||
* @param str pointer to the beginning of a valid UTF-8 JSON string, must end with an unescaped quote.
|
|
||||||
* @param dst pointer to a destination buffer, it must point a region in memory of sufficient size.
|
|
||||||
* @param allow_replacement whether we allow a replacement character when the UTF-8 contains unmatched surrogate pairs.
|
|
||||||
* @return end of the of the written region (exclusive) or nullptr in case of error coupled with a Boolean telling you if a copy was made
|
|
||||||
*/
|
|
||||||
simdjson_warn_unused virtual std::pair<const uint8_t *,bool> parse_string_if_needed(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept = 0;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
* must be an unescaped quote terminating the string. It returns the final output
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
@@ -231,6 +216,12 @@ protected:
|
|||||||
*/
|
*/
|
||||||
size_t _max_depth{0};
|
size_t _max_depth{0};
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Whether to store big integers as strings instead of returning BIGINT_ERROR */
|
||||||
|
bool _number_as_string{false};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
// Declaring these so that subclasses can use them to implement their constructors.
|
// Declaring these so that subclasses can use them to implement their constructors.
|
||||||
simdjson_inline dom_parser_implementation() noexcept;
|
simdjson_inline dom_parser_implementation() noexcept;
|
||||||
simdjson_inline dom_parser_implementation(dom_parser_implementation &&other) noexcept;
|
simdjson_inline dom_parser_implementation(dom_parser_implementation &&other) noexcept;
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ enum instruction_set {
|
|||||||
AVX512VBMI2 = 0x10000,
|
AVX512VBMI2 = 0x10000,
|
||||||
LSX = 0x20000,
|
LSX = 0x20000,
|
||||||
LASX = 0x40000,
|
LASX = 0x40000,
|
||||||
|
//RVV = 0x80000,
|
||||||
|
RVV_VLS = 0x100000,
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ enum class tape_type {
|
|||||||
DOUBLE = 'd',
|
DOUBLE = 'd',
|
||||||
TRUE_VALUE = 't',
|
TRUE_VALUE = 't',
|
||||||
FALSE_VALUE = 'f',
|
FALSE_VALUE = 'f',
|
||||||
NULL_VALUE = 'n'
|
NULL_VALUE = 'n',
|
||||||
|
BIGINT = 'Z' // Big integer stored as string in string buffer
|
||||||
}; // enum class tape_type
|
}; // enum class tape_type
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|||||||
@@ -303,6 +303,7 @@ namespace simd {
|
|||||||
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
|
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
|
||||||
static_assert(NUM_CHUNKS == 2, "LASX kernel should use two registers per 64-byte block.");
|
static_assert(NUM_CHUNKS == 2, "LASX kernel should use two registers per 64-byte block.");
|
||||||
const simd8<T> chunks[NUM_CHUNKS];
|
const simd8<T> chunks[NUM_CHUNKS];
|
||||||
|
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
|
||||||
|
|
||||||
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
|
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
|
||||||
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
|
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ using namespace simd;
|
|||||||
struct backslash_and_quote {
|
struct backslash_and_quote {
|
||||||
public:
|
public:
|
||||||
static constexpr uint32_t BYTES_PROCESSED = 32;
|
static constexpr uint32_t BYTES_PROCESSED = 32;
|
||||||
// We only copy if dst is non-null.
|
|
||||||
simdjson_inline 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_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
|
||||||
@@ -34,9 +33,7 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
|
|||||||
// SIMDJSON_PADDING of padding
|
// SIMDJSON_PADDING of padding
|
||||||
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
|
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
|
||||||
simd8<uint8_t> v(src);
|
simd8<uint8_t> v(src);
|
||||||
if(dst != nullptr) {
|
v.store(dst);
|
||||||
v.store(dst);
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
static_cast<uint32_t>((v == '\\').to_bitmask()), // bs_bits
|
static_cast<uint32_t>((v == '\\').to_bitmask()), // bs_bits
|
||||||
static_cast<uint32_t>((v == '"').to_bitmask()), // quote_bits
|
static_cast<uint32_t>((v == '"').to_bitmask()), // quote_bits
|
||||||
|
|||||||
@@ -260,6 +260,7 @@ namespace simd {
|
|||||||
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
|
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
|
||||||
static_assert(NUM_CHUNKS == 4, "LSX kernel should use four registers per 64-byte block.");
|
static_assert(NUM_CHUNKS == 4, "LSX kernel should use four registers per 64-byte block.");
|
||||||
const simd8<T> chunks[NUM_CHUNKS];
|
const simd8<T> chunks[NUM_CHUNKS];
|
||||||
|
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
|
||||||
|
|
||||||
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
|
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
|
||||||
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
|
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ using namespace simd;
|
|||||||
struct backslash_and_quote {
|
struct backslash_and_quote {
|
||||||
public:
|
public:
|
||||||
static constexpr uint32_t BYTES_PROCESSED = 32;
|
static constexpr uint32_t BYTES_PROCESSED = 32;
|
||||||
// We only copy if dst is non-null.
|
|
||||||
simdjson_inline 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_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
|
||||||
@@ -35,10 +34,8 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
|
|||||||
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
|
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
|
||||||
simd8<uint8_t> v0(src);
|
simd8<uint8_t> v0(src);
|
||||||
simd8<uint8_t> v1(src + sizeof(v0));
|
simd8<uint8_t> v1(src + sizeof(v0));
|
||||||
if(dst != nullptr) {
|
v0.store(dst);
|
||||||
v0.store(dst);
|
v1.store(dst + sizeof(v0));
|
||||||
v1.store(dst + sizeof(v0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getting a 64-bit bitmask is much cheaper than multiple 16-bit bitmasks on LSX; therefore, we
|
// Getting a 64-bit bitmask is much cheaper than multiple 16-bit bitmasks on LSX; therefore, we
|
||||||
// smash them together into a 64-byte mask and get the bitmask from there.
|
// smash them together into a 64-byte mask and get the bitmask from there.
|
||||||
|
|||||||
@@ -10,6 +10,14 @@
|
|||||||
#include <climits>
|
#include <climits>
|
||||||
#include <cwchar>
|
#include <cwchar>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace simdjson {
|
namespace simdjson {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
@@ -160,10 +168,13 @@ inline padded_string::operator padded_string_view() const noexcept simdjson_life
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline simdjson_result<padded_string> padded_string::load(std::string_view filename) noexcept {
|
inline simdjson_result<padded_string> padded_string::load(std::string_view filename) noexcept {
|
||||||
|
// std::string_view is not guaranteed to be null-terminated, but std::fopen requires
|
||||||
|
// a null-terminated C string. Construct a temporary std::string to ensure null-termination.
|
||||||
|
const std::string null_terminated_filename(filename);
|
||||||
// Open the file
|
// Open the file
|
||||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||||
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
|
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
|
||||||
std::FILE *fp = std::fopen(filename.data(), "rb");
|
std::FILE *fp = std::fopen(null_terminated_filename.c_str(), "rb");
|
||||||
SIMDJSON_POP_DISABLE_WARNINGS
|
SIMDJSON_POP_DISABLE_WARNINGS
|
||||||
|
|
||||||
if (fp == nullptr) {
|
if (fp == nullptr) {
|
||||||
@@ -215,10 +226,13 @@ inline simdjson_result<padded_string> padded_string::load(std::string_view filen
|
|||||||
|
|
||||||
#if defined(_WIN32) && SIMDJSON_CPLUSPLUS17
|
#if defined(_WIN32) && SIMDJSON_CPLUSPLUS17
|
||||||
inline simdjson_result<padded_string> padded_string::load(std::wstring_view filename) noexcept {
|
inline simdjson_result<padded_string> padded_string::load(std::wstring_view filename) noexcept {
|
||||||
|
// std::wstring_view is not guaranteed to be null-terminated, but _wfopen requires
|
||||||
|
// a null-terminated wide C string. Construct a temporary std::wstring to ensure null-termination.
|
||||||
|
const std::wstring null_terminated_filename(filename);
|
||||||
// Open the file using the wide characters
|
// Open the file using the wide characters
|
||||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||||
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
|
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
|
||||||
std::FILE *fp = _wfopen(filename.data(), L"rb");
|
std::FILE *fp = _wfopen(null_terminated_filename.c_str(), L"rb");
|
||||||
SIMDJSON_POP_DISABLE_WARNINGS
|
SIMDJSON_POP_DISABLE_WARNINGS
|
||||||
|
|
||||||
if (fp == nullptr) {
|
if (fp == nullptr) {
|
||||||
@@ -341,6 +355,9 @@ inline padded_string padded_string_builder::convert() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline bool padded_string_builder::reserve(size_t additional) noexcept {
|
inline bool padded_string_builder::reserve(size_t additional) noexcept {
|
||||||
|
if (simdjson_unlikely(additional + size < size)) {
|
||||||
|
return false; // overflow: cannot satisfy request
|
||||||
|
}
|
||||||
size_t needed = size + additional;
|
size_t needed = size + additional;
|
||||||
if (needed <= capacity) {
|
if (needed <= capacity) {
|
||||||
return true;
|
return true;
|
||||||
@@ -350,8 +367,9 @@ inline bool padded_string_builder::reserve(size_t additional) noexcept {
|
|||||||
// repeated allocations.
|
// repeated allocations.
|
||||||
if (new_capacity < 4096) {
|
if (new_capacity < 4096) {
|
||||||
new_capacity *= 2;
|
new_capacity *= 2;
|
||||||
} else {
|
// overflow guard: ensure new_capacity + new_capacity/2 does not overflow
|
||||||
new_capacity += new_capacity/2; // grow by 1.5x
|
} else if (new_capacity + new_capacity / 2 > new_capacity) {
|
||||||
|
new_capacity += new_capacity / 2; // grow by 1.5x
|
||||||
}
|
}
|
||||||
char *new_data = internal::allocate_padded_buffer(new_capacity);
|
char *new_data = internal::allocate_padded_buffer(new_capacity);
|
||||||
if (new_data == nullptr) {
|
if (new_data == nullptr) {
|
||||||
@@ -366,6 +384,57 @@ inline bool padded_string_builder::reserve(size_t additional) noexcept {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
simdjson_inline padded_memory_map::padded_memory_map(const char *filename) noexcept {
|
||||||
|
|
||||||
|
int fd = open(filename, O_RDONLY);
|
||||||
|
if (fd == -1) {
|
||||||
|
return; // file not found or cannot be opened, data will be nullptr
|
||||||
|
}
|
||||||
|
struct stat st;
|
||||||
|
if (fstat(fd, &st) == -1) {
|
||||||
|
close(fd);
|
||||||
|
return; // failed to get file size, data will be nullptr
|
||||||
|
}
|
||||||
|
size = static_cast<size_t>(st.st_size);
|
||||||
|
size_t total_size = size + simdjson::SIMDJSON_PADDING;
|
||||||
|
void *anon_map =
|
||||||
|
mmap(NULL, total_size, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||||
|
if (anon_map == MAP_FAILED) {
|
||||||
|
close(fd);
|
||||||
|
return; // failed to create anonymous mapping, data will be nullptr
|
||||||
|
}
|
||||||
|
void *file_map =
|
||||||
|
mmap(anon_map, size, PROT_READ, MAP_SHARED | MAP_FIXED, fd, 0);
|
||||||
|
if (file_map == MAP_FAILED) {
|
||||||
|
munmap(anon_map, total_size);
|
||||||
|
close(fd);
|
||||||
|
return; // failed to mmap file, data will be nullptr
|
||||||
|
}
|
||||||
|
data = static_cast<const char *>(file_map);
|
||||||
|
close(fd); // no longer needed after mapping
|
||||||
|
}
|
||||||
|
|
||||||
|
simdjson_inline padded_memory_map::~padded_memory_map() noexcept {
|
||||||
|
if (data != nullptr) {
|
||||||
|
munmap(const_cast<char *>(data), size + simdjson::SIMDJSON_PADDING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
simdjson_inline simdjson::padded_string_view padded_memory_map::view() const noexcept simdjson_lifetime_bound {
|
||||||
|
if(!is_valid()) {
|
||||||
|
return simdjson::padded_string_view(); // return an empty view if mapping failed
|
||||||
|
}
|
||||||
|
return simdjson::padded_string_view(data, size, size + simdjson::SIMDJSON_PADDING);
|
||||||
|
}
|
||||||
|
|
||||||
|
simdjson_inline bool padded_memory_map::is_valid() const noexcept {
|
||||||
|
return data != nullptr;
|
||||||
|
}
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|
||||||
inline simdjson::padded_string operator ""_padded(const char *str, size_t len) {
|
inline simdjson::padded_string operator ""_padded(const char *str, size_t len) {
|
||||||
@@ -376,4 +445,5 @@ inline simdjson::padded_string operator ""_padded(const char8_t *str, size_t len
|
|||||||
return simdjson::padded_string(reinterpret_cast<const char *>(str), len);
|
return simdjson::padded_string(reinterpret_cast<const char *>(str), len);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // SIMDJSON_PADDED_STRING_INL_H
|
#endif // SIMDJSON_PADDED_STRING_INL_H
|
||||||
|
|||||||
@@ -277,6 +277,62 @@ inline std::ostream& operator<<(std::ostream& out, const padded_string& s) { ret
|
|||||||
inline std::ostream& operator<<(std::ostream& out, simdjson_result<padded_string> &s) noexcept(false) { return out << s.value(); }
|
inline std::ostream& operator<<(std::ostream& out, simdjson_result<padded_string> &s) noexcept(false) { return out << s.value(); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
/**
|
||||||
|
* A class representing a memory-mapped file with padding.
|
||||||
|
* It is only available on non-Windows platforms, as Windows has different APIs for memory mapping.
|
||||||
|
*/
|
||||||
|
class padded_memory_map {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a new padded memory map for the given file.
|
||||||
|
* After creating the memory map, you can call view() to get a padded_string_view of the file content.
|
||||||
|
* The memory map will be automatically released when the padded_memory_map instance is destroyed.
|
||||||
|
* Note that the file content is not copied, so this is efficient for large files. However,
|
||||||
|
* the file must remain unchanged while the memory map is in use. In case of error (e.g., file not found,
|
||||||
|
* permission denied, etc.), the memory map will be invalid and view() will return an empty view.
|
||||||
|
* You can check if the memory map is valid by calling is_valid() before using view().
|
||||||
|
*
|
||||||
|
* @param filename the path to the file to memory-map.
|
||||||
|
*/
|
||||||
|
simdjson_inline padded_memory_map(const char *filename) noexcept;
|
||||||
|
/**
|
||||||
|
* Destroy the padded memory map and release any resources.
|
||||||
|
*/
|
||||||
|
simdjson_inline ~padded_memory_map() noexcept;
|
||||||
|
|
||||||
|
// lifetime of the view is tied to the memory map, so we can return a view
|
||||||
|
// directly
|
||||||
|
/**
|
||||||
|
* Get a view of the memory-mapped file. It always succeeds, but the view may be empty
|
||||||
|
* if the memory map is invalid (e.g., due to file not found, permission denied, etc.).
|
||||||
|
* You can check if the memory map is valid by calling is_valid() before using the view.
|
||||||
|
*
|
||||||
|
* Lifetime of the view is tied to the memory map, so the view should not be used after the
|
||||||
|
* padded_memory_map instance is destroyed.
|
||||||
|
*
|
||||||
|
* @return a padded_string_view representing the memory-mapped file, or an empty view if the memory map is invalid.
|
||||||
|
*/
|
||||||
|
simdjson_inline simdjson::padded_string_view view() const noexcept simdjson_lifetime_bound;
|
||||||
|
/**
|
||||||
|
* Check if the memory map is valid.
|
||||||
|
*
|
||||||
|
* @return true if the memory map is valid, false otherwise.
|
||||||
|
*/
|
||||||
|
simdjson_inline bool is_valid() const noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
padded_memory_map() = delete;
|
||||||
|
padded_memory_map(const padded_memory_map &) = delete;
|
||||||
|
padded_memory_map &operator=(const padded_memory_map &) = delete;
|
||||||
|
const char *data{nullptr};
|
||||||
|
size_t size{0};
|
||||||
|
};
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|
||||||
// This is deliberately outside of simdjson so that people get it without having to use the namespace
|
// This is deliberately outside of simdjson so that people get it without having to use the namespace
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user