Compare commits

..

101 Commits

Author SHA1 Message Date
Daniel Lemire 2db4592571 Last commit for version 0.9.0. (#1503)
* Last commit for version 0.9.0.

* Removing space.
2021-03-17 11:08:44 -04:00
Daniel Lemire e35088d6ff Update performance.md 2021-03-16 17:57:23 -04:00
Daniel Lemire 156b1b20e3 Update basics.md 2021-03-16 17:56:32 -04:00
Daniel Lemire 9294e8b098 Update performance.md 2021-03-16 17:52:59 -04:00
Daniel Lemire 6dc98561a9 Port the performance notes to "on demand". (#1496)
* Port the performance notes to "on demand".

* No more white space.

* Trimmed another space.
2021-03-16 17:32:38 -04:00
Daniel Lemire 4cfad7adf2 Update README.md 2021-03-12 15:02:47 -05:00
Daniel Lemire 3ab0d3ed94 Update README.md 2021-03-12 15:01:52 -05:00
Daniel Lemire 430f230940 Update README.md 2021-03-12 15:01:12 -05:00
Daniel Lemire 727644c13a Add files via upload 2021-03-12 14:57:39 -05:00
Daniel Lemire 3ab8eca09b Update basics.md 2021-03-12 14:24:14 -05:00
Daniel Lemire 0dc07b34a7 Update basics.md 2021-03-12 14:23:01 -05:00
Daniel Lemire 221c55b620 Update README.md 2021-03-12 14:21:23 -05:00
Daniel Lemire 02f9b83353 This moves us to On Demand as the default front-end. (#1494)
* This moves us to On Demand as the default front-end.

* Made casting magical

* Adding another section

* Undoing my damage.
2021-03-12 14:19:11 -05:00
John Keiser cfc965ff9a Merge pull request #1490 from simdjson/jkeiser/single-ondemand
Don't compile On Demand with extra flags
2021-03-09 16:03:58 -08:00
Daniel Lemire 033df0ebfb Update README.md 2021-03-09 16:29:36 -05:00
Daniel Lemire ac34640224 Update README.md 2021-03-09 16:29:05 -05:00
John Keiser a987192040 Remove SIMDJSON_CPP 2021-03-09 09:10:27 -08:00
John Keiser 751696d7eb Move implementation selection to implementations.h 2021-03-09 09:10:08 -08:00
Daniel Lemire 50aa1566ae Under Apple's compiler, mixing the undefined-behavior sanitizer with the address sanitizer is calling for trouble (#1493)
* Nicer support for Apple's compiler.

* Extending to SIMDJSON_SANITIZE_UNDEFINED

* Better wording.
2021-03-09 11:39:43 -05:00
Daniel Lemire 0a5bba7235 Provides a more correct simdjson::ondemand implementation message. (#1492) 2021-03-09 11:39:19 -05:00
Daniel Lemire 8b8af6aee5 Making input capacity more robust. (#1488) 2021-03-09 09:58:38 -05:00
Daniel Lemire 8e8fbc4cff fixing issue 1480 (#1485) 2021-03-08 19:31:42 -05:00
John Keiser 985dfab2c4 Don't use TARGET unless the target options are *not* specified
This eliminates the possibility of inlining target failures for ondemand

Also makes it so we always compile common architectures needed by simdjson.cpp in simdjson.h, since amalgamation has no way to reason about whether to include / exclude it.
2021-03-08 13:49:09 -08:00
John Keiser 633161fe86 Don't include target flags if the compiler already has them on 2021-03-08 13:48:58 -08:00
John Keiser f51d50399c Only include builtin implementation from header 2021-03-08 13:48:53 -08:00
John Keiser cf4e538536 Separate builtin implementation from "all implementations" 2021-03-06 13:08:42 -08:00
John Keiser ec5ba79447 Add base.h to allow src/ to pick and choose includes 2021-03-05 11:48:34 -08:00
John Keiser 0948573e63 Merge pull request #1479 from simdjson/jkeiser/raw_json_token
Add value.raw_json_token()
2021-03-05 10:24:36 -08:00
John Keiser bad582c2d3 Add value.raw_json_token() 2021-03-05 09:07:41 -08:00
John Keiser f55893807a Merge pull request #1473 from simdjson/jkeiser/promise-padded
Support user-provided buffers in On Demand
2021-03-04 20:56:34 -08:00
Daniel Lemire dc2f767171 Ensuring that stage 1's json_block and cie are never copied and using explicit constructors (issue 1475) (#1478) 2021-03-04 07:59:36 -05:00
John Keiser f0e92e3bdd Pass "capacity" straight to iterate, support std::string 2021-03-03 12:51:00 -08:00
John Keiser 3db1a214ce Support user-provided buffers via promise_padded 2021-03-03 12:50:56 -08:00
John Keiser 79e94227c2 Merge pull request #1472 from simdjson/jkeiser/ondemand-type
Add ondemand::value.type()
2021-03-03 12:49:20 -08:00
John Keiser 665514692a Remove UTF-8 from source code 2021-03-03 12:10:59 -08:00
John Keiser 9944db6d73 Move json_type to ondemand to prevent target mismatch inline errors 2021-03-02 18:31:17 -08:00
John Keiser 2ed24666b5 Add value.type() 2021-03-02 17:02:50 -08:00
Daniel Lemire 0c199cffc3 Removing mallocs in main library. (#1468) 2021-03-02 13:09:22 -05:00
John Keiser 29fe1866ef Move dom / implementations include into their own spots 2021-03-02 09:53:28 -08:00
Daniel Lemire 4811c8036b Update the tape description and the dump_tape function (#1465)
* Would fix issue 1446

* Trimming spaces.
2021-03-01 18:47:11 -05:00
Daniel Lemire 036151c1e3 Test that we can copy the iterators. (#1466) 2021-03-01 18:46:50 -05:00
rychale 9d22372cc9 Make dom::document_stream::iterator copyable and default-constructible by holding pointer to document_stream instead of ref (#1463)
Co-authored-by: Alexey Rychkov <arychkov@defytrading.com>
2021-03-01 14:40:51 -05:00
Daniel Lemire a3d3e347a2 Adding -mno-avx256-split-unaligned-load -mno-avx256-split-unaligned-store at GNU GCC under x86/x64 systems. (#1462) 2021-03-01 14:16:30 -05:00
Daniel Lemire 9577c54999 Provide the CMake install the necessarily information (and flags) to hand Windows DLL and add Windows installation tests (#1457)
* This gives the CMake install the necessarily information (and flags) to know
whether we have a Windows DLL and in such cases how to handle the linkage.
2021-02-26 16:17:05 -05:00
Daniel Lemire 72c40be553 This adds tests without threads under linux. (#1460) 2021-02-26 16:16:10 -05:00
Daniel Lemire 6aa8877bf6 This adds tests for the no-exception variant of the library usage under linux. (#1459)
* This adds tests for the no-exception variant of the library usage under linux.

* Fixed name.

* Syntax fix.
2021-02-26 16:15:34 -05:00
Daniel Lemire ef1e256fa7 Workaround for SIMDJSON_ASSUME "side-effect" warning under some compilers. (#1456) 2021-02-25 08:50:57 -05:00
Daniel Lemire ad37651726 Guarding undefined templates with a static_assert. (#1454)
* Guarding undefined templates with a static_assert.

* Fixing comments.

* Undeprecating (or whatever you want to call it).
2021-02-22 16:54:47 -05:00
John Keiser 92caeb039f Merge pull request #1453 from simdjson/jkeiser/number-overrun
Fix ridiculously unlikely buffer overrun in ondemand
2021-02-22 09:39:43 -08:00
John Keiser cdb607f202 Add comments on confusing "simdjson_unused" 2021-02-22 09:39:09 -08:00
John Keiser bcab8d3abf Check for end object/array at top level
This avoids a very unlikely buffer overrun that can occur in a particular kind of invalid JSON:
- the document is invalid with an unclosed top level array or object
- the last thing in the document is a number that ends at EOF
- the padding is filled entirely with numeric digits
2021-02-22 09:35:21 -08:00
John Keiser 9d747642fe Merge pull request #1452 from simdjson/jkeiser/safe-iter-fix
Fix bug reporting out of order iteration
2021-02-22 09:34:49 -08:00
John Keiser a755203aee Update value_iterator-inl.h 2021-02-22 09:34:20 -08:00
John Keiser 814726e5d4 Merge pull request #1432 from simdjson/jkeiser/safe-lookup
Alert user when they use object indexing incorrectly
2021-02-22 09:31:59 -08:00
John Keiser c4a312e6c3 Fix strict overflow warning 2021-02-21 14:06:58 -08:00
Daniel Lemire 81609393f1 Fixing issue 1449. (#1451) 2021-02-21 16:33:05 -05:00
John Keiser 0634958329 Don't emit out of order iteration error for empty array 2021-02-21 11:43:36 -08:00
John Keiser b352b903e7 Fix bug where iterators didn't always report errors 2021-02-21 11:43:36 -08:00
John Keiser 74d6658f39 Make out of order iteration tests actually test errors in the loop 2021-02-21 11:43:36 -08:00
John Keiser 3076de0405 Use SIMDJSON_DEVELOPMENT_CHECKS instead of SIMDJSON_PRODUCTION
Don't enable in retail
2021-02-20 11:46:01 -08:00
John Keiser 4a0a0ed4c6 Split more tests into separate methods 2021-02-20 11:22:24 -08:00
Vadim Peretokin c5def8f706 Document SIMDJSON_EXCEPTIONS (#1443)
* Document SIMDJSON_EXCEPTIONS

* Add an example

* Enchance clarity
2021-02-17 14:30:58 -05:00
Daniel Lemire 610b3ad302 Adds Visual Studio 2017 to CI (for real) and adapt our build/tests (#1444) 2021-02-15 19:49:12 -05:00
Daniel Lemire 4c63a929bc This makes it possible to a have document instance (DOM) that is separate from the parser if you would like. (#1430)
* This makes it possible to a have document instance that is separate from the parser if you would like.
2021-02-10 14:44:53 -05:00
Daniel Lemire 0f72ff3a57 Adding Visual studio 2017 CI tests to GitHub actions. (#1438)
* Adding Visual studio 2017 CI tests to GitHub actions.

* Fixing the name.
2021-02-09 10:05:45 -05:00
Daniel Lemire 67afcd4edd Adding legacy Win32 / Visual Studio 2017 CI tests to GitHub Actions. (#1439) 2021-02-09 10:05:33 -05:00
David CARLIER 6e9ada2d6c unit test macos build fix (#1434) 2021-02-08 14:12:39 -05:00
John Keiser 9651efe626 Split up tests for compile times 2021-02-06 11:07:14 -08:00
John Keiser df7201ba42 Fix Windows assume error 2021-02-06 11:06:53 -08:00
John Keiser 14315ec5cd Default SIMDJSON_PRODUCTION to OFF for bare header usage 2021-02-06 11:06:37 -08:00
John Keiser 0f10fc9ad9 Fix Windows _assume warning 2021-02-05 18:53:39 -08:00
John Keiser ce678fd986 Fix GCC 7 strict-overflow warning 2021-02-05 18:53:31 -08:00
John Keiser 9d693da852 Only set container depth when a container iteration starts 2021-02-05 17:20:24 -08:00
John Keiser 22742b6bd6 Make max_depth() a simple check 2021-02-05 17:11:03 -08:00
John Keiser a33bf40a7d Add tests for sibling indexing detection 2021-02-05 16:39:52 -08:00
John Keiser 3801ea7777 Disable all OUT_OF_ORDER_ITERATION checks when SIMDJSON_API_USAGE_CHECKS
is off
2021-02-05 16:39:44 -08:00
John Keiser c7935ceed1 Put parser capacity / max_depth back into parser 2021-02-05 16:39:36 -08:00
John Keiser ea119a5679 Start parsing at depth 1 instead of using descend_to for it 2021-02-05 16:39:34 -08:00
John Keiser 7a324da548 Add -DSIMDJSON_PRODUCTION flag 2021-02-05 16:34:27 -08:00
John Keiser 0d1c99a6ad Allow object lookup safety to be disabled
Use cmake -DSIMDJSON_API_USAGE_CHECKS=OFF ..
2021-02-05 10:18:01 -08:00
John Keiser e4626d233c Descend into fields at the value position, not the key 2021-02-05 10:18:01 -08:00
John Keiser 9934f65987 Store start index of each depth for safety 2021-02-05 10:17:28 -08:00
John Keiser b2de2dfd1b Merge pull request #1416 from simdjson/jkeiser/safe-iterators-2
Add safety checks for out of order array/object iteration+indexing
2021-02-05 09:47:03 -08:00
John Keiser 3f2639a655 Merge pull request #1414 from simdjson/jkeiser/array-assert
Fix #1409 (assert when trying to get one value as multiple types)
2021-02-05 09:45:49 -08:00
Daniel Lemire 26b5b19f88 Unneeded. 2021-02-02 17:58:10 -05:00
Daniel Lemire 5449365658 This should make issue 1370 easier to track. (#1429)
* This should make issue 1370 easier to track.

* Avoiding exceptions.
2021-02-02 16:12:16 -05:00
Daniel Lemire 96536239c2 Deleting the function. (#1428) 2021-02-02 09:48:01 -05:00
Daniel Lemire 0e18453e34 Potential optimizations applied to jkeiser/array-assert (#1421)
* Some tuning.
* Using table lookups...
2021-02-01 12:39:10 -05:00
Daniel Lemire 777202e1f1 Why would you use a reference when looping? (#1422)
* Why would you use a reference?

* I missed a few cases.
2021-02-01 12:30:36 -05:00
Daniel Lemire 6f61ed1477 It appears that Qt uses macros for common terms like slots, signals and so forth. (#1425) 2021-02-01 11:31:29 -05:00
Daniel Lemire a509e04f9b Let us actually test the find package functionality. (#1418)
* Let us actually test the find package functionality.

* Specifying the path.

* Fixing the path.
2021-01-28 13:59:35 -05:00
tobim 152eb983ff Fix the CMake generated install target (#1412)
* Re-enable installation of simdjson-config.cmake

* Install a simdjson-config-version.cmake file
2021-01-27 18:39:05 -05:00
Daniel Lemire d6f33e4830 This adds a little test to see if we can compiler with very strict flags (conventional casts) (#1417)
* This adds a little test to see if we can compiler with very strict flags.

* Trimming a leftover old-style cast.

* More cleaning.

* A few more pedantic casts.
2021-01-27 18:37:30 -05:00
Tibbel 5613d30e97 partly replacement old-style-cast to c++ *_cast (#1403)
Co-authored-by: Tibbel <tibbel@ma-gi.de>
2021-01-27 13:33:48 -05:00
John Keiser 1bfbb6448a Check out-of-order error in object index 2021-01-26 20:49:14 -08:00
John Keiser c5b44f44f9 Add partial out-of-order check for field lookup 2021-01-26 20:00:39 -08:00
John Keiser 22b3ea93a8 Emit an error if user tries to iterate arrays out of order 2021-01-26 20:00:19 -08:00
John Keiser fe726b0f80 Split up ondemand_dom_api_tests for sanitize build times 2021-01-26 19:42:37 -08:00
John Keiser 18ecc0032d Reenable test that is now working 2021-01-26 15:15:09 -08:00
John Keiser 1a1532c8cc Return INCORRECT_TYPE when numbers fail to parse
Also add tests for trying to get multiple types in a row
2021-01-26 14:59:13 -08:00
John Keiser e6d2b7759a Fix assertion when getting array after failing to get a scalar
Also remove distinction between & and && for array start, acting like
other types
2021-01-26 14:09:54 -08:00
164 changed files with 10349 additions and 25388 deletions
+2 -2
View File
@@ -24,6 +24,6 @@ jobs:
run: |
mkdir build &&
cd build &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake -DCMAKE_CXX_FLAGS="-Werror=old-style-cast -pedantic -Wpedantic" -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . --target checkperf &&
ctest --output-on-failure -R checkperf
ctest --output-on-failure -R checkperf
+34
View File
@@ -0,0 +1,34 @@
name: Ubuntu 20.04 CI (GCC 9) without exceptions
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_EXCEPTIONS=OFF -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest -j --output-on-failure -LE explicitonly &&
make install &&
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
mkdir testfindpackage &&
cd testfindpackage &&
echo -e 'cmake_minimum_required(VERSION 3.1)\nproject(simdjsontester)\nset(CMAKE_CXX_STANDARD 17)\nfind_package(simdjson REQUIRED)'> CMakeLists.txt && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../destination .. && cmake --build .
+34
View File
@@ -0,0 +1,34 @@
name: Ubuntu 20.04 CI (GCC 9) without threads
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_ENABLE_THREADS=OFF -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest -j --output-on-failure -LE explicitonly &&
make install &&
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
mkdir testfindpackage &&
cd testfindpackage &&
echo -e 'cmake_minimum_required(VERSION 3.1)\nproject(simdjsontester)\nset(CMAKE_CXX_STANDARD 17)\nfind_package(simdjson REQUIRED)'> CMakeLists.txt && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../destination .. && cmake --build .
+4 -2
View File
@@ -27,5 +27,7 @@ jobs:
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest -j --output-on-failure -LE explicitonly &&
make install &&
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json
cmake --install . &&
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
cd ../tests/installation_tests/find &&
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .
+46
View File
@@ -0,0 +1,46 @@
name: VS15-CI
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ci:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
name: windows-vs15
runs-on: windows-2016
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 15 2017, arch: Win32, static: ON}
- {gen: Visual Studio 15 2017, arch: Win32, static: OFF}
- {gen: Visual Studio 15 2017, arch: x64, static: ON}
- {gen: Visual Studio 15 2017, arch: x64, static: OFF}
steps:
- name: checkout
uses: actions/checkout@v2
- name: Configure
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_BUILD_STATIC=${{matrix.static}} -B build
- name: Build Debug
run: cmake --build build --config Debug --verbose
- name: Build Release
run: cmake --build build --config Release --verbose
- name: Run tests
run: |
cd build
ctest -C Release -LE explicitonly --output-on-failure
- name: Install
run: |
cmake --install build --config Release
- 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
+28 -21
View File
@@ -15,25 +15,32 @@ jobs:
! contains(toJSON(github.event.commits.*.message), '[skip github]')
name: windows-vs16
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 16 2019, arch: Win32, static: ON}
- {gen: Visual Studio 16 2019, arch: Win32, static: OFF}
- {gen: Visual Studio 16 2019, arch: x64, static: ON}
- {gen: Visual Studio 16 2019, arch: x64, static: OFF}
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: 'Run CMake with VS16'
uses: lukka/run-cmake@v2
with:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildDirectory: "${{ github.workspace }}/../../_temp/windows"
cmakeBuildType: Release
buildWithCMake: true
cmakeGenerator: VS16Win64
cmakeAppendedArgs: -DSIMDJSON_COMPETITION=OFF
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
- name: checkout
uses: actions/checkout@v2
- name: Configure
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_BUILD_STATIC=${{matrix.static}} -B build
- name: Build Debug
run: cmake --build build --config Debug --verbose
- name: Build Release
run: cmake --build build --config Release --verbose
- name: Run tests
run: |
cd build
ctest -C Release -LE explicitonly --output-on-failure
- name: Install
run: |
cmake --install build --config Release
- 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
+7 -8
View File
@@ -21,18 +21,17 @@ jobs:
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: 'Run CMake with VS16'
uses: lukka/run-cmake@v2
- name: 'Run CMake with VS16 Clang'
uses: lukka/run-cmake@v3
with:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildDirectory: "${{ github.workspace }}/../../_temp/windows"
cmakeBuildType: Release
cmakeBuildType: Release
buildWithCMake: true
cmakeGenerator: VS16Win64
cmakeAppendedArgs: -T ClangCL -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_BUILD_STATIC=ON
buildWithCMakeArgs: --config Release
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
+18 -6
View File
@@ -27,13 +27,25 @@ jobs:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildDirectory: "${{ github.workspace }}/../../_temp/windows"
cmakeBuildType: Release
cmakeBuildType: Release
buildWithCMake: true
cmakeGenerator: VS16Win64
cmakeAppendedArgs: -G Ninja -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_BUILD_STATIC=ON
buildWithCMakeArgs: --config Release
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -LE explicitonly --output-on-failure
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
- name: 'Install with CMake'
uses: lukka/run-cmake@v3
with:
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildWithCMakeArgs: '--target install'
- name: 'Test Installation with CMake'
uses: lukka/run-cmake@v3
with:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/tests/installation_tests/find/CMakeLists.txt'
cmakeBuildType: Release
buildWithCMake: true
buildDirectory: '${{ github.workspace }}/tests/installation_tests/find/buildDirectory'
cmakeAppendedArgs: -G Ninja
buildWithCMakeArgs: '--config Release --verbose'
+7 -7
View File
@@ -19,18 +19,18 @@ jobs:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: 'Run CMake with VS16'
uses: lukka/run-cmake@v2
uses: lukka/run-cmake@v3
with:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildDirectory: "${{ github.workspace }}/../../_temp/windows"
cmakeBuildType: Release
cmakeBuildType: Release
buildWithCMake: true
cmakeGenerator: VS16Win64
cmakeGenerator: VS16Win64
cmakeAppendedArgs: -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_EXCEPTIONS=OFF
buildWithCMakeArgs: --config Release
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -LE explicitonly --output-on-failure
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
+14 -5
View File
@@ -1,16 +1,18 @@
cmake_minimum_required(VERSION 3.13)
# CMP0025: Compiler id for Apple Clang is now AppleClang.
# https://cmake.org/cmake/help/v3.17/policy/CMP0025.html
cmake_policy(SET CMP0025 NEW)
project(simdjson
DESCRIPTION "Parsing gigabytes of JSON per second"
LANGUAGES CXX C
)
set(PROJECT_VERSION_MAJOR 0)
set(PROJECT_VERSION_MINOR 8)
set(PROJECT_VERSION_MINOR 9)
set(PROJECT_VERSION_PATCH 0)
set(SIMDJSON_SEMANTIC_VERSION "0.8.0" CACHE STRING "simdjson semantic version")
set(SIMDJSON_LIB_VERSION "7.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "7" CACHE STRING "simdjson library soversion")
set(SIMDJSON_SEMANTIC_VERSION "0.9.0" CACHE STRING "simdjson semantic version")
set(SIMDJSON_LIB_VERSION "8.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "8" CACHE STRING "simdjson library soversion")
set(SIMDJSON_GITHUB_REPOSITORY https://github.com/simdjson/simdjson)
include(GNUInstallDirs)
@@ -50,6 +52,13 @@ configure_package_config_file("${PROJECT_SOURCE_DIR}/cmake/simdjson-config.cmake
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/simdjson"
NO_SET_AND_CHECK_MACRO
NO_CHECK_REQUIRED_COMPONENTS_MACRO)
write_basic_package_version_file(
"${PROJECT_BINARY_DIR}/simdjson-config-version.cmake"
VERSION ${SIMDJSON_SEMANTIC_VERSION}
COMPATIBILITY SameMinorVersion)
install(FILES "${PROJECT_BINARY_DIR}/simdjson-config.cmake"
"${PROJECT_BINARY_DIR}/simdjson-config-version.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/simdjson")
#
# Compile tools / tests / benchmarks
+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 = "0.8.0"
PROJECT_NUMBER = "0.9.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
+22 -75
View File
@@ -3,7 +3,7 @@
[![Ubuntu 20.04 CI](https://github.com/simdjson/simdjson/workflows/Ubuntu%2020.04%20CI%20(GCC%209)/badge.svg)](https://simdjson.org/plots.html)
![VS16-CI](https://github.com/simdjson/simdjson/workflows/VS16-CI/badge.svg)
![MinGW64-CI](https://github.com/simdjson/simdjson/workflows/MinGW64-CI/badge.svg)
[![][license img]][license] [![Doxygen Documentation](https://img.shields.io/badge/docs-doxygen-green.svg)](https://simdjson.org/api/0.8.0/index.html)
[![][license img]][license] [![Doxygen Documentation](https://img.shields.io/badge/docs-doxygen-green.svg)](https://simdjson.org/api/0.9.0/index.html)
simdjson : Parsing gigabytes of JSON per second
===============================================
@@ -11,12 +11,11 @@ simdjson : Parsing gigabytes of JSON per second
<img src="images/logo.png" width="10%" style="float: right">
JSON is everywhere on the Internet. Servers spend a *lot* of time parsing it. We need a fresh
approach. The simdjson library uses commonly available SIMD instructions and microparallel algorithms
to parse JSON 2.5x faster than RapidJSON and 25x faster than JSON for Modern C++.
to parse JSON 4x faster than RapidJSON and 25x faster than JSON for Modern C++.
* **Fast:** Over 2.5x faster than commonly used production-grade JSON parsers.
* **Fast:** Over 4x faster than commonly used production-grade JSON parsers.
* **Record Breaking Features:** Minify JSON at 6 GB/s, validate UTF-8 at 13 GB/s, NDJSON at 3.5 GB/s.
* **Easy:** First-class, easy to use and carefully documented APIs.
* **Beyond DOM:** Try the new On Demand API for twice the speed (>4GB/s).
* **Strict:** Full JSON and UTF-8 validation, lossless parsing. Performance with no compromises.
* **Automatic:** Selects a CPU-tailored parser at runtime. No configuration needed.
* **Reliable:** From memory allocation to error handling, simdjson's design avoids surprises.
@@ -54,13 +53,16 @@ The simdjson library is easily consumable with a single .h and .cpp file.
```
2. Create `quickstart.cpp`:
```c++
#include "simdjson.h"
int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets = parser.load("twitter.json");
std::cout << tweets["search_metadata"]["count"] << " results." << std::endl;
}
```c++
#include "simdjson.h"
using namespace simdjson;
int main(void) {
ondemand::parser parser;
padded_string json = padded_string::load("twitter.json");
ondemand::document tweets = parser.iterate(json);
std::cout << uint64_t(tweets["search_metadata"]["count"]) << " results." << std::endl;
}
```
3. `c++ -o quickstart quickstart.cpp simdjson.cpp`
4. `./quickstart`
@@ -68,34 +70,6 @@ The simdjson library is easily consumable with a single .h and .cpp file.
100 results.
```
### On Demand
The new On Demand JSON parser is just as easy, but much faster due to just-in-time parsing. It is in
alpha right now. More information can be found in the [On Demand Guide](doc/ondemand.md).
1. Do step 1 of the [Quick Start](#quick-start).
2. Create `quickstart.cpp`:
```c++
#include "simdjson.h"
using namespace simdjson;
int main(void) {
ondemand::parser parser;
padded_string json = padded_string::load("twitter.json");
ondemand::document tweets = parser.iterate(json);
std::cout << uint64_t(tweets["search_metadata"]["count"]) << " results." << std::endl;
}
```
3. `c++ -march=native -o quickstart quickstart.cpp simdjson.cpp`
4. `./quickstart`
```
100 results.
```
You'll notice that the code here is very similar to the [main Quick Start code](#quick-start) (and
indeed, it does the same thing). However, if you compare the performance, you should find On
Demand much faster.
Documentation
-------------
@@ -105,59 +79,31 @@ Usage documentation is available:
* [Performance](doc/performance.md) shows some more advanced scenarios and how to tune for them.
* [Implementation Selection](doc/implementation-selection.md) describes runtime CPU detection and
how you can work with it.
* [API](https://simdjson.org/api/0.8.0/annotated.html) contains the automatically generated API documentation.
* [API](https://simdjson.org/api/0.9.0/annotated.html) contains the automatically generated API documentation.
Performance results
-------------------
The simdjson library uses three-quarters less instructions than state-of-the-art parser [RapidJSON](https://rapidjson.org) and
fifty percent less than sajson. To our knowledge, simdjson is the first fully-validating JSON parser
The simdjson library uses three-quarters less instructions than state-of-the-art parser [RapidJSON](https://rapidjson.org). To our knowledge, simdjson is the first fully-validating JSON parser
to run at [gigabytes per second](https://en.wikipedia.org/wiki/Gigabyte) (GB/s) on commodity processors. It can parse millions of JSON documents per second on a single core.
The following figure represents parsing speed in GB/s for parsing various files
on an Intel Skylake processor (3.4 GHz) using the GNU GCC 9 compiler (with the -O3 flag).
We compare against the best and fastest C++ libraries.
on an Intel Skylake processor (3.4 GHz) using the GNU GCC 10 compiler (with the -O3 flag).
We compare against the best and fastest C++ libraries on benchmarks that load and process the data.
The simdjson library offers full unicode ([UTF-8](https://en.wikipedia.org/wiki/UTF-8)) validation and exact
number parsing. The RapidJSON library is tested in two modes: fast and
exact number parsing. The sajson library offers fast (but not exact)
number parsing and partial unicode validation. In this data set, the file
sizes range from 65KB (github_events) all the way to 3.3GB (gsoc-2018).
Many files are mostly made of numbers: canada, mesh.pretty, mesh, random
and numbers: in such instances, we see lower JSON parsing speeds due to the
high cost of number parsing. The simdjson library uses exact number parsing which
is particular taxing.
<img src="doc/gbps.png" width="90%">
On a Skylake processor, the parsing speeds (in GB/s) of various processors on the twitter.json file are as follows, using again GNU GCC 9.1 (with the -O3 flag). The popular JSON for Modern C++ library is particularly slow: it obviously trades parsing speed for other desirable features.
| parser | GB/s |
| ------------------------------------- | ---- |
| simdjson | 2.5 |
| RapidJSON UTF8-validation | 0.29 |
| RapidJSON UTF8-valid., exact numbers | 0.28 |
| RapidJSON insitu, UTF8-validation | 0.41 |
| RapidJSON insitu, UTF8-valid., exact | 0.39 |
| sajson (insitu, dynamic) | 0.62 |
| sajson (insitu, static) | 0.88 |
| dropbox | 0.13 |
| fastjson | 0.27 |
| gason | 0.59 |
| ultrajson | 0.34 |
| jsmn | 0.25 |
| cJSON | 0.31 |
| JSON for Modern C++ (nlohmann/json) | 0.11 |
number parsing.
<img src="doc/rome.png" width="60%">
The simdjson library offers high speed whether it processes tiny files (e.g., 300 bytes)
or larger files (e.g., 3MB). The following plot presents parsing
speed for [synthetic files over various sizes generated with a script](https://github.com/simdjson/simdjson_experiments_vldb2019/blob/master/experiments/growing/gen.py) on a 3.4 GHz Skylake processor (GNU GCC 9, -O3).
<img src="doc/growing.png" width="90%">
<img src="doc/growing.png" width="60%">
[All our experiments are reproducible](https://github.com/simdjson/simdjson_experiments_vldb2019).
You can go beyond 4 GB/s with our new [On Demand API](https://github.com/simdjson/simdjson/blob/master/doc/ondemand.md).
For NDJSON files, we can exceed 3 GB/s with [our multithreaded parsing functions](https://github.com/simdjson/simdjson/blob/master/doc/parse_many.md).
@@ -180,6 +126,7 @@ We distinguish between "bindings" (which just wrap the C++ code) and a port to a
- [ZippyJSON](https://github.com/michaeleisel/zippyjson): Swift bindings for the simdjson project.
- [libpy_simdjson](https://github.com/gerrymanoim/libpy_simdjson/): high-speed Python bindings for simdjson using [libpy](https://github.com/quantopian/libpy).
- [pysimdjson](https://github.com/TkTech/pysimdjson): Python bindings for the simdjson project.
- [cysimdjson](https://github.com/TeskaLabs/cysimdjson): high-speed Python bindings for the simdjson project.
- [simdjson-rs](https://github.com/simd-lite): Rust port.
- [simdjson-rust](https://github.com/SunDoge/simdjson-rust): Rust wrapper (bindings).
- [SimdJsonSharp](https://github.com/EgorBo/SimdJsonSharp): C# version for .NET Core (bindings and full port).
+1 -1
View File
@@ -522,7 +522,7 @@ static void twitter_image_sizes(State& state) {
dom::array media;
if (not (error = tweet["entities"]["media"].get(media))) {
for (dom::object image : media) {
for (auto size : image["sizes"].get<dom::object>()) {
for (auto size : image["sizes"].get_object()) {
image_sizes.emplace(size.value["w"], size.value["h"]);
}
}
+5 -5
View File
@@ -321,7 +321,7 @@ struct benchmarker {
allocate_stage << allocate_count;
// Run it once to get hot buffers
if(hotbuffers) {
auto result = parser.parse((const uint8_t *)json.data(), json.size());
auto result = parser.parse(reinterpret_cast<const uint8_t *>(json.data()), json.size());
if (result.error()) {
exit_error(string("Failed to parse ") + filename + string(":") + error_message(result.error()));
}
@@ -331,7 +331,7 @@ struct benchmarker {
// Stage 1 (find structurals)
collector.start();
error = parser.implementation->stage1((const uint8_t *)json.data(), json.size(), false);
error = parser.implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.size(), false);
event_count stage1_count = collector.end();
stage1 << stage1_count;
if (error) {
@@ -367,7 +367,7 @@ struct benchmarker {
void run_loop(size_t iterations) {
dom::parser parser;
auto firstresult = parser.parse((const uint8_t *)json.data(), json.size());
auto firstresult = parser.parse(reinterpret_cast<const uint8_t *>(json.data()), json.size());
if (firstresult.error()) {
exit_error(string("Failed to parse ") + filename + string(":") + error_message(firstresult.error()));
}
@@ -375,7 +375,7 @@ struct benchmarker {
collector.start();
// some users want something closer to "number of documents per second"
for(size_t i = 0; i < iterations; i++) {
auto result = parser.parse((const uint8_t *)json.data(), json.size());
auto result = parser.parse(reinterpret_cast<const uint8_t *>(json.data()), json.size());
if (result.error()) {
exit_error(string("Failed to parse ") + filename + string(":") + error_message(result.error()));
}
@@ -446,7 +446,7 @@ struct benchmarker {
void print(bool tabbed_output) const {
if (tabbed_output) {
char* filename_copy = (char*)malloc(strlen(filename)+1);
char* filename_copy = reinterpret_cast<char*>(malloc(strlen(filename)+1));
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Validated CRT_SECURE safe here
strcpy(filename_copy, filename);
+1 -1
View File
@@ -7,7 +7,7 @@
# Clone the repository if it's not there
find_package(Git QUIET)
if (Git_FOUND AND (GIT_VERSION_STRING VERSION_GREATER "2.1.4") AND (NOT CMAKE_GENERATOR MATCHES Ninja) ) # We use "-C" which requires a recent git
if (Git_FOUND AND (GIT_VERSION_STRING VERSION_GREATER "2.1.4") AND (NOT CMAKE_GENERATOR MATCHES Ninja) AND (NOT MSVC) ) # We use "-C" which requires a recent git
message(STATUS "Git is available and it is recent. We are enabling checkperf targets.")
# sync_git_repository(myrepo ...) creates two targets:
# myrepo - if the repo does not exist, creates and syncs it against the origin branch
@@ -11,7 +11,8 @@ void maybe_display_implementation() {
if(!displayed_implementation) {
displayed_implementation = true;
std::cout << "simdjson::dom implementation: " << simdjson::active_implementation->name() << std::endl;
std::cout << "simdjson::ondemand implementation: " << simdjson::builtin_implementation()->name() << std::endl;
std::cout << "simdjson::ondemand implementation (stage 1): " << simdjson::active_implementation->name() << std::endl;
std::cout << "simdjson::ondemand implementation (stage 2): " << simdjson::builtin_implementation()->name() << std::endl;
}
}
+4 -2
View File
@@ -16,14 +16,16 @@ struct yyjson_base {
switch (yyjson_get_subtype(val)) {
case YYJSON_SUBTYPE_UINT:
return yyjson_get_uint(val);
return double(yyjson_get_uint(val));
case YYJSON_SUBTYPE_SINT:
return yyjson_get_sint(val);
return double(yyjson_get_sint(val));
case YYJSON_SUBTYPE_REAL:
return yyjson_get_real(val);
default:
SIMDJSON_UNREACHABLE();
}
SIMDJSON_UNREACHABLE();
return 0.0; // unreachable
}
bool run(yyjson_doc *doc, std::vector<point> &result) {
+4 -2
View File
@@ -16,14 +16,16 @@ struct yyjson_base {
switch (yyjson_get_subtype(val)) {
case YYJSON_SUBTYPE_UINT:
return yyjson_get_uint(val);
return double(yyjson_get_uint(val));
case YYJSON_SUBTYPE_SINT:
return yyjson_get_sint(val);
return double(yyjson_get_sint(val));
case YYJSON_SUBTYPE_REAL:
return yyjson_get_real(val);
default:
SIMDJSON_UNREACHABLE();
}
SIMDJSON_UNREACHABLE();
return 0.0; // unreachable
}
bool run(yyjson_doc *doc, std::vector<point> &result) {
+47 -12
View File
@@ -16,18 +16,34 @@ add_library(simdjson-flags INTERFACE)
add_library(simdjson-internal-flags INTERFACE)
target_link_libraries(simdjson-internal-flags INTERFACE simdjson-flags)
option(SIMDJSON_SANITIZE_UNDEFINED "Sanitize undefined behavior" OFF)
if(SIMDJSON_SANITIZE_UNDEFINED)
target_compile_options(simdjson-flags INTERFACE -fsanitize=undefined -fno-sanitize-recover=all)
target_link_libraries(simdjson-flags INTERFACE -fsanitize=undefined -fno-sanitize-recover=all)
endif()
option(SIMDJSON_SANITIZE "Sanitize addresses" OFF)
if(SIMDJSON_SANITIZE)
target_compile_options(simdjson-flags INTERFACE -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all)
target_link_libraries(simdjson-flags INTERFACE -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all)
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
message(STATUS "The address sanitizer under Apple's clang appears to be incompatible with the undefined-behavior sanitizer.")
message(STATUS "You may set SIMDJSON_SANITIZE_UNDEFINED to sanitize undefined behavior.")
target_compile_options(simdjson-flags INTERFACE -fsanitize=address -fno-omit-frame-pointer -fno-sanitize-recover=all)
target_compile_definitions(simdjson-flags INTERFACE ASAN_OPTIONS=detect_leaks=1)
target_link_libraries(simdjson-flags INTERFACE -fsanitize=address -fno-omit-frame-pointer -fno-sanitize-recover=all)
else()
message(STATUS "Setting both the address sanitizer and the undefined sanitizer.")
target_compile_options(simdjson-flags INTERFACE -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all)
target_link_libraries(simdjson-flags INTERFACE -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all)
endif()
# Ubuntu bug for GCC 5.0+ (safe for all versions)
if (CMAKE_COMPILER_IS_GNUCC)
target_link_libraries(simdjson-flags INTERFACE -fuse-ld=gold)
endif()
endif()
if(SIMDJSON_SANITIZE_THREADS)
message(STATUS "Setting both the thread sanitizer and the undefined-behavior sanitizer.")
target_compile_options(simdjson-flags INTERFACE -fsanitize=thread -fsanitize=undefined -fno-sanitize-recover=all)
target_link_libraries(simdjson-flags INTERFACE -fsanitize=thread -fsanitize=undefined -fno-sanitize-recover=all)
@@ -38,10 +54,13 @@ if(SIMDJSON_SANITIZE_THREADS)
endif()
if (NOT CMAKE_BUILD_TYPE)
message(STATUS "No build type selected, default to Release")
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
if(SIMDJSON_SANITIZE)
message(WARNING "No build type selected and you have enabled the sanitizer. Consider setting CMAKE_BUILD_TYPE to Debug to help identify the eventual problems.")
# Deliberately not including SIMDJSON_SANITIZE_THREADS since thread behavior depends on the build type.
if(SIMDJSON_SANITIZE OR SIMDJSON_SANITIZE_UNDEFINED)
message(STATUS "No build type selected and you have enabled the sanitizer, default to Debug. Consider setting CMAKE_BUILD_TYPE.")
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE)
else()
message(STATUS "No build type selected, default to Release")
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
endif()
endif()
@@ -52,6 +71,11 @@ else()
option(SIMDJSON_USE_LIBCPP "Use the libc++ library" OFF)
endif()
if(MSVC AND NOT(SIMDJSON_BUILD_STATIC))
# This will require special handling.
set(SIMDJSON_WINDOWS_DLL TRUE)
endif()
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake")
# We compile tools, tests, etc. with C++ 17. Override yourself if you need on a target.
@@ -72,6 +96,10 @@ set(THREADS_PREFER_PTHREAD_FLAG ON)
option(SIMDJSON_VISUAL_STUDIO_BUILD_WITH_DEBUG_INFO_FOR_PROFILING "Under Visual Studio, add Zi to the compile flag and DEBUG to the link file to add debugging information to the release build for easier profiling inside tools like VTune" OFF)
if(MSVC)
if("${MSVC_TOOLSET_VERSION}" STRLESS "142")
set(SIMDJSON_LEGACY_VISUAL_STUDIO TRUE)
message (STATUS "A legacy Visual Studio version was detected. We recommend Visual Studio 2019 or better on a 64-bit system.")
endif()
if("${MSVC_TOOLSET_VERSION}" STREQUAL "140")
# Visual Studio 2015 issues warnings and we tolerate it, cmake -G"Visual Studio 14" ..
target_compile_options(simdjson-internal-flags INTERFACE /W0 /sdl)
@@ -91,6 +119,12 @@ else(MSVC)
target_compile_options(simdjson-internal-flags INTERFACE -Wsign-compare -Wshadow -Wwrite-strings -Wpointer-arith -Winit-self -Wconversion -Wno-sign-conversion)
endif(MSVC)
# workaround for GNU GCC poor AVX load/store code generation
if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_SYSTEM_PROCESSOR MATCHES "^(i.86|x86(_64)?)$"))
target_compile_options(simdjson-flags INTERFACE -mno-avx256-split-unaligned-load -mno-avx256-split-unaligned-store)
endif()
#
# Optional flags
#
@@ -172,11 +206,10 @@ endif()
#
# Other optional flags
#
option(SIMDJSON_ONDEMAND_SAFETY_RAILS "Validate ondemand user code at runtime to ensure it is being used correctly. Defaults to ON for debug builds, OFF for release builds." $<IF:$<CONFIG:DEBUG>,ON,OFF>)
if(SIMDJSON_ONDEMAND_SAFETY_RAILS)
message(STATUS "Ondemand safety rails enabled. Ondemand user code will be checked at runtime. This will be slower than normal!")
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_ONDEMAND_SAFETY_RAILS)
endif(SIMDJSON_ONDEMAND_SAFETY_RAILS)
option(SIMDJSON_DEVELOPMENT_CHECKS "Enable development-time aids, such as checks for incorrect API usage. Enabled by default in DEBUG." OFF)
if(SIMDJSON_DEVELOPMENT_CHECKS)
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_DEVELOPMENT_CHECKS)
endif()
option(SIMDJSON_BASH "Allow usage of bash within CMake" ON)
@@ -193,6 +226,8 @@ if(NOT SIMDJSON_EXCEPTIONS)
# Because we cannot change the flag above on an invidual target (yet), the definition below must similarly be added globally
add_definitions(-D_HAS_EXCEPTIONS=0)
elseif (CMAKE_COMPILER_IS_GNUCC)
target_link_libraries(simdjson-flags INTERFACE -fno-exceptions)
endif()
endif()
+206 -74
View File
@@ -5,8 +5,10 @@ An overview of what you need to know to use simdjson, with examples.
* [Requirements](#requirements)
* [Including simdjson](#including-simdjson)
* [Using simdjson with package managers](#using-simdjson-with-package-managers)
* [Using simdjson as a CMake dependency](#using-simdjson-as-a-cmake-dependency)
* [The Basics: Loading and Parsing JSON Documents](#the-basics-loading-and-parsing-json-documents)
* [Documents Are Iterators](#documents-are-iterators)
* [Using the Parsed JSON](#using-the-parsed-json)
* [C++11 Support and string_view](#c11-support-and-string_view)
* [C++17 Support](#c17-support)
@@ -64,16 +66,13 @@ include(FetchContent)
FetchContent_Declare(
simdjson
GIT_REPOSITORY https://github.com/simdjson/simdjson.git
GIT_TAG v0.5.0
GIT_TAG v0.9.0
GIT_SHALLOW TRUE)
set(SIMDJSON_JUST_LIBRARY ON CACHE INTERNAL "")
set(SIMDJSON_BUILD_STATIC ON CACHE INTERNAL "")
FetchContent_MakeAvailable(simdjson)
```
You should replace `GIT_TAG v0.5.0` by the version you need. If you omit `GIT_TAG v0.5.0`, you will work from the main branch of simdjson: we recommend that if you are working on production code,
You should replace `GIT_TAG v0.9.0` by the version you need. If you omit `GIT_TAG v0.9.0`, you will work from the main branch of simdjson: we recommend that if you are working on production code,
Elsewhere in your project, you can declare dependencies on simdjson with lines such as these:
@@ -90,88 +89,136 @@ See [our CMake demonstration](https://github.com/simdjson/cmake_demo_single_file
The CMake build in simdjson can be taylored with a few variables. You can see the available variables and their default values by entering the `cmake -LA` command.
The Basics: Loading and Parsing JSON Documents
----------------------------------------------
The simdjson library offers a simple DOM tree API, which you can access by creating a
`dom::parser` and calling the `load()` method:
The simdjson library offers a tree API, which you can access by creating a
`ondemand::parser` and calling the `iterate()` method:
```c++
dom::parser parser;
dom::element doc = parser.load(filename); // load and parse a file
ondemand::parser parser;
auto json = padded_string::load("twitter.json");
ondemand::document doc = parser.iterate(json); // position a pointer at the beginning of the JSON data
```
Or by creating a padded string (for efficiency reasons, simdjson requires a string with
SIMDJSON_PADDING bytes at the end) and calling `parse()`:
SIMDJSON_PADDING bytes at the end) and calling `iterate()`:
```c++
dom::parser parser;
dom::element doc = parser.parse("[1,2,3]"_padded); // parse a string, the _padded suffix creates a simdjson::padded_string instance
ondemand::parser parser;
auto json = "[1,2,3]"_padded; // The _padded suffix creates a simdjson::padded_string instance
ondemand::document doc = parser.iterate(json); // parse a string
```
The parsed document resulting from the `parser.load` and `parser.parse` calls depends on the `parser` instance. Thus the `parser` instance must remain in scope. Furthermore, you must have at most one parsed document in play per `parser` instance.
You cannot copy a `parser` instance, you may only move it.
If you have a buffer of your own with enough padding already (SIMDJSON_PADDING extra bytes allocated), you can use `padded_string_view` to pass it in:
If you need to keep a document around long term, you can keep or move the parser instance. Note that moving a parser instance, or keeping one in a movable data structure like vector or map, can cause any outstanding `element`, `object` or `array` instances to be invalidated. If you need to store a parser in a movable data structure, you should use a `std::unique_ptr` to avoid this invalidation(e.g., `std::unique_ptr<dom::parser> parser(new dom::parser{})`).
```c++
ondemand::parser parser;
char json[3+SIMDJSON_PADDING];
strcpy(json, "[1]");
ondemand::document doc = parser.iterate(json, strlen(json), sizeof(json));
```
During the`load` or `parse` calls, neither the input file nor the input string are ever modified. After calling `load` or `parse`, the source (either a file or a string) can be safely discarded. All of the JSON data is stored in the `parser` instance. The parsed document is also immutable in simdjson: you do not modify it by accessing it.
We recommend against creating many `std::string` or many `std::padding_string` instances in your application to store your JSON data.
Consider reusing the same buffers and limiting memory allocations.
For best performance, a `parser` instance should be reused over several files: otherwise you will needlessly reallocate memory, an expensive process. It is also possible to avoid entirely memory allocations during parsing when using simdjson. [See our performance notes for details](performance.md).
Documents Are Iterators
-----------------------
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).
The simdjson library relies on an approach to parsing JSON that we call "On Demand".
A `document` is *not* a fully-parsed JSON value; rather, it is an **iterator** over the JSON text.
This means that while you iterate an array, or search for a field in an object, it is actually
walking through the original JSON text, merrily reading commas and colons and brackets to make sure
you get where you are going. This is the key to On Demand's performance: since it's just an iterator,
it lets you parse values as you use them. And particularly, it lets you *skip* values you do not want
to use.
### Parser, Document and JSON Scope
Because a document is an iterator over the JSON text, both the JSON text and the parser must
remain alive (in scope) while you are using it. Further, a `parser` may have at most
one document open at a time, since it holds allocated memory used for the parsing.
During the `iterate` call, the original JSON text is never modified--only read. After you are done
with the document, the source (whether file or string) can be safely discarded.
For best performance, a `parser` instance should be reused over several files: otherwise you will
needlessly reallocate memory, an expensive process. It is also possible to avoid entirely memory
allocations during parsing when using simdjson. [See our performance notes for details](performance.md).
Using the Parsed JSON
---------------------
Once you have an element, you can navigate it with idiomatic C++ iterators, operators and casts.
Once you have a document, you can navigate it with idiomatic C++ iterators, operators and casts.
The following show how to use the JSON when exceptions are enabled, but simdjson has full, idiomatic
support for users who avoid exceptions. See [the simdjson error handling documentation](basics.md#error-handling) for more.
* **Extracting Values (with exceptions):** You can cast a JSON element to a native type: `double(element)` or
`double x = json_element`. This works for double, uint64_t, int64_t, bool,
dom::object and dom::array. An exception is thrown if the cast is not possible.
* **Extracting Values (without exceptions):** You can use a variant usage of `get()` with error codes to avoid exceptions. You first declare the variable of the appropriate type (`double`, `uint64_t`, `int64_t`, `bool`,
`dom::object` and `dom::array`) and pass it by reference to `get()` which gives you back an error code: e.g.,
```c++
simdjson::error_code error;
simdjson::padded_string numberstring = "1.2"_padded; // our JSON input ("1.2")
simdjson::dom::parser parser;
double value; // variable where we store the value to be parsed
error = parser.parse(numberstring).get(value);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
std::cout << "I parsed " << value << " from " << numberstring.data() << std::endl;
```
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`.
* **Array Iteration:** To iterate through an array, use `for (auto value : array) { ... }`. If you
know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`
* **Object Iteration:** You can iterate through an object's fields, too: `for (auto [key, value] : object)`
* **Array Index:** To get at an array value by index, use the at() method: `array.at(0)` gets the
first element.
> Note that array[0] does not compile, because implementing [] gives the impression indexing is a
> O(1) operation, which it is not presently in simdjson. Instead, you should iterate over the elements
> using a for-loop, as in our examples.
* **Array and Object size** Given an array or an object, you can get its size (number of elements or keys)
with the `size()` method.
* **Checking an Element Type:** You can check an element's type with `element.type()`. It
returns an `element_type` with values such as `simdjson::dom::element_type::ARRAY`, `simdjson::dom::element_type::OBJECT`, `simdjson::dom::element_type::INT64`, `simdjson::dom::element_type::UINT64`,`simdjson::dom::element_type::DOUBLE`, `simdjson::dom::element_type::BOOL` or, `simdjson::dom::element_type::NULL_VALUE`.
* **Output to streams and strings:** Given a document or an element (or node) out of a JSON document, you can output a minified string version using the C++ stream idiom (`out << element`). You can also request the construction of a minified string version (`simdjson::minify(element)`).
* **Extracting Values:** You can cast a JSON element to a native type:
`double(element)` or `double x = json_element`. This works for double, uint64_t, int64_t, bool,
ondemand::object and ondemand::array. At this point, the number, string or boolean will be parsed,
or the initial `[` or `{` will be verified. An exception is thrown if the cast is not possible.
> IMPORTANT NOTE: values can only be parsed once. Since documents are *iterators*, once you have
> parsed a value (such as by casting to double), you cannot get at it again.
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`. This will
scan through the object looking for the field with the matching string.
> NOTE: simdjson does *not* unescape keys when matching. This is not generally a problem for
> applications with well-defined key names (which generally do not use escapes). If you do need this
> support, it's best to iterate through the object fields to find the field you are looking for.
>
> By default, field lookup is order-insensitive, so you can look up values in any order. However,
> we still encourage you to look up fields in the order you expect them in the JSON, as it is still
> much faster.
>
> If you want to enforce finding fields in order, you can use `object.find_field("foo")` instead.
> This will only look forward, and will fail to find fields in the wrong order: for example, this
> will fail:
>
> ```c++
> ondemand::parser parser;
> auto json = R"( { "x": 1, "y": 2 } )"_padded;
> auto doc = parser.iterate(json);
> double y = doc.find_field("y"); // The cursor is now after the 2 (at })
> double x = doc.find_field("x"); // This fails, because there are no more fields after "y"
> ```
>
> By contrast, using the default (order-insensitive) lookup succeeds:
>
> ```c++
> ondemand::parser parser;
> auto json = R"( { "x": 1, "y": 2 } )"_padded;
> auto doc = parser.iterate(json);
> double y = doc["y"]; // The cursor is now after the 2 (at })
> double x = doc["x"]; // Success: [] loops back around to find "x"
> ```
* **Array Iteration:** To iterate through an array, use `for (auto value : array) { ... }`. This will
step through each value in the JSON array.
If you know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`.
* **Object Iteration:** You can iterate through an object's fields, as well: `for (auto field : object) { ... }`
- `field.unescaped_key()` will get you the key string.
- `field.value()` will get you the value, which you can then use all these other methods on.
* **Array Index:** Because it is forward-only, you cannot look up an array element by index. Instead,
you will need to iterate through the array and keep an index yourself.
### Examples
The following code illustrates all of the above:
The following code illustrates many of the above concepts:
```c++
ondemand::parser parser;
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
// Iterating through an array of objects
for (dom::object car : parser.parse(cars_json)) {
for (ondemand::object car : parser.iterate(cars_json)) {
// Accessing a field by name
cout << "Make/Model: " << car["make"] << "/" << car["model"] << endl;
cout << "Make/Model: " << std::string_view(car["make"]) << "/" << std::string_view(car["model"]) << endl;
// Casting a JSON element to an integer
uint64_t year = car["year"];
@@ -183,44 +230,123 @@ for (dom::object car : parser.parse(cars_json)) {
total_tire_pressure += tire_pressure;
}
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
// Writing out all the information about the car
for (auto field : car) {
cout << "- " << field.key << ": " << field.value << endl;
}
}
```
Here is a different example illustrating the same ideas:
```C++
auto abstract_json = R"( [
{ "12345" : {"a":12.34, "b":56.78, "c": 9998877} },
{ "12545" : {"a":11.44, "b":12.78, "c": 11111111} }
ondemand::parser parser;
auto points_json = R"( [
{ "12345" : {"x":12.34, "y":56.78, "z": 9998877} },
{ "12545" : {"x":11.44, "y":12.78, "z": 11111111} }
] )"_padded;
dom::parser parser;
// Parse and iterate through an array of objects
for (dom::object obj : parser.parse(abstract_json)) {
for(const auto& key_value : obj) {
cout << "key: " << key_value.key << " : ";
dom::object innerobj = key_value.value;
cout << "a: " << double(innerobj["a"]) << ", ";
cout << "b: " << double(innerobj["b"]) << ", ";
cout << "c: " << int64_t(innerobj["c"]) << endl;
}
for (ondemand::object points : parser.iterate(points_json)) {
for (auto point : points) {
cout << "id: " << std::string_view(point.unescaped_key()) << ": (";
cout << point.value()["x"].get_double() << ", ";
cout << point.value()["y"].get_double() << ", ";
cout << point.value()["z"].get_int64() << endl;
}
}
```
And another one:
```C++
auto abstract_json = R"(
{ "str" : { "123" : {"abc" : 3.14 } } }
)"_padded;
ondemand::parser parser;
auto doc = parser.iterate(abstract_json);
cout << doc["str"]["123"]["abc"].get_double() << endl; // Prints 3.14
```
* **Extracting Values (without exceptions):** You can use a variant usage of `get()` with error
codes to avoid exceptions. You first declare the variable of the appropriate type (`double`,
`uint64_t`, `int64_t`, `bool`, `ondemand::object` and `ondemand::array`) and pass it by reference
to `get()` which gives you back an error code: e.g.,
```c++
auto abstract_json = R"(
{ "str" : { "123" : {"abc" : 3.14 } } } )"_padded;
dom::parser parser;
double v = parser.parse(abstract_json)["str"]["123"]["abc"];
cout << "number: " << v << endl;
{ "str" : { "123" : {"abc" : 3.14 } } }
)"_padded;
ondemand::parser parser;
double value;
auto doc = parser.iterate(abstract_json);
auto error = doc["str"]["123"]["abc"].get(value);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
cout << value << endl; // Prints 3.14
```
Tree Walking and JSON Element Types: Sometimes you don't necessarily have a document with a known type, and are trying to generically inspect or walk over JSON elements. To do that, you can use iterators and the type() method. For example, here's a quick and dirty recursive function that verbosely prints the JSON document as JSON:
```c++
// We use a template function because we need to
// support both ondemand::value and ondemand::document
// as a parameter type. Note that we move the values.
template <class T>
void recursive_print_json(T&& element) {
bool add_comma;
switch (element.type()) {
case ondemand::json_type::array:
cout << "[";
add_comma = false;
for (auto child : element.get_array()) {
if (add_comma) {
cout << ",";
}
// We need the call to value() to get
// an ondemand::value type.
recursive_print_json(child.value());
add_comma = true;
}
cout << "]";
break;
case ondemand::json_type::object:
cout << "{";
add_comma = false;
for (auto field : element.get_object()) {
if (add_comma) {
cout << ",";
}
// key() returns the unescaped key, if we
// want the escaped key, we should do
// field.unescaped_key().
cout << "\"" << field.key() << "\": ";
recursive_print_json(field.value());
add_comma = true;
}
cout << "}";
break;
case ondemand::json_type::number:
// assume it fits in a double
cout << element.get_double();
break;
case ondemand::json_type::string:
// get_string() would return escaped string, but
// we are happy with unescaped string.
cout << "\"" << element.get_raw_json_string() << "\"";
break;
case ondemand::json_type::boolean:
cout << element.get_bool();
break;
case ondemand::json_type::null:
cout << "null";
break;
}
}
void basics_treewalk() {
ondemand::parser parser;
auto json = padded_string::load("twitter.json");
recursive_print_json(parser.iterate(json));
}
```
@@ -555,6 +681,12 @@ bool parse_string(const char *j, std::string &s) {
}
```
To ensure you don't write any code that uses exceptions, compile with `SIMDJSON_EXCEPTIONS=OFF`. For example, if including the project via cmake:
```cmake
target_compile_definitions(simdjson PUBLIC SIMDJSON_EXCEPTIONS=OFF)
```
### Exceptions
Users more comfortable with an exception flow may choose to directly cast the `simdjson_result<T>` to the desired type:
+3 -697
View File
@@ -1,697 +1,3 @@
The Basics
==========
An overview of what you need to know to use simdjson, with examples.
Requirements
------------------
- A recent compiler (LLVM clang6 or better, GNU GCC 7 or better) on a 64-bit (PPC, ARM or x64 Intel/AMD) POSIX systems such as macOS, freeBSD or Linux. We require that the compiler supports the C++11 standard or better.
- Visual Studio 2017 or better under 64-bit Windows. Users should target a 64-bit build (x64) instead of a 32-bit build (x86). We support the LLVM clang compiler under Visual Studio (clangcl) as well as as the regular Visual Studio compiler.
Including simdjson
------------------
To include simdjson, copy the simdjson.h and simdjson.cpp files from the singleheader directory
into your project. Then include the header file in your project with:
```
#include "simdjson.h"
using namespace simdjson; // optional
```
You can compile with:
```
c++ myproject.cpp simdjson.cpp
```
Note:
- Users on macOS and other platforms were default compilers do not provide C++11 compliant by default should request it with the appropriate flag (e.g., `c++ -std=c++17 myproject.cpp simdjson.cpp`).
- Visual Studio users should compile with the `_CRT_SECURE_NO_WARNINGS` flag to avoid warnings with respect to our use of standard C functions such as `fopen`.
Using simdjson with package managers
------------------
You can install the simdjson library on your system or in your project using multiple package managers such as MSYS2, the conan package manager, vcpkg, brew, the apt package manager (debian-based Linux systems), the FreeBSD package manager (FreeBSD), and so on. [Visit our wiki for more details](https://github.com/simdjson/simdjson/wiki/Installing-simdjson-with-a-package-manager).
Using simdjson as a CMake dependency
------------------
You can include the simdjson as a CMake dependency by including the following lines in your `CMakeLists.txt`:
```
include(FetchContent)
FetchContent_Declare(
simdjson
GIT_REPOSITORY https://github.com/simdjson/simdjson.git
GIT_TAG v0.6.1
GIT_SHALLOW TRUE)
set(SIMDJSON_JUST_LIBRARY ON CACHE INTERNAL "")
set(SIMDJSON_BUILD_STATIC ON CACHE INTERNAL "")
FetchContent_MakeAvailable(simdjson)
```
You should replace `GIT_TAG v0.6.1` by the version you need. If you omit `GIT_TAG v0.6.1`, you will work from the main branch of simdjson: we recommend that if you are working on production code,
Elsewhere in your project, you can declare dependencies on simdjson with lines such as these:
```
add_executable(myprogram myprogram.cpp)
target_link_libraries(myprogram simdjson)
```
We recommend CMake version 3.15 or better.
See [our CMake demonstration](https://github.com/simdjson/cmake_demo_single_file). It works under Linux, FreeBSD, macOS and Windows (including Visual Studio).
The CMake build in simdjson can be taylored with a few variables. You can see the available variables and their default values by entering the `cmake -LA` command.
The Basics: Loading and Parsing JSON Documents
----------------------------------------------
The simdjson library offers a simple DOM tree API, which you can access by creating a
`dom::parser` and calling the `load()` method:
```
dom::parser parser;
dom::element doc = parser.load(filename); // load and parse a file
```
Or by creating a padded string (for efficiency reasons, simdjson requires a string with
SIMDJSON_PADDING bytes at the end) and calling `parse()`:
```
dom::parser parser;
dom::element doc = parser.parse("[1,2,3]"_padded); // parse a string, the _padded suffix creates a simdjson::padded_string instance
```
The parsed document resulting from the `parser.load` and `parser.parse` calls depends on the `parser` instance. Thus the `parser` instance must remain in scope. Furthermore, you must have at most one parsed document in play per `parser` instance.
You cannot copy a `parser` instance, you may only move it.
If you need to keep a document around long term, you can keep or move the parser instance. Note that moving a parser instance, or keeping one in a movable data structure like vector or map, can cause any outstanding `element`, `object` or `array` instances to be invalidated. If you need to store a parser in a movable data structure, you should use a `std::unique_ptr` to avoid this invalidation(e.g., `std::unique_ptr<dom::parser> parser(new dom::parser{})`).
During the`load` or `parse` calls, neither the input file nor the input string are ever modified. After calling `load` or `parse`, the source (either a file or a string) can be safely discarded. All of the JSON data is stored in the `parser` instance. The parsed document is also immutable in simdjson: you do not modify it by accessing it.
For best performance, a `parser` instance should be reused over several files: otherwise you will needlessly reallocate memory, an expensive process. It is also possible to avoid entirely memory allocations during parsing when using simdjson.
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).
Using the Parsed JSON
---------------------
Once you have an element, you can navigate it with idiomatic C++ iterators, operators and casts.
* **Extracting Values (with exceptions):** You can cast a JSON element to a native type: `double(element)` or
`double x = json_element`. This works for double, uint64_t, int64_t, bool,
dom::object and dom::array. An exception is thrown if the cast is not possible.
* **Extracting Values (without exceptions):** You can use a variant usage of `get()` with error codes to avoid exceptions. You first declare the variable of the appropriate type (`double`, `uint64_t`, `int64_t`, `bool`,
`dom::object` and `dom::array`) and pass it by reference to `get()` which gives you back an error code: e.g.,
```
simdjson::error_code error;
simdjson::padded_string numberstring = "1.2"_padded; // our JSON input ("1.2")
simdjson::dom::parser parser;
double value; // variable where we store the value to be parsed
error = parser.parse(numberstring).get(value);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
std::cout << "I parsed " << value << " from " << numberstring.data() << std::endl;
```
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`.
* **Array Iteration:** To iterate through an array, use `for (auto value : array) { ... }`. If you
know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`
* **Object Iteration:** You can iterate through an object's fields, too: `for (auto [key, value] : object)`
* **Array Index:** To get at an array value by index, use the at() method: `array.at(0)` gets the
first element.
> Note that array[0] does not compile, because implementing [] gives the impression indexing is a
> O(1) operation, which it is not presently in simdjson. Instead, you should iterate over the elements
> using a for-loop, as in our examples.
* **Array and Object size** Given an array or an object, you can get its size (number of elements or keys)
with the `size()` method.
* **Checking an Element Type:** You can check an element's type with `element.type()`. It
returns an `element_type` with values such as `simdjson::dom::element_type::ARRAY`, `simdjson::dom::element_type::OBJECT`, `simdjson::dom::element_type::INT64`, `simdjson::dom::element_type::UINT64`,`simdjson::dom::element_type::DOUBLE`, `simdjson::dom::element_type::BOOL` or, `simdjson::dom::element_type::NULL_VALUE`.
* **Output to Streams and Strings:** Given a document or an element (or node) out of a JSON document, you can output a minified string version using the C++ stream idiom (`out << element`). You can also request the construction of a minified string version (`simdjson::minify(element)`).
The following code illustrates all of the above:
```
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
// Iterating through an array of objects
for (dom::object car : parser.parse(cars_json)) {
// Accessing a field by name
cout << "Make/Model: " << car["make"] << "/" << car["model"] << endl;
// Casting a JSON element to an integer
uint64_t year = car["year"];
cout << "- This car is " << 2020 - year << "years old." << endl;
// Iterating through an array of floats
double total_tire_pressure = 0;
for (double tire_pressure : car["tire_pressure"]) {
total_tire_pressure += tire_pressure;
}
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
// Writing out all the information about the car
for (auto field : car) {
cout << "- " << field.key << ": " << field.value << endl;
}
}
```
Here is a different example illustrating the same ideas:
```
auto abstract_json = R"( [
{ "12345" : {"a":12.34, "b":56.78, "c": 9998877} },
{ "12545" : {"a":11.44, "b":12.78, "c": 11111111} }
] )"_padded;
dom::parser parser;
// Parse and iterate through an array of objects
for (dom::object obj : parser.parse(abstract_json)) {
for(const auto& key_value : obj) {
cout << "key: " << key_value.key << " : ";
dom::object innerobj = key_value.value;
cout << "a: " << double(innerobj["a"]) << ", ";
cout << "b: " << double(innerobj["b"]) << ", ";
cout << "c: " << int64_t(innerobj["c"]) << endl;
}
}
```
And another one:
```
auto abstract_json = R"(
{ "str" : { "123" : {"abc" : 3.14 } } } )"_padded;
dom::parser parser;
double v = parser.parse(abstract_json)["str"]["123"]["abc"];
cout << "number: " << v << endl;
```
C++11 Support and string_view
-------------
The simdjson library builds on compilers supporting the [C++11 standard](https://en.wikipedia.org/wiki/C%2B%2B11). It is also a strict requirement: we have no plan to support older C++ compilers.
We represent parsed strings in simdjson using the `std::string_view` class. It avoids
the need to copy the data, as would be necessary with the `std::string` class. It also
avoids the pitfalls of null-terminated C strings.
The `std::string_view` class has become standard as part of C++17 but it is not always available
on compilers which only supports C++11. When we detect that `string_view` is natively
available, we define the macro `SIMDJSON_HAS_STRING_VIEW`.
When we detect that it is unavailable,
we use [string-view-lite](https://github.com/martinmoene/string-view-lite) as a
substitute. In such cases, we use the type alias `using string_view = nonstd::string_view;` to
offer the same API, irrespective of the compiler and standard library. The macro
`SIMDJSON_HAS_STRING_VIEW` will be *undefined* to indicate that we emulate `string_view`.
C++17 Support
-------------
While the simdjson library can be used in any project using C++ 11 and above, field iteration has special support C++ 17's destructuring syntax. For example:
```
padded_string json = R"( { "foo": 1, "bar": 2 } )"_padded;
dom::parser parser;
dom::object object;
auto error = parser.parse(json).get(object);
if (error) { cerr << error << endl; return; }
for (auto [key, value] : object) {
cout << key << " = " << value << endl;
}
```
For comparison, here is the C++ 11 version of the same code:
```
// C++ 11 version for comparison
padded_string json = R"( { "foo": 1, "bar": 2 } )"_padded;
dom::parser parser;
dom::object object;
auto error = parser.parse(json).get(object);
if (!error) { cerr << error << endl; return; }
for (dom::key_value_pair field : object) {
cout << field.key << " = " << field.value << endl;
}
```
Minifying JSON strings without parsing
----------------------
In some cases, you may have valid JSON strings that you do not wish to parse but that you wish to minify. That is, you wish to remove all unnecessary spaces. We have a fast function for this purpose (`simdjson::minify(const char * input, size_t length, const char * output, size_t& new_length)`). This function does not validate your content, and it does not parse it. It is much faster than parsing the string and re-serializing it in minified form (`simdjson::minify(parser.parse())`). Usage is relatively simple. You must pass an input pointer with a length parameter, as well as an output pointer and an output length parameter (by reference). The output length parameter is not read, but written to. The output pointer should point to a valid memory region that is as large as the original string length. The input pointer and input length are read, but not written to.
```
// Starts with a valid JSON document as a string.
// It does not have to be null-terminated.
const char * some_string = "[ 1, 2, 3, 4] ";
size_t length = std::strlen(some_string);
// Create a buffer to receive the minified string. Make sure that there is enough room (length bytes).
std::unique_ptr<char[]> buffer{new char[length]};
size_t new_length{}; // It will receive the minified length.
auto error = simdjson::minify(some_string, length, buffer.get(), new_length);
// The buffer variable now has "[1,2,3,4]" and new_length has value 9.
```
Though it does not validate the JSON input, it will detect when the document ends with an unterminated string. E.g., it would refuse to minify the string `"this string is not terminated` because of the missing final quote.
UTF-8 validation (alone)
----------------------
The simdjson library has fast functions to validate UTF-8 strings. They are many times faster than most functions commonly found in libraries. You can use our fast functions, even if you do not care about JSON.
```
const char * some_string = "[ 1, 2, 3, 4] ";
size_t length = std::strlen(some_string);
bool is_ok = simdjson::validate_utf8(some_string, length);
```
The UTF-8 validation function merely checks that the input is valid UTF-8: it works with strings in general, not just JSON strings.
Your input string does not need any padding. Any string will do. The `validate_utf8` function does not do any memory allocation on the heap, and it does not throw exceptions.
JSON Pointer
------------
The simdjson library also supports [JSON pointer](https://tools.ietf.org/html/rfc6901) through the
`at_pointer()` method, letting you reach further down into the document in a single call:
```c++
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
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
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.
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 currrent node were a whole JSON document.
Consider the following example:
```c++
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
dom::element cars = parser.parse(cars_json);
cout << cars.at_pointer("/0/tire_pressure/1") << endl; // Prints 39.9
for (dom::element car_element : cars) {
dom::object car;
simdjson::error_code error;
if ((error = car_element.get(car))) { std::cerr << error << std::endl; return; }
double x = car.at_pointer("/tire_pressure/1");
cout << x << endl; // Prints 39.9, 31 and 30
}
```
Error Handling
--------------
All simdjson APIs that can fail return `simdjson_result<T>`, which is a &lt;value, error_code&gt;
pair. You can retrieve the value with .get(), like so:
```
dom::element doc;
auto error = parser.parse(json).get(doc);
if (error) { cerr << error << endl; exit(1); }
```
When you use the code this way, it is your responsibility to check for error before using the
result: if there is an error, the result value will not be valid and using it will caused undefined
behavior.
We can write a "quick start" example where we attempt to parse the following JSON file and access some data, without triggering exceptions:
```JavaScript
{
"statuses": [
{
"id": 505874924095815700
},
{
"id": 505874922023837700
}
],
"search_metadata": {
"count": 100
}
}
```
Our program loads the file, selects value corresponding to key "search_metadata" which expected to be an object, and then
it selects the key "count" within that object.
```C++
#include "simdjson.h"
int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets;
auto error = parser.load("twitter.json").get(tweets);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
simdjson::dom::element res;
if ((error = tweets["search_metadata"]["count"].get(res))) {
std::cerr << "could not access keys" << std::endl;
return EXIT_FAILURE;
}
std::cout << res << " results." << std::endl;
}
```
The following is a similar example where one wants to get the id of the first tweet without
triggering exceptions. To do this, we use `["statuses"].at(0)["id"]`. We break that expression down:
- Get the list of tweets (the `"statuses"` key of the document) using `["statuses"]`). The result is expected to be an array.
- Get the first tweet using `.at(0)`. The result is expected to be an object.
- Get the id of the tweet using ["id"]. We expect the value to be a non-negative integer.
Observe how we use the `at` method when querying an index into an array, and not the bracket operator.
```
#include "simdjson.h"
int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets;
auto error = parser.load("twitter.json").get(tweets);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
uint64_t identifier;
error = tweets["statuses"].at(0)["id"].get(identifier);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
std::cout << identifier << std::endl;
return EXIT_SUCCESS;
}
```
### Error Handling Example
This is how the example in "Using the Parsed JSON" could be written using only error code checking:
```
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
dom::array cars;
auto error = parser.parse(cars_json).get(cars);
if (error) { cerr << error << endl; exit(1); }
// Iterating through an array of objects
for (dom::element car_element : cars) {
dom::object car;
if ((error = car_element.get(car))) { cerr << error << endl; exit(1); }
// Accessing a field by name
std::string_view make, model;
if ((error = car["make"].get(make))) { cerr << error << endl; exit(1); }
if ((error = car["model"].get(model))) { cerr << error << endl; exit(1); }
cout << "Make/Model: " << make << "/" << model << endl;
// Casting a JSON element to an integer
uint64_t year;
if ((error = car["year"].get(year))) { cerr << error << endl; exit(1); }
cout << "- This car is " << 2020 - year << "years old." << endl;
// Iterating through an array of floats
double total_tire_pressure = 0;
dom::array tire_pressure_array;
if ((error = car["tire_pressure"].get(tire_pressure_array))) { cerr << error << endl; exit(1); }
for (dom::element tire_pressure_element : tire_pressure_array) {
double tire_pressure;
if ((error = tire_pressure_element.get(tire_pressure))) { cerr << error << endl; exit(1); }
total_tire_pressure += tire_pressure;
}
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
// Writing out all the information about the car
for (auto field : car) {
cout << "- " << field.key << ": " << field.value << endl;
}
}
```
Here is another example:
```
auto abstract_json = R"( [
{ "12345" : {"a":12.34, "b":56.78, "c": 9998877} },
{ "12545" : {"a":11.44, "b":12.78, "c": 11111111} }
] )"_padded;
dom::parser parser;
dom::array array;
auto error = parser.parse(abstract_json).get(array);
if (error) { cerr << error << endl; exit(1); }
// Iterate through an array of objects
for (dom::element elem : array) {
dom::object obj;
if ((error = elem.get(obj))) { cerr << error << endl; exit(1); }
for (auto & key_value : obj) {
cout << "key: " << key_value.key << " : ";
dom::object innerobj;
if ((error = key_value.value.get(innerobj))) { cerr << error << endl; exit(1); }
double va, vb;
if ((error = innerobj["a"].get(va))) { cerr << error << endl; exit(1); }
cout << "a: " << va << ", ";
if ((error = innerobj["b"].get(vc))) { cerr << error << endl; exit(1); }
cout << "b: " << vb << ", ";
int64_t vc;
if ((error = innerobj["c"].get(vc))) { cerr << error << endl; exit(1); }
cout << "c: " << vc << endl;
}
}
```
And another one:
```
auto abstract_json = R"(
{ "str" : { "123" : {"abc" : 3.14 } } } )"_padded;
dom::parser parser;
double v;
auto error = parser.parse(abstract_json)["str"]["123"]["abc"].get(v);
if (error) { cerr << error << endl; exit(1); }
cout << "number: " << v << endl;
```
Notice how we can string several operations (`parser.parse(abstract_json)["str"]["123"]["abc"].get(v)`) and only check for the error once, a strategy we call *error chaining*.
The next two functions will take as input a JSON document containing an array with a single element, either a string or a number. They return true upon success.
```
simdjson::dom::parser parser{};
bool parse_double(const char *j, double &d) {
auto error = parser.parse(j, std::strlen(j))
.at(0)
.get(d, error);
if (error) { return false; }
return true;
}
bool parse_string(const char *j, std::string &s) {
std::string_view answer;
auto error = parser.parse(j,strlen(j))
.at(0)
.get(answer, error);
if (error) { return false; }
s.assign(answer.data(), answer.size());
return true;
}
```
### Exceptions
Users more comfortable with an exception flow may choose to directly cast the `simdjson_result<T>` to the desired type:
```
dom::element doc = parser.parse(json); // Throws an exception if there was an error!
```
When used this way, a `simdjson_error` exception will be thrown if an error occurs, preventing the
program from continuing if there was an error.
If one is willing to trigger exceptions, it is possible to write simpler code:
```
#include "simdjson.h"
int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets = parser.load("twitter.json");
std::cout << "ID: " << tweets["statuses"].at(0)["id"] << std::endl;
return EXIT_SUCCESS;
}
```
Tree Walking and JSON Element Types
-----------------------------------
Sometimes you don't necessarily have a document with a known type, and are trying to generically
inspect or walk over JSON elements. To do that, you can use iterators and the type() method. For
example, here's a quick and dirty recursive function that verbosely prints the JSON document as JSON
(* ignoring nuances like trailing commas and escaping strings, for brevity's sake):
```
void print_json(dom::element element) {
switch (element.type()) {
case dom::element_type::ARRAY:
cout << "[";
for (dom::element child : dom::array(element)) {
print_json(child);
cout << ",";
}
cout << "]";
break;
case dom::element_type::OBJECT:
cout << "{";
for (dom::key_value_pair field : dom::object(element)) {
cout << "\"" << field.key << "\": ";
print_json(field.value);
}
cout << "}";
break;
case dom::element_type::INT64:
cout << int64_t(element) << endl;
break;
case dom::element_type::UINT64:
cout << uint64_t(element) << endl;
break;
case dom::element_type::DOUBLE:
cout << double(element) << endl;
break;
case dom::element_type::STRING:
cout << std::string_view(element) << endl;
break;
case dom::element_type::BOOL:
cout << bool(element) << endl;
break;
case dom::element_type::NULL_VALUE:
cout << "null" << endl;
break;
}
}
void basics_treewalk_1() {
dom::parser parser;
print_json(parser.load("twitter.json"));
}
```
Newline-Delimited JSON (ndjson) and JSON lines
----------------------------------------------
The simdjson library also support multithreaded JSON streaming through a large file containing many
smaller JSON documents in either [ndjson](http://ndjson.org) or [JSON lines](http://jsonlines.org)
format. If your JSON documents all contain arrays or objects, we even support direct file
concatenation without whitespace. The concatenated file has no size restrictions (including larger
than 4GB), though each individual document must be no larger than 4 GB.
Here is a simple example, given "x.json" with this content:
```
{ "foo": 1 }
{ "foo": 2 }
{ "foo": 3 }
```
```
dom::parser parser;
dom::document_stream docs = parser.load_many("x.json");
for (dom::element doc : docs) {
cout << doc["foo"] << endl;
}
// Prints 1 2 3
```
In-memory ndjson strings can be parsed as well, with `parser.parse_many(string)`:
```
dom::parser parser;
auto json = R"({ "foo": 1 }
{ "foo": 2 }
{ "foo": 3 })"_padded;
dom::document_stream docs = parser.parse_many(json);
for (dom::element doc : docs) {
cout << doc["foo"] << endl;
}
// Prints 1 2 3
```
Unlike `parser.parse`, both `parser.load_many(filename)` and `parser.parse_many(string)` may parse
"on demand" (lazily). That is, no parsing may have been done before you enter the loop
`for (dom::element doc : docs) {` and you should expect the parser to only ever fully parse one JSON
document at a time.
1. When calling `parser.load_many(filename)`, the file's content is loaded up in a memory buffer owned by the `parser`'s instance. Thus the file can be safely deleted after calling `parser.load_many(filename)` as the parser instance owns all of the data.
2. When calling `parser.parse_many(string)`, no copy is made of the provided string input. The provided memory buffer may be accessed each time a JSON document is parsed. Calling `parser.parse_many(string)` on a temporary string buffer (e.g., `docs = parser.parse_many("[1,2,3]"_padded)`) is unsafe (and will not compile) because the `document_stream` instance needs access to the buffer to return the JSON documents. In constrast, calling `doc = parser.parse("[1,2,3]"_padded)` is safe because `parser.parse` eagerly parses the input.
Both `load_many` and `parse_many` take an optional parameter `size_t batch_size` which defines the window processing size. It is set by default to a large value (`1000000` corresponding to 1 MB). None of your JSON documents should exceed this window size, or else you will get the error `simdjson::CAPACITY`. You cannot set this window size larger than 4 GB: you will get the error `simdjson::CAPACITY`. The smaller the window size is, the less memory the function will use. Setting the window size too small (e.g., less than 100 kB) may also impact performance negatively. Leaving it to 1 MB is expected to be a good choice, unless you have some larger documents.
Thread Safety
-------------
We built simdjson with thread safety in mind.
The simdjson library is single-threaded except for `parse_many` which may use secondary threads under its control when the library is compiled with thread support.
We recommend using one `dom::parser` object per thread in which case the library is thread-safe.
It is unsafe to reuse a `dom::parser` object between different threads.
The parsed results (`dom::document`, `dom::element`, `array`, `object`) depend on the `dom::parser`, etc. therefore it is also potentially unsafe to use the result of the parsing between different threads.
The CPU detection, which runs the first time parsing is attempted and switches to the fastest
parser for your CPU, is transparent and thread-safe.
Backwards Compatibility
-----------------------
The only header file supported by simdjson is `simdjson.h`. Older versions of simdjson published a
number of other include files such as `document.h` or `ParsedJson.h` alongside `simdjson.h`; these headers
may be moved or removed in future versions.
* Basics: https://github.com/simdjson/simdjson/blob/master/doc/basics.md is an overview of how to use simdjson and its APIs.
* parse_many: https://github.com/simdjson/simdjson/blob/master/doc/parse_many.md describes an interface providing features to work with files or streams containing multiple small JSON documents. As fast and convenient as possible.
* Performance: https://github.com/simdjson/simdjson/blob/master/doc/performance.md shows some more advanced scenarios and how to tune for them.
+635
View File
@@ -0,0 +1,635 @@
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-ondemand)
* [The Basics: Loading and Parsing JSON Documents](#the-basics-loading-and-parsing-json-documents)
* [Using the Parsed JSON](#using-the-parsed-json)
* [C++17 Support](#c17-support)
* [JSON Pointer](#json-pointer)
* [Error Handling](#error-handling)
* [Error Handling Example](#error-handling-example)
* [Exceptions](#exceptions)
* [Tree Walking and JSON Element Types](#tree-walking-and-json-element-types)
* [Reusing the parser for maximum efficiency](#reusing-the-parser-for-maximum-efficiency)
* [Server Loops: Long-Running Processes and Memory Capacity](#server-loops-long-running-processes-and-memory-capacity)
* [Best Use of the DOM API](#best-use-of-the-dom-api)
* [Padding and Temporary Copies](#padding-and-temporary-copies)
DOM vs On Demand
----------------------------------------------
The simdjson library offers two distinct approaches on how to access a JSON document. We support
a conventional Document-Object-Model (DOM) front-end. In such a scenario, the JSON document is
entirely parsed, validated and materialized in memory as the first step. The programmer may
then access the parsed data using this in-memory model.
The Basics: Loading and Parsing JSON Documents using the DOM front-end
----------------------------------------------
The simdjson library offers a simple DOM tree API, which you can access by creating a
`dom::parser` and calling the `load()` method:
```c++
dom::parser parser;
dom::element doc = parser.load(filename); // load and parse a file
```
Or by creating a padded string (for efficiency reasons, simdjson requires a string with
SIMDJSON_PADDING bytes at the end) and calling `parse()`:
```c++
dom::parser parser;
dom::element doc = parser.parse("[1,2,3]"_padded); // parse a string, the _padded suffix creates a simdjson::padded_string instance
```
The parsed document resulting from the `parser.load` and `parser.parse` calls depends on the `parser` instance. Thus the `parser` instance must remain in scope. Furthermore, you must have at most one parsed document in play per `parser` instance.
You cannot copy a `parser` instance, you may only move it.
If you need to keep a document around long term, you can keep or move the parser instance. Note that moving a parser instance, or keeping one in a movable data structure like vector or map, can cause any outstanding `element`, `object` or `array` instances to be invalidated. If you need to store a parser in a movable data structure, you should use a `std::unique_ptr` to avoid this invalidation(e.g., `std::unique_ptr<dom::parser> parser(new dom::parser{})`).
During the`load` or `parse` calls, neither the input file nor the input string are ever modified. After calling `load` or `parse`, the source (either a file or a string) can be safely discarded. All of the JSON data is stored in the `parser` instance. The parsed document is also immutable in simdjson: you do not modify it by accessing it.
For best performance, a `parser` instance should be reused over several files: otherwise you will needlessly reallocate memory, an expensive process. It is also possible to avoid entirely memory allocations during parsing when using simdjson. [See our performance notes for details](performance.md).
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).
Using the Parsed JSON
---------------------
Once you have an element, you can navigate it with idiomatic C++ iterators, operators and casts.
* **Extracting Values (with exceptions):** You can cast a JSON element to a native type: `double(element)` or
`double x = json_element`. This works for double, uint64_t, int64_t, bool,
dom::object and dom::array. An exception is thrown if the cast is not possible.
* **Extracting Values (without exceptions):** You can use a variant usage of `get()` with error codes to avoid exceptions. You first declare the variable of the appropriate type (`double`, `uint64_t`, `int64_t`, `bool`,
`dom::object` and `dom::array`) and pass it by reference to `get()` which gives you back an error code: e.g.,
```c++
simdjson::error_code error;
simdjson::padded_string numberstring = "1.2"_padded; // our JSON input ("1.2")
simdjson::dom::parser parser;
double value; // variable where we store the value to be parsed
error = parser.parse(numberstring).get(value);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
std::cout << "I parsed " << value << " from " << numberstring.data() << std::endl;
```
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`.
* **Array Iteration:** To iterate through an array, use `for (auto value : array) { ... }`. If you
know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`
* **Object Iteration:** You can iterate through an object's fields, too: `for (auto [key, value] : object)`
* **Array Index:** To get at an array value by index, use the at() method: `array.at(0)` gets the
first element.
> Note that array[0] does not compile, because implementing [] gives the impression indexing is a
> O(1) operation, which it is not presently in simdjson. Instead, you should iterate over the elements
> using a for-loop, as in our examples.
* **Array and Object size** Given an array or an object, you can get its size (number of elements or keys)
with the `size()` method.
* **Checking an Element Type:** You can check an element's type with `element.type()`. It
returns an `element_type` with values such as `simdjson::dom::element_type::ARRAY`, `simdjson::dom::element_type::OBJECT`, `simdjson::dom::element_type::INT64`, `simdjson::dom::element_type::UINT64`,`simdjson::dom::element_type::DOUBLE`, `simdjson::dom::element_type::BOOL` or, `simdjson::dom::element_type::NULL_VALUE`.
* **Output to streams and strings:** Given a document or an element (or node) out of a JSON document, you can output a minified string version using the C++ stream idiom (`out << element`). You can also request the construction of a minified string version (`simdjson::minify(element)`).
### Examples
The following code illustrates all of the above:
```c++
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
// Iterating through an array of objects
for (dom::object car : parser.parse(cars_json)) {
// Accessing a field by name
cout << "Make/Model: " << car["make"] << "/" << car["model"] << endl;
// Casting a JSON element to an integer
uint64_t year = car["year"];
cout << "- This car is " << 2020 - year << "years old." << endl;
// Iterating through an array of floats
double total_tire_pressure = 0;
for (double tire_pressure : car["tire_pressure"]) {
total_tire_pressure += tire_pressure;
}
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
// Writing out all the information about the car
for (auto field : car) {
cout << "- " << field.key << ": " << field.value << endl;
}
}
```
Here is a different example illustrating the same ideas:
```C++
auto abstract_json = R"( [
{ "12345" : {"a":12.34, "b":56.78, "c": 9998877} },
{ "12545" : {"a":11.44, "b":12.78, "c": 11111111} }
] )"_padded;
dom::parser parser;
// Parse and iterate through an array of objects
for (dom::object obj : parser.parse(abstract_json)) {
for(const auto key_value : obj) {
cout << "key: " << key_value.key << " : ";
dom::object innerobj = key_value.value;
cout << "a: " << double(innerobj["a"]) << ", ";
cout << "b: " << double(innerobj["b"]) << ", ";
cout << "c: " << int64_t(innerobj["c"]) << endl;
}
}
```
And another one:
```C++
auto abstract_json = R"(
{ "str" : { "123" : {"abc" : 3.14 } } } )"_padded;
dom::parser parser;
double v = parser.parse(abstract_json)["str"]["123"]["abc"];
cout << "number: " << v << endl;
```
C++17 Support
-------------
While the simdjson library can be used in any project using C++ 11 and above, field iteration has special support C++ 17's destructuring syntax. For example:
```c++
padded_string json = R"( { "foo": 1, "bar": 2 } )"_padded;
dom::parser parser;
dom::object object;
auto error = parser.parse(json).get(object);
if (error) { cerr << error << endl; return; }
for (auto [key, value] : object) {
cout << key << " = " << value << endl;
}
```
For comparison, here is the C++ 11 version of the same code:
```c++
// C++ 11 version for comparison
padded_string json = R"( { "foo": 1, "bar": 2 } )"_padded;
dom::parser parser;
dom::object object;
auto error = parser.parse(json).get(object);
if (error) { cerr << error << endl; return; }
for (dom::key_value_pair field : object) {
cout << field.key << " = " << field.value << endl;
}
```
JSON Pointer
------------
The simdjson library also supports [JSON pointer](https://tools.ietf.org/html/rfc6901) through the
`at_pointer()` method, letting you reach further down into the document in a single call:
```c++
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
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
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.
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 currrent node were a whole JSON document.
Consider the following example:
```c++
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
dom::element cars = parser.parse(cars_json);
cout << cars.at_pointer("/0/tire_pressure/1") << endl; // Prints 39.9
for (dom::element car_element : cars) {
dom::object car;
simdjson::error_code error;
if ((error = car_element.get(car))) { std::cerr << error << std::endl; return; }
double x = car.at_pointer("/tire_pressure/1");
cout << x << endl; // Prints 39.9, 31 and 30
}
```
Error Handling
--------------
All simdjson APIs that can fail return `simdjson_result<T>`, which is a &lt;value, error_code&gt;
pair. You can retrieve the value with .get(), like so:
```c++
dom::element doc;
auto error = parser.parse(json).get(doc);
if (error) { cerr << error << endl; exit(1); }
```
When you use the code this way, it is your responsibility to check for error before using the
result: if there is an error, the result value will not be valid and using it will caused undefined
behavior.
We can write a "quick start" example where we attempt to parse the following JSON file and access some data, without triggering exceptions:
```JavaScript
{
"statuses": [
{
"id": 505874924095815700
},
{
"id": 505874922023837700
}
],
"search_metadata": {
"count": 100
}
}
```
Our program loads the file, selects value corresponding to key "search_metadata" which expected to be an object, and then
it selects the key "count" within that object.
```C++
#include "simdjson.h"
int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets;
auto error = parser.load("twitter.json").get(tweets);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
simdjson::dom::element res;
if ((error = tweets["search_metadata"]["count"].get(res))) {
std::cerr << "could not access keys" << std::endl;
return EXIT_FAILURE;
}
std::cout << res << " results." << std::endl;
}
```
The following is a similar example where one wants to get the id of the first tweet without
triggering exceptions. To do this, we use `["statuses"].at(0)["id"]`. We break that expression down:
- Get the list of tweets (the `"statuses"` key of the document) using `["statuses"]`). The result is expected to be an array.
- Get the first tweet using `.at(0)`. The result is expected to be an object.
- Get the id of the tweet using ["id"]. We expect the value to be a non-negative integer.
Observe how we use the `at` method when querying an index into an array, and not the bracket operator.
```C++
#include "simdjson.h"
int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets;
auto error = parser.load("twitter.json").get(tweets);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
uint64_t identifier;
error = tweets["statuses"].at(0)["id"].get(identifier);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
std::cout << identifier << std::endl;
return EXIT_SUCCESS;
}
```
### Error Handling Example
This is how the example in "Using the Parsed JSON" could be written using only error code checking:
```c++
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
dom::array cars;
auto error = parser.parse(cars_json).get(cars);
if (error) { cerr << error << endl; exit(1); }
// Iterating through an array of objects
for (dom::element car_element : cars) {
dom::object car;
if ((error = car_element.get(car))) { cerr << error << endl; exit(1); }
// Accessing a field by name
std::string_view make, model;
if ((error = car["make"].get(make))) { cerr << error << endl; exit(1); }
if ((error = car["model"].get(model))) { cerr << error << endl; exit(1); }
cout << "Make/Model: " << make << "/" << model << endl;
// Casting a JSON element to an integer
uint64_t year;
if ((error = car["year"].get(year))) { cerr << error << endl; exit(1); }
cout << "- This car is " << 2020 - year << "years old." << endl;
// Iterating through an array of floats
double total_tire_pressure = 0;
dom::array tire_pressure_array;
if ((error = car["tire_pressure"].get(tire_pressure_array))) { cerr << error << endl; exit(1); }
for (dom::element tire_pressure_element : tire_pressure_array) {
double tire_pressure;
if ((error = tire_pressure_element.get(tire_pressure))) { cerr << error << endl; exit(1); }
total_tire_pressure += tire_pressure;
}
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
// Writing out all the information about the car
for (auto field : car) {
cout << "- " << field.key << ": " << field.value << endl;
}
}
```
Here is another example:
```C++
auto abstract_json = R"( [
{ "12345" : {"a":12.34, "b":56.78, "c": 9998877} },
{ "12545" : {"a":11.44, "b":12.78, "c": 11111111} }
] )"_padded;
dom::parser parser;
dom::array array;
auto error = parser.parse(abstract_json).get(array);
if (error) { cerr << error << endl; exit(1); }
// Iterate through an array of objects
for (dom::element elem : array) {
dom::object obj;
if ((error = elem.get(obj))) { cerr << error << endl; exit(1); }
for (auto & key_value : obj) {
cout << "key: " << key_value.key << " : ";
dom::object innerobj;
if ((error = key_value.value.get(innerobj))) { cerr << error << endl; exit(1); }
double va, vb;
if ((error = innerobj["a"].get(va))) { cerr << error << endl; exit(1); }
cout << "a: " << va << ", ";
if ((error = innerobj["b"].get(vc))) { cerr << error << endl; exit(1); }
cout << "b: " << vb << ", ";
int64_t vc;
if ((error = innerobj["c"].get(vc))) { cerr << error << endl; exit(1); }
cout << "c: " << vc << endl;
}
}
```
And another one:
```C++
auto abstract_json = R"(
{ "str" : { "123" : {"abc" : 3.14 } } } )"_padded;
dom::parser parser;
double v;
auto error = parser.parse(abstract_json)["str"]["123"]["abc"].get(v);
if (error) { cerr << error << endl; exit(1); }
cout << "number: " << v << endl;
```
Notice how we can string several operations (`parser.parse(abstract_json)["str"]["123"]["abc"].get(v)`) and only check for the error once, a strategy we call *error chaining*.
The next two functions will take as input a JSON document containing an array with a single element, either a string or a number. They return true upon success.
```C++
simdjson::dom::parser parser{};
bool parse_double(const char *j, double &d) {
auto error = parser.parse(j, std::strlen(j))
.at(0)
.get(d, error);
if (error) { return false; }
return true;
}
bool parse_string(const char *j, std::string &s) {
std::string_view answer;
auto error = parser.parse(j,strlen(j))
.at(0)
.get(answer, error);
if (error) { return false; }
s.assign(answer.data(), answer.size());
return true;
}
```
To ensure you don't write any code that uses exceptions, compile with `SIMDJSON_EXCEPTIONS=OFF`. For example, if including the project via cmake:
```cmake
target_compile_definitions(simdjson PUBLIC SIMDJSON_EXCEPTIONS=OFF)
```
### Exceptions
Users more comfortable with an exception flow may choose to directly cast the `simdjson_result<T>` to the desired type:
```c++
dom::element doc = parser.parse(json); // Throws an exception if there was an error!
```
When used this way, a `simdjson_error` exception will be thrown if an error occurs, preventing the
program from continuing if there was an error.
If one is willing to trigger exceptions, it is possible to write simpler code:
```C++
#include "simdjson.h"
int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets = parser.load("twitter.json");
std::cout << "ID: " << tweets["statuses"].at(0)["id"] << std::endl;
return EXIT_SUCCESS;
}
```
Tree Walking and JSON Element Types
-----------------------------------
Sometimes you don't necessarily have a document with a known type, and are trying to generically
inspect or walk over JSON elements. To do that, you can use iterators and the type() method. For
example, here's a quick and dirty recursive function that verbosely prints the JSON document as JSON
(* ignoring nuances like trailing commas and escaping strings, for brevity's sake):
```c++
void print_json(dom::element element) {
switch (element.type()) {
case dom::element_type::ARRAY:
cout << "[";
for (dom::element child : dom::array(element)) {
print_json(child);
cout << ",";
}
cout << "]";
break;
case dom::element_type::OBJECT:
cout << "{";
for (dom::key_value_pair field : dom::object(element)) {
cout << "\"" << field.key << "\": ";
print_json(field.value);
}
cout << "}";
break;
case dom::element_type::INT64:
cout << int64_t(element) << endl;
break;
case dom::element_type::UINT64:
cout << uint64_t(element) << endl;
break;
case dom::element_type::DOUBLE:
cout << double(element) << endl;
break;
case dom::element_type::STRING:
cout << std::string_view(element) << endl;
break;
case dom::element_type::BOOL:
cout << bool(element) << endl;
break;
case dom::element_type::NULL_VALUE:
cout << "null" << endl;
break;
}
}
void basics_treewalk_1() {
dom::parser parser;
print_json(parser.load("twitter.json"));
}
```
Reusing the parser for maximum efficiency
-----------------------------------------
If you're using simdjson to parse multiple documents, or in a loop, you should make a parser once
and reuse it. The simdjson library will allocate and retain internal buffers between parses, keeping
buffers hot in cache and keeping memory allocation and initialization to a minimum. In this manner,
you can parse terabytes of JSON data without doing any new allocation.
```c++
dom::parser parser;
// This initializes buffers and a document big enough to handle this JSON.
dom::element doc = parser.parse("[ true, false ]"_padded);
cout << doc << endl;
// This reuses the existing buffers, and reuses and *overwrites* the old document
doc = parser.parse("[1, 2, 3]"_padded);
cout << doc << endl;
// This also reuses the existing buffers, and reuses and *overwrites* the old document
dom::element doc2 = parser.parse("true"_padded);
// Even if you keep the old reference around, doc and doc2 refer to the same document.
cout << doc << endl;
cout << doc2 << endl;
```
It's not just internal buffers though. The simdjson library reuses the document itself. The dom::element, dom::object and dom::array instances are *references* to the internal document.
You are only *borrowing* the document from simdjson, which purposely reuses and overwrites it each
time you call parse. This prevent wasteful and unnecessary memory allocation in 99% of cases where
JSON is just read, used, and converted to native values or thrown away.
> **You are only borrowing the document from the simdjson parser. Don't keep it long term!**
This is key: don't keep the `document&`, `dom::element`, `dom::array`, `dom::object`
or `string_view` objects you get back from the API. Convert them to C++ native values, structs and
arrays that you own.
Server Loops: Long-Running Processes and Memory Capacity
--------------------------------------------------------
The simdjson library automatically expands its memory capacity when larger documents are parsed, so
that you don't unexpectedly fail. In a short process that reads a bunch of files and then exits,
this works pretty flawlessly.
Server loops, though, are long-running processes that will keep the parser around forever. This
means that if you encounter a really, really large document, simdjson will not resize back down.
The simdjson library lets you adjust your allocation strategy to prevent your server from growing
without bound:
* You can set a *max capacity* when constructing a parser:
```c++
dom::parser parser(1000*1000); // Never grow past documents > 1MB
for (web_request request : listen()) {
dom::element doc;
auto error = parser.parse(request.body).get(doc);
// If the document was above our limit, emit 413 = payload too large
if (error == CAPACITY) { request.respond(413); continue; }
// ...
}
```
This parser will grow normally as it encounters larger documents, but will never pass 1MB.
* You can set a *fixed capacity* that never grows, as well, which can be excellent for
predictability and reliability, since simdjson will never call malloc after startup!
```c++
dom::parser parser(0); // This parser will refuse to automatically grow capacity
auto error = parser.allocate(1000*1000); // This allocates enough capacity to handle documents <= 1MB
if (error) { cerr << error << endl; exit(1); }
for (web_request request : listen()) {
dom::element doc;
error = parser.parse(request.body).get(doc);
// If the document was above our limit, emit 413 = payload too large
if (error == CAPACITY) { request.respond(413); continue; }
// ...
}
```
Best Use of the DOM API
-------------------------
The simdjson API provides access to the JSON DOM (document-object-model) content as a tree of `dom::element` instances, each representing an object, an array or an atomic type (null, true, false, number). These `dom::element` instances are lightweight objects (e.g., spanning 16 bytes) and it might be advantageous to pass them by value, as opposed to passing them by reference or by pointer.
Padding and Temporary Copies
--------------
The simdjson function `parser.parse` reads data from a padded buffer, containing SIMDJSON_PADDING extra bytes added at the end.
If you are passing a `padded_string` to `parser.parse` or loading the JSON directly from
disk (`parser.load`), padding is automatically handled.
When calling `parser.parse` on a pointer (e.g., `parser.parse(my_char_pointer, my_length_in_bytes)`) a temporary copy is made by default with adequate padding and you, again, do not need to be concerned with padding.
Some users may not be able use our `padded_string` class or to load the data directly from disk (`parser.load`). They may need to pass data pointers to the library. If these users wish to avoid temporary copies and corresponding temporary memory allocations, they may want to call `parser.parse` with the `realloc_if_needed` parameter set to false (e.g., `parser.parse(my_char_pointer, my_length_in_bytes, false)`). In such cases, they need to ensure that there are at least SIMDJSON_PADDING extra bytes at the end that can be safely accessed and read. They do not need to initialize the padded bytes to any value in particular. The following example is safe:
```C++
const char *json = R"({"key":"value"})";
const size_t json_len = std::strlen(json);
std::unique_ptr<char[]> padded_json_copy{new char[json_len + SIMDJSON_PADDING]};
memcpy(padded_json_copy.get(), json, json_len);
memset(padded_json_copy.get() + json_len, 0, SIMDJSON_PADDING);
simdjson::dom::parser parser;
simdjson::dom::element element = parser.parse(padded_json_copy.get(), json_len, false);
````
Setting the `realloc_if_needed` parameter `false` in this manner may lead to better performance since copies are avoided, but it requires that the user takes more responsibilities: the simdjson library cannot verify that the input buffer was padded with SIMDJSON_PADDING extra bytes.
-230
View File
@@ -1,230 +0,0 @@
On Demand Basics
================
On Demand is a new, faster simdjson API with all the ease-of-use you are used to. While it provides a
familiar DOM interface, under the hood it is different: it is parsing values *as you use them.*
With On Demand, you do not waste time parsing JSON you do not use, and you do not pay the cost of generating
an intermediate DOM tree.
We provide an overview of what you need to know to use the simdjson On Demand API, with examples.
* [Including ondemand](#including-on-demand)
* [The Basics: Loading and Parsing JSON Documents](#the-basics-loading-and-parsing-json-documents)
* [Using the Parsed JSON](#using-the-parsed-json)
The On Demand API supports the same JSON standards and C++ compilers as simdjson's DOM API. Refer to the DOM docs for more information:
* [Requirements](basics.md##requirements)
* [Using simdjson as a CMake dependency](#using-simdjson-as-a-cmake-dependency)
* [Error Handling](basics.md#error-handling)
* [Error Handling Example](basics.md#error-handling-example)
* [Exceptions](basics.md#exceptions)
* [Thread Safety](basics.md#thread-safety)
* [Standard Compliance](basics.md#standard-compliance)
* [C++11 Support and string_view](basics.md#c11-support-and-string_view)
* [C++17 Support](basics.md#c17-support)
* [Backwards Compatibility](basics.md#backwards-compatibility)
For deeper information about the design and implementation of the simdjson On Demand API, refer to
the [design document](ondemand.md).
Including On Demand
------------------
To include simdjson, copy [simdjson.h](/singleheader/simdjson.h) and [simdjson.cpp](/singleheader/simdjson.cpp)
into your project. Then include it in your project with:
```c++
#include "simdjson.h"
using namespace simdjson; // optional
```
You can generally compile with:
```
c++ -O3 myproject.cpp simdjson.cpp
```
Note:
- Users on macOS and other platforms where compilers do not provide C++11 compliant by default
should request it with the appropriate flag (e.g., `c++ -march=native -std=c++17 myproject.cpp simdjson.cpp`).
The Basics: Loading and Parsing JSON Documents
----------------------------------------------
The simdjson library offers a simple DOM tree API, which you can access by creating a
`ondemand::parser` and calling the `iterate()` method:
```c++
ondemand::parser parser;
auto json = padded_string::load("twitter.json");
ondemand::document doc = parser.iterate(json); // load and parse a file
```
Or by creating a padded string (for efficiency reasons, simdjson requires a string with
SIMDJSON_PADDING bytes at the end) and calling `iterate()`:
```c++
ondemand::parser parser;
auto json = "[1,2,3]"_padded; // The _padded suffix creates a simdjson::padded_string instance
ondemand::document doc = parser.iterate(json); // parse a string
```
Documents Are Iterators
-----------------------
A `document` is *not* a fully-parsed JSON value; rather, it is an **iterator** over the JSON text.
This means that while you iterate an array, or search for a field in an object, it is actually
walking through the original JSON text, merrily reading commas and colons and brackets to make sure
you get where you are going. This is the key to On Demand's performance: since it's just an iterator,
it lets you parse values as you use them. And particularly, it lets you *skip* values you do not want
to use.
### Parser, Document and JSON Scope
Because a document is an iterator over the JSON text, both the JSON text and the parser must
remain alive (in scope) while you are using it. Further, a `parser` may have at most
one document open at a time, since it holds allocated memory used for the parsing.
During the `iterate` call, the original JSON text is never modified--only read. After you are done
with the document, the source (whether file or string) can be safely discarded.
For best performance, a `parser` instance should be reused over several files: otherwise you will
needlessly reallocate memory, an expensive process. It is also possible to avoid entirely memory
allocations during parsing when using simdjson. [See our performance notes for details](performance.md).
Using the Parsed JSON
---------------------
Once you have a document, you can navigate it with idiomatic C++ iterators, operators and casts.
The following show how to use the JSON when exceptions are enabled, but simdjson has full, idiomatic
support for users who avoid exceptions. See [the simdjson DOM API's error handling documentation](basics.md#error-handling) for more.
* **Extracting Values:** You can cast a JSON element to a native type:
`double(element)` or `double x = json_element`. This works for double, uint64_t, int64_t, bool,
ondemand::object and ondemand::array. At this point, the number, string or boolean will be parsed,
or the initial `[` or `{` will be verified. An exception is thrown if the cast is not possible.
> IMPORTANT NOTE: values can only be parsed once. Since documents are *iterators*, once you have
> parsed a value (such as by casting to double), you cannot get at it again.
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`. This will
scan through the object looking for the field with the matching string.
> NOTE: simdjson does *not* unescape keys when matching. This is not generally a problem for
> applications with well-defined key names (which generally do not use escapes). If you do need this
> support, it's best to iterate through the object fields to find the field you are looking for.
>
> By default, field lookup is order-insensitive, so you can look up values in any order. However,
> we still encourage you to look up fields in the order you expect them in the JSON, as it is still
> much faster.
>
> If you want to enforce finding fields in order, you can use `object.find_field("foo")` instead.
> This will only look forward, and will fail to find fields in the wrong order: for example, this
> will fail:
>
> ```c++
> ondemand::parser parser;
> auto json = R"( { "x": 1, "y": 2 } )"_padded;
> auto doc = parser.iterate(json);
> double y = doc.find_field("y"); // The cursor is now after the 2 (at })
> double x = doc.find_field("x"); // This fails, because there are no more fields after "y"
> ```
>
> By contrast, using the default (order-insensitive) lookup succeeds:
>
> ```c++
> ondemand::parser parser;
> auto json = R"( { "x": 1, "y": 2 } )"_padded;
> auto doc = parser.iterate(json);
> double y = doc["y"]; // The cursor is now after the 2 (at })
> double x = doc["x"]; // Success: [] loops back around to find "x"
> ```
* **Array Iteration:** To iterate through an array, use `for (auto value : array) { ... }`. This will
step through each value in the JSON array.
If you know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`.
* **Object Iteration:** You can iterate through an object's fields, as well: `for (auto field : object) { ... }`
- `field.unescaped_key()` will get you the key string.
- `field.value()` will get you the value, which you can then use all these other methods on.
* **Array Index:** Because it is forward-only, you cannot look up an array element by index. Instead,
you will need to iterate through the array and keep an index yourself.
### Examples
The following code illustrates many of the above concepts:
```c++
ondemand::parser parser;
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
// Iterating through an array of objects
for (ondemand::object car : parser.iterate(cars_json)) {
// Accessing a field by name
cout << "Make/Model: " << std::string_view(car["make"]) << "/" << std::string_view(car["model"]) << endl;
// Casting a JSON element to an integer
uint64_t year = car["year"];
cout << "- This car is " << 2020 - year << "years old." << endl;
// Iterating through an array of floats
double total_tire_pressure = 0;
for (double tire_pressure : car["tire_pressure"]) {
total_tire_pressure += tire_pressure;
}
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
}
```
Here is a different example illustrating the same ideas:
```C++
ondemand::parser parser;
auto points_json = R"( [
{ "12345" : {"x":12.34, "y":56.78, "z": 9998877} },
{ "12545" : {"x":11.44, "y":12.78, "z": 11111111} }
] )"_padded;
// Parse and iterate through an array of objects
for (ondemand::object points : parser.iterate(points_json)) {
for (auto point : points) {
cout << "id: " << std::string_view(point.unescaped_key()) << ": (";
cout << point.value()["x"].get_double() << ", ";
cout << point.value()["y"].get_double() << ", ";
cout << point.value()["z"].get_int64() << endl;
}
}
```
And another one:
```C++
auto abstract_json = R"(
{ "str" : { "123" : {"abc" : 3.14 } } }
)"_padded;
ondemand::parser parser;
auto doc = parser.iterate(abstract_json);
cout << doc["str"]["123"]["abc"].get_double() << endl; // Prints 3.14
```
* **Extracting Values (without exceptions):** You can use a variant usage of `get()` with error
codes to avoid exceptions. You first declare the variable of the appropriate type (`double`,
`uint64_t`, `int64_t`, `bool`, `ondemand::object` and `ondemand::array`) and pass it by reference
to `get()` which gives you back an error code: e.g.,
```c++
auto abstract_json = R"(
{ "str" : { "123" : {"abc" : 3.14 } } }
)"_padded;
ondemand::parser parser;
double value;
auto doc = parser.iterate(abstract_json);
auto error = doc["str"]["123"]["abc"].get(value);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
cout << value << endl; // Prints 3.14
```
+7 -2
View File
@@ -632,7 +632,12 @@ We iterate through object instances using `field` instances which represent key-
is accessible by the `value()` method whereas the key is accessible by the `key()` method.
The keys are treated differently than values are made available as as special type `raw_json_string`
which is a lightweight type that is meant to be used on a temporary basis, amost solely for
direct raw ASCII comparisons (`field.key() == "mykey"`). If you occasionally need to access and store the
direct raw ASCII comparisons: `key().raw()` provides direct access to the unescaped string.
You can compare `key()` with unescaped C strings (e.g., `key()=="test"`). It is expected
that the provided string is a valid JSON string. Importantly,
the C string must not contain an unescaped quote character (`"`). For speed, the comparison is done byte-by-byte
without handling the escaped caracters.
If you occasionally need to access and store the
unescaped key values, you may use the `unescaped_key()` method. Once you have called `unescaped_key()` method,
neither the `key()` nor the `unescaped_key()` methods should be called: the current field instance
has no longer a key (that is by design). Like other strings, the resulting `std::string_view` generated
@@ -775,4 +780,4 @@ Instead of specifying a specific microarchitecture, you can let your compiler do
Passing `-march=native` to the compiler may make On Demand faster by allowing it to use optimizations specific to your machine. You cannot do this, however, if you are compiling code that might be run on less advanced machines. That is, be mindful that when compiling with the `-march=native` flag, the resulting binary will run on the current system but may not run on other systems (e.g., on an old processor).
If you are compiling on an ARM or POWER system, you do not need to be concerned with CPU selection during compilation. The `-march=native` flag useful for best performance on x64 (e.g., Intel) systems but it is generally unsupported on some platforms such as ARM (aarch64) or POWER.
If you are compiling on an ARM or POWER system, you do not need to be concerned with CPU selection during compilation. The `-march=native` flag useful for best performance on x64 (e.g., Intel) systems but it is generally unsupported on some platforms such as ARM (aarch64) or POWER.
+24 -92
View File
@@ -5,15 +5,12 @@ simdjson strives to be at its fastest *without tuning*, and generally achieves t
are still some scenarios where tuning can enhance performance.
* [Reusing the parser for maximum efficiency](#reusing-the-parser-for-maximum-efficiency)
* [Keeping documents around for longer](#keeping-documents-around-for-longer)
* [Reusing string buffers](#reusing-string-buffers)
* [Server Loops: Long-Running Processes and Memory Capacity](#server-loops-long-running-processes-and-memory-capacity)
* [Large files and huge page support](#large-files-and-huge-page-support)
* [Number parsing](#number-parsing)
* [Visual Studio](#visual-studio)
* [Downclocking](#downclocking)
* [Best Use of the DOM API](#best-use-of-the-dom-api)
* [Padding and Temporary Copies](#padding-and-temporary-copies)
Reusing the parser for maximum efficiency
-----------------------------------------
@@ -24,77 +21,40 @@ buffers hot in cache and keeping memory allocation and initialization to a minim
you can parse terabytes of JSON data without doing any new allocation.
```c++
dom::parser parser;
ondemand::parser parser;
// This initializes buffers and a document big enough to handle this JSON.
dom::element doc = parser.parse("[ true, false ]"_padded);
cout << doc << endl;
// This initializes buffers big enough to handle this JSON.
auto json = "[ true, false ]"_padded;
auto doc = parser.iterate(json);
for(bool i : doc.get_array()) {
cout << i << endl;
}
// This reuses the existing buffers, and reuses and *overwrites* the old document
doc = parser.parse("[1, 2, 3]"_padded);
cout << doc << endl;
// This also reuses the existing buffers, and reuses and *overwrites* the old document
dom::element doc2 = parser.parse("true"_padded);
// Even if you keep the old reference around, doc and doc2 refer to the same document.
cout << doc << endl;
cout << doc2 << endl;
// This reuses the existing buffers
auto number_json = "[1, 2, 3]"_padded;
doc = parser.iterate(number_json);
for(int64_t i : doc.get_array()) {
cout << i << endl;
}
```
It's not just internal buffers though. The simdjson library reuses the document itself. The dom::element, dom::object and dom::array instances are *references* to the internal document.
You are only *borrowing* the document from simdjson, which purposely reuses and overwrites it each
time you call parse. This prevent wasteful and unnecessary memory allocation in 99% of cases where
JSON is just read, used, and converted to native values or thrown away.
> **You are only borrowing the document from the simdjson parser. Don't keep it long term!**
Reusing string buffers
-----------------------------------------
This is key: don't keep the `document&`, `dom::element`, `dom::array`, `dom::object`
or `string_view` objects you get back from the API. Convert them to C++ native values, structs and
arrays that you own.
We recommend against creating many `std::string` or `simdjson::padded_string` instances to store the JSON content in your application. [Creating many non-trivial objects is convenient but often surprisingly slow](https://lemire.me/blog/2020/08/08/performance-tip-constructing-many-non-trivial-objects-is-slow/). Instead, as much as possible, you should allocate (once or a few times) reusable memory buffers where you write your JSON content. If you have a buffer `json_str` (of type `char*`) allocated for `capacity` bytes and you store a JSON document spanning `length` bytes, you can pass it to simdjson as follows:
Server Loops: Long-Running Processes and Memory Capacity
--------------------------------------------------------
```c++
auto doc = parser.iterate(padded_string_view(json_str, length, capacity));
```
The simdjson library automatically expands its memory capacity when larger documents are parsed, so
that you don't unexpectedly fail. In a short process that reads a bunch of files and then exits,
this works pretty flawlessly.
or simply
Server loops, though, are long-running processes that will keep the parser around forever. This
means that if you encounter a really, really large document, simdjson will not resize back down.
The simdjson library lets you adjust your allocation strategy to prevent your server from growing
without bound:
* You can set a *max capacity* when constructing a parser:
```c++
auto doc = parser.iterate(json_str, length, capacity);
```
```c++
dom::parser parser(1000*1000); // Never grow past documents > 1MB
for (web_request request : listen()) {
dom::element doc;
auto error = parser.parse(request.body).get(doc);
// If the document was above our limit, emit 413 = payload too large
if (error == CAPACITY) { request.respond(413); continue; }
// ...
}
```
This parser will grow normally as it encounters larger documents, but will never pass 1MB.
* You can set a *fixed capacity* that never grows, as well, which can be excellent for
predictability and reliability, since simdjson will never call malloc after startup!
```c++
dom::parser parser(0); // This parser will refuse to automatically grow capacity
auto error = parser.allocate(1000*1000); // This allocates enough capacity to handle documents <= 1MB
if (error) { cerr << error << endl; exit(1); }
for (web_request request : listen()) {
dom::element doc;
error = parser.parse(request.body).get(doc);
// If the document was above our limit, emit 413 = payload too large
if (error == CAPACITY) { request.respond(413); continue; }
// ...
}
```
Large files and huge page support
---------------------------------
@@ -170,31 +130,3 @@ On some Intel processors, using SIMD instructions in a sustained manner on the s
The simdjson library does not currently support AVX-512 instructions and it does not make use of heavy 256-bit instructions. We do use vectorized multiplications, but only using 128-bit registers. Thus there should be no downclocking due to simdjson on recent processors.
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.
Best Use of the DOM API
-------------------------
The simdjson API provides access to the JSON DOM (document-object-model) content as a tree of `dom::element` instances, each representing an object, an array or an atomic type (null, true, false, number). These `dom::element` instances are lightweight objects (e.g., spanning 16 bytes) and it might be advantageous to pass them by value, as opposed to passing them by reference or by pointer.
Padding and Temporary Copies
--------------
The simdjson function `parser.parse` reads data from a padded buffer, containing SIMDJSON_PADDING extra bytes added at the end.
If you are passing a `padded_string` to `parser.parse` or loading the JSON directly from
disk (`parser.load`), padding is automatically handled.
When calling `parser.parse` on a pointer (e.g., `parser.parse(my_char_pointer, my_length_in_bytes)`) a temporary copy is made by default with adequate padding and you, again, do not need to be concerned with padding.
Some users may not be able use our `padded_string` class or to load the data directly from disk (`parser.load`). They may need to pass data pointers to the library. If these users wish to avoid temporary copies and corresponding temporary memory allocations, they may want to call `parser.parse` with the `realloc_if_needed` parameter set to false (e.g., `parser.parse(my_char_pointer, my_length_in_bytes, false)`). In such cases, they need to ensure that there are at least SIMDJSON_PADDING extra bytes at the end that can be safely accessed and read. They do not need to initialize the padded bytes to any value in particular. The following example is safe:
```C++
const char *json = R"({"key":"value"})";
const size_t json_len = std::strlen(json);
std::unique_ptr<char[]> padded_json_copy{new char[json_len + SIMDJSON_PADDING]};
memcpy(padded_json_copy.get(), json, json_len);
memset(padded_json_copy.get() + json_len, 0, SIMDJSON_PADDING);
simdjson::dom::parser parser;
simdjson::dom::element element = parser.parse(padded_json_copy.get(), json_len, false);
````
Setting the `realloc_if_needed` parameter `false` in this manner may lead to better performance since copies are avoided, but it requires that the user takes more responsibilities: the simdjson library cannot verify that the input buffer was padded with SIMDJSON_PADDING extra bytes.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

+3 -3
View File
@@ -31,7 +31,7 @@ The following is a dump of the content of the tape, with the first number of eac
### The Tape
| index | element (64 bit word) |
| ----- | ------------------------------------------------------------------- |
| 0 | r // pointing to 38 (right after last node) |
| 0 | r // pointing to 39 (right after last node) |
| 1 | { // pointing to next tape location 38 (first node after the scope) |
| 2 | string "Image" |
| 3 | { // pointing to next tape location 37 (first node after the scope) |
@@ -115,7 +115,7 @@ We store string values using UTF-8 encoding with null termination on a separate
JSON arrays are represented using two 64-bit tape elements.
- The first 64-bit tape element contains the value `('[' << 56) + x` where the payload `x` is 1 + the index of the second 64-bit tape element on the tape.
- The first 64-bit tape element contains the value `('[' << 56) + (c << 32) + x` where the payload `x` is 1 + the index of the second 64-bit tape element on the tape as a 32-bit integer and where `c` is the count of the number of elements (immediate children) in the array, satured to a 24-bit value (meaning that it cannot exceed 16777215 and if the real count exceeds 16777215, 16777215 is stored). Note that the exact count of elements can always be computed by iterating (e.g., when it is 16777215 or higher).
- The second 64-bit tape element contains the value `(']' << 56) + x` where the payload `x` contains the index of the first 64-bit tape element on the tape.
All the content of the array is located between these two tape elements, including arrays and objects.
@@ -126,7 +126,7 @@ Performance consideration: We can skip the content of an array entirely by acces
JSON objects are represented using two 64-bit tape elements.
- The first 64-bit tape element contains the value `('{' << 56) + x` where the payload `x` is 1 + the index of the second 64-bit tape element on the tape.
- The first 64-bit tape element contains the value `('{' << 56) + (c << 32) + x` where the payload `x` is 1 + the index of the second 64-bit tape element on the tape as a 32-bit integer and where `c` is the count of the number of key-value pairs (immediate children) in the array, satured to a 24-bit value (meaning that it cannot exceed 16777215 and if the real count exceeds 16777215, 16777215 is stored). Note that the exact count of key-value pairs can always be computed by iterating (e.g., when it is 16777215 or higher).
- The second 64-bit tape element contains the value `('}' << 56) + x` where the payload `x` contains the index of the first 64-bit tape element on the tape.
In-between these two tape elements, we alternate between key (which must be strings) and values. A value could be an object or an array.
+6 -1
View File
@@ -1,4 +1,9 @@
option(ENABLE_FUZZING "enable building the fuzzers" ON)
if(NOT SIMDJSON_LEGACY_VISUAL_STUDIO AND NOT SIMDJSON_WINDOWS_DLL)
option(ENABLE_FUZZING "enable building the fuzzers" ON)
else()
option(ENABLE_FUZZING "enable building the fuzzers" OFF)
endif()
if(ENABLE_FUZZING)
+2 -2
View File
@@ -17,7 +17,7 @@ static void print_json(std::ostream& os, simdjson::dom::element element) noexcep
case simdjson::dom::element_type::ARRAY:
os << "[";
{
simdjson::dom::array array = element.get<simdjson::dom::array>().value_unsafe();
simdjson::dom::array array = element.get_array().value_unsafe();
for (simdjson::dom::element child : array) {
print_json(os, child);
os << ",";
@@ -28,7 +28,7 @@ static void print_json(std::ostream& os, simdjson::dom::element element) noexcep
case simdjson::dom::element_type::OBJECT:
os << "{";
{
simdjson::dom::object object = element.get<simdjson::dom::object>().value_unsafe();
simdjson::dom::object object = element.get_object().value_unsafe();
for (simdjson::dom::key_value_pair field : object) {
os << "\"" << field.key << "\": ";
print_json(os, field.value);
+2 -56
View File
@@ -23,7 +23,7 @@
] )"_padded;
for (simdjson::dom::object obj : parser.parse(abstract_json)) {
for(const auto& key_value : obj) {
for(const auto key_value : obj) {
cout << "key: " << key_value.key << " : ";
simdjson::dom::object innerobj = key_value.value;
cout << "a: " << double(innerobj["a"]) << ", ";
@@ -34,61 +34,7 @@
}
*/
#include "simdjson/compiler_check.h"
#include "simdjson/common_defs.h"
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_UNDESIRED_WARNINGS
// Public API
#include "simdjson/simdjson_version.h"
#include "simdjson/error.h"
#include "simdjson/minify.h"
#include "simdjson/padded_string.h"
#include "simdjson/implementation.h"
#include "simdjson/dom/array.h"
#include "simdjson/dom/document_stream.h"
#include "simdjson/dom/document.h"
#include "simdjson/dom/element.h"
#include "simdjson/dom/object.h"
#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/error-inl.h"
#include "simdjson/dom/object-inl.h"
#include "simdjson/padded_string-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"
// Implementation-internal files (must be included before the implementations themselves, to keep
// amalgamation working--otherwise, the first time a file is included, it might be put inside the
// #ifdef SIMDJSON_IMPLEMENTATION_ARM64/FALLBACK/etc., which means the other implementations can't
// compile unless that implementation is turned on).
#include "simdjson/internal/isadetection.h"
#include "simdjson/internal/jsoncharutils_tables.h"
#include "simdjson/internal/numberparsing_tables.h"
#include "simdjson/internal/simdprune_tables.h"
// Implementations
#include "simdjson/arm64.h"
#include "simdjson/haswell.h"
#include "simdjson/westmere.h"
#include "simdjson/ppc64.h"
#include "simdjson/fallback.h"
#include "simdjson/dom.h"
#include "simdjson/builtin.h"
SIMDJSON_POP_DISABLE_WARNINGS
#endif // SIMDJSON_H
+1 -22
View File
@@ -1,22 +1,7 @@
#ifndef SIMDJSON_ARM64_H
#define SIMDJSON_ARM64_H
#ifdef SIMDJSON_FALLBACK_H
#error "arm64.h must be included before fallback.h"
#endif
#include "simdjson/portability.h"
#ifndef SIMDJSON_IMPLEMENTATION_ARM64
#define SIMDJSON_IMPLEMENTATION_ARM64 (SIMDJSON_IS_ARM64)
#endif
#define SIMDJSON_CAN_ALWAYS_RUN_ARM64 SIMDJSON_IMPLEMENTATION_ARM64 && SIMDJSON_IS_ARM64
#include "simdjson/internal/isadetection.h"
#include "simdjson/internal/jsoncharutils_tables.h"
#include "simdjson/internal/numberparsing_tables.h"
#include "simdjson/internal/simdprune_tables.h"
#include "simdjson/implementation-base.h"
#if SIMDJSON_IMPLEMENTATION_ARM64
@@ -42,12 +27,6 @@ namespace arm64 {
#include "simdjson/generic/atomparsing.h"
#include "simdjson/arm64/stringparsing.h"
#include "simdjson/arm64/numberparsing.h"
#include "simdjson/generic/implementation_simdjson_result_base.h"
#include "simdjson/generic/ondemand.h"
// Inline definitions
#include "simdjson/generic/implementation_simdjson_result_base-inl.h"
#include "simdjson/generic/ondemand-inl.h"
#include "simdjson/arm64/end.h"
#endif // SIMDJSON_IMPLEMENTATION_ARM64
+1 -1
View File
@@ -52,7 +52,7 @@ simdjson_really_inline bool add_overflow(uint64_t value1, uint64_t value2, uint6
return *result < value1;
#else
return __builtin_uaddll_overflow(value1, value2,
(unsigned long long *)result);
reinterpret_cast<unsigned long long *>(result));
#endif
}
+1 -1
View File
@@ -1,7 +1,7 @@
#ifndef SIMDJSON_ARM64_IMPLEMENTATION_H
#define SIMDJSON_ARM64_IMPLEMENTATION_H
#include "simdjson.h"
#include "simdjson/base.h"
#include "simdjson/internal/isadetection.h"
namespace simdjson {
-2
View File
@@ -1,8 +1,6 @@
#ifndef SIMDJSON_ARM64_INTRINSICS_H
#define SIMDJSON_ARM64_INTRINSICS_H
#include "simdjson.h"
// This should be the correct header whether
// you use visual studio or other compilers.
#include <arm_neon.h>
+7 -7
View File
@@ -1,7 +1,7 @@
#ifndef SIMDJSON_ARM64_SIMD_H
#define SIMDJSON_ARM64_SIMD_H
#include "simdjson.h"
#include "simdjson/base.h"
#include "simdjson/internal/simdprune_tables.h"
#include "simdjson/arm64/bitmanipulation.h"
#include <type_traits>
@@ -115,9 +115,9 @@ simdjson_really_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x
simdjson_really_inline simd8<T> operator^(const simd8<T> other) const { return veorq_u8(*this, other); }
simdjson_really_inline simd8<T> bit_andnot(const simd8<T> other) const { return vbicq_u8(*this, other); }
simdjson_really_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
simdjson_really_inline simd8<T>& operator|=(const simd8<T> other) { auto this_cast = (simd8<T>*)this; *this_cast = *this_cast | other; return *this_cast; }
simdjson_really_inline simd8<T>& operator&=(const simd8<T> other) { auto this_cast = (simd8<T>*)this; *this_cast = *this_cast & other; return *this_cast; }
simdjson_really_inline simd8<T>& operator^=(const simd8<T> other) { auto this_cast = (simd8<T>*)this; *this_cast = *this_cast ^ other; return *this_cast; }
simdjson_really_inline simd8<T>& operator|=(const simd8<T> other) { auto this_cast = static_cast<simd8<T>*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_really_inline simd8<T>& operator&=(const simd8<T> other) { auto this_cast = static_cast<simd8<T>*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_really_inline simd8<T>& operator^=(const simd8<T> other) { auto this_cast = static_cast<simd8<T>*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
simdjson_really_inline Mask operator==(const simd8<T> other) const { return vceqq_u8(*this, other); }
@@ -284,9 +284,9 @@ simdjson_really_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x
// only the first pop1 bytes from the first 8 bytes, and then
// it fills in with the bytes from the second 8 bytes + some filling
// at the end.
uint8x16_t compactmask = vld1q_u8((const uint8_t *)(pshufb_combine_table + pop1 * 8));
uint8x16_t compactmask = vld1q_u8(reinterpret_cast<const uint8_t *>(pshufb_combine_table + pop1 * 8));
uint8x16_t answer = vqtbl1q_u8(pruned, compactmask);
vst1q_u8((uint8_t*) output, answer);
vst1q_u8(reinterpret_cast<uint8_t*>(output), answer);
}
template<typename L>
@@ -421,7 +421,7 @@ simdjson_really_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x
const simd8<T> chunks[NUM_CHUNKS];
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T> other) = delete; // no assignment allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_really_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1, const simd8<T> chunk2, const simd8<T> chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {}
+1 -1
View File
@@ -1,7 +1,7 @@
#ifndef SIMDJSON_ARM64_STRINGPARSING_H
#define SIMDJSON_ARM64_STRINGPARSING_H
#include "simdjson.h"
#include "simdjson/base.h"
#include "simdjson/arm64/simd.h"
#include "simdjson/arm64/bitmanipulation.h"
+26
View File
@@ -0,0 +1,26 @@
#ifndef SIMDJSON_BASE_H
#define SIMDJSON_BASE_H
#include "simdjson/compiler_check.h"
#include "simdjson/common_defs.h"
#include "simdjson/portability.h"
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_UNDESIRED_WARNINGS
// Public API
#include "simdjson/simdjson_version.h"
#include "simdjson/error.h"
#include "simdjson/minify.h"
#include "simdjson/padded_string.h"
#include "simdjson/padded_string_view.h"
#include "simdjson/implementation.h"
// Inline functions
#include "simdjson/error-inl.h"
#include "simdjson/padded_string-inl.h"
#include "simdjson/padded_string_view-inl.h"
SIMDJSON_POP_DISABLE_WARNINGS
#endif // SIMDJSON_BASE_H
+17 -2
View File
@@ -1,8 +1,9 @@
#ifndef SIMDJSON_BUILTIN_H
#define SIMDJSON_BUILTIN_H
#include "simdjson/portability.h"
#include "simdjson/implementations.h"
// Determine the best builtin implementation
#ifndef SIMDJSON_BUILTIN_IMPLEMENTATION
#if SIMDJSON_CAN_ALWAYS_RUN_HASWELL
#define SIMDJSON_BUILTIN_IMPLEMENTATION haswell
@@ -19,6 +20,20 @@
#endif
#endif // SIMDJSON_BUILTIN_IMPLEMENTATION
#define SIMDJSON_IMPLEMENTATION SIMDJSON_BUILTIN_IMPLEMENTATION
// ondemand is only compiled as part of the builtin implementation at present
// Interface declarations
#include "simdjson/generic/implementation_simdjson_result_base.h"
#include "simdjson/generic/ondemand.h"
// Inline definitions
#include "simdjson/generic/implementation_simdjson_result_base-inl.h"
#include "simdjson/generic/ondemand-inl.h"
#undef SIMDJSON_IMPLEMENTATION
namespace simdjson {
/**
* Represents the best statically linked simdjson implementation that can be used by the compiling
@@ -32,7 +47,7 @@ namespace simdjson {
*/
namespace builtin = SIMDJSON_BUILTIN_IMPLEMENTATION;
/**
* @overload simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand
* @copydoc simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand
*/
namespace ondemand = SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand;
/**
+37 -3
View File
@@ -149,15 +149,43 @@ constexpr size_t DEFAULT_MAX_DEPTH = 1024;
#if defined(SIMDJSON_VISUAL_STUDIO)
/**
* Windows users need to do some extra work when building
* or using a dynamic library (DLL). When building, we need
* to set SIMDJSON_DLLIMPORTEXPORT to __declspec(dllexport).
* When *using* the DLL, the user needs to set
* SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport).
*
* Static libraries not need require such work.
*
* It does not matter here whether you are using
* the regular visual studio or clang under visual
* studio.
* studio, you still need to handle these issues.
*
* Non-Windows sytems do not have this complexity.
*/
#if SIMDJSON_USING_LIBRARY
#if SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY
// We set SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY when we build a DLL under Windows.
// It should never happen that both SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY and
// SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY are set.
#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllexport)
#elif SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY
// Windows user who call a dynamic library should set SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY to 1.
#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport)
#else
#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllexport)
// We assume by default static linkage
#define SIMDJSON_DLLIMPORTEXPORT
#endif
/**
* Workaround for the vcpkg package manager. Only vcpkg should
* ever touch the next line. The SIMDJSON_USING_LIBRARY macro is otherwise unused.
*/
#if SIMDJSON_USING_LIBRARY
#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport)
#endif
/**
* End of workaround for the vcpkg package manager.
*/
#else
#define SIMDJSON_DLLIMPORTEXPORT
#endif
@@ -165,6 +193,7 @@ constexpr size_t DEFAULT_MAX_DEPTH = 1024;
// C++17 requires string_view.
#if SIMDJSON_CPLUSPLUS17
#define SIMDJSON_HAS_STRING_VIEW
#include <string_view> // by the standard, this has to be safe.
#endif
// This macro (__cpp_lib_string_view) has to be defined
@@ -216,5 +245,10 @@ namespace std {
/// If EXPR is an error, returns it.
#define SIMDJSON_TRY(EXPR) { auto _err = (EXPR); if (_err) { return _err; } }
#ifndef SIMDJSON_DEVELOPMENT_CHECKS
#ifndef NDEBUG
#define SIMDJSON_DEVELOPMENT_CHECKS
#endif
#endif
#endif // SIMDJSON_COMMON_DEFS_H
+35
View File
@@ -0,0 +1,35 @@
#ifndef SIMDJSON_DOM_H
#define SIMDJSON_DOM_H
#include "simdjson/base.h"
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_UNDESIRED_WARNINGS
#include "simdjson/dom/array.h"
#include "simdjson/dom/document_stream.h"
#include "simdjson/dom/document.h"
#include "simdjson/dom/element.h"
#include "simdjson/dom/object.h"
#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"
SIMDJSON_POP_DISABLE_WARNINGS
#endif // SIMDJSON_DOM_H
+1 -1
View File
@@ -61,7 +61,7 @@ inline array::iterator array::end() const noexcept {
inline size_t array::size() const noexcept {
return tape.scope_count();
}
inline size_t array::slots() const noexcept {
inline size_t array::number_of_slots() const noexcept {
return tape.matching_brace_index() - tape.json_index;
}
inline simdjson_result<element> array::at_pointer(std::string_view json_pointer) const noexcept {
+1 -1
View File
@@ -95,7 +95,7 @@ public:
* to figure out the total size of an array (including its children,
* recursively) if you know its structure ahead of time.
**/
inline size_t slots() const noexcept;
inline size_t number_of_slots() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
+34 -17
View File
@@ -19,27 +19,41 @@ namespace dom {
inline element document::root() const noexcept {
return element(internal::tape_ref(this, 1));
}
simdjson_warn_unused
inline size_t document::capacity() const noexcept {
return allocated_capacity;
}
simdjson_warn_unused
inline error_code document::allocate(size_t capacity) noexcept {
if (capacity == 0) {
string_buf.reset();
tape.reset();
allocated_capacity = 0;
return SUCCESS;
}
// a pathological input like "[[[[..." would generate len tape elements, so
// need a capacity of at least len + 1, but it is also possible to do
// a pathological input like "[[[[..." would generate capacity tape elements, so
// need a capacity of at least capacity + 1, but it is also possible to do
// worse with "[7,7,7,7,6,7,7,7,6,7,7,6,[7,7,7,7,6,7,7,7,6,7,7,6,7,7,7,7,7,7,6"
//where len + 1 tape elements are
//where capacity + 1 tape elements are
// generated, see issue https://github.com/lemire/simdjson/issues/345
size_t tape_capacity = SIMDJSON_ROUNDUP_N(capacity + 3, 64);
// a document with only zero-length strings... could have len/3 string
// and we would need len/3 * 5 bytes on the string buffer
// a document with only zero-length strings... could have capacity/3 string
// and we would need capacity/3 * 5 bytes on the string buffer
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * capacity / 3 + SIMDJSON_PADDING, 64);
string_buf.reset( new (std::nothrow) uint8_t[string_capacity]);
tape.reset(new (std::nothrow) uint64_t[tape_capacity]);
return string_buf && tape ? SUCCESS : MEMALLOC;
if(!(string_buf && tape)) {
allocated_capacity = 0;
string_buf.reset();
tape.reset();
return MEMALLOC;
}
// Technically the allocated_capacity might be larger than capacity
// so the next line is pessimistic.
allocated_capacity = capacity;
return SUCCESS;
}
inline bool document::dump_raw_tape(std::ostream &os) const noexcept {
@@ -68,7 +82,7 @@ inline bool document::dump_raw_tape(std::ostream &os) const noexcept {
os << "string \"";
std::memcpy(&string_length, string_buf.get() + payload, sizeof(uint32_t));
os << internal::escape_json_string(std::string_view(
(const char *)(string_buf.get() + payload + sizeof(uint32_t)),
reinterpret_cast<const char *>(string_buf.get() + payload + sizeof(uint32_t)),
string_length
));
os << '"';
@@ -105,20 +119,23 @@ inline bool document::dump_raw_tape(std::ostream &os) const noexcept {
os << "false\n";
break;
case '{': // we have an object
os << "{\t// pointing to next tape location " << payload
<< " (first node after the scope) \n";
break;
case '}': // we end an object
os << "}\t// pointing to previous tape location " << payload
<< " (start of the scope) \n";
os << "{\t// pointing to next tape location " << uint32_t(payload)
<< " (first node after the scope), "
<< " saturated count "
<< ((payload >> 32) & internal::JSON_COUNT_MASK)<< "\n";
break; case '}': // we end an object
os << "}\t// pointing to previous tape location " << uint32_t(payload)
<< " (start of the scope)\n";
break;
case '[': // we start an array
os << "[\t// pointing to next tape location " << payload
<< " (first node after the scope) \n";
os << "[\t// pointing to next tape location " << uint32_t(payload)
<< " (first node after the scope), "
<< " saturated count "
<< ((payload >> 32) & internal::JSON_COUNT_MASK)<< "\n";
break;
case ']': // we end an array
os << "]\t// pointing to previous tape location " << payload
<< " (start of the scope) \n";
os << "]\t// pointing to previous tape location " << uint32_t(payload)
<< " (start of the scope)\n";
break;
case 'r': // we start and end with the root node
// should we be hitting the root node?
+19 -1
View File
@@ -63,9 +63,27 @@ public:
* Should be at least byte_capacity.
*/
std::unique_ptr<uint8_t[]> string_buf{};
/** @private Allocate memory to support
* input JSON documents of up to len bytes.
*
* When calling this function, you lose
* all the data.
*
* The memory allocation is strict: you
* can you use this function to increase
* or lower the amount of allocated memory.
* Passsing zero clears the memory.
*/
error_code allocate(size_t len) noexcept;
/** @private Capacity in bytes, in terms
* of how many bytes of input JSON we can
* support.
*/
size_t capacity() const noexcept;
private:
inline error_code allocate(size_t len) noexcept;
size_t allocated_capacity{0};
friend class parser;
}; // class document
+19 -16
View File
@@ -121,28 +121,32 @@ simdjson_really_inline document_stream::~document_stream() noexcept {
#endif
}
simdjson_really_inline document_stream::iterator::iterator() noexcept
: stream{nullptr}, finished{true} {
}
simdjson_really_inline document_stream::iterator document_stream::begin() noexcept {
start();
// If there are no documents, we're finished.
return iterator(*this, error == EMPTY);
return iterator(this, error == EMPTY);
}
simdjson_really_inline document_stream::iterator document_stream::end() noexcept {
return iterator(*this, true);
return iterator(this, true);
}
simdjson_really_inline document_stream::iterator::iterator(document_stream& _stream, bool is_end) noexcept
simdjson_really_inline document_stream::iterator::iterator(document_stream* _stream, bool is_end) noexcept
: stream{_stream}, finished{is_end} {
}
simdjson_really_inline simdjson_result<element> document_stream::iterator::operator*() noexcept {
simdjson_really_inline document_stream::iterator::reference document_stream::iterator::operator*() noexcept {
// Note that in case of error, we do not yet mark
// the iterator as "finished": this detection is done
// in the operator++ function since it is possible
// to call operator++ repeatedly while omitting
// calls to operator*.
if (stream.error) { return stream.error; }
return stream.parser->doc.root();
if (stream->error) { return stream->error; }
return stream->parser->doc.root();
}
simdjson_really_inline document_stream::iterator& document_stream::iterator::operator++() noexcept {
@@ -156,16 +160,16 @@ simdjson_really_inline document_stream::iterator& document_stream::iterator::ope
//
// Note that setting finished = true is essential otherwise
// we would enter an infinite loop.
if (stream.error) { finished = true; }
// Note that stream.error() is guarded against error conditions
// (it will immediately return if stream.error casts to false).
// In effect, this next function does nothing when (stream.error)
if (stream->error) { finished = true; }
// Note that stream->error() is guarded against error conditions
// (it will immediately return if stream->error casts to false).
// In effect, this next function does nothing when (stream->error)
// is true (hence the risk of an infinite loop).
stream.next();
stream->next();
// If that was the last document, we're finished.
// It is the only type of error we do not want to appear
// in operator*.
if (stream.error == EMPTY) { finished = true; }
if (stream->error == EMPTY) { finished = true; }
// If we had any other kind of error (not EMPTY) then we want
// to pass it along to the operator* and we cannot mark the result
// as "finished" just yet.
@@ -180,7 +184,6 @@ inline void document_stream::start() noexcept {
if (error) { return; }
error = parser->ensure_capacity(batch_size);
if (error) { return; }
// Always run the first stage 1 parse immediately
batch_start = 0;
error = run_stage1(*parser, batch_start);
@@ -206,12 +209,12 @@ inline void document_stream::start() noexcept {
}
simdjson_really_inline size_t document_stream::iterator::current_index() const noexcept {
return stream.doc_index;
return stream->doc_index;
}
simdjson_really_inline std::string_view document_stream::iterator::source() const noexcept {
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 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);
}
+15 -4
View File
@@ -93,12 +93,23 @@ public:
*/
class iterator {
public:
using value_type = simdjson_result<element>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
/**
* Default contructor.
*/
simdjson_really_inline iterator() noexcept;
/**
* Get the current document (or error).
*/
simdjson_really_inline simdjson_result<element> operator*() noexcept;
simdjson_really_inline reference operator*() noexcept;
/**
* Advance to the next document.
* Advance to the next document (prefix).
*/
inline iterator& operator++() noexcept;
/**
@@ -144,9 +155,9 @@ public:
simdjson_really_inline std::string_view source() const noexcept;
private:
simdjson_really_inline iterator(document_stream &s, bool finished) noexcept;
simdjson_really_inline iterator(document_stream *s, bool finished) noexcept;
/** The document_stream we're iterating through. */
document_stream& stream;
document_stream* stream;
/** Whether we're finished or not. */
bool finished;
friend class document_stream;
+15 -23
View File
@@ -48,8 +48,6 @@ public:
/**
* Cast this element to an array.
*
* Equivalent to get<array>().
*
* @returns An object that can be used to iterate the array, or:
* INCORRECT_TYPE if the JSON element is not an array.
*/
@@ -57,8 +55,6 @@ public:
/**
* Cast this element to an object.
*
* Equivalent to get<object>().
*
* @returns An object that can be used to look up or iterate the object's fields, or:
* INCORRECT_TYPE if the JSON element is not an object.
*/
@@ -68,8 +64,6 @@ public:
*
* The string is guaranteed to be valid UTF-8.
*
* The get_c_str() function is equivalent to get<const char *>().
*
* The length of the string is given by get_string_length(). Because JSON strings
* may contain null characters, it may be incorrect to use strlen to determine the
* string length.
@@ -97,8 +91,6 @@ public:
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next time it
* parses a document or when it is destroyed.
* Returns INCORRECT_TYPE if the JSON element is not a string.
@@ -107,8 +99,6 @@ public:
/**
* Cast this element to a signed integer.
*
* Equivalent to get<int64_t>().
*
* @returns A signed 64-bit integer.
* Returns INCORRECT_TYPE if the JSON element is not an integer, or NUMBER_OUT_OF_RANGE
* if it is negative.
@@ -117,8 +107,6 @@ public:
/**
* Cast this element to an unsigned integer.
*
* Equivalent to get<uint64_t>().
*
* @returns An unsigned 64-bit integer.
* Returns INCORRECT_TYPE if the JSON element is not an integer, or NUMBER_OUT_OF_RANGE
* if it is too large.
@@ -127,8 +115,6 @@ public:
/**
* Cast this element to a double floating-point.
*
* Equivalent to get<double>().
*
* @returns A double value.
* Returns INCORRECT_TYPE if the JSON element is not a number.
*/
@@ -136,8 +122,6 @@ public:
/**
* Cast this element to a bool.
*
* Equivalent to get<bool>().
*
* @returns A bool value.
* Returns INCORRECT_TYPE if the JSON element is not a boolean.
*/
@@ -223,13 +207,21 @@ public:
* - Array: dom::array
* - Object: dom::object
*
* You may use get_double(), get_bool(), get_uint64(), get_int64(),
* get_object(), get_array() or get_string() instead.
*
* @tparam T bool, double, uint64_t, int64_t, std::string_view, const char *, dom::array, dom::object
*
* @returns The value cast to the given type, or:
* INCORRECT_TYPE if the value cannot be cast to the given type.
*/
template<typename T>
inline simdjson_result<T> get() const noexcept;
inline simdjson_result<T> get() const noexcept {
// Unless the simdjson library 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.");
}
/**
* Get the value as the provided type (T).
@@ -363,8 +355,8 @@ public:
* The key will be matched against **unescaped** JSON:
*
* dom::parser parser;
* parser.parse(R"({ "a\n": 1 })"_padded)["a\n"].get<uint64_t>().first == 1
* parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get<uint64_t>().error() == NO_SUCH_FIELD
* parser.parse(R"({ "a\n": 1 })"_padded)["a\n"].get_uint64().first == 1
* parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get_uint64().error() == NO_SUCH_FIELD
*
* @return The value associated with this field, or:
* - NO_SUCH_FIELD if the field does not exist in the object
@@ -378,8 +370,8 @@ public:
* The key will be matched against **unescaped** JSON:
*
* dom::parser parser;
* parser.parse(R"({ "a\n": 1 })"_padded)["a\n"].get<uint64_t>().first == 1
* parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get<uint64_t>().error() == NO_SUCH_FIELD
* parser.parse(R"({ "a\n": 1 })"_padded)["a\n"].get_uint64().first == 1
* parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get_uint64().error() == NO_SUCH_FIELD
*
* @return The value associated with this field, or:
* - NO_SUCH_FIELD if the field does not exist in the object
@@ -450,8 +442,8 @@ public:
* The key will be matched against **unescaped** JSON:
*
* dom::parser parser;
* parser.parse(R"({ "a\n": 1 })"_padded)["a\n"].get<uint64_t>().first == 1
* parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get<uint64_t>().error() == NO_SUCH_FIELD
* parser.parse(R"({ "a\n": 1 })"_padded)["a\n"].get_uint64().first == 1
* parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get_uint64().error() == NO_SUCH_FIELD
*
* @return The value associated with this field, or:
* - NO_SUCH_FIELD if the field does not exist in the object
+6 -6
View File
@@ -123,8 +123,8 @@ public:
* The key will be matched against **unescaped** JSON:
*
* dom::parser parser;
* parser.parse(R"({ "a\n": 1 })"_padded)["a\n"].get<uint64_t>().first == 1
* parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get<uint64_t>().error() == NO_SUCH_FIELD
* parser.parse(R"({ "a\n": 1 })"_padded)["a\n"].get_uint64().first == 1
* parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get_uint64().error() == NO_SUCH_FIELD
*
* This function has linear-time complexity: the keys are checked one by one.
*
@@ -140,8 +140,8 @@ public:
* The key will be matched against **unescaped** JSON:
*
* dom::parser parser;
* parser.parse(R"({ "a\n": 1 })"_padded)["a\n"].get<uint64_t>().first == 1
* parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get<uint64_t>().error() == NO_SUCH_FIELD
* parser.parse(R"({ "a\n": 1 })"_padded)["a\n"].get_uint64().first == 1
* parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get_uint64().error() == NO_SUCH_FIELD
*
* This function has linear-time complexity: the keys are checked one by one.
*
@@ -182,8 +182,8 @@ public:
* The key will be matched against **unescaped** JSON:
*
* dom::parser parser;
* parser.parse(R"({ "a\n": 1 })"_padded)["a\n"].get<uint64_t>().first == 1
* parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get<uint64_t>().error() == NO_SUCH_FIELD
* parser.parse(R"({ "a\n": 1 })"_padded)["a\n"].get_uint64().first == 1
* parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get_uint64().error() == NO_SUCH_FIELD
*
* This function has linear-time complexity: the keys are checked one by one.
*
+60 -27
View File
@@ -46,11 +46,19 @@ inline simdjson_result<size_t> parser::read_file(const std::string &path) noexce
std::fclose(fp);
return IO_ERROR;
}
#if defined(SIMDJSON_VISUAL_STUDIO) && !SIMDJSON_IS_32BITS
__int64 len = _ftelli64(fp);
if(len == -1L) {
std::fclose(fp);
return IO_ERROR;
}
#else
long len = std::ftell(fp);
if((len < 0) || (len == LONG_MAX)) {
std::fclose(fp);
return IO_ERROR;
}
#endif
// Make sure we have enough capacity to load the file
if (_loaded_bytes_capacity < size_t(len)) {
@@ -84,26 +92,44 @@ inline simdjson_result<document_stream> parser::load_many(const std::string &pat
auto _error = read_file(path).get(len);
if (_error) { return _error; }
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
return document_stream(*this, (const uint8_t*)loaded_bytes.get(), len, batch_size);
return document_stream(*this, reinterpret_cast<const uint8_t*>(loaded_bytes.get()), len, batch_size);
}
inline simdjson_result<element> parser::parse(const uint8_t *buf, size_t len, bool realloc_if_needed) & noexcept {
error_code _error = ensure_capacity(len);
inline simdjson_result<element> parser::parse_into_document(document& provided_doc, const uint8_t *buf, size_t len, bool realloc_if_needed) & noexcept {
// Important: we need to ensure that document has enough capacity.
// Important: It is possible that provided_doc is actually the internal 'doc' within the parser!!!
error_code _error = ensure_capacity(provided_doc, len);
if (_error) { return _error; }
std::unique_ptr<uint8_t[]> tmp_buf;
if (realloc_if_needed) {
tmp_buf.reset((uint8_t *)internal::allocate_padded_buffer(len));
tmp_buf.reset(reinterpret_cast<uint8_t *>( internal::allocate_padded_buffer(len) ));
if (tmp_buf.get() == nullptr) { return MEMALLOC; }
std::memcpy((void *)tmp_buf.get(), buf, len);
std::memcpy(static_cast<void *>(tmp_buf.get()), buf, len);
}
_error = implementation->parse(realloc_if_needed ? tmp_buf.get() : buf, len, doc);
_error = implementation->parse(realloc_if_needed ? tmp_buf.get() : buf, len, provided_doc);
if (_error) { return _error; }
return doc.root();
return provided_doc.root();
}
simdjson_really_inline simdjson_result<element> parser::parse_into_document(document& provided_doc, const char *buf, size_t len, bool realloc_if_needed) & noexcept {
return parse_into_document(provided_doc, reinterpret_cast<const uint8_t *>(buf), len, realloc_if_needed);
}
simdjson_really_inline simdjson_result<element> parser::parse_into_document(document& provided_doc, const std::string &s) & noexcept {
return parse_into_document(provided_doc, s.data(), s.length(), s.capacity() - s.length() < SIMDJSON_PADDING);
}
simdjson_really_inline simdjson_result<element> parser::parse_into_document(document& provided_doc, const padded_string &s) & noexcept {
return parse_into_document(provided_doc, s.data(), s.length(), false);
}
inline simdjson_result<element> parser::parse(const uint8_t *buf, size_t len, bool realloc_if_needed) & noexcept {
return parse_into_document(doc, buf, len, realloc_if_needed);
}
simdjson_really_inline simdjson_result<element> parser::parse(const char *buf, size_t len, bool realloc_if_needed) & noexcept {
return parse((const uint8_t *)buf, len, realloc_if_needed);
return parse(reinterpret_cast<const uint8_t *>(buf), len, realloc_if_needed);
}
simdjson_really_inline simdjson_result<element> parser::parse(const std::string &s) & noexcept {
return parse(s.data(), s.length(), s.capacity() - s.length() < SIMDJSON_PADDING);
@@ -117,7 +143,7 @@ inline simdjson_result<document_stream> parser::parse_many(const uint8_t *buf, s
return document_stream(*this, buf, len, batch_size);
}
inline simdjson_result<document_stream> parser::parse_many(const char *buf, size_t len, size_t batch_size) noexcept {
return parse_many((const uint8_t *)buf, len, batch_size);
return parse_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size);
}
inline simdjson_result<document_stream> parser::parse_many(const std::string &s, size_t batch_size) noexcept {
return parse_many(s.data(), s.length(), batch_size);
@@ -139,26 +165,15 @@ simdjson_really_inline size_t parser::max_depth() const noexcept {
simdjson_warn_unused
inline error_code parser::allocate(size_t capacity, size_t max_depth) noexcept {
//
// Reallocate implementation and document if needed
// Reallocate implementation if needed
//
error_code err;
//
// It is possible that we change max_depth without touching capacity, in
// which case, we do not want to reallocate the document buffers.
//
bool need_doc_allocation{false};
if (implementation) {
need_doc_allocation = implementation->capacity() != capacity || !doc.tape;
err = implementation->allocate(capacity, max_depth);
} else {
need_doc_allocation = true;
err = simdjson::active_implementation->create_dom_parser_implementation(capacity, max_depth, implementation);
}
if (err) { return err; }
if (need_doc_allocation) {
err = doc.allocate(capacity);
if (err) { return err; }
}
return SUCCESS;
}
@@ -168,22 +183,40 @@ inline bool parser::allocate_capacity(size_t capacity, size_t max_depth) noexcep
return !allocate(capacity, max_depth);
}
#endif // SIMDJSON_DISABLE_DEPRECATED_API
inline error_code parser::ensure_capacity(size_t desired_capacity) noexcept {
return ensure_capacity(doc, desired_capacity);
}
inline error_code parser::ensure_capacity(document& target_document, size_t desired_capacity) noexcept {
// 1. It is wasteful to allocate a document and a parser for documents spanning less than MINIMAL_DOCUMENT_CAPACITY bytes.
// 2. If we allow desired_capacity = 0 then it is possible to exit this function with implementation == nullptr.
if(desired_capacity < MINIMAL_DOCUMENT_CAPACITY) { desired_capacity = MINIMAL_DOCUMENT_CAPACITY; }
// If we don't have enough capacity, (try to) automatically bump it.
// If the document was taken, reallocate that too.
// If the document needs allocation, do it too.
// Both in one if statement to minimize unlikely branching.
if (simdjson_unlikely(capacity() < desired_capacity || !doc.tape)) {
//
// Note: we must make sure that this function is called if capacity() == 0. We do so because we
// ensure that desired_capacity > 0.
if (simdjson_unlikely(capacity() < desired_capacity || target_document.capacity() < desired_capacity)) {
if (desired_capacity > max_capacity()) {
return error = CAPACITY;
}
return allocate(desired_capacity, max_depth());
error_code err1 = target_document.capacity() < desired_capacity ? target_document.allocate(desired_capacity) : SUCCESS;
error_code err2 = capacity() < desired_capacity ? allocate(desired_capacity, max_depth()) : SUCCESS;
if(err1 != SUCCESS) { return error = err1; }
if(err2 != SUCCESS) { return error = err2; }
}
return SUCCESS;
}
simdjson_really_inline void parser::set_max_capacity(size_t max_capacity) noexcept {
_max_capacity = max_capacity;
if(max_capacity < MINIMAL_DOCUMENT_CAPACITY) {
_max_capacity = max_capacity;
} else {
_max_capacity = MINIMAL_DOCUMENT_CAPACITY;
}
}
} // namespace dom
+80 -3
View File
@@ -31,6 +31,11 @@ static constexpr size_t DEFAULT_BATCH_SIZE = 1000000;
*/
static constexpr size_t MINIMAL_BATCH_SIZE = 32;
/**
* It is wasteful to allocate memory for tiny documents (e.g., 4 bytes).
*/
static constexpr size_t MINIMAL_DOCUMENT_CAPACITY = 32;
/**
* A persistent document parser.
*
@@ -109,6 +114,8 @@ public:
* @param path The path to load.
* @return The document, or an error:
* - IO_ERROR if there was an error opening or reading the file.
* Be mindful that on some 32-bit systems,
* the file size might be limited to 2 GB.
* - MEMALLOC if the parser does not have enough capacity and memory allocation fails.
* - CAPACITY if the parser does not have enough capacity and len > max_capacity.
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
@@ -120,7 +127,7 @@ public:
* Parse a JSON document and return a temporary reference to it.
*
* dom::parser parser;
* element doc = parser.parse(buf, len);
* element doc_root = parser.parse(buf, len);
*
* The function eagerly parses the input: the input can be modified and discarded after
* the `parser.parse(buf, len)` call has completed.
@@ -174,7 +181,7 @@ public:
* realloc_if_needed is true.
* @param len The length of the JSON.
* @param realloc_if_needed Whether to reallocate and enlarge the JSON buffer to add padding.
* @return The document, or an error:
* @return An element pointing at the root of the document, or an error:
* - MEMALLOC if realloc_if_needed is true or the parser does not have enough capacity,
* and memory allocation fails.
* - CAPACITY if the parser does not have enough capacity and len > max_capacity.
@@ -196,6 +203,65 @@ public:
/** @private We do not want to allow implicit conversion from C string to std::string. */
simdjson_really_inline simdjson_result<element> parse(const char *buf) noexcept = delete;
/**
* Parse a JSON document into a provide document instance and return a temporary reference to it.
* It is similar to the function `parse` except that instead of parsing into the internal
* `document` instance associated with the parser, it allows the user to provide a document
* instance.
*
* dom::parser parser;
* dom::document doc;
* element doc_root = parser.parse_into_document(doc, buf, len);
*
* The function eagerly parses the input: the input can be modified and discarded after
* the `parser.parse(buf, len)` call has completed.
*
* ### IMPORTANT: Document Lifetime
*
* After the call to parse_into_document, the parser is no longer needed.
*
* The JSON document lives in the document instance: you must keep the document
* instance alive while you navigate through it (i.e., used the returned value from
* parse_into_document). You are encourage to reuse the document instance
* many times with new data to avoid reallocations:
*
* dom::document doc;
* element doc_root1 = parser.parse_into_document(doc, buf1, len);
* //... doc_root1 is a pointer inside doc
* element doc_root2 = parser.parse_into_document(doc, buf1, len);
* //... doc_root2 is a pointer inside doc
* // at this point doc_root1 is no longer safe
*
* Moving the document instance is safe, but it invalidates the element instances. After
* moving a document, you can recover safe access to the document root with its `root()` method.
*
* @param doc The document instance where the parsed data will be stored (on success).
* @param buf The JSON to parse. Must have at least len + SIMDJSON_PADDING allocated bytes, unless
* realloc_if_needed is true.
* @param len The length of the JSON.
* @param realloc_if_needed Whether to reallocate and enlarge the JSON buffer to add padding.
* @return An element pointing at the root of document, or an error:
* - MEMALLOC if realloc_if_needed is true or the parser does not have enough capacity,
* and memory allocation fails.
* - CAPACITY if the parser does not have enough capacity and len > max_capacity.
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
*/
inline simdjson_result<element> parse_into_document(document& doc, const uint8_t *buf, size_t len, bool realloc_if_needed = true) & noexcept;
inline simdjson_result<element> parse_into_document(document& doc, const uint8_t *buf, size_t len, bool realloc_if_needed = true) && =delete;
/** @overload parse_into_document(const uint8_t *buf, size_t len, bool realloc_if_needed) */
simdjson_really_inline simdjson_result<element> parse_into_document(document& doc, const char *buf, size_t len, bool realloc_if_needed = true) & noexcept;
simdjson_really_inline simdjson_result<element> parse_into_document(document& doc, const char *buf, size_t len, bool realloc_if_needed = true) && =delete;
/** @overload parse_into_document(const uint8_t *buf, size_t len, bool realloc_if_needed) */
simdjson_really_inline simdjson_result<element> parse_into_document(document& doc, const std::string &s) & noexcept;
simdjson_really_inline simdjson_result<element> parse_into_document(document& doc, const std::string &s) && =delete;
/** @overload parse_into_document(const uint8_t *buf, size_t len, bool realloc_if_needed) */
simdjson_really_inline simdjson_result<element> parse_into_document(document& doc, const padded_string &s) & noexcept;
simdjson_really_inline simdjson_result<element> parse_into_document(document& doc, const padded_string &s) && =delete;
/** @private We do not want to allow implicit conversion from C string to std::string. */
simdjson_really_inline simdjson_result<element> parse_into_document(document& doc, const char *buf) noexcept = delete;
/**
* Load a file containing many JSON documents.
*
@@ -430,6 +496,10 @@ public:
* The parser may reallocate internal buffers as needed up to this amount as documents are passed
* to it.
*
* Note: To avoid limiting the memory to an absurd value, such as zero or two bytes,
* iff you try to set max_capacity to a value lower than MINIMAL_DOCUMENT_CAPACITY,
* then the maximal capacity is set to MINIMAL_DOCUMENT_CAPACITY.
*
* This call will not allocate or deallocate, even if capacity is currently above max_capacity.
*
* @param max_capacity The new maximum capacity, in bytes.
@@ -515,9 +585,16 @@ private:
/**
* Ensure we have enough capacity to handle at least desired_capacity bytes,
* and auto-allocate if not.
* and auto-allocate if not. This also allocates memory if needed in the
* internal document.
*/
inline error_code ensure_capacity(size_t desired_capacity) noexcept;
/**
* Ensure we have enough capacity to handle at least desired_capacity bytes,
* and auto-allocate if not. This also allocates memory if needed in the
* provided document.
*/
inline error_code ensure_capacity(document& doc, size_t desired_capacity) noexcept;
/** Read the file into loaded_bytes */
inline simdjson_result<size_t> read_file(const std::string &path) noexcept;
+1 -4
View File
@@ -45,9 +45,6 @@ namespace internal {
template<typename T>
simdjson_really_inline void simdjson_result_base<T>::tie(T &value, error_code &error) && noexcept {
// on the clang compiler that comes with current macOS (Apple clang version 11.0.0),
// tie(width, error) = size["w"].get<uint64_t>();
// fails with "error: no viable overloaded '='""
error = this->second;
if (!error) {
value = std::forward<simdjson_result_base<T>>(*this).first;
@@ -99,7 +96,7 @@ simdjson_really_inline const T& simdjson_result_base<T>::value_unsafe() const& n
template<typename T>
simdjson_really_inline T&& simdjson_result_base<T>::value_unsafe() && noexcept {
return std::forward<T>(this->first);;
return std::forward<T>(this->first);
}
template<typename T>
+16 -1
View File
@@ -35,7 +35,8 @@ enum error_code {
INVALID_URI_FRAGMENT, ///< Invalid URI fragment
UNEXPECTED_ERROR, ///< indicative of a bug in simdjson
PARSER_IN_USE, ///< parser is already in use.
/** @private Number of error codes */
OUT_OF_ORDER_ITERATION, ///< tried to iterate an array or object out of order
INSUFFICIENT_PADDING, ///< The JSON doesn't have enough padding for simdjson to safely parse it.
NUM_ERROR_CODES
};
@@ -134,6 +135,13 @@ struct simdjson_result_base : protected std::pair<T, error_code> {
*/
simdjson_really_inline error_code get(T &value) && noexcept;
/**
* Move the value to the provided variable.
*
* @param value The variable to assign the value to. May not be set if there is an error.
*/
simdjson_really_inline const T &value(error_code &error) const & noexcept;
/**
* The error.
*/
@@ -275,6 +283,13 @@ struct simdjson_result : public internal::simdjson_result_base<T> {
}; // struct simdjson_result
#if SIMDJSON_EXCEPTIONS
template<typename T>
inline std::ostream& operator<<(std::ostream& out, simdjson_result<T> value) noexcept { return out << value.value(); }
#endif // SIMDJSON_EXCEPTIONS
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
/**
* @deprecated This is an alias and will be removed, use error_code instead
+1 -14
View File
@@ -1,13 +1,7 @@
#ifndef SIMDJSON_FALLBACK_H
#define SIMDJSON_FALLBACK_H
#include "simdjson/portability.h"
// Default Fallback to on unless a builtin implementation has already been selected.
#ifndef SIMDJSON_IMPLEMENTATION_FALLBACK
#define SIMDJSON_IMPLEMENTATION_FALLBACK 1 // (!SIMDJSON_CAN_ALWAYS_RUN_ARM64 && !SIMDJSON_CAN_ALWAYS_RUN_HASWELL && !SIMDJSON_CAN_ALWAYS_RUN_WESTMERE && !SIMDJSON_CAN_ALWAYS_RUN_PPC64)
#endif
#define SIMDJSON_CAN_ALWAYS_RUN_FALLBACK SIMDJSON_IMPLEMENTATION_FALLBACK
#include "simdjson/implementation-base.h"
#if SIMDJSON_IMPLEMENTATION_FALLBACK
@@ -30,13 +24,6 @@ namespace fallback {
#include "simdjson/generic/atomparsing.h"
#include "simdjson/fallback/stringparsing.h"
#include "simdjson/fallback/numberparsing.h"
#include "simdjson/generic/implementation_simdjson_result_base.h"
#include "simdjson/generic/ondemand.h"
// Inline definitions
#include "simdjson/generic/implementation_simdjson_result_base-inl.h"
#include "simdjson/generic/ondemand-inl.h"
#include "simdjson/fallback/end.h"
#endif // SIMDJSON_IMPLEMENTATION_FALLBACK
+1 -1
View File
@@ -1,7 +1,7 @@
#ifndef SIMDJSON_FALLBACK_BITMANIPULATION_H
#define SIMDJSON_FALLBACK_BITMANIPULATION_H
#include "simdjson.h"
#include "simdjson/base.h"
#include <limits>
namespace simdjson {
+1 -1
View File
@@ -20,7 +20,7 @@ static simdjson_really_inline uint32_t parse_eight_digits_unrolled(const char *c
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
}
static simdjson_really_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
return parse_eight_digits_unrolled((const char *)chars);
return parse_eight_digits_unrolled(reinterpret_cast<const char *>(chars));
}
} // unnamed namespace
+1 -1
View File
@@ -1,7 +1,7 @@
#ifndef SIMDJSON_FALLBACK_STRINGPARSING_H
#define SIMDJSON_FALLBACK_STRINGPARSING_H
#include "simdjson.h"
#include "simdjson/base.h"
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
@@ -1,4 +1,4 @@
#include "simdjson.h"
#include "simdjson/base.h"
#include "simdjson/internal/isadetection.h"
namespace simdjson {
@@ -33,7 +33,6 @@ public:
simdjson_warn_unused error_code parse(const uint8_t *buf, size_t len, dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, bool partial) noexcept final;
simdjson_warn_unused error_code check_for_unclosed_array() noexcept;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
@@ -55,6 +54,7 @@ inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parse
// Leaving these here so they can be inlined if so desired
inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept {
if(capacity > SIMDJSON_MAXSIZE_BYTES) { return CAPACITY; }
// Stage 1 index output
size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7;
structural_indexes.reset( new (std::nothrow) uint32_t[max_structures] );
@@ -7,9 +7,6 @@ namespace SIMDJSON_IMPLEMENTATION {
template<typename T>
simdjson_really_inline void implementation_simdjson_result_base<T>::tie(T &value, error_code &error) && noexcept {
// on the clang compiler that comes with current macOS (Apple clang version 11.0.0),
// tie(width, error) = size["w"].get<uint64_t>();
// fails with "error: no viable overloaded '='""
error = this->second;
if (!error) {
value = std::forward<implementation_simdjson_result_base<T>>(*this).first;
@@ -52,6 +49,16 @@ simdjson_really_inline implementation_simdjson_result_base<T>::operator T&&() &&
return std::forward<implementation_simdjson_result_base<T>>(*this).take_value();
}
template<typename T>
simdjson_really_inline const T& implementation_simdjson_result_base<T>::value_unsafe() const& noexcept {
return this->first;
}
template<typename T>
simdjson_really_inline T&& implementation_simdjson_result_base<T>::value_unsafe() && noexcept {
return std::forward<T>(this->first);
}
#endif // SIMDJSON_EXCEPTIONS
template<typename T>
@@ -97,6 +97,18 @@ struct implementation_simdjson_result_base {
*/
simdjson_really_inline operator T&&() && noexcept(false);
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evoluates to false.
*/
simdjson_really_inline const T& value_unsafe() const& noexcept;
/**
* Take the result value (move it). This function is safe if and only
* the error() method returns a value that evoluates to false.
*/
simdjson_really_inline T&& value_unsafe() && noexcept;
#endif // SIMDJSON_EXCEPTIONS
T first{};
+1 -1
View File
@@ -103,7 +103,7 @@ simdjson_really_inline value128 full_multiplication(uint64_t value1, uint64_t va
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
#endif // _M_ARM64
#else // defined(SIMDJSON_REGULAR_VISUAL_STUDIO) || defined(SIMDJSON_IS_32BITS)
__uint128_t r = ((__uint128_t)value1) * value2;
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#endif
+115 -29
View File
@@ -29,7 +29,7 @@ simdjson_really_inline double to_double(uint64_t mantissa, uint64_t real_exponen
double d;
mantissa &= ~(1ULL << 52);
mantissa |= real_exponent << 52;
mantissa |= (((uint64_t)negative) << 63);
mantissa |= ((static_cast<uint64_t>(negative)) << 63);
std::memcpy(&d, &mantissa, sizeof(d));
return d;
}
@@ -292,7 +292,7 @@ simdjson_really_inline bool compute_float_64(int64_t power, uint64_t i, bool neg
// The string parsing itself always succeeds. We know that there is at least
// one digit.
static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
*outDouble = simdjson::internal::from_chars((const char *)ptr);
*outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr));
// We do not accept infinite values.
// Detecting finite values in a portable manner is ridiculously hard, ideally
@@ -423,7 +423,7 @@ simdjson_really_inline error_code parse_exponent(simdjson_unused const uint8_t *
return SUCCESS;
}
simdjson_really_inline int significant_digits(const uint8_t * start_digits, int digit_count) {
simdjson_really_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.
const uint8_t *start = start_digits;
@@ -431,11 +431,11 @@ simdjson_really_inline int significant_digits(const uint8_t * start_digits, int
start++;
}
// we over-decrement by one when there is a '.'
return digit_count - int(start - start_digits);
return digit_count - size_t(start - start_digits);
}
template<typename W>
simdjson_really_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, int digit_count, int64_t exponent, W &writer) {
simdjson_really_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) {
// If we frequently had to deal with long strings of digits,
// we could extend our code by using a 128-bit integer instead
// of a 64-bit integer. However, this is uncommon in practice.
@@ -529,7 +529,8 @@ simdjson_really_inline error_code parse_number(const uint8_t *const src, W &writ
while (parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
int digit_count = int(p - start_digits);
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) { return INVALID_NUMBER(src); }
//
@@ -549,23 +550,23 @@ simdjson_really_inline error_code parse_number(const uint8_t *const src, W &writ
SIMDJSON_TRY( parse_exponent(src, p, exponent) );
}
if (is_float) {
const bool clean_end = jsoncharutils::is_structural_or_whitespace(*p);
const bool dirty_end = jsoncharutils::is_not_structural_or_whitespace(*p);
SIMDJSON_TRY( write_float(src, negative, i, start_digits, digit_count, exponent, writer) );
if (!clean_end) { return INVALID_NUMBER(src); }
if (dirty_end) { return INVALID_NUMBER(src); }
return SUCCESS;
}
// The longest negative 64-bit number is 19 digits.
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
int longest_digit_count = negative ? 19 : 20;
size_t longest_digit_count = negative ? 19 : 20;
if (digit_count > longest_digit_count) { return INVALID_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); }
WRITE_INTEGER(~i+1, src, writer);
if (!jsoncharutils::is_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
@@ -588,16 +589,81 @@ simdjson_really_inline error_code parse_number(const uint8_t *const src, W &writ
} else {
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
}
if (!jsoncharutils::is_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
}
// SAX functions
// Inlineable functions
namespace {
// This table can be used to characterize the final character of an integer
// string. For JSON structural character and allowable white space characters,
// we return SUCCESS. For 'e', '.' and 'E', we return INCORRECT_TYPE. Otherwise
// we return NUMBER_ERROR.
// Optimization note: we could easily reduce the size of the table by half (to 128)
// at the cost of an extra branch.
// Optimization note: we want the values to use at most 8 bits (not, e.g., 32 bits):
static_assert(error_code(uint8_t(NUMBER_ERROR))== NUMBER_ERROR, "bad NUMBER_ERROR cast");
static_assert(error_code(uint8_t(SUCCESS))== SUCCESS, "bad NUMBER_ERROR cast");
static_assert(error_code(uint8_t(INCORRECT_TYPE))== INCORRECT_TYPE, "bad NUMBER_ERROR cast");
const uint8_t integer_string_finisher[256] = {
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS,
SUCCESS, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS,
NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, INCORRECT_TYPE,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, SUCCESS, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
SUCCESS, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR};
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_really_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src) noexcept {
const uint8_t *p = src;
//
// Parse the integer part.
//
@@ -607,13 +673,23 @@ simdjson_unused simdjson_really_inline simdjson_result<uint64_t> parse_unsigned(
while (parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
int digit_count = int(p - start_digits);
if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) { return NUMBER_ERROR; }
if (!jsoncharutils::is_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
if (digit_count > 20) { return NUMBER_ERROR; }
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if (integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
@@ -627,7 +703,7 @@ simdjson_unused simdjson_really_inline simdjson_result<uint64_t> parse_unsigned(
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to 9,223,372,036,854,775,808 (INT64_MAX).
//
if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return NUMBER_ERROR; }
if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
@@ -650,19 +726,28 @@ simdjson_unused simdjson_really_inline simdjson_result<int64_t> parse_integer(co
while (parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
int digit_count = int(p - start_digits);
if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) { return NUMBER_ERROR; }
if (!jsoncharutils::is_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest negative 64-bit number is 19 digits.
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
int longest_digit_count = negative ? 19 : 20;
if (digit_count > longest_digit_count) { return NUMBER_ERROR; }
size_t longest_digit_count = negative ? 19 : 20;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if(integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
if (digit_count == longest_digit_count) {
if(negative) {
if (negative) {
// Anything negative above INT64_MAX+1 is invalid
if (i > uint64_t(INT64_MAX)+1) { return NUMBER_ERROR; }
if (i > uint64_t(INT64_MAX)+1) { return INCORRECT_TYPE; }
return ~i+1;
// Positive overflow check:
@@ -677,7 +762,7 @@ simdjson_unused simdjson_really_inline simdjson_result<int64_t> parse_integer(co
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to 9,223,372,036,854,775,808 (INT64_MAX).
//
} else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return NUMBER_ERROR; }
} else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return negative ? (~i+1) : i;
@@ -699,7 +784,8 @@ simdjson_unused simdjson_really_inline simdjson_result<double> parse_double(cons
bool leading_zero = (i == 0);
while (parse_digit(*p, i)) { p++; }
// no integer digits, or 0123 (zero must be solo)
if ( p == src || (leading_zero && p != src+1)) { return NUMBER_ERROR; }
if ( p == src ) { return INCORRECT_TYPE; }
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
+1
View File
@@ -1,3 +1,4 @@
#include "simdjson/generic/ondemand/json_type-inl.h"
#include "simdjson/generic/ondemand/logger-inl.h"
#include "simdjson/generic/ondemand/raw_json_string-inl.h"
#include "simdjson/generic/ondemand/token_iterator-inl.h"
+1
View File
@@ -14,6 +14,7 @@ using depth_t = int32_t;
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
#include "simdjson/generic/ondemand/json_type.h"
#include "simdjson/generic/ondemand/token_position.h"
#include "simdjson/generic/ondemand/logger.h"
#include "simdjson/generic/ondemand/raw_json_string.h"
+11 -6
View File
@@ -46,13 +46,15 @@ simdjson_really_inline array::array(const value_iterator &_iter) noexcept
}
simdjson_really_inline simdjson_result<array> array::start(value_iterator &iter) noexcept {
// We don't need to know if the array is empty to start iteration, but we do want to know if there
// is an error--thus `simdjson_unused`.
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_array().get(has_value) );
return array(iter);
}
simdjson_really_inline simdjson_result<array> array::try_start(value_iterator &iter) noexcept {
simdjson_really_inline simdjson_result<array> array::start_root(value_iterator &iter) noexcept {
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.try_start_array().get(has_value) );
SIMDJSON_TRY( iter.start_root_array().get(has_value) );
return array(iter);
}
simdjson_really_inline array array::started(value_iterator &iter) noexcept {
@@ -60,11 +62,14 @@ simdjson_really_inline array array::started(value_iterator &iter) noexcept {
return array(iter);
}
simdjson_really_inline array_iterator array::begin() noexcept {
return iter;
simdjson_really_inline simdjson_result<array_iterator> array::begin() noexcept {
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; }
#endif
return array_iterator(iter);
}
simdjson_really_inline array_iterator array::end() noexcept {
return {};
simdjson_really_inline simdjson_result<array_iterator> array::end() noexcept {
return array_iterator(iter);
}
} // namespace ondemand
+6 -4
View File
@@ -24,13 +24,13 @@ public:
*
* Part of the std::iterable interface.
*/
simdjson_really_inline array_iterator begin() noexcept;
simdjson_really_inline simdjson_result<array_iterator> begin() noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_really_inline array_iterator end() noexcept;
simdjson_really_inline simdjson_result<array_iterator> end() noexcept;
protected:
/**
@@ -42,13 +42,14 @@ protected:
*/
static simdjson_really_inline simdjson_result<array> start(value_iterator &iter) noexcept;
/**
* Begin array iteration.
* Begin array iteration from the root.
*
* @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the
* resulting array.
* @error INCORRECT_TYPE if the iterator is not at [.
* @error TAPE_ERROR if there is no closing ] at the end of the document.
*/
static simdjson_really_inline simdjson_result<array> try_start(value_iterator &iter) noexcept;
static simdjson_really_inline simdjson_result<array> start_root(value_iterator &iter) noexcept;
/**
* Begin array iteration.
*
@@ -76,6 +77,7 @@ protected:
value_iterator iter{};
friend class value;
friend class document;
friend struct simdjson_result<value>;
friend struct simdjson_result<array>;
friend class array_iterator;
@@ -37,6 +37,7 @@ simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_
) noexcept
: SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator>(std::forward<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator>::simdjson_result(error_code error) noexcept
: SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator>({}, error)
@@ -44,20 +45,21 @@ simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator>::operator*() noexcept {
if (this->error()) { this->second = SUCCESS; return this->error(); }
return *this->first;
if (error()) { return error(); }
return *first;
}
simdjson_really_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator>::operator==(const simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> &other) const noexcept {
if (this->error()) { return true; }
return this->first == other.first;
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
simdjson_really_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator>::operator!=(const simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> &other) const noexcept {
if (this->error()) { return false; }
return this->first != other.first;
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> &simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator>::operator++() noexcept {
if (this->error()) { return *this; }
++(this->first);
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++(first);
return *this;
}
@@ -21,15 +21,13 @@ simdjson_really_inline value_iterator document::get_root_value_iterator() noexce
simdjson_really_inline value document::resume_value() noexcept {
return resume_value_iterator();
}
simdjson_really_inline value document::get_root_value() noexcept {
return get_root_value_iterator();
}
simdjson_really_inline simdjson_result<array> document::get_array() & noexcept {
return get_root_value().get_array();
auto value = get_root_value_iterator();
return array::start_root(value);
}
simdjson_really_inline simdjson_result<object> document::get_object() & noexcept {
return get_root_value().get_object();
auto value = get_root_value_iterator();
return object::start_root(value);
}
simdjson_really_inline simdjson_result<uint64_t> document::get_uint64() noexcept {
return get_root_value_iterator().get_root_uint64();
@@ -113,6 +111,15 @@ simdjson_really_inline simdjson_result<value> document::operator[](const char *k
return resume_value()[key];
}
simdjson_really_inline simdjson_result<json_type> document::type() noexcept {
return get_root_value_iterator().type();
}
simdjson_really_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());
}
} // namespace ondemand
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
@@ -237,6 +244,11 @@ template<> simdjson_really_inline error_code simdjson_result<SIMDJSON_IMPLEMENTA
return SUCCESS;
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_type> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
#if SIMDJSON_EXCEPTIONS
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
@@ -272,4 +284,9 @@ simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::docume
}
#endif
simdjson_really_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
} // namespace simdjson
+59 -7
View File
@@ -71,8 +71,6 @@ public:
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
@@ -106,12 +104,23 @@ public:
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool
*
* You may use get_double(), get_bool(), get_uint64(), get_int64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template<typename T> simdjson_really_inline simdjson_result<T> get() & noexcept;
template<typename T> simdjson_really_inline simdjson_result<T> get() & noexcept {
// Unless the simdjson library 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.");
}
/** @overload template<typename T> simdjson_result<T> get() & noexcept */
template<typename T> simdjson_really_inline simdjson_result<T> get() && noexcept;
template<typename T> simdjson_really_inline simdjson_result<T> get() && noexcept {
// Unless the simdjson library 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.");
}
/**
* Get this value as the given type.
@@ -167,8 +176,6 @@ public:
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
@@ -256,6 +263,47 @@ public:
/** @overload simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<value> operator[](const char *key) & noexcept;
/**
* Get the type of this JSON value.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_really_inline simdjson_result<json_type> type() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. If this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view may be the padded buffer.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*/
simdjson_really_inline simdjson_result<std::string_view> raw_json_token() noexcept;
/**
* Get the root value.
*/
simdjson_really_inline value get_root_value() noexcept;
simdjson_really_inline operator value() noexcept;
protected:
simdjson_really_inline document(ondemand::json_iterator &&iter) noexcept;
simdjson_really_inline const uint8_t *text(uint32_t idx) const noexcept;
@@ -263,7 +311,6 @@ protected:
simdjson_really_inline value_iterator resume_value_iterator() noexcept;
simdjson_really_inline value_iterator get_root_value_iterator() noexcept;
simdjson_really_inline value resume_value() noexcept;
simdjson_really_inline value get_root_value() noexcept;
static simdjson_really_inline document start(ondemand::json_iterator &&iter) noexcept;
//
@@ -330,6 +377,11 @@ public:
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](const char *key) & noexcept;
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_type> type() noexcept;
/** @copydoc simdjson_really_inline std::string_view document::raw_json_token() const noexcept */
simdjson_really_inline simdjson_result<std::string_view> raw_json_token() noexcept;
};
} // namespace simdjson
+2 -2
View File
@@ -30,8 +30,8 @@ public:
*/
simdjson_really_inline simdjson_warn_unused simdjson_result<std::string_view> unescaped_key() noexcept;
/**
* Get the key as a raw_json_string: this is fast and allows straight comparisons.
* We want this to be the default for most users.
* Get the key as a raw_json_string. Can be used for direct comparison with
* an unescaped C string: e.g., key() == "test".
*/
simdjson_really_inline raw_json_string key() const noexcept;
/**
@@ -20,12 +20,11 @@ simdjson_really_inline json_iterator &json_iterator::operator=(json_iterator &&o
}
simdjson_really_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser) noexcept
: token(buf, _parser->dom_parser->structural_indexes.get()),
: token(buf, _parser->implementation->structural_indexes.get()),
parser{_parser},
_string_buf_loc{parser->string_buf.get()},
_depth{1}
{
// Release the string buf so it can be reused by the next document
logger::log_headers();
}
@@ -69,7 +68,7 @@ simdjson_warn_unused simdjson_really_inline error_code json_iterator::skip_child
}
// Now that we've considered the first value, we only increment/decrement for arrays/objects
auto end = &parser->dom_parser->structural_indexes[parser->dom_parser->n_structural_indexes];
auto end = &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes];
while (token.index <= end) {
switch (*advance()) {
case '[': case '{':
@@ -102,19 +101,19 @@ simdjson_really_inline bool json_iterator::at_root() const noexcept {
}
simdjson_really_inline token_position json_iterator::root_checkpoint() const noexcept {
return parser->dom_parser->structural_indexes.get();
return parser->implementation->structural_indexes.get();
}
simdjson_really_inline void json_iterator::assert_at_root() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
// Visual Studio Clang treats unique_ptr.get() as "side effecting."
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME( token.index == parser->dom_parser->structural_indexes.get() );
SIMDJSON_ASSUME( token.index == parser->implementation->structural_indexes.get() );
#endif
}
simdjson_really_inline bool json_iterator::at_eof() const noexcept {
return token.index == &parser->dom_parser->structural_indexes[parser->dom_parser->n_structural_indexes];
return token.index == &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes];
}
simdjson_really_inline bool json_iterator::is_alive() const noexcept {
@@ -146,6 +145,18 @@ simdjson_really_inline uint32_t json_iterator::peek_length(token_position positi
return token.peek_length(position);
}
simdjson_really_inline token_position json_iterator::last_document_position() const noexcept {
// The following line fails under some compilers...
// SIMDJSON_ASSUME(parser->implementation->n_structural_indexes > 0);
// since it has side-effects.
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
SIMDJSON_ASSUME(n_structural_indexes > 0);
return &parser->implementation->structural_indexes[n_structural_indexes - 1];
}
simdjson_really_inline const uint8_t *json_iterator::peek_last() const noexcept {
return token.peek(last_document_position());
}
simdjson_really_inline void json_iterator::ascend_to(depth_t parent_depth) noexcept {
SIMDJSON_ASSUME(parent_depth >= 0 && parent_depth < INT32_MAX - 1);
SIMDJSON_ASSUME(_depth == parent_depth + 1);
@@ -176,10 +187,28 @@ simdjson_really_inline error_code json_iterator::report_error(error_code _error,
simdjson_really_inline token_position json_iterator::position() const noexcept {
return token.position();
}
simdjson_really_inline void json_iterator::set_position(token_position target_checkpoint) noexcept {
token.set_position(target_checkpoint);
simdjson_really_inline void json_iterator::reenter_child(token_position position, depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME(position >= parser->start_positions[child_depth]);
#endif
#endif
token.set_position(position);
_depth = child_depth;
}
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
simdjson_really_inline token_position json_iterator::start_position(depth_t depth) const noexcept {
return parser->start_positions[depth];
}
simdjson_really_inline void json_iterator::set_start_position(depth_t depth, token_position position) noexcept {
parser->start_positions[depth] = position;
}
#endif
simdjson_really_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD);
@@ -130,6 +130,15 @@ public:
* @param index The position of the token to retrieve.
*/
simdjson_really_inline uint32_t peek_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 ...
*/
simdjson_really_inline const uint8_t *peek_last() const noexcept;
/**
* Ascend one level.
@@ -148,6 +157,7 @@ public:
* @param child_depth the expected child depth.
*/
simdjson_really_inline void descend_to(depth_t parent_depth) noexcept;
simdjson_really_inline void descend_to(depth_t parent_depth, int32_t delta) noexcept;
/**
* Get current depth.
@@ -179,10 +189,15 @@ public:
template<int N> simdjson_warn_unused simdjson_really_inline bool advance_to_buffer(uint8_t (&tmpbuf)[N]) noexcept;
simdjson_really_inline token_position position() const noexcept;
simdjson_really_inline void set_position(token_position target_checkpoint) noexcept;
simdjson_really_inline void reenter_child(token_position position, depth_t child_depth) noexcept;
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
simdjson_really_inline token_position start_position(depth_t depth) const noexcept;
simdjson_really_inline void set_start_position(depth_t depth, token_position position) noexcept;
#endif
protected:
simdjson_really_inline json_iterator(const uint8_t *buf, ondemand::parser *parser) noexcept;
simdjson_really_inline token_position last_document_position() const noexcept;
friend class document;
friend class object;
@@ -0,0 +1,35 @@
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace ondemand {
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept {
switch (type) {
case json_type::array: out << "array"; break;
case json_type::object: out << "object"; break;
case json_type::number: out << "number"; break;
case json_type::string: out << "string"; break;
case json_type::boolean: out << "boolean"; break;
case json_type::null: out << "null"; break;
default: SIMDJSON_UNREACHABLE();
}
return out;
}
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false) {
return out << type.value();
}
#endif
} // namespace ondemand
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_type>::simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::json_type &&value) noexcept
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::json_type>(std::forward<SIMDJSON_IMPLEMENTATION::ondemand::json_type>(value)) {}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_type>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::json_type>(error) {}
} // namespace simdjson
@@ -0,0 +1,54 @@
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace ondemand {
/**
* The type of a JSON value.
*/
enum class json_type {
// Start at 1 to catch uninitialized / default values more easily
array=1, ///< A JSON array ( [ 1, 2, 3 ... ] )
object, ///< A JSON object ( { "a": 1, "b" 2, ... } )
number, ///< A JSON number ( 1 or -2.3 or 4.5e6 ...)
string, ///< A JSON string ( "a" or "hello world\n" ...)
boolean, ///< A JSON boolean (true or false)
null ///< A JSON null (null)
};
/**
* Write the JSON type to the output stream
*
* @param out The output stream.
* @param type The json_type.
*/
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Send JSON type to an output stream.
*
* @param out The output stream.
* @param type The json_type.
* @throw simdjson_error if the result being printed has an error. If there is an error with the
* underlying output stream, that error will be propagated (simdjson_error will not be
* thrown).
*/
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false);
#endif
} // namespace ondemand
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_type> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::json_type> {
public:
simdjson_really_inline simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::json_type &&value) noexcept; ///< @private
simdjson_really_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_really_inline simdjson_result() noexcept = default;
simdjson_really_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson
+18 -6
View File
@@ -21,20 +21,28 @@ static simdjson_really_inline char printable_char(char c) {
simdjson_really_inline void log_event(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta);
}
simdjson_really_inline void log_value(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta);
}
simdjson_really_inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "", type, detail);
}
simdjson_really_inline void log_value(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta);
}
simdjson_really_inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "+", type, detail);
if (LOG_ENABLED) { log_depth++; }
}
simdjson_really_inline void log_start_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_line(iter, "+", type, "", delta, depth_delta);
log_depth++;
if (LOG_ENABLED) { log_depth++; }
}
simdjson_really_inline void log_end_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_depth--;
if (LOG_ENABLED) { log_depth--; }
log_line(iter, "-", type, "", delta, depth_delta);
}
simdjson_really_inline void log_error(const json_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_line(iter, "ERROR: ", error, detail, delta, depth_delta);
}
@@ -45,22 +53,26 @@ simdjson_really_inline void log_error(const json_iterator &iter, token_position
simdjson_really_inline void log_event(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_event(iter.json_iter(), type, detail, delta, depth_delta);
}
simdjson_really_inline void log_value(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_value(iter.json_iter(), type, detail, delta, depth_delta);
}
simdjson_really_inline void log_start_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_start_value(iter.json_iter(), type, delta, depth_delta);
}
simdjson_really_inline void log_end_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_end_value(iter.json_iter(), type, delta, depth_delta);
}
simdjson_really_inline void log_error(const value_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_error(iter.json_iter(), error, detail, delta, depth_delta);
}
simdjson_really_inline void log_headers() noexcept {
log_depth = 0;
if (LOG_ENABLED) {
log_depth = 0;
printf("\n");
printf("| %-*s ", LOG_EVENT_LEN, "Event");
printf("| %-*s ", LOG_BUFFER_LEN, "Buffer");
+4 -3
View File
@@ -14,15 +14,16 @@ namespace logger {
#endif
static simdjson_really_inline void log_headers() noexcept;
static simdjson_really_inline void log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta) noexcept;
static simdjson_really_inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail) noexcept;
static simdjson_really_inline void log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta) noexcept;
static simdjson_really_inline void log_event(const json_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static simdjson_really_inline void log_value(const json_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static simdjson_really_inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static simdjson_really_inline void log_value(const json_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static simdjson_really_inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static simdjson_really_inline void log_start_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static simdjson_really_inline void log_end_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static simdjson_really_inline void log_error(const json_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
static simdjson_really_inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail="") noexcept;
static simdjson_really_inline void log_error(const json_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
static simdjson_really_inline void log_event(const value_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static simdjson_really_inline void log_value(const value_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
+11 -8
View File
@@ -34,13 +34,15 @@ simdjson_really_inline simdjson_result<value> object::find_field(const std::stri
}
simdjson_really_inline simdjson_result<object> object::start(value_iterator &iter) noexcept {
// We don't need to know if the object is empty to start iteration, but we do want to know if there
// is an error--thus `simdjson_unused`.
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_object().get(has_value) );
return object(iter);
}
simdjson_really_inline simdjson_result<object> object::try_start(value_iterator &iter) noexcept {
simdjson_really_inline simdjson_result<object> object::start_root(value_iterator &iter) noexcept {
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.try_start_object().get(has_value) );
SIMDJSON_TRY( iter.start_root_object().get(has_value) );
return object(iter);
}
simdjson_really_inline object object::started(value_iterator &iter) noexcept {
@@ -56,13 +58,14 @@ simdjson_really_inline object::object(const value_iterator &_iter) noexcept
{
}
simdjson_really_inline object_iterator object::begin() noexcept {
// Expanded version of SIMDJSON_ASSUME( iter.at_field_start() || !iter.is_open() )
SIMDJSON_ASSUME( (iter._json_iter->token.index == iter._start_position + 1) || (iter._json_iter->_depth < iter._depth) );
return iter;
simdjson_really_inline simdjson_result<object_iterator> object::begin() noexcept {
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; }
#endif
return object_iterator(iter);
}
simdjson_really_inline object_iterator object::end() noexcept {
return {};
simdjson_really_inline simdjson_result<object_iterator> object::end() noexcept {
return object_iterator(iter);
}
} // namespace ondemand
+8 -3
View File
@@ -16,8 +16,8 @@ public:
*/
simdjson_really_inline object() noexcept = default;
simdjson_really_inline object_iterator begin() noexcept;
simdjson_really_inline object_iterator end() noexcept;
simdjson_really_inline simdjson_result<object_iterator> begin() noexcept;
simdjson_really_inline simdjson_result<object_iterator> end() noexcept;
/**
* Look up a field by name on an object (order-sensitive).
@@ -32,6 +32,8 @@ public:
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
@@ -59,6 +61,9 @@ public:
* 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).
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* @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.
*/
@@ -72,7 +77,7 @@ public:
protected:
static simdjson_really_inline simdjson_result<object> start(value_iterator &iter) noexcept;
static simdjson_really_inline simdjson_result<object> try_start(value_iterator &iter) noexcept;
static simdjson_really_inline simdjson_result<object> start_root(value_iterator &iter) noexcept;
static simdjson_really_inline object started(value_iterator &iter) noexcept;
static simdjson_really_inline object resume(const value_iterator &iter) noexcept;
simdjson_really_inline object(const value_iterator &iter) noexcept;
@@ -25,6 +25,7 @@ simdjson_really_inline bool object_iterator::operator==(const object_iterator &o
simdjson_really_inline bool object_iterator::operator!=(const object_iterator &) const noexcept {
return iter.is_open();
}
simdjson_really_inline object_iterator &object_iterator::operator++() noexcept {
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
@@ -90,6 +91,7 @@ simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object
) noexcept
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator>(std::forward<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator>({}, error)
@@ -97,22 +99,23 @@ simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::field> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator>::operator*() noexcept {
if (error()) { second = SUCCESS; return error(); }
if (error()) { return error(); }
return *first;
}
// Assumes it's being compared with the end. true if depth < iter->depth.
// If we're iterating and there is an error, return the error once.
simdjson_really_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator>::operator==(const simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator> &other) const noexcept {
if (error()) { return true; }
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
// Assumes it's being compared with the end. true if depth >= iter->depth.
// If we're iterating and there is an error, return the error once.
simdjson_really_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator>::operator!=(const simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator> &other) const noexcept {
if (error()) { return false; }
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
// Checks for ']' and ','
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator> &simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator>::operator++() noexcept {
if (error()) { return *this; }
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++first;
return *this;
}
+55 -18
View File
@@ -3,53 +3,90 @@ namespace SIMDJSON_IMPLEMENTATION {
namespace ondemand {
simdjson_warn_unused simdjson_really_inline error_code parser::allocate(size_t new_capacity, size_t new_max_depth) noexcept {
if (string_buf && new_capacity == _capacity && new_max_depth == _max_depth) { return SUCCESS; }
if (string_buf && new_capacity == capacity() && new_max_depth == max_depth()) { return SUCCESS; }
// string_capacity copied from document::allocate
_capacity = 0;
_max_depth = 0;
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * new_capacity / 3 + SIMDJSON_PADDING, 64);
string_buf.reset(new (std::nothrow) uint8_t[string_capacity]);
if (dom_parser) {
SIMDJSON_TRY( dom_parser->set_capacity(new_capacity) );
SIMDJSON_TRY( dom_parser->set_max_depth(new_max_depth) );
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
start_positions.reset(new (std::nothrow) token_position[new_max_depth]);
#endif
if (implementation) {
SIMDJSON_TRY( implementation->set_capacity(new_capacity) );
SIMDJSON_TRY( implementation->set_max_depth(new_max_depth) );
} else {
SIMDJSON_TRY( simdjson::active_implementation->create_dom_parser_implementation(new_capacity, new_max_depth, dom_parser) );
SIMDJSON_TRY( simdjson::active_implementation->create_dom_parser_implementation(new_capacity, new_max_depth, implementation) );
}
_capacity = new_capacity;
_max_depth = new_max_depth;
return SUCCESS;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<document> parser::iterate(const padded_string &buf) & noexcept {
simdjson_warn_unused simdjson_really_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; }
// Allocate if needed
if (_capacity < buf.size() || !string_buf) {
SIMDJSON_TRY( allocate(buf.size(), _max_depth) );
if (capacity() < json.length() || !string_buf) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( dom_parser->stage1((const uint8_t *)buf.data(), buf.size(), false) );
return document::start({ (const uint8_t *)buf.data(), this });
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), false) );
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this });
}
simdjson_warn_unused simdjson_really_inline simdjson_result<document> parser::iterate(const char *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_really_inline simdjson_result<document> parser::iterate(const uint8_t *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_really_inline simdjson_result<document> parser::iterate(std::string_view json, size_t allocated) & noexcept {
return iterate(padded_string_view(json, allocated));
}
simdjson_warn_unused simdjson_really_inline simdjson_result<document> parser::iterate(const std::string &json) & noexcept {
return iterate(padded_string_view(json));
}
simdjson_warn_unused simdjson_really_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string_view> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
padded_string_view json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_really_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
const padded_string &buf = result.value_unsafe();
return iterate(buf);
const padded_string &json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_really_inline simdjson_result<json_iterator> parser::iterate_raw(const padded_string &buf) & noexcept {
simdjson_warn_unused simdjson_really_inline simdjson_result<json_iterator> parser::iterate_raw(padded_string_view json) & noexcept {
if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; }
// Allocate if needed
if (_capacity < buf.size()) {
SIMDJSON_TRY( allocate(buf.size(), _max_depth) );
if (capacity() < json.length()) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( dom_parser->stage1((const uint8_t *)buf.data(), buf.size(), false) );
return json_iterator((const uint8_t *)buf.data(), this);
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), false) );
return json_iterator(reinterpret_cast<const uint8_t *>(json.data()), this);
}
simdjson_really_inline size_t parser::capacity() const noexcept {
return _capacity;
}
simdjson_really_inline size_t parser::max_depth() const noexcept {
return _max_depth;
}
} // namespace ondemand
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
+31 -6
View File
@@ -53,8 +53,11 @@ public:
* those bytes are initialized to, as long as they are allocated.
*
* @param json The JSON to parse.
* @param len The length of the JSON.
* @param capacity The number of bytes allocated in the JSON (must be at least len+SIMDJSON_PADDING).
*
* @return The document, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
@@ -62,11 +65,22 @@ public:
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<document> iterate(const padded_string &json) & noexcept;
simdjson_warn_unused simdjson_result<document> iterate(padded_string_view json) & noexcept;
/** @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 */
simdjson_warn_unused simdjson_result<document> iterate(const uint8_t *json, size_t len, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(std::string_view json, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string_view> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(padded_string &&json) & noexcept = delete;
simdjson_warn_unused simdjson_result<document> iterate(const std::string_view &json) & noexcept = delete;
simdjson_warn_unused simdjson_result<document> iterate(const std::string &json) & noexcept = delete;
/**
* @private
*
@@ -92,8 +106,11 @@ public:
* those bytes are initialized to, as long as they are allocated.
*
* @param json The JSON to parse.
* @param len The length of the JSON.
* @param allocated The number of bytes allocated in the JSON (must be at least len+SIMDJSON_PADDING).
*
* @return The iterator, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
@@ -101,14 +118,22 @@ public:
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<json_iterator> iterate_raw(const padded_string &json) & noexcept;
simdjson_warn_unused simdjson_result<json_iterator> iterate_raw(padded_string_view json) & noexcept;
/** The capacity of this parser (the largest document it can process). */
simdjson_really_inline size_t capacity() const noexcept;
/** The maximum depth of this parser (the most deeply nested objects and arrays it can process). */
simdjson_really_inline size_t max_depth() const noexcept;
private:
/** @private [for benchmarking access] The implementation to use */
std::unique_ptr<internal::dom_parser_implementation> dom_parser{};
std::unique_ptr<internal::dom_parser_implementation> implementation{};
size_t _capacity{0};
size_t _max_depth{0};
size_t _max_depth{DEFAULT_MAX_DEPTH};
std::unique_ptr<uint8_t[]> string_buf{};
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
std::unique_ptr<token_position[]> start_positions{};
#endif
/**
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
@@ -1,37 +1,158 @@
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace ondemand {
simdjson_really_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {}
simdjson_really_inline const char * raw_json_string::raw() const noexcept { return (const char *)buf; }
simdjson_really_inline const char * raw_json_string::raw() const noexcept { return reinterpret_cast<const char *>(buf); }
simdjson_really_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape(uint8_t *&dst) const noexcept {
uint8_t *end = stringparsing::parse_string(buf, dst);
if (!end) { return STRING_ERROR; }
std::string_view result((const char *)dst, end-dst);
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
dst = end;
return result;
}
simdjson_really_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept {
size_t pos{0};
// if the content has no escape character, just scan through it quickly!
for(;pos < target.size() && target[pos] != '\\';pos++) {}
// slow path may begin.
bool escaping{false};
for(;pos < target.size();pos++) {
if((target[pos] == '"') && !escaping) {
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
return true;
}
simdjson_really_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept {
size_t pos{0};
// if the content has no escape character, just scan through it quickly!
for(;target[pos] && target[pos] != '\\';pos++) {}
// slow path may begin.
bool escaping{false};
for(;target[pos];pos++) {
if((target[pos] == '"') && !escaping) {
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
return true;
}
simdjson_really_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string_view target) const noexcept {
// If we are going to call memcmp, then we must know something about the length of the raw_json_string.
return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
simdjson_really_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept {
// Assumptions: does not contain unescaped quote characters, and
// the raw content is quote terminated within a valid JSON string.
if(target.size() <= SIMDJSON_PADDING) {
return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
const char * r{raw()};
size_t pos{0};
for(;pos < target.size();pos++) {
if(r[pos] != target[pos]) { return false; }
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_really_inline bool raw_json_string::is_equal(std::string_view target) const noexcept {
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;pos < target.size();pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_really_inline bool raw_json_string::unsafe_is_equal(const char * target) const noexcept {
// Assumptions: 'target' does not contain unescaped quote characters, is null terminated and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_really_inline bool raw_json_string::is_equal(const char* target) const noexcept {
// Assumptions: does not contain unescaped quote characters, and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_unused simdjson_really_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept {
return a.unsafe_is_equal(c);
}
simdjson_unused simdjson_really_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept {
return a == c;
}
simdjson_unused simdjson_really_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept {
return !(a == c);
}
simdjson_unused simdjson_really_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept {
return !(a == c);
}
simdjson_really_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape(json_iterator &iter) const noexcept {
return unescape(iter.string_buf_loc());
}
simdjson_unused simdjson_really_inline bool operator==(const raw_json_string &a, std::string_view b) noexcept {
return !memcmp(a.raw(), b.data(), b.size());
}
simdjson_unused simdjson_really_inline bool operator==(std::string_view a, const raw_json_string &b) noexcept {
return b == a;
}
simdjson_unused simdjson_really_inline bool operator!=(const raw_json_string &a, std::string_view b) noexcept {
return !(a == b);
}
simdjson_unused simdjson_really_inline bool operator!=(std::string_view a, const raw_json_string &b) noexcept {
return !(a == b);
}
simdjson_unused simdjson_really_inline std::ostream &operator<<(std::ostream &out, const raw_json_string &str) noexcept {
bool in_escape = false;
@@ -16,7 +16,7 @@ class json_iterator;
* JSON file.)
*
* This class is deliberately simplistic and has little functionality. You can
* compare two raw_json_string instances, or compare a raw_json_string with a string_view, but
* compare a raw_json_string instance with an unescaped C string, but
* that is pretty much all you can do.
*
* They originate typically from field instance which in turn represent key-value pairs from
@@ -49,7 +49,76 @@ public:
*/
simdjson_really_inline const char * raw() const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done) on target.size() characters,
* and if the raw_json_string instance has a quote character at byte index target.size().
* We never read more than length + 1 bytes in the raw_json_string instance.
* If length is smaller than target.size(), this will return false.
*
* The std::string_view instance may contain any characters. However, the caller
* is responsible for setting length so that length bytes may be read in the
* raw_json_string.
*
* Performance: the comparison may be done using memcmp which may be efficient
* for long strings.
*/
simdjson_really_inline bool unsafe_is_equal(size_t length, std::string_view target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The std::string_view instance should not contain unescaped quote characters:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* Performance: the comparison is done byte-by-byte which might be inefficient for
* long strings.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penatly...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_really_inline bool unsafe_is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The provided C string should not contain an unescaped quote character:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penatly...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_really_inline bool unsafe_is_equal(const char* target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_really_inline bool is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_really_inline bool is_equal(const char* target) const noexcept;
/**
* Returns true if target is free from unescaped quote. If target is known at
* compile-time, we might expect the computation to happen at compile time with
* many compilers (not all!).
*/
static simdjson_really_inline bool is_free_from_unescaped_quote(std::string_view target) noexcept;
static simdjson_really_inline bool is_free_from_unescaped_quote(const char* target) noexcept;
private:
/**
* This will set the inner pointer to zero, effectively making
* this instance unusable.
@@ -92,13 +161,18 @@ private:
friend struct simdjson_result<raw_json_string>;
};
simdjson_unused simdjson_really_inline bool operator==(const raw_json_string &a, std::string_view b) noexcept;
simdjson_unused simdjson_really_inline bool operator==(std::string_view a, const raw_json_string &b) noexcept;
simdjson_unused simdjson_really_inline bool operator!=(const raw_json_string &a, std::string_view b) noexcept;
simdjson_unused simdjson_really_inline bool operator!=(std::string_view a, const raw_json_string &b) noexcept;
simdjson_unused simdjson_really_inline std::ostream &operator<<(std::ostream &, const raw_json_string &) noexcept;
/**
* Comparisons between raw_json_string and std::string_view instances are potentially unsafe: the user is responsible
* for providing a string with no unescaped quote. Note that unescaped quotes cannot be present in valid JSON strings.
*/
simdjson_unused simdjson_really_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_really_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept;
simdjson_unused simdjson_really_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_really_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept;
} // namespace ondemand
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
+53 -142
View File
@@ -13,26 +13,13 @@ simdjson_really_inline value value::resume(const value_iterator &iter) noexcept
return iter;
}
simdjson_really_inline simdjson_result<array> value::get_array() && noexcept {
simdjson_really_inline simdjson_result<array> value::get_array() noexcept {
return array::start(iter);
}
simdjson_really_inline simdjson_result<array> value::get_array() & noexcept {
return array::try_start(iter);
}
simdjson_really_inline simdjson_result<object> value::get_object() && noexcept {
simdjson_really_inline simdjson_result<object> value::get_object() noexcept {
return object::start(iter);
}
simdjson_really_inline simdjson_result<object> value::get_object() & noexcept {
return object::try_start(iter);
}
simdjson_really_inline simdjson_result<object> value::start_or_resume_object() & noexcept {
if (iter.at_start()) {
return get_object();
} else {
return object::resume(iter);
}
}
simdjson_really_inline simdjson_result<object> value::start_or_resume_object() && noexcept {
simdjson_really_inline simdjson_result<object> value::start_or_resume_object() noexcept {
if (iter.at_start()) {
return get_object();
} else {
@@ -62,44 +49,25 @@ simdjson_really_inline bool value::is_null() noexcept {
return iter.is_null();
}
template<> simdjson_really_inline simdjson_result<array> value::get() & noexcept { return get_array(); }
template<> simdjson_really_inline simdjson_result<object> value::get() & noexcept { return get_object(); }
template<> simdjson_really_inline simdjson_result<raw_json_string> value::get() & noexcept { return get_raw_json_string(); }
template<> simdjson_really_inline simdjson_result<std::string_view> value::get() & noexcept { return get_string(); }
template<> simdjson_really_inline simdjson_result<double> value::get() & noexcept { return get_double(); }
template<> simdjson_really_inline simdjson_result<uint64_t> value::get() & noexcept { return get_uint64(); }
template<> simdjson_really_inline simdjson_result<int64_t> value::get() & noexcept { return get_int64(); }
template<> simdjson_really_inline simdjson_result<bool> value::get() & noexcept { return get_bool(); }
template<> simdjson_really_inline simdjson_result<array> value::get() noexcept { return get_array(); }
template<> simdjson_really_inline simdjson_result<object> value::get() noexcept { return get_object(); }
template<> simdjson_really_inline simdjson_result<raw_json_string> value::get() noexcept { return get_raw_json_string(); }
template<> simdjson_really_inline simdjson_result<std::string_view> value::get() noexcept { return get_string(); }
template<> simdjson_really_inline simdjson_result<double> value::get() noexcept { return get_double(); }
template<> simdjson_really_inline simdjson_result<uint64_t> value::get() noexcept { return get_uint64(); }
template<> simdjson_really_inline simdjson_result<int64_t> value::get() noexcept { return get_int64(); }
template<> simdjson_really_inline simdjson_result<bool> value::get() noexcept { return get_bool(); }
template<> simdjson_really_inline simdjson_result<value> value::get() && noexcept { return std::forward<value>(*this); }
template<> simdjson_really_inline simdjson_result<array> value::get() && noexcept { return std::forward<value>(*this).get_array(); }
template<> simdjson_really_inline simdjson_result<object> value::get() && noexcept { return std::forward<value>(*this).get_object(); }
template<> simdjson_really_inline simdjson_result<raw_json_string> value::get() && noexcept { return std::forward<value>(*this).get_raw_json_string(); }
template<> simdjson_really_inline simdjson_result<std::string_view> value::get() && noexcept { return std::forward<value>(*this).get_string(); }
template<> simdjson_really_inline simdjson_result<double> value::get() && noexcept { return std::forward<value>(*this).get_double(); }
template<> simdjson_really_inline simdjson_result<uint64_t> value::get() && noexcept { return std::forward<value>(*this).get_uint64(); }
template<> simdjson_really_inline simdjson_result<int64_t> value::get() && noexcept { return std::forward<value>(*this).get_int64(); }
template<> simdjson_really_inline simdjson_result<bool> value::get() && noexcept { return std::forward<value>(*this).get_bool(); }
template<typename T> simdjson_really_inline error_code value::get(T &out) & noexcept {
template<typename T> simdjson_really_inline error_code value::get(T &out) noexcept {
return get<T>().get(out);
}
template<typename T> simdjson_really_inline error_code value::get(T &out) && noexcept {
return std::forward<value>(*this).get<T>().get(out);
}
#if SIMDJSON_EXCEPTIONS
simdjson_really_inline value::operator array() && noexcept(false) {
return std::forward<value>(*this).get_array();
simdjson_really_inline value::operator array() noexcept(false) {
return get_array();
}
simdjson_really_inline value::operator array() & noexcept(false) {
return std::forward<value>(*this).get_array();
}
simdjson_really_inline value::operator object() && noexcept(false) {
return std::forward<value>(*this).get_object();
}
simdjson_really_inline value::operator object() & noexcept(false) {
return std::forward<value>(*this).get_object();
simdjson_really_inline value::operator object() noexcept(false) {
return get_object();
}
simdjson_really_inline value::operator uint64_t() noexcept(false) {
return get_uint64();
@@ -128,43 +96,33 @@ simdjson_really_inline simdjson_result<array_iterator> value::end() & noexcept {
return {};
}
simdjson_really_inline simdjson_result<value> value::find_field(std::string_view key) & noexcept {
simdjson_really_inline simdjson_result<value> value::find_field(std::string_view key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_really_inline simdjson_result<value> value::find_field(std::string_view key) && noexcept {
return std::forward<value>(*this).start_or_resume_object().find_field(key);
}
simdjson_really_inline simdjson_result<value> value::find_field(const char *key) & noexcept {
simdjson_really_inline simdjson_result<value> value::find_field(const char *key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_really_inline simdjson_result<value> value::find_field(const char *key) && noexcept {
return std::forward<value>(*this).start_or_resume_object().find_field(key);
}
simdjson_really_inline simdjson_result<value> value::find_field_unordered(std::string_view key) & noexcept {
simdjson_really_inline simdjson_result<value> value::find_field_unordered(std::string_view key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_really_inline simdjson_result<value> value::find_field_unordered(std::string_view key) && noexcept {
return std::forward<value>(*this).start_or_resume_object().find_field_unordered(key);
}
simdjson_really_inline simdjson_result<value> value::find_field_unordered(const char *key) & noexcept {
simdjson_really_inline simdjson_result<value> value::find_field_unordered(const char *key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_really_inline simdjson_result<value> value::find_field_unordered(const char *key) && noexcept {
return std::forward<value>(*this).start_or_resume_object().find_field_unordered(key);
simdjson_really_inline simdjson_result<value> value::operator[](std::string_view key) noexcept {
return start_or_resume_object()[key];
}
simdjson_really_inline simdjson_result<value> value::operator[](const char *key) noexcept {
return start_or_resume_object()[key];
}
simdjson_really_inline simdjson_result<value> value::operator[](std::string_view key) & noexcept {
return start_or_resume_object()[key];
simdjson_really_inline simdjson_result<json_type> value::type() noexcept {
return iter.type();
}
simdjson_really_inline simdjson_result<value> value::operator[](std::string_view key) && noexcept {
return std::forward<value>(*this).start_or_resume_object()[key];
}
simdjson_really_inline simdjson_result<value> value::operator[](const char *key) & noexcept {
return start_or_resume_object()[key];
}
simdjson_really_inline simdjson_result<value> value::operator[](const char *key) && noexcept {
return std::forward<value>(*this).start_or_resume_object()[key];
simdjson_really_inline std::string_view value::raw_json_token() noexcept {
return std::string_view(reinterpret_cast<const char*>(iter.peek_start()), iter.peek_start_length());
}
} // namespace ondemand
@@ -197,73 +155,41 @@ simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_
return {};
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::find_field(std::string_view key) & noexcept {
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::find_field(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::find_field(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::value>(first).find_field(key);
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::find_field(const char *key) & noexcept {
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::find_field(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::find_field(const char *key) && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::value>(first).find_field(key);
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::find_field_unordered(std::string_view key) & noexcept {
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::find_field_unordered(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::find_field_unordered(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::value>(first).find_field_unordered(key);
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::find_field_unordered(const char *key) & noexcept {
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::find_field_unordered(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::find_field_unordered(const char *key) && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::value>(first).find_field_unordered(key);
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator[](std::string_view key) & noexcept {
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator[](std::string_view key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator[](std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::value>(first)[key];
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator[](const char *key) & noexcept {
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator[](const char *key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator[](const char *key) && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::value>(first)[key];
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_array() & noexcept {
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_array() noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_array() && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::value>(first).get_array();
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_object() & noexcept {
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_object() noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_object() && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::value>(first).get_object();
}
simdjson_really_inline simdjson_result<uint64_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
@@ -293,59 +219,39 @@ simdjson_really_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::v
return first.is_null();
}
template<typename T> simdjson_really_inline simdjson_result<T> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get() & noexcept {
template<typename T> simdjson_really_inline simdjson_result<T> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get() noexcept {
if (error()) { return error(); }
return first.get<T>();
}
template<typename T> simdjson_really_inline simdjson_result<T> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get() && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::value>(first).get<T>();
}
template<typename T> simdjson_really_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get(T &out) & noexcept {
template<typename T> simdjson_really_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get(T &out) noexcept {
if (error()) { return error(); }
return first.get<T>(out);
}
template<typename T> simdjson_really_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get(T &out) && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::value>(first).get<T>(out);
}
template<> simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get<SIMDJSON_IMPLEMENTATION::ondemand::value>() & noexcept {
template<> simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get<SIMDJSON_IMPLEMENTATION::ondemand::value>() noexcept {
if (error()) { return error(); }
return std::move(first);
}
template<> simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get<SIMDJSON_IMPLEMENTATION::ondemand::value>() && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::value>(first);
}
template<> simdjson_really_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get<SIMDJSON_IMPLEMENTATION::ondemand::value>(SIMDJSON_IMPLEMENTATION::ondemand::value &out) & noexcept {
template<> simdjson_really_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get<SIMDJSON_IMPLEMENTATION::ondemand::value>(SIMDJSON_IMPLEMENTATION::ondemand::value &out) noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
template<> simdjson_really_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get<SIMDJSON_IMPLEMENTATION::ondemand::value>(SIMDJSON_IMPLEMENTATION::ondemand::value &out) && noexcept {
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_type> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::type() noexcept {
if (error()) { return error(); }
out = std::forward<SIMDJSON_IMPLEMENTATION::ondemand::value>(first);
return SUCCESS;
return first.type();
}
#if SIMDJSON_EXCEPTIONS
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false) {
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator SIMDJSON_IMPLEMENTATION::ondemand::array() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator SIMDJSON_IMPLEMENTATION::ondemand::array() && noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::value>(first);
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator SIMDJSON_IMPLEMENTATION::ondemand::object() & noexcept(false) {
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator SIMDJSON_IMPLEMENTATION::ondemand::object() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator SIMDJSON_IMPLEMENTATION::ondemand::object() && noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::value>(first);
}
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
@@ -372,4 +278,9 @@ simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>
}
#endif
simdjson_really_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
} // namespace simdjson
+96 -79
View File
@@ -27,12 +27,17 @@ public:
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool
*
* You may use get_double(), get_bool(), get_uint64(), get_int64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template<typename T> simdjson_really_inline simdjson_result<T> get() & noexcept;
/** @overload template<typename T> simdjson_result<T> get() & noexcept */
template<typename T> simdjson_really_inline simdjson_result<T> get() && noexcept;
template<typename T> simdjson_really_inline simdjson_result<T> get() noexcept {
// Unless the simdjson library 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.");
}
/**
* Get this value as the given type.
@@ -43,9 +48,7 @@ public:
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T> simdjson_really_inline error_code get(T &out) & noexcept;
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_really_inline error_code get(T &out) && noexcept;
template<typename T> simdjson_really_inline error_code get(T &out) noexcept;
/**
* Cast this JSON value to an array.
@@ -53,9 +56,7 @@ public:
* @returns An object that can be used to iterate the array.
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_really_inline simdjson_result<array> get_array() && noexcept;
/** @overload simdjson_really_inline operator get_array() && noexcept(false); */
simdjson_really_inline simdjson_result<array> get_array() & noexcept;
simdjson_really_inline simdjson_result<array> get_array() noexcept;
/**
* Cast this JSON value to an object.
@@ -63,9 +64,7 @@ public:
* @returns An object that can be used to look up or iterate fields.
* @returns INCORRECT_TYPE If the JSON value is not an object.
*/
simdjson_really_inline simdjson_result<object> get_object() && noexcept;
/** @overload simdjson_really_inline operator object() && noexcept(false); */
simdjson_really_inline simdjson_result<object> get_object() & noexcept;
simdjson_really_inline simdjson_result<object> get_object() noexcept;
/**
* Cast this JSON value to an unsigned integer.
@@ -136,18 +135,14 @@ public:
* @returns An object that can be used to iterate the array.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array.
*/
simdjson_really_inline operator array() && noexcept(false);
/** @overload simdjson_really_inline operator array() && noexcept(false); */
simdjson_really_inline operator array() & noexcept(false);
simdjson_really_inline operator array() noexcept(false);
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object.
*/
simdjson_really_inline operator object() && noexcept(false);
/** @overload simdjson_really_inline operator object() && noexcept(false); */
simdjson_really_inline operator object() & noexcept(false);
simdjson_really_inline operator object() noexcept(false);
/**
* Cast this JSON value to an unsigned integer.
*
@@ -227,20 +222,18 @@ public:
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* @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.
*/
simdjson_really_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_really_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<value> find_field(std::string_view key) && noexcept;
/** @overload simdjson_really_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<value> find_field(const char *key) & noexcept;
/** @overload simdjson_really_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<value> find_field(const char *key) && noexcept;
simdjson_really_inline simdjson_result<value> find_field(std::string_view key) noexcept;
/** @overload simdjson_really_inline simdjson_result<value> find_field(std::string_view key) noexcept; */
simdjson_really_inline simdjson_result<value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
@@ -255,27 +248,60 @@ public:
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* 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).
*
* @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.
*/
simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) && noexcept;
/** @overload simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
/** @overload simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<value> find_field_unordered(const char *key) && noexcept;
/** @overload simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<value> operator[](std::string_view key) && noexcept;
/** @overload simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<value> operator[](const char *key) & noexcept;
/** @overload simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<value> operator[](const char *key) && noexcept;
simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_really_inline simdjson_result<value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_really_inline simdjson_result<value> operator[](std::string_view key) noexcept;
/** @overload simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_really_inline simdjson_result<value> operator[](const char *key) noexcept;
/**
* Get the type of this JSON value.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* @return The type of JSON value (json_type::array, json_type::object, json_type::string,
* json_type::number, json_type::boolean, or json_type::null).
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_really_inline simdjson_result<json_type> type() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. However, if this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view is guaranteed to be
* a non-space token.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*/
simdjson_really_inline std::string_view raw_json_token() noexcept;
protected:
/**
@@ -303,9 +329,7 @@ protected:
/**
* Get the object, starting or resuming it as necessary
*/
simdjson_really_inline simdjson_result<object> start_or_resume_object() & noexcept;
/** @overload simdjson_really_inline simdjson_result<object> start_or_resume_object() & noexcept; */
simdjson_really_inline simdjson_result<object> start_or_resume_object() && noexcept;
simdjson_really_inline simdjson_result<object> start_or_resume_object() noexcept;
// simdjson_really_inline void log_value(const char *type) const noexcept;
// simdjson_really_inline void log_error(const char *message) const noexcept;
@@ -334,11 +358,8 @@ public:
simdjson_really_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_really_inline simdjson_result() noexcept = default;
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array> get_array() && noexcept;
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array> get_array() & noexcept;
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object> get_object() && noexcept;
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object> get_object() & noexcept;
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array> get_array() noexcept;
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object> get_object() noexcept;
simdjson_really_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_really_inline simdjson_result<int64_t> get_int64() noexcept;
@@ -348,17 +369,13 @@ public:
simdjson_really_inline simdjson_result<bool> get_bool() noexcept;
simdjson_really_inline bool is_null() noexcept;
template<typename T> simdjson_really_inline simdjson_result<T> get() & noexcept;
template<typename T> simdjson_really_inline simdjson_result<T> get() && noexcept;
template<typename T> simdjson_really_inline simdjson_result<T> get() noexcept;
template<typename T> simdjson_really_inline error_code get(T &out) & noexcept;
template<typename T> simdjson_really_inline error_code get(T &out) && noexcept;
template<typename T> simdjson_really_inline error_code get(T &out) noexcept;
#if SIMDJSON_EXCEPTIONS
simdjson_really_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() && noexcept(false);
simdjson_really_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
simdjson_really_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() && noexcept(false);
simdjson_really_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() & noexcept(false);
simdjson_really_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() noexcept(false);
simdjson_really_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() noexcept(false);
simdjson_really_inline operator uint64_t() noexcept(false);
simdjson_really_inline operator int64_t() noexcept(false);
simdjson_really_inline operator double() noexcept(false);
@@ -390,13 +407,9 @@ public:
* @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.
*/
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field(std::string_view key) && noexcept;
/** @overload simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field(const char *key) & noexcept;
/** @overload simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field(const char *key) && noexcept;
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field(std::string_view key) noexcept;
/** @overload simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field(std::string_view key) noexcept; */
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
@@ -417,21 +430,25 @@ public:
* @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.
*/
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) && noexcept;
/** @overload simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(const char *key) & noexcept;
/** @overload simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(const char *key) && noexcept;
/** @overload simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](std::string_view key) && noexcept;
/** @overload simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](const char *key) & noexcept;
/** @overload simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](const char *key) && noexcept;
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](std::string_view key) noexcept;
/** @overload simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](const char *key) noexcept;
/**
* Get the type of this JSON value.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*/
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_type> type() noexcept;
/** @copydoc simdjson_really_inline std::string_view value::raw_json_token() const noexcept */
simdjson_really_inline simdjson_result<std::string_view> raw_json_token() noexcept;
};
} // namespace simdjson
@@ -10,28 +10,31 @@ simdjson_really_inline value_iterator::value_iterator(json_iterator *json_iter,
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::start_object() noexcept {
assert_at_start();
if (*_json_iter->advance() != '{') { logger::log_error(*_json_iter, "Not an object"); return INCORRECT_TYPE; }
const uint8_t *json;
SIMDJSON_TRY( advance_container_start("object", json) );
if (*json != '{') { return incorrect_type_error("Not an object"); }
return started_object();
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::try_start_object() noexcept {
assert_at_start();
if (*_json_iter->peek() != '{') { logger::log_error(*_json_iter, "Not an object"); return INCORRECT_TYPE; }
_json_iter->advance();
return started_object();
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::start_root_object() noexcept {
bool result;
SIMDJSON_TRY( start_object().get(result) );
if (*_json_iter->peek_last() != '}') { return _json_iter->report_error(TAPE_ERROR, "object invalid: { at beginning of document unmatched by } at end of document"); }
return result;
}
simdjson_warn_unused simdjson_really_inline bool value_iterator::started_object() noexcept {
assert_at_container_start();
if (*_json_iter->peek() == '}') {
logger::log_value(*_json_iter, "empty object");
_json_iter->advance();
_json_iter->ascend_to(depth()-1);
return false;
}
_json_iter->descend_to(depth()+1);
logger::log_start_value(*_json_iter, "object");
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
_json_iter->set_start_position(_depth, _start_position);
#endif
return true;
}
@@ -44,7 +47,6 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
_json_iter->ascend_to(depth()-1);
return false;
case ',':
_json_iter->descend_to(depth()+1);
return true;
default:
return _json_iter->report_error(TAPE_ERROR, "Missing comma between object fields");
@@ -54,7 +56,6 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::find_field_raw(const std::string_view key) noexcept {
error_code error;
bool has_value;
//
// Initially, the object can be in one of a few different places:
//
@@ -64,6 +65,10 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2, index 1)
// ```
//
if (at_first_field()) {
has_value = true;
//
// 2. When a previous search did not yield a value or the object is empty:
//
@@ -74,9 +79,14 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// ^ (depth 0, index 2)
// ```
//
if (!is_open()) { return false; }
if (at_first_field()) {
has_value = true;
} else if (!is_open()) {
#ifdef 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,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
has_value = false;
// 3. When a previous search found a field or an iterator yielded a value:
//
@@ -95,15 +105,26 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
} else {
if ((error = skip_child() )) { abandon(); return error; }
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
if (_json_iter->start_position(_depth) != _start_position) { return OUT_OF_ORDER_ITERATION; }
#endif
}
while (has_value) {
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
if ((error = field_value() )) { abandon(); return error; }
if ((error = field_value() )) { abandon(); return error; }
// If it matches, stop and return
if (actual_key == key) {
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
//if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
return true;
}
@@ -121,7 +142,6 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::find_field_unordered_raw(const std::string_view key) noexcept {
error_code error;
bool has_value;
//
// Initially, the object can be in one of a few different places:
//
@@ -146,6 +166,12 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// ```
//
} else if (!is_open()) {
#ifdef 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,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
has_value = false;
// 3. When a previous search found a field or an iterator yielded a value:
@@ -166,6 +192,9 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// Finish the previous value and see if , or } is next
if ((error = skip_child() )) { abandon(); return error; }
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
if (_json_iter->start_position(_depth) != _start_position) { return OUT_OF_ORDER_ITERATION; }
#endif
}
// After initial processing, we will be in one of two states:
@@ -188,15 +217,24 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// Next, we find a match starting from the current position.
while (has_value) {
SIMDJSON_ASSUME( _json_iter->_depth == _depth + 1 ); // We must be at the start of a field
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
if ((error = field_value() )) { abandon(); return error; }
// If it matches, stop and return
if (actual_key == key) {
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
// if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
return true;
}
@@ -211,21 +249,29 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
// beginning of the object.
// (We have already run through the object before, so we've already validated its structure. We
// don't check errors in this bit.)
_json_iter->set_position(_start_position + 1);
_json_iter->descend_to(_depth);
_json_iter->reenter_child(_start_position + 1, _depth);
has_value = started_object();
while (_json_iter->position() < search_start) {
SIMDJSON_ASSUME(has_value); // we should reach search_start before ever reaching the end of the object
SIMDJSON_ASSUME( _json_iter->_depth == _depth + 1 ); // We must be at the start of a field
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
error = field_key().get(actual_key); SIMDJSON_ASSUME(!error);
error = field_value(); SIMDJSON_ASSUME(!error);
// If it matches, stop and return
if (actual_key == key) {
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
// if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
return true;
}
@@ -241,7 +287,7 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
}
simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> value_iterator::field_key() noexcept {
assert_at_child();
assert_at_next();
const uint8_t *key = _json_iter->advance();
if (*(key++) != '"') { return _json_iter->report_error(TAPE_ERROR, "Object key is not a string"); }
@@ -249,28 +295,29 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> val
}
simdjson_warn_unused simdjson_really_inline error_code value_iterator::field_value() noexcept {
assert_at_child();
assert_at_next();
if (*_json_iter->advance() != ':') { return _json_iter->report_error(TAPE_ERROR, "Missing colon in object field"); }
_json_iter->descend_to(depth()+1);
return SUCCESS;
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::start_array() noexcept {
assert_at_start();
if (*_json_iter->advance() != '[') { logger::log_error(*_json_iter, "Not an array"); return INCORRECT_TYPE; }
const uint8_t *json;
SIMDJSON_TRY( advance_container_start("array", json) );
if (*json != '[') { return incorrect_type_error("Not an array"); }
return started_array();
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::try_start_array() noexcept {
assert_at_start();
if (*_json_iter->peek() != '[') { logger::log_error(*_json_iter, "Not an array"); return INCORRECT_TYPE; }
_json_iter->advance();
return started_array();
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::start_root_array() noexcept {
bool result;
SIMDJSON_TRY( start_array().get(result) );
if (*_json_iter->peek_last() != ']') { return _json_iter->report_error(TAPE_ERROR, "array invalid: [ at beginning of document unmatched by ] at end of document"); }
return result;
}
simdjson_warn_unused simdjson_really_inline bool value_iterator::started_array() noexcept {
assert_at_container_start();
if (*_json_iter->peek() == ']') {
logger::log_value(*_json_iter, "empty array");
_json_iter->advance();
@@ -279,6 +326,9 @@ simdjson_warn_unused simdjson_really_inline bool value_iterator::started_array()
}
logger::log_start_value(*_json_iter, "array");
_json_iter->descend_to(depth()+1);
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
_json_iter->set_start_position(_depth, _start_position);
#endif
return true;
}
@@ -313,7 +363,7 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<std::string_view> va
return get_raw_json_string().unescape(_json_iter->string_buf_loc());
}
simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> value_iterator::get_raw_json_string() noexcept {
auto json = advance_scalar("string");
auto json = advance_start("string");
if (*json != '"') { return incorrect_type_error("Not a string"); }
return raw_json_string(json+1);
}
@@ -342,21 +392,21 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> val
return get_raw_json_string();
}
simdjson_warn_unused simdjson_really_inline simdjson_result<uint64_t> value_iterator::get_root_uint64() noexcept {
auto max_len = peek_scalar_length();
auto max_len = peek_start_length();
auto json = advance_root_scalar("uint64");
uint8_t tmpbuf[20+1]; // <20 digits> is the longest possible unsigned integer
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { logger::log_error(*_json_iter, _start_position, depth(), "Root number more than 20 characters"); return NUMBER_ERROR; }
return numberparsing::parse_unsigned(tmpbuf);
}
simdjson_warn_unused simdjson_really_inline simdjson_result<int64_t> value_iterator::get_root_int64() noexcept {
auto max_len = peek_scalar_length();
auto max_len = peek_start_length();
auto json = advance_root_scalar("int64");
uint8_t tmpbuf[20+1]; // -<19 digits> is the longest possible integer
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { logger::log_error(*_json_iter, _start_position, depth(), "Root number more than 20 characters"); return NUMBER_ERROR; }
return numberparsing::parse_integer(tmpbuf);
}
simdjson_warn_unused simdjson_really_inline simdjson_result<double> value_iterator::get_root_double() noexcept {
auto max_len = peek_scalar_length();
auto max_len = peek_start_length();
auto json = advance_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 number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1];
@@ -364,14 +414,14 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<double> value_iterat
return numberparsing::parse_double(tmpbuf);
}
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::get_root_bool() noexcept {
auto max_len = peek_scalar_length();
auto max_len = peek_start_length();
auto json = advance_root_scalar("bool");
uint8_t tmpbuf[5+1];
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { return incorrect_type_error("Not a boolean"); }
return parse_bool(tmpbuf);
}
simdjson_really_inline bool value_iterator::is_root_null() noexcept {
auto max_len = peek_scalar_length();
auto max_len = peek_start_length();
auto json = advance_root_scalar("null");
return max_len >= 4 && !atomparsing::str4ncmp(json, "null") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[5]));
@@ -389,9 +439,15 @@ simdjson_really_inline value_iterator value_iterator::child() const noexcept {
return { _json_iter, depth()+1, _json_iter->token.position() };
}
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and iterator depth, which is a desired effect. It does not happen if is_open is
// marked non-inline.
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_really_inline bool value_iterator::is_open() const noexcept {
return _json_iter->depth() >= depth();
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_really_inline bool value_iterator::at_eof() const noexcept {
return _json_iter->at_eof();
@@ -410,7 +466,6 @@ simdjson_really_inline void value_iterator::abandon() noexcept {
_json_iter->abandon();
}
simdjson_warn_unused simdjson_really_inline depth_t value_iterator::depth() const noexcept {
return _depth;
}
@@ -427,17 +482,17 @@ simdjson_warn_unused simdjson_really_inline json_iterator &value_iterator::json_
return *_json_iter;
}
simdjson_really_inline const uint8_t *value_iterator::peek_scalar() const noexcept {
simdjson_really_inline const uint8_t *value_iterator::peek_start() const noexcept {
return _json_iter->peek(_start_position);
}
simdjson_really_inline uint32_t value_iterator::peek_scalar_length() const noexcept {
simdjson_really_inline uint32_t value_iterator::peek_start_length() const noexcept {
return _json_iter->peek_length(_start_position);
}
simdjson_really_inline const uint8_t *value_iterator::advance_scalar(const char *type) const noexcept {
simdjson_really_inline const uint8_t *value_iterator::advance_start(const char *type) const noexcept {
logger::log_value(*_json_iter, _start_position, depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
if (!is_at_start()) { return peek_scalar(); }
if (!is_at_start()) { return peek_start(); }
// Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
assert_at_start();
@@ -445,9 +500,26 @@ simdjson_really_inline const uint8_t *value_iterator::advance_scalar(const char
_json_iter->ascend_to(depth()-1);
return result;
}
simdjson_really_inline error_code value_iterator::advance_container_start(const char *type, const uint8_t *&json) const noexcept {
logger::log_start_value(*_json_iter, _start_position, depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
if (!is_at_start()) {
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
if (!is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; }
#endif
json = peek_start();
return SUCCESS;
}
// Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
assert_at_start();
json = _json_iter->advance();
return SUCCESS;
}
simdjson_really_inline const uint8_t *value_iterator::advance_root_scalar(const char *type) const noexcept {
logger::log_value(*_json_iter, _start_position, depth(), type);
if (!is_at_start()) { return peek_scalar(); }
if (!is_at_start()) { return peek_start(); }
assert_at_root();
auto result = _json_iter->advance();
@@ -456,7 +528,7 @@ simdjson_really_inline const uint8_t *value_iterator::advance_root_scalar(const
}
simdjson_really_inline const uint8_t *value_iterator::advance_non_root_scalar(const char *type) const noexcept {
logger::log_value(*_json_iter, _start_position, depth(), type);
if (!is_at_start()) { return peek_scalar(); }
if (!is_at_start()) { return peek_start(); }
assert_at_non_root_start();
auto result = _json_iter->advance();
@@ -472,6 +544,14 @@ simdjson_really_inline error_code value_iterator::incorrect_type_error(const cha
simdjson_really_inline bool value_iterator::is_at_start() const noexcept {
return _json_iter->token.index == _start_position;
}
simdjson_really_inline bool value_iterator::is_at_container_start() const noexcept {
return _json_iter->token.index == _start_position + 1;
}
simdjson_really_inline bool value_iterator::is_at_iterator_start() const noexcept {
// We can legitimately be either at the first value ([1]), or after the array if it's empty ([]).
auto delta = _json_iter->token.index - _start_position;
return delta == 1 || delta == 2;
}
simdjson_really_inline void value_iterator::assert_at_start() const noexcept {
SIMDJSON_ASSUME( _json_iter->token.index == _start_position );
@@ -479,6 +559,12 @@ simdjson_really_inline void value_iterator::assert_at_start() const noexcept {
SIMDJSON_ASSUME( _depth > 0 );
}
simdjson_really_inline void value_iterator::assert_at_container_start() const noexcept {
SIMDJSON_ASSUME( _json_iter->token.index == _start_position + 1 );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
simdjson_really_inline void value_iterator::assert_at_next() const noexcept {
SIMDJSON_ASSUME( _json_iter->token.index > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
@@ -501,6 +587,35 @@ simdjson_really_inline void value_iterator::assert_at_non_root_start() const noe
SIMDJSON_ASSUME( _depth > 1 );
}
simdjson_really_inline void value_iterator::assert_is_valid() const noexcept {
SIMDJSON_ASSUME( _json_iter != nullptr );
}
simdjson_really_inline bool value_iterator::is_valid() const noexcept {
return _json_iter != nullptr;
}
simdjson_really_inline simdjson_result<json_type> value_iterator::type() noexcept {
switch (*peek_start()) {
case '{':
return json_type::object;
case '[':
return json_type::array;
case '"':
return json_type::string;
case 'n':
return json_type::null;
case 't': case 'f':
return json_type::boolean;
case '-':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return json_type::number;
default:
return TAPE_ERROR;
}
}
} // namespace ondemand
} // namespace SIMDJSON_IMPLEMENTATION
@@ -513,4 +628,4 @@ simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value_
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::value_iterator>(error) {}
} // namespace simdjson
} // namespace simdjson
@@ -80,6 +80,13 @@ public:
*/
simdjson_really_inline depth_t depth() const noexcept;
/**
* Get the JSON type of this value.
*
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_really_inline simdjson_result<json_type> type() noexcept;
/**
* @addtogroup object Object iteration
*
@@ -97,12 +104,13 @@ public:
*/
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> start_object() noexcept;
/**
* Check for an opening { and start an object iteration.
* Start an object iteration from the root.
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCORRECT_TYPE if there is no opening {
* @error TAPE_ERROR if there is no matching } at end of document
*/
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> try_start_object() noexcept;
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> start_root_object() noexcept;
/**
* Start an object iteration after the user has already checked and moved past the {.
@@ -203,18 +211,18 @@ public:
/**
* Check for an opening [ and start an array iteration.
*
* @param json A pointer to the potential [.
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
*/
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> start_array() noexcept;
/**
* Check for an opening [ and start an array iteration.
* Check for an opening [ and start an array iteration while at the root.
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
* @error TAPE_ERROR if there is no matching ] at end of document
*/
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> try_start_array() noexcept;
simdjson_warn_unused simdjson_really_inline simdjson_result<bool> start_root_array() noexcept;
/**
* Start an array iteration after the user has already checked and moved past the [.
@@ -270,6 +278,9 @@ public:
simdjson_really_inline const json_iterator &json_iter() const noexcept;
simdjson_really_inline json_iterator &json_iter() noexcept;
simdjson_really_inline void assert_is_valid() const noexcept;
simdjson_really_inline bool is_valid() const noexcept;
/** @} */
protected:
@@ -278,16 +289,20 @@ protected:
simdjson_really_inline bool parse_null(const uint8_t *json) const noexcept;
simdjson_really_inline simdjson_result<bool> parse_bool(const uint8_t *json) const noexcept;
simdjson_really_inline const uint8_t *peek_scalar() const noexcept;
simdjson_really_inline uint32_t peek_scalar_length() const noexcept;
simdjson_really_inline const uint8_t *advance_scalar(const char *type) const noexcept;
simdjson_really_inline const uint8_t *peek_start() const noexcept;
simdjson_really_inline uint32_t peek_start_length() const noexcept;
simdjson_really_inline const uint8_t *advance_start(const char *type) const noexcept;
simdjson_really_inline error_code advance_container_start(const char *type, const uint8_t *&json) const noexcept;
simdjson_really_inline const uint8_t *advance_root_scalar(const char *type) const noexcept;
simdjson_really_inline const uint8_t *advance_non_root_scalar(const char *type) const noexcept;
simdjson_really_inline error_code incorrect_type_error(const char *message) const noexcept;
simdjson_really_inline bool is_at_start() const noexcept;
simdjson_really_inline bool is_at_container_start() const noexcept;
simdjson_really_inline bool is_at_iterator_start() const noexcept;
simdjson_really_inline void assert_at_start() const noexcept;
simdjson_really_inline void assert_at_container_start() const noexcept;
simdjson_really_inline void assert_at_root() const noexcept;
simdjson_really_inline void assert_at_child() const noexcept;
simdjson_really_inline void assert_at_next() const noexcept;
+1 -1
View File
@@ -127,7 +127,7 @@ simdjson_unused simdjson_warn_unused simdjson_really_inline error_code parse_str
if (*(src++) != '"') { return STRING_ERROR; }
auto end = stringparsing::parse_string(src, current_string_buf_loc);
if (!end) { return STRING_ERROR; }
s = std::string_view((const char *)current_string_buf_loc, end-current_string_buf_loc);
s = std::string_view(reinterpret_cast<const char *>(current_string_buf_loc), end-current_string_buf_loc);
current_string_buf_loc = end;
return SUCCESS;
}
+8 -25
View File
@@ -1,27 +1,17 @@
#ifndef SIMDJSON_HASWELL_H
#define SIMDJSON_HASWELL_H
#ifdef SIMDJSON_WESTMERE_H
#error "haswell.h must be included before westmere.h"
#endif
#ifdef SIMDJSON_FALLBACK_H
#error "haswell.h must be included before fallback.h"
#endif
#include "simdjson/portability.h"
// Default Haswell to on if this is x86-64. Even if we're not compiled for it, it could be selected
// at runtime.
#ifndef SIMDJSON_IMPLEMENTATION_HASWELL
#define SIMDJSON_IMPLEMENTATION_HASWELL (SIMDJSON_IS_X86_64)
#endif
// To see why (__BMI__) && (__PCLMUL__) && (__LZCNT__) are not part of this next line, see
// https://github.com/simdjson/simdjson/issues/1247
#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__))
#include "simdjson/implementation-base.h"
#if SIMDJSON_IMPLEMENTATION_HASWELL
#if SIMDJSON_CAN_ALWAYS_RUN_HASWELL
#define SIMDJSON_TARGET_HASWELL
#define SIMDJSON_UNTARGET_HASWELL
#else
#define SIMDJSON_TARGET_HASWELL SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt")
#define SIMDJSON_UNTARGET_HASWELL SIMDJSON_UNTARGET_REGION
#endif
namespace simdjson {
/**
@@ -32,7 +22,7 @@ namespace haswell {
} // namespace simdjson
//
// These two need to be included outside SIMDJSON_TARGET_REGION
// These two need to be included outside SIMDJSON_TARGET_HASWELL
//
#include "simdjson/haswell/implementation.h"
#include "simdjson/haswell/intrinsics.h"
@@ -51,13 +41,6 @@ namespace haswell {
#include "simdjson/generic/atomparsing.h"
#include "simdjson/haswell/stringparsing.h"
#include "simdjson/haswell/numberparsing.h"
#include "simdjson/generic/implementation_simdjson_result_base.h"
#include "simdjson/generic/ondemand.h"
// Inline definitions
#include "simdjson/generic/implementation_simdjson_result_base-inl.h"
#include "simdjson/generic/ondemand-inl.h"
#include "simdjson/haswell/end.h"
#endif // SIMDJSON_IMPLEMENTATION_HASWELL
+1 -1
View File
@@ -50,7 +50,7 @@ simdjson_really_inline bool add_overflow(uint64_t value1, uint64_t value2,
reinterpret_cast<unsigned __int64 *>(result));
#else
return __builtin_uaddll_overflow(value1, value2,
(unsigned long long *)result);
reinterpret_cast<unsigned long long *>(result));
#endif
}
+1 -1
View File
@@ -1,2 +1,2 @@
SIMDJSON_UNTARGET_REGION
SIMDJSON_UNTARGET_HASWELL
#undef SIMDJSON_IMPLEMENTATION
+1 -1
View File
@@ -3,7 +3,7 @@
#include "simdjson/implementation.h"
// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_REGION
// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_HASWELL
namespace simdjson {
namespace haswell {
+2 -2
View File
@@ -1,7 +1,7 @@
#ifndef SIMDJSON_HASWELL_INTRINSICS_H
#define SIMDJSON_HASWELL_INTRINSICS_H
#include "simdjson.h"
#include "simdjson/base.h"
#ifdef SIMDJSON_VISUAL_STUDIO
// under clang within visual studio, this will include <x86intrin.h>
@@ -45,7 +45,7 @@ SIMDJSON_TARGET_HASWELL
static simdjson_really_inline uint64_t _blsr_u64(uint64_t n) {
return (n - 1) & n;
}
SIMDJSON_UNTARGET_REGION
SIMDJSON_UNTARGET_HASWELL
#endif // _blsr_u64
#endif // SIMDJSON_CLANG_VISUAL_STUDIO
+10 -10
View File
@@ -28,9 +28,9 @@ namespace simd {
simdjson_really_inline Child operator&(const Child other) const { return _mm256_and_si256(*this, other); }
simdjson_really_inline Child operator^(const Child other) const { return _mm256_xor_si256(*this, other); }
simdjson_really_inline Child bit_andnot(const Child other) const { return _mm256_andnot_si256(other, *this); }
simdjson_really_inline Child& operator|=(const Child other) { auto this_cast = (Child*)this; *this_cast = *this_cast | other; return *this_cast; }
simdjson_really_inline Child& operator&=(const Child other) { auto this_cast = (Child*)this; *this_cast = *this_cast & other; return *this_cast; }
simdjson_really_inline Child& operator^=(const Child other) { auto this_cast = (Child*)this; *this_cast = *this_cast ^ other; return *this_cast; }
simdjson_really_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_really_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_really_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
// Forward-declared so they can be used by splat and friends.
@@ -99,8 +99,8 @@ namespace simd {
// Addition/subtraction are the same for signed and unsigned
simdjson_really_inline simd8<T> operator+(const simd8<T> other) const { return _mm256_add_epi8(*this, other); }
simdjson_really_inline simd8<T> operator-(const simd8<T> other) const { return _mm256_sub_epi8(*this, other); }
simdjson_really_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *(simd8<T>*)this; }
simdjson_really_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *(simd8<T>*)this; }
simdjson_really_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
simdjson_really_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
// Override to distinguish from bool version
simdjson_really_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
@@ -148,9 +148,9 @@ namespace simd {
// then load the corresponding mask
// could be done with _mm256_loadu2_m128i but many standard libraries omit this intrinsic.
__m256i v256 = _mm256_castsi128_si256(
_mm_loadu_si128((const __m128i *)(pshufb_combine_table + pop1 * 8)));
_mm_loadu_si128(reinterpret_cast<const __m128i *>(pshufb_combine_table + pop1 * 8)));
__m256i compactmask = _mm256_insertf128_si256(v256,
_mm_loadu_si128((const __m128i *)(pshufb_combine_table + pop3 * 8)), 1);
_mm_loadu_si128(reinterpret_cast<const __m128i *>(pshufb_combine_table + pop3 * 8)), 1);
__m256i almostthere = _mm256_shuffle_epi8(pruned, compactmask);
// We just need to write out the result.
// This is the tricky bit that is hard to do
@@ -159,9 +159,9 @@ namespace simd {
// the two 128-bit lanes with an offset.
__m128i v128;
v128 = _mm256_castsi256_si128(almostthere);
_mm_storeu_si128( (__m128i *)output, v128);
_mm_storeu_si128( reinterpret_cast<__m128i *>(output), v128);
v128 = _mm256_extractf128_si256(almostthere, 1);
_mm_storeu_si128( (__m128i *)(output + 16 - count_ones(mask & 0xFFFF)), v128);
_mm_storeu_si128( reinterpret_cast<__m128i *>(output + 16 - count_ones(mask & 0xFFFF)), v128);
}
template<typename L>
@@ -297,7 +297,7 @@ namespace simd {
const simd8<T> chunks[NUM_CHUNKS];
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T> other) = delete; // no assignment allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_really_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1) : chunks{chunk0, chunk1} {}
+3 -3
View File
@@ -1,7 +1,7 @@
#ifndef SIMDJSON_HASWELL_STRINGPARSING_H
#define SIMDJSON_HASWELL_STRINGPARSING_H
#include "simdjson.h"
#include "simdjson/base.h"
#include "simdjson/haswell/simd.h"
#include "simdjson/haswell/bitmanipulation.h"
@@ -34,8 +34,8 @@ simdjson_really_inline backslash_and_quote backslash_and_quote::copy_and_find(co
// store to dest unconditionally - we can overwrite the bits we don't like later
v.store(dst);
return {
(uint32_t)(v == '\\').to_bitmask(), // bs_bits
(uint32_t)(v == '"').to_bitmask(), // quote_bits
static_cast<uint32_t>((v == '\\').to_bitmask()), // bs_bits
static_cast<uint32_t>((v == '"').to_bitmask()), // quote_bits
};
}
+22
View File
@@ -0,0 +1,22 @@
#ifndef SIMDJSON_IMPLEMENTATION_BASE_H
#define SIMDJSON_IMPLEMENTATION_BASE_H
/**
* @file
*
* Includes common stuff needed for implementations.
*/
#include "simdjson/base.h"
#include "simdjson/implementation.h"
// Implementation-internal files (must be included before the implementations themselves, to keep
// amalgamation working--otherwise, the first time a file is included, it might be put inside the
// #ifdef SIMDJSON_IMPLEMENTATION_ARM64/FALLBACK/etc., which means the other implementations can't
// compile unless that implementation is turned on).
#include "simdjson/internal/isadetection.h"
#include "simdjson/internal/jsoncharutils_tables.h"
#include "simdjson/internal/numberparsing_tables.h"
#include "simdjson/internal/simdprune_tables.h"
#endif // SIMDJSON_IMPLEMENTATION_BASE_H
+57
View File
@@ -0,0 +1,57 @@
#ifndef SIMDJSON_IMPLEMENTATIONS_H
#define SIMDJSON_IMPLEMENTATIONS_H
#include "simdjson/implementation-base.h"
//
// First, figure out which implementations can be run. Doing it here makes it so we don't have to worry about the order
// in which we include them.
//
#ifndef SIMDJSON_IMPLEMENTATION_ARM64
#define SIMDJSON_IMPLEMENTATION_ARM64 (SIMDJSON_IS_ARM64)
#endif
#define SIMDJSON_CAN_ALWAYS_RUN_ARM64 SIMDJSON_IMPLEMENTATION_ARM64 && SIMDJSON_IS_ARM64
// Default Haswell to on if this is x86-64. Even if we're not compiled for it, it could be selected
// at runtime.
#ifndef SIMDJSON_IMPLEMENTATION_HASWELL
#define SIMDJSON_IMPLEMENTATION_HASWELL (SIMDJSON_IS_X86_64)
#endif
// To see why (__BMI__) && (__PCLMUL__) && (__LZCNT__) are not part of this next line, see
// https://github.com/simdjson/simdjson/issues/1247
#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__))
// Default Westmere to on if this is x86-64, unless we'll always select Haswell.
#ifndef SIMDJSON_IMPLEMENTATION_WESTMERE
#define SIMDJSON_IMPLEMENTATION_WESTMERE (SIMDJSON_IS_X86_64 && !SIMDJSON_REQUIRES_HASWELL)
#endif
#define SIMDJSON_CAN_ALWAYS_RUN_WESTMERE (SIMDJSON_IMPLEMENTATION_WESTMERE && SIMDJSON_IS_X86_64 && __SSE4_2__ && __PCLMUL__)
#ifndef SIMDJSON_IMPLEMENTATION_PPC64
#define SIMDJSON_IMPLEMENTATION_PPC64 (SIMDJSON_IS_PPC64)
#endif
#define SIMDJSON_CAN_ALWAYS_RUN_PPC64 SIMDJSON_IMPLEMENTATION_PPC64 && SIMDJSON_IS_PPC64
// Default Fallback to on unless a builtin implementation has already been selected.
#ifndef SIMDJSON_IMPLEMENTATION_FALLBACK
#define SIMDJSON_IMPLEMENTATION_FALLBACK 1 // (!SIMDJSON_CAN_ALWAYS_RUN_ARM64 && !SIMDJSON_CAN_ALWAYS_RUN_HASWELL && !SIMDJSON_CAN_ALWAYS_RUN_WESTMERE && !SIMDJSON_CAN_ALWAYS_RUN_PPC64)
#endif
#define SIMDJSON_CAN_ALWAYS_RUN_FALLBACK SIMDJSON_IMPLEMENTATION_FALLBACK
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_UNDESIRED_WARNINGS
// Implementations
#include "simdjson/arm64.h"
#include "simdjson/fallback.h"
#include "simdjson/haswell.h"
#include "simdjson/ppc64.h"
#include "simdjson/westmere.h"
// Builtin implementation
#include "simdjson/builtin.h"
SIMDJSON_POP_DISABLE_WARNINGS
#endif // SIMDJSON_IMPLEMENTATIONS_H
@@ -83,6 +83,9 @@ public:
/**
* Change the capacity of this parser.
*
* The capacity can never exceed SIMDJSON_MAXSIZE_BYTES (e.g., 4 GB)
* and an CAPACITY error is returned if it is attempted.
*
* Generally used for reallocation.
*
* @param capacity The new capacity.
@@ -1,7 +1,7 @@
#ifndef SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H
#define SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H
#include "simdjson.h"
#include "simdjson/common_defs.h"
#ifdef JSON_TEST_STRINGS
void found_string(const uint8_t *buf, const uint8_t *parsed_begin,
+1 -1
View File
@@ -46,7 +46,7 @@ inline std::ostream& operator<<(std::ostream& out, const escape_json_string &une
out << "\\\\";
break;
default:
if ((unsigned char)unescaped.str[i] <= 0x1F) {
if (static_cast<unsigned char>(unescaped.str[i]) <= 0x1F) {
// TODO can this be done once at the beginning, or will it mess up << char?
std::ios::fmtflags f(out.flags());
out << "\\u" << std::hex << std::setw(4) << std::setfill('0') << int(unescaped.str[i]);
@@ -1,7 +1,7 @@
#ifndef SIMDJSON_INTERNAL_NUMBERPARSING_TABLES_H
#define SIMDJSON_INTERNAL_NUMBERPARSING_TABLES_H
#include "simdjson.h"
#include "simdjson/base.h"
namespace simdjson {
namespace internal {

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