Compare commits

...

102 Commits

Author SHA1 Message Date
Daniel Lemire ccf8694510 v3.10.0 2024-08-01 09:32:54 -04:00
Daniel Lemire 9b67497ed0 Allows field::unescape_key to take in a string parameter + additional dev. checks for string overflow (#2224)
* This PR does the following:

1. Upgrade cxxopts.
2. Allows field::unescape_key to take in a string parameter (syntaxic sugar).
3. Adds a dev. check to detect a string buffer overflow (indicating broken code). Note that this is unrecoverable and indicates bad code.

* tweak
2024-08-01 09:31:50 -04:00
Daniel Lemire 0336684df7 [no-ci] Update basics.md 2024-07-31 11:10:01 -04:00
Daniel Lemire c19320dd6e making it more precise 2024-07-31 10:08:04 -04:00
Daniel Lemire 412a5680e8 update 2024-07-31 10:06:47 -04:00
Daniel Lemire a05a56856d fix: use On-Demand throughout. (#2222) 2024-07-29 15:54:21 -04:00
didarpin 58173a6a1f Added the functionality to convert dom::object and dom::array to dom::element. (#2221)
Co-authored-by: didarpin <didarpin@163.com>
2024-07-25 22:26:20 -04:00
Francisco Geiman Thiesen 1721032cfd Merge pull request #2220 from simdjson/adding_macros_for_cpp20_cpp23
fix: add macros to detected C++20 and C++23
2024-07-25 17:44:36 -07:00
Daniel Lemire b73877f95e Update compiler_check.h 2024-07-23 14:25:27 -04:00
Daniel Lemire 09723897e9 fix: add macros to detected C++20 and C++23 2024-07-23 14:23:00 -04:00
Daniel Lemire 49e231b634 [no-ci] Update README.md 2024-07-16 16:05:32 -04:00
Daniel Lemire acdbbab916 adding an example of value capture with std::string_view (#2216)
* adding an example of value capture with std::string_view

* minor fix

* minor fix
2024-07-16 15:51:43 -04:00
Daniel Lemire 5090247c34 adding test (#2214) 2024-07-13 11:40:13 -04:00
Daniel Lemire 4180e05730 [no-ci] fix 'null_ptr' written as 'null_nullptrptr' in the comments 2024-07-11 08:25:27 -04:00
Daniel Lemire feea2bce2c Create config.yml 2024-07-04 16:48:09 -04:00
Daniel Lemire 692f43cd84 Update standard-issue-template.md 2024-07-04 16:46:34 -04:00
Daniel Lemire 3240d55bcc chore: add RelWithDebInfo to our CI tests (#2209)
* chore: add RelWithDebInfo to our tests

* fix

* chore: add various build types to ubuntu ci
2024-07-04 16:26:22 -04:00
Daniel Lemire 66fd28fc00 Marking a few functions as pure (no side-effect) (#2210)
* marking a few trivial functions as pure

* adding other marks

* additional marks

* vs will issue warnings, so don't use [[gnu::pure]] when __clang__ or __GNUC__ is not defined
2024-07-04 16:26:11 -04:00
Daniel Lemire 5f638951c6 Update basics.md 2024-06-27 14:49:57 -04:00
Daniel Lemire 3e94eea939 adding another example (#2206)
Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2024-06-27 13:22:44 -04:00
Tunghohin 0e8311f812 reset moved object's viable_size to 0 in the move ctor of padded_string (#2204) 2024-06-25 18:06:32 -04:00
Daniel Lemire 3620e9d151 version bump 2024-06-11 15:37:22 -04:00
Daniel Lemire d017cd7ca4 Merge branch 'master' of github.com:simdjson/simdjson 2024-06-11 14:08:20 -04:00
Daniel Lemire d9d1ff5856 chore: remove unneeded gcc13 ci tests 2024-06-11 14:07:56 -04:00
Daniel Lemire eb8f2bce14 fix: add test for issue 2199 (#2200)
* fix: add test for issue 2199

* added ubuntu 24 workflow + silencing a warning
2024-06-11 13:55:55 -04:00
Dirk Stolle 2a4ff73468 update string_view lite to version 1.8.0 (#2197)
This is the header as seen for the tag v1.8.0,
commit a47222b9855dd6e6d1eac38acaa495822e2caa69, on
<https://github.com/martinmoene/string-view-lite>.
2024-06-10 10:29:25 -04:00
Daniel Lemire 77fc2b8447 doc: explaining the page trick (#2196)
* doc: explaining the page trick

* simplify

* did as john said

* trying something else

* flipping order

* trying some other order

* hmmm
2024-06-07 22:12:08 -04:00
Daniel Lemire ba8b66a633 Update basics.md 2024-06-07 12:49:32 -04:00
Daniel Lemire 66eec5feaf Update basics.md 2024-06-05 08:55:12 -04:00
Janeczko Jakub 3964f3e5d2 pull size_t from the std namespace (#2191) 2024-06-03 14:08:23 -04:00
Daniel Lemire ee8515122d version bump 2024-05-30 10:53:40 -04:00
halx99 5d35e7ca1f Fix compile error on llvm-19 (#2187) 2024-05-30 10:52:38 -04:00
spershin deefc88b9c Adding path for parsing incomplete json. (#2189)
1. Allows processing inclomplete, damaged, corrupted json to some extent.
2. Pariity with the Presto Java functionality.
3. Protected with SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON define.
4. Does not interfere with the normal path (can co-exist).
5. Tested in production forkflow.
2024-05-30 10:52:09 -04:00
Yuriy Chernyshov c80dda7c58 Fix building simdjson against libc++ with _LIBCPP_REMOVE_TRANSITIVE_INCLUDES defined (#2184)
```
src/implementation.cpp:193:20: error: no template named 'is_trivially_destructible' in namespace 'std'; did you mean 'is_trivially_move_constructible'?
static_assert(std::is_trivially_destructible<detect_best_supported_implementation_on_first_use>::value, "detect_best_supported_implementation_on_first_use should be trivially destructible");
              ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
                   is_trivially_move_constructible
```
2024-05-23 16:54:23 -04:00
Daniel Lemire d2954ef68b Update ubuntu22-gcc13.yml 2024-05-23 16:53:48 -04:00
Daniel Lemire ac719827ff fix: solve issue 2181 (#2182) 2024-05-11 20:44:38 -04:00
Daniel Lemire 6ea77392a7 Update basics.md 2024-05-10 12:21:26 -04:00
pnck e2f879751c fix: issue #2154 (#2178) 2024-05-10 00:33:09 -04:00
Daniel Lemire bf7834179c version bump 2024-05-07 19:15:43 -04:00
Daniel Lemire 69ab8848bf fix: satisfy a C++20 compiler warning (#2177) 2024-05-07 18:04:59 -04:00
Daniel Lemire 7288323e36 Update basics.md 2024-05-06 21:09:56 -04:00
Daniel Lemire 3f381cf3ee Update README.md 2024-05-06 11:11:34 -04:00
Daniel Lemire 52406402ed fix: replace vm*vq_u8 by vm*vq_u32 for better performance under some systems. 2024-05-01 14:53:48 -04:00
Daniel Lemire 799d8e3fc9 fix ref 2024-04-26 14:09:42 -04:00
Daniel Lemire 6c647d9f3b Update README.md 2024-04-24 16:23:45 -04:00
Daniel Lemire 8519e24f12 issue 2170 (#2172) 2024-04-24 01:41:40 -04:00
Daniel Lemire fffb62743c removing power fuzzer. 2024-04-23 23:50:49 -04:00
Daniel Lemire c888075f8d makes implementations trivially destructible (#2171) 2024-04-23 23:49:20 -04:00
Daniel Lemire 9b8aac5f22 [skip ci] updating documentation 2024-04-22 09:18:49 -04:00
Javier Blazquez 40b414d184 add Windows ARM64EC build and CI support (#2168)
* add Windows ARM64EC support

* add ARM64EC to vs17-arm-ci workflow
2024-04-09 14:56:58 -04:00
Daniel Lemire c85e8a7db1 version bump 2024-04-05 15:19:02 -04:00
Daniel Lemire 83f390f8e7 fix: adopt recommendation from issue 2163 (#2164) 2024-04-05 15:17:57 -04:00
Daniel Lemire 82563dcf70 correcting version bump 2024-04-04 14:08:25 -04:00
Daniel Lemire 646bd3f2c3 version bump 2024-04-04 12:24:07 -04:00
Daniel Lemire 4b0a908bb2 [skip ci] adding documentation regarding new hardware support 2024-04-04 12:22:03 -04:00
Jinyang He 4c98e51c53 Add LoongArch LSX and LASX support (#2159)
* Add LoongArch SX support

* Add LoongArch ASX support
2024-04-04 12:09:36 -04:00
Daniel Lemire 58e3d5d9cc fix: fix path to cross-compiler 2024-04-03 22:37:46 -04:00
Daniel Lemire a22a92dc72 trying to add loong to our ci tests (#2160)
* trying to add loong to our ci tests

* tuning loongarch

* updating the loong toolset
2024-04-03 15:18:45 -04:00
Daniel Lemire c4654553dd [skip ci] windows and unicode 2024-03-26 09:31:50 -04:00
Daniel Lemire 5997f82c37 Update basics.md 2024-03-20 14:26:11 -04:00
Daniel Lemire bee410b315 comment 2024-03-20 01:52:42 -04:00
Daniel Lemire b115e3ef61 some updates to our CI (#2151) 2024-03-17 11:59:50 -04:00
Daniel Lemire 669229df5d more documentation. 2024-03-17 10:29:41 -04:00
Daniel Lemire b6a2286e62 adding some documentation for escaped_key(). 2024-03-17 10:27:13 -04:00
Twice 3a97c57773 Add escaped_key method to ondemand::field (#2150) 2024-03-17 10:10:31 -04:00
Daniel Lemire e1c6a778f8 3.8.0 2024-03-10 13:25:53 -04:00
Daniel Lemire d413dd5b0e v3.7.1 2024-03-10 13:24:07 -04:00
Daniel Lemire a33fdc2770 Unconfusing clang (#2147)
* In some cases, clang might try to cast an ondemand::document to an
ondemand::document, instead of calling the move constructor. So we
can disable the template cast.

* In some cases, clang might get confused when constructing an ondemand
document in a constructor: instead of calling the move constructor, it
somehow ends up trying to cast a document to a document. We can easily
disallow this behavior with std::enable_if.

* making compatible with C++11

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2024-03-10 13:20:39 -04:00
Daniel Lemire 87f5f7a250 added some documentation in the code related to custom types (#2146) 2024-03-09 18:45:05 -05:00
Daniel Lemire 9923d27994 upper case 2024-03-06 16:01:21 -05:00
Piotr Rżysko 876be23545 Add link to simdjson-java (#2145) 2024-03-06 15:55:16 -05:00
Daniel Lemire 2028bdaeac This documents the big-int feature, and adds a few tests. (#2142)
* This documents the big-int feature, and adds a few tests.

* moving check_if_integer

* trimming the example.

* More trimming.

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2024-02-26 20:39:20 -05:00
Daniel Lemire 1ac3a00ca9 verify issue 2137 (#2138)
* fix issue 2137

* updating the single header

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2024-02-26 20:39:08 -05:00
Daniel Lemire 6d7cc2c7c1 fixing build 2024-02-26 20:00:17 -05:00
Daniel Lemire 61a52287fc Fixing apple builds 2024-02-26 19:50:22 -05:00
Daniel Lemire 37a9aaad48 [no ci] tweaking the wording 2024-02-26 14:05:12 -05:00
Serge Aleynikov 78325888ea Add functionality to detect big integers (#2139) 2024-02-26 14:00:33 -05:00
Shi Pujin a90cb5d900 add loongarch64 (#2141) 2024-02-26 13:54:49 -05:00
Daniel Lemire 34a01ec3af removing unnecessary spaces 2024-02-23 19:21:31 -05:00
Daniel Lemire e510ed2774 Release candidate (3.7.0) (#2136)
* Release candidate (3.7.0)

* some extra comments.
2024-02-23 16:50:55 -05:00
Dirk Stolle dabac13dde Add missing word in documentation (#2133) 2024-02-18 11:50:12 -05:00
Daniel Lemire aef7111c05 Documenting Unicode support in JSONPath and JSON Pointer (#2132)
* Documenting our support of JSON Path and JSON Pointer vs. Unicode characters

* using more standard terminology (nitpicking)

* specifying UTF-8 encoding

* allow char8_t when compiling as C++20

* casting

* minor doc corrections
2024-02-15 20:28:38 -05:00
Daniel Lemire 0c08894a54 adopting string literals (#2128)
* adopting string literals

* String suffixes are C++14
2024-02-15 10:40:11 -05:00
Daniel Lemire 2fe3a1467d This PR adds a convenience method (is_string) as well as a new method (#2131)
key_raw_json_token() to get the raw key token.
2024-02-15 10:39:30 -05:00
Daniel Lemire 167a46a0e7 adding remark 2024-02-15 09:56:05 -05:00
Francisco Geiman Thiesen 2b532489cc Adding on-demand support for a subset of json path. (#2127)
* First implementation of the conversion logic.

- Not yet tested
- Not yet compiled
- Not yet used in ondemand array/object/etc...

* Adding at_path to ondemand arrays

* Adding at_path to array, document and value.

* Adding at_path to ondemand object as well.

Pending:
- Building
- Adding tests for each of the on-demand classes usage of at_path
- Properly documenting the subset of json path that is currently supported.

* Adding a simple json_path test to on_demand

* Still trying to compile the ondemand_readme_examples test with the at_path() call

* Fixing linking issues with array::at_path

* Fixing issues with the path -> pointer conversion and removing comments

* Adding a clone of json_pointer to test json_path extensively

* Adding ondemand_json_path_tests (all tests passing)

* Adding documentation for at_path()

* Removing some newlines

* Removing simdjson_result from the return type of the string conversion function. Now json_path_to_pointer_conversion returns a std::string.

* Fixing typo

* Making string concatenation explicit to avoid ubuntu gcc12 issue with -O3 flag.

* Addressing latest reviews

* Addressing latest reviews
2024-02-15 09:47:25 -05:00
Daniel Lemire 85163df88c Update ondemand_design.md 2024-02-14 23:36:25 -05:00
Daniel Lemire 6a5be0f0db Create SECURITY.md 2024-02-09 17:16:54 -05:00
Daniel Lemire 710a3c7241 Update implementation-selection.md 2024-02-09 11:46:22 -05:00
Daniel Lemire c93e02e172 Update implementation-selection.md 2024-02-09 11:45:15 -05:00
Daniel Lemire 517143aec4 Update implementation-selection.md 2024-02-09 11:36:38 -05:00
Francisco Geiman Thiesen 9feda5e703 Update HACKING.md (#2122)
Updating windows command
2024-02-09 10:29:07 -05:00
Daniel Lemire cda198b7b9 Update ondemand_design.md 2024-02-09 10:22:44 -05:00
Daniel Lemire aad17a6a8d Update basics.md 2024-02-09 10:20:42 -05:00
Daniel Lemire 32d072a208 fixing issue2120 (#2121) 2024-02-08 11:34:36 -05:00
Daniel Lemire 5eab555053 documenting the support for custom types (#2098)
* documenting the support for custom types

* fixes

* getting around -Weffc++

* calling get_raw_json_string()

* documenting the support for custom types

* fixes

* getting around -Weffc++

* calling get_raw_json_string()
2024-02-06 13:20:35 -05:00
Daniel Lemire 6430fbd39a Better documentation and support for the case where you have a single implementation. (#2118) 2024-02-06 13:20:23 -05:00
Daniel Lemire 4d2fc31b27 removes simdjson.cpp include from the amalgated demo (#2119)
Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2024-02-05 19:34:50 -05:00
Bo Anderson 33610b59eb Allow some iterators to be used in standard C++ iterator functions (#2106) 2024-02-05 17:52:01 -05:00
Daniel Lemire 23290ae48d retiring deprecated DOM APIs (#2109) 2024-02-04 21:58:48 -05:00
Daniel Lemire eb3a085e0f Bash check is unneeded. (#2112) 2024-02-04 21:58:36 -05:00
Daniel Lemire 4c8ec35a48 fixing the order of the apple counters 2024-01-31 14:30:44 -05:00
184 changed files with 53613 additions and 7884 deletions
+5 -1
View File
@@ -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:
+1
View File
@@ -0,0 +1 @@
blank_issues_enabled: false
@@ -18,7 +18,7 @@ We do not make changes to simdjson without clearly identifiable benefits, which
Is your issue:
1. A bug report? If so, please point at a reproducible test. Indicate whether you are willing or able to provide a bug fix as a pull request.
1. A bug report? If so, please point at a reproducible test. Indicate whether you are willing or able to provide a bug fix as a pull request. As a matter of policy, we do not consider a compiler warning to be a bug.
2. A build issue? If so, provide all possible details regarding your system configuration. If we cannot reproduce your issue, we cannot fix it.
+1 -1
View File
@@ -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') }}
+3 -3
View File
@@ -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/
+50
View File
@@ -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
+1 -1
View File
@@ -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') }}
+4 -1
View File
@@ -20,12 +20,15 @@ jobs:
- msystem: "MINGW64"
install: mingw-w64-x86_64-libxml2 mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-clang
type: Debug
- msystem: "MINGW64"
install: mingw-w64-x86_64-libxml2 mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-clang
type: RelWithDebInfo
env:
CMAKE_GENERATOR: Ninja
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
+4 -1
View File
@@ -22,12 +22,15 @@ jobs:
- msystem: "MINGW64"
install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc
type: Debug
- msystem: "MINGW64"
install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc
type: RelWithDebInfo
env:
CMAKE_GENERATOR: Ninja
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
-71
View File
@@ -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"
+1 -1
View File
@@ -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') }}
+1 -1
View File
@@ -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') }}
+1 -1
View File
@@ -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') }}
+1 -1
View File
@@ -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') }}
+2 -2
View File
@@ -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') }}
+1 -1
View File
@@ -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') }}
+1 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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') }}
-23
View File
@@ -1,23 +0,0 @@
name: Ubuntu 22.04 CI (GCC 13)
on: [push, pull_request]
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Use cmake
run: |
mkdir build &&
cd build &&
CXX=g++-13 cmake -DSIMDJSON_DEVELOPER_MODE=ON .. &&
cmake --build . &&
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') }}
+3 -3
View File
@@ -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
+1 -1
View File
@@ -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') }}
+25
View File
@@ -0,0 +1,25 @@
name: Ubuntu 24.04 CI
on: [push, pull_request]
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-24.04
strategy:
matrix:
shared: [ON, OFF]
cxx: [g++-13, clang++-16]
sanitizer: [ON, OFF]
build_type: [RelWithDebInfo, Debug, Release]
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Prepare
run: cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_SANITIZE=${{matrix.sanitizer}} -DBUILD_SHARED_LIBS=${{matrix.shared}} -B build
env:
CXX: ${{matrix.cxx}}
- name: Build
run: cmake --build build -j=2
- name: Test
run: ctest --output-on-failure --test-dir build
+2 -1
View File
@@ -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
+11 -15
View File
@@ -13,10 +13,12 @@ jobs:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 17 2022, arch: Win32, shared: ON}
- {gen: Visual Studio 17 2022, arch: Win32, shared: OFF}
- {gen: Visual Studio 17 2022, arch: x64, shared: ON}
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF}
- {gen: Visual Studio 17 2022, arch: Win32, shared: ON, build_type: Release}
- {gen: Visual Studio 17 2022, arch: Win32, shared: OFF, build_type: Release}
- {gen: Visual Studio 17 2022, arch: x64, shared: ON, build_type: Release}
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Debug}
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Release}
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: RelWithDebInfo}
steps:
- name: checkout
uses: actions/checkout@v4
@@ -24,21 +26,15 @@ jobs:
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -DBUILD_SHARED_LIBS=${{matrix.shared}} -B build
- name: Build Debug
run: cmake --build build --config Debug --verbose
- name: Build Release
run: cmake --build build --config Release --verbose
- name: Run Release tests
run: cmake --build build --config ${{build_type}} --verbose
- name: Run tests
run: |
cd build
ctest -C Release -LE explicitonly --output-on-failure
- name: Run Debug tests
run: |
cd build
ctest -C Debug -LE explicitonly --output-on-failure
ctest -C ${{build_type}} -LE explicitonly --output-on-failure
- name: Install
run: |
cmake --install build --config Release
cmake --install build --config ${{build_type}}
- name: Test Installation
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
cmake --build build_install_test --config Release
cmake --build build_install_test --config ${{build_type}}
+9 -13
View File
@@ -13,29 +13,25 @@ jobs:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 17 2022, arch: x64}
- {gen: Visual Studio 17 2022, arch: x64, build_type: Debug}
- {gen: Visual Studio 17 2022, arch: x64, build_type: Release}
- {gen: Visual Studio 17 2022, arch: x64, build_type: RelWithDebInfo}
steps:
- name: checkout
uses: actions/checkout@v4
- name: Configure
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -B build
- name: Build Debug
run: cmake --build build --config Debug --verbose
- name: Build Release
run: cmake --build build --config Release --verbose
- name: Run Release tests
- name: Build
run: cmake --build build --config ${{matrix.build_type}} --verbose
- name: Run tests
run: |
cd build
ctest -C Release -LE explicitonly --output-on-failure
- name: Run Debug tests
run: |
cd build
ctest -C Debug -LE explicitonly --output-on-failure
ctest -C ${{matrix.build_type}} -LE explicitonly --output-on-failure
- name: Install
run: |
cmake --install build --config Release
cmake --install build --config ${{matrix.build_type}}
- name: Test Installation
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
cmake --build build_install_test --config Release
cmake --build build_install_test --config ${{matrix.build_type}}
+1 -1
View File
@@ -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') }}
+12 -1
View File
@@ -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
View File
@@ -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.10.0
DESCRIPTION "Parsing gigabytes of JSON per second"
HOMEPAGE_URL "https://simdjson.org/"
LANGUAGES CXX C
@@ -20,8 +20,8 @@ string(
# ---- Options, variables ----
# These version numbers are modified by tools/release.py
set(SIMDJSON_LIB_VERSION "19.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "19" CACHE STRING "simdjson library soversion")
set(SIMDJSON_LIB_VERSION "23.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "23" 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
View File
@@ -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
--------------
+1 -1
View File
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = "3.6.4"
PROJECT_NUMBER = "3.10.0"
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
+6 -10
View File
@@ -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.
@@ -88,8 +88,8 @@ simdjson's source structure, from the top level, looks like this:
* simdjson/ondemand.h: the `simdjson::ondemand` namespace. Includes all public ondemand classes.
* simdjson/builtin.h: the `simdjson::builtin` namespace. Aliased to the most universal implementation available.
* simdjson/builtin/ondemand.h: the `simdjson::builtin::ondemand` namespace.
* simdjson/arm64|fallback|haswell|icelake|ppc64|westmere/ondemand.h: the `simdjson::<implementation>::ondemand` namespace. on demand compiled for the specific implementation.
* simdjson/generic/ondemand/*.h: individual on demand classes, generically written.
* simdjson/arm64|fallback|haswell|icelake|ppc64|westmere/ondemand.h: the `simdjson::<implementation>::ondemand` namespace. On-Demand compiled for the specific implementation.
* simdjson/generic/ondemand/*.h: individual On-Demand classes, generically written.
* simdjson/generic/ondemand/dependencies.h: dependencies on common, non-implementation-specific simdjson classes. This will be included before including amalgamated.h.
* simdjson/generic/ondemand/amalgamated.h: all generic ondemand classes for an implementation.
* **src:** The source files for non-inlined functionality (e.g. the architecture-specific parser
@@ -99,7 +99,7 @@ simdjson's source structure, from the top level, looks like this:
* *.cpp: other misc. implementations, such as `simdjson::implementation` and the minifier.
* arm64|fallback|haswell|icelake|ppc64|westmere.cpp: Architecture-specific parser implementations.
* generic/*.h: `simdjson::<implementation>` namespace. Generic implementation of the parser, particularly the `dom_parser_implementation`.
* generic/stage1/*.h: `simdjson::<implementation>::stage1` namespace. Generic implementation of the simd-heavy tokenizer/indexer pass of the simdjson parser. Used for the On Demand interface
* generic/stage1/*.h: `simdjson::<implementation>::stage1` namespace. Generic implementation of the simd-heavy tokenizer/indexer pass of the simdjson parser. Used for the On-Demand interface
* generic/stage2/*.h: `simdjson::<implementation>::stage2` namespace. Generic implementation of the tape creator, which consumes the index from stage 1 and actually parses numbers and string and such. Used for the DOM interface.
Other important files and directories:
@@ -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
+6 -3
View File
@@ -46,6 +46,7 @@ Real-world usage
- [Meta Velox](https://velox-lib.io)
- [Google Pax](https://github.com/google/paxml)
- [milvus](https://github.com/milvus-io/milvus)
- [QuestDB](https://questdb.io/blog/questdb-release-8-0-3/)
- [Clang Build Analyzer](https://github.com/aras-p/ClangBuildAnalyzer)
- [Shopify HeapProfiler](https://github.com/Shopify/heap-profiler)
- [StarRocks](https://github.com/StarRocks/starrocks)
@@ -59,6 +60,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 +170,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
--------------
@@ -177,9 +180,9 @@ The simdjson library takes advantage of modern microarchitectures, parallelizing
instructions, reducing branch misprediction, and reducing data dependency to take advantage of each
CPU's multiple execution cores.
Our default front-end is called On Demand, and we wrote a paper about it:
Our default front-end is called On-Demand, and we wrote a paper about it:
- John Keiser, Daniel Lemire, [On-Demand JSON: A Better Way to Parse Documents?](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:
+7
View File
@@ -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
+2 -3
View File
@@ -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]]};
}
};
-158
View File
@@ -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();
-30
View File
@@ -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;
+1 -1
View File
@@ -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;
}
+1 -1
View File
@@ -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;
}
+1 -1
View File
@@ -13,7 +13,7 @@ class OnDemand {
public:
OnDemand() {
if(!displayed_implementation) {
std::cout << "On Demand implementation: " << builtin_implementation()->name() << std::endl;
std::cout << "On-Demand implementation: " << builtin_implementation()->name() << std::endl;
displayed_implementation = true;
}
}
+10 -6
View File
@@ -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()
@@ -151,6 +155,6 @@ if(SIMDJSON_CXXOPTS)
set_off(CXXOPTS_BUILD_TESTS)
set_off(CXXOPTS_ENABLE_INSTALL)
import_dependency(cxxopts jarro2783/cxxopts 794c975)
import_dependency(cxxopts jarro2783/cxxopts 5965670)
add_dependency(cxxopts)
endif()
+645 -98
View File
File diff suppressed because it is too large Load Diff
+11 -5
View File
@@ -3,7 +3,7 @@ The Document-Object-Model (DOM) front-end
An overview of what you need to know to use simdjson, with examples.
* [DOM vs On Demand](#dom-vs-on-demand)
* [DOM vs On-Demand](#dom-vs-on-demand)
* [The Basics: Loading and Parsing JSON Documents](#the-basics-loading-and-parsing-json-documents-using-the-dom-front-end)
* [Using the Parsed JSON](#using-the-parsed-json)
* [C++17 Support](#c17-support)
@@ -18,7 +18,7 @@ An overview of what you need to know to use simdjson, with examples.
* [Padding and Temporary Copies](#padding-and-temporary-copies)
* [Performance Tips](#performance-tips)
DOM vs On Demand
DOM vs On-Demand
----------------------------------------------
The simdjson library offers two distinct approaches on how to access a JSON document. We support
@@ -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:
+10 -7
View File
@@ -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
+32 -32
View File
@@ -9,8 +9,8 @@ Whether we parse JSON or XML, or any other serialized format, there are relative
- Another popular approach is the schema-based deserialization model.
We propose an approach that is as easy to use and often as flexible as the DOM approach, yet as fast and
efficient as the schema-based or event-based approaches. We call this new approach "On Demand". The
simdjson On Demand API offers a familiar, friendly DOM API and
efficient as the schema-based or event-based approaches. We call this new approach "On-Demand". The
simdjson On-Demand API offers a familiar, friendly DOM API and
provides the performance of just-in-time parsing on top of the simdjson superior performance.
To achieve ease of use, we mimicked the *form* of a traditional DOM API: you can iterate over
@@ -18,7 +18,7 @@ arrays, look up fields in objects, and extract native values like `double`, `uin
To achieve performance, we introduced some key limitations that make the DOM API *streaming*:
array/object iteration cannot be restarted, and string/number values can only be parsed once. If
these limitations are acceptable to you, the On Demand API could help you write maintainable
these limitations are acceptable to you, the On-Demand API could help you write maintainable
applications with a computation efficiency that is difficult to surpass.
A code example illustrates our API from a programmer's point of view:
@@ -72,24 +72,24 @@ This streaming approach means that unused fields and values are not parsed or
converted, thus saving space and time. In our example, the `"name"`, `"followers_count"`,
and `"friends_count"` keys and matching values are skipped.
Further, the On Demand API does not parse a value *at all* until you try to convert it (e.g., to `double`,
Further, the On-Demand API does not parse a value *at all* until you try to convert it (e.g., to `double`,
`int`, `string`, or `bool`). In our example, when accessing the key-value pair `"retweet_count": 82`, the parser
may not convert the pair of characters `82` to the binary integer 82. Because the programmer specifies the data
type, we avoid branch mispredictions related to data type determination and improve the performance.
We expect users of an On Demand API to work in terms of a JSON dialect, which is a set of expectations and
We expect users of an On-Demand API to work in terms of a JSON dialect, which is a set of expectations and
specifications that come in addition to the [JSON specification](https://www.rfc-editor.org/rfc/rfc8259.txt).
The On Demand approach is designed around several principles:
The On-Demand approach is designed around several principles:
* **Streaming (\*):** It avoids preparsing values, keeping the memory usage and the latency down.
* **Forward-Only:** To prevent reiteration of the same values and to keep the number of variables down (literally), only a single index is maintained and everything uses it (even if you have nested for loops). This means when you are going through an array of arrays, for example, that the inner array loop will advance the index to the next comma, and the array can just pick it up and look at it.
* **Natural Iteration:** A JSON array or object can be iterated with a normal C++ for loop. Nested arrays and objects are supported by nested for loops.
* **Use-Specific Parsing:** Parsing is always specific to the type required by the programmer. For example, if the programmer asks for an unsigned integer, we just start parsing digits. If there were no digits, we toss an error. There are even different parsers for `double`, `uint64_t` and `int64_t` values. This use-specific parsing avoids the branchiness of a generic "type switch," and makes the code more inlineable and compact.
* **Validate What You Use:** On Demand deliberately validates the values you use and the structure leading to it, but nothing else. The goal is a guarantee that the value you asked for is the correct one and is not malformed: there must be no confusion over whether you got the right value.
* **Validate What You Use:** On-Demand deliberately validates the values you use and the structure leading to it, but nothing else. The goal is a guarantee that the value you asked for is the correct one and is not malformed: there must be no confusion over whether you got the right value.
To understand why On Demand is different, it is helpful to review the major
To understand why On-Demand is different, it is helpful to review the major
approaches to parsing and parser APIs in use today.
### DOM Parsers
@@ -106,7 +106,7 @@ DOM tree is often easy enough that many users use the DOM as-is instead of creat
their own custom data structures.
The DOM approach was the only way to parse JSON documents up to version 0.6 of the simdjson library.
Our DOM API looks similar to our On Demand example, except
Our DOM API looks similar to our On-Demand example, except
it calls `parse` instead of `iterate`:
```c++
@@ -152,7 +152,7 @@ a tweet right now, or is this from some other place in the document
entirely? Though an event-based approach may allow superior performance, it is demanding of the programmer
who must efficiently keep track of its current state within the JSON input.
The following is event-based example of the Twitter problem we have reviewed in the DOM and On Demand
The following is event-based example of the Twitter problem we have reviewed in the DOM and On-Demand
examples. To make it short enough to use as an example at all, it has heavily redacted: it only solves
a part of the problem (does not get user.screen_name), it has bugs (it does not handle sub-objects
in a tweet at all), and it uses a theoretical, simple event-based API that minimizes ceremony.
@@ -257,7 +257,7 @@ stress the branch prediction. Though branch predictors improve with each new gen
the cost of branch mispredictions also tends to increase as pipelines expand, and the processors become
able to schedule longer streams of instructions.
On Demand parsing is tailor-made to solve this problem at the source, parsing values only after the
On-Demand parsing is tailor-made to solve this problem at the source, parsing values only after the
user declares their type by asking for a `double`, an `int`, a `string`, etc. It attempts to do so while
preserving most of the flexibility of DOM parsing.
@@ -297,7 +297,7 @@ To help visualize the algorithm, we'll walk through the example C++ given at the
Since this is the first time this parser has been used, `iterate()` first allocates internal
parser buffers if this is the first time through. When reusing an existing parser, allocation
only happens if the new document is bigger than internal buffers can handle. The On Demand
only happens if the new document is bigger than internal buffers can handle. The On-Demand
API only ever allocates memory in the `iterate()` function call.
The simdjson library then preprocesses the JSON text at high speed, finding all tokens (i.e. the starting
@@ -492,7 +492,7 @@ To help visualize the algorithm, we'll walk through the example C++ given at the
Because of the cast to uint64_t, simdjson knows it's parsing an unsigned integer. This lets
us use a fast parser which *only* knows how to parse digits. It validates that it is an integer
by rejecting negative numbers, strings, and other values based on the fact that they are not the
digits 0-9. This type specificity is part of why parsing with on demand is so fast: you lose all
digits 0-9. This type specificity is part of why parsing with On-Demand is so fast: you lose all
the code that has to understand those other types.
The iterator is advanced to the `}`, and depth decreased back to 3 (root > statuses > tweet).
@@ -597,7 +597,7 @@ To help visualize the algorithm, we'll walk through the example C++ given at the
This means you can very efficiently do things like read a single value from a JSON file, or take
the top N, for example. It also means the things you don't use won't be fully validated. This is
a general principle of On Demand: don't validate what you don't use. We still fully validate
a general principle of On-Demand: don't validate what you don't use. We still fully validate
values you do use, however, as well as the objects and arrays that lead to them, so that you can
be sure you get the information you need.
@@ -654,7 +654,7 @@ for(auto field : doc.get_object()) {
### Iteration Safety
The On Demand API is powerful. To compensate, we add some safeguards to ensure that it can be used without fear
The On-Demand API is powerful. To compensate, we add some safeguards to ensure that it can be used without fear
in production systems:
- If the value fails to be parsed as one type, the program can try to parse it as something else until the program succeeds. Thus
@@ -667,7 +667,7 @@ in production systems:
if it was `nullptr` but did not care what the actual value was--it will iterate. The destructor automates
the iteration.
Some care is needed when using the On Demand API in scenarios where you need to access several sibling arrays or objects because
Some care is needed when using the On-Demand API in scenarios where you need to access several sibling arrays or objects because
only one object or array can be active at any one time. Let us consider the following example:
```C++
@@ -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;
@@ -709,36 +709,36 @@ in production systems:
}
```
### Benefits of the On Demand Approach
### Benefits of the On-Demand Approach
We expect that the On Demand approach has many of the performance benefits of the schema-based approach, while providing a flexibility that is similar to that of the DOM-based approach.
We expect that the On-Demand approach has many of the performance benefits of the schema-based approach, while providing a flexibility that is similar to that of the DOM-based approach.
* Faster than DOM in some cases. Reduced memory usage.
* Straightforward, programmer-friendly interface (arrays and objects).
* Highly expressive, beyond deserialization and pointer queries: many tasks can be accomplished with little code.
### Limitations of the On Demand Approach
### Limitations of the On-Demand Approach
The On Demand approach has some limitations:
The On-Demand approach has some limitations:
* Because it operates in streaming mode, you only have access to the current element in the JSON document. Furthermore, the document is traversed in order so the code is sensitive to the order of the JSON nodes in the same manner as an event-based approach (e.g., SAX). (The one exception to this is field lookup, which is more *performant* when the order of lookups matches the order of fields in the document, but which will still work with out-of-order fields, with a performance hit.)
* The On Demand approach is less safe than DOM: we only validate the components of the JSON document that are used and it is possible to begin ingesting an invalid document only to find out later that the document is invalid. Are you fine ingesting a large JSON document that starts with well formed JSON but ends with invalid JSON content?
* The On-Demand approach is less safe than DOM: we only validate the components of the JSON document that are used and it is possible to begin ingesting an invalid document only to find out later that the document is invalid. Are you fine ingesting a large JSON document that starts with well formed JSON but ends with invalid JSON content?
There are currently additional technical limitations which we expect to resolve in future releases of the simdjson library:
* The simdjson library offers runtime dispatching which allows you to compile one binary and have it run at full speed on different processors, taking advantage of the specific features of the processor. The On Demand API has limited runtime dispatch support. Under x64 systems, to fully benefit from the On Demand API, we recommend that you compile your code for a specific processor. E.g., if your processor supports AVX2 instructions, you should compile your binary executable with AVX2 instruction support (by using your compiler's commands). If you are sufficiently technically proficient, you can implement runtime dispatching within your application, by compiling your On Demand code for different processors.
* The simdjson library offers runtime dispatching which allows you to compile one binary and have it run at full speed on different processors, taking advantage of the specific features of the processor. The On-Demand API has limited runtime dispatch support. Under x64 systems, to fully benefit from the On-Demand API, we recommend that you compile your code for a specific processor. E.g., if your processor supports AVX2 instructions, you should compile your binary executable with AVX2 instruction support (by using your compiler's commands). If you are sufficiently technically proficient, you can implement runtime dispatching within your application, by compiling your On-Demand code for different processors.
* There is an initial phase which scans the entire document quickly, irrespective of the size of the document. We plan to break this phase into distinct steps for large files in a future release as we have done with other components of our API (e.g., `parse_many`).
### Applicability of the On Demand Approach
### Applicability of the On-Demand Approach
At this time we recommend the On Demand API in the following cases:
At this time we recommend the On-Demand API in the following cases:
1. The 64-bit hardware (CPU) used to run the software is known at compile time. If you need runtime dispatching because you cannot be certain of the hardware used to run your software, you will be better served with the core simdjson API. (This only applies to x64 (AMD/Intel). On 64-bit ARM hardware, runtime dispatching is unnecessary.)
2. The used parts of JSON files do not need to be validated and the layout of the nodes follows a strict JSON dialect. If you are receiving JSON from other systems, you might be better served with core simdjson API as it fully validates the JSON inputs and allows you to navigate through the document at will.
3. Speed and efficiency are of the utmost importance. Keep in mind that the core simdjson API is highly efficient so adopting the On Demand API is not necessary for high efficiency.
3. Speed and efficiency are of the utmost importance. Keep in mind that the core simdjson API is highly efficient so adopting the On-Demand API is not necessary for high efficiency.
4. As a developer, you value a clean, flexible and maintainable API.
Good applications for the On Demand API might be:
Good applications for the On-Demand API might be:
* You are working from pre-existing large JSON files that have been vetted. You expect them to be well formed according to a known JSON dialect and to have a consistent layout. For example, you might be doing biomedical research or machine learning on top of static data dumps in JSON.
* Both the generation and the consumption of JSON data is within your system. Your team controls both the software that produces the JSON and the software the parses it, your team knows and control the hardware. Thus you can fully test your system.
@@ -746,13 +746,13 @@ Good applications for the On Demand API might be:
## Checking Your CPU Selection (x64 systems)
The On Demand API uses advanced architecture-specific code for many common processors to make JSON preprocessing and string parsing faster. By default, however, most c++ compilers will compile to the least common denominator (since the program could theoretically be run anywhere). Since On Demand is inlined into your own code, it cannot always use these advanced versions unless the compiler is told to target them.
The On-Demand API uses advanced architecture-specific code for many common processors to make JSON preprocessing and string parsing faster. By default, however, most c++ compilers will compile to the least common denominator (since the program could theoretically be run anywhere). Since On-Demand is inlined into your own code, it cannot always use these advanced versions unless the compiler is told to target them.
On relevant systems, the On Demand API provides some support for runtime dispatching: that is, it will attempt to detect, at runtime, the instructions that your processor supports and optimize the code accordingly. However, it cannot always make full use of the features of your processor.
On relevant systems, the On-Demand API provides some support for runtime dispatching: that is, it will attempt to detect, at runtime, the instructions that your processor supports and optimize the code accordingly. However, it cannot always make full use of the features of your processor.
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;
@@ -777,6 +777,6 @@ In these examples, the `-march=haswell` flags targets a haswell processor and th
Instead of specifying a specific microarchitecture, you can let your compiler do the work. The `-march=native` flags says "target the current computer," which is a reasonable default for many applications which both compile and run on the same processor.
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).
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.
+110 -2
View File
@@ -14,6 +14,7 @@ testing and get the best performance.
* [Number parsing](#number-parsing)
* [Visual Studio](#visual-studio)
* [Power Usage and Downclocking](#power-usage-and-downclocking)
* [Free Padding](#free-padding)
NDEBUG directive
@@ -74,7 +75,7 @@ or simply
Server Loops: Long-Running Processes and Memory Capacity
---------------------------------
The On Demand approach also automatically expands its memory capacity when larger documents are parsed. However, for longer processes where very large files are processed (such as server loops), this capacity is not resized down. On Demand also lets you adjust the maximal capacity that the parser can process:
The On-Demand approach also automatically expands its memory capacity when larger documents are parsed. However, for longer processes where very large files are processed (such as server loops), this capacity is not resized down. On-Demand also lets you adjust the maximal capacity that the parser can process:
* You can set an upper bound (*max_capacity*) when construction the parser:
```C++
@@ -159,7 +160,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
@@ -179,3 +180,110 @@ The simdjson library does not generally make use of heavy 256-bit instructions.
the macro `SIMDJSON_AVX512_ALLOWED` to `0` in C++ prior to importing the headers.
You may still be worried about which SIMD instruction set is used by simdjson. Thankfully, [you can always determine and change which architecture-specific implementation is used](implementation-selection.md) by simdjson. Thus even if your CPU supports AVX2, you do not need to use AVX2. You are in control.
Free Padding
-------
For performance reasons, the simdjson library requires that the JSON input contain at least
`simdjson::SIMDJSON_PADDING` bytes at the end of the stream. The value `simdjson::SIMDJSON_PADDING` is
small (e.g., 64 bytes). On modern systems, you can safely read beyond an allocated buffers,
as long as you remain within an allocated page. Pages on modern systems span at least 4 kilobytes,
but can be significantly larger. E.g., Apple systems favour pages spanning 16 kilobytes.
In effect, it means that you can almost always read a few bytes beyond your current buffer---without
allocating extra memory. However, tools such as valgrind or memory sanitizers will flag such behavior as unsafe.
Nevertheless, you can still make sure of this capability in your code if you are an expert
programmer and you are willing to silence sanitizer warnings. The following code provides
a portable example.
The conditional compilation checks for the `_MSC_VER` macro (indicating Microsoft Visual Studio)
and includes platform-specific headers accordingly.
The `page_size()` function determines the default size of a memory page in bytes on the system.
On Windows (when `_WIN32` is defined), it uses `GetSystemInfo()` to retrieve system information and obtain the page size.
On other platforms (non-Windows), it uses `sysconf(_SC_PAGESIZE)` to get the page size.
The function returns the page size.
The `need_allocation()` function checks whether the buffer (given by `buf`) plus the specified length (`len`) is near a page boundary.
If the buffer extends beyond the current page when padded by `simdjson::SIMDJSON_PADDING`, it returns true, indicating that reallocation is needed.
Otherwise, it returns false.
The `get_padded_string_view()` creates a `padded_string_view` from the input buffer.
If reallocation is needed (unlikely case), it allocates a new padded_string and assigns it to `jsonbuffer`.
Otherwise (very likely), it creates a `padded_string_view` directly from the buffer.
The `simdjson::SIMDJSON_PADDING` ensures that there is additional padding for parsing efficiency.
The calling code just needs to provide `jsonbuffer` (an instance of `simdjson::padded_string`)
and pass `get_padded_string_view(buf, len, jsonbuffer)` to `parser.iterate`. Most of the time,
this code will not allocate new memory.
```cpp
#ifdef _WIN32
#include <windows.h>
#include <sysinfoapi.h>
#else
#include <unistd.h>
#endif
#include "simdjson.h"
#include <cstdio>
// Returns the default size of the page in bytes on this system.
long page_size() {
#ifdef _WIN32
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
long pagesize = sysInfo.dwPageSize;
#else
long pagesize = sysconf(_SC_PAGESIZE);
#endif
return pagesize;
}
// Returns true if the buffer + len + simdjson::SIMDJSON_PADDING crosses the
// page boundary.
bool need_allocation(const char *buf, size_t len) {
return ((reinterpret_cast<uintptr_t>(buf + len - 1) % page_size()) <
simdjson::SIMDJSON_PADDING);
}
simdjson::padded_string_view
get_padded_string_view(const char *buf, size_t len,
simdjson::padded_string &jsonbuffer) {
if (need_allocation(buf, len)) { // unlikely case
jsonbuffer = simdjson::padded_string(buf, len);
return jsonbuffer;
} else { // no reallcation needed (very likely)
return simdjson::padded_string_view(buf, len,
len + simdjson::SIMDJSON_PADDING);
}
}
int main() {
printf("page_size: %ld\n", page_size());
const char *jsonpoiner = R"(
{
"key": "value"
}
)";
size_t len = strlen(jsonpoiner);
simdjson::padded_string jsonbuffer; // only allocate if needed
simdjson::ondemand::parser parser;
simdjson::ondemand::document doc;
simdjson::error_code error =
parser.iterate(get_padded_string_view(jsonpoiner, len, jsonbuffer))
.get(doc);
if (error) {
printf("error: %s\n", simdjson::error_message(error));
return EXIT_FAILURE;
}
std::string_view value;
error = doc["key"].get_string().get(value);
if (error) {
return EXIT_FAILURE;
}
printf("Value: \"%.*s\"\n", (int)value.size(), value.data());
if (value != "value") {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
```
+1 -1
View File
@@ -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) |
+1 -1
View File
@@ -25,7 +25,7 @@ IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
add_quickstart_test(quickstart2_noexceptions quickstart2_noexceptions.cpp NO_EXCEPTIONS LABELS acceptance)
add_quickstart_test(quickstart2_noexceptions11 quickstart2_noexceptions.cpp NO_EXCEPTIONS CXX_STANDARD c++11)
# On Demand Quick Start
# On-Demand Quick Start
if (SIMDJSON_EXCEPTIONS)
add_quickstart_test(quickstart_ondemand quickstart_ondemand.cpp LABELS quickstart_ondemand acceptance)
add_quickstart_test(quickstart_ondemand11 quickstart_ondemand.cpp CXX_STANDARD c++11 LABELS quickstart_ondemand acceptance)
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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);
+4 -4
View File
@@ -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);
+1 -1
View File
@@ -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
+4
View File
@@ -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
+4
View File
@@ -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
+4
View File
@@ -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
View File
@@ -13,6 +13,16 @@
#endif
#endif
// C++ 23
#if !defined(SIMDJSON_CPLUSPLUS23) && (SIMDJSON_CPLUSPLUS >= 202302L)
#define SIMDJSON_CPLUSPLUS23 1
#endif
// C++ 20
#if !defined(SIMDJSON_CPLUSPLUS20) && (SIMDJSON_CPLUSPLUS >= 202002L)
#define SIMDJSON_CPLUSPLUS20 1
#endif
// C++ 17
#if !defined(SIMDJSON_CPLUSPLUS17) && (SIMDJSON_CPLUSPLUS >= 201703L)
#define SIMDJSON_CPLUSPLUS17 1
-6
View File
@@ -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"
+4
View File
@@ -123,6 +123,10 @@ inline simdjson_result<element> array::at(size_t index) const noexcept {
return INDEX_OUT_OF_BOUNDS;
}
inline array::operator element() const noexcept {
return element(tape);
}
//
// array::iterator inline implementation
//
+9 -1
View File
@@ -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.
*
@@ -123,6 +126,11 @@ public:
*/
inline simdjson_result<element> at(size_t index) const noexcept;
/**
* Implicitly convert object to element
*/
inline operator element() const noexcept;
private:
simdjson_inline array(const internal::tape_ref &tape) noexcept;
internal::tape_ref tape;
+5 -1
View File
@@ -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);
}
}
+21 -1
View File
@@ -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
+2 -3
View File
@@ -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);
/**
-121
View File
@@ -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
+4
View File
@@ -153,6 +153,10 @@ inline simdjson_result<element> object::at_key_case_insensitive(std::string_view
return NO_SUCH_FIELD;
}
inline object::operator element() const noexcept {
return element(tape);
}
//
// object::iterator inline implementation
//
+10 -2
View File
@@ -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.
*
@@ -197,6 +200,11 @@ public:
*/
inline simdjson_result<element> at_key_case_insensitive(std::string_view key) const noexcept;
/**
* Implicitly convert object to element
*/
inline operator element() const noexcept;
private:
simdjson_inline object(const internal::tape_ref &tape) noexcept;
-17
View File
@@ -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
-231
View File
@@ -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
+1 -1
View File
@@ -191,7 +191,7 @@ simdjson_inline size_t parser::capacity() const noexcept {
simdjson_inline size_t parser::max_capacity() const noexcept {
return _max_capacity;
}
simdjson_inline size_t parser::max_depth() const noexcept {
simdjson_pure simdjson_inline size_t parser::max_depth() const noexcept {
return implementation ? implementation->max_depth() : DEFAULT_MAX_DEPTH;
}
+10 -1
View File
@@ -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.
@@ -518,7 +527,7 @@ public:
*
* @return Maximum depth, in bytes.
*/
simdjson_inline size_t max_depth() const noexcept;
simdjson_pure simdjson_inline size_t max_depth() const noexcept;
/**
* Set max_capacity. This is the largest document this parser can automatically support.
+3 -3
View 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:
+2 -1
View File
@@ -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);
+6 -1
View File
@@ -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
+61 -12
View File
@@ -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
+58 -1
View File
@@ -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
+18 -1
View File
@@ -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
+77 -11
View File
@@ -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
+40 -1
View File
@@ -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
@@ -38,11 +38,32 @@ simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> field::un
return answer;
}
template <typename string_type>
simdjson_inline simdjson_warn_unused error_code field::unescaped_key(string_type& receiver, bool allow_replacement) noexcept {
std::string_view key;
SIMDJSON_TRY( unescaped_key(allow_replacement).get(key) );
receiver = key;
return SUCCESS;
}
simdjson_inline raw_json_string field::key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
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 +97,28 @@ 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);
}
template<typename string_type>
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::field>::unescaped_key(string_type &receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(receiver, allow_replacement);
}
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());
+33 -2
View File
@@ -36,12 +36,39 @@ public:
* This consumes the key: once you have called unescaped_key(), you cannot
* call it again nor can you call key().
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescaped_key(bool allow_replacement) noexcept;
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
/**
* Get the key as a string_view (for higher speed, consider raw_key).
* We deliberately use a more cumbersome name (unescaped_key) to force users
* to think twice about using it. The content is stored in the receiver.
*
* This consumes the key: once you have called unescaped_key(), you cannot
* call it again nor can you call key().
*/
template <typename string_type>
simdjson_inline simdjson_warn_unused error_code unescaped_key(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Get the key as a raw_json_string. Can be used for direct comparison with
* an unescaped C string: e.g., key() == "test".
* an unescaped C string: e.g., key() == "test". This does not count as
* consumption of the content: you can safely call it repeatedly.
* See escaped_key() for a similar function which returns
* a more convenient std::string_view result.
*/
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. This does not count as
* consumption of the content: you can safely call it repeatedly.
* See escaped_key().
*/
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). It does not count as a consumption of the content:
* you can safely call it repeatedly. Use unescaped_key() to get the unescaped key.
*/
simdjson_inline std::string_view escaped_key() const noexcept;
/**
* Get the field value.
*/
@@ -73,7 +100,11 @@ public:
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
template<typename string_type>
simdjson_inline error_code unescaped_key(string_type &receiver, 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...
@@ -331,11 +354,23 @@ simdjson_inline token_position json_iterator::position() const noexcept {
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape(raw_json_string in, bool allow_replacement) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
auto result = parser->unescape(in, _string_buf_loc, allow_replacement);
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
return result;
#else
return parser->unescape(in, _string_buf_loc, allow_replacement);
#endif
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape_wobbly(raw_json_string in) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
auto result = parser->unescape_wobbly(in, _string_buf_loc);
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
return result;
#else
return parser->unescape_wobbly(in, _string_buf_loc);
#endif
}
simdjson_inline void json_iterator::reenter_child(token_position position, depth_t child_depth) noexcept {
@@ -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;
}
+17 -1
View File
@@ -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
+23 -4
View File
@@ -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,11 +144,24 @@ 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
* can iterate through the object more than once. If you unescape a string within
* the object more than once, you have unsafe code. Note that rewinding an object
* can iterate through the object more than once. If you unescape a string or a key
* within the object more than once, you have unsafe code. Note that rewinding an object
* means that you may need to reparse it anew: it is not a free operation.
*
* @returns true if the object contains some elements (not empty)
@@ -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
+29 -3
View File
@@ -42,6 +42,11 @@ simdjson_warn_unused simdjson_inline error_code parser::allocate(size_t new_capa
_max_depth = new_max_depth;
return SUCCESS;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline simdjson_warn_unused bool parser::string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept {
return (string_buf_loc < string_buf.get()) || (size_t(string_buf_loc - string_buf.get()) >= capacity());
}
#endif
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; }
@@ -58,6 +63,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));
}
@@ -129,13 +155,13 @@ inline simdjson_result<document_stream> parser::iterate_many(const padded_string
return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated);
}
simdjson_inline size_t parser::capacity() const noexcept {
simdjson_pure simdjson_inline size_t parser::capacity() const noexcept {
return _capacity;
}
simdjson_inline size_t parser::max_capacity() const noexcept {
simdjson_pure simdjson_inline size_t parser::max_capacity() const noexcept {
return _max_capacity;
}
simdjson_inline size_t parser::max_depth() const noexcept {
simdjson_pure simdjson_inline size_t parser::max_depth() const noexcept {
return _max_depth;
}
+19 -5
View File
@@ -13,8 +13,8 @@ namespace SIMDJSON_IMPLEMENTATION {
namespace ondemand {
/**
* The default batch size for document_stream instances for this On Demand kernel.
* Note that different On Demand kernel may use a different DEFAULT_BATCH_SIZE value
* The default batch size for document_stream instances for this On-Demand kernel.
* Note that different On-Demand kernel may use a different DEFAULT_BATCH_SIZE value
* in the future.
*/
static constexpr size_t DEFAULT_BATCH_SIZE = 1000000;
@@ -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 */
@@ -242,9 +245,9 @@ public:
simdjson_result<document_stream> iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete;
/** The capacity of this parser (the largest document it can process). */
simdjson_inline size_t capacity() const noexcept;
simdjson_pure simdjson_inline size_t capacity() const noexcept;
/** The maximum capacity of this parser (the largest document it is allowed to process). */
simdjson_inline size_t max_capacity() const noexcept;
simdjson_pure simdjson_inline size_t max_capacity() const noexcept;
simdjson_inline void set_max_capacity(size_t max_capacity) noexcept;
/**
* The maximum depth of this parser (the most deeply nested objects and arrays it can process).
@@ -252,7 +255,7 @@ public:
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*/
simdjson_inline size_t max_depth() const noexcept;
simdjson_pure simdjson_inline size_t max_depth() const noexcept;
/**
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
@@ -324,6 +327,17 @@ public:
*/
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept;
#if SIMDJSON_DEVELOPMENT_CHECKS
/**
* Returns true if string_buf_loc is outside of the allocated range for the
* the string buffer. When true, it indicates that the string buffer has overflowed.
* This is a development-time check that is not needed in production. It can be
* used to detect buffer overflows in the string buffer and usafe usage of the
* string buffer.
*/
bool string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept;
#endif
private:
/** @private [for benchmarking access] The implementation to use */
std::unique_ptr<internal::dom_parser_implementation> implementation{};
@@ -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.
*/
+74 -3
View File
@@ -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
+50 -9
View File
@@ -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);

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