mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b7b01fd788 | |||
| 2c07a242ea | |||
| a63c77a977 | |||
| 43219e50e0 | |||
| 49b9860899 | |||
| 0388d79770 | |||
| c0fa1aec66 | |||
| 6e61b7f6ef | |||
| a76e778804 | |||
| ccf8694510 | |||
| 9b67497ed0 | |||
| 0336684df7 | |||
| c19320dd6e | |||
| 412a5680e8 | |||
| a05a56856d | |||
| 58173a6a1f | |||
| 1721032cfd | |||
| b73877f95e | |||
| 09723897e9 | |||
| 49e231b634 | |||
| acdbbab916 | |||
| 5090247c34 | |||
| 4180e05730 | |||
| feea2bce2c | |||
| 692f43cd84 |
@@ -0,0 +1 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
@@ -18,7 +18,7 @@ We do not make changes to simdjson without clearly identifiable benefits, which
|
|||||||
|
|
||||||
Is your issue:
|
Is your issue:
|
||||||
|
|
||||||
1. A bug report? If so, please point at a reproducible test. Indicate whether you are willing or able to provide a bug fix as a pull request.
|
1. A bug report? If so, please point at a reproducible test. Indicate whether you are willing or able to provide a bug fix as a pull request. As a matter of policy, we do not consider a compiler warning to be a bug.
|
||||||
|
|
||||||
2. A build issue? If so, provide all possible details regarding your system configuration. If we cannot reproduce your issue, we cannot fix it.
|
2. A build issue? If so, provide all possible details regarding your system configuration. If we cannot reproduce your issue, we cannot fix it.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
name: Ubuntu ppc64le (GCC 11)
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: uraimo/run-on-arch-action@v2
|
||||||
|
name: Test
|
||||||
|
id: runcmd
|
||||||
|
with:
|
||||||
|
arch: aarch64
|
||||||
|
distro: ubuntu_latest
|
||||||
|
githubToken: ${{ github.token }}
|
||||||
|
install: |
|
||||||
|
apt-get update -q -y
|
||||||
|
apt-get install -y cmake make g++
|
||||||
|
run: |
|
||||||
|
cmake -DSIMDJSON_SANITIZE_UNDEFINED=ON -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -B build
|
||||||
|
cmake --build build -j=2
|
||||||
|
ctest --output-on-failure --test-dir build
|
||||||
@@ -17,7 +17,7 @@ jobs:
|
|||||||
fuzz-seconds: 600
|
fuzz-seconds: 600
|
||||||
dry-run: false
|
dry-run: false
|
||||||
- name: Upload Crash
|
- name: Upload Crash
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v4
|
||||||
if: failure() && steps.build.outcome == 'success'
|
if: failure() && steps.build.outcome == 'success'
|
||||||
with:
|
with:
|
||||||
name: artifacts
|
name: artifacts
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
echo "no trailing whitespace found, good!"
|
echo "no trailing whitespace found, good!"
|
||||||
fi
|
fi
|
||||||
- name: Archive whitespace patch
|
- name: Archive whitespace patch
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v4
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: whitespace-patch
|
name: whitespace-patch
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
- msystem: "MINGW64"
|
- msystem: "MINGW64"
|
||||||
install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc
|
install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc
|
||||||
type: Debug
|
type: Debug
|
||||||
- msystem: "MINGW64"
|
- msystem: "MINGW64"
|
||||||
install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc
|
install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc
|
||||||
type: RelWithDebInfo
|
type: RelWithDebInfo
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -24,6 +24,6 @@ jobs:
|
|||||||
apt-get update -q -y
|
apt-get update -q -y
|
||||||
apt-get install -y cmake make g++
|
apt-get install -y cmake make g++
|
||||||
run: |
|
run: |
|
||||||
cmake -DCMAKE_BUILD_TYPE=Release -B build
|
cmake -DCMAKE_BUILD_TYPE=Release -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -B build
|
||||||
cmake --build build -j=2
|
cmake --build build -j=2
|
||||||
ctest --output-on-failure --test-dir build
|
ctest --output-on-failure --test-dir build
|
||||||
|
|||||||
@@ -24,6 +24,6 @@ jobs:
|
|||||||
apt-get update -q -y
|
apt-get update -q -y
|
||||||
apt-get install -y cmake make g++
|
apt-get install -y cmake make g++
|
||||||
run: |
|
run: |
|
||||||
cmake -DCMAKE_BUILD_TYPE=Release -B build
|
cmake -DCMAKE_BUILD_TYPE=Release -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -B build
|
||||||
cmake --build build -j=2
|
cmake --build build -j=2
|
||||||
ctest --output-on-failure --test-dir build
|
ctest --output-on-failure --test-dir build
|
||||||
|
|||||||
@@ -24,6 +24,6 @@ jobs:
|
|||||||
apt-get update -q -y
|
apt-get update -q -y
|
||||||
apt-get install -y cmake make g++
|
apt-get install -y cmake make g++
|
||||||
run: |
|
run: |
|
||||||
cmake -DCMAKE_BUILD_TYPE=Release -B build
|
cmake -DCMAKE_BUILD_TYPE=Release -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -B build
|
||||||
cmake --build build -j=2
|
cmake --build build -j=2
|
||||||
ctest --output-on-failure --test-dir build
|
ctest --output-on-failure --test-dir build
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
name: Ubuntu 24.04 CI (CXX 20)
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
jobs:
|
||||||
|
ubuntu-build:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
cxx: [g++-13, clang++-16]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||||
|
- name: Prepare
|
||||||
|
run: cmake -DSIMDJSON_CXX_STANDARD=20 -DSIMDJSON_DEVELOPER_MODE=ON -B build
|
||||||
|
env:
|
||||||
|
CXX: ${{matrix.cxx}}
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build build -j=2
|
||||||
|
- name: Test
|
||||||
|
run: ctest --output-on-failure --test-dir build
|
||||||
@@ -26,15 +26,15 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -DBUILD_SHARED_LIBS=${{matrix.shared}} -B build
|
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -DBUILD_SHARED_LIBS=${{matrix.shared}} -B build
|
||||||
- name: Build Debug
|
- name: Build Debug
|
||||||
run: cmake --build build --config ${{build_type}} --verbose
|
run: cmake --build build --config ${{matrix.build_type}} --verbose
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: |
|
run: |
|
||||||
cd build
|
cd build
|
||||||
ctest -C ${{build_type}} -LE explicitonly --output-on-failure
|
ctest -C ${{matrix.build_type}} -LE explicitonly --output-on-failure
|
||||||
- name: Install
|
- name: Install
|
||||||
run: |
|
run: |
|
||||||
cmake --install build --config ${{build_type}}
|
cmake --install build --config ${{matrix.build_type}}
|
||||||
- name: Test Installation
|
- name: Test Installation
|
||||||
run: |
|
run: |
|
||||||
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
|
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
|
||||||
cmake --build build_install_test --config ${{build_type}}
|
cmake --build build_install_test --config ${{matrix.build_type}}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
name: VS17-CLANG-CI
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
name: windows-vs17
|
||||||
|
runs-on: windows-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- {gen: Visual Studio 17 2022, arch: x64, build_type: Debug}
|
||||||
|
- {gen: Visual Studio 17 2022, arch: x64, build_type: Release}
|
||||||
|
- {gen: Visual Studio 17 2022, arch: x64, build_type: RelWithDebInfo}
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Configure
|
||||||
|
run: |
|
||||||
|
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -B build
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build build --config ${{matrix.build_type}} --verbose
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
ctest -C ${{matrix.build_type}} -LE explicitonly --output-on-failure
|
||||||
|
- name: Install
|
||||||
|
run: |
|
||||||
|
cmake --install build --config ${{matrix.build_type}}
|
||||||
|
- name: Test Installation
|
||||||
|
run: |
|
||||||
|
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
|
||||||
|
cmake --build build_install_test --config ${{matrix.build_type}}
|
||||||
Vendored
+18
-1
@@ -109,6 +109,23 @@
|
|||||||
"numbers": "cpp",
|
"numbers": "cpp",
|
||||||
"semaphore": "cpp",
|
"semaphore": "cpp",
|
||||||
"stop_token": "cpp",
|
"stop_token": "cpp",
|
||||||
"cfenv": "cpp"
|
"cfenv": "cpp",
|
||||||
|
"format": "cpp",
|
||||||
|
"xlocmes": "cpp",
|
||||||
|
"xlocmon": "cpp",
|
||||||
|
"xlocnum": "cpp",
|
||||||
|
"xloctime": "cpp",
|
||||||
|
"xutility": "cpp",
|
||||||
|
"coroutine": "cpp",
|
||||||
|
"xfacet": "cpp",
|
||||||
|
"xhash": "cpp",
|
||||||
|
"xiosbase": "cpp",
|
||||||
|
"xlocale": "cpp",
|
||||||
|
"xlocbuf": "cpp",
|
||||||
|
"xlocinfo": "cpp",
|
||||||
|
"xmemory": "cpp",
|
||||||
|
"xstring": "cpp",
|
||||||
|
"xtr1common": "cpp",
|
||||||
|
"xtree": "cpp"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3
-3
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14)
|
|||||||
project(
|
project(
|
||||||
simdjson
|
simdjson
|
||||||
# The version number is modified by tools/release.py
|
# The version number is modified by tools/release.py
|
||||||
VERSION 3.9.4
|
VERSION 3.10.0
|
||||||
DESCRIPTION "Parsing gigabytes of JSON per second"
|
DESCRIPTION "Parsing gigabytes of JSON per second"
|
||||||
HOMEPAGE_URL "https://simdjson.org/"
|
HOMEPAGE_URL "https://simdjson.org/"
|
||||||
LANGUAGES CXX C
|
LANGUAGES CXX C
|
||||||
@@ -20,8 +20,8 @@ string(
|
|||||||
# ---- Options, variables ----
|
# ---- Options, variables ----
|
||||||
|
|
||||||
# These version numbers are modified by tools/release.py
|
# These version numbers are modified by tools/release.py
|
||||||
set(SIMDJSON_LIB_VERSION "22.0.0" CACHE STRING "simdjson library version")
|
set(SIMDJSON_LIB_VERSION "23.0.0" CACHE STRING "simdjson library version")
|
||||||
set(SIMDJSON_LIB_SOVERSION "22" CACHE STRING "simdjson library soversion")
|
set(SIMDJSON_LIB_SOVERSION "23" CACHE STRING "simdjson library soversion")
|
||||||
|
|
||||||
option(SIMDJSON_BUILD_STATIC_LIB "Build simdjson_static library along with simdjson" OFF)
|
option(SIMDJSON_BUILD_STATIC_LIB "Build simdjson_static library along with simdjson" OFF)
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
|
|||||||
# could be handy for archiving the generated documentation or if some version
|
# could be handy for archiving the generated documentation or if some version
|
||||||
# control system is used.
|
# control system is used.
|
||||||
|
|
||||||
PROJECT_NUMBER = "3.9.4"
|
PROJECT_NUMBER = "3.10.0"
|
||||||
|
|
||||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||||
# for a project that appears at the top of each page and should give viewer a
|
# for a project that appears at the top of each page and should give viewer a
|
||||||
|
|||||||
+3
-3
@@ -88,8 +88,8 @@ simdjson's source structure, from the top level, looks like this:
|
|||||||
* simdjson/ondemand.h: the `simdjson::ondemand` namespace. Includes all public ondemand classes.
|
* simdjson/ondemand.h: the `simdjson::ondemand` namespace. Includes all public ondemand classes.
|
||||||
* simdjson/builtin.h: the `simdjson::builtin` namespace. Aliased to the most universal implementation available.
|
* simdjson/builtin.h: the `simdjson::builtin` namespace. Aliased to the most universal implementation available.
|
||||||
* simdjson/builtin/ondemand.h: the `simdjson::builtin::ondemand` namespace.
|
* simdjson/builtin/ondemand.h: the `simdjson::builtin::ondemand` namespace.
|
||||||
* simdjson/arm64|fallback|haswell|icelake|ppc64|westmere/ondemand.h: the `simdjson::<implementation>::ondemand` namespace. on demand compiled for the specific implementation.
|
* simdjson/arm64|fallback|haswell|icelake|ppc64|westmere/ondemand.h: the `simdjson::<implementation>::ondemand` namespace. On-Demand compiled for the specific implementation.
|
||||||
* simdjson/generic/ondemand/*.h: individual on demand classes, generically written.
|
* simdjson/generic/ondemand/*.h: individual On-Demand classes, generically written.
|
||||||
* simdjson/generic/ondemand/dependencies.h: dependencies on common, non-implementation-specific simdjson classes. This will be included before including amalgamated.h.
|
* simdjson/generic/ondemand/dependencies.h: dependencies on common, non-implementation-specific simdjson classes. This will be included before including amalgamated.h.
|
||||||
* simdjson/generic/ondemand/amalgamated.h: all generic ondemand classes for an implementation.
|
* simdjson/generic/ondemand/amalgamated.h: all generic ondemand classes for an implementation.
|
||||||
* **src:** The source files for non-inlined functionality (e.g. the architecture-specific parser
|
* **src:** The source files for non-inlined functionality (e.g. the architecture-specific parser
|
||||||
@@ -99,7 +99,7 @@ simdjson's source structure, from the top level, looks like this:
|
|||||||
* *.cpp: other misc. implementations, such as `simdjson::implementation` and the minifier.
|
* *.cpp: other misc. implementations, such as `simdjson::implementation` and the minifier.
|
||||||
* arm64|fallback|haswell|icelake|ppc64|westmere.cpp: Architecture-specific parser implementations.
|
* arm64|fallback|haswell|icelake|ppc64|westmere.cpp: Architecture-specific parser implementations.
|
||||||
* generic/*.h: `simdjson::<implementation>` namespace. Generic implementation of the parser, particularly the `dom_parser_implementation`.
|
* generic/*.h: `simdjson::<implementation>` namespace. Generic implementation of the parser, particularly the `dom_parser_implementation`.
|
||||||
* generic/stage1/*.h: `simdjson::<implementation>::stage1` namespace. Generic implementation of the simd-heavy tokenizer/indexer pass of the simdjson parser. Used for the On Demand interface
|
* generic/stage1/*.h: `simdjson::<implementation>::stage1` namespace. Generic implementation of the simd-heavy tokenizer/indexer pass of the simdjson parser. Used for the On-Demand interface
|
||||||
* generic/stage2/*.h: `simdjson::<implementation>::stage2` namespace. Generic implementation of the tape creator, which consumes the index from stage 1 and actually parses numbers and string and such. Used for the DOM interface.
|
* generic/stage2/*.h: `simdjson::<implementation>::stage2` namespace. Generic implementation of the tape creator, which consumes the index from stage 1 and actually parses numbers and string and such. Used for the DOM interface.
|
||||||
|
|
||||||
Other important files and directories:
|
Other important files and directories:
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ Real-world usage
|
|||||||
- [Meta Velox](https://velox-lib.io)
|
- [Meta Velox](https://velox-lib.io)
|
||||||
- [Google Pax](https://github.com/google/paxml)
|
- [Google Pax](https://github.com/google/paxml)
|
||||||
- [milvus](https://github.com/milvus-io/milvus)
|
- [milvus](https://github.com/milvus-io/milvus)
|
||||||
|
- [QuestDB](https://questdb.io/blog/questdb-release-8-0-3/)
|
||||||
- [Clang Build Analyzer](https://github.com/aras-p/ClangBuildAnalyzer)
|
- [Clang Build Analyzer](https://github.com/aras-p/ClangBuildAnalyzer)
|
||||||
- [Shopify HeapProfiler](https://github.com/Shopify/heap-profiler)
|
- [Shopify HeapProfiler](https://github.com/Shopify/heap-profiler)
|
||||||
- [StarRocks](https://github.com/StarRocks/starrocks)
|
- [StarRocks](https://github.com/StarRocks/starrocks)
|
||||||
@@ -179,7 +180,7 @@ The simdjson library takes advantage of modern microarchitectures, parallelizing
|
|||||||
instructions, reducing branch misprediction, and reducing data dependency to take advantage of each
|
instructions, reducing branch misprediction, and reducing data dependency to take advantage of each
|
||||||
CPU's multiple execution cores.
|
CPU's multiple execution cores.
|
||||||
|
|
||||||
Our default front-end is called On Demand, and we wrote a paper about it:
|
Our default front-end is called On-Demand, and we wrote a paper about it:
|
||||||
|
|
||||||
- John Keiser, Daniel Lemire, [On-Demand JSON: A Better Way to Parse Documents?](http://arxiv.org/abs/2312.17149), Software: Practice and Experience 54 (6), 2024.
|
- John Keiser, Daniel Lemire, [On-Demand JSON: A Better Way to Parse Documents?](http://arxiv.org/abs/2312.17149), Software: Practice and Experience 54 (6), 2024.
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ struct nlohmann_json {
|
|||||||
auto root = nlohmann::json::parse(json.data(), json.data() + json.size());
|
auto root = nlohmann::json::parse(json.data(), json.data() + json.size());
|
||||||
for (auto tweet : root["statuses"]) {
|
for (auto tweet : root["statuses"]) {
|
||||||
if (tweet["id"] == find_id) {
|
if (tweet["id"] == find_id) {
|
||||||
result = tweet["text"];
|
result = to_string(tweet["text"]);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class OnDemand {
|
|||||||
public:
|
public:
|
||||||
OnDemand() {
|
OnDemand() {
|
||||||
if(!displayed_implementation) {
|
if(!displayed_implementation) {
|
||||||
std::cout << "On Demand implementation: " << builtin_implementation()->name() << std::endl;
|
std::cout << "On-Demand implementation: " << builtin_implementation()->name() << std::endl;
|
||||||
displayed_implementation = true;
|
displayed_implementation = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ struct nlohmann_json {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result.text = top_tweet["text"];
|
result.text = to_string(top_tweet["text"]);
|
||||||
result.screen_name = top_tweet["user"]["screen_name"];
|
result.screen_name = to_string(top_tweet["user"]["screen_name"]);
|
||||||
return result.retweet_count != -1;
|
return result.retweet_count != -1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Vendored
+1
-1
@@ -155,6 +155,6 @@ if(SIMDJSON_CXXOPTS)
|
|||||||
set_off(CXXOPTS_BUILD_TESTS)
|
set_off(CXXOPTS_BUILD_TESTS)
|
||||||
set_off(CXXOPTS_ENABLE_INSTALL)
|
set_off(CXXOPTS_ENABLE_INSTALL)
|
||||||
|
|
||||||
import_dependency(cxxopts jarro2783/cxxopts 794c975)
|
import_dependency(cxxopts jarro2783/cxxopts 5965670)
|
||||||
add_dependency(cxxopts)
|
add_dependency(cxxopts)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
+334
-107
@@ -17,6 +17,8 @@ An overview of what you need to know to use simdjson, with examples.
|
|||||||
- [Using the parsed JSON](#using-the-parsed-json)
|
- [Using the parsed JSON](#using-the-parsed-json)
|
||||||
- [Using the parsed JSON: additional examples](#using-the-parsed-json-additional-examples)
|
- [Using the parsed JSON: additional examples](#using-the-parsed-json-additional-examples)
|
||||||
- [Adding support for custom types](#adding-support-for-custom-types)
|
- [Adding support for custom types](#adding-support-for-custom-types)
|
||||||
|
- [1. Specialize `simdjson::ondemand::value::get` to get custom types (pre-C++20)](#1-specialize-simdjsonondemandvalueget-to-get-custom-types-pre-c20)
|
||||||
|
- [2. Use `tag_invoke` for custom types (C++20)](#2-use-tag_invoke-for-custom-types-c20)
|
||||||
- [Minifying JSON strings without parsing](#minifying-json-strings-without-parsing)
|
- [Minifying JSON strings without parsing](#minifying-json-strings-without-parsing)
|
||||||
- [UTF-8 validation (alone)](#utf-8-validation-alone)
|
- [UTF-8 validation (alone)](#utf-8-validation-alone)
|
||||||
- [JSON Pointer](#json-pointer)
|
- [JSON Pointer](#json-pointer)
|
||||||
@@ -229,16 +231,16 @@ codepage, and they may call SetFileApisToOEM accordingly.
|
|||||||
Documents are iterators
|
Documents are iterators
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
The simdjson library relies on an approach to parsing JSON that we call "On Demand".
|
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.
|
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
|
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
|
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,
|
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
|
it lets you parse values as you use them. And particularly, it lets you *skip* values you do not want
|
||||||
to use. On Demand is also ideally suited when you want to capture part of the document without parsing it
|
to use. On-Demand is also ideally suited when you want to capture part of the document without parsing it
|
||||||
immediately (e.g., see [General direct access to the raw JSON string](#general-direct-access-to-the-raw-json-string)).
|
immediately (e.g., see [General direct access to the raw JSON string](#general-direct-access-to-the-raw-json-string)).
|
||||||
|
|
||||||
We refer to "On Demand" as a front-end component since it is an interface between the
|
We refer to "On-Demand" as a front-end component since it is an interface between the
|
||||||
low-level parsing functions and the user. It hides much of the complexity of parsing JSON
|
low-level parsing functions and the user. It hides much of the complexity of parsing JSON
|
||||||
documents.
|
documents.
|
||||||
|
|
||||||
@@ -344,7 +346,7 @@ floating-point values followed by an integer.
|
|||||||
|
|
||||||
We invite you to keep the following rules in mind:
|
We invite you to keep the following rules in mind:
|
||||||
1. While you are accessing the document, the `document` instance should remain in scope: it is your "iterator" which keeps track of where you are in the JSON document. By design, there is one and only one `document` instance per JSON document.
|
1. While you are accessing the document, the `document` instance should remain in scope: it is your "iterator" which keeps track of where you are in the JSON document. By design, there is one and only one `document` instance per JSON document.
|
||||||
2. Because On Demand is really just an iterator, you must fully consume the current object or array before accessing a sibling object or array.
|
2. Because On-Demand is really just an iterator, you must fully consume the current object or array before accessing a sibling object or array.
|
||||||
3. Values can only be consumed once, you should get the values and store them if you plan to need them multiple times. You are expected to access the keys of an object just once. You are expected to go through the values of an array just once.
|
3. Values can only be consumed once, you should get the values and store them if you plan to need them multiple times. You are expected to access the keys of an object just once. You are expected to go through the values of an array just once.
|
||||||
|
|
||||||
The simdjson library makes generous use of `std::string_view` instances. If you are unfamiliar
|
The simdjson library makes generous use of `std::string_view` instances. If you are unfamiliar
|
||||||
@@ -357,7 +359,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
|||||||
|
|
||||||
* **Validate What You Use:** When calling `iterate`, the document is quickly indexed. If it is
|
* **Validate What You Use:** When calling `iterate`, the document is quickly indexed. If it is
|
||||||
not a valid Unicode (UTF-8) string or if there is an unclosed string, an error may be reported right away.
|
not a valid Unicode (UTF-8) string or if there is an unclosed string, an error may be reported right away.
|
||||||
However, it is not fully validated. On Demand only fully validates the values you use and the
|
However, it is not fully validated. On-Demand only fully validates the values you use and the
|
||||||
structure leading to it. It means that at every step as you traverse the document, you may encounter an error. You can handle errors either with exceptions or with error codes.
|
structure leading to it. It means that at every step as you traverse the document, you may encounter an error. You can handle errors either with exceptions or with error codes.
|
||||||
* **Extracting Values:** You can cast a JSON element to a native type:
|
* **Extracting Values:** You can cast a JSON element to a native type:
|
||||||
`double(element)`. This works for `std::string_view`, double, uint64_t, int64_t, bool,
|
`double(element)`. This works for `std::string_view`, double, uint64_t, int64_t, bool,
|
||||||
@@ -407,7 +409,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
|||||||
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`. This will
|
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`. This will
|
||||||
scan through the object looking for the field with the matching string, doing a character-by-character
|
scan through the object looking for the field with the matching string, doing a character-by-character
|
||||||
comparison. It may generate the error `simdjson::NO_SUCH_FIELD` if there is no such key in the object, it may throw an exception (see [Error handling](#error-handling)). For efficiency reason, you should avoid looking up the same field repeatedly: e.g., do
|
comparison. It may generate the error `simdjson::NO_SUCH_FIELD` if there is no such key in the object, it may throw an exception (see [Error handling](#error-handling)). For efficiency reason, you should avoid looking up the same field repeatedly: e.g., do
|
||||||
not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. For best performance, you should try to query the keys in the same order they appear in the document. If you need several keys and you cannot predict the order they will appear in, it is recommended to iterate through all keys `for(auto field : object) {...}`. Generally, you should not mix and match iterating through an object (`for(auto field : object) {...}`) and key accesses (`object["foo"]`): if you need to iterate through an object after a key access, you need to call `reset()` on the object. Keep in mind that On Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if (!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsibility as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an
|
not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. For best performance, you should try to query the keys in the same order they appear in the document. If you need several keys and you cannot predict the order they will appear in, it is recommended to iterate through all keys `for(auto field : object) {...}`. Generally, you should not mix and match iterating through an object (`for(auto field : object) {...}`) and key accesses (`object["foo"]`): if you need to iterate through an object after a key access, you need to call `reset()` on the object. Whenever you call `reset()`, you need to keep in mind that though you can iterate over the array repeatedly, values should be consumedonly once (e.g., repeatedly calling `unescaped_key()` on the same key is forbidden). Keep in mind that On-Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if (!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsibility as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an
|
||||||
object twice: `std::string_view(object["foo"]` followed by `std::string_view(object["foo"]` then your code
|
object twice: `std::string_view(object["foo"]` followed by `std::string_view(object["foo"]` then your code
|
||||||
is in error. Furthermore, you can only consume one field at a time, on the same object. The
|
is in error. Furthermore, you can only consume one field at a time, on the same object. The
|
||||||
value instance you get from `content["bids"]` becomes invalid when you call `content["asks"]`.
|
value instance you get from `content["bids"]` becomes invalid when you call `content["asks"]`.
|
||||||
@@ -526,7 +528,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
|||||||
double value;
|
double value;
|
||||||
auto doc = parser.iterate(abstract_json);
|
auto doc = parser.iterate(abstract_json);
|
||||||
auto error = doc["str"]["123"]["abc"].get(value);
|
auto error = doc["str"]["123"]["abc"].get(value);
|
||||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
|
||||||
cout << value << endl; // Prints 3.14
|
cout << value << endl; // Prints 3.14
|
||||||
```
|
```
|
||||||
This examples also show how we can string several operations and only check for the error once, a strategy we call *error chaining*.
|
This examples also show how we can string several operations and only check for the error once, a strategy we call *error chaining*.
|
||||||
@@ -788,6 +790,18 @@ for (ondemand::object points : parser.iterate(points_json)) {
|
|||||||
Adding support for custom types
|
Adding support for custom types
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
There are 2 main ways provided by simdjson to deserialize a value into a custom type:
|
||||||
|
|
||||||
|
1. Provide a [**template specialization** for member functions](https://en.cppreference.com/w/cpp/language/template_specialization#Members_of_specializations)
|
||||||
|
1. Specialize `simdjson::ondemand::document::get` for the whole document
|
||||||
|
2. Specialize `simdjson::ondemand::value::get` for each value
|
||||||
|
2. Using `tag_invoke` *(the recommended way if your system supports C++20 or better)*
|
||||||
|
|
||||||
|
We describe both of them in the following sections. Most users who have systems compatible with
|
||||||
|
C++20 or better should skip ahead to [using `tag_invoke` for custom types (C++20)](#2-use-tag_invoke-for-custom-types-c20) as it is more powerful and simpler.
|
||||||
|
|
||||||
|
### 1. Specialize `simdjson::ondemand::value::get` to get custom types (pre-C++20)
|
||||||
|
|
||||||
Suppose you have your own types, such as a `Car` struct:
|
Suppose you have your own types, such as a `Car` struct:
|
||||||
|
|
||||||
```C++
|
```C++
|
||||||
@@ -821,9 +835,15 @@ type:
|
|||||||
```
|
```
|
||||||
|
|
||||||
We may do so by providing additional template definitions to the `ondemand::value` type.
|
We may do so by providing additional template definitions to the `ondemand::value` type.
|
||||||
We may start by providing a definition for `std::vector<double>` as follows:
|
We may start by providing a definition for `std::vector<double>` as follows. Observe
|
||||||
|
how we guard the code with `#if !SIMDJSON_SUPPORTS_DESERIALIZATION`: that is because the necessary code
|
||||||
|
is automatically provided by simdjson if C++20 (and concepts) are available.
|
||||||
|
See [Use `tag_invoke` for custom types](#2-use-tag_invoke-for-custom-types-c20) if you have
|
||||||
|
C++20 support.
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
|
#if !SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
// The code is unnecessary with C++20:
|
||||||
template <>
|
template <>
|
||||||
simdjson_inline simdjson_result<std::vector<double>>
|
simdjson_inline simdjson_result<std::vector<double>>
|
||||||
simdjson::ondemand::value::get() noexcept {
|
simdjson::ondemand::value::get() noexcept {
|
||||||
@@ -839,6 +859,7 @@ simdjson::ondemand::value::get() noexcept {
|
|||||||
}
|
}
|
||||||
return vec;
|
return vec;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
```
|
```
|
||||||
|
|
||||||
We may then provide support for our `Car` struct:
|
We may then provide support for our `Car` struct:
|
||||||
@@ -850,26 +871,10 @@ simdjson_inline simdjson_result<Car> simdjson::ondemand::value::get() noexcept {
|
|||||||
auto error = get_object().get(obj);
|
auto error = get_object().get(obj);
|
||||||
if (error) { return error; }
|
if (error) { return error; }
|
||||||
Car car;
|
Car car;
|
||||||
// Instead of repeatedly obj["something"], we iterate through the object which
|
if((error = obj["make"].get_string(car.make))) { return error; }
|
||||||
// we expect to be faster.
|
if((error = obj["model"].get_string(car.model))) { return error; }
|
||||||
for (auto field : obj) {
|
if((error = obj["year"].get_int64().get(car.year))) { return error; }
|
||||||
raw_json_string key;
|
if((error = obj["tire_pressure"].get<std::vector<double>>().get(car.tire_pressure))) { return error; }
|
||||||
error = field.key().get(key);
|
|
||||||
if (error) { return error; }
|
|
||||||
if (key == "make") {
|
|
||||||
error = field.value().get_string(car.make);
|
|
||||||
if (error) { return error; }
|
|
||||||
} else if (key == "model") {
|
|
||||||
error = field.value().get_string(car.model);
|
|
||||||
if (error) { return error; }
|
|
||||||
} else if (key == "year") {
|
|
||||||
error = field.value().get_int64().get(car.year);
|
|
||||||
if (error) { return error; }
|
|
||||||
} else if (key == "tire_pressure") {
|
|
||||||
error = field.value().get<std::vector<double>>().get(car.tire_pressure);
|
|
||||||
if (auto error) { return error; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return car;
|
return car;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -893,6 +898,8 @@ struct Car {
|
|||||||
std::vector<double> tire_pressure;
|
std::vector<double> tire_pressure;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if !SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
// This code is not necessary if you have a C++20 compliant system:
|
||||||
template <>
|
template <>
|
||||||
simdjson_inline simdjson_result<std::vector<double>>
|
simdjson_inline simdjson_result<std::vector<double>>
|
||||||
simdjson::ondemand::value::get() noexcept {
|
simdjson::ondemand::value::get() noexcept {
|
||||||
@@ -908,7 +915,7 @@ simdjson::ondemand::value::get() noexcept {
|
|||||||
}
|
}
|
||||||
return vec;
|
return vec;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
simdjson_inline simdjson_result<Car> simdjson::ondemand::value::get() noexcept {
|
simdjson_inline simdjson_result<Car> simdjson::ondemand::value::get() noexcept {
|
||||||
@@ -916,26 +923,10 @@ simdjson_inline simdjson_result<Car> simdjson::ondemand::value::get() noexcept {
|
|||||||
auto error = get_object().get(obj);
|
auto error = get_object().get(obj);
|
||||||
if (error) { return error; }
|
if (error) { return error; }
|
||||||
Car car;
|
Car car;
|
||||||
// Instead of repeatedly obj["something"], we iterate through the object which
|
if((error = obj["make"].get_string(car.make))) { return error; }
|
||||||
// we expect to be faster.
|
if((error = obj["model"].get_string(car.model))) { return error; }
|
||||||
for (auto field : obj) {
|
if((error = obj["year"].get_int64().get(car.year))) { return error; }
|
||||||
raw_json_string key;
|
if((error = obj["tire_pressure"].get<std::vector<double>>().get(car.tire_pressure))) { return error; }
|
||||||
error = field.key().get(key);
|
|
||||||
if (error) { return error; }
|
|
||||||
if (key == "make") {
|
|
||||||
error = field.value().get_string(car.make);
|
|
||||||
if (error) { return error; }
|
|
||||||
} else if (key == "model") {
|
|
||||||
error = field.value().get_string(car.model);
|
|
||||||
if (error) { return error; }
|
|
||||||
} else if (key == "year") {
|
|
||||||
error = field.value().get_int64().get(car.year);
|
|
||||||
if (error) { return error; }
|
|
||||||
} else if (key == "tire_pressure") {
|
|
||||||
error = field.value().get<std::vector<double>>().get(car.tire_pressure);
|
|
||||||
if (error) { return error; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return car;
|
return car;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -950,15 +941,14 @@ int main(void) {
|
|||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
ondemand::document doc = parser.iterate(json);
|
ondemand::document doc = parser.iterate(json);
|
||||||
for (auto val : doc) {
|
for (auto val : doc) {
|
||||||
Car c(val);
|
Car c(val); // an exception may be thrown
|
||||||
std::cout << c.make << std::endl;
|
std::cout << c.make << std::endl;
|
||||||
}
|
}
|
||||||
direct();
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Observe that we require an explicit cast (`Car c(val)` instead of `for (Car c : doc) {`): it is by design.
|
Observe that we require an explicit cast (`Car c(val)` instead of `for (Car c : doc) {`): it is by design. We require explicit casting.
|
||||||
|
|
||||||
If you prefer to avoid exceptions, you may modify the `main` function as follows:
|
If you prefer to avoid exceptions, you may modify the `main` function as follows:
|
||||||
|
|
||||||
@@ -974,11 +964,11 @@ int main(void) {
|
|||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
ondemand::document doc;
|
ondemand::document doc;
|
||||||
auto error = parser.iterate(json).get(doc);
|
auto error = parser.iterate(json).get(doc);
|
||||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
|
||||||
for (auto val : doc) {
|
for (auto val : doc) {
|
||||||
Car c;
|
Car c;
|
||||||
error = val.get<Car>().get(c);
|
error = val.get<Car>().get(c);
|
||||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
|
||||||
std::cout << c.make << std::endl;
|
std::cout << c.make << std::endl;
|
||||||
}
|
}
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
@@ -1009,49 +999,49 @@ struct Car {
|
|||||||
std::vector<double> tire_pressure;
|
std::vector<double> tire_pressure;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if !SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
// This code is not necessary if you have a C++20 compliant system:
|
||||||
template <>
|
template <>
|
||||||
simdjson_inline simdjson_result<std::vector<double>>
|
simdjson_inline simdjson_result<std::vector<double>>
|
||||||
simdjson::ondemand::value::get() noexcept {
|
simdjson::ondemand::value::get() noexcept {
|
||||||
ondemand::array array;
|
ondemand::array array;
|
||||||
if (auto error = get_array().get(array); error) { return error; }
|
auto error = get_array().get(array);
|
||||||
|
if (error) { return error; }
|
||||||
std::vector<double> vec;
|
std::vector<double> vec;
|
||||||
for (auto v : array) {
|
for (auto v : array) {
|
||||||
double val;
|
double val;
|
||||||
if (auto error = v.get_double().get(val); error) { return error; }
|
error = v.get_double().get(val);
|
||||||
|
if (error) { return error; }
|
||||||
vec.push_back(val);
|
vec.push_back(val);
|
||||||
}
|
}
|
||||||
return vec;
|
return vec;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
simdjson_inline simdjson_result<Car> simdjson::ondemand::document::get() & noexcept {
|
simdjson_inline simdjson_result<Car> simdjson::ondemand::document::get() & noexcept {
|
||||||
ondemand::object obj;
|
ondemand::object obj;
|
||||||
auto error = get_object().get(obj);
|
auto error = get_object().get(obj);
|
||||||
if (error) {
|
if (error) { return error; }
|
||||||
return error;
|
|
||||||
}
|
|
||||||
Car car;
|
Car car;
|
||||||
// Instead of repeatedly obj["something"], we iterate through the object which
|
if((error = obj["make"].get_string(car.make))) { return error; }
|
||||||
// we expect to be faster.
|
if((error = obj["model"].get_string(car.model))) { return error; }
|
||||||
for (auto field : obj) {
|
if((error = obj["year"].get_int64().get(car.year))) { return error; }
|
||||||
raw_json_string key;
|
if((error = obj["tire_pressure"].get<std::vector<double>>().get(car.tire_pressure))) { return error; }
|
||||||
error = field.key().get(key);
|
return car;
|
||||||
if (error) { return error; }
|
}
|
||||||
if (key == "make") {
|
|
||||||
error = field.value().get_string(car.make);
|
|
||||||
if (error) { return error; }
|
template <>
|
||||||
} else if (key == "model") {
|
simdjson_inline simdjson_result<Car> simdjson::ondemand::document::get() noexcept {
|
||||||
error = field.value().get_string(car.model);
|
ondemand::object obj;
|
||||||
if (error) { return error; }
|
auto error = get_object().get(obj);
|
||||||
} else if (key == "year") {
|
if (error) { return error; }
|
||||||
error = field.value().get_int64().get(car.year);
|
Car car;
|
||||||
if (error) { return error; }
|
if((error = obj["make"].get_string(car.make))) { return error; }
|
||||||
} else if (key == "tire_pressure") {
|
if((error = obj["model"].get_string(car.model))) { return error; }
|
||||||
error = field.value().get<std::vector<double>>().get(car.tire_pressure);
|
if((error = obj["year"].get_int64().get(car.year))) { return error; }
|
||||||
if (error) { return error; }
|
if((error = obj["tire_pressure"].get<std::vector<double>>().get(car.tire_pressure))) { return error; }
|
||||||
}
|
|
||||||
}
|
|
||||||
return car;
|
return car;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1066,6 +1056,219 @@ int main(void) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 2. Use `tag_invoke` for custom types (C++20)
|
||||||
|
|
||||||
|
In C++20, the standard introduced the notion of *customization point*.
|
||||||
|
A customization point is a function or function object that can be customized for different types. It allows library authors to provide default behavior while giving users the ability to override this behavior for specific types.
|
||||||
|
|
||||||
|
A tag_invoke function serves as a mechanism for customization points. It is not directly part of the C++ standard library but is often used in libraries that implement customization points.
|
||||||
|
The tag_invoke function is typically a generic function that takes a tag type and additional arguments.
|
||||||
|
The first argument is usually a tag type (often an empty struct) that uniquely identifies the customization point (e.g., deserialization of custom types in simdjson). Users or library providers can specialize tag_invoke for their types by defining it in the appropriate namespace, often inline namespace.
|
||||||
|
|
||||||
|
|
||||||
|
If your system supports C++20, we recommend that you adopt the `tag_invoke` approach
|
||||||
|
instead to deserialize custom types. It may prove to be considerably simpler. When
|
||||||
|
simdjson detects the necessary support, it sets the `SIMDJSON_SUPPORTS_DESERIALIZATION` macro
|
||||||
|
to 1, otherwise it is set to 0.
|
||||||
|
|
||||||
|
Consider a custom class `Car`:
|
||||||
|
|
||||||
|
```C++
|
||||||
|
struct Car {
|
||||||
|
std::string make;
|
||||||
|
std::string model;
|
||||||
|
int year;
|
||||||
|
std::vector<float> tire_pressure;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Observe how we defined the class to use types that simdjson does not directly support (`float`, `int`).
|
||||||
|
With C++20 support, the library grabs from the JSON the generic type (`double`, `int`) and then it
|
||||||
|
casts it automatically.
|
||||||
|
|
||||||
|
You may support deserializing directly from a JSON value or document to your own `Car` instance
|
||||||
|
by defining a single `tag_invoke` function:
|
||||||
|
|
||||||
|
|
||||||
|
```C++
|
||||||
|
namespace simdjson {
|
||||||
|
// This tag_invoke MUST be inside simdjson namespace
|
||||||
|
template <typename simdjson_value>
|
||||||
|
auto tag_invoke(deserialize_tag, simdjson_value &val, Car& car) {
|
||||||
|
ondemand::object obj;
|
||||||
|
auto error = val.get_object().get(obj);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["make"].get_string(car.make))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["model"].get_string(car.model))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["year"].get(car.year))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["tire_pressure"].get<std::vector<float>>().get(
|
||||||
|
car.tire_pressure))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return simdjson::SUCCESS;
|
||||||
|
}
|
||||||
|
} // namespace simdjson
|
||||||
|
```
|
||||||
|
|
||||||
|
Observe how we call `get<std::vector<float>>()` even though we never defined support
|
||||||
|
for `std::vector<float>` in the simdjson library: it is all automated thanks to C++20 concepts.
|
||||||
|
|
||||||
|
Importantly, the `tag_invoke` function must be inside the `simdjson` namespace.
|
||||||
|
Let us explain each argument of `tag_invoke` function.
|
||||||
|
|
||||||
|
- `simdjson::deserialize_tag`: it is the tag for Customization Point Object (CPO). You may often ignore this parameter. It is used to indicate that you mean to provide a deserialization function for simdjson.
|
||||||
|
- `var`: It receives automatically a `simdjson` value type (document, value, document_reference).
|
||||||
|
- The third parameter is an instance of the type that you want to support.
|
||||||
|
|
||||||
|
You can use it like so:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
simdjson::padded_string json =
|
||||||
|
R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] })"_padded;
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
Car c(doc);
|
||||||
|
std::cout << c.make << std::endl;
|
||||||
|
```
|
||||||
|
|
||||||
|
Observe how we first get an instance of `document` and then we cast.
|
||||||
|
|
||||||
|
You can also handle errors explicitly:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
Car c;
|
||||||
|
auto error = doc.get(c);
|
||||||
|
if(error) { std::cerr << simdjson::error_message(error); return false; }
|
||||||
|
std::cout << c.make << std::endl;
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also read instances of `Car` from an array or an object:
|
||||||
|
```cpp
|
||||||
|
simdjson::padded_string json =
|
||||||
|
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
])"_padded;
|
||||||
|
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
for (auto val : doc) {
|
||||||
|
Car c(val); // an exception may be thrown
|
||||||
|
std::cout << c.year << std::endl;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Observe how we first get a generic (`val`) which we cast to `Car`. It is by design: we require
|
||||||
|
explicit casting. The cast may throw an exception.
|
||||||
|
|
||||||
|
Once more, you can handle errors explicitly:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
for (auto val : doc) {
|
||||||
|
Car c;
|
||||||
|
auto error = val.get(c);
|
||||||
|
if(error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also use the custom `Car` type as part of a template such as `std::vector`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
std::vector<Car> cars(doc);
|
||||||
|
// visual studio users need an explicit call:
|
||||||
|
// std::vector<Car> cars = doc.get<std::vector<Car>>();
|
||||||
|
// because the compiler does not know whether to convert
|
||||||
|
// doc to an unsigned int or to a vector.
|
||||||
|
for(Car& c : cars) {
|
||||||
|
std::cout << c.year << std::endl;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, we support a wide range of standard templates such as
|
||||||
|
`std::vector`, `std::list`, `std::set`, `std::stack`, `std:queue`,
|
||||||
|
`std:deque`, `std::priority_queue`, `std::unordered_set`, `std::multiset`,
|
||||||
|
`std::unordered_multiset`, `std::unique_ptr`, `std::shared_ptr`, `std::optional`,
|
||||||
|
etc. They are handled automatically.
|
||||||
|
|
||||||
|
E.g., you can recover an `std::unique_ptr<Car>` like so:
|
||||||
|
```C++
|
||||||
|
int main() {
|
||||||
|
auto const json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] })"_padded;
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
std::unique_ptr<Car> c(doc);
|
||||||
|
std::cout << c->make << std::endl;
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You may also conditionally fill in `std::optional` values.
|
||||||
|
|
||||||
|
```C++
|
||||||
|
|
||||||
|
padded_string json =
|
||||||
|
R"( { "car1": { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] }
|
||||||
|
})"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc = parser.iterate(json);
|
||||||
|
std::optional<Car> car;
|
||||||
|
error = doc["key not found"].get<std::optional<Car>>().get(car);
|
||||||
|
// car has no value, error != simdjson::SUCCESS
|
||||||
|
error = doc["car1"].get<std::optional<Car>>().get(car);
|
||||||
|
// car has value Car{"Toyota", "Camry", 2018, {40.1f, 39.9f}}
|
||||||
|
// error is simdjson::SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
And so forth.
|
||||||
|
|
||||||
|
Advanced users may want to overwrite the defaults provided by the simdjson library.
|
||||||
|
Suppose for example that you want to construct an instance of `std::list<Car>`, but
|
||||||
|
you also want to filter out any car made by Toyota. You may provide your own
|
||||||
|
`tag_invoke` function:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
namespace simdjson {
|
||||||
|
// suppose we want to filter out all Toyotas
|
||||||
|
template <typename simdjson_value>
|
||||||
|
auto tag_invoke(deserialize_tag, simdjson_value &val, std::list<Car>& car) {
|
||||||
|
ondemand::array arr;
|
||||||
|
auto error = val.get_array().get(arr);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
for (auto v : arr) {
|
||||||
|
Car c;
|
||||||
|
if ((error = v.get<Car>().get(c))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if(c.make != "Toyota") {
|
||||||
|
car.push_back(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return simdjson::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
With this code, deserializing an `std::list<Car>` instance would capture only the cars
|
||||||
|
that are not made by Toyota.
|
||||||
|
|
||||||
|
|
||||||
Minifying JSON strings without parsing
|
Minifying JSON strings without parsing
|
||||||
----------------------
|
----------------------
|
||||||
@@ -1081,6 +1284,7 @@ In some cases, you may have valid JSON strings that you do not wish to parse but
|
|||||||
std::unique_ptr<char[]> buffer{new char[length]};
|
std::unique_ptr<char[]> buffer{new char[length]};
|
||||||
size_t new_length{}; // It will receive the minified length.
|
size_t new_length{}; // It will receive the minified length.
|
||||||
auto error = simdjson::minify(some_string, length, buffer.get(), new_length);
|
auto error = simdjson::minify(some_string, length, buffer.get(), new_length);
|
||||||
|
if(error) { std::cerr << simdjson::error_message(error); }
|
||||||
// The buffer variable now has "[1,2,3,4]" and new_length has value 9.
|
// The buffer variable now has "[1,2,3,4]" and new_length has value 9.
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -1107,9 +1311,9 @@ If you find yourself needing only fast Unicode functions, consider using the sim
|
|||||||
JSON Pointer
|
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. JSON pointer is supported by both the [DOM approach](https://github.com/simdjson/simdjson/blob/master/doc/dom.md#json-pointer) as well as the On Demand approach.
|
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. JSON pointer is supported by both the [DOM approach](https://github.com/simdjson/simdjson/blob/master/doc/dom.md#json-pointer) as well as the On-Demand approach.
|
||||||
|
|
||||||
**Note:** The On Demand implementation of JSON pointer relies on `find_field` which implies that it does not unescape keys when matching.
|
**Note:** The On-Demand implementation of JSON pointer relies on `find_field` which implies that it does not unescape keys when matching.
|
||||||
|
|
||||||
Consider the following example:
|
Consider the following example:
|
||||||
|
|
||||||
@@ -1278,7 +1482,7 @@ pair. You can retrieve the value with .get() without generating an exception, li
|
|||||||
```c++
|
```c++
|
||||||
ondemand::document doc;
|
ondemand::document doc;
|
||||||
auto error = parser.iterate(json).get(doc);
|
auto error = parser.iterate(json).get(doc);
|
||||||
if (error) { cerr << error << endl; exit(1); }
|
if(error) { std::cerr << simdjson::error_message(error); exit(1); }
|
||||||
```
|
```
|
||||||
|
|
||||||
When there is no error, the error code `simdjson::SUCCESS`is returned: it evaluates as false as a Boolean.
|
When there is no error, the error code `simdjson::SUCCESS`is returned: it evaluates as false as a Boolean.
|
||||||
@@ -1309,7 +1513,7 @@ bool simple_error_example() {
|
|||||||
auto error = doc["bad number"].get_double().get(x);
|
auto error = doc["bad number"].get_double().get(x);
|
||||||
// returns "simdjson::NUMBER_ERROR"
|
// returns "simdjson::NUMBER_ERROR"
|
||||||
if (error != SUCCESS) {
|
if (error != SUCCESS) {
|
||||||
std::cout << error << std::endl;
|
std::cerr << simdjson::error_message(error) << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::cout << "Got " << x << std::endl;
|
std::cout << "Got " << x << std::endl;
|
||||||
@@ -1343,7 +1547,7 @@ Notice how we can retrieve the exact error condition (in this instance `simdjson
|
|||||||
from the exception.
|
from the exception.
|
||||||
|
|
||||||
We can write a "quick start" example where we attempt to parse the following JSON file and access some data, without triggering exceptions:
|
We can write a "quick start" example where we attempt to parse the following JSON file and access some data, without triggering exceptions:
|
||||||
```JavaScript
|
```JSON
|
||||||
{
|
{
|
||||||
"statuses": [
|
"statuses": [
|
||||||
{
|
{
|
||||||
@@ -1362,7 +1566,6 @@ We can write a "quick start" example where we attempt to parse the following JSO
|
|||||||
Our program loads the file, selects value corresponding to key `"search_metadata"` which expected to be an object, and then
|
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.
|
it selects the key `"count"` within that object.
|
||||||
|
|
||||||
|
|
||||||
```C++
|
```C++
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "simdjson.h"
|
#include "simdjson.h"
|
||||||
@@ -1370,10 +1573,10 @@ it selects the key `"count"` within that object.
|
|||||||
int main(void) {
|
int main(void) {
|
||||||
simdjson::ondemand::parser parser;
|
simdjson::ondemand::parser parser;
|
||||||
auto error = padded_string::load("twitter.json").get(json);
|
auto error = padded_string::load("twitter.json").get(json);
|
||||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
|
||||||
simdjson::ondemand::document tweets;
|
simdjson::ondemand::document tweets;
|
||||||
error = parser.iterate(json).get(tweets);
|
error = parser.iterate(json).get(tweets);
|
||||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
|
||||||
simdjson::ondemand::value res;
|
simdjson::ondemand::value res;
|
||||||
error = tweets["search_metadata"]["count"].get(res);
|
error = tweets["search_metadata"]["count"].get(res);
|
||||||
if (error != SUCCESS) {
|
if (error != SUCCESS) {
|
||||||
@@ -1404,12 +1607,12 @@ int main(void) {
|
|||||||
simdjson::ondemand::document tweets;
|
simdjson::ondemand::document tweets;
|
||||||
padded_string json;
|
padded_string json;
|
||||||
auto error = padded_string::load("twitter.json").get(json);
|
auto error = padded_string::load("twitter.json").get(json);
|
||||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
|
||||||
error = parser.iterate(json).get(tweets);
|
error = parser.iterate(json).get(tweets);
|
||||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
|
||||||
uint64_t identifier;
|
uint64_t identifier;
|
||||||
error = tweets["statuses"].at(0)["id"].get(identifier);
|
error = tweets["statuses"].at(0)["id"].get(identifier);
|
||||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
|
||||||
std::cout << identifier << std::endl;
|
std::cout << identifier << std::endl;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -1433,40 +1636,40 @@ bool parse() {
|
|||||||
|
|
||||||
// Iterating through an array of objects
|
// Iterating through an array of objects
|
||||||
auto error = parser.iterate(cars_json).get(doc);
|
auto error = parser.iterate(cars_json).get(doc);
|
||||||
if (error) { std::cerr << error << std::endl; return false; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
|
||||||
ondemand::array cars; // invalid until the get() succeeds
|
ondemand::array cars; // invalid until the get() succeeds
|
||||||
error = doc.get_array().get(cars);
|
error = doc.get_array().get(cars);
|
||||||
|
|
||||||
for (auto car_value : cars) {
|
for (auto car_value : cars) {
|
||||||
ondemand::object car; // invalid until the get() succeeds
|
ondemand::object car; // invalid until the get() succeeds
|
||||||
error = car_value.get_object().get(car);
|
error = car_value.get_object().get(car);
|
||||||
if (error) { std::cerr << error << std::endl; return false; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
|
||||||
|
|
||||||
// Accessing a field by name
|
// Accessing a field by name
|
||||||
std::string_view make;
|
std::string_view make;
|
||||||
std::string_view model;
|
std::string_view model;
|
||||||
error = car["make"].get(make);
|
error = car["make"].get(make);
|
||||||
if (error) { std::cerr << error << std::endl; return false; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
|
||||||
error = car["model"].get(model);
|
error = car["model"].get(model);
|
||||||
if (error) { std::cerr << error << std::endl; return false; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
|
||||||
|
|
||||||
cout << "Make/Model: " << make << "/" << model << endl;
|
cout << "Make/Model: " << make << "/" << model << endl;
|
||||||
|
|
||||||
// Casting a JSON element to an integer
|
// Casting a JSON element to an integer
|
||||||
uint64_t year{};
|
uint64_t year{};
|
||||||
error = car["year"].get(year);
|
error = car["year"].get(year);
|
||||||
if (error) { std::cerr << error << std::endl; return false; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
|
||||||
cout << "- This car is " << 2020 - year << " years old." << endl;
|
cout << "- This car is " << 2020 - year << " years old." << endl;
|
||||||
|
|
||||||
// Iterating through an array of floats
|
// Iterating through an array of floats
|
||||||
double total_tire_pressure = 0;
|
double total_tire_pressure = 0;
|
||||||
ondemand::array pressures;
|
ondemand::array pressures;
|
||||||
error = car["tire_pressure"].get_array().get(pressures);
|
error = car["tire_pressure"].get_array().get(pressures);
|
||||||
if (error) { std::cerr << error << std::endl; return false; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
|
||||||
for (auto tire_pressure_value : pressures) {
|
for (auto tire_pressure_value : pressures) {
|
||||||
double tire_pressure;
|
double tire_pressure;
|
||||||
error = tire_pressure_value.get_double().get(tire_pressure);
|
error = tire_pressure_value.get_double().get(tire_pressure);
|
||||||
if (error) { std::cerr << error << std::endl; return false; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
|
||||||
total_tire_pressure += tire_pressure;
|
total_tire_pressure += tire_pressure;
|
||||||
}
|
}
|
||||||
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
|
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
|
||||||
@@ -1588,7 +1791,7 @@ auto doc = parser.iterate(broken_json);
|
|||||||
int64_t i;
|
int64_t i;
|
||||||
auto error = doc["integer"].get_int64().get(i); // Expect to get integer from "integer" key, but get TAPE_ERROR
|
auto error = doc["integer"].get_int64().get(i); // Expect to get integer from "integer" key, but get TAPE_ERROR
|
||||||
if (error) {
|
if (error) {
|
||||||
std::cout << error << std::endl; // Prints TAPE_ERROR error message
|
std::cerr << simdjson::error_message(error) << std::endl; // Prints TAPE_ERROR error message
|
||||||
// Recover a pointer to the location of the first error:
|
// Recover a pointer to the location of the first error:
|
||||||
const char * ptr;
|
const char * ptr;
|
||||||
doc.current_location().get(ptr);
|
doc.current_location().get(ptr);
|
||||||
@@ -1625,7 +1828,7 @@ auto doc = parser.iterate(json);
|
|||||||
int64_t i;
|
int64_t i;
|
||||||
auto error = doc["integer"].get_int64().get(i); // Incorrect call on array, INCORRECT_TYPE error
|
auto error = doc["integer"].get_int64().get(i); // Incorrect call on array, INCORRECT_TYPE error
|
||||||
if (error) {
|
if (error) {
|
||||||
std::cout << error << std::endl; // Prints INCORRECT_TYPE error message
|
std::cerr << simdjson::error_message(error) << std::endl; // Prints INCORRECT_TYPE error message
|
||||||
std::cout<< doc.current_location() << std::endl; // Prints "[1,2,3] " (location of INCORRECT_TYPE error)
|
std::cout<< doc.current_location() << std::endl; // Prints "[1,2,3] " (location of INCORRECT_TYPE error)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -1727,7 +1930,7 @@ before printout the data.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Performance note: the On Demand front-end does not materialize the parsed numbers and other values. If you are accessing everything twice, you may need to parse them twice. Thus the rewind functionality is best suited for cases where the first pass only scans the structure of the document.
|
Performance note: the On-Demand front-end does not materialize the parsed numbers and other values. If you are accessing everything twice, you may need to parse them twice. Thus the rewind functionality is best suited for cases where the first pass only scans the structure of the document.
|
||||||
|
|
||||||
Both arrays and objects have a similar method `reset()`. It is similar
|
Both arrays and objects have a similar method `reset()`. It is similar
|
||||||
to the document `rewind()` method, except that it does not rewind the
|
to the document `rewind()` method, except that it does not rewind the
|
||||||
@@ -1766,7 +1969,7 @@ for (auto doc : docs) {
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Unlike `parser.iterate`, `parser.iterate_many` may parse "on demand" (lazily). That is, no parsing may have been done before you enter the loop
|
Unlike `parser.iterate`, `parser.iterate_many` may parse "On-Demand" (lazily). That is, no parsing may have been done before you enter the loop
|
||||||
`for (auto doc : docs) {` and you should expect the parser to only ever fully parse one JSON document at a time.
|
`for (auto doc : docs) {` and you should expect the parser to only ever fully parse one JSON document at a time.
|
||||||
|
|
||||||
As with `parser.iterate`, when calling `parser.iterate_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.iterate_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.
|
As with `parser.iterate`, when calling `parser.iterate_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.iterate_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.
|
||||||
@@ -1798,7 +2001,7 @@ for (auto doc: stream) {
|
|||||||
error = doc.at_pointer("/4").get(val);
|
error = doc.at_pointer("/4").get(val);
|
||||||
// error == simdjson::CAPACITY
|
// error == simdjson::CAPACITY
|
||||||
if (error) {
|
if (error) {
|
||||||
std::cerr << error << std::endl;
|
std::cerr << simdjson::error_message(error) << std::endl;
|
||||||
// We left 293 bytes unprocessed at the tail end of the input.
|
// We left 293 bytes unprocessed at the tail end of the input.
|
||||||
std::cout << " unprocessed bytes at the end: " << stream.truncated_bytes() << std::endl;
|
std::cout << " unprocessed bytes at the end: " << stream.truncated_bytes() << std::endl;
|
||||||
break;
|
break;
|
||||||
@@ -2297,7 +2500,7 @@ We built simdjson with thread safety in mind.
|
|||||||
The simdjson library is single-threaded except for [`iterate_many`](iterate_many.md) and [`parse_many`](parse_many.md) which may use secondary threads under their control when the library is compiled with thread support.
|
The simdjson library is single-threaded except for [`iterate_many`](iterate_many.md) and [`parse_many`](parse_many.md) which may use secondary threads under their control when the library is compiled with thread support.
|
||||||
|
|
||||||
|
|
||||||
We recommend using one `parser` object per thread. When using the On Demand front-end (our default), you should access the `document` instances in a single-threaded manner since it
|
We recommend using one `parser` object per thread. When using the On-Demand front-end (our default), you should access the `document` instances in a single-threaded manner since it
|
||||||
acts as an iterator (and is therefore not thread safe).
|
acts as an iterator (and is therefore not thread safe).
|
||||||
|
|
||||||
The CPU detection, which runs the first time parsing is attempted and switches to the fastest
|
The CPU detection, which runs the first time parsing is attempted and switches to the fastest
|
||||||
@@ -2623,12 +2826,37 @@ int main(void) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
* Example 4: Value capture with `std::string_view` instances
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
void example() {
|
||||||
|
ondemand::parser parser;
|
||||||
|
const padded_string json = R"({ "parent": {"child1": {"name": "John"} , "child2": {"name": "Daniel"}} })"_padded;
|
||||||
|
auto doc = parser.iterate(json);
|
||||||
|
ondemand::object parent = doc["parent"];
|
||||||
|
// parent owns the focus
|
||||||
|
ondemand::object c1 = parent["child1"];
|
||||||
|
// c1 owns the focus
|
||||||
|
//
|
||||||
|
std::string_view as1 = c1["name"];
|
||||||
|
// We have that as1 == "John", as long as 'parser' and 'json' live
|
||||||
|
// c2 attempts to grab the focus from parent but fails
|
||||||
|
ondemand::object c2 = parent["child2"];
|
||||||
|
// c2 owns the focus, at this point c1 is invalid
|
||||||
|
std::string_view as2 = c2["name"];
|
||||||
|
// We have that as2 == "Daniel", as long as 'parser' and 'json' live
|
||||||
|
std::cout << as1 << " " << as2 << std::endl; // prints John Daniel
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Performance tips
|
Performance tips
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
|
||||||
- Read [our performance notes](performance.md) for advanced topics.
|
- Read [our performance notes](performance.md) for advanced topics.
|
||||||
- The On Demand front-end works best when doing a single pass over the input: avoid calling `count_elements`, `rewind` and similar methods.
|
- To better understand the operation of your On-Demand parser, and whether it is performing as well as you think it should be, there is a logger feature built in to simdjson! To use it, define the pre-processor directive `SIMDJSON_VERBOSE_LOGGING` prior to including the `simdjson.h` header, which enables logging in simdjson. Run your code. It may generate a lot of logging output; adding printouts from your application that show each section may be helpful. The log's output will show step-by-step information on state, buffer pointer position, depth, and key retrieval status. Importantly, unless `SIMDJSON_VERBOSE_LOGGING` is defined, logging is entirely disabled and thus carries no overhead.
|
||||||
|
- The On-Demand front-end works best when doing a single pass over the input: avoid calling `count_elements`, `rewind`, `reset` and similar methods.
|
||||||
- If you are familiar with assembly language, you may use the online tool godbolt to explore the compiled code. The following example may work: [https://godbolt.org/z/xE4GWs573](https://godbolt.org/z/xE4GWs573).
|
- If you are familiar with assembly language, you may use the online tool godbolt to explore the compiled code. The following example may work: [https://godbolt.org/z/xE4GWs573](https://godbolt.org/z/xE4GWs573).
|
||||||
- Given a field `field` in an object, calling `field.key()` is often faster than `field.unescaped_key()` so if you do not need an unescaped `std::string_view` instance, prefer `field.key()`. Similarly, we expect `field.escaped_key()` to be faster than `field.unescaped_key()` even though both return a `std::string_view` instance.
|
- Given a field `field` in an object, calling `field.key()` is often faster than `field.unescaped_key()` so if you do not need an unescaped `std::string_view` instance, prefer `field.key()`. Similarly, we expect `field.escaped_key()` to be faster than `field.unescaped_key()` even though both return a `std::string_view` instance.
|
||||||
- For release builds, we recommend setting `NDEBUG` pre-processor directive when compiling the `simdjson` library. Importantly, using the optimization flags `-O2` or `-O3` under GCC and LLVM clang does not set the `NDEBUG` directive, you must set it manually (e.g., `-DNDEBUG`).
|
- For release builds, we recommend setting `NDEBUG` pre-processor directive when compiling the `simdjson` library. Importantly, using the optimization flags `-O2` or `-O3` under GCC and LLVM clang does not set the `NDEBUG` directive, you must set it manually (e.g., `-DNDEBUG`).
|
||||||
@@ -2648,7 +2876,6 @@ Performance tips
|
|||||||
std::string_view year = data["year"];
|
std::string_view year = data["year"];
|
||||||
std::string_view rating = data["rating"];
|
std::string_view rating = data["rating"];
|
||||||
```
|
```
|
||||||
- To better understand the operation of your On Demand parser, and whether it is performing as well as you think it should be, there is a logger feature built in to simdjson! To use it, define the pre-processor directive `SIMDJSON_VERBOSE_LOGGING` prior to including the `simdjson.h` header, which enables logging in simdjson. Run your code. It may generate a lot of logging output; adding printouts from your application that show each section may be helpful. The log's output will show step-by-step information on state, buffer pointer position, depth, and key retrieval status. Importantly, unless `SIMDJSON_VERBOSE_LOGGING` is defined, logging is entirely disabled and thus carries no overhead.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -3,7 +3,7 @@ The Document-Object-Model (DOM) front-end
|
|||||||
|
|
||||||
An overview of what you need to know to use simdjson, with examples.
|
An overview of what you need to know to use simdjson, with examples.
|
||||||
|
|
||||||
* [DOM vs On Demand](#dom-vs-on-demand)
|
* [DOM vs On-Demand](#dom-vs-on-demand)
|
||||||
* [The Basics: Loading and Parsing JSON Documents](#the-basics-loading-and-parsing-json-documents-using-the-dom-front-end)
|
* [The Basics: Loading and Parsing JSON Documents](#the-basics-loading-and-parsing-json-documents-using-the-dom-front-end)
|
||||||
* [Using the Parsed JSON](#using-the-parsed-json)
|
* [Using the Parsed JSON](#using-the-parsed-json)
|
||||||
* [C++17 Support](#c17-support)
|
* [C++17 Support](#c17-support)
|
||||||
@@ -18,7 +18,7 @@ An overview of what you need to know to use simdjson, with examples.
|
|||||||
* [Padding and Temporary Copies](#padding-and-temporary-copies)
|
* [Padding and Temporary Copies](#padding-and-temporary-copies)
|
||||||
* [Performance Tips](#performance-tips)
|
* [Performance Tips](#performance-tips)
|
||||||
|
|
||||||
DOM vs On Demand
|
DOM vs On-Demand
|
||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
|
|
||||||
The simdjson library offers two distinct approaches on how to access a JSON document. We support
|
The simdjson library offers two distinct approaches on how to access a JSON document. We support
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ Contents
|
|||||||
- [Use cases](#use-cases)
|
- [Use cases](#use-cases)
|
||||||
- [Tracking your position](#tracking-your-position)
|
- [Tracking your position](#tracking-your-position)
|
||||||
- [Incomplete streams](#incomplete-streams)
|
- [Incomplete streams](#incomplete-streams)
|
||||||
|
- [C++20 features](#c20-features)
|
||||||
|
|
||||||
Motivation
|
Motivation
|
||||||
-----------
|
-----------
|
||||||
@@ -288,3 +289,111 @@ string
|
|||||||
object
|
object
|
||||||
array
|
array
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
C++20 features
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
In C++20, the standard introduced the notion of *customization point*.
|
||||||
|
A customization point is a function or function object that can be customized for different types. It allows library authors to provide default behavior while giving users the ability to override this behavior for specific types.
|
||||||
|
|
||||||
|
A tag_invoke function serves as a mechanism for customization points. It is not directly part of the C++ standard library but is often used in libraries that implement customization points.
|
||||||
|
The tag_invoke function is typically a generic function that takes a tag type and additional arguments.
|
||||||
|
The first argument is usually a tag type (often an empty struct) that uniquely identifies the customization point (e.g., deserialization of custom types in simdjson). Users or library providers can specialize tag_invoke for their types by defining it in the appropriate namespace, often inline namespace.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
You can deserialize you own data structures conveniently if your system supports C++20.
|
||||||
|
When it is the case, the macro `SIMDJSON_SUPPORTS_DESERIALIZATION` will be set to 1 by
|
||||||
|
the simdjson library.
|
||||||
|
|
||||||
|
Consider a custom class `Car`:
|
||||||
|
|
||||||
|
```C++
|
||||||
|
struct Car {
|
||||||
|
std::string make;
|
||||||
|
std::string model;
|
||||||
|
int year;
|
||||||
|
std::vector<float> tire_pressure;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
You may support deserializing directly from a JSON value or document to your own `Car` instance
|
||||||
|
by defining a single `tag_invoke` function:
|
||||||
|
|
||||||
|
|
||||||
|
```C++
|
||||||
|
namespace simdjson {
|
||||||
|
// This tag_invoke MUST be inside simdjson namespace
|
||||||
|
template <typename simdjson_value>
|
||||||
|
auto tag_invoke(deserialize_tag, simdjson_value &val, Car& car) {
|
||||||
|
ondemand::object obj;
|
||||||
|
auto error = val.get_object().get(obj);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["make"].get_string(car.make))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["model"].get_string(car.model))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["year"].get(car.year))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["tire_pressure"].get<std::vector<float>>().get(
|
||||||
|
car.tire_pressure))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return simdjson::SUCCESS;
|
||||||
|
}
|
||||||
|
} // namespace simdjson
|
||||||
|
```
|
||||||
|
|
||||||
|
Importantly, the `tag_invoke` function must be inside the `simdjson` namespace.
|
||||||
|
Let us explain each argument of `tag_invoke` function.
|
||||||
|
|
||||||
|
- `simdjson::deserialize_tag`: it is the tag for Customization Point Object (CPO). You may often ignore this parameter. It is used to indicate that you mean to provide a deserialization function for simdjson.
|
||||||
|
- `var`: It receives automatically a `simdjson` value type (document, value, document_reference).
|
||||||
|
- The third parameter is an instance of the type that you want to support.
|
||||||
|
|
||||||
|
Please see our main documentation (`basics.md`) under
|
||||||
|
"Use `tag_invoke` for custom types (C++20)" for details about
|
||||||
|
tag_invoke functions.
|
||||||
|
|
||||||
|
Given a stream of JSON documents, you can add them to a data struture
|
||||||
|
such as a `std::vector<Car>` like so if you support exceptions:
|
||||||
|
|
||||||
|
```C++
|
||||||
|
padded_string json =
|
||||||
|
R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] }
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] }
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
)"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream stream;
|
||||||
|
[[maybe_unused]] auto error = parser.iterate_many(json).get(stream);
|
||||||
|
std::vector<Car> cars;
|
||||||
|
for(auto doc : stream) {
|
||||||
|
cars.push_back((Car)doc); // an exception may be thrown
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Otherwise you may use this longer version for explicit handling of errors:
|
||||||
|
|
||||||
|
|
||||||
|
```C++
|
||||||
|
std::vector<Car> cars;
|
||||||
|
for(auto doc : stream) {
|
||||||
|
Car c;
|
||||||
|
if ((error = doc.get<Car>().get(c))) {
|
||||||
|
std::cerr << simdjson::error_message(error); << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
cars.push_back(c);
|
||||||
|
}
|
||||||
|
```
|
||||||
+30
-30
@@ -9,8 +9,8 @@ Whether we parse JSON or XML, or any other serialized format, there are relative
|
|||||||
- Another popular approach is the schema-based deserialization model.
|
- Another popular approach is the schema-based deserialization model.
|
||||||
|
|
||||||
We propose an approach that is as easy to use and often as flexible as the DOM approach, yet as fast and
|
We propose an approach that is as easy to use and often as flexible as the DOM approach, yet as fast and
|
||||||
efficient as the schema-based or event-based approaches. We call this new approach "On Demand". The
|
efficient as the schema-based or event-based approaches. We call this new approach "On-Demand". The
|
||||||
simdjson On Demand API offers a familiar, friendly DOM API and
|
simdjson On-Demand API offers a familiar, friendly DOM API and
|
||||||
provides the performance of just-in-time parsing on top of the simdjson superior performance.
|
provides the performance of just-in-time parsing on top of the simdjson superior performance.
|
||||||
|
|
||||||
To achieve ease of use, we mimicked the *form* of a traditional DOM API: you can iterate over
|
To achieve ease of use, we mimicked the *form* of a traditional DOM API: you can iterate over
|
||||||
@@ -18,7 +18,7 @@ arrays, look up fields in objects, and extract native values like `double`, `uin
|
|||||||
|
|
||||||
To achieve performance, we introduced some key limitations that make the DOM API *streaming*:
|
To achieve performance, we introduced some key limitations that make the DOM API *streaming*:
|
||||||
array/object iteration cannot be restarted, and string/number values can only be parsed once. If
|
array/object iteration cannot be restarted, and string/number values can only be parsed once. If
|
||||||
these limitations are acceptable to you, the On Demand API could help you write maintainable
|
these limitations are acceptable to you, the On-Demand API could help you write maintainable
|
||||||
applications with a computation efficiency that is difficult to surpass.
|
applications with a computation efficiency that is difficult to surpass.
|
||||||
|
|
||||||
A code example illustrates our API from a programmer's point of view:
|
A code example illustrates our API from a programmer's point of view:
|
||||||
@@ -72,24 +72,24 @@ This streaming approach means that unused fields and values are not parsed or
|
|||||||
converted, thus saving space and time. In our example, the `"name"`, `"followers_count"`,
|
converted, thus saving space and time. In our example, the `"name"`, `"followers_count"`,
|
||||||
and `"friends_count"` keys and matching values are skipped.
|
and `"friends_count"` keys and matching values are skipped.
|
||||||
|
|
||||||
Further, the On Demand API does not parse a value *at all* until you try to convert it (e.g., to `double`,
|
Further, the On-Demand API does not parse a value *at all* until you try to convert it (e.g., to `double`,
|
||||||
`int`, `string`, or `bool`). In our example, when accessing the key-value pair `"retweet_count": 82`, the parser
|
`int`, `string`, or `bool`). In our example, when accessing the key-value pair `"retweet_count": 82`, the parser
|
||||||
may not convert the pair of characters `82` to the binary integer 82. Because the programmer specifies the data
|
may not convert the pair of characters `82` to the binary integer 82. Because the programmer specifies the data
|
||||||
type, we avoid branch mispredictions related to data type determination and improve the performance.
|
type, we avoid branch mispredictions related to data type determination and improve the performance.
|
||||||
|
|
||||||
|
|
||||||
We expect users of an On Demand API to work in terms of a JSON dialect, which is a set of expectations and
|
We expect users of an On-Demand API to work in terms of a JSON dialect, which is a set of expectations and
|
||||||
specifications that come in addition to the [JSON specification](https://www.rfc-editor.org/rfc/rfc8259.txt).
|
specifications that come in addition to the [JSON specification](https://www.rfc-editor.org/rfc/rfc8259.txt).
|
||||||
The On Demand approach is designed around several principles:
|
The On-Demand approach is designed around several principles:
|
||||||
|
|
||||||
* **Streaming (\*):** It avoids preparsing values, keeping the memory usage and the latency down.
|
* **Streaming (\*):** It avoids preparsing values, keeping the memory usage and the latency down.
|
||||||
* **Forward-Only:** To prevent reiteration of the same values and to keep the number of variables down (literally), only a single index is maintained and everything uses it (even if you have nested for loops). This means when you are going through an array of arrays, for example, that the inner array loop will advance the index to the next comma, and the array can just pick it up and look at it.
|
* **Forward-Only:** To prevent reiteration of the same values and to keep the number of variables down (literally), only a single index is maintained and everything uses it (even if you have nested for loops). This means when you are going through an array of arrays, for example, that the inner array loop will advance the index to the next comma, and the array can just pick it up and look at it.
|
||||||
* **Natural Iteration:** A JSON array or object can be iterated with a normal C++ for loop. Nested arrays and objects are supported by nested for loops.
|
* **Natural Iteration:** A JSON array or object can be iterated with a normal C++ for loop. Nested arrays and objects are supported by nested for loops.
|
||||||
* **Use-Specific Parsing:** Parsing is always specific to the type required by the programmer. For example, if the programmer asks for an unsigned integer, we just start parsing digits. If there were no digits, we toss an error. There are even different parsers for `double`, `uint64_t` and `int64_t` values. This use-specific parsing avoids the branchiness of a generic "type switch," and makes the code more inlineable and compact.
|
* **Use-Specific Parsing:** Parsing is always specific to the type required by the programmer. For example, if the programmer asks for an unsigned integer, we just start parsing digits. If there were no digits, we toss an error. There are even different parsers for `double`, `uint64_t` and `int64_t` values. This use-specific parsing avoids the branchiness of a generic "type switch," and makes the code more inlineable and compact.
|
||||||
* **Validate What You Use:** On Demand deliberately validates the values you use and the structure leading to it, but nothing else. The goal is a guarantee that the value you asked for is the correct one and is not malformed: there must be no confusion over whether you got the right value.
|
* **Validate What You Use:** On-Demand deliberately validates the values you use and the structure leading to it, but nothing else. The goal is a guarantee that the value you asked for is the correct one and is not malformed: there must be no confusion over whether you got the right value.
|
||||||
|
|
||||||
|
|
||||||
To understand why On Demand is different, it is helpful to review the major
|
To understand why On-Demand is different, it is helpful to review the major
|
||||||
approaches to parsing and parser APIs in use today.
|
approaches to parsing and parser APIs in use today.
|
||||||
|
|
||||||
### DOM Parsers
|
### DOM Parsers
|
||||||
@@ -106,7 +106,7 @@ DOM tree is often easy enough that many users use the DOM as-is instead of creat
|
|||||||
their own custom data structures.
|
their own custom data structures.
|
||||||
|
|
||||||
The DOM approach was the only way to parse JSON documents up to version 0.6 of the simdjson library.
|
The DOM approach was the only way to parse JSON documents up to version 0.6 of the simdjson library.
|
||||||
Our DOM API looks similar to our On Demand example, except
|
Our DOM API looks similar to our On-Demand example, except
|
||||||
it calls `parse` instead of `iterate`:
|
it calls `parse` instead of `iterate`:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
@@ -152,7 +152,7 @@ a tweet right now, or is this from some other place in the document
|
|||||||
entirely? Though an event-based approach may allow superior performance, it is demanding of the programmer
|
entirely? Though an event-based approach may allow superior performance, it is demanding of the programmer
|
||||||
who must efficiently keep track of its current state within the JSON input.
|
who must efficiently keep track of its current state within the JSON input.
|
||||||
|
|
||||||
The following is event-based example of the Twitter problem we have reviewed in the DOM and On Demand
|
The following is event-based example of the Twitter problem we have reviewed in the DOM and On-Demand
|
||||||
examples. To make it short enough to use as an example at all, it has heavily redacted: it only solves
|
examples. To make it short enough to use as an example at all, it has heavily redacted: it only solves
|
||||||
a part of the problem (does not get user.screen_name), it has bugs (it does not handle sub-objects
|
a part of the problem (does not get user.screen_name), it has bugs (it does not handle sub-objects
|
||||||
in a tweet at all), and it uses a theoretical, simple event-based API that minimizes ceremony.
|
in a tweet at all), and it uses a theoretical, simple event-based API that minimizes ceremony.
|
||||||
@@ -257,7 +257,7 @@ stress the branch prediction. Though branch predictors improve with each new gen
|
|||||||
the cost of branch mispredictions also tends to increase as pipelines expand, and the processors become
|
the cost of branch mispredictions also tends to increase as pipelines expand, and the processors become
|
||||||
able to schedule longer streams of instructions.
|
able to schedule longer streams of instructions.
|
||||||
|
|
||||||
On Demand parsing is tailor-made to solve this problem at the source, parsing values only after the
|
On-Demand parsing is tailor-made to solve this problem at the source, parsing values only after the
|
||||||
user declares their type by asking for a `double`, an `int`, a `string`, etc. It attempts to do so while
|
user declares their type by asking for a `double`, an `int`, a `string`, etc. It attempts to do so while
|
||||||
preserving most of the flexibility of DOM parsing.
|
preserving most of the flexibility of DOM parsing.
|
||||||
|
|
||||||
@@ -297,7 +297,7 @@ To help visualize the algorithm, we'll walk through the example C++ given at the
|
|||||||
|
|
||||||
Since this is the first time this parser has been used, `iterate()` first allocates internal
|
Since this is the first time this parser has been used, `iterate()` first allocates internal
|
||||||
parser buffers if this is the first time through. When reusing an existing parser, allocation
|
parser buffers if this is the first time through. When reusing an existing parser, allocation
|
||||||
only happens if the new document is bigger than internal buffers can handle. The On Demand
|
only happens if the new document is bigger than internal buffers can handle. The On-Demand
|
||||||
API only ever allocates memory in the `iterate()` function call.
|
API only ever allocates memory in the `iterate()` function call.
|
||||||
|
|
||||||
The simdjson library then preprocesses the JSON text at high speed, finding all tokens (i.e. the starting
|
The simdjson library then preprocesses the JSON text at high speed, finding all tokens (i.e. the starting
|
||||||
@@ -492,7 +492,7 @@ To help visualize the algorithm, we'll walk through the example C++ given at the
|
|||||||
Because of the cast to uint64_t, simdjson knows it's parsing an unsigned integer. This lets
|
Because of the cast to uint64_t, simdjson knows it's parsing an unsigned integer. This lets
|
||||||
us use a fast parser which *only* knows how to parse digits. It validates that it is an integer
|
us use a fast parser which *only* knows how to parse digits. It validates that it is an integer
|
||||||
by rejecting negative numbers, strings, and other values based on the fact that they are not the
|
by rejecting negative numbers, strings, and other values based on the fact that they are not the
|
||||||
digits 0-9. This type specificity is part of why parsing with on demand is so fast: you lose all
|
digits 0-9. This type specificity is part of why parsing with On-Demand is so fast: you lose all
|
||||||
the code that has to understand those other types.
|
the code that has to understand those other types.
|
||||||
|
|
||||||
The iterator is advanced to the `}`, and depth decreased back to 3 (root > statuses > tweet).
|
The iterator is advanced to the `}`, and depth decreased back to 3 (root > statuses > tweet).
|
||||||
@@ -597,7 +597,7 @@ To help visualize the algorithm, we'll walk through the example C++ given at the
|
|||||||
|
|
||||||
This means you can very efficiently do things like read a single value from a JSON file, or take
|
This means you can very efficiently do things like read a single value from a JSON file, or take
|
||||||
the top N, for example. It also means the things you don't use won't be fully validated. This is
|
the top N, for example. It also means the things you don't use won't be fully validated. This is
|
||||||
a general principle of On Demand: don't validate what you don't use. We still fully validate
|
a general principle of On-Demand: don't validate what you don't use. We still fully validate
|
||||||
values you do use, however, as well as the objects and arrays that lead to them, so that you can
|
values you do use, however, as well as the objects and arrays that lead to them, so that you can
|
||||||
be sure you get the information you need.
|
be sure you get the information you need.
|
||||||
|
|
||||||
@@ -654,7 +654,7 @@ for(auto field : doc.get_object()) {
|
|||||||
|
|
||||||
### Iteration Safety
|
### Iteration Safety
|
||||||
|
|
||||||
The On Demand API is powerful. To compensate, we add some safeguards to ensure that it can be used without fear
|
The On-Demand API is powerful. To compensate, we add some safeguards to ensure that it can be used without fear
|
||||||
in production systems:
|
in production systems:
|
||||||
|
|
||||||
- If the value fails to be parsed as one type, the program can try to parse it as something else until the program succeeds. Thus
|
- If the value fails to be parsed as one type, the program can try to parse it as something else until the program succeeds. Thus
|
||||||
@@ -667,7 +667,7 @@ in production systems:
|
|||||||
if it was `nullptr` but did not care what the actual value was--it will iterate. The destructor automates
|
if it was `nullptr` but did not care what the actual value was--it will iterate. The destructor automates
|
||||||
the iteration.
|
the iteration.
|
||||||
|
|
||||||
Some care is needed when using the On Demand API in scenarios where you need to access several sibling arrays or objects because
|
Some care is needed when using the On-Demand API in scenarios where you need to access several sibling arrays or objects because
|
||||||
only one object or array can be active at any one time. Let us consider the following example:
|
only one object or array can be active at any one time. Let us consider the following example:
|
||||||
|
|
||||||
```C++
|
```C++
|
||||||
@@ -709,36 +709,36 @@ A correct usage is given by the following example:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Benefits of the On Demand Approach
|
### Benefits of the On-Demand Approach
|
||||||
|
|
||||||
We expect that the On Demand approach has many of the performance benefits of the schema-based approach, while providing a flexibility that is similar to that of the DOM-based approach.
|
We expect that the On-Demand approach has many of the performance benefits of the schema-based approach, while providing a flexibility that is similar to that of the DOM-based approach.
|
||||||
|
|
||||||
* Faster than DOM in some cases. Reduced memory usage.
|
* Faster than DOM in some cases. Reduced memory usage.
|
||||||
* Straightforward, programmer-friendly interface (arrays and objects).
|
* Straightforward, programmer-friendly interface (arrays and objects).
|
||||||
* Highly expressive, beyond deserialization and pointer queries: many tasks can be accomplished with little code.
|
* Highly expressive, beyond deserialization and pointer queries: many tasks can be accomplished with little code.
|
||||||
|
|
||||||
### Limitations of the On Demand Approach
|
### Limitations of the On-Demand Approach
|
||||||
|
|
||||||
The On Demand approach has some limitations:
|
The On-Demand approach has some limitations:
|
||||||
|
|
||||||
* Because it operates in streaming mode, you only have access to the current element in the JSON document. Furthermore, the document is traversed in order so the code is sensitive to the order of the JSON nodes in the same manner as an event-based approach (e.g., SAX). (The one exception to this is field lookup, which is more *performant* when the order of lookups matches the order of fields in the document, but which will still work with out-of-order fields, with a performance hit.)
|
* Because it operates in streaming mode, you only have access to the current element in the JSON document. Furthermore, the document is traversed in order so the code is sensitive to the order of the JSON nodes in the same manner as an event-based approach (e.g., SAX). (The one exception to this is field lookup, which is more *performant* when the order of lookups matches the order of fields in the document, but which will still work with out-of-order fields, with a performance hit.)
|
||||||
* The On Demand approach is less safe than DOM: we only validate the components of the JSON document that are used and it is possible to begin ingesting an invalid document only to find out later that the document is invalid. Are you fine ingesting a large JSON document that starts with well formed JSON but ends with invalid JSON content?
|
* The On-Demand approach is less safe than DOM: we only validate the components of the JSON document that are used and it is possible to begin ingesting an invalid document only to find out later that the document is invalid. Are you fine ingesting a large JSON document that starts with well formed JSON but ends with invalid JSON content?
|
||||||
|
|
||||||
There are currently additional technical limitations which we expect to resolve in future releases of the simdjson library:
|
There are currently additional technical limitations which we expect to resolve in future releases of the simdjson library:
|
||||||
|
|
||||||
* The simdjson library offers runtime dispatching which allows you to compile one binary and have it run at full speed on different processors, taking advantage of the specific features of the processor. The On Demand API has limited runtime dispatch support. Under x64 systems, to fully benefit from the On Demand API, we recommend that you compile your code for a specific processor. E.g., if your processor supports AVX2 instructions, you should compile your binary executable with AVX2 instruction support (by using your compiler's commands). If you are sufficiently technically proficient, you can implement runtime dispatching within your application, by compiling your On Demand code for different processors.
|
* The simdjson library offers runtime dispatching which allows you to compile one binary and have it run at full speed on different processors, taking advantage of the specific features of the processor. The On-Demand API has limited runtime dispatch support. Under x64 systems, to fully benefit from the On-Demand API, we recommend that you compile your code for a specific processor. E.g., if your processor supports AVX2 instructions, you should compile your binary executable with AVX2 instruction support (by using your compiler's commands). If you are sufficiently technically proficient, you can implement runtime dispatching within your application, by compiling your On-Demand code for different processors.
|
||||||
* There is an initial phase which scans the entire document quickly, irrespective of the size of the document. We plan to break this phase into distinct steps for large files in a future release as we have done with other components of our API (e.g., `parse_many`).
|
* There is an initial phase which scans the entire document quickly, irrespective of the size of the document. We plan to break this phase into distinct steps for large files in a future release as we have done with other components of our API (e.g., `parse_many`).
|
||||||
|
|
||||||
### Applicability of the On Demand Approach
|
### Applicability of the On-Demand Approach
|
||||||
|
|
||||||
At this time we recommend the On Demand API in the following cases:
|
At this time we recommend the On-Demand API in the following cases:
|
||||||
|
|
||||||
1. The 64-bit hardware (CPU) used to run the software is known at compile time. If you need runtime dispatching because you cannot be certain of the hardware used to run your software, you will be better served with the core simdjson API. (This only applies to x64 (AMD/Intel). On 64-bit ARM hardware, runtime dispatching is unnecessary.)
|
1. The 64-bit hardware (CPU) used to run the software is known at compile time. If you need runtime dispatching because you cannot be certain of the hardware used to run your software, you will be better served with the core simdjson API. (This only applies to x64 (AMD/Intel). On 64-bit ARM hardware, runtime dispatching is unnecessary.)
|
||||||
2. The used parts of JSON files do not need to be validated and the layout of the nodes follows a strict JSON dialect. If you are receiving JSON from other systems, you might be better served with core simdjson API as it fully validates the JSON inputs and allows you to navigate through the document at will.
|
2. The used parts of JSON files do not need to be validated and the layout of the nodes follows a strict JSON dialect. If you are receiving JSON from other systems, you might be better served with core simdjson API as it fully validates the JSON inputs and allows you to navigate through the document at will.
|
||||||
3. Speed and efficiency are of the utmost importance. Keep in mind that the core simdjson API is highly efficient so adopting the On Demand API is not necessary for high efficiency.
|
3. Speed and efficiency are of the utmost importance. Keep in mind that the core simdjson API is highly efficient so adopting the On-Demand API is not necessary for high efficiency.
|
||||||
4. As a developer, you value a clean, flexible and maintainable API.
|
4. As a developer, you value a clean, flexible and maintainable API.
|
||||||
|
|
||||||
Good applications for the On Demand API might be:
|
Good applications for the On-Demand API might be:
|
||||||
|
|
||||||
* You are working from pre-existing large JSON files that have been vetted. You expect them to be well formed according to a known JSON dialect and to have a consistent layout. For example, you might be doing biomedical research or machine learning on top of static data dumps in JSON.
|
* You are working from pre-existing large JSON files that have been vetted. You expect them to be well formed according to a known JSON dialect and to have a consistent layout. For example, you might be doing biomedical research or machine learning on top of static data dumps in JSON.
|
||||||
* Both the generation and the consumption of JSON data is within your system. Your team controls both the software that produces the JSON and the software the parses it, your team knows and control the hardware. Thus you can fully test your system.
|
* Both the generation and the consumption of JSON data is within your system. Your team controls both the software that produces the JSON and the software the parses it, your team knows and control the hardware. Thus you can fully test your system.
|
||||||
@@ -746,13 +746,13 @@ Good applications for the On Demand API might be:
|
|||||||
|
|
||||||
## Checking Your CPU Selection (x64 systems)
|
## Checking Your CPU Selection (x64 systems)
|
||||||
|
|
||||||
The On Demand API uses advanced architecture-specific code for many common processors to make JSON preprocessing and string parsing faster. By default, however, most c++ compilers will compile to the least common denominator (since the program could theoretically be run anywhere). Since On Demand is inlined into your own code, it cannot always use these advanced versions unless the compiler is told to target them.
|
The On-Demand API uses advanced architecture-specific code for many common processors to make JSON preprocessing and string parsing faster. By default, however, most c++ compilers will compile to the least common denominator (since the program could theoretically be run anywhere). Since On-Demand is inlined into your own code, it cannot always use these advanced versions unless the compiler is told to target them.
|
||||||
|
|
||||||
On relevant systems, the On Demand API provides some support for runtime dispatching: that is, it will attempt to detect, at runtime, the instructions that your processor supports and optimize the code accordingly. However, it cannot always make full use of the features of your processor.
|
On relevant systems, the On-Demand API provides some support for runtime dispatching: that is, it will attempt to detect, at runtime, the instructions that your processor supports and optimize the code accordingly. However, it cannot always make full use of the features of your processor.
|
||||||
|
|
||||||
Some users wish to run at the best possible speed. Under recent Intel and AMD processors, these users should take additional steps to verify that their code is well optimized.
|
Some users wish to run at the best possible speed. Under recent Intel and AMD processors, these users should take additional steps to verify that their code is well optimized.
|
||||||
|
|
||||||
Given that the On Demand API offer limited runtime dispatching, it matters that your code is compiled against a specific CPU target. You should verify that the code is compiled against the target you expect. Thankfully, the simdjson library will tell you exactly what it detects as an implementation: `icelake` (AVX512 x64 processors), `haswell` (AVX2 x64 processors), `westmere` (SSE4 x64 processors), `arm64` (64-bit ARM), `ppc64` (64-bit POWER), `lasx` (LoongArch), `lsx` (LoongArch), `fallback` (others). Under x64 processors, many programmers will want to target `haswell` whereas under ARM, most programmers will want to target `arm64` (and it should do so automatically). The `fallback` is probably only good for testing purposes, not for deployment.
|
Given that the On-Demand API offer limited runtime dispatching, it matters that your code is compiled against a specific CPU target. You should verify that the code is compiled against the target you expect. Thankfully, the simdjson library will tell you exactly what it detects as an implementation: `icelake` (AVX512 x64 processors), `haswell` (AVX2 x64 processors), `westmere` (SSE4 x64 processors), `arm64` (64-bit ARM), `ppc64` (64-bit POWER), `lasx` (LoongArch), `lsx` (LoongArch), `fallback` (others). Under x64 processors, many programmers will want to target `haswell` whereas under ARM, most programmers will want to target `arm64` (and it should do so automatically). The `fallback` is probably only good for testing purposes, not for deployment.
|
||||||
|
|
||||||
```C++
|
```C++
|
||||||
std::cout << simdjson::builtin_implementation()->name() << std::endl;
|
std::cout << simdjson::builtin_implementation()->name() << std::endl;
|
||||||
@@ -777,6 +777,6 @@ In these examples, the `-march=haswell` flags targets a haswell processor and th
|
|||||||
|
|
||||||
Instead of specifying a specific microarchitecture, you can let your compiler do the work. The `-march=native` flags says "target the current computer," which is a reasonable default for many applications which both compile and run on the same processor.
|
Instead of specifying a specific microarchitecture, you can let your compiler do the work. The `-march=native` flags says "target the current computer," which is a reasonable default for many applications which both compile and run on the same processor.
|
||||||
|
|
||||||
Passing `-march=native` to the compiler may make On Demand faster by allowing it to use optimizations specific to your machine. You cannot do this, however, if you are compiling code that might be run on less advanced machines. That is, be mindful that when compiling with the `-march=native` flag, the resulting binary will run on the current system but may not run on other systems (e.g., on an old processor).
|
Passing `-march=native` to the compiler may make On-Demand faster by allowing it to use optimizations specific to your machine. You cannot do this, however, if you are compiling code that might be run on less advanced machines. That is, be mindful that when compiling with the `-march=native` flag, the resulting binary will run on the current system but may not run on other systems (e.g., on an old processor).
|
||||||
|
|
||||||
If you are compiling on an ARM or POWER system, you do not need to be concerned with CPU selection during compilation. The `-march=native` flag is useful for best performance on x64 (e.g., Intel) systems but it is generally unsupported on some platforms such as ARM (aarch64) or POWER.
|
If you are compiling on an ARM or POWER system, you do not need to be concerned with CPU selection during compilation. The `-march=native` flag is useful for best performance on x64 (e.g., Intel) systems but it is generally unsupported on some platforms such as ARM (aarch64) or POWER.
|
||||||
|
|||||||
+1
-1
@@ -75,7 +75,7 @@ or simply
|
|||||||
Server Loops: Long-Running Processes and Memory Capacity
|
Server Loops: Long-Running Processes and Memory Capacity
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
The On Demand approach also automatically expands its memory capacity when larger documents are parsed. However, for longer processes where very large files are processed (such as server loops), this capacity is not resized down. On Demand also lets you adjust the maximal capacity that the parser can process:
|
The On-Demand approach also automatically expands its memory capacity when larger documents are parsed. However, for longer processes where very large files are processed (such as server loops), this capacity is not resized down. On-Demand also lets you adjust the maximal capacity that the parser can process:
|
||||||
|
|
||||||
* You can set an upper bound (*max_capacity*) when construction the parser:
|
* You can set an upper bound (*max_capacity*) when construction the parser:
|
||||||
```C++
|
```C++
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
|||||||
add_quickstart_test(quickstart2_noexceptions quickstart2_noexceptions.cpp NO_EXCEPTIONS LABELS acceptance)
|
add_quickstart_test(quickstart2_noexceptions quickstart2_noexceptions.cpp NO_EXCEPTIONS LABELS acceptance)
|
||||||
add_quickstart_test(quickstart2_noexceptions11 quickstart2_noexceptions.cpp NO_EXCEPTIONS CXX_STANDARD c++11)
|
add_quickstart_test(quickstart2_noexceptions11 quickstart2_noexceptions.cpp NO_EXCEPTIONS CXX_STANDARD c++11)
|
||||||
|
|
||||||
# On Demand Quick Start
|
# On-Demand Quick Start
|
||||||
if (SIMDJSON_EXCEPTIONS)
|
if (SIMDJSON_EXCEPTIONS)
|
||||||
add_quickstart_test(quickstart_ondemand quickstart_ondemand.cpp LABELS quickstart_ondemand acceptance)
|
add_quickstart_test(quickstart_ondemand quickstart_ondemand.cpp LABELS quickstart_ondemand acceptance)
|
||||||
add_quickstart_test(quickstart_ondemand11 quickstart_ondemand.cpp CXX_STANDARD c++11 LABELS quickstart_ondemand acceptance)
|
add_quickstart_test(quickstart_ondemand11 quickstart_ondemand.cpp CXX_STANDARD c++11 LABELS quickstart_ondemand acceptance)
|
||||||
|
|||||||
@@ -53,5 +53,4 @@
|
|||||||
|
|
||||||
#include "simdjson/dom.h"
|
#include "simdjson/dom.h"
|
||||||
#include "simdjson/ondemand.h"
|
#include "simdjson/ondemand.h"
|
||||||
|
|
||||||
#endif // SIMDJSON_H
|
#endif // SIMDJSON_H
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "simdjson/compiler_check.h"
|
#include "simdjson/compiler_check.h"
|
||||||
#include "simdjson/error.h"
|
#include "simdjson/error.h"
|
||||||
#include "simdjson/portability.h"
|
#include "simdjson/portability.h"
|
||||||
|
#include "simdjson/concepts.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The top level simdjson namespace, containing everything the library provides.
|
* @brief The top level simdjson namespace, containing everything the library provides.
|
||||||
|
|||||||
@@ -13,6 +13,16 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// C++ 23
|
||||||
|
#if !defined(SIMDJSON_CPLUSPLUS23) && (SIMDJSON_CPLUSPLUS >= 202302L)
|
||||||
|
#define SIMDJSON_CPLUSPLUS23 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// C++ 20
|
||||||
|
#if !defined(SIMDJSON_CPLUSPLUS20) && (SIMDJSON_CPLUSPLUS >= 202002L)
|
||||||
|
#define SIMDJSON_CPLUSPLUS20 1
|
||||||
|
#endif
|
||||||
|
|
||||||
// C++ 17
|
// C++ 17
|
||||||
#if !defined(SIMDJSON_CPLUSPLUS17) && (SIMDJSON_CPLUSPLUS >= 201703L)
|
#if !defined(SIMDJSON_CPLUSPLUS17) && (SIMDJSON_CPLUSPLUS >= 201703L)
|
||||||
#define SIMDJSON_CPLUSPLUS17 1
|
#define SIMDJSON_CPLUSPLUS17 1
|
||||||
@@ -40,4 +50,16 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __has_include
|
||||||
|
#if __has_include(<version>)
|
||||||
|
#include <version>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cpp_concepts
|
||||||
|
#include <utility>
|
||||||
|
#define SIMDJSON_SUPPORTS_DESERIALIZATION 1
|
||||||
|
#else // __cpp_concepts
|
||||||
|
#define SIMDJSON_SUPPORTS_DESERIALIZATION 0
|
||||||
|
#endif
|
||||||
#endif // SIMDJSON_COMPILER_CHECK_H
|
#endif // SIMDJSON_COMPILER_CHECK_H
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
#ifndef SIMDJSON_CONCEPTS_H
|
||||||
|
#define SIMDJSON_CONCEPTS_H
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
|
||||||
|
#include <concepts>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace simdjson {
|
||||||
|
namespace concepts {
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
#define SIMDJSON_IMPL_CONCEPT(name, method) \
|
||||||
|
template <typename T> \
|
||||||
|
concept supports_##name = !std::is_const_v<T> && requires { \
|
||||||
|
typename std::remove_cvref_t<T>::value_type; \
|
||||||
|
requires requires(typename std::remove_cvref_t<T>::value_type &&val, \
|
||||||
|
T obj) { \
|
||||||
|
obj.method(std::move(val)); \
|
||||||
|
requires !requires { obj = std::move(val); }; \
|
||||||
|
}; \
|
||||||
|
};
|
||||||
|
|
||||||
|
SIMDJSON_IMPL_CONCEPT(emplace_back, emplace_back);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(emplace, emplace);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(push_back, push_back);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(add, add);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(push, push);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(append, append);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(insert, insert);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(op_append, operator+=);
|
||||||
|
|
||||||
|
#undef SIMDJSON_IMPL_CONCEPT
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
/// Check if T is a container that we can append to, including:
|
||||||
|
/// std::vector, std::deque, std::list, std::string, ...
|
||||||
|
template <typename T>
|
||||||
|
concept appendable_containers =
|
||||||
|
details::supports_emplace_back<T> || details::supports_emplace<T> ||
|
||||||
|
details::supports_push_back<T> || details::supports_push<T> ||
|
||||||
|
details::supports_add<T> || details::supports_append<T> ||
|
||||||
|
details::supports_insert<T>;
|
||||||
|
|
||||||
|
/// Insert into the container however possible
|
||||||
|
template <appendable_containers T, typename... Args>
|
||||||
|
constexpr decltype(auto) emplace_one(T &vec, Args &&...args) {
|
||||||
|
if constexpr (details::supports_emplace_back<T>) {
|
||||||
|
return vec.emplace_back(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_emplace<T>) {
|
||||||
|
return vec.emplace(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_push_back<T>) {
|
||||||
|
return vec.push_back(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_push<T>) {
|
||||||
|
return vec.push(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_add<T>) {
|
||||||
|
return vec.add(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_append<T>) {
|
||||||
|
return vec.append(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_insert<T>) {
|
||||||
|
return vec.insert(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_op_append<T> && sizeof...(Args) == 1) {
|
||||||
|
return vec.operator+=(std::forward<Args>(args)...);
|
||||||
|
} else {
|
||||||
|
static_assert(!sizeof(T *),
|
||||||
|
"We don't know how to add things to this container");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This checks if the container will return a reference to the newly added
|
||||||
|
/// element after an insert which for example `std::vector::emplace_back` does
|
||||||
|
/// since C++17; this will allow some optimizations.
|
||||||
|
template <typename T>
|
||||||
|
concept returns_reference = appendable_containers<T> && requires {
|
||||||
|
typename std::remove_cvref_t<T>::reference;
|
||||||
|
requires requires(typename std::remove_cvref_t<T>::value_type &&val, T obj) {
|
||||||
|
{
|
||||||
|
emplace_one(obj, std::move(val))
|
||||||
|
} -> std::same_as<typename std::remove_cvref_t<T>::reference>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept smart_pointer = requires(std::remove_cvref_t<T> ptr) {
|
||||||
|
// Check if T has a member type named element_type
|
||||||
|
typename std::remove_cvref_t<T>::element_type;
|
||||||
|
|
||||||
|
// Check if T has a get() member function
|
||||||
|
{
|
||||||
|
ptr.get()
|
||||||
|
} -> std::same_as<typename std::remove_cvref_t<T>::element_type *>;
|
||||||
|
|
||||||
|
// Check if T can be dereferenced
|
||||||
|
{ *ptr } -> std::same_as<typename std::remove_cvref_t<T>::element_type &>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept optional_type = requires(std::remove_cvref_t<T> obj) {
|
||||||
|
typename std::remove_cvref_t<T>::value_type;
|
||||||
|
{ obj.value() } -> std::same_as<typename std::remove_cvref_t<T>::value_type&>;
|
||||||
|
requires requires(typename std::remove_cvref_t<T>::value_type &&val) {
|
||||||
|
obj.emplace(std::move(val));
|
||||||
|
obj = std::move(val);
|
||||||
|
{
|
||||||
|
obj.value_or(val)
|
||||||
|
} -> std::convertible_to<typename std::remove_cvref_t<T>::value_type>;
|
||||||
|
};
|
||||||
|
{ static_cast<bool>(obj) } -> std::same_as<bool>; // convertible to bool
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace concepts
|
||||||
|
} // namespace simdjson
|
||||||
|
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
#endif // SIMDJSON_CONCEPTS_H
|
||||||
@@ -123,6 +123,10 @@ inline simdjson_result<element> array::at(size_t index) const noexcept {
|
|||||||
return INDEX_OUT_OF_BOUNDS;
|
return INDEX_OUT_OF_BOUNDS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline array::operator element() const noexcept {
|
||||||
|
return element(tape);
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// array::iterator inline implementation
|
// array::iterator inline implementation
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -126,6 +126,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
inline simdjson_result<element> at(size_t index) const noexcept;
|
inline simdjson_result<element> at(size_t index) const noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implicitly convert object to element
|
||||||
|
*/
|
||||||
|
inline operator element() const noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
simdjson_inline array(const internal::tape_ref &tape) noexcept;
|
simdjson_inline array(const internal::tape_ref &tape) noexcept;
|
||||||
internal::tape_ref tape;
|
internal::tape_ref tape;
|
||||||
|
|||||||
@@ -153,6 +153,10 @@ inline simdjson_result<element> object::at_key_case_insensitive(std::string_view
|
|||||||
return NO_SUCH_FIELD;
|
return NO_SUCH_FIELD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline object::operator element() const noexcept {
|
||||||
|
return element(tape);
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// object::iterator inline implementation
|
// object::iterator inline implementation
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -200,6 +200,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
inline simdjson_result<element> at_key_case_insensitive(std::string_view key) const noexcept;
|
inline simdjson_result<element> at_key_case_insensitive(std::string_view key) const noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implicitly convert object to element
|
||||||
|
*/
|
||||||
|
inline operator element() const noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
simdjson_inline object(const internal::tape_ref &tape) noexcept;
|
simdjson_inline object(const internal::tape_ref &tape) noexcept;
|
||||||
|
|
||||||
|
|||||||
@@ -16,5 +16,4 @@
|
|||||||
#include "simdjson/internal/jsoncharutils_tables.h"
|
#include "simdjson/internal/jsoncharutils_tables.h"
|
||||||
#include "simdjson/internal/numberparsing_tables.h"
|
#include "simdjson/internal/numberparsing_tables.h"
|
||||||
#include "simdjson/internal/simdprune_tables.h"
|
#include "simdjson/internal/simdprune_tables.h"
|
||||||
|
|
||||||
#endif // SIMDJSON_GENERIC_DEPENDENCIES_H
|
#endif // SIMDJSON_GENERIC_DEPENDENCIES_H
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
// Stuff other things depend on
|
// Stuff other things depend on
|
||||||
#include "simdjson/generic/ondemand/base.h"
|
#include "simdjson/generic/ondemand/base.h"
|
||||||
|
#include "simdjson/generic/ondemand/deserialize.h"
|
||||||
#include "simdjson/generic/ondemand/value_iterator.h"
|
#include "simdjson/generic/ondemand/value_iterator.h"
|
||||||
#include "simdjson/generic/ondemand/value.h"
|
#include "simdjson/generic/ondemand/value.h"
|
||||||
#include "simdjson/generic/ondemand/logger.h"
|
#include "simdjson/generic/ondemand/logger.h"
|
||||||
@@ -23,9 +24,13 @@
|
|||||||
#include "simdjson/generic/ondemand/object_iterator.h"
|
#include "simdjson/generic/ondemand/object_iterator.h"
|
||||||
#include "simdjson/generic/ondemand/serialization.h"
|
#include "simdjson/generic/ondemand/serialization.h"
|
||||||
|
|
||||||
|
// Deserialization for standard types
|
||||||
|
#include "simdjson/generic/ondemand/std_deserialize.h"
|
||||||
|
|
||||||
// Inline definitions
|
// Inline definitions
|
||||||
#include "simdjson/generic/ondemand/array-inl.h"
|
#include "simdjson/generic/ondemand/array-inl.h"
|
||||||
#include "simdjson/generic/ondemand/array_iterator-inl.h"
|
#include "simdjson/generic/ondemand/array_iterator-inl.h"
|
||||||
|
#include "simdjson/generic/ondemand/value-inl.h"
|
||||||
#include "simdjson/generic/ondemand/document-inl.h"
|
#include "simdjson/generic/ondemand/document-inl.h"
|
||||||
#include "simdjson/generic/ondemand/document_stream-inl.h"
|
#include "simdjson/generic/ondemand/document_stream-inl.h"
|
||||||
#include "simdjson/generic/ondemand/field-inl.h"
|
#include "simdjson/generic/ondemand/field-inl.h"
|
||||||
@@ -38,5 +43,6 @@
|
|||||||
#include "simdjson/generic/ondemand/raw_json_string-inl.h"
|
#include "simdjson/generic/ondemand/raw_json_string-inl.h"
|
||||||
#include "simdjson/generic/ondemand/serialization-inl.h"
|
#include "simdjson/generic/ondemand/serialization-inl.h"
|
||||||
#include "simdjson/generic/ondemand/token_iterator-inl.h"
|
#include "simdjson/generic/ondemand/token_iterator-inl.h"
|
||||||
#include "simdjson/generic/ondemand/value-inl.h"
|
|
||||||
#include "simdjson/generic/ondemand/value_iterator-inl.h"
|
#include "simdjson/generic/ondemand/value_iterator-inl.h"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
|
||||||
|
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
|
||||||
|
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||||
|
#define SIMDJSON_ONDEMAND_DESERIALIZE_H
|
||||||
|
#include "simdjson/generic/ondemand/base.h"
|
||||||
|
#include "simdjson/generic/ondemand/array.h"
|
||||||
|
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||||
|
|
||||||
|
#include <concepts>
|
||||||
|
namespace simdjson {
|
||||||
|
|
||||||
|
namespace tag_invoke_fn_ns {
|
||||||
|
void tag_invoke();
|
||||||
|
|
||||||
|
struct tag_invoke_fn {
|
||||||
|
template <typename Tag, typename... Args>
|
||||||
|
requires requires(Tag tag, Args &&...args) {
|
||||||
|
tag_invoke(std::forward<Tag>(tag), std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
constexpr auto operator()(Tag tag, Args &&...args) const
|
||||||
|
noexcept(noexcept(tag_invoke(std::forward<Tag>(tag),
|
||||||
|
std::forward<Args>(args)...)))
|
||||||
|
-> decltype(tag_invoke(std::forward<Tag>(tag),
|
||||||
|
std::forward<Args>(args)...)) {
|
||||||
|
return tag_invoke(std::forward<Tag>(tag), std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace tag_invoke_fn_ns
|
||||||
|
|
||||||
|
inline namespace tag_invoke_ns {
|
||||||
|
inline constexpr tag_invoke_fn_ns::tag_invoke_fn tag_invoke = {};
|
||||||
|
} // namespace tag_invoke_ns
|
||||||
|
|
||||||
|
template <typename Tag, typename... Args>
|
||||||
|
concept tag_invocable = requires(Tag tag, Args... args) {
|
||||||
|
tag_invoke(std::forward<Tag>(tag), std::forward<Args>(args)...);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Tag, typename... Args>
|
||||||
|
concept nothrow_tag_invocable =
|
||||||
|
tag_invocable<Tag, Args...> && requires(Tag tag, Args... args) {
|
||||||
|
{
|
||||||
|
tag_invoke(std::forward<Tag>(tag), std::forward<Args>(args)...)
|
||||||
|
} noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Tag, typename... Args>
|
||||||
|
using tag_invoke_result =
|
||||||
|
std::invoke_result<decltype(tag_invoke), Tag, Args...>;
|
||||||
|
|
||||||
|
template <typename Tag, typename... Args>
|
||||||
|
using tag_invoke_result_t =
|
||||||
|
std::invoke_result_t<decltype(tag_invoke), Tag, Args...>;
|
||||||
|
|
||||||
|
template <auto &Tag> using tag_t = std::decay_t<decltype(Tag)>;
|
||||||
|
|
||||||
|
|
||||||
|
struct deserialize_tag;
|
||||||
|
|
||||||
|
/// These types are deserializable in a built-in way
|
||||||
|
template <typename> struct is_builtin_deserializable : std::false_type {};
|
||||||
|
template <> struct is_builtin_deserializable<int64_t> : std::true_type {};
|
||||||
|
template <> struct is_builtin_deserializable<uint64_t> : std::true_type {};
|
||||||
|
template <> struct is_builtin_deserializable<double> : std::true_type {};
|
||||||
|
template <> struct is_builtin_deserializable<bool> : std::true_type {};
|
||||||
|
template <> struct is_builtin_deserializable<SIMDJSON_IMPLEMENTATION::ondemand::array> : std::true_type {};
|
||||||
|
template <> struct is_builtin_deserializable<SIMDJSON_IMPLEMENTATION::ondemand::object> : std::true_type {};
|
||||||
|
template <> struct is_builtin_deserializable<SIMDJSON_IMPLEMENTATION::ondemand::value> : std::true_type {};
|
||||||
|
template <> struct is_builtin_deserializable<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> : std::true_type {};
|
||||||
|
template <> struct is_builtin_deserializable<std::string_view> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept is_builtin_deserializable_v = is_builtin_deserializable<T>::value;
|
||||||
|
|
||||||
|
template <typename T, typename ValT = SIMDJSON_IMPLEMENTATION::ondemand::value>
|
||||||
|
concept custom_deserializable = tag_invocable<deserialize_tag, ValT&, T&>;
|
||||||
|
|
||||||
|
template <typename T, typename ValT = SIMDJSON_IMPLEMENTATION::ondemand::value>
|
||||||
|
concept deserializable = custom_deserializable<T, ValT> || is_builtin_deserializable_v<T>;
|
||||||
|
|
||||||
|
template <typename T, typename ValT = SIMDJSON_IMPLEMENTATION::ondemand::value>
|
||||||
|
concept nothrow_custom_deserializable = nothrow_tag_invocable<deserialize_tag, ValT&, T&>;
|
||||||
|
|
||||||
|
// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned.
|
||||||
|
template <typename T, typename ValT = SIMDJSON_IMPLEMENTATION::ondemand::value>
|
||||||
|
concept nothrow_deserializable = nothrow_custom_deserializable<T, ValT> || is_builtin_deserializable_v<T>;
|
||||||
|
|
||||||
|
/// Deserialize Tag
|
||||||
|
inline constexpr struct deserialize_tag {
|
||||||
|
using value_type = SIMDJSON_IMPLEMENTATION::ondemand::value;
|
||||||
|
using document_type = SIMDJSON_IMPLEMENTATION::ondemand::document;
|
||||||
|
using document_reference_type = SIMDJSON_IMPLEMENTATION::ondemand::document_reference;
|
||||||
|
|
||||||
|
// Customization Point for value
|
||||||
|
template <typename T>
|
||||||
|
requires custom_deserializable<T, value_type>
|
||||||
|
[[nodiscard]] constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
|
||||||
|
return tag_invoke(*this, object, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Customization Point for document
|
||||||
|
template <typename T>
|
||||||
|
requires custom_deserializable<T, document_type>
|
||||||
|
[[nodiscard]] constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_type>) {
|
||||||
|
return tag_invoke(*this, object, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Customization Point for document reference
|
||||||
|
template <typename T>
|
||||||
|
requires custom_deserializable<T, document_reference_type>
|
||||||
|
[[nodiscard]] constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_reference_type>) {
|
||||||
|
return tag_invoke(*this, object, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} deserialize{};
|
||||||
|
|
||||||
|
} // namespace simdjson
|
||||||
|
|
||||||
|
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
|
||||||
|
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
|
||||||
@@ -13,7 +13,9 @@
|
|||||||
#include "simdjson/generic/ondemand/object-inl.h"
|
#include "simdjson/generic/ondemand/object-inl.h"
|
||||||
#include "simdjson/generic/ondemand/raw_json_string.h"
|
#include "simdjson/generic/ondemand/raw_json_string.h"
|
||||||
#include "simdjson/generic/ondemand/value.h"
|
#include "simdjson/generic/ondemand/value.h"
|
||||||
|
#include "simdjson/generic/ondemand/value-inl.h"
|
||||||
#include "simdjson/generic/ondemand/value_iterator-inl.h"
|
#include "simdjson/generic/ondemand/value_iterator-inl.h"
|
||||||
|
#include "simdjson/generic/ondemand/deserialize.h"
|
||||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||||
|
|
||||||
namespace simdjson {
|
namespace simdjson {
|
||||||
@@ -167,20 +169,16 @@ template<> simdjson_inline simdjson_result<int64_t> document::get() & noexcept {
|
|||||||
template<> simdjson_inline simdjson_result<bool> document::get() & noexcept { return get_bool(); }
|
template<> simdjson_inline simdjson_result<bool> document::get() & noexcept { return get_bool(); }
|
||||||
template<> simdjson_inline simdjson_result<value> document::get() & noexcept { return get_value(); }
|
template<> simdjson_inline simdjson_result<value> document::get() & noexcept { return get_value(); }
|
||||||
|
|
||||||
template<> simdjson_inline simdjson_result<raw_json_string> document::get() && noexcept { return get_raw_json_string(); }
|
template<> simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); }
|
||||||
template<> simdjson_inline simdjson_result<std::string_view> document::get() && noexcept { return get_string(false); }
|
template<> simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); }
|
||||||
template<> simdjson_inline simdjson_result<double> document::get() && noexcept { return std::forward<document>(*this).get_double(); }
|
template<> simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); }
|
||||||
template<> simdjson_inline simdjson_result<uint64_t> document::get() && noexcept { return std::forward<document>(*this).get_uint64(); }
|
template<> simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); }
|
||||||
template<> simdjson_inline simdjson_result<int64_t> document::get() && noexcept { return std::forward<document>(*this).get_int64(); }
|
template<> simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); }
|
||||||
template<> simdjson_inline simdjson_result<bool> document::get() && noexcept { return std::forward<document>(*this).get_bool(); }
|
template<> simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); }
|
||||||
template<> simdjson_inline simdjson_result<value> document::get() && noexcept { return get_value(); }
|
template<> simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); }
|
||||||
|
template<> simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); }
|
||||||
|
template<> simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); }
|
||||||
|
|
||||||
template<typename T> simdjson_inline error_code document::get(T &out) & noexcept {
|
|
||||||
return get<T>().get(out);
|
|
||||||
}
|
|
||||||
template<typename T> simdjson_inline error_code document::get(T &out) && noexcept {
|
|
||||||
return std::forward<document>(*this).get<T>().get(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if SIMDJSON_EXCEPTIONS
|
#if SIMDJSON_EXCEPTIONS
|
||||||
template <class T>
|
template <class T>
|
||||||
@@ -818,6 +816,26 @@ simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::o
|
|||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.is_null();
|
return first.is_null();
|
||||||
}
|
}
|
||||||
|
template<typename T>
|
||||||
|
simdjson_inline simdjson_result<T> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get() & noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return first.get<T>();
|
||||||
|
}
|
||||||
|
template<typename T>
|
||||||
|
simdjson_inline simdjson_result<T> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get() && noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>(first).get<T>();
|
||||||
|
}
|
||||||
|
template <class T>
|
||||||
|
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(T &out) & noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return first.get<T>(out);
|
||||||
|
}
|
||||||
|
template <class T>
|
||||||
|
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(T &out) && noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>(first).get<T>(out);
|
||||||
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_type> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::type() noexcept {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_type> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::type() noexcept {
|
||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.type();
|
return first.type();
|
||||||
@@ -830,6 +848,18 @@ simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::o
|
|||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.is_string();
|
return first.is_string();
|
||||||
}
|
}
|
||||||
|
template <>
|
||||||
|
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(SIMDJSON_IMPLEMENTATION::ondemand::document_reference &out) & noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
out = first;
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(SIMDJSON_IMPLEMENTATION::ondemand::document_reference &out) && noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
out = first;
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::is_negative() noexcept {
|
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::is_negative() noexcept {
|
||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.is_negative();
|
return first.is_negative();
|
||||||
@@ -847,10 +877,12 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number> simdj
|
|||||||
return first.get_number();
|
return first.get_number();
|
||||||
}
|
}
|
||||||
#if SIMDJSON_EXCEPTIONS
|
#if SIMDJSON_EXCEPTIONS
|
||||||
template <class T, typename std::enable_if<std::is_same<T, SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::value == false>::type>
|
template <class T>
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator T() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator T() noexcept(false) {
|
||||||
|
static_assert(std::is_same<T, SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::value == false, "You should not call get<T> when T is a document");
|
||||||
|
static_assert(std::is_same<T, SIMDJSON_IMPLEMENTATION::ondemand::document>::value == false, "You should not call get<T> when T is a document");
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get<T>();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
|
|||||||
@@ -4,8 +4,11 @@
|
|||||||
#define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
|
#define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
|
||||||
#include "simdjson/generic/ondemand/base.h"
|
#include "simdjson/generic/ondemand/base.h"
|
||||||
#include "simdjson/generic/ondemand/json_iterator.h"
|
#include "simdjson/generic/ondemand/json_iterator.h"
|
||||||
|
#include "simdjson/generic/ondemand/deserialize.h"
|
||||||
|
#include "simdjson/generic/ondemand/value.h"
|
||||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||||
|
|
||||||
|
|
||||||
namespace simdjson {
|
namespace simdjson {
|
||||||
namespace SIMDJSON_IMPLEMENTATION {
|
namespace SIMDJSON_IMPLEMENTATION {
|
||||||
namespace ondemand {
|
namespace ondemand {
|
||||||
@@ -178,24 +181,39 @@ public:
|
|||||||
* @returns A value of the given type, parsed from the JSON.
|
* @returns A value of the given type, parsed from the JSON.
|
||||||
* @returns INCORRECT_TYPE If the JSON value is not the given type.
|
* @returns INCORRECT_TYPE If the JSON value is not the given type.
|
||||||
*/
|
*/
|
||||||
template<typename T> simdjson_inline simdjson_result<T> get() & noexcept {
|
template <typename T>
|
||||||
// Unless the simdjson library or the user provides an inline implementation, calling this method should
|
simdjson_inline simdjson_result<T> get() &
|
||||||
// immediately fail.
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
|
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document> : true)
|
||||||
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
|
#else
|
||||||
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
|
noexcept
|
||||||
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
|
#endif
|
||||||
" You may also add support for custom types, see our documentation.");
|
{
|
||||||
|
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
|
||||||
|
T out{};
|
||||||
|
SIMDJSON_TRY(get<T>(out));
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
/** @overload template<typename T> simdjson_result<T> get() & noexcept */
|
/**
|
||||||
template<typename T> simdjson_inline simdjson_result<T> get() && noexcept {
|
* @overload template<typename T> simdjson_result<T> get() & noexcept
|
||||||
// Unless the simdjson library or the user provides an inline implementation, calling this method should
|
*
|
||||||
// immediately fail.
|
* We disallow the use tag_invoke CPO on a moved document; it may create UB
|
||||||
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
|
* if user uses `ondemand::array` or `ondemand::object` in their custom type.
|
||||||
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
|
*
|
||||||
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
|
* The member function is still remains specialize-able for compatibility
|
||||||
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
|
* reasons, but we completely disallow its use when a tag_invoke customization
|
||||||
" You may also add support for custom types, see our documentation.");
|
* is provided.
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
simdjson_inline simdjson_result<T> get() &&
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document> : true)
|
||||||
|
#else
|
||||||
|
noexcept
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document being alive; that would be Undefined Behaviour.");
|
||||||
|
return static_cast<document&>(*this).get<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -209,7 +227,32 @@ public:
|
|||||||
* @returns INCORRECT_TYPE If the JSON value is not an object.
|
* @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.
|
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
|
||||||
*/
|
*/
|
||||||
template<typename T> simdjson_inline error_code get(T &out) & noexcept;
|
template<typename T>
|
||||||
|
simdjson_inline error_code get(T &out) &
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document> : true)
|
||||||
|
#else
|
||||||
|
noexcept
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
if constexpr (custom_deserializable<T, document>) {
|
||||||
|
return deserialize(*this, out);
|
||||||
|
} else {
|
||||||
|
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
// Unless the simdjson library or the user provides an inline implementation, calling this method should
|
||||||
|
// immediately fail.
|
||||||
|
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
|
||||||
|
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
|
||||||
|
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
|
||||||
|
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
|
||||||
|
" You may also add support for custom types, see our documentation.");
|
||||||
|
static_cast<void>(out); // to get rid of unused errors
|
||||||
|
return UNINITIALIZED;
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
/** @overload template<typename T> error_code get(T &out) & noexcept */
|
/** @overload template<typename T> error_code get(T &out) & noexcept */
|
||||||
template<typename T> simdjson_inline error_code get(T &out) && noexcept;
|
template<typename T> simdjson_inline error_code get(T &out) && noexcept;
|
||||||
|
|
||||||
@@ -713,7 +756,70 @@ public:
|
|||||||
simdjson_inline simdjson_result<value> get_value() noexcept;
|
simdjson_inline simdjson_result<value> get_value() noexcept;
|
||||||
|
|
||||||
simdjson_inline simdjson_result<bool> is_null() noexcept;
|
simdjson_inline simdjson_result<bool> is_null() noexcept;
|
||||||
template<typename T> simdjson_inline simdjson_result<T> get() & noexcept;
|
template <typename T>
|
||||||
|
simdjson_inline simdjson_result<T> get() &
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document> : true)
|
||||||
|
#else
|
||||||
|
noexcept
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
|
||||||
|
T out{};
|
||||||
|
SIMDJSON_TRY(get<T>(out));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
template<typename T>
|
||||||
|
simdjson_inline simdjson_result<T> get() &&
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document> : true)
|
||||||
|
#else
|
||||||
|
noexcept
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document_reference being alive; that would be Undefined Behaviour.");
|
||||||
|
return static_cast<document&>(*this).get<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get this value as the given type.
|
||||||
|
*
|
||||||
|
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool, value
|
||||||
|
*
|
||||||
|
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
|
||||||
|
*
|
||||||
|
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
|
||||||
|
* @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_inline error_code get(T &out) &
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document_reference> : true)
|
||||||
|
#else
|
||||||
|
noexcept
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
if constexpr (custom_deserializable<T, document_reference>) {
|
||||||
|
return deserialize(*this, out);
|
||||||
|
} else {
|
||||||
|
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
// Unless the simdjson library or the user provides an inline implementation, calling this method should
|
||||||
|
// immediately fail.
|
||||||
|
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
|
||||||
|
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
|
||||||
|
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
|
||||||
|
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
|
||||||
|
" You may also add support for custom types, see our documentation.");
|
||||||
|
static_cast<void>(out); // to get rid of unused errors
|
||||||
|
return UNINITIALIZED;
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
/** @overload template<typename T> error_code get(T &out) & noexcept */
|
||||||
|
template<typename T> simdjson_inline error_code get(T &out) && noexcept;
|
||||||
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
|
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
|
||||||
simdjson_inline operator document&() const noexcept;
|
simdjson_inline operator document&() const noexcept;
|
||||||
#if SIMDJSON_EXCEPTIONS
|
#if SIMDJSON_EXCEPTIONS
|
||||||
@@ -865,8 +971,14 @@ public:
|
|||||||
simdjson_inline simdjson_result<bool> get_bool() noexcept;
|
simdjson_inline simdjson_result<bool> get_bool() noexcept;
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> get_value() noexcept;
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> get_value() noexcept;
|
||||||
simdjson_inline simdjson_result<bool> is_null() noexcept;
|
simdjson_inline simdjson_result<bool> is_null() noexcept;
|
||||||
|
|
||||||
|
template<typename T> simdjson_inline simdjson_result<T> get() & noexcept;
|
||||||
|
template<typename T> simdjson_inline simdjson_result<T> get() && noexcept;
|
||||||
|
|
||||||
|
template<typename T> simdjson_inline error_code get(T &out) & noexcept;
|
||||||
|
template<typename T> simdjson_inline error_code get(T &out) && noexcept;
|
||||||
#if SIMDJSON_EXCEPTIONS
|
#if SIMDJSON_EXCEPTIONS
|
||||||
template <class T, typename std::enable_if<std::is_same<T, SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::value == false>::type>
|
template <class T>
|
||||||
explicit simdjson_inline operator T() noexcept(false);
|
explicit simdjson_inline operator T() noexcept(false);
|
||||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
|
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
|
||||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() & noexcept(false);
|
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() & noexcept(false);
|
||||||
|
|||||||
@@ -152,7 +152,6 @@ simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
simdjson_inline simdjson_result<ondemand::document_reference> document_stream::iterator::operator*() noexcept {
|
simdjson_inline simdjson_result<ondemand::document_reference> document_stream::iterator::operator*() noexcept {
|
||||||
//if(stream->error) { return stream->error; }
|
|
||||||
return simdjson_result<ondemand::document_reference>(stream->doc, stream->error);
|
return simdjson_result<ondemand::document_reference>(stream->doc, stream->error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,14 @@ simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> field::un
|
|||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename string_type>
|
||||||
|
simdjson_inline simdjson_warn_unused error_code field::unescaped_key(string_type& receiver, bool allow_replacement) noexcept {
|
||||||
|
std::string_view key;
|
||||||
|
SIMDJSON_TRY( unescaped_key(allow_replacement).get(key) );
|
||||||
|
receiver = key;
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
simdjson_inline raw_json_string field::key() const noexcept {
|
simdjson_inline raw_json_string field::key() const noexcept {
|
||||||
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
|
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
|
||||||
return first;
|
return first;
|
||||||
@@ -105,6 +113,12 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
|
|||||||
return first.unescaped_key(allow_replacement);
|
return first.unescaped_key(allow_replacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename string_type>
|
||||||
|
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::field>::unescaped_key(string_type &receiver, bool allow_replacement) noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return first.unescaped_key(receiver, allow_replacement);
|
||||||
|
}
|
||||||
|
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::field>::value() noexcept {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::field>::value() noexcept {
|
||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return std::move(first.value());
|
return std::move(first.value());
|
||||||
|
|||||||
@@ -36,21 +36,37 @@ public:
|
|||||||
* This consumes the key: once you have called unescaped_key(), you cannot
|
* This consumes the key: once you have called unescaped_key(), you cannot
|
||||||
* call it again nor can you call key().
|
* call it again nor can you call key().
|
||||||
*/
|
*/
|
||||||
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescaped_key(bool allow_replacement) noexcept;
|
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
|
||||||
|
/**
|
||||||
|
* Get the key as a string_view (for higher speed, consider raw_key).
|
||||||
|
* We deliberately use a more cumbersome name (unescaped_key) to force users
|
||||||
|
* to think twice about using it. The content is stored in the receiver.
|
||||||
|
*
|
||||||
|
* This consumes the key: once you have called unescaped_key(), you cannot
|
||||||
|
* call it again nor can you call key().
|
||||||
|
*/
|
||||||
|
template <typename string_type>
|
||||||
|
simdjson_inline simdjson_warn_unused error_code unescaped_key(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||||
/**
|
/**
|
||||||
* Get the key as a raw_json_string. Can be used for direct comparison with
|
* Get the key as a raw_json_string. Can be used for direct comparison with
|
||||||
* an unescaped C string: e.g., key() == "test".
|
* an unescaped C string: e.g., key() == "test". This does not count as
|
||||||
|
* consumption of the content: you can safely call it repeatedly.
|
||||||
|
* See escaped_key() for a similar function which returns
|
||||||
|
* a more convenient std::string_view result.
|
||||||
*/
|
*/
|
||||||
simdjson_inline raw_json_string key() const noexcept;
|
simdjson_inline raw_json_string key() const noexcept;
|
||||||
/**
|
/**
|
||||||
* Get the unprocessed key as a string_view. This includes the quotes and may include
|
* Get the unprocessed key as a string_view. This includes the quotes and may include
|
||||||
* some spaces after the last quote.
|
* some spaces after the last quote. This does not count as
|
||||||
|
* consumption of the content: you can safely call it repeatedly.
|
||||||
|
* See escaped_key().
|
||||||
*/
|
*/
|
||||||
simdjson_inline std::string_view key_raw_json_token() const noexcept;
|
simdjson_inline std::string_view key_raw_json_token() const noexcept;
|
||||||
/**
|
/**
|
||||||
* Get the key as a string_view. This does not include the quotes and
|
* Get the key as a string_view. This does not include the quotes and
|
||||||
* the string is unprocessed key so it may contain escape characters
|
* the string is unprocessed key so it may contain escape characters
|
||||||
* (e.g., \uXXXX or \n). Use unescaped_key() to get the unescaped key.
|
* (e.g., \uXXXX or \n). It does not count as a consumption of the content:
|
||||||
|
* you can safely call it repeatedly. Use unescaped_key() to get the unescaped key.
|
||||||
*/
|
*/
|
||||||
simdjson_inline std::string_view escaped_key() const noexcept;
|
simdjson_inline std::string_view escaped_key() const noexcept;
|
||||||
/**
|
/**
|
||||||
@@ -84,6 +100,8 @@ public:
|
|||||||
simdjson_inline simdjson_result() noexcept = default;
|
simdjson_inline simdjson_result() noexcept = default;
|
||||||
|
|
||||||
simdjson_inline simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
|
simdjson_inline simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
|
||||||
|
template<typename string_type>
|
||||||
|
simdjson_inline error_code unescaped_key(string_type &receiver, bool allow_replacement = false) noexcept;
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> key() noexcept;
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> key() noexcept;
|
||||||
simdjson_inline simdjson_result<std::string_view> key_raw_json_token() noexcept;
|
simdjson_inline simdjson_result<std::string_view> key_raw_json_token() noexcept;
|
||||||
simdjson_inline simdjson_result<std::string_view> escaped_key() noexcept;
|
simdjson_inline simdjson_result<std::string_view> escaped_key() noexcept;
|
||||||
|
|||||||
@@ -354,11 +354,23 @@ simdjson_inline token_position json_iterator::position() const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape(raw_json_string in, bool allow_replacement) noexcept {
|
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape(raw_json_string in, bool allow_replacement) noexcept {
|
||||||
|
#if SIMDJSON_DEVELOPMENT_CHECKS
|
||||||
|
auto result = parser->unescape(in, _string_buf_loc, allow_replacement);
|
||||||
|
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
|
||||||
|
return result;
|
||||||
|
#else
|
||||||
return parser->unescape(in, _string_buf_loc, allow_replacement);
|
return parser->unescape(in, _string_buf_loc, allow_replacement);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape_wobbly(raw_json_string in) noexcept {
|
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape_wobbly(raw_json_string in) noexcept {
|
||||||
|
#if SIMDJSON_DEVELOPMENT_CHECKS
|
||||||
|
auto result = parser->unescape_wobbly(in, _string_buf_loc);
|
||||||
|
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
|
||||||
|
return result;
|
||||||
|
#else
|
||||||
return parser->unescape_wobbly(in, _string_buf_loc);
|
return parser->unescape_wobbly(in, _string_buf_loc);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
simdjson_inline void json_iterator::reenter_child(token_position position, depth_t child_depth) noexcept {
|
simdjson_inline void json_iterator::reenter_child(token_position position, depth_t child_depth) noexcept {
|
||||||
|
|||||||
@@ -160,8 +160,8 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Reset the iterator so that we are pointing back at the
|
* Reset the iterator so that we are pointing back at the
|
||||||
* beginning of the object. You should still consume values only once even if you
|
* beginning of the object. You should still consume values only once even if you
|
||||||
* can iterate through the object more than once. If you unescape a string within
|
* can iterate through the object more than once. If you unescape a string or a key
|
||||||
* the object more than once, you have unsafe code. Note that rewinding an object
|
* within the object more than once, you have unsafe code. Note that rewinding an object
|
||||||
* means that you may need to reparse it anew: it is not a free operation.
|
* means that you may need to reparse it anew: it is not a free operation.
|
||||||
*
|
*
|
||||||
* @returns true if the object contains some elements (not empty)
|
* @returns true if the object contains some elements (not empty)
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ simdjson_warn_unused simdjson_inline error_code parser::allocate(size_t new_capa
|
|||||||
_max_depth = new_max_depth;
|
_max_depth = new_max_depth;
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
#if SIMDJSON_DEVELOPMENT_CHECKS
|
||||||
|
simdjson_inline simdjson_warn_unused bool parser::string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept {
|
||||||
|
return (string_buf_loc < string_buf.get()) || (size_t(string_buf_loc - string_buf.get()) >= capacity());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
|
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
|
||||||
if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; }
|
if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; }
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ namespace SIMDJSON_IMPLEMENTATION {
|
|||||||
namespace ondemand {
|
namespace ondemand {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default batch size for document_stream instances for this On Demand kernel.
|
* The default batch size for document_stream instances for this On-Demand kernel.
|
||||||
* Note that different On Demand kernel may use a different DEFAULT_BATCH_SIZE value
|
* Note that different On-Demand kernel may use a different DEFAULT_BATCH_SIZE value
|
||||||
* in the future.
|
* in the future.
|
||||||
*/
|
*/
|
||||||
static constexpr size_t DEFAULT_BATCH_SIZE = 1000000;
|
static constexpr size_t DEFAULT_BATCH_SIZE = 1000000;
|
||||||
@@ -327,6 +327,17 @@ public:
|
|||||||
*/
|
*/
|
||||||
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept;
|
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept;
|
||||||
|
|
||||||
|
#if SIMDJSON_DEVELOPMENT_CHECKS
|
||||||
|
/**
|
||||||
|
* Returns true if string_buf_loc is outside of the allocated range for the
|
||||||
|
* the string buffer. When true, it indicates that the string buffer has overflowed.
|
||||||
|
* This is a development-time check that is not needed in production. It can be
|
||||||
|
* used to detect buffer overflows in the string buffer and usafe usage of the
|
||||||
|
* string buffer.
|
||||||
|
*/
|
||||||
|
bool string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept;
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** @private [for benchmarking access] The implementation to use */
|
/** @private [for benchmarking access] The implementation to use */
|
||||||
std::unique_ptr<internal::dom_parser_implementation> implementation{};
|
std::unique_ptr<internal::dom_parser_implementation> implementation{};
|
||||||
|
|||||||
@@ -0,0 +1,166 @@
|
|||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
|
||||||
|
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
|
||||||
|
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||||
|
#define SIMDJSON_ONDEMAND_DESERIALIZE_H
|
||||||
|
#include "simdjson/generic/ondemand/array.h"
|
||||||
|
#include "simdjson/generic/ondemand/base.h"
|
||||||
|
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||||
|
|
||||||
|
#include <concepts>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace simdjson {
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool require_custom_serialization = false;
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
// Number deserialization
|
||||||
|
//////////////////////////////
|
||||||
|
|
||||||
|
template <std::unsigned_integral T>
|
||||||
|
requires(!require_custom_serialization<T>)
|
||||||
|
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
||||||
|
using limits = std::numeric_limits<T>;
|
||||||
|
|
||||||
|
uint64_t x;
|
||||||
|
SIMDJSON_TRY(val.get_uint64().get(x));
|
||||||
|
if (x > (limits::max)()) {
|
||||||
|
return NUMBER_OUT_OF_RANGE;
|
||||||
|
}
|
||||||
|
out = static_cast<T>(x);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::floating_point T>
|
||||||
|
requires(!require_custom_serialization<T>)
|
||||||
|
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
||||||
|
double x;
|
||||||
|
SIMDJSON_TRY(val.get_double().get(x));
|
||||||
|
out = static_cast<T>(x);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::signed_integral T>
|
||||||
|
requires(!require_custom_serialization<T>)
|
||||||
|
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
||||||
|
using limits = std::numeric_limits<T>;
|
||||||
|
|
||||||
|
int64_t x;
|
||||||
|
SIMDJSON_TRY(val.get_int64().get(x));
|
||||||
|
if (x > (limits::max)() || x < (limits::min)()) {
|
||||||
|
return NUMBER_OUT_OF_RANGE;
|
||||||
|
}
|
||||||
|
out = static_cast<T>(x);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* STL containers have several constructors including one that takes a single
|
||||||
|
* size argument. Thus, some compilers (Visual Studio) will not be able to
|
||||||
|
* disambiguate between the size and container constructor. Users should
|
||||||
|
* explicitly specify the type of the container as needed: e.g.,
|
||||||
|
* doc.get<std::vector<int>>().
|
||||||
|
*/
|
||||||
|
template <concepts::appendable_containers T, typename ValT>
|
||||||
|
requires(!require_custom_serialization<T>)
|
||||||
|
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
|
||||||
|
using value_type = typename std::remove_cvref_t<T>::value_type;
|
||||||
|
static_assert(
|
||||||
|
deserializable<value_type, ValT>,
|
||||||
|
"The specified type inside the container must itself be deserializable");
|
||||||
|
static_assert(
|
||||||
|
std::is_default_constructible_v<value_type>,
|
||||||
|
"The specified type inside the container must default constructible.");
|
||||||
|
|
||||||
|
SIMDJSON_IMPLEMENTATION::ondemand::array arr;
|
||||||
|
SIMDJSON_TRY(val.get_array().get(arr));
|
||||||
|
for (auto v : arr) {
|
||||||
|
if constexpr (concepts::returns_reference<T>) {
|
||||||
|
if (auto const err = v.get<value_type>().get(concepts::emplace_one(out));
|
||||||
|
err) {
|
||||||
|
// If an error occurs, the empty element that we just inserted gets
|
||||||
|
// removed. We're not using a temp variable because if T is a heavy
|
||||||
|
// type, we want the valid path to be the fast path and the slow path be
|
||||||
|
// the path that has errors in it.
|
||||||
|
if constexpr (requires { out.pop_back(); }) {
|
||||||
|
static_cast<void>(out.pop_back());
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value_type temp;
|
||||||
|
if (auto const err = v.get<value_type>().get(temp); err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
concepts::emplace_one(out, std::move(temp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This CPO (Customization Point Object) will help deserialize into
|
||||||
|
* smart pointers.
|
||||||
|
*
|
||||||
|
* If constructing T is nothrow, this conversion should be nothrow as well since
|
||||||
|
* we return MEMALLOC if we're not able to allocate memory instead of throwing
|
||||||
|
* the error message.
|
||||||
|
*
|
||||||
|
* @tparam T The type inside the smart pointer
|
||||||
|
* @tparam ValT document/value type
|
||||||
|
* @param val document/value
|
||||||
|
* @param out a reference to the smart pointer
|
||||||
|
* @return status of the conversion
|
||||||
|
*/
|
||||||
|
template <concepts::smart_pointer T, typename ValT>
|
||||||
|
requires(!require_custom_serialization<T>)
|
||||||
|
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable<typename std::remove_cvref_t<T>::element_type, ValT>) {
|
||||||
|
using element_type = typename std::remove_cvref_t<T>::element_type;
|
||||||
|
|
||||||
|
// For better error messages, don't use these as constraints on
|
||||||
|
// the tag_invoke CPO.
|
||||||
|
static_assert(
|
||||||
|
deserializable<element_type, ValT>,
|
||||||
|
"The specified type inside the unique_ptr must itself be deserializable");
|
||||||
|
static_assert(
|
||||||
|
std::is_default_constructible_v<element_type>,
|
||||||
|
"The specified type inside the unique_ptr must default constructible.");
|
||||||
|
|
||||||
|
auto ptr = new (std::nothrow) element_type();
|
||||||
|
if (ptr == nullptr) {
|
||||||
|
return MEMALLOC;
|
||||||
|
}
|
||||||
|
SIMDJSON_TRY(val.template get<element_type>(*ptr));
|
||||||
|
out.reset(ptr);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This CPO (Customization Point Object) will help deserialize into optional types.
|
||||||
|
*/
|
||||||
|
template <concepts::optional_type T, typename ValT>
|
||||||
|
requires(!require_custom_serialization<T>)
|
||||||
|
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable<typename std::remove_cvref_t<T>::value_type, ValT>) {
|
||||||
|
using value_type = typename std::remove_cvref_t<T>::value_type;
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
deserializable<value_type, ValT>,
|
||||||
|
"The specified type inside the unique_ptr must itself be deserializable");
|
||||||
|
static_assert(
|
||||||
|
std::is_default_constructible_v<value_type>,
|
||||||
|
"The specified type inside the unique_ptr must default constructible.");
|
||||||
|
|
||||||
|
if (!out) {
|
||||||
|
out.emplace();
|
||||||
|
}
|
||||||
|
SIMDJSON_TRY(val.template get<value_type>(out.value()));
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace simdjson
|
||||||
|
|
||||||
|
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
|
||||||
|
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
@@ -80,6 +80,7 @@ simdjson_inline simdjson_result<bool> value::get_bool() noexcept {
|
|||||||
simdjson_inline simdjson_result<bool> value::is_null() noexcept {
|
simdjson_inline simdjson_result<bool> value::is_null() noexcept {
|
||||||
return iter.is_null();
|
return iter.is_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> simdjson_inline simdjson_result<array> value::get() noexcept { return get_array(); }
|
template<> simdjson_inline simdjson_result<array> value::get() noexcept { return get_array(); }
|
||||||
template<> simdjson_inline simdjson_result<object> value::get() noexcept { return get_object(); }
|
template<> simdjson_inline simdjson_result<object> value::get() noexcept { return get_object(); }
|
||||||
template<> simdjson_inline simdjson_result<raw_json_string> value::get() noexcept { return get_raw_json_string(); }
|
template<> simdjson_inline simdjson_result<raw_json_string> value::get() noexcept { return get_raw_json_string(); }
|
||||||
@@ -90,9 +91,16 @@ template<> simdjson_inline simdjson_result<uint64_t> value::get() noexcept { ret
|
|||||||
template<> simdjson_inline simdjson_result<int64_t> value::get() noexcept { return get_int64(); }
|
template<> simdjson_inline simdjson_result<int64_t> value::get() noexcept { return get_int64(); }
|
||||||
template<> simdjson_inline simdjson_result<bool> value::get() noexcept { return get_bool(); }
|
template<> simdjson_inline simdjson_result<bool> value::get() noexcept { return get_bool(); }
|
||||||
|
|
||||||
template<typename T> simdjson_inline error_code value::get(T &out) noexcept {
|
|
||||||
return get<T>().get(out);
|
template<> simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); }
|
||||||
}
|
template<> simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); }
|
||||||
|
template<> simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); }
|
||||||
|
template<> simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); }
|
||||||
|
template<> simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); }
|
||||||
|
template<> simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); }
|
||||||
|
template<> simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); }
|
||||||
|
template<> simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); }
|
||||||
|
template<> simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); }
|
||||||
|
|
||||||
#if SIMDJSON_EXCEPTIONS
|
#if SIMDJSON_EXCEPTIONS
|
||||||
template <class T>
|
template <class T>
|
||||||
@@ -416,6 +424,12 @@ simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::o
|
|||||||
return first.is_null();
|
return first.is_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<> simdjson_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<typename T> simdjson_inline simdjson_result<T> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get() noexcept {
|
template<typename T> simdjson_inline simdjson_result<T> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get() noexcept {
|
||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.get<T>();
|
return first.get<T>();
|
||||||
@@ -429,11 +443,6 @@ template<> simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::va
|
|||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return std::move(first);
|
return std::move(first);
|
||||||
}
|
}
|
||||||
template<> simdjson_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_type> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::type() noexcept {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_type> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::type() noexcept {
|
||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
@@ -467,7 +476,7 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number> simdj
|
|||||||
template <class T>
|
template <class T>
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator T() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator T() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return static_cast<T>(first);
|
return first.get<T>();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator SIMDJSON_IMPLEMENTATION::ondemand::array() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator SIMDJSON_IMPLEMENTATION::ondemand::array() noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
|
|||||||
@@ -5,12 +5,15 @@
|
|||||||
#include "simdjson/generic/ondemand/base.h"
|
#include "simdjson/generic/ondemand/base.h"
|
||||||
#include "simdjson/generic/implementation_simdjson_result_base.h"
|
#include "simdjson/generic/implementation_simdjson_result_base.h"
|
||||||
#include "simdjson/generic/ondemand/value_iterator.h"
|
#include "simdjson/generic/ondemand/value_iterator.h"
|
||||||
|
#include "simdjson/generic/ondemand/deserialize.h"
|
||||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
namespace simdjson {
|
namespace simdjson {
|
||||||
|
|
||||||
namespace SIMDJSON_IMPLEMENTATION {
|
namespace SIMDJSON_IMPLEMENTATION {
|
||||||
namespace ondemand {
|
namespace ondemand {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An ephemeral JSON value returned during iteration. It is only valid for as long as you do
|
* An ephemeral JSON value returned during iteration. It is only valid for as long as you do
|
||||||
* not access more data in the JSON document.
|
* not access more data in the JSON document.
|
||||||
@@ -35,16 +38,21 @@ public:
|
|||||||
* @returns A value of the given type, parsed from the JSON.
|
* @returns A value of the given type, parsed from the JSON.
|
||||||
* @returns INCORRECT_TYPE If the JSON value is not the given type.
|
* @returns INCORRECT_TYPE If the JSON value is not the given type.
|
||||||
*/
|
*/
|
||||||
template<typename T> simdjson_inline simdjson_result<T> get() noexcept {
|
template <typename T>
|
||||||
// Unless the simdjson library or the user provides an inline implementation, calling this method should
|
simdjson_inline simdjson_result<T> get()
|
||||||
// immediately fail.
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
|
noexcept(custom_deserializable<T, value> ? nothrow_custom_deserializable<T, value> : true)
|
||||||
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
|
#else
|
||||||
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
|
noexcept
|
||||||
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
|
#endif
|
||||||
" You may also add support for custom types, see our documentation.");
|
{
|
||||||
|
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
|
||||||
|
T out{};
|
||||||
|
SIMDJSON_TRY(get<T>(out));
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get this value as the given type.
|
* Get this value as the given type.
|
||||||
*
|
*
|
||||||
@@ -54,7 +62,32 @@ public:
|
|||||||
* @returns INCORRECT_TYPE If the JSON value is not an object.
|
* @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.
|
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
|
||||||
*/
|
*/
|
||||||
template<typename T> simdjson_inline error_code get(T &out) noexcept;
|
template <typename T>
|
||||||
|
simdjson_inline error_code get(T &out)
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
noexcept(custom_deserializable<T, value> ? nothrow_custom_deserializable<T, value> : true)
|
||||||
|
#else
|
||||||
|
noexcept
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
if constexpr (custom_deserializable<T, value>) {
|
||||||
|
return deserialize(*this, out);
|
||||||
|
} else {
|
||||||
|
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
// Unless the simdjson library or the user provides an inline implementation, calling this method should
|
||||||
|
// immediately fail.
|
||||||
|
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
|
||||||
|
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
|
||||||
|
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
|
||||||
|
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
|
||||||
|
" You may also add support for custom types, see our documentation.");
|
||||||
|
static_cast<void>(out); // to get rid of unused errors
|
||||||
|
return UNINITIALIZED;
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cast this JSON value to an array.
|
* Cast this JSON value to an array.
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ public:
|
|||||||
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
* must be an unescaped quote terminating the string. It returns the final output
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
||||||
* then null_nullptrptr is returned. It is assumed that the output buffer is large
|
* then null_ptr is returned. It is assumed that the output buffer is large
|
||||||
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
||||||
* SIMDJSON_PADDING bytes.
|
* SIMDJSON_PADDING bytes.
|
||||||
*
|
*
|
||||||
@@ -123,7 +123,7 @@ public:
|
|||||||
* Unescape a NON-valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
* Unescape a NON-valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
* must be an unescaped quote terminating the string. It returns the final output
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
||||||
* then null_nullptrptr is returned. It is assumed that the output buffer is large
|
* then null_ptr is returned. It is assumed that the output buffer is large
|
||||||
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
||||||
* SIMDJSON_PADDING bytes.
|
* SIMDJSON_PADDING bytes.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#define SIMDJSON_SIMDJSON_VERSION_H
|
#define SIMDJSON_SIMDJSON_VERSION_H
|
||||||
|
|
||||||
/** The version of simdjson being used (major.minor.revision) */
|
/** The version of simdjson being used (major.minor.revision) */
|
||||||
#define SIMDJSON_VERSION "3.9.4"
|
#define SIMDJSON_VERSION "3.10.0"
|
||||||
|
|
||||||
namespace simdjson {
|
namespace simdjson {
|
||||||
enum {
|
enum {
|
||||||
@@ -15,11 +15,11 @@ enum {
|
|||||||
/**
|
/**
|
||||||
* The minor version (major.MINOR.revision) of simdjson being used.
|
* The minor version (major.MINOR.revision) of simdjson being used.
|
||||||
*/
|
*/
|
||||||
SIMDJSON_VERSION_MINOR = 9,
|
SIMDJSON_VERSION_MINOR = 10,
|
||||||
/**
|
/**
|
||||||
* The revision (major.minor.REVISION) of simdjson being used.
|
* The revision (major.minor.REVISION) of simdjson being used.
|
||||||
*/
|
*/
|
||||||
SIMDJSON_VERSION_REVISION = 4
|
SIMDJSON_VERSION_REVISION = 0
|
||||||
};
|
};
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|
||||||
|
|||||||
+165
-23
@@ -1,4 +1,4 @@
|
|||||||
/* auto-generated on 2024-06-11 14:08:20 -0400. Do not edit! */
|
/* auto-generated on 2024-09-30 10:57:33 -0400. Do not edit! */
|
||||||
/* including simdjson.cpp: */
|
/* including simdjson.cpp: */
|
||||||
/* begin file simdjson.cpp */
|
/* begin file simdjson.cpp */
|
||||||
#define SIMDJSON_SRC_SIMDJSON_CPP
|
#define SIMDJSON_SRC_SIMDJSON_CPP
|
||||||
@@ -40,6 +40,16 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// C++ 23
|
||||||
|
#if !defined(SIMDJSON_CPLUSPLUS23) && (SIMDJSON_CPLUSPLUS >= 202302L)
|
||||||
|
#define SIMDJSON_CPLUSPLUS23 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// C++ 20
|
||||||
|
#if !defined(SIMDJSON_CPLUSPLUS20) && (SIMDJSON_CPLUSPLUS >= 202002L)
|
||||||
|
#define SIMDJSON_CPLUSPLUS20 1
|
||||||
|
#endif
|
||||||
|
|
||||||
// C++ 17
|
// C++ 17
|
||||||
#if !defined(SIMDJSON_CPLUSPLUS17) && (SIMDJSON_CPLUSPLUS >= 201703L)
|
#if !defined(SIMDJSON_CPLUSPLUS17) && (SIMDJSON_CPLUSPLUS >= 201703L)
|
||||||
#define SIMDJSON_CPLUSPLUS17 1
|
#define SIMDJSON_CPLUSPLUS17 1
|
||||||
@@ -67,6 +77,18 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __has_include
|
||||||
|
#if __has_include(<version>)
|
||||||
|
#include <version>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cpp_concepts
|
||||||
|
#include <utility>
|
||||||
|
#define SIMDJSON_SUPPORTS_DESERIALIZATION 1
|
||||||
|
#else // __cpp_concepts
|
||||||
|
#define SIMDJSON_SUPPORTS_DESERIALIZATION 0
|
||||||
|
#endif
|
||||||
#endif // SIMDJSON_COMPILER_CHECK_H
|
#endif // SIMDJSON_COMPILER_CHECK_H
|
||||||
/* end file simdjson/compiler_check.h */
|
/* end file simdjson/compiler_check.h */
|
||||||
/* including simdjson/portability.h: #include "simdjson/portability.h" */
|
/* including simdjson/portability.h: #include "simdjson/portability.h" */
|
||||||
@@ -224,6 +246,11 @@ using std::size_t;
|
|||||||
#define SIMDJSON_NO_SANITIZE_UNDEFINED
|
#define SIMDJSON_NO_SANITIZE_UNDEFINED
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__clang__) || defined(__GNUC__)
|
||||||
|
#define simdjson_pure [[gnu::pure]]
|
||||||
|
#else
|
||||||
|
#define simdjson_pure
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(__clang__) || defined(__GNUC__)
|
#if defined(__clang__) || defined(__GNUC__)
|
||||||
#if defined(__has_feature)
|
#if defined(__has_feature)
|
||||||
@@ -2643,6 +2670,122 @@ inline const std::string error_message(int error) noexcept;
|
|||||||
#endif // SIMDJSON_ERROR_H
|
#endif // SIMDJSON_ERROR_H
|
||||||
/* end file simdjson/error.h */
|
/* end file simdjson/error.h */
|
||||||
/* skipped duplicate #include "simdjson/portability.h" */
|
/* skipped duplicate #include "simdjson/portability.h" */
|
||||||
|
/* including simdjson/concepts.h: #include "simdjson/concepts.h" */
|
||||||
|
/* begin file simdjson/concepts.h */
|
||||||
|
#ifndef SIMDJSON_CONCEPTS_H
|
||||||
|
#define SIMDJSON_CONCEPTS_H
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
|
||||||
|
#include <concepts>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace simdjson {
|
||||||
|
namespace concepts {
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
#define SIMDJSON_IMPL_CONCEPT(name, method) \
|
||||||
|
template <typename T> \
|
||||||
|
concept supports_##name = !std::is_const_v<T> && requires { \
|
||||||
|
typename std::remove_cvref_t<T>::value_type; \
|
||||||
|
requires requires(typename std::remove_cvref_t<T>::value_type &&val, \
|
||||||
|
T obj) { \
|
||||||
|
obj.method(std::move(val)); \
|
||||||
|
requires !requires { obj = std::move(val); }; \
|
||||||
|
}; \
|
||||||
|
};
|
||||||
|
|
||||||
|
SIMDJSON_IMPL_CONCEPT(emplace_back, emplace_back);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(emplace, emplace);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(push_back, push_back);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(add, add);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(push, push);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(append, append);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(insert, insert);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(op_append, operator+=);
|
||||||
|
|
||||||
|
#undef SIMDJSON_IMPL_CONCEPT
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
/// Check if T is a container that we can append to, including:
|
||||||
|
/// std::vector, std::deque, std::list, std::string, ...
|
||||||
|
template <typename T>
|
||||||
|
concept appendable_containers =
|
||||||
|
details::supports_emplace_back<T> || details::supports_emplace<T> ||
|
||||||
|
details::supports_push_back<T> || details::supports_push<T> ||
|
||||||
|
details::supports_add<T> || details::supports_append<T> ||
|
||||||
|
details::supports_insert<T>;
|
||||||
|
|
||||||
|
/// Insert into the container however possible
|
||||||
|
template <appendable_containers T, typename... Args>
|
||||||
|
constexpr decltype(auto) emplace_one(T &vec, Args &&...args) {
|
||||||
|
if constexpr (details::supports_emplace_back<T>) {
|
||||||
|
return vec.emplace_back(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_emplace<T>) {
|
||||||
|
return vec.emplace(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_push_back<T>) {
|
||||||
|
return vec.push_back(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_push<T>) {
|
||||||
|
return vec.push(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_add<T>) {
|
||||||
|
return vec.add(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_append<T>) {
|
||||||
|
return vec.append(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_insert<T>) {
|
||||||
|
return vec.insert(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_op_append<T> && sizeof...(Args) == 1) {
|
||||||
|
return vec.operator+=(std::forward<Args>(args)...);
|
||||||
|
} else {
|
||||||
|
static_assert(!sizeof(T *),
|
||||||
|
"We don't know how to add things to this container");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This checks if the container will return a reference to the newly added
|
||||||
|
/// element after an insert which for example `std::vector::emplace_back` does
|
||||||
|
/// since C++17; this will allow some optimizations.
|
||||||
|
template <typename T>
|
||||||
|
concept returns_reference = appendable_containers<T> && requires {
|
||||||
|
typename std::remove_cvref_t<T>::reference;
|
||||||
|
requires requires(typename std::remove_cvref_t<T>::value_type &&val, T obj) {
|
||||||
|
{
|
||||||
|
emplace_one(obj, std::move(val))
|
||||||
|
} -> std::same_as<typename std::remove_cvref_t<T>::reference>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept smart_pointer = requires(std::remove_cvref_t<T> ptr) {
|
||||||
|
// Check if T has a member type named element_type
|
||||||
|
typename std::remove_cvref_t<T>::element_type;
|
||||||
|
|
||||||
|
// Check if T has a get() member function
|
||||||
|
{
|
||||||
|
ptr.get()
|
||||||
|
} -> std::same_as<typename std::remove_cvref_t<T>::element_type *>;
|
||||||
|
|
||||||
|
// Check if T can be dereferenced
|
||||||
|
{ *ptr } -> std::same_as<typename std::remove_cvref_t<T>::element_type &>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept optional_type = requires(std::remove_cvref_t<T> obj) {
|
||||||
|
typename std::remove_cvref_t<T>::value_type;
|
||||||
|
{ obj.value() } -> std::same_as<typename std::remove_cvref_t<T>::value_type&>;
|
||||||
|
requires requires(typename std::remove_cvref_t<T>::value_type &&val) {
|
||||||
|
obj.emplace(std::move(val));
|
||||||
|
obj = std::move(val);
|
||||||
|
{
|
||||||
|
obj.value_or(val)
|
||||||
|
} -> std::convertible_to<typename std::remove_cvref_t<T>::value_type>;
|
||||||
|
};
|
||||||
|
{ static_cast<bool>(obj) } -> std::same_as<bool>; // convertible to bool
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace concepts
|
||||||
|
} // namespace simdjson
|
||||||
|
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
#endif // SIMDJSON_CONCEPTS_H
|
||||||
|
/* end file simdjson/concepts.h */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The top level simdjson namespace, containing everything the library provides.
|
* @brief The top level simdjson namespace, containing everything the library provides.
|
||||||
@@ -5949,7 +6092,7 @@ public:
|
|||||||
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
* must be an unescaped quote terminating the string. It returns the final output
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
||||||
* then null_nullptrptr is returned. It is assumed that the output buffer is large
|
* then null_ptr is returned. It is assumed that the output buffer is large
|
||||||
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
||||||
* SIMDJSON_PADDING bytes.
|
* SIMDJSON_PADDING bytes.
|
||||||
*
|
*
|
||||||
@@ -5966,7 +6109,7 @@ public:
|
|||||||
* Unescape a NON-valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
* Unescape a NON-valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
* must be an unescaped quote terminating the string. It returns the final output
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
||||||
* then null_nullptrptr is returned. It is assumed that the output buffer is large
|
* then null_ptr is returned. It is assumed that the output buffer is large
|
||||||
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
||||||
* SIMDJSON_PADDING bytes.
|
* SIMDJSON_PADDING bytes.
|
||||||
*
|
*
|
||||||
@@ -6020,14 +6163,14 @@ public:
|
|||||||
*
|
*
|
||||||
* @return Current capacity, in bytes.
|
* @return Current capacity, in bytes.
|
||||||
*/
|
*/
|
||||||
simdjson_inline size_t capacity() const noexcept;
|
simdjson_pure simdjson_inline size_t capacity() const noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum level of nested object and arrays supported by this parser.
|
* The maximum level of nested object and arrays supported by this parser.
|
||||||
*
|
*
|
||||||
* @return Maximum depth, in bytes.
|
* @return Maximum depth, in bytes.
|
||||||
*/
|
*/
|
||||||
simdjson_inline size_t max_depth() const noexcept;
|
simdjson_pure simdjson_inline size_t max_depth() const noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
|
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
|
||||||
@@ -6068,11 +6211,11 @@ simdjson_inline dom_parser_implementation::dom_parser_implementation() noexcept
|
|||||||
simdjson_inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default;
|
simdjson_inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default;
|
||||||
simdjson_inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default;
|
simdjson_inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default;
|
||||||
|
|
||||||
simdjson_inline size_t dom_parser_implementation::capacity() const noexcept {
|
simdjson_pure simdjson_inline size_t dom_parser_implementation::capacity() const noexcept {
|
||||||
return _capacity;
|
return _capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
simdjson_inline size_t dom_parser_implementation::max_depth() const noexcept {
|
simdjson_pure simdjson_inline size_t dom_parser_implementation::max_depth() const noexcept {
|
||||||
return _max_depth;
|
return _max_depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6428,7 +6571,6 @@ extern SIMDJSON_DLLIMPORTEXPORT const uint64_t thintable_epi8[256];
|
|||||||
|
|
||||||
#endif // SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H
|
#endif // SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H
|
||||||
/* end file simdjson/internal/simdprune_tables.h */
|
/* end file simdjson/internal/simdprune_tables.h */
|
||||||
|
|
||||||
#endif // SIMDJSON_GENERIC_DEPENDENCIES_H
|
#endif // SIMDJSON_GENERIC_DEPENDENCIES_H
|
||||||
/* end file simdjson/generic/dependencies.h */
|
/* end file simdjson/generic/dependencies.h */
|
||||||
/* including generic/dependencies.h: #include <generic/dependencies.h> */
|
/* including generic/dependencies.h: #include <generic/dependencies.h> */
|
||||||
@@ -12477,7 +12619,7 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
|
|||||||
}
|
}
|
||||||
parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get());
|
parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get());
|
||||||
/***
|
/***
|
||||||
* The On Demand API requires special padding.
|
* The On-Demand API requires special padding.
|
||||||
*
|
*
|
||||||
* This is related to https://github.com/simdjson/simdjson/issues/906
|
* This is related to https://github.com/simdjson/simdjson/issues/906
|
||||||
* Basically, we want to make sure that if the parsing continues beyond the last (valid)
|
* Basically, we want to make sure that if the parsing continues beyond the last (valid)
|
||||||
@@ -13352,7 +13494,7 @@ simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr,
|
|||||||
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
* must be an unescaped quote terminating the string. It returns the final output
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
||||||
* then null_nullptrptr is returned. It is assumed that the output buffer is large
|
* then null_ptr is returned. It is assumed that the output buffer is large
|
||||||
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
||||||
* SIMDJSON_PADDING bytes.
|
* SIMDJSON_PADDING bytes.
|
||||||
*/
|
*/
|
||||||
@@ -18696,7 +18838,7 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
|
|||||||
}
|
}
|
||||||
parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get());
|
parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get());
|
||||||
/***
|
/***
|
||||||
* The On Demand API requires special padding.
|
* The On-Demand API requires special padding.
|
||||||
*
|
*
|
||||||
* This is related to https://github.com/simdjson/simdjson/issues/906
|
* This is related to https://github.com/simdjson/simdjson/issues/906
|
||||||
* Basically, we want to make sure that if the parsing continues beyond the last (valid)
|
* Basically, we want to make sure that if the parsing continues beyond the last (valid)
|
||||||
@@ -19571,7 +19713,7 @@ simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr,
|
|||||||
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
* must be an unescaped quote terminating the string. It returns the final output
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
||||||
* then null_nullptrptr is returned. It is assumed that the output buffer is large
|
* then null_ptr is returned. It is assumed that the output buffer is large
|
||||||
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
||||||
* SIMDJSON_PADDING bytes.
|
* SIMDJSON_PADDING bytes.
|
||||||
*/
|
*/
|
||||||
@@ -24908,7 +25050,7 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
|
|||||||
}
|
}
|
||||||
parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get());
|
parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get());
|
||||||
/***
|
/***
|
||||||
* The On Demand API requires special padding.
|
* The On-Demand API requires special padding.
|
||||||
*
|
*
|
||||||
* This is related to https://github.com/simdjson/simdjson/issues/906
|
* This is related to https://github.com/simdjson/simdjson/issues/906
|
||||||
* Basically, we want to make sure that if the parsing continues beyond the last (valid)
|
* Basically, we want to make sure that if the parsing continues beyond the last (valid)
|
||||||
@@ -25783,7 +25925,7 @@ simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr,
|
|||||||
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
* must be an unescaped quote terminating the string. It returns the final output
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
||||||
* then null_nullptrptr is returned. It is assumed that the output buffer is large
|
* then null_ptr is returned. It is assumed that the output buffer is large
|
||||||
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
||||||
* SIMDJSON_PADDING bytes.
|
* SIMDJSON_PADDING bytes.
|
||||||
*/
|
*/
|
||||||
@@ -31391,7 +31533,7 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
|
|||||||
}
|
}
|
||||||
parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get());
|
parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get());
|
||||||
/***
|
/***
|
||||||
* The On Demand API requires special padding.
|
* The On-Demand API requires special padding.
|
||||||
*
|
*
|
||||||
* This is related to https://github.com/simdjson/simdjson/issues/906
|
* This is related to https://github.com/simdjson/simdjson/issues/906
|
||||||
* Basically, we want to make sure that if the parsing continues beyond the last (valid)
|
* Basically, we want to make sure that if the parsing continues beyond the last (valid)
|
||||||
@@ -32266,7 +32408,7 @@ simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr,
|
|||||||
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
* must be an unescaped quote terminating the string. It returns the final output
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
||||||
* then null_nullptrptr is returned. It is assumed that the output buffer is large
|
* then null_ptr is returned. It is assumed that the output buffer is large
|
||||||
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
||||||
* SIMDJSON_PADDING bytes.
|
* SIMDJSON_PADDING bytes.
|
||||||
*/
|
*/
|
||||||
@@ -38448,7 +38590,7 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
|
|||||||
}
|
}
|
||||||
parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get());
|
parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get());
|
||||||
/***
|
/***
|
||||||
* The On Demand API requires special padding.
|
* The On-Demand API requires special padding.
|
||||||
*
|
*
|
||||||
* This is related to https://github.com/simdjson/simdjson/issues/906
|
* This is related to https://github.com/simdjson/simdjson/issues/906
|
||||||
* Basically, we want to make sure that if the parsing continues beyond the last (valid)
|
* Basically, we want to make sure that if the parsing continues beyond the last (valid)
|
||||||
@@ -39323,7 +39465,7 @@ simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr,
|
|||||||
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
* must be an unescaped quote terminating the string. It returns the final output
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
||||||
* then null_nullptrptr is returned. It is assumed that the output buffer is large
|
* then null_ptr is returned. It is assumed that the output buffer is large
|
||||||
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
||||||
* SIMDJSON_PADDING bytes.
|
* SIMDJSON_PADDING bytes.
|
||||||
*/
|
*/
|
||||||
@@ -44472,7 +44614,7 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
|
|||||||
}
|
}
|
||||||
parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get());
|
parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get());
|
||||||
/***
|
/***
|
||||||
* The On Demand API requires special padding.
|
* The On-Demand API requires special padding.
|
||||||
*
|
*
|
||||||
* This is related to https://github.com/simdjson/simdjson/issues/906
|
* This is related to https://github.com/simdjson/simdjson/issues/906
|
||||||
* Basically, we want to make sure that if the parsing continues beyond the last (valid)
|
* Basically, we want to make sure that if the parsing continues beyond the last (valid)
|
||||||
@@ -45347,7 +45489,7 @@ simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr,
|
|||||||
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
* must be an unescaped quote terminating the string. It returns the final output
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
||||||
* then null_nullptrptr is returned. It is assumed that the output buffer is large
|
* then null_ptr is returned. It is assumed that the output buffer is large
|
||||||
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
||||||
* SIMDJSON_PADDING bytes.
|
* SIMDJSON_PADDING bytes.
|
||||||
*/
|
*/
|
||||||
@@ -50487,7 +50629,7 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
|
|||||||
}
|
}
|
||||||
parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get());
|
parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get());
|
||||||
/***
|
/***
|
||||||
* The On Demand API requires special padding.
|
* The On-Demand API requires special padding.
|
||||||
*
|
*
|
||||||
* This is related to https://github.com/simdjson/simdjson/issues/906
|
* This is related to https://github.com/simdjson/simdjson/issues/906
|
||||||
* Basically, we want to make sure that if the parsing continues beyond the last (valid)
|
* Basically, we want to make sure that if the parsing continues beyond the last (valid)
|
||||||
@@ -51362,7 +51504,7 @@ simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr,
|
|||||||
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
* must be an unescaped quote terminating the string. It returns the final output
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
||||||
* then null_nullptrptr is returned. It is assumed that the output buffer is large
|
* then null_ptr is returned. It is assumed that the output buffer is large
|
||||||
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
||||||
* SIMDJSON_PADDING bytes.
|
* SIMDJSON_PADDING bytes.
|
||||||
*/
|
*/
|
||||||
@@ -54563,7 +54705,7 @@ simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr,
|
|||||||
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
* must be an unescaped quote terminating the string. It returns the final output
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
||||||
* then null_nullptrptr is returned. It is assumed that the output buffer is large
|
* then null_ptr is returned. It is assumed that the output buffer is large
|
||||||
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
||||||
* SIMDJSON_PADDING bytes.
|
* SIMDJSON_PADDING bytes.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+9246
-4838
File diff suppressed because it is too large
Load Diff
@@ -263,7 +263,7 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
|
|||||||
}
|
}
|
||||||
parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get());
|
parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get());
|
||||||
/***
|
/***
|
||||||
* The On Demand API requires special padding.
|
* The On-Demand API requires special padding.
|
||||||
*
|
*
|
||||||
* This is related to https://github.com/simdjson/simdjson/issues/906
|
* This is related to https://github.com/simdjson/simdjson/issues/906
|
||||||
* Basically, we want to make sure that if the parsing continues beyond the last (valid)
|
* Basically, we want to make sure that if the parsing continues beyond the last (valid)
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr,
|
|||||||
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
|
||||||
* must be an unescaped quote terminating the string. It returns the final output
|
* must be an unescaped quote terminating the string. It returns the final output
|
||||||
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
* position as pointer. In case of error (e.g., the string has bad escaped codes),
|
||||||
* then null_nullptrptr is returned. It is assumed that the output buffer is large
|
* then null_ptr is returned. It is assumed that the output buffer is large
|
||||||
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
|
||||||
* SIMDJSON_PADDING bytes.
|
* SIMDJSON_PADDING bytes.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -38,6 +38,25 @@ const size_t AMAZON_CELLPHONES_NDJSON_DOC_COUNT = 793;
|
|||||||
|
|
||||||
namespace number_tests {
|
namespace number_tests {
|
||||||
|
|
||||||
|
bool build(const std::string& json) {
|
||||||
|
simdjson::dom::parser parser;
|
||||||
|
simdjson::dom::element recvdJson;
|
||||||
|
auto error = parser.parse(json.c_str(), json.size()).get(recvdJson);
|
||||||
|
if (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool issue2213() {
|
||||||
|
TEST_START();
|
||||||
|
std::string jsonStr = "[1,2,3,\"4\", {\"a\": 5}]";
|
||||||
|
for (int i = 0; i < 15; ++i) {
|
||||||
|
if (!build(jsonStr)) {
|
||||||
|
TEST_FAIL("The JSON is valid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
bool ground_truth() {
|
bool ground_truth() {
|
||||||
std::cout << __func__ << std::endl;
|
std::cout << __func__ << std::endl;
|
||||||
std::pair<std::string,double> ground_truth[] = {
|
std::pair<std::string,double> ground_truth[] = {
|
||||||
@@ -397,7 +416,8 @@ namespace number_tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool run() {
|
bool run() {
|
||||||
return bomskip() &&
|
return issue2213() &&
|
||||||
|
bomskip() &&
|
||||||
issue2017() &&
|
issue2017() &&
|
||||||
truncated_borderline() &&
|
truncated_borderline() &&
|
||||||
specific_tests() &&
|
specific_tests() &&
|
||||||
@@ -950,6 +970,36 @@ namespace dom_api_tests {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool convert_object_to_element() {
|
||||||
|
std::cout << "Running " << __func__ << std::endl;
|
||||||
|
string json(R"({ "a": 1, "b": 2, "c": 3 })");
|
||||||
|
|
||||||
|
dom::parser parser;
|
||||||
|
dom::object object;
|
||||||
|
dom::element element;
|
||||||
|
ASSERT_SUCCESS( parser.parse(json).get(object) );
|
||||||
|
element = object;
|
||||||
|
ASSERT_EQUAL( element["a"].get_uint64().value_unsafe(), 1 );
|
||||||
|
ASSERT_EQUAL( element["b"].get_uint64().value_unsafe(), 2 );
|
||||||
|
ASSERT_EQUAL( element["c"].get_uint64().value_unsafe(), 3 );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool convert_array_to_element() {
|
||||||
|
std::cout << "Running " << __func__ << std::endl;
|
||||||
|
string json(R"([ 1, 10, 100 ])");
|
||||||
|
|
||||||
|
dom::parser parser;
|
||||||
|
dom::array array;
|
||||||
|
dom::element element;
|
||||||
|
ASSERT_SUCCESS( parser.parse(json).get(array) );
|
||||||
|
element = array;
|
||||||
|
ASSERT_EQUAL( element.at(0).get_uint64().value_unsafe(), 1 );
|
||||||
|
ASSERT_EQUAL( element.at(1).get_uint64().value_unsafe(), 10 );
|
||||||
|
ASSERT_EQUAL( element.at(2).get_uint64().value_unsafe(), 100 );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool string_value() {
|
bool string_value() {
|
||||||
std::cout << "Running " << __func__ << std::endl;
|
std::cout << "Running " << __func__ << std::endl;
|
||||||
string json(R"([ "hi", "has backslash\\" ])");
|
string json(R"([ "hi", "has backslash\\" ])");
|
||||||
@@ -1331,6 +1381,8 @@ namespace dom_api_tests {
|
|||||||
array_iterator_empty() &&
|
array_iterator_empty() &&
|
||||||
object_iterator_advance() &&
|
object_iterator_advance() &&
|
||||||
array_iterator_advance() &&
|
array_iterator_advance() &&
|
||||||
|
convert_object_to_element() &&
|
||||||
|
convert_array_to_element() &&
|
||||||
string_value() &&
|
string_value() &&
|
||||||
numeric_values() &&
|
numeric_values() &&
|
||||||
boolean_values() &&
|
boolean_values() &&
|
||||||
|
|||||||
@@ -2,32 +2,37 @@
|
|||||||
link_libraries(simdjson)
|
link_libraries(simdjson)
|
||||||
include_directories(..)
|
include_directories(..)
|
||||||
add_subdirectory(compilation_failure_tests)
|
add_subdirectory(compilation_failure_tests)
|
||||||
add_cpp_test(ondemand_log_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_car_tag_invoke_deserialization LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_log_error_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_car_deserialization LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_tostring_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_log_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_active_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_log_error_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_array_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_tostring_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_array_error_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_active_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_compilation_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_array_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_document_stream_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_array_error_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_error_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_compilation_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_error_location_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_document_stream_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_json_pointer_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_error_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_json_path_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_error_location_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_key_string_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_json_pointer_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_misc_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_json_path_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_number_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_key_string_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_number_in_string_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_misc_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_object_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_number_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_object_error_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_number_in_string_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_ordering_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_object_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_parse_api_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_object_error_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_readme_examples LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_ordering_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_scalar_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_parse_api_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_to_string LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_readme_examples LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_twitter_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_scalar_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_wrong_type_error_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_to_string LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_iterate_many_csv LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_twitter_tests LABELS ondemand acceptance per_implementation)
|
||||||
|
add_cpp_test(ondemand_wrong_type_error_tests LABELS ondemand acceptance per_implementation)
|
||||||
|
add_cpp_test(ondemand_iterate_many_csv LABELS ondemand acceptance per_implementation)
|
||||||
|
add_cpp_test(ondemand_custom_types_tests LABELS ondemand acceptance per_implementation)
|
||||||
|
add_cpp_test(ondemand_custom_types_document_tests LABELS ondemand acceptance per_implementation)
|
||||||
|
add_cpp_test(ondemand_stl_types_tests LABELS ondemand acceptance per_implementation)
|
||||||
if(NOT SIMDJSON_SANITIZE)
|
if(NOT SIMDJSON_SANITIZE)
|
||||||
add_cpp_test(ondemand_cacheline LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_cacheline LABELS ondemand acceptance per_implementation)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -0,0 +1,263 @@
|
|||||||
|
#include "simdjson.h"
|
||||||
|
#include "test_ondemand.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace simdjson;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom type that we want to parse.
|
||||||
|
*/
|
||||||
|
struct Car {
|
||||||
|
std::string make{};
|
||||||
|
std::string model{};
|
||||||
|
int64_t year{};
|
||||||
|
std::vector<double> tire_pressure{};
|
||||||
|
bool operator==(const Car &other) const {
|
||||||
|
return make == other.make && model == other.model && year == other.year &&
|
||||||
|
tire_pressure == other.tire_pressure;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream &operator<<(std::ostream &os, const Car &c) {
|
||||||
|
os << c.make << " " << c.model << " " << c.year << " ";
|
||||||
|
for (auto p : c.tire_pressure) {
|
||||||
|
os << p << " ";
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
// This code is not necessary if you have a C++20 compliant system:
|
||||||
|
template <>
|
||||||
|
simdjson_inline simdjson_result<std::vector<double>>
|
||||||
|
simdjson::ondemand::value::get() noexcept {
|
||||||
|
ondemand::array array;
|
||||||
|
auto error = get_array().get(array);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
std::vector<double> vec;
|
||||||
|
for (auto v : array) {
|
||||||
|
double val;
|
||||||
|
error = v.get_double().get(val);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
vec.push_back(val);
|
||||||
|
}
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <>
|
||||||
|
simdjson_inline simdjson_result<Car> simdjson::ondemand::value::get() noexcept {
|
||||||
|
ondemand::object obj;
|
||||||
|
auto error = get_object().get(obj);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
Car car;
|
||||||
|
if ((error = obj["make"].get_string(car.make))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["model"].get_string(car.model))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["year"].get_int64().get(car.year))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["tire_pressure"].get<std::vector<double>>().get(
|
||||||
|
car.tire_pressure))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return car;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
simdjson_inline simdjson_result<Car>
|
||||||
|
simdjson::ondemand::document::get() &noexcept {
|
||||||
|
ondemand::object obj;
|
||||||
|
auto error = get_object().get(obj);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
Car car;
|
||||||
|
if ((error = obj["make"].get_string(car.make))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["model"].get_string(car.model))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["year"].get_int64().get(car.year))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["tire_pressure"].get<std::vector<double>>().get(
|
||||||
|
car.tire_pressure))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return car;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <>
|
||||||
|
simdjson_inline simdjson_result<Car>
|
||||||
|
simdjson::ondemand::document_reference::get() &noexcept {
|
||||||
|
ondemand::object obj;
|
||||||
|
auto error = get_object().get(obj);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
Car car;
|
||||||
|
if ((error = obj["make"].get_string(car.make))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["model"].get_string(car.model))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["year"].get_int64().get(car.year))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["tire_pressure"].get<std::vector<double>>().get(
|
||||||
|
car.tire_pressure))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return car;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main_should_compile(void) {
|
||||||
|
padded_string json =
|
||||||
|
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
])"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
[[maybe_unused]] auto doc_error = parser.iterate(json).get(doc);
|
||||||
|
for (auto val : doc) {
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
Car c(val); // an exception may be thrown
|
||||||
|
#else
|
||||||
|
Car c;
|
||||||
|
if (auto error = val.get<Car>().get(c)) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace car_error_tests {
|
||||||
|
bool car_deserialize() {
|
||||||
|
TEST_START();
|
||||||
|
padded_string json =
|
||||||
|
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
])"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
[[maybe_unused]] auto doc_error = parser.iterate(json).get(doc);
|
||||||
|
std::vector<Car> cars;
|
||||||
|
for (auto val : doc) {
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
Car c(val); // an exception may be thrown
|
||||||
|
#else
|
||||||
|
Car c;
|
||||||
|
if (auto error = val.get<Car>().get(c)) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
cars.push_back(c);
|
||||||
|
std::cout << c.make << std::endl;
|
||||||
|
}
|
||||||
|
std::vector<Car> expected = {{"Toyota", "Camry", 2018, {40.1, 39.9}},
|
||||||
|
{"Kia", "Soul", 2012, {30.1, 31.0}},
|
||||||
|
{"Toyota", "Tercel", 1999, {29.8, 30.0}}};
|
||||||
|
ASSERT_EQUAL(cars.size(), expected.size());
|
||||||
|
for (size_t i = 0; i < cars.size(); i++) {
|
||||||
|
ASSERT_EQUAL(cars[i], expected[i]);
|
||||||
|
}
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool car_doc_deserialize() {
|
||||||
|
TEST_START();
|
||||||
|
padded_string json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] }
|
||||||
|
)"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
[[maybe_unused]] auto doc_error = parser.iterate(json).get(doc);
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
Car c(doc); // an exception may be thrown
|
||||||
|
#else
|
||||||
|
Car c;
|
||||||
|
if (auto error = doc.get<Car>().get(c)) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
Car expected = {"Toyota", "Camry", 2018, {40.1, 39.9}};
|
||||||
|
ASSERT_EQUAL(c, expected);
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool car_stream_deserialize() {
|
||||||
|
TEST_START();
|
||||||
|
padded_string json =
|
||||||
|
R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] }
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] }
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
)"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream stream;
|
||||||
|
auto error = parser.iterate_many(json).get(stream);
|
||||||
|
if(error) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
std::vector<Car> cars;
|
||||||
|
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
for(ondemand::document_reference doc : stream) {
|
||||||
|
//Car c(doc); // an exception may be thrown
|
||||||
|
cars.push_back(Car(doc)); // an exception may be thrown
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
for(auto doc : stream) {
|
||||||
|
Car c;
|
||||||
|
if ((error = doc.get<Car>().get(c))) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
cars.push_back(c);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
std::vector<Car> expected = {{"Toyota", "Camry", 2018, {40.1, 39.9}},
|
||||||
|
{"Kia", "Soul", 2012, {30.1, 31.0}},
|
||||||
|
{"Toyota", "Tercel", 1999, {29.8, 30.0}}};
|
||||||
|
ASSERT_EQUAL(cars.size(), expected.size());
|
||||||
|
for (size_t i = 0; i < cars.size(); i++) {
|
||||||
|
ASSERT_EQUAL(cars[i], expected[i]);
|
||||||
|
}
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool run() { return car_stream_deserialize() && car_doc_deserialize() && car_deserialize(); }
|
||||||
|
} // namespace car_error_tests
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
return test_main(argc, argv, car_error_tests::run);
|
||||||
|
}
|
||||||
@@ -0,0 +1,335 @@
|
|||||||
|
#include "simdjson.h"
|
||||||
|
#include "test_ondemand.h"
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
using namespace simdjson;
|
||||||
|
|
||||||
|
#if !SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
printf("This test is only relevant when SIMDJSON_SUPPORTS_DESERIALIZATION is true (C++20)\n");
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom type that we want to parse.
|
||||||
|
*/
|
||||||
|
struct Car {
|
||||||
|
std::string make{};
|
||||||
|
std::string model{};
|
||||||
|
int year{};
|
||||||
|
std::vector<float> tire_pressure{};
|
||||||
|
bool operator==(const Car &other) const {
|
||||||
|
return make == other.make && model == other.model && year == other.year &&
|
||||||
|
tire_pressure == other.tire_pressure;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream &operator<<(std::ostream &os, const Car &c) {
|
||||||
|
os << c.make << " " << c.model << " " << c.year << " ";
|
||||||
|
for (auto p : c.tire_pressure) {
|
||||||
|
os << p << " ";
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace simdjson {
|
||||||
|
// This tag_invoke MUST be inside simdjson namespace
|
||||||
|
template <typename simdjson_value>
|
||||||
|
auto tag_invoke(deserialize_tag, simdjson_value &val, Car& car) {
|
||||||
|
ondemand::object obj;
|
||||||
|
auto error = val.get_object().get(obj);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["make"].get_string(car.make))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["model"].get_string(car.model))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["year"].get(car.year))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["tire_pressure"].get<std::vector<float>>().get(
|
||||||
|
car.tire_pressure))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return simdjson::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// suppose we want to filter out all Toyotas
|
||||||
|
template <typename simdjson_value>
|
||||||
|
auto tag_invoke(deserialize_tag, simdjson_value &val, std::list<Car>& car) {
|
||||||
|
ondemand::array arr;
|
||||||
|
auto error = val.get_array().get(arr);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
for (auto v : arr) {
|
||||||
|
Car c;
|
||||||
|
if ((error = v.get<Car>().get(c))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if(c.make != "Toyota") {
|
||||||
|
car.push_back(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return simdjson::SUCCESS;
|
||||||
|
}
|
||||||
|
} // namespace simdjson
|
||||||
|
|
||||||
|
|
||||||
|
int main_should_compile(void) {
|
||||||
|
padded_string json =
|
||||||
|
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
])"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
[[maybe_unused]] auto doc_error = parser.iterate(json).get(doc);
|
||||||
|
for (auto val : doc) {
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
Car c(val); // an exception may be thrown
|
||||||
|
#else
|
||||||
|
Car c;
|
||||||
|
if (auto error = val.get<Car>().get(c)) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace car_error_tests {
|
||||||
|
bool car_deserialize() {
|
||||||
|
TEST_START();
|
||||||
|
padded_string json =
|
||||||
|
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
])"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
[[maybe_unused]] auto doc_error = parser.iterate(json).get(doc);
|
||||||
|
std::vector<Car> cars;
|
||||||
|
for (auto val : doc) {
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
Car c(val); // an exception may be thrown
|
||||||
|
#else
|
||||||
|
Car c;
|
||||||
|
if (auto error = val.get<Car>().get(c)) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
cars.push_back(c);
|
||||||
|
std::cout << c.make << std::endl;
|
||||||
|
}
|
||||||
|
std::vector<Car> expected = {{"Toyota", "Camry", 2018, {40.1f, 39.9f}},
|
||||||
|
{"Kia", "Soul", 2012, {30.1f, 31.0f}},
|
||||||
|
{"Toyota", "Tercel", 1999, {29.8f, 30.0f}}};
|
||||||
|
ASSERT_EQUAL(cars.size(), expected.size());
|
||||||
|
for (size_t i = 0; i < cars.size(); i++) {
|
||||||
|
ASSERT_EQUAL(cars[i], expected[i]);
|
||||||
|
}
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool vector_car_deserialize() {
|
||||||
|
TEST_START();
|
||||||
|
padded_string json =
|
||||||
|
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
])"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
[[maybe_unused]] auto doc_error = parser.iterate(json).get(doc);
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
#if SIMDJSON_REGULAR_VISUAL_STUDIO
|
||||||
|
std::vector<Car> cars = doc.get<std::vector<Car>>(); // an exception may be thrown
|
||||||
|
#else
|
||||||
|
std::vector<Car> cars(doc); // an exception may be thrown
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
std::vector<Car> cars;
|
||||||
|
if (auto error = doc.get<std::vector<Car>>().get(cars)) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
std::vector<Car> expected = {{"Toyota", "Camry", 2018, {40.1f, 39.9f}},
|
||||||
|
{"Kia", "Soul", 2012, {30.1f, 31.0f}},
|
||||||
|
{"Toyota", "Tercel", 1999, {29.8f, 30.0f}}};
|
||||||
|
ASSERT_EQUAL(cars.size(), expected.size());
|
||||||
|
for (size_t i = 0; i < cars.size(); i++) {
|
||||||
|
ASSERT_EQUAL(cars[i], expected[i]);
|
||||||
|
}
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool list_car_deserialize() {
|
||||||
|
TEST_START();
|
||||||
|
padded_string json =
|
||||||
|
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
])"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
[[maybe_unused]] auto doc_error = parser.iterate(json).get(doc);
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
#if SIMDJSON_REGULAR_VISUAL_STUDIO
|
||||||
|
std::list<Car> cars = doc.get<std::list<Car>>(); // an exception may be thrown
|
||||||
|
#else
|
||||||
|
std::list<Car> cars(doc); // an exception may be thrown
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
std::list<Car> cars;
|
||||||
|
if (auto error = doc.get<std::list<Car>>().get(cars)) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
std::list<Car> expected = {{"Kia", "Soul", 2012, {30.1f, 31.0f}}};
|
||||||
|
ASSERT_EQUAL(cars.size(), expected.size());
|
||||||
|
ASSERT_EQUAL(cars.front(), expected.front());
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool optional_car_deserialize() {
|
||||||
|
TEST_START();
|
||||||
|
padded_string json =
|
||||||
|
R"( { "car1": { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] }
|
||||||
|
})"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
[[maybe_unused]] auto error = parser.iterate(json).get(doc);
|
||||||
|
std::optional<Car> car;
|
||||||
|
error = doc["key not found"].get<std::optional<Car>>().get(car);
|
||||||
|
ASSERT_TRUE(!car);
|
||||||
|
error = doc["car1"].get<std::optional<Car>>().get(car);
|
||||||
|
std::optional<Car> expected = Car{"Toyota", "Camry", 2018, {40.1f, 39.9f}};
|
||||||
|
ASSERT_TRUE(car == expected);
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool car_doc_deserialize() {
|
||||||
|
TEST_START();
|
||||||
|
padded_string json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] }
|
||||||
|
)"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
[[maybe_unused]] auto doc_error = parser.iterate(json).get(doc);
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
Car c(doc); // an exception may be thrown
|
||||||
|
#else
|
||||||
|
Car c;
|
||||||
|
if (auto error = doc.get<Car>().get(c)) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
Car expected = {"Toyota", "Camry", 2018, {40.1f, 39.9f}};
|
||||||
|
ASSERT_EQUAL(c, expected);
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool car_stream_deserialize() {
|
||||||
|
TEST_START();
|
||||||
|
padded_string json =
|
||||||
|
R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] }
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] }
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
)"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream stream;
|
||||||
|
[[maybe_unused]] auto error = parser.iterate_many(json).get(stream);
|
||||||
|
std::vector<Car> cars;
|
||||||
|
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
for(auto doc : stream) {
|
||||||
|
cars.push_back((Car)doc); // an exception may be thrown
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
for(auto doc : stream) {
|
||||||
|
Car c;
|
||||||
|
if ((error = doc.get<Car>().get(c))) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
cars.push_back(c);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
std::vector<Car> expected = {{"Toyota", "Camry", 2018, {40.1f, 39.9f}},
|
||||||
|
{"Kia", "Soul", 2012, {30.1f, 31.0f}},
|
||||||
|
{"Toyota", "Tercel", 1999, {29.8f, 30.0f}}};
|
||||||
|
ASSERT_EQUAL(cars.size(), expected.size());
|
||||||
|
for (size_t i = 0; i < cars.size(); i++) {
|
||||||
|
ASSERT_EQUAL(cars[i], expected[i]);
|
||||||
|
}
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool car_unique_ptr_deserialize() {
|
||||||
|
TEST_START();
|
||||||
|
auto const json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] })"_padded;
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc;
|
||||||
|
[[maybe_unused]] auto error = parser.iterate(json).get(doc);
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
std::unique_ptr<Car> c(doc);
|
||||||
|
#else
|
||||||
|
std::unique_ptr<Car> c;
|
||||||
|
if ((error = doc.get<std::unique_ptr<Car>>().get(c))) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
ASSERT_EQUAL(c->make, "Toyota");
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool run() { return list_car_deserialize()
|
||||||
|
&& optional_car_deserialize()
|
||||||
|
&& car_unique_ptr_deserialize()
|
||||||
|
&& vector_car_deserialize()
|
||||||
|
&& car_stream_deserialize()
|
||||||
|
&& car_doc_deserialize()
|
||||||
|
&& car_deserialize(); }
|
||||||
|
} // namespace car_error_tests
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
return test_main(argc, argv, car_error_tests::run);
|
||||||
|
}
|
||||||
|
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
#include "simdjson.h"
|
||||||
|
#include "test_ondemand.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
namespace doc_custom_types_tests {
|
||||||
|
#if SIMDJSON_EXCEPTIONS && defined(__cpp_concepts)
|
||||||
|
struct Car {
|
||||||
|
std::string make{};
|
||||||
|
std::string model{};
|
||||||
|
int64_t year{};
|
||||||
|
std::vector<double> tire_pressure{};
|
||||||
|
|
||||||
|
friend error_code tag_invoke(simdjson::deserialize_tag,
|
||||||
|
auto &val, Car& car) {
|
||||||
|
simdjson::ondemand::object obj;
|
||||||
|
if (auto const error = val.get_object().get(obj)) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
// Instead of repeatedly obj["something"], we iterate through the object
|
||||||
|
// which we expect to be faster.
|
||||||
|
for (auto field : obj) {
|
||||||
|
simdjson::ondemand::raw_json_string key;
|
||||||
|
if (auto const error = field.key().get(key)) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if (key == "make") {
|
||||||
|
if (auto const error = field.value().get_string(car.make)) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
} else if (key == "model") {
|
||||||
|
if (auto const error = field.value().get_string(car.model)) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
} else if (key == "year") {
|
||||||
|
if (auto const error = field.value().get_int64().get(car.year)) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
} else if (key == "tire_pressure") {
|
||||||
|
if (auto const error = field.value().get<std::vector<double>>().get(
|
||||||
|
car.tire_pressure)) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error_code::SUCCESS;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(simdjson::custom_deserializable<std::unique_ptr<Car>, simdjson::ondemand::value>, "It should be invocable");
|
||||||
|
static_assert(simdjson::custom_deserializable<std::unique_ptr<Car>, simdjson::ondemand::document>, "Tag_invoke should work with document as well.");
|
||||||
|
static_assert(simdjson::custom_deserializable<std::vector<Car>, simdjson::ondemand::value>, "It should be invocable");
|
||||||
|
static_assert(simdjson::custom_deserializable<std::vector<Car>, simdjson::ondemand::document>, "Tag_invoke should work with document as well.");
|
||||||
|
|
||||||
|
bool custom_test() {
|
||||||
|
TEST_START();
|
||||||
|
auto const json = R"( {
|
||||||
|
"make": "Toyota",
|
||||||
|
"model": "Camry",
|
||||||
|
"year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ]
|
||||||
|
} )"_padded;
|
||||||
|
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
|
||||||
|
// creating car directly from "doc":
|
||||||
|
const std::unique_ptr<Car> car(doc);
|
||||||
|
if (car->make != "Toyota") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (car->model != "Camry") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (car->year != 2018) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (car->tire_pressure.size() != 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readme_test() {
|
||||||
|
TEST_START();
|
||||||
|
auto const json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] })"_padded;
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
std::unique_ptr<Car> c(doc);
|
||||||
|
std::cout << c->make << std::endl;
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool simple_document_test() {
|
||||||
|
TEST_START();
|
||||||
|
simdjson::padded_string json =
|
||||||
|
R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] })"_padded;
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
Car c(doc);
|
||||||
|
std::cout << c.make << std::endl;
|
||||||
|
ASSERT_EQUAL(c.make, "Toyota");
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool custom_test_crazier() {
|
||||||
|
TEST_START();
|
||||||
|
simdjson::padded_string json =
|
||||||
|
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
])"_padded;
|
||||||
|
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
#if SIMDJSON_REGULAR_VISUAL_STUDIO
|
||||||
|
std::vector<Car> cars = doc.get<std::vector<Car>>();
|
||||||
|
#else
|
||||||
|
std::vector<Car> cars(doc);
|
||||||
|
#endif
|
||||||
|
for(Car& c : cars) {
|
||||||
|
std::cout << c.year << std::endl;
|
||||||
|
if (c.make != "Toyota" && c.make != "Kia") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (c.model != "Camry" && c.model != "Soul" && c.model != "Tercel") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (c.year != 2018 && c.year != 2012 && c.year != 1999) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (c.tire_pressure.size() != 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool simple_document_test_no_except() {
|
||||||
|
TEST_START();
|
||||||
|
simdjson::padded_string json =
|
||||||
|
R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] })"_padded;
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
Car c;
|
||||||
|
auto error = doc.get(c);
|
||||||
|
if(error) { std::cerr << simdjson::error_message(error); return false; }
|
||||||
|
std::cout << c.make << std::endl;
|
||||||
|
ASSERT_EQUAL(c.make, "Toyota");
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
|
bool run() {
|
||||||
|
return
|
||||||
|
#if SIMDJSON_EXCEPTIONS && defined(__cpp_concepts)
|
||||||
|
readme_test() &&
|
||||||
|
custom_test() &&
|
||||||
|
simple_document_test() &&
|
||||||
|
simple_document_test_no_except() &&
|
||||||
|
custom_test_crazier() &&
|
||||||
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
|
true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace doc_custom_types_tests
|
||||||
|
|
||||||
|
int main(const int argc, char *argv[]) {
|
||||||
|
return test_main(argc, argv, doc_custom_types_tests::run);
|
||||||
|
}
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
#include "simdjson.h"
|
||||||
|
#include "test_ondemand.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
namespace custom_types_tests {
|
||||||
|
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
struct Car {
|
||||||
|
std::string make{};
|
||||||
|
std::string model{};
|
||||||
|
int year{};
|
||||||
|
std::vector<double> tire_pressure{};
|
||||||
|
|
||||||
|
friend simdjson::error_code tag_invoke(simdjson::deserialize_tag, auto &val, Car& car) {
|
||||||
|
simdjson::ondemand::object obj;
|
||||||
|
auto error = val.get_object().get(obj);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
// Instead of repeatedly obj["something"], we iterate through the object
|
||||||
|
// which we expect to be faster.
|
||||||
|
for (auto field : obj) {
|
||||||
|
simdjson::ondemand::raw_json_string key;
|
||||||
|
error = field.key().get(key);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if (key == "make") {
|
||||||
|
error = field.value().get_string(car.make);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
} else if (key == "model") {
|
||||||
|
error = field.value().get_string(car.model);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
} else if (key == "year") {
|
||||||
|
error = field.value().get(car.year);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
} else if (key == "tire_pressure") {
|
||||||
|
error = field.value().get(car.tire_pressure);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return simdjson::SUCCESS;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(simdjson::custom_deserializable<std::unique_ptr<Car>>, "It should be deserializable");
|
||||||
|
|
||||||
|
bool custom_uniqueptr_test() {
|
||||||
|
TEST_START();
|
||||||
|
simdjson::padded_string json =
|
||||||
|
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
])"_padded;
|
||||||
|
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
for (auto val : doc) {
|
||||||
|
std::unique_ptr<Car> c(val);
|
||||||
|
if (c->make != "Toyota" && c->make != "Kia") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (c->model != "Camry" && c->model != "Soul" && c->model != "Tercel") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (c->year != 2018 && c->year != 2012 && c->year != 1999) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (c->tire_pressure.size() != 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool custom_test() {
|
||||||
|
TEST_START();
|
||||||
|
simdjson::padded_string json =
|
||||||
|
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
])"_padded;
|
||||||
|
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
for (auto val : doc) {
|
||||||
|
Car c(val);
|
||||||
|
if (c.make != "Toyota" && c.make != "Kia") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (c.model != "Camry" && c.model != "Soul" && c.model != "Tercel") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (c.year != 2018 && c.year != 2012 && c.year != 1999) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (c.tire_pressure.size() != 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool custom_test_crazy() {
|
||||||
|
TEST_START();
|
||||||
|
simdjson::padded_string json =
|
||||||
|
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
])"_padded;
|
||||||
|
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
for (auto val : doc) {
|
||||||
|
Car c(val);
|
||||||
|
std::cout << c.year << std::endl;
|
||||||
|
if (c.make != "Toyota" && c.make != "Kia") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (c.model != "Camry" && c.model != "Soul" && c.model != "Tercel") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (c.year != 2018 && c.year != 2012 && c.year != 1999) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (c.tire_pressure.size() != 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool custom_no_except() {
|
||||||
|
TEST_START();
|
||||||
|
simdjson::padded_string json =
|
||||||
|
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
])"_padded;
|
||||||
|
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
for (auto val : doc) {
|
||||||
|
Car c;
|
||||||
|
auto error = val.get(c);
|
||||||
|
if(error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
|
bool run() {
|
||||||
|
return
|
||||||
|
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
custom_test() &&
|
||||||
|
custom_uniqueptr_test() &&
|
||||||
|
custom_no_except() &&
|
||||||
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
|
true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace custom_types_tests
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
return test_main(argc, argv, custom_types_tests::run);
|
||||||
|
}
|
||||||
@@ -700,6 +700,22 @@ namespace object_tests {
|
|||||||
}
|
}
|
||||||
return got_key;
|
return got_key;
|
||||||
}));
|
}));
|
||||||
|
SUBTEST("ondemand::unescapedkey(std)", test_ondemand_doc(json, [&](auto doc_result) {
|
||||||
|
ondemand::object object;
|
||||||
|
bool got_key = false;
|
||||||
|
ASSERT_SUCCESS( doc_result.get(object) );
|
||||||
|
for (auto field : object) {
|
||||||
|
std::string keyv;
|
||||||
|
ASSERT_SUCCESS( field.unescaped_key(keyv) );
|
||||||
|
if(keyv == "key") {
|
||||||
|
int64_t value;
|
||||||
|
ASSERT_SUCCESS( field.value().get(value) );
|
||||||
|
ASSERT_EQUAL( value, 1);
|
||||||
|
got_key = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return got_key;
|
||||||
|
}));
|
||||||
SUBTEST("ondemand::rawkey", test_ondemand_doc(json, [&](auto doc_result) {
|
SUBTEST("ondemand::rawkey", test_ondemand_doc(json, [&](auto doc_result) {
|
||||||
ondemand::object object;
|
ondemand::object object;
|
||||||
ASSERT_SUCCESS( doc_result.get(object) );
|
ASSERT_SUCCESS( doc_result.get(object) );
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ struct Car {
|
|||||||
std::vector<double> tire_pressure;
|
std::vector<double> tire_pressure;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifndef __cpp_concepts
|
||||||
template <>
|
template <>
|
||||||
simdjson_inline simdjson_result<std::vector<double>>
|
simdjson_inline simdjson_result<std::vector<double>>
|
||||||
simdjson::ondemand::value::get() noexcept {
|
simdjson::ondemand::value::get() noexcept {
|
||||||
@@ -66,7 +67,7 @@ simdjson::ondemand::value::get() noexcept {
|
|||||||
}
|
}
|
||||||
return vec;
|
return vec;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@@ -75,29 +76,13 @@ simdjson_inline simdjson_result<Car> simdjson::ondemand::value::get() noexcept {
|
|||||||
auto error = get_object().get(obj);
|
auto error = get_object().get(obj);
|
||||||
if (error) { return error; }
|
if (error) { return error; }
|
||||||
Car car;
|
Car car;
|
||||||
// Instead of repeatedly obj["something"], we iterate through the object which
|
if((error = obj["make"].get_string(car.make))) { return error; }
|
||||||
// we expect to be faster.
|
if((error = obj["model"].get_string(car.model))) { return error; }
|
||||||
for (auto field : obj) {
|
if((error = obj["year"].get_int64().get(car.year))) { return error; }
|
||||||
raw_json_string key;
|
if((error = obj["tire_pressure"].get<std::vector<double>>().get(car.tire_pressure))) { return error; }
|
||||||
if (error = field.key().get(key); error) { return error; }
|
|
||||||
if (key == "make") {
|
|
||||||
error = field.value().get_string(car.make);
|
|
||||||
if(error) { return error; }
|
|
||||||
} else if (key == "model") {
|
|
||||||
error = field.value().get_string(car.model);
|
|
||||||
if(error) { return error; }
|
|
||||||
} else if (key == "year") {
|
|
||||||
error = field.value().get_int64().get(car.year);
|
|
||||||
if(error) { return error; }
|
|
||||||
} else if (key == "tire_pressure") {
|
|
||||||
error = field.value().get<std::vector<double>>().get(car.tire_pressure);
|
|
||||||
if (error) { return error; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return car;
|
return car;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int custom_type_without_exceptions() {
|
int custom_type_without_exceptions() {
|
||||||
padded_string json = R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
padded_string json = R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
"tire_pressure": [ 40.1, 39.9 ] },
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
@@ -193,8 +178,8 @@ int custom_type_with_exceptions() {
|
|||||||
])"_padded;
|
])"_padded;
|
||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
ondemand::document doc = parser.iterate(json);
|
ondemand::document doc = parser.iterate(json);
|
||||||
for (auto val : doc) {
|
for (auto value : doc) {
|
||||||
Car c(val);
|
Car c(value);
|
||||||
std::cout << c.make << std::endl;
|
std::cout << c.make << std::endl;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1557,6 +1542,29 @@ bool allow_comma_separated_example() {
|
|||||||
}
|
}
|
||||||
TEST_SUCCEED();
|
TEST_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool issue2215() {
|
||||||
|
TEST_START();
|
||||||
|
ondemand::parser parser;
|
||||||
|
const padded_string json = R"({ "parent": {"child1": {"name": "John"} , "child2": {"name": "Daniel"}} })"_padded;
|
||||||
|
auto doc = parser.iterate(json);
|
||||||
|
ondemand::object parent = doc["parent"];
|
||||||
|
// parent owns the focus
|
||||||
|
ondemand::object c1 = parent["child1"];
|
||||||
|
// c1 owns the focus
|
||||||
|
//
|
||||||
|
std::string_view as1 = c1["name"];
|
||||||
|
// We have that as1 == "John", as long as 'parser' and 'json' live
|
||||||
|
// c2 attempts to grab the focus from parent but fails
|
||||||
|
ondemand::object c2 = parent["child2"];
|
||||||
|
// c2 owns the focus, at this point c1 is invalid
|
||||||
|
std::string_view as2 = c2["name"];
|
||||||
|
// We have that as2 == "Daniel", as long as 'parser' and 'json' live
|
||||||
|
ASSERT_EQUAL(as1, "John");
|
||||||
|
ASSERT_EQUAL(as2, "Daniel");
|
||||||
|
std::cout << as1 << " " << as2 << std::endl;
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
bool test_load_example() {
|
bool test_load_example() {
|
||||||
TEST_START();
|
TEST_START();
|
||||||
@@ -1918,6 +1926,7 @@ bool run() {
|
|||||||
&& current_location_no_error()
|
&& current_location_no_error()
|
||||||
&& to_string_example_no_except()
|
&& to_string_example_no_except()
|
||||||
#if SIMDJSON_EXCEPTIONS
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
&& issue2215()
|
||||||
&& to_string_example()
|
&& to_string_example()
|
||||||
&& raw_string()
|
&& raw_string()
|
||||||
&& number_tests()
|
&& number_tests()
|
||||||
|
|||||||
@@ -0,0 +1,257 @@
|
|||||||
|
#include "simdjson.h"
|
||||||
|
#include "test_ondemand.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
|
#include <set>
|
||||||
|
#include <stack>
|
||||||
|
#include <queue>
|
||||||
|
#include <deque>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
class Array : public std::vector<float> {};
|
||||||
|
static_assert(simdjson::concepts::appendable_containers<Array>, "Array must be appendable_containers");
|
||||||
|
static_assert(!simdjson::require_custom_serialization<Array>);
|
||||||
|
namespace simdjson {
|
||||||
|
// This tag_invoke MUST be inside simdjson namespace
|
||||||
|
template <typename simdjson_value>
|
||||||
|
auto tag_invoke(deserialize_tag, simdjson_value &val, Array& out) {
|
||||||
|
simdjson::ondemand::array arr;
|
||||||
|
SIMDJSON_TRY(val.get_array().get(arr));
|
||||||
|
for (auto v : arr) {
|
||||||
|
float temp;
|
||||||
|
if (auto const err = v.get<float>().get(temp); err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
out.push_back(temp);
|
||||||
|
out.push_back(temp); // adding it twice
|
||||||
|
}
|
||||||
|
return error_code::SUCCESS;
|
||||||
|
}
|
||||||
|
} // namespace simdjson
|
||||||
|
|
||||||
|
// require_custom_serialization
|
||||||
|
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
|
||||||
|
namespace stl_types {
|
||||||
|
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
|
||||||
|
bool basic_general_madness() {
|
||||||
|
TEST_START();
|
||||||
|
simdjson::padded_string json =
|
||||||
|
R"( [ { "codes": [1,2,3,4,18,19] },
|
||||||
|
{ "codes": [1,3,4,111,1111] } ])"_padded;
|
||||||
|
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
std::vector<uint16_t> codes;
|
||||||
|
for (auto val : doc) {
|
||||||
|
simdjson::ondemand::object obj;
|
||||||
|
SIMDJSON_TRY(val.get_object().get(obj));
|
||||||
|
obj["codes"].get(codes); // append to it
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codes.size() != 11) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (codes[5] != 19 || codes[7] != 3 || codes[9] != 111) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool basic_general_madness_vector() {
|
||||||
|
TEST_START();
|
||||||
|
simdjson::padded_string json =
|
||||||
|
R"( [ { "codes": [1.2, 3.4, 5.6, 7.8, 9.0] },
|
||||||
|
{ "codes": [2.2, 4.4, 6.6, 8.8, 10.0] } ])"_padded;
|
||||||
|
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
std::vector<float> codes;
|
||||||
|
for (auto val : doc) {
|
||||||
|
simdjson::ondemand::object obj;
|
||||||
|
SIMDJSON_TRY(val.get_object().get(obj));
|
||||||
|
obj["codes"].get(codes); // append to it
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codes.size() != 10) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (codes[5] != 2.2f || codes[7] != 6.6f || codes[9] != 10.0f) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool basic_general_madness_list() {
|
||||||
|
TEST_START();
|
||||||
|
simdjson::padded_string json =
|
||||||
|
R"( [ { "codes": [1.2, 3.4, 5.6, 7.8, 9.0] },
|
||||||
|
{ "codes": [2.2, 4.4, 6.6, 8.8, 10.0] } ])"_padded;
|
||||||
|
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
std::list<float> codes;
|
||||||
|
for (auto val : doc) {
|
||||||
|
simdjson::ondemand::object obj;
|
||||||
|
SIMDJSON_TRY(val.get_object().get(obj));
|
||||||
|
obj["codes"].get(codes); // append to it
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codes.size() != 10) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (codes.back() != 10.0f || codes.front() != 1.2f) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool basic_general_madness_Array() {
|
||||||
|
TEST_START();
|
||||||
|
simdjson::padded_string json =
|
||||||
|
R"( [ { "codes": [1.2, 3.4, 5.6, 7.8, 9.0] },
|
||||||
|
{ "codes": [2.2, 4.4, 6.6, 8.8, 10.0] } ])"_padded;
|
||||||
|
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
Array codes;
|
||||||
|
for (auto val : doc) {
|
||||||
|
simdjson::ondemand::object obj;
|
||||||
|
SIMDJSON_TRY(val.get_object().get(obj));
|
||||||
|
obj["codes"].get(codes); // append to it
|
||||||
|
}
|
||||||
|
ASSERT_EQUAL(codes.size(), 20);
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <template <typename...> typename Container, typename ValT = float>
|
||||||
|
bool basic_general_madness_container() {
|
||||||
|
TYPED_TEST_START(Container<ValT>);
|
||||||
|
|
||||||
|
simdjson::padded_string json =
|
||||||
|
R"( [ { "codes": [1.0, 3.4, 5.6, 7.8, 9.0] },
|
||||||
|
{ "codes": [2.2, 4.4, 6.6, 8.8, 10.0] } ])"_padded;
|
||||||
|
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
Container<ValT> codes;
|
||||||
|
for (auto val : doc) {
|
||||||
|
simdjson::ondemand::object obj;
|
||||||
|
SIMDJSON_TRY(val.get_object().get(obj));
|
||||||
|
obj["codes"].get(codes); // append to it
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codes.size() != 10) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if constexpr (requires {codes.front();}) {
|
||||||
|
if constexpr (requires {*codes.front();}) {
|
||||||
|
if (*codes.front() != 1.0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (codes.front() != 1.0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if constexpr (requires {codes.back();} ) {
|
||||||
|
if constexpr (requires {*codes.back();}) {
|
||||||
|
if (*codes.back() != 10.0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (codes.back() != 10.0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
|
bool run() {
|
||||||
|
return
|
||||||
|
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
basic_general_madness() &&
|
||||||
|
basic_general_madness_vector() &&
|
||||||
|
basic_general_madness_list() &&
|
||||||
|
basic_general_madness_Array() &&
|
||||||
|
basic_general_madness_container<std::vector>() &&
|
||||||
|
basic_general_madness_container<std::list>() &&
|
||||||
|
basic_general_madness_container<std::set>() &&
|
||||||
|
basic_general_madness_container<std::stack>() &&
|
||||||
|
basic_general_madness_container<std::queue>() &&
|
||||||
|
basic_general_madness_container<std::deque>() &&
|
||||||
|
basic_general_madness_container<std::priority_queue>() &&
|
||||||
|
basic_general_madness_container<std::unordered_set>() &&
|
||||||
|
basic_general_madness_container<std::multiset>() &&
|
||||||
|
basic_general_madness_container<std::unordered_multiset>() &&
|
||||||
|
// double:
|
||||||
|
basic_general_madness_container<std::vector, double>() &&
|
||||||
|
basic_general_madness_container<std::list, double>() &&
|
||||||
|
basic_general_madness_container<std::set, double>() &&
|
||||||
|
basic_general_madness_container<std::stack, double>() &&
|
||||||
|
basic_general_madness_container<std::queue, double>() &&
|
||||||
|
basic_general_madness_container<std::deque, double>() &&
|
||||||
|
basic_general_madness_container<std::priority_queue, double>() &&
|
||||||
|
basic_general_madness_container<std::unordered_set, double>() &&
|
||||||
|
basic_general_madness_container<std::multiset, double>() &&
|
||||||
|
basic_general_madness_container<std::unordered_multiset, double>() &&
|
||||||
|
// unique_ptr<double>:
|
||||||
|
basic_general_madness_container<std::vector, std::unique_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::list, std::unique_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::set, std::unique_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::stack, std::unique_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::deque, std::unique_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::queue, std::unique_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::priority_queue, std::unique_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::unordered_set, std::unique_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::multiset, std::unique_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::unordered_multiset, std::unique_ptr<double>>() &&
|
||||||
|
// shared_ptr<double>:
|
||||||
|
basic_general_madness_container<std::vector, std::shared_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::list, std::shared_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::set, std::shared_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::stack, std::shared_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::deque, std::shared_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::queue, std::shared_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::priority_queue, std::shared_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::unordered_set, std::shared_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::multiset, std::shared_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::unordered_multiset, std::shared_ptr<double>>() &&
|
||||||
|
// optional<double>:
|
||||||
|
basic_general_madness_container<std::vector, std::optional<double>>() &&
|
||||||
|
basic_general_madness_container<std::list, std::optional<double>>() &&
|
||||||
|
basic_general_madness_container<std::set, std::optional<double>>() &&
|
||||||
|
basic_general_madness_container<std::stack, std::optional<double>>() &&
|
||||||
|
basic_general_madness_container<std::deque, std::optional<double>>() &&
|
||||||
|
basic_general_madness_container<std::queue, std::optional<double>>() &&
|
||||||
|
basic_general_madness_container<std::priority_queue, std::optional<double>>() &&
|
||||||
|
basic_general_madness_container<std::unordered_set, std::optional<double>>() &&
|
||||||
|
basic_general_madness_container<std::multiset, std::optional<double>>() &&
|
||||||
|
basic_general_madness_container<std::unordered_multiset, std::optional<double>>() &&
|
||||||
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
|
true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace stl_types
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
return test_main(argc, argv, stl_types::run);
|
||||||
|
}
|
||||||
@@ -110,6 +110,7 @@ simdjson_inline bool assert_iterate_error(T &arr, simdjson::error_code expected,
|
|||||||
return assert_equal( count, 1, operation );
|
return assert_equal( count, 1, operation );
|
||||||
}
|
}
|
||||||
#define TEST_START() do { std::cout << "> Running " << __func__ << " ..." << std::endl; } while(0);
|
#define TEST_START() do { std::cout << "> Running " << __func__ << " ..." << std::endl; } while(0);
|
||||||
|
#define TYPED_TEST_START(T) do { std::cout << "> Running " << __func__ << "<" << typeid(T).name() << "> ..." << std::endl; } while(0);
|
||||||
#define SUBTEST(NAME, TEST) do { std::cout << " - Subtest " << (NAME) << " ..." << std::endl; if (!(TEST)) { return false; } } while (0);
|
#define SUBTEST(NAME, TEST) do { std::cout << " - Subtest " << (NAME) << " ..." << std::endl; if (!(TEST)) { return false; } } while (0);
|
||||||
#define ASSERT_EQUAL(ACTUAL, EXPECTED) do { if (!::assert_equal ((ACTUAL), (EXPECTED), #ACTUAL)) { return false; } } while (0);
|
#define ASSERT_EQUAL(ACTUAL, EXPECTED) do { if (!::assert_equal ((ACTUAL), (EXPECTED), #ACTUAL)) { return false; } } while (0);
|
||||||
#define ASSERT_RESULT(ACTUAL, EXPECTED) do { if (!::assert_result ((ACTUAL), (EXPECTED), #ACTUAL)) { return false; } } while (0);
|
#define ASSERT_RESULT(ACTUAL, EXPECTED) do { if (!::assert_result ((ACTUAL), (EXPECTED), #ACTUAL)) { return false; } } while (0);
|
||||||
|
|||||||
+2
-2
@@ -31,7 +31,7 @@ int main(int argc, const char *argv[]) {
|
|||||||
cxxopts::Options options(progName, progUsage);
|
cxxopts::Options options(progName, progUsage);
|
||||||
|
|
||||||
options.add_options()
|
options.add_options()
|
||||||
("z,ondemand", "Use On Demand front-end.", cxxopts::value<bool>()->default_value("false"))
|
("z,ondemand", "Use On-Demand front-end.", cxxopts::value<bool>()->default_value("false"))
|
||||||
("d,rawdump", "Dumps the raw content of the tape.", cxxopts::value<bool>()->default_value("false"))
|
("d,rawdump", "Dumps the raw content of the tape.", cxxopts::value<bool>()->default_value("false"))
|
||||||
("f,file", "File name.", cxxopts::value<std::string>())
|
("f,file", "File name.", cxxopts::value<std::string>())
|
||||||
("h,help", "Print usage.")
|
("h,help", "Print usage.")
|
||||||
@@ -92,7 +92,7 @@ int main(int argc, const char *argv[]) {
|
|||||||
}
|
}
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
#ifdef __cpp_exceptions
|
#ifdef __cpp_exceptions
|
||||||
} catch (const cxxopts::OptionException& e) {
|
} catch (const cxxopts::exceptions::option_has_no_value& e) {
|
||||||
std::cout << "error parsing options: " << e.what() << std::endl;
|
std::cout << "error parsing options: " << e.what() << std::endl;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -278,7 +278,7 @@ int main(int argc, const char *argv[]) {
|
|||||||
s.repeated_key_byte_count, s.maximum_depth);
|
s.repeated_key_byte_count, s.maximum_depth);
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
#ifdef __cpp_exceptions
|
#ifdef __cpp_exceptions
|
||||||
} catch (const cxxopts::OptionException& e) {
|
} catch (const cxxopts::exceptions::option_has_no_value& e) {
|
||||||
std::cout << "error parsing options: " << e.what() << std::endl;
|
std::cout << "error parsing options: " << e.what() << std::endl;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -111,7 +111,7 @@ int main(int argc, const char *argv[]) {
|
|||||||
}
|
}
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
#ifdef __cpp_exceptions
|
#ifdef __cpp_exceptions
|
||||||
} catch (const cxxopts::OptionException& e) {
|
} catch (const cxxopts::exceptions::option_has_no_value& e) {
|
||||||
std::cout << "error parsing options: " << e.what() << std::endl;
|
std::cout << "error parsing options: " << e.what() << std::endl;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user