mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ee8515122d | |||
| 5d35e7ca1f | |||
| deefc88b9c | |||
| c80dda7c58 | |||
| d2954ef68b | |||
| ac719827ff | |||
| 6ea77392a7 | |||
| e2f879751c | |||
| bf7834179c | |||
| 69ab8848bf | |||
| 7288323e36 | |||
| 3f381cf3ee | |||
| 52406402ed | |||
| 799d8e3fc9 | |||
| 6c647d9f3b | |||
| 8519e24f12 | |||
| fffb62743c | |||
| c888075f8d | |||
| 9b8aac5f22 | |||
| 40b414d184 | |||
| c85e8a7db1 | |||
| 83f390f8e7 | |||
| 82563dcf70 | |||
| 646bd3f2c3 | |||
| 4b0a908bb2 | |||
| 4c98e51c53 | |||
| 58e3d5d9cc | |||
| a22a92dc72 | |||
| c4654553dd | |||
| 5997f82c37 | |||
| bee410b315 | |||
| b115e3ef61 | |||
| 669229df5d | |||
| b6a2286e62 | |||
| 3a97c57773 | |||
| e1c6a778f8 | |||
| d413dd5b0e | |||
| a33fdc2770 | |||
| 87f5f7a250 | |||
| 9923d27994 | |||
| 876be23545 | |||
| 2028bdaeac | |||
| 1ac3a00ca9 | |||
| 6d7cc2c7c1 | |||
| 61a52287fc | |||
| 37a9aaad48 | |||
| 78325888ea | |||
| a90cb5d900 | |||
| 34a01ec3af | |||
| e510ed2774 | |||
| dabac13dde | |||
| aef7111c05 | |||
| 0c08894a54 | |||
| 2fe3a1467d | |||
| 167a46a0e7 | |||
| 2b532489cc | |||
| 85163df88c | |||
| 6a5be0f0db | |||
| 710a3c7241 | |||
| c93e02e172 | |||
| 517143aec4 | |||
| 9feda5e703 | |||
| cda198b7b9 | |||
| aad17a6a8d | |||
| 32d072a208 | |||
| 5eab555053 | |||
| 6430fbd39a | |||
| 4d2fc31b27 | |||
| 33610b59eb | |||
| 23290ae48d | |||
| eb3a085e0f | |||
| 4c8ec35a48 |
@@ -45,12 +45,16 @@ It is fine to report bugs against our main branch, but if that is what you are d
|
||||
|
||||
**Configuration (please complete the following information if relevant)**
|
||||
- OS: [e.g. Ubuntu 16.04.6 LTS]
|
||||
- Compiler [e.g. Apple clang version 11.0.3 (clang-1103.0.32.59) x86_64-apple-darwin19.4.0]
|
||||
- Compiler* [e.g. Apple clang version 11.0.3 (clang-1103.0.32.59) x86_64-apple-darwin19.4.0]
|
||||
- Version [e.g. 22]
|
||||
- Optimization setting (e.g., -O3)
|
||||
|
||||
We support up-to-date 64-bit ARM and x64 FreeBSD, macOS, Windows and Linux systems. Please ensure that your configuration is supported before labelling the issue as a bug.
|
||||
|
||||
* 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
|
||||
invite contributions either in the form an analysis or of a code contribution.
|
||||
|
||||
**Indicate whether you are willing or able to provide a bug fix as a pull request**
|
||||
|
||||
If you plan to contribute to simdjson, please read our guide:
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -34,18 +34,18 @@ jobs:
|
||||
sudo apt update
|
||||
sudo apt-get install --quiet ninja-build valgrind zip unzip lsb-release wget software-properties-common gnupg
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14
|
||||
sudo apt-get purge --auto-remove llvm python3-lldb-15 llvm-15
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh $CLANGVERSION
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
id: cache-corpus
|
||||
with:
|
||||
path: out/
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
name: LoongArch64-CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
loongarch64:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- { toolchain-version: 2023.08.08 }
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install build requirements
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y --no-install-recommends cmake
|
||||
|
||||
- uses: actions/cache/restore@v4
|
||||
id: restore-cache
|
||||
with:
|
||||
path: /opt/cross-tools
|
||||
key: loongarch64-${{ matrix.platform.toolchain-version }}
|
||||
|
||||
- name: Download LoongArch64 gcc+glibc toolchain
|
||||
if: ${{ !steps.restore-cache.outputs.cache-hit }}
|
||||
run: |
|
||||
url="https://github.com/loongson/build-tools/releases/download/${{ matrix.platform.toolchain-version }}/x86_64-cross-tools-loongarch64-gcc-libc.tar.xz"
|
||||
|
||||
wget "$url" -O /tmp/toolchain.tar.xz
|
||||
|
||||
mkdir -p /opt
|
||||
tar -C /opt -x -f /tmp/toolchain.tar.xz
|
||||
|
||||
- uses: actions/cache/save@v3
|
||||
if: ${{ !steps.restore-cache.outputs.cache-hit }}
|
||||
with:
|
||||
path: /opt/cross-tools
|
||||
key: loongarch64-${{ matrix.platform.toolchain-version }}
|
||||
|
||||
- name: setup Loongarch64 build environment
|
||||
run: |
|
||||
echo "/opt/cross-tools/bin" >> $GITHUB_PATH
|
||||
echo "CC=loongarch64-unknown-linux-gnu-gcc" >> $GITHUB_ENV
|
||||
echo "CXX=loongarch64-unknown-linux-gnu-g++" >> $GITHUB_ENV
|
||||
- name: configure
|
||||
run: cmake -B build -DCMAKE_SYSTEM_PROCESSOR=loongarch64 -DARCH=lonngarch64 -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_C_COMPILER=loongarch64-unknown-linux-gnu-gcc -DCMAKE_CXX_COMPILER=loongarch64-unknown-linux-gnu-g++
|
||||
- name: build
|
||||
run: cmake --build build
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
name: short fuzz on the power arch
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
armv7_job:
|
||||
if: >-
|
||||
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||
# The host should always be Linux
|
||||
runs-on: ubuntu-20.04
|
||||
name: Build on ubuntu-20.04 ppc64le
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: uraimo/run-on-arch-action@v2.0.5
|
||||
name: Run commands
|
||||
id: runcmd
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
with:
|
||||
arch: ppc64le
|
||||
distro: buster
|
||||
|
||||
# Not required, but speeds up builds by storing container images in
|
||||
# a GitHub package registry.
|
||||
githubToken: ${{ github.token }}
|
||||
|
||||
run: |
|
||||
export CLANGSUFFIX="-7"
|
||||
apt-get -qq update
|
||||
apt-get install -q -y clang-7 libfuzzer-7-dev git wget zip ninja-build gnupg software-properties-common
|
||||
wget -q -O - "https://raw.githubusercontent.com/simdjson/debian-ppa/master/key.gpg" | apt-key add -
|
||||
apt-add-repository "deb https://raw.githubusercontent.com/simdjson/debian-ppa/master simdjson main"
|
||||
apt-get -qq update
|
||||
apt-get purge cmake cmake-data
|
||||
apt-get -t simdjson -y install cmake
|
||||
mkdir -p build ; cd build
|
||||
cmake .. -GNinja \
|
||||
-DCMAKE_CXX_COMPILER=clang++$CLANGSUFFIX \
|
||||
-DCMAKE_C_COMPILER=clang$CLANGSUFFIX \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DSIMDJSON_DEVELOPER_MODE=ON \
|
||||
-DSIMDJSON_ENABLE_FUZZING=On \
|
||||
-DSIMDJSON_COMPETITION=OFF \
|
||||
-DSIMDJSON_GOOGLE_BENCHMARKS=OFF \
|
||||
-DSIMDJSON_DISABLE_DEPRECATED_API=On \
|
||||
-DSIMDJSON_FUZZ_LDFLAGS=-lFuzzer \
|
||||
-DCMAKE_CXX_FLAGS="-fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=" \
|
||||
-DCMAKE_C_FLAGS="-fsanitize=fuzzer-no-link" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DSIMDJSON_FUZZ_LINKMAIN=Off
|
||||
cd ..
|
||||
builddir=build
|
||||
cmake --build $builddir
|
||||
wget -O corpus.tar.gz https://readonly:readonly@www.pauldreik.se/fuzzdata/index.php?project=simdjson
|
||||
tar xf corpus.tar.gz
|
||||
fuzzernames=$(cmake --build $builddir --target print_all_fuzzernames |tail -n1)
|
||||
for fuzzer in $fuzzernames ; do
|
||||
exe=$builddir/fuzz/$fuzzer
|
||||
shortname=$(echo $fuzzer |cut -f2- -d_)
|
||||
echo found fuzzer $shortname with executable $exe
|
||||
mkdir -p out/$shortname
|
||||
others=$(find out -type d -not -name $shortname -not -name out -not -name cmin)
|
||||
$exe -max_total_time=20 -max_len=4000 out/$shortname $others
|
||||
echo "*************************************************************************"
|
||||
done
|
||||
echo "all is good, no errors found in any of these fuzzers: $fuzzernames"
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
CC: gcc-8
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Ubuntu 22.04 CI (GCC 13)
|
||||
name: Ubuntu 24.04 CI (GCC 13)
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
@@ -7,10 +7,10 @@ jobs:
|
||||
if: >-
|
||||
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
@@ -20,4 +20,4 @@ jobs:
|
||||
cd build &&
|
||||
CXX=g++-13 cmake -DSIMDJSON_DEVELOPER_MODE=ON .. &&
|
||||
cmake --build . &&
|
||||
ctest --output-on-failure -LE explicitonly -j
|
||||
ctest --output-on-failure -LE explicitonly -j
|
||||
|
||||
@@ -9,7 +9,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Ubuntu 22.04 CI (GCC 11) with Thread Sanitizer
|
||||
name: Ubuntu 22.04 CI (GCC 12) with Thread Sanitizer
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
run: |
|
||||
mkdir build &&
|
||||
cd build &&
|
||||
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 &&
|
||||
ctest --output-on-failure -R parse_many_test &&
|
||||
ctest --output-on-failure -R document_stream_tests
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -12,10 +12,11 @@ jobs:
|
||||
include:
|
||||
- {arch: ARM}
|
||||
- {arch: ARM64}
|
||||
- {arch: ARM64EC}
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Use cmake
|
||||
run: |
|
||||
cmake -A ${{ matrix.arch }} -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 &&
|
||||
cmake --build build --verbose
|
||||
@@ -8,7 +8,7 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
Vendored
+12
-1
@@ -98,6 +98,17 @@
|
||||
"queue": "cpp",
|
||||
"shared_mutex": "cpp",
|
||||
"ranges": "cpp",
|
||||
"span": "cpp"
|
||||
"span": "cpp",
|
||||
"__verbose_abort": "cpp",
|
||||
"charconv": "cpp",
|
||||
"source_location": "cpp",
|
||||
"strstream": "cpp",
|
||||
"typeindex": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"numbers": "cpp",
|
||||
"semaphore": "cpp",
|
||||
"stop_token": "cpp",
|
||||
"cfenv": "cpp"
|
||||
}
|
||||
}
|
||||
+21
-3
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14)
|
||||
project(
|
||||
simdjson
|
||||
# The version number is modified by tools/release.py
|
||||
VERSION 3.6.4
|
||||
VERSION 3.9.3
|
||||
DESCRIPTION "Parsing gigabytes of JSON per second"
|
||||
HOMEPAGE_URL "https://simdjson.org/"
|
||||
LANGUAGES CXX C
|
||||
@@ -20,8 +20,8 @@ string(
|
||||
# ---- Options, variables ----
|
||||
|
||||
# These version numbers are modified by tools/release.py
|
||||
set(SIMDJSON_LIB_VERSION "19.0.0" CACHE STRING "simdjson library version")
|
||||
set(SIMDJSON_LIB_SOVERSION "19" CACHE STRING "simdjson library soversion")
|
||||
set(SIMDJSON_LIB_VERSION "22.0.0" CACHE STRING "simdjson library version")
|
||||
set(SIMDJSON_LIB_SOVERSION "22" CACHE STRING "simdjson library soversion")
|
||||
|
||||
option(SIMDJSON_BUILD_STATIC_LIB "Build simdjson_static library along with simdjson" OFF)
|
||||
|
||||
@@ -106,6 +106,24 @@ if(
|
||||
)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(loongarch64)$")
|
||||
option(SIMDJSON_PREFER_LSX "Prefer LoongArch SX" ON)
|
||||
include(CheckCXXCompilerFlag)
|
||||
check_cxx_compiler_flag(-mlasx COMPILER_SUPPORTS_LASX)
|
||||
check_cxx_compiler_flag(-mlsx COMPILER_SUPPORTS_LSX)
|
||||
if(COMPILER_SUPPORTS_LASX AND NOT SIMDJSON_PREFER_LSX)
|
||||
simdjson_add_props(
|
||||
target_compile_options PRIVATE
|
||||
-mlasx
|
||||
)
|
||||
elseif(COMPILER_SUPPORTS_LSX)
|
||||
simdjson_add_props(
|
||||
target_compile_options PRIVATE
|
||||
-mlsx
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# GCC and Clang have horrendous Debug builds when using SIMD.
|
||||
# A common fix is to use '-Og' instead.
|
||||
# bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412
|
||||
|
||||
+2
-1
@@ -52,10 +52,11 @@ General Guidelines
|
||||
Contributors are encouraged to :
|
||||
|
||||
- Document their changes. Though we do not enforce a rule regarding code comments, we prefer that non-trivial algorithms and techniques be somewhat documented in the code.
|
||||
- Follow as much as possible the existing code style. We do not enforce a specific code style, but we prefer consistency.
|
||||
- Follow as much as possible the existing code style. We do not enforce a specific code style, but we prefer consistency. We avoid contractions (isn't, aren't) in the comments.
|
||||
- Modify as few lines of code as possible when working on an issue. The more lines you modify, the harder it is for your fellow human beings to understand what is going on.
|
||||
- Tools may report "problems" with the code, but we never delegate programming to tools: if there is a problem with the code, we need to understand it. Thus we will not "fix" code merely to please a static analyzer.
|
||||
- Provide tests for any new feature. We will not merge a new feature without tests.
|
||||
- Run before/after benchmarks so that we can appreciate the effect of the changes on the performance.
|
||||
|
||||
Pull Requests
|
||||
--------------
|
||||
|
||||
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = "3.6.4"
|
||||
PROJECT_NUMBER = "3.9.3"
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
|
||||
+3
-7
@@ -80,7 +80,7 @@ simdjson's source structure, from the top level, looks like this:
|
||||
* simdjson/internal/*.h: the `simdjson::internal` namespace. Private classes and functions used by the rest of simdjson.
|
||||
* simdjson/dom.h: the `simdjson::dom` namespace. Includes all public DOM classes.
|
||||
* simdjson/dom/*.h: Declarations/definitions for individual DOM classes.
|
||||
* simdjson/arm64|fallback|haswell|icelake|ppc64|westmere.h: `simdjson::<implementation>` namesapce. Common implementation-specific tools like number and string parsing, as well as minification.
|
||||
* simdjson/arm64|fallback|haswell|icelake|ppc64|westmere.h: `simdjson::<implementation>` namespace. Common implementation-specific tools like number and string parsing, as well as minification.
|
||||
* simdjson/arm64|fallback|haswell|icelake|ppc64|westmere/*.h: implementation-specific functions such as , etc.
|
||||
* simdjson/generic/*.h: the bulk of the actual code, written generically and compiled for each implementation, using functions defined in the implementation's .h files.
|
||||
* simdjson/generic/dependencies.h: dependencies on common, non-implementation-specific simdjson classes. This will be included before including amalgamated.h.
|
||||
@@ -283,8 +283,6 @@ If your compiler does not default on C++11 support or better you may get failing
|
||||
|
||||
Note that the name of directory (`build`) is arbitrary, you can name it as you want (e.g., `buildgcc`) and you can have as many different such directories as you would like (one per configuration).
|
||||
|
||||
|
||||
|
||||
## Usage (CMake on 64-bit Windows using Visual Studio 2019 or better)
|
||||
|
||||
Recent versions of Visual Studio support CMake natively, [please refer to the Visual Studio documentation](https://learn.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio?view=msvc-170).
|
||||
@@ -304,17 +302,15 @@ Though having Visual Studio installed is necessary, one can build simdjson using
|
||||
- `mkdir build`
|
||||
- `cd build`
|
||||
- `cmake ..`
|
||||
- `cmake --build . -config Release`
|
||||
- `cmake --build . --config Release`
|
||||
|
||||
|
||||
Furthermore, if you have installed LLVM clang on Windows, for example as a component of Visual Studio 2019, you can configure and build simdjson using LLVM clang on Windows using cmake:
|
||||
|
||||
|
||||
- `mkdir build`
|
||||
- `cd build`
|
||||
- `cmake -T ClangCL ..`
|
||||
- `cmake --build . -config Release`
|
||||
|
||||
- `cmake --build . --config Release`
|
||||
|
||||
## Various References
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ Real-world usage
|
||||
- [vast](https://github.com/tenzir/vast)
|
||||
- [ada-url](https://github.com/ada-url/ada)
|
||||
- [fastgron](https://github.com/adamritter/fastgron)
|
||||
- [WasmEdge](https://wasmedge.org)
|
||||
|
||||
If you are planning to use simdjson in a product, please work from one of our releases.
|
||||
|
||||
@@ -168,7 +169,8 @@ We distinguish between "bindings" (which just wrap the C++ code) and a port to a
|
||||
- [simdjzon](https://github.com/travisstaloch/simdjzon): zig port.
|
||||
- [JSON-Simd](https://github.com/rawleyfowler/JSON-simd): Raku bindings.
|
||||
- [JSON::SIMD](https://metacpan.org/pod/JSON::SIMD): Perl bindings; fully-featured JSON module that uses simdjson for decoding.
|
||||
- [gemmaJSON](https://github.com/sainttttt/gemmaJSON): Nim json parser based on simdjson bindings.
|
||||
- [gemmaJSON](https://github.com/sainttttt/gemmaJSON): Nim JSON parser based on simdjson bindings.
|
||||
- [simdjson-java](https://github.com/simdjson/simdjson-java): Java port.
|
||||
|
||||
About simdjson
|
||||
--------------
|
||||
@@ -179,7 +181,7 @@ CPU's multiple execution cores.
|
||||
|
||||
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?](http://arxiv.org/abs/2312.17149), Software: Practice and Experience (to appear)
|
||||
- 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
|
||||
and implementation of simdjson is in our research article:
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please use the following contact information for reporting a vulnerability:
|
||||
|
||||
- [Daniel Lemire](https://github.com/lemire) - daniel@lemire.me
|
||||
@@ -993,7 +993,6 @@ struct AppleEvents {
|
||||
// check permission
|
||||
int force_ctrs = 0;
|
||||
if (kpc_force_all_ctrs_get(&force_ctrs)) {
|
||||
printf("Permission denied, xnu/kpc requires root privileges.\n");
|
||||
return (worked = false);
|
||||
}
|
||||
int ret;
|
||||
@@ -1100,8 +1099,8 @@ struct AppleEvents {
|
||||
return 1;
|
||||
}
|
||||
return performance_counters {
|
||||
counters_0[counter_map[0]], counters_0[counter_map[3]],
|
||||
counters_0[counter_map[2]], counters_0[counter_map[1]]};
|
||||
counters_0[counter_map[0]], counters_0[counter_map[2]],
|
||||
counters_0[counter_map[3]], counters_0[counter_map[1]]};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -473,28 +473,6 @@ static void twitter_count(State& state) {
|
||||
}
|
||||
BENCHMARK(twitter_count);
|
||||
|
||||
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
|
||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||
SIMDJSON_DISABLE_DEPRECATED_WARNING
|
||||
static void iterator_twitter_count(State& state) {
|
||||
// Prints the number of results in twitter.json
|
||||
padded_string json = padded_string::load(TWITTER_JSON);
|
||||
ParsedJson pj = build_parsed_json(json);
|
||||
for (simdjson_unused auto _ : state) {
|
||||
ParsedJson::Iterator iter(pj);
|
||||
// uint64_t result_count = doc["search_metadata"]["count"];
|
||||
if (!iter.move_to_key("search_metadata")) { return; }
|
||||
if (!iter.move_to_key("count")) { return; }
|
||||
if (!iter.is_integer()) { return; }
|
||||
int64_t result_count = iter.get_integer();
|
||||
|
||||
if (result_count != 100) { return; }
|
||||
}
|
||||
}
|
||||
BENCHMARK(iterator_twitter_count);
|
||||
SIMDJSON_POP_DISABLE_WARNINGS
|
||||
#endif // SIMDJSON_DISABLE_DEPRECATED_API
|
||||
|
||||
static void twitter_default_profile(State& state) {
|
||||
// Count unique users with a default profile.
|
||||
dom::parser parser;
|
||||
@@ -578,54 +556,6 @@ static void error_code_twitter_default_profile(State& state) noexcept {
|
||||
}
|
||||
BENCHMARK(error_code_twitter_default_profile);
|
||||
|
||||
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
|
||||
|
||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||
SIMDJSON_DISABLE_DEPRECATED_WARNING
|
||||
|
||||
static void iterator_twitter_default_profile(State& state) {
|
||||
// Count unique users with a default profile.
|
||||
padded_string json;
|
||||
auto error = padded_string::load(TWITTER_JSON).get(json);
|
||||
if (error) { std::cerr << error << std::endl; return; }
|
||||
ParsedJson pj = build_parsed_json(json);
|
||||
for (simdjson_unused auto _ : state) {
|
||||
set<string_view> default_users;
|
||||
ParsedJson::Iterator iter(pj);
|
||||
|
||||
// for (dom::object tweet : doc["statuses"]) {
|
||||
if (!(iter.move_to_key("statuses") && iter.is_array())) { return; }
|
||||
if (iter.down()) { // first status
|
||||
do {
|
||||
|
||||
// dom::object user = tweet["user"];
|
||||
if (!(iter.move_to_key("user") && iter.is_object())) { return; }
|
||||
|
||||
// if (user["default_profile"]) {
|
||||
if (iter.move_to_key("default_profile")) {
|
||||
if (iter.is_true()) {
|
||||
if (!iter.up()) { return; } // back to user
|
||||
|
||||
// default_users.insert(user["screen_name"]);
|
||||
if (!(iter.move_to_key("screen_name") && iter.is_string())) { return; }
|
||||
default_users.emplace(iter.get_string(), iter.get_string_length());
|
||||
}
|
||||
if (!iter.up()) { return; } // back to user
|
||||
}
|
||||
|
||||
if (!iter.up()) { return; } // back to status
|
||||
|
||||
} while (iter.next()); // next status
|
||||
}
|
||||
|
||||
if (default_users.size() != 86) { return; }
|
||||
}
|
||||
}
|
||||
|
||||
SIMDJSON_POP_DISABLE_WARNINGS
|
||||
BENCHMARK(iterator_twitter_default_profile);
|
||||
#endif // SIMDJSON_DISABLE_DEPRECATED_API
|
||||
|
||||
static void error_code_twitter_image_sizes(State& state) noexcept {
|
||||
// Count unique image sizes
|
||||
dom::parser parser;
|
||||
@@ -680,92 +610,4 @@ static void parse_surrogate_pairs(State& state) {
|
||||
}
|
||||
BENCHMARK(parse_surrogate_pairs);
|
||||
|
||||
|
||||
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
|
||||
|
||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||
SIMDJSON_DISABLE_DEPRECATED_WARNING
|
||||
static void iterator_twitter_image_sizes(State& state) {
|
||||
// Count unique image sizes
|
||||
padded_string json;
|
||||
auto error = padded_string::load(TWITTER_JSON).get(json);
|
||||
if (error) { std::cerr << error << std::endl; return; }
|
||||
ParsedJson pj = build_parsed_json(json);
|
||||
for (simdjson_unused auto _ : state) {
|
||||
set<tuple<uint64_t, uint64_t>> image_sizes;
|
||||
ParsedJson::Iterator iter(pj);
|
||||
|
||||
// for (dom::object tweet : doc["statuses"]) {
|
||||
if (!(iter.move_to_key("statuses") && iter.is_array())) { return; }
|
||||
if (iter.down()) { // first status
|
||||
do {
|
||||
|
||||
// dom::object media;
|
||||
// not_found = tweet["entities"]["media"].get(media);
|
||||
// if (!not_found) {
|
||||
if (iter.move_to_key("entities")) {
|
||||
if (!iter.is_object()) { return; }
|
||||
if (iter.move_to_key("media")) {
|
||||
if (!iter.is_array()) { return; }
|
||||
|
||||
// for (dom::object image : media) {
|
||||
if (iter.down()) { // first media
|
||||
do {
|
||||
|
||||
// for (auto [key, size] : dom::object(image["sizes"])) {
|
||||
if (!(iter.move_to_key("sizes") && iter.is_object())) { return; }
|
||||
if (iter.down()) { // first size
|
||||
do {
|
||||
iter.move_to_value();
|
||||
|
||||
// image_sizes.insert({ size["w"], size["h"] });
|
||||
if (!(iter.move_to_key("w")) && !iter.is_integer()) { return; }
|
||||
uint64_t width = iter.get_integer();
|
||||
if (!iter.up()) { return; } // back to size
|
||||
if (!(iter.move_to_key("h")) && !iter.is_integer()) { return; }
|
||||
uint64_t height = iter.get_integer();
|
||||
if (!iter.up()) { return; } // back to size
|
||||
image_sizes.emplace(width, height);
|
||||
|
||||
} while (iter.next()); // next size
|
||||
if (!iter.up()) { return; } // back to sizes
|
||||
}
|
||||
if (!iter.up()) { return; } // back to image
|
||||
} while (iter.next()); // next image
|
||||
if (!iter.up()) { return; } // back to media
|
||||
}
|
||||
if (!iter.up()) { return; } // back to entities
|
||||
}
|
||||
if (!iter.up()) { return; } // back to status
|
||||
}
|
||||
} while (iter.next()); // next status
|
||||
}
|
||||
|
||||
if (image_sizes.size() != 15) { return; };
|
||||
}
|
||||
}
|
||||
BENCHMARK(iterator_twitter_image_sizes);
|
||||
|
||||
#endif // SIMDJSON_DISABLE_DEPRECATED_API
|
||||
|
||||
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
|
||||
static void print_json(State& state) noexcept {
|
||||
// Prints the number of results in twitter.json
|
||||
dom::parser parser;
|
||||
|
||||
padded_string json;
|
||||
auto error = padded_string::load(TWITTER_JSON).get(json);
|
||||
if (error) { std::cerr << error << std::endl; return; }
|
||||
|
||||
int code = json_parse(json, parser);
|
||||
if (code) { cerr << error_message(code) << endl; return; }
|
||||
for (simdjson_unused auto _ : state) {
|
||||
std::stringstream s;
|
||||
if (!parser.print_json(s)) { cerr << "print_json failed" << endl; return; }
|
||||
}
|
||||
}
|
||||
BENCHMARK(print_json);
|
||||
#endif // SIMDJSON_DISABLE_DEPRECATED_API
|
||||
SIMDJSON_POP_DISABLE_WARNINGS
|
||||
|
||||
BENCHMARK_MAIN();
|
||||
|
||||
@@ -169,22 +169,6 @@ BENCHMARK(parse_gsoc)->Repetitions(10)->ComputeStatistics("max", [](const std::v
|
||||
})->DisplayAggregatesOnly(true);
|
||||
|
||||
|
||||
|
||||
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
|
||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||
SIMDJSON_DISABLE_DEPRECATED_WARNING
|
||||
static void json_parse(State& state) {
|
||||
ParsedJson pj;
|
||||
if (!pj.allocate_capacity(EMPTY_ARRAY.length())) { return; }
|
||||
for (simdjson_unused auto _ : state) {
|
||||
auto error = json_parse(EMPTY_ARRAY, pj);
|
||||
if (error) { return; }
|
||||
}
|
||||
}
|
||||
SIMDJSON_POP_DISABLE_WARNINGS
|
||||
BENCHMARK(json_parse);
|
||||
#endif // SIMDJSON_DISABLE_DEPRECATED_API
|
||||
|
||||
static void parser_parse_error_code(State& state) {
|
||||
dom::parser parser;
|
||||
if (parser.allocate(EMPTY_ARRAY.length())) { return; }
|
||||
@@ -213,20 +197,6 @@ BENCHMARK(parser_parse_exception);
|
||||
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
|
||||
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
|
||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||
SIMDJSON_DISABLE_DEPRECATED_WARNING
|
||||
static void build_parsed_json(State& state) {
|
||||
for (simdjson_unused auto _ : state) {
|
||||
dom::parser parser = simdjson::build_parsed_json(EMPTY_ARRAY);
|
||||
if (!parser.valid) { return; }
|
||||
}
|
||||
}
|
||||
SIMDJSON_POP_DISABLE_WARNINGS
|
||||
|
||||
BENCHMARK(build_parsed_json);
|
||||
#endif
|
||||
|
||||
static void document_parse_error_code(State& state) {
|
||||
for (simdjson_unused auto _ : state) {
|
||||
dom::parser parser;
|
||||
|
||||
@@ -24,7 +24,7 @@ struct file_runner : public runner_base<I> {
|
||||
|
||||
simdjson_warn_unused bool before_run(benchmark::State &state) {
|
||||
if (!runner_base<I>::after_run(state)) { return false; };
|
||||
// Copy the original json in case we did *in situ* last time
|
||||
// Copy the original JSON in case we did *in situ* last time
|
||||
std::memcpy(json.data(), original_json.data(), original_json.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ struct string_runner : public runner_base<I> {
|
||||
|
||||
simdjson_warn_unused bool before_run(benchmark::State &state) {
|
||||
if (!runner_base<I>::after_run(state)) { return false; };
|
||||
// Copy the original json in case we did *in situ*
|
||||
// Copy the original JSON in case we did *in situ*
|
||||
std::memcpy(json.data(), original_json.data(), original_json.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
Vendored
+9
-5
@@ -22,7 +22,7 @@ endif()
|
||||
# The bulk of our benchmarking and testing data has been moved simdjson/simdjson-data
|
||||
import_dependency(simdjson-data simdjson/simdjson-data a5b13babe65c1bba7186b41b43d4cbdc20a5c470)
|
||||
add_dependency(simdjson-data)
|
||||
|
||||
option(SIMDJSON_USE_BOOST_JSON "Try to include BOOST_JSON, this may break your binaries under some systems." OFF)
|
||||
# This prevents variables declared with set() from unnecessarily escaping and
|
||||
# should not be called more than once
|
||||
function(competition_scope_)
|
||||
@@ -36,8 +36,8 @@ function(competition_scope_)
|
||||
#endif
|
||||
|
||||
int main() {}
|
||||
]] USE_BOOST_JSON)
|
||||
if(USE_BOOST_JSON)
|
||||
]] SIMDJSON_FOUND_STRING_VIEW)
|
||||
if(SIMDJSON_FOUND_STRING_VIEW AND SIMDJSON_USE_BOOST_JSON)
|
||||
import_dependency(boostjson boostorg/json ee8d72d)
|
||||
add_library(boostjson STATIC "${boostjson_SOURCE_DIR}/src/src.cpp")
|
||||
target_compile_definitions(boostjson PUBLIC BOOST_JSON_STANDALONE)
|
||||
@@ -104,7 +104,11 @@ int main() {}
|
||||
target_compile_definitions(rapidjson INTERFACE RAPIDJSON_HAS_STDSTRING)
|
||||
target_include_directories(rapidjson SYSTEM INTERFACE
|
||||
"${rapidjson_SOURCE_DIR}/include")
|
||||
target_compile_definitions(rapidjson INTERFACE SIMDJSON_COMPETITION_RAPIDJSON)
|
||||
if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14)
|
||||
message(STATUS "Disabling rapidjson")
|
||||
else()
|
||||
target_compile_definitions(rapidjson INTERFACE SIMDJSON_COMPETITION_RAPIDJSON)
|
||||
endif()
|
||||
|
||||
import_dependency(sajson chadaustin/sajson 2dcfd35)
|
||||
add_library(sajson INTERFACE)
|
||||
@@ -130,7 +134,7 @@ int main() {}
|
||||
add_library(competition-core INTERFACE)
|
||||
target_link_libraries(competition-core INTERFACE nlohmann_json rapidjson sajson cjson jsmn yyjson)
|
||||
|
||||
if(USE_BOOST_JSON)
|
||||
if(TARGET boostjson)
|
||||
target_compile_definitions(boostjson INTERFACE HAS_BOOST_JSON)
|
||||
target_link_libraries(competition-core INTERFACE boostjson)
|
||||
endif()
|
||||
|
||||
+570
-81
@@ -9,34 +9,37 @@ An overview of what you need to know to use simdjson, with examples.
|
||||
- [Using simdjson with package managers](#using-simdjson-with-package-managers)
|
||||
- [Using simdjson as a CMake dependency](#using-simdjson-as-a-cmake-dependency)
|
||||
- [Versions](#versions)
|
||||
- [The Basics: Loading and Parsing JSON Documents](#the-basics-loading-and-parsing-json-documents)
|
||||
- [Documents are Iterators](#documents-are-iterators)
|
||||
- [Parser, Document and JSON Scope](#parser-document-and-json-scope)
|
||||
- [The basics: loading and parsing JSON documents](#the-basics-loading-and-parsing-json-documents)
|
||||
- [Documents are iterators](#documents-are-iterators)
|
||||
- [Parser, document and JSON scope](#parser-document-and-json-scope)
|
||||
- [string_view](#string_view)
|
||||
- [Using the Parsed JSON](#using-the-parsed-json)
|
||||
- [Using the Parsed JSON: Additional examples](#using-the-parsed-json-additional-examples)
|
||||
- [Avoiding pitfalls: enable development checks](#avoiding-pitfalls-enable-development-checks)
|
||||
- [Using the parsed JSON](#using-the-parsed-json)
|
||||
- [Using the parsed JSON: additional examples](#using-the-parsed-json-additional-examples)
|
||||
- [Adding support for custom types](#adding-support-for-custom-types)
|
||||
- [Minifying JSON strings without parsing](#minifying-json-strings-without-parsing)
|
||||
- [UTF-8 validation (alone)](#utf-8-validation-alone)
|
||||
- [JSON Pointer](#json-pointer)
|
||||
- [Error Handling](#error-handling)
|
||||
- [Error Handling Examples without Exceptions](#error-handling-examples-without-exceptions)
|
||||
- [Disabling Exceptions](#disabling-exceptions)
|
||||
- [JSONPath](#jsonpath)
|
||||
- [Error handling](#error-handling)
|
||||
- [Error handling examples without exceptions](#error-handling-examples-without-exceptions)
|
||||
- [Disabling exceptions](#disabling-exceptions)
|
||||
- [Exceptions](#exceptions)
|
||||
- [Current location in document](#current-location-in-document)
|
||||
- [Checking for trailing content](#checking-for-trailing-content)
|
||||
- [Rewinding](#rewinding)
|
||||
- [Newline-Delimited JSON (ndjson) and JSON lines](#newline-delimited-json-ndjson-and-json-lines)
|
||||
- [Parsing Numbers Inside Strings](#parsing-numbers-inside-strings)
|
||||
- [Parsing numbers inside strings](#parsing-numbers-inside-strings)
|
||||
- [Dynamic Number Types](#dynamic-number-types)
|
||||
- [Raw Strings](#raw-strings)
|
||||
- [General Direct Access to the Raw JSON String](#general-direct-access-to-the-raw-json-string)
|
||||
- [Storing Directly into an Existing String Instance](#storing-directly-into-an-existing-string-instance)
|
||||
- [Thread Safety](#thread-safety)
|
||||
- [Standard Compliance](#standard-compliance)
|
||||
- [Backwards Compatibility](#backwards-compatibility)
|
||||
- [Raw strings from keys](#raw-strings-from-keys)
|
||||
- [General direct access to the raw JSON string](#general-direct-access-to-the-raw-json-string)
|
||||
- [Storing directly into an existing string instance](#storing-directly-into-an-existing-string-instance)
|
||||
- [Thread safety](#thread-safety)
|
||||
- [Standard compliance](#standard-compliance)
|
||||
- [Backwards compatibility](#backwards-compatibility)
|
||||
- [Examples](#examples)
|
||||
- [Performance Tips](#performance-tips)
|
||||
- [Further Reading](#further-reading)
|
||||
- [Performance tips](#performance-tips)
|
||||
- [Further reading](#further-reading)
|
||||
|
||||
|
||||
Requirements
|
||||
@@ -48,7 +51,7 @@ Requirements
|
||||
|
||||
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.
|
||||
|
||||
We test the library on a big-endian system (IBM s390x with Linux) .
|
||||
We test the library on a big-endian system (IBM s390x with Linux).
|
||||
|
||||
Including simdjson
|
||||
------------------
|
||||
@@ -69,13 +72,15 @@ c++ myproject.cpp simdjson.cpp
|
||||
|
||||
Note:
|
||||
- Users on macOS and other platforms where default compilers do not provide C++11 compliant by default should request it with the appropriate flag (e.g., `c++ -std=c++11 myproject.cpp simdjson.cpp`).
|
||||
- The library relies on [runtime CPU detection](implementation-selection.md): avoid specifying an architecture at compile time (e.g., `-march-native`).
|
||||
- The library relies on [runtime CPU detection](implementation-selection.md): avoid specifying an architecture at compile time (e.g., `-march-native`) if you want your binaries to run everywhere.
|
||||
|
||||
Using simdjson with package managers
|
||||
------------------
|
||||
|
||||
You can install the simdjson library on your system or in your project using multiple package managers such as MSYS2, the conan package manager, vcpkg, brew, the apt package manager (debian-based Linux systems), the FreeBSD package manager (FreeBSD), and so on. [Visit our wiki for more details](https://github.com/simdjson/simdjson/wiki/Installing-simdjson-with-a-package-manager).
|
||||
|
||||
The following Linux distributions provide simdjson packages: Alpine, RedHat, Rocky Linux, Debian, Fedora, and Ubuntu.
|
||||
|
||||
Using simdjson as a CMake dependency
|
||||
------------------
|
||||
|
||||
@@ -139,7 +144,7 @@ https://github.com/simdjson/simdjson/blob/vx.y.z/doc/basics.md
|
||||
where `x.y.z` should correspond to the version number you have
|
||||
chosen.
|
||||
|
||||
The Basics: Loading and Parsing JSON Documents
|
||||
The basics: loading and parsing JSON documents
|
||||
----------------------------------------------
|
||||
|
||||
The simdjson library allows you to navigate and validate JSON documents ([RFC 8259](https://www.tbray.org/ongoing/When/201x/2017/12/14/rfc8259.html)).
|
||||
@@ -202,7 +207,7 @@ simdjson::padded_string my_padded_data(data); // copies to a padded buffer
|
||||
We recommend against creating many `std::string` or many `std::padding_string` instances in your application to store your JSON data.
|
||||
Consider reusing the same buffers and limiting memory allocations.
|
||||
|
||||
By default, the simdjson library throws exceptions (`simdjson_error`) on errors. We omit `try`-`catch` clauses from our illustrating examples: if you omit `try`-`catch` in your code, an uncaught exception will halt your program. It is also possible to use simdjson without generating exceptions, and you may even build the library without exception support at all. See [Error Handling](#error-handling) for details.
|
||||
By default, the simdjson library throws exceptions (`simdjson_error`) on errors. We omit `try`-`catch` clauses from our illustrating examples: if you omit `try`-`catch` in your code, an uncaught exception will halt your program. It is also possible to use simdjson without generating exceptions, and you may even build the library without exception support at all. See [Error handling](#error-handling) for details.
|
||||
|
||||
|
||||
Some users may want to browse code along with the compiled assembly. You want to check out the following lists of examples:
|
||||
@@ -210,8 +215,14 @@ Some users may want to browse code along with the compiled assembly. You want to
|
||||
* [simdjson examples with errors handled through exceptions](https://godbolt.org/z/98Kx9Kqjn)
|
||||
* [simdjson examples with errors without exceptions](https://godbolt.org/z/PKG7GdbPo)
|
||||
|
||||
*Windows-specific*: Windows users who need to read files with
|
||||
non-ANSI characters in the name should set their code page to
|
||||
UTF-8 (65001). This should be the default with Windows 11 and better.
|
||||
Further, they may use the AreFileApisANSI function to determine whether
|
||||
the filename is interpreted using the ANSI or the system default OEM
|
||||
codepage, and they may call SetFileApisToOEM accordingly.
|
||||
|
||||
Documents are Iterators
|
||||
Documents are iterators
|
||||
-----------------------
|
||||
|
||||
The simdjson library relies on an approach to parsing JSON that we call "On Demand".
|
||||
@@ -221,13 +232,13 @@ walking through the original JSON text, merrily reading commas and colons and br
|
||||
you get where you are going. This is the key to On Demand's performance: since it's just an iterator,
|
||||
it lets you parse values as you use them. And particularly, it lets you *skip* values you do not want
|
||||
to use. On Demand is also ideally suited when you want to capture part of the document without parsing it
|
||||
immediately (e.g., see [Raw Strings](#raw-strings)).
|
||||
immediately (e.g., see [General direct access to the raw JSON string](#general-direct-access-to-the-raw-json-string)).
|
||||
|
||||
We refer to "On Demand" as a front-end component since it is an interface between the
|
||||
low-level parsing functions and the user. It hides much of the complexity of parsing JSON
|
||||
documents.
|
||||
|
||||
### Parser, Document and JSON Scope
|
||||
### Parser, document and JSON scope
|
||||
|
||||
For code safety, you should keep (1) the `parser` instance, (2) the input string and (3) the document instance alive throughout your parsing. Additionally, you should follow the following rules:
|
||||
|
||||
@@ -256,7 +267,7 @@ copy the data into their own favorite class instances (e.g., alternatives to `st
|
||||
|
||||
A `std::string_view` instance is effectively just a pointer to a region in memory representing
|
||||
a string. In simdjson, we return `std::string_view` instances that either point within the
|
||||
input string you parsed (when using [raw Strings](#raw-strings)), or to a temporary string buffer inside
|
||||
input string you parsed (see [General direct access to the raw JSON string](#general-direct-access-to-the-raw-json-string)), or to a temporary string buffer inside
|
||||
our parser class instances that is valid until the parser object is destroyed or you use it to parse another document.
|
||||
When using `std::string_view` instances, it is your responsibility to ensure that
|
||||
`std::string_view` instance does not outlive the pointed-to memory (e.g., either the input
|
||||
@@ -281,23 +292,31 @@ Some users prefer to use non-JSON native encoding formats such as UTF-16 or UTF-
|
||||
transcode the UTF-8 strings produced by the simdjson library to other formats. See the
|
||||
[simdutf library](https://github.com/simdutf/simdutf), for example.
|
||||
|
||||
Using the Parsed JSON
|
||||
---------------------
|
||||
Avoiding pitfalls: enable development checks
|
||||
--------------------
|
||||
|
||||
We recommend that you first compile and run your code in Debug mode: under Visual Studio,
|
||||
it means having the `_DEBUG` macro defined, and, for other compilers, it means leaving
|
||||
the `__OPTIMIZE__` macro undefined. The simdjson code will set `SIMDJSON_DEVELOPMENT_CHECKS=1`.
|
||||
We recommend that you first compile and run your code in Debug mode:
|
||||
|
||||
- under Visual Studio, it means having the `_DEBUG` macro defined,
|
||||
- for other compilers, it means leaving the `__OPTIMIZE__` macro undefined.
|
||||
|
||||
The simdjson code will set `SIMDJSON_DEVELOPMENT_CHECKS=1` in debug mode.
|
||||
Alternatively, you can set the macro `SIMDJSON_DEVELOPMENT_CHECKS` to 1 prior to including
|
||||
the `simdjson.h` header to enable these additional checks: just make sure you remove the
|
||||
definition once your code has been tested. When `SIMDJSON_DEVELOPMENT_CHECKS` is set to 1, the
|
||||
simdjson library runs additional (expensive) tests on your code to help ensure that you are
|
||||
using the library in a safe manner. Once your code has been tested, you can then run it in
|
||||
using the library in a safe manner.
|
||||
|
||||
Once your code has been tested, you can then run it in
|
||||
Release mode: under Visual Studio, it means having the `_DEBUG` macro undefined, and, for other
|
||||
compilers, it means setting `__OPTIMIZE__` to a positive integer. You can also forcefully
|
||||
disable these checks by setting `SIMDJSON_DEVELOPMENT_CHECKS` to 0. Once your code is tested, we
|
||||
further encourage you to define `NDEBUG` in your Release builds to disable additional runtime
|
||||
testing and get the best performance.
|
||||
|
||||
Using the parsed JSON
|
||||
---------------------
|
||||
|
||||
Once you have a document (`simdjson::ondemand::document`), you can navigate it with
|
||||
idiomatic C++ iterators, operators and casts. Besides the document instances and
|
||||
native types (`double`, `uint64_t`, `int64_t`, `bool`), we also access
|
||||
@@ -327,7 +346,7 @@ We invite you to keep the following rules in mind:
|
||||
The simdjson library makes generous use of `std::string_view` instances. If you are unfamiliar
|
||||
with `std::string_view` in C++, make sure to [read the section on std::string_view](#string_view).
|
||||
They behave much like an immutable `std::string` but they require no memory allocation. You can
|
||||
create a `std::string` instance from an `std::string_view` when you need it.
|
||||
create a `std::string` instance from a `std::string_view` when you need it.
|
||||
|
||||
The following specific instructions indicate how to use the JSON when exceptions are enabled, but simdjson has full, idiomatic
|
||||
support for users who avoid exceptions. See [the simdjson error handling documentation](basics.md#error-handling) for more.
|
||||
@@ -341,7 +360,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
ondemand::object and ondemand::array. We also have explicit methods such as `get_string()`, `get_double()`,
|
||||
`get_uint64()`, `get_int64()`, `get_bool()`, `get_object()` and `get_array()`. After a cast or an explicit method,
|
||||
the number, string or boolean will be parsed, or the initial `{` or `[` will be verified for `ondemand::object` and `ondemand::array`. An exception may be thrown if
|
||||
the cast is not possible: there error code is `simdjson::INCORRECT_TYPE` (see [Error Handling](#error-handling)). Importantly, when getting an ondemand::object or ondemand::array instance, its content is
|
||||
the cast is not possible: there error code is `simdjson::INCORRECT_TYPE` (see [Error handling](#error-handling)). Importantly, when getting an ondemand::object or ondemand::array instance, its content is
|
||||
not validated: you are only guaranteed that the corresponding initial character (`{` or `[`) is present. Thus,
|
||||
for example, you could have an ondemand::object instance pointing at the invalid JSON `{ "this is not a valid object" }`: the validation occurs as you access the content.
|
||||
The `get_string()` returns a valid UTF-8 string, after
|
||||
@@ -357,20 +376,43 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
> IMPORTANT NOTE: values can only be parsed once. Since documents are *iterators*, once you have
|
||||
> parsed a value (such as by casting to double), you cannot get at it again. It is an error to call
|
||||
> `get_string()` twice on an object (or to cast an object twice to `std::string_view`).
|
||||
* **Array Iteration:** To iterate through an array, use `for (auto value : array) { ... }`. This will
|
||||
step through each value in the JSON array.
|
||||
|
||||
To iterate through an array, you should be at the beginning
|
||||
of the array: to warn you, an OUT_OF_ORDER_ITERATION error is generated [when development checks](#avoiding-pitfalls-enable-development-checks) are active. If you need to access an array more
|
||||
than once, you may call `reset()` on it although we discourage this practice. Keep in mind that
|
||||
you should consume each value at most once.
|
||||
|
||||
If you know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`.
|
||||
|
||||
You may also use explicit iterators: `for(auto i = array.begin(); i != array.end(); i++) {}`. You can check that an array is empty with the condition `auto i = array.begin(); if (i == array.end()) {...}`.
|
||||
* **Object Iteration:** You can iterate through an object's fields, as well: `for (auto field : object) { ... }`. You may also use explicit iterators : `for(auto i = object.begin(); i != object.end(); i++) { auto field = *i; .... }`. You can check that an object is empty with the condition `auto i = object.begin(); if (i == object.end()) {...}`.
|
||||
- `field.unescaped_key()` will get you the unescaped key string as a `std::string_view` instance. E.g., the JSON string `"\u00e1"` becomes the Unicode string `á`. Optionally, you pass `true` as a parameter to the `unescaped_key` method if you want invalid escape sequences to be replaced by a default replacement character (e.g., `\ud800\ud801\ud811`): otherwise bad escape sequences lead to an immediate error.
|
||||
- `field.escaped_key()` will get you the key string as as a `std::string_view` instance, but unlike `unescaped_key()`, the key is not processed, so no unescaping is done. E.g., the JSON string `"\u00e1"` becomes the Unicode string `\u00e1`. We expect that `escaped_key()` is faster than `field.unescaped_key()`.
|
||||
- `field.value()` will get you the value, which you can then use all these other methods on.
|
||||
|
||||
|
||||
To iterate through an object, you should be at the beginning
|
||||
of the object: to warn you, an OUT_OF_ORDER_ITERATION error is generated [when development checks](#avoiding-pitfalls-enable-development-checks) are active. If you need to access an object more
|
||||
than once, you may call `reset()` on it although we discourage this practice. Keep in mind that
|
||||
you should consume each value at most once.
|
||||
* **Array Index:** Because it is forward-only, you cannot look up an array element by index by index. Instead,
|
||||
you should iterate through the array and keep an index yourself. Exceptionally, if need a single value
|
||||
out of the array, you may use an array access (e.g., `array[1]`).
|
||||
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`. This will
|
||||
scan through the object looking for the field with the matching string, doing a character-by-character
|
||||
comparison. It may generate the error `simdjson::NO_SUCH_FIELD` if there is no such key in the object, it may throw an exception (see [Error Handling](#error-handling)). For efficiency reason, you should avoid looking up the same field repeatedly: e.g., do
|
||||
not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. For best performance, you should try to query the keys in the same order they appear in the document. If you need several keys and you cannot predict the order they will appear in, it is recommended to iterate through all keys `for(auto field : object) {...}`. Keep in mind that On Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if (!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsibility as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an
|
||||
comparison. It may generate the error `simdjson::NO_SUCH_FIELD` if there is no such key in the object, it may throw an exception (see [Error handling](#error-handling)). For efficiency reason, you should avoid looking up the same field repeatedly: e.g., do
|
||||
not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. For best performance, you should try to query the keys in the same order they appear in the document. If you need several keys and you cannot predict the order they will appear in, it is recommended to iterate through all keys `for(auto field : object) {...}`. Generally, you should not mix and match iterating through an object (`for(auto field : object) {...}`) and key accesses (`object["foo"]`): if you need to iterate through an object after a key access, you need to call `reset()` on the object. Keep in mind that On Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if (!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsibility as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an
|
||||
object twice: `std::string_view(object["foo"]` followed by `std::string_view(object["foo"]` then your code
|
||||
is in error. Furthermore, you can only consume one field at a time, on the same object. The
|
||||
value instance you get from `content["bids"]` becomes invalid when you call `content["asks"]`.
|
||||
If you have retrieved `content["bids"].get_array()` and you later call
|
||||
`content["asks"].get_array()`, then the first array should no longer be accessed: it would be
|
||||
unsafe to do so. You can detect such mistakes by first compiling and running the code in
|
||||
Debug mode: an OUT_OF_ORDER_ITERATION error is generated.
|
||||
unsafe to do so. You can detect such mistakes by first compiling and running the code [with development checks](#avoiding-pitfalls-enable-development-checks): an OUT_OF_ORDER_ITERATION error is generated.
|
||||
|
||||
> NOTE: JSON allows you to escape characters in keys. E.g., the key `"date"` may be written as
|
||||
> `"\u0064\u0061\u0074\u0065"`. By default, simdjson does *not* unescape keys when matching by default.
|
||||
> `"\u0064\u0061\u0074\u0065"`. By default, simdjson does *not* unescape keys when matching.
|
||||
> Thus if you search for the key `"date"` and the JSON document uses `"\u0064\u0061\u0074\u0065"`
|
||||
> as a key, it will not be recognized. This is not generally a problem. Nevertheless, if you do need
|
||||
> to support escaped keys, the method `unescaped_key()` provides the desired unescaped keys by
|
||||
@@ -379,7 +421,9 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
> Unicode sequences with replacement, meaning that the decoding always succeeds but bogus Unicode
|
||||
> replacement characters are inserted. In general, you should expect a performance penalty
|
||||
> when using `unescaped_key()` compared to `key()` because of the string processing: the `key()`
|
||||
> function just points inside the source JSON document.
|
||||
> function just points inside the source JSON document. As a compromise, you may use `escaped_key()``
|
||||
> which returns a `std::string_view` instance pointing directly in the document, like `key()`, although,
|
||||
> unlike `key()`, it has to determine the location of the final quote character.
|
||||
>
|
||||
> ```c++
|
||||
> auto json = R"({"k\u0065y": 1})"_padded;
|
||||
@@ -389,6 +433,9 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
> for(auto field : object) {
|
||||
> // parses and writes out the key, after unescaping it,
|
||||
> // to a string buffer. It causes a performance penalty.
|
||||
> // If you do not expect that unescaping is useful, you
|
||||
> // may replace field.unescaped_key() with
|
||||
> // field.escaped_key().
|
||||
> std::string_view keyv = field.unescaped_key();
|
||||
> if (keyv == "key") { std::cout << uint64_t(field.value()); }
|
||||
> }
|
||||
@@ -419,17 +466,6 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
> double y = doc["y"]; // The cursor is now after the 2 (at })
|
||||
> double x = doc["x"]; // Success: [] loops back around to find "x"
|
||||
> ```
|
||||
* **Array Iteration:** To iterate through an array, use `for (auto value : array) { ... }`. This will
|
||||
step through each value in the JSON array.
|
||||
|
||||
If you know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`.
|
||||
|
||||
You may also use explicit iterators: `for(auto i = array.begin(); i != array.end(); i++) {}`. You can check that an array is empty with the condition `auto i = array.begin(); if(i == array.end()) {...}`.
|
||||
* **Object Iteration:** You can iterate through an object's fields, as well: `for (auto field : object) { ... }`. You may also use explicit iterators : `for(auto i = object.begin(); i != object.end(); i++) { auto field = *i; .... }`. You can check that an object is empty with the condition `auto i = object.begin(); if(i == object.end()) {...}`.
|
||||
- `field.unescaped_key()` will get you the unescaped key string. E.g., the JSON string `"\u00e1"` becomes the Unicode string `á`. Optionally, you pass `true` as a parameter to the `unescaped_key` method if you want invalid escape sequences to be replaced by a default replacement character (e.g., `\ud800\ud801\ud811`): otherwise bad escape sequences lead to an immediate error.
|
||||
- `field.value()` will get you the value, which you can then use all these other methods on.
|
||||
* **Array Index:** Because it is forward-only, you cannot look up an array element by index by index. Instead,
|
||||
you should iterate through the array and keep an index yourself.
|
||||
* **Output to strings:** Given a document, a value, an array or an object in a JSON document, you can output a JSON string version suitable to be parsed again as JSON content: `simdjson::to_json_string(element)`. A call to `to_json_string` consumes fully the element: if you apply it on a document, the JSON pointer is advanced to the end of the document. The `simdjson::to_json_string` does not allocate memory. The `to_json_string` function should not be confused with retrieving the value of a string instance which are escaped and represented using a lightweight `std::string_view` instance pointing at an internal string buffer inside the parser instance. To illustrate, the first of the following two code segments will print the unescaped string `"test"` complete with the quote whereas the second one will print the escaped content of the string (without the quotes).
|
||||
> ```C++
|
||||
> // serialize a JSON to an escaped std::string instance so that it can be parsed again as JSON
|
||||
@@ -546,7 +582,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
* **Tree Walking and JSON Element Types:** Sometimes you don't necessarily have a document
|
||||
with a known type, and are trying to generically inspect or walk over JSON elements.
|
||||
You can also represent arbitrary JSON values with
|
||||
`ondemand::value` instances: it can represent anything except a scalar document (lone number, string, null or Boolean). You can check for scalar documents with the method `scalar()`. You can cast a document that is either an array or an object to an `ondemand::value` instance immediately after you create the document instance: you cannot create a `ondemand::value` instance from a document that has already been accessed as it would mean that you would have two instances of the object or array simultaneously (see [rewinding](#rewinding)). You can query the type of a document or a value with the `type()` method. The `type()` method does not consume or validate documents and values, but it tells you whether they are
|
||||
`ondemand::value` instances: it can represent anything except a scalar document (lone number, string, null or Boolean). You can check for scalar documents with the method `scalar()`. You can cast a document that is either an array or an object to an `ondemand::value` instance immediately after you create the document instance: you cannot create an `ondemand::value` instance from a document that has already been accessed as it would mean that you would have two instances of the object or array simultaneously (see [rewinding](#rewinding)). You can query the type of a document or a value with the `type()` method. The `type()` method does not consume or validate documents and values, but it tells you whether they are
|
||||
- arrays (`json_type::array`),
|
||||
- objects (`json_type::object`)
|
||||
- numbers (`json_type::number`),
|
||||
@@ -555,7 +591,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
- null (`json_type::null`).
|
||||
|
||||
You must still validate and consume the values (e.g., call `is_null()`) after calling `type()`.
|
||||
You may also access [raw strings](#raw-strings).
|
||||
You may also access [the raw JSON string](#general-direct-access-to-the-raw-json-string).
|
||||
For example, the following is a quick and dirty recursive function that verbosely prints the JSON document as JSON. This example also illustrates lifecycle requirements: the `document` instance holds the iterator. The document must remain in scope while you are accessing instances of `value`, `object` and `array`.
|
||||
```c++
|
||||
void recursive_print_json(ondemand::value element) {
|
||||
@@ -585,6 +621,9 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
// key() returns the key as it appears in the raw
|
||||
// JSON document, if we want the unescaped key,
|
||||
// we should do field.unescaped_key().
|
||||
// We could also use field.escaped_key() if we want
|
||||
// a std::string_view instance, but we do not need
|
||||
// escaping.
|
||||
cout << "\"" << field.key() << "\": ";
|
||||
recursive_print_json(field.value());
|
||||
add_comma = true;
|
||||
@@ -626,7 +665,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
}
|
||||
```
|
||||
|
||||
### Using the Parsed JSON: Additional examples
|
||||
### Using the parsed JSON: additional examples
|
||||
|
||||
|
||||
Let us review these concepts with some additional examples. For simplicity, we omit the include clauses (`#include "simdjson.h"`) as well as namespace-using clauses (`using namespace simdjson;`).
|
||||
@@ -689,6 +728,8 @@ ondemand::document doc = parser.iterate(cars_json);
|
||||
for (ondemand::field key_car : doc.get_object()) {
|
||||
// If I need a string_view and/or, I can use key_car.unescaped_key() instead, but
|
||||
// key_car.key() will be more performant otherwise.
|
||||
// If we want a std::string_view instance but we do not care about escaping, we
|
||||
// can also use key_car.escaped_key().
|
||||
cout << "identifier : " << key_car.key() << std::endl;
|
||||
// I can now access the subobject:
|
||||
ondemand::object car = key_car.value();
|
||||
@@ -739,6 +780,289 @@ for (ondemand::object points : parser.iterate(points_json)) {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Adding support for custom types
|
||||
----------------------
|
||||
|
||||
Suppose you have your own types, such as a `Car` struct:
|
||||
|
||||
```C++
|
||||
struct Car {
|
||||
std::string make;
|
||||
std::string model;
|
||||
int64_t year;
|
||||
std::vector<double> tire_pressure;
|
||||
};
|
||||
```
|
||||
|
||||
You might want to write code that automatically parses the JSON content to your custom
|
||||
type:
|
||||
|
||||
```C++
|
||||
padded_string json = R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||
"tire_pressure": [ 40.1, 39.9 ] },
|
||||
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||
"tire_pressure": [ 30.1, 31.0 ] },
|
||||
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||
"tire_pressure": [ 29.8, 30.0 ] }
|
||||
])"_padded;
|
||||
|
||||
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc = parser.iterate(json);
|
||||
for (auto val : doc) {
|
||||
Car c(val);
|
||||
std::cout << c.make << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
We may do so by providing additional template definitions to the `ondemand::value` type.
|
||||
We may start by providing a definition for `std::vector<double>` as follows:
|
||||
|
||||
```c++
|
||||
template <>
|
||||
simdjson_inline simdjson_result<std::vector<double>>
|
||||
simdjson::ondemand::value::get() noexcept {
|
||||
ondemand::array array;
|
||||
auto error = get_array().get(array);
|
||||
if (error) { return error; }
|
||||
std::vector<double> vec;
|
||||
for (auto v : array) {
|
||||
double val;
|
||||
error = v.get_double().get(val);
|
||||
if (error) { return error; }
|
||||
vec.push_back(val);
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
```
|
||||
|
||||
We may then provide support for our `Car` struct:
|
||||
|
||||
```C++
|
||||
template <>
|
||||
simdjson_inline simdjson_result<Car> simdjson::ondemand::value::get() noexcept {
|
||||
ondemand::object obj;
|
||||
auto error = get_object().get(obj);
|
||||
if (error) { return error; }
|
||||
Car car;
|
||||
// Instead of repeatedly obj["something"], we iterate through the object which
|
||||
// we expect to be faster.
|
||||
for (auto field : obj) {
|
||||
raw_json_string key;
|
||||
error = field.key().get(key);
|
||||
if (error) { return error; }
|
||||
if (key == "make") {
|
||||
error = field.value().get_string(car.make);
|
||||
if (error) { return error; }
|
||||
} else if (key == "model") {
|
||||
error = field.value().get_string(car.model);
|
||||
if (error) { return error; }
|
||||
} else if (key == "year") {
|
||||
error = field.value().get_int64().get(car.year);
|
||||
if (error) { return error; }
|
||||
} else if (key == "tire_pressure") {
|
||||
error = field.value().get<std::vector<double>>().get(car.tire_pressure);
|
||||
if (auto error) { return error; }
|
||||
}
|
||||
}
|
||||
return car;
|
||||
}
|
||||
```
|
||||
|
||||
And that is all that is needed! The following code is a complete example:
|
||||
|
||||
```c++
|
||||
#include "simdjson.h"
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
/**
|
||||
* A custom type that we want to parse.
|
||||
*/
|
||||
struct Car {
|
||||
std::string make;
|
||||
std::string model;
|
||||
int64_t year;
|
||||
std::vector<double> tire_pressure;
|
||||
};
|
||||
|
||||
template <>
|
||||
simdjson_inline simdjson_result<std::vector<double>>
|
||||
simdjson::ondemand::value::get() noexcept {
|
||||
ondemand::array array;
|
||||
auto error = get_array().get(array);
|
||||
if (error) { return error; }
|
||||
std::vector<double> vec;
|
||||
for (auto v : array) {
|
||||
double val;
|
||||
error = v.get_double().get(val);
|
||||
if (error) { return error; }
|
||||
vec.push_back(val);
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
simdjson_inline simdjson_result<Car> simdjson::ondemand::value::get() noexcept {
|
||||
ondemand::object obj;
|
||||
auto error = get_object().get(obj);
|
||||
if (error) { return error; }
|
||||
Car car;
|
||||
// Instead of repeatedly obj["something"], we iterate through the object which
|
||||
// we expect to be faster.
|
||||
for (auto field : obj) {
|
||||
raw_json_string key;
|
||||
error = field.key().get(key);
|
||||
if (error) { return error; }
|
||||
if (key == "make") {
|
||||
error = field.value().get_string(car.make);
|
||||
if (error) { return error; }
|
||||
} else if (key == "model") {
|
||||
error = field.value().get_string(car.model);
|
||||
if (error) { return error; }
|
||||
} else if (key == "year") {
|
||||
error = field.value().get_int64().get(car.year);
|
||||
if (error) { return error; }
|
||||
} else if (key == "tire_pressure") {
|
||||
error = field.value().get<std::vector<double>>().get(car.tire_pressure);
|
||||
if (error) { return error; }
|
||||
}
|
||||
}
|
||||
return car;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
padded_string json = R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||
"tire_pressure": [ 40.1, 39.9 ] },
|
||||
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||
"tire_pressure": [ 30.1, 31.0 ] },
|
||||
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||
"tire_pressure": [ 29.8, 30.0 ] }
|
||||
])"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc = parser.iterate(json);
|
||||
for (auto val : doc) {
|
||||
Car c(val);
|
||||
std::cout << c.make << std::endl;
|
||||
}
|
||||
direct();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
Observe that we require an explicit cast (`Car c(val)` instead of `for (Car c : doc) {`): it is by design.
|
||||
|
||||
If you prefer to avoid exceptions, you may modify the `main` function as follows:
|
||||
|
||||
```c++
|
||||
int main(void) {
|
||||
padded_string json = R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||
"tire_pressure": [ 40.1, 39.9 ] },
|
||||
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||
"tire_pressure": [ 30.1, 31.0 ] },
|
||||
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||
"tire_pressure": [ 29.8, 30.0 ] }
|
||||
])"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc;
|
||||
auto error = parser.iterate(json).get(doc);
|
||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||
for (auto val : doc) {
|
||||
Car c;
|
||||
error = val.get<Car>().get(c);
|
||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||
std::cout << c.make << std::endl;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
Our example is limited to `ondemand::value` instances. If you wish to also be able to map
|
||||
directly the document instance itself to a custom type, you need to provide the definitions to
|
||||
the `ondemand::document` type. In this instance, we must replace the function with signature
|
||||
`simdjson_result<Car> simdjson::ondemand::value::get()` with a function having signature
|
||||
`simdjson_result<Car> simdjson::ondemand::document::get() &`. The following is a complete
|
||||
example:
|
||||
|
||||
```C++
|
||||
#include "simdjson.h"
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
/**
|
||||
* A custom type that we want to parse.
|
||||
*/
|
||||
struct Car {
|
||||
std::string make;
|
||||
std::string model;
|
||||
int64_t year;
|
||||
std::vector<double> tire_pressure;
|
||||
};
|
||||
|
||||
template <>
|
||||
simdjson_inline simdjson_result<std::vector<double>>
|
||||
simdjson::ondemand::value::get() noexcept {
|
||||
ondemand::array array;
|
||||
if (auto error = get_array().get(array); error) { return error; }
|
||||
std::vector<double> vec;
|
||||
for (auto v : array) {
|
||||
double val;
|
||||
if (auto error = v.get_double().get(val); error) { return error; }
|
||||
vec.push_back(val);
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
simdjson_inline simdjson_result<Car> simdjson::ondemand::document::get() & noexcept {
|
||||
ondemand::object obj;
|
||||
auto error = get_object().get(obj);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
Car car;
|
||||
// Instead of repeatedly obj["something"], we iterate through the object which
|
||||
// we expect to be faster.
|
||||
for (auto field : obj) {
|
||||
raw_json_string key;
|
||||
error = field.key().get(key);
|
||||
if (error) { return error; }
|
||||
if (key == "make") {
|
||||
error = field.value().get_string(car.make);
|
||||
if (error) { return error; }
|
||||
} else if (key == "model") {
|
||||
error = field.value().get_string(car.model);
|
||||
if (error) { return error; }
|
||||
} else if (key == "year") {
|
||||
error = field.value().get_int64().get(car.year);
|
||||
if (error) { return error; }
|
||||
} else if (key == "tire_pressure") {
|
||||
error = field.value().get<std::vector<double>>().get(car.tire_pressure);
|
||||
if (error) { return error; }
|
||||
}
|
||||
}
|
||||
return car;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
padded_string json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||
"tire_pressure": [ 40.1, 39.9 ] } )"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc = parser.iterate(json);
|
||||
Car c(doc);
|
||||
std::cout << c.make << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Minifying JSON strings without parsing
|
||||
----------------------
|
||||
|
||||
@@ -796,10 +1120,10 @@ auto cars = parser.iterate(cars_json);
|
||||
cout << cars.at_pointer("/0/tire_pressure/1") << endl; // Prints 39.9
|
||||
```
|
||||
|
||||
A JSON Path is a sequence of segments each starting with the '/' character. Within arrays, an integer
|
||||
A JSON Pointer path is a sequence of segments each starting with the '/' character. Within arrays, a zero-based integer
|
||||
index allows you to select the indexed node. Within objects, the string value of the key allows you to
|
||||
select the value. If your keys contain the characters '/' or '~', they must be escaped as '~1' and
|
||||
'~0' respectively. An empty JSON Path refers to the whole document.
|
||||
'~0' respectively. An empty JSON Pointer Path refers to the whole document.
|
||||
|
||||
For multiple JSON pointer queries on a document, one can call `at_pointer` multiple times.
|
||||
|
||||
@@ -820,6 +1144,17 @@ for (size_t i = 0; i < size; i++) {
|
||||
}
|
||||
```
|
||||
|
||||
In most instances, a JSON Pointer is an ASCII string and the keys in a JSON document
|
||||
are ASCII strings. We support UTF-8 in JSON Pointer, but key values are matched exactly, without unescaping or Unicode normalization. We do a byte-by-byte comparison. The e acute character is
|
||||
considered distinct from its escaped version `\u00E9`. E.g.,
|
||||
|
||||
```c++
|
||||
const padded_string json = "{\"\\u00E9\":123}"_padded;
|
||||
auto doc = parser.iterate(json);
|
||||
doc.at_pointer("/\\u00E9") == 123; // true
|
||||
doc.at_pointer((const char*)u8"/\u00E9") // returns an error (NO_SUCH_FIELD)
|
||||
```
|
||||
|
||||
Note that `at_pointer` calls [`rewind`](#rewind) to reset the parser at the beginning of the document. Hence, it invalidates all previously parsed values, objects and arrays: make sure to consume the values between each call to `at_pointer`. Consider the following example where one wants to store each object from the JSON into a vector of `struct car_type`:
|
||||
|
||||
```c++
|
||||
@@ -879,10 +1214,56 @@ doc.rewind(); // Need to manually rewind to be able to use find_field properly f
|
||||
std::cout << doc.find_field("k0") << std::endl; // Prints 27
|
||||
```
|
||||
|
||||
When the JSON path is the empty string (`""`) applied to a scalar document (lone string, number, Boolean or null), a SCALAR_DOCUMENT_AS_VALUE error is returned because scalar document cannot
|
||||
When the JSON Pointer Path is the empty string (`""`) applied to a scalar document (lone string, number, Boolean or null), a SCALAR_DOCUMENT_AS_VALUE error is returned because scalar document cannot
|
||||
be represented as `value` instances. You can check that a document is a scalar with the method `scalar()`.
|
||||
|
||||
Error Handling
|
||||
JSONPath
|
||||
------------
|
||||
|
||||
The simdjson library now supports a subset of [JSONPath](https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00) through the `at_path()` method, allowing you to reach further into the document in a single call. The subset of JSONPath that is implemented is the subset that is trivially convertible into the JSON Pointer format, using `.` to access a field and `[]` to access a specific index.
|
||||
|
||||
This implementation relies on `at_path()` converting its argument to JSON Pointer and then calling `at_pointer`, which makes use of [`rewind`](#rewind) to reset the parser at the beginning of the document. Hence, it invalidates all previously parsed values, objects and arrays: make sure to consume the values between each call to `at_path`.
|
||||
|
||||
Consider the following example:
|
||||
|
||||
```c++
|
||||
auto cars_json = R"( [
|
||||
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
|
||||
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
|
||||
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
|
||||
] )"_padded;
|
||||
ondemand::parser parser;
|
||||
auto cars = parser.iterate(cars_json);
|
||||
cout << cars.at_path("[0].tire_pressure[1]") << endl; // Prints 39.9
|
||||
```
|
||||
|
||||
A call to `at_path(json_path)` can result in any of the errors that are returned by the `at_pointer` method and if the conversion of `json_path` to JSON Pointer fails, it will lead to an `simdjson::INVALID_JSON_POINTER`error.
|
||||
|
||||
```c++
|
||||
auto cars_json = R"( [
|
||||
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
|
||||
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
|
||||
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
|
||||
] )"_padded;
|
||||
ondemand::parser parser;
|
||||
auto cars = parser.iterate(cars_json);
|
||||
ASSERT_ERROR(cars.at_path("[0].tire_presure[1").get(x), INVALID_JSON_POINTER); // Fails on conversion to JSON Pointer, since last square bracket was not properly closed.
|
||||
ASSERT_ERROR(cars.at_path("[0].incorrect_field[1]").get(x), NO_SUCH_FIELD); // Conversion to JSON Pointer succeeds, but fails on at_pointer() since the path is invalid.
|
||||
```
|
||||
|
||||
In most instances, a JSONPath is an ASCII string and the keys in a JSON document
|
||||
are ASCII strings. We support UTF-8 within a JSONPath expression, but key values are
|
||||
matched exactly, without unescaping or Unicode normalization. We do a byte-by-byte comparison.
|
||||
The e acute character is considered distinct from its escaped version `\u00E9`. E.g.,
|
||||
|
||||
```c++
|
||||
const padded_string json = "{\"\\u00E9\":123}"_padded;
|
||||
auto doc = parser.iterate(json);
|
||||
doc.at_path(".\\u00E9") == 123; // true
|
||||
doc.at_path((const char*)u8".\u00E9") // returns an error (NO_SUCH_FIELD)
|
||||
```
|
||||
|
||||
Error handling
|
||||
--------------
|
||||
|
||||
Error handing with exception and a single try/catch clause makes the code simple, but it gives you little control over errors. For easier debugging or more robust error handling, you may want to consider our exception-free approach.
|
||||
@@ -1032,9 +1413,9 @@ int main(void) {
|
||||
The `at` method can only be called once on an array. It cannot be used
|
||||
to iterate through the values of an array.
|
||||
|
||||
### Error Handling Examples without Exceptions
|
||||
### Error handling examples without exceptions
|
||||
|
||||
This is how the example in "Using the Parsed JSON" could be written using only error code checking (without exceptions):
|
||||
This is how the example in "Using the parsed JSON" could be written using only error code checking (without exceptions):
|
||||
|
||||
```c++
|
||||
bool parse() {
|
||||
@@ -1116,7 +1497,7 @@ having to handle exceptions.
|
||||
error = doc.get_object().get(object);
|
||||
if (error) { return false; }
|
||||
for(auto field : object) {
|
||||
// We could replace 'field.key() with field.unescaped_key(),
|
||||
// We could replace 'field.key() with field.unescaped_key() or field.escaped_key(),
|
||||
// and ondemand::raw_json_string by std::string_view.
|
||||
ondemand::raw_json_string keyv;
|
||||
error = field.key().get(keyv);
|
||||
@@ -1130,7 +1511,7 @@ having to handle exceptions.
|
||||
}
|
||||
```
|
||||
|
||||
### Disabling Exceptions
|
||||
### Disabling exceptions
|
||||
|
||||
The simdjson can be build with exceptions entirely disabled. It checks the `__cpp_exceptions` macro at compile time. Even if exceptions are enabled in your compiler, you may still disable exceptions specifically for simdjson, by setting `SIMDJSON_EXCEPTIONS` to `0` (false) at compile-time when building the simdjson library. If you are building with CMake, to ensure you don't write any code that uses exceptions, you compile with `SIMDJSON_EXCEPTIONS=OFF`. For example, if including the project via cmake:
|
||||
|
||||
@@ -1441,7 +1822,7 @@ If your documents are large (e.g., larger than a megabyte), then the `iterate_ma
|
||||
We also provide some support for comma-separated documents and other advanced features.
|
||||
See [iterate_many.md](iterate_many.md) for detailed information and design.
|
||||
|
||||
Parsing Numbers Inside Strings
|
||||
Parsing numbers inside strings
|
||||
------------------------------
|
||||
|
||||
Though the JSON specification allows for numbers and string values, many engineers choose to integrate the numbers inside strings, e.g., they prefer `{"a":"1.9"}` to`{"a":1.9}`.
|
||||
@@ -1535,7 +1916,7 @@ 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
|
||||
will report a `INCORRECT_TYPE` error otherwise.
|
||||
will report an `INCORRECT_TYPE` error otherwise.
|
||||
|
||||
The `*_in_string` methods can also be called on a single document instance:
|
||||
e.g., when your document consist solely of a quoted number.
|
||||
@@ -1568,8 +1949,10 @@ An `ondemand::number` instance may contain an integer value or a floating-point
|
||||
Thus it is a dynamically typed number. Before accessing the value, you must determine the detected type:
|
||||
|
||||
* `number.get_number_type()` has value `number_type::signed_integer` if we have a integer in [-9223372036854775808,9223372036854775808). You can recover the value by the `get_int64()` method applied on the `ondemand::number` instance. When `number.get_number_type()` has value `number_type::signed_integer`, you also have that `number.is_int64()` is true. Calling `get_int64()` on the `ondemand::number` instance when `number.get_number_type()` is not `number_type::signed_integer` is unsafe. You may replace `get_int64()` by a cast to a `int64_t` value.
|
||||
* `number.get_number_type()` has value `number_type::unsigned_integer` if we have a integer in [9223372036854775808,18446744073709551616). You can recover the value by the `get_uint64()` method applied on the `ondemand::number` instance. When `number.get_number_type()` has value `number_type::unsigned_integer`, you also have that `number.is_uint64()` is true. Calling `get_uint64()` on the `ondemand::number` instance when `number.get_number_type()` is not `number_type::unsigned_integer` is unsafe. You may replace `get_uint64()` by a cast to a `uint64_t` value.
|
||||
* `number.get_number_type()` has value `number_type::unsigned_integer` if we have a integer in `[9223372036854775808,18446744073709551616)`. You can recover the value by the `get_uint64()` method applied on the `ondemand::number` instance. When `number.get_number_type()` has value `number_type::unsigned_integer`, you also have that `number.is_uint64()` is true. Calling `get_uint64()` on the `ondemand::number` instance when `number.get_number_type()` is not `number_type::unsigned_integer` is unsafe. You may replace `get_uint64()` by a cast to a `uint64_t` value.
|
||||
* `number.get_number_type()` has value `number_type::floating_point_number` if we have and we have a floating-point (binary64) number. You can recover the value by the `get_double()` method applied on the `ondemand::number` instance. When `number.get_number_type()` has value `number_type::floating_point_number`, you also have that `number.is_double()` is true. Calling `get_double()` on the `ondemand::number` instance when `number.get_number_type()` is not `number_type::floating_point_number` is unsafe. You may replace `get_double()` by a cast to a `double` value.
|
||||
* When the value is an integer outside of the valid ranges for a 64-bit integers, e.g., when it is smaller than -9223372036854775808 or larger than 18446744073709551615, then `number.get_number_type()` has value `number_type::big_integer`. If you try to parse
|
||||
such a number of `get_number()`, you get the error `BIGINT_ERROR`. You can access the underlying string of digits with the function `raw_json_token()` which returns a `std::string_view` instance starting at the beginning of the digit. You can also call `get_double()` to get a floating-point approximation.
|
||||
|
||||
|
||||
You must check the type before accessing the value: it is an error to call `get_int64()` when `number.get_number_type()` is not `number_type::signed_integer` and when `number.is_int64()` is false. You are responsible for this check as the user of the library.
|
||||
@@ -1602,6 +1985,9 @@ Consider the following example:
|
||||
std::cout << "float: " << double(num) << " ";
|
||||
std::cout << "float: " << num.get_double() << std::endl;
|
||||
break;
|
||||
case ondemand::number_type::big_integer:
|
||||
std::cout << "big-integer: " << val.raw_json_token() << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1617,7 +2003,56 @@ It will output:
|
||||
9999999999999999999 negative: 0 is_integer: 1 large 64-bit integer: 9999999999999999999 large 64-bit integer: 9999999999999999999
|
||||
```
|
||||
|
||||
Raw Strings
|
||||
In the following example, we have an array of integers that are outside the valid range of 64-bit signed or
|
||||
unsigned integers. Calling `get_number_type()` on the values returns `ondemand::number_type::big_integer`.
|
||||
You can try to represent these big integers as 64-bit floating-point numbers, though you typically lose
|
||||
precision in the process (as illustrated in the example).
|
||||
|
||||
```C++
|
||||
ondemand::parser parser;
|
||||
padded_string docdata = R"([-9223372036854775809, 18446744073709551617, 99999999999999999999999 ])"_padded;
|
||||
double dexpected[] = {-9223372036854775808.0, 18446744073709551616.0, 1e23};
|
||||
ondemand::document doc = parser.iterate(docdata);
|
||||
ondemand::array arr = doc.get_array();
|
||||
for(ondemand::value val : arr) {
|
||||
if(val.get_number_type() == ondemand::number_type::big_integer) {
|
||||
std::cout << val.get_double() << std::endl;
|
||||
// might print -9.22337e+18, 1.84467e+19, 1e+23
|
||||
}
|
||||
}
|
||||
```
|
||||
This program might print:
|
||||
```
|
||||
-9.22337e+18
|
||||
1.84467e+19
|
||||
1e+23
|
||||
```
|
||||
|
||||
You may get access to the underlying string representing the big integer with
|
||||
`raw_json_token()` and you may parse the resulting number strings using your own parser.
|
||||
|
||||
```c++
|
||||
ondemand::parser parser;
|
||||
padded_string docdata = R"([-9223372036854775809, 18446744073709551617, 99999999999999999999999 ])"_padded;
|
||||
ondemand::document doc = parser.iterate(docdata);
|
||||
ondemand::array arr = doc.get_array();
|
||||
for(ondemand::value val : arr) {
|
||||
// val.get_number_type() == ondemand::number_type::big_integer
|
||||
if(val.get_number_type() == ondemand::number_type::big_integer) {
|
||||
std::string_view token = val.raw_json_token();
|
||||
// token = "-9223372036854775809", "18446744073709551617", "99999999999999999999999 "
|
||||
std::cout << "'" << token << "'" << std::endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
This code prints the following:
|
||||
```
|
||||
'-9223372036854775809'
|
||||
'18446744073709551617'
|
||||
'99999999999999999999999 '
|
||||
```
|
||||
|
||||
Raw strings from keys
|
||||
-----------
|
||||
|
||||
It is sometimes useful to have access to a raw (unescaped) string: we make available a
|
||||
@@ -1625,7 +2060,7 @@ minimalist `raw_json_string` data type which contains a pointer inside the strin
|
||||
original document, right after the quote. It is accessible via `get_raw_json_string()` on a
|
||||
string instance and returned by the `key()` method on an object's field instance. It is always
|
||||
optional: replacing `get_raw_json_string()` with `get_string()` and `key()` by
|
||||
`unescaped_key()` returns an `string_view` instance of the unescaped string.
|
||||
`unescaped_key()` or `escaped_key()` returns an `string_view` instance of the unescaped/unprocessed string.
|
||||
|
||||
You can quickly compare a `raw_json_string` instance with a target string. You may also
|
||||
unescape the `raw_json_string` on your own string buffer: `parser.unescape(mystr, ptr)`
|
||||
@@ -1653,10 +2088,25 @@ JSON string to a user-provided buffer:
|
||||
}
|
||||
```
|
||||
|
||||
Some users might prefer to have a direct access to a `std::string_view` instance
|
||||
pointing inside the source document. The `key_raw_json_token()` method serves this
|
||||
purpose. It provides a view on the key, including the starting quote character,
|
||||
and everything up to the next `:` character after the final quote character. E.g.,
|
||||
if the key is `"name"` then `key_raw_json_token()` returns a `std::string_view` which
|
||||
begins with `"name"` and may containing trailing white-space characters.
|
||||
```C++
|
||||
auto json = R"( {"name" : "Jack The Ripper \u0033"} )"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc = parser.iterate(json);
|
||||
for (auto key_value : doc.get_object()) {
|
||||
std::string_view keysv = key_value.key_raw_json_token(); // keysv is "\"name\" "
|
||||
}
|
||||
```
|
||||
|
||||
General Direct Access to the Raw JSON String
|
||||
|
||||
General direct access to the raw JSON string
|
||||
--------------------------------
|
||||
If your value is a string, the `raw_json_string` you with `get_raw_json_string()` gives you direct access to the unprocessed
|
||||
If your value is a string, the `raw_json_string` you get with `get_raw_json_string()` gives you direct access to the unprocessed
|
||||
string. But the simdjson library allows you to have access to the raw underlying JSON
|
||||
more generally, not just for strings.
|
||||
|
||||
@@ -1691,6 +2141,13 @@ string_view token = obj["value"].raw_json_token();
|
||||
|
||||
The `raw_json_token()` should be fast and free of allocation.
|
||||
|
||||
Given a quote-deliminated string, you find the string sequence inside the quote with a
|
||||
single line of code:
|
||||
|
||||
```C++
|
||||
std::string_view noquote(std::string_view v) { return {v.data()+1, v.find_last_of('"')-1}; }
|
||||
```
|
||||
|
||||
If your value is an array or an object, `raw_json_token()` returns effectively a single
|
||||
character (`[`) or (`}`) which is not very useful. For arrays and objects, we have another
|
||||
method called `raw_json()` which consumes (traverses) the array or the object.
|
||||
@@ -1754,14 +2211,14 @@ string representation.
|
||||
```
|
||||
|
||||
|
||||
Storing Directly into an Existing String Instance
|
||||
Storing directly into an existing string instance
|
||||
-----------------------------------------------------
|
||||
|
||||
The simdjson library favours the use of `std::string_view` instances because
|
||||
it tends to lead to better performance due to causing fewer memory allocations.
|
||||
However, they are cases where you need to store a string result in an `std::string``
|
||||
However, they are cases where you need to store a string result in a `std::string``
|
||||
instance. You can do so with a templated version of the `to_string()` method which takes as
|
||||
a parameter a reference to an `std::string`.
|
||||
a parameter a reference to a `std::string`.
|
||||
|
||||
```C++
|
||||
auto json = R"({
|
||||
@@ -1779,14 +2236,14 @@ The same routine can be written without exceptions handling:
|
||||
```C++
|
||||
std::string name;
|
||||
auto err = doc["name"].get_string(name);
|
||||
if(err) { /* handle error */ }
|
||||
if (err) { /* handle error */ }
|
||||
```
|
||||
|
||||
The `std::string` instance, once created, is independent. Unlike our `std::string_view` instances,
|
||||
it does not point at data that is within our `parser` instance. The same caveat applies: you should
|
||||
only consume a JSON string once.
|
||||
|
||||
Because `get_string()` is a template that requires a type that can be assigned an `std::string`, you
|
||||
Because `get_string()` is a template that requires a type that can be assigned a `std::string`, you
|
||||
can use it with features such as `std::optional`:
|
||||
|
||||
```C++
|
||||
@@ -1794,14 +2251,14 @@ can use it with features such as `std::optional`:
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc = parser.iterate(json);
|
||||
std::optional<std::string> value;
|
||||
if(doc["foo1"].get_string(value)) { /* error */ }
|
||||
if (doc["foo1"].get_string(value)) { /* error */ }
|
||||
// value was populated with "3.1416"
|
||||
```
|
||||
|
||||
You should be mindful of the trade-off: allocating multiple
|
||||
`std::string` instances can become expensive.
|
||||
|
||||
Thread Safety
|
||||
Thread safety
|
||||
-------------
|
||||
|
||||
We built simdjson with thread safety in mind.
|
||||
@@ -1814,10 +2271,15 @@ acts as an iterator (and is therefore not thread safe).
|
||||
|
||||
The CPU detection, which runs the first time parsing is attempted and switches to the fastest
|
||||
parser for your CPU, is transparent and thread-safe.
|
||||
Our runtime dispatching is based on global objects that are instantiated at the beginning of the
|
||||
main thread and may be discarded at the end of the main thread. If you have multiple threads running
|
||||
and some threads use the library while the main thread is cleaning up ressources, you may encounter
|
||||
issues. If you expect such problems, you may consider using [std::quick_exit](https://en.cppreference.com/w/cpp/utility/program/quick_exit).
|
||||
|
||||
In a threaded environment, stack space is often limited. Running code like simdjson in debug mode may require hundreds of kilobytes of stack memory. Thus stack overflows are a possibility. We recommend you turn on optimization when working in an environment where stack space is limited. If you must run your code in debug mode, we recommend you configure your system to have more stack space. We discourage you from running production code based on a debug build.
|
||||
|
||||
Standard Compliance
|
||||
|
||||
Standard compliance
|
||||
--------------------
|
||||
|
||||
The simdjson library is fully compliant with the [RFC 8259](https://www.tbray.org/ongoing/When/201x/2017/12/14/rfc8259.html) JSON specification.
|
||||
@@ -1835,7 +2297,7 @@ The simdjson library is fully compliant with the [RFC 8259](https://www.tbray.o
|
||||
- The specification states that an implementation may set limits on the maximum depth of nesting. By default, the simdjson will refuse to parse documents with a depth exceeding 1024.
|
||||
|
||||
|
||||
Backwards Compatibility
|
||||
Backwards compatibility
|
||||
-----------------------
|
||||
|
||||
The only header file supported by simdjson is `simdjson.h`. Older versions of simdjson published a
|
||||
@@ -2002,7 +2464,7 @@ bool example() {
|
||||
ondemand::document doc = parser.iterate(json);
|
||||
ondemand::object root_object = doc.get_object();
|
||||
for(auto key_value : root_object) {
|
||||
// could get std::string_view with 'unescaped_key()':
|
||||
// could get std::string_view with 'unescaped_key()' or 'escaped_key()':
|
||||
std::cout << "key: " << key_value.key() << std::endl;
|
||||
ondemand::object obj = key_value.value();
|
||||
|
||||
@@ -2075,6 +2537,8 @@ bool example() {
|
||||
std::cout << "\n\ntop level:" << field.key() << std::endl;
|
||||
// You can get a proper std::string_view for the key with:
|
||||
// std::string_view key = field.unescaped_key();
|
||||
// or
|
||||
// std::string_view key = field.escaped_key();
|
||||
// and second for-range loop to get child-elements here
|
||||
for (ondemand::object inner_object : field.value()) {
|
||||
auto i = inner_object.begin();
|
||||
@@ -2088,6 +2552,9 @@ bool example() {
|
||||
<< "\" : " << inner_field.value() << ", ";
|
||||
// You can get proper std::string_view for the key and value with:
|
||||
// std::string_view inner_key = field.unescaped_key();
|
||||
// or
|
||||
// std::string_view inner_key = field.escaped_key();
|
||||
// and
|
||||
// std::string_view value_str = field.value();
|
||||
}
|
||||
}
|
||||
@@ -2101,15 +2568,37 @@ bool example() {
|
||||
}
|
||||
```
|
||||
|
||||
* Example 4: Passing an array to a function
|
||||
|
||||
```C++
|
||||
|
||||
#include "simdjson.h"
|
||||
#include <iostream>
|
||||
|
||||
// prints the content of the array as hexadecimal 64-bit integers
|
||||
void f(simdjson::ondemand::array v) {
|
||||
for(uint64_t val : v) {
|
||||
std::cout << "0x" << std::hex << val << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Performance Tips
|
||||
int main(void) {
|
||||
simdjson::padded_string json = R"( [ 897314173811950000, 3122321 ])"_padded;
|
||||
simdjson::ondemand::parser parser;
|
||||
simdjson::ondemand::document doc = parser.iterate(json);
|
||||
f(doc.get_array());
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
Performance tips
|
||||
--------
|
||||
|
||||
|
||||
- The On Demand front-end works best when doing a single pass over the input: avoid calling `count_elements`, `rewind` and similar methods.
|
||||
- If you are familiar with assembly language, you may use the online tool godbolt to explore the compiled code. The following example may work: [https://godbolt.org/z/xE4GWs573](https://godbolt.org/z/xE4GWs573).
|
||||
- Given a field `field` in an object, calling `field.key()` is often faster than `field.unescaped_key()` so if you do not need an unescaped `std::string_view` instance, prefer `field.key()`.
|
||||
- Given a field `field` in an object, calling `field.key()` is often faster than `field.unescaped_key()` so if you do not need an unescaped `std::string_view` instance, prefer `field.key()`. Similarly, we expect `field.escaped_key()` to be faster than `field.unescaped_key()` even though both return a `std::string_view` instance.
|
||||
- For release builds, we recommend setting `NDEBUG` pre-processor directive when compiling the `simdjson` library. Importantly, using the optimization flags `-O2` or `-O3` under GCC and LLVM clang does not set the `NDEBUG` directive, you must set it manually (e.g., `-DNDEBUG`).
|
||||
- For long streams of JSON documents, consider [`iterate_many`](iterate_many.md) and [`parse_many`](parse_many.md) for better performance.
|
||||
- Never seek to access a field twice (e.g., o["data"] and later again o["data"]). Instead capture once an ondemand::value and reuse it.
|
||||
@@ -2133,4 +2622,4 @@ Further reading
|
||||
--------
|
||||
|
||||
|
||||
- John Keiser, Daniel Lemire, [On-Demand JSON: A Better Way to Parse Documents?](http://arxiv.org/abs/2312.17149), Software: Practice and Experience (to appear)
|
||||
- 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
|
||||
|
||||
+9
-3
@@ -71,6 +71,12 @@ For best performance, a `parser` instance should be reused over several files: o
|
||||
If you need a lower-level interface, you may call the function `parser.parse(const char * p, size_t l)` on a pointer `p` while specifying the
|
||||
length of your input `l` in bytes. To see how to get the very best performance from a low-level approach, you way want to read our [performance notes](https://github.com/simdjson/simdjson/blob/master/doc/performance.md#padding-and-temporary-copies) on this topic (see the Padding and Temporary Copies section).
|
||||
|
||||
*Windows-specific*: Windows users who need to read files with
|
||||
non-ANSI characters in the name should set their code page to
|
||||
UTF-8 (65001). This should be the default with Windows 11 and better.
|
||||
Further, they may use the AreFileApisANSI function to determine whether
|
||||
the filename is interpreted using the ANSI or the system default OEM
|
||||
codepage, and they may call SetFileApisToOEM accordingly.
|
||||
|
||||
Using the Parsed JSON
|
||||
---------------------
|
||||
@@ -223,13 +229,13 @@ dom::element cars = parser.parse(cars_json);
|
||||
cout << cars.at_pointer("/0/tire_pressure/1") << endl; // Prints 39.9
|
||||
```
|
||||
|
||||
A JSON Path is a sequence of segments each starting with the '/' character. Within arrays, an integer
|
||||
A JSON Pointer expression is a sequence of segments each starting with the '/' character. Within arrays, an integer
|
||||
index allows you to select the indexed node. Within objects, the string value of the key allows you to
|
||||
select the value. If your keys contain the characters '/' or '~', they must be escaped as '~1' and
|
||||
'~0' respectively. An empty JSON Path refers to the whole document.
|
||||
'~0' respectively. An empty JSON Pointer expression refers to the whole document.
|
||||
|
||||
We also extend the JSON Pointer support to include *relative* paths.
|
||||
You can apply a JSON path to any node and the path gets interpreted relatively, as if the current node were a whole JSON document.
|
||||
You can apply a JSON Pointer expression to any node and the path gets interpreted relatively, as if the current node were a whole JSON document.
|
||||
|
||||
Consider the following example:
|
||||
|
||||
|
||||
@@ -11,27 +11,31 @@ CPU Architecture-Specific Implementations
|
||||
Overview
|
||||
--------
|
||||
|
||||
|
||||
|
||||
The simdjson library takes advantage of SIMD instruction sets such as NEON, SSE and AVX to achieve
|
||||
much of its speed. Because these instruction sets work differently, simdjson has to compile a
|
||||
different version of the JSON parser for different CPU architectures, often with different
|
||||
algorithms to take better advantage of a given CPU!
|
||||
|
||||
The current implementations are:
|
||||
* icelake: AVX-512F, AVX-512VBMI, etc.
|
||||
* haswell: AVX2 (2013 Intel Haswell or later)
|
||||
* icelake: AVX-512F, AVX-512_VBMI, AVX-512_VBMI2, AVX-512_DQ, AVX-512512_CD, AVX-512_BW, AVX-512_VL (2019 Intel Ice Lake, Intel Rocket Lake, Intel Sapphire Rapids, AMD Zen 4)
|
||||
* haswell: AVX2 (2013 Intel Haswell or later, all AMD Zen processors)
|
||||
* westmere: SSE4.2 (2010 Westmere or later).
|
||||
* arm64: 64-bit ARMv8-A NEON
|
||||
* ppc64: 64-bit POWER8 and POWER9 with VSX and ALTIVEC extensions. Both big endian and little endian are implemented, depends on the compiler you are using. The library is tested on recent, little-endian, POWER systems.
|
||||
* ppc64: 64-bit POWER8 and POWER9 with VSX and ALTIVEC extensions. Both big endian and little endian are implemented, depending on the compiler you are using. The library is tested on recent, little-endian, POWER systems.
|
||||
* lasx: Loongson Advanced SIMD EXtension (LASX), a 256-bit vector expansion for the LoongArch architecture.
|
||||
* lsx: Loongson SIMD EXtension (LSX), a 128-bit vector expansion for the LoongArch architecture.
|
||||
* fallback: A generic implementation that runs on any 64-bit processor.
|
||||
|
||||
In many cases, you don't know where your compiled binary is going to run, so simdjson automatically
|
||||
compiles *all* the implementations into the executable. On Intel, it will include 4 implementations
|
||||
(icelake, haswell, westmere and fallback), on ARM it will include 2 (arm64 and fallback), and on PPC
|
||||
(icelake, haswell, westmere and fallback), on 64-bit ARM it will include just one since running dispatching is unnecessary, and on PPC
|
||||
it will include 2 (ppc64 and fallback).
|
||||
|
||||
If you know more about where you're going to run and want to save the space, you can disable any of
|
||||
these implementations at compile time with `-DSIMDJSON_IMPLEMENTATION_X=0` (where X is ICELAKE, HASWELL,
|
||||
WESTMERE, ARM64, PPC64 and FALLBACK).
|
||||
WESTMERE, ARM64, PPC64, LSX, LASX and FALLBACK).
|
||||
|
||||
The simdjson library automatically sets header flags for each implementation as it compiles; there
|
||||
is no need to set architecture-specific flags yourself (e.g., `-mavx2`, `/AVX2` or
|
||||
@@ -75,8 +79,7 @@ And look them up by name:
|
||||
```c++
|
||||
cout << simdjson::get_available_implementations()["fallback"]->description() << endl;
|
||||
```
|
||||
Though the fallback implementation should always be available, others might be missing. When
|
||||
an implementation is not available, the bracket call `simdjson::get_available_implementations()[name]`
|
||||
When an implementation is not available, the bracket call `simdjson::get_available_implementations()[name]`
|
||||
will return the null pointer.
|
||||
|
||||
The available implementations have been compiled but may not necessarily be run safely on your system
|
||||
|
||||
@@ -686,7 +686,7 @@ in production systems:
|
||||
// if (std::string_view(c2["name"]) != "Daniel") { return false; }
|
||||
```
|
||||
|
||||
A correct usage is given by the following example:
|
||||
A correct usage is given by the following example:
|
||||
|
||||
```C++
|
||||
ondemand::parser parser;
|
||||
@@ -752,7 +752,7 @@ On relevant systems, the On Demand API provides some support for runtime dispatc
|
||||
|
||||
Some users wish to run at the best possible speed. Under recent Intel and AMD processors, these users should take additional steps to verify that their code is well optimized.
|
||||
|
||||
Given that the On Demand API offer limited runtime dispatching, it matters that your code is compiled against a specific CPU target. You should verify that the code is compiled against the target you expect. Thankfully, the simdjson library will tell you exactly what it detects as an implementation: `icelake` (AVX512 x64 processors), `haswell` (AVX2 x64 processors), `westmere` (SSE4 x64 processors), `arm64` (64-bit ARM), `ppc64` (64-bit POWER), `fallback` (others). Under x64 processors, many programmers will want to target `haswell` whereas under ARM, most programmers will want to target `arm64` (and it should do so automatically). The `fallback` is probably only good for testing purposes, not for deployment.
|
||||
Given that the On Demand API offer limited runtime dispatching, it matters that your code is compiled against a specific CPU target. You should verify that the code is compiled against the target you expect. Thankfully, the simdjson library will tell you exactly what it detects as an implementation: `icelake` (AVX512 x64 processors), `haswell` (AVX2 x64 processors), `westmere` (SSE4 x64 processors), `arm64` (64-bit ARM), `ppc64` (64-bit POWER), `lasx` (LoongArch), `lsx` (LoongArch), `fallback` (others). Under x64 processors, many programmers will want to target `haswell` whereas under ARM, most programmers will want to target `arm64` (and it should do so automatically). The `fallback` is probably only good for testing purposes, not for deployment.
|
||||
|
||||
```C++
|
||||
std::cout << simdjson::builtin_implementation()->name() << std::endl;
|
||||
@@ -779,4 +779,4 @@ Instead of specifying a specific microarchitecture, you can let your compiler do
|
||||
|
||||
Passing `-march=native` to the compiler may make On Demand faster by allowing it to use optimizations specific to your machine. You cannot do this, however, if you are compiling code that might be run on less advanced machines. That is, be mindful that when compiling with the `-march=native` flag, the resulting binary will run on the current system but may not run on other systems (e.g., on an old processor).
|
||||
|
||||
If you are compiling on an ARM or POWER system, you do not need to be concerned with CPU selection during compilation. The `-march=native` flag useful for best performance on x64 (e.g., Intel) systems but it is generally unsupported on some platforms such as ARM (aarch64) or POWER.
|
||||
If you are compiling on an ARM or POWER system, you do not need to be concerned with CPU selection during compilation. The `-march=native` flag is useful for best performance on x64 (e.g., Intel) systems but it is generally unsupported on some platforms such as ARM (aarch64) or POWER.
|
||||
|
||||
+1
-1
@@ -159,7 +159,7 @@ When compiling with Visual Studio, we recommend the flags `/Ob2 /O2` or better.
|
||||
|
||||
Recent versions of Microsoft Visual Studio on Windows provides support for the LLVM Clang compiler. You only need to install the "Clang compiler" optional component (ClangCL). You may also get a copy of the 64-bit LLVM CLang compiler for [Windows directly from LLVM](https://releases.llvm.org/download.html). The simdjson library fully supports the LLVM Clang compiler under Windows. In fact, you may get better performance out of simdjson with the LLVM Clang compiler than with the regular Visual Studio compiler. Meanwhile the [LLVM CLang compiler is binary compatible with Visual Studio](https://clang.llvm.org/docs/MSVCCompatibility.html) which means that you can combine their binaries (executables and libraries).
|
||||
|
||||
Under Windows, we also support the GNU GCC compiler via MSYS2. The performance of 64-bit MSYS2 under Windows excellent (on par with Linux).
|
||||
Under Windows, we also support the GNU GCC compiler via MSYS2. The performance of 64-bit MSYS2 under Windows is excellent (on par with Linux).
|
||||
|
||||
|
||||
Power Usage and Downclocking
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ It is sometimes useful to start with an example. Consider the following JSON doc
|
||||
The following is a dump of the content of the tape, with the first number of each line representing the index of a tape element.
|
||||
|
||||
### The Tape
|
||||
| index | element (64 bit word) |
|
||||
| index | element (64-bit word) |
|
||||
| ----- | ------------------------------------------------------------------- |
|
||||
| 0 | r // pointing to 39 (right after last node) |
|
||||
| 1 | { // pointing to next tape location 38 (first node after the scope) |
|
||||
|
||||
@@ -78,7 +78,7 @@ fi
|
||||
# Different levels may uncover different types of bugs, see this interesting
|
||||
# thread: https://github.com/google/oss-fuzz/issues/2295#issuecomment-481493392
|
||||
# Oss-fuzz uses -O1 so it may be relevant to use something else than that,
|
||||
# to do something oss-fuzz doesn't.
|
||||
# to do something oss-fuzz does not do.
|
||||
variant=sanitizers-O3
|
||||
|
||||
if [ ! -d build-$variant ] ; then
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
|
||||
// Split data into two strings, json pointer and the document string.
|
||||
// Split data into two strings, JSON Pointer and the document string.
|
||||
// Might end up with none, either or both being empty, important for
|
||||
// covering edge cases such as
|
||||
// https://github.com/simdjson/simdjson/issues/1142 Inputs missing the
|
||||
// separator line will get an empty json pointer but the all the input put in
|
||||
// separator line will get an empty JSON Pointer but the all the input put in
|
||||
// the document string. This means test data from other fuzzers that take json
|
||||
// input works for this fuzzer as well.
|
||||
FuzzData fd(Data, Size);
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#if _M_ARM64
|
||||
#if SIMDJSON_REGULAR_VISUAL_STUDIO && SIMDJSON_IS_ARM64
|
||||
// __umulh requires intrin.h
|
||||
#include <intrin.h>
|
||||
#endif // _M_ARM64
|
||||
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO && SIMDJSON_IS_ARM64
|
||||
|
||||
namespace simdjson {
|
||||
namespace arm64 {
|
||||
@@ -32,13 +32,13 @@ static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars
|
||||
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
|
||||
internal::value128 answer;
|
||||
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
|
||||
#ifdef _M_ARM64
|
||||
#if SIMDJSON_IS_ARM64
|
||||
// ARM64 has native support for 64-bit multiplications, no need to emultate
|
||||
answer.high = __umulh(value1, value2);
|
||||
answer.low = value1 * value2;
|
||||
#else
|
||||
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
|
||||
#endif // _M_ARM64
|
||||
#endif // SIMDJSON_IS_ARM64
|
||||
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
|
||||
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
|
||||
answer.low = uint64_t(r);
|
||||
|
||||
@@ -134,7 +134,7 @@ namespace {
|
||||
tmp = vpaddq_u8(tmp, tmp);
|
||||
return vgetq_lane_u16(vreinterpretq_u16_u8(tmp), 0);
|
||||
}
|
||||
simdjson_inline bool any() const { return vmaxvq_u8(*this) != 0; }
|
||||
simdjson_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; }
|
||||
};
|
||||
|
||||
// Unsigned bytes
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
#include "simdjson/ppc64.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(westmere)
|
||||
#include "simdjson/westmere.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lsx)
|
||||
#include "simdjson/lsx.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
|
||||
#include "simdjson/lasx.h"
|
||||
#else
|
||||
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
||||
#endif
|
||||
|
||||
@@ -17,6 +17,10 @@ namespace simdjson {
|
||||
namespace ppc64 {}
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(westmere)
|
||||
namespace westmere {}
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lsx)
|
||||
namespace lsx {}
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
|
||||
namespace lasx {}
|
||||
#else
|
||||
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
||||
#endif
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
#include "simdjson/ppc64/implementation.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(westmere)
|
||||
#include "simdjson/westmere/implementation.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lsx)
|
||||
#include "simdjson/lsx/implementation.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
|
||||
#include "simdjson/lasx/implementation.h"
|
||||
#else
|
||||
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
||||
#endif
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
#include "simdjson/ppc64/ondemand.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(westmere)
|
||||
#include "simdjson/westmere/ondemand.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lsx)
|
||||
#include "simdjson/lsx/ondemand.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
|
||||
#include "simdjson/lasx/ondemand.h"
|
||||
#else
|
||||
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
||||
#endif
|
||||
|
||||
@@ -10,18 +10,12 @@
|
||||
#include "simdjson/dom/parser.h"
|
||||
#include "simdjson/dom/serialization.h"
|
||||
|
||||
// Deprecated API
|
||||
#include "simdjson/dom/jsonparser.h"
|
||||
#include "simdjson/dom/parsedjson.h"
|
||||
#include "simdjson/dom/parsedjson_iterator.h"
|
||||
|
||||
// Inline functions
|
||||
#include "simdjson/dom/array-inl.h"
|
||||
#include "simdjson/dom/document_stream-inl.h"
|
||||
#include "simdjson/dom/document-inl.h"
|
||||
#include "simdjson/dom/element-inl.h"
|
||||
#include "simdjson/dom/object-inl.h"
|
||||
#include "simdjson/dom/parsedjson_iterator-inl.h"
|
||||
#include "simdjson/dom/parser-inl.h"
|
||||
#include "simdjson/internal/tape_ref-inl.h"
|
||||
#include "simdjson/dom/serialization-inl.h"
|
||||
|
||||
@@ -19,11 +19,14 @@ public:
|
||||
public:
|
||||
using value_type = element;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = void;
|
||||
using reference = value_type;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
/**
|
||||
* Get the actual value
|
||||
*/
|
||||
inline value_type operator*() const noexcept;
|
||||
inline reference operator*() const noexcept;
|
||||
/**
|
||||
* Get the next value.
|
||||
*
|
||||
|
||||
@@ -223,7 +223,11 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
|
||||
return std::string_view(start, next_doc_index - current_index() + 1);
|
||||
} else {
|
||||
size_t next_doc_index = stream->batch_start + stream->parser->implementation->structural_indexes[stream->parser->implementation->next_structural_index];
|
||||
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), next_doc_index - current_index() - 1);
|
||||
size_t svlen = next_doc_index - current_index();
|
||||
while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0')) {
|
||||
svlen--;
|
||||
}
|
||||
return std::string_view(start, svlen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -375,6 +375,23 @@ inline simdjson_result<element> element::operator[](const char *key) const noexc
|
||||
return at_key(key);
|
||||
}
|
||||
|
||||
inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept {
|
||||
if (simdjson_unlikely(json_pointer[0] != '/')) {
|
||||
return false;
|
||||
}
|
||||
size_t escape = json_pointer.find('~');
|
||||
if (escape == std::string_view::npos) {
|
||||
return true;
|
||||
}
|
||||
if (escape == json_pointer.size() - 1) {
|
||||
return false;
|
||||
}
|
||||
if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline simdjson_result<element> element::at_pointer(std::string_view json_pointer) const noexcept {
|
||||
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
|
||||
switch (tape.tape_ref_type()) {
|
||||
@@ -383,7 +400,10 @@ inline simdjson_result<element> element::at_pointer(std::string_view json_pointe
|
||||
case internal::tape_type::START_ARRAY:
|
||||
return array(tape).at_pointer(json_pointer);
|
||||
default: {
|
||||
if(!json_pointer.empty()) { // a non-empty string is invalid on an atom
|
||||
if (!json_pointer.empty()) { // a non-empty string can be invalid, or accessing a primitive (issue 2154)
|
||||
if (is_pointer_well_formed(json_pointer)) {
|
||||
return NO_SUCH_FIELD;
|
||||
}
|
||||
return INVALID_JSON_POINTER;
|
||||
}
|
||||
// an empty string means that we return the current node
|
||||
|
||||
@@ -293,7 +293,7 @@ public:
|
||||
*
|
||||
* @return The integer value.
|
||||
* @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an integer
|
||||
* @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer doesn't fit in 64 bits or is negative
|
||||
* @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer does not fit in 64 bits or is negative
|
||||
*/
|
||||
inline operator uint64_t() const noexcept(false);
|
||||
/**
|
||||
@@ -301,7 +301,7 @@ public:
|
||||
*
|
||||
* @return The integer value.
|
||||
* @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an integer
|
||||
* @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer doesn't fit in 64 bits
|
||||
* @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer does not fit in 64 bits
|
||||
*/
|
||||
inline operator int64_t() const noexcept(false);
|
||||
/**
|
||||
@@ -309,7 +309,6 @@ public:
|
||||
*
|
||||
* @return The double value.
|
||||
* @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not a number
|
||||
* @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer doesn't fit in 64 bits or is negative
|
||||
*/
|
||||
inline operator double() const noexcept(false);
|
||||
/**
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
// TODO Remove this -- deprecated API and files
|
||||
|
||||
#ifndef SIMDJSON_DOM_JSONPARSER_H
|
||||
#define SIMDJSON_DOM_JSONPARSER_H
|
||||
|
||||
#include "simdjson/dom/base.h"
|
||||
#include "simdjson/dom/parser.h"
|
||||
#include "simdjson/dom/element.h"
|
||||
|
||||
#include "simdjson/dom/parser-inl.h"
|
||||
|
||||
namespace simdjson {
|
||||
|
||||
//
|
||||
// C API (json_parse and build_parsed_json) declarations
|
||||
//
|
||||
|
||||
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
|
||||
[[deprecated("Use parser.parse() instead")]]
|
||||
inline int json_parse(const uint8_t *buf, size_t len, dom::parser &parser, bool realloc_if_needed = true) noexcept {
|
||||
error_code code = parser.parse(buf, len, realloc_if_needed).error();
|
||||
// The deprecated json_parse API is a signal that the user plans to *use* the error code / valid
|
||||
// bits in the parser instead of heeding the result code. The normal parser unsets those in
|
||||
// anticipation of making the error code ephemeral.
|
||||
// Here we put the code back into the parser, until we've removed this method.
|
||||
parser.valid = code == SUCCESS;
|
||||
parser.error = code;
|
||||
return code;
|
||||
}
|
||||
[[deprecated("Use parser.parse() instead")]]
|
||||
inline int json_parse(const char *buf, size_t len, dom::parser &parser, bool realloc_if_needed = true) noexcept {
|
||||
error_code code = parser.parse(buf, len, realloc_if_needed).error();
|
||||
// The deprecated json_parse API is a signal that the user plans to *use* the error code / valid
|
||||
// bits in the parser instead of heeding the result code. The normal parser unsets those in
|
||||
// anticipation of making the error code ephemeral.
|
||||
// Here we put the code back into the parser, until we've removed this method.
|
||||
parser.valid = code == SUCCESS;
|
||||
parser.error = code;
|
||||
return code;
|
||||
}
|
||||
[[deprecated("Use parser.parse() instead")]]
|
||||
inline int json_parse(const std::string &s, dom::parser &parser, bool realloc_if_needed = true) noexcept {
|
||||
error_code code = parser.parse(s.data(), s.length(), realloc_if_needed).error();
|
||||
// The deprecated json_parse API is a signal that the user plans to *use* the error code / valid
|
||||
// bits in the parser instead of heeding the result code. The normal parser unsets those in
|
||||
// anticipation of making the error code ephemeral.
|
||||
// Here we put the code back into the parser, until we've removed this method.
|
||||
parser.valid = code == SUCCESS;
|
||||
parser.error = code;
|
||||
return code;
|
||||
}
|
||||
[[deprecated("Use parser.parse() instead")]]
|
||||
inline int json_parse(const padded_string &s, dom::parser &parser) noexcept {
|
||||
error_code code = parser.parse(s).error();
|
||||
// The deprecated json_parse API is a signal that the user plans to *use* the error code / valid
|
||||
// bits in the parser instead of heeding the result code. The normal parser unsets those in
|
||||
// anticipation of making the error code ephemeral.
|
||||
// Here we put the code back into the parser, until we've removed this method.
|
||||
parser.valid = code == SUCCESS;
|
||||
parser.error = code;
|
||||
return code;
|
||||
}
|
||||
|
||||
[[deprecated("Use parser.parse() instead")]]
|
||||
simdjson_warn_unused inline dom::parser build_parsed_json(const uint8_t *buf, size_t len, bool realloc_if_needed = true) noexcept {
|
||||
dom::parser parser;
|
||||
error_code code = parser.parse(buf, len, realloc_if_needed).error();
|
||||
// The deprecated json_parse API is a signal that the user plans to *use* the error code / valid
|
||||
// bits in the parser instead of heeding the result code. The normal parser unsets those in
|
||||
// anticipation of making the error code ephemeral.
|
||||
// Here we put the code back into the parser, until we've removed this method.
|
||||
parser.valid = code == SUCCESS;
|
||||
parser.error = code;
|
||||
return parser;
|
||||
}
|
||||
[[deprecated("Use parser.parse() instead")]]
|
||||
simdjson_warn_unused inline dom::parser build_parsed_json(const char *buf, size_t len, bool realloc_if_needed = true) noexcept {
|
||||
dom::parser parser;
|
||||
error_code code = parser.parse(buf, len, realloc_if_needed).error();
|
||||
// The deprecated json_parse API is a signal that the user plans to *use* the error code / valid
|
||||
// bits in the parser instead of heeding the result code. The normal parser unsets those in
|
||||
// anticipation of making the error code ephemeral.
|
||||
// Here we put the code back into the parser, until we've removed this method.
|
||||
parser.valid = code == SUCCESS;
|
||||
parser.error = code;
|
||||
return parser;
|
||||
}
|
||||
[[deprecated("Use parser.parse() instead")]]
|
||||
simdjson_warn_unused inline dom::parser build_parsed_json(const std::string &s, bool realloc_if_needed = true) noexcept {
|
||||
dom::parser parser;
|
||||
error_code code = parser.parse(s.data(), s.length(), realloc_if_needed).error();
|
||||
// The deprecated json_parse API is a signal that the user plans to *use* the error code / valid
|
||||
// bits in the parser instead of heeding the result code. The normal parser unsets those in
|
||||
// anticipation of making the error code ephemeral.
|
||||
// Here we put the code back into the parser, until we've removed this method.
|
||||
parser.valid = code == SUCCESS;
|
||||
parser.error = code;
|
||||
return parser;
|
||||
}
|
||||
[[deprecated("Use parser.parse() instead")]]
|
||||
simdjson_warn_unused inline dom::parser build_parsed_json(const padded_string &s) noexcept {
|
||||
dom::parser parser;
|
||||
error_code code = parser.parse(s).error();
|
||||
// The deprecated json_parse API is a signal that the user plans to *use* the error code / valid
|
||||
// bits in the parser instead of heeding the result code. The normal parser unsets those in
|
||||
// anticipation of making the error code ephemeral.
|
||||
// Here we put the code back into the parser, until we've removed this method.
|
||||
parser.valid = code == SUCCESS;
|
||||
parser.error = code;
|
||||
return parser;
|
||||
}
|
||||
#endif // SIMDJSON_DISABLE_DEPRECATED_API
|
||||
|
||||
/** @private We do not want to allow implicit conversion from C string to std::string. */
|
||||
int json_parse(const char *buf, dom::parser &parser) noexcept = delete;
|
||||
/** @private We do not want to allow implicit conversion from C string to std::string. */
|
||||
dom::parser build_parsed_json(const char *buf) noexcept = delete;
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_DOM_JSONPARSER_H
|
||||
@@ -18,13 +18,16 @@ public:
|
||||
|
||||
class iterator {
|
||||
public:
|
||||
using value_type = key_value_pair;
|
||||
using value_type = const key_value_pair;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = void;
|
||||
using reference = value_type;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
/**
|
||||
* Get the actual key/value pair
|
||||
*/
|
||||
inline const value_type operator*() const noexcept;
|
||||
inline reference operator*() const noexcept;
|
||||
/**
|
||||
* Get the next key/value pair.
|
||||
*
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
// TODO Remove this -- deprecated API and files
|
||||
|
||||
#ifndef SIMDJSON_DOM_PARSEDJSON_H
|
||||
#define SIMDJSON_DOM_PARSEDJSON_H
|
||||
|
||||
#include "simdjson/dom/base.h"
|
||||
|
||||
namespace simdjson {
|
||||
|
||||
/**
|
||||
* @deprecated Use `dom::parser` instead.
|
||||
*/
|
||||
using ParsedJson [[deprecated("Use dom::parser instead")]] = dom::parser;
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_DOM_PARSEDJSON_H
|
||||
@@ -1,545 +0,0 @@
|
||||
#ifndef SIMDJSON_PARSEDJSON_ITERATOR_INL_H
|
||||
#define SIMDJSON_PARSEDJSON_ITERATOR_INL_H
|
||||
|
||||
#include "simdjson/dom/base.h"
|
||||
#include "simdjson/dom/parsedjson_iterator.h"
|
||||
#include "simdjson/internal/jsonformatutils.h"
|
||||
|
||||
#include "simdjson/dom/parser-inl.h"
|
||||
#include "simdjson/internal/tape_ref-inl.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <ostream>
|
||||
|
||||
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
|
||||
|
||||
namespace simdjson {
|
||||
|
||||
// VS2017 reports deprecated warnings when you define a deprecated class's methods.
|
||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||
SIMDJSON_DISABLE_DEPRECATED_WARNING
|
||||
|
||||
// Because of template weirdness, the actual class definition is inline in the document class
|
||||
simdjson_warn_unused bool dom::parser::Iterator::is_ok() const {
|
||||
return location < tape_length;
|
||||
}
|
||||
|
||||
// useful for debugging purposes
|
||||
size_t dom::parser::Iterator::get_tape_location() const {
|
||||
return location;
|
||||
}
|
||||
|
||||
// useful for debugging purposes
|
||||
size_t dom::parser::Iterator::get_tape_length() const {
|
||||
return tape_length;
|
||||
}
|
||||
|
||||
// returns the current depth (start at 1 with 0 reserved for the fictitious root
|
||||
// node)
|
||||
size_t dom::parser::Iterator::get_depth() const {
|
||||
return depth;
|
||||
}
|
||||
|
||||
// A scope is a series of nodes at the same depth, typically it is either an
|
||||
// object ({) or an array ([). The root node has type 'r'.
|
||||
uint8_t dom::parser::Iterator::get_scope_type() const {
|
||||
return depth_index[depth].scope_type;
|
||||
}
|
||||
|
||||
bool dom::parser::Iterator::move_forward() {
|
||||
if (location + 1 >= tape_length) {
|
||||
return false; // we are at the end!
|
||||
}
|
||||
|
||||
if ((current_type == '[') || (current_type == '{')) {
|
||||
// We are entering a new scope
|
||||
depth++;
|
||||
assert(depth < max_depth);
|
||||
depth_index[depth].start_of_scope = location;
|
||||
depth_index[depth].scope_type = current_type;
|
||||
} else if ((current_type == ']') || (current_type == '}')) {
|
||||
// Leaving a scope.
|
||||
depth--;
|
||||
} else if (is_number()) {
|
||||
// these types use 2 locations on the tape, not just one.
|
||||
location += 1;
|
||||
}
|
||||
|
||||
location += 1;
|
||||
current_val = doc.tape[location];
|
||||
current_type = uint8_t(current_val >> 56);
|
||||
return true;
|
||||
}
|
||||
|
||||
void dom::parser::Iterator::move_to_value() {
|
||||
// assume that we are on a key, so move by 1.
|
||||
location += 1;
|
||||
current_val = doc.tape[location];
|
||||
current_type = uint8_t(current_val >> 56);
|
||||
}
|
||||
|
||||
bool dom::parser::Iterator::move_to_key(const char *key) {
|
||||
if (down()) {
|
||||
do {
|
||||
const bool right_key = (strcmp(get_string(), key) == 0);
|
||||
move_to_value();
|
||||
if (right_key) {
|
||||
return true;
|
||||
}
|
||||
} while (next());
|
||||
up();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dom::parser::Iterator::move_to_key_insensitive(
|
||||
const char *key) {
|
||||
if (down()) {
|
||||
do {
|
||||
const bool right_key = (simdjson_strcasecmp(get_string(), key) == 0);
|
||||
move_to_value();
|
||||
if (right_key) {
|
||||
return true;
|
||||
}
|
||||
} while (next());
|
||||
up();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dom::parser::Iterator::move_to_key(const char *key,
|
||||
uint32_t length) {
|
||||
if (down()) {
|
||||
do {
|
||||
bool right_key = ((get_string_length() == length) &&
|
||||
(memcmp(get_string(), key, length) == 0));
|
||||
move_to_value();
|
||||
if (right_key) {
|
||||
return true;
|
||||
}
|
||||
} while (next());
|
||||
up();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dom::parser::Iterator::move_to_index(uint32_t index) {
|
||||
if (down()) {
|
||||
uint32_t i = 0;
|
||||
for (; i < index; i++) {
|
||||
if (!next()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == index) {
|
||||
return true;
|
||||
}
|
||||
up();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dom::parser::Iterator::prev() {
|
||||
size_t target_location = location;
|
||||
to_start_scope();
|
||||
size_t npos = location;
|
||||
if (target_location == npos) {
|
||||
return false; // we were already at the start
|
||||
}
|
||||
size_t oldnpos;
|
||||
// we have that npos < target_location here
|
||||
do {
|
||||
oldnpos = npos;
|
||||
if ((current_type == '[') || (current_type == '{')) {
|
||||
// we need to jump
|
||||
npos = uint32_t(current_val);
|
||||
} else {
|
||||
npos = npos + ((current_type == 'd' || current_type == 'l') ? 2 : 1);
|
||||
}
|
||||
} while (npos < target_location);
|
||||
location = oldnpos;
|
||||
current_val = doc.tape[location];
|
||||
current_type = uint8_t(current_val >> 56);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dom::parser::Iterator::up() {
|
||||
if (depth == 1) {
|
||||
return false; // don't allow moving back to root
|
||||
}
|
||||
to_start_scope();
|
||||
// next we just move to the previous value
|
||||
depth--;
|
||||
location -= 1;
|
||||
current_val = doc.tape[location];
|
||||
current_type = uint8_t(current_val >> 56);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dom::parser::Iterator::down() {
|
||||
if (location + 1 >= tape_length) {
|
||||
return false;
|
||||
}
|
||||
if ((current_type == '[') || (current_type == '{')) {
|
||||
size_t npos = uint32_t(current_val);
|
||||
if (npos == location + 2) {
|
||||
return false; // we have an empty scope
|
||||
}
|
||||
depth++;
|
||||
assert(depth < max_depth);
|
||||
location = location + 1;
|
||||
depth_index[depth].start_of_scope = location;
|
||||
depth_index[depth].scope_type = current_type;
|
||||
current_val = doc.tape[location];
|
||||
current_type = uint8_t(current_val >> 56);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void dom::parser::Iterator::to_start_scope() {
|
||||
location = depth_index[depth].start_of_scope;
|
||||
current_val = doc.tape[location];
|
||||
current_type = uint8_t(current_val >> 56);
|
||||
}
|
||||
|
||||
inline void dom::parser::Iterator::rewind() {
|
||||
while (up())
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
bool dom::parser::Iterator::next() {
|
||||
size_t npos;
|
||||
if ((current_type == '[') || (current_type == '{')) {
|
||||
// we need to jump
|
||||
npos = uint32_t(current_val);
|
||||
} else {
|
||||
npos = location + (is_number() ? 2 : 1);
|
||||
}
|
||||
uint64_t next_val = doc.tape[npos];
|
||||
uint8_t next_type = uint8_t(next_val >> 56);
|
||||
if ((next_type == ']') || (next_type == '}')) {
|
||||
return false; // we reached the end of the scope
|
||||
}
|
||||
location = npos;
|
||||
current_val = next_val;
|
||||
current_type = next_type;
|
||||
return true;
|
||||
}
|
||||
dom::parser::Iterator::Iterator(const dom::parser &pj) noexcept(false)
|
||||
: doc(pj.doc)
|
||||
{
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
if (!pj.valid) { throw simdjson_error(pj.error); }
|
||||
#else
|
||||
if (!pj.valid) { return; } // abort() usage is forbidden in the library
|
||||
#endif
|
||||
|
||||
max_depth = pj.max_depth();
|
||||
depth_index = new scopeindex_t[max_depth + 1];
|
||||
depth_index[0].start_of_scope = location;
|
||||
current_val = doc.tape[location++];
|
||||
current_type = uint8_t(current_val >> 56);
|
||||
depth_index[0].scope_type = current_type;
|
||||
tape_length = size_t(current_val & internal::JSON_VALUE_MASK);
|
||||
if (location < tape_length) {
|
||||
// If we make it here, then depth_capacity must >=2, but the compiler
|
||||
// may not know this.
|
||||
current_val = doc.tape[location];
|
||||
current_type = uint8_t(current_val >> 56);
|
||||
depth++;
|
||||
assert(depth < max_depth);
|
||||
depth_index[depth].start_of_scope = location;
|
||||
depth_index[depth].scope_type = current_type;
|
||||
}
|
||||
}
|
||||
dom::parser::Iterator::Iterator(
|
||||
const dom::parser::Iterator &o) noexcept
|
||||
: doc(o.doc),
|
||||
max_depth(o.depth),
|
||||
depth(o.depth),
|
||||
location(o.location),
|
||||
tape_length(o.tape_length),
|
||||
current_type(o.current_type),
|
||||
current_val(o.current_val)
|
||||
{
|
||||
depth_index = new scopeindex_t[max_depth+1];
|
||||
std::memcpy(depth_index, o.depth_index, (depth + 1) * sizeof(depth_index[0]));
|
||||
}
|
||||
|
||||
dom::parser::Iterator::~Iterator() noexcept {
|
||||
if (depth_index) { delete[] depth_index; }
|
||||
}
|
||||
|
||||
bool dom::parser::Iterator::print(std::ostream &os, bool escape_strings) const {
|
||||
if (!is_ok()) {
|
||||
return false;
|
||||
}
|
||||
switch (current_type) {
|
||||
case '"': // we have a string
|
||||
os << '"';
|
||||
if (escape_strings) {
|
||||
os << internal::escape_json_string(std::string_view(get_string(), get_string_length()));
|
||||
} else {
|
||||
// was: os << get_string();, but given that we can include null chars, we
|
||||
// have to do something crazier:
|
||||
std::copy(get_string(), get_string() + get_string_length(), std::ostream_iterator<char>(os));
|
||||
}
|
||||
os << '"';
|
||||
break;
|
||||
case 'l': // we have a long int
|
||||
os << get_integer();
|
||||
break;
|
||||
case 'u':
|
||||
os << get_unsigned_integer();
|
||||
break;
|
||||
case 'd':
|
||||
os << get_double();
|
||||
break;
|
||||
case 'n': // we have a null
|
||||
os << "null";
|
||||
break;
|
||||
case 't': // we have a true
|
||||
os << "true";
|
||||
break;
|
||||
case 'f': // we have a false
|
||||
os << "false";
|
||||
break;
|
||||
case '{': // we have an object
|
||||
case '}': // we end an object
|
||||
case '[': // we start an array
|
||||
case ']': // we end an array
|
||||
os << char(current_type);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dom::parser::Iterator::move_to(const char *pointer,
|
||||
uint32_t length) {
|
||||
char *new_pointer = nullptr;
|
||||
if (pointer[0] == '#') {
|
||||
// Converting fragment representation to string representation
|
||||
new_pointer = new char[length];
|
||||
uint32_t new_length = 0;
|
||||
for (uint32_t i = 1; i < length; i++) {
|
||||
if (pointer[i] == '%' && pointer[i + 1] == 'x') {
|
||||
#if __cpp_exceptions
|
||||
try {
|
||||
#endif
|
||||
int fragment =
|
||||
std::stoi(std::string(&pointer[i + 2], 2), nullptr, 16);
|
||||
if (fragment == '\\' || fragment == '"' || (fragment <= 0x1F)) {
|
||||
// escaping the character
|
||||
new_pointer[new_length] = '\\';
|
||||
new_length++;
|
||||
}
|
||||
new_pointer[new_length] = char(fragment);
|
||||
i += 3;
|
||||
#if __cpp_exceptions
|
||||
} catch (std::invalid_argument &) {
|
||||
delete[] new_pointer;
|
||||
return false; // the fragment is invalid
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
new_pointer[new_length] = pointer[i];
|
||||
}
|
||||
new_length++;
|
||||
}
|
||||
length = new_length;
|
||||
pointer = new_pointer;
|
||||
}
|
||||
|
||||
// saving the current state
|
||||
size_t depth_s = depth;
|
||||
size_t location_s = location;
|
||||
uint8_t current_type_s = current_type;
|
||||
uint64_t current_val_s = current_val;
|
||||
|
||||
rewind(); // The json pointer is used from the root of the document.
|
||||
|
||||
bool found = relative_move_to(pointer, length);
|
||||
delete[] new_pointer;
|
||||
|
||||
if (!found) {
|
||||
// since the pointer has found nothing, we get back to the original
|
||||
// position.
|
||||
depth = depth_s;
|
||||
location = location_s;
|
||||
current_type = current_type_s;
|
||||
current_val = current_val_s;
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
inline bool dom::parser::Iterator::move_to(const std::string &pointer) {
|
||||
return move_to(pointer.c_str(), uint32_t(pointer.length()));
|
||||
}
|
||||
|
||||
inline int64_t dom::parser::Iterator::get_integer() const {
|
||||
if (location + 1 >= tape_length) {
|
||||
return 0; // default value in case of error
|
||||
}
|
||||
return static_cast<int64_t>(doc.tape[location + 1]);
|
||||
}
|
||||
|
||||
inline uint64_t dom::parser::Iterator::get_unsigned_integer() const {
|
||||
if (location + 1 >= tape_length) {
|
||||
return 0; // default value in case of error
|
||||
}
|
||||
return doc.tape[location + 1];
|
||||
}
|
||||
|
||||
inline const char * dom::parser::Iterator::get_string() const {
|
||||
return reinterpret_cast<const char *>(
|
||||
doc.string_buf.get() + (current_val & internal::JSON_VALUE_MASK) + sizeof(uint32_t));
|
||||
}
|
||||
|
||||
inline uint32_t dom::parser::Iterator::get_string_length() const {
|
||||
uint32_t answer;
|
||||
std::memcpy(&answer,
|
||||
reinterpret_cast<const char *>(doc.string_buf.get() +
|
||||
(current_val & internal::JSON_VALUE_MASK)),
|
||||
sizeof(uint32_t));
|
||||
return answer;
|
||||
}
|
||||
|
||||
inline double dom::parser::Iterator::get_double() const {
|
||||
if (location + 1 >= tape_length) {
|
||||
return std::numeric_limits<double>::quiet_NaN(); // default value in
|
||||
// case of error
|
||||
}
|
||||
double answer;
|
||||
std::memcpy(&answer, &doc.tape[location + 1], sizeof(answer));
|
||||
return answer;
|
||||
}
|
||||
|
||||
bool dom::parser::Iterator::relative_move_to(const char *pointer,
|
||||
uint32_t length) {
|
||||
if (length == 0) {
|
||||
// returns the whole document
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pointer[0] != '/') {
|
||||
// '/' must be the first character
|
||||
return false;
|
||||
}
|
||||
|
||||
// finding the key in an object or the index in an array
|
||||
std::string key_or_index;
|
||||
uint32_t offset = 1;
|
||||
|
||||
// checking for the "-" case
|
||||
if (is_array() && pointer[1] == '-') {
|
||||
if (length != 2) {
|
||||
// the pointer must be exactly "/-"
|
||||
// there can't be anything more after '-' as an index
|
||||
return false;
|
||||
}
|
||||
key_or_index = '-';
|
||||
offset = length; // will skip the loop coming right after
|
||||
}
|
||||
|
||||
// We either transform the first reference token to a valid json key
|
||||
// or we make sure it is a valid index in an array.
|
||||
for (; offset < length; offset++) {
|
||||
if (pointer[offset] == '/') {
|
||||
// beginning of the next key or index
|
||||
break;
|
||||
}
|
||||
if (is_array() && (pointer[offset] < '0' || pointer[offset] > '9')) {
|
||||
// the index of an array must be an integer
|
||||
// we also make sure std::stoi won't discard whitespaces later
|
||||
return false;
|
||||
}
|
||||
if (pointer[offset] == '~') {
|
||||
// "~1" represents "/"
|
||||
if (pointer[offset + 1] == '1') {
|
||||
key_or_index += '/';
|
||||
offset++;
|
||||
continue;
|
||||
}
|
||||
// "~0" represents "~"
|
||||
if (pointer[offset + 1] == '0') {
|
||||
key_or_index += '~';
|
||||
offset++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (pointer[offset] == '\\') {
|
||||
if (pointer[offset + 1] == '\\' || pointer[offset + 1] == '"' ||
|
||||
(pointer[offset + 1] <= 0x1F)) {
|
||||
key_or_index += pointer[offset + 1];
|
||||
offset++;
|
||||
continue;
|
||||
}
|
||||
return false; // invalid escaped character
|
||||
}
|
||||
if (pointer[offset] == '\"') {
|
||||
// unescaped quote character. this is an invalid case.
|
||||
// lets do nothing and assume most pointers will be valid.
|
||||
// it won't find any corresponding json key anyway.
|
||||
// return false;
|
||||
}
|
||||
key_or_index += pointer[offset];
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
if (is_object()) {
|
||||
if (move_to_key(key_or_index.c_str(), uint32_t(key_or_index.length()))) {
|
||||
found = relative_move_to(pointer + offset, length - offset);
|
||||
}
|
||||
} else if (is_array()) {
|
||||
if (key_or_index == "-") { // handling "-" case first
|
||||
if (down()) {
|
||||
while (next())
|
||||
; // moving to the end of the array
|
||||
// moving to the nonexistent value right after...
|
||||
size_t npos;
|
||||
if ((current_type == '[') || (current_type == '{')) {
|
||||
// we need to jump
|
||||
npos = uint32_t(current_val);
|
||||
} else {
|
||||
npos =
|
||||
location + ((current_type == 'd' || current_type == 'l') ? 2 : 1);
|
||||
}
|
||||
location = npos;
|
||||
current_val = doc.tape[npos];
|
||||
current_type = uint8_t(current_val >> 56);
|
||||
return true; // how could it fail ?
|
||||
}
|
||||
} else { // regular numeric index
|
||||
// The index can't have a leading '0'
|
||||
if (key_or_index[0] == '0' && key_or_index.length() > 1) {
|
||||
return false;
|
||||
}
|
||||
// it cannot be empty
|
||||
if (key_or_index.length() == 0) {
|
||||
return false;
|
||||
}
|
||||
// we already checked the index contains only valid digits
|
||||
uint32_t index = std::stoi(key_or_index);
|
||||
if (move_to_index(index)) {
|
||||
found = relative_move_to(pointer + offset, length - offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
SIMDJSON_POP_DISABLE_WARNINGS
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_DISABLE_DEPRECATED_API
|
||||
|
||||
|
||||
#endif // SIMDJSON_PARSEDJSON_ITERATOR_INL_H
|
||||
@@ -1,231 +0,0 @@
|
||||
// TODO Remove this -- deprecated API and files
|
||||
|
||||
#ifndef SIMDJSON_DOM_PARSEDJSON_ITERATOR_H
|
||||
#define SIMDJSON_DOM_PARSEDJSON_ITERATOR_H
|
||||
|
||||
#include "simdjson/dom/base.h"
|
||||
#include "simdjson/dom/parser.h"
|
||||
|
||||
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
|
||||
|
||||
namespace simdjson {
|
||||
/** @private **/
|
||||
class [[deprecated("Use the new DOM navigation API instead (see doc/basics.md)")]] dom::parser::Iterator {
|
||||
public:
|
||||
inline Iterator(const dom::parser &parser) noexcept(false);
|
||||
inline Iterator(const Iterator &o) noexcept;
|
||||
inline ~Iterator() noexcept;
|
||||
|
||||
inline Iterator& operator=(const Iterator&) = delete;
|
||||
|
||||
inline bool is_ok() const;
|
||||
|
||||
// useful for debugging purposes
|
||||
inline size_t get_tape_location() const;
|
||||
|
||||
// useful for debugging purposes
|
||||
inline size_t get_tape_length() const;
|
||||
|
||||
// returns the current depth (start at 1 with 0 reserved for the fictitious
|
||||
// root node)
|
||||
inline size_t get_depth() const;
|
||||
|
||||
// A scope is a series of nodes at the same depth, typically it is either an
|
||||
// object ({) or an array ([). The root node has type 'r'.
|
||||
inline uint8_t get_scope_type() const;
|
||||
|
||||
// move forward in document order
|
||||
inline bool move_forward();
|
||||
|
||||
// retrieve the character code of what we're looking at:
|
||||
// [{"slutfn are the possibilities
|
||||
inline uint8_t get_type() const {
|
||||
return current_type; // short functions should be inlined!
|
||||
}
|
||||
|
||||
// get the int64_t value at this node; valid only if get_type is "l"
|
||||
inline int64_t get_integer() const;
|
||||
|
||||
// get the value as uint64; valid only if if get_type is "u"
|
||||
inline uint64_t get_unsigned_integer() const;
|
||||
|
||||
// get the string value at this node (NULL ended); valid only if get_type is "
|
||||
// note that tabs, and line endings are escaped in the returned value (see
|
||||
// print_with_escapes) return value is valid UTF-8, it may contain NULL chars
|
||||
// within the string: get_string_length determines the true string length.
|
||||
inline const char *get_string() const;
|
||||
|
||||
// return the length of the string in bytes
|
||||
inline uint32_t get_string_length() const;
|
||||
|
||||
// get the double value at this node; valid only if
|
||||
// get_type() is "d"
|
||||
inline double get_double() const;
|
||||
|
||||
inline bool is_object_or_array() const { return is_object() || is_array(); }
|
||||
|
||||
inline bool is_object() const { return get_type() == '{'; }
|
||||
|
||||
inline bool is_array() const { return get_type() == '['; }
|
||||
|
||||
inline bool is_string() const { return get_type() == '"'; }
|
||||
|
||||
// Returns true if the current type of the node is an signed integer.
|
||||
// You can get its value with `get_integer()`.
|
||||
inline bool is_integer() const { return get_type() == 'l'; }
|
||||
|
||||
// Returns true if the current type of the node is an unsigned integer.
|
||||
// You can get its value with `get_unsigned_integer()`.
|
||||
//
|
||||
// NOTE:
|
||||
// Only a large value, which is out of range of a 64-bit signed integer, is
|
||||
// represented internally as an unsigned node. On the other hand, a typical
|
||||
// positive integer, such as 1, 42, or 1000000, is as a signed node.
|
||||
// Be aware this function returns false for a signed node.
|
||||
inline bool is_unsigned_integer() const { return get_type() == 'u'; }
|
||||
// Returns true if the current type of the node is a double floating-point number.
|
||||
inline bool is_double() const { return get_type() == 'd'; }
|
||||
// Returns true if the current type of the node is a number (integer or floating-point).
|
||||
inline bool is_number() const {
|
||||
return is_integer() || is_unsigned_integer() || is_double();
|
||||
}
|
||||
// Returns true if the current type of the node is a bool with true value.
|
||||
inline bool is_true() const { return get_type() == 't'; }
|
||||
// Returns true if the current type of the node is a bool with false value.
|
||||
inline bool is_false() const { return get_type() == 'f'; }
|
||||
// Returns true if the current type of the node is null.
|
||||
inline bool is_null() const { return get_type() == 'n'; }
|
||||
// Returns true if the type byte represents an object of an array
|
||||
static bool is_object_or_array(uint8_t type) {
|
||||
return ((type == '[') || (type == '{'));
|
||||
}
|
||||
|
||||
// when at {, go one level deep, looking for a given key
|
||||
// if successful, we are left pointing at the value,
|
||||
// if not, we are still pointing at the object ({)
|
||||
// (in case of repeated keys, this only finds the first one).
|
||||
// We seek the key using C's strcmp so if your JSON strings contain
|
||||
// NULL chars, this would trigger a false positive: if you expect that
|
||||
// to be the case, take extra precautions.
|
||||
// Furthermore, we do the comparison character-by-character
|
||||
// without taking into account Unicode equivalence.
|
||||
inline bool move_to_key(const char *key);
|
||||
|
||||
// as above, but case insensitive lookup (strcmpi instead of strcmp)
|
||||
inline bool move_to_key_insensitive(const char *key);
|
||||
|
||||
// when at {, go one level deep, looking for a given key
|
||||
// if successful, we are left pointing at the value,
|
||||
// if not, we are still pointing at the object ({)
|
||||
// (in case of repeated keys, this only finds the first one).
|
||||
// The string we search for can contain NULL values.
|
||||
// Furthermore, we do the comparison character-by-character
|
||||
// without taking into account Unicode equivalence.
|
||||
inline bool move_to_key(const char *key, uint32_t length);
|
||||
|
||||
// when at a key location within an object, this moves to the accompanying
|
||||
// value (located next to it). This is equivalent but much faster than
|
||||
// calling "next()".
|
||||
inline void move_to_value();
|
||||
|
||||
// when at [, go one level deep, and advance to the given index.
|
||||
// if successful, we are left pointing at the value,
|
||||
// if not, we are still pointing at the array ([)
|
||||
inline bool move_to_index(uint32_t index);
|
||||
|
||||
// Moves the iterator to the value corresponding to the json pointer.
|
||||
// Always search from the root of the document.
|
||||
// if successful, we are left pointing at the value,
|
||||
// if not, we are still pointing the same value we were pointing before the
|
||||
// call. The json pointer follows the rfc6901 standard's syntax:
|
||||
// https://tools.ietf.org/html/rfc6901 However, the standard says "If a
|
||||
// referenced member name is not unique in an object, the member that is
|
||||
// referenced is undefined, and evaluation fails". Here we just return the
|
||||
// first corresponding value. The length parameter is the length of the
|
||||
// jsonpointer string ('pointer').
|
||||
inline bool move_to(const char *pointer, uint32_t length);
|
||||
|
||||
// Moves the iterator to the value corresponding to the json pointer.
|
||||
// Always search from the root of the document.
|
||||
// if successful, we are left pointing at the value,
|
||||
// if not, we are still pointing the same value we were pointing before the
|
||||
// call. The json pointer implementation follows the rfc6901 standard's
|
||||
// syntax: https://tools.ietf.org/html/rfc6901 However, the standard says
|
||||
// "If a referenced member name is not unique in an object, the member that
|
||||
// is referenced is undefined, and evaluation fails". Here we just return
|
||||
// the first corresponding value.
|
||||
inline bool move_to(const std::string &pointer);
|
||||
|
||||
private:
|
||||
// Almost the same as move_to(), except it searches from the current
|
||||
// position. The pointer's syntax is identical, though that case is not
|
||||
// handled by the rfc6901 standard. The '/' is still required at the
|
||||
// beginning. However, contrary to move_to(), the URI Fragment Identifier
|
||||
// Representation is not supported here. Also, in case of failure, we are
|
||||
// left pointing at the closest value it could reach. For these reasons it
|
||||
// is private. It exists because it is used by move_to().
|
||||
inline bool relative_move_to(const char *pointer, uint32_t length);
|
||||
|
||||
public:
|
||||
// throughout return true if we can do the navigation, false
|
||||
// otherwise
|
||||
|
||||
// Within a given scope (series of nodes at the same depth within either an
|
||||
// array or an object), we move forward.
|
||||
// Thus, given [true, null, {"a":1}, [1,2]], we would visit true, null, {
|
||||
// and [. At the object ({) or at the array ([), you can issue a "down" to
|
||||
// visit their content. valid if we're not at the end of a scope (returns
|
||||
// true).
|
||||
inline bool next();
|
||||
|
||||
// Within a given scope (series of nodes at the same depth within either an
|
||||
// array or an object), we move backward.
|
||||
// Thus, given [true, null, {"a":1}, [1,2]], we would visit ], }, null, true
|
||||
// when starting at the end of the scope. At the object ({) or at the array
|
||||
// ([), you can issue a "down" to visit their content.
|
||||
// Performance warning: This function is implemented by starting again
|
||||
// from the beginning of the scope and scanning forward. You should expect
|
||||
// it to be relatively slow.
|
||||
inline bool prev();
|
||||
|
||||
// Moves back to either the containing array or object (type { or [) from
|
||||
// within a contained scope.
|
||||
// Valid unless we are at the first level of the document
|
||||
inline bool up();
|
||||
|
||||
// Valid if we're at a [ or { and it starts a non-empty scope; moves us to
|
||||
// start of that deeper scope if it not empty. Thus, given [true, null,
|
||||
// {"a":1}, [1,2]], if we are at the { node, we would move to the "a" node.
|
||||
inline bool down();
|
||||
|
||||
// move us to the start of our current scope,
|
||||
// a scope is a series of nodes at the same level
|
||||
inline void to_start_scope();
|
||||
|
||||
inline void rewind();
|
||||
|
||||
|
||||
|
||||
// print the node we are currently pointing at
|
||||
inline bool print(std::ostream &os, bool escape_strings = true) const;
|
||||
|
||||
private:
|
||||
const document &doc;
|
||||
size_t max_depth{};
|
||||
size_t depth{};
|
||||
size_t location{}; // our current location on a tape
|
||||
size_t tape_length{};
|
||||
uint8_t current_type{};
|
||||
uint64_t current_val{};
|
||||
typedef struct {
|
||||
size_t start_of_scope;
|
||||
uint8_t scope_type;
|
||||
} scopeindex_t;
|
||||
|
||||
scopeindex_t *depth_index{};
|
||||
};
|
||||
|
||||
} // namespace simdjson
|
||||
#endif // SIMDJSON_DISABLE_DEPRECATED_API
|
||||
|
||||
#endif // SIMDJSON_DOM_PARSEDJSON_ITERATOR_H
|
||||
@@ -83,6 +83,15 @@ public:
|
||||
* If the parser's current capacity is less than the file length, it will allocate enough capacity
|
||||
* to handle it (up to max_capacity).
|
||||
*
|
||||
* ## Windows and Unicode
|
||||
*
|
||||
* Windows users who need to read files with non-ANSI characters in the
|
||||
* name should set their code page to UTF-8 (65001) before calling this
|
||||
* function. This should be the default with Windows 11 and better.
|
||||
* Further, they may use the AreFileApisANSI function to determine whether
|
||||
* the filename is interpreted using the ANSI or the system default OEM
|
||||
* codepage, and they may call SetFileApisToOEM accordingly.
|
||||
*
|
||||
* @param path The path to load.
|
||||
* @return The document, or an error:
|
||||
* - IO_ERROR if there was an error opening or reading the file.
|
||||
|
||||
@@ -57,15 +57,15 @@ public:
|
||||
simdjson_inline void one_char(char c);
|
||||
|
||||
simdjson_inline void call_print_newline() {
|
||||
this->print_newline();
|
||||
static_cast<formatter*>(this)->print_newline();
|
||||
}
|
||||
|
||||
simdjson_inline void call_print_indents(size_t depth) {
|
||||
this->print_indents(depth);
|
||||
static_cast<formatter*>(this)->print_indents(depth);
|
||||
}
|
||||
|
||||
simdjson_inline void call_print_space() {
|
||||
this->print_space();
|
||||
static_cast<formatter*>(this)->print_space();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -27,6 +27,7 @@ enum error_code {
|
||||
F_ATOM_ERROR, ///< Problem while parsing an atom starting with the letter 'f'
|
||||
N_ATOM_ERROR, ///< Problem while parsing an atom starting with the letter 'n'
|
||||
NUMBER_ERROR, ///< Problem while parsing a number
|
||||
BIGINT_ERROR, ///< The integer value exceeds 64 bits
|
||||
UTF8_ERROR, ///< the input is not valid UTF-8
|
||||
UNINITIALIZED, ///< unknown error, or uninitialized document
|
||||
EMPTY, ///< no structural element found
|
||||
@@ -38,7 +39,7 @@ enum error_code {
|
||||
INDEX_OUT_OF_BOUNDS, ///< JSON array index too large
|
||||
NO_SUCH_FIELD, ///< JSON field not found in object
|
||||
IO_ERROR, ///< Error reading a file
|
||||
INVALID_JSON_POINTER, ///< Invalid JSON pointer reference
|
||||
INVALID_JSON_POINTER, ///< Invalid JSON pointer syntax
|
||||
INVALID_URI_FRAGMENT, ///< Invalid URI fragment
|
||||
UNEXPECTED_ERROR, ///< indicative of a bug in simdjson
|
||||
PARSER_IN_USE, ///< parser is already in use.
|
||||
|
||||
@@ -56,13 +56,13 @@ static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi)
|
||||
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
|
||||
internal::value128 answer;
|
||||
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
|
||||
#ifdef _M_ARM64
|
||||
#if SIMDJSON_IS_ARM64
|
||||
// ARM64 has native support for 64-bit multiplications, no need to emultate
|
||||
answer.high = __umulh(value1, value2);
|
||||
answer.low = value1 * value2;
|
||||
#else
|
||||
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
|
||||
#endif // _M_ARM64
|
||||
#endif // SIMDJSON_IS_ARM64
|
||||
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
|
||||
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
|
||||
answer.low = uint64_t(r);
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
#include "simdjson/arm64/begin.h"
|
||||
#elif SIMDJSON_IMPLEMENTATION_PPC64
|
||||
#include "simdjson/ppc64/begin.h"
|
||||
#elif SIMDJSON_IMPLEMENTATION_LSX
|
||||
#include "simdjson/lsx/begin.h"
|
||||
#elif SIMDJSON_IMPLEMENTATION_LASX
|
||||
#include "simdjson/lasx/begin.h"
|
||||
#elif SIMDJSON_IMPLEMENTATION_FALLBACK
|
||||
#include "simdjson/fallback/begin.h"
|
||||
#else
|
||||
@@ -37,7 +41,8 @@ class dom_parser_implementation;
|
||||
enum class number_type {
|
||||
floating_point_number=1, /// a binary64 number
|
||||
signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
|
||||
unsigned_integer /// a positive integer larger or equal to 1<<63
|
||||
unsigned_integer, /// a positive integer larger or equal to 1<<63
|
||||
big_integer /// a big integer that does not fit in a 64-bit word
|
||||
};
|
||||
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
|
||||
@@ -20,11 +20,13 @@ namespace numberparsing {
|
||||
#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE)))
|
||||
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE)))
|
||||
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE)))
|
||||
#define BIGINT_NUMBER(SRC) (found_invalid_number((SRC)), BIGINT_ERROR)
|
||||
#else
|
||||
#define INVALID_NUMBER(SRC) (NUMBER_ERROR)
|
||||
#define WRITE_INTEGER(VALUE, SRC, WRITER) (WRITER).append_s64((VALUE))
|
||||
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (WRITER).append_u64((VALUE))
|
||||
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (WRITER).append_double((VALUE))
|
||||
#define BIGINT_NUMBER(SRC) (BIGINT_ERROR)
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
@@ -355,6 +357,10 @@ simdjson_inline bool parse_digit(const uint8_t c, I &i) {
|
||||
return true;
|
||||
}
|
||||
|
||||
simdjson_inline bool is_digit(const uint8_t c) {
|
||||
return static_cast<uint8_t>(c - '0') <= 9;
|
||||
}
|
||||
|
||||
simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) {
|
||||
// we continue with the fiction that we have an integer. If the
|
||||
// floating point number is representable as x * 10^z for some integer
|
||||
@@ -412,7 +418,7 @@ simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const s
|
||||
// If there were more than 18 digits, we may have overflowed the integer. We have to do
|
||||
// something!!!!
|
||||
if (simdjson_unlikely(p > start_exp+18)) {
|
||||
// Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow
|
||||
// Skip leading zeroes: 1e000000000000000000001 is technically valid and does not overflow
|
||||
while (*start_exp == '0') { start_exp++; }
|
||||
// 19 digits could overflow int64_t and is kind of absurd anyway. We don't
|
||||
// support exponents smaller than -999,999,999,999,999,999 and bigger
|
||||
@@ -434,6 +440,23 @@ simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const s
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
simdjson_inline bool check_if_integer(const uint8_t *const src, size_t max_length) {
|
||||
const uint8_t *const srcend = src + max_length;
|
||||
bool negative = (*src == '-'); // we can always read at least one character after the '-'
|
||||
const uint8_t *p = src + uint8_t(negative);
|
||||
if(p == srcend) { return false; }
|
||||
if(*p == '0') {
|
||||
++p;
|
||||
if(p == srcend) { return true; }
|
||||
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
|
||||
return true;
|
||||
}
|
||||
while(p != srcend && is_digit(*p)) { ++p; }
|
||||
if(p == srcend) { return true; }
|
||||
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
simdjson_inline size_t significant_digits(const uint8_t * start_digits, size_t digit_count) {
|
||||
// It is possible that the integer had an overflow.
|
||||
// We have to handle the case where we have 0.0000somenumber.
|
||||
@@ -508,6 +531,18 @@ simdjson_inline error_code write_float(const uint8_t *const src, bool negative,
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
// parse the number at src
|
||||
// define JSON_TEST_NUMBERS for unit testing
|
||||
//
|
||||
// It is assumed that the number is followed by a structural ({,},],[) character
|
||||
// or a white space character. If that is not the case (e.g., when the JSON
|
||||
// document is made of a single number), then it is necessary to copy the
|
||||
// content and append a space before calling this function.
|
||||
//
|
||||
// Our objective is accurate parsing (ULP of 0) at high speed.
|
||||
template<typename W>
|
||||
simdjson_inline error_code parse_number(const uint8_t *const src, W &writer);
|
||||
|
||||
// for performance analysis, it is sometimes useful to skip parsing
|
||||
#ifdef SIMDJSON_SKIPNUMBERPARSING
|
||||
|
||||
@@ -586,11 +621,11 @@ simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
|
||||
// The longest positive 64-bit number is 20 digits.
|
||||
// We do it this way so we don't trigger this branch unless we must.
|
||||
size_t longest_digit_count = negative ? 19 : 20;
|
||||
if (digit_count > longest_digit_count) { return INVALID_NUMBER(src); }
|
||||
if (digit_count > longest_digit_count) { return BIGINT_NUMBER(src); }
|
||||
if (digit_count == longest_digit_count) {
|
||||
if (negative) {
|
||||
// Anything negative above INT64_MAX+1 is invalid
|
||||
if (i > uint64_t(INT64_MAX)+1) { return INVALID_NUMBER(src); }
|
||||
if (i > uint64_t(INT64_MAX)+1) { return BIGINT_NUMBER(src); }
|
||||
WRITE_INTEGER(~i+1, src, writer);
|
||||
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
|
||||
return SUCCESS;
|
||||
@@ -609,7 +644,7 @@ simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
|
||||
} else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INVALID_NUMBER(src); }
|
||||
}
|
||||
|
||||
// Write unsigned if it doesn't fit in a signed integer.
|
||||
// Write unsigned if it does not fit in a signed integer.
|
||||
if (i > uint64_t(INT64_MAX)) {
|
||||
WRITE_UNSIGNED(i, src, writer);
|
||||
} else {
|
||||
@@ -1060,19 +1095,32 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
|
||||
src += uint8_t(negative);
|
||||
const uint8_t *p = src;
|
||||
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
|
||||
size_t digit_count = size_t(p - src);
|
||||
if ( p == src ) { return NUMBER_ERROR; }
|
||||
if (jsoncharutils::is_structural_or_whitespace(*p)) {
|
||||
static const uint8_t * smaller_big_integer = reinterpret_cast<const uint8_t *>("9223372036854775808");
|
||||
// We have an integer.
|
||||
if(simdjson_unlikely(digit_count > 20)) {
|
||||
return number_type::big_integer;
|
||||
}
|
||||
// If the number is negative and valid, it must be a signed integer.
|
||||
if(negative) { return number_type::signed_integer; }
|
||||
if(negative) {
|
||||
if (simdjson_unlikely(digit_count > 19)) return number_type::big_integer;
|
||||
if (simdjson_unlikely(digit_count == 19 && memcmp(src, smaller_big_integer, 19) > 0)) {
|
||||
return number_type::big_integer;
|
||||
}
|
||||
return number_type::signed_integer;
|
||||
}
|
||||
// Let us check if we have a big integer (>=2**64).
|
||||
static const uint8_t * two_to_sixtyfour = reinterpret_cast<const uint8_t *>("18446744073709551616");
|
||||
if((digit_count > 20) || (digit_count == 20 && memcmp(src, two_to_sixtyfour, 20) >= 0)) {
|
||||
return number_type::big_integer;
|
||||
}
|
||||
// The number is positive and smaller than 18446744073709551616 (or 2**64).
|
||||
// We want values larger or equal to 9223372036854775808 to be unsigned
|
||||
// integers, and the other values to be signed integers.
|
||||
int digit_count = int(p - src);
|
||||
if(digit_count >= 19) {
|
||||
const uint8_t * smaller_big_integer = reinterpret_cast<const uint8_t *>("9223372036854775808");
|
||||
if((digit_count >= 20) || (memcmp(src, smaller_big_integer, 19) >= 0)) {
|
||||
return number_type::unsigned_integer;
|
||||
}
|
||||
if((digit_count == 20) || (digit_count >= 19 && memcmp(src, smaller_big_integer, 19) >= 0)) {
|
||||
return number_type::unsigned_integer;
|
||||
}
|
||||
return number_type::signed_integer;
|
||||
}
|
||||
@@ -1250,6 +1298,7 @@ inline std::ostream& operator<<(std::ostream& out, number_type type) noexcept {
|
||||
case number_type::signed_integer: out << "integer in [-9223372036854775808,9223372036854775808)"; break;
|
||||
case number_type::unsigned_integer: out << "unsigned integer in [9223372036854775808,18446744073709551616)"; break;
|
||||
case number_type::floating_point_number: out << "floating-point number (binary64)"; break;
|
||||
case number_type::big_integer: out << "big integer"; break;
|
||||
default: SIMDJSON_UNREACHABLE();
|
||||
}
|
||||
return out;
|
||||
@@ -1258,4 +1307,4 @@ inline std::ostream& operator<<(std::ostream& out, number_type type) noexcept {
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_GENERIC_NUMBERPARSING_H
|
||||
#endif // SIMDJSON_GENERIC_NUMBERPARSING_H
|
||||
|
||||
@@ -163,6 +163,59 @@ inline simdjson_result<value> array::at_pointer(std::string_view json_pointer) n
|
||||
return child;
|
||||
}
|
||||
|
||||
inline std::string json_path_to_pointer_conversion(std::string_view json_path) {
|
||||
if (json_path.empty() || (json_path.front() != '.' &&
|
||||
json_path.front() != '[')) {
|
||||
return "-1"; // This is just a sentinel value, the caller should check for this and return an error.
|
||||
}
|
||||
|
||||
std::string result;
|
||||
// Reserve space to reduce allocations, adjusting for potential increases due
|
||||
// to escaping.
|
||||
result.reserve(json_path.size() * 2);
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
while (i < json_path.length()) {
|
||||
if (json_path[i] == '.') {
|
||||
result += '/';
|
||||
} else if (json_path[i] == '[') {
|
||||
result += '/';
|
||||
++i; // Move past the '['
|
||||
while (i < json_path.length() && json_path[i] != ']') {
|
||||
if (json_path[i] == '~') {
|
||||
result += "~0";
|
||||
} else if (json_path[i] == '/') {
|
||||
result += "~1";
|
||||
} else {
|
||||
result += json_path[i];
|
||||
}
|
||||
++i;
|
||||
}
|
||||
if (i == json_path.length() || json_path[i] != ']') {
|
||||
return "-1"; // Using sentinel value that will be handled as an error by the caller.
|
||||
}
|
||||
} else {
|
||||
if (json_path[i] == '~') {
|
||||
result += "~0";
|
||||
} else if (json_path[i] == '/') {
|
||||
result += "~1";
|
||||
} else {
|
||||
result += json_path[i];
|
||||
}
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline simdjson_result<value> array::at_path(std::string_view json_path) noexcept {
|
||||
auto json_pointer = json_path_to_pointer_conversion(json_path);
|
||||
if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
|
||||
return at_pointer(json_pointer);
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<value> array::at(size_t index) noexcept {
|
||||
size_t i = 0;
|
||||
for (auto value : *this) {
|
||||
@@ -217,10 +270,14 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdj
|
||||
if (error()) { return error(); }
|
||||
return first.at_pointer(json_pointer);
|
||||
}
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::at_path(std::string_view json_path) noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.at_path(json_path);
|
||||
}
|
||||
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::raw_json() noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.raw_json();
|
||||
}
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
|
||||
|
||||
@@ -100,6 +100,22 @@ public:
|
||||
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
|
||||
*/
|
||||
inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
|
||||
|
||||
/**
|
||||
* Get the value associated with the given JSONPath expression. We only support
|
||||
* JSONPath queries that trivially convertible to JSON Pointer queries: key
|
||||
* names and array indices.
|
||||
*
|
||||
* https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00
|
||||
*
|
||||
* @return The value associated with the given JSONPath expression, or:
|
||||
* - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails
|
||||
* - NO_SUCH_FIELD if a field does not exist in an object
|
||||
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
|
||||
* - INCORRECT_TYPE if a non-integer is used to access an array
|
||||
*/
|
||||
inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
|
||||
|
||||
/**
|
||||
* Consumes the array and returns a string_view instance corresponding to the
|
||||
* array as represented in JSON. It points inside the original document.
|
||||
@@ -190,10 +206,11 @@ public:
|
||||
inline simdjson_result<bool> reset() & 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_path(std::string_view json_path) noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
|
||||
|
||||
};
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "simdjson/generic/ondemand/array_iterator.h"
|
||||
#include "simdjson/generic/ondemand/document.h"
|
||||
#include "simdjson/generic/ondemand/json_iterator-inl.h"
|
||||
#include "simdjson/generic/ondemand/json_path_to_pointer_conversion.h"
|
||||
#include "simdjson/generic/ondemand/json_path_to_pointer_conversion-inl.h"
|
||||
#include "simdjson/generic/ondemand/json_type.h"
|
||||
#include "simdjson/generic/ondemand/object-inl.h"
|
||||
#include "simdjson/generic/ondemand/raw_json_string.h"
|
||||
@@ -181,6 +183,8 @@ template<typename T> simdjson_inline error_code document::get(T &out) && noexcep
|
||||
}
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <class T>
|
||||
simdjson_inline document::operator T() noexcept(false) { return get<T>(); }
|
||||
simdjson_inline document::operator array() & noexcept(false) { return get_array(); }
|
||||
simdjson_inline document::operator object() & noexcept(false) { return get_object(); }
|
||||
simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); }
|
||||
@@ -265,6 +269,13 @@ simdjson_inline simdjson_result<bool> document::is_scalar() noexcept {
|
||||
return ! ((this_type == json_type::array) || (this_type == json_type::object));
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<bool> document::is_string() noexcept {
|
||||
json_type this_type;
|
||||
auto error = type().get(this_type);
|
||||
if(error) { return error; }
|
||||
return (this_type == json_type::string);
|
||||
}
|
||||
|
||||
simdjson_inline bool document::is_negative() noexcept {
|
||||
return get_root_value_iterator().is_root_negative();
|
||||
}
|
||||
@@ -284,7 +295,7 @@ simdjson_inline simdjson_result<number> document::get_number() noexcept {
|
||||
|
||||
simdjson_inline simdjson_result<std::string_view> document::raw_json_token() noexcept {
|
||||
auto _iter = get_root_value_iterator();
|
||||
return std::string_view(reinterpret_cast<const char*>(_iter.peek_start()), _iter.peek_start_length());
|
||||
return std::string_view(reinterpret_cast<const char*>(_iter.peek_start()), _iter.peek_root_length());
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
|
||||
@@ -305,6 +316,23 @@ simdjson_inline simdjson_result<value> document::at_pointer(std::string_view jso
|
||||
}
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<value> document::at_path(std::string_view json_path) noexcept {
|
||||
rewind(); // Rewind the document each time at_pointer is called
|
||||
if (json_path.empty()) {
|
||||
return this->get_value();
|
||||
}
|
||||
json_type t;
|
||||
SIMDJSON_TRY(type().get(t));
|
||||
switch (t) {
|
||||
case json_type::array:
|
||||
return (*this).get_array().at_path(json_path);
|
||||
case json_type::object:
|
||||
return (*this).get_object().at_path(json_path);
|
||||
default:
|
||||
return INVALID_JSON_POINTER;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ondemand
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
@@ -480,6 +508,10 @@ simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::o
|
||||
return first.is_scalar();
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::is_string() noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.is_string();
|
||||
}
|
||||
|
||||
simdjson_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::is_negative() noexcept {
|
||||
if (error()) { return error(); }
|
||||
@@ -503,6 +535,11 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number> simdj
|
||||
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <class T, typename std::enable_if<std::is_same<T, SIMDJSON_IMPLEMENTATION::ondemand::document>::value == false>::type>
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator T() noexcept(false) {
|
||||
if (error()) { throw simdjson_error(error()); }
|
||||
return first;
|
||||
}
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false) {
|
||||
if (error()) { throw simdjson_error(error()); }
|
||||
return first;
|
||||
@@ -568,6 +605,10 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
|
||||
return first.at_pointer(json_pointer);
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::at_path(std::string_view json_path) noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.at_path(json_path);
|
||||
}
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
@@ -609,15 +650,25 @@ simdjson_inline simdjson_result<raw_json_string> document_reference::get_raw_jso
|
||||
simdjson_inline simdjson_result<bool> document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); }
|
||||
simdjson_inline simdjson_result<value> document_reference::get_value() noexcept { return doc->get_value(); }
|
||||
simdjson_inline simdjson_result<bool> document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); }
|
||||
|
||||
template<> simdjson_inline simdjson_result<array> document_reference::get() & noexcept { return get_array(); }
|
||||
template<> simdjson_inline simdjson_result<object> document_reference::get() & noexcept { return get_object(); }
|
||||
template<> simdjson_inline simdjson_result<raw_json_string> document_reference::get() & noexcept { return get_raw_json_string(); }
|
||||
template<> simdjson_inline simdjson_result<std::string_view> document_reference::get() & noexcept { return get_string(false); }
|
||||
template<> simdjson_inline simdjson_result<double> document_reference::get() & noexcept { return get_double(); }
|
||||
template<> simdjson_inline simdjson_result<uint64_t> document_reference::get() & noexcept { return get_uint64(); }
|
||||
template<> simdjson_inline simdjson_result<int64_t> document_reference::get() & noexcept { return get_int64(); }
|
||||
template<> simdjson_inline simdjson_result<bool> document_reference::get() & noexcept { return get_bool(); }
|
||||
template<> simdjson_inline simdjson_result<value> document_reference::get() & noexcept { return get_value(); }
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <class T>
|
||||
simdjson_inline document_reference::operator T() noexcept(false) { return get<T>(); }
|
||||
simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); }
|
||||
simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); }
|
||||
simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); }
|
||||
simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); }
|
||||
simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); }
|
||||
simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); }
|
||||
simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return raw_json_string(*doc); }
|
||||
simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); }
|
||||
simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); }
|
||||
simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); }
|
||||
#endif
|
||||
@@ -634,6 +685,7 @@ simdjson_inline simdjson_result<value> document_reference::find_field_unordered(
|
||||
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); }
|
||||
simdjson_inline simdjson_result<json_type> document_reference::type() noexcept { return doc->type(); }
|
||||
simdjson_inline simdjson_result<bool> document_reference::is_scalar() noexcept { return doc->is_scalar(); }
|
||||
simdjson_inline simdjson_result<bool> document_reference::is_string() noexcept { return doc->is_string(); }
|
||||
simdjson_inline simdjson_result<const char *> document_reference::current_location() noexcept { return doc->current_location(); }
|
||||
simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); }
|
||||
simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); }
|
||||
@@ -642,6 +694,7 @@ simdjson_inline simdjson_result<number_type> document_reference::get_number_type
|
||||
simdjson_inline simdjson_result<number> document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); }
|
||||
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_path(std::string_view json_path) noexcept { return doc->at_path(json_path); }
|
||||
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json() noexcept { return doc->raw_json();}
|
||||
simdjson_inline document_reference::operator document&() const noexcept { return *doc; }
|
||||
|
||||
@@ -773,6 +826,10 @@ simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::o
|
||||
if (error()) { return error(); }
|
||||
return first.is_scalar();
|
||||
}
|
||||
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::is_string() noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.is_string();
|
||||
}
|
||||
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::is_negative() noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.is_negative();
|
||||
@@ -790,6 +847,11 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number> simdj
|
||||
return first.get_number();
|
||||
}
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <class T, typename std::enable_if<std::is_same<T, SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::value == false>::type>
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator T() noexcept(false) {
|
||||
if (error()) { throw simdjson_error(error()); }
|
||||
return first;
|
||||
}
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false) {
|
||||
if (error()) { throw simdjson_error(error()); }
|
||||
return first;
|
||||
@@ -843,7 +905,13 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
|
||||
return first.at_pointer(json_pointer);
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::at_path(std::string_view json_path) noexcept {
|
||||
if (error()) {
|
||||
return error();
|
||||
}
|
||||
return first.at_path(json_path);
|
||||
}
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
|
||||
|
||||
@@ -179,21 +179,23 @@ public:
|
||||
* @returns INCORRECT_TYPE If the JSON value is not the given type.
|
||||
*/
|
||||
template<typename T> simdjson_inline simdjson_result<T> get() & noexcept {
|
||||
// Unless the simdjson library provides an inline implementation, calling this method should
|
||||
// Unless the simdjson library or the user provides an inline implementation, calling this method should
|
||||
// immediately fail.
|
||||
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
|
||||
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
|
||||
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
|
||||
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template.");
|
||||
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
|
||||
" You may also add support for custom types, see our documentation.");
|
||||
}
|
||||
/** @overload template<typename T> simdjson_result<T> get() & noexcept */
|
||||
template<typename T> simdjson_inline simdjson_result<T> get() && noexcept {
|
||||
// Unless the simdjson library provides an inline implementation, calling this method should
|
||||
// Unless the simdjson library or the user provides an inline implementation, calling this method should
|
||||
// immediately fail.
|
||||
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
|
||||
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
|
||||
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
|
||||
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template.");
|
||||
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
|
||||
" You may also add support for custom types, see our documentation.");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -212,6 +214,17 @@ public:
|
||||
template<typename T> simdjson_inline error_code get(T &out) && noexcept;
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
/**
|
||||
* Cast this JSON value to an instance of type T. The programmer is responsible for
|
||||
* providing an implementation of get<T> for the type T, if T is not one of the types
|
||||
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.)
|
||||
*
|
||||
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
|
||||
*
|
||||
* @returns An instance of type T
|
||||
*/
|
||||
template <class T>
|
||||
explicit simdjson_inline operator T() noexcept(false);
|
||||
/**
|
||||
* Cast this JSON value to an array.
|
||||
*
|
||||
@@ -384,7 +397,7 @@ public:
|
||||
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
|
||||
*
|
||||
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
|
||||
* field wasn't there when they aren't).
|
||||
* field was not there when they are not in order).
|
||||
*
|
||||
* You must consume the fields on an object one at a time. A request for a new key
|
||||
* invalidates previous field values: it makes them unsafe. E.g., the array
|
||||
@@ -430,6 +443,14 @@ public:
|
||||
*/
|
||||
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
|
||||
|
||||
/**
|
||||
* Checks whether the document is a string.
|
||||
*
|
||||
* @returns true if the type is string
|
||||
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
|
||||
*/
|
||||
simdjson_inline simdjson_result<bool> is_string() noexcept;
|
||||
|
||||
/**
|
||||
* Checks whether the document is a negative number.
|
||||
*
|
||||
@@ -457,9 +478,11 @@ public:
|
||||
* get_number().get_number_type().
|
||||
*
|
||||
* get_number_type() is number_type::unsigned_integer if we have
|
||||
* an integer greater or equal to 9223372036854775808
|
||||
* an integer greater or equal to 9223372036854775808 and no larger than 18446744073709551615.
|
||||
* get_number_type() is number_type::signed_integer if we have an
|
||||
* integer that is less than 9223372036854775808
|
||||
* integer that is less than 9223372036854775808 and greater or equal to -9223372036854775808.
|
||||
* get_number_type() is number_type::big_integer if we have an integer outside
|
||||
* of those ranges (either larger than 18446744073709551615 or smaller than -9223372036854775808).
|
||||
* Otherwise, get_number_type() has value number_type::floating_point_number
|
||||
*
|
||||
* This function requires processing the number string, but it is expected
|
||||
@@ -577,6 +600,14 @@ public:
|
||||
* auto doc = parser.iterate(json);
|
||||
* doc.at_pointer("//a/1") == 20
|
||||
*
|
||||
* Key values are matched exactly, without unescaping or Unicode normalization.
|
||||
* We do a byte-by-byte comparison. E.g.
|
||||
*
|
||||
* const padded_string json = "{\"\\u00E9\":123}"_padded;
|
||||
* auto doc = parser.iterate(json);
|
||||
* doc.at_pointer("/\\u00E9") == 123
|
||||
* doc.at_pointer((const char*)u8"/\u00E9") returns an error (NO_SUCH_FIELD)
|
||||
*
|
||||
* Note that at_pointer() automatically calls rewind between each call. Thus
|
||||
* all values, objects and arrays that you have created so far (including unescaped strings)
|
||||
* are invalidated. After calling at_pointer, you need to consume the result: string values
|
||||
@@ -593,6 +624,30 @@ public:
|
||||
* - SCALAR_DOCUMENT_AS_VALUE if the json_pointer is empty and the document is not a scalar (see is_scalar() function).
|
||||
*/
|
||||
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
|
||||
|
||||
/**
|
||||
* Get the value associated with the given JSONPath expression. We only support
|
||||
* JSONPath queries that trivially convertible to JSON Pointer queries: key
|
||||
* names and array indices.
|
||||
*
|
||||
* https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00
|
||||
*
|
||||
* Key values are matched exactly, without unescaping or Unicode normalization.
|
||||
* We do a byte-by-byte comparison. E.g.
|
||||
*
|
||||
* const padded_string json = "{\"\\u00E9\":123}"_padded;
|
||||
* auto doc = parser.iterate(json);
|
||||
* doc.at_path(".\\u00E9") == 123
|
||||
* doc.at_path((const char*)u8".\u00E9") returns an error (NO_SUCH_FIELD)
|
||||
*
|
||||
* @return The value associated with the given JSONPath expression, or:
|
||||
* - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails
|
||||
* - NO_SUCH_FIELD if a field does not exist in an object
|
||||
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
|
||||
* - INCORRECT_TYPE if a non-integer is used to access an array
|
||||
*/
|
||||
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
|
||||
|
||||
/**
|
||||
* Consumes the document and returns a string_view instance corresponding to the
|
||||
* document as represented in JSON. It points inside the original byte array containing
|
||||
@@ -658,10 +713,12 @@ public:
|
||||
simdjson_inline simdjson_result<value> get_value() noexcept;
|
||||
|
||||
simdjson_inline simdjson_result<bool> is_null() noexcept;
|
||||
template<typename T> simdjson_inline simdjson_result<T> get() & noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
|
||||
simdjson_inline operator document&() const noexcept;
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <class T>
|
||||
explicit simdjson_inline operator T() noexcept(false);
|
||||
simdjson_inline operator array() & noexcept(false);
|
||||
simdjson_inline operator object() & noexcept(false);
|
||||
simdjson_inline operator uint64_t() noexcept(false);
|
||||
@@ -686,6 +743,7 @@ public:
|
||||
|
||||
simdjson_inline simdjson_result<json_type> type() noexcept;
|
||||
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
|
||||
simdjson_inline simdjson_result<bool> is_string() noexcept;
|
||||
|
||||
simdjson_inline simdjson_result<const char *> current_location() noexcept;
|
||||
simdjson_inline int32_t current_depth() const noexcept;
|
||||
@@ -695,6 +753,8 @@ public:
|
||||
simdjson_inline simdjson_result<number> get_number() 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_path(std::string_view json_path) noexcept;
|
||||
|
||||
private:
|
||||
document *doc{nullptr};
|
||||
};
|
||||
@@ -734,8 +794,9 @@ public:
|
||||
|
||||
template<typename T> simdjson_inline error_code get(T &out) & noexcept;
|
||||
template<typename T> simdjson_inline error_code get(T &out) && noexcept;
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <class T, typename std::enable_if<std::is_same<T, SIMDJSON_IMPLEMENTATION::ondemand::document>::value == false>::type>
|
||||
explicit simdjson_inline operator T() noexcept(false);
|
||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
|
||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() & noexcept(false);
|
||||
simdjson_inline operator uint64_t() noexcept(false);
|
||||
@@ -759,6 +820,7 @@ public:
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(const char *key) & noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_type> type() noexcept;
|
||||
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
|
||||
simdjson_inline simdjson_result<bool> is_string() noexcept;
|
||||
simdjson_inline simdjson_result<const char *> current_location() noexcept;
|
||||
simdjson_inline int32_t current_depth() const noexcept;
|
||||
simdjson_inline bool at_end() const noexcept;
|
||||
@@ -770,6 +832,7 @@ public:
|
||||
simdjson_inline simdjson_result<std::string_view> raw_json_token() 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;
|
||||
};
|
||||
|
||||
|
||||
@@ -802,8 +865,9 @@ public:
|
||||
simdjson_inline simdjson_result<bool> get_bool() noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> get_value() noexcept;
|
||||
simdjson_inline simdjson_result<bool> is_null() noexcept;
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <class T, typename std::enable_if<std::is_same<T, SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::value == false>::type>
|
||||
explicit simdjson_inline operator T() noexcept(false);
|
||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
|
||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() & noexcept(false);
|
||||
simdjson_inline operator uint64_t() noexcept(false);
|
||||
@@ -827,6 +891,7 @@ public:
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(const char *key) & noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_type> type() noexcept;
|
||||
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
|
||||
simdjson_inline simdjson_result<bool> is_string() noexcept;
|
||||
simdjson_inline simdjson_result<const char *> current_location() noexcept;
|
||||
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
|
||||
simdjson_inline simdjson_result<bool> is_negative() noexcept;
|
||||
@@ -837,9 +902,10 @@ public:
|
||||
simdjson_inline simdjson_result<std::string_view> raw_json_token() 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;
|
||||
};
|
||||
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
|
||||
|
||||
@@ -342,9 +342,18 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
|
||||
depth--;
|
||||
break;
|
||||
default: // Scalar value document
|
||||
// TODO: Remove any trailing whitespaces
|
||||
// TODO: We could remove trailing whitespaces
|
||||
// This returns a string spanning from start of value to the beginning of the next document (excluded)
|
||||
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[++cur_struct_index] - current_index() - 1);
|
||||
{
|
||||
auto next_index = stream->parser->implementation->structural_indexes[++cur_struct_index];
|
||||
// normally the length would be next_index - current_index() - 1, except for the last document
|
||||
size_t svlen = next_index - current_index();
|
||||
const char *start = reinterpret_cast<const char*>(stream->buf) + current_index();
|
||||
while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0')) {
|
||||
svlen--;
|
||||
}
|
||||
return std::string_view(start, svlen);
|
||||
}
|
||||
}
|
||||
cur_struct_index++;
|
||||
}
|
||||
|
||||
@@ -122,10 +122,9 @@ public:
|
||||
class iterator {
|
||||
public:
|
||||
using value_type = simdjson_result<document>;
|
||||
using reference = value_type;
|
||||
|
||||
using reference = simdjson_result<ondemand::document_reference>;
|
||||
using pointer = void;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
/**
|
||||
@@ -135,7 +134,7 @@ public:
|
||||
/**
|
||||
* Get the current document (or error).
|
||||
*/
|
||||
simdjson_inline simdjson_result<ondemand::document_reference> operator*() noexcept;
|
||||
simdjson_inline reference operator*() noexcept;
|
||||
/**
|
||||
* Advance to the next document (prefix).
|
||||
*/
|
||||
@@ -335,4 +334,4 @@ public:
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace simdjson {
|
||||
namespace SIMDJSON_IMPLEMENTATION {
|
||||
namespace ondemand {
|
||||
|
||||
// clang 6 doesn't think the default constructor can be noexcept, so we make it explicit
|
||||
// clang 6 does not think the default constructor can be noexcept, so we make it explicit
|
||||
simdjson_inline field::field() noexcept : std::pair<raw_json_string, ondemand::value>() {}
|
||||
|
||||
simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept
|
||||
@@ -43,6 +43,19 @@ simdjson_inline raw_json_string field::key() const noexcept {
|
||||
return first;
|
||||
}
|
||||
|
||||
|
||||
simdjson_inline std::string_view field::key_raw_json_token() const noexcept {
|
||||
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
|
||||
return std::string_view(reinterpret_cast<const char*>(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1);
|
||||
}
|
||||
|
||||
simdjson_inline std::string_view field::escaped_key() const noexcept {
|
||||
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
|
||||
auto end_quote = second.iter._json_iter->token.peek(-1);
|
||||
while(*end_quote != '"') end_quote--;
|
||||
return std::string_view(reinterpret_cast<const char*>(first.buf), end_quote - first.buf);
|
||||
}
|
||||
|
||||
simdjson_inline value &field::value() & noexcept {
|
||||
return second;
|
||||
}
|
||||
@@ -76,10 +89,22 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_stri
|
||||
if (error()) { return error(); }
|
||||
return first.key();
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::field>::key_raw_json_token() noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.key_raw_json_token();
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::field>::escaped_key() noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.escaped_key();
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::field>::unescaped_key(bool allow_replacement) noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.unescaped_key(allow_replacement);
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::field>::value() noexcept {
|
||||
if (error()) { return error(); }
|
||||
return std::move(first.value());
|
||||
|
||||
@@ -42,6 +42,17 @@ public:
|
||||
* an unescaped C string: e.g., key() == "test".
|
||||
*/
|
||||
simdjson_inline raw_json_string key() const noexcept;
|
||||
/**
|
||||
* Get the unprocessed key as a string_view. This includes the quotes and may include
|
||||
* some spaces after the last quote.
|
||||
*/
|
||||
simdjson_inline std::string_view key_raw_json_token() const noexcept;
|
||||
/**
|
||||
* Get the key as a string_view. This does not include the quotes and
|
||||
* the string is unprocessed key so it may contain escape characters
|
||||
* (e.g., \uXXXX or \n). Use unescaped_key() to get the unescaped key.
|
||||
*/
|
||||
simdjson_inline std::string_view escaped_key() const noexcept;
|
||||
/**
|
||||
* Get the field value.
|
||||
*/
|
||||
@@ -74,6 +85,8 @@ public:
|
||||
|
||||
simdjson_inline simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> key() noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> key_raw_json_token() noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> escaped_key() noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> value() noexcept;
|
||||
};
|
||||
|
||||
|
||||
@@ -54,6 +54,23 @@ simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parse
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
||||
simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser, bool streaming) noexcept
|
||||
: token(buf, &_parser->implementation->structural_indexes[0]),
|
||||
parser{_parser},
|
||||
_string_buf_loc{parser->string_buf.get()},
|
||||
_depth{1},
|
||||
_root{parser->implementation->structural_indexes.get()},
|
||||
_streaming{streaming}
|
||||
|
||||
{
|
||||
logger::log_headers();
|
||||
#if SIMDJSON_CHECK_EOF
|
||||
assert_more_tokens();
|
||||
#endif
|
||||
}
|
||||
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
||||
|
||||
inline void json_iterator::rewind() noexcept {
|
||||
token.set_position( root_position() );
|
||||
logger::log_headers(); // We start again
|
||||
@@ -286,6 +303,12 @@ simdjson_inline uint32_t json_iterator::peek_length(token_position position) con
|
||||
#endif // SIMDJSON_CHECK_EOF
|
||||
return token.peek_length(position);
|
||||
}
|
||||
simdjson_inline uint32_t json_iterator::peek_root_length(token_position position) const noexcept {
|
||||
#if SIMDJSON_CHECK_EOF
|
||||
assert_valid_position(position);
|
||||
#endif // SIMDJSON_CHECK_EOF
|
||||
return token.peek_root_length(position);
|
||||
}
|
||||
|
||||
simdjson_inline token_position json_iterator::last_position() const noexcept {
|
||||
// The following line fails under some compilers...
|
||||
|
||||
@@ -146,7 +146,7 @@ public:
|
||||
* @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token.
|
||||
*
|
||||
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
|
||||
* it isn't used ...
|
||||
* it is not used ...
|
||||
*/
|
||||
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
|
||||
/**
|
||||
@@ -174,7 +174,7 @@ public:
|
||||
* @param position The position of the token to retrieve.
|
||||
*
|
||||
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
|
||||
* it isn't used ...
|
||||
* it is not used ...
|
||||
*/
|
||||
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
|
||||
/**
|
||||
@@ -185,13 +185,21 @@ public:
|
||||
* @param position The position of the token to retrieve.
|
||||
*/
|
||||
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
|
||||
/**
|
||||
* Get the maximum length of the JSON text for the current root token.
|
||||
*
|
||||
* The length will include any whitespace at the end of the token.
|
||||
*
|
||||
* @param position The position of the token to retrieve.
|
||||
*/
|
||||
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
|
||||
/**
|
||||
* Get the JSON text for the last token in the document.
|
||||
*
|
||||
* This is not null-terminated; it is a view into the JSON.
|
||||
*
|
||||
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
|
||||
* it isn't used ...
|
||||
* it is not used ...
|
||||
*/
|
||||
simdjson_inline const uint8_t *peek_last() const noexcept;
|
||||
|
||||
@@ -285,6 +293,9 @@ public:
|
||||
inline bool balanced() const noexcept;
|
||||
protected:
|
||||
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser) noexcept;
|
||||
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
||||
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser, bool streaming) noexcept;
|
||||
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
||||
/// The last token before the end
|
||||
simdjson_inline token_position last_position() const noexcept;
|
||||
/// The token *at* the end. This points at gibberish and should only be used for comparison.
|
||||
@@ -300,6 +311,7 @@ protected:
|
||||
friend class raw_json_string;
|
||||
friend class parser;
|
||||
friend class value_iterator;
|
||||
friend class field;
|
||||
template <typename... Args>
|
||||
friend simdjson_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept;
|
||||
template <typename... Args>
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SIMDJSON_ONDEMAND_GENERIC_JSON_PATH_TO_POINTER_CONVERSION_INL_H
|
||||
#define SIMDJSON_ONDEMAND_GENERIC_JSON_PATH_TO_POINTER_CONVERSION_INL_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#define SIMDJSON_GENERIC_ONDEMAND_JSON_PATH_TO_POINTER_CONVERSION_INL_H
|
||||
#include "simdjson/generic/ondemand/json_path_to_pointer_conversion.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
namespace simdjson {
|
||||
namespace SIMDJSON_IMPLEMENTATION {}
|
||||
namespace ondemand {
|
||||
|
||||
simdjson_inline std::string json_path_to_pointer_conversion(std::string_view json_path) {
|
||||
if (json_path.empty() || (json_path.front() != '.' && json_path.front() != '[') {
|
||||
return "-1"; // Sentinel value to be handled as an error by the caller.
|
||||
}
|
||||
|
||||
std::string result;
|
||||
// Reserve space to reduce allocations, adjusting for potential increases due
|
||||
// to escaping.
|
||||
result.reserve(json_path.size() * 2);
|
||||
|
||||
// Skip the initial '.' as it's assumed every path starts with it.
|
||||
size_t i = 0;
|
||||
|
||||
while (i < json_path.length()) {
|
||||
if (json_path[i] == '.') {
|
||||
result += '/';
|
||||
} else if (json_path[i] == '[') {
|
||||
result += '/';
|
||||
++i; // Move past the '['
|
||||
while (i < json_path.length() && json_path[i] != ']') {
|
||||
if (json_path[i] == '~') {
|
||||
result += "~0";
|
||||
} else if (json_path[i] == '/') {
|
||||
result += "~1";
|
||||
} else {
|
||||
result += json_path[i];
|
||||
}
|
||||
++i;
|
||||
}
|
||||
if (i == json_path.length() || json_path[i] != ']') {
|
||||
return "-1"; // Returning sentinel value that will be handled as an error by the caller
|
||||
}
|
||||
} else {
|
||||
if (json_path[i] == '~') {
|
||||
result += "~0";
|
||||
} else if (json_path[i] == '/') {
|
||||
result += "~1";
|
||||
} else {
|
||||
result += json_path[i];
|
||||
}
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
return simdjson_result<std::string>(result);
|
||||
}
|
||||
|
||||
|
||||
} // namespace ondemand
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_ONDEMAND_GENERIC_JSON_PATH_TO_POINTER_CONVERSION_INL_H
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SIMDJSON_ONDEMAND_GENERIC_JSON_PATH_TO_POINTER_CONVERSION_H
|
||||
#define SIMDJSON_ONDEMAND_GENERIC_JSON_PATH_TO_POINTER_CONVERSION_H
|
||||
|
||||
namespace simdjson {
|
||||
namespace SIMDJSON_IMPLEMENTATION {
|
||||
namespace internal {
|
||||
|
||||
/**
|
||||
* Converts JSONPath to JSON Pointer.
|
||||
* @param json_path The JSONPath string to be converted.
|
||||
* @return A string containing the equivalent JSON Pointer.
|
||||
* @throws simdjson_error If the conversion fails.
|
||||
*/
|
||||
simdjson_inline std::string json_path_to_pointer_conversion(std::string_view json_path);
|
||||
|
||||
} // namespace internal
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_JSON_PATH_TO_POINTER_CONVERSION_H
|
||||
@@ -48,7 +48,6 @@ simdjson_inline number::operator uint64_t() const noexcept {
|
||||
return get_uint64();
|
||||
}
|
||||
|
||||
|
||||
simdjson_inline bool number::is_int64() const noexcept {
|
||||
return get_number_type() == number_type::signed_integer;
|
||||
}
|
||||
|
||||
@@ -164,6 +164,14 @@ inline simdjson_result<value> object::at_pointer(std::string_view json_pointer)
|
||||
return child;
|
||||
}
|
||||
|
||||
inline simdjson_result<value> object::at_path(std::string_view json_path) noexcept {
|
||||
auto json_pointer = json_path_to_pointer_conversion(json_path);
|
||||
if (json_pointer == "-1") {
|
||||
return INVALID_JSON_POINTER;
|
||||
}
|
||||
return at_pointer(json_pointer);
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<size_t> object::count_fields() & noexcept {
|
||||
size_t count{0};
|
||||
// Important: we do not consume any of the values.
|
||||
@@ -236,6 +244,14 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
|
||||
return first.at_pointer(json_pointer);
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::at_path(
|
||||
std::string_view json_path) noexcept {
|
||||
if (error()) {
|
||||
return error();
|
||||
}
|
||||
return first.at_path(json_path);
|
||||
}
|
||||
|
||||
inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::reset() noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.reset();
|
||||
@@ -257,4 +273,4 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPL
|
||||
}
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
|
||||
|
||||
@@ -56,6 +56,8 @@ public:
|
||||
* key a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
|
||||
* is an error.
|
||||
*
|
||||
* If you expect to have keys with escape characters, please review our documentation.
|
||||
*
|
||||
* @param key The key to look up.
|
||||
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
|
||||
*/
|
||||
@@ -77,7 +79,7 @@ public:
|
||||
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
|
||||
*
|
||||
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
|
||||
* field wasn't there when they aren't).
|
||||
* field was not there when they are not in order).
|
||||
*
|
||||
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
|
||||
* that only one field is returned.
|
||||
@@ -93,6 +95,8 @@ public:
|
||||
* You are expected to access keys only once. You should access the value corresponding to a key
|
||||
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string() is an error.
|
||||
*
|
||||
* If you expect to have keys with escape characters, please review our documentation.
|
||||
*
|
||||
* @param key The key to look up.
|
||||
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
|
||||
*/
|
||||
@@ -140,6 +144,19 @@ public:
|
||||
*/
|
||||
inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
|
||||
|
||||
/**
|
||||
* Get the value associated with the given JSONPath expression. We only support
|
||||
* JSONPath queries that trivially convertible to JSON Pointer queries: key
|
||||
* names and array indices.
|
||||
*
|
||||
* @return The value associated with the given JSONPath expression, or:
|
||||
* - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails
|
||||
* - NO_SUCH_FIELD if a field does not exist in an object
|
||||
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
|
||||
* - INCORRECT_TYPE if a non-integer is used to access an array
|
||||
*/
|
||||
inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
|
||||
|
||||
/**
|
||||
* Reset the iterator so that we are pointing back at the
|
||||
* beginning of the object. You should still consume values only once even if you
|
||||
@@ -227,6 +244,8 @@ public:
|
||||
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_path(std::string_view json_path) noexcept;
|
||||
|
||||
inline simdjson_result<bool> reset() noexcept;
|
||||
inline simdjson_result<bool> is_empty() noexcept;
|
||||
inline simdjson_result<size_t> count_fields() & noexcept;
|
||||
@@ -236,4 +255,4 @@ public:
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
|
||||
|
||||
@@ -58,6 +58,27 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(p
|
||||
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this });
|
||||
}
|
||||
|
||||
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept {
|
||||
if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; }
|
||||
|
||||
json.remove_utf8_bom();
|
||||
|
||||
// Allocate if needed
|
||||
if (capacity() < json.length() || !string_buf) {
|
||||
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
|
||||
}
|
||||
|
||||
// Run stage 1.
|
||||
const simdjson::error_code err = implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular);
|
||||
if (err) {
|
||||
if (err != UNCLOSED_STRING)
|
||||
return err;
|
||||
}
|
||||
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this, true });
|
||||
}
|
||||
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
||||
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const char *json, size_t len, size_t allocated) & noexcept {
|
||||
return iterate(padded_string_view(json, len, allocated));
|
||||
}
|
||||
|
||||
@@ -98,6 +98,9 @@ public:
|
||||
* - UNCLOSED_STRING if there is an unclosed string in the document.
|
||||
*/
|
||||
simdjson_warn_unused simdjson_result<document> iterate(padded_string_view json) & noexcept;
|
||||
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
||||
simdjson_warn_unused simdjson_result<document> iterate_allow_incomplete_json(padded_string_view json) & noexcept;
|
||||
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
||||
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
|
||||
simdjson_warn_unused simdjson_result<document> iterate(const char *json, size_t len, size_t capacity) & noexcept;
|
||||
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
|
||||
|
||||
@@ -37,6 +37,11 @@ simdjson_inline uint32_t token_iterator::peek_length(token_position position) co
|
||||
return *(position+1) - *position;
|
||||
}
|
||||
|
||||
simdjson_inline uint32_t token_iterator::peek_root_length(token_position position) const noexcept {
|
||||
return *(position+2) - *(position) > *(position+1) - *(position) ?
|
||||
*(position+1) - *(position)
|
||||
: *(position+2) - *(position);
|
||||
}
|
||||
simdjson_inline const uint8_t *token_iterator::peek(int32_t delta) const noexcept {
|
||||
return &buf[*(_position+delta)];
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
* 1 = next token, -1 = prev token.
|
||||
*
|
||||
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
|
||||
* it isn't used ...
|
||||
* it is not used...
|
||||
*/
|
||||
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
|
||||
/**
|
||||
@@ -77,7 +77,14 @@ public:
|
||||
* @param position The position of the token.
|
||||
*/
|
||||
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
|
||||
|
||||
/**
|
||||
* Get the maximum length of the JSON text for a root token.
|
||||
*
|
||||
* The length will include any whitespace at the end of the token.
|
||||
*
|
||||
* @param position The position of the token (start of the document).
|
||||
*/
|
||||
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
|
||||
/**
|
||||
* Return the current index.
|
||||
*/
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "simdjson/generic/ondemand/array.h"
|
||||
#include "simdjson/generic/ondemand/array_iterator.h"
|
||||
#include "simdjson/generic/ondemand/json_iterator.h"
|
||||
#include "simdjson/generic/ondemand/json_path_to_pointer_conversion.h"
|
||||
#include "simdjson/generic/ondemand/json_path_to_pointer_conversion-inl.h"
|
||||
#include "simdjson/generic/ondemand/json_type.h"
|
||||
#include "simdjson/generic/ondemand/object.h"
|
||||
#include "simdjson/generic/ondemand/raw_json_string.h"
|
||||
@@ -93,6 +95,10 @@ template<typename T> simdjson_inline error_code value::get(T &out) noexcept {
|
||||
}
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <class T>
|
||||
simdjson_inline value::operator T() noexcept(false) {
|
||||
return get<T>();
|
||||
}
|
||||
simdjson_inline value::operator array() noexcept(false) {
|
||||
return get_array();
|
||||
}
|
||||
@@ -179,6 +185,14 @@ simdjson_inline simdjson_result<bool> value::is_scalar() noexcept {
|
||||
return ! ((this_type == json_type::array) || (this_type == json_type::object));
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<bool> value::is_string() noexcept {
|
||||
json_type this_type;
|
||||
auto error = type().get(this_type);
|
||||
if(error) { return error; }
|
||||
return (this_type == json_type::string);
|
||||
}
|
||||
|
||||
|
||||
simdjson_inline bool value::is_negative() noexcept {
|
||||
return iter.is_negative();
|
||||
}
|
||||
@@ -225,6 +239,26 @@ simdjson_inline int32_t value::current_depth() const noexcept{
|
||||
return iter.json_iter().depth();
|
||||
}
|
||||
|
||||
inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept {
|
||||
if (simdjson_unlikely(json_pointer.empty())) { // can't be
|
||||
return false;
|
||||
}
|
||||
if (simdjson_unlikely(json_pointer[0] != '/')) {
|
||||
return false;
|
||||
}
|
||||
size_t escape = json_pointer.find('~');
|
||||
if (escape == std::string_view::npos) {
|
||||
return true;
|
||||
}
|
||||
if (escape == json_pointer.size() - 1) {
|
||||
return false;
|
||||
}
|
||||
if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<value> value::at_pointer(std::string_view json_pointer) noexcept {
|
||||
json_type t;
|
||||
SIMDJSON_TRY(type().get(t));
|
||||
@@ -235,6 +269,23 @@ simdjson_inline simdjson_result<value> value::at_pointer(std::string_view json_p
|
||||
case json_type::object:
|
||||
return (*this).get_object().at_pointer(json_pointer);
|
||||
default:
|
||||
// a non-empty string can be invalid, or accessing a primitive (issue 2154)
|
||||
if (is_pointer_well_formed(json_pointer)) {
|
||||
return NO_SUCH_FIELD;
|
||||
}
|
||||
return INVALID_JSON_POINTER;
|
||||
}
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<value> value::at_path(std::string_view json_path) noexcept {
|
||||
json_type t;
|
||||
SIMDJSON_TRY(type().get(t));
|
||||
switch (t) {
|
||||
case json_type::array:
|
||||
return (*this).get_array().at_path(json_path);
|
||||
case json_type::object:
|
||||
return (*this).get_object().at_path(json_path);
|
||||
default:
|
||||
return INVALID_JSON_POINTER;
|
||||
}
|
||||
}
|
||||
@@ -392,6 +443,10 @@ simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::o
|
||||
if (error()) { return error(); }
|
||||
return first.is_scalar();
|
||||
}
|
||||
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::is_string() noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.is_string();
|
||||
}
|
||||
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::is_negative() noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.is_negative();
|
||||
@@ -409,6 +464,11 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number> simdj
|
||||
return first.get_number();
|
||||
}
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <class T>
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator T() noexcept(false) {
|
||||
if (error()) { throw simdjson_error(error()); }
|
||||
return static_cast<T>(first);
|
||||
}
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator SIMDJSON_IMPLEMENTATION::ondemand::array() noexcept(false) {
|
||||
if (error()) { throw simdjson_error(error()); }
|
||||
return first;
|
||||
@@ -463,11 +523,22 @@ simdjson_inline simdjson_result<int32_t> simdjson_result<SIMDJSON_IMPLEMENTATION
|
||||
return first.current_depth();
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::at_pointer(std::string_view json_pointer) noexcept {
|
||||
if (error()) { return error(); }
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::at_pointer(
|
||||
std::string_view json_pointer) noexcept {
|
||||
if (error()) {
|
||||
return error();
|
||||
}
|
||||
return first.at_pointer(json_pointer);
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::at_path(
|
||||
std::string_view json_path) noexcept {
|
||||
if (error()) {
|
||||
return error();
|
||||
}
|
||||
return first.at_path(json_path);
|
||||
}
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
|
||||
|
||||
@@ -36,12 +36,13 @@ public:
|
||||
* @returns INCORRECT_TYPE If the JSON value is not the given type.
|
||||
*/
|
||||
template<typename T> simdjson_inline simdjson_result<T> get() noexcept {
|
||||
// Unless the simdjson library provides an inline implementation, calling this method should
|
||||
// Unless the simdjson library or the user provides an inline implementation, calling this method should
|
||||
// immediately fail.
|
||||
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
|
||||
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
|
||||
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
|
||||
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template.");
|
||||
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
|
||||
" You may also add support for custom types, see our documentation.");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,6 +195,17 @@ public:
|
||||
simdjson_inline simdjson_result<bool> is_null() noexcept;
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
/**
|
||||
* Cast this JSON value to an instance of type T. The programmer is responsible for
|
||||
* providing an implementation of get<T> for the type T, if T is not one of the types
|
||||
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.).
|
||||
*
|
||||
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
|
||||
*
|
||||
* @returns An instance of type T
|
||||
*/
|
||||
template <class T>
|
||||
explicit simdjson_inline operator T() noexcept(false);
|
||||
/**
|
||||
* Cast this JSON value to an array.
|
||||
*
|
||||
@@ -357,7 +369,7 @@ public:
|
||||
* that only one field is returned.
|
||||
*
|
||||
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
|
||||
* field wasn't there when they aren't).
|
||||
* field as not there when they are not in order).
|
||||
*
|
||||
* @param key The key to look up.
|
||||
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
|
||||
@@ -393,6 +405,13 @@ public:
|
||||
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
|
||||
*/
|
||||
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
|
||||
/**
|
||||
* Checks whether the value is a string.
|
||||
*
|
||||
* @returns true if the type is string
|
||||
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
|
||||
*/
|
||||
simdjson_inline simdjson_result<bool> is_string() noexcept;
|
||||
|
||||
/**
|
||||
* Checks whether the value is a negative number.
|
||||
@@ -425,10 +444,12 @@ public:
|
||||
* get_number().get_number_type().
|
||||
*
|
||||
* get_number_type() is number_type::unsigned_integer if we have
|
||||
* an integer greater or equal to 9223372036854775808
|
||||
* an integer greater or equal to 9223372036854775808.
|
||||
* get_number_type() is number_type::signed_integer if we have an
|
||||
* integer that is less than 9223372036854775808
|
||||
* Otherwise, get_number_type() has value number_type::floating_point_number
|
||||
* integer that is less than 9223372036854775808.
|
||||
* get_number_type() is number_type::big_integer for integers that do not fit in 64 bits,
|
||||
* in which case the digit_count is set to the length of the big integer string.
|
||||
* Otherwise, get_number_type() has value number_type::floating_point_number.
|
||||
*
|
||||
* This function requires processing the number string, but it is expected
|
||||
* to be faster than get_number().get_number_type() because it is does not
|
||||
@@ -455,6 +476,8 @@ public:
|
||||
* You can recover the value by calling number.get_uint64() and you
|
||||
* have that number.is_uint64() is true.
|
||||
*
|
||||
* For integers that do not fit in 64 bits, the function returns BIGINT_ERROR error code.
|
||||
*
|
||||
* Otherwise, number.get_number_type() has value number_type::floating_point_number
|
||||
* and we have a binary64 number.
|
||||
* You can recover the value by calling number.get_double() and you
|
||||
@@ -470,7 +493,6 @@ public:
|
||||
*/
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
|
||||
|
||||
|
||||
/**
|
||||
* Get the raw JSON for this token.
|
||||
*
|
||||
@@ -568,6 +590,20 @@ public:
|
||||
*/
|
||||
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
|
||||
|
||||
/**
|
||||
* Get the value associated with the given JSONPath expression. We only support
|
||||
* JSONPath queries that trivially convertible to JSON Pointer queries: key
|
||||
* names and array indices.
|
||||
*
|
||||
* @return The value associated with the given JSONPath expression, or:
|
||||
* - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails
|
||||
* - NO_SUCH_FIELD if a field does not exist in an object
|
||||
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
|
||||
* - INCORRECT_TYPE if a non-integer is used to access an array
|
||||
*/
|
||||
simdjson_inline simdjson_result<value> at_path(std::string_view at_path) noexcept;
|
||||
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Create a value.
|
||||
@@ -607,6 +643,7 @@ protected:
|
||||
friend class object;
|
||||
friend struct simdjson_result<value>;
|
||||
friend struct simdjson_result<field>;
|
||||
friend class field;
|
||||
};
|
||||
|
||||
} // namespace ondemand
|
||||
@@ -644,6 +681,8 @@ public:
|
||||
template<typename T> simdjson_inline error_code get(T &out) noexcept;
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <class T>
|
||||
explicit simdjson_inline operator T() noexcept(false);
|
||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() noexcept(false);
|
||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() noexcept(false);
|
||||
simdjson_inline operator uint64_t() noexcept(false);
|
||||
@@ -697,7 +736,7 @@ public:
|
||||
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
|
||||
*
|
||||
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
|
||||
* field wasn't there when they aren't).
|
||||
* field as not there when they are not in order).
|
||||
*
|
||||
* @param key The key to look up.
|
||||
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
|
||||
@@ -719,6 +758,7 @@ public:
|
||||
*/
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_type> type() noexcept;
|
||||
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
|
||||
simdjson_inline simdjson_result<bool> is_string() noexcept;
|
||||
simdjson_inline simdjson_result<bool> is_negative() noexcept;
|
||||
simdjson_inline simdjson_result<bool> is_integer() noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::number_type> get_number_type() noexcept;
|
||||
@@ -733,8 +773,9 @@ public:
|
||||
/** @copydoc simdjson_inline 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_path(std::string_view json_path) noexcept;
|
||||
};
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_H
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_H
|
||||
|
||||
@@ -137,7 +137,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_
|
||||
} else if (!is_open()) {
|
||||
#if SIMDJSON_DEVELOPMENT_CHECKS
|
||||
// If we're past the end of the object, we're being iterated out of order.
|
||||
// Note: this isn't perfect detection. It's possible the user is inside some other object; if so,
|
||||
// Note: this is not perfect detection. It's possible the user is inside some other object; if so,
|
||||
// this object iterator will blithely scan that object for fields.
|
||||
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
|
||||
#endif
|
||||
@@ -249,7 +249,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_
|
||||
|
||||
#if SIMDJSON_DEVELOPMENT_CHECKS
|
||||
// If we're past the end of the object, we're being iterated out of order.
|
||||
// Note: this isn't perfect detection. It's possible the user is inside some other object; if so,
|
||||
// Note: this is not perfect detection. It's possible the user is inside some other object; if so,
|
||||
// this object iterator will blithely scan that object for fields.
|
||||
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
|
||||
#endif
|
||||
@@ -591,7 +591,7 @@ simdjson_inline simdjson_result<number> value_iterator::get_number() noexcept {
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<bool> value_iterator::is_root_integer(bool check_trailing) noexcept {
|
||||
auto max_len = peek_start_length();
|
||||
auto max_len = peek_root_length();
|
||||
auto json = peek_root_scalar("is_root_integer");
|
||||
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
|
||||
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
|
||||
@@ -607,7 +607,7 @@ simdjson_inline simdjson_result<bool> value_iterator::is_root_integer(bool check
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::number_type> value_iterator::get_root_number_type(bool check_trailing) noexcept {
|
||||
auto max_len = peek_start_length();
|
||||
auto max_len = peek_root_length();
|
||||
auto json = peek_root_scalar("number");
|
||||
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
|
||||
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
|
||||
@@ -615,7 +615,12 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::number_type> value_iter
|
||||
uint8_t tmpbuf[1074+8+1+1];
|
||||
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
|
||||
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
|
||||
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
|
||||
if(numberparsing::check_if_integer(json, max_len)) {
|
||||
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
|
||||
logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
|
||||
return number_type::big_integer;
|
||||
}
|
||||
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
|
||||
return NUMBER_ERROR;
|
||||
}
|
||||
auto answer = numberparsing::get_number_type(tmpbuf);
|
||||
@@ -623,15 +628,21 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::number_type> value_iter
|
||||
return answer;
|
||||
}
|
||||
simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool check_trailing) noexcept {
|
||||
auto max_len = peek_start_length();
|
||||
auto max_len = peek_root_length();
|
||||
auto json = peek_root_scalar("number");
|
||||
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
|
||||
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
|
||||
// number: -0.<fraction>e-308.
|
||||
// NOTE: the current approach doesn't work for very big integer numbers containing more than 1074 digits.
|
||||
uint8_t tmpbuf[1074+8+1+1];
|
||||
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
|
||||
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
|
||||
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
|
||||
if(numberparsing::check_if_integer(json, max_len)) {
|
||||
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
|
||||
logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
|
||||
return BIGINT_ERROR;
|
||||
}
|
||||
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
|
||||
return NUMBER_ERROR;
|
||||
}
|
||||
number num;
|
||||
@@ -663,7 +674,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iter
|
||||
return raw_json_string(json+1);
|
||||
}
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64(bool check_trailing) noexcept {
|
||||
auto max_len = peek_start_length();
|
||||
auto max_len = peek_root_length();
|
||||
auto json = peek_root_scalar("uint64");
|
||||
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
|
||||
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
|
||||
@@ -679,7 +690,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::g
|
||||
return result;
|
||||
}
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept {
|
||||
auto max_len = peek_start_length();
|
||||
auto max_len = peek_root_length();
|
||||
auto json = peek_root_scalar("uint64");
|
||||
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
|
||||
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
|
||||
@@ -695,7 +706,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::g
|
||||
return result;
|
||||
}
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64(bool check_trailing) noexcept {
|
||||
auto max_len = peek_start_length();
|
||||
auto max_len = peek_root_length();
|
||||
auto json = peek_root_scalar("int64");
|
||||
uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
|
||||
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
|
||||
@@ -712,7 +723,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::ge
|
||||
return result;
|
||||
}
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string(bool check_trailing) noexcept {
|
||||
auto max_len = peek_start_length();
|
||||
auto max_len = peek_root_length();
|
||||
auto json = peek_root_scalar("int64");
|
||||
uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
|
||||
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
|
||||
@@ -729,7 +740,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::ge
|
||||
return result;
|
||||
}
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double(bool check_trailing) noexcept {
|
||||
auto max_len = peek_start_length();
|
||||
auto max_len = peek_root_length();
|
||||
auto json = peek_root_scalar("double");
|
||||
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
|
||||
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
|
||||
@@ -749,7 +760,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
|
||||
}
|
||||
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double_in_string(bool check_trailing) noexcept {
|
||||
auto max_len = peek_start_length();
|
||||
auto max_len = peek_root_length();
|
||||
auto json = peek_root_scalar("double");
|
||||
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
|
||||
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
|
||||
@@ -768,7 +779,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
|
||||
return result;
|
||||
}
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_root_bool(bool check_trailing) noexcept {
|
||||
auto max_len = peek_start_length();
|
||||
auto max_len = peek_root_length();
|
||||
auto json = peek_root_scalar("bool");
|
||||
uint8_t tmpbuf[5+1+1]; // +1 for null termination
|
||||
tmpbuf[5+1] = '\0'; // make sure that buffer is always null terminated.
|
||||
@@ -781,7 +792,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_r
|
||||
return result;
|
||||
}
|
||||
simdjson_inline simdjson_result<bool> value_iterator::is_root_null(bool check_trailing) noexcept {
|
||||
auto max_len = peek_start_length();
|
||||
auto max_len = peek_root_length();
|
||||
auto json = peek_root_scalar("null");
|
||||
bool result = (max_len >= 4 && !atomparsing::str4ncmp(json, "null") &&
|
||||
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
|
||||
@@ -853,6 +864,9 @@ simdjson_inline const uint8_t *value_iterator::peek_start() const noexcept {
|
||||
simdjson_inline uint32_t value_iterator::peek_start_length() const noexcept {
|
||||
return _json_iter->peek_length(start_position());
|
||||
}
|
||||
simdjson_inline uint32_t value_iterator::peek_root_length() const noexcept {
|
||||
return _json_iter->peek_root_length(start_position());
|
||||
}
|
||||
|
||||
simdjson_inline const uint8_t *value_iterator::peek_scalar(const char *type) noexcept {
|
||||
logger::log_value(*_json_iter, start_position(), depth(), type);
|
||||
|
||||
@@ -375,6 +375,7 @@ protected:
|
||||
simdjson_inline simdjson_result<bool> parse_bool(const uint8_t *json) const noexcept;
|
||||
simdjson_inline const uint8_t *peek_start() const noexcept;
|
||||
simdjson_inline uint32_t peek_start_length() const noexcept;
|
||||
simdjson_inline uint32_t peek_root_length() const noexcept;
|
||||
|
||||
/**
|
||||
* The general idea of the advance_... methods and the peek_* methods
|
||||
@@ -469,6 +470,7 @@ protected:
|
||||
friend class object;
|
||||
friend class array;
|
||||
friend class value;
|
||||
friend class field;
|
||||
}; // value_iterator
|
||||
|
||||
} // namespace ondemand
|
||||
|
||||
@@ -37,13 +37,13 @@ static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars
|
||||
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
|
||||
internal::value128 answer;
|
||||
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
|
||||
#ifdef _M_ARM64
|
||||
#if SIMDJSON_IS_ARM64
|
||||
// ARM64 has native support for 64-bit multiplications, no need to emultate
|
||||
answer.high = __umulh(value1, value2);
|
||||
answer.low = value1 * value2;
|
||||
#else
|
||||
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
|
||||
#endif // _M_ARM64
|
||||
#endif // SIMDJSON_IS_ARM64
|
||||
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
|
||||
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
|
||||
answer.low = uint64_t(r);
|
||||
|
||||
@@ -65,10 +65,10 @@ namespace simd {
|
||||
struct simd8<bool>: base8<bool> {
|
||||
static simdjson_inline simd8<bool> splat(bool _value) { return _mm256_set1_epi8(uint8_t(-(!!_value))); }
|
||||
|
||||
simdjson_inline simd8<bool>() : base8() {}
|
||||
simdjson_inline simd8<bool>(const __m256i _value) : base8<bool>(_value) {}
|
||||
simdjson_inline simd8() : base8() {}
|
||||
simdjson_inline simd8(const __m256i _value) : base8<bool>(_value) {}
|
||||
// Splat constructor
|
||||
simdjson_inline simd8<bool>(bool _value) : base8<bool>(splat(_value)) {}
|
||||
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
|
||||
|
||||
simdjson_inline int to_bitmask() const { return _mm256_movemask_epi8(*this); }
|
||||
simdjson_inline bool any() const { return !_mm256_testz_si256(*this, *this); }
|
||||
|
||||
@@ -33,13 +33,13 @@ static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars
|
||||
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
|
||||
internal::value128 answer;
|
||||
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
|
||||
#ifdef _M_ARM64
|
||||
#if SIMDJSON_IS_ARM64
|
||||
// ARM64 has native support for 64-bit multiplications, no need to emultate
|
||||
answer.high = __umulh(value1, value2);
|
||||
answer.low = value1 * value2;
|
||||
#else
|
||||
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
|
||||
#endif // _M_ARM64
|
||||
#endif // SIMDJSON_IS_ARM64
|
||||
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
|
||||
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
|
||||
answer.low = uint64_t(r);
|
||||
|
||||
@@ -93,10 +93,10 @@ namespace simd {
|
||||
struct simd8<bool>: base8<bool> {
|
||||
static simdjson_inline simd8<bool> splat(bool _value) { return _mm512_set1_epi8(uint8_t(-(!!_value))); }
|
||||
|
||||
simdjson_inline simd8<bool>() : base8() {}
|
||||
simdjson_inline simd8<bool>(const __m512i _value) : base8<bool>(_value) {}
|
||||
simdjson_inline simd8() : base8() {}
|
||||
simdjson_inline simd8(const __m512i _value) : base8<bool>(_value) {}
|
||||
// Splat constructor
|
||||
simdjson_inline simd8<bool>(bool _value) : base8<bool>(splat(_value)) {}
|
||||
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
|
||||
simdjson_inline bool any() const { return !!_mm512_test_epi8_mask (*this, *this); }
|
||||
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
|
||||
};
|
||||
|
||||
@@ -53,7 +53,7 @@ public:
|
||||
*
|
||||
* @return the name of the implementation, e.g. "haswell", "westmere", "arm64".
|
||||
*/
|
||||
virtual const std::string &name() const { return _name; }
|
||||
virtual std::string name() const { return std::string(_name); }
|
||||
|
||||
/**
|
||||
* The description of this implementation.
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
*
|
||||
* @return the description of the implementation, e.g. "Intel/AMD AVX2", "Intel/AMD SSE4.2", "ARM NEON".
|
||||
*/
|
||||
virtual const std::string &description() const { return _description; }
|
||||
virtual std::string description() const { return std::string(_description); }
|
||||
|
||||
/**
|
||||
* The instruction sets this implementation is compiled against
|
||||
@@ -139,18 +139,19 @@ protected:
|
||||
_required_instruction_sets(required_instruction_sets)
|
||||
{
|
||||
}
|
||||
virtual ~implementation()=default;
|
||||
protected:
|
||||
~implementation() = default;
|
||||
|
||||
private:
|
||||
/**
|
||||
* The name of this implementation.
|
||||
*/
|
||||
const std::string _name;
|
||||
std::string_view _name;
|
||||
|
||||
/**
|
||||
* The description of this implementation.
|
||||
*/
|
||||
const std::string _description;
|
||||
std::string_view _description;
|
||||
|
||||
/**
|
||||
* Instruction sets required for this implementation.
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#define SIMDJSON_IMPLEMENTATION_ID_icelake 4
|
||||
#define SIMDJSON_IMPLEMENTATION_ID_ppc64 5
|
||||
#define SIMDJSON_IMPLEMENTATION_ID_westmere 6
|
||||
#define SIMDJSON_IMPLEMENTATION_ID_lsx 7
|
||||
#define SIMDJSON_IMPLEMENTATION_ID_lasx 8
|
||||
|
||||
#define SIMDJSON_IMPLEMENTATION_ID_FOR(IMPL) SIMDJSON_CAT(SIMDJSON_IMPLEMENTATION_ID_, IMPL)
|
||||
#define SIMDJSON_IMPLEMENTATION_ID SIMDJSON_IMPLEMENTATION_ID_FOR(SIMDJSON_IMPLEMENTATION)
|
||||
@@ -24,7 +26,11 @@
|
||||
#ifndef SIMDJSON_IMPLEMENTATION_ARM64
|
||||
#define SIMDJSON_IMPLEMENTATION_ARM64 (SIMDJSON_IS_ARM64)
|
||||
#endif
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_ARM64 SIMDJSON_IMPLEMENTATION_ARM64 && SIMDJSON_IS_ARM64
|
||||
#if SIMDJSON_IMPLEMENTATION_ARM64 && SIMDJSON_IS_ARM64
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_ARM64 1
|
||||
#else
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_ARM64 0
|
||||
#endif
|
||||
|
||||
// Default Icelake to on if this is x86-64. Even if we're not compiled for it, it could be selected
|
||||
// at runtime.
|
||||
@@ -35,9 +41,20 @@
|
||||
#ifdef _MSC_VER
|
||||
// To see why (__BMI__) && (__PCLMUL__) && (__LZCNT__) are not part of this next line, see
|
||||
// https://github.com/simdjson/simdjson/issues/1247
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE ((SIMDJSON_IMPLEMENTATION_ICELAKE) && (__AVX2__) && (__AVX512F__) && (__AVX512DQ__) && (__AVX512CD__) && (__AVX512BW__) && (__AVX512VL__) && (__AVX512VBMI2__))
|
||||
#if ((SIMDJSON_IMPLEMENTATION_ICELAKE) && (__AVX2__) && (__AVX512F__) && (__AVX512DQ__) && (__AVX512CD__) && (__AVX512BW__) && (__AVX512VL__) && (__AVX512VBMI2__))
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE 1
|
||||
#else
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE ((SIMDJSON_IMPLEMENTATION_ICELAKE) && (__AVX2__) && (__BMI__) && (__PCLMUL__) && (__LZCNT__) && (__AVX512F__) && (__AVX512DQ__) && (__AVX512CD__) && (__AVX512BW__) && (__AVX512VL__) && (__AVX512VBMI2__))
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE 0
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#if ((SIMDJSON_IMPLEMENTATION_ICELAKE) && (__AVX2__) && (__BMI__) && (__PCLMUL__) && (__LZCNT__) && (__AVX512F__) && (__AVX512DQ__) && (__AVX512CD__) && (__AVX512BW__) && (__AVX512VL__) && (__AVX512VBMI2__))
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE 1
|
||||
#else
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE 0
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// Default Haswell to on if this is x86-64. Even if we're not compiled for it, it could be selected
|
||||
@@ -53,9 +70,20 @@
|
||||
#ifdef _MSC_VER
|
||||
// To see why (__BMI__) && (__PCLMUL__) && (__LZCNT__) are not part of this next line, see
|
||||
// https://github.com/simdjson/simdjson/issues/1247
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__))
|
||||
#if ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__))
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL 1
|
||||
#else
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__) && (__BMI__) && (__PCLMUL__) && (__LZCNT__))
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL 0
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#if ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__) && (__BMI__) && (__PCLMUL__) && (__LZCNT__))
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL 1
|
||||
#else
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL 0
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// Default Westmere to on if this is x86-64.
|
||||
@@ -67,16 +95,40 @@
|
||||
#define SIMDJSON_IMPLEMENTATION_WESTMERE SIMDJSON_IS_X86_64
|
||||
#endif
|
||||
#endif
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_WESTMERE (SIMDJSON_IMPLEMENTATION_WESTMERE && SIMDJSON_IS_X86_64 && __SSE4_2__ && __PCLMUL__)
|
||||
|
||||
#if (SIMDJSON_IMPLEMENTATION_WESTMERE && SIMDJSON_IS_X86_64 && __SSE4_2__ && __PCLMUL__)
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_WESTMERE 1
|
||||
#else
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_WESTMERE 0
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SIMDJSON_IMPLEMENTATION_PPC64
|
||||
#define SIMDJSON_IMPLEMENTATION_PPC64 (SIMDJSON_IS_PPC64 && SIMDJSON_IS_PPC64_VMX)
|
||||
#endif
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_PPC64 SIMDJSON_IMPLEMENTATION_PPC64 && SIMDJSON_IS_PPC64 && SIMDJSON_IS_PPC64_VMX
|
||||
#if SIMDJSON_IMPLEMENTATION_PPC64 && SIMDJSON_IS_PPC64 && SIMDJSON_IS_PPC64_VMX
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_PPC64 1
|
||||
#else
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_PPC64 0
|
||||
#endif
|
||||
|
||||
#ifndef SIMDJSON_IMPLEMENTATION_LASX
|
||||
#define SIMDJSON_IMPLEMENTATION_LASX (SIMDJSON_IS_LOONGARCH64 && __loongarch_asx)
|
||||
#endif
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_LASX (SIMDJSON_IMPLEMENTATION_LASX)
|
||||
|
||||
#ifndef SIMDJSON_IMPLEMENTATION_LSX
|
||||
#if SIMDJSON_CAN_ALWAYS_RUN_LASX
|
||||
#define SIMDJSON_IMPLEMENTATION_LSX 0
|
||||
#else
|
||||
#define SIMDJSON_IMPLEMENTATION_LSX (SIMDJSON_IS_LOONGARCH64 && __loongarch_sx)
|
||||
#endif
|
||||
#endif
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_LSX (SIMDJSON_IMPLEMENTATION_LSX)
|
||||
|
||||
// Default Fallback to on unless a builtin implementation has already been selected.
|
||||
#ifndef SIMDJSON_IMPLEMENTATION_FALLBACK
|
||||
#if SIMDJSON_CAN_ALWAYS_RUN_ARM64 || SIMDJSON_CAN_ALWAYS_RUN_ICELAKE || SIMDJSON_CAN_ALWAYS_RUN_HASWELL || SIMDJSON_CAN_ALWAYS_RUN_WESTMERE || SIMDJSON_CAN_ALWAYS_RUN_PPC64
|
||||
#if SIMDJSON_CAN_ALWAYS_RUN_ARM64 || SIMDJSON_CAN_ALWAYS_RUN_ICELAKE || SIMDJSON_CAN_ALWAYS_RUN_HASWELL || SIMDJSON_CAN_ALWAYS_RUN_WESTMERE || SIMDJSON_CAN_ALWAYS_RUN_PPC64 || SIMDJSON_CAN_ALWAYS_RUN_LSX || SIMDJSON_CAN_ALWAYS_RUN_LASX
|
||||
// if anything at all except fallback can always run, then disable fallback.
|
||||
#define SIMDJSON_IMPLEMENTATION_FALLBACK 0
|
||||
#else
|
||||
@@ -98,6 +150,10 @@
|
||||
#define SIMDJSON_BUILTIN_IMPLEMENTATION arm64
|
||||
#elif SIMDJSON_CAN_ALWAYS_RUN_PPC64
|
||||
#define SIMDJSON_BUILTIN_IMPLEMENTATION ppc64
|
||||
#elif SIMDJSON_CAN_ALWAYS_RUN_LSX
|
||||
#define SIMDJSON_BUILTIN_IMPLEMENTATION lsx
|
||||
#elif SIMDJSON_CAN_ALWAYS_RUN_LASX
|
||||
#define SIMDJSON_BUILTIN_IMPLEMENTATION lasx
|
||||
#elif SIMDJSON_CAN_ALWAYS_RUN_FALLBACK
|
||||
#define SIMDJSON_BUILTIN_IMPLEMENTATION fallback
|
||||
#else
|
||||
|
||||
@@ -66,7 +66,9 @@ enum instruction_set {
|
||||
AVX512CD = 0x2000,
|
||||
AVX512BW = 0x4000,
|
||||
AVX512VL = 0x8000,
|
||||
AVX512VBMI2 = 0x10000
|
||||
AVX512VBMI2 = 0x10000,
|
||||
LSX = 0x20000,
|
||||
LASX = 0x40000,
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef SIMDJSON_LASX_H
|
||||
#define SIMDJSON_LASX_H
|
||||
|
||||
#include "simdjson/lasx/begin.h"
|
||||
#include "simdjson/generic/amalgamated.h"
|
||||
#include "simdjson/lasx/end.h"
|
||||
|
||||
#endif // SIMDJSON_LASX_H
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef SIMDJSON_LASX_BASE_H
|
||||
#define SIMDJSON_LASX_BASE_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/base.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
namespace simdjson {
|
||||
/**
|
||||
* Implementation for LASX.
|
||||
*/
|
||||
namespace lasx {
|
||||
|
||||
class implementation;
|
||||
|
||||
namespace {
|
||||
namespace simd {
|
||||
template <typename T> struct simd8;
|
||||
template <typename T> struct simd8x64;
|
||||
} // namespace simd
|
||||
} // unnamed namespace
|
||||
|
||||
} // namespace lasx
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_LASX_BASE_H
|
||||
@@ -0,0 +1,10 @@
|
||||
#define SIMDJSON_IMPLEMENTATION lasx
|
||||
#include "simdjson/lasx/base.h"
|
||||
#include "simdjson/lasx/intrinsics.h"
|
||||
#include "simdjson/lasx/bitmanipulation.h"
|
||||
#include "simdjson/lasx/bitmask.h"
|
||||
#include "simdjson/lasx/numberparsing_defs.h"
|
||||
#include "simdjson/lasx/simd.h"
|
||||
#include "simdjson/lasx/stringparsing_defs.h"
|
||||
|
||||
#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user