mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5900ac42d9 | |||
| 5bb9c8eed5 | |||
| 122bea262a | |||
| e48f7bf98b |
@@ -58,7 +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. As of 2026, Windows 10 is no longer supported.
|
* 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).
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: uraimo/run-on-arch-action@v3
|
- uses: uraimo/run-on-arch-action@v3
|
||||||
name: Test
|
name: Test
|
||||||
id: runcmd
|
id: runcmd
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ jobs:
|
|||||||
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: dependencies/.cache
|
path: dependencies/.cache
|
||||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ jobs:
|
|||||||
image: debian:testing
|
image: debian:testing
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
url: ${{ steps.deployment.outputs.page_url }}
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- name: Install Doxygen
|
- name: Install Doxygen
|
||||||
run: sudo apt-get install doxygen graphviz -y
|
run: sudo apt-get install doxygen graphviz -y
|
||||||
- run: mkdir docs
|
- run: mkdir docs
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
- uses: mymindstorm/setup-emsdk@6ab9eb1bda2574c4ddb79809fc9247783eaf9021 # v14
|
- uses: mymindstorm/setup-emsdk@6ab9eb1bda2574c4ddb79809fc9247783eaf9021 # v14
|
||||||
- name: Verify
|
- name: Verify
|
||||||
run: emcc -v
|
run: emcc -v
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v3.6.0
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: emcmake cmake -B build
|
run: emcmake cmake -B build
|
||||||
- name: Build # We build but do not test
|
- name: Build # We build but do not test
|
||||||
run: cmake --build build
|
run: cmake --build build
|
||||||
@@ -6,7 +6,7 @@ jobs:
|
|||||||
whitespace:
|
whitespace:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- name: Remove whitespace and check the diff
|
- name: Remove whitespace and check the diff
|
||||||
run: |
|
run: |
|
||||||
set -eu
|
set -eu
|
||||||
|
|||||||
@@ -38,14 +38,14 @@ jobs:
|
|||||||
chmod +x llvm.sh
|
chmod +x llvm.sh
|
||||||
sudo ./llvm.sh $CLANGVERSION
|
sudo ./llvm.sh $CLANGVERSION
|
||||||
|
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: dependencies/.cache
|
path: dependencies/.cache
|
||||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v4
|
||||||
id: cache-corpus
|
id: cache-corpus
|
||||||
with:
|
with:
|
||||||
path: out/
|
path: out/
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
name: gcc 16
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container:
|
|
||||||
image: 'gcc:16'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
apt -y update
|
|
||||||
apt -y --no-install-recommends install cmake ninja-build
|
|
||||||
- name: Build and test
|
|
||||||
run: |
|
|
||||||
cmake -B build -D SIMDJSON_STATIC_REFLECTION=ON -DSIMDJSON_DEVELOPER_MODE=ON -GNinja
|
|
||||||
cmake --build build
|
|
||||||
ctest --test-dir build --parallel $(nproc)
|
|
||||||
@@ -11,13 +11,13 @@ jobs:
|
|||||||
platform:
|
platform:
|
||||||
- { toolchain-version: 2023.08.08 }
|
- { toolchain-version: 2023.08.08 }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- name: Install build requirements
|
- name: Install build requirements
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -y
|
sudo apt-get update -y
|
||||||
sudo apt-get install -y --no-install-recommends cmake
|
sudo apt-get install -y --no-install-recommends cmake
|
||||||
|
|
||||||
- uses: actions/cache/restore@v5
|
- uses: actions/cache/restore@v4
|
||||||
id: restore-cache
|
id: restore-cache
|
||||||
with:
|
with:
|
||||||
path: /opt/cross-tools
|
path: /opt/cross-tools
|
||||||
@@ -33,7 +33,7 @@ jobs:
|
|||||||
mkdir -p /opt
|
mkdir -p /opt
|
||||||
tar -C /opt -x -f /tmp/toolchain.tar.xz
|
tar -C /opt -x -f /tmp/toolchain.tar.xz
|
||||||
|
|
||||||
- uses: actions/cache/save@v5
|
- uses: actions/cache/save@v3
|
||||||
if: ${{ !steps.restore-cache.outputs.cache-hit }}
|
if: ${{ !steps.restore-cache.outputs.cache-hit }}
|
||||||
with:
|
with:
|
||||||
path: /opt/cross-tools
|
path: /opt/cross-tools
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ jobs:
|
|||||||
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: dependencies/.cache
|
path: dependencies/.cache
|
||||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
@@ -42,14 +42,3 @@ jobs:
|
|||||||
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
|
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
|
||||||
cd ../tests/installation_tests/find &&
|
cd ../tests/installation_tests/find &&
|
||||||
mkdir buildshared && cd buildshared && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../buildshared/destination .. && cmake --build .
|
mkdir buildshared && cd buildshared && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../buildshared/destination .. && cmake --build .
|
||||||
- name: Use cmake (parsing for NaN/Infinity enabled)
|
|
||||||
run: |
|
|
||||||
mkdir build_nan_inf &&
|
|
||||||
cd build_nan_inf &&
|
|
||||||
cmake -DSIMDJSON_ENABLE_NAN_INF=ON -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
|
|
||||||
cmake --build . &&
|
|
||||||
ctest --output-on-failure -LE explicitonly -j &&
|
|
||||||
cmake --install . &&
|
|
||||||
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
|
|
||||||
cd ../tests/installation_tests/find &&
|
|
||||||
mkdir build_nan_inf && cd build_nan_inf && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build_nan_inf/destination .. && cmake --build .
|
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ jobs:
|
|||||||
CMAKE_GENERATOR: Ninja
|
CMAKE_GENERATOR: Ninja
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: dependencies/.cache
|
path: dependencies/.cache
|
||||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ jobs:
|
|||||||
CMAKE_GENERATOR: Ninja
|
CMAKE_GENERATOR: Ninja
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: dependencies/.cache
|
path: dependencies/.cache
|
||||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: uraimo/run-on-arch-action@v3
|
- uses: uraimo/run-on-arch-action@v3
|
||||||
name: Test
|
name: Test
|
||||||
id: runcmd
|
id: runcmd
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: uraimo/run-on-arch-action@v3
|
- uses: uraimo/run-on-arch-action@v3
|
||||||
name: Test
|
name: Test
|
||||||
id: runcmd
|
id: runcmd
|
||||||
@@ -26,4 +26,4 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cmake -DCMAKE_BUILD_TYPE=Release -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -B build
|
cmake -DCMAKE_BUILD_TYPE=Release -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -B build
|
||||||
cmake --build build -j=2
|
cmake --build build -j=2
|
||||||
ctest --output-on-failure --test-dir build -E ondemand_cacheline
|
ctest --output-on-failure --test-dir build
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- name: Install packages
|
- name: Install packages
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -q -y
|
sudo apt-get update -q -y
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- name: Install packages
|
- name: Install packages
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -q -y
|
sudo apt-get update -q -y
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- name: Install packages
|
- name: Install packages
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -q -y
|
sudo apt-get update -q -y
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- name: Install packages
|
- name: Install packages
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -q -y
|
sudo apt-get update -q -y
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- name: Install packages
|
- name: Install packages
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -q -y
|
sudo apt-get update -q -y
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: uraimo/run-on-arch-action@v3
|
- uses: uraimo/run-on-arch-action@v3
|
||||||
name: Test
|
name: Test
|
||||||
id: runcmd
|
id: runcmd
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ jobs:
|
|||||||
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: dependencies/.cache
|
path: dependencies/.cache
|
||||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
@@ -20,4 +20,4 @@ jobs:
|
|||||||
cd build &&
|
cd build &&
|
||||||
CXX=clang++-13 cmake -DSIMDJSON_DEVELOPER_MODE=ON .. &&
|
CXX=clang++-13 cmake -DSIMDJSON_DEVELOPER_MODE=ON .. &&
|
||||||
cmake --build . &&
|
cmake --build . &&
|
||||||
ctest --output-on-failure -LE explicitonly -j
|
ctest --output-on-failure -LE explicitonly -j
|
||||||
@@ -9,8 +9,8 @@ jobs:
|
|||||||
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: dependencies/.cache
|
path: dependencies/.cache
|
||||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ jobs:
|
|||||||
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: dependencies/.cache
|
path: dependencies/.cache
|
||||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ jobs:
|
|||||||
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: dependencies/.cache
|
path: dependencies/.cache
|
||||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
@@ -20,4 +20,4 @@ jobs:
|
|||||||
cd build &&
|
cd build &&
|
||||||
CXX=g++-12 cmake -DSIMDJSON_DEVELOPER_MODE=ON .. &&
|
CXX=g++-12 cmake -DSIMDJSON_DEVELOPER_MODE=ON .. &&
|
||||||
cmake --build . &&
|
cmake --build . &&
|
||||||
ctest --output-on-failure -LE explicitonly -j
|
ctest --output-on-failure -LE explicitonly -j
|
||||||
@@ -8,8 +8,8 @@ jobs:
|
|||||||
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: dependencies/.cache
|
path: dependencies/.cache
|
||||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
@@ -21,4 +21,4 @@ jobs:
|
|||||||
cd build &&
|
cd build &&
|
||||||
CXX=g++-12 cmake -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GLIBCXX_ASSERTIONS=ON -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON .. &&
|
CXX=g++-12 cmake -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GLIBCXX_ASSERTIONS=ON -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON .. &&
|
||||||
cmake --build . &&
|
cmake --build . &&
|
||||||
ctest . -E avoid_
|
ctest . -E avoid_
|
||||||
@@ -9,8 +9,8 @@ jobs:
|
|||||||
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: dependencies/.cache
|
path: dependencies/.cache
|
||||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
@@ -21,4 +21,4 @@ jobs:
|
|||||||
CXX=g++-12 cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_SANITIZE_THREADS=ON .. &&
|
CXX=g++-12 cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_SANITIZE_THREADS=ON .. &&
|
||||||
cmake --build . --target document_stream_tests --target ondemand_document_stream_tests --target parse_many_test &&
|
cmake --build . --target document_stream_tests --target ondemand_document_stream_tests --target parse_many_test &&
|
||||||
ctest --output-on-failure -R parse_many_test &&
|
ctest --output-on-failure -R parse_many_test &&
|
||||||
ctest --output-on-failure -R document_stream_tests
|
ctest --output-on-failure -R document_stream_tests
|
||||||
@@ -9,8 +9,8 @@ jobs:
|
|||||||
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: dependencies/.cache
|
path: dependencies/.cache
|
||||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ jobs:
|
|||||||
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: dependencies/.cache
|
path: dependencies/.cache
|
||||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
name: Ubuntu 24.04 CI (CLANG 20)
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
ubuntu-build:
|
|
||||||
if: >-
|
|
||||||
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
|
||||||
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
- uses: actions/cache@v5
|
|
||||||
with:
|
|
||||||
path: dependencies/.cache
|
|
||||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
|
||||||
- name: Install clang-20
|
|
||||||
run: |
|
|
||||||
sudo apt-get update -q -y
|
|
||||||
sudo apt-get install -y clang-20
|
|
||||||
- name: Use cmake
|
|
||||||
run: |
|
|
||||||
mkdir build &&
|
|
||||||
cd build &&
|
|
||||||
CXX=clang++-20 cmake -DSIMDJSON_DEVELOPER_MODE=ON .. &&
|
|
||||||
cmake --build . &&
|
|
||||||
ctest --output-on-failure -LE explicitonly -j
|
|
||||||
@@ -11,7 +11,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
cxx: [g++-13, clang++-16]
|
cxx: [g++-13, clang++-16]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||||
- name: Prepare
|
- name: Prepare
|
||||||
run: cmake -DSIMDJSON_CXX_STANDARD=20 -DSIMDJSON_EXCEPTIONS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -B build
|
run: cmake -DSIMDJSON_CXX_STANDARD=20 -DSIMDJSON_EXCEPTIONS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -B build
|
||||||
env:
|
env:
|
||||||
@@ -19,4 +19,4 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build -j=2
|
run: cmake --build build -j=2
|
||||||
- name: Test
|
- name: Test
|
||||||
run: ctest --output-on-failure --test-dir build
|
run: ctest --output-on-failure --test-dir build
|
||||||
@@ -11,7 +11,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
cxx: [g++-13, clang++-16]
|
cxx: [g++-13, clang++-16]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||||
- name: Prepare
|
- name: Prepare
|
||||||
run: cmake -DSIMDJSON_CXX_STANDARD=20 -DSIMDJSON_DEVELOPER_MODE=ON -B build
|
run: cmake -DSIMDJSON_CXX_STANDARD=20 -DSIMDJSON_DEVELOPER_MODE=ON -B build
|
||||||
env:
|
env:
|
||||||
@@ -19,4 +19,4 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build -j=2
|
run: cmake --build build -j=2
|
||||||
- name: Test
|
- name: Test
|
||||||
run: ctest --output-on-failure --test-dir build
|
run: ctest --output-on-failure --test-dir build
|
||||||
@@ -9,8 +9,8 @@ jobs:
|
|||||||
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: dependencies/.cache
|
path: dependencies/.cache
|
||||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ jobs:
|
|||||||
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: dependencies/.cache
|
path: dependencies/.cache
|
||||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ jobs:
|
|||||||
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: dependencies/.cache
|
path: dependencies/.cache
|
||||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
@@ -21,21 +21,14 @@ jobs:
|
|||||||
cmake -DSIMDJSON_SANITIZE=ON -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF .. &&
|
cmake -DSIMDJSON_SANITIZE=ON -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF .. &&
|
||||||
cmake --build . &&
|
cmake --build . &&
|
||||||
ctest --output-on-failure -LE explicitonly -j
|
ctest --output-on-failure -LE explicitonly -j
|
||||||
- name: Use cmake with address sanitizer (Parsing of NaN/Infinity enabled)
|
|
||||||
run: |
|
|
||||||
mkdir builddebug_nan_inf &&
|
|
||||||
cd builddebug_nan_inf &&
|
|
||||||
cmake -DSIMDJSON_SANITIZE=ON -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_ENABLE_NAN_INF=ON .. &&
|
|
||||||
cmake --build . &&
|
|
||||||
ctest --output-on-failure -LE explicitonly -j
|
|
||||||
ubuntu-build-undefined-sanitizer:
|
ubuntu-build-undefined-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]')
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: dependencies/.cache
|
path: dependencies/.cache
|
||||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
@@ -46,10 +39,3 @@ jobs:
|
|||||||
cmake -DSIMDJSON_SANITIZE_UNDEFINED=ON -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF .. &&
|
cmake -DSIMDJSON_SANITIZE_UNDEFINED=ON -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF .. &&
|
||||||
cmake --build . &&
|
cmake --build . &&
|
||||||
ctest --output-on-failure -LE explicitonly -j
|
ctest --output-on-failure -LE explicitonly -j
|
||||||
- name: Use cmake with undefined sanitizer (Parsing of NaN/Infinity enabled)
|
|
||||||
run: |
|
|
||||||
mkdir builddebugundefsani_nan_inf &&
|
|
||||||
cd builddebugundefsani_nan_inf &&
|
|
||||||
cmake -DSIMDJSON_SANITIZE_UNDEFINED=ON -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_ENABLE_NAN_INF=ON .. &&
|
|
||||||
cmake --build . &&
|
|
||||||
ctest --output-on-failure -LE explicitonly -j
|
|
||||||
|
|||||||
@@ -12,12 +12,11 @@ jobs:
|
|||||||
shared: [ON, OFF]
|
shared: [ON, OFF]
|
||||||
cxx: [g++-13, clang++-16]
|
cxx: [g++-13, clang++-16]
|
||||||
sanitizer: [ON, OFF]
|
sanitizer: [ON, OFF]
|
||||||
nan_inf: [ON, OFF]
|
|
||||||
build_type: [RelWithDebInfo, Debug, Release]
|
build_type: [RelWithDebInfo, Debug, Release]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||||
- name: Prepare
|
- name: Prepare
|
||||||
run: cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_SANITIZE=${{matrix.sanitizer}} -DBUILD_SHARED_LIBS=${{matrix.shared}} -DSIMDJSON_ENABLE_NAN_INF=${{matrix.nan_inf}} -B build
|
run: cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_SANITIZE=${{matrix.sanitizer}} -DBUILD_SHARED_LIBS=${{matrix.shared}} -B build
|
||||||
env:
|
env:
|
||||||
CXX: ${{matrix.cxx}}
|
CXX: ${{matrix.cxx}}
|
||||||
- name: Build
|
- name: Build
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
- {arch: ARM64EC}
|
- {arch: ARM64EC}
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v4
|
||||||
- name: Use cmake
|
- name: Use cmake
|
||||||
run: |
|
run: |
|
||||||
cmake -A ${{ matrix.arch }} -DCMAKE_SYSTEM_VERSION="10.0.22621.0" -DCMAKE_CROSSCOMPILING=1 -DSIMDJSON_DEVELOPER_MODE=ON -D SIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_EXCEPTIONS=OFF -B build &&
|
cmake -A ${{ matrix.arch }} -DCMAKE_SYSTEM_VERSION="10.0.22621.0" -DCMAKE_CROSSCOMPILING=1 -DSIMDJSON_DEVELOPER_MODE=ON -D SIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_EXCEPTIONS=OFF -B build &&
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF}
|
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF}
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v4
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: |
|
run: |
|
||||||
cmake -DSIMDJSON_CXX_STANDARD=20 -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -DBUILD_SHARED_LIBS=${{matrix.shared}} -B build
|
cmake -DSIMDJSON_CXX_STANDARD=20 -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -DBUILD_SHARED_LIBS=${{matrix.shared}} -B build
|
||||||
@@ -41,4 +41,4 @@ jobs:
|
|||||||
- name: Test Installation
|
- name: Test Installation
|
||||||
run: |
|
run: |
|
||||||
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
|
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
|
||||||
cmake --build build_install_test --config Release
|
cmake --build build_install_test --config Release
|
||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: RelWithDebInfo}
|
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: RelWithDebInfo}
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v4
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: |
|
run: |
|
||||||
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSANITIZE=ON -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -DBUILD_SHARED_LIBS=${{matrix.shared}} -B build
|
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSANITIZE=ON -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -DBUILD_SHARED_LIBS=${{matrix.shared}} -B build
|
||||||
@@ -27,4 +27,4 @@ jobs:
|
|||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: |
|
run: |
|
||||||
cd build
|
cd build
|
||||||
ctest -C ${{matrix.build_type}} -LE explicitonly --output-on-failure
|
ctest -C ${{matrix.build_type}} -LE explicitonly --output-on-failure
|
||||||
@@ -13,20 +13,20 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- {gen: Visual Studio 17 2022, arch: Win32, shared: ON, build_type: Release, memory_map: OFF, nan_inf: OFF}
|
- {gen: Visual Studio 17 2022, arch: Win32, shared: ON, build_type: Release, memory_map: OFF}
|
||||||
- {gen: Visual Studio 17 2022, arch: Win32, shared: OFF, build_type: Release, memory_map: OFF, nan_inf: OFF}
|
- {gen: Visual Studio 17 2022, arch: Win32, shared: OFF, build_type: Release, memory_map: OFF}
|
||||||
- {gen: Visual Studio 17 2022, arch: x64, shared: ON, build_type: Release, memory_map: ON, nan_inf: OFF}
|
- {gen: Visual Studio 17 2022, arch: x64, shared: ON, build_type: Release, memory_map: OFF}
|
||||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Debug, memory_map: ON, nan_inf: OFF}
|
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Debug, memory_map: OFF}
|
||||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Release, memory_map: ON, nan_inf: OFF}
|
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Release, memory_map: OFF}
|
||||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: RelWithDebInfo, memory_map: ON, nan_inf: OFF}
|
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: RelWithDebInfo, memory_map: OFF}
|
||||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Debug, memory_map: ON, nan_inf: ON}
|
# Exercise the opt-in Windows memory-file mapping path at least once in CI.
|
||||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Release, memory_map: ON, nan_inf: ON}
|
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Release, memory_map: ON}
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v4
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: |
|
run: |
|
||||||
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -DBUILD_SHARED_LIBS=${{matrix.shared}} -DSIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS=${{matrix.memory_map}} -DSIMDJSON_ENABLE_NAN_INF=${{matrix.nan_inf}} -B build
|
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -DBUILD_SHARED_LIBS=${{matrix.shared}} -DSIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS=${{matrix.memory_map}} -B build
|
||||||
- name: Build Debug
|
- name: Build Debug
|
||||||
run: cmake --build build --config ${{matrix.build_type}} --verbose
|
run: cmake --build build --config ${{matrix.build_type}} --verbose
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
@@ -39,4 +39,4 @@ jobs:
|
|||||||
- name: Test Installation
|
- name: Test Installation
|
||||||
run: |
|
run: |
|
||||||
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
|
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
|
||||||
cmake --build build_install_test --config ${{matrix.build_type}}
|
cmake --build build_install_test --config ${{matrix.build_type}}
|
||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
- {gen: Visual Studio 17 2022, arch: x64, build_type: RelWithDebInfo}
|
- {gen: Visual Studio 17 2022, arch: x64, build_type: RelWithDebInfo}
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v4
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: |
|
run: |
|
||||||
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -B build
|
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -B build
|
||||||
@@ -34,4 +34,4 @@ jobs:
|
|||||||
- name: Test Installation
|
- name: Test Installation
|
||||||
run: |
|
run: |
|
||||||
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
|
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
|
||||||
cmake --build build_install_test --config ${{matrix.build_type}}
|
cmake --build build_install_test --config ${{matrix.build_type}}
|
||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
- {gen: Visual Studio 17 2022, arch: x64, build_type: RelWithDebInfo}
|
- {gen: Visual Studio 17 2022, arch: x64, build_type: RelWithDebInfo}
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v4
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: |
|
run: |
|
||||||
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -B build
|
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -B build
|
||||||
@@ -34,4 +34,4 @@ jobs:
|
|||||||
- name: Test Installation
|
- name: Test Installation
|
||||||
run: |
|
run: |
|
||||||
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
|
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
|
||||||
cmake --build build_install_test --config ${{matrix.build_type}}
|
cmake --build build_install_test --config ${{matrix.build_type}}
|
||||||
@@ -7,8 +7,8 @@ jobs:
|
|||||||
name: windows-vs17
|
name: windows-vs17
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: dependencies/.cache
|
path: dependencies/.cache
|
||||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
|||||||
@@ -254,12 +254,6 @@ if(SIMDJSON_MINUS_ZERO_AS_FLOAT)
|
|||||||
simdjson_add_props(target_compile_definitions PRIVATE SIMDJSON_MINUS_ZERO_AS_FLOAT=1)
|
simdjson_add_props(target_compile_definitions PRIVATE SIMDJSON_MINUS_ZERO_AS_FLOAT=1)
|
||||||
endif(SIMDJSON_MINUS_ZERO_AS_FLOAT)
|
endif(SIMDJSON_MINUS_ZERO_AS_FLOAT)
|
||||||
|
|
||||||
option(SIMDJSON_ENABLE_NAN_INF "Allow parsing of NaN and Infinity JSON values" OFF)
|
|
||||||
if(SIMDJSON_ENABLE_NAN_INF)
|
|
||||||
message(STATUS "simdjson NaN and Infinity parsing is enabled.")
|
|
||||||
simdjson_add_props(target_compile_definitions PUBLIC SIMDJSON_ENABLE_NAN_INF=1)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# GCC and Clang have horrendous Debug builds when using SIMD.
|
# GCC and Clang have horrendous Debug builds when using SIMD.
|
||||||
# A common fix is to use '-Og' instead.
|
# A common fix is to use '-Og' instead.
|
||||||
# bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412
|
# bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412
|
||||||
|
|||||||
@@ -190,7 +190,6 @@ We distinguish between "bindings" (which just wrap the C++ code) and a port to a
|
|||||||
- [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.
|
- [mruby-fast-json](https://github.com/Asmod4n/mruby-fast-json): mruby binding with high API coverage.
|
||||||
- [simdjson-dart](https://github.com/xaldarof/simdjson-dart): Dart bindings for the simdjson project.
|
|
||||||
|
|
||||||
About simdjson
|
About simdjson
|
||||||
--------------
|
--------------
|
||||||
@@ -201,7 +200,7 @@ CPU's multiple execution cores.
|
|||||||
|
|
||||||
Our default front-end is called On-Demand, and we wrote a paper about it:
|
Our default front-end is called On-Demand, and we wrote a paper about it:
|
||||||
|
|
||||||
- John Keiser, Daniel Lemire, [On-Demand JSON: A Better Way to Parse Documents?](https://arxiv.org/abs/2312.17149), Software: Practice and Experience 54 (6), 2024.
|
- John Keiser, Daniel Lemire, [On-Demand JSON: A Better Way to Parse Documents?](http://arxiv.org/abs/2312.17149), Software: Practice and Experience 54 (6), 2024.
|
||||||
|
|
||||||
Some people [enjoy reading the first (2019) simdjson paper](https://arxiv.org/abs/1902.08318): A description of the design
|
Some people [enjoy reading the first (2019) simdjson paper](https://arxiv.org/abs/1902.08318): A description of the design
|
||||||
and implementation of simdjson is in our research article:
|
and implementation of simdjson is in our research article:
|
||||||
@@ -214,12 +213,12 @@ 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, we had a talk at QCon San Francisco 2019<br />
|
For the video inclined, we had a talk at QCon San Francisco 2019<br />
|
||||||
[](https://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!
|
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!
|
||||||
|
|
||||||
[](https://www.youtube.com/watch?v=Mcgk3CxHYMs)<br />
|
[](http://www.youtube.com/watch?v=Mcgk3CxHYMs)<br />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -272,7 +271,7 @@ This code is made available under the [Apache License 2.0](https://www.apache.or
|
|||||||
|
|
||||||
Under Windows, we build some tools using the windows/dirent_portable.h file (which is outside our library code): it is under the liberal (business-friendly) MIT license.
|
Under Windows, we build some tools using the windows/dirent_portable.h file (which is outside our library code): it is under the liberal (business-friendly) MIT license.
|
||||||
|
|
||||||
For compilers that do not support [C++17](https://en.wikipedia.org/wiki/C%2B%2B17), we bundle the string-view library which is published under the [Boost license](https://www.boost.org/LICENSE_1_0.txt). Like the Apache license, the Boost license is a permissive license allowing commercial redistribution.
|
For compilers that do not support [C++17](https://en.wikipedia.org/wiki/C%2B%2B17), we bundle the string-view library which is published under the [Boost license](http://www.boost.org/LICENSE_1_0.txt). Like the Apache license, the Boost license is a permissive license allowing commercial redistribution.
|
||||||
|
|
||||||
For efficient number serialization, we bundle Florian Loitsch's implementation of the Grisu2 algorithm for binary to decimal floating-point numbers. The implementation was slightly modified by JSON for Modern C++ library. Both Florian Loitsch's implementation and JSON for Modern C++ are provided under the MIT license.
|
For efficient number serialization, we bundle Florian Loitsch's implementation of the Grisu2 algorithm for binary to decimal floating-point numbers. The implementation was slightly modified by JSON for Modern C++ library. Both Florian Loitsch's implementation and JSON for Modern C++ are provided under the MIT license.
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ SIMDJSON_POP_DISABLE_WARNINGS
|
|||||||
#include "json2msgpack/boostjson.h"
|
#include "json2msgpack/boostjson.h"
|
||||||
|
|
||||||
#include "partial_tweets/simdjson_ondemand.h"
|
#include "partial_tweets/simdjson_ondemand.h"
|
||||||
|
#include "partial_tweets/simdjson_ondemand_key_selector.h"
|
||||||
#include "partial_tweets/simdjson_dom.h"
|
#include "partial_tweets/simdjson_dom.h"
|
||||||
#include "partial_tweets/yyjson.h"
|
#include "partial_tweets/yyjson.h"
|
||||||
#if SIMDJSON_COMPETITION_ONDEMAND_SAJSON
|
#if SIMDJSON_COMPETITION_ONDEMAND_SAJSON
|
||||||
@@ -124,7 +125,6 @@ SIMDJSON_POP_DISABLE_WARNINGS
|
|||||||
#include "kostya/boostjson.h"
|
#include "kostya/boostjson.h"
|
||||||
|
|
||||||
#include "large_random/simdjson_ondemand.h"
|
#include "large_random/simdjson_ondemand.h"
|
||||||
#include "large_random/simdjson_ondemand_ranges.h"
|
|
||||||
#if SIMDJSON_COMPETITION_ONDEMAND_UNORDERED
|
#if SIMDJSON_COMPETITION_ONDEMAND_UNORDERED
|
||||||
#include "large_random/simdjson_ondemand_unordered.h"
|
#include "large_random/simdjson_ondemand_unordered.h"
|
||||||
#endif // SIMDJSON_COMPETITION_ONDEMAND_UNORDERED
|
#endif // SIMDJSON_COMPETITION_ONDEMAND_UNORDERED
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_RANGES
|
|
||||||
|
|
||||||
#include "large_random.h"
|
|
||||||
|
|
||||||
namespace large_random {
|
|
||||||
|
|
||||||
using namespace simdjson;
|
|
||||||
|
|
||||||
// Identical to simdjson_ondemand but uses get_range() for iteration.
|
|
||||||
// Demonstrates that the ranges wrapper has zero per-element overhead.
|
|
||||||
struct simdjson_ondemand_ranges {
|
|
||||||
static constexpr diff_flags DiffFlags = diff_flags::NONE;
|
|
||||||
|
|
||||||
ondemand::parser parser{};
|
|
||||||
|
|
||||||
bool run(simdjson::padded_string &json, std::vector<point> &result) {
|
|
||||||
auto doc = parser.iterate(json);
|
|
||||||
for (auto coord_result : ondemand::get_range(doc.get_array())) {
|
|
||||||
ondemand::object coord = coord_result;
|
|
||||||
result.emplace_back(json_benchmark::point{coord.find_field("x"), coord.find_field("y"), coord.find_field("z")});
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
BENCHMARK_TEMPLATE(large_random, simdjson_ondemand_ranges)->UseManualTime();
|
|
||||||
|
|
||||||
} // namespace large_random
|
|
||||||
|
|
||||||
#endif // SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_RANGES
|
|
||||||
@@ -103,12 +103,7 @@ error_code Sax::RunNoExcept(const padded_string &json) noexcept {
|
|||||||
|
|
||||||
error_code Sax::Allocate(size_t new_capacity) {
|
error_code Sax::Allocate(size_t new_capacity) {
|
||||||
// string_capacity copied from document::allocate
|
// string_capacity copied from document::allocate
|
||||||
// a document with only zero-length strings... could have capacity/3 string
|
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * new_capacity / 3 + SIMDJSON_PADDING, 64);
|
||||||
// and we would need capacity/3 * 5 bytes on the string buffer
|
|
||||||
if(5 * (new_capacity / 3) + SIMDJSON_PADDING < SIMDJSON_PADDING) {
|
|
||||||
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
|
|
||||||
}
|
|
||||||
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * (new_capacity / 3) + SIMDJSON_PADDING, 64);
|
|
||||||
string_buf.reset(new (std::nothrow) uint8_t[string_capacity]);
|
string_buf.reset(new (std::nothrow) uint8_t[string_capacity]);
|
||||||
if (auto error = dom_parser.set_capacity(new_capacity)) { return error; }
|
if (auto error = dom_parser.set_capacity(new_capacity)) { return error; }
|
||||||
if (capacity == 0) { // set max depth the first time only
|
if (capacity == 0) { // set max depth the first time only
|
||||||
|
|||||||
@@ -61,29 +61,22 @@ struct yyjson_base {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct yyjson : yyjson_base {
|
struct yyjson : yyjson_base {
|
||||||
// The document owns the string memory that result's string_views point into,
|
|
||||||
// so it must outlive each run() (the verification diff happens after run()
|
|
||||||
// returns). Free it on the next run() / at destruction, not before the views
|
|
||||||
// are read.
|
|
||||||
yyjson_doc *doc{};
|
|
||||||
~yyjson() { if (doc != nullptr) { yyjson_doc_free(doc); } }
|
|
||||||
bool run(simdjson::padded_string &json, std::vector<tweet<std::string_view>> &result) {
|
bool run(simdjson::padded_string &json, std::vector<tweet<std::string_view>> &result) {
|
||||||
if (doc != nullptr) { yyjson_doc_free(doc); doc = nullptr; }
|
yyjson_doc *doc = yyjson_read(json.data(), json.size(), 0);
|
||||||
doc = yyjson_read(json.data(), json.size(), 0);
|
bool b = yyjson_base::run(doc, result);
|
||||||
return yyjson_base::run(doc, result);
|
yyjson_doc_free(doc);
|
||||||
|
return b;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
BENCHMARK_TEMPLATE(partial_tweets, yyjson)->UseManualTime();
|
BENCHMARK_TEMPLATE(partial_tweets, yyjson)->UseManualTime();
|
||||||
|
|
||||||
#if SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
#if SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||||
struct yyjson_insitu : yyjson_base {
|
struct yyjson_insitu : yyjson_base {
|
||||||
// See the note on yyjson above: the document must outlive result's views.
|
|
||||||
yyjson_doc *doc{};
|
|
||||||
~yyjson_insitu() { if (doc != nullptr) { yyjson_doc_free(doc); } }
|
|
||||||
bool run(simdjson::padded_string &json, std::vector<tweet<std::string_view>> &result) {
|
bool run(simdjson::padded_string &json, std::vector<tweet<std::string_view>> &result) {
|
||||||
if (doc != nullptr) { yyjson_doc_free(doc); doc = nullptr; }
|
yyjson_doc *doc = yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0);
|
||||||
doc = yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0);
|
bool b = yyjson_base::run(doc, result);
|
||||||
return yyjson_base::run(doc, result);
|
yyjson_doc_free(doc);
|
||||||
|
return b;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
BENCHMARK_TEMPLATE(partial_tweets, yyjson_insitu)->UseManualTime();
|
BENCHMARK_TEMPLATE(partial_tweets, yyjson_insitu)->UseManualTime();
|
||||||
|
|||||||
+31
-201
@@ -23,15 +23,11 @@ separate document](https://github.com/simdjson/simdjson/blob/master/doc/builder.
|
|||||||
* [2. Use `tag_invoke` for custom types (C++20)](#2-use-tag_invoke-for-custom-types-c20)
|
* [2. Use `tag_invoke` for custom types (C++20)](#2-use-tag_invoke-for-custom-types-c20)
|
||||||
* [3. Using static reflection (C++26)](#3-using-static-reflection-c26)
|
* [3. Using static reflection (C++26)](#3-using-static-reflection-c26)
|
||||||
+ [Special cases](#special-cases)
|
+ [Special cases](#special-cases)
|
||||||
+ [Renaming and skipping fields with annotations](#renaming-and-skipping-fields-with-annotations)
|
|
||||||
* [The simdjson::from shortcut (experimental, C++20)](#the-simdjsonfrom-shortcut-experimental-c20)
|
* [The simdjson::from shortcut (experimental, C++20)](#the-simdjsonfrom-shortcut-experimental-c20)
|
||||||
- [Minifying JSON strings without parsing](#minifying-json-strings-without-parsing)
|
- [Minifying JSON strings without parsing](#minifying-json-strings-without-parsing)
|
||||||
- [UTF-8 validation (alone)](#utf-8-validation-alone)
|
- [UTF-8 validation (alone)](#utf-8-validation-alone)
|
||||||
- [JSON Pointer](#json-pointer)
|
- [JSON Pointer](#json-pointer)
|
||||||
- [JSONPath](#jsonpath)
|
- [JSONPath](#jsonpath)
|
||||||
* [Using `for_each_at_path_with_wildcard` for JSONPath Queries (On-Demand)](#using-for_each_at_path_with_wildcard-for-jsonpath-queries-on-demand)
|
|
||||||
+ [Example Usage](#example-usage)
|
|
||||||
- [C++20 Ranges Support](#c20-ranges-support)
|
|
||||||
- [Compile-Time JSONPath and JSON Pointer (C++26 Reflection)](#compile-time-jsonpath-and-json-pointer-c26-reflection)
|
- [Compile-Time JSONPath and JSON Pointer (C++26 Reflection)](#compile-time-jsonpath-and-json-pointer-c26-reflection)
|
||||||
- [Error handling](#error-handling)
|
- [Error handling](#error-handling)
|
||||||
* [Error handling examples without exceptions](#error-handling-examples-without-exceptions)
|
* [Error handling examples without exceptions](#error-handling-examples-without-exceptions)
|
||||||
@@ -43,7 +39,6 @@ separate document](https://github.com/simdjson/simdjson/blob/master/doc/builder.
|
|||||||
- [Newline-Delimited JSON (ndjson) and JSON lines](#newline-delimited-json-ndjson-and-json-lines)
|
- [Newline-Delimited JSON (ndjson) and JSON lines](#newline-delimited-json-ndjson-and-json-lines)
|
||||||
- [Parsing numbers inside strings](#parsing-numbers-inside-strings)
|
- [Parsing numbers inside strings](#parsing-numbers-inside-strings)
|
||||||
- [Dynamic Number Types](#dynamic-number-types)
|
- [Dynamic Number Types](#dynamic-number-types)
|
||||||
- [Infinity and NaN support](#infinity-and-nan-support)
|
|
||||||
- [Raw strings from keys](#raw-strings-from-keys)
|
- [Raw strings from keys](#raw-strings-from-keys)
|
||||||
- [General direct access to the raw JSON string](#general-direct-access-to-the-raw-json-string)
|
- [General direct access to the raw JSON string](#general-direct-access-to-the-raw-json-string)
|
||||||
* [Raw JSON string for objects and arrays](#raw-json-string-for-objects-and-arrays)
|
* [Raw JSON string for objects and arrays](#raw-json-string-for-objects-and-arrays)
|
||||||
@@ -61,7 +56,7 @@ Requirements
|
|||||||
The simdjson library is widely deployed in popular systems such as the Node.js runtime
|
The simdjson library is widely deployed in popular systems such as the Node.js runtime
|
||||||
environment.
|
environment.
|
||||||
|
|
||||||
- A recent compiler (LLVM clang 6 or better, GNU GCC 7.4 or better, Xcode 11 or better) on POSIX systems such as macOS, FreeBSD or Linux. We require that the compiler supports the C++11 standard or better. We test the library on a big-endian system (IBM s390x with Linux). We support [Fil-C, the memory-safe C/C++ compiler](https://fil-c.org).
|
- A recent compiler (LLVM clang 6 or better, GNU GCC 7.4 or better, Xcode 11 or better) on POSIX systems such as macOS, FreeBSD or Linux. We require that the compiler supports the C++11 standard or better. We test the library on a big-endian system (IBM s390x with Linux).
|
||||||
- Visual Studio 2017 or better. We support the LLVM clang compiler under Visual Studio (clang-cl) as well as the regular Visual Studio compiler. For better release performance (both compile time and execution time), we recommend Visual Studio users adopt LLVM (clang-cl). We discourage using GCC under Windows: there [is a long-running bug with GCC under Windows](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412).
|
- Visual Studio 2017 or better. We support the LLVM clang compiler under Visual Studio (clang-cl) as well as the regular Visual Studio compiler. For better release performance (both compile time and execution time), we recommend Visual Studio users adopt LLVM (clang-cl). We discourage using GCC under Windows: there [is a long-running bug with GCC under Windows](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412).
|
||||||
|
|
||||||
Support for AVX-512 require a processor with AVX512-VBMI2 support (Ice Lake or better, AMD Zen 4 or better) under a 64-bit system and a recent compiler (LLVM clang 6 or better, GCC 8 or better, Visual Studio 2019 or better). You need a correspondingly recent assembler such as gas (2.30+) or nasm (2.14+): recent compilers usually come with recent assemblers. If you mix a recent compiler with an incompatible/old assembler (e.g., when using a recent compiler with an old Linux distribution), you may get errors at build time because the compiler produces instructions that the assembler does not recognize: you should update your assembler to match your compiler (e.g., upgrade binutils to version 2.30 or better under Linux) or use an older compiler matching the capabilities of your assembler.
|
Support for AVX-512 require a processor with AVX512-VBMI2 support (Ice Lake or better, AMD Zen 4 or better) under a 64-bit system and a recent compiler (LLVM clang 6 or better, GCC 8 or better, Visual Studio 2019 or better). You need a correspondingly recent assembler such as gas (2.30+) or nasm (2.14+): recent compilers usually come with recent assemblers. If you mix a recent compiler with an incompatible/old assembler (e.g., when using a recent compiler with an old Linux distribution), you may get errors at build time because the compiler produces instructions that the assembler does not recognize: you should update your assembler to match your compiler (e.g., upgrade binutils to version 2.30 or better under Linux) or use an older compiler matching the capabilities of your assembler.
|
||||||
@@ -457,7 +452,7 @@ We also have a generic ephemeral type (`simdjson::ondemand::value`) which repres
|
|||||||
array or object, or scalar type (`double`, `uint64_t`, `int64_t`, `bool`, `null`, string) inside
|
array or object, or scalar type (`double`, `uint64_t`, `int64_t`, `bool`, `null`, string) inside
|
||||||
an array or an object. Both generic types (`simdjson::ondemand::document` and
|
an array or an object. Both generic types (`simdjson::ondemand::document` and
|
||||||
`simdjson::ondemand::value`) have a `type()` method returning a `json_type` value describing indicating the type (`json_type::array`, `json_type::object`, `json_type::number`, `json_type::string`,
|
`simdjson::ondemand::value`) have a `type()` method returning a `json_type` value describing indicating the type (`json_type::array`, `json_type::object`, `json_type::number`, `json_type::string`,
|
||||||
`json_type::boolean`, `json_type::null`, and `json_type::unknown` for unrecognized types). The `type()` method does not consume nor validate the value: e.g., you must still call `is_null()` to check that the value is a `null` even if `json_type::null` is returned. Starting with simdjson 4.0, we return `json_type::unknown` for bad tokens (such as the `NaN` token in `{"key":NaN}` when using the default strict parsing behavior). A `json_type::unknown` type value indicates an error in the JSON document but you might still be able to proceed, see [General direct access to the raw JSON string](#general-direct-access-to-the-raw-json-string). A generic value (`simdjson::ondemand::value`)
|
`json_type::boolean`, `json_type::null`, and `json_type::unknown` for unrecognized types). The `type()` method does not consume nor validate the value: e.g., you must still call `is_null()` to check that the value is a `null` even if `json_type::null` is returned. Starting with simdjson 4.0, we return `json_type::unknown` for bad tokens such as the `NaN` token in `{"key":NaN}`. A `json_type::unknown` type value indicates an error in the JSON document but you might still be able to proceed, see [General direct access to the raw JSON string](#general-direct-access-to-the-raw-json-string). A generic value (`simdjson::ondemand::value`)
|
||||||
is only valid temporarily, as soon as you access other values, other keys in objects, etc.
|
is only valid temporarily, as soon as you access other values, other keys in objects, etc.
|
||||||
it becomes invalid: you should therefore consume the value immediately by converting it to a
|
it becomes invalid: you should therefore consume the value immediately by converting it to a
|
||||||
scalar type, an array or an object.
|
scalar type, an array or an object.
|
||||||
@@ -1593,51 +1588,6 @@ You can also automatically serialize the `Car` instance to a JSON string, see
|
|||||||
our [Builder documentation](builder.md).
|
our [Builder documentation](builder.md).
|
||||||
|
|
||||||
|
|
||||||
#### Renaming and skipping fields with annotations
|
|
||||||
|
|
||||||
**This is experimental: the syntax may change slightly in the future.**
|
|
||||||
|
|
||||||
C++26 annotations provide a convenient way to customize (de)serialization
|
|
||||||
without writing `tag_invoke` functions. You can rename the JSON key that
|
|
||||||
corresponds to a C++ data member, or skip a member entirely.
|
|
||||||
|
|
||||||
The syntax is:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// rename cppFieldName (in C++) to json_key_name (in JSON)
|
|
||||||
[[= simdjson::rename<"json_key_name">]] std::string cppFieldName;
|
|
||||||
// do not serialize or deserialize this field
|
|
||||||
[[= simdjson::skip]] int internalState;
|
|
||||||
```
|
|
||||||
|
|
||||||
Full examples:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
struct RenamedFields {
|
|
||||||
[[= simdjson::rename<"first_name">]] std::string firstName = "";
|
|
||||||
[[= simdjson::rename<"last_name">]] std::string lastName = "";
|
|
||||||
int age = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SkippedField {
|
|
||||||
std::string name = "";
|
|
||||||
[[= simdjson::skip]] int internalCache = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MixedAnnotations {
|
|
||||||
[[= simdjson::rename<"user_name">]] std::string userName = "";
|
|
||||||
[[= simdjson::skip]] int sessionToken = 0;
|
|
||||||
int age = 0;
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
- Serialization via `simdjson::to_json(r)` or `builder << r` will use the renamed
|
|
||||||
keys and omit skipped fields.
|
|
||||||
- Deserialization via `doc.get<RenamedFields>()` will map the JSON keys back
|
|
||||||
to the C++ fields. Skipped fields are never written during deserialization
|
|
||||||
(they keep their default-initialized value), and keys matching skipped fields
|
|
||||||
in the JSON input are ignored.
|
|
||||||
|
|
||||||
|
|
||||||
### The simdjson::from shortcut (experimental, C++20)
|
### The simdjson::from shortcut (experimental, C++20)
|
||||||
|
|
||||||
@@ -1649,10 +1599,6 @@ type without a document instance like so:
|
|||||||
Car car = simdjson::from(json);
|
Car car = simdjson::from(json);
|
||||||
```
|
```
|
||||||
|
|
||||||
The string must be a `simdjson::padded_string_view`, which can be created from an std::string
|
|
||||||
instance with `simdjson::pad()` function, from a `simdjson::padded_string` instance, or string literal using the `_padded` user-defined literal.
|
|
||||||
|
|
||||||
|
|
||||||
You can also use the `simdjson::from` syntax without exceptions, like so:
|
You can also use the `simdjson::from` syntax without exceptions, like so:
|
||||||
```cpp
|
```cpp
|
||||||
Car car;
|
Car car;
|
||||||
@@ -1893,15 +1839,15 @@ int64_t x = obj.at_path("$.c.foo.a[1]"); // 20
|
|||||||
x = obj.at_path("$.d.foo2.a.2"); // 30
|
x = obj.at_path("$.d.foo2.a.2"); // 30
|
||||||
```
|
```
|
||||||
|
|
||||||
## Using `for_each_at_path_with_wildcard` for JSONPath Queries (On-Demand)
|
## Using `at_path_with_wildcard` for JSONPath Queries (On-Demand)
|
||||||
|
|
||||||
The `for_each_at_path_with_wildcard` function in simdjson extends the JSONPath querying capabilities by supporting wildcard expressions (`*`) in JSON paths. It calls a user-provided callback for each matching element, avoiding the need to materialize all results into a vector. For example, you can use `$.address.*` to fetch all fields within the `address` object or `$.phoneNumbers[*].numbers[*]` to retrieve all phone numbers across multiple objects in an array.
|
The `at_path_with_wildcard` function in simdjson extends the JSONPath querying capabilities by supporting wildcard expressions (`*`) in JSON paths. This allows users to retrieve multiple elements from a JSON document in a single query. For example, you can use `$.address.*` to fetch all fields within the `address` object or `$.phoneNumbers[*].numbers[*]` to retrieve all phone numbers across multiple objects in an array.
|
||||||
|
|
||||||
The `*` wildcard matches all elements at a specific level. For instance, `$.address.*` retrieves all key-value pairs in the `address` object, while `$.*.streetAddress` fetches all `streetAddress` fields across objects at the root level. You can combine wildcards with array indexing. For example, `$.phoneNumbers[*].numbers[1]` retrieves the second number from each `numbers` array in the `phoneNumbers` array. If no elements match the wildcard query, the callback is simply never called. For instance, querying `$.empty_object.*` or `$.empty_array.*` will yield no callbacks.
|
The `*` wildcard matches all elements at a specific level. For instance, `$.address.*` retrieves all key-value pairs in the `address` object, while `$.*.streetAddress` fetches all `streetAddress` fields across objects at the root level. You can combine wildcards with array indexing. For example, `$.phoneNumbers[*].numbers[1]` retrieves the second number from each `numbers` array in the `phoneNumbers` array. If no elements match the wildcard query, the function returns an empty result. For instance, querying `$.empty_object.*` or `$.empty_array.*` will yield an empty set.
|
||||||
|
|
||||||
### Example Usage
|
### Example Usage
|
||||||
|
|
||||||
Here is an example demonstrating the use of `for_each_at_path_with_wildcard`:
|
Here is an example demonstrating the use of `at_path_with_wildcard`:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
simdjson::padded_string json_string = R"(
|
simdjson::padded_string json_string = R"(
|
||||||
@@ -1930,86 +1876,31 @@ ondemand::parser parser;
|
|||||||
ondemand::document doc = parser.iterate(json_string);
|
ondemand::document doc = parser.iterate(json_string);
|
||||||
|
|
||||||
// Fetch all fields in the address object
|
// Fetch all fields in the address object
|
||||||
auto error = doc.for_each_at_path_with_wildcard("$.address.*",
|
std::vector<ondemand::value> values;
|
||||||
[](ondemand::value value) {
|
auto error = doc.at_path_with_wildcard("$.address.*").get(values);
|
||||||
std::string_view field;
|
if (!error) {
|
||||||
if (value.get(field) == SUCCESS) {
|
for (auto value : values) {
|
||||||
std::cout << field << std::endl;
|
std::string_view field;
|
||||||
}
|
if (value.get(field) == SUCCESS) {
|
||||||
});
|
std::cout << field << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch all phone numbers
|
// Fetch all phone numbers
|
||||||
doc.for_each_at_path_with_wildcard("$.phoneNumbers[*].numbers[*]",
|
error = doc.at_path_with_wildcard("$.phoneNumbers[*].numbers[*]").get(values);
|
||||||
[](ondemand::value value) {
|
if (!error) {
|
||||||
std::string_view number;
|
for (auto value : values) {
|
||||||
if (value.get(number) == SUCCESS) {
|
std::string_view number;
|
||||||
std::cout << number << std::endl;
|
if (value.get(number) == SUCCESS) {
|
||||||
}
|
std::cout << number << std::endl;
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This function is particularly useful for extracting data from complex JSON structures with nested arrays and objects. By leveraging wildcards, you can simplify your queries and reduce the need for multiple iterations.
|
This function is particularly useful for extracting data from complex JSON structures with nested arrays and objects. By leveraging wildcards, you can simplify your queries and reduce the need for multiple iterations.
|
||||||
|
|
||||||
## C++20 Ranges Support
|
|
||||||
|
|
||||||
When compiling with C++20 (or later), you can use `std::ranges` with the On-Demand API
|
|
||||||
via the `get_range()` helper. This enables use of range adaptors such as `std::views::transform`.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#include "simdjson.h"
|
|
||||||
#include <ranges>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
auto json = R"([
|
|
||||||
{ "name": "Alice", "age": 30 },
|
|
||||||
{ "name": "Bob", "age": 25 },
|
|
||||||
{ "name": "Carol", "age": 35 }
|
|
||||||
])"_padded;
|
|
||||||
|
|
||||||
ondemand::parser parser;
|
|
||||||
auto doc = parser.iterate(json);
|
|
||||||
auto arr = doc.get_array();
|
|
||||||
|
|
||||||
// Use std::views::transform to extract names
|
|
||||||
auto names = ondemand::get_range(arr)
|
|
||||||
| std::views::transform([](auto elem) -> std::string {
|
|
||||||
return std::string(std::string_view(elem["name"]));
|
|
||||||
});
|
|
||||||
|
|
||||||
for (auto name : names) {
|
|
||||||
std::cout << name << std::endl; // Alice, Bob, Carol
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The `get_range()` and `get_key_value_range()` functions wrap an `ondemand::array`
|
|
||||||
or `ondemand::object` in a `std::ranges::view` that satisfies `std::ranges::input_range`.
|
|
||||||
They work with both exception and non-exception code:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// With exceptions:
|
|
||||||
auto range = ondemand::get_range(doc.get_array());
|
|
||||||
|
|
||||||
// Without exceptions:
|
|
||||||
ondemand::array arr;
|
|
||||||
if (doc.get_array().get(arr) == SUCCESS) {
|
|
||||||
auto range = ondemand::get_range(arr);
|
|
||||||
for (auto elem : range) { /* ... */ }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Object iteration uses `get_key_value_range()` and yields `simdjson_result<ondemand::field>` elements:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
auto obj = doc.get_object();
|
|
||||||
for (auto field_result : ondemand::get_key_value_range(obj)) {
|
|
||||||
std::cout << field_result.key() << std::endl;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The range wrappers are zero-cost: they forward directly to the underlying
|
|
||||||
On-Demand iterators with no value buffering or extra per-element overhead.
|
|
||||||
|
|
||||||
## Compile-Time JSONPath and JSON Pointer (C++26 Reflection)
|
## Compile-Time JSONPath and JSON Pointer (C++26 Reflection)
|
||||||
|
|
||||||
The simdjson library provides **compile-time validated** JSONPath and JSON Pointer accessors when using C++26 Static Reflection. These accessors validate paths against struct definitions at compile time and generate optimized code with zero runtime overhead. In some cases, we find that it is much faster. Furthermore, it is safer in the sense that the expression
|
The simdjson library provides **compile-time validated** JSONPath and JSON Pointer accessors when using C++26 Static Reflection. These accessors validate paths against struct definitions at compile time and generate optimized code with zero runtime overhead. In some cases, we find that it is much faster. Furthermore, it is safer in the sense that the expression
|
||||||
@@ -2694,7 +2585,7 @@ Market: btce Price: 432.89 Volume: 8561.06
|
|||||||
*/
|
*/
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, here is an example dealing with errors where the user wants to convert the string `"Infinity"`(`"change"` key) to a float with infinity value when using the default strict parsing behavior:
|
Finally, here is an example dealing with errors where the user wants to convert the string `"Infinity"`(`"change"` key) to a float with infinity value.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
@@ -2709,7 +2600,7 @@ if (error) {
|
|||||||
error = value.get_string().get(view);
|
error = value.get_string().get(view);
|
||||||
if (error) { /* Handle error */ }
|
if (error) { /* Handle error */ }
|
||||||
else if (view == "Infinity") {
|
else if (view == "Infinity") {
|
||||||
d = std::numeric_limits<double>::infinity();
|
d = std::numeric_limits::infinity();
|
||||||
}
|
}
|
||||||
else { /* Handle wrong value */ }
|
else { /* Handle wrong value */ }
|
||||||
}
|
}
|
||||||
@@ -2717,15 +2608,6 @@ if (error) {
|
|||||||
It is also important to note that when dealing an invalid number inside a string, simdjson will report a `NUMBER_ERROR` error if the string begins with a number whereas simdjson
|
It is also important to note that when dealing an invalid number inside a string, simdjson will report a `NUMBER_ERROR` error if the string begins with a number whereas simdjson
|
||||||
will report an `INCORRECT_TYPE` error otherwise.
|
will report an `INCORRECT_TYPE` error otherwise.
|
||||||
|
|
||||||
When `SIMDJSON_ENABLE_NAN_INF` is enabled, simdjson can parse `"Infinity"`, `"-Infinity"`, and `"NaN"` from a string using `get_double_in_string` without needing extra error handling:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
ondemand::parser parser;
|
|
||||||
auto doc = parser.iterate(json);
|
|
||||||
// Get "change"/"Infinity" key/value pair as a double.
|
|
||||||
double d = doc["ticker"]["change"].get_double_in_string();
|
|
||||||
```
|
|
||||||
|
|
||||||
The `*_in_string` methods can also be called on a single document instance:
|
The `*_in_string` methods can also be called on a single document instance:
|
||||||
e.g., when your document consist solely of a quoted number.
|
e.g., when your document consist solely of a quoted number.
|
||||||
|
|
||||||
@@ -2865,53 +2747,6 @@ This code prints the following:
|
|||||||
'99999999999999999999999 '
|
'99999999999999999999999 '
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Infinity and NaN support
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
The JSON specification does not support `Infinity` and `NaN` literals. However, some engineers use literal `Infinity` and `NaN` tokens when serializing floating-point values to JSON.
|
|
||||||
|
|
||||||
The simdjson library achieves maximum JSON parsing performance by adhering to a strict interpretation of the JSON specification. Therefore strict parsing is enabled by default - `Infinity` and `NaN` literals are not parsed as valid JSON.
|
|
||||||
|
|
||||||
Users can opt-in to parsing `Infinity`, `-Infinity`, and `NaN` as `double` values by setting the `SIMDJSON_ENABLE_NAN_INF` flag to `ON` when building simdjson: `cmake -B build -D SIMDJSON_ENABLE_NAN_INF=ON` and setting `SIMDJSON_ENABLE_NAN_INF` to 1 before including `"simdjson.h"`. When enabled, `Infinity`, `-Infinity`, `Inf`, `-Inf`, and `NaN` literals are case-insensitively matched and parsed as `double`:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// The SIMDJSON_ENABLE_NAN_INF flag also needs to be set before including simdjson.h
|
|
||||||
#define SIMDJSON_ENABLE_NAN_INF 1
|
|
||||||
#include "simdjson.h"
|
|
||||||
using namespace simdjson;
|
|
||||||
|
|
||||||
// ...
|
|
||||||
|
|
||||||
ondemand::parser parser;
|
|
||||||
auto inf_nan_literals = "[Infinity, -Infinity, NaN, inf, -inf, nan]"_padded;
|
|
||||||
ondemand::document doc = parser.iterate(inf_nan_literals);
|
|
||||||
|
|
||||||
// Parse and iterate through the array of literal inf/nan values.
|
|
||||||
for (ondemand::value val: doc.get_array()) {
|
|
||||||
ondemand::number num = val.get_number();
|
|
||||||
ondemand::number_type t = num.get_number_type();
|
|
||||||
|
|
||||||
if (t == ondemand::number_type::floating_point_number) {
|
|
||||||
std::cout << "Parsed floating-point number: " << num.get_double() << std::endl;
|
|
||||||
} else {
|
|
||||||
std::cout << "Failed to parse." << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
produces the following output:
|
|
||||||
|
|
||||||
```
|
|
||||||
Parsed floating-point number: inf
|
|
||||||
Parsed floating-point number: -inf
|
|
||||||
Parsed floating-point number: nan
|
|
||||||
Parsed floating-point number: inf
|
|
||||||
Parsed floating-point number: -inf
|
|
||||||
Parsed floating-point number: nan
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Raw strings from keys
|
Raw strings from keys
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
@@ -3010,7 +2845,7 @@ std::string_view noquote(std::string_view v) { return {v.data()+1, v.find_last_o
|
|||||||
|
|
||||||
|
|
||||||
The `raw_json_token()` method can enable you to provide fallbacks when parsing fails.
|
The `raw_json_token()` method can enable you to provide fallbacks when parsing fails.
|
||||||
Consider the following example under the default strict parsing behavior (`NaN` parsing is disabled):
|
Consider the following example.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
padded_string json = "{\"key\": NaN}"_padded;
|
padded_string json = "{\"key\": NaN}"_padded;
|
||||||
@@ -3032,9 +2867,6 @@ Consider the following example under the default strict parsing behavior (`NaN`
|
|||||||
The NaN is not supported in JSON. However, in the On-Demand API, you can check
|
The NaN is not supported in JSON. However, in the On-Demand API, you can check
|
||||||
the string corresponding to the JSON token and determine how to handle it.
|
the string corresponding to the JSON token and determine how to handle it.
|
||||||
|
|
||||||
In this particular case, `SIMDJSON_ENABLE_NAN_INF` can be enabled to parse `Infinity` and `NaN` tokens.
|
|
||||||
However other tokens will still need to be handled via the `raw_json_token()` method.
|
|
||||||
|
|
||||||
### Raw JSON string for objects and arrays
|
### Raw JSON string for objects and arrays
|
||||||
|
|
||||||
If your value is an array or an object, `raw_json_token()` returns effectively a single
|
If your value is an array or an object, `raw_json_token()` returns effectively a single
|
||||||
@@ -3058,10 +2890,8 @@ simdjson::ondemand::array arr = doc.get_array();
|
|||||||
string_view token = arr.raw_json(); // gives you `[1,2,3]`
|
string_view token = arr.raw_json(); // gives you `[1,2,3]`
|
||||||
```
|
```
|
||||||
|
|
||||||
Because `raw_json()` consumes the object or the array, if you want both the raw
|
Because `raw_json()` consumes to object or the array, if you want to both have
|
||||||
substring and later field access on the same instance, call `reset()` on that
|
access to the raw string, and also use the array or object, you should call `reset()`.
|
||||||
object or array (as below). To re-parse the whole document from the start,
|
|
||||||
use `document::rewind()` instead (see [Rewinding](#rewinding)).
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
simdjson::ondemand::parser parser;
|
simdjson::ondemand::parser parser;
|
||||||
@@ -3069,7 +2899,7 @@ simdjson::padded_string docdata = R"({"value":123})"_padded;
|
|||||||
simdjson::ondemand::document doc = parser.iterate(docdata);
|
simdjson::ondemand::document doc = parser.iterate(docdata);
|
||||||
simdjson::ondemand::object obj = doc.get_object();
|
simdjson::ondemand::object obj = doc.get_object();
|
||||||
string_view token = obj.raw_json(); // gives you `{"value":123}`
|
string_view token = obj.raw_json(); // gives you `{"value":123}`
|
||||||
obj.reset(); // re-open the same object for further iteration
|
obj.reset(); // revise the object
|
||||||
uint64_t x = obj["value"]; // gives me 123
|
uint64_t x = obj["value"]; // gives me 123
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ speed and high convenience.
|
|||||||
* [Overview: string_builder](#overview--string-builder)
|
* [Overview: string_builder](#overview--string-builder)
|
||||||
* [Example: string_builder](#example--string-builder)
|
* [Example: string_builder](#example--string-builder)
|
||||||
* [C++26 static reflection](#c--26-static-reflection)
|
* [C++26 static reflection](#c--26-static-reflection)
|
||||||
+ [Renaming and skipping fields with annotations](#renaming-and-skipping-fields-with-annotations)
|
|
||||||
+ [Without `string_buffer` instance](#without--string-buffer--instance)
|
+ [Without `string_buffer` instance](#without--string-buffer--instance)
|
||||||
+ [Without `string_buffer` instance but with explicit error handling](#without--string-buffer--instance-but-with-explicit-error-handling)
|
+ [Without `string_buffer` instance but with explicit error handling](#without--string-buffer--instance-but-with-explicit-error-handling)
|
||||||
+ [Pretty formatted (fractured JSON)](#pretty-formatted-fractured-json)
|
+ [Pretty formatted (fractured JSON)](#pretty-formatted-fractured-json)
|
||||||
@@ -261,48 +260,6 @@ automatically. In most cases, it should work automatically:
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
#### Renaming and skipping fields with annotations
|
|
||||||
|
|
||||||
|
|
||||||
**This is experimental: the syntax may change slightly in the future.**
|
|
||||||
|
|
||||||
When using C++26 static reflection for automatic serialization (and deserialization),
|
|
||||||
you can annotate your struct members to rename the corresponding JSON keys or to
|
|
||||||
exclude fields from the JSON representation.
|
|
||||||
|
|
||||||
|
|
||||||
The syntax is:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// rename cppFieldName (in C++) to json_key_name (in JSON)
|
|
||||||
[[= simdjson::rename<"json_key_name">]] std::string cppFieldName;
|
|
||||||
// do not serialize or deserialize this field
|
|
||||||
[[= simdjson::skip]] int internalState;
|
|
||||||
```
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
struct Person {
|
|
||||||
[[= simdjson::rename<"first_name">]] std::string firstName = "";
|
|
||||||
[[= simdjson::rename<"last_name">]] std::string lastName = "";
|
|
||||||
[[= simdjson::skip]] int internalCache = 0;
|
|
||||||
int age = 0;
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Serialization then produces:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
Person p{"Alice", "Smith", 999, 30};
|
|
||||||
std::string json = simdjson::to_json(p);
|
|
||||||
// json == R"({"first_name":"Alice","last_name":"Smith","age":30})"
|
|
||||||
// Note: internalCache is omitted entirely.
|
|
||||||
```
|
|
||||||
|
|
||||||
The `skip` annotation also affects deserialization: the field keeps its default value
|
|
||||||
and any corresponding key in the input JSON is ignored.
|
|
||||||
|
|
||||||
### Without `string_buffer` instance
|
### Without `string_buffer` instance
|
||||||
|
|
||||||
In some instances, you might want to create a string directly from your own data type.
|
In some instances, you might want to create a string directly from your own data type.
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
#ifndef SIMDJSON_ANNOTATIONS_H
|
|
||||||
#define SIMDJSON_ANNOTATIONS_H
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file annotations.h
|
|
||||||
* @brief Provides compile-time annotations for simdjson structures.
|
|
||||||
* This header defines annotations that can be applied to data members of structures
|
|
||||||
* to control how they are serialized/deserialized with simdjson.
|
|
||||||
* This is currently experimental and subject to change (syntax and semantics may evolve).
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if SIMDJSON_STATIC_REFLECTION
|
|
||||||
|
|
||||||
#include <meta>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
namespace simdjson {
|
|
||||||
|
|
||||||
// Structural compile-time string — char array avoids the pointer-based
|
|
||||||
// 'reflect_constant failed' that occurs with const char* / string_view members.
|
|
||||||
template <size_t N>
|
|
||||||
struct fixed_string {
|
|
||||||
char data[N];
|
|
||||||
|
|
||||||
consteval fixed_string(const char (&s)[N]) noexcept {
|
|
||||||
for (size_t i = 0; i < N; ++i) { data[i] = s[i]; }
|
|
||||||
}
|
|
||||||
|
|
||||||
consteval std::string_view view() const noexcept { return {data, N - 1}; }
|
|
||||||
|
|
||||||
consteval bool operator==(const fixed_string&) const noexcept = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Usage: [[= simdjson::rename<"first_name">]] std::string firstName;
|
|
||||||
namespace detail {
|
|
||||||
template <fixed_string Name>
|
|
||||||
struct rename_t {
|
|
||||||
static constexpr auto name = Name;
|
|
||||||
};
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
template <fixed_string Name>
|
|
||||||
inline constexpr detail::rename_t<Name> rename{};
|
|
||||||
|
|
||||||
// Usage: [[= simdjson::skip]] int internalCache;
|
|
||||||
namespace detail {
|
|
||||||
struct skip_tag {};
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
inline constexpr detail::skip_tag skip{};
|
|
||||||
|
|
||||||
// Returns the JSON key for a reflected data member.
|
|
||||||
template <auto dm>
|
|
||||||
consteval const char* get_json_key_name() {
|
|
||||||
template for (constexpr auto ann :
|
|
||||||
std::define_static_array(std::meta::annotations_of(dm))) {
|
|
||||||
constexpr auto ann_type = std::meta::type_of(ann);
|
|
||||||
if constexpr (std::meta::has_template_arguments(ann_type) &&
|
|
||||||
std::meta::template_of(ann_type) == ^^detail::rename_t) {
|
|
||||||
constexpr auto args =
|
|
||||||
std::define_static_array(std::meta::template_arguments_of(ann_type));
|
|
||||||
return std::define_static_string([:args[0]:].view());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return std::define_static_string(std::meta::identifier_of(dm));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace simdjson
|
|
||||||
|
|
||||||
#endif // SIMDJSON_STATIC_REFLECTION
|
|
||||||
#endif // SIMDJSON_ANNOTATIONS_H
|
|
||||||
@@ -30,10 +30,6 @@ double from_chars(const char *first, const char* end) noexcept;
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef SIMDJSON_ENABLE_NAN_INF
|
|
||||||
#define SIMDJSON_ENABLE_NAN_INF 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|
||||||
#if defined(__GNUC__)
|
#if defined(__GNUC__)
|
||||||
@@ -49,6 +45,9 @@ double from_chars(const char *first, const char* end) noexcept;
|
|||||||
|
|
||||||
// Align to N-byte boundary
|
// Align to N-byte boundary
|
||||||
#define SIMDJSON_ROUNDUP_N(a, n) (((a) + ((n)-1)) & ~((n)-1))
|
#define SIMDJSON_ROUNDUP_N(a, n) (((a) + ((n)-1)) & ~((n)-1))
|
||||||
|
#define SIMDJSON_ROUNDDOWN_N(a, n) ((a) & ~((n)-1))
|
||||||
|
|
||||||
|
#define SIMDJSON_ISALIGNED_N(ptr, n) (((uintptr_t)(ptr) & ((n)-1)) == 0)
|
||||||
|
|
||||||
#if SIMDJSON_REGULAR_VISUAL_STUDIO
|
#if SIMDJSON_REGULAR_VISUAL_STUDIO
|
||||||
// We could use [[deprecated]] but it requires C++14
|
// We could use [[deprecated]] but it requires C++14
|
||||||
@@ -56,7 +55,6 @@ double from_chars(const char *first, const char* end) noexcept;
|
|||||||
|
|
||||||
#define simdjson_really_inline __forceinline
|
#define simdjson_really_inline __forceinline
|
||||||
#define simdjson_never_inline __declspec(noinline)
|
#define simdjson_never_inline __declspec(noinline)
|
||||||
#define simdjson_really_flatten [[msvc::flatten]]
|
|
||||||
|
|
||||||
#define simdjson_unused
|
#define simdjson_unused
|
||||||
#define simdjson_warn_unused
|
#define simdjson_warn_unused
|
||||||
@@ -97,7 +95,6 @@ double from_chars(const char *first, const char* end) noexcept;
|
|||||||
|
|
||||||
#define simdjson_really_inline inline __attribute__((always_inline))
|
#define simdjson_really_inline inline __attribute__((always_inline))
|
||||||
#define simdjson_never_inline inline __attribute__((noinline))
|
#define simdjson_never_inline inline __attribute__((noinline))
|
||||||
#define simdjson_really_flatten [[gnu::flatten]]
|
|
||||||
|
|
||||||
#define simdjson_unused __attribute__((unused))
|
#define simdjson_unused __attribute__((unused))
|
||||||
#define simdjson_warn_unused __attribute__((warn_unused_result))
|
#define simdjson_warn_unused __attribute__((warn_unused_result))
|
||||||
@@ -174,15 +171,6 @@ double from_chars(const char *first, const char* end) noexcept;
|
|||||||
#define simdjson_inline simdjson_really_inline
|
#define simdjson_inline simdjson_really_inline
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(simdjson_flatten)
|
|
||||||
// Prefer the user's definition of simdjson_flatten; don't define it ourselves.
|
|
||||||
#elif (defined(__GNUC__) && !defined(__OPTIMIZE__)) || (defined(_DEBUG) && _MSC_VER )
|
|
||||||
// Flattening can lead to significant code bloat and high compile times. Don't use it for unoptimized builds.
|
|
||||||
#define simdjson_flatten
|
|
||||||
#else
|
|
||||||
#define simdjson_flatten simdjson_really_flatten
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if SIMDJSON_VISUAL_STUDIO
|
#if SIMDJSON_VISUAL_STUDIO
|
||||||
/**
|
/**
|
||||||
* Windows users need to do some extra work when building
|
* Windows users need to do some extra work when building
|
||||||
|
|||||||
@@ -59,6 +59,14 @@ concept appendable_containers =
|
|||||||
details::supports_add<T> || details::supports_append<T> ||
|
details::supports_add<T> || details::supports_append<T> ||
|
||||||
details::supports_insert<T>) && !string_view_keyed_map<T>;
|
details::supports_insert<T>) && !string_view_keyed_map<T>;
|
||||||
|
|
||||||
|
/// Check if T is a key_selector type for efficient JSON field lookup
|
||||||
|
template <typename T>
|
||||||
|
concept key_selector_type = requires(T selector) {
|
||||||
|
{ selector.size() } -> std::same_as<std::size_t>;
|
||||||
|
{ selector.index_of(std::string_view{}) } -> std::same_as<std::size_t>;
|
||||||
|
{ selector.get_key(std::size_t{}) } -> std::same_as<std::string_view>;
|
||||||
|
};
|
||||||
|
|
||||||
/// Insert into the container however possible
|
/// Insert into the container however possible
|
||||||
template <appendable_containers T, typename... Args>
|
template <appendable_containers T, typename... Args>
|
||||||
constexpr decltype(auto) emplace_one(T &vec, Args &&...args) {
|
constexpr decltype(auto) emplace_one(T &vec, Args &&...args) {
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ inline auto_parser<parser_type>::auto_parser(parser_type parser, ondemand::docum
|
|||||||
: auto_parser{*parser, std::move(doc)} {}
|
: auto_parser{*parser, std::move(doc)} {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template <typename parser_type>
|
template <typename parser_type>
|
||||||
inline std::remove_pointer_t<parser_type> &auto_parser<parser_type>::parser() noexcept {
|
inline std::remove_pointer_t<parser_type> &auto_parser<parser_type>::parser() noexcept {
|
||||||
if constexpr (std::is_pointer_v<parser_type>) {
|
if constexpr (std::is_pointer_v<parser_type>) {
|
||||||
@@ -115,6 +118,16 @@ template <typename T>
|
|||||||
inline auto to_adaptor<T>::operator()(ondemand::parser &parser, padded_string_view const str) const noexcept {
|
inline auto to_adaptor<T>::operator()(ondemand::parser &parser, padded_string_view const str) const noexcept {
|
||||||
return auto_parser<ondemand::parser *>{parser, str};
|
return auto_parser<ondemand::parser *>{parser, str};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline auto to_adaptor<T>::operator()(std::string str) const noexcept {
|
||||||
|
return auto_parser<ondemand::parser *>{pad_with_reserve(str)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline auto to_adaptor<T>::operator()(ondemand::parser &parser, std::string str) const noexcept {
|
||||||
|
return auto_parser<ondemand::parser *>{parser, pad_with_reserve(str)};
|
||||||
|
}
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace convert
|
} // namespace convert
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|||||||
+12
-12
@@ -36,12 +36,12 @@ public:
|
|||||||
explicit auto_parser(std::remove_pointer_t<parser_type> &parser, padded_string_view const str) noexcept requires(std::is_pointer_v<parser_type>);
|
explicit auto_parser(std::remove_pointer_t<parser_type> &parser, padded_string_view const str) noexcept requires(std::is_pointer_v<parser_type>);
|
||||||
explicit auto_parser(padded_string_view const str) noexcept requires(std::is_pointer_v<parser_type>);
|
explicit auto_parser(padded_string_view const str) noexcept requires(std::is_pointer_v<parser_type>);
|
||||||
explicit auto_parser(parser_type parser, ondemand::document &&doc) noexcept requires(std::is_pointer_v<parser_type>);
|
explicit auto_parser(parser_type parser, ondemand::document &&doc) noexcept requires(std::is_pointer_v<parser_type>);
|
||||||
auto_parser(auto_parser const &) = delete;
|
auto_parser(auto_parser const &) = delete;
|
||||||
auto_parser &operator=(auto_parser const &) = delete;
|
auto_parser &operator=(auto_parser const &) = delete;
|
||||||
~auto_parser() = default;
|
auto_parser(auto_parser &&) noexcept = default;
|
||||||
// Prevent moving
|
auto_parser &operator=(auto_parser &&) noexcept = default;
|
||||||
auto_parser(auto_parser&&) = delete;
|
~auto_parser() = default;
|
||||||
auto_parser &operator=(auto_parser &&) noexcept = delete;
|
|
||||||
simdjson_warn_unused std::remove_pointer_t<parser_type> &parser() noexcept;
|
simdjson_warn_unused std::remove_pointer_t<parser_type> &parser() noexcept;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -75,6 +75,11 @@ struct to_adaptor {
|
|||||||
T operator()(simdjson_result<ondemand::value> &val) const noexcept;
|
T operator()(simdjson_result<ondemand::value> &val) const noexcept;
|
||||||
auto operator()(padded_string_view const str) const noexcept;
|
auto operator()(padded_string_view const str) const noexcept;
|
||||||
auto operator()(ondemand::parser &parser, padded_string_view const str) const noexcept;
|
auto operator()(ondemand::parser &parser, padded_string_view const str) const noexcept;
|
||||||
|
// The std::string is padded with reserve to ensure there is enough space for padding.
|
||||||
|
// Some sanitizers may not like this, so you can use simdjson::pad instead.
|
||||||
|
// simdjson::from(simdjson::pad(str))
|
||||||
|
auto operator()(std::string str) const noexcept;
|
||||||
|
auto operator()(ondemand::parser &parser, std::string str) const noexcept;
|
||||||
};
|
};
|
||||||
// deduction guide
|
// deduction guide
|
||||||
auto_parser(padded_string_view const str) -> auto_parser<ondemand::parser*>;
|
auto_parser(padded_string_view const str) -> auto_parser<ondemand::parser*>;
|
||||||
@@ -85,12 +90,7 @@ auto_parser(padded_string_view const str) -> auto_parser<ondemand::parser*>;
|
|||||||
* The simdjson::from instance is EXPERIMENTAL AND SUBJECT TO CHANGES.
|
* The simdjson::from instance is EXPERIMENTAL AND SUBJECT TO CHANGES.
|
||||||
*
|
*
|
||||||
* The `from` instance is a utility adaptor for parsing JSON strings into objects.
|
* The `from` instance is a utility adaptor for parsing JSON strings into objects.
|
||||||
*
|
* It provides a convenient way to convert JSON data into C++ objects using the `auto_parser`.
|
||||||
* The string must be a simdjson::padded_string_view, which can be created from a std::string
|
|
||||||
* with simdjson::pad(), from a simdjson::padded_string, or string literal using the `_padded`
|
|
||||||
* user-defined literal.
|
|
||||||
*
|
|
||||||
* The `from` instance provides a convenient way to convert JSON data into C++ objects using the `auto_parser`.
|
|
||||||
*
|
*
|
||||||
* Example usage:
|
* Example usage:
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -103,9 +103,21 @@ inline simdjson_result<element> array::at_pointer(std::string_view json_pointer)
|
|||||||
// We don't support this, because we're returning a real element, not a position.
|
// We don't support this, because we're returning a real element, not a position.
|
||||||
if (json_pointer == "-") { return INDEX_OUT_OF_BOUNDS; }
|
if (json_pointer == "-") { return INDEX_OUT_OF_BOUNDS; }
|
||||||
|
|
||||||
|
// Read the array index
|
||||||
size_t array_index = 0;
|
size_t array_index = 0;
|
||||||
size_t i;
|
size_t i;
|
||||||
SIMDJSON_TRY(internal::parse_json_pointer_array_index(json_pointer, array_index, i));
|
for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) {
|
||||||
|
uint8_t digit = uint8_t(json_pointer[i] - '0');
|
||||||
|
// Check for non-digit in array index. If it's there, we're trying to get a field in an object
|
||||||
|
if (digit > 9) { return INCORRECT_TYPE; }
|
||||||
|
array_index = array_index*10 + digit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0 followed by other digits is invalid
|
||||||
|
if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0"
|
||||||
|
|
||||||
|
// Empty string is invalid; so is a "/" with no digits before it
|
||||||
|
if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index"
|
||||||
|
|
||||||
// Get the child
|
// Get the child
|
||||||
auto child = array(tape).at(array_index);
|
auto child = array(tape).at(array_index);
|
||||||
|
|||||||
@@ -42,16 +42,10 @@ inline error_code document::allocate(size_t capacity) noexcept {
|
|||||||
// worse with "[7,7,7,7,6,7,7,7,6,7,7,6,[7,7,7,7,6,7,7,7,6,7,7,6,7,7,7,7,7,7,6"
|
// worse with "[7,7,7,7,6,7,7,7,6,7,7,6,[7,7,7,7,6,7,7,7,6,7,7,6,7,7,7,7,7,7,6"
|
||||||
//where capacity + 1 tape elements are
|
//where capacity + 1 tape elements are
|
||||||
// generated, see issue https://github.com/simdjson/simdjson/issues/345
|
// generated, see issue https://github.com/simdjson/simdjson/issues/345
|
||||||
if(capacity + 3 < capacity) {
|
|
||||||
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
|
|
||||||
}
|
|
||||||
size_t tape_capacity = SIMDJSON_ROUNDUP_N(capacity + 3, 64);
|
size_t tape_capacity = SIMDJSON_ROUNDUP_N(capacity + 3, 64);
|
||||||
// a document with only zero-length strings... could have capacity/3 string
|
// a document with only zero-length strings... could have capacity/3 string
|
||||||
// and we would need capacity/3 * 5 bytes on the string buffer
|
// and we would need capacity/3 * 5 bytes on the string buffer
|
||||||
if(5 * (capacity / 3) + SIMDJSON_PADDING < SIMDJSON_PADDING) {
|
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * capacity / 3 + SIMDJSON_PADDING, 64);
|
||||||
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
|
|
||||||
}
|
|
||||||
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * (capacity / 3) + SIMDJSON_PADDING, 64);
|
|
||||||
string_buf.reset( new (std::nothrow) uint8_t[string_capacity]);
|
string_buf.reset( new (std::nothrow) uint8_t[string_capacity]);
|
||||||
tape.reset(new (std::nothrow) uint64_t[tape_capacity]);
|
tape.reset(new (std::nothrow) uint64_t[tape_capacity]);
|
||||||
if(!(string_buf && tape)) {
|
if(!(string_buf && tape)) {
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
|
|||||||
// cannot legally appear in a JSON value at the source level (control
|
// cannot legally appear in a JSON value at the source level (control
|
||||||
// characters in strings must be escaped as \u001E), so stripping it is
|
// characters in strings must be escaped as \u001E), so stripping it is
|
||||||
// safe in every stream_format.
|
// safe in every stream_format.
|
||||||
while(svlen > 1 && (std::isspace(static_cast<unsigned char>(start[svlen-1])) || start[svlen-1] == '\0' || static_cast<uint8_t>(start[svlen-1]) == 0x1E || (stream->format == stream_format::comma_delimited && start[svlen-1] == ','))) {
|
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);
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ inline element_metrics structure_analyzer::analyze_scalar(const dom::element& el
|
|||||||
metrics.complexity = 0;
|
metrics.complexity = 0;
|
||||||
metrics.child_count = 0;
|
metrics.child_count = 0;
|
||||||
metrics.can_inline = true;
|
metrics.can_inline = true;
|
||||||
metrics.recommended_layout = layout_mode::single_line;
|
metrics.recommended_layout = layout_mode::INLINE;
|
||||||
|
|
||||||
switch (elem.type()) {
|
switch (elem.type()) {
|
||||||
case dom::element_type::STRING: {
|
case dom::element_type::STRING: {
|
||||||
@@ -158,16 +158,16 @@ inline element_metrics structure_analyzer::analyze_array(const dom::array& arr,
|
|||||||
|
|
||||||
// Decide layout
|
// Decide layout
|
||||||
if (metrics.child_count == 0) {
|
if (metrics.child_count == 0) {
|
||||||
metrics.recommended_layout = layout_mode::single_line;
|
metrics.recommended_layout = layout_mode::INLINE;
|
||||||
} else if (metrics.can_inline) {
|
} else if (metrics.can_inline) {
|
||||||
metrics.recommended_layout = layout_mode::single_line;
|
metrics.recommended_layout = layout_mode::INLINE;
|
||||||
} else if (metrics.is_uniform_array && !metrics.common_keys.empty()) {
|
} else if (metrics.is_uniform_array && !metrics.common_keys.empty()) {
|
||||||
metrics.recommended_layout = layout_mode::table;
|
metrics.recommended_layout = layout_mode::TABLE;
|
||||||
} else if (current_opts_->enable_compact_multiline &&
|
} else if (current_opts_->enable_compact_multiline &&
|
||||||
max_child_complexity <= current_opts_->max_compact_array_complexity) {
|
max_child_complexity <= current_opts_->max_compact_array_complexity) {
|
||||||
metrics.recommended_layout = layout_mode::compact_multiline;
|
metrics.recommended_layout = layout_mode::COMPACT_MULTILINE;
|
||||||
} else {
|
} else {
|
||||||
metrics.recommended_layout = layout_mode::expanded;
|
metrics.recommended_layout = layout_mode::EXPANDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
return metrics;
|
return metrics;
|
||||||
@@ -206,9 +206,9 @@ inline element_metrics structure_analyzer::analyze_object(const dom::object& obj
|
|||||||
|
|
||||||
// Objects use inline or expanded (no table/compact for objects)
|
// Objects use inline or expanded (no table/compact for objects)
|
||||||
if (metrics.child_count == 0 || metrics.can_inline) {
|
if (metrics.child_count == 0 || metrics.can_inline) {
|
||||||
metrics.recommended_layout = layout_mode::single_line;
|
metrics.recommended_layout = layout_mode::INLINE;
|
||||||
} else {
|
} else {
|
||||||
metrics.recommended_layout = layout_mode::expanded;
|
metrics.recommended_layout = layout_mode::EXPANDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
return metrics;
|
return metrics;
|
||||||
@@ -227,18 +227,8 @@ inline size_t structure_analyzer::estimate_string_length(std::string_view s) con
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline size_t structure_analyzer::estimate_number_length(double d) const {
|
inline size_t structure_analyzer::estimate_number_length(double d) const {
|
||||||
if (!std::isfinite(d)) {
|
if (std::isnan(d) || std::isinf(d)) {
|
||||||
#if SIMDJSON_ENABLE_NAN_INF
|
|
||||||
if (std::isnan(d)) {
|
|
||||||
return 3; // "NaN"
|
|
||||||
} else if (d < 0) {
|
|
||||||
return 9; // "-Infinity"
|
|
||||||
} else {
|
|
||||||
return 8; // "Infinity"
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
return 4; // "null" for invalid numbers
|
return 4; // "null" for invalid numbers
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
// Rough estimate: up to 17 significant digits + sign + decimal point + exponent
|
// Rough estimate: up to 17 significant digits + sign + decimal point + exponent
|
||||||
char buf[32];
|
char buf[32];
|
||||||
@@ -356,28 +346,28 @@ inline layout_mode structure_analyzer::decide_layout(const element_metrics& metr
|
|||||||
size_t depth,
|
size_t depth,
|
||||||
size_t available_width) const {
|
size_t available_width) const {
|
||||||
if (metrics.child_count == 0) {
|
if (metrics.child_count == 0) {
|
||||||
return layout_mode::single_line;
|
return layout_mode::INLINE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check inline feasibility
|
// Check inline feasibility
|
||||||
size_t indent_width = depth * current_opts_->indent_spaces;
|
size_t indent_width = depth * current_opts_->indent_spaces;
|
||||||
if (metrics.can_inline &&
|
if (metrics.can_inline &&
|
||||||
metrics.estimated_inline_len + indent_width <= available_width) {
|
metrics.estimated_inline_len + indent_width <= available_width) {
|
||||||
return layout_mode::single_line;
|
return layout_mode::INLINE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check table mode
|
// Check table mode
|
||||||
if (metrics.is_uniform_array && !metrics.common_keys.empty()) {
|
if (metrics.is_uniform_array && !metrics.common_keys.empty()) {
|
||||||
return layout_mode::table;
|
return layout_mode::TABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check compact multiline
|
// Check compact multiline
|
||||||
if (current_opts_->enable_compact_multiline &&
|
if (current_opts_->enable_compact_multiline &&
|
||||||
metrics.complexity <= current_opts_->max_compact_array_complexity + 1) {
|
metrics.complexity <= current_opts_->max_compact_array_complexity + 1) {
|
||||||
return layout_mode::compact_multiline;
|
return layout_mode::COMPACT_MULTILINE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return layout_mode::expanded;
|
return layout_mode::EXPANDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -388,7 +378,7 @@ inline fractured_formatter::fractured_formatter(const fractured_json_options& op
|
|||||||
: options_(opts), column_widths_{} {}
|
: options_(opts), column_widths_{} {}
|
||||||
|
|
||||||
simdjson_inline void fractured_formatter::print_newline() {
|
simdjson_inline void fractured_formatter::print_newline() {
|
||||||
if (current_layout_ == layout_mode::single_line) {
|
if (current_layout_ == layout_mode::INLINE) {
|
||||||
return; // No newlines in inline mode
|
return; // No newlines in inline mode
|
||||||
}
|
}
|
||||||
one_char('\n');
|
one_char('\n');
|
||||||
@@ -396,7 +386,7 @@ simdjson_inline void fractured_formatter::print_newline() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
simdjson_inline void fractured_formatter::print_indents(size_t depth) {
|
simdjson_inline void fractured_formatter::print_indents(size_t depth) {
|
||||||
if (current_layout_ == layout_mode::single_line) {
|
if (current_layout_ == layout_mode::INLINE) {
|
||||||
return; // No indentation in inline mode
|
return; // No indentation in inline mode
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < depth * options_.indent_spaces; i++) {
|
for (size_t i = 0; i < depth * options_.indent_spaces; i++) {
|
||||||
@@ -543,16 +533,16 @@ inline void fractured_string_builder::format_array(const dom::array& arr,
|
|||||||
const element_metrics& metrics,
|
const element_metrics& metrics,
|
||||||
size_t depth) {
|
size_t depth) {
|
||||||
switch (metrics.recommended_layout) {
|
switch (metrics.recommended_layout) {
|
||||||
case layout_mode::single_line:
|
case layout_mode::INLINE:
|
||||||
format_array_inline(arr, metrics);
|
format_array_inline(arr, metrics);
|
||||||
break;
|
break;
|
||||||
case layout_mode::compact_multiline:
|
case layout_mode::COMPACT_MULTILINE:
|
||||||
format_array_compact_multiline(arr, metrics, depth);
|
format_array_compact_multiline(arr, metrics, depth);
|
||||||
break;
|
break;
|
||||||
case layout_mode::table:
|
case layout_mode::TABLE:
|
||||||
format_array_as_table(arr, metrics, depth);
|
format_array_as_table(arr, metrics, depth);
|
||||||
break;
|
break;
|
||||||
case layout_mode::expanded:
|
case layout_mode::EXPANDED:
|
||||||
default:
|
default:
|
||||||
format_array_expanded(arr, metrics, depth);
|
format_array_expanded(arr, metrics, depth);
|
||||||
break;
|
break;
|
||||||
@@ -562,7 +552,7 @@ inline void fractured_string_builder::format_array(const dom::array& arr,
|
|||||||
inline void fractured_string_builder::format_array_inline(const dom::array& arr,
|
inline void fractured_string_builder::format_array_inline(const dom::array& arr,
|
||||||
const element_metrics& metrics) {
|
const element_metrics& metrics) {
|
||||||
layout_mode prev_layout = format_.get_layout_mode();
|
layout_mode prev_layout = format_.get_layout_mode();
|
||||||
format_.set_layout_mode(layout_mode::single_line);
|
format_.set_layout_mode(layout_mode::INLINE);
|
||||||
|
|
||||||
format_.start_array();
|
format_.start_array();
|
||||||
|
|
||||||
@@ -623,7 +613,7 @@ inline void fractured_string_builder::format_array_compact_multiline(const dom::
|
|||||||
|
|
||||||
// Format element inline
|
// Format element inline
|
||||||
layout_mode prev_layout = format_.get_layout_mode();
|
layout_mode prev_layout = format_.get_layout_mode();
|
||||||
format_.set_layout_mode(layout_mode::single_line);
|
format_.set_layout_mode(layout_mode::INLINE);
|
||||||
const element_metrics& child_metrics = (child_idx < metrics.children.size())
|
const element_metrics& child_metrics = (child_idx < metrics.children.size())
|
||||||
? metrics.children[child_idx] : element_metrics{};
|
? metrics.children[child_idx] : element_metrics{};
|
||||||
format_element(elem, child_metrics, depth + 1);
|
format_element(elem, child_metrics, depth + 1);
|
||||||
@@ -719,7 +709,7 @@ inline void fractured_string_builder::format_array_as_table(const dom::array& ar
|
|||||||
// Write value
|
// Write value
|
||||||
if (found) {
|
if (found) {
|
||||||
layout_mode prev_layout = format_.get_layout_mode();
|
layout_mode prev_layout = format_.get_layout_mode();
|
||||||
format_.set_layout_mode(layout_mode::single_line);
|
format_.set_layout_mode(layout_mode::INLINE);
|
||||||
const element_metrics& value_metrics = (field_idx < row_metrics.children.size())
|
const element_metrics& value_metrics = (field_idx < row_metrics.children.size())
|
||||||
? row_metrics.children[field_idx] : element_metrics{};
|
? row_metrics.children[field_idx] : element_metrics{};
|
||||||
format_element(value, value_metrics, depth + 1);
|
format_element(value, value_metrics, depth + 1);
|
||||||
@@ -788,7 +778,7 @@ inline void fractured_string_builder::format_array_expanded(const dom::array& ar
|
|||||||
inline void fractured_string_builder::format_object(const dom::object& obj,
|
inline void fractured_string_builder::format_object(const dom::object& obj,
|
||||||
const element_metrics& metrics,
|
const element_metrics& metrics,
|
||||||
size_t depth) {
|
size_t depth) {
|
||||||
if (metrics.recommended_layout == layout_mode::single_line || metrics.can_inline) {
|
if (metrics.recommended_layout == layout_mode::INLINE || metrics.can_inline) {
|
||||||
format_object_inline(obj, metrics);
|
format_object_inline(obj, metrics);
|
||||||
} else {
|
} else {
|
||||||
format_object_expanded(obj, metrics, depth);
|
format_object_expanded(obj, metrics, depth);
|
||||||
@@ -798,7 +788,7 @@ inline void fractured_string_builder::format_object(const dom::object& obj,
|
|||||||
inline void fractured_string_builder::format_object_inline(const dom::object& obj,
|
inline void fractured_string_builder::format_object_inline(const dom::object& obj,
|
||||||
const element_metrics& metrics) {
|
const element_metrics& metrics) {
|
||||||
layout_mode prev_layout = format_.get_layout_mode();
|
layout_mode prev_layout = format_.get_layout_mode();
|
||||||
format_.set_layout_mode(layout_mode::single_line);
|
format_.set_layout_mode(layout_mode::INLINE);
|
||||||
|
|
||||||
format_.start_object();
|
format_.start_object();
|
||||||
|
|
||||||
@@ -960,14 +950,6 @@ inline size_t fractured_string_builder::measure_value_length(const dom::element&
|
|||||||
case dom::element_type::DOUBLE: {
|
case dom::element_type::DOUBLE: {
|
||||||
double val;
|
double val;
|
||||||
if (elem.get_double().get(val) == SUCCESS) {
|
if (elem.get_double().get(val) == SUCCESS) {
|
||||||
#if SIMDJSON_ENABLE_NAN_INF
|
|
||||||
if (!std::isfinite(val)) {
|
|
||||||
if (std::isnan(val))
|
|
||||||
return 3; // "NaN"
|
|
||||||
// "-Infinity" (9) or "Infinity" (8)
|
|
||||||
return val < 0 ? 9 : 8;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
char buf[32];
|
char buf[32];
|
||||||
int len = snprintf(buf, sizeof(buf), "%.17g", val);
|
int len = snprintf(buf, sizeof(buf), "%.17g", val);
|
||||||
return len > 0 ? static_cast<size_t>(len) : 1;
|
return len > 0 ? static_cast<size_t>(len) : 1;
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
#include "simdjson/dom/object-inl.h"
|
#include "simdjson/dom/object-inl.h"
|
||||||
#include "simdjson/internal/tape_ref-inl.h"
|
#include "simdjson/internal/tape_ref-inl.h"
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
namespace simdjson {
|
namespace simdjson {
|
||||||
@@ -182,22 +181,6 @@ simdjson_inline void base_formatter<formatter>::number(int64_t x) {
|
|||||||
|
|
||||||
template <class formatter>
|
template <class formatter>
|
||||||
simdjson_inline void base_formatter<formatter>::number(double x) {
|
simdjson_inline void base_formatter<formatter>::number(double x) {
|
||||||
#if SIMDJSON_ENABLE_NAN_INF
|
|
||||||
if (simdjson_unlikely(!std::isfinite(x))) {
|
|
||||||
if (std::isnan(x)) {
|
|
||||||
char const *s = "NaN";
|
|
||||||
chars(s, s + 3);
|
|
||||||
} else {
|
|
||||||
if (x < 0) {
|
|
||||||
one_char('-');
|
|
||||||
}
|
|
||||||
char const *s = "Infinity";
|
|
||||||
chars(s, s + 8);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
char number_buffer[24];
|
char number_buffer[24];
|
||||||
// Currently, passing the nullptr to the second argument is
|
// Currently, passing the nullptr to the second argument is
|
||||||
// safe because our implementation does not check the second
|
// safe because our implementation does not check the second
|
||||||
|
|||||||
@@ -22,9 +22,6 @@ namespace atomparsing {
|
|||||||
// to the compile-time constant 1936482662.
|
// to the compile-time constant 1936482662.
|
||||||
simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; }
|
simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; }
|
||||||
|
|
||||||
// Acts on the same principle as string_to_uint32, but on an 8-byte block of memory
|
|
||||||
simdjson_inline uint64_t string_to_uint64(const char* str) { uint64_t val; std::memcpy(&val, str, sizeof(uint64_t)); return val; }
|
|
||||||
|
|
||||||
|
|
||||||
// Again in str4ncmp we use a memcpy to avoid undefined behavior. The memcpy may appear expensive.
|
// Again in str4ncmp we use a memcpy to avoid undefined behavior. The memcpy may appear expensive.
|
||||||
// Yet all decent optimizing compilers will compile memcpy to a single instruction, just about.
|
// Yet all decent optimizing compilers will compile memcpy to a single instruction, just about.
|
||||||
@@ -36,28 +33,6 @@ simdjson_inline uint32_t str4ncmp(const uint8_t *src, const char* atom) {
|
|||||||
return srcval ^ string_to_uint32(atom);
|
return srcval ^ string_to_uint32(atom);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks that the first 8 characters of the input string match the given atom in a case-insensitive manner.
|
|
||||||
//
|
|
||||||
// 'atom' must consist of only lowercase letters.
|
|
||||||
simdjson_warn_unused
|
|
||||||
simdjson_inline uint64_t str8ncmp_case_insensitive(const uint8_t *src, const char* atom) {
|
|
||||||
uint64_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++)
|
|
||||||
static_assert(sizeof(uint64_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 8 bytes");
|
|
||||||
std::memcpy(&srcval, src, sizeof(uint64_t));
|
|
||||||
|
|
||||||
return (srcval | 0x2020202020202020ull) ^ string_to_uint64(atom);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks that the first 3 characters of 'src' match 'atom' in a case-insensitive way.
|
|
||||||
//
|
|
||||||
// 'atom' must consist of only lowercase letters.
|
|
||||||
simdjson_warn_unused
|
|
||||||
simdjson_inline uint32_t str3ncmp_case_insensitive(const uint8_t *src, const char* atom) {
|
|
||||||
return ((src[0] | 0x20) ^ atom[0]) //
|
|
||||||
| ((src[1] | 0x20) ^ atom[1]) //
|
|
||||||
| ((src[2] | 0x20) ^ atom[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
simdjson_warn_unused
|
simdjson_warn_unused
|
||||||
simdjson_inline bool is_valid_true_atom(const uint8_t *src) {
|
simdjson_inline bool is_valid_true_atom(const uint8_t *src) {
|
||||||
return (str4ncmp(src, "true") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0;
|
return (str4ncmp(src, "true") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0;
|
||||||
@@ -94,74 +69,9 @@ simdjson_inline bool is_valid_null_atom(const uint8_t *src, size_t len) {
|
|||||||
else { return false; }
|
else { return false; }
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SIMDJSON_ENABLE_NAN_INF
|
|
||||||
// "nan" is 3 bytes; we check characters and then verify the next
|
|
||||||
// character is structural or whitespace. We accept both "nan" and "NaN".
|
|
||||||
simdjson_warn_unused
|
|
||||||
simdjson_inline bool is_valid_nan_atom(const uint8_t *src) {
|
|
||||||
return (str3ncmp_case_insensitive(src, "nan")
|
|
||||||
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// checks that the next four characters of a string are 'nan"', where the 'nan'
|
|
||||||
// is checked in a case-insensitive way.
|
|
||||||
simdjson_warn_unused
|
|
||||||
simdjson_inline bool is_valid_nan_in_string(const uint8_t *src) {
|
|
||||||
return (str3ncmp_case_insensitive(src, "nan") | (src[3] ^ '"')) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
simdjson_warn_unused
|
|
||||||
simdjson_inline bool is_valid_nan_atom(const uint8_t *src, size_t len) {
|
|
||||||
if (len > 3) { return is_valid_nan_atom(src); }
|
|
||||||
if (len == 3) { return str3ncmp_case_insensitive(src, "nan") == 0; }
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function will accept any case-insensitive 3-character spelling of
|
|
||||||
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
|
|
||||||
//
|
|
||||||
// Any capitalization of 'infinity' is also accepted.
|
|
||||||
simdjson_warn_unused
|
|
||||||
simdjson_inline bool is_valid_inf_atom(const uint8_t *src) {
|
|
||||||
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf")
|
|
||||||
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
|
|
||||||
if(is_short_inf) return true;
|
|
||||||
|
|
||||||
// Check for 'infinity' (any capitalization)
|
|
||||||
return (str8ncmp_case_insensitive(src, "infinity") | jsoncharutils::is_not_structural_or_whitespace(src[8])) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
simdjson_warn_unused
|
|
||||||
simdjson_inline bool is_valid_inf_in_string(const uint8_t *src) {
|
|
||||||
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf") | (src[3] ^ '"')) == 0;
|
|
||||||
if(is_short_inf) return true;
|
|
||||||
|
|
||||||
return (str8ncmp_case_insensitive(src, "infinity") | (src[8] ^ '"')) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// This function will accept any case-insensitive 3-character spelling of
|
|
||||||
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
|
|
||||||
//
|
|
||||||
// Any capitalization of 'infinity' is also accepted.
|
|
||||||
simdjson_warn_unused
|
|
||||||
simdjson_inline bool is_valid_inf_atom(const uint8_t *src, size_t len) {
|
|
||||||
if (len > 8) { return is_valid_inf_atom(src); }
|
|
||||||
if (len == 8 && str8ncmp_case_insensitive(src, "infinity") == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (len > 3) {
|
|
||||||
return (str3ncmp_case_insensitive(src, "inf")
|
|
||||||
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
|
|
||||||
}
|
|
||||||
if (len == 3) { return str3ncmp_case_insensitive(src, "inf") == 0; }
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif // SIMDJSON_ENABLE_NAN_INF
|
|
||||||
|
|
||||||
} // namespace atomparsing
|
} // namespace atomparsing
|
||||||
} // unnamed namespace
|
} // unnamed namespace
|
||||||
} // namespace SIMDJSON_IMPLEMENTATION
|
} // namespace SIMDJSON_IMPLEMENTATION
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|
||||||
#endif // SIMDJSON_GENERIC_ATOMPARSING_H
|
#endif // SIMDJSON_GENERIC_ATOMPARSING_H
|
||||||
@@ -10,6 +10,5 @@
|
|||||||
// Otherwise, amalgamation will fail.
|
// Otherwise, amalgamation will fail.
|
||||||
#include "simdjson/concepts.h"
|
#include "simdjson/concepts.h"
|
||||||
#include "simdjson/dom/fractured_json.h"
|
#include "simdjson/dom/fractured_json.h"
|
||||||
#include "simdjson/annotations.h"
|
|
||||||
|
|
||||||
#endif // SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H
|
#endif // SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H
|
||||||
@@ -4,7 +4,6 @@
|
|||||||
#define SIMDJSON_GENERIC_STRING_BUILDER_H
|
#define SIMDJSON_GENERIC_STRING_BUILDER_H
|
||||||
#include "simdjson/generic/builder/json_string_builder.h"
|
#include "simdjson/generic/builder/json_string_builder.h"
|
||||||
#include "simdjson/concepts.h"
|
#include "simdjson/concepts.h"
|
||||||
#include "simdjson/annotations.h"
|
|
||||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||||
#if SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
|
|
||||||
@@ -22,113 +21,23 @@ namespace simdjson {
|
|||||||
namespace SIMDJSON_IMPLEMENTATION {
|
namespace SIMDJSON_IMPLEMENTATION {
|
||||||
namespace builder {
|
namespace builder {
|
||||||
|
|
||||||
// Forward-declare helpers defined in json_string_builder-inl.h so the
|
|
||||||
// writer-based atom code below can call them (the -inl.h is not yet
|
|
||||||
// included at the point this header is parsed; without these forwards,
|
|
||||||
// name lookup falls back to the wrong outer namespace).
|
|
||||||
namespace internal {
|
|
||||||
simdjson_really_inline char *write_uint_jeaiii(char *p, uint64_t v) noexcept;
|
|
||||||
} // namespace internal
|
|
||||||
inline size_t write_string_escaped(const std::string_view input, char *out);
|
|
||||||
|
|
||||||
// =============================================================
|
|
||||||
// `writer`: position-as-local hot-path writer used by the reflection
|
|
||||||
// atom code below. Holds the buffer pointer, write position and
|
|
||||||
// capacity in three fields that, once `writer` itself is a stack-local
|
|
||||||
// in the caller and all atom() functions are inlined, become true
|
|
||||||
// register-resident locals after SROA. Glaze achieves the same effect
|
|
||||||
// by passing `B&& b, auto&& ix` through every helper. Holding `pos`
|
|
||||||
// in a register (rather than as a member of string_builder) is what
|
|
||||||
// breaks the strict-aliasing penalty on every char* write through the
|
|
||||||
// buffer, which forces a reload of `b.position` and `b.capacity`
|
|
||||||
// after every byte.
|
|
||||||
// =============================================================
|
|
||||||
struct writer {
|
|
||||||
char *ptr; // buffer pointer (refreshed after a grow)
|
|
||||||
size_t pos; // write position (local)
|
|
||||||
size_t cap; // capacity (refreshed after a grow)
|
|
||||||
string_builder &sb; // back-ref for grow / sync
|
|
||||||
|
|
||||||
// Snapshot string_builder state into a writer for the duration of
|
|
||||||
// a write chain.
|
|
||||||
simdjson_really_inline writer(string_builder &builder) noexcept
|
|
||||||
: ptr(builder.unsafe_data())
|
|
||||||
, pos(builder.unsafe_position())
|
|
||||||
, cap(builder.unsafe_capacity())
|
|
||||||
, sb(builder) {}
|
|
||||||
|
|
||||||
// Write the local position back to the underlying string_builder.
|
|
||||||
// Caller is responsible for invoking before the writer is dropped
|
|
||||||
// (otherwise data is lost). Idempotent.
|
|
||||||
simdjson_really_inline void sync() noexcept {
|
|
||||||
sb.unsafe_set_position(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure at least `n` more bytes of free capacity. Grows the
|
|
||||||
// underlying buffer if needed (rare path). Returns false on
|
|
||||||
// allocation failure.
|
|
||||||
simdjson_really_inline bool ensure(size_t n) noexcept {
|
|
||||||
// Use subtraction (relying on the pos <= cap invariant) so a huge n
|
|
||||||
// cannot wrap pos + n to a small value that spuriously passes the test.
|
|
||||||
// This is pedantic except maybe on 32-bit targets.
|
|
||||||
if (simdjson_likely(n <= cap - pos)) return true;
|
|
||||||
return grow_slow(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slow path of ensure(). Out-of-line via simdjson_inline (not
|
|
||||||
// simdjson_really_inline) to keep the hot path short.
|
|
||||||
simdjson_inline bool grow_slow(size_t n) noexcept {
|
|
||||||
// Detect overflow.
|
|
||||||
// This is pedantic except maybe on 32-bit targets.
|
|
||||||
if (simdjson_unlikely(pos + n < pos)) return false;
|
|
||||||
sb.unsafe_set_position(pos);
|
|
||||||
// even if 2*capacity overflows, the (std::max) below will pick the needed value,
|
|
||||||
// so we do not need a separate overflow check here.
|
|
||||||
if (!sb.unsafe_grow((std::max)(cap * 2, pos + n))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ptr = sb.unsafe_data();
|
|
||||||
cap = sb.unsafe_capacity();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// === Helper: invoke a string_builder member that writes variable-length
|
|
||||||
// content (escape_and_append_with_quotes etc), syncing the writer's local
|
|
||||||
// state before the call and reloading after. Used for string fields where
|
|
||||||
// rewriting the entire SIMD escape path through the writer would be a much
|
|
||||||
// bigger refactor.
|
|
||||||
template <class F>
|
|
||||||
simdjson_really_inline void call_through_string_builder(writer &w, F &&f) noexcept {
|
|
||||||
w.sync();
|
|
||||||
f(w.sb);
|
|
||||||
w.ptr = w.sb.unsafe_data();
|
|
||||||
w.pos = w.sb.unsafe_position();
|
|
||||||
w.cap = w.sb.unsafe_capacity();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
requires(concepts::container_but_not_string<T> && ! concepts::optional_type<T> && !require_custom_serialization<T>)
|
requires(concepts::container_but_not_string<T> && ! concepts::optional_type<T> && !require_custom_serialization<T>)
|
||||||
simdjson_really_inline constexpr void atom(writer &w, 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();
|
||||||
if (it == end) {
|
if (it == end) {
|
||||||
if (!w.ensure(2)) return;
|
b.append_raw("[]");
|
||||||
std::memcpy(w.ptr + w.pos, "[]", 2);
|
|
||||||
w.pos += 2;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!w.ensure(1)) return;
|
b.append('[');
|
||||||
w.ptr[w.pos++] = '[';
|
atom(b, *it);
|
||||||
atom(w, *it);
|
|
||||||
++it;
|
++it;
|
||||||
for (; it != end; ++it) {
|
for (; it != end; ++it) {
|
||||||
if (!w.ensure(1)) return;
|
b.append(',');
|
||||||
w.ptr[w.pos++] = ',';
|
atom(b, *it);
|
||||||
atom(w, *it);
|
|
||||||
}
|
}
|
||||||
if (!w.ensure(1)) return;
|
b.append(']');
|
||||||
w.ptr[w.pos++] = ']';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
@@ -136,100 +45,37 @@ template <class T>
|
|||||||
std::is_same_v<T, std::string_view> ||
|
std::is_same_v<T, std::string_view> ||
|
||||||
std::is_same_v<T, const char *> ||
|
std::is_same_v<T, const char *> ||
|
||||||
std::is_same_v<T, char>)
|
std::is_same_v<T, char>)
|
||||||
simdjson_really_inline constexpr void atom(writer &w, const T &t) {
|
constexpr void atom(string_builder &b, const T &t) {
|
||||||
// Inline the escape path through the writer so we never round-trip
|
b.escape_and_append_with_quotes(t);
|
||||||
// pos through memory for string fields (Twitter is dominated by
|
|
||||||
// these — sync/reload around each string was a real cost).
|
|
||||||
std::string_view input;
|
|
||||||
if constexpr (std::is_same_v<T, char>) {
|
|
||||||
input = std::string_view(&t, 1);
|
|
||||||
} else {
|
|
||||||
input = std::string_view(t);
|
|
||||||
}
|
|
||||||
// Worst-case escape: every byte expands to \uXXXX (6 chars), plus 2 quotes.
|
|
||||||
// Guard against 2 + 6 * input.size() wrapping for huge inputs — if it
|
|
||||||
// wrapped to a small value, ensure() would spuriously succeed and the
|
|
||||||
// subsequent escape would overflow the buffer.
|
|
||||||
// Note that this is pedantic except maybe on 32-bit targets.
|
|
||||||
if (simdjson_unlikely(input.size() > ((std::numeric_limits<size_t>::max)() - 2) / 6)) { return; }
|
|
||||||
if (!w.ensure(2 + 6 * input.size())) { return; }
|
|
||||||
w.ptr[w.pos++] = '"';
|
|
||||||
w.pos += write_string_escaped(input, w.ptr + w.pos);
|
|
||||||
w.ptr[w.pos++] = '"';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <concepts::string_view_keyed_map T>
|
template <concepts::string_view_keyed_map T>
|
||||||
requires(!require_custom_serialization<T>)
|
requires(!require_custom_serialization<T>)
|
||||||
simdjson_really_inline constexpr void atom(writer &w, const T &m) {
|
constexpr void atom(string_builder &b, const T &m) {
|
||||||
if (m.empty()) {
|
if (m.empty()) {
|
||||||
if (!w.ensure(2)) return;
|
b.append_raw("{}");
|
||||||
std::memcpy(w.ptr + w.pos, "{}", 2);
|
|
||||||
w.pos += 2;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!w.ensure(1)) return;
|
b.append('{');
|
||||||
w.ptr[w.pos++] = '{';
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (const auto& [key, value] : m) {
|
for (const auto& [key, value] : m) {
|
||||||
if (!first) {
|
if (!first) {
|
||||||
if (!w.ensure(1)) return;
|
b.append(',');
|
||||||
w.ptr[w.pos++] = ',';
|
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
// Keys must be convertible to string_view per the concept.
|
// Keys must be convertible to string_view per the concept
|
||||||
std::string_view key_sv(key);
|
b.escape_and_append_with_quotes(key);
|
||||||
// Guard against 3 + 6 * key_sv.size() wrapping for huge keys, if it
|
b.append(':');
|
||||||
// wrapped to a small value, ensure() would spuriously succeed and the
|
atom(b, value);
|
||||||
// subsequent escape would overflow the buffer.
|
|
||||||
// Note that this is pedantic except maybe on 32-bit targets.
|
|
||||||
if (simdjson_unlikely(key_sv.size() > ((std::numeric_limits<size_t>::max)() - 3) / 6)) { return; }
|
|
||||||
if (!w.ensure(2 + 6 * key_sv.size() + 1)) { return; }
|
|
||||||
w.ptr[w.pos++] = '"';
|
|
||||||
w.pos += write_string_escaped(key_sv, w.ptr + w.pos);
|
|
||||||
w.ptr[w.pos++] = '"';
|
|
||||||
w.ptr[w.pos++] = ':';
|
|
||||||
atom(w, value);
|
|
||||||
}
|
}
|
||||||
if (!w.ensure(1)) return;
|
b.append('}');
|
||||||
w.ptr[w.pos++] = '}';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<typename number_type,
|
template<typename number_type,
|
||||||
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
|
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
|
||||||
simdjson_really_inline constexpr void atom(writer &w, const number_type t) {
|
constexpr void atom(string_builder &b, const number_type t) {
|
||||||
// Booleans / floats: defer to string_builder (rare path; keeps writer hot
|
b.append(t);
|
||||||
// path free of float-formatter machinery). For integers, write directly
|
|
||||||
// via jeaiii using local pos.
|
|
||||||
if constexpr (std::is_same_v<number_type, bool>) {
|
|
||||||
if (t) {
|
|
||||||
if (!w.ensure(4)) return;
|
|
||||||
std::memcpy(w.ptr + w.pos, "true", 4);
|
|
||||||
w.pos += 4;
|
|
||||||
} else {
|
|
||||||
if (!w.ensure(5)) return;
|
|
||||||
std::memcpy(w.ptr + w.pos, "false", 5);
|
|
||||||
w.pos += 5;
|
|
||||||
}
|
|
||||||
} else if constexpr (std::is_floating_point_v<number_type>) {
|
|
||||||
call_through_string_builder(w, [&](string_builder &b) { b.append(t); });
|
|
||||||
} else if constexpr (std::is_unsigned_v<number_type>) {
|
|
||||||
if (!w.ensure(20)) return;
|
|
||||||
char *end = internal::write_uint_jeaiii(
|
|
||||||
w.ptr + w.pos, static_cast<uint64_t>(t));
|
|
||||||
w.pos = static_cast<size_t>(end - w.ptr);
|
|
||||||
} else {
|
|
||||||
// signed integral
|
|
||||||
if (!w.ensure(20)) return;
|
|
||||||
using U = typename std::make_unsigned<number_type>::type;
|
|
||||||
bool negative = t < 0;
|
|
||||||
U pv = negative ? U(0) - static_cast<U>(t) : static_cast<U>(t);
|
|
||||||
w.ptr[w.pos] = '-';
|
|
||||||
w.pos += negative;
|
|
||||||
char *end = internal::write_uint_jeaiii(
|
|
||||||
w.ptr + w.pos, static_cast<uint64_t>(pv));
|
|
||||||
w.pos = static_cast<size_t>(end - w.ptr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
@@ -242,86 +88,61 @@ template <class T>
|
|||||||
!std::is_same_v<T, std::string_view> &&
|
!std::is_same_v<T, std::string_view> &&
|
||||||
!std::is_same_v<T, const char*> &&
|
!std::is_same_v<T, const char*> &&
|
||||||
!std::is_same_v<T, char> && !require_custom_serialization<T>)
|
!std::is_same_v<T, char> && !require_custom_serialization<T>)
|
||||||
simdjson_really_inline constexpr void atom(writer &w, const T &t) {
|
constexpr void atom(string_builder &b, const T &t) {
|
||||||
// Per-field block: ensure key+value worst case, then write key + value
|
int i = 0;
|
||||||
// through the writer's local pos. For arithmetic fields, the integer
|
b.append('{');
|
||||||
// write happens directly via write_uint_jeaiii on w.ptr+w.pos, so pos
|
|
||||||
// never round-trips through memory.
|
|
||||||
bool first = true;
|
|
||||||
if (!w.ensure(1)) return;
|
|
||||||
w.ptr[w.pos++] = '{';
|
|
||||||
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
|
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
|
||||||
if constexpr (std::meta::annotations_of_with_type(dm, ^^simdjson::detail::skip_tag).empty()) {
|
if (i != 0)
|
||||||
constexpr const char* key_name = simdjson::get_json_key_name<dm>();
|
b.append(',');
|
||||||
constexpr auto first_key = std::define_static_string(
|
constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm)));
|
||||||
constevalutil::consteval_to_quoted_escaped(key_name) + ":");
|
b.append_raw(key);
|
||||||
constexpr auto rest_key = std::define_static_string(
|
b.append(':');
|
||||||
std::string(",") + constevalutil::consteval_to_quoted_escaped(key_name) + ":");
|
atom(b, t.[:dm:]);
|
||||||
constexpr size_t first_key_len = std::char_traits<char>::length(first_key);
|
i++;
|
||||||
constexpr size_t rest_key_len = std::char_traits<char>::length(rest_key);
|
|
||||||
if (!w.ensure(rest_key_len)) return;
|
|
||||||
if (first) {
|
|
||||||
std::memcpy(w.ptr + w.pos, first_key, first_key_len);
|
|
||||||
w.pos += first_key_len;
|
|
||||||
} else {
|
|
||||||
std::memcpy(w.ptr + w.pos, rest_key, rest_key_len);
|
|
||||||
w.pos += rest_key_len;
|
|
||||||
}
|
|
||||||
first = false;
|
|
||||||
atom(w, t.[:dm:]);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
if (!w.ensure(1)) return;
|
b.append('}');
|
||||||
w.ptr[w.pos++] = '}';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Support for optional types (std::optional, etc.)
|
// Support for optional types (std::optional, etc.)
|
||||||
template <concepts::optional_type T>
|
template <concepts::optional_type T>
|
||||||
requires(!require_custom_serialization<T>)
|
requires(!require_custom_serialization<T>)
|
||||||
simdjson_really_inline constexpr void atom(writer &w, const T &opt) {
|
constexpr void atom(string_builder &b, const T &opt) {
|
||||||
if (opt) {
|
if (opt) {
|
||||||
atom(w, opt.value());
|
atom(b, opt.value());
|
||||||
} else {
|
} else {
|
||||||
if (!w.ensure(4)) return;
|
b.append_raw("null");
|
||||||
std::memcpy(w.ptr + w.pos, "null", 4);
|
|
||||||
w.pos += 4;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.)
|
// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.)
|
||||||
template <concepts::smart_pointer T>
|
template <concepts::smart_pointer T>
|
||||||
requires(!require_custom_serialization<T>)
|
requires(!require_custom_serialization<T>)
|
||||||
simdjson_really_inline constexpr void atom(writer &w, const T &ptr) {
|
constexpr void atom(string_builder &b, const T &ptr) {
|
||||||
if (ptr) {
|
if (ptr) {
|
||||||
atom(w, *ptr);
|
atom(b, *ptr);
|
||||||
} else {
|
} else {
|
||||||
if (!w.ensure(4)) return;
|
b.append_raw("null");
|
||||||
std::memcpy(w.ptr + w.pos, "null", 4);
|
|
||||||
w.pos += 4;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Support for enums - serialize as string representation using expand approach from P2996R12
|
// Support for enums - serialize as string representation using expand approach from P2996R12
|
||||||
template <typename T>
|
template <typename T>
|
||||||
requires(std::is_enum_v<T> && !require_custom_serialization<T>)
|
requires(std::is_enum_v<T> && !require_custom_serialization<T>)
|
||||||
simdjson_really_inline void atom(writer &w, const T &e) {
|
void atom(string_builder &b, const T &e) {
|
||||||
#if SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
static 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)));
|
||||||
constexpr size_t enum_str_len = std::char_traits<char>::length(enum_str);
|
|
||||||
if (e == [:enum_val:]) {
|
if (e == [:enum_val:]) {
|
||||||
if (!w.ensure(enum_str_len)) return;
|
b.append_raw(enum_str);
|
||||||
std::memcpy(w.ptr + w.pos, enum_str, enum_str_len);
|
|
||||||
w.pos += enum_str_len;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Fallback to integer if enum value not found
|
// Fallback to integer if enum value not found
|
||||||
atom(w, static_cast<std::underlying_type_t<T>>(e));
|
atom(b, static_cast<std::underlying_type_t<T>>(e));
|
||||||
#else
|
#else
|
||||||
// Fallback: serialize as integer if reflection not available
|
// Fallback: serialize as integer if reflection not available
|
||||||
atom(w, static_cast<std::underlying_type_t<T>>(e));
|
atom(b, static_cast<std::underlying_type_t<T>>(e));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,37 +152,28 @@ template <concepts::appendable_containers T>
|
|||||||
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
|
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
|
||||||
!std::is_same_v<T, std::string> &&
|
!std::is_same_v<T, std::string> &&
|
||||||
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
|
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
|
||||||
simdjson_really_inline constexpr void atom(writer &w, const T &container) {
|
constexpr void atom(string_builder &b, const T &container) {
|
||||||
if (container.empty()) {
|
if (container.empty()) {
|
||||||
if (!w.ensure(2)) return;
|
b.append_raw("[]");
|
||||||
std::memcpy(w.ptr + w.pos, "[]", 2);
|
|
||||||
w.pos += 2;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!w.ensure(1)) return;
|
b.append('[');
|
||||||
w.ptr[w.pos++] = '[';
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (const auto& item : container) {
|
for (const auto& item : container) {
|
||||||
if (!first) {
|
if (!first) {
|
||||||
if (!w.ensure(1)) return;
|
b.append(',');
|
||||||
w.ptr[w.pos++] = ',';
|
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
atom(w, item);
|
atom(b, item);
|
||||||
}
|
}
|
||||||
if (!w.ensure(1)) return;
|
b.append(']');
|
||||||
w.ptr[w.pos++] = ']';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// append() — top-level entry. Each overload constructs a stack-local
|
// append functions that delegate to atom functions for primitive types
|
||||||
// writer, runs atom(w, t) through the inlined call chain, then syncs
|
|
||||||
// the local position back into the string_builder.
|
|
||||||
template <class T>
|
template <class T>
|
||||||
requires(std::is_arithmetic_v<T> && !std::is_same_v<T, char>)
|
requires(std::is_arithmetic_v<T> && !std::is_same_v<T, char>)
|
||||||
simdjson_inline void append(string_builder &b, const T &t) {
|
void append(string_builder &b, const T &t) {
|
||||||
writer w(b);
|
atom(b, t);
|
||||||
atom(w, t);
|
|
||||||
w.sync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
@@ -369,26 +181,20 @@ template <class T>
|
|||||||
std::is_same_v<T, std::string_view> ||
|
std::is_same_v<T, std::string_view> ||
|
||||||
std::is_same_v<T, const char *> ||
|
std::is_same_v<T, const char *> ||
|
||||||
std::is_same_v<T, char>)
|
std::is_same_v<T, char>)
|
||||||
simdjson_inline void append(string_builder &b, const T &t) {
|
void append(string_builder &b, const T &t) {
|
||||||
writer w(b);
|
atom(b, t);
|
||||||
atom(w, t);
|
|
||||||
w.sync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <concepts::optional_type T>
|
template <concepts::optional_type T>
|
||||||
requires(!require_custom_serialization<T>)
|
requires(!require_custom_serialization<T>)
|
||||||
simdjson_inline void append(string_builder &b, const T &t) {
|
void append(string_builder &b, const T &t) {
|
||||||
writer w(b);
|
atom(b, t);
|
||||||
atom(w, t);
|
|
||||||
w.sync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <concepts::smart_pointer T>
|
template <concepts::smart_pointer T>
|
||||||
requires(!require_custom_serialization<T>)
|
requires(!require_custom_serialization<T>)
|
||||||
simdjson_inline void append(string_builder &b, const T &t) {
|
void append(string_builder &b, const T &t) {
|
||||||
writer w(b);
|
atom(b, t);
|
||||||
atom(w, t);
|
|
||||||
w.sync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <concepts::appendable_containers T>
|
template <concepts::appendable_containers T>
|
||||||
@@ -396,18 +202,14 @@ template <concepts::appendable_containers T>
|
|||||||
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
|
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
|
||||||
!std::is_same_v<T, std::string> &&
|
!std::is_same_v<T, std::string> &&
|
||||||
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
|
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
|
||||||
simdjson_inline void append(string_builder &b, const T &t) {
|
void append(string_builder &b, const T &t) {
|
||||||
writer w(b);
|
atom(b, t);
|
||||||
atom(w, t);
|
|
||||||
w.sync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <concepts::string_view_keyed_map T>
|
template <concepts::string_view_keyed_map T>
|
||||||
requires(!require_custom_serialization<T>)
|
requires(!require_custom_serialization<T>)
|
||||||
simdjson_inline void append(string_builder &b, const T &t) {
|
void append(string_builder &b, const T &t) {
|
||||||
writer w(b);
|
atom(b, t);
|
||||||
atom(w, t);
|
|
||||||
w.sync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// works for struct
|
// works for struct
|
||||||
@@ -421,19 +223,39 @@ template <class Z>
|
|||||||
!std::is_same_v<Z, std::string_view> &&
|
!std::is_same_v<Z, std::string_view> &&
|
||||||
!std::is_same_v<Z, const char*> &&
|
!std::is_same_v<Z, const char*> &&
|
||||||
!std::is_same_v<Z, char> && !require_custom_serialization<Z>)
|
!std::is_same_v<Z, char> && !require_custom_serialization<Z>)
|
||||||
simdjson_inline void append(string_builder &b, const Z &z) {
|
void append(string_builder &b, const Z &z) {
|
||||||
writer w(b);
|
int i = 0;
|
||||||
atom(w, z);
|
b.append('{');
|
||||||
w.sync();
|
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) {
|
||||||
|
if (i != 0)
|
||||||
|
b.append(',');
|
||||||
|
constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm)));
|
||||||
|
b.append_raw(key);
|
||||||
|
b.append(':');
|
||||||
|
atom(b, z.[:dm:]);
|
||||||
|
i++;
|
||||||
|
};
|
||||||
|
b.append('}');
|
||||||
}
|
}
|
||||||
|
|
||||||
// works for container that have begin() and end() iterators
|
// works for container that have begin() and end() iterators
|
||||||
template <class Z>
|
template <class Z>
|
||||||
requires(concepts::container_but_not_string<Z> && !require_custom_serialization<Z>)
|
requires(concepts::container_but_not_string<Z> && !require_custom_serialization<Z>)
|
||||||
simdjson_inline void append(string_builder &b, const Z &z) {
|
void append(string_builder &b, const Z &z) {
|
||||||
writer w(b);
|
auto it = z.begin();
|
||||||
atom(w, z);
|
auto end = z.end();
|
||||||
w.sync();
|
if (it == end) {
|
||||||
|
b.append_raw("[]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
b.append('[');
|
||||||
|
atom(b, *it);
|
||||||
|
++it;
|
||||||
|
for (; it != end; ++it) {
|
||||||
|
b.append(',');
|
||||||
|
atom(b, *it);
|
||||||
|
}
|
||||||
|
b.append(']');
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Z>
|
template <class Z>
|
||||||
@@ -472,9 +294,7 @@ 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) {
|
||||||
writer w(b);
|
b.append('{');
|
||||||
if (!w.ensure(1)) { w.sync(); return; }
|
|
||||||
w.ptr[w.pos++] = '{';
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
// Iterate through all members of T using reflection
|
// Iterate through all members of T using reflection
|
||||||
static constexpr auto members = std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()));
|
static constexpr auto members = std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()));
|
||||||
@@ -484,29 +304,23 @@ void extract_from(string_builder &b, const T &obj) {
|
|||||||
|
|
||||||
// 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 (((FieldNames.view() == key) || ...)) {
|
if constexpr (((FieldNames.view() == key) || ...)) {
|
||||||
static constexpr auto first_key = std::define_static_string(
|
if (!first) {
|
||||||
constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
|
b.append(',');
|
||||||
static constexpr auto rest_key = std::define_static_string(
|
|
||||||
std::string(",") + constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
|
|
||||||
constexpr size_t first_key_len = std::char_traits<char>::length(first_key);
|
|
||||||
constexpr size_t rest_key_len = std::char_traits<char>::length(rest_key);
|
|
||||||
if (!w.ensure(rest_key_len)) { w.sync(); return; }
|
|
||||||
if (first) {
|
|
||||||
std::memcpy(w.ptr + w.pos, first_key, first_key_len);
|
|
||||||
w.pos += first_key_len;
|
|
||||||
} else {
|
|
||||||
std::memcpy(w.ptr + w.pos, rest_key, rest_key_len);
|
|
||||||
w.pos += rest_key_len;
|
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
atom(w, obj.[:mem:]);
|
|
||||||
|
// Serialize the key
|
||||||
|
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(':');
|
||||||
|
|
||||||
|
// Serialize the value
|
||||||
|
atom(b, obj.[:mem:]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!w.ensure(1)) { w.sync(); return; }
|
b.append('}');
|
||||||
w.ptr[w.pos++] = '}';
|
|
||||||
w.sync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<constevalutil::fixed_string... FieldNames, typename T>
|
template<constevalutil::fixed_string... FieldNames, typename T>
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <cmath>
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <limits>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#ifndef SIMDJSON_GENERIC_STRING_BUILDER_INL_H
|
#ifndef SIMDJSON_GENERIC_STRING_BUILDER_INL_H
|
||||||
|
|
||||||
@@ -173,35 +171,6 @@ 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_LSX
|
|
||||||
simdjson_inline bool fast_needs_escaping(std::string_view view) {
|
|
||||||
if (view.size() < 16) {
|
|
||||||
return simple_needs_escaping(view);
|
|
||||||
}
|
|
||||||
size_t i = 0;
|
|
||||||
__m128i running = __lsx_vreplgr2vr_b(0);
|
|
||||||
__m128i v34 = __lsx_vreplgr2vr_b(34);
|
|
||||||
__m128i v92 = __lsx_vreplgr2vr_b(92);
|
|
||||||
__m128i v32 = __lsx_vreplgr2vr_b(32);
|
|
||||||
|
|
||||||
for (; i + 15 < view.size(); i += 16) {
|
|
||||||
__m128i word =
|
|
||||||
__lsx_vld(reinterpret_cast<const char *>(view.data() + i), 0);
|
|
||||||
__m128i chunk = __lsx_vseq_b(word, v34);
|
|
||||||
chunk = __lsx_vor_v(chunk, __lsx_vseq_b(word, v92));
|
|
||||||
chunk = __lsx_vor_v(chunk, __lsx_vslt_bu(word, v32));
|
|
||||||
running = __lsx_vor_v(running, chunk);
|
|
||||||
}
|
|
||||||
if (i < view.size()) {
|
|
||||||
__m128i word = __lsx_vld(
|
|
||||||
reinterpret_cast<const char *>(view.data() + view.length() - 16), 0);
|
|
||||||
__m128i chunk = __lsx_vseq_b(word, v34);
|
|
||||||
chunk = __lsx_vor_v(chunk, __lsx_vseq_b(word, v92));
|
|
||||||
chunk = __lsx_vor_v(chunk, __lsx_vslt_bu(word, v32));
|
|
||||||
running = __lsx_vor_v(running, chunk);
|
|
||||||
}
|
|
||||||
return !__lsx_bz_v(running);
|
|
||||||
}
|
|
||||||
#elif SIMDJSON_EXPERIMENTAL_HAS_PPC64
|
#elif SIMDJSON_EXPERIMENTAL_HAS_PPC64
|
||||||
simdjson_inline bool fast_needs_escaping(std::string_view view) {
|
simdjson_inline bool fast_needs_escaping(std::string_view view) {
|
||||||
if (view.size() < 16) {
|
if (view.size() < 16) {
|
||||||
@@ -592,6 +561,62 @@ simdjson_inline void string_builder::clear() noexcept {
|
|||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename number_type, typename = typename std::enable_if<
|
||||||
|
std::is_unsigned<number_type>::value>::type>
|
||||||
|
simdjson_really_inline int int_log2(number_type x) {
|
||||||
|
return 63 - leading_zeroes(uint64_t(x) | 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
simdjson_really_inline int fast_digit_count_32(uint32_t x) {
|
||||||
|
static uint64_t table[] = {
|
||||||
|
4294967296, 8589934582, 8589934582, 8589934582, 12884901788,
|
||||||
|
12884901788, 12884901788, 17179868184, 17179868184, 17179868184,
|
||||||
|
21474826480, 21474826480, 21474826480, 21474826480, 25769703776,
|
||||||
|
25769703776, 25769703776, 30063771072, 30063771072, 30063771072,
|
||||||
|
34349738368, 34349738368, 34349738368, 34349738368, 38554705664,
|
||||||
|
38554705664, 38554705664, 41949672960, 41949672960, 41949672960,
|
||||||
|
42949672960, 42949672960};
|
||||||
|
return uint32_t((x + table[int_log2(x)]) >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
simdjson_really_inline int fast_digit_count_64(uint64_t x) {
|
||||||
|
static uint64_t table[] = {9,
|
||||||
|
99,
|
||||||
|
999,
|
||||||
|
9999,
|
||||||
|
99999,
|
||||||
|
999999,
|
||||||
|
9999999,
|
||||||
|
99999999,
|
||||||
|
999999999,
|
||||||
|
9999999999,
|
||||||
|
99999999999,
|
||||||
|
999999999999,
|
||||||
|
9999999999999,
|
||||||
|
99999999999999,
|
||||||
|
999999999999999ULL,
|
||||||
|
9999999999999999ULL,
|
||||||
|
99999999999999999ULL,
|
||||||
|
999999999999999999ULL,
|
||||||
|
9999999999999999999ULL};
|
||||||
|
int y = (19 * int_log2(x) >> 6);
|
||||||
|
y += x > table[y];
|
||||||
|
return y + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename number_type, typename = typename std::enable_if<
|
||||||
|
std::is_unsigned<number_type>::value>::type>
|
||||||
|
simdjson_really_inline size_t digit_count(number_type v) noexcept {
|
||||||
|
static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 ||
|
||||||
|
sizeof(number_type) == 2 || sizeof(number_type) == 1,
|
||||||
|
"We only support 8-bit, 16-bit, 32-bit and 64-bit numbers");
|
||||||
|
SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) {
|
||||||
|
return fast_digit_count_32(static_cast<uint32_t>(v));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return fast_digit_count_64(static_cast<uint64_t>(v));
|
||||||
|
}
|
||||||
|
}
|
||||||
static const char decimal_table[200] = {
|
static const char decimal_table[200] = {
|
||||||
0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35,
|
0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35,
|
||||||
0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31,
|
0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31,
|
||||||
@@ -611,78 +636,6 @@ static const char decimal_table[200] = {
|
|||||||
0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35,
|
0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35,
|
||||||
0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39,
|
0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Forward unsigned-int writer (cascade-on-magnitude, no upfront digit_count).
|
|
||||||
// Built from a non-recursive DAG of always_inline helpers — gcc and MSVC
|
|
||||||
// refuse to inline recursive `always_inline`/`__forceinline` functions.
|
|
||||||
// Caller must guarantee at least 20 bytes available at p. All helpers
|
|
||||||
// return pointer past the last digit written.
|
|
||||||
|
|
||||||
// Caller guarantees v < 100. Writes 1-2 digits.
|
|
||||||
simdjson_really_inline char* write_lt100(char* p, uint64_t v) noexcept {
|
|
||||||
if (v < 10) { *p++ = char('0' + v); return p; }
|
|
||||||
std::memcpy(p, &decimal_table[v * 2], 2);
|
|
||||||
return p + 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Caller guarantees v < 10000. Writes 1-4 digits.
|
|
||||||
simdjson_really_inline char* write_lt10000(char* p, uint64_t v) noexcept {
|
|
||||||
if (v < 100) return write_lt100(p, v);
|
|
||||||
uint64_t hi = v / 100, lo = v % 100;
|
|
||||||
if (v < 1000) {
|
|
||||||
*p++ = char('0' + hi);
|
|
||||||
} else {
|
|
||||||
std::memcpy(p, &decimal_table[hi * 2], 2);
|
|
||||||
p += 2;
|
|
||||||
}
|
|
||||||
std::memcpy(p, &decimal_table[lo * 2], 2);
|
|
||||||
return p + 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Caller guarantees v < 10000. Always writes exactly 4 digits.
|
|
||||||
simdjson_really_inline void write_4_digits(char* p, uint64_t v) noexcept {
|
|
||||||
uint64_t hi = v / 100, lo = v % 100;
|
|
||||||
std::memcpy(p, &decimal_table[hi * 2], 2);
|
|
||||||
std::memcpy(p + 2, &decimal_table[lo * 2], 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Caller guarantees v < 10^8. Writes 1-8 digits.
|
|
||||||
simdjson_really_inline char* write_lt1e8(char* p, uint64_t v) noexcept {
|
|
||||||
if (v < 10000) return write_lt10000(p, v);
|
|
||||||
uint64_t hi = v / 10000, lo = v % 10000;
|
|
||||||
p = write_lt10000(p, hi);
|
|
||||||
write_4_digits(p, lo);
|
|
||||||
return p + 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
simdjson_really_inline char* write_uint_jeaiii(char* p, uint64_t v) noexcept {
|
|
||||||
if (v < 10000ULL) return write_lt10000(p, v);
|
|
||||||
if (v < 100000000ULL) { // 5-8 digits
|
|
||||||
uint64_t hi = v / 10000, lo = v % 10000;
|
|
||||||
p = write_lt10000(p, hi);
|
|
||||||
write_4_digits(p, lo);
|
|
||||||
return p + 4;
|
|
||||||
}
|
|
||||||
if (v < 10000000000000000ULL) { // 9-16 digits
|
|
||||||
uint64_t hi = v / 100000000ULL, lo = v % 100000000ULL;
|
|
||||||
p = write_lt1e8(p, hi);
|
|
||||||
uint64_t lo_hi = lo / 10000, lo_lo = lo % 10000;
|
|
||||||
write_4_digits(p, lo_hi);
|
|
||||||
write_4_digits(p + 4, lo_lo);
|
|
||||||
return p + 8;
|
|
||||||
}
|
|
||||||
// 17-20 digits
|
|
||||||
uint64_t hi = v / 10000000000000000ULL, lo = v % 10000000000000000ULL;
|
|
||||||
p = write_lt10000(p, hi);
|
|
||||||
uint64_t lo_a = lo / 100000000ULL, lo_b = lo % 100000000ULL;
|
|
||||||
uint64_t lo_a_hi = lo_a / 10000, lo_a_lo = lo_a % 10000;
|
|
||||||
uint64_t lo_b_hi = lo_b / 10000, lo_b_lo = lo_b % 10000;
|
|
||||||
write_4_digits(p, lo_a_hi);
|
|
||||||
write_4_digits(p + 4, lo_a_lo);
|
|
||||||
write_4_digits(p + 8, lo_b_hi);
|
|
||||||
write_4_digits(p + 12, lo_b_lo);
|
|
||||||
return p + 16;
|
|
||||||
}
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
template <typename number_type, typename>
|
template <typename number_type, typename>
|
||||||
@@ -710,59 +663,87 @@ 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;
|
||||||
char* end = internal::write_uint_jeaiii(
|
unsigned_type pv = static_cast<unsigned_type>(v);
|
||||||
buffer.get() + position,
|
size_t dc = internal::digit_count(pv);
|
||||||
static_cast<uint64_t>(static_cast<unsigned_type>(v)));
|
char *write_pointer = buffer.get() + position + dc - 1;
|
||||||
position = end - buffer.get();
|
|
||||||
|
// 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) {
|
||||||
|
memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2);
|
||||||
|
write_pointer -= 2;
|
||||||
|
pv /= 100;
|
||||||
|
}
|
||||||
|
if (pv >= 10) {
|
||||||
|
*write_pointer-- = char('0' + (pv % 10));
|
||||||
|
pv /= 10;
|
||||||
|
}
|
||||||
|
*write_pointer = char('0' + pv);
|
||||||
|
position += dc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
|
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
|
||||||
// 19 digits (max abs value of int64_t) + optional minus sign.
|
// 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;
|
||||||
bool negative = v < 0;
|
bool negative = v < 0;
|
||||||
// 0 - pv (rather than -pv) avoids an MSVC unary-minus warning.
|
unsigned_type pv = static_cast<unsigned_type>(v);
|
||||||
unsigned_type pv = negative
|
if (negative) {
|
||||||
? unsigned_type(0) - static_cast<unsigned_type>(v)
|
pv = 0 - pv; // the 0 is for Microsoft
|
||||||
: static_cast<unsigned_type>(v);
|
}
|
||||||
// Branchless: always write '-', advance only if negative.
|
size_t dc = internal::digit_count(pv);
|
||||||
|
// by always writing the minus sign, we avoid the branch.
|
||||||
buffer.get()[position] = '-';
|
buffer.get()[position] = '-';
|
||||||
position += negative;
|
position += negative ? 1 : 0;
|
||||||
char* end = internal::write_uint_jeaiii(
|
char *write_pointer = buffer.get() + position + dc - 1;
|
||||||
buffer.get() + position, static_cast<uint64_t>(pv));
|
|
||||||
position = end - buffer.get();
|
// 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) {
|
||||||
|
memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2);
|
||||||
|
write_pointer -= 2;
|
||||||
|
pv /= 100;
|
||||||
|
}
|
||||||
|
if (pv >= 10) {
|
||||||
|
*write_pointer-- = char('0' + (pv % 10));
|
||||||
|
pv /= 10;
|
||||||
|
}
|
||||||
|
*write_pointer = char('0' + pv);
|
||||||
|
position += dc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
|
else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
|
||||||
constexpr size_t max_number_size = 24;
|
constexpr size_t max_number_size = 24;
|
||||||
if (capacity_check(max_number_size)) {
|
if (capacity_check(max_number_size)) {
|
||||||
#if SIMDJSON_ENABLE_NAN_INF
|
|
||||||
// Check if the input might be NaN or infinity
|
|
||||||
if (simdjson_unlikely(!std::isfinite(v))) {
|
|
||||||
if (std::isnan(v)) {
|
|
||||||
constexpr char nan_literal[] = "NaN";
|
|
||||||
constexpr size_t nan_len = sizeof(nan_literal) - 1;
|
|
||||||
|
|
||||||
std::memcpy(buffer.get() + position, nan_literal, nan_len);
|
|
||||||
position += nan_len;
|
|
||||||
} else {
|
|
||||||
constexpr char inf_literal[] = "Infinity";
|
|
||||||
constexpr size_t inf_len = sizeof(inf_literal) - 1;
|
|
||||||
if (v < 0) {
|
|
||||||
buffer.get()[position] = '-';
|
|
||||||
++position;
|
|
||||||
}
|
|
||||||
std::memcpy(buffer.get() + position, inf_literal, inf_len);
|
|
||||||
position += inf_len;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// We could specialize for float.
|
// We could specialize for float.
|
||||||
char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr,
|
char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr,
|
||||||
double(v));
|
double(v));
|
||||||
@@ -774,11 +755,6 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
|
|||||||
simdjson_inline void
|
simdjson_inline void
|
||||||
string_builder::escape_and_append(std::string_view input) noexcept {
|
string_builder::escape_and_append(std::string_view input) noexcept {
|
||||||
// escaping might turn a control character into \x00xx so 6 characters.
|
// escaping might turn a control character into \x00xx so 6 characters.
|
||||||
// Guard against size_t overflow in the multiplication below.
|
|
||||||
if (input.size() > (std::numeric_limits<size_t>::max)() / 6) {
|
|
||||||
set_valid(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (capacity_check(6 * input.size())) {
|
if (capacity_check(6 * input.size())) {
|
||||||
position += write_string_escaped(input, buffer.get() + position);
|
position += write_string_escaped(input, buffer.get() + position);
|
||||||
}
|
}
|
||||||
@@ -787,11 +763,6 @@ string_builder::escape_and_append(std::string_view input) noexcept {
|
|||||||
simdjson_inline void
|
simdjson_inline void
|
||||||
string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
|
string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
|
||||||
// escaping might turn a control character into \x00xx so 6 characters.
|
// escaping might turn a control character into \x00xx so 6 characters.
|
||||||
// Guard against size_t overflow in the arithmetic below.
|
|
||||||
if (input.size() > ((std::numeric_limits<size_t>::max)() - 2) / 6) {
|
|
||||||
set_valid(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (capacity_check(2 + 6 * input.size())) {
|
if (capacity_check(2 + 6 * input.size())) {
|
||||||
buffer.get()[position++] = '"';
|
buffer.get()[position++] = '"';
|
||||||
position += write_string_escaped(input, buffer.get() + position);
|
position += write_string_escaped(input, buffer.get() + position);
|
||||||
@@ -823,9 +794,7 @@ simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
simdjson_inline void string_builder::append_raw(const char *c) noexcept {
|
simdjson_inline void string_builder::append_raw(const char *c) noexcept {
|
||||||
// char_traits::length is constexpr; lets the compiler fold the length
|
size_t len = std::strlen(c);
|
||||||
// when called with a pointer to a compile-time-constant string.
|
|
||||||
size_t len = std::char_traits<char>::length(c);
|
|
||||||
append_raw(c, len);
|
append_raw(c, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -844,14 +813,6 @@ simdjson_inline void string_builder::append_raw(const char *str,
|
|||||||
position += len;
|
position += len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t N>
|
|
||||||
simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
|
|
||||||
if (capacity_check(N)) {
|
|
||||||
std::memcpy(buffer.get() + position, str, N);
|
|
||||||
position += N;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||||
// Support for optional types (std::optional, etc.)
|
// Support for optional types (std::optional, etc.)
|
||||||
template <concepts::optional_type T>
|
template <concepts::optional_type T>
|
||||||
|
|||||||
@@ -185,15 +185,6 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
|
|||||||
* There is no UTF-8 validation.
|
* There is no UTF-8 validation.
|
||||||
*/
|
*/
|
||||||
simdjson_inline void append_raw(const char *str, size_t len) noexcept;
|
simdjson_inline void append_raw(const char *str, size_t len) noexcept;
|
||||||
|
|
||||||
/**
|
|
||||||
* Append exactly N characters from str. The length is a template parameter
|
|
||||||
* so the compiler can fully inline the memcpy with a compile-time-constant
|
|
||||||
* size, avoiding the libc call. Used for compile-time-constant keys in the
|
|
||||||
* reflection struct atom.
|
|
||||||
*/
|
|
||||||
template <size_t N>
|
|
||||||
simdjson_inline void append_raw_n(const char *str) noexcept;
|
|
||||||
#if SIMDJSON_EXCEPTIONS
|
#if SIMDJSON_EXCEPTIONS
|
||||||
/**
|
/**
|
||||||
* Creates an std::string from the written JSON buffer.
|
* Creates an std::string from the written JSON buffer.
|
||||||
@@ -245,26 +236,6 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
|
|||||||
*/
|
*/
|
||||||
simdjson_inline size_t size() const noexcept;
|
simdjson_inline size_t size() const noexcept;
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// Internal hooks for the position-as-local writer in json_builder.h.
|
|
||||||
// These exist so the reflection atom code can hold buffer pointer,
|
|
||||||
// position and capacity in registers across long write chains rather
|
|
||||||
// than reloading them after every char* write (strict aliasing
|
|
||||||
// forces those reloads when accessed via members of *this). User
|
|
||||||
// code should NOT call these directly.
|
|
||||||
// ============================================================
|
|
||||||
simdjson_inline char *unsafe_data() noexcept { return buffer.get(); }
|
|
||||||
simdjson_inline size_t unsafe_position() const noexcept { return position; }
|
|
||||||
simdjson_inline size_t unsafe_capacity() const noexcept { return capacity; }
|
|
||||||
simdjson_inline void unsafe_set_position(size_t p) noexcept { position = p; }
|
|
||||||
/// Make capacity available for at least `n` more bytes after the current
|
|
||||||
/// position. Returns false if the allocation failed.
|
|
||||||
simdjson_inline bool unsafe_grow(size_t needed_total_capacity) noexcept {
|
|
||||||
grow_buffer(needed_total_capacity);
|
|
||||||
return is_valid;
|
|
||||||
}
|
|
||||||
simdjson_inline bool unsafe_is_valid() const noexcept { return is_valid; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* Returns true if we can write at least upcoming_bytes bytes.
|
* Returns true if we can write at least upcoming_bytes bytes.
|
||||||
|
|||||||
@@ -63,11 +63,7 @@ inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parse
|
|||||||
inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept {
|
inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept {
|
||||||
if(capacity > SIMDJSON_MAXSIZE_BYTES) { return CAPACITY; }
|
if(capacity > SIMDJSON_MAXSIZE_BYTES) { return CAPACITY; }
|
||||||
// Stage 1 index output
|
// Stage 1 index output
|
||||||
size_t rounded_capacity = SIMDJSON_ROUNDUP_N(capacity, 64);
|
size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7;
|
||||||
if(rounded_capacity + 9 < rounded_capacity) {
|
|
||||||
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
|
|
||||||
}
|
|
||||||
size_t max_structures = rounded_capacity + 9;
|
|
||||||
structural_indexes.reset( new (std::nothrow) uint32_t[max_structures] );
|
structural_indexes.reset( new (std::nothrow) uint32_t[max_structures] );
|
||||||
if (!structural_indexes) { _capacity = 0; return MEMALLOC; }
|
if (!structural_indexes) { _capacity = 0; return MEMALLOC; }
|
||||||
structural_indexes[0] = 0;
|
structural_indexes[0] = 0;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#define SIMDJSON_GENERIC_NUMBERPARSING_H
|
#define SIMDJSON_GENERIC_NUMBERPARSING_H
|
||||||
#include "simdjson/generic/base.h"
|
#include "simdjson/generic/base.h"
|
||||||
#include "simdjson/generic/jsoncharutils.h"
|
#include "simdjson/generic/jsoncharutils.h"
|
||||||
#include "simdjson/generic/atomparsing.h"
|
|
||||||
#include "simdjson/internal/numberparsing_tables.h"
|
#include "simdjson/internal/numberparsing_tables.h"
|
||||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||||
|
|
||||||
@@ -300,24 +299,6 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SIMDJSON_ENABLE_NAN_INF
|
|
||||||
// Parses a nan or infinity. Returns true on success, false on failure.
|
|
||||||
simdjson_unused simdjson_inline bool compute_nan_inf(const uint8_t* src, bool negative, double& d) noexcept {
|
|
||||||
if (atomparsing::is_valid_inf_atom(src)) {
|
|
||||||
double inf = std::numeric_limits<double>::infinity();
|
|
||||||
d = negative ? -inf : inf;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (atomparsing::is_valid_nan_atom(src)) {
|
|
||||||
d = std::numeric_limits<double>::quiet_NaN();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// We call a fallback floating-point parser that might be slow. Note
|
// We call a fallback floating-point parser that might be slow. Note
|
||||||
// it will accept JSON numbers, but the JSON spec. is more restrictive so
|
// it will accept JSON numbers, but the JSON spec. is more restrictive so
|
||||||
// before you call parse_float_fallback, you need to have validated the input
|
// before you call parse_float_fallback, you need to have validated the input
|
||||||
@@ -497,7 +478,7 @@ simdjson_inline size_t significant_digits(const uint8_t * start_digits, size_t d
|
|||||||
} // unnamed namespace
|
} // unnamed namespace
|
||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
inline error_code slow_float_parsing(simdjson_unused const uint8_t * src, double* answer) {
|
static error_code slow_float_parsing(simdjson_unused const uint8_t * src, double* answer) {
|
||||||
if (parse_float_fallback(src, answer)) {
|
if (parse_float_fallback(src, answer)) {
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -619,21 +600,7 @@ simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *cons
|
|||||||
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
|
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
|
||||||
// Optimization note: size_t is expected to be unsigned.
|
// Optimization note: size_t is expected to be unsigned.
|
||||||
size_t digit_count = size_t(p - start_digits);
|
size_t digit_count = size_t(p - start_digits);
|
||||||
if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) {
|
if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) { return INVALID_NUMBER(src); }
|
||||||
|
|
||||||
#if SIMDJSON_ENABLE_NAN_INF
|
|
||||||
// By this point, we know that our input does not begin with a digit. We will attempt
|
|
||||||
// to handle NaN/Infinity.
|
|
||||||
|
|
||||||
double d;
|
|
||||||
if (compute_nan_inf(p, negative, d)) {
|
|
||||||
writer.append_double(d);
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return INVALID_NUMBER(src);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Handle floats if there is a . or e (or both)
|
// Handle floats if there is a . or e (or both)
|
||||||
@@ -1064,17 +1031,7 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
|
|||||||
bool leading_zero = (i == 0);
|
bool leading_zero = (i == 0);
|
||||||
while (parse_digit(*p, i)) { p++; }
|
while (parse_digit(*p, i)) { p++; }
|
||||||
// no integer digits, or 0123 (zero must be solo)
|
// no integer digits, or 0123 (zero must be solo)
|
||||||
if ( p == src ) {
|
if ( p == src ) { return INCORRECT_TYPE; }
|
||||||
|
|
||||||
#if SIMDJSON_ENABLE_NAN_INF
|
|
||||||
// If there are no loading digits, the number may be nan or infinity.
|
|
||||||
// Attempt to compute those, and return on success.
|
|
||||||
double d;
|
|
||||||
if (compute_nan_inf(p, negative, d)) { return d; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return INCORRECT_TYPE;
|
|
||||||
}
|
|
||||||
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
|
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -1292,22 +1249,7 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
|
|||||||
bool leading_zero = (i == 0);
|
bool leading_zero = (i == 0);
|
||||||
while (parse_digit(*p, i)) { p++; }
|
while (parse_digit(*p, i)) { p++; }
|
||||||
// no integer digits, or 0123 (zero must be solo)
|
// no integer digits, or 0123 (zero must be solo)
|
||||||
if ( p == src ) {
|
if ( p == src ) { return INCORRECT_TYPE; }
|
||||||
#if SIMDJSON_ENABLE_NAN_INF
|
|
||||||
// If there are no leading digits, attempt to parse numbers that are either
|
|
||||||
// NaN or Infinity
|
|
||||||
if (atomparsing::is_valid_inf_in_string(src)) {
|
|
||||||
double inf = std::numeric_limits<double>::infinity();
|
|
||||||
return negative ? -inf : inf;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (atomparsing::is_valid_nan_in_string(src)) {
|
|
||||||
return std::numeric_limits<double>::quiet_NaN();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return INCORRECT_TYPE;
|
|
||||||
}
|
|
||||||
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
|
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -20,9 +20,9 @@
|
|||||||
#include "simdjson/generic/ondemand/document.h"
|
#include "simdjson/generic/ondemand/document.h"
|
||||||
#include "simdjson/generic/ondemand/document_stream.h"
|
#include "simdjson/generic/ondemand/document_stream.h"
|
||||||
#include "simdjson/generic/ondemand/field.h"
|
#include "simdjson/generic/ondemand/field.h"
|
||||||
|
#include "simdjson/generic/ondemand/key_selector.h"
|
||||||
#include "simdjson/generic/ondemand/object.h"
|
#include "simdjson/generic/ondemand/object.h"
|
||||||
#include "simdjson/generic/ondemand/object_iterator.h"
|
#include "simdjson/generic/ondemand/object_iterator.h"
|
||||||
#include "simdjson/generic/ondemand/ranges.h"
|
|
||||||
#include "simdjson/generic/ondemand/serialization.h"
|
#include "simdjson/generic/ondemand/serialization.h"
|
||||||
|
|
||||||
// Deserialization for standard types
|
// Deserialization for standard types
|
||||||
@@ -40,7 +40,6 @@
|
|||||||
#include "simdjson/generic/ondemand/logger-inl.h"
|
#include "simdjson/generic/ondemand/logger-inl.h"
|
||||||
#include "simdjson/generic/ondemand/object-inl.h"
|
#include "simdjson/generic/ondemand/object-inl.h"
|
||||||
#include "simdjson/generic/ondemand/object_iterator-inl.h"
|
#include "simdjson/generic/ondemand/object_iterator-inl.h"
|
||||||
#include "simdjson/generic/ondemand/ranges-inl.h"
|
|
||||||
#include "simdjson/generic/ondemand/parser-inl.h"
|
#include "simdjson/generic/ondemand/parser-inl.h"
|
||||||
#include "simdjson/generic/ondemand/raw_json_string-inl.h"
|
#include "simdjson/generic/ondemand/raw_json_string-inl.h"
|
||||||
#include "simdjson/generic/ondemand/token_iterator-inl.h"
|
#include "simdjson/generic/ondemand/token_iterator-inl.h"
|
||||||
|
|||||||
@@ -135,9 +135,21 @@ inline simdjson_result<value> array::at_pointer(std::string_view json_pointer) n
|
|||||||
// We don't support this, because we're returning a real element, not a position.
|
// We don't support this, because we're returning a real element, not a position.
|
||||||
if (json_pointer == "-") { return INDEX_OUT_OF_BOUNDS; }
|
if (json_pointer == "-") { return INDEX_OUT_OF_BOUNDS; }
|
||||||
|
|
||||||
|
// Read the array index
|
||||||
size_t array_index = 0;
|
size_t array_index = 0;
|
||||||
size_t i;
|
size_t i;
|
||||||
SIMDJSON_TRY(internal::parse_json_pointer_array_index(json_pointer, array_index, i));
|
for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) {
|
||||||
|
uint8_t digit = uint8_t(json_pointer[i] - '0');
|
||||||
|
// Check for non-digit in array index. If it's there, we're trying to get a field in an object
|
||||||
|
if (digit > 9) { return INCORRECT_TYPE; }
|
||||||
|
array_index = array_index*10 + digit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0 followed by other digits is invalid
|
||||||
|
if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0"
|
||||||
|
|
||||||
|
// Empty string is invalid; so is a "/" with no digits before it
|
||||||
|
if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index"
|
||||||
// Get the child
|
// Get the child
|
||||||
auto child = at(array_index);
|
auto child = at(array_index);
|
||||||
// If there is an error, it ends here
|
// If there is an error, it ends here
|
||||||
@@ -158,34 +170,46 @@ inline simdjson_result<value> array::at_path(std::string_view json_path) noexcep
|
|||||||
return at_pointer(json_pointer);
|
return at_pointer(json_pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
inline simdjson_result<std::vector<value>> array::at_path_with_wildcard(std::string_view json_path) noexcept {
|
||||||
template <typename Func>
|
std::vector<value> result;
|
||||||
requires std::invocable<Func, value>
|
|
||||||
#else
|
|
||||||
template <typename Func>
|
|
||||||
#endif
|
|
||||||
inline error_code array::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
|
|
||||||
auto result_pair = get_next_key_and_json_path(json_path);
|
auto result_pair = get_next_key_and_json_path(json_path);
|
||||||
std::string_view key = result_pair.first;
|
std::string_view key = result_pair.first;
|
||||||
std::string_view remaining_path = result_pair.second;
|
std::string_view remaining_path = result_pair.second;
|
||||||
// Wildcard case
|
// Wildcard case
|
||||||
if (key=="*"){
|
if(key=="*"){
|
||||||
for(auto element: *this) {
|
for(auto element: *this){
|
||||||
value val;
|
|
||||||
SIMDJSON_TRY(element.get(val));
|
if(element.error()){
|
||||||
if (remaining_path.empty()) {
|
return element.error();
|
||||||
callback(val);
|
}
|
||||||
} else {
|
|
||||||
error_code err = element.for_each_at_path_with_wildcard(remaining_path, callback);
|
if(remaining_path.empty()){
|
||||||
if(err) { return err; }
|
// Use value_unsafe() because we've already checked for errors above.
|
||||||
|
// The 'element' is a simdjson_result<value> wrapper, and we need to extract
|
||||||
|
// the underlying value. value_unsafe() is safe here because error() returned false.
|
||||||
|
result.push_back(std::move(element).value_unsafe());
|
||||||
|
|
||||||
|
}else{
|
||||||
|
auto nested_result = element.at_path_with_wildcard(remaining_path);
|
||||||
|
|
||||||
|
if(nested_result.error()){
|
||||||
|
return nested_result.error();
|
||||||
|
}
|
||||||
|
// Same logic as above.
|
||||||
|
std::vector<value> nested_matches = std::move(nested_result).value_unsafe();
|
||||||
|
|
||||||
|
result.insert(result.end(),
|
||||||
|
std::make_move_iterator(nested_matches.begin()),
|
||||||
|
std::make_move_iterator(nested_matches.end()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return SUCCESS;
|
return result;
|
||||||
} else {
|
}else{
|
||||||
// Specific index case in which we access the element at the given index
|
// Specific index case in which we access the element at the given index
|
||||||
size_t idx = 0;
|
size_t idx=0;
|
||||||
|
|
||||||
for (char c : key) {
|
for(char c:key){
|
||||||
if(c < '0' || c > '9'){
|
if(c < '0' || c > '9'){
|
||||||
return INVALID_JSON_POINTER;
|
return INVALID_JSON_POINTER;
|
||||||
}
|
}
|
||||||
@@ -193,13 +217,16 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto element = at(idx);
|
auto element = at(idx);
|
||||||
value val;
|
|
||||||
SIMDJSON_TRY(element.get(val));
|
if(element.error()){
|
||||||
if (remaining_path.empty()){
|
return element.error();
|
||||||
callback(val);
|
}
|
||||||
return SUCCESS;
|
|
||||||
} else {
|
if(remaining_path.empty()){
|
||||||
return element.for_each_at_path_with_wildcard(remaining_path, callback);
|
result.push_back(std::move(element).value_unsafe());
|
||||||
|
return result;
|
||||||
|
}else{
|
||||||
|
return element.at_path_with_wildcard(remaining_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,15 +289,9 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdj
|
|||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.at_path(json_path);
|
return first.at_path(json_path);
|
||||||
}
|
}
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::at_path_with_wildcard(std::string_view json_path) noexcept {
|
||||||
template <typename Func>
|
|
||||||
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
|
|
||||||
#else
|
|
||||||
template <typename Func>
|
|
||||||
#endif
|
|
||||||
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
|
|
||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
|
return first.at_path_with_wildcard(json_path);
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::raw_json() noexcept {
|
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::raw_json() noexcept {
|
||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
|
|||||||
@@ -119,21 +119,13 @@ public:
|
|||||||
inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
|
inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call the provided callback for each value matching the given JSONPath
|
* Get all values matching the given JSONPath expression with wildcard support.
|
||||||
* expression with wildcard support.
|
|
||||||
* Supports wildcard patterns like "[*]" to match all array elements.
|
* Supports wildcard patterns like "[*]" to match all array elements.
|
||||||
*
|
*
|
||||||
* @param json_path JSONPath expression with wildcards
|
* @param json_path JSONPath expression with wildcards
|
||||||
* @param callback Function called for each matching value
|
* @return Vector of values matching the wildcard pattern
|
||||||
* @return error_code indicating success or failure
|
|
||||||
*/
|
*/
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
inline simdjson_result<std::vector<value>> at_path_with_wildcard(std::string_view json_path) noexcept;
|
||||||
template <typename Func>
|
|
||||||
requires std::invocable<Func, value>
|
|
||||||
#else
|
|
||||||
template <typename Func>
|
|
||||||
#endif
|
|
||||||
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Consumes the array and returns a string_view instance corresponding to the
|
* Consumes the array and returns a string_view instance corresponding to the
|
||||||
@@ -257,13 +249,7 @@ public:
|
|||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at(size_t index) noexcept;
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at(size_t index) noexcept;
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> at_path_with_wildcard(std::string_view json_path) noexcept;
|
||||||
template <typename Func>
|
|
||||||
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
|
|
||||||
#else
|
|
||||||
template <typename Func>
|
|
||||||
#endif
|
|
||||||
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
|
|
||||||
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
|
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||||
// TODO: move this code into object-inl.h
|
// TODO: move this code into object-inl.h
|
||||||
|
|||||||
@@ -40,13 +40,6 @@ class token_iterator;
|
|||||||
class value;
|
class value;
|
||||||
class value_iterator;
|
class value_iterator;
|
||||||
|
|
||||||
#if SIMDJSON_SUPPORTS_RANGES
|
|
||||||
class array_range;
|
|
||||||
class array_range_iterator;
|
|
||||||
class object_range;
|
|
||||||
class object_range_iterator;
|
|
||||||
#endif // SIMDJSON_SUPPORTS_RANGES
|
|
||||||
|
|
||||||
} // namespace ondemand
|
} // namespace ondemand
|
||||||
} // namespace SIMDJSON_IMPLEMENTATION
|
} // namespace SIMDJSON_IMPLEMENTATION
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|||||||
@@ -14,6 +14,5 @@
|
|||||||
#include "simdjson/padded_string_view.h"
|
#include "simdjson/padded_string_view.h"
|
||||||
#include "simdjson/internal/dom_parser_implementation.h"
|
#include "simdjson/internal/dom_parser_implementation.h"
|
||||||
#include "simdjson/jsonpathutil.h"
|
#include "simdjson/jsonpathutil.h"
|
||||||
#include "simdjson/annotations.h"
|
|
||||||
|
|
||||||
#endif // SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H
|
#endif // SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H
|
||||||
@@ -365,14 +365,8 @@ simdjson_inline simdjson_result<value> document::at_path(std::string_view json_p
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
simdjson_inline simdjson_result<std::vector<value>> document::at_path_with_wildcard(std::string_view json_path) noexcept {
|
||||||
template <typename Func>
|
rewind(); // Rewind the document each time at_path_with_wildcard is called
|
||||||
requires std::invocable<Func, value>
|
|
||||||
#else
|
|
||||||
template <typename Func>
|
|
||||||
#endif
|
|
||||||
simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
|
|
||||||
rewind(); // Rewind the document each time for_each_at_path_with_wildcard is called
|
|
||||||
if (json_path.empty()) {
|
if (json_path.empty()) {
|
||||||
return INVALID_JSON_POINTER;
|
return INVALID_JSON_POINTER;
|
||||||
}
|
}
|
||||||
@@ -380,9 +374,9 @@ simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_
|
|||||||
SIMDJSON_TRY(type().get(t));
|
SIMDJSON_TRY(type().get(t));
|
||||||
switch (t) {
|
switch (t) {
|
||||||
case json_type::array:
|
case json_type::array:
|
||||||
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
|
return (*this).get_array().at_path_with_wildcard(json_path);
|
||||||
case json_type::object:
|
case json_type::object:
|
||||||
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
|
return (*this).get_object().at_path_with_wildcard(json_path);
|
||||||
default:
|
default:
|
||||||
return INVALID_JSON_POINTER;
|
return INVALID_JSON_POINTER;
|
||||||
}
|
}
|
||||||
@@ -719,15 +713,9 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
|
|||||||
return first.at_path(json_path);
|
return first.at_path(json_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::at_path_with_wildcard(std::string_view json_path) noexcept {
|
||||||
template <typename Func>
|
|
||||||
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
|
|
||||||
#else
|
|
||||||
template <typename Func>
|
|
||||||
#endif
|
|
||||||
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
|
|
||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
|
return first.at_path_with_wildcard(json_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
@@ -838,13 +826,7 @@ simdjson_inline simdjson_result<number> document_reference::get_number() noexcep
|
|||||||
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json_token() noexcept { return doc->raw_json_token(); }
|
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json_token() noexcept { return doc->raw_json_token(); }
|
||||||
simdjson_inline simdjson_result<value> document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); }
|
simdjson_inline simdjson_result<value> document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); }
|
||||||
simdjson_inline simdjson_result<value> document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); }
|
simdjson_inline simdjson_result<value> document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); }
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
simdjson_inline simdjson_result<std::vector<value>> document_reference::at_path_with_wildcard(std::string_view json_path) noexcept { return doc->at_path_with_wildcard(json_path); }
|
||||||
template <typename Func>
|
|
||||||
requires std::invocable<Func, value>
|
|
||||||
#else
|
|
||||||
template <typename Func>
|
|
||||||
#endif
|
|
||||||
simdjson_inline error_code document_reference::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept { return doc->for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback)); }
|
|
||||||
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json() noexcept { return doc->raw_json();}
|
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json() noexcept { return doc->raw_json();}
|
||||||
simdjson_inline document_reference::operator document&() const noexcept { return *doc; }
|
simdjson_inline document_reference::operator document&() const noexcept { return *doc; }
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
|
||||||
@@ -1109,17 +1091,11 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
|
|||||||
}
|
}
|
||||||
return first.at_path(json_path);
|
return first.at_path(json_path);
|
||||||
}
|
}
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::at_path_with_wildcard(std::string_view json_path) noexcept {
|
||||||
template <typename Func>
|
|
||||||
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
|
|
||||||
#else
|
|
||||||
template <typename Func>
|
|
||||||
#endif
|
|
||||||
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
|
|
||||||
if (error()) {
|
if (error()) {
|
||||||
return error();
|
return error();
|
||||||
}
|
}
|
||||||
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
|
return first.at_path_with_wildcard(json_path);
|
||||||
}
|
}
|
||||||
#if SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
template<constevalutil::fixed_string... FieldNames, typename T>
|
template<constevalutil::fixed_string... FieldNames, typename T>
|
||||||
|
|||||||
@@ -744,24 +744,21 @@ public:
|
|||||||
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
|
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call the provided callback for each value matching the given JSONPath
|
* Get all values matching the given JSONPath expression with wildcard support.
|
||||||
* expression with wildcard support.
|
|
||||||
*
|
*
|
||||||
* Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements.
|
* Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements.
|
||||||
*
|
*
|
||||||
|
* This method materializes all matching values into a vector.
|
||||||
* The document will be consumed after this call.
|
* The document will be consumed after this call.
|
||||||
*
|
*
|
||||||
* @param json_path JSONPath expression with wildcards
|
* @param json_path JSONPath expression with wildcards
|
||||||
* @param callback Function called for each matching value
|
* @return Vector of values matching the wildcard pattern, or:
|
||||||
* @return error_code indicating success or failure
|
* - INVALID_JSON_POINTER if the JSONPath cannot be parsed
|
||||||
|
* - NO_SUCH_FIELD if a field does not exist
|
||||||
|
* - INDEX_OUT_OF_BOUNDS if an array index is out of bounds
|
||||||
|
* - INCORRECT_TYPE if path traversal encounters wrong type
|
||||||
*/
|
*/
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
simdjson_inline simdjson_result<std::vector<value>> at_path_with_wildcard(std::string_view json_path) noexcept;
|
||||||
template <typename Func>
|
|
||||||
requires std::invocable<Func, value>
|
|
||||||
#else
|
|
||||||
template <typename Func>
|
|
||||||
#endif
|
|
||||||
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Consumes the document and returns a string_view instance corresponding to the
|
* Consumes the document and returns a string_view instance corresponding to the
|
||||||
@@ -982,13 +979,7 @@ public:
|
|||||||
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
|
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
|
||||||
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
|
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
|
||||||
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
|
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
simdjson_inline simdjson_result<std::vector<value>> at_path_with_wildcard(std::string_view json_path) noexcept;
|
||||||
template <typename Func>
|
|
||||||
requires std::invocable<Func, value>
|
|
||||||
#else
|
|
||||||
template <typename Func>
|
|
||||||
#endif
|
|
||||||
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
document *doc{nullptr};
|
document *doc{nullptr};
|
||||||
@@ -1074,13 +1065,7 @@ public:
|
|||||||
|
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> at_path_with_wildcard(std::string_view json_path) noexcept;
|
||||||
template <typename Func>
|
|
||||||
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
|
|
||||||
#else
|
|
||||||
template <typename Func>
|
|
||||||
#endif
|
|
||||||
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
|
|
||||||
#if SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
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))
|
||||||
@@ -1165,13 +1150,7 @@ public:
|
|||||||
|
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> at_path_with_wildcard(std::string_view json_path) noexcept;
|
||||||
template <typename Func>
|
|
||||||
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
|
|
||||||
#else
|
|
||||||
template <typename Func>
|
|
||||||
#endif
|
|
||||||
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
|
|
||||||
#if SIMDJSON_STATIC_REFLECTION
|
#if SIMDJSON_STATIC_REFLECTION
|
||||||
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))
|
||||||
|
|||||||
@@ -398,7 +398,7 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
|
|||||||
// legally appear in a JSON value at the source level (control
|
// legally appear in a JSON value at the source level (control
|
||||||
// characters in strings must be escaped as \u001E), so stripping
|
// characters in strings must be escaped as \u001E), so stripping
|
||||||
// it is safe in every stream_format.
|
// it is safe in every stream_format.
|
||||||
while(svlen > 1 && (std::isspace(static_cast<unsigned char>(start[svlen-1])) || start[svlen-1] == '\0' || static_cast<uint8_t>(start[svlen-1]) == 0x1E || (stream->format == stream_format::comma_delimited && start[svlen-1] == ','))) {
|
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);
|
||||||
|
|||||||
@@ -24,9 +24,6 @@ simdjson_inline json_iterator::json_iterator(json_iterator &&other) noexcept
|
|||||||
_depth{other._depth},
|
_depth{other._depth},
|
||||||
_root{other._root},
|
_root{other._root},
|
||||||
_streaming{other._streaming}
|
_streaming{other._streaming}
|
||||||
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
|
||||||
, _allow_incomplete_json{other._allow_incomplete_json}
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
other.parser = nullptr;
|
other.parser = nullptr;
|
||||||
}
|
}
|
||||||
@@ -38,9 +35,6 @@ simdjson_inline json_iterator &json_iterator::operator=(json_iterator &&other) n
|
|||||||
_depth = other._depth;
|
_depth = other._depth;
|
||||||
_root = other._root;
|
_root = other._root;
|
||||||
_streaming = other._streaming;
|
_streaming = other._streaming;
|
||||||
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
|
||||||
_allow_incomplete_json = other._allow_incomplete_json;
|
|
||||||
#endif
|
|
||||||
other.parser = nullptr;
|
other.parser = nullptr;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -67,8 +61,7 @@ simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parse
|
|||||||
_string_buf_loc{parser->string_buf.get()},
|
_string_buf_loc{parser->string_buf.get()},
|
||||||
_depth{1},
|
_depth{1},
|
||||||
_root{parser->implementation->structural_indexes.get()},
|
_root{parser->implementation->structural_indexes.get()},
|
||||||
_streaming{streaming},
|
_streaming{streaming}
|
||||||
_allow_incomplete_json{true}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
logger::log_headers();
|
logger::log_headers();
|
||||||
@@ -200,17 +193,6 @@ simdjson_inline bool json_iterator::streaming() const noexcept {
|
|||||||
return _streaming;
|
return _streaming;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
|
||||||
simdjson_inline bool json_iterator::allow_incomplete_json() const noexcept {
|
|
||||||
return _allow_incomplete_json;
|
|
||||||
}
|
|
||||||
|
|
||||||
simdjson_inline size_t json_iterator::remaining_input_length(const uint8_t *json) const noexcept {
|
|
||||||
const uint8_t *end = token.buf + parser->_document_len;
|
|
||||||
return json < end ? size_t(end - json) : 0;
|
|
||||||
}
|
|
||||||
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
|
||||||
|
|
||||||
simdjson_inline token_position json_iterator::root_position() const noexcept {
|
simdjson_inline token_position json_iterator::root_position() const noexcept {
|
||||||
return _root;
|
return _root;
|
||||||
}
|
}
|
||||||
@@ -468,4 +450,4 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_iterator
|
|||||||
|
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|
||||||
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
|
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
|
||||||
@@ -60,9 +60,6 @@ protected:
|
|||||||
* value of this attribute.
|
* value of this attribute.
|
||||||
*/
|
*/
|
||||||
bool _streaming{false};
|
bool _streaming{false};
|
||||||
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
|
||||||
bool _allow_incomplete_json{false};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
simdjson_inline json_iterator() noexcept = default;
|
simdjson_inline json_iterator() noexcept = default;
|
||||||
@@ -87,10 +84,6 @@ public:
|
|||||||
* start_root_array() and start_root_object().
|
* start_root_array() and start_root_object().
|
||||||
*/
|
*/
|
||||||
simdjson_inline bool streaming() const noexcept;
|
simdjson_inline bool streaming() const noexcept;
|
||||||
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
|
||||||
simdjson_inline bool allow_incomplete_json() const noexcept;
|
|
||||||
simdjson_inline size_t remaining_input_length(const uint8_t *json) const noexcept;
|
|
||||||
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the root value iterator
|
* Get the root value iterator
|
||||||
@@ -342,4 +335,4 @@ public:
|
|||||||
|
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|
||||||
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H
|
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H
|
||||||
@@ -0,0 +1,339 @@
|
|||||||
|
#ifndef SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
|
||||||
|
#define SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
|
||||||
|
|
||||||
|
#include "simdjson/base.h"
|
||||||
|
#include "simdjson/common_defs.h"
|
||||||
|
#include <array>
|
||||||
|
#include <string_view>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||||
|
|
||||||
|
namespace simdjson {
|
||||||
|
namespace SIMDJSON_IMPLEMENTATION {
|
||||||
|
namespace ondemand {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Forward declaration
|
||||||
|
class object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A compile-time key selector for efficient JSON object field lookup.
|
||||||
|
* Uses perfect hashing (gperf-style) to map keys to identifiers.
|
||||||
|
*/
|
||||||
|
template <std::size_t N>
|
||||||
|
class key_selector {
|
||||||
|
static_assert(N > 0, "key_selector requires at least one key");
|
||||||
|
static_assert(N <= 100, "key_selector supports at most 100 keys");
|
||||||
|
|
||||||
|
// Perfect hash table data (gperf-style)
|
||||||
|
static constexpr std::size_t MAX_POSITIONS = 16;
|
||||||
|
static constexpr std::size_t POS_LAST_CHAR = std::size_t(-1);
|
||||||
|
static constexpr std::size_t MAX_TABLE_SIZE = 256; // Power of 2, fits in uint8_t
|
||||||
|
|
||||||
|
std::array<std::array<std::uint8_t, 256>, MAX_POSITIONS> asso_values_{};
|
||||||
|
std::uint8_t num_positions_{};
|
||||||
|
std::array<std::size_t, MAX_POSITIONS> positions_{};
|
||||||
|
std::array<std::uint8_t, MAX_TABLE_SIZE> slot_to_key_{};
|
||||||
|
std::array<std::uint8_t, N> key_to_slot_{};
|
||||||
|
std::array<std::array<char, 64>, N> key_data_{};
|
||||||
|
std::array<std::uint8_t, N> key_lengths_{};
|
||||||
|
std::size_t table_size_{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Validate keys at compile time
|
||||||
|
constexpr void validate_keys(const std::array<std::string_view, N>& keys) {
|
||||||
|
for (std::size_t i = 0; i < N; ++i) {
|
||||||
|
auto key = keys[i];
|
||||||
|
if (key.empty()) {
|
||||||
|
throw "Empty keys are not allowed in key_selector";
|
||||||
|
}
|
||||||
|
if (key.size() > SIMDJSON_PADDING) {
|
||||||
|
throw "Key length exceeds SIMDJSON_PADDING (64 bytes)";
|
||||||
|
}
|
||||||
|
for (char c : key) {
|
||||||
|
if (c == '\\') {
|
||||||
|
throw "Escape characters (\\) are not allowed in key_selector keys";
|
||||||
|
}
|
||||||
|
if (c == '\0') {
|
||||||
|
throw "Null characters are not allowed in key_selector keys";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gperf-style perfect hash generation using partition-based algorithm
|
||||||
|
constexpr void generate_hash_table(const std::array<std::string_view, N>& keys) {
|
||||||
|
// Try power-of-two table sizes starting from next_power_of_2(N)
|
||||||
|
constexpr std::size_t START_M = next_power_of_2(N);
|
||||||
|
if constexpr (START_M <= MAX_TABLE_SIZE) {
|
||||||
|
if (try_compute_phf<START_M>(keys)) return;
|
||||||
|
if constexpr (START_M * 2 <= MAX_TABLE_SIZE) {
|
||||||
|
if (try_compute_phf<START_M * 2>(keys)) return;
|
||||||
|
if constexpr (START_M * 4 <= MAX_TABLE_SIZE) {
|
||||||
|
if (try_compute_phf<START_M * 4>(keys)) return;
|
||||||
|
if constexpr (START_M * 8 <= MAX_TABLE_SIZE) {
|
||||||
|
if (try_compute_phf<START_M * 8>(keys)) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: linear table
|
||||||
|
table_size_ = N;
|
||||||
|
num_positions_ = 0;
|
||||||
|
std::fill(slot_to_key_.begin(), slot_to_key_.begin() + MAX_TABLE_SIZE, static_cast<std::uint8_t>(N));
|
||||||
|
for (std::size_t i = 0; i < N; ++i) {
|
||||||
|
slot_to_key_[i] = static_cast<std::uint8_t>(i);
|
||||||
|
key_to_slot_[i] = static_cast<std::uint8_t>(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Helper functions for gperf algorithm
|
||||||
|
static constexpr std::size_t next_power_of_2(std::size_t n) {
|
||||||
|
if (n == 0) return 1;
|
||||||
|
std::size_t p = 1;
|
||||||
|
while (p < n) p <<= 1;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr std::size_t char_at(std::string_view key, std::size_t pos) {
|
||||||
|
if (pos == POS_LAST_CHAR) {
|
||||||
|
return key.empty() ? 256 : static_cast<unsigned char>(key.back());
|
||||||
|
}
|
||||||
|
return (pos < key.size()) ? static_cast<unsigned char>(key[pos]) : 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t M>
|
||||||
|
constexpr bool try_compute_phf(const std::array<std::string_view, N>& keys) {
|
||||||
|
// Initialize
|
||||||
|
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
|
||||||
|
std::size_t npos = 0;
|
||||||
|
std::array<std::size_t, MAX_POSITIONS> pos{};
|
||||||
|
std::array<std::size_t, M> s2k{};
|
||||||
|
|
||||||
|
// Try to generate gperf
|
||||||
|
if (try_generate_gperf<M>(keys, asso, npos, pos, s2k)) {
|
||||||
|
table_size_ = M;
|
||||||
|
num_positions_ = static_cast<std::uint8_t>(npos);
|
||||||
|
for (std::size_t p = 0; p < MAX_POSITIONS; ++p) {
|
||||||
|
positions_[p] = pos[p];
|
||||||
|
for (std::size_t c = 0; c < 256; ++c) {
|
||||||
|
asso_values_[p][c] = static_cast<std::uint8_t>(asso[p][c]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (std::size_t i = 0; i < M; ++i) {
|
||||||
|
slot_to_key_[i] = static_cast<std::uint8_t>(s2k[i]);
|
||||||
|
}
|
||||||
|
// Fill remaining slots with sentinel
|
||||||
|
for (std::size_t i = M; i < MAX_TABLE_SIZE; ++i) {
|
||||||
|
slot_to_key_[i] = static_cast<std::uint8_t>(N);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build key_to_slot mapping
|
||||||
|
for (std::size_t i = 0; i < N; ++i) {
|
||||||
|
key_to_slot_[i] = static_cast<std::uint8_t>(N); // Initialize
|
||||||
|
}
|
||||||
|
for (std::size_t slot = 0; slot < M; ++slot) {
|
||||||
|
std::size_t key_idx = s2k[slot];
|
||||||
|
if (key_idx < N) {
|
||||||
|
key_to_slot_[key_idx] = static_cast<std::uint8_t>(slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t M>
|
||||||
|
static constexpr bool try_generate_gperf(
|
||||||
|
const std::array<std::string_view, N>& keys,
|
||||||
|
std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
|
||||||
|
std::size_t& num_positions,
|
||||||
|
std::array<std::size_t, MAX_POSITIONS>& positions,
|
||||||
|
std::array<std::size_t, M>& slot_to_key)
|
||||||
|
{
|
||||||
|
// Initialize
|
||||||
|
for (std::size_t p = 0; p < MAX_POSITIONS; ++p) {
|
||||||
|
for (std::size_t c = 0; c < 256; ++c) {
|
||||||
|
asso_values[p][c] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (std::size_t i = 0; i < M; ++i) {
|
||||||
|
slot_to_key[i] = N;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try length-only hashing first
|
||||||
|
bool success = true;
|
||||||
|
for (std::size_t i = 0; i < N && success; ++i) {
|
||||||
|
std::size_t slot = keys[i].size() % M;
|
||||||
|
if (slot_to_key[slot] != N) {
|
||||||
|
success = false;
|
||||||
|
} else {
|
||||||
|
slot_to_key[slot] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
num_positions = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try with position 0
|
||||||
|
positions[0] = 0;
|
||||||
|
num_positions = 1;
|
||||||
|
|
||||||
|
// Find a working assignment of asso_values for position 0
|
||||||
|
// Use a simple approach: try different offsets
|
||||||
|
for (std::size_t offset = 0; offset < M; ++offset) {
|
||||||
|
// Reset
|
||||||
|
for (std::size_t i = 0; i < M; ++i) {
|
||||||
|
slot_to_key[i] = N;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign asso_values based on offset
|
||||||
|
for (std::size_t c = 0; c < 256; ++c) {
|
||||||
|
asso_values[0][c] = (c + offset) % M;
|
||||||
|
}
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
for (std::size_t i = 0; i < N && success; ++i) {
|
||||||
|
std::size_t h = keys[i].size();
|
||||||
|
std::size_t ch = char_at(keys[i], 0);
|
||||||
|
if (ch < 256) h += asso_values[0][ch];
|
||||||
|
std::size_t slot = h % M;
|
||||||
|
|
||||||
|
if (slot_to_key[slot] != N) {
|
||||||
|
success = false;
|
||||||
|
} else {
|
||||||
|
slot_to_key[slot] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try with positions {0, last_char}
|
||||||
|
if (N <= 50) { // Only for smaller N to avoid complexity
|
||||||
|
positions[0] = 0;
|
||||||
|
positions[1] = POS_LAST_CHAR;
|
||||||
|
num_positions = 2;
|
||||||
|
|
||||||
|
for (std::size_t offset1 = 0; offset1 < 4 && !success; ++offset1) {
|
||||||
|
for (std::size_t offset2 = 0; offset2 < 4 && !success; ++offset2) {
|
||||||
|
// Reset
|
||||||
|
for (std::size_t i = 0; i < M; ++i) {
|
||||||
|
slot_to_key[i] = N;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign asso_values
|
||||||
|
for (std::size_t c = 0; c < 256; ++c) {
|
||||||
|
asso_values[0][c] = (c + offset1) % M;
|
||||||
|
asso_values[1][c] = (c + offset2) % M;
|
||||||
|
}
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
for (std::size_t i = 0; i < N && success; ++i) {
|
||||||
|
std::size_t h = keys[i].size();
|
||||||
|
std::size_t ch1 = char_at(keys[i], 0);
|
||||||
|
if (ch1 < 256) h += asso_values[0][ch1];
|
||||||
|
std::size_t ch2 = char_at(keys[i], POS_LAST_CHAR);
|
||||||
|
if (ch2 < 256) h += asso_values[1][ch2];
|
||||||
|
std::size_t slot = h % M;
|
||||||
|
|
||||||
|
if (slot_to_key[slot] != N) {
|
||||||
|
success = false;
|
||||||
|
} else {
|
||||||
|
slot_to_key[slot] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr key_selector(const std::array<std::string_view, N>& keys) {
|
||||||
|
validate_keys(keys);
|
||||||
|
|
||||||
|
// Store key data
|
||||||
|
for (std::size_t i = 0; i < N; ++i) {
|
||||||
|
key_lengths_[i] = static_cast<std::uint8_t>(keys[i].size());
|
||||||
|
std::copy(keys[i].begin(), keys[i].end(), key_data_[i].begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_hash_table(keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr std::size_t size() const noexcept { return N; }
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr simdjson_really_inline std::size_t compute_hash(std::string_view key) const noexcept {
|
||||||
|
std::size_t h = key.size();
|
||||||
|
const char* kp = key.data();
|
||||||
|
for (std::uint8_t i = 0; i < num_positions_; ++i) {
|
||||||
|
std::size_t pos = positions_[i];
|
||||||
|
std::size_t ch;
|
||||||
|
if (pos == POS_LAST_CHAR) {
|
||||||
|
ch = static_cast<unsigned char>(key.back());
|
||||||
|
} else {
|
||||||
|
ch = static_cast<unsigned char>(kp[pos]);
|
||||||
|
}
|
||||||
|
h += asso_values_[i][ch];
|
||||||
|
}
|
||||||
|
return h & (table_size_ - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr simdjson_really_inline bool contains(std::string_view key) const noexcept {
|
||||||
|
std::size_t slot = compute_hash(key);
|
||||||
|
if (slot >= table_size_) return false;
|
||||||
|
|
||||||
|
std::uint8_t key_idx = slot_to_key_[slot];
|
||||||
|
if (key_idx >= N) return false;
|
||||||
|
|
||||||
|
// Compare key
|
||||||
|
if (key_lengths_[key_idx] != key.size()) return false;
|
||||||
|
return std::equal(key.begin(), key.end(), key_data_[key_idx].begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr simdjson_really_inline std::size_t index_of(std::string_view key) const noexcept {
|
||||||
|
std::size_t slot = compute_hash(key);
|
||||||
|
if (slot >= table_size_) return N; // Invalid index
|
||||||
|
|
||||||
|
std::uint8_t key_idx = slot_to_key_[slot];
|
||||||
|
if (key_idx >= N) return N;
|
||||||
|
|
||||||
|
// Compare key
|
||||||
|
if (key_lengths_[key_idx] != key.size()) return N;
|
||||||
|
if (!std::equal(key.begin(), key.end(), key_data_[key_idx].begin())) return N;
|
||||||
|
|
||||||
|
return key_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accessors for key data (used by object::find_field)
|
||||||
|
[[nodiscard]] constexpr std::string_view get_key(std::size_t index) const noexcept {
|
||||||
|
if (index >= N) return {};
|
||||||
|
return std::string_view(key_data_[index].data(), key_lengths_[index]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ondemand
|
||||||
|
} // namespace SIMDJSON_IMPLEMENTATION
|
||||||
|
} // namespace simdjson
|
||||||
|
|
||||||
|
#endif // SIMDJSON_SUPPORTS_CONCEPTS
|
||||||
|
|
||||||
|
#endif // SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
|
||||||
@@ -63,6 +63,42 @@ simdjson_inline simdjson_result<value> object::find_field(const std::string_view
|
|||||||
return value(iter.child());
|
return value(iter.child());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||||
|
template <concepts::key_selector_type Selector>
|
||||||
|
simdjson_inline std::pair<std::size_t, simdjson_result<value>> object::find_field(const Selector& selector) & noexcept {
|
||||||
|
// Try to find any of the keys in the selector
|
||||||
|
for (std::size_t i = 0; i < selector.size(); ++i) {
|
||||||
|
std::string_view key = selector.get_key(i);
|
||||||
|
auto result = iter.find_field_unordered_raw(key);
|
||||||
|
if (result.error()) {
|
||||||
|
return {selector.size(), result.error()};
|
||||||
|
}
|
||||||
|
bool has_value = result.value();
|
||||||
|
if (has_value) {
|
||||||
|
return {i, value(iter.child())};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {selector.size(), NO_SUCH_FIELD}; // Return size() as invalid index
|
||||||
|
}
|
||||||
|
|
||||||
|
template <concepts::key_selector_type Selector>
|
||||||
|
simdjson_inline std::pair<std::size_t, simdjson_result<value>> object::find_field(const Selector& selector) && noexcept {
|
||||||
|
// Try to find any of the keys in the selector
|
||||||
|
for (std::size_t i = 0; i < selector.size(); ++i) {
|
||||||
|
std::string_view key = selector.get_key(i);
|
||||||
|
auto result = iter.find_field_unordered_raw(key);
|
||||||
|
if (result.error()) {
|
||||||
|
return {selector.size(), result.error()};
|
||||||
|
}
|
||||||
|
bool has_value = result.value();
|
||||||
|
if (has_value) {
|
||||||
|
return {i, value(iter.child())};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {selector.size(), NO_SUCH_FIELD}; // Return size() as invalid index
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
simdjson_inline simdjson_result<object> object::start(value_iterator &iter) noexcept {
|
simdjson_inline simdjson_result<object> object::start(value_iterator &iter) noexcept {
|
||||||
SIMDJSON_TRY( iter.start_object().error() );
|
SIMDJSON_TRY( iter.start_object().error() );
|
||||||
return object(iter);
|
return object(iter);
|
||||||
@@ -177,13 +213,9 @@ inline simdjson_result<value> object::at_path(std::string_view json_path) noexce
|
|||||||
return at_pointer(json_pointer);
|
return at_pointer(json_pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
inline simdjson_result<std::vector<value>> object::at_path_with_wildcard(std::string_view json_path) noexcept {
|
||||||
template <typename Func>
|
std::vector<value> result;
|
||||||
requires std::invocable<Func, value>
|
|
||||||
#else
|
|
||||||
template <typename Func>
|
|
||||||
#endif
|
|
||||||
inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
|
|
||||||
auto result_pair = get_next_key_and_json_path(json_path);
|
auto result_pair = get_next_key_and_json_path(json_path);
|
||||||
std::string_view key = result_pair.first;
|
std::string_view key = result_pair.first;
|
||||||
std::string_view remaining_path = result_pair.second;
|
std::string_view remaining_path = result_pair.second;
|
||||||
@@ -193,22 +225,34 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p
|
|||||||
for (auto field : *this) {
|
for (auto field : *this) {
|
||||||
value val;
|
value val;
|
||||||
SIMDJSON_TRY(field.value().get(val));
|
SIMDJSON_TRY(field.value().get(val));
|
||||||
|
|
||||||
if (remaining_path.empty()) {
|
if (remaining_path.empty()) {
|
||||||
callback(val);
|
result.push_back(std::move(val));
|
||||||
} else {
|
} else {
|
||||||
SIMDJSON_TRY(val.for_each_at_path_with_wildcard(remaining_path, callback));
|
auto nested_result = val.at_path_with_wildcard(remaining_path);
|
||||||
|
|
||||||
|
if (nested_result.error()) {
|
||||||
|
return nested_result.error();
|
||||||
|
}
|
||||||
|
// Extract and append all nested matches to our result
|
||||||
|
std::vector<value> nested_vec;
|
||||||
|
SIMDJSON_TRY(std::move(nested_result).get(nested_vec));
|
||||||
|
|
||||||
|
result.insert(result.end(),
|
||||||
|
std::make_move_iterator(nested_vec.begin()),
|
||||||
|
std::make_move_iterator(nested_vec.end()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return SUCCESS;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
value val;
|
value val;
|
||||||
SIMDJSON_TRY(find_field(key).get(val));
|
SIMDJSON_TRY(find_field(key).get(val));
|
||||||
|
|
||||||
if (remaining_path.empty()) {
|
if (remaining_path.empty()) {
|
||||||
callback(val);
|
result.push_back(std::move(val));
|
||||||
return SUCCESS;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
return val.for_each_at_path_with_wildcard(remaining_path, callback);
|
return val.at_path_with_wildcard(remaining_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -326,6 +370,20 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
|
|||||||
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::object>(first).find_field(key);
|
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::object>(first).find_field(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||||
|
template <concepts::key_selector_type Selector>
|
||||||
|
simdjson_inline std::pair<std::size_t, simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::find_field(const Selector& selector) & noexcept {
|
||||||
|
if (error()) { return {0, error()}; }
|
||||||
|
return first.find_field(selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <concepts::key_selector_type Selector>
|
||||||
|
simdjson_inline std::pair<std::size_t, simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::find_field(const Selector& selector) && noexcept {
|
||||||
|
if (error()) { return {0, error()}; }
|
||||||
|
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::object>(first).find_field(selector);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::at_pointer(std::string_view json_pointer) noexcept {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::at_pointer(std::string_view json_pointer) noexcept {
|
||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.at_pointer(json_pointer);
|
return first.at_pointer(json_pointer);
|
||||||
@@ -339,15 +397,9 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
|
|||||||
return first.at_path(json_path);
|
return first.at_path(json_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::at_path_with_wildcard(std::string_view json_path) noexcept {
|
||||||
template <typename Func>
|
|
||||||
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
|
|
||||||
#else
|
|
||||||
template <typename Func>
|
|
||||||
#endif
|
|
||||||
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
|
|
||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
|
return first.at_path_with_wildcard(json_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::reset() noexcept {
|
inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::reset() noexcept {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "simdjson/generic/ondemand/base.h"
|
#include "simdjson/generic/ondemand/base.h"
|
||||||
#include "simdjson/generic/implementation_simdjson_result_base.h"
|
#include "simdjson/generic/implementation_simdjson_result_base.h"
|
||||||
#include "simdjson/generic/ondemand/value_iterator.h"
|
#include "simdjson/generic/ondemand/value_iterator.h"
|
||||||
|
#include "simdjson/generic/ondemand/key_selector.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS
|
#if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS
|
||||||
#include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string
|
#include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string
|
||||||
@@ -122,6 +123,23 @@ public:
|
|||||||
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
|
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
|
||||||
simdjson_inline simdjson_result<value> operator[](std::string_view key) && noexcept;
|
simdjson_inline simdjson_result<value> operator[](std::string_view key) && noexcept;
|
||||||
|
|
||||||
|
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||||
|
/**
|
||||||
|
* Look up a field by name using a key_selector. This method is similar to find_field_unordered()
|
||||||
|
* but uses a compile-time generated perfect hash table for efficient lookup.
|
||||||
|
*
|
||||||
|
* @tparam Selector The key_selector type
|
||||||
|
* @param selector The key selector instance
|
||||||
|
* @returns A pair containing the key identifier (index in the selector) and the value,
|
||||||
|
* or NO_SUCH_FIELD if the field is not in the object.
|
||||||
|
*/
|
||||||
|
template <concepts::key_selector_type Selector>
|
||||||
|
simdjson_inline std::pair<std::size_t, simdjson_result<value>> find_field(const Selector& selector) & noexcept;
|
||||||
|
/** @overload template <concepts::key_selector_type Selector> simdjson_inline std::pair<std::size_t, simdjson_result<value>> find_field(const Selector& selector) & noexcept; */
|
||||||
|
template <concepts::key_selector_type Selector>
|
||||||
|
simdjson_inline std::pair<std::size_t, simdjson_result<value>> find_field(const Selector& selector) && noexcept;
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the value associated with the given JSON pointer. We use the RFC 6901
|
* Get the value associated with the given JSON pointer. We use the RFC 6901
|
||||||
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
|
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
|
||||||
@@ -172,21 +190,13 @@ public:
|
|||||||
inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
|
inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call the provided callback for each value matching the given JSONPath
|
* Get all values matching the given JSONPath expression with wildcard support.
|
||||||
* expression with wildcard support.
|
|
||||||
* Supports wildcard patterns like ".*" to match all object fields.
|
* Supports wildcard patterns like ".*" to match all object fields.
|
||||||
*
|
*
|
||||||
* @param json_path JSONPath expression with wildcards
|
* @param json_path JSONPath expression with wildcards
|
||||||
* @param callback Function called for each matching value
|
* @return Vector of values matching the wildcard pattern
|
||||||
* @return error_code indicating success or failure
|
|
||||||
*/
|
*/
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
inline simdjson_result<std::vector<value>> at_path_with_wildcard(std::string_view json_path) noexcept;
|
||||||
template <typename Func>
|
|
||||||
requires std::invocable<Func, value>
|
|
||||||
#else
|
|
||||||
template <typename Func>
|
|
||||||
#endif
|
|
||||||
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset the iterator so that we are pointing back at the
|
* Reset the iterator so that we are pointing back at the
|
||||||
@@ -332,17 +342,17 @@ public:
|
|||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field(std::string_view key) && noexcept;
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field(std::string_view key) && noexcept;
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) && noexcept;
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) && noexcept;
|
||||||
|
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||||
|
template <concepts::key_selector_type Selector>
|
||||||
|
simdjson_inline std::pair<std::size_t, simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>> find_field(const Selector& selector) & noexcept;
|
||||||
|
template <concepts::key_selector_type Selector>
|
||||||
|
simdjson_inline std::pair<std::size_t, simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>> find_field(const Selector& selector) && noexcept;
|
||||||
|
#endif
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](std::string_view key) & noexcept;
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](std::string_view key) & noexcept;
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](std::string_view key) && noexcept;
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](std::string_view key) && noexcept;
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> at_path_with_wildcard(std::string_view json_path) noexcept;
|
||||||
template <typename Func>
|
|
||||||
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
|
|
||||||
#else
|
|
||||||
template <typename Func>
|
|
||||||
#endif
|
|
||||||
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
|
|
||||||
inline simdjson_result<bool> reset() noexcept;
|
inline simdjson_result<bool> reset() noexcept;
|
||||||
inline simdjson_result<bool> is_empty() noexcept;
|
inline simdjson_result<bool> is_empty() noexcept;
|
||||||
inline simdjson_result<size_t> count_fields() & noexcept;
|
inline simdjson_result<size_t> count_fields() & noexcept;
|
||||||
|
|||||||
@@ -27,10 +27,7 @@ simdjson_warn_unused simdjson_inline error_code parser::allocate(size_t new_capa
|
|||||||
|
|
||||||
// string_capacity copied from document::allocate
|
// string_capacity copied from document::allocate
|
||||||
_capacity = 0;
|
_capacity = 0;
|
||||||
if(5 * (new_capacity / 3) + SIMDJSON_PADDING < SIMDJSON_PADDING) {
|
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * new_capacity / 3 + SIMDJSON_PADDING, 64);
|
||||||
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
|
|
||||||
}
|
|
||||||
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * (new_capacity / 3) + SIMDJSON_PADDING, 64);
|
|
||||||
string_buf.reset(new (std::nothrow) uint8_t[string_capacity]);
|
string_buf.reset(new (std::nothrow) uint8_t[string_capacity]);
|
||||||
#if SIMDJSON_DEVELOPMENT_CHECKS
|
#if SIMDJSON_DEVELOPMENT_CHECKS
|
||||||
start_positions.reset(new (std::nothrow) token_position[new_max_depth]);
|
start_positions.reset(new (std::nothrow) token_position[new_max_depth]);
|
||||||
@@ -55,7 +52,6 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(p
|
|||||||
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
|
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
|
||||||
|
|
||||||
json.remove_utf8_bom();
|
json.remove_utf8_bom();
|
||||||
_document_len = json.length();
|
|
||||||
|
|
||||||
// Allocate if needed
|
// Allocate if needed
|
||||||
if (capacity() < json.length() || !string_buf) {
|
if (capacity() < json.length() || !string_buf) {
|
||||||
@@ -72,7 +68,6 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate_a
|
|||||||
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
|
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
|
||||||
|
|
||||||
json.remove_utf8_bom();
|
json.remove_utf8_bom();
|
||||||
_document_len = json.length();
|
|
||||||
|
|
||||||
// Allocate if needed
|
// Allocate if needed
|
||||||
if (capacity() < json.length() || !string_buf) {
|
if (capacity() < json.length() || !string_buf) {
|
||||||
@@ -277,7 +272,7 @@ simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() {
|
|||||||
return *parser::get_parser_instance();
|
return *parser::get_parser_instance();
|
||||||
}
|
}
|
||||||
|
|
||||||
simdjson_inline bool parser::release_parser() {
|
simdjson_inline bool release_parser() {
|
||||||
auto &parser_instance = parser::get_threadlocal_parser_if_exists();
|
auto &parser_instance = parser::get_threadlocal_parser_if_exists();
|
||||||
if (parser_instance) {
|
if (parser_instance) {
|
||||||
parser_instance.reset();
|
parser_instance.reset();
|
||||||
@@ -314,4 +309,4 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::parser>::simd
|
|||||||
|
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|
||||||
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H
|
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H
|
||||||
@@ -422,6 +422,8 @@ public:
|
|||||||
static simdjson_inline bool release_parser();
|
static simdjson_inline bool release_parser();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend bool release_parser();
|
||||||
|
friend ondemand::parser& get_parser();
|
||||||
/** Get the thread-local parser instance, allocates it if needed */
|
/** Get the thread-local parser instance, allocates it if needed */
|
||||||
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_parser_instance();
|
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_parser_instance();
|
||||||
/** Get the thread-local parser instance, it might be null */
|
/** Get the thread-local parser instance, it might be null */
|
||||||
@@ -431,7 +433,6 @@ private:
|
|||||||
size_t _capacity{0};
|
size_t _capacity{0};
|
||||||
size_t _max_capacity;
|
size_t _max_capacity;
|
||||||
size_t _max_depth{DEFAULT_MAX_DEPTH};
|
size_t _max_depth{DEFAULT_MAX_DEPTH};
|
||||||
size_t _document_len{0};
|
|
||||||
std::unique_ptr<uint8_t[]> string_buf{};
|
std::unique_ptr<uint8_t[]> string_buf{};
|
||||||
|
|
||||||
#if SIMDJSON_DEVELOPMENT_CHECKS
|
#if SIMDJSON_DEVELOPMENT_CHECKS
|
||||||
|
|||||||
@@ -1,138 +0,0 @@
|
|||||||
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
|
|
||||||
|
|
||||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
|
||||||
#define SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
|
|
||||||
#include "simdjson/generic/ondemand/base.h"
|
|
||||||
#include "simdjson/generic/ondemand/ranges.h"
|
|
||||||
#include "simdjson/generic/ondemand/array-inl.h"
|
|
||||||
#include "simdjson/generic/ondemand/array_iterator-inl.h"
|
|
||||||
#include "simdjson/generic/ondemand/object-inl.h"
|
|
||||||
#include "simdjson/generic/ondemand/object_iterator-inl.h"
|
|
||||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
|
||||||
|
|
||||||
#if SIMDJSON_SUPPORTS_RANGES
|
|
||||||
|
|
||||||
namespace simdjson {
|
|
||||||
namespace SIMDJSON_IMPLEMENTATION {
|
|
||||||
namespace ondemand {
|
|
||||||
|
|
||||||
//
|
|
||||||
// array_range_iterator
|
|
||||||
//
|
|
||||||
|
|
||||||
simdjson_inline array_range_iterator::array_range_iterator(array_iterator iter) noexcept
|
|
||||||
: iter_{iter} {}
|
|
||||||
|
|
||||||
simdjson_inline simdjson_result<value> array_range_iterator::operator*() const noexcept {
|
|
||||||
return *iter_;
|
|
||||||
}
|
|
||||||
|
|
||||||
simdjson_inline array_range_iterator& array_range_iterator::operator++() noexcept {
|
|
||||||
++iter_;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
|
|
||||||
simdjson_inline void array_range_iterator::operator++(int) noexcept {
|
|
||||||
++*this;
|
|
||||||
}
|
|
||||||
SIMDJSON_POP_DISABLE_WARNINGS
|
|
||||||
|
|
||||||
//
|
|
||||||
// array_range
|
|
||||||
//
|
|
||||||
|
|
||||||
simdjson_inline array_range::array_range(array& arr) noexcept {
|
|
||||||
auto b = arr.begin();
|
|
||||||
if (b.error()) { error_ = b.error(); return; }
|
|
||||||
begin_ = b.value_unsafe();
|
|
||||||
end_ = arr.end().value_unsafe();
|
|
||||||
}
|
|
||||||
|
|
||||||
simdjson_inline array_range_iterator array_range::begin() noexcept {
|
|
||||||
return array_range_iterator(begin_);
|
|
||||||
}
|
|
||||||
|
|
||||||
simdjson_inline array_range_iterator array_range::end() noexcept {
|
|
||||||
return array_range_iterator(end_);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// object_range_iterator
|
|
||||||
//
|
|
||||||
|
|
||||||
simdjson_inline object_range_iterator::object_range_iterator(object_iterator iter) noexcept
|
|
||||||
: iter_{iter} {}
|
|
||||||
|
|
||||||
simdjson_inline simdjson_result<field> object_range_iterator::operator*() const noexcept {
|
|
||||||
return *iter_;
|
|
||||||
}
|
|
||||||
|
|
||||||
simdjson_inline object_range_iterator& object_range_iterator::operator++() noexcept {
|
|
||||||
++iter_;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
|
|
||||||
simdjson_inline void object_range_iterator::operator++(int) noexcept {
|
|
||||||
++*this;
|
|
||||||
}
|
|
||||||
SIMDJSON_POP_DISABLE_WARNINGS
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// object_range
|
|
||||||
//
|
|
||||||
|
|
||||||
simdjson_inline object_range::object_range(object& obj) noexcept {
|
|
||||||
auto b = obj.begin();
|
|
||||||
if (b.error()) { error_ = b.error(); return; }
|
|
||||||
begin_ = b.value_unsafe();
|
|
||||||
end_ = obj.end().value_unsafe();
|
|
||||||
}
|
|
||||||
|
|
||||||
simdjson_inline object_range_iterator object_range::begin() noexcept {
|
|
||||||
return object_range_iterator(begin_);
|
|
||||||
}
|
|
||||||
|
|
||||||
simdjson_inline object_range_iterator object_range::end() noexcept {
|
|
||||||
return object_range_iterator(end_);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Free functions
|
|
||||||
//
|
|
||||||
|
|
||||||
simdjson_inline array_range get_range(array& arr) noexcept {
|
|
||||||
return array_range(arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
simdjson_inline object_range get_key_value_range(object& obj) noexcept {
|
|
||||||
return object_range(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if SIMDJSON_EXCEPTIONS
|
|
||||||
simdjson_inline array_range get_range(simdjson_result<array> result) {
|
|
||||||
return array_range(result.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
simdjson_inline object_range get_key_value_range(simdjson_result<object> result) {
|
|
||||||
return object_range(result.value());
|
|
||||||
}
|
|
||||||
#endif // SIMDJSON_EXCEPTIONS
|
|
||||||
|
|
||||||
} // namespace ondemand
|
|
||||||
} // namespace SIMDJSON_IMPLEMENTATION
|
|
||||||
} // namespace simdjson
|
|
||||||
|
|
||||||
// Verify the range wrapper types satisfy the expected C++20 concepts.
|
|
||||||
static_assert(std::input_iterator<simdjson::SIMDJSON_IMPLEMENTATION::ondemand::array_range_iterator>);
|
|
||||||
static_assert(std::input_iterator<simdjson::SIMDJSON_IMPLEMENTATION::ondemand::object_range_iterator>);
|
|
||||||
static_assert(std::ranges::input_range<simdjson::SIMDJSON_IMPLEMENTATION::ondemand::array_range>);
|
|
||||||
static_assert(std::ranges::input_range<simdjson::SIMDJSON_IMPLEMENTATION::ondemand::object_range>);
|
|
||||||
static_assert(std::ranges::view<simdjson::SIMDJSON_IMPLEMENTATION::ondemand::array_range>);
|
|
||||||
static_assert(std::ranges::view<simdjson::SIMDJSON_IMPLEMENTATION::ondemand::object_range>);
|
|
||||||
|
|
||||||
#endif // SIMDJSON_SUPPORTS_RANGES
|
|
||||||
|
|
||||||
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_H
|
|
||||||
|
|
||||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
|
||||||
#define SIMDJSON_GENERIC_ONDEMAND_RANGES_H
|
|
||||||
#include "simdjson/generic/ondemand/base.h"
|
|
||||||
#include "simdjson/generic/ondemand/array.h"
|
|
||||||
#include "simdjson/generic/ondemand/array_iterator.h"
|
|
||||||
#include "simdjson/generic/ondemand/object.h"
|
|
||||||
#include "simdjson/generic/ondemand/object_iterator.h"
|
|
||||||
#include "simdjson/generic/ondemand/field.h"
|
|
||||||
#include "simdjson/generic/ondemand/value.h"
|
|
||||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
|
||||||
|
|
||||||
#if SIMDJSON_SUPPORTS_RANGES
|
|
||||||
|
|
||||||
namespace simdjson {
|
|
||||||
namespace SIMDJSON_IMPLEMENTATION {
|
|
||||||
namespace ondemand {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A ranges-compatible iterator adapter for JSON arrays.
|
|
||||||
*
|
|
||||||
* Wraps array_iterator to satisfy std::input_iterator by providing:
|
|
||||||
* - const operator* (via mutable internal state)
|
|
||||||
* - post-increment operator
|
|
||||||
* - iterator_concept tag
|
|
||||||
*
|
|
||||||
* The mutable approach is standard for single-pass input iterators that
|
|
||||||
* read from external sources (similar to std::istream_iterator).
|
|
||||||
*/
|
|
||||||
class array_range_iterator {
|
|
||||||
public:
|
|
||||||
using iterator_concept = std::input_iterator_tag;
|
|
||||||
using value_type = simdjson_result<value>;
|
|
||||||
using reference = simdjson_result<value>;
|
|
||||||
using difference_type = std::ptrdiff_t;
|
|
||||||
|
|
||||||
simdjson_inline array_range_iterator() noexcept = default;
|
|
||||||
simdjson_inline explicit array_range_iterator(array_iterator iter) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current element. Const-qualified for std::indirectly_readable;
|
|
||||||
* internally delegates to the mutable wrapped iterator.
|
|
||||||
*/
|
|
||||||
simdjson_inline simdjson_result<value> operator*() const noexcept;
|
|
||||||
simdjson_inline array_range_iterator& operator++() noexcept;
|
|
||||||
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
|
|
||||||
simdjson_inline void operator++(int) noexcept;
|
|
||||||
SIMDJSON_POP_DISABLE_WARNINGS
|
|
||||||
/**
|
|
||||||
* Comparison delegates to array_iterator::operator==, which checks
|
|
||||||
* whether the underlying parser has finished the array (depth-based).
|
|
||||||
*/
|
|
||||||
simdjson_inline friend bool operator==(const array_range_iterator& a,
|
|
||||||
const array_range_iterator& b) noexcept {
|
|
||||||
return a.iter_ == b.iter_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
mutable array_iterator iter_{};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A std::ranges::view over a JSON array.
|
|
||||||
*
|
|
||||||
* Wraps an ondemand::array and exposes begin()/end() that return
|
|
||||||
* array_range_iterator (satisfying std::input_iterator), enabling
|
|
||||||
* use with std::views::transform and other range adaptors.
|
|
||||||
*
|
|
||||||
* If the array's begin() returns an error (only possible under
|
|
||||||
* SIMDJSON_DEVELOPMENT_CHECKS), the range will be empty and error()
|
|
||||||
* will return the error code.
|
|
||||||
*
|
|
||||||
* Usage:
|
|
||||||
* ondemand::parser parser;
|
|
||||||
* auto doc = parser.iterate(json);
|
|
||||||
* auto arr = doc.get_array().value();
|
|
||||||
* for (auto elem : ondemand::get_range(arr)) { ... }
|
|
||||||
*/
|
|
||||||
class array_range {
|
|
||||||
public:
|
|
||||||
simdjson_inline array_range() noexcept = default;
|
|
||||||
simdjson_inline explicit array_range(array& arr) noexcept;
|
|
||||||
|
|
||||||
simdjson_inline array_range_iterator begin() noexcept;
|
|
||||||
simdjson_inline array_range_iterator end() noexcept;
|
|
||||||
|
|
||||||
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
|
|
||||||
simdjson_inline error_code error() const noexcept { return error_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
array_iterator begin_{};
|
|
||||||
array_iterator end_{};
|
|
||||||
error_code error_{SUCCESS};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A ranges-compatible iterator adapter for JSON objects.
|
|
||||||
*
|
|
||||||
* Wraps object_iterator to satisfy std::input_iterator, yielding
|
|
||||||
* simdjson_result<field> elements (key-value pairs).
|
|
||||||
*/
|
|
||||||
class object_range_iterator {
|
|
||||||
public:
|
|
||||||
using iterator_concept = std::input_iterator_tag;
|
|
||||||
using value_type = simdjson_result<field>;
|
|
||||||
using reference = simdjson_result<field>;
|
|
||||||
using difference_type = std::ptrdiff_t;
|
|
||||||
|
|
||||||
simdjson_inline object_range_iterator() noexcept = default;
|
|
||||||
simdjson_inline explicit object_range_iterator(object_iterator iter) noexcept;
|
|
||||||
|
|
||||||
simdjson_inline simdjson_result<field> operator*() const noexcept;
|
|
||||||
simdjson_inline object_range_iterator& operator++() noexcept;
|
|
||||||
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
|
|
||||||
simdjson_inline void operator++(int) noexcept;
|
|
||||||
SIMDJSON_POP_DISABLE_WARNINGS
|
|
||||||
simdjson_inline friend bool operator==(const object_range_iterator& a,
|
|
||||||
const object_range_iterator& b) noexcept {
|
|
||||||
return a.iter_ == b.iter_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
mutable object_iterator iter_{};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A std::ranges::view over a JSON object.
|
|
||||||
*
|
|
||||||
* Wraps an ondemand::object and exposes begin()/end() that return
|
|
||||||
* object_range_iterator, enabling use with range adaptors.
|
|
||||||
*
|
|
||||||
* If the object's begin() returns an error, the range will be empty
|
|
||||||
* and error() will return the error code.
|
|
||||||
*/
|
|
||||||
class object_range {
|
|
||||||
public:
|
|
||||||
simdjson_inline object_range() noexcept = default;
|
|
||||||
simdjson_inline explicit object_range(object& obj) noexcept;
|
|
||||||
|
|
||||||
simdjson_inline object_range_iterator begin() noexcept;
|
|
||||||
simdjson_inline object_range_iterator end() noexcept;
|
|
||||||
|
|
||||||
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
|
|
||||||
simdjson_inline error_code error() const noexcept { return error_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
object_iterator begin_{};
|
|
||||||
object_iterator end_{};
|
|
||||||
error_code error_{SUCCESS};
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Get a std::ranges compatible view over a JSON array. */
|
|
||||||
simdjson_inline array_range get_range(array& arr) noexcept;
|
|
||||||
|
|
||||||
/** Get a std::ranges compatible view over a JSON object (key-value pairs). */
|
|
||||||
simdjson_inline object_range get_key_value_range(object& obj) noexcept;
|
|
||||||
|
|
||||||
#if SIMDJSON_EXCEPTIONS
|
|
||||||
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
|
|
||||||
simdjson_inline array_range get_range(simdjson_result<array> result);
|
|
||||||
|
|
||||||
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
|
|
||||||
simdjson_inline object_range get_key_value_range(simdjson_result<object> result);
|
|
||||||
#endif // SIMDJSON_EXCEPTIONS
|
|
||||||
|
|
||||||
} // namespace ondemand
|
|
||||||
} // namespace SIMDJSON_IMPLEMENTATION
|
|
||||||
} // namespace simdjson
|
|
||||||
|
|
||||||
namespace std {
|
|
||||||
namespace ranges {
|
|
||||||
template<>
|
|
||||||
inline constexpr bool enable_view<simdjson::SIMDJSON_IMPLEMENTATION::ondemand::array_range> = true;
|
|
||||||
template<>
|
|
||||||
inline constexpr bool enable_view<simdjson::SIMDJSON_IMPLEMENTATION::ondemand::object_range> = true;
|
|
||||||
} // namespace ranges
|
|
||||||
} // namespace std
|
|
||||||
|
|
||||||
#endif // SIMDJSON_SUPPORTS_RANGES
|
|
||||||
|
|
||||||
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_H
|
|
||||||
@@ -6,7 +6,6 @@
|
|||||||
#include "simdjson/generic/ondemand/object.h"
|
#include "simdjson/generic/ondemand/object.h"
|
||||||
#include "simdjson/generic/ondemand/array.h"
|
#include "simdjson/generic/ondemand/array.h"
|
||||||
#include "simdjson/generic/ondemand/base.h"
|
#include "simdjson/generic/ondemand/base.h"
|
||||||
#include "simdjson/annotations.h"
|
|
||||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||||
|
|
||||||
#include <concepts>
|
#include <concepts>
|
||||||
@@ -278,9 +277,8 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
|
|||||||
SIMDJSON_TRY(val.get_object().get(obj));
|
SIMDJSON_TRY(val.get_object().get(obj));
|
||||||
}
|
}
|
||||||
template for (constexpr auto mem : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
|
template for (constexpr auto mem : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
|
||||||
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)
|
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
|
||||||
&& std::meta::annotations_of_with_type(mem, ^^simdjson::detail::skip_tag).empty()) {
|
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
|
||||||
constexpr std::string_view key = simdjson::get_json_key_name<mem>();
|
|
||||||
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
|
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
|
||||||
// for optional members, it's ok if the key is missing
|
// for optional members, it's ok if the key is missing
|
||||||
auto error = obj[key].get(out.[:mem:]);
|
auto error = obj[key].get(out.[:mem:]);
|
||||||
|
|||||||
@@ -314,20 +314,14 @@ simdjson_inline simdjson_result<value> value::at_path(std::string_view json_path
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
inline simdjson_result<std::vector<value>> value::at_path_with_wildcard(std::string_view json_path) noexcept {
|
||||||
template <typename Func>
|
|
||||||
requires std::invocable<Func, value>
|
|
||||||
#else
|
|
||||||
template <typename Func>
|
|
||||||
#endif
|
|
||||||
inline error_code value::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
|
|
||||||
json_type t;
|
json_type t;
|
||||||
SIMDJSON_TRY(type().get(t));
|
SIMDJSON_TRY(type().get(t));
|
||||||
switch (t) {
|
switch (t) {
|
||||||
case json_type::array:
|
case json_type::array:
|
||||||
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
|
return (*this).get_array().at_path_with_wildcard(json_path);
|
||||||
case json_type::object:
|
case json_type::object:
|
||||||
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
|
return (*this).get_object().at_path_with_wildcard(json_path);
|
||||||
default:
|
default:
|
||||||
return INVALID_JSON_POINTER;
|
return INVALID_JSON_POINTER;
|
||||||
}
|
}
|
||||||
@@ -591,18 +585,12 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
|
|||||||
return first.at_path(json_path);
|
return first.at_path(json_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::at_path_with_wildcard(
|
||||||
template <typename Func>
|
std::string_view json_path) noexcept {
|
||||||
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
|
|
||||||
#else
|
|
||||||
template <typename Func>
|
|
||||||
#endif
|
|
||||||
inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::for_each_at_path_with_wildcard(
|
|
||||||
std::string_view json_path, Func&& callback) noexcept {
|
|
||||||
if (error()) {
|
if (error()) {
|
||||||
return error();
|
return error();
|
||||||
}
|
}
|
||||||
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
|
return first.at_path_with_wildcard(json_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|||||||
@@ -698,21 +698,13 @@ public:
|
|||||||
simdjson_inline simdjson_result<value> at_path(std::string_view at_path) noexcept;
|
simdjson_inline simdjson_result<value> at_path(std::string_view at_path) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call the provided callback for each value matching the given JSONPath
|
* Get all values matching the given JSONPath expression with wildcard support.
|
||||||
* expression with wildcard support.
|
|
||||||
* Supports wildcard character (*) for arrays or ".*" for objects.
|
* Supports wildcard character (*) for arrays or ".*" for objects.
|
||||||
*
|
*
|
||||||
* @param json_path JSONPath expression with wildcards
|
* @param json_path JSONPath expression with wildcards
|
||||||
* @param callback Function called for each matching value
|
* @return Vector of values matching the wildcard pattern
|
||||||
* @return error_code indicating success or failure
|
|
||||||
*/
|
*/
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
simdjson_inline simdjson_result<std::vector<value>> at_path_with_wildcard(std::string_view json_path) noexcept;
|
||||||
template <typename Func>
|
|
||||||
requires std::invocable<Func, value>
|
|
||||||
#else
|
|
||||||
template <typename Func>
|
|
||||||
#endif
|
|
||||||
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
@@ -905,23 +897,9 @@ public:
|
|||||||
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
|
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
|
||||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> at_path_with_wildcard(std::string_view json_path) noexcept;
|
||||||
template <typename Func>
|
|
||||||
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
|
|
||||||
#else
|
|
||||||
template <typename Func>
|
|
||||||
#endif
|
|
||||||
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Forward-declare explicit specializations so MSVC /permissive- sees them before
|
|
||||||
// any template instantiation that would resolve element.get(val) to the primary.
|
|
||||||
template<> simdjson_inline error_code
|
|
||||||
simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get<SIMDJSON_IMPLEMENTATION::ondemand::value>(
|
|
||||||
SIMDJSON_IMPLEMENTATION::ondemand::value &out) noexcept;
|
|
||||||
template<> simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>
|
|
||||||
simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get<SIMDJSON_IMPLEMENTATION::ondemand::value>() noexcept;
|
|
||||||
|
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|
||||||
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_H
|
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_H
|
||||||
|
|||||||
@@ -15,27 +15,6 @@ namespace simdjson {
|
|||||||
namespace SIMDJSON_IMPLEMENTATION {
|
namespace SIMDJSON_IMPLEMENTATION {
|
||||||
namespace ondemand {
|
namespace ondemand {
|
||||||
|
|
||||||
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
|
||||||
simdjson_inline bool raw_json_string_is_quote_terminated(const uint8_t *json, uint32_t max_len) noexcept {
|
|
||||||
bool escaping{false};
|
|
||||||
for (uint32_t i = 1; i < max_len; i++) {
|
|
||||||
switch (json[i]) {
|
|
||||||
case '"':
|
|
||||||
if (!escaping) { return true; }
|
|
||||||
escaping = false;
|
|
||||||
break;
|
|
||||||
case '\\':
|
|
||||||
escaping = !escaping;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
escaping = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
|
||||||
|
|
||||||
simdjson_inline value_iterator::value_iterator(
|
simdjson_inline value_iterator::value_iterator(
|
||||||
json_iterator *json_iter,
|
json_iterator *json_iter,
|
||||||
depth_t depth,
|
depth_t depth,
|
||||||
@@ -551,15 +530,6 @@ simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_ite
|
|||||||
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::get_raw_json_string() noexcept {
|
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::get_raw_json_string() noexcept {
|
||||||
auto json = peek_scalar("string");
|
auto json = peek_scalar("string");
|
||||||
if (*json != '"') { return incorrect_type_error("Not a string"); }
|
if (*json != '"') { return incorrect_type_error("Not a string"); }
|
||||||
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
|
||||||
if (_json_iter->allow_incomplete_json()) {
|
|
||||||
const size_t remaining_input_length = _json_iter->remaining_input_length(json);
|
|
||||||
const uint32_t max_len = remaining_input_length < peek_start_length() ? uint32_t(remaining_input_length) : peek_start_length();
|
|
||||||
if (!raw_json_string_is_quote_terminated(json, max_len)) {
|
|
||||||
return STRING_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
|
||||||
advance_scalar("string");
|
advance_scalar("string");
|
||||||
return raw_json_string(json+1);
|
return raw_json_string(json+1);
|
||||||
}
|
}
|
||||||
@@ -707,15 +677,6 @@ simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iter
|
|||||||
auto json = peek_scalar("string");
|
auto json = peek_scalar("string");
|
||||||
if (*json != '"') { return incorrect_type_error("Not a string"); }
|
if (*json != '"') { return incorrect_type_error("Not a string"); }
|
||||||
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
|
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
|
||||||
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
|
||||||
if (_json_iter->allow_incomplete_json()) {
|
|
||||||
const size_t remaining_input_length = _json_iter->remaining_input_length(json);
|
|
||||||
const uint32_t max_len = remaining_input_length < peek_root_length() ? uint32_t(remaining_input_length) : peek_root_length();
|
|
||||||
if (!raw_json_string_is_quote_terminated(json, max_len)) {
|
|
||||||
return STRING_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
|
||||||
advance_scalar("string");
|
advance_scalar("string");
|
||||||
return raw_json_string(json+1);
|
return raw_json_string(json+1);
|
||||||
}
|
}
|
||||||
@@ -1143,4 +1104,4 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value_iterato
|
|||||||
|
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|
||||||
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H
|
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H
|
||||||
@@ -76,7 +76,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
fractured_json_options options_;
|
fractured_json_options options_;
|
||||||
layout_mode current_layout_ = layout_mode::expanded;
|
layout_mode current_layout_ = layout_mode::EXPANDED;
|
||||||
size_t current_depth_ = 0;
|
size_t current_depth_ = 0;
|
||||||
size_t current_line_length_ = 0;
|
size_t current_line_length_ = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ namespace internal {
|
|||||||
* Layout mode for fractured JSON formatting.
|
* Layout mode for fractured JSON formatting.
|
||||||
*/
|
*/
|
||||||
enum class layout_mode {
|
enum class layout_mode {
|
||||||
single_line, // Single line: [1, 2, 3] or {"a": 1}
|
INLINE, // Single line: [1, 2, 3] or {"a": 1}
|
||||||
compact_multiline, // Multiple items per line with breaks
|
COMPACT_MULTILINE, // Multiple items per line with breaks
|
||||||
table, // Tabular format for arrays of similar objects
|
TABLE, // Tabular format for arrays of similar objects
|
||||||
expanded // Traditional multi-line with indentation
|
EXPANDED // Traditional multi-line with indentation
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,7 +50,7 @@ struct element_metrics {
|
|||||||
std::vector<std::string> common_keys{};
|
std::vector<std::string> common_keys{};
|
||||||
|
|
||||||
/** Recommended layout mode based on analysis */
|
/** Recommended layout mode based on analysis */
|
||||||
layout_mode recommended_layout = layout_mode::expanded;
|
layout_mode recommended_layout = layout_mode::EXPANDED;
|
||||||
|
|
||||||
/** Child metrics for arrays and objects (in order of iteration) */
|
/** Child metrics for arrays and objects (in order of iteration) */
|
||||||
std::vector<element_metrics> children{};
|
std::vector<element_metrics> children{};
|
||||||
|
|||||||
@@ -1,55 +1,12 @@
|
|||||||
#ifndef SIMDJSON_JSONPATHUTIL_H
|
#ifndef SIMDJSON_JSONPATHUTIL_H
|
||||||
#define SIMDJSON_JSONPATHUTIL_H
|
#define SIMDJSON_JSONPATHUTIL_H
|
||||||
|
|
||||||
#include "simdjson/error.h"
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "simdjson/common_defs.h"
|
#include "simdjson/common_defs.h"
|
||||||
|
|
||||||
#include <limits>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace simdjson {
|
namespace simdjson {
|
||||||
namespace internal {
|
|
||||||
/**
|
|
||||||
* Parses the next JSON Pointer array index token.
|
|
||||||
*
|
|
||||||
* The caller passes a pointer fragment with no leading '/', such as "123/foo".
|
|
||||||
* On success, array_index receives the parsed index and token_length receives
|
|
||||||
* the number of bytes consumed before the next '/' or the end of the fragment.
|
|
||||||
*/
|
|
||||||
simdjson_inline error_code parse_json_pointer_array_index(std::string_view json_pointer,
|
|
||||||
size_t &array_index,
|
|
||||||
size_t &token_length) noexcept {
|
|
||||||
array_index = 0;
|
|
||||||
token_length = 0;
|
|
||||||
|
|
||||||
for (; token_length < json_pointer.length() && json_pointer[token_length] != '/';
|
|
||||||
token_length++) {
|
|
||||||
uint8_t digit = uint8_t(json_pointer[token_length] - '0');
|
|
||||||
// Check for non-digit in array index. If it's there, we're trying to get a field in an object.
|
|
||||||
if (digit > 9) {
|
|
||||||
return INCORRECT_TYPE;
|
|
||||||
}
|
|
||||||
// 0 followed by other digits is invalid.
|
|
||||||
if (token_length > 0 && json_pointer[0] == '0') {
|
|
||||||
return INVALID_JSON_POINTER;
|
|
||||||
}
|
|
||||||
if (array_index >
|
|
||||||
(((std::numeric_limits<size_t>::max)() - digit) / 10)) {
|
|
||||||
return INDEX_OUT_OF_BOUNDS;
|
|
||||||
}
|
|
||||||
array_index = array_index * 10 + digit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty string is invalid; so is a "/" with no digits before it.
|
|
||||||
if (token_length == 0) {
|
|
||||||
return INVALID_JSON_POINTER;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts JSONPath to JSON Pointer.
|
* Converts JSONPath to JSON Pointer.
|
||||||
* @param json_path The JSONPath string to be converted.
|
* @param json_path The JSONPath string to be converted.
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public:
|
|||||||
simdjson_inline implementation() : simdjson::implementation(
|
simdjson_inline implementation() : simdjson::implementation(
|
||||||
"rvv_vls",
|
"rvv_vls",
|
||||||
"RISC-V V extension",
|
"RISC-V V extension",
|
||||||
internal::instruction_set::RVV_VLS
|
0
|
||||||
) {}
|
) {}
|
||||||
simdjson_warn_unused error_code create_dom_parser_implementation(
|
simdjson_warn_unused error_code create_dom_parser_implementation(
|
||||||
size_t capacity,
|
size_t capacity,
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ if (Python3_Interpreter_FOUND AND (NOT WIN32))
|
|||||||
AMALGAMATE_SOURCE_PATH=${PROJECT_SOURCE_DIR}/src
|
AMALGAMATE_SOURCE_PATH=${PROJECT_SOURCE_DIR}/src
|
||||||
AMALGAMATE_INPUT_PATH=${PROJECT_SOURCE_DIR}/include
|
AMALGAMATE_INPUT_PATH=${PROJECT_SOURCE_DIR}/include
|
||||||
AMALGAMATE_OUTPUT_PATH=${CMAKE_CURRENT_BINARY_DIR}
|
AMALGAMATE_OUTPUT_PATH=${CMAKE_CURRENT_BINARY_DIR}
|
||||||
${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/amalgamate.py --quiet
|
${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/amalgamate.py
|
||||||
#
|
#
|
||||||
# This is the best way I could find to make amalgamation trigger whenever source files or
|
# This is the best way I could find to make amalgamation trigger whenever source files or
|
||||||
# header files change: since the "simdjson" library has to get rebuilt when that happens, we
|
# header files change: since the "simdjson" library has to get rebuilt when that happens, we
|
||||||
|
|||||||
+37
-161
@@ -11,7 +11,6 @@ import re
|
|||||||
import shutil
|
import shutil
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
import argparse
|
|
||||||
from typing import Dict, List, Optional, Set, TextIO, Union, cast
|
from typing import Dict, List, Optional, Set, TextIO, Union, cast
|
||||||
|
|
||||||
# Pre-compile regex patterns for performance
|
# Pre-compile regex patterns for performance
|
||||||
@@ -31,18 +30,6 @@ if sys.version_info < (3, 0):
|
|||||||
sys.stdout.write("Sorry, requires Python 3.x or better\n")
|
sys.stdout.write("Sorry, requires Python 3.x or better\n")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Parse command-line arguments.
|
|
||||||
arg_parser = argparse.ArgumentParser(description="Creates the amalgamated source files.")
|
|
||||||
arg_parser.add_argument("--quiet", action="store_true",
|
|
||||||
help="Suppress informational output (errors and warnings are still printed).")
|
|
||||||
args = arg_parser.parse_args()
|
|
||||||
QUIET = args.quiet
|
|
||||||
|
|
||||||
def log(*log_args, **log_kwargs):
|
|
||||||
"""Print informational output unless --quiet was passed."""
|
|
||||||
if not QUIET:
|
|
||||||
print(*log_args, **log_kwargs)
|
|
||||||
|
|
||||||
rules = """
|
rules = """
|
||||||
|
|
||||||
Amalgamation Rules for simdjson
|
Amalgamation Rules for simdjson
|
||||||
@@ -112,11 +99,11 @@ If adding a new implementation, edit the IMPLEMENTATIONS list in this script.
|
|||||||
|
|
||||||
SCRIPTPATH = os.path.dirname(os.path.abspath(sys.argv[0]))
|
SCRIPTPATH = os.path.dirname(os.path.abspath(sys.argv[0]))
|
||||||
PROJECTPATH = os.path.dirname(SCRIPTPATH)
|
PROJECTPATH = os.path.dirname(SCRIPTPATH)
|
||||||
log(f"SCRIPTPATH={SCRIPTPATH} PROJECTPATH={PROJECTPATH}")
|
print(f"SCRIPTPATH={SCRIPTPATH} PROJECTPATH={PROJECTPATH}")
|
||||||
|
|
||||||
|
|
||||||
log("We are about to amalgamate all simdjson files into one source file.")
|
print("We are about to amalgamate all simdjson files into one source file.")
|
||||||
log("See https://www.sqlite.org/amalgamation.html and https://en.wikipedia.org/wiki/Single_Compilation_Unit for rationale.")
|
print("See https://www.sqlite.org/amalgamation.html and https://en.wikipedia.org/wiki/Single_Compilation_Unit for rationale.")
|
||||||
if "AMALGAMATE_SOURCE_PATH" not in os.environ:
|
if "AMALGAMATE_SOURCE_PATH" not in os.environ:
|
||||||
AMALGAMATE_SOURCE_PATH = os.path.join(PROJECTPATH, "src")
|
AMALGAMATE_SOURCE_PATH = os.path.join(PROJECTPATH, "src")
|
||||||
else:
|
else:
|
||||||
@@ -278,93 +265,37 @@ class SimdjsonFile:
|
|||||||
return self.filename == 'dependencies.h'
|
return self.filename == 'dependencies.h'
|
||||||
|
|
||||||
def add_include(self, include: 'SimdjsonFile'):
|
def add_include(self, include: 'SimdjsonFile'):
|
||||||
log(f" Adding include: {self} includes {include}")
|
print(f" Adding include: {self} includes {include}")
|
||||||
if self.is_conditional_include:
|
if self.is_conditional_include:
|
||||||
# If I have a dependency file, I can only include something that has a dependency file.
|
# If I have a dependency file, I can only include something that has a dependency file.
|
||||||
if not include.is_conditional_include:
|
if not include.is_conditional_include:
|
||||||
dep = self.dependency_file
|
dep = self.dependency_file
|
||||||
if dep:
|
dep_hint = f" and add it to the dependency file '{dep}'" if dep else ""
|
||||||
step2 = (
|
|
||||||
f" 2. Add '{include}' to the dependency manifest so the amalgamator\n"
|
|
||||||
f" emits it before any file that needs it:\n\n"
|
|
||||||
f" // in '{dep}'\n"
|
|
||||||
f" #include \"{include}\"\n\n"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
step2 = (
|
|
||||||
f" 2. '{self}' has no associated dependencies.h file, so the\n"
|
|
||||||
f" amalgamator cannot be told to emit '{include}' earlier.\n"
|
|
||||||
f" Either give '{self}' a dependencies.h, or restructure so\n"
|
|
||||||
f" the include is not needed here.\n\n"
|
|
||||||
)
|
|
||||||
raise AssertionError(
|
raise AssertionError(
|
||||||
f"Error: amalgamated file '{self}' includes non-amalgamated header '{include}'.\n\n"
|
f"Error: Amalgamated file '{self}' is trying to include '{include}', "
|
||||||
f" Why: '{self}' is stitched into the single-header output. Top-level\n"
|
f"but '{include}' is not an amalgamated file.\n\n"
|
||||||
f" headers like '{include}' are already emitted earlier in that\n"
|
f"FIX: Wrap the #include \"{include}\" in a conditional block:\n\n"
|
||||||
f" output, so re-including them inside an amalgamated file would\n"
|
f" #ifndef SIMDJSON_CONDITIONAL_INCLUDE\n"
|
||||||
f" either duplicate content or break include ordering.\n\n"
|
f" #include \"{include}\"\n"
|
||||||
f"FIX (two steps):\n\n"
|
f" #endif // SIMDJSON_CONDITIONAL_INCLUDE\n\n"
|
||||||
f" 1. In '{self}', wrap the include so it is skipped during amalgamation\n"
|
f"This makes the include editor-only (skipped during amalgamation){dep_hint}.\n\n"
|
||||||
f" but still visible to your editor/IDE:\n\n"
|
f"During amalgamation, '{include}' is already included earlier in the "
|
||||||
f" #ifndef SIMDJSON_CONDITIONAL_INCLUDE\n"
|
f"amalgamated output, so it does not need to be included again.\n\n"
|
||||||
f" #include \"{include}\"\n"
|
f"{rules}"
|
||||||
f" #endif // SIMDJSON_CONDITIONAL_INCLUDE\n\n"
|
|
||||||
f" Place it alongside the existing '#ifndef SIMDJSON_CONDITIONAL_INCLUDE'\n"
|
|
||||||
f" block near the top of the file, if there is one.\n\n"
|
|
||||||
f"{step2}"
|
|
||||||
f"After both edits, re-run:\n\n"
|
|
||||||
f" python3 singleheader/amalgamate.py\n\n"
|
|
||||||
f"See HACKING.md for the full amalgamation rules.\n"
|
|
||||||
)
|
)
|
||||||
# TODO make sure we only include amalgamated files that are guaranteed to be included with us (or before us)
|
# TODO make sure we only include amalgamated files that are guaranteed to be included with us (or before us)
|
||||||
# if include.amalgamator_file:
|
# if include.amalgamator_file:
|
||||||
# assert include.amalgamator_file == self, f"{self} cannot include {include}: it should be included from {include.amalgamator_file} instead."
|
# assert include.amalgamator_file == self, f"{self} cannot include {include}: it should be included from {include.amalgamator_file} instead."
|
||||||
else:
|
else:
|
||||||
if not (include.is_amalgamator or not include.is_conditional_include):
|
assert include.is_amalgamator or not include.is_conditional_include, f"Error: Free dependency file '{self}' is trying to include '{include}', which is an amalgamated file. Free dependency files (top-level headers) can only include amalgamator files or other free files, not amalgamated files directly. This prevents improper layering. Move the include to an amalgamator or restructure dependencies. {rules}"
|
||||||
raise AssertionError(
|
|
||||||
f"Error: free (top-level) header '{self}' includes amalgamated file '{include}'.\n\n"
|
|
||||||
f" Why: top-level headers are emitted once into the single-header output\n"
|
|
||||||
f" and form the public API surface. Amalgamated files are emitted\n"
|
|
||||||
f" multiple times (once per CPU implementation), so including one\n"
|
|
||||||
f" from the top level would either duplicate them or pin them to a\n"
|
|
||||||
f" single implementation.\n\n"
|
|
||||||
f"FIX: move the include to an amalgamator file (e.g. simdjson/arm64.h,\n"
|
|
||||||
f" simdjson/generic/amalgamated.h) or, if you actually need the\n"
|
|
||||||
f" declarations at the top level, factor them out into a top-level\n"
|
|
||||||
f" header that '{self}' can include instead.\n\n"
|
|
||||||
f"After editing, re-run:\n\n"
|
|
||||||
f" python3 singleheader/amalgamate.py\n\n"
|
|
||||||
f"See HACKING.md for the full amalgamation rules.\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.includes.append(include)
|
self.includes.append(include)
|
||||||
include.included_from.add(self)
|
include.included_from.add(self)
|
||||||
|
|
||||||
def add_editor_only_include(self, include: 'SimdjsonFile'):
|
def add_editor_only_include(self, include: 'SimdjsonFile'):
|
||||||
if not self.is_conditional_include:
|
assert self.is_conditional_include, f"Error: File '{self}' uses '#ifndef SIMDJSON_CONDITIONAL_INCLUDE', but '{self}' is not an amalgamated file. Conditional include blocks are only allowed in amalgamated files (those with dependencies). Remove the conditional block or ensure the file is amalgamated. {rules}"
|
||||||
raise AssertionError(
|
if not include.is_conditional_include:
|
||||||
f"Error: '{self}' uses '#ifndef SIMDJSON_CONDITIONAL_INCLUDE',\n"
|
assert self.dependency_file, f"Error: In '{self}', editor-only include of '{include}' requires a dependency file, but '{self}' has none. Ensure '{self}' has an associated dependencies.h file. {rules}"
|
||||||
f" but '{self}' is not an amalgamated file (it has no\n"
|
|
||||||
f" associated dependencies.h).\n\n"
|
|
||||||
f" Why: conditional-include blocks only make sense in amalgamated files,\n"
|
|
||||||
f" because the SIMDJSON_CONDITIONAL_INCLUDE macro is only defined\n"
|
|
||||||
f" while the amalgamator is emitting them.\n\n"
|
|
||||||
f"FIX: either remove the '#ifndef SIMDJSON_CONDITIONAL_INCLUDE' block from\n"
|
|
||||||
f" '{self}' (and include the headers unconditionally), or — if '{self}'\n"
|
|
||||||
f" really should be amalgamated — give it an associated dependencies.h\n"
|
|
||||||
f" file under the same directory.\n\n"
|
|
||||||
f"See HACKING.md for the full amalgamation rules.\n"
|
|
||||||
)
|
|
||||||
if not include.is_conditional_include and not self.dependency_file:
|
|
||||||
raise AssertionError(
|
|
||||||
f"Error: '{self}' editor-only-includes '{include}', but '{self}' has no\n"
|
|
||||||
f" associated dependencies.h, so the amalgamator has nowhere to record\n"
|
|
||||||
f" that '{include}' must be emitted earlier in the single-header output.\n\n"
|
|
||||||
f"FIX: add a 'dependencies.h' file in the same directory as '{self}',\n"
|
|
||||||
f" and list '{include}' inside it:\n\n"
|
|
||||||
f" #include \"{include}\"\n\n"
|
|
||||||
f"See HACKING.md for the full amalgamation rules.\n"
|
|
||||||
)
|
|
||||||
# TODO make sure we only include amalgamated files that are guaranteed to be included with us (or before us)
|
# TODO make sure we only include amalgamated files that are guaranteed to be included with us (or before us)
|
||||||
# elif include.amalgamator_file:
|
# elif include.amalgamator_file:
|
||||||
# assert self.is_amalgamated_before(self.amalgamator_file), f"{self} cannot include {include}: it should be included from {include.amalgamator_file} instead."
|
# assert self.is_amalgamated_before(self.amalgamator_file), f"{self} cannot include {include}: it should be included from {include.amalgamator_file} instead."
|
||||||
@@ -379,40 +310,11 @@ class SimdjsonFile:
|
|||||||
if file.dependency_file == self:
|
if file.dependency_file == self:
|
||||||
for editor_only_include in file.editor_only_includes:
|
for editor_only_include in file.editor_only_includes:
|
||||||
if not editor_only_include.is_conditional_include:
|
if not editor_only_include.is_conditional_include:
|
||||||
if editor_only_include not in self.includes:
|
assert editor_only_include in self.includes, f"Error: Dependency file '{self}' is missing an include for '{editor_only_include}', which is editor-only included in '{file}'. Add '{editor_only_include}' to '{self}' to ensure completeness. {rules}"
|
||||||
raise AssertionError(
|
|
||||||
f"Error: dependency manifest '{self}' is missing\n"
|
|
||||||
f" '{editor_only_include}'.\n\n"
|
|
||||||
f" It is editor-only-included by '{file}'\n"
|
|
||||||
f" (inside a '#ifndef SIMDJSON_CONDITIONAL_INCLUDE' block),\n"
|
|
||||||
f" which means the amalgamator does NOT see that include —\n"
|
|
||||||
f" so unless '{self}' lists it, '{editor_only_include}' will\n"
|
|
||||||
f" never be emitted in the single-header build.\n\n"
|
|
||||||
f"FIX: add this line to '{self}':\n\n"
|
|
||||||
f" #include \"{editor_only_include}\"\n\n"
|
|
||||||
f"Then re-run:\n\n"
|
|
||||||
f" python3 singleheader/amalgamate.py\n\n"
|
|
||||||
f"See HACKING.md for the full amalgamation rules.\n"
|
|
||||||
)
|
|
||||||
if editor_only_include in extra_include_set:
|
if editor_only_include in extra_include_set:
|
||||||
extra_include_set.remove(editor_only_include)
|
extra_include_set.remove(editor_only_include)
|
||||||
|
|
||||||
if len(extra_include_set) > 0:
|
assert len(extra_include_set) == 0, f"Error: Dependency file '{self}' includes {extra_include_set}, which are not used in any amalgamated files. Remove these unnecessary includes to clean up dependencies. {rules}"
|
||||||
extras = sorted(str(f) for f in extra_include_set)
|
|
||||||
bullet_list = "\n".join(f" #include \"{e}\"" for e in extras)
|
|
||||||
raise AssertionError(
|
|
||||||
f"Error: dependency manifest '{self}' lists includes that no\n"
|
|
||||||
f" amalgamated file actually needs:\n\n"
|
|
||||||
f"{bullet_list}\n\n"
|
|
||||||
f" Why: every include in a dependencies.h file should correspond to a\n"
|
|
||||||
f" '#ifndef SIMDJSON_CONDITIONAL_INCLUDE' block in some amalgamated\n"
|
|
||||||
f" sibling file. Stale entries here pull unused headers into the\n"
|
|
||||||
f" single-header build.\n\n"
|
|
||||||
f"FIX: either delete the unused entries from '{self}', or, if they were\n"
|
|
||||||
f" supposed to support a real consumer, add the matching editor-only\n"
|
|
||||||
f" '#ifndef SIMDJSON_CONDITIONAL_INCLUDE' block to that consumer.\n\n"
|
|
||||||
f"See HACKING.md for the full amalgamation rules.\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
class SimdjsonRepository:
|
class SimdjsonRepository:
|
||||||
def __init__(self, project_path: str, relative_roots: List[RelativeRoot]):
|
def __init__(self, project_path: str, relative_roots: List[RelativeRoot]):
|
||||||
@@ -464,28 +366,12 @@ class SimdjsonRepository:
|
|||||||
used_files = set([file.include_path for file in self if file.root == root])
|
used_files = set([file.include_path for file in self if file.root == root])
|
||||||
all_files.difference_update(used_files)
|
all_files.difference_update(used_files)
|
||||||
all_files.difference_update(DEPRECATED_FILES)
|
all_files.difference_update(DEPRECATED_FILES)
|
||||||
if len(all_files) > 0:
|
assert len(all_files) == 0, f"Error: The following files in '{root}' are not used in the amalgamation: {sorted(all_files)}. All .h and .cpp files must be included or added to DEPRECATED_FILES. Check for missing includes or deprecate unused files."
|
||||||
bullet_list = "\n".join(f" {root}/{f}" for f in sorted(all_files))
|
|
||||||
raise AssertionError(
|
|
||||||
f"Error: the following files under '{root}/' exist on disk but are not\n"
|
|
||||||
f" reachable from any #include chain seen by the amalgamator:\n\n"
|
|
||||||
f"{bullet_list}\n\n"
|
|
||||||
f" Why: every .h/.cpp file in the tree must either be transitively included\n"
|
|
||||||
f" from the amalgamation roots, or explicitly listed in DEPRECATED_FILES\n"
|
|
||||||
f" in singleheader/amalgamate.py. This catches files that were forgotten\n"
|
|
||||||
f" after a rename, or were added without a corresponding #include.\n\n"
|
|
||||||
f"FIX: pick one for each file above:\n"
|
|
||||||
f" - Add an #include for it in the appropriate amalgamator/dependencies file.\n"
|
|
||||||
f" - If the file is genuinely obsolete, delete it.\n"
|
|
||||||
f" - If the file is a deprecated public header kept only for source\n"
|
|
||||||
f" compatibility, add it to the DEPRECATED_FILES set in\n"
|
|
||||||
f" singleheader/amalgamate.py.\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
class Amalgamator:
|
class Amalgamator:
|
||||||
@classmethod
|
@classmethod
|
||||||
def amalgamate(cls, output_path: str, filename: str, roots: List[RelativeRoot], timestamp: str, version: str):
|
def amalgamate(cls, output_path: str, filename: str, roots: List[RelativeRoot], timestamp: str, version: str):
|
||||||
log(f"Creating {output_path}")
|
print(f"Creating {output_path}")
|
||||||
fid = open(output_path, 'w')
|
fid = open(output_path, 'w')
|
||||||
print(f"/* auto-generated on {timestamp}. version {version} Do not edit! */", file=fid)
|
print(f"/* auto-generated on {timestamp}. version {version} Do not edit! */", file=fid)
|
||||||
amalgamator = cls(fid, SimdjsonRepository(PROJECTPATH, roots))
|
amalgamator = cls(fid, SimdjsonRepository(PROJECTPATH, roots))
|
||||||
@@ -539,17 +425,7 @@ class Amalgamator:
|
|||||||
|
|
||||||
def write_file(self, file: SimdjsonFile):
|
def write_file(self, file: SimdjsonFile):
|
||||||
# Detect cyclic dependencies
|
# Detect cyclic dependencies
|
||||||
if file in self.include_stack:
|
assert file not in self.include_stack, f"Error: Cyclic include detected: {self.include_stack} -> {file}. Remove the circular dependency by restructuring includes."
|
||||||
cycle_start = self.include_stack.index(file)
|
|
||||||
chain = self.include_stack[cycle_start:] + [file]
|
|
||||||
arrow_chain = "\n -> ".join(str(f) for f in chain)
|
|
||||||
raise AssertionError(
|
|
||||||
f"Error: cyclic include detected:\n\n"
|
|
||||||
f" {arrow_chain}\n\n"
|
|
||||||
f"FIX: break the cycle by moving the shared declarations into a separate\n"
|
|
||||||
f" header that both ends of the cycle can include, or by forward-declaring\n"
|
|
||||||
f" instead of including.\n"
|
|
||||||
)
|
|
||||||
self.include_stack.append(file)
|
self.include_stack.append(file)
|
||||||
|
|
||||||
file.processed = False
|
file.processed = False
|
||||||
@@ -701,12 +577,12 @@ class Amalgamator:
|
|||||||
try:
|
try:
|
||||||
proc = subprocess.run(['git', 'show', '-s', '--format=%ci', 'HEAD'],
|
proc = subprocess.run(['git', 'show', '-s', '--format=%ci', 'HEAD'],
|
||||||
stdout=subprocess.PIPE)
|
stdout=subprocess.PIPE)
|
||||||
log("the commandline is {}".format(proc.args))
|
print("the commandline is {}".format(proc.args))
|
||||||
timestamp = proc.stdout.decode('utf-8').strip()
|
timestamp = proc.stdout.decode('utf-8').strip()
|
||||||
except:
|
except:
|
||||||
print("git not found, timestamp based on current time")
|
print("git not found, timestamp based on current time")
|
||||||
timestamp = str(datetime.datetime.now())
|
timestamp = str(datetime.datetime.now())
|
||||||
log(f"timestamp is {timestamp}")
|
print(f"timestamp is {timestamp}")
|
||||||
|
|
||||||
os.makedirs(AMALGAMATE_OUTPUT_PATH, exist_ok=True)
|
os.makedirs(AMALGAMATE_OUTPUT_PATH, exist_ok=True)
|
||||||
AMAL_H = os.path.join(AMALGAMATE_OUTPUT_PATH, "simdjson.h")
|
AMAL_H = os.path.join(AMALGAMATE_OUTPUT_PATH, "simdjson.h")
|
||||||
@@ -749,7 +625,7 @@ if not validate_implementations():
|
|||||||
print("Validation failed. Please update IMPLEMENTATIONS list in amalgamate.py.")
|
print("Validation failed. Please update IMPLEMENTATIONS list in amalgamate.py.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
log("implementation validated")
|
print("implementation validated")
|
||||||
Amalgamator.amalgamate(AMAL_H, "simdjson.h", ['include'], timestamp, version).validate_all_files_used('include')
|
Amalgamator.amalgamate(AMAL_H, "simdjson.h", ['include'], timestamp, version).validate_all_files_used('include')
|
||||||
Amalgamator.amalgamate(AMAL_C, "simdjson.cpp", ['src', 'include'], timestamp, version).validate_all_files_used('src')
|
Amalgamator.amalgamate(AMAL_C, "simdjson.cpp", ['src', 'include'], timestamp, version).validate_all_files_used('src')
|
||||||
|
|
||||||
@@ -766,20 +642,20 @@ def create_zip():
|
|||||||
outdir = AMALGAMATE_OUTPUT_PATH
|
outdir = AMALGAMATE_OUTPUT_PATH
|
||||||
|
|
||||||
path = os.path.join(outdir, "singleheader.zip")
|
path = os.path.join(outdir, "singleheader.zip")
|
||||||
log(f"Creating {path}")
|
print(f"Creating {path}")
|
||||||
with zipfile.ZipFile(path, 'w') as zf:
|
with zipfile.ZipFile(path, 'w') as zf:
|
||||||
for name in ["simdjson.cpp", "simdjson.h"]:
|
for name in ["simdjson.cpp", "simdjson.h"]:
|
||||||
source = os.path.join(outdir, name)
|
source = os.path.join(outdir, name)
|
||||||
log(f"Adding {source}")
|
print(f"Adding {source}")
|
||||||
zf.write(source, name)
|
zf.write(source, name)
|
||||||
log(f"Created {path}")
|
print(f"Created {path}")
|
||||||
create_zip()
|
create_zip()
|
||||||
log("Done with all files generation.")
|
print("Done with all files generation.")
|
||||||
|
|
||||||
log(f"Files have been written to directory: {AMALGAMATE_OUTPUT_PATH}/")
|
print(f"Files have been written to directory: {AMALGAMATE_OUTPUT_PATH}/")
|
||||||
log(subprocess.run(['ls', '-la', AMAL_C, AMAL_H, DEMOCPP, README],
|
print(subprocess.run(['ls', '-la', AMAL_C, AMAL_H, DEMOCPP, README],
|
||||||
stdout=subprocess.PIPE).stdout.decode('utf-8').strip())
|
stdout=subprocess.PIPE).stdout.decode('utf-8').strip())
|
||||||
log("Done with all files generation.")
|
print("Done with all files generation.")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -788,7 +664,7 @@ log("Done with all files generation.")
|
|||||||
# Instructions to create demo
|
# Instructions to create demo
|
||||||
#
|
#
|
||||||
|
|
||||||
log("\nGiving final instructions:")
|
print("\nGiving final instructions:")
|
||||||
with open(README) as r:
|
with open(README) as r:
|
||||||
for line in r:
|
for line in r:
|
||||||
log(line)
|
print(line)
|
||||||
|
|||||||
+45
-33
@@ -38,43 +38,55 @@ namespace {
|
|||||||
using namespace simd;
|
using namespace simd;
|
||||||
|
|
||||||
simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64<uint8_t>& in) {
|
simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64<uint8_t>& in) {
|
||||||
const uint8x16_t op_table = simd8<uint8_t>(
|
// Functional programming causes trouble with Visual Studio.
|
||||||
0xff, 0, ',', ':', 0, '[', ']', '{', '}', 0, 0, 0, 0, 0, 0, 0
|
// Keeping this version in comments since it is much nicer:
|
||||||
);
|
// auto v = in.map<uint8_t>([&](simd8<uint8_t> chunk) {
|
||||||
const uint8x16_t ws_table = simd8<uint8_t>(
|
// auto nib_lo = chunk & 0xf;
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 0, 0xff, 0, 0
|
// auto nib_hi = chunk.shr<4>();
|
||||||
|
// auto shuf_lo = nib_lo.lookup_16<uint8_t>(16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0);
|
||||||
|
// auto shuf_hi = nib_hi.lookup_16<uint8_t>(8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0);
|
||||||
|
// return shuf_lo & shuf_hi;
|
||||||
|
// });
|
||||||
|
const simd8<uint8_t> table1(16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0);
|
||||||
|
const simd8<uint8_t> table2(8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0);
|
||||||
|
|
||||||
|
simd8x64<uint8_t> v(
|
||||||
|
(in.chunks[0] & 0xf).lookup_16(table1) & (in.chunks[0].shr<4>()).lookup_16(table2),
|
||||||
|
(in.chunks[1] & 0xf).lookup_16(table1) & (in.chunks[1].shr<4>()).lookup_16(table2),
|
||||||
|
(in.chunks[2] & 0xf).lookup_16(table1) & (in.chunks[2].shr<4>()).lookup_16(table2),
|
||||||
|
(in.chunks[3] & 0xf).lookup_16(table1) & (in.chunks[3].shr<4>()).lookup_16(table2)
|
||||||
);
|
);
|
||||||
|
|
||||||
const uint8x16_t d0_0 = in.chunks[0];
|
|
||||||
const uint8x16_t d0_1 = in.chunks[1];
|
|
||||||
const uint8x16_t d0_2 = in.chunks[2];
|
|
||||||
const uint8x16_t d0_3 = in.chunks[3];
|
|
||||||
|
|
||||||
const uint8x16_t match_op_0 = vceqq_u8(vqtbl1q_u8(op_table, vshrq_n_u8(vaddq_u8(d0_0, vdupq_n_u8(3)), 4)), d0_0);
|
// We compute whitespace and op separately. If the code later only use one or the
|
||||||
const uint8x16_t match_op_1 = vceqq_u8(vqtbl1q_u8(op_table, vshrq_n_u8(vaddq_u8(d0_1, vdupq_n_u8(3)), 4)), d0_1);
|
// other, given the fact that all functions are aggressively inlined, we can
|
||||||
const uint8x16_t match_op_2 = vceqq_u8(vqtbl1q_u8(op_table, vshrq_n_u8(vaddq_u8(d0_2, vdupq_n_u8(3)), 4)), d0_2);
|
// hope that useless computations will be omitted. This is namely case when
|
||||||
const uint8x16_t match_op_3 = vceqq_u8(vqtbl1q_u8(op_table, vshrq_n_u8(vaddq_u8(d0_3, vdupq_n_u8(3)), 4)), d0_3);
|
// minifying (we only need whitespace). *However* if we only need spaces,
|
||||||
|
// it is likely that we will still compute 'v' above with two lookup_16: one
|
||||||
|
// could do it a bit cheaper. This is in contrast with the x64 implementations
|
||||||
|
// where we can, efficiently, do the white space and structural matching
|
||||||
|
// separately. One reason for this difference is that on ARM NEON, the table
|
||||||
|
// lookups either zero or leave unchanged the characters exceeding 0xF whereas
|
||||||
|
// on x64, the equivalent instruction (pshufb) automatically applies a mask,
|
||||||
|
// ignoring the 4 most significant bits. Thus the x64 implementation is
|
||||||
|
// optimized differently. This being said, if you use this code strictly
|
||||||
|
// just for minification (or just to identify the structural characters),
|
||||||
|
// there is a small untaken optimization opportunity here. We deliberately
|
||||||
|
// do not pick it up.
|
||||||
|
|
||||||
const uint8x16_t match_ws_0 = vqtbx1q_u8(vceqq_u8(d0_0, vdupq_n_u8(' ')), ws_table, d0_0);
|
uint64_t op = simd8x64<bool>(
|
||||||
const uint8x16_t match_ws_1 = vqtbx1q_u8(vceqq_u8(d0_1, vdupq_n_u8(' ')), ws_table, d0_1);
|
v.chunks[0].any_bits_set(0x7),
|
||||||
const uint8x16_t match_ws_2 = vqtbx1q_u8(vceqq_u8(d0_2, vdupq_n_u8(' ')), ws_table, d0_2);
|
v.chunks[1].any_bits_set(0x7),
|
||||||
const uint8x16_t match_ws_3 = vqtbx1q_u8(vceqq_u8(d0_3, vdupq_n_u8(' ')), ws_table, d0_3);
|
v.chunks[2].any_bits_set(0x7),
|
||||||
|
v.chunks[3].any_bits_set(0x7)
|
||||||
|
).to_bitmask();
|
||||||
|
|
||||||
const uint8x16_t bit_mask = simd8<uint8_t>(
|
uint64_t whitespace = simd8x64<bool>(
|
||||||
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
|
v.chunks[0].any_bits_set(0x18),
|
||||||
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
|
v.chunks[1].any_bits_set(0x18),
|
||||||
);
|
v.chunks[2].any_bits_set(0x18),
|
||||||
|
v.chunks[3].any_bits_set(0x18)
|
||||||
uint8x16_t op_sum0 = vpaddq_u8(vandq_u8(match_op_0, bit_mask), vandq_u8(match_op_1, bit_mask));
|
).to_bitmask();
|
||||||
uint8x16_t ws_sum0 = vpaddq_u8(vandq_u8(match_ws_0, bit_mask), vandq_u8(match_ws_1, bit_mask));
|
|
||||||
uint8x16_t op_sum1 = vpaddq_u8(vandq_u8(match_op_2, bit_mask), vandq_u8(match_op_3, bit_mask));
|
|
||||||
uint8x16_t ws_sum1 = vpaddq_u8(vandq_u8(match_ws_2, bit_mask), vandq_u8(match_ws_3, bit_mask));
|
|
||||||
op_sum0 = vpaddq_u8(op_sum0, op_sum1);
|
|
||||||
ws_sum0 = vpaddq_u8(ws_sum0, ws_sum1);
|
|
||||||
op_sum0 = vpaddq_u8(op_sum0, op_sum0);
|
|
||||||
ws_sum0 = vpaddq_u8(ws_sum0, ws_sum0);
|
|
||||||
const uint64_t op = vgetq_lane_u64(vreinterpretq_u64_u8(op_sum0), 0);
|
|
||||||
const uint64_t whitespace = vgetq_lane_u64(vreinterpretq_u64_u8(ws_sum0), 0);
|
|
||||||
|
|
||||||
return { whitespace, op };
|
return { whitespace, op };
|
||||||
}
|
}
|
||||||
@@ -120,7 +132,7 @@ simdjson_warn_unused error_code implementation::minify(const uint8_t *buf, size_
|
|||||||
return arm64::stage1::json_minifier::minify<64>(buf, len, dst, dst_len);
|
return arm64::stage1::json_minifier::minify<64>(buf, len, dst, dst_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
simdjson_flatten simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode streaming) noexcept {
|
simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode streaming) noexcept {
|
||||||
this->buf = _buf;
|
this->buf = _buf;
|
||||||
this->len = _len;
|
this->len = _len;
|
||||||
return arm64::stage1::json_structural_indexer::index<64>(buf, len, *this, streaming);
|
return arm64::stage1::json_structural_indexer::index<64>(buf, len, *this, streaming);
|
||||||
|
|||||||
@@ -291,20 +291,7 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primit
|
|||||||
case '"': return visitor.visit_root_string(*this, value);
|
case '"': return visitor.visit_root_string(*this, value);
|
||||||
case 't': return visitor.visit_root_true_atom(*this, value);
|
case 't': return visitor.visit_root_true_atom(*this, value);
|
||||||
case 'f': return visitor.visit_root_false_atom(*this, value);
|
case 'f': return visitor.visit_root_false_atom(*this, value);
|
||||||
#if SIMDJSON_ENABLE_NAN_INF
|
|
||||||
case 'n': {
|
|
||||||
auto err = visitor.visit_root_null_atom(*this, value);
|
|
||||||
if (err == SUCCESS) { return err; }
|
|
||||||
// propagate the error value returned by a bad 'null' atom if parsing 'nan' fails
|
|
||||||
return visitor.visit_root_nan_atom(*this, value, err);
|
|
||||||
}
|
|
||||||
// 'N' isn't a canonically recognized atom, so we return a TAPE_ERROR if failure occurs
|
|
||||||
case 'N': return visitor.visit_root_nan_atom(*this, value, TAPE_ERROR);
|
|
||||||
case 'i':
|
|
||||||
case 'I': return visitor.visit_root_inf_atom(*this, value);
|
|
||||||
#else
|
|
||||||
case 'n': return visitor.visit_root_null_atom(*this, value);
|
case 'n': return visitor.visit_root_null_atom(*this, value);
|
||||||
#endif
|
|
||||||
case '-':
|
case '-':
|
||||||
case '0': case '1': case '2': case '3': case '4':
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
case '5': case '6': case '7': case '8': case '9':
|
case '5': case '6': case '7': case '8': case '9':
|
||||||
@@ -326,20 +313,7 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V
|
|||||||
switch (*value) {
|
switch (*value) {
|
||||||
case 't': return visitor.visit_true_atom(*this, value);
|
case 't': return visitor.visit_true_atom(*this, value);
|
||||||
case 'f': return visitor.visit_false_atom(*this, value);
|
case 'f': return visitor.visit_false_atom(*this, value);
|
||||||
#if SIMDJSON_ENABLE_NAN_INF
|
|
||||||
case 'n': {
|
|
||||||
auto err = visitor.visit_null_atom(*this, value);
|
|
||||||
if (err == SUCCESS) { return err; }
|
|
||||||
// propagate the error value returned by a bad 'null' atom if parsing 'nan' fails
|
|
||||||
return visitor.visit_nan_atom(*this, value, err);
|
|
||||||
}
|
|
||||||
// 'N' isn't a canonically recognized atom, so we return a TAPE_ERROR if failure occurs
|
|
||||||
case 'N': return visitor.visit_nan_atom(*this, value, TAPE_ERROR);
|
|
||||||
case 'i':
|
|
||||||
case 'I': return visitor.visit_inf_atom(*this, value);
|
|
||||||
#else
|
|
||||||
case 'n': return visitor.visit_null_atom(*this, value);
|
case 'n': return visitor.visit_null_atom(*this, value);
|
||||||
#endif
|
|
||||||
default:
|
default:
|
||||||
log_error("Non-value found when value was expected!");
|
log_error("Non-value found when value was expected!");
|
||||||
return TAPE_ERROR;
|
return TAPE_ERROR;
|
||||||
@@ -351,4 +325,4 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V
|
|||||||
} // namespace SIMDJSON_IMPLEMENTATION
|
} // namespace SIMDJSON_IMPLEMENTATION
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|
||||||
#endif // SIMDJSON_SRC_GENERIC_STAGE2_JSON_ITERATOR_H
|
#endif // SIMDJSON_SRC_GENERIC_STAGE2_JSON_ITERATOR_H
|
||||||
@@ -76,15 +76,6 @@ struct tape_builder {
|
|||||||
simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept;
|
simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept;
|
||||||
simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept;
|
simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept;
|
||||||
|
|
||||||
#if SIMDJSON_ENABLE_NAN_INF
|
|
||||||
simdjson_warn_unused simdjson_inline error_code visit_nan_atom(json_iterator &iter, const uint8_t *value, error_code errc) noexcept;
|
|
||||||
simdjson_warn_unused simdjson_inline error_code visit_root_nan_atom(json_iterator &iter, const uint8_t *value, error_code errc) noexcept;
|
|
||||||
// Attempts to parse 'inf' or 'infinity' (case insensitive). Because neither are canonical atoms,
|
|
||||||
// this returns a tape error on failure.
|
|
||||||
simdjson_warn_unused simdjson_inline error_code visit_inf_atom(json_iterator &iter, const uint8_t *value) noexcept;
|
|
||||||
simdjson_warn_unused simdjson_inline error_code visit_root_inf_atom(json_iterator &iter, const uint8_t *value) noexcept;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/** Called each time a new field or element in an array or object is found. */
|
/** Called each time a new field or element in an array or object is found. */
|
||||||
simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator &iter) noexcept;
|
simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator &iter) noexcept;
|
||||||
|
|
||||||
@@ -264,38 +255,6 @@ simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_at
|
|||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SIMDJSON_ENABLE_NAN_INF
|
|
||||||
simdjson_warn_unused simdjson_inline error_code tape_builder::visit_nan_atom(json_iterator &iter, const uint8_t *value, error_code errc) noexcept {
|
|
||||||
iter.log_value("nan");
|
|
||||||
if (!atomparsing::is_valid_nan_atom(value)) { return errc; }
|
|
||||||
tape.append_double(std::numeric_limits<double>::quiet_NaN());
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_nan_atom(json_iterator &iter, const uint8_t *value, error_code errc) noexcept {
|
|
||||||
iter.log_value("nan");
|
|
||||||
if (!atomparsing::is_valid_nan_atom(value, iter.remaining_len())) { return errc; }
|
|
||||||
tape.append_double(std::numeric_limits<double>::quiet_NaN());
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
simdjson_warn_unused simdjson_inline error_code tape_builder::visit_inf_atom(json_iterator &iter, const uint8_t *value) noexcept {
|
|
||||||
iter.log_value("inf");
|
|
||||||
// Because 'inf' is an extension, non a canonical atom, a tape error should be returned on failure
|
|
||||||
if (!atomparsing::is_valid_inf_atom(value)) { return TAPE_ERROR; }
|
|
||||||
tape.append_double(std::numeric_limits<double>::infinity());
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_inf_atom(json_iterator &iter, const uint8_t *value) noexcept {
|
|
||||||
iter.log_value("inf");
|
|
||||||
// Because 'inf' is an extension, non a canonical atom, a tape error should be returned on failure
|
|
||||||
if (!atomparsing::is_valid_inf_atom(value, iter.remaining_len())) { return TAPE_ERROR; }
|
|
||||||
tape.append_double(std::numeric_limits<double>::infinity());
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
#endif // SIMDJSON_ENABLE_NAN_INF
|
|
||||||
|
|
||||||
// private:
|
// private:
|
||||||
|
|
||||||
simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator &iter) const noexcept {
|
simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator &iter) const noexcept {
|
||||||
@@ -351,4 +310,4 @@ simdjson_inline void tape_builder::on_end_string(uint8_t *dst) noexcept {
|
|||||||
} // namespace SIMDJSON_IMPLEMENTATION
|
} // namespace SIMDJSON_IMPLEMENTATION
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|
||||||
#endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H
|
#endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H
|
||||||
@@ -52,17 +52,13 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
#include <intrin.h>
|
#include <intrin.h>
|
||||||
#elif (defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID)) || defined(__FILC__)
|
#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID)
|
||||||
#include <cpuid.h>
|
#include <cpuid.h>
|
||||||
#endif
|
#endif
|
||||||
#if defined(__loongarch__) && defined(__linux__)
|
#if defined(__loongarch__) && defined(__linux__)
|
||||||
#include <sys/auxv.h>
|
#include <sys/auxv.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __FILC__
|
|
||||||
#include <stdfil.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace simdjson {
|
namespace simdjson {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
@@ -113,7 +109,7 @@ static inline void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx,
|
|||||||
*ebx = cpu_info[1];
|
*ebx = cpu_info[1];
|
||||||
*ecx = cpu_info[2];
|
*ecx = cpu_info[2];
|
||||||
*edx = cpu_info[3];
|
*edx = cpu_info[3];
|
||||||
#elif (defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID)) || defined(__FILC__)
|
#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID)
|
||||||
uint32_t level = *eax;
|
uint32_t level = *eax;
|
||||||
__get_cpuid(level, eax, ebx, ecx, edx);
|
__get_cpuid(level, eax, ebx, ecx, edx);
|
||||||
#else
|
#else
|
||||||
@@ -130,8 +126,6 @@ static inline void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx,
|
|||||||
static inline uint64_t xgetbv() {
|
static inline uint64_t xgetbv() {
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
return _xgetbv(0);
|
return _xgetbv(0);
|
||||||
#elif defined(__FILC__)
|
|
||||||
return zxgetbv();
|
|
||||||
#else
|
#else
|
||||||
uint32_t xcr0_lo, xcr0_hi;
|
uint32_t xcr0_lo, xcr0_hi;
|
||||||
asm volatile("xgetbv\n\t" : "=a" (xcr0_lo), "=d" (xcr0_hi) : "c" (0));
|
asm volatile("xgetbv\n\t" : "=a" (xcr0_lo), "=d" (xcr0_hi) : "c" (0));
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ if(SIMDJSON_STATIC_REFLECTION)
|
|||||||
add_cpp_test(static_reflection_edge_cases_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(static_reflection_edge_cases_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(static_reflection_enum_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(static_reflection_enum_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(static_reflection_fractured_json_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(static_reflection_fractured_json_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(static_reflection_annotations_tests LABELS ondemand acceptance per_implementation)
|
|
||||||
endif(SIMDJSON_STATIC_REFLECTION)
|
endif(SIMDJSON_STATIC_REFLECTION)
|
||||||
# Copy the simdjson dll into the tests directory
|
# Copy the simdjson dll into the tests directory
|
||||||
if(MSVC AND BUILD_SHARED_LIBS)
|
if(MSVC AND BUILD_SHARED_LIBS)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user