Compare commits
115
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f81cfafd05 | ||
|
|
d6c0098faa | ||
|
|
2943c8eee7 | ||
|
|
d29bd4e051 | ||
|
|
afe5107ded | ||
|
|
780fdbc8af | ||
|
|
3bef3ece93 | ||
|
|
8c6ace2599 | ||
|
|
7e80bd9d26 | ||
|
|
18c06a4817 | ||
|
|
6d818ad052 | ||
|
|
e5c99c8083 | ||
|
|
c0c27e7a46 | ||
|
|
cf2fef1200 | ||
|
|
b17abdbc1f | ||
|
|
2abc6cdf04 | ||
|
|
fa79a54437 | ||
|
|
1b0af3e9e5 | ||
|
|
22a1cabcc7 | ||
|
|
0601785aff | ||
|
|
1781ba55d6 | ||
|
|
a34011f836 | ||
|
|
0d00c72483 | ||
|
|
52e5d44470 | ||
|
|
0b1293cead | ||
|
|
ad7614e873 | ||
|
|
9c7c00ed90 | ||
|
|
43ee2dd1f4 | ||
|
|
f1dae10fb5 | ||
|
|
dfff491a9a | ||
|
|
81c13d409e | ||
|
|
0c6ee56838 | ||
|
|
5838630c05 | ||
|
|
80b1ea198b | ||
|
|
48b8fb5708 | ||
|
|
987f80c71b | ||
|
|
281ae4025d | ||
|
|
3013b7a597 | ||
|
|
40c53dcf72 | ||
|
|
ae8e504ce4 | ||
|
|
d62977e92f | ||
|
|
0c2591bf62 | ||
|
|
a5156878a7 | ||
|
|
44b721b4b6 | ||
|
|
567f49823b | ||
|
|
0bb6e2b745 | ||
|
|
3103d9c4a3 | ||
|
|
8d499a24ed | ||
|
|
ac25ffd844 | ||
|
|
b259614abb | ||
|
|
0f972febe7 | ||
|
|
b65bc83bb3 | ||
|
|
f90945ce5a | ||
|
|
79f6cf065a | ||
|
|
eec9cce70a | ||
|
|
87d0338b4a | ||
|
|
d5c46ba220 | ||
|
|
1f57c9d447 | ||
|
|
44cfd03626 | ||
|
|
e15be2ec09 | ||
|
|
adb6a562a4 | ||
|
|
c25e408111 | ||
|
|
3f47b9667b | ||
|
|
9b24490dd9 | ||
|
|
87c07e0e0f | ||
|
|
084395fdf7 | ||
|
|
3dcfa83076 | ||
|
|
38c7ea47ad | ||
|
|
9b7a337faa | ||
|
|
087db1efcc | ||
|
|
e987c9e080 | ||
|
|
5f5a29fc85 | ||
|
|
c45b561f5a | ||
|
|
6823d6e8f0 | ||
|
|
2128ead3f9 | ||
|
|
f3f7abb9f2 | ||
|
|
264929cce6 | ||
|
|
9e71c7eb65 | ||
|
|
48f0a6355c | ||
|
|
5ae968e200 | ||
|
|
d06752a484 | ||
|
|
0f1f90ee70 | ||
|
|
dee7849cbe | ||
|
|
5766819adb | ||
|
|
42e7cf7783 | ||
|
|
baad827f0d | ||
|
|
5bf10fdc9a | ||
|
|
fc13764864 | ||
|
|
2c6c38c33b | ||
|
|
89f52818da | ||
|
|
8a0679558f | ||
|
|
09879b6b5d | ||
|
|
42ab1fa573 | ||
|
|
624f4b030d | ||
|
|
758b6a76fe | ||
|
|
6052e793c0 | ||
|
|
0c3caee646 | ||
|
|
624660b04e | ||
|
|
d7f97d140d | ||
|
|
e587d3e5fd | ||
|
|
bed7ff801e | ||
|
|
cd787fb0f7 | ||
|
|
88dc1e6ae2 | ||
|
|
18c4935d99 | ||
|
|
f0fc46fa03 | ||
|
|
4dcba03757 | ||
|
|
402cb4f835 | ||
|
|
32d4f7fa33 | ||
|
|
f4aceabef3 | ||
|
|
6952ff6363 | ||
|
|
f5f864298c | ||
|
|
40dbea3de2 | ||
|
|
80e2aae3e2 | ||
|
|
f56f36c188 | ||
|
|
0f6c4dda11 |
No files matched your search
@@ -28,7 +28,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
|
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
|
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -31,13 +31,13 @@ jobs:
|
|||||||
mkdir -p ./pr
|
mkdir -p ./pr
|
||||||
echo ${{ github.event.number }} > ./pr/number
|
echo ${{ github.event.number }} > ./pr/number
|
||||||
echo ${{ github.event.pull_request.user.login }} > ./pr/author
|
echo ${{ github.event.pull_request.user.login }} > ./pr/author
|
||||||
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: pr
|
name: pr
|
||||||
path: pr/
|
path: pr/
|
||||||
|
|
||||||
check:
|
check:
|
||||||
runs-on: ubuntu-26.04
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
MAIN_DIR: ${{ github.workspace }}/main
|
MAIN_DIR: ${{ github.workspace }}/main
|
||||||
INCLUDE_DIR: ${{ github.workspace }}/main/single_include/nlohmann
|
INCLUDE_DIR: ${{ github.workspace }}/main/single_include/nlohmann
|
||||||
@@ -46,7 +46,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
|
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -77,12 +77,13 @@ jobs:
|
|||||||
cd $MAIN_DIR
|
cd $MAIN_DIR
|
||||||
|
|
||||||
# Built from this PR's own tools/macro_builder/main.cpp, not a
|
# Built from this PR's own tools/macro_builder/main.cpp, not a
|
||||||
# develop checkout: unlike amalgamate.py, this tool has no other
|
# develop checkout: unlike amalgamate.py and generate_natvis.py,
|
||||||
# source of truth to check against (its own README documents that
|
# this tool has no other source of truth to check against (its
|
||||||
# it must reproduce macro_scope.hpp byte for byte), so there is
|
# own README documents that it must reproduce macro_scope.hpp
|
||||||
# nothing to gain from checking out a separate copy, and doing so
|
# byte for byte), so there is nothing to gain from checking out a
|
||||||
# would make this step fail on a PR that adds support for a new
|
# separate copy, and doing so would make this step fail on a PR
|
||||||
# dispatch table until that PR itself merges to develop.
|
# that adds support for a new dispatch table until that PR itself
|
||||||
|
# merges to develop.
|
||||||
TMPDIR=$(mktemp -d ./macro_builder_check.XXXXXX)
|
TMPDIR=$(mktemp -d ./macro_builder_check.XXXXXX)
|
||||||
c++ -std=c++11 tools/macro_builder/main.cpp -o "$TMPDIR/macro_builder"
|
c++ -std=c++11 tools/macro_builder/main.cpp -o "$TMPDIR/macro_builder"
|
||||||
"$TMPDIR/macro_builder" > "$TMPDIR/paste.hpp"
|
"$TMPDIR/macro_builder" > "$TMPDIR/paste.hpp"
|
||||||
@@ -145,14 +146,14 @@ jobs:
|
|||||||
- name: Regenerate nlohmann_json.natvis
|
- name: Regenerate nlohmann_json.natvis
|
||||||
run: |
|
run: |
|
||||||
cd $MAIN_DIR
|
cd $MAIN_DIR
|
||||||
# The generator and its template are what this check verifies the
|
# The template is what this check verifies the committed natvis
|
||||||
# committed natvis file against (which namespaces it lists, and
|
# file against, so take it from this PR rather than from the
|
||||||
# what each entry looks like), so run this PR's copy rather than
|
# develop checkout: generate_natvis.py loads the template from its
|
||||||
# the develop checkout's: with develop's, a PR that changes either
|
# own directory, and with develop's copy a PR that changes the
|
||||||
# could never pass. generate_natvis.py loads the template from its
|
# template could never pass. Like the macro_builder step above,
|
||||||
# own directory. Like the macro_builder step above, this only runs
|
# this only renders PR content in a job without credentials.
|
||||||
# PR content in a job without credentials.
|
cp tools/generate_natvis/nlohmann_json.natvis.j2 $NATVIS_TOOL_DIR/
|
||||||
python3 tools/generate_natvis/generate_natvis.py $MAIN_DIR
|
python3 $NATVIS_TOOL_DIR/generate_natvis.py $MAIN_DIR
|
||||||
|
|
||||||
- name: Build patch and check for differences
|
- name: Build patch and check for differences
|
||||||
id: diff
|
id: diff
|
||||||
@@ -172,7 +173,7 @@ jobs:
|
|||||||
# instead of installing the pinned astyle version locally.
|
# instead of installing the pinned astyle version locally.
|
||||||
- name: Upload patch
|
- name: Upload patch
|
||||||
if: steps.diff.outputs.has_diff == 'true'
|
if: steps.diff.outputs.has_diff == 'true'
|
||||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: amalgamation-patch
|
name: amalgamation-patch
|
||||||
path: patch/amalgamation.patch
|
path: patch/amalgamation.patch
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ jobs:
|
|||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
|
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
|
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ jobs:
|
|||||||
dry-run: false
|
dry-run: false
|
||||||
language: c++
|
language: c++
|
||||||
- name: Upload Crash
|
- name: Upload Crash
|
||||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
if: failure() && steps.build.outcome == 'success'
|
if: failure() && steps.build.outcome == 'success'
|
||||||
with:
|
with:
|
||||||
name: artifacts
|
name: artifacts
|
||||||
|
|||||||
@@ -21,13 +21,13 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
CodeQL-Build:
|
CodeQL-Build:
|
||||||
|
|
||||||
runs-on: ubuntu-26.04
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
security-events: write
|
security-events: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
|
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -38,14 +38,14 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
|
uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
|
||||||
with:
|
with:
|
||||||
languages: c-cpp
|
languages: c-cpp
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
|
uses: github/codeql-action/autobuild@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
|
uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
|
||||||
@@ -20,7 +20,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
|
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
|
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
security-events: write
|
security-events: write
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
|
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -47,6 +47,6 @@ jobs:
|
|||||||
output: 'flawfinder_results.sarif'
|
output: 'flawfinder_results.sarif'
|
||||||
|
|
||||||
- name: Upload analysis results to GitHub Security tab
|
- name: Upload analysis results to GitHub Security tab
|
||||||
uses: github/codeql-action/upload-sarif@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
|
uses: github/codeql-action/upload-sarif@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
|
||||||
with:
|
with:
|
||||||
sarif_file: ${{github.workspace}}/flawfinder_results.sarif
|
sarif_file: ${{github.workspace}}/flawfinder_results.sarif
|
||||||
@@ -23,7 +23,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
|
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
|
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
|
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: "Run analysis"
|
- name: "Run analysis"
|
||||||
uses: ossf/scorecard-action@fa7117db83c52d787ae5a89f350999701505d78e # v2.5.0
|
uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
|
||||||
with:
|
with:
|
||||||
results_file: results.sarif
|
results_file: results.sarif
|
||||||
results_format: sarif
|
results_format: sarif
|
||||||
@@ -72,7 +72,7 @@ jobs:
|
|||||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||||
# format to the repository Actions tab.
|
# format to the repository Actions tab.
|
||||||
- name: "Upload artifact"
|
- name: "Upload artifact"
|
||||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: SARIF file
|
name: SARIF file
|
||||||
path: results.sarif
|
path: results.sarif
|
||||||
@@ -80,6 +80,6 @@ jobs:
|
|||||||
|
|
||||||
# Upload the results to GitHub's code scanning dashboard.
|
# Upload the results to GitHub's code scanning dashboard.
|
||||||
- name: "Upload to code-scanning"
|
- name: "Upload to code-scanning"
|
||||||
uses: github/codeql-action/upload-sarif@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
|
uses: github/codeql-action/upload-sarif@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
|
||||||
with:
|
with:
|
||||||
sarif_file: results.sarif
|
sarif_file: results.sarif
|
||||||
@@ -33,10 +33,10 @@ jobs:
|
|||||||
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
|
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
|
||||||
actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status
|
actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status
|
||||||
name: Scan
|
name: Scan
|
||||||
runs-on: ubuntu-26.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
|
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ jobs:
|
|||||||
|
|
||||||
# Upload SARIF file generated in previous step
|
# Upload SARIF file generated in previous step
|
||||||
- name: Upload SARIF file
|
- name: Upload SARIF file
|
||||||
uses: github/codeql-action/upload-sarif@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
|
uses: github/codeql-action/upload-sarif@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
|
||||||
with:
|
with:
|
||||||
sarif_file: semgrep.sarif
|
sarif_file: semgrep.sarif
|
||||||
if: always()
|
if: always()
|
||||||
@@ -16,7 +16,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
|
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -25,20 +25,20 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build --target ci_test_gcc
|
run: cmake --build build --target ci_test_gcc
|
||||||
|
|
||||||
ci_meson_install:
|
ci_meson_install:
|
||||||
runs-on: ubuntu-26.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Check that Meson and pkg-config offer the CMake options
|
- name: Check that Meson and pkg-config offer the CMake options
|
||||||
run: make check_build_options
|
run: make check_build_options
|
||||||
- name: Install Meson
|
- name: Install Meson
|
||||||
@@ -77,10 +77,10 @@ jobs:
|
|||||||
cmake --build build-import-split
|
cmake --build build-import-split
|
||||||
|
|
||||||
ci_infer:
|
ci_infer:
|
||||||
runs-on: ubuntu-26.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
|
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -92,27 +92,27 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build --target ci_infer
|
run: cmake --build build --target ci_infer
|
||||||
- name: Archive Infer report
|
- name: Archive Infer report
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: infer-report
|
name: infer-report
|
||||||
path: ${{ github.workspace }}/build/build_infer/infer-out/report.txt
|
path: ${{ github.workspace }}/build/build_infer/infer-out/report.txt
|
||||||
if-no-files-found: ignore
|
if-no-files-found: ignore
|
||||||
|
|
||||||
ci_static_analysis_ubuntu:
|
ci_static_analysis_ubuntu:
|
||||||
runs-on: ubuntu-26.04
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
target: [ci_test_single_header, ci_cppcheck, ci_cpplint, ci_reproducible_tests, ci_non_git_tests, ci_offline_testdata, ci_reuse_compliance, ci_test_valgrind]
|
target: [ci_test_single_header, ci_cppcheck, ci_cpplint, ci_reproducible_tests, ci_non_git_tests, ci_offline_testdata, ci_reuse_compliance, ci_test_valgrind]
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
|
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -142,7 +142,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -168,17 +168,17 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build --target ${{ matrix.target }}
|
run: cmake --build build --target ${{ matrix.target }}
|
||||||
|
|
||||||
ci_test_coverage:
|
ci_test_coverage:
|
||||||
runs-on: ubuntu-26.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
|
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -197,7 +197,7 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build --target ci_test_coverage
|
run: cmake --build build --target ci_test_coverage
|
||||||
- name: Archive coverage report
|
- name: Archive coverage report
|
||||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: code-coverage-report
|
name: code-coverage-report
|
||||||
path: ${{ github.workspace }}/build/html
|
path: ${{ github.workspace }}/build/html
|
||||||
@@ -244,7 +244,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: CXX=g++-${{ matrix.compiler }} cmake -S . -B build -DJSON_CI=On
|
run: CXX=g++-${{ matrix.compiler }} cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -262,7 +262,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -279,7 +279,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Disable filesystem support (std::filesystem is unusable) for clang 7 / 8 / 9 / 10
|
- name: Disable filesystem support (std::filesystem is unusable) for clang 7 / 8 / 9 / 10
|
||||||
run: echo "JSON_FORCED_GLOBAL_COMPILE_OPTIONS=-DJSON_HAS_FILESYSTEM=0;-DJSON_HAS_EXPERIMENTAL_FILESYSTEM=0" >> "$GITHUB_ENV"
|
run: echo "JSON_FORCED_GLOBAL_COMPILE_OPTIONS=-DJSON_HAS_FILESYSTEM=0;-DJSON_HAS_EXPERIMENTAL_FILESYSTEM=0" >> "$GITHUB_ENV"
|
||||||
if: ${{ matrix.compiler == '7' || matrix.compiler == '8' || matrix.compiler == '9' || matrix.compiler == '10' }}
|
if: ${{ matrix.compiler == '7' || matrix.compiler == '8' || matrix.compiler == '9' || matrix.compiler == '10' }}
|
||||||
@@ -299,7 +299,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -319,7 +319,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build with libc++
|
- name: Build with libc++
|
||||||
@@ -346,7 +346,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -366,7 +366,7 @@ jobs:
|
|||||||
# import-std support. Its opt-in token is CMake-version-specific, so pin
|
# import-std support. Its opt-in token is CMake-version-specific, so pin
|
||||||
# CMake to the version whose token is set in tests/module_cpp20/CMakeLists.txt.
|
# CMake to the version whose token is set in tests/module_cpp20/CMakeLists.txt.
|
||||||
- name: Get pinned CMake and ninja
|
- name: Get pinned CMake and ninja
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
with:
|
with:
|
||||||
cmakeVersion: 4.3.4
|
cmakeVersion: 4.3.4
|
||||||
# Clang: the std library module is provided by libc++ (the image's libstdc++
|
# Clang: the std library module is provided by libc++ (the image's libstdc++
|
||||||
@@ -392,7 +392,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -409,7 +409,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -423,17 +423,17 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build --target ci_nvhpc
|
run: cmake --build build --target ci_nvhpc
|
||||||
|
|
||||||
ci_emscripten:
|
ci_emscripten:
|
||||||
runs-on: ubuntu-26.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
|
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -443,20 +443,20 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -GNinja
|
run: cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -GNinja
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build
|
run: cmake --build build
|
||||||
|
|
||||||
ci_test_documentation:
|
ci_test_documentation:
|
||||||
runs-on: ubuntu-26.04
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
target: [ci_test_examples, ci_test_build_documentation, ci_test_documentation_mermaid]
|
target: [ci_test_examples, ci_test_build_documentation, ci_test_documentation_mermaid]
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@ccd8616d44fd3846e67624a50d5aad6d37bf2d25 # v2.22.1
|
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -465,7 +465,7 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
# the git-revision-date-localized plugin needs the history; a shallow clone makes the strict build fail
|
# the git-revision-date-localized plugin needs the history; a shallow clone makes the strict build fail
|
||||||
fetch-depth: ${{ matrix.target == 'ci_test_build_documentation' && '0' || '1' }}
|
fetch-depth: ${{ matrix.target == 'ci_test_build_documentation' && '0' || '1' }}
|
||||||
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||||
if: matrix.target == 'ci_test_documentation_mermaid'
|
if: matrix.target == 'ci_test_documentation_mermaid'
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 24
|
||||||
@@ -484,7 +484,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Check that the job runs natively on arm64
|
- name: Check that the job runs natively on arm64
|
||||||
run: uname -m && test "$(uname -m)" = aarch64
|
run: uname -m && test "$(uname -m)" = aarch64
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Set extra CXX_FLAGS for latest std_version
|
- name: Set extra CXX_FLAGS for latest std_version
|
||||||
# /wd5285 silences C5285 emitted by the bundled third-party doctest.h, which
|
# /wd5285 silences C5285 emitted by the bundled third-party doctest.h, which
|
||||||
# specializes std::tuple (newly diagnosed by the VS2026 v145 toolset)
|
# specializes std::tuple (newly diagnosed by the VS2026 v145 toolset)
|
||||||
@@ -166,21 +166,19 @@ jobs:
|
|||||||
# link, but the binaries clang 11.0.1 and clang 18.1.8 then produce crash
|
# link, but the binaries clang 11.0.1 and clang 18.1.8 then produce crash
|
||||||
# before doctest prints its first line - 39 of 102 tests on clang 18.
|
# before doctest prints its first line - 39 of 102 tests on clang 18.
|
||||||
# Keep the objects small by splitting the test files instead.
|
# Keep the objects small by splitting the test files instead.
|
||||||
# The tests link with LLD: GNU ld fails on objects with more than 32767
|
# Objects with more than 65535 sections fail to link with "relocation
|
||||||
# sections with "relocation truncated to fit: IMAGE_REL_AMD64_REL32
|
# truncated to fit: IMAGE_REL_AMD64_REL32 against `.rdata'": the MinGW
|
||||||
# against `.rdata'", as it stores the section an associative COMDAT
|
# linker stores the section an associative COMDAT section belongs to in
|
||||||
# section belongs to in 16 bits (x_associated in binutils'
|
# 16 bits (x_associated in binutils' include/coff/internal.h), so it
|
||||||
# include/coff/internal.h) and so discards the jump tables of inline
|
# discards the jump tables of inline functions together with the wrong
|
||||||
# functions together with the wrong function. Each basic_json
|
# function. Each basic_json specialization a test file instantiates adds
|
||||||
# specialization and input type a test file instantiates adds many
|
# many sections, so test a second one in a file of its own.
|
||||||
# sections, so unit-msgpack.cpp and unit-regression2.cpp kept crossing
|
|
||||||
# that limit as the library grew.
|
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build ^
|
run: cmake -S . -B build ^
|
||||||
-DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang++.exe" ^
|
-DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang++.exe" ^
|
||||||
-DCMAKE_CXX_FLAGS="--target=x86_64-w64-mingw32 -stdlib=libstdc++ -pthread" ^
|
-DCMAKE_CXX_FLAGS="--target=x86_64-w64-mingw32 -stdlib=libstdc++ -pthread" ^
|
||||||
-DCMAKE_CXX_FLAGS_DEBUG="-g0" ^
|
-DCMAKE_CXX_FLAGS_DEBUG="-g0" ^
|
||||||
-DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld -lwinpthread" ^
|
-DCMAKE_EXE_LINKER_FLAGS="-lwinpthread" ^
|
||||||
-G"MinGW Makefiles" ^
|
-G"MinGW Makefiles" ^
|
||||||
-DCMAKE_BUILD_TYPE=Debug ^
|
-DCMAKE_BUILD_TYPE=Debug ^
|
||||||
-DJSON_BuildTests=On
|
-DJSON_BuildTests=On
|
||||||
@@ -216,7 +214,7 @@ jobs:
|
|||||||
# import-std support. Its opt-in token is CMake-version-specific, so pin
|
# import-std support. Its opt-in token is CMake-version-specific, so pin
|
||||||
# CMake to the version whose token is set in tests/module_cpp20/CMakeLists.txt.
|
# CMake to the version whose token is set in tests/module_cpp20/CMakeLists.txt.
|
||||||
- name: Get pinned CMake and ninja
|
- name: Get pinned CMake and ninja
|
||||||
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
with:
|
with:
|
||||||
cmakeVersion: 4.3.4
|
cmakeVersion: 4.3.4
|
||||||
# CMAKE_CXX_FLAGS replaces CMake's MSVC defaults (which include /EHsc), and
|
# CMAKE_CXX_FLAGS replaces CMake's MSVC defaults (which include /EHsc), and
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"_comment": "Used by the ci_infer CMake target (#5715 item 4b). fail-on-issue makes CI fail on Infer findings; disable-issue-type is a type-level baseline for the ~174 pre-existing findings (all PULSE_UNNECESSARY_COPY*/PULSE_RESOURCE_LEAK/PULSE_CONST_REFABLE, mostly in test code) triaged in run https://github.com/nlohmann/json/actions/runs/35829411620 on commit 1054b2097, so CI fails only on a NEW issue type. Remove an entry here once its findings have been fixed or explicitly accepted.",
|
"_comment": "Used by the ci_infer CMake target (#5715 item 4b). fail-on-issue makes CI fail on Infer findings; disable-issue-type is a type-level baseline for the ~174 pre-existing findings (all PULSE_UNNECESSARY_COPY*/PULSE_RESOURCE_LEAK/PULSE_CONST_REFABLE, mostly in test code) triaged in run https://github.com/nlohmann/json/actions/runs/35829411620 on commit 1054b2097, so CI fails only on a NEW issue type. Remove an entry here once its findings have been fixed or explicitly accepted.",
|
||||||
"_comment_pulse": "Pulse stops exploring paths after pulse-max-disjuncts (default 20). With the default, basic_json::replace_value() (destroy + assert_invariant) exceeds the limit, Pulse loses the stored type, and reports false NULLPTR_DEREFERENCE findings for get_ptr() results in tests/src/unit-pointer_access.cpp.",
|
"_comment_pulse": "Pulse stops exploring paths after pulse-max-disjuncts (default 20). With the default, basic_json::replace_value() (destroy + assert_invariant) exceeds the limit, Pulse loses the stored type, and reports false NULLPTR_DEREFERENCE findings for get_ptr() results in tests/src/unit-pointer_access.cpp.",
|
||||||
"_comment_view": "Infer's model of std::unique_ptr mixes up values that are returned by value and own one: basic_json_document (parse() results) and the `built` values of the build() helper in tests/src/unit-json_view_builder.cpp. It reports USE_AFTER_DELETE for one of them after the destructor of an unrelated object (e.g. ~built() on line 345 invalidating `shallow` on line 350 of that file, or two documents parsed in one lambda in tests/src/unit-json_view_dump.cpp). AddressSanitizer and Valgrind are clean on the same tests, so reports for the json_view test files are excluded; findings reported in the library headers are still reported.",
|
|
||||||
"pulse-max-disjuncts": 40,
|
"pulse-max-disjuncts": 40,
|
||||||
"fail-on-issue": true,
|
"fail-on-issue": true,
|
||||||
"disable-issue-type": [
|
"disable-issue-type": [
|
||||||
@@ -11,8 +10,5 @@
|
|||||||
"PULSE_UNNECESSARY_COPY_OPTIONAL",
|
"PULSE_UNNECESSARY_COPY_OPTIONAL",
|
||||||
"PULSE_RESOURCE_LEAK",
|
"PULSE_RESOURCE_LEAK",
|
||||||
"PULSE_CONST_REFABLE"
|
"PULSE_CONST_REFABLE"
|
||||||
],
|
|
||||||
"report-block-list-path-regex": [
|
|
||||||
"tests/src/unit-json_view.*\\.cpp"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
+4
-11
@@ -361,10 +361,10 @@ add_custom_target(ci_test_coverage
|
|||||||
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_coverage32
|
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_coverage32
|
||||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_coverage32 && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
|
COMMAND cd ${PROJECT_BINARY_DIR}/build_coverage32 && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
|
||||||
|
|
||||||
COMMAND ${LCOV_TOOL} --directory . --capture --output-file json.info --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 --ignore-errors mismatch --ignore-errors unused --ignore-errors inconsistent
|
COMMAND ${LCOV_TOOL} --directory . --capture --output-file json.info --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 --ignore-errors mismatch --ignore-errors unused
|
||||||
COMMAND ${LCOV_TOOL} -e json.info ${SRC_FILES} --output-file json.info.filtered --rc branch_coverage=1 --ignore-errors unused --ignore-errors inconsistent
|
COMMAND ${LCOV_TOOL} -e json.info ${SRC_FILES} --output-file json.info.filtered --rc branch_coverage=1 --ignore-errors unused
|
||||||
COMMAND ${CMAKE_SOURCE_DIR}/tests/thirdparty/imapdl/filterbr.py json.info.filtered > json.info.filtered.noexcept
|
COMMAND ${CMAKE_SOURCE_DIR}/tests/thirdparty/imapdl/filterbr.py json.info.filtered > json.info.filtered.noexcept
|
||||||
COMMAND ${GENHTML_TOOL} --title "JSON for Modern C++" --legend --demangle-cpp --output-directory html --show-details --branch-coverage --ignore-errors inconsistent json.info.filtered.noexcept
|
COMMAND ${GENHTML_TOOL} --title "JSON for Modern C++" --legend --demangle-cpp --output-directory html --show-details --branch-coverage json.info.filtered.noexcept
|
||||||
|
|
||||||
COMMENT "Compile and test with coverage"
|
COMMENT "Compile and test with coverage"
|
||||||
)
|
)
|
||||||
@@ -488,13 +488,6 @@ add_custom_target(ci_clang_analyze
|
|||||||
# Check code with Cppcheck.
|
# Check code with Cppcheck.
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
# default_defines.hpp expands the Hedley macros to the compiler's feature-test operators, which Cppcheck does not know.
|
|
||||||
# Since Cppcheck 2.20, it rejects their invocations in #if conditions instead of evaluating them as 0.
|
|
||||||
file(WRITE ${CMAKE_BINARY_DIR}/cppcheck_feature_tests.hpp "")
|
|
||||||
foreach(OPERATOR __has_attribute __has_builtin __has_cpp_attribute __has_declspec_attribute __has_extension __has_feature __has_include __has_warning)
|
|
||||||
file(APPEND ${CMAKE_BINARY_DIR}/cppcheck_feature_tests.hpp "#define ${OPERATOR}(x) 0\n")
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
add_custom_target(ci_cppcheck
|
add_custom_target(ci_cppcheck
|
||||||
COMMAND mkdir -p cppcheck
|
COMMAND mkdir -p cppcheck
|
||||||
COMMAND clang -dM -E -x c++ -std=c++11 ${CMAKE_SOURCE_DIR}/include/nlohmann/thirdparty/hedley/hedley.hpp > default_defines.hpp 2> /dev/null
|
COMMAND clang -dM -E -x c++ -std=c++11 ${CMAKE_SOURCE_DIR}/include/nlohmann/thirdparty/hedley/hedley.hpp > default_defines.hpp 2> /dev/null
|
||||||
@@ -503,7 +496,7 @@ add_custom_target(ci_cppcheck
|
|||||||
COMMAND venv_cppcheck/bin/cppcheck --enable=warning --check-level=exhaustive --inline-suppr --inconclusive --force
|
COMMAND venv_cppcheck/bin/cppcheck --enable=warning --check-level=exhaustive --inline-suppr --inconclusive --force
|
||||||
--std=c++11 ${PROJECT_SOURCE_DIR}/include/nlohmann/json.hpp -I ${CMAKE_SOURCE_DIR}/include
|
--std=c++11 ${PROJECT_SOURCE_DIR}/include/nlohmann/json.hpp -I ${CMAKE_SOURCE_DIR}/include
|
||||||
--error-exitcode=1 --relative-paths=${PROJECT_SOURCE_DIR} -j ${N} --include=default_defines.hpp
|
--error-exitcode=1 --relative-paths=${PROJECT_SOURCE_DIR} -j ${N} --include=default_defines.hpp
|
||||||
--include=${CMAKE_BINARY_DIR}/cppcheck_feature_tests.hpp --cppcheck-build-dir=cppcheck
|
--cppcheck-build-dir=cppcheck
|
||||||
-UJSON_CATCH_USER -UJSON_TRY_USER -UJSON_ASSERT -UJSON_INTERNAL_CATCH -UJSON_THROW
|
-UJSON_CATCH_USER -UJSON_TRY_USER -UJSON_ASSERT -UJSON_INTERNAL_CATCH -UJSON_THROW
|
||||||
-DJSON_HAS_CPP_11 -UJSON_HAS_CPP_14 -UJSON_HAS_CPP_17 -UJSON_HAS_CPP_20 -UJSON_HAS_THREE_WAY_COMPARISON
|
-DJSON_HAS_CPP_11 -UJSON_HAS_CPP_14 -UJSON_HAS_CPP_17 -UJSON_HAS_CPP_20 -UJSON_HAS_THREE_WAY_COMPARISON
|
||||||
COMMENT "Check code with Cppcheck"
|
COMMENT "Check code with Cppcheck"
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
cppcheck==1.5.3
|
cppcheck==1.5.1
|
||||||
@@ -132,10 +132,6 @@ outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-by
|
|||||||
- [operator>>](../operator_gtgt.md) - deserialize from stream
|
- [operator>>](../operator_gtgt.md) - deserialize from stream
|
||||||
- [`JSON_STRICT_NUL_HANDLING`](../macros/json_strict_nul_handling.md) - opt in to rejecting a NUL byte in the input
|
- [`JSON_STRICT_NUL_HANDLING`](../macros/json_strict_nul_handling.md) - opt in to rejecting a NUL byte in the input
|
||||||
instead of treating it as end of input
|
instead of treating it as end of input
|
||||||
- [`JSON_DISABLE_STREAM_BULK_SCAN`](../macros/json_disable_stream_bulk_scan.md) - read a `std::istream` one character
|
|
||||||
at a time
|
|
||||||
- [`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md) - read a `FILE*` with the locking stdio
|
|
||||||
functions
|
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
@@ -147,10 +143,6 @@ outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-by
|
|||||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||||
- `JSON_STRICT_NUL_HANDLING` added in version 3.13.0 to optionally reject a NUL byte in the input instead of treating
|
- `JSON_STRICT_NUL_HANDLING` added in version 3.13.0 to optionally reject a NUL byte in the input instead of treating
|
||||||
it as end of input; planned to become the default in version 4.0.0.
|
it as end of input; planned to become the default in version 4.0.0.
|
||||||
- Parsing from an `std::istream` scans the characters in its stream buffer in bulk in version 3.13.0
|
|
||||||
([`JSON_DISABLE_STREAM_BULK_SCAN`](../macros/json_disable_stream_bulk_scan.md)).
|
|
||||||
- Parsing from a `FILE*` locks the file once instead of once per character in version 3.13.0
|
|
||||||
([`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md)).
|
|
||||||
|
|
||||||
!!! warning "Deprecation"
|
!!! warning "Deprecation"
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ To store objects in C++, a type is defined by the template parameters explained
|
|||||||
|
|
||||||
### Default type
|
### Default type
|
||||||
|
|
||||||
With the default values for `ArrayType` (`std::vector`) and `AllocatorType` (`std::allocator`), the default type for
|
With the default values for `ArrayType` (`std::vector`) and `AllocatorType` (`std::allocator`), the default value for
|
||||||
`array_t` is:
|
`array_t` is:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
|||||||
@@ -68,14 +68,6 @@ basic_json(basic_json&& other) noexcept;
|
|||||||
`std::unordered_map`, `std::multimap`, and `std::unordered_multimap` with a `key_type` compatible to `string_t`
|
`std::unordered_map`, `std::multimap`, and `std::unordered_multimap` with a `key_type` compatible to `string_t`
|
||||||
and a `value_type` from which a `basic_json` value can be constructed.
|
and a `value_type` from which a `basic_json` value can be constructed.
|
||||||
- **strings**: `string_t`, string literals, and all compatible string containers can be used.
|
- **strings**: `string_t`, string literals, and all compatible string containers can be used.
|
||||||
|
|
||||||
!!! note "Rvalue containers are moved from"
|
|
||||||
|
|
||||||
If `val` is an rvalue of a compatible array-like or object-like container other than `array_t`/`object_t`
|
|
||||||
(e.g., `json j = std::move(vec);` or `json j(std::vector<std::string>{...});`), the elements are moved into
|
|
||||||
the JSON value instead of copied. Afterwards, the elements of the source container are in a valid but
|
|
||||||
unspecified state (**moved-from**). Keys of associative containers are `const` and therefore still copied.
|
|
||||||
Lvalues and `const` rvalues are copied, and the source is left unchanged.
|
|
||||||
- **numbers**: [`number_integer_t`](number_integer_t.md), [`number_unsigned_t`](number_unsigned_t.md),
|
- **numbers**: [`number_integer_t`](number_integer_t.md), [`number_unsigned_t`](number_unsigned_t.md),
|
||||||
[`number_float_t`](number_float_t.md), and all convertible number types such as `int`, `size_t`, `int64_t`, `float`
|
[`number_float_t`](number_float_t.md), and all convertible number types such as `int`, `size_t`, `int64_t`, `float`
|
||||||
or `double` can be used.
|
or `double` can be used.
|
||||||
@@ -244,9 +236,7 @@ basic_json(basic_json&& other) noexcept;
|
|||||||
2. No-throw guarantee: this constructor never throws exceptions.
|
2. No-throw guarantee: this constructor never throws exceptions.
|
||||||
3. Depends on the called constructor. For types directly supported by the library (i.e., all types for which no
|
3. Depends on the called constructor. For types directly supported by the library (i.e., all types for which no
|
||||||
`to_json()` function was provided), a strong guarantee holds: if an exception is thrown, there are no changes to any
|
`to_json()` function was provided), a strong guarantee holds: if an exception is thrown, there are no changes to any
|
||||||
JSON value. **Exception:** when `val` is an rvalue array-like or object-like container, its elements are moved one
|
JSON value.
|
||||||
by one; if an exception is thrown in the middle of the conversion (e.g., an allocation failure), the JSON value is
|
|
||||||
unchanged, but the source container may be left partially moved-from.
|
|
||||||
4. Depends on the called constructor. For types directly supported by the library (i.e., all types for which no
|
4. Depends on the called constructor. For types directly supported by the library (i.e., all types for which no
|
||||||
`to_json()` function was provided), a strong guarantee holds: if an exception is thrown, there are no changes to any
|
`to_json()` function was provided), a strong guarantee holds: if an exception is thrown, there are no changes to any
|
||||||
JSON value.
|
JSON value.
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ To store boolean values in C++, a type is defined by the template parameter `Bo
|
|||||||
|
|
||||||
### Default type
|
### Default type
|
||||||
|
|
||||||
With the default values for `BooleanType` (`#!cpp bool`), the default type for `boolean_t` is `#!cpp bool`.
|
With the default values for `BooleanType` (`#!cpp bool`), the default value for `boolean_t` is `#!cpp bool`.
|
||||||
|
|
||||||
### Storage
|
### Storage
|
||||||
|
|
||||||
|
|||||||
@@ -93,8 +93,7 @@ Logarithmic in the size of the JSON object.
|
|||||||
|
|
||||||
- [count](count.md) returns the number of occurrences of a key
|
- [count](count.md) returns the number of occurrences of a key
|
||||||
- [contains](contains.md) checks whether a key exists
|
- [contains](contains.md) checks whether a key exists
|
||||||
- [basic_json_view::find](../basic_json_view/find.md) - the same lookup on a zero-copy view (a duplicate key resolves
|
- [basic_json_view::find](../basic_json_view/find.md) - the same lookup on a zero-copy view
|
||||||
to the first member, whereas `parse()` keeps the last)
|
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -51,8 +51,7 @@ Constant.
|
|||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [back](back.md) to access the last element
|
- [back](back.md) to access the last element
|
||||||
- [basic_json_view::front](../basic_json_view/front.md) - the same access on a zero-copy view (document order, not
|
- [basic_json_view::front](../basic_json_view/front.md) - the same access on a zero-copy view
|
||||||
sorted; duplicate keys are visited)
|
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,6 @@ constexpr bool is_number_unsigned() const noexcept;
|
|||||||
This function returns `#!cpp true` if and only if the JSON value is an unsigned integer number. This excludes
|
This function returns `#!cpp true` if and only if the JSON value is an unsigned integer number. This excludes
|
||||||
floating-point and signed integer values.
|
floating-point and signed integer values.
|
||||||
|
|
||||||
If [`number_unsigned_t`](number_unsigned_t.md) is the same type as [`number_integer_t`](number_integer_t.md), every
|
|
||||||
integer is stored as `number_integer_t`, and this function always returns `#!cpp false`.
|
|
||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
`#!cpp true` if type is an unsigned integer number, `#!cpp false` otherwise.
|
`#!cpp true` if type is an unsigned integer number, `#!cpp false` otherwise.
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ When iterating over an array, `key()` will return the index of the element as st
|
|||||||
- [begin](begin.md) returns an iterator to the first element
|
- [begin](begin.md) returns an iterator to the first element
|
||||||
- [end](end.md) returns an iterator to one past the last element
|
- [end](end.md) returns an iterator to one past the last element
|
||||||
- [basic_json_view::items](../basic_json_view/items.md) - the same range on a zero-copy view (`#!cpp const auto`,
|
- [basic_json_view::items](../basic_json_view/items.md) - the same range on a zero-copy view (`#!cpp const auto`,
|
||||||
not `#!cpp const auto&`: items are produced on the fly; document order, not sorted; duplicate keys are visited)
|
not `#!cpp const auto&`: items are produced on the fly)
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ type to use.
|
|||||||
|
|
||||||
### Default type
|
### Default type
|
||||||
|
|
||||||
With the default values for `NumberFloatType` (`double`), the default type for `number_float_t` is `#!cpp double`.
|
With the default values for `NumberFloatType` (`double`), the default value for `number_float_t` is `#!cpp double`.
|
||||||
|
|
||||||
### Default behavior
|
### Default behavior
|
||||||
|
|
||||||
@@ -52,9 +52,8 @@ With the default values for `NumberFloatType` (`double`), the default type for `
|
|||||||
> interoperability can be achieved by implementations that expect no more precision or range than these provide, in the
|
> interoperability can be achieved by implementations that expect no more precision or range than these provide, in the
|
||||||
> sense that implementations will approximate JSON numbers within the expected precision.
|
> sense that implementations will approximate JSON numbers within the expected precision.
|
||||||
|
|
||||||
This implementation does exactly follow this approach, as it uses double precision floating-point numbers by default.
|
This implementation does exactly follow this approach, as it uses double precision floating-point numbers. Note that
|
||||||
Note that with the default `double`, numbers smaller than `-1.79769313486232e+308` or greater than
|
numbers smaller than `-1.79769313486232e+308` or greater than `1.79769313486232e+308` are rejected during parsing with
|
||||||
`1.79769313486232e+308` are rejected during parsing with
|
|
||||||
[`out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406). Non-finite values (NaN and infinity) can
|
[`out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406). Non-finite values (NaN and infinity) can
|
||||||
only be stored by assigning them directly and are serialized to `null`.
|
only be stored by assigning them directly and are serialized to `null`.
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ to use.
|
|||||||
|
|
||||||
### Default type
|
### Default type
|
||||||
|
|
||||||
With the default values for `NumberIntegerType` (`std::int64_t`), the default type for `number_integer_t` is
|
With the default values for `NumberIntegerType` (`std::int64_t`), the default value for `number_integer_t` is
|
||||||
`#!cpp std::int64_t`.
|
`#!cpp std::int64_t`.
|
||||||
|
|
||||||
### Default behavior
|
### Default behavior
|
||||||
|
|||||||
@@ -25,15 +25,14 @@ the type to use.
|
|||||||
`NumberUnsignedType`
|
`NumberUnsignedType`
|
||||||
: the type to store unsigned integers. It must be an **unsigned integral** type (`#!cpp std::is_integral`) with a
|
: the type to store unsigned integers. It must be an **unsigned integral** type (`#!cpp std::is_integral`) with a
|
||||||
`#!cpp std::numeric_limits` specialization, and it must be able to represent the absolute value of every
|
`#!cpp std::numeric_limits` specialization, and it must be able to represent the absolute value of every
|
||||||
[`number_integer_t`](number_integer_t.md) value. Alternatively, it can be the same type as
|
[`number_integer_t`](number_integer_t.md) value. See
|
||||||
[`number_integer_t`](number_integer_t.md); then every integer is stored as `number_integer_t`. See
|
|
||||||
[Template Parameter Requirements](../../features/types/template_parameters.md#numberintegertype-and-numberunsignedtype).
|
[Template Parameter Requirements](../../features/types/template_parameters.md#numberintegertype-and-numberunsignedtype).
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
### Default type
|
### Default type
|
||||||
|
|
||||||
With the default values for `NumberUnsignedType` (`std::uint64_t`), the default type for `number_unsigned_t` is
|
With the default values for `NumberUnsignedType` (`std::uint64_t`), the default value for `number_unsigned_t` is
|
||||||
`#!cpp std::uint64_t`.
|
`#!cpp std::uint64_t`.
|
||||||
|
|
||||||
### Default behavior
|
### Default behavior
|
||||||
@@ -90,4 +89,3 @@ Integer number values are stored directly inside a `basic_json` type.
|
|||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 2.0.0.
|
- Added in version 2.0.0.
|
||||||
- Can be the same type as `number_integer_t` since version 3.13.0.
|
|
||||||
@@ -36,7 +36,7 @@ To store objects in C++, a type is defined by the template parameters described
|
|||||||
### Default type
|
### Default type
|
||||||
|
|
||||||
With the default values for `ObjectType` (`std::map`), `StringType` (`std::string`), and `AllocatorType`
|
With the default values for `ObjectType` (`std::map`), `StringType` (`std::string`), and `AllocatorType`
|
||||||
(`std::allocator`), the default type for `object_t` is:
|
(`std::allocator`), the default value for `object_t` is:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// until C++14
|
// until C++14
|
||||||
|
|||||||
@@ -275,9 +275,8 @@ Strong exception safety: if an exception occurs, the original value stays intact
|
|||||||
- documentation on [runtime assertions](../../features/assertions.md)
|
- documentation on [runtime assertions](../../features/assertions.md)
|
||||||
- see [`at`](at.md) for access by reference with range checking
|
- see [`at`](at.md) for access by reference with range checking
|
||||||
- see [`value`](value.md) for access with default value
|
- see [`value`](value.md) for access with default value
|
||||||
- [basic_json_view::operator[]](../basic_json_view/operator%5B%5D.md) - the same access on a zero-copy view (a duplicate
|
- [basic_json_view::operator[]](../basic_json_view/operator%5B%5D.md) - the same access on a zero-copy view (always
|
||||||
key resolves to the first member, whereas `parse()` keeps the last; a missing key gives a discarded view instead of
|
returns a discarded view instead of assuming undefined behavior)
|
||||||
undefined behavior)
|
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -112,16 +112,6 @@ By default, a `'\0'` (NUL) byte anywhere in the input is treated as end of input
|
|||||||
outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-bytes-in-the-input) for details and the
|
outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-bytes-in-the-input) for details and the
|
||||||
[`JSON_STRICT_NUL_HANDLING`](../macros/json_strict_nul_handling.md) macro to opt into rejecting it instead.
|
[`JSON_STRICT_NUL_HANDLING`](../macros/json_strict_nul_handling.md) macro to opt into rejecting it instead.
|
||||||
|
|
||||||
!!! tip "Performance"
|
|
||||||
|
|
||||||
Contiguous inputs (strings, string views, vectors, arrays, pointer ranges) are the fastest to parse. A
|
|
||||||
`#!cpp std::istream` comes close: the parser scans the characters its stream buffer holds in bulk, without reading
|
|
||||||
ahead, so the stream is left exactly where it would be after reading one character at a time. A `#!cpp FILE*` is
|
|
||||||
read one character at a time, but the file is locked only once, so other threads that use the same `FILE` object
|
|
||||||
wait until parsing is done. See [input sources and performance](../../features/parsing/index.md#input-sources-and-performance),
|
|
||||||
[`JSON_DISABLE_STREAM_BULK_SCAN`](../macros/json_disable_stream_bulk_scan.md), and
|
|
||||||
[`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md).
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example "Example: (1) parse from a character array"
|
??? example "Example: (1) parse from a character array"
|
||||||
@@ -257,10 +247,6 @@ outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-by
|
|||||||
- [operator>>](../operator_gtgt.md) - deserialize from stream
|
- [operator>>](../operator_gtgt.md) - deserialize from stream
|
||||||
- [`JSON_STRICT_NUL_HANDLING`](../macros/json_strict_nul_handling.md) - opt in to rejecting a NUL byte in the input
|
- [`JSON_STRICT_NUL_HANDLING`](../macros/json_strict_nul_handling.md) - opt in to rejecting a NUL byte in the input
|
||||||
instead of treating it as end of input
|
instead of treating it as end of input
|
||||||
- [`JSON_DISABLE_STREAM_BULK_SCAN`](../macros/json_disable_stream_bulk_scan.md) - read a `std::istream` one character
|
|
||||||
at a time
|
|
||||||
- [`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md) - read a `FILE*` with the locking stdio
|
|
||||||
functions
|
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
@@ -275,11 +261,6 @@ outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-by
|
|||||||
it as end of input; planned to become the default in version 4.0.0.
|
it as end of input; planned to become the default in version 4.0.0.
|
||||||
- Extended empty-input detection to also cover an `std::istream` without a stream buffer, and fixed a crash
|
- Extended empty-input detection to also cover an `std::istream` without a stream buffer, and fixed a crash
|
||||||
(`std::terminate`) when parsing from an `std::istream` with `eofbit` in its exception mask, in version 3.13.0.
|
(`std::terminate`) when parsing from an `std::istream` with `eofbit` in its exception mask, in version 3.13.0.
|
||||||
- Parsing from an `std::istream` scans the characters in its stream buffer in bulk in version 3.13.0
|
|
||||||
([`JSON_DISABLE_STREAM_BULK_SCAN`](../macros/json_disable_stream_bulk_scan.md) restores reading one character at a
|
|
||||||
time).
|
|
||||||
- Parsing from a `FILE*` locks the file once instead of once per character in version 3.13.0
|
|
||||||
([`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md) restores `std::fgetc`).
|
|
||||||
|
|
||||||
!!! warning "Deprecation"
|
!!! warning "Deprecation"
|
||||||
|
|
||||||
|
|||||||
@@ -116,10 +116,6 @@ consumer `sax` has a super-linear complexity.
|
|||||||
|
|
||||||
A UTF-8 byte order mark is silently ignored.
|
A UTF-8 byte order mark is silently ignored.
|
||||||
|
|
||||||
When reading from a `#!c FILE*`, the file is locked while it is parsed. The lock is recursive, so the SAX handler may
|
|
||||||
use the same file with stdio functions, but it must not wait for another thread that uses the same `FILE` object; see
|
|
||||||
[`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md).
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
@@ -141,10 +137,6 @@ use the same file with stdio functions, but it must not wait for another thread
|
|||||||
|
|
||||||
- [parse](parse.md) - deserialize from a compatible input
|
- [parse](parse.md) - deserialize from a compatible input
|
||||||
- [accept](accept.md) - check if the input is valid JSON
|
- [accept](accept.md) - check if the input is valid JSON
|
||||||
- [`JSON_DISABLE_STREAM_BULK_SCAN`](../macros/json_disable_stream_bulk_scan.md) - read a `std::istream` one character
|
|
||||||
at a time
|
|
||||||
- [`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md) - read a `FILE*` with the locking stdio
|
|
||||||
functions
|
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
@@ -156,10 +148,6 @@ use the same file with stdio functions, but it must not wait for another thread
|
|||||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||||
- `JSON_PRECISE_STREAM_POSITION` added in version 3.13.0 to optionally leave a `#!cpp std::istream` positioned right
|
- `JSON_PRECISE_STREAM_POSITION` added in version 3.13.0 to optionally leave a `#!cpp std::istream` positioned right
|
||||||
after the parsed value when `strict` is `#!cpp false`.
|
after the parsed value when `strict` is `#!cpp false`.
|
||||||
- Parsing from an `std::istream` scans the characters in its stream buffer in bulk in version 3.13.0
|
|
||||||
([`JSON_DISABLE_STREAM_BULK_SCAN`](../macros/json_disable_stream_bulk_scan.md)).
|
|
||||||
- Parsing from a `FILE*` locks the file once instead of once per character in version 3.13.0
|
|
||||||
([`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md)).
|
|
||||||
|
|
||||||
!!! warning "Deprecation"
|
!!! warning "Deprecation"
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ JSON class into byte-sized characters during deserialization.
|
|||||||
|
|
||||||
### Default type
|
### Default type
|
||||||
|
|
||||||
With the default values for `StringType` (`std::string`), the default type for `string_t` is `#!cpp std::string`.
|
With the default values for `StringType` (`std::string`), the default value for `string_t` is `#!cpp std::string`.
|
||||||
|
|
||||||
### Encoding
|
### Encoding
|
||||||
|
|
||||||
|
|||||||
@@ -124,5 +124,3 @@ Linear in the size of the JSON value `j`.
|
|||||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316`.
|
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316`.
|
||||||
- Throws `type_error.321` for a discarded value since version 3.13.0; previously, a discarded value nested in an
|
- Throws `type_error.321` for a discarded value since version 3.13.0; previously, a discarded value nested in an
|
||||||
array or object was silently skipped, producing invalid BJData.
|
array or object was silently skipped, producing invalid BJData.
|
||||||
- Writes unsigned integers wider than 64 bits as high-precision numbers since version 3.13.0; previously, they were
|
|
||||||
silently truncated to 64 bits.
|
|
||||||
@@ -37,9 +37,8 @@ With (2), the bytes written before the exception remain in the output adapter.
|
|||||||
|
|
||||||
## Exceptions
|
## Exceptions
|
||||||
|
|
||||||
- Throws [out_of_range.407](../../home/exceptions.md#jsonexceptionout_of_range407) if `j` contains an integer outside
|
- Throws [out_of_range.407](../../home/exceptions.md#jsonexceptionout_of_range407) if `j` contains an unsigned integer
|
||||||
the range of int64 (an unsigned integer above 9223372036854775807, or, with a number type wider than 64 bits, any
|
above 9223372036854775807, which BON8 cannot represent
|
||||||
integer beyond int64), which BON8 cannot represent
|
|
||||||
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if `j` contains a string that is not
|
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if `j` contains a string that is not
|
||||||
valid UTF-8
|
valid UTF-8
|
||||||
- Throws [type_error.321](../../home/exceptions.md#jsonexceptiontype_error321) if `j` or a value nested in it is
|
- Throws [type_error.321](../../home/exceptions.md#jsonexceptiontype_error321) if `j` or a value nested in it is
|
||||||
|
|||||||
@@ -47,10 +47,6 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
|
|
||||||
- Throws [`type_error.317`](../../home/exceptions.md#jsonexceptiontype_error317) if the top-level type of the JSON value
|
- Throws [`type_error.317`](../../home/exceptions.md#jsonexceptiontype_error317) if the top-level type of the JSON value
|
||||||
is not an object; example: `"to serialize to BSON, top-level type must be object, but is string"`
|
is not an object; example: `"to serialize to BSON, top-level type must be object, but is string"`
|
||||||
- Throws [`out_of_range.407`](../../home/exceptions.md#jsonexceptionout_of_range407) if `j` contains a signed integer
|
|
||||||
outside the range of int64 or an unsigned integer outside the range of uint64, which is only possible with a number
|
|
||||||
type wider than 64 bits; example:
|
|
||||||
`"integer number 9223372036854775808 cannot be represented by BSON as it does not fit int64"`
|
|
||||||
- Throws [`out_of_range.409`](../../home/exceptions.md#jsonexceptionout_of_range409) if a key in the JSON object contains
|
- Throws [`out_of_range.409`](../../home/exceptions.md#jsonexceptionout_of_range409) if a key in the JSON object contains
|
||||||
a null byte (code point U+0000); example: `"BSON key cannot contain code point U+0000 (at byte 2)"`
|
a null byte (code point U+0000); example: `"BSON key cannot contain code point U+0000 (at byte 2)"`
|
||||||
- Throws [`out_of_range.412`](../../home/exceptions.md#jsonexceptionout_of_range412) if the length of a document, array,
|
- Throws [`out_of_range.412`](../../home/exceptions.md#jsonexceptionout_of_range412) if the length of a document, array,
|
||||||
@@ -123,5 +119,3 @@ pass before anything is written.
|
|||||||
that is not valid UTF-8 unchanged, as before; `strict` (the default if
|
that is not valid UTF-8 unchanged, as before; `strict` (the default if
|
||||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316` before anything
|
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316` before anything
|
||||||
is written.
|
is written.
|
||||||
- Throws `out_of_range.407` for integers that do not fit 64 bits since version 3.13.0; previously, integers of a
|
|
||||||
number type wider than 64 bits were silently truncated.
|
|
||||||
@@ -46,9 +46,6 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
|
|
||||||
## Exceptions
|
## Exceptions
|
||||||
|
|
||||||
- Throws [`out_of_range.407`](../../home/exceptions.md#jsonexceptionout_of_range407) if `j` contains an integer
|
|
||||||
outside [-2^64, 2^64-1], which is only possible with a number type wider than 64 bits; example:
|
|
||||||
`"integer number 18446744073709551616 cannot be represented by CBOR as it does not fit [-2^64, 2^64-1]"`
|
|
||||||
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is
|
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is
|
||||||
not valid UTF-8 and `error_handler` is `strict` (the default only if
|
not valid UTF-8 and `error_handler` is `strict` (the default only if
|
||||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled)
|
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled)
|
||||||
@@ -93,5 +90,3 @@ Linear in the size of the JSON value `j`.
|
|||||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316`.
|
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316`.
|
||||||
- Throws `type_error.321` for a discarded value since version 3.13.0; previously, a discarded value nested in an
|
- Throws `type_error.321` for a discarded value since version 3.13.0; previously, a discarded value nested in an
|
||||||
array or object was silently skipped, producing invalid CBOR.
|
array or object was silently skipped, producing invalid CBOR.
|
||||||
- Throws `out_of_range.407` for integers that do not fit 64 bits since version 3.13.0; previously, integers of a
|
|
||||||
number type wider than 64 bits were silently truncated.
|
|
||||||
@@ -46,9 +46,6 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
|
|
||||||
## Exceptions
|
## Exceptions
|
||||||
|
|
||||||
- Throws [`out_of_range.407`](../../home/exceptions.md#jsonexceptionout_of_range407) if `j` contains an integer
|
|
||||||
outside [-2^63, 2^64-1], which is only possible with a number type wider than 64 bits; example:
|
|
||||||
`"integer number 18446744073709551616 cannot be represented by MessagePack as it does not fit [-2^63, 2^64-1]"`
|
|
||||||
- Throws [`out_of_range.412`](../../home/exceptions.md#jsonexceptionout_of_range412) if the length of a string, binary
|
- Throws [`out_of_range.412`](../../home/exceptions.md#jsonexceptionout_of_range412) if the length of a string, binary
|
||||||
value, array, or object exceeds 4294967295, the maximum MessagePack can store; example:
|
value, array, or object exceeds 4294967295, the maximum MessagePack can store; example:
|
||||||
`"MessagePack length 4294967296 exceeds maximum of 4294967295"`
|
`"MessagePack length 4294967296 exceeds maximum of 4294967295"`
|
||||||
@@ -115,5 +112,3 @@ Linear in the size of the JSON value `j`.
|
|||||||
`number_unsigned_t`.
|
`number_unsigned_t`.
|
||||||
- Throws `type_error.321` for a discarded value since version 3.13.0; previously, a discarded value nested in an
|
- Throws `type_error.321` for a discarded value since version 3.13.0; previously, a discarded value nested in an
|
||||||
array or object was silently skipped, producing invalid MessagePack.
|
array or object was silently skipped, producing invalid MessagePack.
|
||||||
- Throws `out_of_range.407` for integers that do not fit 64 bits since version 3.13.0; previously, integers of a
|
|
||||||
number type wider than 64 bits were silently truncated.
|
|
||||||
@@ -18,10 +18,10 @@ has `erase`; calling it on a read-only `basic_json_document` fails to compile (`
|
|||||||
1. Removes every member of `object` whose key is `key` (see [Notes](#notes) on duplicate keys) and returns how many
|
1. Removes every member of `object` whose key is `key` (see [Notes](#notes) on duplicate keys) and returns how many
|
||||||
were removed; `#!cpp 0` if `object` has no member with this key.
|
were removed; `#!cpp 0` if `object` has no member with this key.
|
||||||
2. Removes the element at index `idx` of `array`, which must already exist (`#!cpp idx < array.size()`).
|
2. Removes the element at index `idx` of `array`, which must already exist (`#!cpp idx < array.size()`).
|
||||||
3. Removes the value the JSON pointer `ptr` refers to, relative to [`root()`](root.md). `ptr` must not be empty
|
3. Removes the value the JSON pointer `ptr` refers to, relative to [`root()`](root.md), and returns how many values
|
||||||
([`root()`](root.md) itself cannot be erased), and the *parent* of the target must already exist. The target is
|
were removed: the *parent* of the target must already exist, and the target itself is removed as in 1. (an object
|
||||||
removed as in 1. if the parent is an object (returns the number of removed members, `#!cpp 0` or more) and as in 2.
|
member; `#!cpp 0` or more) or 2. (an array element; always `#!cpp 1`). `ptr` must not be empty -- [`root()`](root.md)
|
||||||
if it is an array (always returns `#!cpp 1`).
|
itself cannot be erased.
|
||||||
|
|
||||||
## Template parameters
|
## Template parameters
|
||||||
|
|
||||||
@@ -69,10 +69,10 @@ has `erase`; calling it on a read-only `basic_json_document` fails to compile (`
|
|||||||
[`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) (an empty token); otherwise (an
|
[`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) (an empty token); otherwise (an
|
||||||
object, or a primitive value the pointer's parent resolves to) throws what 1. throws.
|
object, or a primitive value the pointer's parent resolves to) throws what 1. throws.
|
||||||
|
|
||||||
Overloads 1. and 2. also throw [`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202)
|
Every overload also throws [`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) ("view
|
||||||
("view does not belong to this document") if `object`/`array` is a [discarded](../basic_json_view/is_discarded.md)
|
does not belong to this document") if `object`/`array` is a [discarded](../basic_json_view/is_discarded.md) view or a
|
||||||
view or a view of a *different* document. Overload 3. never throws `invalid_iterator.202`: it takes no view, and it
|
view of a *different* document (overloads 1-2 only; overload 3 always starts from this document's own
|
||||||
always starts from this document's own [`root()`](root.md).
|
[`root()`](root.md)).
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
@@ -92,10 +92,9 @@ always starts from this document's own [`root()`](root.md).
|
|||||||
|
|
||||||
Like [`set`](set.md) and [`push_back`](push_back.md), `erase` never moves an element's *value*: a view still
|
Like [`set`](set.md) and [`push_back`](push_back.md), `erase` never moves an element's *value*: a view still
|
||||||
referring to a removed member or element keeps showing what it last held (see [Edits](index.md#edits)) -- it just no
|
referring to a removed member or element keeps showing what it last held (see [Edits](index.md#edits)) -- it just no
|
||||||
longer appears when `array`/`object` is read, dumped, or iterated. What does change is the container's sequence of
|
longer appears when `array`/`object` is read, dumped, or iterated. Removing an element of `array` (2.) does shift the
|
||||||
*links* to its elements: removing an element of `array` (2.) shifts the links to the elements after it by one, as
|
*links* to the elements after it, the same way `insert`, `set`, or `push_back` on the same array would; any iterator
|
||||||
[`insert`](insert.md) does in the other direction. *Every* iterator already taken over `array`/`object` is invalidated
|
already taken over `array`/`object` is invalidated by an erase, since it was walking the old layout.
|
||||||
by an erase, not only those at or after the removed element, since the container may now use a different sequence.
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|||||||
@@ -110,30 +110,17 @@ enum class image_check
|
|||||||
|
|
||||||
How thoroughly `load()` validates `image` before trusting it.
|
How thoroughly `load()` validates `image` before trusting it.
|
||||||
|
|
||||||
`full` (the default)
|
| value | checks | guarantees |
|
||||||
: Checks everything the parser itself guarantees: structure and bounds; that every string is valid UTF-8 (and, for a
|
|----------|--------------------------------------------------------------------------------------------------------------|------------|
|
||||||
string still in the source text, that it contains no quote, backslash, or control character); and that every number
|
| `full` | everything the parser itself guarantees: structure and bounds; that every string is valid UTF-8 (and, for a string still in the source text, that it contains no quote, backslash, or control character); and that every number token is well-formed and matches the value stored for it; strings and float tokens may share a range only if the ranges are identical (as nodes that share a value do), and never overlap otherwise | reading and serializing a checked image is safe and always produces valid JSON, exactly as for a parsed document |
|
||||||
token is well-formed and matches the value stored for it. Strings and float tokens may share a range only if the
|
| `bounds` | structure and bounds only -- that every offset and count in the node index stays inside the image | reading and serializing stay memory-safe, but a crafted image can hold strings that are not valid UTF-8 or that serialize to invalid JSON ([`dump()`](../basic_json_view/dump.md) writes them unchanged or throws [`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316)), and numbers whose values differ from their text |
|
||||||
ranges are identical (as nodes that share a value do), and never overlap otherwise. Reading and serializing a
|
| `none` | nothing | images from a trusted source only -- reading a damaged image is undefined behavior |
|
||||||
checked image is safe and always produces valid JSON, exactly as for a parsed document. This is the right choice for
|
|
||||||
an image from anything you do not fully control -- a file, a cache shared with other processes, a peer on the
|
|
||||||
network.
|
|
||||||
|
|
||||||
`bounds`
|
`full` is the default and the right choice for an image from anything you do not fully control -- a file, a cache
|
||||||
: Checks structure and bounds only: that every offset and count in the node index stays inside the image. So
|
shared with other processes, a peer on the network. `bounds` skips scanning the text and the decoded strings, so it
|
||||||
`load()`, [`root()`](root.md), element access, and [`materialize()`](../basic_json_view/materialize.md) can never
|
fits a cache your own process just wrote and reads straight back, where damage would mean a bug or a hardware fault
|
||||||
read outside the image, even on a damaged one. It does *not* guarantee that the image describes valid JSON: a
|
rather than adversarial input; it still cannot crash or read out of bounds. `none` skips validation entirely and
|
||||||
string that a `full` check would have rejected can make [`dump()`](../basic_json_view/dump.md) write invalid UTF-8
|
should only be used for an image you trust as much as your own memory.
|
||||||
or invalid JSON, or throw [`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316), and a number can
|
|
||||||
read back with a value that does not match how it is spelled. An [editable document](../json_editable_document.md)
|
|
||||||
does not take such a string over: [`set`](set.md), [`push_back`](push_back.md) and [`insert`](insert.md) throw
|
|
||||||
`type_error.316` when they copy it from a view of the loaded document, and leave the editable document unchanged.
|
|
||||||
`bounds` skips scanning the text and the decoded strings, so it fits a cache your own process just wrote and reads
|
|
||||||
straight back, where damage would mean a bug or a hardware fault rather than adversarial input.
|
|
||||||
|
|
||||||
`none`
|
|
||||||
: Checks nothing. Reading a damaged image is undefined behavior, so use it only for an image you trust as much as
|
|
||||||
your own memory.
|
|
||||||
|
|
||||||
**Lifetime.** Overloads 1 and 2 borrow `image`: it must stay alive and byte-for-byte unchanged for as long as the
|
**Lifetime.** Overloads 1 and 2 borrow `image`: it must stay alive and byte-for-byte unchanged for as long as the
|
||||||
returned document, and any [view](../basic_json_view/index.md) taken from it, is used -- exactly like a document
|
returned document, and any [view](../basic_json_view/index.md) taken from it, is used -- exactly like a document
|
||||||
@@ -147,6 +134,16 @@ returned document, and any [view](../basic_json_view/index.md) taken from it, is
|
|||||||
("unknown format"). Use images to cache a document within one build of the library, or to hand one to another
|
("unknown format"). Use images to cache a document within one build of the library, or to hand one to another
|
||||||
process running the *same* build on the *same* (little-endian) machine -- not as a long-term storage format.
|
process running the *same* build on the *same* (little-endian) machine -- not as a long-term storage format.
|
||||||
|
|
||||||
|
**What `image_check::bounds` does not guarantee.** A bounds-checked image can never make `load()`,
|
||||||
|
[`root()`](root.md), element access, or [`materialize()`](../basic_json_view/materialize.md) read outside the image,
|
||||||
|
so those stay safe on a damaged one. It does *not* guarantee that the image describes valid JSON: a string
|
||||||
|
that a `full` check would have rejected can make [`dump()`](../basic_json_view/dump.md) write invalid UTF-8 or invalid
|
||||||
|
JSON, or throw `type_error.316`, and a number can read back with a value that does not match how it is spelled.
|
||||||
|
Reserve `bounds` for images you already trust to be well-formed, and use it only to skip the extra scan.
|
||||||
|
An [editable document](../json_editable_document.md) does not take such a string over either:
|
||||||
|
[`set`](set.md), [`push_back`](push_back.md) and [`insert`](insert.md) throw `type_error.316` when they copy it from
|
||||||
|
a view of the loaded document, and leave the editable document unchanged.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example "Caching a document, ownership, and a rejected image"
|
??? example "Caching a document, ownership, and a rejected image"
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ basic_json_view at(const json_pointer& ptr) const;
|
|||||||
|
|
||||||
## Exception safety
|
## Exception safety
|
||||||
|
|
||||||
Strong guarantee: there are no changes to the view or the document it refers to.
|
Strong exception safety: if an exception is thrown, there are no changes to the view or the document it refers to.
|
||||||
|
|
||||||
## Exceptions
|
## Exceptions
|
||||||
|
|
||||||
@@ -77,8 +77,8 @@ None of these exceptions carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics
|
|||||||
1. Linear in the number of members: as for [`ordered_json`](../ordered_json.md), members are compared one after
|
1. Linear in the number of members: as for [`ordered_json`](../ordered_json.md), members are compared one after
|
||||||
another, in document order, stopping at the first match. Each comparison first checks the
|
another, in document order, stopping at the first match. Each comparison first checks the
|
||||||
key's length -- already known from the index, without reading the key bytes -- before comparing its content.
|
key's length -- already known from the index, without reading the key bytes -- before comparing its content.
|
||||||
Objects with 128 or more members get a hash index of their keys while parsing, so that a lookup in them takes
|
Objects with 128 or more members get a hash index while parsing, so that a lookup in them takes constant time
|
||||||
constant time on average.
|
on average.
|
||||||
2. Linear in `idx`: elements are skipped one at a time from the first one, since they are not a fixed size in the
|
2. Linear in `idx`: elements are skipped one at a time from the first one, since they are not a fixed size in the
|
||||||
index (unlike `BasicJsonType`'s array, which is random-access).
|
index (unlike `BasicJsonType`'s array, which is random-access).
|
||||||
3. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
3. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ The last element or member value. For a primitive value (number, string, boolean
|
|||||||
|
|
||||||
## Exception safety
|
## Exception safety
|
||||||
|
|
||||||
Strong guarantee: there are no changes to the view or the document it refers to.
|
Strong exception safety: if an exception is thrown, there are no changes to the view or the document it refers to.
|
||||||
|
|
||||||
## Exceptions
|
## Exceptions
|
||||||
|
|
||||||
@@ -33,7 +33,8 @@ index.
|
|||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
Unlike [`BasicJsonType::back()`](../basic_json/back.md), which has undefined behavior for an empty array or object,
|
Unlike [`BasicJsonType::back()`](../basic_json/back.md), which has undefined behavior for an empty array or object,
|
||||||
`back()` throws `invalid_iterator.214` for an empty array, an empty object, a null value, or a discarded view.
|
`back()` throws `invalid_iterator.214` in that case -- the same way it already does for `#!json null` and for a
|
||||||
|
discarded view, where `BasicJsonType::back()` also throws.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ iterator begin() const noexcept;
|
|||||||
|
|
||||||
Returns an iterator to the first element of an array, or the first member value of an object, in **document order**
|
Returns an iterator to the first element of an array, or the first member value of an object, in **document order**
|
||||||
-- the order the values appear in the source text, not sorted by key. A primitive value iterates as a range of one
|
-- the order the values appear in the source text, not sorted by key. A primitive value iterates as a range of one
|
||||||
element (itself), and a `#!json null` value or a [discarded](is_discarded.md) view iterates as an empty range.
|
element (itself); `#!json null` and a [discarded](is_discarded.md) view iterate as an empty range.
|
||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ Constant.
|
|||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
For an object, iteration visits **every** member, including all occurrences of a duplicate key -- unlike
|
For an object, iteration visits **every** member, including all occurrences of a duplicate key -- unlike
|
||||||
[`operator[]`](operator[].md), [`at`](at.md), [`find`](find.md), and [`contains`](contains.md),
|
[`operator[]`](operator[].md), [`at`](at.md), [`find`](find.md), [`contains`](contains.md), and [`count`](count.md),
|
||||||
which all resolve to the *first* member with a given key. See the
|
which all resolve to the *first* member with a given key. See the
|
||||||
[Notes on duplicate keys](operator[].md#notes) of `operator[]`.
|
[Notes on duplicate keys](operator[].md#notes) of `operator[]`.
|
||||||
|
|
||||||
@@ -31,10 +31,6 @@ Because objects are iterated in document order rather than sorted by key, the or
|
|||||||
iterating the [`materialize()`](materialize.md)d `BasicJsonType` value would produce: a `#!cpp basic_json` object
|
iterating the [`materialize()`](materialize.md)d `BasicJsonType` value would produce: a `#!cpp basic_json` object
|
||||||
(`std::map`-backed by default) sorts its keys, while a view does not.
|
(`std::map`-backed by default) sorts its keys, while a view does not.
|
||||||
|
|
||||||
An `ordered_json` value also keeps document order, but it keeps only one entry per key: as `parse()` does, it keeps the
|
|
||||||
*last* value, at the position of the key's *first* occurrence. The view, in contrast, visits every member, including
|
|
||||||
the duplicates.
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ No-throw guarantee: this function never throws exceptions.
|
|||||||
1. Linear in the number of members: as for [`ordered_json`](../ordered_json.md), members are compared one after
|
1. Linear in the number of members: as for [`ordered_json`](../ordered_json.md), members are compared one after
|
||||||
another, in document order, stopping at the first match. Each comparison first checks the
|
another, in document order, stopping at the first match. Each comparison first checks the
|
||||||
key's length -- already known from the index, without reading the key bytes -- before comparing its content.
|
key's length -- already known from the index, without reading the key bytes -- before comparing its content.
|
||||||
Objects with 128 or more members get a hash index of their keys while parsing, so that a lookup in them takes
|
Objects with 128 or more members get a hash index while parsing, so that a lookup in them takes constant time
|
||||||
constant time on average.
|
on average.
|
||||||
2. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
2. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
||||||
that level or the index into the array -- as for [`operator[]`](operator[].md#complexity) and
|
that level or the index into the array -- as for [`operator[]`](operator[].md#complexity) and
|
||||||
[`at`](at.md#complexity) with a JSON pointer.
|
[`at`](at.md#complexity) with a JSON pointer.
|
||||||
@@ -55,8 +55,7 @@ view.
|
|||||||
|
|
||||||
!!! info "Postconditions"
|
!!! info "Postconditions"
|
||||||
|
|
||||||
If `#!cpp v.contains(key)` returns `#!cpp true`, then `#!cpp v[key]` is not [discarded](is_discarded.md) and
|
If `#!cpp v.contains(key)` returns `#!cpp true`, then `#!cpp v[key]` is not [discarded](is_discarded.md). If
|
||||||
`#!cpp v.at(key)` does not throw. If
|
|
||||||
`#!cpp v.contains(ptr)` returns `#!cpp true`, then `#!cpp v[ptr]` is not discarded and `#!cpp v.at(ptr)` does not
|
`#!cpp v.contains(ptr)` returns `#!cpp true`, then `#!cpp v[ptr]` is not discarded and `#!cpp v.at(ptr)` does not
|
||||||
throw.
|
throw.
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ size_type count(const char* key) const;
|
|||||||
size_type count(const string_t& key) const;
|
size_type count(const string_t& key) const;
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns the number of members with key `key` if the value is an object, `#!cpp 0` otherwise. Members with a repeated
|
Returns `#!cpp 1` if the value is an object with a member with key `key`, `#!cpp 0` otherwise.
|
||||||
key are all counted.
|
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
@@ -16,8 +15,7 @@ key are all counted.
|
|||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
the number of members of the object with key `key` (duplicates included), or `#!cpp 0` if the value is not an object
|
`#!cpp 1` if the value is an object and has a member with key `key`, `#!cpp 0` otherwise.
|
||||||
or has no such member.
|
|
||||||
|
|
||||||
## Exception safety
|
## Exception safety
|
||||||
|
|
||||||
@@ -25,30 +23,36 @@ No-throw guarantee: this function never throws exceptions.
|
|||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
Linear in the number of members: unlike the lookup functions, `count()` does not stop at the first match, because it
|
Linear in the number of members: as for [`ordered_json`](../ordered_json.md), members are compared one after another,
|
||||||
has to count every member with the key. Members are compared one after another, in document order. Each comparison
|
in document order, stopping at the first match. Each comparison first checks the key's length
|
||||||
first checks the key's length -- already known from the index, without reading the key bytes -- before comparing its
|
-- already known from the index, without reading the key bytes -- before comparing its content.
|
||||||
content. The hash index of the keys of objects with 128 or more members does not help here, since every member
|
Objects with 128 or more members get a hash index while parsing, so that a lookup in them takes constant time on
|
||||||
is visited.
|
average.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
!!! warning "Duplicate keys: the first member wins"
|
||||||
|
|
||||||
|
If the source text repeats a key, this resolves to the *first* member with it, not to the last one that
|
||||||
|
[`materialize()`](materialize.md) and `parse()` keep. See the [Notes on duplicate keys](operator[].md#notes) of
|
||||||
|
`operator[]` and [Duplicate keys](../../features/json_view.md#duplicate-keys) for the reasons and for how to get
|
||||||
|
the last value.
|
||||||
|
|
||||||
This method always returns `#!cpp 0` when the value is not an object -- including a [discarded](is_discarded.md)
|
This method always returns `#!cpp 0` when the value is not an object -- including a [discarded](is_discarded.md)
|
||||||
view.
|
view.
|
||||||
|
|
||||||
If the source text repeats a key, `count()` returns more than `#!cpp 1` for it, even though the
|
Unlike [`BasicJsonType::count()`](../basic_json/count.md), whose return value can in principle exceed `#!cpp 1` for
|
||||||
[materialized](materialize.md) value (like the result of `parse()`) would have only one member with that key. Hence
|
an `ObjectType` that allows multiple entries per key, `count()` here never does: it is exactly
|
||||||
`#!cpp count(key) > 1` detects duplicate keys. This is unlike the lookup functions
|
[`contains()`](contains.md) as `#!cpp 0`/`#!cpp 1`. This holds even if the source text has a duplicate key -- see the
|
||||||
([`operator[]`](operator[].md), [`at`](at.md), [`find`](find.md), [`contains`](contains.md)), which resolve to the
|
[Notes on duplicate keys](operator[].md#notes) of `operator[]` -- because a `#!cpp count() > 1` result would require
|
||||||
*first* member; see the [Notes on duplicate keys](operator[].md#notes) of `operator[]`.
|
counting every member with a matching key (the lookup functions resolve to the *first* one).
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
|
|
||||||
The example below validates that every transaction of a batch carries exactly one `amount` field, using
|
The example below validates that every transaction of a batch carries a mandatory `amount` field, using
|
||||||
`count()` before deciding whether to materialize a transaction at all. It reports a transaction with no `amount`
|
`count()` before deciding whether to materialize a transaction at all.
|
||||||
field and one with a duplicated `amount` field.
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
--8<-- "examples/basic_json_view__count.cpp"
|
--8<-- "examples/basic_json_view__count.cpp"
|
||||||
|
|||||||
@@ -60,13 +60,11 @@ Linear in the size of the output text.
|
|||||||
The walk over the subtree is iterative, so the nesting depth it can write is limited by available memory only, not by
|
The walk over the subtree is iterative, so the nesting depth it can write is limited by available memory only, not by
|
||||||
the call stack -- as for [`materialize()`](materialize.md).
|
the call stack -- as for [`materialize()`](materialize.md).
|
||||||
|
|
||||||
Strings are escaped by the same rules as [`BasicJsonType::dump`](../basic_json/dump.md).
|
Strings are escaped by the same rules as [`BasicJsonType::dump`](../basic_json/dump.md). With
|
||||||
|
`#!cpp numbers == number_format::shortest`, floats are written with the library's shortest round-trip conversion,
|
||||||
With `#!cpp numbers == number_format::shortest`, floats are written with the library's shortest round-trip conversion,
|
|
||||||
exactly as [`BasicJsonType::dump`](../basic_json/dump.md) would (e.g. `#!cpp 1.5`, `#!cpp 100.0`, `#!cpp 1e+100`), and
|
exactly as [`BasicJsonType::dump`](../basic_json/dump.md) would (e.g. `#!cpp 1.5`, `#!cpp 100.0`, `#!cpp 1e+100`), and
|
||||||
integers are copied from the source text -- already canonical in JSON, so this matches their shortest form too --
|
integers are copied from the source text -- already canonical in JSON, so this matches their shortest form too --
|
||||||
except that `#!cpp -0` is written as `#!cpp 0`, the way [`BasicJsonType::parse()`](../basic_json/parse.md) reads it.
|
except that `#!cpp -0` is written as `#!cpp 0`, the way [`BasicJsonType::parse()`](../basic_json/parse.md) reads it.
|
||||||
|
|
||||||
`#!cpp number_format::source` copies every number exactly as written in the source text instead, with no exception
|
`#!cpp number_format::source` copies every number exactly as written in the source text instead, with no exception
|
||||||
for `#!cpp -0` -- `#!cpp 1.50`, `#!cpp 1E2`, `#!cpp -0.0`, `#!cpp -0`, or all digits of an integer literal with more
|
for `#!cpp -0` -- `#!cpp 1.50`, `#!cpp 1E2`, `#!cpp -0.0`, `#!cpp -0`, or all digits of an integer literal with more
|
||||||
digits than any number type holds (such a literal is itself classified as a float, see
|
digits than any number type holds (such a literal is itself classified as a float, see
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ iterator end() const noexcept;
|
|||||||
```
|
```
|
||||||
|
|
||||||
Returns an iterator to one past the last element of an array, one past the last member value of an object, in
|
Returns an iterator to one past the last element of an array, one past the last member value of an object, in
|
||||||
**document order** -- see [`begin()`](begin.md). A primitive value iterates as a range of one element (itself), and a
|
**document order** -- see [`begin()`](begin.md). A primitive value iterates as a range of one element (itself);
|
||||||
`#!json null` value or a [discarded](is_discarded.md) view iterates as an empty range (`#!cpp begin() == end()`).
|
`#!json null` and a [discarded](is_discarded.md) view iterate as an empty range, so `#!cpp begin() == end()` for
|
||||||
|
them.
|
||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ No-throw guarantee: this function never throws exceptions.
|
|||||||
Linear in the number of members: as for [`ordered_json`](../ordered_json.md), members are compared one after another,
|
Linear in the number of members: as for [`ordered_json`](../ordered_json.md), members are compared one after another,
|
||||||
in document order, stopping at the first match. Each comparison first checks the key's length
|
in document order, stopping at the first match. Each comparison first checks the key's length
|
||||||
-- already known from the index, without reading the key bytes -- before comparing its content.
|
-- already known from the index, without reading the key bytes -- before comparing its content.
|
||||||
Objects with 128 or more members get a hash index of their keys while parsing, so that a lookup in them takes
|
Objects with 128 or more members get a hash index while parsing, so that a lookup in them takes constant time on
|
||||||
constant time on average.
|
average.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ The first element or member value. For a primitive value (number, string, boolea
|
|||||||
|
|
||||||
## Exception safety
|
## Exception safety
|
||||||
|
|
||||||
Strong guarantee: there are no changes to the view or the document it refers to.
|
Strong exception safety: if an exception is thrown, there are no changes to the view or the document it refers to.
|
||||||
|
|
||||||
## Exceptions
|
## Exceptions
|
||||||
|
|
||||||
@@ -31,8 +31,8 @@ Constant.
|
|||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
Unlike [`BasicJsonType::front()`](../basic_json/front.md), which has undefined behavior for an empty array or
|
Unlike [`BasicJsonType::front()`](../basic_json/front.md), which has undefined behavior for an empty array or
|
||||||
object, `front()` throws `invalid_iterator.214` for an empty array, an empty object, a null value, or a discarded
|
object, `front()` throws `invalid_iterator.214` in that case -- the same way it already does for `#!json null` and
|
||||||
view.
|
for a discarded view, where `BasicJsonType::front()` also throws.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ Converts the value to `T`.
|
|||||||
For the types below, the conversion works directly on the flat index -- no `BasicJsonType` value is built for it:
|
For the types below, the conversion works directly on the flat index -- no `BasicJsonType` value is built for it:
|
||||||
|
|
||||||
- `#!cpp bool`
|
- `#!cpp bool`
|
||||||
- arithmetic types other than `#!cpp bool` (for a boolean, `#!cpp 0`/`#!cpp 1`, exactly as for
|
- arithmetic types other than `#!cpp bool` (from a number; from a boolean, as `#!cpp 0`/`#!cpp 1`, exactly as
|
||||||
[`BasicJsonType::get<T>()`](../basic_json/get.md))
|
[`BasicJsonType::get<T>()`](../basic_json/get.md) converts a boolean)
|
||||||
- `#!cpp std::nullptr_t`
|
- `#!cpp std::nullptr_t`
|
||||||
- `#!cpp std::basic_string<char, Traits, Alloc>` (including `string_t`) -- a copy of the string
|
- `#!cpp std::basic_string<char, Traits, Alloc>` (including `string_t`) -- a copy of the string
|
||||||
- [`string_view_t`](index.md#member-types) -- **no copy**: the returned view points into the document's
|
- [`string_view_t`](index.md#member-types) -- **no copy**: the returned view points into the document's
|
||||||
@@ -19,10 +19,10 @@ For the types below, the conversion works directly on the flat index -- no `Basi
|
|||||||
document's own buffer of decoded strings (see [`get_string()`](get_string.md))
|
document's own buffer of decoded strings (see [`get_string()`](get_string.md))
|
||||||
- `BasicJsonType` -- equivalent to [`materialize()`](materialize.md)
|
- `BasicJsonType` -- equivalent to [`materialize()`](materialize.md)
|
||||||
- `basic_json_view` -- returns `#!cpp *this`
|
- `basic_json_view` -- returns `#!cpp *this`
|
||||||
- `#!cpp std::vector<U, A>` -- from an array, element by element, each converted with `#!cpp get<U>()`; `#!cpp
|
- `#!cpp std::vector<U, A>` -- element by element, each converted with `#!cpp get<U>()`; `#!cpp
|
||||||
std::vector<basic_json_view>` keeps a view of every element instead of a value
|
std::vector<basic_json_view>` keeps a view of every element instead of a value
|
||||||
- `#!cpp std::map<K, V, C, A>` and `#!cpp std::unordered_map<K, V, H, E, A>`, if `K` is constructible from a `#!cpp
|
- `#!cpp std::map<K, V, C, A>` and `#!cpp std::unordered_map<K, V, H, E, A>`, if `K` is constructible from a `#!cpp
|
||||||
(const char*, std::size_t)` pair -- from an object, member by member, each value converted with `#!cpp get<V>()`; with a repeated
|
(const char*, std::size_t)` pair -- member by member, each value converted with `#!cpp get<V>()`; with a repeated
|
||||||
key, the *last* value is kept, as [`BasicJsonType::parse()`](../basic_json/parse.md) (and
|
key, the *last* value is kept, as [`BasicJsonType::parse()`](../basic_json/parse.md) (and
|
||||||
[`materialize()`](materialize.md)) does; `#!cpp std::map<std::string, basic_json_view>` keeps views of the members
|
[`materialize()`](materialize.md)) does; `#!cpp std::map<std::string, basic_json_view>` keeps views of the members
|
||||||
instead of values
|
instead of values
|
||||||
@@ -43,7 +43,7 @@ the value, converted to `T`
|
|||||||
|
|
||||||
## Exception safety
|
## Exception safety
|
||||||
|
|
||||||
Strong guarantee: there are no changes to the view or the document it refers to.
|
Strong exception safety: if an exception is thrown, there are no changes to the view or the document it refers to.
|
||||||
|
|
||||||
## Exceptions
|
## Exceptions
|
||||||
|
|
||||||
@@ -62,9 +62,6 @@ None of the exceptions thrown directly by this function (the first three bullets
|
|||||||
[`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) path: the view has no `BasicJsonType` value to point at. An
|
[`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) path: the view has no `BasicJsonType` value to point at. An
|
||||||
exception thrown while converting through `materialize()` (the last bullet) is different: it is thrown by a real
|
exception thrown while converting through `materialize()` (the last bullet) is different: it is thrown by a real
|
||||||
`BasicJsonType` value, so it **does** carry a `JSON_DIAGNOSTICS` path if `BasicJsonType` was built with it enabled.
|
`BasicJsonType` value, so it **does** carry a `JSON_DIAGNOSTICS` path if `BasicJsonType` was built with it enabled.
|
||||||
That path is relative to the root of the view's subtree, because `materialize()` builds only the subtree (with parent
|
|
||||||
pointers): it is empty if the failing value is the subtree's root itself, and it never is the path within the whole
|
|
||||||
document.
|
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
@@ -76,8 +73,8 @@ document.
|
|||||||
- `#!cpp std::map`/`#!cpp std::unordered_map`: linear in the number of members for walking them, plus the container's
|
- `#!cpp std::map`/`#!cpp std::unordered_map`: linear in the number of members for walking them, plus the container's
|
||||||
own insertion cost per member (logarithmic for `#!cpp std::map`, amortized constant for `#!cpp
|
own insertion cost per member (logarithmic for `#!cpp std::map`, amortized constant for `#!cpp
|
||||||
std::unordered_map`), times the complexity of converting one member to the mapped type.
|
std::unordered_map`), times the complexity of converting one member to the mapped type.
|
||||||
- every other `T`: the cost of [`materialize()`](materialize.md) and of destroying the `BasicJsonType` value
|
- every other `T`: linear in the size of the subtree (building the `BasicJsonType` value), plus the complexity of
|
||||||
afterwards, plus that of [`BasicJsonType::get<T>()`](../basic_json/get.md).
|
[`BasicJsonType::get<T>()`](../basic_json/get.md) on it.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ had to be decoded once when the document was parsed).
|
|||||||
|
|
||||||
## Exception safety
|
## Exception safety
|
||||||
|
|
||||||
Strong guarantee: there are no changes to the view or the document it refers to.
|
Strong exception safety: if an exception is thrown, there are no changes to the view or the document it refers to.
|
||||||
|
|
||||||
## Exceptions
|
## Exceptions
|
||||||
|
|
||||||
@@ -37,14 +37,9 @@ hand out a reference to a stored `string_t`. `get_string()` (equivalently, [`get
|
|||||||
zero-copy alternative: [`BasicJsonType::get_ref<const string_t&>()`](../basic_json/get_ref.md) is its closest
|
zero-copy alternative: [`BasicJsonType::get_ref<const string_t&>()`](../basic_json/get_ref.md) is its closest
|
||||||
counterpart, except that it returns a view instead of a reference to a value that must already exist.
|
counterpart, except that it returns a view instead of a reference to a value that must already exist.
|
||||||
|
|
||||||
`get_string()` borrows the string: nothing is copied, whereas [`get<std::string>()`](get.md) copies it. For a borrowed
|
|
||||||
document, the returned [`string_view_t`](index.md#member-types) is valid as long as the caller's buffer; for an owned
|
|
||||||
one, as long as the document.
|
|
||||||
|
|
||||||
The returned [`string_view_t`](index.md#member-types) is valid exactly as long as the view that produced it -- see the
|
The returned [`string_view_t`](index.md#member-types) is valid exactly as long as the view that produced it -- see the
|
||||||
[validity rules](index.md) of `basic_json_view` -- and, for a string with no escapes, for as long as the document's
|
[validity rules](index.md) of `basic_json_view` -- and, for a string with no escapes, for as long as the document's
|
||||||
source text, which for a borrowed document must remain valid **and unmodified** for as long as the document exists,
|
source text.
|
||||||
since floating-point values and unescaped strings are read from the source lazily.
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|||||||
@@ -5,15 +5,12 @@ template<typename T>
|
|||||||
T& get_to(T& v) const;
|
T& get_to(T& v) const;
|
||||||
```
|
```
|
||||||
|
|
||||||
Converts the value to `T` and stores it in `v`, in one of two ways:
|
Converts the value to `T` and assigns it to `v`. Equivalent to
|
||||||
|
|
||||||
- For the types that [`get<T>()`](get.md) converts directly -- arithmetic types, `#!cpp bool`,
|
```cpp
|
||||||
`#!cpp std::nullptr_t`, [`string_view_t`](index.md#member-types), `#!cpp std::basic_string`, `BasicJsonType`,
|
v = get<T>();
|
||||||
`basic_json_view`, `#!cpp std::vector`, and `#!cpp std::map`/`#!cpp std::unordered_map` with string keys -- `v` is
|
return v;
|
||||||
assigned: `#!cpp v = get<T>()`.
|
```
|
||||||
- For every other type, the subtree is built into a `BasicJsonType` value and `#!cpp materialize().get_to(v)` is
|
|
||||||
called, i.e. `from_json()` is invoked with the existing `v`, exactly as
|
|
||||||
[`BasicJsonType::get_to()`](../basic_json/get_to.md) does.
|
|
||||||
|
|
||||||
## Template parameters
|
## Template parameters
|
||||||
|
|
||||||
@@ -31,27 +28,15 @@ Converts the value to `T` and stores it in `v`, in one of two ways:
|
|||||||
|
|
||||||
## Exception safety
|
## Exception safety
|
||||||
|
|
||||||
- For the directly converted types: strong exception safety. If an exception is thrown, `v` is not modified.
|
Strong exception safety: if an exception is thrown, `v` is not modified.
|
||||||
- For every other type: as for [`BasicJsonType::get_to()`](../basic_json/get_to.md). `materialize()` throws before `v`
|
|
||||||
is touched, but a `from_json()` that throws may leave `v` partly modified.
|
|
||||||
|
|
||||||
## Exceptions
|
## Exceptions
|
||||||
|
|
||||||
- For the directly converted types: whatever [`get<T>()`](get.md) throws for the same value and `T`.
|
Whatever [`get<T>()`](get.md) throws for the same value and `T`.
|
||||||
- For every other type: whatever [`materialize()`](materialize.md) and
|
|
||||||
[`BasicJsonType::get_to()`](../basic_json/get_to.md) throw.
|
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
- For the directly converted types: whatever [`get<T>()`](get.md) has for the same `T`.
|
Whatever [`get<T>()`](get.md) has for the same `T`.
|
||||||
- For every other type: that of [`materialize()`](materialize.md), plus that of
|
|
||||||
[`BasicJsonType::get_to()`](../basic_json/get_to.md).
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
The difference between the two ways matters for a `from_json()` that sets only the fields present in the JSON: since
|
|
||||||
it runs on the existing `v`, the other fields of `v` keep their values, whereas assigning `get<T>()` would replace
|
|
||||||
them with those of a default-constructed `T`.
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|||||||
@@ -14,27 +14,18 @@ to the document and a pointer into its index), trivially copyable. A view is val
|
|||||||
- the document has not been re-parsed with [`read()`](../basic_json_document/read.md) (or
|
- the document has not been re-parsed with [`read()`](../basic_json_document/read.md) (or
|
||||||
[`parse()`](../basic_json_document/parse.md) into it) or shrunk with
|
[`parse()`](../basic_json_document/parse.md) into it) or shrunk with
|
||||||
[`shrink_to_fit()`](../basic_json_document/shrink_to_fit.md) since the view was taken, and
|
[`shrink_to_fit()`](../basic_json_document/shrink_to_fit.md) since the view was taken, and
|
||||||
- if the document borrows its source text, that text is still alive and unmodified (floating-point values and
|
- if the document borrows its source text, that text is still alive.
|
||||||
unescaped strings are read from it lazily).
|
|
||||||
|
|
||||||
Moving the document itself does not invalidate its views: the index is heap-allocated independently of the
|
Moving the document itself does not invalidate its views: the index is heap-allocated independently of the
|
||||||
`basic_json_document` object.
|
`basic_json_document` object.
|
||||||
|
|
||||||
`basic_json_view` provides the read-only part of the `BasicJsonType` interface:
|
`basic_json_view` provides the read-only part of the `BasicJsonType` interface: the type-inspection functions, element
|
||||||
|
access, lookup, iteration, conversion, and comparison -- [`get<T>()`](get.md), [`get_string()`](get_string.md),
|
||||||
- the type-inspection functions
|
[`number_token()`](number_token.md), and [`materialize()`](materialize.md) to build the `BasicJsonType` value of a
|
||||||
- element access
|
subtree on demand. [`operator[]`](operator%5B%5D.md), [`at`](at.md), [`contains`](contains.md), and
|
||||||
- lookup
|
[`value`](value.md) also accept a [`json_pointer`](../json_pointer/index.md). [`operator==`](operator_eq.md) and
|
||||||
- iteration
|
[`operator!=`](operator_ne.md) compare two views, or a view and a `BasicJsonType` value, without ever building a
|
||||||
- conversion: [`get<T>()`](get.md), [`get_string()`](get_string.md), [`number_token()`](number_token.md), and
|
`BasicJsonType` value for a view; no ordering comparison (`#!cpp operator<`) is provided.
|
||||||
[`materialize()`](materialize.md) to build the `BasicJsonType` value of a subtree on demand
|
|
||||||
- comparison: [`operator==`](operator_eq.md) and [`operator!=`](operator_ne.md) compare two views, or a view and a
|
|
||||||
`BasicJsonType` value, without ever building a `BasicJsonType` value for a view; no ordering comparison
|
|
||||||
(`#!cpp operator<`) is provided
|
|
||||||
|
|
||||||
[`operator[]`](operator%5B%5D.md), [`at`](at.md), [`contains`](contains.md), and [`value`](value.md) also accept a
|
|
||||||
[`json_pointer`](../json_pointer/index.md). Unlike `basic_json`, there are no implicit conversion operators (no
|
|
||||||
`#!cpp operator ValueType()`), and no `#!cpp get_ptr()` or `#!cpp get_ref()`; see [`get`](get.md).
|
|
||||||
|
|
||||||
`basic_json_view` itself is always read-only -- it never has a `set` or `push_back` of its own. A view of an
|
`basic_json_view` itself is always read-only -- it never has a `set` or `push_back` of its own. A view of an
|
||||||
**editable** document (`#!cpp Editable == true`) sees every edit made through
|
**editable** document (`#!cpp Editable == true`) sees every edit made through
|
||||||
@@ -70,6 +61,7 @@ still refers to it -- including ones taken before the change -- reads the new va
|
|||||||
- **string_view_t** - `#!cpp std::string_view` on C++17 and newer, a minimal internal substitute otherwise
|
- **string_view_t** - `#!cpp std::string_view` on C++17 and newer, a minimal internal substitute otherwise
|
||||||
- **iterator**, **const_iterator** - a forward iterator over the elements of an array or the member values of an
|
- **iterator**, **const_iterator** - a forward iterator over the elements of an array or the member values of an
|
||||||
object, in document order; both names refer to the same type, since a view is always read-only
|
object, in document order; both names refer to the same type, since a view is always read-only
|
||||||
|
- **item** - a (key, value) pair produced by [`items()`](items.md)
|
||||||
- [**number_format**](number_format.md) - how [`dump()`](dump.md) writes numbers
|
- [**number_format**](number_format.md) - how [`dump()`](dump.md) writes numbers
|
||||||
|
|
||||||
## Member functions
|
## Member functions
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
/* unspecified */ items() const noexcept;
|
/* unspecified */ items() const noexcept;
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns a range of items -- (key, value) pairs -- for use in range-based for loops. An item's `#!cpp key()` is a
|
Returns a range of [`item`](index.md#member-types) values -- (key, value) pairs -- for use in range-based for loops.
|
||||||
`#!cpp string_view_t` (for an array element, its index converted to a string, as for
|
The key of an array element is its index, converted to a string, as for
|
||||||
[`BasicJsonType::items()`](../basic_json/items.md)), and its `#!cpp value()` is a `basic_json_view` of the element.
|
[`BasicJsonType::items()`](../basic_json/items.md).
|
||||||
|
|
||||||
The returned type is not part of the public API and may change between versions; use a range-based for loop (see the
|
The returned type is not part of the public API and may change between versions; use a range-based for loop (see the
|
||||||
example), or `#!cpp decltype(v.items())` if you need to name it.
|
example), or `#!cpp decltype(v.items())` if you need to name it.
|
||||||
@@ -33,8 +33,8 @@ is nothing for a reference to bind to.
|
|||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
A range whose iterators dereference to an item (with `#!cpp key()` and `#!cpp value()`, see above) and whose
|
A range whose iterators dereference to [`item`](index.md#member-types) and whose `#!cpp begin()`/`#!cpp end()` are
|
||||||
`#!cpp begin()`/`#!cpp end()` are equivalent to [`basic_json_view::begin()`](begin.md)/[`end()`](end.md), in document order.
|
equivalent to [`basic_json_view::begin()`](begin.md)/[`end()`](end.md), in document order.
|
||||||
|
|
||||||
## Exception safety
|
## Exception safety
|
||||||
|
|
||||||
@@ -48,14 +48,14 @@ Constant.
|
|||||||
|
|
||||||
As for [`begin()`](begin.md)/[`end()`](end.md), `items()` visits **every** member of an object, including all
|
As for [`begin()`](begin.md)/[`end()`](end.md), `items()` visits **every** member of an object, including all
|
||||||
occurrences of a duplicate key -- unlike [`operator[]`](operator[].md), [`at`](at.md), [`find`](find.md),
|
occurrences of a duplicate key -- unlike [`operator[]`](operator[].md), [`at`](at.md), [`find`](find.md),
|
||||||
and [`contains`](contains.md), which resolve to the *first* member with a given key. See the
|
[`contains`](contains.md), and [`count`](count.md), which resolve to the *first* member with a given key. See the
|
||||||
[Notes on duplicate keys](operator[].md#notes) of `operator[]`.
|
[Notes on duplicate keys](operator[].md#notes) of `operator[]`.
|
||||||
|
|
||||||
!!! danger "Lifetime issues"
|
!!! danger "Lifetime issues"
|
||||||
|
|
||||||
Unlike `BasicJsonType::items()` (see [#2040](https://github.com/nlohmann/json/issues/2040)), calling `items()` on
|
As for `BasicJsonType::items()`, calling `items()` on a temporary view (or a temporary document) is dangerous:
|
||||||
a temporary view is safe, for instance `#!cpp for (auto item : v["a"].items())`: the returned range holds a copy
|
the range refers back to the document, so the document must outlive the loop. See
|
||||||
of the view. What must outlive the loop is the document and, for a borrowed document, the source buffer.
|
[#2040](https://github.com/nlohmann/json/issues/2040) for the `BasicJsonType` background.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ and [`materialize()`](materialize.md) can only represent approximately, as a `nu
|
|||||||
|
|
||||||
## Exception safety
|
## Exception safety
|
||||||
|
|
||||||
Strong guarantee: there are no changes to the view or the document it refers to.
|
Strong exception safety: if an exception is thrown, there are no changes to the view or the document it refers to.
|
||||||
|
|
||||||
## Exceptions
|
## Exceptions
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ basic_json_view operator[](const json_pointer& ptr) const;
|
|||||||
|
|
||||||
## Exception safety
|
## Exception safety
|
||||||
|
|
||||||
Strong guarantee: there are no changes to the view or the document it refers to.
|
Strong exception safety: if an exception is thrown, there are no changes to the view or the document it refers to.
|
||||||
|
|
||||||
## Exceptions
|
## Exceptions
|
||||||
|
|
||||||
@@ -83,8 +83,8 @@ None of these exceptions carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics
|
|||||||
another, in document order, stopping at the first match. Each comparison first checks the
|
another, in document order, stopping at the first match. Each comparison first checks the
|
||||||
key's length -- already known from the index, without reading the key bytes -- before comparing its content, so a
|
key's length -- already known from the index, without reading the key bytes -- before comparing its content, so a
|
||||||
key of a different length than `key` is rejected without touching the source text.
|
key of a different length than `key` is rejected without touching the source text.
|
||||||
Objects with 128 or more members get a hash index of their keys while parsing, so that a lookup in them takes
|
Objects with 128 or more members get a hash index while parsing, so that a lookup in them takes constant time
|
||||||
constant time on average.
|
on average.
|
||||||
2. Linear in `idx`: elements are skipped one at a time from the first one, since they are not a fixed size in the
|
2. Linear in `idx`: elements are skipped one at a time from the first one, since they are not a fixed size in the
|
||||||
index (unlike `BasicJsonType`'s array, which is random-access).
|
index (unlike `BasicJsonType`'s array, which is random-access).
|
||||||
3. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
3. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
||||||
@@ -110,13 +110,13 @@ document.
|
|||||||
!!! warning "Duplicate keys: the first member wins"
|
!!! warning "Duplicate keys: the first member wins"
|
||||||
|
|
||||||
If the source text has an object with a duplicate key, `#!cpp operator[]` (and [`at`](at.md), [`find`](find.md),
|
If the source text has an object with a duplicate key, `#!cpp operator[]` (and [`at`](at.md), [`find`](find.md),
|
||||||
[`contains`](contains.md), [`value`](value.md), and JSON pointer resolution) all resolve to
|
[`contains`](contains.md), [`count`](count.md), [`value`](value.md), and JSON pointer resolution) all resolve to
|
||||||
the *first* member with that key, because a lookup can stop as soon as it finds a match. This is different from
|
the *first* member with that key, because a lookup can stop as soon as it finds a match. This is different from
|
||||||
[`materialize()`](materialize.md) (and [`BasicJsonType::parse()`](../basic_json/parse.md)), which replay every
|
[`materialize()`](materialize.md) (and [`BasicJsonType::parse()`](../basic_json/parse.md)), which replay every
|
||||||
member in order and so keep the *last* value for a repeated key, so `#!cpp v["a"]` and
|
member in order and so keep the *last* value for a repeated key, so `#!cpp v["a"]` and
|
||||||
`#!cpp v.materialize()["a"]` can differ. To get the value `parse()` would give, use
|
`#!cpp v.materialize()["a"]` can differ. To get the value `parse()` would give, use
|
||||||
[`materialize()`](materialize.md) or iterate the members with [`items()`](items.md) and keep the last match.
|
[`materialize()`](materialize.md) or iterate the members with [`items()`](items.md) and keep the last match.
|
||||||
The key hash index of a larger object (128 members or more) leads to the first member of a key as well.
|
The hash index of a larger object (128 members or more) leads to the first member of a key as well.
|
||||||
[`begin()`](begin.md)/[`end()`](end.md) and [`items()`](items.md) iterate over *all* members, including
|
[`begin()`](begin.md)/[`end()`](end.md) and [`items()`](items.md) iterate over *all* members, including
|
||||||
duplicates, in document order. See [Duplicate keys](../../features/json_view.md#duplicate-keys) and
|
duplicates, in document order. See [Duplicate keys](../../features/json_view.md#duplicate-keys) and
|
||||||
[`size()`](size.md#notes).
|
[`size()`](size.md#notes).
|
||||||
|
|||||||
@@ -14,14 +14,14 @@ same string for a discarded `BasicJsonType` value).
|
|||||||
a string representation of the type ([`value_t`](../basic_json/value_t.md)):
|
a string representation of the type ([`value_t`](../basic_json/value_t.md)):
|
||||||
|
|
||||||
| Value type | return value |
|
| Value type | return value |
|
||||||
|----------------------------------------------------|---------------|
|
|-----------------------------------------------------|---------------|
|
||||||
| `#!json null` | `"null"` |
|
| `#!json null` | `"null"` |
|
||||||
| boolean | `"boolean"` |
|
| boolean | `"boolean"` |
|
||||||
| string | `"string"` |
|
| string | `"string"` |
|
||||||
| number (integer, unsigned integer, floating-point) | `"number"` |
|
| number (integer, unsigned integer, floating-point) | `"number"` |
|
||||||
| object | `"object"` |
|
| object | `"object"` |
|
||||||
| array | `"array"` |
|
| array | `"array"` |
|
||||||
| discarded | `"discarded"` |
|
| discarded | `"discarded"` |
|
||||||
|
|
||||||
`type_name()` never returns `#!cpp "binary"`, since a JSON text has no binary values (see
|
`type_name()` never returns `#!cpp "binary"`, since a JSON text has no binary values (see
|
||||||
[`is_binary()`](is_binary.md)); it also never returns `#!cpp "invalid"`, since a view's `#!cpp kind` always comes
|
[`is_binary()`](is_binary.md)); it also never returns `#!cpp "invalid"`, since a view's `#!cpp kind` always comes
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ equivalent) deduce `string_t`, not `const char*`, for their return type and for
|
|||||||
|
|
||||||
## Exception safety
|
## Exception safety
|
||||||
|
|
||||||
Strong guarantee: there are no changes to the view or the document it refers to.
|
Strong exception safety: if an exception is thrown, there are no changes to the view or the document it refers to.
|
||||||
|
|
||||||
## Exceptions
|
## Exceptions
|
||||||
|
|
||||||
@@ -70,8 +70,8 @@ None of these exceptions carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics
|
|||||||
1. Linear in the number of members: as for [`operator[]`](operator[].md#complexity), members are compared one after
|
1. Linear in the number of members: as for [`operator[]`](operator[].md#complexity), members are compared one after
|
||||||
another, in document order, stopping at the first match. Plus the complexity of converting
|
another, in document order, stopping at the first match. Plus the complexity of converting
|
||||||
the found member to `T` (see [`get`](get.md)).
|
the found member to `T` (see [`get`](get.md)).
|
||||||
Objects with 128 or more members get a hash index of their keys while parsing, so that a lookup in them takes
|
Objects with 128 or more members get a hash index while parsing, so that a lookup in them takes constant time
|
||||||
constant time on average.
|
on average.
|
||||||
2. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
2. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
||||||
that level or the index into the array -- as for the [`operator[]`](operator[].md#complexity) and
|
that level or the index into the array -- as for the [`operator[]`](operator[].md#complexity) and
|
||||||
[`at`](at.md#complexity) overloads that take a JSON pointer. Plus the complexity of converting the resolved value
|
[`at`](at.md#complexity) overloads that take a JSON pointer. Plus the complexity of converting the resolved value
|
||||||
|
|||||||
@@ -16,10 +16,6 @@ header. See also the [macro overview page](../../features/macros.md).
|
|||||||
|
|
||||||
## Parsing
|
## Parsing
|
||||||
|
|
||||||
- [**JSON_DISABLE_STREAM_BULK_SCAN**](json_disable_stream_bulk_scan.md) - read a `std::istream` one character at a time
|
|
||||||
instead of scanning its stream buffer in bulk
|
|
||||||
- [**JSON_DISABLE_UNLOCKED_FILE_IO**](json_disable_unlocked_file_io.md) - read a `FILE*` with `std::fgetc` instead of
|
|
||||||
locking it once and using the unlocked stdio functions
|
|
||||||
- [**JSON_PRECISE_STREAM_POSITION**](json_precise_stream_position.md) - opt in to leaving an input stream positioned
|
- [**JSON_PRECISE_STREAM_POSITION**](json_precise_stream_position.md) - opt in to leaving an input stream positioned
|
||||||
right after a parsed number
|
right after a parsed number
|
||||||
- [**JSON_STRICT_BINARY_UTF8**](json_strict_binary_utf8.md) - opt in to checking strings for valid UTF-8 in the CBOR,
|
- [**JSON_STRICT_BINARY_UTF8**](json_strict_binary_utf8.md) - opt in to checking strings for valid UTF-8 in the CBOR,
|
||||||
|
|||||||
@@ -1,144 +0,0 @@
|
|||||||
# JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#define JSON_DISABLE_STREAM_BULK_SCAN /* value */
|
|
||||||
```
|
|
||||||
|
|
||||||
When defined to `1`, parsing from a `#!cpp std::istream` reads the input one character at a time through the stream
|
|
||||||
buffer, as in version 3.12.0 and earlier, instead of scanning the characters that the stream buffer already holds in
|
|
||||||
bulk.
|
|
||||||
|
|
||||||
The macro is an escape hatch for stream buffers that do not follow the usual contract of `std::basic_streambuf` (see
|
|
||||||
the requirements on the stream buffer in the notes below). It does not change any result: values, exceptions, and the
|
|
||||||
position of the stream after parsing are the same either way. Only the speed differs.
|
|
||||||
|
|
||||||
## Default definition
|
|
||||||
|
|
||||||
The default value is `0`: streams are scanned in bulk.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#define JSON_DISABLE_STREAM_BULK_SCAN 0
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
!!! note "How streams are scanned"
|
|
||||||
|
|
||||||
A `std::streambuf` keeps the part of the input it has read but not yet handed out in its *get area*, the range
|
|
||||||
from `gptr()` to `egptr()`. A `std::filebuf`, for instance, reads the file in blocks of a few kilobytes into its get
|
|
||||||
area, and a `std::stringbuf` exposes its whole string as one get area.
|
|
||||||
|
|
||||||
When parsing from a `std::istream`, the parser scans this get area directly, with the same fast paths that it uses
|
|
||||||
for a `std::string`: runs of ordinary string characters, numbers, and indentation are recognized several bytes at a
|
|
||||||
time instead of being requested from the stream buffer one character at a time. When the parser reaches the end of
|
|
||||||
the get area, it asks the stream buffer for the next character as before, which makes the stream buffer refill its
|
|
||||||
get area.
|
|
||||||
|
|
||||||
The parser only reads characters that it would also have read one at a time, and it advances the stream buffer
|
|
||||||
exactly over the characters it consumes. Nothing is read ahead, buffered elsewhere, or put back. Therefore, the
|
|
||||||
stream is left exactly where it was left before:
|
|
||||||
|
|
||||||
- [`operator>>`](../operator_gtgt.md) and [`sax_parse`](../basic_json/sax_parse.md) with `strict = false` can still
|
|
||||||
read several values from one stream, and the stream can still be used with other `std::istream` functions after a
|
|
||||||
value has been read.
|
|
||||||
- With [`JSON_PRECISE_STREAM_POSITION`](json_precise_stream_position.md), the stream is still positioned right after
|
|
||||||
a parsed number.
|
|
||||||
- Exception messages, including the line, the column, and the last read token, are unchanged.
|
|
||||||
|
|
||||||
!!! note "Performance"
|
|
||||||
|
|
||||||
Parsing from a `std::ifstream` or `std::istringstream` is typically 1.5 to 2 times as fast as before, and reading
|
|
||||||
many small values from one stream with `operator>>` 1.4 to 4 times as fast. Parsing from a stream is then within
|
|
||||||
about 10 to 20 % of first reading the whole file into a `std::string` and parsing that.
|
|
||||||
|
|
||||||
A stream buffer without a get area (one that only overrides `underflow()` and `uflow()` and hands out every
|
|
||||||
character from there) offers nothing to scan in bulk, so it is read one character at a time as before.
|
|
||||||
|
|
||||||
!!! note "Requirements on the stream buffer"
|
|
||||||
|
|
||||||
The bulk scan relies on the following behavior, which every stream buffer of the standard library and every stream
|
|
||||||
buffer that keeps its input in its get area has:
|
|
||||||
|
|
||||||
- When `sgetc()` returns a character while `gptr() < egptr()`, that character is `*gptr()`; when `sbumpc()` returns
|
|
||||||
one, it was at `gptr()` before the call.
|
|
||||||
- Characters in the get area are not changed while they are in the get area.
|
|
||||||
- `underflow()` or `uflow()` either sets up a get area that holds the returned character at `gptr()` (or at
|
|
||||||
`gptr() - 1` after `uflow()`), or leaves the get area empty.
|
|
||||||
|
|
||||||
A stream buffer that returns characters from `uflow()` while leaving unrelated characters in its get area, or that
|
|
||||||
changes the characters in its get area in place, does not meet these requirements. Define the macro to `1` for
|
|
||||||
such stream buffers.
|
|
||||||
|
|
||||||
!!! note "ABI compatibility"
|
|
||||||
|
|
||||||
The value of this macro is encoded in the [namespace](../../features/namespace.md) (tag `_dsbs`), resulting in
|
|
||||||
distinct symbol names. Translation units compiled with and without it can therefore be linked into the same program
|
|
||||||
without One Definition Rule (ODR) violations, but they cannot exchange instances of library types.
|
|
||||||
|
|
||||||
!!! note "Other inputs"
|
|
||||||
|
|
||||||
Strings, string views, vectors, arrays, and pointer or iterator ranges over contiguous memory are scanned in bulk
|
|
||||||
anyway. Reading from a `#!cpp FILE*` is not affected by this macro; see
|
|
||||||
[`JSON_DISABLE_UNLOCKED_FILE_IO`](json_disable_unlocked_file_io.md) for that input.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
??? example "Example: reading values from a stream (default)"
|
|
||||||
|
|
||||||
The stream is scanned in bulk, and the values are read one after the other:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
std::istringstream input(R"({"id": 1} {"id": 2} 42 rest)");
|
|
||||||
json j1, j2, j3;
|
|
||||||
input >> j1 >> j2 >> j3; // {"id":1}, {"id":2}, 42
|
|
||||||
|
|
||||||
std::string word;
|
|
||||||
input >> word; // "rest"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
??? example "Example: reading one character at a time (macro defined to 1)"
|
|
||||||
|
|
||||||
With the macro, the same program reads the stream one character at a time, with the same results:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#define JSON_DISABLE_STREAM_BULK_SCAN 1
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
std::istringstream input(R"({"id": 1} {"id": 2} 42 rest)");
|
|
||||||
json j1, j2, j3;
|
|
||||||
input >> j1 >> j2 >> j3; // {"id":1}, {"id":2}, 42
|
|
||||||
|
|
||||||
std::string word;
|
|
||||||
input >> word; // "rest"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## See also
|
|
||||||
|
|
||||||
- [**parse**](../basic_json/parse.md) - deserialize from a compatible input
|
|
||||||
- [**operator>>**](../operator_gtgt.md) - deserialize from stream
|
|
||||||
- [**sax_parse**](../basic_json/sax_parse.md) - generate SAX events
|
|
||||||
- [**JSON_PRECISE_STREAM_POSITION**](json_precise_stream_position.md) - leave an input stream positioned right after a
|
|
||||||
parsed number
|
|
||||||
- [**JSON_DISABLE_UNLOCKED_FILE_IO**](json_disable_unlocked_file_io.md) - read a `FILE*` with the locking stdio
|
|
||||||
functions
|
|
||||||
- [Parsing: input sources and performance](../../features/parsing/index.md#input-sources-and-performance)
|
|
||||||
|
|
||||||
## Version history
|
|
||||||
|
|
||||||
- Added in version 3.13.0.
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
# JSON_DISABLE_UNLOCKED_FILE_IO
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#define JSON_DISABLE_UNLOCKED_FILE_IO /* value */
|
|
||||||
```
|
|
||||||
|
|
||||||
When defined to `1`, parsing from a `#!cpp FILE*` reads every character with `std::fgetc`, as in version 3.12.0 and
|
|
||||||
earlier, instead of locking the file once and reading it with the unlocked stdio functions.
|
|
||||||
|
|
||||||
It does not change any result: values, exceptions, and the position of the file after parsing are the same either way.
|
|
||||||
Only the speed and the locking of the file differ (see the notes on locking below).
|
|
||||||
|
|
||||||
Builds with MemorySanitizer use `std::fgetc` automatically, because the inline `getc_unlocked` of glibc reads the stdio
|
|
||||||
buffer directly, which glibc (not instrumented) filled, and MemorySanitizer would report it as uninitialized.
|
|
||||||
|
|
||||||
## Default definition
|
|
||||||
|
|
||||||
The default value is `0`: the file is locked once and read with the unlocked stdio functions where they are available.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#define JSON_DISABLE_UNLOCKED_FILE_IO 0
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
!!! note "Background"
|
|
||||||
|
|
||||||
The C standard library makes every `std::fgetc` call thread-safe by locking and unlocking the `FILE` object. When
|
|
||||||
parsing from a `FILE*`, the parser reads one character at a time, so this happens for every character of the input.
|
|
||||||
|
|
||||||
Instead, the parser locks the file once when parsing starts and unlocks it when parsing ends - also when an
|
|
||||||
exception is thrown - and reads the characters in between without locking:
|
|
||||||
|
|
||||||
| C library | functions |
|
|
||||||
|------------------------------------------------------------|----------------------------------------------------|
|
|
||||||
| glibc (Linux), Apple (macOS, iOS), Android, FreeBSD, NetBSD, OpenBSD, DragonFly BSD | `flockfile`, `getc_unlocked`, `funlockfile` (POSIX) |
|
|
||||||
| Microsoft (MSVC, clang-cl) | `_lock_file`, `_fgetc_nolock`, `_unlock_file` |
|
|
||||||
| others (e.g., musl, MinGW, newlib) | `std::fgetc`, as before |
|
|
||||||
|
|
||||||
This makes parsing from a `FILE*` 3 to 4 times as fast with the Apple C library and 1.1 to 1.3 times as fast with
|
|
||||||
glibc, whose locking is cheaper. The binary formats ([`from_cbor`](../basic_json/from_cbor.md),
|
|
||||||
[`from_msgpack`](../basic_json/from_msgpack.md), ...) read from a `FILE*` the same way.
|
|
||||||
|
|
||||||
!!! note "Locking"
|
|
||||||
|
|
||||||
While a value is parsed from a `FILE*`, the parsing thread holds the file's lock. The lock is recursive, so the
|
|
||||||
parsing thread itself - for instance, a [SAX](../json_sax/index.md) handler or a
|
|
||||||
[parser callback](../basic_json/parser_callback_t.md) - can keep using the file with stdio functions such as
|
|
||||||
`std::ftell`. Other threads that use the same `FILE` object wait until the parser is done. Before, they could
|
|
||||||
interleave their reads with the parser's, which made the parsed input unpredictable anyway.
|
|
||||||
|
|
||||||
A program in which a SAX handler or parser callback waits for **another** thread that uses the **same** `FILE`
|
|
||||||
object would deadlock. Define the macro to `1` for such programs.
|
|
||||||
|
|
||||||
!!! note "ABI compatibility"
|
|
||||||
|
|
||||||
The value of this macro is encoded in the [namespace](../../features/namespace.md) (tag `_dufio`), resulting in
|
|
||||||
distinct symbol names. Translation units compiled with and without it can therefore be linked into the same program
|
|
||||||
without One Definition Rule (ODR) violations, but they cannot exchange instances of library types.
|
|
||||||
|
|
||||||
!!! note "Other inputs"
|
|
||||||
|
|
||||||
Reading from a `#!cpp std::istream` is not affected by this macro; see
|
|
||||||
[`JSON_DISABLE_STREAM_BULK_SCAN`](json_disable_stream_bulk_scan.md) for that input.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
??? example "Example: parsing from a `FILE*` (default)"
|
|
||||||
|
|
||||||
The file is locked once while it is parsed:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#include <cstdio>
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
std::FILE* f = std::fopen("example.json", "rb");
|
|
||||||
json j = json::parse(f); // f is locked until parse() returns
|
|
||||||
std::fclose(f);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
??? example "Example: locking for every character (macro defined to 1)"
|
|
||||||
|
|
||||||
With the macro, every character is read with `std::fgetc`, with the same result:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#define JSON_DISABLE_UNLOCKED_FILE_IO 1
|
|
||||||
#include <cstdio>
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
std::FILE* f = std::fopen("example.json", "rb");
|
|
||||||
json j = json::parse(f); // std::fgetc for every character
|
|
||||||
std::fclose(f);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## See also
|
|
||||||
|
|
||||||
- [**parse**](../basic_json/parse.md) - deserialize from a compatible input
|
|
||||||
- [**sax_parse**](../basic_json/sax_parse.md) - generate SAX events
|
|
||||||
- [**JSON_DISABLE_STREAM_BULK_SCAN**](json_disable_stream_bulk_scan.md) - read a `std::istream` one character at a
|
|
||||||
time
|
|
||||||
- [Parsing: input sources and performance](../../features/parsing/index.md#input-sources-and-performance)
|
|
||||||
|
|
||||||
## Version history
|
|
||||||
|
|
||||||
- Added in version 3.13.0.
|
|
||||||
@@ -72,12 +72,6 @@ The default value is `0` (disabled — existing behavior is preserved).
|
|||||||
distinct symbol names. Translation units compiled with and without it can therefore be linked into the same program
|
distinct symbol names. Translation units compiled with and without it can therefore be linked into the same program
|
||||||
without One Definition Rule (ODR) violations, but they cannot exchange instances of library types.
|
without One Definition Rule (ODR) violations, but they cannot exchange instances of library types.
|
||||||
|
|
||||||
!!! note "Bulk scanning"
|
|
||||||
|
|
||||||
The parser scans the characters in the stream buffer in bulk (see
|
|
||||||
[`JSON_DISABLE_STREAM_BULK_SCAN`](json_disable_stream_bulk_scan.md)), but it never reads ahead. The stream is left
|
|
||||||
at the same position with and without bulk scanning, in both modes of this macro.
|
|
||||||
|
|
||||||
!!! tip "Workaround without the macro"
|
!!! tip "Workaround without the macro"
|
||||||
|
|
||||||
Separate the values in the stream with whitespace. The character consumed after a number is then the separator,
|
Separate the values in the stream with whitespace. The character consumed after a number is then the separator,
|
||||||
|
|||||||
@@ -12,9 +12,8 @@ non-ASCII characters (e.g. CJK or emoji) this can validate several times faster.
|
|||||||
This is an **opt-in external dependency**. The library itself remains header-only and its behavior is unchanged: the
|
This is an **opt-in external dependency**. The library itself remains header-only and its behavior is unchanged: the
|
||||||
same input is accepted or rejected either way, and every parse error is reported at the same position with the same
|
same input is accepted or rejected either way, and every parse error is reported at the same position with the same
|
||||||
message (simdutf is only used to fast-path *valid* runs; anything it flags falls back to the scalar path so the exact
|
message (simdutf is only used to fast-path *valid* runs; anything it flags falls back to the scalar path so the exact
|
||||||
diagnostic is preserved). A `std::istream` uses simdutf for the characters its stream buffer holds (see
|
diagnostic is preserved). Streaming inputs (files, `std::istream`, wide strings, user-defined adapters) always use the
|
||||||
[`JSON_DISABLE_STREAM_BULK_SCAN`](json_disable_stream_bulk_scan.md)); other streaming inputs (`FILE*`, wide strings,
|
scalar path.
|
||||||
user-defined adapters) always use the scalar path.
|
|
||||||
|
|
||||||
When `JSON_USE_SIMDUTF` is defined you must make the `simdutf.h` header available on the include path and link the
|
When `JSON_USE_SIMDUTF` is defined you must make the `simdutf.h` header available on the include path and link the
|
||||||
simdutf library. When it is not defined, no simdutf header is included and there is no dependency.
|
simdutf library. When it is not defined, no simdutf header is included and there is no dependency.
|
||||||
|
|||||||
@@ -14,12 +14,10 @@ The same input is accepted or rejected either way, with the same values, and err
|
|||||||
the speed differs. The macro exists for platforms whose compilers lack the intrinsics headers, and to test the portable
|
the speed differs. The macro exists for platforms whose compilers lack the intrinsics headers, and to test the portable
|
||||||
code.
|
code.
|
||||||
|
|
||||||
!!! note "ABI compatibility"
|
!!! warning "Define consistently"
|
||||||
|
|
||||||
The macro selects between two definitions of the same inline functions. Whether it is defined is encoded in the
|
The macro selects between two definitions of the same inline functions. It must therefore be defined identically for
|
||||||
[namespace](../../features/namespace.md) (tag `_vns`), resulting in distinct symbol names. Translation units
|
**every** translation unit that includes `<nlohmann/json_view.hpp>`; prefer a compile definition on the target.
|
||||||
compiled with and without it can therefore be linked into the same program without One Definition Rule (ODR)
|
|
||||||
violations, but they cannot exchange instances of library types.
|
|
||||||
|
|
||||||
## Default definition
|
## Default definition
|
||||||
|
|
||||||
|
|||||||
@@ -17,12 +17,12 @@ Define the macro only together with a compiler option that enables SSSE3 (e.g. `
|
|||||||
has it), and only for programs that run on such CPUs. It saves the check of the CPU, which costs little. The same
|
has it), and only for programs that run on such CPUs. It saves the check of the CPU, which costs little. The same
|
||||||
input is accepted or rejected either way; only the speed of non-ASCII text differs.
|
input is accepted or rejected either way; only the speed of non-ASCII text differs.
|
||||||
|
|
||||||
!!! note "ABI compatibility"
|
!!! warning "Define consistently"
|
||||||
|
|
||||||
The macro selects between two definitions of the same inline functions. Whether it is defined is encoded in the
|
The macro selects between two definitions of the same inline functions. It must therefore be defined identically,
|
||||||
[namespace](../../features/namespace.md) (tag `_vssse3`, on every architecture), resulting in distinct symbol
|
with the same compiler options, for **every** translation unit that includes `<nlohmann/json_view.hpp>`; mixing
|
||||||
names. Translation units compiled with and without it can therefore be linked into the same program without One
|
translation units that define it with ones that do not is an ODR violation. Prefer a compile definition on the
|
||||||
Definition Rule (ODR) violations, but they cannot exchange instances of library types.
|
target.
|
||||||
|
|
||||||
## Default definition
|
## Default definition
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,5 @@ the library.
|
|||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 3.11.0. Changed inline namespace name in version 3.11.2. Added the ABI tag `_dp` in
|
- Added in version 3.11.0. Changed inline namespace name in version 3.11.2. Added the ABI tag `_dp` in
|
||||||
version 3.12.0, and the ABI tags `_bics`, `_psp`, `_snul`, `_sbu8`, `_ekmo`, `_dsbs`, `_dufio`, `_vns`, and
|
version 3.12.0, and the ABI tags `_bics`, `_psp`, `_snul`, `_sbu8`, and `_ekmo` in version 3.13.0; see
|
||||||
`_vssse3` in version 3.13.0; see
|
|
||||||
[`nlohmann` Namespace](../../features/namespace.md#structure).
|
[`nlohmann` Namespace](../../features/namespace.md#structure).
|
||||||
@@ -121,7 +121,6 @@ being read.
|
|||||||
- [`JSON_STRICT_NUL_HANDLING`](macros/json_strict_nul_handling.md) - opt in to rejecting a NUL byte in the input
|
- [`JSON_STRICT_NUL_HANDLING`](macros/json_strict_nul_handling.md) - opt in to rejecting a NUL byte in the input
|
||||||
instead of treating it as end of input
|
instead of treating it as end of input
|
||||||
- [`JSON_PRECISE_STREAM_POSITION`](macros/json_precise_stream_position.md) - opt in to leaving the stream positioned right after a number
|
- [`JSON_PRECISE_STREAM_POSITION`](macros/json_precise_stream_position.md) - opt in to leaving the stream positioned right after a number
|
||||||
- [`JSON_DISABLE_STREAM_BULK_SCAN`](macros/json_disable_stream_bulk_scan.md) - read the stream one character at a time
|
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
@@ -134,6 +133,3 @@ being read.
|
|||||||
crash (`std::terminate`) when `i` has `eofbit` in its exception mask, in version 3.13.0.
|
crash (`std::terminate`) when `i` has `eofbit` in its exception mask, in version 3.13.0.
|
||||||
- Changed to the strong exception safety guarantee in version 3.13.0: `j` is no longer left with a partially parsed
|
- Changed to the strong exception safety guarantee in version 3.13.0: `j` is no longer left with a partially parsed
|
||||||
value if parsing throws.
|
value if parsing throws.
|
||||||
- Scans the characters in the stream buffer of `i` in bulk in version 3.13.0; the position of `i` after the call is
|
|
||||||
unchanged ([`JSON_DISABLE_STREAM_BULK_SCAN`](macros/json_disable_stream_bulk_scan.md) restores reading one character
|
|
||||||
at a time).
|
|
||||||
@@ -135,9 +135,6 @@ Note: Some modern features (like C++20 ranges or filesystem support) may be disa
|
|||||||
headers are included.
|
headers are included.
|
||||||
- [x] On Windows, the library is compiled with `<Windows.h>` being included to detect and avoid common bugs (see
|
- [x] On Windows, the library is compiled with `<Windows.h>` being included to detect and avoid common bugs (see
|
||||||
[`unit-windows_h.cpp`](https://github.com/nlohmann/json/blob/develop/tests/src/unit-windows_h.cpp)).
|
[`unit-windows_h.cpp`](https://github.com/nlohmann/json/blob/develop/tests/src/unit-windows_h.cpp)).
|
||||||
- [x] The library is compiled with Qt's keyword macros (`slots`, `signals`, `emit`, `foreach`, `forever`) being defined
|
|
||||||
to make sure it does not use them as identifiers (see
|
|
||||||
[`unit-qt_keywords.cpp`](https://github.com/nlohmann/json/blob/develop/tests/src/unit-qt_keywords.cpp)).
|
|
||||||
- [x] The library is compiled with exceptions disabled to support alternative means of error handling.
|
- [x] The library is compiled with exceptions disabled to support alternative means of error handling.
|
||||||
|
|
||||||
## Stable public API
|
## Stable public API
|
||||||
|
|||||||
@@ -5,14 +5,13 @@ using json_document = nlohmann::json_document;
|
|||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
// validate that every transaction of a batch carries exactly one
|
// validate that every transaction of a batch carries a mandatory
|
||||||
// "amount" field before materializing any of them into a nlohmann::json
|
// "amount" field before materializing any of them into a nlohmann::json
|
||||||
// value -- count() counts every member with the key
|
// value -- count() returns 0 or 1 for an object
|
||||||
json_document batch = json_document::parse(R"(
|
json_document batch = json_document::parse(R"(
|
||||||
[
|
[
|
||||||
{"id": 1, "amount": 9.99},
|
{"id": 1, "amount": 9.99},
|
||||||
{"id": 2},
|
{"id": 2}
|
||||||
{"id": 3, "amount": 5.00, "amount": 500.00}
|
|
||||||
]
|
]
|
||||||
)");
|
)");
|
||||||
|
|
||||||
@@ -20,10 +19,9 @@ int main()
|
|||||||
for (std::size_t i = 0; i < transactions.size(); ++i)
|
for (std::size_t i = 0; i < transactions.size(); ++i)
|
||||||
{
|
{
|
||||||
const auto transaction = transactions[i];
|
const auto transaction = transactions[i];
|
||||||
const auto amounts = transaction.count("amount");
|
if (transaction.count("amount") == 0)
|
||||||
if (amounts != 1)
|
|
||||||
{
|
{
|
||||||
std::cout << "transaction " << i << " has " << amounts << " \"amount\" fields\n";
|
std::cout << "transaction " << i << " is missing \"amount\"\n";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
std::cout << "transaction " << i << ": " << transaction["amount"].materialize().dump() << '\n';
|
std::cout << "transaction " << i << ": " << transaction["amount"].materialize().dump() << '\n';
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
transaction 0: 9.99
|
transaction 0: 9.99
|
||||||
transaction 1 has 0 "amount" fields
|
transaction 1 is missing "amount"
|
||||||
transaction 2 has 2 "amount" fields
|
|
||||||
@@ -14,8 +14,8 @@ C++ types, and finally serialize it again.
|
|||||||
[parsing untrusted input](parsing/untrusted_input.md).
|
[parsing untrusted input](parsing/untrusted_input.md).
|
||||||
- [Zero-copy JSON views](json_view.md) — read a JSON text through a flat index instead of building a `json` tree;
|
- [Zero-copy JSON views](json_view.md) — read a JSON text through a flat index instead of building a `json` tree;
|
||||||
strings and numbers stay in the input and are only decoded when needed.
|
strings and numbers stay in the input and are only decoded when needed.
|
||||||
- [Editable documents](json_view.md#editing-a-document) — modify a parsed document without building a `json` tree.
|
[Editable documents](json_view.md#editing-a-document) can also be modified, and [images](json_view.md#images) load a
|
||||||
- [Images](json_view.md#images) — save a parsed document and load it again without parsing it.
|
parsed document again without parsing it.
|
||||||
- [Comments](comments.md) and [trailing commas](trailing_commas.md) — opt-in relaxations of the JSON grammar.
|
- [Comments](comments.md) and [trailing commas](trailing_commas.md) — opt-in relaxations of the JSON grammar.
|
||||||
|
|
||||||
## Accessing and modifying values
|
## Accessing and modifying values
|
||||||
|
|||||||
@@ -16,9 +16,8 @@ most of the resulting tree is thrown away almost immediately.
|
|||||||
[`basic_json_document::parse()`](../api/basic_json_document/parse.md) parses the same JSON grammar, with the same
|
[`basic_json_document::parse()`](../api/basic_json_document/parse.md) parses the same JSON grammar, with the same
|
||||||
options, but instead of a tree it builds a flat index of the values it found: one
|
options, but instead of a tree it builds a flat index of the values it found: one
|
||||||
[16-byte entry](../home/architecture.md#node-index-of-json-views) per value (and one per object key), in document
|
[16-byte entry](../home/architecture.md#node-index-of-json-views) per value (and one per object key), in document
|
||||||
order. Integers are converted while parsing and stored in their entry. Floating-point numbers and strings are
|
order. Strings and numbers are referenced in place in the source text; only strings that contain escape sequences
|
||||||
referenced in place in the source text: a float is converted when it is read, and only strings that contain escape
|
are decoded, into one shared buffer owned by the document.
|
||||||
sequences are decoded, into one shared buffer owned by the document.
|
|
||||||
|
|
||||||
[`basic_json_view`](../api/basic_json_view/index.md) is a small, trivially copyable handle (two pointers) into that
|
[`basic_json_view`](../api/basic_json_view/index.md) is a small, trivially copyable handle (two pointers) into that
|
||||||
index. It gives you the read-only, type-inspection part of the `basic_json` interface --
|
index. It gives you the read-only, type-inspection part of the `basic_json` interface --
|
||||||
@@ -67,10 +66,9 @@ table). [`parse_copy()`](../api/basic_json_document/parse_copy.md) always owns a
|
|||||||
|
|
||||||
!!! warning "A borrowed document depends on your buffer"
|
!!! warning "A borrowed document depends on your buffer"
|
||||||
|
|
||||||
If a document borrows its text, that text **must outlive the document** (and every view taken from it) and must
|
If a document borrows its text, that text **must outlive the document** (and every view taken from it). Reading
|
||||||
stay **unmodified** for as long as the document exists, since floating-point values and unescaped strings are
|
or writing through a view after the underlying buffer is gone is undefined behavior, exactly as it would be for
|
||||||
read from it lazily. Reading or writing through a view after the underlying buffer is gone is undefined behavior,
|
a dangling `#!cpp std::string_view`.
|
||||||
exactly as it would be for a dangling `#!cpp std::string_view`.
|
|
||||||
|
|
||||||
A view is valid only while all of the following hold:
|
A view is valid only while all of the following hold:
|
||||||
|
|
||||||
@@ -78,7 +76,7 @@ A view is valid only while all of the following hold:
|
|||||||
- the document has not been re-parsed since the view was taken (with [`read()`](../api/basic_json_document/read.md)
|
- the document has not been re-parsed since the view was taken (with [`read()`](../api/basic_json_document/read.md)
|
||||||
or [`parse()`](../api/basic_json_document/parse.md) into it),
|
or [`parse()`](../api/basic_json_document/parse.md) into it),
|
||||||
- the document has not had [`shrink_to_fit()`](../api/basic_json_document/shrink_to_fit.md) called on it since, and
|
- the document has not had [`shrink_to_fit()`](../api/basic_json_document/shrink_to_fit.md) called on it since, and
|
||||||
- if the document borrows its source text, that text is still alive and unmodified.
|
- if the document borrows its source text, that text is still alive.
|
||||||
|
|
||||||
Moving the `basic_json_document<>` itself is fine and does **not** invalidate its views: the index is a separate heap
|
Moving the `basic_json_document<>` itself is fine and does **not** invalidate its views: the index is a separate heap
|
||||||
allocation that keeps its address across the move. Take a fresh view from [`root()`](../api/basic_json_document/root.md)
|
allocation that keeps its address across the move. Take a fresh view from [`root()`](../api/basic_json_document/root.md)
|
||||||
@@ -168,10 +166,10 @@ document: `#!cpp auto v = json_document::parse(text).root();` does not compile.
|
|||||||
|
|
||||||
If an object in the source text repeats a key, [`operator[]`](../api/basic_json_view/operator%5B%5D.md),
|
If an object in the source text repeats a key, [`operator[]`](../api/basic_json_view/operator%5B%5D.md),
|
||||||
[`at`](../api/basic_json_view/at.md), [`find`](../api/basic_json_view/find.md),
|
[`at`](../api/basic_json_view/at.md), [`find`](../api/basic_json_view/find.md),
|
||||||
[`value`](../api/basic_json_view/value.md), [`contains`](../api/basic_json_view/contains.md), and JSON pointer
|
[`value`](../api/basic_json_view/value.md), [`contains`](../api/basic_json_view/contains.md),
|
||||||
resolution all return the **first** member with that key. `basic_json::parse()` instead keeps the **last** value
|
[`count`](../api/basic_json_view/count.md), and JSON pointer resolution all return the **first** member with that
|
||||||
of a repeated key, so for `#!cpp {"a":1,"a":2}`, `#!cpp view["a"]` is `#!cpp 1` while `#!cpp parse(text)["a"]` is
|
key. `basic_json::parse()` instead keeps the **last** value of a repeated key, so for `#!cpp {"a":1,"a":2}`,
|
||||||
`#!cpp 2`. [`count`](../api/basic_json_view/count.md) counts every member with the key, so it is `#!cpp 2` here.
|
`#!cpp view["a"]` is `#!cpp 1` while `#!cpp parse(text)["a"]` is `#!cpp 2`.
|
||||||
|
|
||||||
[`begin()`](../api/basic_json_view/begin.md)/[`end()`](../api/basic_json_view/end.md) and
|
[`begin()`](../api/basic_json_view/begin.md)/[`end()`](../api/basic_json_view/end.md) and
|
||||||
[`items()`](../api/basic_json_view/items.md) visit *every* occurrence, in document order, and
|
[`items()`](../api/basic_json_view/items.md) visit *every* occurrence, in document order, and
|
||||||
@@ -180,7 +178,7 @@ document: `#!cpp auto v = json_document::parse(text).root();` does not compile.
|
|||||||
|
|
||||||
**Why the first?** A lookup can stop as soon as it finds a match. Returning the last member would force every lookup
|
**Why the first?** A lookup can stop as soon as it finds a match. Returning the last member would force every lookup
|
||||||
to scan all members of the object, even when the key is found at the very first one: this made lookups in small
|
to scan all members of the object, even when the key is found at the very first one: this made lookups in small
|
||||||
objects 1.6 to 3.4 times slower. The hash index of the keys of objects with 128 members or more leads to the first member of a
|
objects 1.6 to 3.4 times slower. The hash index of objects with 128 members or more leads to the first member of a
|
||||||
key as well. Other zero-copy parsers that index the source text, such as yyjson and simdjson, also return the first
|
key as well. Other zero-copy parsers that index the source text, such as yyjson and simdjson, also return the first
|
||||||
member. RFC 8259 only says that names within an object SHOULD be unique and that the behavior of a receiver that sees
|
member. RFC 8259 only says that names within an object SHOULD be unique and that the behavior of a receiver that sees
|
||||||
duplicates is unpredictable, so neither choice is wrong.
|
duplicates is unpredictable, so neither choice is wrong.
|
||||||
|
|||||||
@@ -133,14 +133,6 @@ When defined, default parse and serialize functions for enums are excluded and h
|
|||||||
|
|
||||||
See [full documentation of `JSON_DISABLE_ENUM_SERIALIZATION`](../api/macros/json_disable_enum_serialization.md).
|
See [full documentation of `JSON_DISABLE_ENUM_SERIALIZATION`](../api/macros/json_disable_enum_serialization.md).
|
||||||
|
|
||||||
## `JSON_DISABLE_STREAM_BULK_SCAN`
|
|
||||||
|
|
||||||
When defined to `1`, parsing from a `#!cpp std::istream` reads one character at a time instead of scanning the
|
|
||||||
characters that the stream buffer holds in bulk. Results are the same either way; the macro is an escape hatch for
|
|
||||||
stream buffers that do not follow the usual `std::streambuf` contract. The default value is `0`.
|
|
||||||
|
|
||||||
See [full documentation of `JSON_DISABLE_STREAM_BULK_SCAN`](../api/macros/json_disable_stream_bulk_scan.md).
|
|
||||||
|
|
||||||
## `JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`
|
## `JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`
|
||||||
|
|
||||||
When defined to `1`, a JSON value can no longer be created from a one-element `std::tuple` holding a reference to a JSON
|
When defined to `1`, a JSON value can no longer be created from a one-element `std::tuple` holding a reference to a JSON
|
||||||
@@ -149,14 +141,6 @@ is planned to become the default in version 4.0.0.
|
|||||||
|
|
||||||
See [full documentation of `JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`](../api/macros/json_disable_tuple_reference_conversion.md).
|
See [full documentation of `JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`](../api/macros/json_disable_tuple_reference_conversion.md).
|
||||||
|
|
||||||
## `JSON_DISABLE_UNLOCKED_FILE_IO`
|
|
||||||
|
|
||||||
When defined to `1`, parsing from a `#!cpp FILE*` reads every character with `std::fgetc`, which locks the file each
|
|
||||||
time, instead of locking the file once and reading it with the unlocked stdio functions. Results are the same either
|
|
||||||
way. The default value is `0`.
|
|
||||||
|
|
||||||
See [full documentation of `JSON_DISABLE_UNLOCKED_FILE_IO`](../api/macros/json_disable_unlocked_file_io.md).
|
|
||||||
|
|
||||||
## `JSON_NO_AUTOMATIC_UDLS`
|
## `JSON_NO_AUTOMATIC_UDLS`
|
||||||
|
|
||||||
When defined, `<nlohmann/json.hpp>` does not include `<nlohmann/json_literals.hpp>` with the user-defined string literals
|
When defined, `<nlohmann/json.hpp>` does not include `<nlohmann/json_literals.hpp>` with the user-defined string literals
|
||||||
|
|||||||
@@ -23,12 +23,6 @@ The complete default namespace name is derived as follows:
|
|||||||
- [`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md) defined non-zero appends `_sbu8`.
|
- [`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md) defined non-zero appends `_sbu8`.
|
||||||
- [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md) defined non-zero
|
- [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md) defined non-zero
|
||||||
appends `_ekmo`.
|
appends `_ekmo`.
|
||||||
- [`JSON_DISABLE_STREAM_BULK_SCAN`](../api/macros/json_disable_stream_bulk_scan.md) defined non-zero appends
|
|
||||||
`_dsbs`.
|
|
||||||
- [`JSON_DISABLE_UNLOCKED_FILE_IO`](../api/macros/json_disable_unlocked_file_io.md) defined non-zero appends
|
|
||||||
`_dufio`.
|
|
||||||
- [`JSON_VIEW_NO_SIMD`](../api/macros/json_view_no_simd.md) defined appends `_vns`.
|
|
||||||
- [`JSON_VIEW_USE_SSSE3`](../api/macros/json_view_use_ssse3.md) defined appends `_vssse3`.
|
|
||||||
- The inline namespace ends with the suffix `_v` followed by the 3 components of the version number separated by
|
- The inline namespace ends with the suffix `_v` followed by the 3 components of the version number separated by
|
||||||
underscores. To omit the version component, see [Disabling the version component](#disabling-the-version-component)
|
underscores. To omit the version component, see [Disabling the version component](#disabling-the-version-component)
|
||||||
below.
|
below.
|
||||||
@@ -111,9 +105,5 @@ follows:
|
|||||||
- Added ABI tags `_bics` ([`JSON_BRACE_INIT_COPY_SEMANTICS`](../api/macros/json_brace_init_copy_semantics.md)), `_psp`
|
- Added ABI tags `_bics` ([`JSON_BRACE_INIT_COPY_SEMANTICS`](../api/macros/json_brace_init_copy_semantics.md)), `_psp`
|
||||||
([`JSON_PRECISE_STREAM_POSITION`](../api/macros/json_precise_stream_position.md)), `_snul`
|
([`JSON_PRECISE_STREAM_POSITION`](../api/macros/json_precise_stream_position.md)), `_snul`
|
||||||
([`JSON_STRICT_NUL_HANDLING`](../api/macros/json_strict_nul_handling.md)), `_sbu8`
|
([`JSON_STRICT_NUL_HANDLING`](../api/macros/json_strict_nul_handling.md)), `_sbu8`
|
||||||
([`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md)), `_ekmo`
|
([`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md)), and `_ekmo`
|
||||||
([`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md)), `_dsbs`
|
([`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md)) in version 3.13.0.
|
||||||
([`JSON_DISABLE_STREAM_BULK_SCAN`](../api/macros/json_disable_stream_bulk_scan.md)), `_dufio`
|
|
||||||
([`JSON_DISABLE_UNLOCKED_FILE_IO`](../api/macros/json_disable_unlocked_file_io.md)), `_vns`
|
|
||||||
([`JSON_VIEW_NO_SIMD`](../api/macros/json_view_no_simd.md)), and `_vssse3`
|
|
||||||
([`JSON_VIEW_USE_SSSE3`](../api/macros/json_view_use_ssse3.md)) in version 3.13.0.
|
|
||||||
@@ -55,30 +55,6 @@ whitespace, because `operator>>` consumes the character that terminates a number
|
|||||||
[`JSON_PRECISE_STREAM_POSITION`](../../api/macros/json_precise_stream_position.md) is defined to `1` — see the
|
[`JSON_PRECISE_STREAM_POSITION`](../../api/macros/json_precise_stream_position.md) is defined to `1` — see the
|
||||||
[`operator>>` notes](../../api/operator_gtgt.md#notes) for details and examples.
|
[`operator>>` notes](../../api/operator_gtgt.md#notes) for details and examples.
|
||||||
|
|
||||||
## Input sources and performance
|
|
||||||
|
|
||||||
All inputs produce the same values and the same exceptions, but they are read in different ways, which makes some of
|
|
||||||
them faster than others:
|
|
||||||
|
|
||||||
| input | how it is read |
|
|
||||||
|--------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|
|
|
||||||
| strings, string views, string literals, contiguous containers, pointer or iterator ranges over contiguous memory | the whole input is scanned in bulk: runs of string characters, numbers, and indentation are recognized several bytes at a time |
|
|
||||||
| `#!cpp std::istream` (`#!cpp std::ifstream`, `#!cpp std::istringstream`, ...) | the characters the stream buffer holds are scanned in bulk like a string; the stream buffer is asked for more when they are used up |
|
|
||||||
| `#!cpp FILE*` | one character at a time, with the file locked once for the whole parse |
|
|
||||||
| other iterator ranges (e.g., `#!cpp std::list<char>`, `#!cpp std::istreambuf_iterator<char>`), wide strings | one character at a time |
|
|
||||||
|
|
||||||
Parsing a file from a `#!cpp std::ifstream` is therefore only about 10 to 20 % slower than reading the whole file into a
|
|
||||||
`#!cpp std::string` first and parsing that, and it needs no memory for a copy of the file. The stream is never read
|
|
||||||
ahead: after parsing, it is positioned exactly as if every character had been read one at a time, so
|
|
||||||
[`operator>>`](../../api/operator_gtgt.md) can still read several values from one stream (see also
|
|
||||||
[JSON Lines](json_lines.md)).
|
|
||||||
|
|
||||||
Two macros switch these optimizations off without changing any result:
|
|
||||||
[`JSON_DISABLE_STREAM_BULK_SCAN`](../../api/macros/json_disable_stream_bulk_scan.md) for stream buffers that do not
|
|
||||||
follow the usual `std::streambuf` contract, and
|
|
||||||
[`JSON_DISABLE_UNLOCKED_FILE_IO`](../../api/macros/json_disable_unlocked_file_io.md) for programs in which another
|
|
||||||
thread must use the same `#!cpp FILE` object while it is parsed.
|
|
||||||
|
|
||||||
## SAX vs. DOM parsing
|
## SAX vs. DOM parsing
|
||||||
|
|
||||||
The library offers two parsing models:
|
The library offers two parsing models:
|
||||||
|
|||||||
@@ -20,38 +20,31 @@ below apply on every supported standard. This adapter exposes two optimizations
|
|||||||
|
|
||||||
- it can reconstruct already-consumed input on demand for error messages, instead of copying every character as it is
|
- it can reconstruct already-consumed input on demand for error messages, instead of copying every character as it is
|
||||||
read, and
|
read, and
|
||||||
- the lexer can scan ordinary string characters, numbers, and indentation directly out of the buffer, several bytes at
|
- the lexer can scan ordinary string characters directly out of the buffer, several bytes at a time, rather than one
|
||||||
a time, rather than one character (and one function call) at a time.
|
character (and one function call) at a time.
|
||||||
|
|
||||||
A `#!cpp std::istream` (including `#!cpp std::ifstream`) gets the same bulk scanning: `input_stream_adapter` scans the
|
A `#!cpp std::istream` (including `#!cpp std::ifstream`) or `#!cpp FILE*`, by contrast, is read through
|
||||||
characters its stream buffer already holds (the *get area*) and asks the stream buffer for more only when they are used
|
`input_stream_adapter` or `file_input_adapter`, which read one character (or one block, for binary formats) at a time
|
||||||
up. Nothing is read ahead, so the stream is left exactly where it would be after reading one character at a time, and
|
and expose neither optimization -- the lexer falls back to the same byte-at-a-time path it uses for any
|
||||||
[`operator>>`](../api/operator_gtgt.md) can still read several values from one stream. A stream buffer without a get
|
non-contiguous, general-purpose iterator range. An iterator pair over non-contiguous but random-access storage (e.g.
|
||||||
area is read one character at a time. A `#!cpp FILE*` is still read one character at a time, but `file_input_adapter`
|
`#!cpp std::deque<char>::iterator`) gets the first optimization but not the second, since the byte-scanning fast path
|
||||||
locks the file once per parse instead of once per character. See
|
additionally requires contiguous storage.
|
||||||
[input sources and performance](parsing/index.md#input-sources-and-performance) for the full table. According to the
|
|
||||||
description of [#5828](https://github.com/nlohmann/json/pull/5828), parsing from a stream is within about 10 to 20 % of
|
|
||||||
parsing an equivalent `#!cpp std::string`.
|
|
||||||
|
|
||||||
Inputs that are still read one character at a time: iterator ranges that are not contiguous (e.g.
|
Practically: if the JSON text is already in memory, or small enough to read into memory, prefer passing a
|
||||||
`#!cpp std::list<char>` or `#!cpp std::istreambuf_iterator<char>`), wide strings, and user-defined adapters. An
|
`#!cpp std::string`, a `#!cpp std::vector<char>`, or a pointer range to `parse` over a `#!cpp std::istream`. For a
|
||||||
iterator pair over non-contiguous but random-access storage (e.g. `#!cpp std::deque<char>::iterator`) gets the first
|
file, that means reading it into a string first and then parsing the string, rather than passing a
|
||||||
optimization but not the second, since the byte-scanning fast path requires contiguous storage.
|
`#!cpp std::ifstream` directly to `parse` -- the latter never benefits from either optimization:
|
||||||
|
|
||||||
Practically: a file can be passed directly to `parse`, which also avoids the memory for a copy of the file. Reading it
|
|
||||||
into a string first is no longer necessary for speed:
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// fast: the stream buffer's get area is scanned in bulk
|
// gets the contiguous fast paths
|
||||||
std::ifstream f("example.json");
|
std::ifstream f("example.json");
|
||||||
json j = json::parse(f);
|
std::string contents((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
|
||||||
```
|
json j = json::parse(contents);
|
||||||
|
|
||||||
Two macros switch the stream optimizations off without changing any result:
|
// does not: input_stream_adapter has no fast path
|
||||||
[`JSON_DISABLE_STREAM_BULK_SCAN`](../api/macros/json_disable_stream_bulk_scan.md) for stream buffers that do not follow
|
std::ifstream f2("example.json");
|
||||||
the usual `#!cpp std::streambuf` contract, and
|
json j2 = json::parse(f2);
|
||||||
[`JSON_DISABLE_UNLOCKED_FILE_IO`](../api/macros/json_disable_unlocked_file_io.md) for programs in which another thread
|
```
|
||||||
must use the same `#!cpp FILE` object while it is parsed.
|
|
||||||
|
|
||||||
For contiguous input with many non-ASCII characters, [`JSON_USE_SIMDUTF`](../api/macros/json_use_simdutf.md) can
|
For contiguous input with many non-ASCII characters, [`JSON_USE_SIMDUTF`](../api/macros/json_use_simdutf.md) can
|
||||||
additionally speed up UTF-8 validation by using the [simdutf](https://github.com/simdutf/simdutf) library instead of
|
additionally speed up UTF-8 validation by using the [simdutf](https://github.com/simdutf/simdutf) library instead of
|
||||||
@@ -72,8 +65,8 @@ a DOM, three alternatives avoid building it:
|
|||||||
unparsed remainder of the input) instead of the whole document -- see the [recipe for streaming a large homogeneous
|
unparsed remainder of the input) instead of the whole document -- see the [recipe for streaming a large homogeneous
|
||||||
array](parsing/parser_callbacks.md#recipe-streaming-a-large-homogeneous-array).
|
array](parsing/parser_callbacks.md#recipe-streaming-a-large-homogeneous-array).
|
||||||
- Parse into a [`json_document`](json_view.md) (`#!cpp <nlohmann/json_view.hpp>`) instead of a `basic_json`. It keeps
|
- Parse into a [`json_document`](json_view.md) (`#!cpp <nlohmann/json_view.hpp>`) instead of a `basic_json`. It keeps
|
||||||
the input text and builds a flat index of 16 bytes per value; strings and floating-point numbers are not copied, but
|
the input text and builds a flat index of 16 bytes per value; strings and numbers are not copied, but read from the
|
||||||
read from the text when needed. Read-only [views](../api/basic_json_view/index.md) give the familiar element access, and only the
|
text when needed. Read-only [views](../api/basic_json_view/index.md) give the familiar element access, and only the
|
||||||
parts you [`materialize()`](../api/basic_json_view/materialize.md) become `basic_json` values. A document that
|
parts you [`materialize()`](../api/basic_json_view/materialize.md) become `basic_json` values. A document that
|
||||||
borrows the text instead of owning a copy needs the text to outlive it; see
|
borrows the text instead of owning a copy needs the text to outlive it; see
|
||||||
[choosing between `json`, the SAX interface, and `json_view`](json_view.md#choosing-between-json-ordered_json-the-sax-interface-and-json_view).
|
[choosing between `json`, the SAX interface, and `json_view`](json_view.md#choosing-between-json-ordered_json-the-sax-interface-and-json_view).
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ requirements are collected in [Template Parameter Requirements](template_paramet
|
|||||||
|
|
||||||
### Default type
|
### Default type
|
||||||
|
|
||||||
With the default values for *ObjectType* (`std::map`), *StringType* (`std::string`), and *AllocatorType* (`std::allocator`), the default type for `object_t` is:
|
With the default values for *ObjectType* (`std::map`), *StringType* (`std::string`), and *AllocatorType* (`std::allocator`), the default value for `object_t` is:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
std::map<
|
std::map<
|
||||||
@@ -177,7 +177,7 @@ json j = m;
|
|||||||
|
|
||||||
### Default type
|
### Default type
|
||||||
|
|
||||||
With the default values for *ArrayType* (`std::vector`) and *AllocatorType* (`std::allocator`), the default type for `array_t` is:
|
With the default values for *ArrayType* (`std::vector`) and *AllocatorType* (`std::allocator`), the default value for `array_t` is:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
std::vector<
|
std::vector<
|
||||||
@@ -209,7 +209,7 @@ Unicode values are split by the JSON class into byte-sized characters during des
|
|||||||
|
|
||||||
### Default type
|
### Default type
|
||||||
|
|
||||||
With the default values for *StringType* (`std::string`), the default type for `string_t` is `#!cpp std::string`.
|
With the default values for *StringType* (`std::string`), the default value for `string_t` is `#!cpp std::string`.
|
||||||
|
|
||||||
### Encoding
|
### Encoding
|
||||||
|
|
||||||
@@ -234,7 +234,7 @@ String values are stored as pointers in a `basic_json` type. That is, for any ac
|
|||||||
|
|
||||||
### Default type
|
### Default type
|
||||||
|
|
||||||
With the default values for *BooleanType* (`#!cpp bool`), the default type for `boolean_t` is `#!cpp bool`.
|
With the default values for *BooleanType* (`#!cpp bool`), the default value for `boolean_t` is `#!cpp bool`.
|
||||||
|
|
||||||
### Storage
|
### Storage
|
||||||
|
|
||||||
@@ -252,9 +252,9 @@ This description includes both integer and floating-point numbers. However, C++
|
|||||||
|
|
||||||
### Default types
|
### Default types
|
||||||
|
|
||||||
With the default values for *NumberIntegerType* (`std::int64_t`), the default type for `number_integer_t` is `std::int64_t`.
|
With the default values for *NumberIntegerType* (`std::int64_t`), the default value for `number_integer_t` is `std::int64_t`.
|
||||||
With the default values for *NumberUnsignedType* (`std::uint64_t`), the default type for `number_unsigned_t` is `std::uint64_t`.
|
With the default values for *NumberUnsignedType* (`std::uint64_t`), the default value for `number_unsigned_t` is `std::uint64_t`.
|
||||||
With the default values for *NumberFloatType* (`#!cpp double`), the default type for `number_float_t` is `#!cpp double`.
|
With the default values for *NumberFloatType* (`#!cpp double`), the default value for `number_float_t` is `#!cpp double`.
|
||||||
|
|
||||||
### Default behavior
|
### Default behavior
|
||||||
|
|
||||||
@@ -281,7 +281,7 @@ As this range is a subrange of the exactly supported range [`INT64_MIN`, `INT64_
|
|||||||
|
|
||||||
> This specification allows implementations to set limits on the range and precision of numbers accepted. Since software that implements IEEE 754-2008 binary64 (double precision) numbers is generally available and widely used, good interoperability can be achieved by implementations that expect no more precision or range than these provide, in the sense that implementations will approximate JSON numbers within the expected precision.
|
> This specification allows implementations to set limits on the range and precision of numbers accepted. Since software that implements IEEE 754-2008 binary64 (double precision) numbers is generally available and widely used, good interoperability can be achieved by implementations that expect no more precision or range than these provide, in the sense that implementations will approximate JSON numbers within the expected precision.
|
||||||
|
|
||||||
This implementation does exactly follow this approach, as it uses double precision floating-point numbers by default (see *NumberFloatType* above). Note that with the default `#!cpp double`, numbers smaller than `#!c -1.79769313486232e+308` or greater than `#!c 1.79769313486232e+308` are rejected during parsing with [`out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406). Non-finite values (NaN and infinity) can only be stored by assigning them directly and are serialized to `#!json null`.
|
This implementation does exactly follow this approach, as it uses double precision floating-point numbers. Note that numbers smaller than `#!c -1.79769313486232e+308` or greater than `#!c 1.79769313486232e+308` are rejected during parsing with [`out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406). Non-finite values (NaN and infinity) can only be stored by assigning them directly and are serialized to `#!json null`.
|
||||||
|
|
||||||
### Storage
|
### Storage
|
||||||
|
|
||||||
|
|||||||
@@ -345,11 +345,8 @@ The number types can be changed with template parameters.
|
|||||||
in case of overflow.
|
in case of overflow.
|
||||||
- The type for unsigned integers must be convertible from `#!c unsigned long long`. The type for floating-point
|
- The type for unsigned integers must be convertible from `#!c unsigned long long`. The type for floating-point
|
||||||
numbers is used in case of overflow.
|
numbers is used in case of overflow.
|
||||||
- The type for unsigned integers can also be the same signed type as the type for signed integers (e.g.,
|
- The types for signed and unsigned integers must be distinct, see
|
||||||
`#!c std::int64_t` for both). Then every integer is stored as a signed integer, and
|
[#2573](https://github.com/nlohmann/json/issues/2573).
|
||||||
[`is_number_unsigned()`](../../api/basic_json/is_number_unsigned.md) is always `#!c false`, see
|
|
||||||
[Template Parameter Requirements](template_parameters.md#same-type-for-both).
|
|
||||||
- The type for floating-point numbers must be distinct from the integer types.
|
|
||||||
- Only `#!c double`, `#!c float`, and `#!c long double` are supported for floating-point numbers.
|
- Only `#!c double`, `#!c float`, and `#!c long double` are supported for floating-point numbers.
|
||||||
|
|
||||||
!!! example
|
!!! example
|
||||||
|
|||||||
@@ -40,19 +40,19 @@ Requirements are split into two groups:
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
| Template parameter | Default | Notable substitutes |
|
| Template parameter | Default | Notable substitutes |
|
||||||
|-------------------------------------------------------------------|-----------------------------------|--------------------------------------------------------------------------------------------------|
|
|-------------------------------------------------------------------|-----------------------------------|-----------------------------------------------------------------------|
|
||||||
| [`ObjectType`](#objecttype) | `std::map` | [`nlohmann::ordered_map`](../../api/ordered_map.md), Abseil hash maps |
|
| [`ObjectType`](#objecttype) | `std::map` | [`nlohmann::ordered_map`](../../api/ordered_map.md), Abseil hash maps |
|
||||||
| [`ArrayType`](#arraytype) | `std::vector` | `#!cpp std::deque` |
|
| [`ArrayType`](#arraytype) | `std::vector` | `#!cpp std::deque` |
|
||||||
| [`StringType`](#stringtype) | `std::string` | `std::string`-like types over `char` |
|
| [`StringType`](#stringtype) | `std::string` | `std::string`-like types over `char` |
|
||||||
| [`BooleanType`](#booleantype) | `bool` | none worth using |
|
| [`BooleanType`](#booleantype) | `bool` | none worth using |
|
||||||
| [`NumberIntegerType`](#numberintegertype-and-numberunsignedtype) | `std::int64_t` | any signed integer type |
|
| [`NumberIntegerType`](#numberintegertype-and-numberunsignedtype) | `std::int64_t` | any signed integer type |
|
||||||
| [`NumberUnsignedType`](#numberintegertype-and-numberunsignedtype) | `std::uint64_t` | any unsigned integer type at least as wide as `NumberIntegerType`, or `NumberIntegerType` itself |
|
| [`NumberUnsignedType`](#numberintegertype-and-numberunsignedtype) | `std::uint64_t` | any unsigned integer type at least as wide as `NumberIntegerType` |
|
||||||
| [`NumberFloatType`](#numberfloattype) | `double` | `float` (`long double`: no binary formats) |
|
| [`NumberFloatType`](#numberfloattype) | `double` | `float` (`long double`: no binary formats) |
|
||||||
| [`AllocatorType`](#allocatortype) | `std::allocator` | stateless allocators |
|
| [`AllocatorType`](#allocatortype) | `std::allocator` | stateless allocators |
|
||||||
| [`JSONSerializer`](#jsonserializer) | `adl_serializer` | serializers with the same interface |
|
| [`JSONSerializer`](#jsonserializer) | `adl_serializer` | serializers with the same interface |
|
||||||
| [`BinaryType`](#binarytype) | `#!cpp std::vector<std::uint8_t>` | `#!cpp std::vector<char>` |
|
| [`BinaryType`](#binarytype) | `#!cpp std::vector<std::uint8_t>` | `#!cpp std::vector<char>` |
|
||||||
| [`CustomBaseClass`](#custombaseclass) | `void` | any default-constructible class |
|
| [`CustomBaseClass`](#custombaseclass) | `void` | any default-constructible class |
|
||||||
|
|
||||||
!!! warning "Third-party containers and incomplete types"
|
!!! warning "Third-party containers and incomplete types"
|
||||||
|
|
||||||
@@ -504,14 +504,13 @@ Both types are stored **directly** inside `basic_json`'s union.
|
|||||||
### Always required
|
### Always required
|
||||||
|
|
||||||
- `#!cpp std::is_integral` must be satisfied: `NumberIntegerType` must be a **signed** integer type,
|
- `#!cpp std::is_integral` must be satisfied: `NumberIntegerType` must be a **signed** integer type,
|
||||||
`NumberUnsignedType` an **unsigned** integer type or the same type as `NumberIntegerType` (see
|
`NumberUnsignedType` an **unsigned** integer type. Class types are not supported -- among others, the constructors
|
||||||
[below](#same-type-for-both)). Class types are not supported -- among others, the constructors taking integer values
|
taking integer values are constrained on `#!cpp std::is_integral`.
|
||||||
are constrained on `#!cpp std::is_integral`.
|
|
||||||
- Trivially default-constructible, trivially copyable, and trivially destructible (union member).
|
- Trivially default-constructible, trivially copyable, and trivially destructible (union member).
|
||||||
- `#!cpp std::numeric_limits` must be specialized for both types.
|
- `#!cpp std::numeric_limits` must be specialized for both types.
|
||||||
- An unsigned `NumberUnsignedType` must be able to represent the absolute value of every `NumberIntegerType` value;
|
- `NumberUnsignedType` must be able to represent the absolute value of every `NumberIntegerType` value; serialization
|
||||||
serialization of negative numbers converts the value to `NumberUnsignedType`. A `#!cpp static_assert` requires it to
|
of negative numbers converts the value to `NumberUnsignedType`. A `#!cpp static_assert` requires it to be at least as
|
||||||
be at least as wide as `NumberIntegerType`, which is what that amounts to for the standard integer types.
|
wide as `NumberIntegerType`, which is what that amounts to for the standard integer types.
|
||||||
- Both types must fit into the internal 64-character number buffer used by
|
- Both types must fit into the internal 64-character number buffer used by
|
||||||
[`dump`](../../api/basic_json/dump.md), which is the case for all standard integer types.
|
[`dump`](../../api/basic_json/dump.md), which is the case for all standard integer types.
|
||||||
- [`std::hash<basic_json>`](../../api/basic_json/std_hash.md) additionally requires `#!cpp std::hash` specializations.
|
- [`std::hash<basic_json>`](../../api/basic_json/std_hash.md) additionally requires `#!cpp std::hash` specializations.
|
||||||
@@ -523,23 +522,6 @@ is stored as [`number_float_t`](../../api/basic_json/number_float_t.md) instead.
|
|||||||
therefore silently changes parse results rather than raising an error. See
|
therefore silently changes parse results rather than raising an error. See
|
||||||
[Number Handling](number_handling.md) for details.
|
[Number Handling](number_handling.md) for details.
|
||||||
|
|
||||||
#### Same type for both
|
|
||||||
|
|
||||||
`NumberUnsignedType` may be the same type as `NumberIntegerType`, e.g.
|
|
||||||
`#!cpp nlohmann::json::with_integers_t<std::int64_t, std::int64_t>` (see
|
|
||||||
[`with_integers_t`](../../api/basic_json/with_t.md)), for code that only wants to handle one integer type. Such a `basic_json` stores every integer as
|
|
||||||
[`number_integer_t`](../../api/basic_json/number_integer_t.md):
|
|
||||||
|
|
||||||
- [`is_number_unsigned()`](../../api/basic_json/is_number_unsigned.md) is always `#!cpp false`, and
|
|
||||||
[`type()`](../../api/basic_json/type.md) never returns `value_t::number_unsigned`. Constructing a value from
|
|
||||||
`value_t::number_unsigned` gives a `value_t::number_integer` value.
|
|
||||||
- Values of unsigned C++ types (e.g., `#!cpp 5u`) are converted to `NumberIntegerType`, so values above its maximum
|
|
||||||
wrap around, as values outside the range of a narrower `NumberIntegerType` already do.
|
|
||||||
- The parsers store a non-negative integer above the maximum of `NumberIntegerType` (e.g., `18446744073709551615` for
|
|
||||||
`#!cpp std::int64_t`) as [`number_float_t`](../../api/basic_json/number_float_t.md), like any other integer that does
|
|
||||||
not fit.
|
|
||||||
- Converting from a `basic_json` with distinct types turns its unsigned values into `number_integer_t` values.
|
|
||||||
|
|
||||||
### Compatible types
|
### Compatible types
|
||||||
|
|
||||||
| Type pair | Support |
|
| Type pair | Support |
|
||||||
@@ -547,9 +529,8 @@ therefore silently changes parse results rather than raising an error. See
|
|||||||
| `#!cpp std::int64_t` / `#!cpp std::uint64_t` (default) | full |
|
| `#!cpp std::int64_t` / `#!cpp std::uint64_t` (default) | full |
|
||||||
| `#!cpp std::int32_t` / `#!cpp std::uint32_t`, `#!cpp long long` / `#!cpp unsigned long long` | full; narrower types change which literals the parser can represent |
|
| `#!cpp std::int32_t` / `#!cpp std::uint32_t`, `#!cpp long long` / `#!cpp unsigned long long` | full; narrower types change which literals the parser can represent |
|
||||||
| any other pair of standard signed/unsigned integer types | full |
|
| any other pair of standard signed/unsigned integer types | full |
|
||||||
| the same signed integer type for both, e.g. `#!cpp std::int64_t` / `#!cpp std::int64_t` | full; every integer is stored as `number_integer_t`, see [above](#same-type-for-both) |
|
|
||||||
| class types, enumerations | not usable; `#!cpp std::is_integral` must hold |
|
| class types, enumerations | not usable; `#!cpp std::is_integral` must hold |
|
||||||
| `#!cpp bool`, `number_float_t`, or the type of another member of the union | not usable; `#!cpp std::is_integral<bool>` is in fact `#!cpp true`, but the `get_impl_ptr` overloads for `boolean_t`, `number_integer_t`, `number_unsigned_t` and `number_float_t` would collide |
|
| `#!cpp bool`, or a type already used for another member of the union | not usable; `#!cpp std::is_integral<bool>` is in fact `#!cpp true`, but the `get_impl_ptr` overloads for `boolean_t`, `number_integer_t`, `number_unsigned_t` and `number_float_t` would collide |
|
||||||
|
|
||||||
## `NumberFloatType`
|
## `NumberFloatType`
|
||||||
|
|
||||||
|
|||||||
@@ -194,9 +194,9 @@ packet-beta
|
|||||||
|
|
||||||
| Bytes | Field | Type | Contents |
|
| Bytes | Field | Type | Contents |
|
||||||
|-------|---------|------------|-------------------------------------------------------------------------------------------------------------------------------|
|
|-------|---------|------------|-------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| 0 | `kind` | `uint8_t` | the type, numbered as [`value_t`](../api/basic_json/value_t.md): 0 null, 1 object, 2 array, 3 string, 4 boolean, 5 signed integer, 6 unsigned integer, 7 float; 10 for a link (see below). There is no node for a discarded value: a [discarded](../api/basic_json_view/is_discarded.md) view has none |
|
| 0 | `kind` | `uint8_t` | the type, numbered as [`value_t`](../api/basic_json/value_t.md): 0 null, 1 object, 2 array, 3 string, 4 boolean, 5 signed integer, 6 unsigned integer, 7 float; 10 for a link (see below) |
|
||||||
| 1 | `flags` | `uint8_t` | bits 0-1: where a string's bytes (or a number's token) are (0: the source text, 1: the buffer of decoded strings, for strings with escapes, 2: the edit buffer); bit 2: the value of a boolean; bit 3: moved, bit 4: new, bit 5: linked (see below) |
|
| 1 | `flags` | `uint8_t` | bits 0-1: where a string's bytes (or a number's token) are (0: the source text, 1: the buffer of decoded strings, for strings with escapes, 2: the edit buffer); bit 2: the value of a boolean; bits 3 and 4: moved and new (see below) |
|
||||||
| 2-3 | `extra` | `uint16_t` | numbers: the number of integer digits (low byte) and fraction digits (high byte), 255 for more; objects: the number of their key hash index (1-based), or 0; otherwise 0 |
|
| 2-3 | `extra` | `uint16_t` | numbers: the number of integer digits (low byte) and fraction digits (high byte), 255 for more; objects: the number of their hash index (1-based), or 0; otherwise 0 |
|
||||||
| 4-7 | `off` | `uint32_t` | where the value starts: the first byte after a string's opening quote (or its position in the buffer of decoded strings), the first byte of a number or literal, the bracket of an array or object |
|
| 4-7 | `off` | `uint32_t` | where the value starts: the first byte after a string's opening quote (or its position in the buffer of decoded strings), the first byte of a number or literal, the bracket of an array or object |
|
||||||
| 8-11 | `len` | `uint32_t` | strings: the length after decoding; floats and literals: the length of the token; arrays and objects: the number of elements |
|
| 8-11 | `len` | `uint32_t` | strings: the length after decoding; floats and literals: the length of the token; arrays and objects: the number of elements |
|
||||||
| 12-15 | `next` | `uint32_t` | arrays and objects: the number of nodes of the subtree, including the node itself |
|
| 12-15 | `next` | `uint32_t` | arrays and objects: the number of nodes of the subtree, including the node itself |
|
||||||
@@ -212,7 +212,7 @@ packet-beta
|
|||||||
step from element to element this way and skip whole subtrees in constant time.
|
step from element to element this way and skip whole subtrees in constant time.
|
||||||
- **Offsets** are 32 bits wide, so a document is limited to 4294967279 bytes, 4 GiB minus 16 bytes (a margin below
|
- **Offsets** are 32 bits wide, so a document is limited to 4294967279 bytes, 4 GiB minus 16 bytes (a margin below
|
||||||
2^32 for positions one scanner step past the end of the text; `out_of_range.416`).
|
2^32 for positions one scanner step past the end of the text; `out_of_range.416`).
|
||||||
- **Large objects** (128 members or more) get a hash index for their keys after parsing
|
- **Large objects** (128 members or more) get a hash index after parsing
|
||||||
([`detail/view/object_index.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/view/object_index.hpp)):
|
([`detail/view/object_index.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/view/object_index.hpp)):
|
||||||
an open-addressing table whose slots hold the distance from the object's node to a key's node, so that a lookup does
|
an open-addressing table whose slots hold the distance from the object's node to a key's node, so that a lookup does
|
||||||
not compare every key. Of duplicate keys the table leads to the first, as a linear search does. The object's `extra`
|
not compare every key. Of duplicate keys the table leads to the first, as a linear search does. The object's `extra`
|
||||||
@@ -257,8 +257,6 @@ edited:
|
|||||||
sequence (a header node, then the entries), whose number is in `off`. The entries are links (`kind` 10), whose bytes
|
sequence (a header node, then the entries), whose number is in `off`. The entries are links (`kind` 10), whose bytes
|
||||||
8-15 hold the address of the value's node, so values never move.
|
8-15 hold the address of the value's node, so values never move.
|
||||||
- A node written by an edit gets the flag *new* (bit 4): it has no position in the source text.
|
- A node written by an edit gets the flag *new* (bit 4): it has no position in the source text.
|
||||||
- A value that an entry of such a sequence links to gets the flag *linked* (bit 5): its extent in the parsed layout
|
|
||||||
(`next`) no longer matters, so a scalar can replace an array or object there in place.
|
|
||||||
- Views of read-only documents compile without any of this: how views walk the index is a template parameter
|
- Views of read-only documents compile without any of this: how views walk the index is a template parameter
|
||||||
(`navigation<Editable>`).
|
(`navigation<Editable>`).
|
||||||
|
|
||||||
|
|||||||
@@ -972,19 +972,9 @@ double-precision number when `number_float_t` is `#!cpp float`.
|
|||||||
|
|
||||||
### json.exception.out_of_range.407
|
### json.exception.out_of_range.407
|
||||||
|
|
||||||
An integer number cannot be represented by the binary format it is serialized to:
|
An unsigned integer number greater than 9223372036854775807 cannot be represented in the binary format. This exception
|
||||||
|
is only thrown by [`to_bon8`](../api/basic_json/to_bon8.md), as [BON8](../features/binary_formats/bon8.md) has no
|
||||||
- [BON8](../features/binary_formats/bon8.md) only stores integers that fit into int64, so
|
unsigned 64-bit integer type.
|
||||||
[`to_bon8`](../api/basic_json/to_bon8.md) throws for unsigned integers greater than 9223372036854775807.
|
|
||||||
- [CBOR](../features/binary_formats/cbor.md), [MessagePack](../features/binary_formats/messagepack.md), and
|
|
||||||
[BSON](../features/binary_formats/bson.md) store integers in at most 64 bits. With the default number types, every
|
|
||||||
integer fits, but a [`number_integer_t`](../api/basic_json/number_integer_t.md) or
|
|
||||||
[`number_unsigned_t`](../api/basic_json/number_unsigned_t.md) wider than 64 bits (e.g., `__int128`) can hold values
|
|
||||||
outside the range of the format: [-2^64, 2^64-1] for CBOR, [-2^63, 2^64-1] for MessagePack, and the range of int64
|
|
||||||
(signed integers) or uint64 (unsigned integers) for BSON.
|
|
||||||
|
|
||||||
[UBJSON](../features/binary_formats/ubjson.md) and [BJData](../features/binary_formats/bjdata.md) never throw this
|
|
||||||
exception, because they serialize integers beyond 64 bits as high-precision numbers.
|
|
||||||
|
|
||||||
!!! note "Former occurrences"
|
!!! note "Former occurrences"
|
||||||
|
|
||||||
@@ -993,17 +983,11 @@ exception, because they serialize integers beyond 64 bits as high-precision numb
|
|||||||
- Since version 3.9.0, integer numbers beyond int64 are serialized as high-precision UBJSON numbers.
|
- Since version 3.9.0, integer numbers beyond int64 are serialized as high-precision UBJSON numbers.
|
||||||
- Since version 3.12.0, integer numbers beyond int64 are serialized as uint64 BSON numbers.
|
- Since version 3.12.0, integer numbers beyond int64 are serialized as uint64 BSON numbers.
|
||||||
|
|
||||||
Before version 3.13.0, CBOR, MessagePack, and BSON silently truncated integers wider than 64 bits, and BJData
|
!!! failure "Example message"
|
||||||
truncated unsigned integers wider than 64 bits.
|
|
||||||
|
|
||||||
!!! failure "Example messages"
|
|
||||||
|
|
||||||
```
|
```
|
||||||
integer number 18446744073709551615 cannot be represented by BON8 as it does not fit int64
|
integer number 18446744073709551615 cannot be represented by BON8 as it does not fit int64
|
||||||
```
|
```
|
||||||
```
|
|
||||||
integer number 1267650600228229401496703205376 cannot be represented by CBOR as it does not fit [-2^64, 2^64-1]
|
|
||||||
```
|
|
||||||
|
|
||||||
### json.exception.out_of_range.408
|
### json.exception.out_of_range.408
|
||||||
|
|
||||||
|
|||||||
@@ -9,15 +9,6 @@ published on the [GitHub releases page](https://github.com/nlohmann/json/release
|
|||||||
This documentation is built from the `develop` branch and may describe changes that are not part of a release
|
This documentation is built from the `develop` branch and may describe changes that are not part of a release
|
||||||
yet. Their version numbers are followed by an <span class="unreleased-version">unreleased</span> badge.
|
yet. Their version numbers are followed by an <span class="unreleased-version">unreleased</span> badge.
|
||||||
|
|
||||||
## Unreleased
|
|
||||||
|
|
||||||
- Converting an rvalue container to JSON (`json j = std::move(vec);`, `json j = std::map<std::string, std::string>{...};`)
|
|
||||||
now **moves** the elements instead of copying them (e.g., `std::vector<std::string>` with 100,000 strings: 4.7 ms to
|
|
||||||
2.3 ms). Two consequences: the source container is left in a valid but unspecified (moved-from) state, and if an
|
|
||||||
exception is thrown during the conversion, the source may be partially moved-from, so the strong exception guarantee
|
|
||||||
no longer holds for the source of an rvalue conversion (the JSON value itself is unaffected). Lvalues and `const`
|
|
||||||
objects are still copied. See [`basic_json`](../api/basic_json/basic_json.md).
|
|
||||||
|
|
||||||
## v3.12.0 (2025-04-11)
|
## v3.12.0 (2025-04-11)
|
||||||
|
|
||||||
Fixes bugs found in 3.11.3 and adds several features. All changes are backward-compatible.
|
Fixes bugs found in 3.11.3 and adds several features. All changes are backward-compatible.
|
||||||
|
|||||||
@@ -370,9 +370,7 @@ nav:
|
|||||||
- 'JSON_DELETE_DEPRECATED_FUNCTIONS': api/macros/json_delete_deprecated_functions.md
|
- 'JSON_DELETE_DEPRECATED_FUNCTIONS': api/macros/json_delete_deprecated_functions.md
|
||||||
- 'JSON_DIAGNOSTIC_POSITIONS': api/macros/json_diagnostic_positions.md
|
- 'JSON_DIAGNOSTIC_POSITIONS': api/macros/json_diagnostic_positions.md
|
||||||
- 'JSON_DISABLE_ENUM_SERIALIZATION': api/macros/json_disable_enum_serialization.md
|
- 'JSON_DISABLE_ENUM_SERIALIZATION': api/macros/json_disable_enum_serialization.md
|
||||||
- 'JSON_DISABLE_STREAM_BULK_SCAN': api/macros/json_disable_stream_bulk_scan.md
|
|
||||||
- 'JSON_DISABLE_TUPLE_REFERENCE_CONVERSION': api/macros/json_disable_tuple_reference_conversion.md
|
- 'JSON_DISABLE_TUPLE_REFERENCE_CONVERSION': api/macros/json_disable_tuple_reference_conversion.md
|
||||||
- 'JSON_DISABLE_UNLOCKED_FILE_IO': api/macros/json_disable_unlocked_file_io.md
|
|
||||||
- 'JSON_HAS_CPP_11, JSON_HAS_CPP_14, JSON_HAS_CPP_17, JSON_HAS_CPP_20, JSON_HAS_CPP_23, JSON_HAS_CPP_26': api/macros/json_has_cpp_11.md
|
- 'JSON_HAS_CPP_11, JSON_HAS_CPP_14, JSON_HAS_CPP_17, JSON_HAS_CPP_20, JSON_HAS_CPP_23, JSON_HAS_CPP_26': api/macros/json_has_cpp_11.md
|
||||||
- 'JSON_HAS_EXPERIMENTAL_FILESYSTEM, JSON_HAS_FILESYSTEM': api/macros/json_has_filesystem.md
|
- 'JSON_HAS_EXPERIMENTAL_FILESYSTEM, JSON_HAS_FILESYSTEM': api/macros/json_has_filesystem.md
|
||||||
- 'JSON_HAS_RANGE_VIEW_CONVERSION': api/macros/json_has_range_view_conversion.md
|
- 'JSON_HAS_RANGE_VIEW_CONVERSION': api/macros/json_has_range_view_conversion.md
|
||||||
|
|||||||
@@ -54,14 +54,6 @@
|
|||||||
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 0
|
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
#define JSON_DISABLE_STREAM_BULK_SCAN 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef JSON_DISABLE_UNLOCKED_FILE_IO
|
|
||||||
#define JSON_DISABLE_UNLOCKED_FILE_IO 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_DIAGNOSTICS
|
#if JSON_DIAGNOSTICS
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||||
#else
|
#else
|
||||||
@@ -110,38 +102,14 @@
|
|||||||
#define NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS
|
#define NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DISABLE_STREAM_BULK_SCAN _dsbs
|
|
||||||
#else
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DISABLE_STREAM_BULK_SCAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_DISABLE_UNLOCKED_FILE_IO
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DISABLE_UNLOCKED_FILE_IO _dufio
|
|
||||||
#else
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DISABLE_UNLOCKED_FILE_IO
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef JSON_VIEW_NO_SIMD
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_VIEW_NO_SIMD _vns
|
|
||||||
#else
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_VIEW_NO_SIMD
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef JSON_VIEW_USE_SSSE3
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_VIEW_USE_SSSE3 _vssse3
|
|
||||||
#else
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_VIEW_USE_SSSE3
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||||
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Construct the namespace ABI tags component
|
// Construct the namespace ABI tags component
|
||||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h, i, j, k, l) json_abi ## a ## b ## c ## d ## e ## f ## g ## h ## i ## j ## k ## l
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h) json_abi ## a ## b ## c ## d ## e ## f ## g ## h
|
||||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f, g, h, i, j, k, l) \
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f, g, h) \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h, i, j, k, l)
|
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h)
|
||||||
|
|
||||||
#define NLOHMANN_JSON_ABI_TAGS \
|
#define NLOHMANN_JSON_ABI_TAGS \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||||
@@ -152,11 +120,7 @@
|
|||||||
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
||||||
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING, \
|
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING, \
|
||||||
NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8, \
|
NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8, \
|
||||||
NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS, \
|
NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS)
|
||||||
NLOHMANN_JSON_ABI_TAG_DISABLE_STREAM_BULK_SCAN, \
|
|
||||||
NLOHMANN_JSON_ABI_TAG_DISABLE_UNLOCKED_FILE_IO, \
|
|
||||||
NLOHMANN_JSON_ABI_TAG_VIEW_NO_SIMD, \
|
|
||||||
NLOHMANN_JSON_ABI_TAG_VIEW_USE_SSSE3)
|
|
||||||
|
|
||||||
// Construct the namespace version component
|
// Construct the namespace version component
|
||||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||||
|
|||||||
@@ -80,11 +80,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
|
|||||||
{
|
{
|
||||||
case value_t::number_unsigned:
|
case value_t::number_unsigned:
|
||||||
{
|
{
|
||||||
// never null: a basic_json whose number_unsigned_t is number_integer_t
|
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
|
||||||
// stores no number_unsigned values (#2573)
|
|
||||||
const auto* ptr = j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>();
|
|
||||||
JSON_ASSERT(ptr != nullptr);
|
|
||||||
val = static_cast<ArithmeticType>(*ptr);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case value_t::number_integer:
|
case value_t::number_integer:
|
||||||
@@ -154,7 +150,7 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_flo
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
inline void from_json(const BasicJsonType& j, number_unsigned_overload_t<BasicJsonType>& val)
|
inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
|
||||||
{
|
{
|
||||||
get_arithmetic_value(j, val);
|
get_arithmetic_value(j, val);
|
||||||
}
|
}
|
||||||
@@ -270,22 +266,6 @@ template<typename ConstructibleArrayType>
|
|||||||
void from_json_array_reserve(ConstructibleArrayType& /*arr*/, std::size_t /*size*/, priority_tag<0> /*unused*/)
|
void from_json_array_reserve(ConstructibleArrayType& /*arr*/, std::size_t /*size*/, priority_tag<0> /*unused*/)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
// append through push_back() where available; containers without it (such as
|
|
||||||
// std::set) fall back to insert(end(), ...), which is what std::inserter did
|
|
||||||
template<typename ConstructibleArrayType, typename ValueType>
|
|
||||||
auto from_json_array_append(ConstructibleArrayType& arr, ValueType&& value, priority_tag<1> /*unused*/)
|
|
||||||
-> decltype(arr.push_back(std::forward<ValueType>(value)), void())
|
|
||||||
{
|
|
||||||
arr.push_back(std::forward<ValueType>(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ConstructibleArrayType, typename ValueType>
|
|
||||||
void from_json_array_append(ConstructibleArrayType& arr, ValueType&& value, priority_tag<0> /*unused*/)
|
|
||||||
{
|
|
||||||
using std::end;
|
|
||||||
arr.insert(end(arr), std::forward<ValueType>(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename ConstructibleArrayType,
|
template<typename BasicJsonType, typename ConstructibleArrayType,
|
||||||
enable_if_t<
|
enable_if_t<
|
||||||
std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
|
std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
|
||||||
@@ -295,15 +275,17 @@ auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, p
|
|||||||
j.template get<typename ConstructibleArrayType::value_type>(),
|
j.template get<typename ConstructibleArrayType::value_type>(),
|
||||||
void())
|
void())
|
||||||
{
|
{
|
||||||
|
using std::end;
|
||||||
|
|
||||||
ConstructibleArrayType ret;
|
ConstructibleArrayType ret;
|
||||||
const auto* inner_array = j.template get_ptr<const typename BasicJsonType::array_t*>();
|
from_json_array_reserve(ret, j.size(), priority_tag<1> {});
|
||||||
from_json_array_reserve(ret, inner_array->size(), priority_tag<1> {});
|
std::transform(j.begin(), j.end(),
|
||||||
for (const auto& elem : *inner_array)
|
std::inserter(ret, end(ret)), [](const BasicJsonType & i)
|
||||||
{
|
{
|
||||||
// get<BasicJsonType>() returns *this, this won't call a from_json
|
// get<BasicJsonType>() returns *this, this won't call a from_json
|
||||||
// method when value_type is BasicJsonType
|
// method when value_type is BasicJsonType
|
||||||
from_json_array_append(ret, elem.template get<typename ConstructibleArrayType::value_type>(), priority_tag<1> {});
|
return i.template get<typename ConstructibleArrayType::value_type>();
|
||||||
}
|
});
|
||||||
arr = std::move(ret);
|
arr = std::move(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,23 +371,6 @@ template<typename ConstructibleObjectType>
|
|||||||
inline void from_json_object_reserve(ConstructibleObjectType& /*obj*/, std::size_t /*size*/, priority_tag<0> /*unused*/)
|
inline void from_json_object_reserve(ConstructibleObjectType& /*obj*/, std::size_t /*size*/, priority_tag<0> /*unused*/)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
// the keys of the source are sorted for the usual object types, so hinting the
|
|
||||||
// end makes every insertion constant-time (for containers that order their keys
|
|
||||||
// the same way); containers without emplace_hint() (such as ordered_map) fall
|
|
||||||
// back to emplace()
|
|
||||||
template<typename ConstructibleObjectType, typename Key, typename Value>
|
|
||||||
auto from_json_object_emplace(ConstructibleObjectType& obj, Key&& key, Value&& value, priority_tag<1> /*unused*/)
|
|
||||||
-> decltype(obj.emplace_hint(obj.end(), std::forward<Key>(key), std::forward<Value>(value)), void())
|
|
||||||
{
|
|
||||||
obj.emplace_hint(obj.end(), std::forward<Key>(key), std::forward<Value>(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ConstructibleObjectType, typename Key, typename Value>
|
|
||||||
void from_json_object_emplace(ConstructibleObjectType& obj, Key&& key, Value&& value, priority_tag<0> /*unused*/)
|
|
||||||
{
|
|
||||||
obj.emplace(std::forward<Key>(key), std::forward<Value>(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename ConstructibleObjectType,
|
template<typename BasicJsonType, typename ConstructibleObjectType,
|
||||||
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
|
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
|
||||||
inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
|
inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
|
||||||
@@ -420,7 +385,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
|
|||||||
from_json_object_reserve(ret, inner_object->size(), priority_tag<1> {});
|
from_json_object_reserve(ret, inner_object->size(), priority_tag<1> {});
|
||||||
for (const auto& p : *inner_object)
|
for (const auto& p : *inner_object)
|
||||||
{
|
{
|
||||||
from_json_object_emplace(ret, p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>(), priority_tag<1> {});
|
ret.emplace(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());
|
||||||
}
|
}
|
||||||
obj = std::move(ret);
|
obj = std::move(ret);
|
||||||
}
|
}
|
||||||
@@ -441,11 +406,7 @@ inline void from_json(const BasicJsonType& j, ArithmeticType& val)
|
|||||||
{
|
{
|
||||||
case value_t::number_unsigned:
|
case value_t::number_unsigned:
|
||||||
{
|
{
|
||||||
// never null: a basic_json whose number_unsigned_t is number_integer_t
|
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
|
||||||
// stores no number_unsigned values (#2573)
|
|
||||||
const auto* ptr = j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>();
|
|
||||||
JSON_ASSERT(ptr != nullptr);
|
|
||||||
val = static_cast<ArithmeticType>(*ptr);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case value_t::number_integer:
|
case value_t::number_integer:
|
||||||
|
|||||||
@@ -1342,32 +1342,14 @@ inline char* write_shortest(char* first, const zmij::shortest_decimal d) noexcep
|
|||||||
return end + (three ? 5 : 4);
|
return end + (three ? 5 : 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// the powers of ten up to 10^19 (all that fit in std::uint64_t)
|
/// the powers of ten up to 10^16
|
||||||
inline const std::array<std::uint64_t, 20>& powers_of_ten() noexcept
|
inline const std::array<std::uint64_t, 17>& powers_of_ten_16() noexcept
|
||||||
{
|
{
|
||||||
static const std::array<std::uint64_t, 20> powers =
|
static const std::array<std::uint64_t, 17> powers =
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
1u,
|
1u, 10u, 100u, 1000u, 10000u, 100000u, 1000000u, 10000000u, 100000000u, 1000000000u, 10000000000u,
|
||||||
10u,
|
100000000000u, 1000000000000u, 10000000000000u, 100000000000000u, 1000000000000000u, 10000000000000000u
|
||||||
100u,
|
|
||||||
1000u,
|
|
||||||
10000u,
|
|
||||||
100000u,
|
|
||||||
1000000u,
|
|
||||||
10000000u,
|
|
||||||
100000000u,
|
|
||||||
1000000000u,
|
|
||||||
10000000000u,
|
|
||||||
100000000000u,
|
|
||||||
1000000000000u,
|
|
||||||
10000000000000u,
|
|
||||||
100000000000000u,
|
|
||||||
1000000000000000u,
|
|
||||||
10000000000000000u,
|
|
||||||
100000000000000000u,
|
|
||||||
1000000000000000000u,
|
|
||||||
10000000000000000000u
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return powers;
|
return powers;
|
||||||
@@ -1385,9 +1367,9 @@ JSON_HEDLEY_NON_NULL(1)
|
|||||||
JSON_HEDLEY_RETURNS_NON_NULL
|
JSON_HEDLEY_RETURNS_NON_NULL
|
||||||
inline char* write_short_decimal(char* first, std::uint64_t digits, int count, int exp) noexcept
|
inline char* write_short_decimal(char* first, std::uint64_t digits, int count, int exp) noexcept
|
||||||
{
|
{
|
||||||
JSON_ASSERT(digits >= powers_of_ten()[static_cast<std::size_t>(count - 1)] && count <= 15);
|
JSON_ASSERT(digits >= powers_of_ten_16()[static_cast<std::size_t>(count - 1)] && count <= 15);
|
||||||
const int scale = 16 - count;
|
const int scale = 16 - count;
|
||||||
return write_shortest(first, zmij::shortest_decimal{digits * powers_of_ten()[static_cast<std::size_t>(scale)], exp - scale - 1, 0, false});
|
return write_shortest(first, zmij::shortest_decimal{digits * powers_of_ten_16()[static_cast<std::size_t>(scale)], exp - scale - 1, 0, false});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// as write_short_decimal(), counting the digits (not 0, less than 10^15)
|
/// as write_short_decimal(), counting the digits (not 0, less than 10^15)
|
||||||
@@ -1398,7 +1380,7 @@ inline char* write_short_decimal(char* first, std::uint64_t digits, int exp) noe
|
|||||||
JSON_ASSERT(digits != 0 && digits < 1000000000000000u);
|
JSON_ASSERT(digits != 0 && digits < 1000000000000000u);
|
||||||
// floor(log10(2^bits)) + 1 digits, or one less
|
// floor(log10(2^bits)) + 1 digits, or one less
|
||||||
const int log2_bound = ((64 - count_leading_zeros(digits)) * 1233) >> 12;
|
const int log2_bound = ((64 - count_leading_zeros(digits)) * 1233) >> 12;
|
||||||
const int count = log2_bound + (digits >= powers_of_ten()[static_cast<std::size_t>(log2_bound)] ? 1 : 0);
|
const int count = log2_bound + (digits >= powers_of_ten_16()[static_cast<std::size_t>(log2_bound)] ? 1 : 0);
|
||||||
return write_short_decimal(first, digits, count, exp);
|
return write_short_decimal(first, digits, count, exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#include <optional> // optional
|
#include <optional> // optional
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <iterator> // begin, end, make_move_iterator, move_iterator
|
#include <iterator> // begin, end
|
||||||
#include <memory> // allocator_traits
|
#include <memory> // allocator_traits
|
||||||
#include <string> // basic_string, char_traits
|
#include <string> // basic_string, char_traits
|
||||||
#include <tuple> // tuple, get
|
#include <tuple> // tuple, get
|
||||||
@@ -48,32 +48,6 @@ namespace detail
|
|||||||
* leave j with the type of the new value, but the pointer to the destroyed old one.
|
* leave j with the type of the new value, but the pointer to the destroyed old one.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// a move iterator over the elements of an rvalue container, unless they are
|
|
||||||
// const (e.g., std::set): libstdc++ before GCC 5 defines the reference of a
|
|
||||||
// std::move_iterator as value_type&&, which cannot bind to a const element
|
|
||||||
template<typename Iterator>
|
|
||||||
std::move_iterator<Iterator> make_move_iterator_if_mutable_impl(const Iterator& it, std::true_type /*mutable*/)
|
|
||||||
{
|
|
||||||
return std::make_move_iterator(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Iterator>
|
|
||||||
Iterator make_move_iterator_if_mutable_impl(const Iterator& it, std::false_type /*mutable*/)
|
|
||||||
{
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Iterator>
|
|
||||||
using is_mutable_iterator = std::integral_constant < bool,
|
|
||||||
!std::is_const<typename std::remove_reference<decltype(*std::declval<Iterator&>())>::type>::value >;
|
|
||||||
|
|
||||||
template<typename Iterator>
|
|
||||||
auto make_move_iterator_if_mutable(const Iterator& it)
|
|
||||||
-> decltype(make_move_iterator_if_mutable_impl(it, is_mutable_iterator<Iterator> {}))
|
|
||||||
{
|
|
||||||
return make_move_iterator_if_mutable_impl(it, is_mutable_iterator<Iterator> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<value_t> struct external_constructor;
|
template<value_t> struct external_constructor;
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
@@ -193,17 +167,6 @@ struct external_constructor<value_t::array>
|
|||||||
j.replace_value(value_t::array, value);
|
j.replace_value(value_t::array, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// an rvalue container: move its elements
|
|
||||||
template<typename BasicJsonType, typename CompatibleArrayType>
|
|
||||||
static void construct_moving(BasicJsonType& j, CompatibleArrayType& arr)
|
|
||||||
{
|
|
||||||
using std::begin;
|
|
||||||
using std::end;
|
|
||||||
|
|
||||||
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::array_t>(make_move_iterator_if_mutable(begin(arr)), make_move_iterator_if_mutable(end(arr))));
|
|
||||||
j.replace_value(value_t::array, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
||||||
{
|
{
|
||||||
@@ -254,17 +217,6 @@ struct external_constructor<value_t::object>
|
|||||||
j.replace_value(value_t::object, value);
|
j.replace_value(value_t::object, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// an rvalue container: move its values
|
|
||||||
template<typename BasicJsonType, typename CompatibleObjectType>
|
|
||||||
static void construct_moving(BasicJsonType& j, CompatibleObjectType& obj)
|
|
||||||
{
|
|
||||||
using std::begin;
|
|
||||||
using std::end;
|
|
||||||
|
|
||||||
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::object_t>(make_move_iterator_if_mutable(begin(obj)), make_move_iterator_if_mutable(end(obj))));
|
|
||||||
j.replace_value(value_t::object, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template < typename BasicJsonType, typename CompatibleObjectType,
|
template < typename BasicJsonType, typename CompatibleObjectType,
|
||||||
enable_if_t < !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int > = 0 >
|
enable_if_t < !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int > = 0 >
|
||||||
static void construct(BasicJsonType& j, const CompatibleObjectType& obj)
|
static void construct(BasicJsonType& j, const CompatibleObjectType& obj)
|
||||||
@@ -351,14 +303,14 @@ inline void to_json(BasicJsonType& j, FloatType val) noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
|
template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
|
||||||
enable_if_t<is_compatible_integer_type<number_unsigned_overload_t<BasicJsonType>, CompatibleNumberUnsignedType>::value, int> = 0>
|
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>
|
||||||
inline void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
|
inline void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
|
||||||
{
|
{
|
||||||
external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
|
external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType, typename CompatibleNumberIntegerType,
|
template<typename BasicJsonType, typename CompatibleNumberIntegerType,
|
||||||
enable_if_t<is_compatible_number_integer_type<BasicJsonType, CompatibleNumberIntegerType>::value, int> = 0>
|
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>
|
||||||
inline void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
|
inline void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
|
||||||
{
|
{
|
||||||
external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
|
external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
|
||||||
@@ -402,68 +354,6 @@ inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
|
|||||||
external_constructor<value_t::array>::construct(j, arr);
|
external_constructor<value_t::array>::construct(j, arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// types that have a dedicated to_json overload and must not be treated as generic containers
|
|
||||||
template<typename T>
|
|
||||||
struct has_dedicated_to_json : std::false_type {};
|
|
||||||
|
|
||||||
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
|
||||||
template<>
|
|
||||||
struct has_dedicated_to_json<std_fs::path> : std::true_type {};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__cpp_lib_char8_t)
|
|
||||||
template<typename Tr, typename Allocator>
|
|
||||||
struct has_dedicated_to_json<std::basic_string<char8_t, Tr, Allocator>> : std::true_type {};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_HAS_RANGE_VIEW_CONVERSION
|
|
||||||
template<typename T>
|
|
||||||
struct is_range_view_type : is_compatible_range_view<T> {};
|
|
||||||
#else
|
|
||||||
template<typename T>
|
|
||||||
struct is_range_view_type : std::false_type {};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
|
||||||
template<typename T>
|
|
||||||
struct is_enum_keyed_map_type : is_enum_keyed_map<T> {};
|
|
||||||
#else
|
|
||||||
template<typename T>
|
|
||||||
struct is_enum_keyed_map_type : std::false_type {};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// rvalue (non-const) container: the generic to_json takes its elements by move
|
|
||||||
template<typename T>
|
|
||||||
using is_movable_container_rvalue = negation<disjunction<
|
|
||||||
std::is_lvalue_reference<T>,
|
|
||||||
std::is_const<T>,
|
|
||||||
has_dedicated_to_json<T>,
|
|
||||||
is_basic_json<T>,
|
|
||||||
is_enum_keyed_map_type<T>,
|
|
||||||
std::is_same<T, std::vector<bool>>>>;
|
|
||||||
|
|
||||||
// rvalue array-like containers; object types are ruled out before views are
|
|
||||||
// checked, as std::ranges::view<T> for the object_t of an ordered_json would
|
|
||||||
// need that ordered_json to be complete (GCC 11, see #3312)
|
|
||||||
template<typename BasicJsonType, typename T>
|
|
||||||
using is_rvalue_array_to_json = conjunction<is_movable_container_rvalue<T>,
|
|
||||||
negation<is_compatible_object_type<BasicJsonType, T>>,
|
|
||||||
negation<is_range_view_type<T>>,
|
|
||||||
is_compatible_array_type<BasicJsonType, T>,
|
|
||||||
negation<disjunction<
|
|
||||||
std::is_same<T, typename BasicJsonType::array_t>,
|
|
||||||
std::is_same<T, typename BasicJsonType::binary_t>,
|
|
||||||
is_compatible_string_type<BasicJsonType, T>,
|
|
||||||
is_compatible_binary_type<BasicJsonType, T>>>>;
|
|
||||||
|
|
||||||
// rvalue containers: their elements are moved instead of copied
|
|
||||||
template<typename BasicJsonType, typename CompatibleArrayType,
|
|
||||||
enable_if_t<is_rvalue_array_to_json<BasicJsonType, CompatibleArrayType>::value, int> = 0>
|
|
||||||
inline void to_json(BasicJsonType& j, CompatibleArrayType && arr) // NOLINT(cppcoreguidelines-missing-std-forward)
|
|
||||||
{
|
|
||||||
external_constructor<value_t::array>::construct_moving(j, arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if JSON_HAS_RANGE_VIEW_CONVERSION
|
#if JSON_HAS_RANGE_VIEW_CONVERSION
|
||||||
template < typename BasicJsonType, typename T,
|
template < typename BasicJsonType, typename T,
|
||||||
enable_if_t < is_compatible_range_view<std::remove_cvref_t<T>>::value
|
enable_if_t < is_compatible_range_view<std::remove_cvref_t<T>>::value
|
||||||
@@ -537,20 +427,6 @@ inline void to_json(BasicJsonType& j, const EnumKeyedMap& map)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// rvalue object-like containers (views have no key_type and mapped_type, so
|
|
||||||
// they are no compatible object type)
|
|
||||||
template<typename BasicJsonType, typename T>
|
|
||||||
using is_rvalue_object_to_json = conjunction<is_movable_container_rvalue<T>,
|
|
||||||
negation<std::is_same<T, typename BasicJsonType::object_t>>,
|
|
||||||
is_compatible_object_type<BasicJsonType, T>>;
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename CompatibleObjectType,
|
|
||||||
enable_if_t<is_rvalue_object_to_json<BasicJsonType, CompatibleObjectType>::value, int> = 0>
|
|
||||||
inline void to_json(BasicJsonType& j, CompatibleObjectType && obj) // NOLINT(cppcoreguidelines-missing-std-forward)
|
|
||||||
{
|
|
||||||
external_constructor<value_t::object>::construct_moving(j, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
inline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
inline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -184,10 +184,7 @@ JSON_HEDLEY_ALWAYS_INLINE shortest_decimal to_shortest(std::uint64_t bits) noexc
|
|||||||
const uint128_parts p10 = pow10(-dec_exp - 1);
|
const uint128_parts p10 = pow10(-dec_exp - 1);
|
||||||
const uint128_parts p = umul192_hi128(p10.high, p10.low, bin_sig << static_cast<unsigned>(shift));
|
const uint128_parts p = umul192_hi128(p10.high, p10.low, bin_sig << static_cast<unsigned>(shift));
|
||||||
std::uint64_t integral = p.high >> static_cast<unsigned>(extra_shift);
|
std::uint64_t integral = p.high >> static_cast<unsigned>(extra_shift);
|
||||||
// (no casts in the shifts: MSVC 2015 warns C4554 on them despite the parentheses)
|
const std::uint64_t fractional = (p.high << static_cast<unsigned>(64 - extra_shift)) | (p.low >> static_cast<unsigned>(extra_shift));
|
||||||
constexpr auto fractional_low_shift = static_cast<unsigned>(extra_shift);
|
|
||||||
constexpr unsigned fractional_high_shift = 64u - fractional_low_shift;
|
|
||||||
const std::uint64_t fractional = (p.high << fractional_high_shift) | (p.low >> fractional_low_shift);
|
|
||||||
std::uint64_t digit = 0;
|
std::uint64_t digit = 0;
|
||||||
bool round_up = false;
|
bool round_up = false;
|
||||||
bool round_down = false;
|
bool round_down = false;
|
||||||
|
|||||||
@@ -428,7 +428,7 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
@throw type_error.302 always
|
@throw type_error.302 always
|
||||||
*/
|
*/
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_type_must_be(const char* expected, const BasicJsonType& j)
|
JSON_HEDLEY_NO_RETURN inline void throw_type_must_be(const char* expected, const BasicJsonType& j)
|
||||||
{
|
{
|
||||||
static_cast<void>(expected); // unused when JSON_NOEXCEPTION is defined
|
static_cast<void>(expected); // unused when JSON_NOEXCEPTION is defined
|
||||||
static_cast<void>(j);
|
static_cast<void>(j);
|
||||||
@@ -443,7 +443,7 @@ JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_type_must_be(const cha
|
|||||||
@throw type_error always
|
@throw type_error always
|
||||||
*/
|
*/
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_cannot_use_with(const exception_id id_, const char* operation, const BasicJsonType& j)
|
JSON_HEDLEY_NO_RETURN inline void throw_cannot_use_with(const exception_id id_, const char* operation, const BasicJsonType& j)
|
||||||
{
|
{
|
||||||
static_cast<void>(id_); // unused when JSON_NOEXCEPTION is defined
|
static_cast<void>(id_); // unused when JSON_NOEXCEPTION is defined
|
||||||
static_cast<void>(operation);
|
static_cast<void>(operation);
|
||||||
@@ -451,76 +451,6 @@ JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_cannot_use_with(const
|
|||||||
JSON_THROW(type_error::create(id_, concat("cannot use ", operation, " with ", j.type_name()), &j));
|
JSON_THROW(type_error::create(id_, concat("cannot use ", operation, " with ", j.type_name()), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief throws ExceptionType with a fixed message; kept out of line so that callers do not carry the exception construction
|
|
||||||
*/
|
|
||||||
template<typename ExceptionType, typename BasicJsonContext>
|
|
||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_literal(const exception_id id_, const char* what_arg, BasicJsonContext context)
|
|
||||||
{
|
|
||||||
static_cast<void>(id_); // unused when JSON_NOEXCEPTION is defined
|
|
||||||
static_cast<void>(what_arg);
|
|
||||||
static_cast<void>(context);
|
|
||||||
JSON_THROW(ExceptionType::create(id_, what_arg, context));
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSON Patch errors; constructed out of line so that patch_inplace() does not carry them
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_add_parent(const BasicJsonType& parent)
|
|
||||||
{
|
|
||||||
static_cast<void>(parent);
|
|
||||||
JSON_THROW(out_of_range::create(exception_id::patch_add_parent_not_container, concat("cannot add value: the JSON Patch 'add' target's parent is of type ", parent.type_name(), ", but must be an object or array"), &parent));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_remove_parent(const BasicJsonType& parent)
|
|
||||||
{
|
|
||||||
static_cast<void>(parent);
|
|
||||||
JSON_THROW(out_of_range::create(exception_id::patch_remove_parent_not_container, concat("cannot remove value: the JSON Patch 'remove' target's parent is of type ", parent.type_name(), ", but must be an object or array"), &parent));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_not_an_array(const BasicJsonType& j)
|
|
||||||
{
|
|
||||||
static_cast<void>(j);
|
|
||||||
JSON_THROW(parse_error::create(exception_id::patch_not_an_array, 0, "JSON patch must be an array of objects", &j));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename StringType, typename BasicJsonType>
|
|
||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_missing_member(const StringType& op, const StringType& member, const bool string_type, const BasicJsonType& val)
|
|
||||||
{
|
|
||||||
static_cast<void>(op);
|
|
||||||
static_cast<void>(member);
|
|
||||||
static_cast<void>(string_type);
|
|
||||||
static_cast<void>(val);
|
|
||||||
// context-sensitive error message
|
|
||||||
JSON_THROW(parse_error::create(exception_id::patch_invalid_operation, 0, concat((op == "op") ? std::string("operation") : concat("operation '", op, '\''), string_type ? " must have string member '" : " must have member '", member, "'"), &val));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename StringType, typename BasicJsonType>
|
|
||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_move_into_child(const StringType& from_path, const StringType& path, const BasicJsonType& result)
|
|
||||||
{
|
|
||||||
static_cast<void>(from_path);
|
|
||||||
static_cast<void>(path);
|
|
||||||
static_cast<void>(result);
|
|
||||||
JSON_THROW(out_of_range::create(exception_id::patch_move_into_child, concat("cannot move value: 'from' path '", from_path, "' is a proper prefix of 'path' '", path, "'"), &result));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_test_failed(const BasicJsonType& val)
|
|
||||||
{
|
|
||||||
static_cast<void>(val);
|
|
||||||
JSON_THROW(other_error::create(exception_id::patch_test_failed, concat("unsuccessful: ", val.dump()), &val));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename StringType, typename BasicJsonType>
|
|
||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_invalid_operation(const StringType& op, const BasicJsonType& val)
|
|
||||||
{
|
|
||||||
static_cast<void>(op);
|
|
||||||
static_cast<void>(val);
|
|
||||||
JSON_THROW(parse_error::create(exception_id::patch_invalid_operation, 0, concat("operation value '", op, "' is invalid"), &val));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ class binary_reader
|
|||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(current != char_traits<char_type>::eof()))
|
if (JSON_HEDLEY_UNLIKELY(current != char_traits<char_type>::eof()))
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::unexpected_end_of_input, "expected end of input", "value", "");
|
return last_byte_error(exception_id::unexpected_end_of_input, concat("expected end of input; last byte: 0x", get_token_string()), "value");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,7 +317,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(document_size < 0 || static_cast<std::size_t>(document_size) != chars_read - document_start))
|
if (JSON_HEDLEY_UNLIKELY(document_size < 0 || static_cast<std::size_t>(document_size) != chars_read - document_start))
|
||||||
{
|
{
|
||||||
return bson_document_size_error(document_size, chars_read - document_start);
|
return last_byte_error(exception_id::unexpected_byte, concat("document size ", std::to_string(document_size), " does not match the number of bytes read (", std::to_string(chars_read - document_start), ")"), "document");
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -509,7 +509,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(len < 1))
|
if (JSON_HEDLEY_UNLIKELY(len < 1))
|
||||||
{
|
{
|
||||||
return bson_length_error("string length must be at least 1, is ", len, "string");
|
return last_byte_error(exception_id::unexpected_byte, concat("string length must be at least 1, is ", std::to_string(len)), "string");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_string(len - static_cast<NumberType>(1), result)))
|
if (JSON_HEDLEY_UNLIKELY(!get_string(len - static_cast<NumberType>(1), result)))
|
||||||
@@ -539,7 +539,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(len < 0))
|
if (JSON_HEDLEY_UNLIKELY(len < 0))
|
||||||
{
|
{
|
||||||
return bson_length_error("byte array length cannot be negative, is ", len, "binary");
|
return last_byte_error(exception_id::unexpected_byte, concat("byte array length cannot be negative, is ", std::to_string(len)), "binary");
|
||||||
}
|
}
|
||||||
|
|
||||||
// All BSON binary values have a subtype
|
// All BSON binary values have a subtype
|
||||||
@@ -629,7 +629,9 @@ class binary_reader
|
|||||||
|
|
||||||
default: // anything else is not supported (yet)
|
default: // anything else is not supported (yet)
|
||||||
{
|
{
|
||||||
return bson_unsupported_type_error(element_type_parse_position, element_type);
|
const std::string cr_str = hex_byte(static_cast<std::uint8_t>(element_type));
|
||||||
|
return sax->parse_error(element_type_parse_position, cr_str,
|
||||||
|
parse_error::create(exception_id::bson_unsupported_type, element_type_parse_position, concat("Unsupported BSON record type 0x", cr_str), nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1058,7 +1060,6 @@ class binary_reader
|
|||||||
@param[in] context parsing context for the error message
|
@param[in] context parsing context for the error message
|
||||||
@return whether the SAX consumer accepts the parse error
|
@return whether the SAX consumer accepts the parse error
|
||||||
*/
|
*/
|
||||||
JSON_HEDLEY_NEVER_INLINE
|
|
||||||
bool cbor_indefinite_string_error(const char* type_name, const char* context)
|
bool cbor_indefinite_string_error(const char* type_name, const char* context)
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::invalid_string_or_size, concat("indefinite-length ", type_name, " is not allowed inside indefinite-length ", type_name, "; last byte: 0x", get_token_string()), context);
|
return last_byte_error(exception_id::invalid_string_or_size, concat("indefinite-length ", type_name, " is not allowed inside indefinite-length ", type_name, "; last byte: 0x", get_token_string()), context);
|
||||||
@@ -1138,7 +1139,7 @@ class binary_reader
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::invalid_string_or_size, "expected length specification (0x60-0x7B)", "string", inside_indefinite ? "" : " or indefinite string type (0x7F)");
|
return last_byte_error(exception_id::invalid_string_or_size, concat("expected length specification (0x60-0x7B)", inside_indefinite ? "" : " or indefinite string type (0x7F)", "; last byte: 0x", get_token_string()), "string");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1268,7 +1269,7 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return last_byte_error(exception_id::invalid_string_or_size, "only string keys are supported, but found ", "object key", found);
|
return last_byte_error(exception_id::invalid_string_or_size, concat("only string keys are supported, but found ", found, "; last byte: 0x", get_token_string()), "object key");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -1349,7 +1350,7 @@ class binary_reader
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::invalid_string_or_size, "expected length specification (0x40-0x5B)", "binary", inside_indefinite ? "" : " or indefinite binary array type (0x5F)");
|
return last_byte_error(exception_id::invalid_string_or_size, concat("expected length specification (0x40-0x5B)", inside_indefinite ? "" : " or indefinite binary array type (0x5F)", "; last byte: 0x", get_token_string()), "binary");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1495,7 +1496,8 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(len) || len == detail::unknown_size()))
|
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(len) || len == detail::unknown_size()))
|
||||||
{
|
{
|
||||||
return container_size_error(context);
|
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large,
|
||||||
|
exception_message(concat("excessive ", context, " size"), "size"), nullptr));
|
||||||
}
|
}
|
||||||
result = conditional_static_cast<std::size_t>(len);
|
result = conditional_static_cast<std::size_t>(len);
|
||||||
return true;
|
return true;
|
||||||
@@ -2063,7 +2065,7 @@ class binary_reader
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::invalid_string_or_size, "expected length specification (0xA0-0xBF, 0xD9-0xDB)", "string", "");
|
return last_byte_error(exception_id::invalid_string_or_size, concat("expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x", get_token_string()), "string");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2155,7 +2157,7 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return last_byte_error(exception_id::invalid_string_or_size, "only string keys are supported, but found ", "object key", found);
|
return last_byte_error(exception_id::invalid_string_or_size, concat("only string keys are supported, but found ", found, "; last byte: 0x", get_token_string()), "object key");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -2653,7 +2655,8 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(number)))
|
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(number)))
|
||||||
{
|
{
|
||||||
return out_of_range_error(exception_id::container_too_large, "integer value overflow", "size");
|
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large,
|
||||||
|
exception_message("integer value overflow", "size"), nullptr));
|
||||||
}
|
}
|
||||||
result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
|
result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
|
||||||
return true;
|
return true;
|
||||||
@@ -2750,7 +2753,8 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
if (!value_in_range_of<std::size_t>(number))
|
if (!value_in_range_of<std::size_t>(number))
|
||||||
{
|
{
|
||||||
return out_of_range_error(exception_id::container_too_large, "integer value overflow", "size");
|
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large,
|
||||||
|
exception_message("integer value overflow", "size"), nullptr));
|
||||||
}
|
}
|
||||||
result = detail::conditional_static_cast<std::size_t>(number);
|
result = detail::conditional_static_cast<std::size_t>(number);
|
||||||
return true;
|
return true;
|
||||||
@@ -2828,7 +2832,7 @@ class binary_reader
|
|||||||
// or SIZE_MAX.
|
// or SIZE_MAX.
|
||||||
if (JSON_HEDLEY_UNLIKELY(result > (std::numeric_limits<std::size_t>::max)() / i))
|
if (JSON_HEDLEY_UNLIKELY(result > (std::numeric_limits<std::size_t>::max)() / i))
|
||||||
{
|
{
|
||||||
return out_of_range_error(exception_id::container_too_large, "excessive ndarray size caused overflow", "size");
|
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large, exception_message("excessive ndarray size caused overflow", "size"), nullptr));
|
||||||
}
|
}
|
||||||
result *= i;
|
result *= i;
|
||||||
// the pre-check above already rules out result becoming 0
|
// the pre-check above already rules out result becoming 0
|
||||||
@@ -2837,7 +2841,7 @@ class binary_reader
|
|||||||
// unknown-size container (see get_ubjson_size_type())
|
// unknown-size container (see get_ubjson_size_type())
|
||||||
if (result == npos)
|
if (result == npos)
|
||||||
{
|
{
|
||||||
return out_of_range_error(exception_id::container_too_large, "excessive ndarray size caused overflow", "size");
|
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large, exception_message("excessive ndarray size caused overflow", "size"), nullptr));
|
||||||
}
|
}
|
||||||
if (JSON_HEDLEY_UNLIKELY(!emit_unsigned(i)))
|
if (JSON_HEDLEY_UNLIKELY(!emit_unsigned(i)))
|
||||||
{
|
{
|
||||||
@@ -2887,7 +2891,7 @@ class binary_reader
|
|||||||
if (input_format == input_format_t::bjdata
|
if (input_format == input_format_t::bjdata
|
||||||
&& JSON_HEDLEY_UNLIKELY(is_bjd_excluded_optimized_type(result.second)))
|
&& JSON_HEDLEY_UNLIKELY(is_bjd_excluded_optimized_type(result.second)))
|
||||||
{
|
{
|
||||||
return optimized_type_error();
|
return last_byte_error(exception_id::unexpected_byte, concat("marker 0x", get_token_string(), " is not a permitted optimized array type"), "type");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("type")))
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("type")))
|
||||||
@@ -2902,7 +2906,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return last_byte_error(exception_id::unexpected_byte, "expected '#' after type information", "size", "");
|
return last_byte_error(exception_id::unexpected_byte, concat("expected '#' after type information; last byte: 0x", get_token_string()), "size");
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool is_error = get_ubjson_size_value(result.first, is_ndarray, 0, result.second);
|
const bool is_error = get_ubjson_size_value(result.first, is_ndarray, 0, result.second);
|
||||||
@@ -3053,7 +3057,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
if (JSON_HEDLEY_UNLIKELY(current > 127))
|
if (JSON_HEDLEY_UNLIKELY(current > 127))
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::invalid_string_or_size, "byte after 'C' must be in range 0x00..0x7F", "char", "");
|
return last_byte_error(exception_id::invalid_string_or_size, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", get_token_string()), "char");
|
||||||
}
|
}
|
||||||
string_t s(1, static_cast<typename string_t::value_type>(current));
|
string_t s(1, static_cast<typename string_t::value_type>(current));
|
||||||
return sax->string(s);
|
return sax->string(s);
|
||||||
@@ -3137,7 +3141,8 @@ class binary_reader
|
|||||||
if (JSON_HEDLEY_UNLIKELY((size_and_type.second == 'Z' || size_and_type.second == 'T' || size_and_type.second == 'F')
|
if (JSON_HEDLEY_UNLIKELY((size_and_type.second == 'Z' || size_and_type.second == 'T' || size_and_type.second == 'F')
|
||||||
&& size_and_type.first > max_valueless_container_size))
|
&& size_and_type.first > max_valueless_container_size))
|
||||||
{
|
{
|
||||||
return out_of_range_error(exception_id::container_too_large, "excessive array size", "size");
|
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large,
|
||||||
|
exception_message("excessive array size", "size"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!enter_array(size_and_type.first, size_and_type.second)))
|
if (JSON_HEDLEY_UNLIKELY(!enter_array(size_and_type.first, size_and_type.second)))
|
||||||
@@ -3849,7 +3854,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
// in case of failure, advance position by 1 to report the failing location
|
// in case of failure, advance position by 1 to report the failing location
|
||||||
++chars_read;
|
++chars_read;
|
||||||
static_cast<void>(report_eof(context));
|
sax->parse_error(chars_read, "<end of file>", parse_error::create(exception_id::unexpected_end_of_input, chars_read, exception_message("unexpected end of input", context), nullptr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -4002,7 +4007,8 @@ class binary_reader
|
|||||||
const auto result = static_cast<number_float_t>(number);
|
const auto result = static_cast<number_float_t>(number);
|
||||||
if (JSON_HEDLEY_UNLIKELY(std::isfinite(number) && !std::isfinite(result)))
|
if (JSON_HEDLEY_UNLIKELY(std::isfinite(number) && !std::isfinite(result)))
|
||||||
{
|
{
|
||||||
return out_of_range_error(exception_id::number_overflow, "number overflow", "value");
|
return sax->parse_error(chars_read, get_token_string(),
|
||||||
|
out_of_range::create(exception_id::number_overflow, exception_message("number overflow", "value"), nullptr));
|
||||||
}
|
}
|
||||||
return sax->number_float(result, "");
|
return sax->number_float(result, "");
|
||||||
}
|
}
|
||||||
@@ -4217,82 +4223,22 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(current == char_traits<char_type>::eof()))
|
if (JSON_HEDLEY_UNLIKELY(current == char_traits<char_type>::eof()))
|
||||||
{
|
{
|
||||||
return report_eof(context);
|
return sax->parse_error(chars_read, "<end of file>",
|
||||||
|
parse_error::create(exception_id::unexpected_end_of_input, chars_read, exception_message("unexpected end of input", context), nullptr));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief reports an unexpected end of input (out of line to keep the exception
|
|
||||||
construction out of the hot reading functions)
|
|
||||||
@param[in] context further context information (for diagnostics)
|
|
||||||
@return the result of the SAX parser's parse_error()
|
|
||||||
*/
|
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
|
||||||
JSON_HEDLEY_NEVER_INLINE
|
|
||||||
bool report_eof(const char* context) const
|
|
||||||
{
|
|
||||||
return sax->parse_error(chars_read, "<end of file>",
|
|
||||||
parse_error::create(exception_id::unexpected_end_of_input, chars_read, exception_message("unexpected end of input", context), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief reports an out_of_range error at the last read byte (out of line)
|
|
||||||
*/
|
|
||||||
JSON_HEDLEY_NEVER_INLINE
|
|
||||||
bool out_of_range_error(const exception_id id_, const char* detail, const char* context) const
|
|
||||||
{
|
|
||||||
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(id_, exception_message(detail, context), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_HEDLEY_NEVER_INLINE
|
|
||||||
bool container_size_error(const char* what) const
|
|
||||||
{
|
|
||||||
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(exception_id::container_too_large,
|
|
||||||
exception_message(concat("excessive ", what, " size"), "size"), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_HEDLEY_NEVER_INLINE
|
|
||||||
bool optimized_type_error() const
|
|
||||||
{
|
|
||||||
return last_byte_error(exception_id::unexpected_byte, concat("marker 0x", get_token_string(), " is not a permitted optimized array type"), "type");
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename NumberType>
|
|
||||||
JSON_HEDLEY_NEVER_INLINE
|
|
||||||
bool bson_length_error(const char* prefix, const NumberType len, const char* context) const
|
|
||||||
{
|
|
||||||
return last_byte_error(exception_id::unexpected_byte, concat(prefix, std::to_string(len)), context);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_HEDLEY_NEVER_INLINE
|
|
||||||
bool bson_document_size_error(const std::int32_t document_size, const std::size_t bytes_read) const
|
|
||||||
{
|
|
||||||
return last_byte_error(exception_id::unexpected_byte, concat("document size ", std::to_string(document_size), " does not match the number of bytes read (", std::to_string(bytes_read), ")"), "document");
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_HEDLEY_NEVER_INLINE
|
|
||||||
bool bson_unsupported_type_error(const std::size_t position, const char_int_type element_type) const
|
|
||||||
{
|
|
||||||
const std::string cr_str = hex_byte(static_cast<std::uint8_t>(element_type));
|
|
||||||
return sax->parse_error(position, cr_str,
|
|
||||||
parse_error::create(exception_id::bson_unsupported_type, position, concat("Unsupported BSON record type 0x", cr_str), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief reports a parse error at the last read byte
|
@brief reports a parse error at the last read byte
|
||||||
@param[in] id_ the id of the parse_error exception
|
@param[in] id_ the id of the parse_error exception
|
||||||
@param[in] detail a detailed error message
|
@param[in] detail a detailed error message
|
||||||
@param[in] context further context information
|
@param[in] context further context information
|
||||||
@param[in] tail optional text appended to the detail, followed by "; last byte: 0x<token>"
|
|
||||||
@return the result of the SAX parser's parse_error()
|
@return the result of the SAX parser's parse_error()
|
||||||
*/
|
*/
|
||||||
JSON_HEDLEY_NEVER_INLINE
|
bool last_byte_error(const exception_id id_, const std::string& detail, const std::string& context) const
|
||||||
bool last_byte_error(const exception_id id_, const std::string& detail, const char* context, const char* tail = nullptr) const
|
|
||||||
{
|
{
|
||||||
// with a tail, the message reads "<detail><tail>; last byte: 0x<token>"
|
return sax->parse_error(chars_read, get_token_string(), parse_error::create(id_, chars_read, exception_message(detail, context), nullptr));
|
||||||
return sax->parse_error(chars_read, get_token_string(), parse_error::create(id_, chars_read,
|
|
||||||
exception_message(tail == nullptr ? detail : concat(detail, tail, "; last byte: 0x", get_token_string()), context), nullptr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -4301,8 +4247,7 @@ class binary_reader
|
|||||||
@param[in] context further context information
|
@param[in] context further context information
|
||||||
@return the result of the SAX parser's parse_error()
|
@return the result of the SAX parser's parse_error()
|
||||||
*/
|
*/
|
||||||
JSON_HEDLEY_NEVER_INLINE
|
bool unexpected_byte(const char* detail, const std::string& context) const
|
||||||
bool unexpected_byte(const char* detail, const char* context) const
|
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::unexpected_byte, concat(detail, ": 0x", get_token_string()), context);
|
return last_byte_error(exception_id::unexpected_byte, concat(detail, ": 0x", get_token_string()), context);
|
||||||
}
|
}
|
||||||
@@ -4313,7 +4258,6 @@ class binary_reader
|
|||||||
@param[in] context further context information
|
@param[in] context further context information
|
||||||
@return the result of the SAX parser's parse_error()
|
@return the result of the SAX parser's parse_error()
|
||||||
*/
|
*/
|
||||||
JSON_HEDLEY_NEVER_INLINE
|
|
||||||
bool length_type_error(const char* position, const char* context) const
|
bool length_type_error(const char* position, const char* context) const
|
||||||
{
|
{
|
||||||
const char* types = input_format == input_format_t::bjdata ? "U, i, u, I, m, l, M, L" : "U, i, I, l, L";
|
const char* types = input_format == input_format_t::bjdata ? "U, i, u, I, m, l, M, L" : "U, i, I, l, L";
|
||||||
|
|||||||
@@ -14,12 +14,10 @@
|
|||||||
#include <cstdint> // uint32_t
|
#include <cstdint> // uint32_t
|
||||||
#include <cstring> // strlen
|
#include <cstring> // strlen
|
||||||
#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next, reverse_iterator
|
#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next, reverse_iterator
|
||||||
#include <limits> // numeric_limits
|
|
||||||
#include <streambuf> // streambuf
|
#include <streambuf> // streambuf
|
||||||
#include <string> // string, char_traits
|
#include <string> // string, char_traits
|
||||||
#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
|
#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
|
||||||
#include <utility> // pair, declval
|
#include <utility> // pair, declval
|
||||||
#include <vector> // vector
|
|
||||||
|
|
||||||
#ifndef JSON_NO_IO
|
#ifndef JSON_NO_IO
|
||||||
#include <cstdio> // FILE *
|
#include <cstdio> // FILE *
|
||||||
@@ -32,33 +30,6 @@
|
|||||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||||
#include <nlohmann/detail/string_utils.hpp>
|
#include <nlohmann/detail/string_utils.hpp>
|
||||||
|
|
||||||
#ifndef JSON_NO_IO
|
|
||||||
// Which unlocked stdio functions file_input_adapter uses (see
|
|
||||||
// JSON_DISABLE_UNLOCKED_FILE_IO): 1 = POSIX flockfile/getc_unlocked,
|
|
||||||
// 2 = Microsoft CRT _lock_file/_fgetc_nolock, 0 = none (std::fgetc, which
|
|
||||||
// locks the file for every character). Only C libraries known to declare
|
|
||||||
// the functions are listed; the macros are tested after <cstdio>, which
|
|
||||||
// defines __GLIBC__. MemorySanitizer builds always use std::fgetc: glibc's
|
|
||||||
// inline getc_unlocked reads the stdio buffer directly, which glibc (not
|
|
||||||
// instrumented) filled, so MemorySanitizer would report a false positive.
|
|
||||||
#if defined(__has_feature)
|
|
||||||
#if __has_feature(memory_sanitizer)
|
|
||||||
#define JSON_UNLOCKED_FILE_IO_MSAN 1
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_DISABLE_UNLOCKED_FILE_IO || defined(JSON_UNLOCKED_FILE_IO_MSAN)
|
|
||||||
#define JSON_UNLOCKED_FILE_IO 0
|
|
||||||
#elif defined(_MSC_VER)
|
|
||||||
#define JSON_UNLOCKED_FILE_IO 2
|
|
||||||
#elif defined(__GLIBC__) || defined(__APPLE__) || defined(__ANDROID__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
|
|
||||||
#define JSON_UNLOCKED_FILE_IO 1
|
|
||||||
#else
|
|
||||||
#define JSON_UNLOCKED_FILE_IO 0
|
|
||||||
#endif
|
|
||||||
#undef JSON_UNLOCKED_FILE_IO_MSAN
|
|
||||||
#endif // JSON_NO_IO
|
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
@@ -85,35 +56,18 @@ class file_input_adapter
|
|||||||
: m_file(f)
|
: m_file(f)
|
||||||
{
|
{
|
||||||
JSON_ASSERT(m_file != nullptr);
|
JSON_ASSERT(m_file != nullptr);
|
||||||
lock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// make class move-only
|
// make class move-only
|
||||||
file_input_adapter(const file_input_adapter&) = delete;
|
file_input_adapter(const file_input_adapter&) = delete;
|
||||||
file_input_adapter(file_input_adapter&& rhs) noexcept
|
file_input_adapter(file_input_adapter&&) noexcept = default;
|
||||||
: m_file(rhs.m_file)
|
|
||||||
{
|
|
||||||
// the lock is owned by the thread, not the adapter: hand it over
|
|
||||||
rhs.m_file = nullptr;
|
|
||||||
}
|
|
||||||
file_input_adapter& operator=(const file_input_adapter&) = delete;
|
file_input_adapter& operator=(const file_input_adapter&) = delete;
|
||||||
file_input_adapter& operator=(file_input_adapter&&) = delete;
|
file_input_adapter& operator=(file_input_adapter&&) = delete;
|
||||||
~file_input_adapter()
|
~file_input_adapter() = default;
|
||||||
{
|
|
||||||
unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::char_traits<char>::int_type get_character() noexcept
|
std::char_traits<char>::int_type get_character() noexcept
|
||||||
{
|
{
|
||||||
// the file is locked for the lifetime of the adapter, so the
|
|
||||||
// per-character lock of std::fgetc can be skipped
|
|
||||||
#if JSON_UNLOCKED_FILE_IO == 1
|
|
||||||
return getc_unlocked(m_file);
|
|
||||||
#elif JSON_UNLOCKED_FILE_IO == 2
|
|
||||||
return _fgetc_nolock(m_file);
|
|
||||||
#else
|
|
||||||
return std::fgetc(m_file);
|
return std::fgetc(m_file);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns the number of characters successfully read
|
// returns the number of characters successfully read
|
||||||
@@ -124,32 +78,6 @@ class file_input_adapter
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Lock the file once for the whole parse instead of once per character.
|
|
||||||
// The lock is recursive, so fread() and the user's own stdio calls on the
|
|
||||||
// same thread keep working.
|
|
||||||
void lock() noexcept
|
|
||||||
{
|
|
||||||
#if JSON_UNLOCKED_FILE_IO == 1
|
|
||||||
flockfile(m_file);
|
|
||||||
#elif JSON_UNLOCKED_FILE_IO == 2
|
|
||||||
_lock_file(m_file);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void unlock() noexcept
|
|
||||||
{
|
|
||||||
// a moved-from adapter has no file and holds no lock
|
|
||||||
if (m_file == nullptr)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#if JSON_UNLOCKED_FILE_IO == 1
|
|
||||||
funlockfile(m_file);
|
|
||||||
#elif JSON_UNLOCKED_FILE_IO == 2
|
|
||||||
_unlock_file(m_file);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// the file pointer to read from
|
/// the file pointer to read from
|
||||||
std::FILE* m_file;
|
std::FILE* m_file;
|
||||||
};
|
};
|
||||||
@@ -199,26 +127,15 @@ class input_stream_adapter
|
|||||||
input_stream_adapter& operator=(input_stream_adapter&) = delete;
|
input_stream_adapter& operator=(input_stream_adapter&) = delete;
|
||||||
input_stream_adapter& operator=(input_stream_adapter&&) = delete;
|
input_stream_adapter& operator=(input_stream_adapter&&) = delete;
|
||||||
|
|
||||||
input_stream_adapter(input_stream_adapter&& rhs) noexcept
|
|
||||||
: is(rhs.is), sb(rhs.sb)
|
|
||||||
#if JSON_PRECISE_STREAM_POSITION
|
#if JSON_PRECISE_STREAM_POSITION
|
||||||
, lookahead(rhs.lookahead)
|
input_stream_adapter(input_stream_adapter&& rhs) noexcept
|
||||||
#endif
|
: is(rhs.is), sb(rhs.sb), lookahead(rhs.lookahead)
|
||||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
, token_carry(std::move(rhs.token_carry))
|
|
||||||
, token_segment(rhs.token_segment)
|
|
||||||
, current_in_get_area(rhs.current_in_get_area)
|
|
||||||
, tracking_token(rhs.tracking_token)
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
rhs.is = nullptr;
|
rhs.is = nullptr;
|
||||||
rhs.sb = nullptr;
|
rhs.sb = nullptr;
|
||||||
#if JSON_PRECISE_STREAM_POSITION
|
|
||||||
rhs.lookahead = false;
|
rhs.lookahead = false;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if JSON_PRECISE_STREAM_POSITION
|
|
||||||
// Whether the character last returned by get_character() can be given back
|
// Whether the character last returned by get_character() can be given back
|
||||||
// to the input with release_lookahead().
|
// to the input with release_lookahead().
|
||||||
static constexpr bool supports_lookahead = true;
|
static constexpr bool supports_lookahead = true;
|
||||||
@@ -238,41 +155,17 @@ class input_stream_adapter
|
|||||||
sb->sbumpc();
|
sb->sbumpc();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
// sgetc() refills the get area if it is exhausted
|
|
||||||
const bool refill = streambuf_access::get_ptr(sb) == streambuf_access::get_end(sb);
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(refill))
|
|
||||||
{
|
|
||||||
save_token_segment();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
auto res = sb->sgetc();
|
auto res = sb->sgetc();
|
||||||
// set eof manually, as we don't use the istream interface.
|
// set eof manually, as we don't use the istream interface.
|
||||||
if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))
|
if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))
|
||||||
{
|
{
|
||||||
// there is nothing to step over next time
|
// there is nothing to step over next time
|
||||||
lookahead = false;
|
lookahead = false;
|
||||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
current_in_get_area = false;
|
|
||||||
#endif
|
|
||||||
is->clear(is->rdstate() | std::ios::eofbit);
|
is->clear(is->rdstate() | std::ios::eofbit);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
lookahead = true;
|
lookahead = true;
|
||||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(refill))
|
|
||||||
{
|
|
||||||
// the peeked character is at gptr(), unless the streambuf has
|
|
||||||
// no get area
|
|
||||||
after_refill(res, streambuf_access::get_ptr(sb) != streambuf_access::get_end(sb));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
current_in_get_area = true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -286,6 +179,13 @@ class input_stream_adapter
|
|||||||
lookahead = false;
|
lookahead = false;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
input_stream_adapter(input_stream_adapter&& rhs) noexcept
|
||||||
|
: is(rhs.is), sb(rhs.sb)
|
||||||
|
{
|
||||||
|
rhs.is = nullptr;
|
||||||
|
rhs.sb = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// std::istream/std::streambuf use std::char_traits<char>::to_int_type, to
|
// std::istream/std::streambuf use std::char_traits<char>::to_int_type, to
|
||||||
// ensure that std::char_traits<char>::eof() and the character 0xFF do not
|
// ensure that std::char_traits<char>::eof() and the character 0xFF do not
|
||||||
// end up as the same value, e.g., 0xFFFFFFFF.
|
// end up as the same value, e.g., 0xFFFFFFFF.
|
||||||
@@ -294,37 +194,12 @@ class input_stream_adapter
|
|||||||
// stays consumed after parsing; see JSON_PRECISE_STREAM_POSITION.
|
// stays consumed after parsing; see JSON_PRECISE_STREAM_POSITION.
|
||||||
std::char_traits<char>::int_type get_character()
|
std::char_traits<char>::int_type get_character()
|
||||||
{
|
{
|
||||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
// sbumpc() refills the get area if it is exhausted
|
|
||||||
const bool refill = streambuf_access::get_ptr(sb) == streambuf_access::get_end(sb);
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(refill))
|
|
||||||
{
|
|
||||||
save_token_segment();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
auto res = sb->sbumpc();
|
auto res = sb->sbumpc();
|
||||||
// set eof manually, as we don't use the istream interface.
|
// set eof manually, as we don't use the istream interface.
|
||||||
if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))
|
if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))
|
||||||
{
|
{
|
||||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
current_in_get_area = false;
|
|
||||||
#endif
|
|
||||||
is->clear(is->rdstate() | std::ios::eofbit);
|
is->clear(is->rdstate() | std::ios::eofbit);
|
||||||
}
|
}
|
||||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
else if (JSON_HEDLEY_UNLIKELY(refill))
|
|
||||||
{
|
|
||||||
// the consumed character is at gptr() - 1, unless the streambuf
|
|
||||||
// has no get area and returned it from uflow()
|
|
||||||
const char* const next = streambuf_access::get_ptr(sb);
|
|
||||||
after_refill(res, next != nullptr && next != streambuf_access::get_begin(sb));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
current_in_get_area = true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -343,165 +218,7 @@ class input_stream_adapter
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
// The lexer may scan the characters in the get area of the streambuf
|
|
||||||
// directly (SWAR fast paths). The get area is only a window into the
|
|
||||||
// input: it ends wherever the streambuf's buffer ends, and is refilled by
|
|
||||||
// the next get_character(). The lexer therefore keeps counting the
|
|
||||||
// position itself and treats the end of the window as "unknown", not as
|
|
||||||
// the end of the input.
|
|
||||||
static constexpr bool supports_bulk_scan = true;
|
|
||||||
static constexpr bool bulk_window = true;
|
|
||||||
|
|
||||||
// Pointer to the next unread character; only valid when
|
|
||||||
// bulk_remaining() > 0. The character before it is the one last returned
|
|
||||||
// by get_character().
|
|
||||||
const char* bulk_data() const
|
|
||||||
{
|
|
||||||
return current_end();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Number of unread characters in the get area.
|
|
||||||
std::size_t bulk_remaining() const
|
|
||||||
{
|
|
||||||
if (!current_in_get_area)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return static_cast<std::size_t>(streambuf_access::get_end(sb) - current_end());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consume @a n characters previously inspected via bulk_data(); the last
|
|
||||||
// of them becomes the character last returned by get_character().
|
|
||||||
void bulk_skip(std::size_t n)
|
|
||||||
{
|
|
||||||
JSON_ASSERT(n <= bulk_remaining());
|
|
||||||
// gbump() takes an int, but a get area (e.g., of a std::stringbuf) can
|
|
||||||
// be larger
|
|
||||||
constexpr auto max_bump = static_cast<std::size_t>((std::numeric_limits<int>::max)());
|
|
||||||
for (; n > max_bump; n -= max_bump)
|
|
||||||
{
|
|
||||||
streambuf_access::bump(sb, static_cast<int>(max_bump));
|
|
||||||
}
|
|
||||||
streambuf_access::bump(sb, static_cast<int>(n));
|
|
||||||
}
|
|
||||||
|
|
||||||
// The adapter keeps the current token for the lexer's error messages: the
|
|
||||||
// part in the get area is only remembered as a pointer, and copied out
|
|
||||||
// when the get area is about to be refilled.
|
|
||||||
static constexpr bool supports_token_tracking = true;
|
|
||||||
|
|
||||||
// Start a token with the characters read from now on. Until this is
|
|
||||||
// called, nothing is tracked (the binary readers never call it).
|
|
||||||
void begin_token() noexcept
|
|
||||||
{
|
|
||||||
tracking_token = true;
|
|
||||||
token_carry.clear();
|
|
||||||
token_segment = current_end();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start a new token with the character @a c last returned by
|
|
||||||
// get_character().
|
|
||||||
void mark_token_start(std::char_traits<char>::int_type c)
|
|
||||||
{
|
|
||||||
tracking_token = true;
|
|
||||||
token_carry.clear();
|
|
||||||
if (current_in_get_area)
|
|
||||||
{
|
|
||||||
token_segment = current_end() - 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
token_segment = nullptr;
|
|
||||||
token_carry.push_back(std::char_traits<char>::to_char_type(c));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the characters read since mark_token_start() to @a out, without the
|
|
||||||
// last one if @a drop_last.
|
|
||||||
void copy_token(std::vector<char>& out, bool drop_last) const
|
|
||||||
{
|
|
||||||
out = token_carry;
|
|
||||||
if (token_segment != nullptr)
|
|
||||||
{
|
|
||||||
out.insert(out.end(), token_segment, current_end());
|
|
||||||
}
|
|
||||||
if (drop_last && !out.empty())
|
|
||||||
{
|
|
||||||
out.pop_back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
// Reads the protected get-area pointers of any std::streambuf. Naming the
|
|
||||||
// members through a derived class makes forming the member pointers
|
|
||||||
// accessible; calling them on another streambuf object is then ordinary
|
|
||||||
// member access through a pointer to member.
|
|
||||||
struct streambuf_access : std::streambuf
|
|
||||||
{
|
|
||||||
static const char* get_ptr(std::streambuf* s)
|
|
||||||
{
|
|
||||||
return (s->*(&streambuf_access::gptr))();
|
|
||||||
}
|
|
||||||
static const char* get_end(std::streambuf* s)
|
|
||||||
{
|
|
||||||
return (s->*(&streambuf_access::egptr))();
|
|
||||||
}
|
|
||||||
static const char* get_begin(std::streambuf* s)
|
|
||||||
{
|
|
||||||
return (s->*(&streambuf_access::eback))();
|
|
||||||
}
|
|
||||||
static void bump(std::streambuf* s, int n)
|
|
||||||
{
|
|
||||||
(s->*(&streambuf_access::gbump))(n);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// one past the character last returned by get_character(), if that is in
|
|
||||||
// the get area
|
|
||||||
const char* current_end() const
|
|
||||||
{
|
|
||||||
#if JSON_PRECISE_STREAM_POSITION
|
|
||||||
// the character is peeked at gptr() and not stepped over yet, unless
|
|
||||||
// it was given back with release_lookahead()
|
|
||||||
return streambuf_access::get_ptr(sb) + (lookahead ? 1 : 0);
|
|
||||||
#else
|
|
||||||
return streambuf_access::get_ptr(sb);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// the get area is about to be replaced: copy the token's part in it
|
|
||||||
void save_token_segment()
|
|
||||||
{
|
|
||||||
if (token_segment != nullptr)
|
|
||||||
{
|
|
||||||
token_carry.insert(token_carry.end(), token_segment, streambuf_access::get_end(sb));
|
|
||||||
token_segment = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// character @a c was just read into a new get area (@a in_get_area) or
|
|
||||||
// returned without one
|
|
||||||
void after_refill(std::char_traits<char>::int_type c, bool in_get_area)
|
|
||||||
{
|
|
||||||
current_in_get_area = in_get_area;
|
|
||||||
if (!tracking_token)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (in_get_area)
|
|
||||||
{
|
|
||||||
token_segment = current_end() - 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
token_carry.push_back(std::char_traits<char>::to_char_type(c));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_PRECISE_STREAM_POSITION
|
#if JSON_PRECISE_STREAM_POSITION
|
||||||
// Step over the character last returned by get_character(). The character
|
// Step over the character last returned by get_character(). The character
|
||||||
// has already been peeked successfully, so for every streambuf with a get
|
// has already been peeked successfully, so for every streambuf with a get
|
||||||
@@ -523,16 +240,6 @@ class input_stream_adapter
|
|||||||
/// whether get_character() peeked a character that is not consumed yet
|
/// whether get_character() peeked a character that is not consumed yet
|
||||||
bool lookahead = false;
|
bool lookahead = false;
|
||||||
#endif
|
#endif
|
||||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
/// the part of the current token that was in earlier get areas
|
|
||||||
std::vector<char> token_carry{}; // NOLINT(readability-redundant-member-init)
|
|
||||||
/// start of the current token's part in the get area, or nullptr
|
|
||||||
const char* token_segment = nullptr;
|
|
||||||
/// whether the character last returned by get_character() is in the get area
|
|
||||||
bool current_in_get_area = false;
|
|
||||||
/// whether a token is tracked at all; the binary readers never start one
|
|
||||||
bool tracking_token = false;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
#endif // JSON_NO_IO
|
#endif // JSON_NO_IO
|
||||||
|
|
||||||
|
|||||||
@@ -187,19 +187,12 @@ Shared by json_sax_dom_parser and json_sax_dom_callback_parser.
|
|||||||
@param[in] ref the object or array that was just created
|
@param[in] ref the object or array that was just created
|
||||||
@return whether parsing should continue (false after reporting out_of_range.408)
|
@return whether parsing should continue (false after reporting out_of_range.408)
|
||||||
*/
|
*/
|
||||||
template<typename SAX, typename BasicJsonType>
|
|
||||||
JSON_HEDLEY_NEVER_INLINE
|
|
||||||
bool container_too_large_error(SAX& sax, std::size_t len, const char* kind, BasicJsonType* ref)
|
|
||||||
{
|
|
||||||
return sax.parse_error(0, "", out_of_range::create(exception_id::container_too_large, concat("excessive ", kind, " size: ", std::to_string(len)), ref));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename SAX, typename BasicJsonType>
|
template<typename SAX, typename BasicJsonType>
|
||||||
bool check_container_size(SAX& sax, std::size_t len, const char* kind, BasicJsonType* ref)
|
bool check_container_size(SAX& sax, std::size_t len, const char* kind, BasicJsonType* ref)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref->max_size()))
|
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref->max_size()))
|
||||||
{
|
{
|
||||||
return container_too_large_error(sax, len, kind, ref);
|
return sax.parse_error(0, "", out_of_range::create(exception_id::container_too_large, concat("excessive ", kind, " size: ", std::to_string(len)), ref));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -681,19 +674,16 @@ class json_sax_dom_callback_parser
|
|||||||
if (keep && ref_stack.back())
|
if (keep && ref_stack.back())
|
||||||
{
|
{
|
||||||
auto& obj = *ref_stack.back()->m_data.m_value.object;
|
auto& obj = *ref_stack.back()->m_data.m_value.object;
|
||||||
// a single lookup: insert the placeholder, or find the existing member
|
const auto it = obj.find(val);
|
||||||
const auto inserted = obj.emplace(val, discarded);
|
if (it != obj.end())
|
||||||
const auto it = inserted.first;
|
|
||||||
if (!inserted.second)
|
|
||||||
{
|
{
|
||||||
// this is a duplicate key (legal in JSON); remember its
|
// this is a duplicate key (legal in JSON); remember its
|
||||||
// current value so it can be restored later if the new
|
// current value so it can be restored later if the new
|
||||||
// value is rejected by the callback, instead of being
|
// value is rejected by the callback, instead of being
|
||||||
// erased together with the discarded placeholder
|
// erased together with the discarded placeholder
|
||||||
duplicate_key_stash.emplace_back(&(it->second), it->second);
|
duplicate_key_stash.emplace_back(&(it->second), it->second);
|
||||||
it->second = discarded;
|
|
||||||
}
|
}
|
||||||
object_element = &(it->second);
|
object_element = &(obj[val] = discarded);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -166,43 +166,6 @@ constexpr bool input_adapter_supports_bulk_scan(std::false_type /*detected*/)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect whether an input adapter keeps the current token itself and can
|
|
||||||
// hand it out on demand (see input_stream_adapter::supports_token_tracking),
|
|
||||||
// so the lexer need not copy every scanned character into token_string.
|
|
||||||
template<typename InputAdapterType>
|
|
||||||
using detect_supports_token_tracking = decltype(InputAdapterType::supports_token_tracking);
|
|
||||||
|
|
||||||
template<typename InputAdapterType>
|
|
||||||
constexpr bool input_adapter_supports_token_tracking(std::true_type /*detected*/)
|
|
||||||
{
|
|
||||||
return InputAdapterType::supports_token_tracking;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename InputAdapterType>
|
|
||||||
constexpr bool input_adapter_supports_token_tracking(std::false_type /*detected*/)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect whether the contiguous block of a bulk-scan adapter is only a window
|
|
||||||
// into the input (see input_stream_adapter::bulk_window): its end is not the
|
|
||||||
// end of the input, so a token that touches it must be finished character by
|
|
||||||
// character.
|
|
||||||
template<typename InputAdapterType>
|
|
||||||
using detect_bulk_window = decltype(InputAdapterType::bulk_window);
|
|
||||||
|
|
||||||
template<typename InputAdapterType>
|
|
||||||
constexpr bool input_adapter_bulk_window(std::true_type /*detected*/)
|
|
||||||
{
|
|
||||||
return InputAdapterType::bulk_window;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename InputAdapterType>
|
|
||||||
constexpr bool input_adapter_bulk_window(std::false_type /*detected*/)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief lexical analysis
|
@brief lexical analysis
|
||||||
|
|
||||||
@@ -221,20 +184,9 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
/// whether the last read token can be reconstructed from the input adapter
|
/// whether the last read token can be reconstructed from the input adapter
|
||||||
/// on demand (in error paths) instead of being copied on every scanned
|
/// on demand (in error paths) instead of being copied on every scanned
|
||||||
/// character; see input_adapter_supports_seek
|
/// character; see input_adapter_supports_seek
|
||||||
static constexpr bool seekable =
|
static constexpr bool lazy_token_string =
|
||||||
input_adapter_supports_seek<InputAdapterType>(is_detected<detect_supports_seek, InputAdapterType> {});
|
input_adapter_supports_seek<InputAdapterType>(is_detected<detect_supports_seek, InputAdapterType> {});
|
||||||
|
|
||||||
/// whether the adapter keeps the token itself (mark_token_start() /
|
|
||||||
/// copy_token()); see input_adapter_supports_token_tracking
|
|
||||||
static constexpr bool adapter_token =
|
|
||||||
!seekable && input_adapter_supports_token_tracking<InputAdapterType>(is_detected<detect_supports_token_tracking, InputAdapterType> {});
|
|
||||||
|
|
||||||
/// whether the last read token is obtained from the input adapter on
|
|
||||||
/// demand (in error paths) instead of being copied on every scanned
|
|
||||||
/// character: seekable adapters reconstruct it from the consumed input,
|
|
||||||
/// token-tracking adapters keep it themselves
|
|
||||||
static constexpr bool lazy_token_string = seekable || adapter_token;
|
|
||||||
|
|
||||||
/// whether the read position is derived from the input adapter on demand
|
/// whether the read position is derived from the input adapter on demand
|
||||||
/// instead of being counted on every scanned character. Seekable adapters
|
/// instead of being counted on every scanned character. Seekable adapters
|
||||||
/// know how much input they consumed and can revisit it, so
|
/// know how much input they consumed and can revisit it, so
|
||||||
@@ -242,7 +194,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
/// and line and column are recomputed from the consumed input only when
|
/// and line and column are recomputed from the consumed input only when
|
||||||
/// get_position() is called (errors). Counting per character kept two
|
/// get_position() is called (errors). Counting per character kept two
|
||||||
/// increments and a newline check in the hot path of every get().
|
/// increments and a newline check in the hot path of every get().
|
||||||
static constexpr bool lazy_position = seekable;
|
static constexpr bool lazy_position = lazy_token_string;
|
||||||
|
|
||||||
/// whether a simulated unget can be passed on to the input adapter, which
|
/// whether a simulated unget can be passed on to the input adapter, which
|
||||||
/// then leaves the character in the input; see
|
/// then leaves the character in the input; see
|
||||||
@@ -251,17 +203,14 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
input_adapter_supports_lookahead<InputAdapterType>(is_detected<detect_supports_lookahead, InputAdapterType> {});
|
input_adapter_supports_lookahead<InputAdapterType>(is_detected<detect_supports_lookahead, InputAdapterType> {});
|
||||||
|
|
||||||
/// whether string scanning may bulk-consume runs of ordinary characters
|
/// whether string scanning may bulk-consume runs of ordinary characters
|
||||||
/// directly from a contiguous input buffer (SWAR fast path). Without
|
/// directly from a contiguous input buffer (SWAR fast path). This requires
|
||||||
/// lazy_token_string / lazy_position, the bulk paths copy the consumed
|
/// the token to be reconstructible lazily (lazy_token_string), so bypassing
|
||||||
/// characters into token_string and advance the position counters
|
/// the per-character capture in get() cannot lose error diagnostics. It
|
||||||
/// themselves (see bulk_consume).
|
/// also implies lazy_position, so the bulk paths need not advance the
|
||||||
|
/// position counters.
|
||||||
static constexpr bool bulk_scan =
|
static constexpr bool bulk_scan =
|
||||||
input_adapter_supports_bulk_scan<InputAdapterType>(is_detected<detect_supports_bulk_scan, InputAdapterType> {});
|
lazy_token_string
|
||||||
|
&& input_adapter_supports_bulk_scan<InputAdapterType>(is_detected<detect_supports_bulk_scan, InputAdapterType> {});
|
||||||
/// whether the contiguous block is only a window into the input, so that
|
|
||||||
/// its end does not end a token; see input_adapter_bulk_window
|
|
||||||
static constexpr bool bulk_window =
|
|
||||||
input_adapter_bulk_window<InputAdapterType>(is_detected<detect_bulk_window, InputAdapterType> {});
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using token_type = typename lexer_base<BasicJsonType>::token_type;
|
using token_type = typename lexer_base<BasicJsonType>::token_type;
|
||||||
@@ -270,11 +219,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
: ia(std::move(adapter))
|
: ia(std::move(adapter))
|
||||||
, ignore_comments(ignore_comments_)
|
, ignore_comments(ignore_comments_)
|
||||||
, discard_number_values(discard_number_values_)
|
, discard_number_values(discard_number_values_)
|
||||||
{
|
{}
|
||||||
// the token string of an error before the first token is everything
|
|
||||||
// read so far
|
|
||||||
begin_token(std::integral_constant<bool, adapter_token> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
// deleted because of pointer members
|
// deleted because of pointer members
|
||||||
lexer(const lexer&) = delete;
|
lexer(const lexer&) = delete;
|
||||||
@@ -291,8 +236,8 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
/// contiguous input: try to decode the 4 hex digits following `\\u`
|
/// contiguous input: try to decode the 4 hex digits following `\\u`
|
||||||
/// directly from the input buffer via hex_codepoint(), instead of 4 calls
|
/// directly from the input buffer via hex_codepoint(), instead of 4 calls
|
||||||
/// to get(). On success, advances the adapter exactly as those 4 get()
|
/// to get(). On success, advances the adapter exactly as those 4 get()
|
||||||
/// calls would (including the position and token bookkeeping of a
|
/// calls would (bulk_scan implies lazy_position, so there are no position
|
||||||
/// streaming adapter, see bulk_consume) and leaves @a current holding the last of
|
/// counters to advance) and leaves @a current holding the last of
|
||||||
/// the 4 digits, just as the last such get() would; the codepoint is
|
/// the 4 digits, just as the last such get() would; the codepoint is
|
||||||
/// written to @a out. Makes no state change and returns false for a
|
/// written to @a out. Makes no state change and returns false for a
|
||||||
/// pending unget, fewer than 4 remaining bytes, or any of the 4 bytes not
|
/// pending unget, fewer than 4 remaining bytes, or any of the 4 bytes not
|
||||||
@@ -311,7 +256,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bulk_consume(reinterpret_cast<const char*>(raw), 4);
|
ia.bulk_skip(4);
|
||||||
current = char_traits<char_type>::to_int_type(raw[3]);
|
current = char_traits<char_type>::to_int_type(raw[3]);
|
||||||
out = codepoint;
|
out = codepoint;
|
||||||
return true;
|
return true;
|
||||||
@@ -440,36 +385,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
token_buffer.append(reinterpret_cast<const typename string_t::value_type*>(data), pos);
|
token_buffer.append(reinterpret_cast<const typename string_t::value_type*>(data), pos);
|
||||||
bulk_consume(reinterpret_cast<const char*>(data), pos);
|
ia.bulk_skip(pos);
|
||||||
}
|
|
||||||
|
|
||||||
/// consume @a n characters at @a data (the adapter's bulk_data()) and do
|
|
||||||
/// the bookkeeping get() would have done for them; the characters are
|
|
||||||
/// never newlines
|
|
||||||
void bulk_consume(const char* data, std::size_t n)
|
|
||||||
{
|
|
||||||
bulk_track_position(n, std::integral_constant<bool, lazy_position> {});
|
|
||||||
bulk_track_token(data, n, std::integral_constant<bool, lazy_token_string> {});
|
|
||||||
ia.bulk_skip(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// seekable adapter: the position is derived from the adapter
|
|
||||||
void bulk_track_position(std::size_t /*n*/, std::true_type /*lazy*/) const noexcept {}
|
|
||||||
|
|
||||||
/// streaming adapter: count the characters
|
|
||||||
void bulk_track_position(std::size_t n, std::false_type /*lazy*/) noexcept
|
|
||||||
{
|
|
||||||
position.chars_read_total += n;
|
|
||||||
position.chars_read_current_line += n;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// the token is obtained from the adapter
|
|
||||||
void bulk_track_token(const char* /*data*/, std::size_t /*n*/, std::true_type /*lazy*/) const noexcept {}
|
|
||||||
|
|
||||||
/// streaming adapter: capture the characters
|
|
||||||
void bulk_track_token(const char* data, std::size_t n, std::false_type /*lazy*/)
|
|
||||||
{
|
|
||||||
token_string.insert(token_string.end(), data, data + n);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// streaming input: no bulk fast path
|
/// streaming input: no bulk fast path
|
||||||
@@ -1625,12 +1541,9 @@ scan_number_done:
|
|||||||
const std::size_t avail = rem + 1;
|
const std::size_t avail = rem + 1;
|
||||||
|
|
||||||
// validate + classify the number extent (mirrors scan_number()'s grammar)
|
// validate + classify the number extent (mirrors scan_number()'s grammar)
|
||||||
// and accumulate the mantissa digits on the way, so that tokens with up
|
|
||||||
// to 19 digits need no second pass for their value
|
|
||||||
std::size_t i = 0;
|
std::size_t i = 0;
|
||||||
std::size_t dot_index = std::string::npos;
|
std::size_t dot_index = std::string::npos;
|
||||||
token_type number_type = token_type::value_unsigned;
|
token_type number_type = token_type::value_unsigned;
|
||||||
std::uint64_t w = 0;
|
|
||||||
if (data[0] == '-')
|
if (data[0] == '-')
|
||||||
{
|
{
|
||||||
number_type = token_type::value_integer;
|
number_type = token_type::value_integer;
|
||||||
@@ -1640,21 +1553,22 @@ scan_number_done:
|
|||||||
return token_type::uninitialized;
|
return token_type::uninitialized;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const std::size_t integer_begin = i;
|
|
||||||
if (data[i] == '0')
|
if (data[i] == '0')
|
||||||
{
|
{
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
else if (data[i] >= '1' && data[i] <= '9')
|
else if (data[i] >= '1' && data[i] <= '9')
|
||||||
{
|
{
|
||||||
i = accumulate_digits(data, i, avail, w);
|
++i;
|
||||||
|
while (i < avail && data[i] >= '0' && data[i] <= '9')
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return token_type::uninitialized;
|
return token_type::uninitialized;
|
||||||
}
|
}
|
||||||
std::size_t digit_count = i - integer_begin;
|
|
||||||
std::size_t fraction_digits = 0;
|
|
||||||
if (i < avail && data[i] == '.')
|
if (i < avail && data[i] == '.')
|
||||||
{
|
{
|
||||||
number_type = token_type::value_float;
|
number_type = token_type::value_float;
|
||||||
@@ -1664,45 +1578,31 @@ scan_number_done:
|
|||||||
{
|
{
|
||||||
return token_type::uninitialized;
|
return token_type::uninitialized;
|
||||||
}
|
}
|
||||||
const std::size_t fraction_begin = i;
|
while (i < avail && data[i] >= '0' && data[i] <= '9')
|
||||||
i = accumulate_digits(data, i, avail, w);
|
{
|
||||||
fraction_digits = i - fraction_begin;
|
++i;
|
||||||
digit_count += fraction_digits;
|
}
|
||||||
}
|
}
|
||||||
// the mantissa ends here, whether or not an exponent part follows
|
// the mantissa ends here, whether or not an exponent part follows
|
||||||
const std::size_t mantissa_end = i;
|
const std::size_t mantissa_end = i;
|
||||||
std::int64_t exponent = 0;
|
|
||||||
if (i < avail && (data[i] == 'e' || data[i] == 'E'))
|
if (i < avail && (data[i] == 'e' || data[i] == 'E'))
|
||||||
{
|
{
|
||||||
number_type = token_type::value_float;
|
number_type = token_type::value_float;
|
||||||
++i;
|
++i;
|
||||||
bool negative_exponent = false;
|
|
||||||
if (i < avail && (data[i] == '+' || data[i] == '-'))
|
if (i < avail && (data[i] == '+' || data[i] == '-'))
|
||||||
{
|
{
|
||||||
negative_exponent = data[i] == '-';
|
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
if (i >= avail || !(data[i] >= '0' && data[i] <= '9'))
|
if (i >= avail || !(data[i] >= '0' && data[i] <= '9'))
|
||||||
{
|
{
|
||||||
return token_type::uninitialized;
|
return token_type::uninitialized;
|
||||||
}
|
}
|
||||||
// saturated far beyond every range, see parse_float_exponent()
|
|
||||||
constexpr std::int64_t saturation = 100000000000000000; // 10^17
|
|
||||||
while (i < avail && data[i] >= '0' && data[i] <= '9')
|
while (i < avail && data[i] >= '0' && data[i] <= '9')
|
||||||
{
|
{
|
||||||
if (exponent < saturation)
|
|
||||||
{
|
|
||||||
exponent = (exponent * 10) + (data[i] - '0');
|
|
||||||
}
|
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
exponent = negative_exponent ? -exponent : exponent;
|
|
||||||
}
|
}
|
||||||
const std::size_t len = i;
|
const std::size_t len = i;
|
||||||
if (number_may_continue(len == avail, std::integral_constant<bool, bulk_window> {}))
|
|
||||||
{
|
|
||||||
return token_type::uninitialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset() records where this token starts (for diagnostics), so it has
|
// reset() records where this token starts (for diagnostics), so it has
|
||||||
// to run before the input position advances below
|
// to run before the input position advances below
|
||||||
@@ -1717,14 +1617,10 @@ scan_number_done:
|
|||||||
#if !JSON_DIAGNOSTIC_POSITIONS
|
#if !JSON_DIAGNOSTIC_POSITIONS
|
||||||
if (number_type != token_type::value_float)
|
if (number_type != token_type::value_float)
|
||||||
{
|
{
|
||||||
// up to 18 digits always fit into std::uint64_t and std::int64_t
|
const token_type integer_result = convert_integer(number_type, data, data + len);
|
||||||
const token_type integer_result = (digit_count <= 18)
|
|
||||||
? store_integer(number_type, w)
|
|
||||||
: convert_integer(number_type, data, data + len);
|
|
||||||
if (JSON_HEDLEY_LIKELY(integer_result != token_type::uninitialized))
|
if (JSON_HEDLEY_LIKELY(integer_result != token_type::uninitialized))
|
||||||
{
|
{
|
||||||
bulk_consume(data + 1, len - 1);
|
ia.bulk_skip(len - 1);
|
||||||
read_number_terminator(std::integral_constant<bool, bulk_window> {});
|
|
||||||
return integer_result;
|
return integer_result;
|
||||||
}
|
}
|
||||||
// The value does not fit an integer, so this token converts as a
|
// The value does not fit an integer, so this token converts as a
|
||||||
@@ -1734,99 +1630,17 @@ scan_number_done:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
decimal_point_position = dot_index;
|
|
||||||
|
|
||||||
bulk_consume(data + 1, len - 1);
|
|
||||||
read_number_terminator(std::integral_constant<bool, bulk_window> {});
|
|
||||||
|
|
||||||
// a float whose digits all fit into w converts from the accumulated
|
|
||||||
// value; longer ones (and other float formats) take the general path
|
|
||||||
if (number_type == token_type::value_float && digit_count <= 19)
|
|
||||||
{
|
|
||||||
float_significand s;
|
|
||||||
s.w = w;
|
|
||||||
s.exponent = exponent - static_cast<std::int64_t>(fraction_digits);
|
|
||||||
s.negative = data[0] == '-';
|
|
||||||
if (convert_accumulated_float(s, std::integral_constant<bool, has_native_float_format<number_float_t>::value> {}))
|
|
||||||
{
|
|
||||||
// the token text is only copied into token_buffer when
|
|
||||||
// get_string() asks for it; the library's own SAX handlers
|
|
||||||
// ignore it. A window into a stream may be refilled by the
|
|
||||||
// next read, so there the text is copied right away.
|
|
||||||
if (bulk_window)
|
|
||||||
{
|
|
||||||
token_buffer.append(reinterpret_cast<const typename string_t::value_type*>(data), len);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pending_number_text = data;
|
|
||||||
pending_number_length = len;
|
|
||||||
}
|
|
||||||
return token_type::value_float;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// materialize the token exactly as scan_number() would. reset() already
|
// materialize the token exactly as scan_number() would. reset() already
|
||||||
// cleared token_buffer, so append() fills it (assign() is avoided
|
// cleared token_buffer, so append() fills it (assign() is avoided
|
||||||
// because custom string_t types need not provide it)
|
// because custom string_t types need not provide it)
|
||||||
token_buffer.append(reinterpret_cast<const typename string_t::value_type*>(data), len);
|
token_buffer.append(reinterpret_cast<const typename string_t::value_type*>(data), len);
|
||||||
|
decimal_point_position = dot_index;
|
||||||
|
|
||||||
|
ia.bulk_skip(len - 1);
|
||||||
|
|
||||||
return convert_number(number_type, mantissa_end);
|
return convert_number(number_type, mantissa_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// window adapter: a number that reaches the end of the window may continue
|
|
||||||
/// after it, so scan_number() has to read it character by character
|
|
||||||
static constexpr bool number_may_continue(bool at_end, std::true_type /*window*/) noexcept
|
|
||||||
{
|
|
||||||
return at_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// the end of the block is the end of the input, which ends the number
|
|
||||||
static constexpr bool number_may_continue(bool /*at_end*/, std::false_type /*window*/) noexcept
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// window adapter: read the character after a bulk-scanned number and unget
|
|
||||||
/// it, as scan_number() does. The adapter is a stream that is handed back to
|
|
||||||
/// the user, who must find the same input consumed either way.
|
|
||||||
void read_number_terminator(std::true_type /*window*/)
|
|
||||||
{
|
|
||||||
get();
|
|
||||||
unget();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// the input is not handed back, so the terminator can stay unread
|
|
||||||
void read_number_terminator(std::false_type /*window*/) const noexcept {}
|
|
||||||
|
|
||||||
/// binary32/binary64: the value of a float token from its accumulated
|
|
||||||
/// significand (see scan_number_bulk_contiguous())
|
|
||||||
bool convert_accumulated_float(const float_significand& s, std::true_type /*native*/) noexcept
|
|
||||||
{
|
|
||||||
value_float = decimal_to_float<number_float_t>(s);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// other formats (e.g. x87 long double): not instantiated for them, as
|
|
||||||
/// decimal_to_float() only supports binary32/binary64
|
|
||||||
static bool convert_accumulated_float(const float_significand& /*s*/, std::false_type /*native*/) noexcept
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// the value of an integer token with at most 18 digits, accumulated in @a w
|
|
||||||
token_type store_integer(token_type number_type, std::uint64_t w) noexcept
|
|
||||||
{
|
|
||||||
if (number_type == token_type::value_unsigned)
|
|
||||||
{
|
|
||||||
value_unsigned = static_cast<number_unsigned_t>(w);
|
|
||||||
// reject values that do not round-trip into a narrower type
|
|
||||||
return (static_cast<std::uint64_t>(value_unsigned) == w) ? token_type::value_unsigned : token_type::uninitialized;
|
|
||||||
}
|
|
||||||
const std::int64_t x = -static_cast<std::int64_t>(w);
|
|
||||||
value_integer = static_cast<number_integer_t>(x);
|
|
||||||
return (static_cast<std::int64_t>(value_integer) == x) ? token_type::value_integer : token_type::uninitialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// contiguous input: try the number fast path, else the byte-path scanner
|
/// contiguous input: try the number fast path, else the byte-path scanner
|
||||||
token_type scan_number_dispatch(std::true_type /*bulk*/)
|
token_type scan_number_dispatch(std::true_type /*bulk*/)
|
||||||
{
|
{
|
||||||
@@ -1869,7 +1683,6 @@ scan_number_done:
|
|||||||
void reset() noexcept
|
void reset() noexcept
|
||||||
{
|
{
|
||||||
token_buffer.clear();
|
token_buffer.clear();
|
||||||
pending_number_text = nullptr;
|
|
||||||
decimal_point_position = std::string::npos;
|
decimal_point_position = std::string::npos;
|
||||||
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
@@ -1883,30 +1696,11 @@ scan_number_done:
|
|||||||
/// seekable adapter: remember where the current token starts so it can be
|
/// seekable adapter: remember where the current token starts so it can be
|
||||||
/// reconstructed from the input on error; current has already been
|
/// reconstructed from the input on error; current has already been
|
||||||
/// consumed, hence the -1
|
/// consumed, hence the -1
|
||||||
void note_token_start(std::true_type /*lazy*/)
|
void note_token_start(std::true_type /*lazy*/) noexcept
|
||||||
{
|
|
||||||
note_token_start_lazy(std::integral_constant<bool, adapter_token> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
void note_token_start_lazy(std::false_type /*adapter_token*/) noexcept
|
|
||||||
{
|
{
|
||||||
token_string_start = ia.get_consumed_count() - 1;
|
token_string_start = ia.get_consumed_count() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// token-tracking adapter: start tracking the token at the first character
|
|
||||||
void begin_token(std::true_type /*adapter_token*/) noexcept
|
|
||||||
{
|
|
||||||
ia.begin_token();
|
|
||||||
}
|
|
||||||
|
|
||||||
void begin_token(std::false_type /*adapter_token*/) const noexcept {}
|
|
||||||
|
|
||||||
/// token-tracking adapter: the token starts with the current character
|
|
||||||
void note_token_start_lazy(std::true_type /*adapter_token*/)
|
|
||||||
{
|
|
||||||
ia.mark_token_start(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// streaming adapter: start copying the token eagerly, beginning with the
|
/// streaming adapter: start copying the token eagerly, beginning with the
|
||||||
/// already-read first character
|
/// already-read first character
|
||||||
void note_token_start(std::false_type /*lazy*/) noexcept
|
void note_token_start(std::false_type /*lazy*/) noexcept
|
||||||
@@ -2129,30 +1923,10 @@ scan_number_done:
|
|||||||
/// return current string value
|
/// return current string value
|
||||||
string_t& get_string()
|
string_t& get_string()
|
||||||
{
|
{
|
||||||
if (pending_number_text != nullptr)
|
|
||||||
{
|
|
||||||
token_buffer.append(reinterpret_cast<const typename string_t::value_type*>(pending_number_text), pending_number_length);
|
|
||||||
pending_number_text = nullptr;
|
|
||||||
}
|
|
||||||
// a number token holds '.' regardless of the locale (#4084)
|
// a number token holds '.' regardless of the locale (#4084)
|
||||||
return token_buffer;
|
return token_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// the value of a string token (key or string), which never has pending
|
|
||||||
/// number text, so get_string()'s check is not needed
|
|
||||||
string_t& get_string_token() noexcept
|
|
||||||
{
|
|
||||||
JSON_ASSERT(pending_number_text == nullptr);
|
|
||||||
return token_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// the text of a float token for a SAX handler that ignores it (the
|
|
||||||
/// library's own handlers): empty, without copying the token
|
|
||||||
const string_t& get_ignored_number_text() const noexcept
|
|
||||||
{
|
|
||||||
return token_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////
|
/////////////////////
|
||||||
// diagnostics
|
// diagnostics
|
||||||
/////////////////////
|
/////////////////////
|
||||||
@@ -2247,20 +2021,6 @@ scan_number_done:
|
|||||||
public:
|
public:
|
||||||
/// seekable adapter: rebuild the last read token from the input on demand
|
/// seekable adapter: rebuild the last read token from the input on demand
|
||||||
const std::vector<char_type>& collect_token_chars(std::vector<char_type>& out, std::true_type /*lazy*/) const
|
const std::vector<char_type>& collect_token_chars(std::vector<char_type>& out, std::true_type /*lazy*/) const
|
||||||
{
|
|
||||||
return collect_token_chars_lazy(out, std::integral_constant<bool, adapter_token> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// token-tracking adapter: the adapter hands out everything read since
|
|
||||||
/// mark_token_start(), except a pending unget (see below)
|
|
||||||
const std::vector<char_type>& collect_token_chars_lazy(std::vector<char_type>& out, std::true_type /*adapter_token*/) const
|
|
||||||
{
|
|
||||||
const bool pending_real_unget = next_unget && current != char_traits<char_type>::eof();
|
|
||||||
ia.copy_token(out, pending_real_unget);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<char_type>& collect_token_chars_lazy(std::vector<char_type>& out, std::false_type /*adapter_token*/) const
|
|
||||||
{
|
{
|
||||||
// a pending unget of a real (non-EOF) character means that character
|
// a pending unget of a real (non-EOF) character means that character
|
||||||
// was consumed from the input but is not part of the token; EOF is
|
// was consumed from the input but is not part of the token; EOF is
|
||||||
@@ -2382,8 +2142,9 @@ scan_number_done:
|
|||||||
Pretty-printed JSON indents every line with a run of spaces, which the loop
|
Pretty-printed JSON indents every line with a run of spaces, which the loop
|
||||||
in skip_whitespace() would otherwise read one character at a time. The
|
in skip_whitespace() would otherwise read one character at a time. The
|
||||||
spaces are skipped 8 bytes at a time; the last 7 bytes of the input and any
|
spaces are skipped 8 bytes at a time; the last 7 bytes of the input and any
|
||||||
other whitespace are left to that loop. Skipping bypasses get();
|
other whitespace are left to that loop. Skipping bypasses get(), which is
|
||||||
bulk_consume() does its bookkeeping.
|
fine because bulk_scan implies lazy_position: the position is derived from
|
||||||
|
the adapter, not counted per character.
|
||||||
*/
|
*/
|
||||||
void skip_indentation(std::true_type /*bulk*/)
|
void skip_indentation(std::true_type /*bulk*/)
|
||||||
{
|
{
|
||||||
@@ -2406,7 +2167,7 @@ scan_number_done:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bulk_consume(data, pos);
|
ia.bulk_skip(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// streaming input: no bulk fast path
|
/// streaming input: no bulk fast path
|
||||||
@@ -2548,11 +2309,6 @@ scan_number_done:
|
|||||||
/// whether the next get() call should just return current
|
/// whether the next get() call should just return current
|
||||||
bool next_unget = false;
|
bool next_unget = false;
|
||||||
|
|
||||||
/// the text of the current float token in the input, not yet copied into
|
|
||||||
/// token_buffer (see get_string()), or nullptr
|
|
||||||
const char* pending_number_text = nullptr;
|
|
||||||
std::size_t pending_number_length = 0;
|
|
||||||
|
|
||||||
/// the start position of the current token
|
/// the start position of the current token
|
||||||
position_t position {};
|
position_t position {};
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
#include <cstring> // memcpy
|
#include <cstring> // memcpy
|
||||||
#include <limits> // numeric_limits
|
#include <limits> // numeric_limits
|
||||||
#include <string> // string
|
#include <string> // string
|
||||||
#include <type_traits> // conditional, integral_constant, is_signed, true_type, false_type
|
#include <type_traits> // conditional, integral_constant, true_type, false_type
|
||||||
#include <utility> // move
|
#include <utility> // move
|
||||||
|
|
||||||
#include <nlohmann/detail/bit_ops.hpp>
|
#include <nlohmann/detail/bit_ops.hpp>
|
||||||
@@ -50,20 +50,6 @@ NLOHMANN_JSON_NAMESPACE_BEGIN
|
|||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
|
|
||||||
// whether an integer of a signed type is negative; the overload for other types
|
|
||||||
// avoids comparing an unsigned value with zero
|
|
||||||
template<typename NumberType>
|
|
||||||
constexpr bool is_negative_integer(NumberType x, std::true_type /*is_signed*/) noexcept
|
|
||||||
{
|
|
||||||
return x < 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename NumberType>
|
|
||||||
constexpr bool is_negative_integer(NumberType /*unused*/, std::false_type /*is_signed*/) noexcept
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief fast integer parser for an already-validated unsigned integer
|
@brief fast integer parser for an already-validated unsigned integer
|
||||||
|
|
||||||
@@ -97,10 +83,8 @@ bool parse_integer_unsigned(const char* first, const char* last, NumberUnsignedT
|
|||||||
x = (x * 10u) + digit;
|
x = (x * 10u) + digit;
|
||||||
}
|
}
|
||||||
value = static_cast<NumberUnsignedType>(x);
|
value = static_cast<NumberUnsignedType>(x);
|
||||||
// reject values that do not round-trip into a narrower NumberUnsignedType,
|
// reject values that do not round-trip into a narrower NumberUnsignedType
|
||||||
// and values that wrap around to negative numbers if it is a signed type
|
return static_cast<std::uint64_t>(value) == x;
|
||||||
// (number_unsigned_t may be the same type as number_integer_t, #2573)
|
|
||||||
return static_cast<std::uint64_t>(value) == x && !is_negative_integer(value, std::is_signed<NumberUnsignedType>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -275,77 +259,13 @@ JSON_HEDLEY_ALWAYS_INLINE std::uint32_t parse_eight_digits(std::uint64_t v) noex
|
|||||||
return static_cast<std::uint32_t>(((v & 0x0000FFFF0000FFFFu) * 42949672960001u) >> 32u);
|
return static_cast<std::uint32_t>(((v & 0x0000FFFF0000FFFFu) * 42949672960001u) >> 32u);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// whether the eight bytes in @a v (see read_eight_bytes()) are all ASCII
|
/// whether [first, last) contains a digit other than '0'
|
||||||
/// digits (after fast_float)
|
inline bool has_nonzero_digit(const char* first, const char* last) noexcept
|
||||||
JSON_HEDLEY_ALWAYS_INLINE bool is_eight_digits(std::uint64_t v) noexcept
|
|
||||||
{
|
{
|
||||||
return ((v & 0xF0F0F0F0F0F0F0F0u) | (((v + 0x0606060606060606u) & 0xF0F0F0F0F0F0F0F0u) >> 4u)) == 0x3333333333333333u;
|
return std::any_of(first, last, [](char c) noexcept
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief skip the ASCII digits starting at data[i] and accumulate their value
|
|
||||||
|
|
||||||
Eight digits are taken at a time while at least eight bytes remain. @a w wraps
|
|
||||||
around after 19 digits; the caller only uses it for shorter digit runs.
|
|
||||||
|
|
||||||
@return the index of the first byte at or after @a i that is not a digit
|
|
||||||
*/
|
|
||||||
JSON_HEDLEY_ALWAYS_INLINE std::size_t accumulate_digits(const char* data, std::size_t i, std::size_t avail, std::uint64_t& w) noexcept
|
|
||||||
{
|
|
||||||
while (avail - i >= 8)
|
|
||||||
{
|
{
|
||||||
const std::uint64_t v = read_eight_bytes(data + i);
|
return c != '0';
|
||||||
if (!is_eight_digits(v))
|
});
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
w = (w * 100000000u) + parse_eight_digits(v);
|
|
||||||
i += 8;
|
|
||||||
}
|
|
||||||
while (i < avail && data[i] >= '0' && data[i] <= '9')
|
|
||||||
{
|
|
||||||
w = (w * 10u) + static_cast<std::uint64_t>(data[i] - '0');
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// whether [first, last) contains a digit other than '0'; a plain loop, as
|
|
||||||
/// GCC calls std::any_of out of line even for the usual empty range
|
|
||||||
JSON_HEDLEY_ALWAYS_INLINE bool has_nonzero_digit(const char* first, const char* last) noexcept
|
|
||||||
{
|
|
||||||
for (; first != last; ++first)
|
|
||||||
{
|
|
||||||
if (*first != '0')
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief accumulate the validated digits [p, last) into @a w, as long as
|
|
||||||
@a remaining digits still fit into it
|
|
||||||
|
|
||||||
Eight digits are taken at a time while at least eight remain, and @a remaining
|
|
||||||
is decreased by the number of digits taken.
|
|
||||||
|
|
||||||
@return a pointer past the last digit taken
|
|
||||||
*/
|
|
||||||
JSON_HEDLEY_ALWAYS_INLINE const char* accumulate_significant_digits(const char* p, const char* last, int& remaining, std::uint64_t& w) noexcept
|
|
||||||
{
|
|
||||||
while (remaining >= 8 && last - p >= 8)
|
|
||||||
{
|
|
||||||
w = (w * 100000000u) + parse_eight_digits(read_eight_bytes(p));
|
|
||||||
p += 8;
|
|
||||||
remaining -= 8;
|
|
||||||
}
|
|
||||||
for (; remaining > 0 && p != last; ++p, --remaining)
|
|
||||||
{
|
|
||||||
w = (w * 10u) + static_cast<std::uint64_t>(*p - '0');
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// the value of the validated exponent digits [+-]?[0-9]+ in [first, last),
|
/// the value of the validated exponent digits [+-]?[0-9]+ in [first, last),
|
||||||
@@ -391,7 +311,7 @@ truncated is set.
|
|||||||
std::string::npos if there is none
|
std::string::npos if there is none
|
||||||
@param[in] mantissa_end index of the 'e'/'E', or the token length
|
@param[in] mantissa_end index of the 'e'/'E', or the token length
|
||||||
*/
|
*/
|
||||||
JSON_HEDLEY_ALWAYS_INLINE float_significand parse_float_significand(const char* first, const char* last,
|
inline float_significand parse_float_significand(const char* first, const char* last,
|
||||||
std::size_t decimal_point_position, std::size_t mantissa_end) noexcept
|
std::size_t decimal_point_position, std::size_t mantissa_end) noexcept
|
||||||
{
|
{
|
||||||
float_significand s;
|
float_significand s;
|
||||||
@@ -406,7 +326,16 @@ JSON_HEDLEY_ALWAYS_INLINE float_significand parse_float_significand(const char*
|
|||||||
int remaining = 19; // digits that still fit into w
|
int remaining = 19; // digits that still fit into w
|
||||||
if (*p != '0') // the integer part is "0" or [1-9][0-9]*
|
if (*p != '0') // the integer part is "0" or [1-9][0-9]*
|
||||||
{
|
{
|
||||||
p = accumulate_significant_digits(p, integer_last, remaining, w);
|
while (remaining >= 8 && integer_last - p >= 8)
|
||||||
|
{
|
||||||
|
w = (w * 100000000u) + parse_eight_digits(read_eight_bytes(p));
|
||||||
|
p += 8;
|
||||||
|
remaining -= 8;
|
||||||
|
}
|
||||||
|
for (; remaining > 0 && p != integer_last; ++p, --remaining)
|
||||||
|
{
|
||||||
|
w = (w * 10u) + static_cast<std::uint64_t>(*p - '0');
|
||||||
|
}
|
||||||
s.exponent = integer_last - p;
|
s.exponent = integer_last - p;
|
||||||
s.truncated = has_nonzero_digit(p, integer_last);
|
s.truncated = has_nonzero_digit(p, integer_last);
|
||||||
}
|
}
|
||||||
@@ -425,7 +354,16 @@ JSON_HEDLEY_ALWAYS_INLINE float_significand parse_float_significand(const char*
|
|||||||
s.exponent -= p - zeros;
|
s.exponent -= p - zeros;
|
||||||
}
|
}
|
||||||
const char* const digits = p;
|
const char* const digits = p;
|
||||||
p = accumulate_significant_digits(p, mantissa_last, remaining, w);
|
while (remaining >= 8 && mantissa_last - p >= 8)
|
||||||
|
{
|
||||||
|
w = (w * 100000000u) + parse_eight_digits(read_eight_bytes(p));
|
||||||
|
p += 8;
|
||||||
|
remaining -= 8;
|
||||||
|
}
|
||||||
|
for (; remaining > 0 && p != mantissa_last; ++p, --remaining)
|
||||||
|
{
|
||||||
|
w = (w * 10u) + static_cast<std::uint64_t>(*p - '0');
|
||||||
|
}
|
||||||
s.exponent -= p - digits;
|
s.exponent -= p - digits;
|
||||||
s.truncated = s.truncated || has_nonzero_digit(p, mantissa_last);
|
s.truncated = s.truncated || has_nonzero_digit(p, mantissa_last);
|
||||||
}
|
}
|
||||||
@@ -766,7 +704,7 @@ inline void float_from_bits(std::uint64_t bits, float& value) noexcept
|
|||||||
{
|
{
|
||||||
static_assert(sizeof(float) == sizeof(std::uint32_t), "float must have 32 bits");
|
static_assert(sizeof(float) == sizeof(std::uint32_t), "float must have 32 bits");
|
||||||
const auto bits32 = static_cast<std::uint32_t>(bits);
|
const auto bits32 = static_cast<std::uint32_t>(bits);
|
||||||
std::memcpy(&value, &bits32, sizeof(bits32));
|
std::memcpy(&value, &bits32, sizeof(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// the powers of ten that are exact in binary64 (up to 10^22)
|
/// the powers of ten that are exact in binary64 (up to 10^22)
|
||||||
|
|||||||
@@ -51,20 +51,6 @@ template<typename BasicJsonType>
|
|||||||
using parser_callback_t =
|
using parser_callback_t =
|
||||||
std::function<bool(int /*depth*/, parse_event_t /*event*/, BasicJsonType& /*parsed*/)>;
|
std::function<bool(int /*depth*/, parse_event_t /*event*/, BasicJsonType& /*parsed*/)>;
|
||||||
|
|
||||||
/// whether a SAX handler ignores the text of float tokens (the library's own
|
|
||||||
/// handlers do), so that the parser need not materialize it
|
|
||||||
template<typename SAX>
|
|
||||||
struct sax_ignores_number_text : std::false_type {};
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename InputAdapterType>
|
|
||||||
struct sax_ignores_number_text<json_sax_dom_parser<BasicJsonType, InputAdapterType>> : std::true_type {};
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename InputAdapterType>
|
|
||||||
struct sax_ignores_number_text<json_sax_dom_callback_parser<BasicJsonType, InputAdapterType>> : std::true_type {};
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
struct sax_ignores_number_text<json_sax_acceptor<BasicJsonType>> : std::true_type {};
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief syntax analysis
|
@brief syntax analysis
|
||||||
|
|
||||||
@@ -185,23 +171,6 @@ class parser
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// the text of the current float token for @a sax (see sax_ignores_number_text)
|
|
||||||
template<typename SAX>
|
|
||||||
const string_t& number_float_text(SAX* /*sax*/)
|
|
||||||
{
|
|
||||||
return number_float_text(std::integral_constant<bool, sax_ignores_number_text<SAX>::value> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
const string_t& number_float_text(std::true_type /*ignored*/) const noexcept
|
|
||||||
{
|
|
||||||
return m_lexer.get_ignored_number_text();
|
|
||||||
}
|
|
||||||
|
|
||||||
string_t& number_float_text(std::false_type /*ignored*/)
|
|
||||||
{
|
|
||||||
return m_lexer.get_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief run a DOM SAX parser to completion and position the lexer
|
@brief run a DOM SAX parser to completion and position the lexer
|
||||||
|
|
||||||
@@ -278,7 +247,7 @@ class parser
|
|||||||
{
|
{
|
||||||
return syntax_error(*sax, exception_message(token_type::value_string, "object key"));
|
return syntax_error(*sax, exception_message(token_type::value_string, "object key"));
|
||||||
}
|
}
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string_token())))
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -332,7 +301,7 @@ class parser
|
|||||||
out_of_range::create(exception_id::number_overflow, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr));
|
out_of_range::create(exception_id::number_overflow, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, number_float_text(sax))))
|
if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -378,7 +347,7 @@ class parser
|
|||||||
|
|
||||||
case token_type::value_string:
|
case token_type::value_string:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string_token())))
|
if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string())))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -487,7 +456,7 @@ class parser
|
|||||||
return syntax_error(*sax, exception_message(token_type::value_string, "object key"));
|
return syntax_error(*sax, exception_message(token_type::value_string, "object key"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string_token())))
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -280,7 +280,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// @throw invalid_iterator.214 always
|
/// @throw invalid_iterator.214 always
|
||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_cannot_get_value() const
|
JSON_HEDLEY_NO_RETURN void throw_cannot_get_value() const
|
||||||
{
|
{
|
||||||
JSON_THROW(invalid_iterator::create(exception_id::iterator_value_unavailable, "cannot get value", m_object));
|
JSON_THROW(invalid_iterator::create(exception_id::iterator_value_unavailable, "cannot get value", m_object));
|
||||||
}
|
}
|
||||||
@@ -294,25 +294,40 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
{
|
{
|
||||||
JSON_ASSERT(m_object != nullptr);
|
JSON_ASSERT(m_object != nullptr);
|
||||||
|
|
||||||
if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array))
|
switch (m_object->m_data.m_type)
|
||||||
{
|
{
|
||||||
JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end());
|
case value_t::object:
|
||||||
return *m_it.array_iterator;
|
{
|
||||||
}
|
JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end());
|
||||||
|
return m_it.object_iterator->second;
|
||||||
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::object))
|
case value_t::array:
|
||||||
{
|
{
|
||||||
JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end());
|
JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end());
|
||||||
return m_it.object_iterator->second;
|
return *m_it.array_iterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
// neither array nor object: a primitive value (null has no value to point to)
|
case value_t::null:
|
||||||
if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type != value_t::null && m_it.primitive_iterator.is_begin()))
|
throw_cannot_get_value();
|
||||||
{
|
|
||||||
return *m_object;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw_cannot_get_value();
|
case value_t::string:
|
||||||
|
case value_t::boolean:
|
||||||
|
case value_t::number_integer:
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
case value_t::number_float:
|
||||||
|
case value_t::binary:
|
||||||
|
case value_t::discarded:
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))
|
||||||
|
{
|
||||||
|
return *m_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw_cannot_get_value();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -323,25 +338,38 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
{
|
{
|
||||||
JSON_ASSERT(m_object != nullptr);
|
JSON_ASSERT(m_object != nullptr);
|
||||||
|
|
||||||
if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array))
|
switch (m_object->m_data.m_type)
|
||||||
{
|
{
|
||||||
JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end());
|
case value_t::object:
|
||||||
return &*m_it.array_iterator;
|
{
|
||||||
}
|
JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end());
|
||||||
|
return &(m_it.object_iterator->second);
|
||||||
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::object))
|
case value_t::array:
|
||||||
{
|
{
|
||||||
JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end());
|
JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end());
|
||||||
return &(m_it.object_iterator->second);
|
return &*m_it.array_iterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
// neither array nor object: a primitive value
|
case value_t::null:
|
||||||
if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))
|
case value_t::string:
|
||||||
{
|
case value_t::boolean:
|
||||||
return m_object;
|
case value_t::number_integer:
|
||||||
}
|
case value_t::number_unsigned:
|
||||||
|
case value_t::number_float:
|
||||||
|
case value_t::binary:
|
||||||
|
case value_t::discarded:
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))
|
||||||
|
{
|
||||||
|
return m_object;
|
||||||
|
}
|
||||||
|
|
||||||
throw_cannot_get_value();
|
throw_cannot_get_value();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -363,20 +391,35 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
{
|
{
|
||||||
JSON_ASSERT(m_object != nullptr);
|
JSON_ASSERT(m_object != nullptr);
|
||||||
|
|
||||||
if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array))
|
switch (m_object->m_data.m_type)
|
||||||
{
|
{
|
||||||
++m_it.array_iterator;
|
case value_t::object:
|
||||||
return *this;
|
{
|
||||||
|
std::advance(m_it.object_iterator, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::array:
|
||||||
|
{
|
||||||
|
std::advance(m_it.array_iterator, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::null:
|
||||||
|
case value_t::string:
|
||||||
|
case value_t::boolean:
|
||||||
|
case value_t::number_integer:
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
case value_t::number_float:
|
||||||
|
case value_t::binary:
|
||||||
|
case value_t::discarded:
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
++m_it.primitive_iterator;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::object))
|
|
||||||
{
|
|
||||||
std::advance(m_it.object_iterator, 1);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// neither array nor object: a primitive value
|
|
||||||
++m_it.primitive_iterator;
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,20 +442,35 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
{
|
{
|
||||||
JSON_ASSERT(m_object != nullptr);
|
JSON_ASSERT(m_object != nullptr);
|
||||||
|
|
||||||
if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array))
|
switch (m_object->m_data.m_type)
|
||||||
{
|
{
|
||||||
--m_it.array_iterator;
|
case value_t::object:
|
||||||
return *this;
|
{
|
||||||
|
std::advance(m_it.object_iterator, -1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::array:
|
||||||
|
{
|
||||||
|
std::advance(m_it.array_iterator, -1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::null:
|
||||||
|
case value_t::string:
|
||||||
|
case value_t::boolean:
|
||||||
|
case value_t::number_integer:
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
case value_t::number_float:
|
||||||
|
case value_t::binary:
|
||||||
|
case value_t::discarded:
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
--m_it.primitive_iterator;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::object))
|
|
||||||
{
|
|
||||||
std::advance(m_it.object_iterator, -1);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// neither array nor object: a primitive value
|
|
||||||
--m_it.primitive_iterator;
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,7 +484,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
// if objects are not the same, the comparison is undefined
|
// if objects are not the same, the comparison is undefined
|
||||||
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
|
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
|
||||||
{
|
{
|
||||||
throw_literal<invalid_iterator>(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object);
|
JSON_THROW(invalid_iterator::create(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object));
|
||||||
}
|
}
|
||||||
|
|
||||||
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
|
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
|
||||||
@@ -435,18 +493,25 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array))
|
switch (m_object->m_data.m_type)
|
||||||
{
|
{
|
||||||
return (m_it.array_iterator == other.m_it.array_iterator);
|
case value_t::object:
|
||||||
}
|
return (m_it.object_iterator == other.m_it.object_iterator);
|
||||||
|
|
||||||
if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::object))
|
case value_t::array:
|
||||||
{
|
return (m_it.array_iterator == other.m_it.array_iterator);
|
||||||
return (m_it.object_iterator == other.m_it.object_iterator);
|
|
||||||
}
|
|
||||||
|
|
||||||
// neither array nor object: a primitive value
|
case value_t::null:
|
||||||
return (m_it.primitive_iterator == other.m_it.primitive_iterator);
|
case value_t::string:
|
||||||
|
case value_t::boolean:
|
||||||
|
case value_t::number_integer:
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
case value_t::number_float:
|
||||||
|
case value_t::binary:
|
||||||
|
case value_t::discarded:
|
||||||
|
default:
|
||||||
|
return (m_it.primitive_iterator == other.m_it.primitive_iterator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -468,7 +533,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
// if objects are not the same, the comparison is undefined
|
// if objects are not the same, the comparison is undefined
|
||||||
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
|
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
|
||||||
{
|
{
|
||||||
throw_literal<invalid_iterator>(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object);
|
JSON_THROW(invalid_iterator::create(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object));
|
||||||
}
|
}
|
||||||
|
|
||||||
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
|
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
|
||||||
@@ -481,7 +546,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
switch (m_object->m_data.m_type)
|
switch (m_object->m_data.m_type)
|
||||||
{
|
{
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
throw_literal<invalid_iterator>(exception_id::iterator_order_on_object, "cannot compare order of object iterators", m_object);
|
JSON_THROW(invalid_iterator::create(exception_id::iterator_order_on_object, "cannot compare order of object iterators", m_object));
|
||||||
|
|
||||||
case value_t::array:
|
case value_t::array:
|
||||||
return (m_it.array_iterator < other.m_it.array_iterator);
|
return (m_it.array_iterator < other.m_it.array_iterator);
|
||||||
@@ -537,7 +602,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
switch (m_object->m_data.m_type)
|
switch (m_object->m_data.m_type)
|
||||||
{
|
{
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
throw_literal<invalid_iterator>(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object);
|
JSON_THROW(invalid_iterator::create(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object));
|
||||||
|
|
||||||
case value_t::array:
|
case value_t::array:
|
||||||
{
|
{
|
||||||
@@ -616,7 +681,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
switch (m_object->m_data.m_type)
|
switch (m_object->m_data.m_type)
|
||||||
{
|
{
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
throw_literal<invalid_iterator>(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object);
|
JSON_THROW(invalid_iterator::create(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object));
|
||||||
|
|
||||||
case value_t::array:
|
case value_t::array:
|
||||||
return m_it.array_iterator - other.m_it.array_iterator;
|
return m_it.array_iterator - other.m_it.array_iterator;
|
||||||
@@ -645,7 +710,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
switch (m_object->m_data.m_type)
|
switch (m_object->m_data.m_type)
|
||||||
{
|
{
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
throw_literal<invalid_iterator>(exception_id::iterator_subscript_on_object, "cannot use operator[] for object iterators", m_object);
|
JSON_THROW(invalid_iterator::create(exception_id::iterator_subscript_on_object, "cannot use operator[] for object iterators", m_object));
|
||||||
|
|
||||||
case value_t::array:
|
case value_t::array:
|
||||||
return *std::next(m_it.array_iterator, n);
|
return *std::next(m_it.array_iterator, n);
|
||||||
@@ -680,12 +745,12 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
|||||||
{
|
{
|
||||||
JSON_ASSERT(m_object != nullptr);
|
JSON_ASSERT(m_object != nullptr);
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!m_object->is_object()))
|
if (JSON_HEDLEY_LIKELY(m_object->is_object()))
|
||||||
{
|
{
|
||||||
throw_literal<invalid_iterator>(exception_id::iterator_key_not_object, "cannot use key() for non-object iterators", m_object);
|
return m_it.object_iterator->first;
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_it.object_iterator->first;
|
JSON_THROW(invalid_iterator::create(exception_id::iterator_key_not_object, "cannot use key() for non-object iterators", m_object));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
@@ -11,19 +11,19 @@
|
|||||||
#include <algorithm> // all_of
|
#include <algorithm> // all_of
|
||||||
#include <cctype> // isdigit
|
#include <cctype> // isdigit
|
||||||
#include <cerrno> // errno, ERANGE
|
#include <cerrno> // errno, ERANGE
|
||||||
#include <cstddef> // size_t
|
|
||||||
#include <cstdlib> // strtoull
|
#include <cstdlib> // strtoull
|
||||||
#ifndef JSON_NO_IO
|
#ifndef JSON_NO_IO
|
||||||
#include <iosfwd> // ostream
|
#include <iosfwd> // ostream
|
||||||
#endif // JSON_NO_IO
|
#endif // JSON_NO_IO
|
||||||
#include <limits> // max
|
#include <limits> // max
|
||||||
|
#include <map> // map
|
||||||
|
#include <numeric> // accumulate
|
||||||
#include <string> // string
|
#include <string> // string
|
||||||
#include <utility> // move
|
#include <utility> // move
|
||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
|
|
||||||
#include <nlohmann/detail/exceptions.hpp>
|
#include <nlohmann/detail/exceptions.hpp>
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
|
||||||
#include <nlohmann/detail/string_concat.hpp>
|
#include <nlohmann/detail/string_concat.hpp>
|
||||||
#include <nlohmann/detail/string_escape.hpp>
|
#include <nlohmann/detail/string_escape.hpp>
|
||||||
#include <nlohmann/detail/string_utils.hpp>
|
#include <nlohmann/detail/string_utils.hpp>
|
||||||
@@ -76,21 +76,12 @@ class json_pointer
|
|||||||
/// @sa https://json.nlohmann.me/api/json_pointer/to_string/
|
/// @sa https://json.nlohmann.me/api/json_pointer/to_string/
|
||||||
string_t to_string() const
|
string_t to_string() const
|
||||||
{
|
{
|
||||||
// one buffer: every token adds its escaped form (at least its length) and a slash
|
return std::accumulate(reference_tokens.begin(), reference_tokens.end(),
|
||||||
std::size_t length = 0;
|
string_t{},
|
||||||
for (const auto& reference_token : reference_tokens)
|
[](const string_t& a, const string_t& b)
|
||||||
{
|
{
|
||||||
length += reference_token.size() + 1;
|
return detail::concat<string_t>(a, '/', detail::escape(b));
|
||||||
}
|
});
|
||||||
|
|
||||||
string_t result;
|
|
||||||
result.reserve(length);
|
|
||||||
for (const auto& reference_token : reference_tokens)
|
|
||||||
{
|
|
||||||
result.append("/", 1);
|
|
||||||
detail::escape_into(result, reference_token);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief return a string representation of the JSON pointer
|
/// @brief return a string representation of the JSON pointer
|
||||||
@@ -145,29 +136,21 @@ class json_pointer
|
|||||||
friend json_pointer operator/(const json_pointer& lhs,
|
friend json_pointer operator/(const json_pointer& lhs,
|
||||||
const json_pointer& rhs)
|
const json_pointer& rhs)
|
||||||
{
|
{
|
||||||
json_pointer result;
|
return json_pointer(lhs) /= rhs;
|
||||||
result.reference_tokens.reserve(lhs.reference_tokens.size() + rhs.reference_tokens.size());
|
|
||||||
result.reference_tokens.insert(result.reference_tokens.end(), lhs.reference_tokens.begin(), lhs.reference_tokens.end());
|
|
||||||
result.reference_tokens.insert(result.reference_tokens.end(), rhs.reference_tokens.begin(), rhs.reference_tokens.end());
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer
|
/// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
|
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
|
||||||
friend json_pointer operator/(const json_pointer& lhs, string_t token) // NOLINT(performance-unnecessary-value-param)
|
friend json_pointer operator/(const json_pointer& lhs, string_t token) // NOLINT(performance-unnecessary-value-param)
|
||||||
{
|
{
|
||||||
json_pointer result;
|
return json_pointer(lhs) /= std::move(token);
|
||||||
result.reference_tokens.reserve(lhs.reference_tokens.size() + 1);
|
|
||||||
result.reference_tokens.insert(result.reference_tokens.end(), lhs.reference_tokens.begin(), lhs.reference_tokens.end());
|
|
||||||
result.reference_tokens.push_back(std::move(token));
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer
|
/// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
|
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
|
||||||
friend json_pointer operator/(const json_pointer& lhs, std::size_t array_idx)
|
friend json_pointer operator/(const json_pointer& lhs, std::size_t array_idx)
|
||||||
{
|
{
|
||||||
return lhs / detail::to_string<string_t>(array_idx);
|
return json_pointer(lhs) /= array_idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief returns the parent of this JSON pointer
|
/// @brief returns the parent of this JSON pointer
|
||||||
@@ -179,8 +162,8 @@ class json_pointer
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
json_pointer res;
|
json_pointer res = *this;
|
||||||
res.reference_tokens = std::vector<string_t>(reference_tokens.begin(), reference_tokens.end() - 1);
|
res.pop_back();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,14 +252,14 @@ class json_pointer
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
/// @throw out_of_range.405 always
|
/// @throw out_of_range.405 always
|
||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE static void throw_no_parent()
|
JSON_HEDLEY_NO_RETURN static void throw_no_parent()
|
||||||
{
|
{
|
||||||
JSON_THROW(detail::out_of_range::create(detail::exception_id::patch_on_root, "JSON pointer has no parent", nullptr));
|
JSON_THROW(detail::out_of_range::create(detail::exception_id::patch_on_root, "JSON pointer has no parent", nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @throw out_of_range.404 always
|
/// @throw out_of_range.404 always
|
||||||
template<typename BasicJsonContext>
|
template<typename BasicJsonContext>
|
||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE static void throw_unresolved(const string_t& reference_token, BasicJsonContext context)
|
JSON_HEDLEY_NO_RETURN static void throw_unresolved(const string_t& reference_token, BasicJsonContext context)
|
||||||
{
|
{
|
||||||
static_cast<void>(reference_token); // unused when JSON_NOEXCEPTION is defined
|
static_cast<void>(reference_token); // unused when JSON_NOEXCEPTION is defined
|
||||||
static_cast<void>(context);
|
static_cast<void>(context);
|
||||||
@@ -329,28 +312,6 @@ class json_pointer
|
|||||||
return array_index_status::not_a_number;
|
return array_index_status::not_a_number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fast path: up to 18 decimal digits cannot overflow unsigned long long or
|
|
||||||
// size_type (9 digits for a 32-bit size_type), so strtoull and errno are not needed
|
|
||||||
constexpr std::size_t max_fast_digits = sizeof(size_type) >= 8 ? 18 : 9;
|
|
||||||
if (!s.empty() && s.size() <= max_fast_digits)
|
|
||||||
{
|
|
||||||
size_type acc = 0;
|
|
||||||
for (std::size_t i = 0; i < s.size(); ++i)
|
|
||||||
{
|
|
||||||
const char c = s[i];
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(c < '0' || c > '9'))
|
|
||||||
{
|
|
||||||
break; // not a plain number: let strtoull decide
|
|
||||||
}
|
|
||||||
acc = static_cast<size_type>((acc * 10) + static_cast<size_type>(c - '0'));
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(i == s.size() - 1))
|
|
||||||
{
|
|
||||||
idx = acc;
|
|
||||||
return array_index_status::ok;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* p = s.data();
|
const char* p = s.data();
|
||||||
char* p_end = nullptr; // NOLINT(misc-const-correctness)
|
char* p_end = nullptr; // NOLINT(misc-const-correctness)
|
||||||
errno = 0; // strtoull doesn't reset errno
|
errno = 0; // strtoull doesn't reset errno
|
||||||
@@ -407,83 +368,6 @@ class json_pointer
|
|||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief compare with a JSON pointer string without splitting it into tokens
|
|
||||||
|
|
||||||
@throw parse_error.107 if the string is not empty or begins with '/'
|
|
||||||
@throw parse_error.108 if character '~' is not followed by '0' or '1'
|
|
||||||
*/
|
|
||||||
bool equals_string(const string_t& reference_string) const
|
|
||||||
{
|
|
||||||
if (reference_string.empty())
|
|
||||||
{
|
|
||||||
return reference_tokens.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))
|
|
||||||
{
|
|
||||||
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_missing_slash, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
// the whole string is validated, whether or not it matches
|
|
||||||
const std::size_t size = reference_string.size();
|
|
||||||
for (std::size_t pos = reference_string.find_first_of('~');
|
|
||||||
pos != string_t::npos;
|
|
||||||
pos = reference_string.find_first_of('~', pos + 1))
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(pos == size - 1 || (reference_string[pos + 1] != '0' && reference_string[pos + 1] != '1')))
|
|
||||||
{
|
|
||||||
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_invalid_escape, 0, "escape character '~' must be followed with '0' or '1'", nullptr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// compare token by token, unescaping the string on the fly
|
|
||||||
std::size_t pos = 0; // the slash in front of the next token
|
|
||||||
for (const auto& reference_token : reference_tokens)
|
|
||||||
{
|
|
||||||
if (pos >= size)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t i = pos + 1;
|
|
||||||
std::size_t j = 0;
|
|
||||||
const std::size_t token_size = reference_token.size();
|
|
||||||
while (i < size && reference_string[i] != '/')
|
|
||||||
{
|
|
||||||
char c = reference_string[i++];
|
|
||||||
if (c == '~')
|
|
||||||
{
|
|
||||||
c = (reference_string[i++] == '0') ? '~' : '/';
|
|
||||||
}
|
|
||||||
if (j == token_size || reference_token[j] != c)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
++j;
|
|
||||||
}
|
|
||||||
if (j != token_size)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
pos = i;
|
|
||||||
}
|
|
||||||
return pos == size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief object member access with a key that is only copied if it is inserted
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static BasicJsonType& object_member(BasicJsonType& object, const string_t& key)
|
|
||||||
{
|
|
||||||
auto& members = *object.m_data.m_value.object;
|
|
||||||
const auto it = members.find(key);
|
|
||||||
if (it != members.end())
|
|
||||||
{
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
return object.operator[](key);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_PRIVATE_UNLESS_TESTED:
|
JSON_PRIVATE_UNLESS_TESTED:
|
||||||
json_pointer top() const
|
json_pointer top() const
|
||||||
{
|
{
|
||||||
@@ -507,141 +391,84 @@ class json_pointer
|
|||||||
the result independent of the order in which the flattened object is
|
the result independent of the order in which the flattened object is
|
||||||
iterated, which is unspecified for some object types.
|
iterated, which is unspecified for some object types.
|
||||||
|
|
||||||
The prefixes form a tree and are numbered (number 0 is the empty prefix),
|
The prefixes form a tree and are numbered, so each of them is stored only
|
||||||
so each of them is stored only once (as a node) rather than as a copy of
|
once (as a node) rather than as a copy of all of its reference tokens.
|
||||||
all of its reference tokens. The tree does not own the reference tokens:
|
|
||||||
it refers to the tokens of the parsed keys, which must outlive it.
|
|
||||||
*/
|
*/
|
||||||
struct prefix_tree
|
struct prefix_tree
|
||||||
{
|
{
|
||||||
// open-addressing hash table from (parent, last reference token) to
|
// children[id] maps a reference token to the number of the prefix
|
||||||
// the number of the node; empty slots have token == nullptr
|
// extended by that token; number 0 is the empty prefix
|
||||||
struct slot
|
std::vector<std::map<string_t, std::size_t>> children;
|
||||||
{
|
|
||||||
std::size_t hash;
|
|
||||||
std::size_t parent;
|
|
||||||
const string_t* token;
|
|
||||||
std::size_t id;
|
|
||||||
|
|
||||||
slot() noexcept
|
|
||||||
: hash(0), parent(0), token(nullptr), id(0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
slot(std::size_t h, std::size_t p, const string_t* t, std::size_t i) noexcept
|
|
||||||
: hash(h), parent(p), token(t), id(i)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<slot> table;
|
|
||||||
std::size_t used = 0;
|
|
||||||
|
|
||||||
static std::size_t hash_of(std::size_t parent, const string_t& token) noexcept
|
|
||||||
{
|
|
||||||
std::size_t h = parent * static_cast<std::size_t>(0x9E3779B97F4A7C15ULL & (std::numeric_limits<std::size_t>::max)());
|
|
||||||
for (std::size_t i = 0; i < token.size(); ++i)
|
|
||||||
{
|
|
||||||
h = (h ^ static_cast<unsigned char>(token[i])) * static_cast<std::size_t>(0x100000001B3ULL & (std::numeric_limits<std::size_t>::max)());
|
|
||||||
}
|
|
||||||
return h ^ (h >> 29u);
|
|
||||||
}
|
|
||||||
|
|
||||||
void grow()
|
|
||||||
{
|
|
||||||
std::vector<slot> old(table.size() * 2);
|
|
||||||
old.swap(table);
|
|
||||||
const std::size_t mask = table.size() - 1;
|
|
||||||
for (const auto& s : old)
|
|
||||||
{
|
|
||||||
if (s.token != nullptr)
|
|
||||||
{
|
|
||||||
std::size_t i = s.hash & mask;
|
|
||||||
while (table[i].token != nullptr)
|
|
||||||
{
|
|
||||||
i = (i + 1) & mask;
|
|
||||||
}
|
|
||||||
table[i] = s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parent[id] is the number of the prefix without its last token
|
|
||||||
std::vector<std::size_t> parent;
|
|
||||||
// is_array[id] is true iff some flattened key has the reference token
|
// is_array[id] is true iff some flattened key has the reference token
|
||||||
// 0 directly below the prefix with number id
|
// 0 directly below the prefix with number id
|
||||||
std::vector<bool> is_array;
|
std::vector<bool> is_array;
|
||||||
|
|
||||||
// start with the empty prefix only
|
// start with the empty prefix only
|
||||||
prefix_tree()
|
prefix_tree()
|
||||||
: table(16)
|
: children(1)
|
||||||
, parent(1, 0)
|
|
||||||
, is_array(1, false)
|
, is_array(1, false)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
// return the number of the prefix with number id extended by
|
// return the number of the prefix with number id extended by
|
||||||
// reference_token, adding it if it is new
|
// reference_token, adding it if it is new
|
||||||
std::size_t add_child(std::size_t id, const string_t& reference_token)
|
std::size_t add_child(std::size_t id, string_t&& reference_token)
|
||||||
{
|
{
|
||||||
const std::size_t hash = hash_of(id, reference_token);
|
|
||||||
const std::size_t mask = table.size() - 1;
|
|
||||||
std::size_t i = hash & mask;
|
|
||||||
while (table[i].token != nullptr)
|
|
||||||
{
|
|
||||||
if (table[i].hash == hash && table[i].parent == id && *table[i].token == reference_token)
|
|
||||||
{
|
|
||||||
return table[i].id;
|
|
||||||
}
|
|
||||||
i = (i + 1) & mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
// new prefix
|
|
||||||
const std::size_t child = parent.size();
|
|
||||||
table[i] = slot(hash, id, &reference_token, child);
|
|
||||||
if (reference_token == "0")
|
if (reference_token == "0")
|
||||||
{
|
{
|
||||||
is_array[id] = true;
|
is_array[id] = true;
|
||||||
}
|
}
|
||||||
parent.push_back(id);
|
|
||||||
is_array.push_back(false);
|
|
||||||
|
|
||||||
// keep the load factor below 1/2
|
// read the number before the emplace_back below, which may
|
||||||
if (++used * 2 > table.size())
|
// reallocate children and invalidate the iterator
|
||||||
|
const std::size_t next = children.size();
|
||||||
|
const auto inserted = children[id].emplace(std::move(reference_token), next);
|
||||||
|
const std::size_t child = inserted.first->second;
|
||||||
|
if (inserted.second)
|
||||||
{
|
{
|
||||||
grow();
|
children.emplace_back();
|
||||||
|
is_array.push_back(false);
|
||||||
}
|
}
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return the number of the prefix with number id extended by
|
||||||
|
// reference_token, which must have been added before
|
||||||
|
std::size_t find_child(std::size_t id, const string_t& reference_token) const
|
||||||
|
{
|
||||||
|
const auto it = children[id].find(reference_token);
|
||||||
|
JSON_ASSERT(it != children[id].end());
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief create and return a reference to the pointed to value
|
@brief create and return a reference to the pointed to value
|
||||||
|
|
||||||
@param[in] j the value to create the pointed to value in
|
Complexity: Linear in the number of reference tokens (times the logarithm
|
||||||
@param[in] tokens the reference tokens of the pointer
|
of the number of siblings for the prefix lookup).
|
||||||
@param[in] prefixes prefixes[i] is the number in @a tree of the prefix
|
|
||||||
consisting of the first i reference tokens
|
|
||||||
|
|
||||||
Complexity: Linear in the number of reference tokens.
|
|
||||||
|
|
||||||
@throw parse_error.106 if an array index begins with '0'
|
@throw parse_error.106 if an array index begins with '0'
|
||||||
@throw parse_error.109 if array index is not a number
|
@throw parse_error.109 if array index is not a number
|
||||||
@throw type_error.313 if value cannot be unflattened
|
@throw type_error.313 if value cannot be unflattened
|
||||||
*/
|
*/
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static BasicJsonType& get_and_create(BasicJsonType& j, const std::vector<string_t>& tokens,
|
BasicJsonType& get_and_create(BasicJsonType& j, const prefix_tree& tree) const
|
||||||
const std::vector<std::size_t>& prefixes, const prefix_tree& tree)
|
|
||||||
{
|
{
|
||||||
auto* result = &j;
|
auto* result = &j;
|
||||||
|
|
||||||
|
// the number of the prefix consumed so far; used to look up whether
|
||||||
|
// the value to be created below is an array or an object
|
||||||
|
std::size_t id = 0;
|
||||||
|
|
||||||
// in case no reference tokens exist, return a reference to the JSON value
|
// in case no reference tokens exist, return a reference to the JSON value
|
||||||
// j which will be overwritten by a primitive value
|
// j which will be overwritten by a primitive value
|
||||||
for (std::size_t i = 0; i < tokens.size(); ++i)
|
for (const auto& reference_token : reference_tokens)
|
||||||
{
|
{
|
||||||
const auto& reference_token = tokens[i];
|
|
||||||
switch (result->type())
|
switch (result->type())
|
||||||
{
|
{
|
||||||
case detail::value_t::null:
|
case detail::value_t::null:
|
||||||
{
|
{
|
||||||
if (tree.is_array[prefixes[i]])
|
if (tree.is_array[id])
|
||||||
{
|
{
|
||||||
// some reference token below this position is 0, so the
|
// some reference token below this position is 0, so the
|
||||||
// value is an array
|
// value is an array
|
||||||
@@ -658,7 +485,7 @@ class json_pointer
|
|||||||
case detail::value_t::object:
|
case detail::value_t::object:
|
||||||
{
|
{
|
||||||
// create an entry in the object
|
// create an entry in the object
|
||||||
result = &object_member(*result, reference_token);
|
result = &result->operator[](reference_token);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -685,6 +512,8 @@ class json_pointer
|
|||||||
default:
|
default:
|
||||||
JSON_THROW(detail::type_error::create(detail::exception_id::unflatten_invalid_value, "invalid value to unflatten", &j));
|
JSON_THROW(detail::type_error::create(detail::exception_id::unflatten_invalid_value, "invalid value to unflatten", &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id = tree.find_child(id, reference_token);
|
||||||
}
|
}
|
||||||
|
|
||||||
return *result;
|
return *result;
|
||||||
@@ -736,7 +565,7 @@ class json_pointer
|
|||||||
case detail::value_t::object:
|
case detail::value_t::object:
|
||||||
{
|
{
|
||||||
// use unchecked object access
|
// use unchecked object access
|
||||||
ptr = &object_member(*ptr, reference_token);
|
ptr = &ptr->operator[](reference_token);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -786,13 +615,8 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
case detail::value_t::object:
|
case detail::value_t::object:
|
||||||
{
|
{
|
||||||
const auto it = ptr->m_data.m_value.object->find(reference_token);
|
// note: at performs range check
|
||||||
if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end()))
|
ptr = &ptr->at(reference_token);
|
||||||
{
|
|
||||||
// at() throws out_of_range.403 with the usual message
|
|
||||||
ptr->at(reference_token);
|
|
||||||
}
|
|
||||||
ptr = &it->second;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -813,7 +637,7 @@ class json_pointer
|
|||||||
JSON_THROW(detail::out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat(
|
JSON_THROW(detail::out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat(
|
||||||
"array index ", std::to_string(idx), " is out of range"), ptr));
|
"array index ", std::to_string(idx), " is out of range"), ptr));
|
||||||
}
|
}
|
||||||
ptr = &(*ptr->m_data.m_value.array)[idx];
|
ptr = &ptr->operator[](idx);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -861,9 +685,7 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
// use unchecked object access; the const operator[]
|
// use unchecked object access; the const operator[]
|
||||||
// asserts that the key exists
|
// asserts that the key exists
|
||||||
const auto it = ptr->m_data.m_value.object->find(reference_token);
|
ptr = &ptr->operator[](reference_token);
|
||||||
JSON_ASSERT(it != ptr->m_data.m_value.object->end());
|
|
||||||
ptr = &it->second;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -877,9 +699,7 @@ class json_pointer
|
|||||||
|
|
||||||
// use unchecked array access; the const operator[]
|
// use unchecked array access; the const operator[]
|
||||||
// asserts that the index exists
|
// asserts that the index exists
|
||||||
const auto idx = array_index<BasicJsonType>(reference_token);
|
ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
|
||||||
JSON_ASSERT(idx < ptr->m_data.m_value.array->size());
|
|
||||||
ptr = &(*ptr->m_data.m_value.array)[idx];
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -920,12 +740,12 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
case detail::value_t::object:
|
case detail::value_t::object:
|
||||||
{
|
{
|
||||||
const auto it = ptr->m_data.m_value.object->find(reference_token);
|
const auto it = ptr->find(reference_token);
|
||||||
if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end()))
|
if (JSON_HEDLEY_UNLIKELY(it == ptr->end()))
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
ptr = &it->second;
|
ptr = &*it;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -962,7 +782,7 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
ptr = &(*ptr->m_data.m_value.array)[idx];
|
ptr = &ptr->operator[](idx);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -995,14 +815,13 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
case detail::value_t::object:
|
case detail::value_t::object:
|
||||||
{
|
{
|
||||||
const auto it = ptr->m_data.m_value.object->find(reference_token);
|
if (!ptr->contains(reference_token))
|
||||||
if (it == ptr->m_data.m_value.object->end())
|
|
||||||
{
|
{
|
||||||
// we did not find the key in the object
|
// we did not find the key in the object
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr = &it->second;
|
ptr = &ptr->operator[](reference_token);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1024,13 +843,13 @@ class json_pointer
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (idx >= ptr->m_data.m_value.array->size())
|
if (idx >= ptr->size())
|
||||||
{
|
{
|
||||||
// index out of range
|
// index out of range
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr = &(*ptr->m_data.m_value.array)[idx];
|
ptr = &ptr->operator[](idx);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1080,15 +899,6 @@ class json_pointer
|
|||||||
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_missing_slash, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr));
|
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_missing_slash, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// every slash starts a reference token
|
|
||||||
const std::size_t size = reference_string.size();
|
|
||||||
std::size_t token_count = 0;
|
|
||||||
for (std::size_t i = 0; i < size; ++i)
|
|
||||||
{
|
|
||||||
token_count += static_cast<std::size_t>(reference_string[i] == '/');
|
|
||||||
}
|
|
||||||
result.reserve(token_count);
|
|
||||||
|
|
||||||
// extract the reference tokens:
|
// extract the reference tokens:
|
||||||
// - slash: position of the last read slash (or end of string)
|
// - slash: position of the last read slash (or end of string)
|
||||||
// - start: position after the previous slash
|
// - start: position after the previous slash
|
||||||
@@ -1110,52 +920,31 @@ class json_pointer
|
|||||||
const auto count = (slash == string_t::npos ? reference_string.size() : slash) - start;
|
const auto count = (slash == string_t::npos ? reference_string.size() : slash) - start;
|
||||||
auto reference_token = string_t(reference_string.data() + start, count);
|
auto reference_token = string_t(reference_string.data() + start, count);
|
||||||
|
|
||||||
// check reference tokens are properly escaped; a token without
|
// check reference tokens are properly escaped
|
||||||
// a '~' (the common case) needs neither check nor unescaping
|
for (std::size_t pos = reference_token.find_first_of('~');
|
||||||
std::size_t pos = reference_token.find_first_of('~');
|
pos != string_t::npos;
|
||||||
if (pos != string_t::npos)
|
pos = reference_token.find_first_of('~', pos + 1))
|
||||||
{
|
{
|
||||||
for (; pos != string_t::npos; pos = reference_token.find_first_of('~', pos + 1))
|
JSON_ASSERT(reference_token[pos] == '~');
|
||||||
|
|
||||||
|
// ~ must be followed by 0 or 1
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 ||
|
||||||
|
(reference_token[pos + 1] != '0' &&
|
||||||
|
reference_token[pos + 1] != '1')))
|
||||||
{
|
{
|
||||||
JSON_ASSERT(reference_token[pos] == '~');
|
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_invalid_escape, 0, "escape character '~' must be followed with '0' or '1'", nullptr));
|
||||||
|
|
||||||
// ~ must be followed by 0 or 1
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 ||
|
|
||||||
(reference_token[pos + 1] != '0' &&
|
|
||||||
reference_token[pos + 1] != '1')))
|
|
||||||
{
|
|
||||||
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_invalid_escape, 0, "escape character '~' must be followed with '0' or '1'", nullptr));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
detail::unescape(reference_token);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// finally, store the reference token
|
// finally, store the reference token
|
||||||
result.push_back(std::move(reference_token));
|
detail::unescape(reference_token);
|
||||||
|
result.push_back(reference_token);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Add the entry (key, v) to the object `obj`. Flattened paths are pairwise
|
|
||||||
// distinct, so objects that can append a key known to be absent
|
|
||||||
// (ordered_map) skip the lookup, which is linear and would make flatten
|
|
||||||
// quadratic; all other object types use operator[].
|
|
||||||
template<typename ObjectType, typename ValueType>
|
|
||||||
static auto add_flattened(ObjectType& obj, const string_t& key, ValueType&& v, detail::priority_tag<1> /*unused*/)
|
|
||||||
-> decltype(obj.emplace_new_key(key, std::forward<ValueType>(v)), void())
|
|
||||||
{
|
|
||||||
obj.emplace_new_key(key, std::forward<ValueType>(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ObjectType, typename ValueType>
|
|
||||||
static void add_flattened(ObjectType& obj, const string_t& key, ValueType&& v, detail::priority_tag<0> /*unused*/)
|
|
||||||
{
|
|
||||||
obj[key] = std::forward<ValueType>(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[in] reference_string the reference string to the current value
|
@param[in] reference_string the reference string to the current value
|
||||||
@param[in] value the value to consider
|
@param[in] value the value to consider
|
||||||
@@ -1200,8 +989,7 @@ class json_pointer
|
|||||||
|
|
||||||
// flatten `v`, whose path is `path`: primitives and empty containers
|
// flatten `v`, whose path is `path`: primitives and empty containers
|
||||||
// are added to the result right away; other containers get a frame
|
// are added to the result right away; other containers get a frame
|
||||||
auto& object = *result.m_data.m_value.object;
|
const auto enter = [&stack, &path, &result](const BasicJsonType & v)
|
||||||
const auto enter = [&stack, &path, &object](const BasicJsonType & v)
|
|
||||||
{
|
{
|
||||||
switch (v.type())
|
switch (v.type())
|
||||||
{
|
{
|
||||||
@@ -1210,7 +998,7 @@ class json_pointer
|
|||||||
if (v.m_data.m_value.array->empty())
|
if (v.m_data.m_value.array->empty())
|
||||||
{
|
{
|
||||||
// flatten empty array as null
|
// flatten empty array as null
|
||||||
add_flattened(object, path, nullptr, detail::priority_tag<1> {});
|
result[path] = nullptr;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1224,7 +1012,7 @@ class json_pointer
|
|||||||
if (v.m_data.m_value.object->empty())
|
if (v.m_data.m_value.object->empty())
|
||||||
{
|
{
|
||||||
// flatten empty object as null
|
// flatten empty object as null
|
||||||
add_flattened(object, path, nullptr, detail::priority_tag<1> {});
|
result[path] = nullptr;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1244,7 +1032,7 @@ class json_pointer
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// add a primitive value with its reference string
|
// add a primitive value with its reference string
|
||||||
add_flattened(object, path, v, detail::priority_tag<1> {});
|
result[path] = v;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1290,10 +1078,6 @@ class json_pointer
|
|||||||
enter(it->second);
|
enter(it->second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// the entries were added without going through basic_json, so the
|
|
||||||
// parent pointers (JSON_DIAGNOSTICS) have to be set now
|
|
||||||
result.set_parents();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -1317,53 +1101,21 @@ class json_pointer
|
|||||||
|
|
||||||
BasicJsonType result;
|
BasicJsonType result;
|
||||||
|
|
||||||
// parse every key once; the reference tokens are kept for the second
|
|
||||||
// pass (the vectors are never resized, so the tree may refer to them)
|
|
||||||
std::vector<std::vector<string_t>> keys;
|
|
||||||
keys.reserve(value.m_data.m_value.object->size());
|
|
||||||
for (const auto& element : *value.m_data.m_value.object)
|
|
||||||
{
|
|
||||||
json_pointer ptr(element.first);
|
|
||||||
keys.push_back(std::move(ptr.reference_tokens));
|
|
||||||
}
|
|
||||||
|
|
||||||
// collect the pointer prefixes that have a reference token 0 among
|
// collect the pointer prefixes that have a reference token 0 among
|
||||||
// their children; the values below them are arrays, all others are
|
// their children; the values below them are arrays, all others are
|
||||||
// objects (see prefix_tree)
|
// objects (see prefix_tree)
|
||||||
prefix_tree tree;
|
prefix_tree tree;
|
||||||
// the number of the prefix with i + 1 tokens of the previous key; keys
|
for (const auto& element : *value.m_data.m_value.object)
|
||||||
// are often sorted, so most of the prefix is the same as for the
|
|
||||||
// previous key and need not be looked up again
|
|
||||||
std::vector<std::size_t> path;
|
|
||||||
const std::vector<string_t>* previous = nullptr;
|
|
||||||
// the number of the complete prefix (i.e., the key) of each key
|
|
||||||
std::vector<std::size_t> leaves;
|
|
||||||
leaves.reserve(keys.size());
|
|
||||||
for (const auto& tokens : keys)
|
|
||||||
{
|
{
|
||||||
std::size_t common = 0;
|
json_pointer ptr(element.first);
|
||||||
if (previous != nullptr)
|
std::size_t id = 0;
|
||||||
|
for (auto& reference_token : ptr.reference_tokens)
|
||||||
{
|
{
|
||||||
const std::size_t limit = (std::min)(tokens.size(), previous->size());
|
id = tree.add_child(id, std::move(reference_token));
|
||||||
while (common < limit && tokens[common] == (*previous)[common])
|
|
||||||
{
|
|
||||||
++common;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
path.resize(common);
|
|
||||||
std::size_t id = common == 0 ? 0 : path[common - 1];
|
|
||||||
for (std::size_t i = common; i < tokens.size(); ++i)
|
|
||||||
{
|
|
||||||
id = tree.add_child(id, tokens[i]);
|
|
||||||
path.push_back(id);
|
|
||||||
}
|
|
||||||
leaves.push_back(id);
|
|
||||||
previous = &tokens;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// iterate the JSON object values
|
// iterate the JSON object values
|
||||||
std::vector<std::size_t> prefixes;
|
|
||||||
std::size_t key_index = 0;
|
|
||||||
for (const auto& element : *value.m_data.m_value.object)
|
for (const auto& element : *value.m_data.m_value.object)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))
|
if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))
|
||||||
@@ -1371,21 +1123,11 @@ class json_pointer
|
|||||||
JSON_THROW(detail::type_error::create(detail::exception_id::unflatten_value_not_primitive, "values in object must be primitive", &element.second));
|
JSON_THROW(detail::type_error::create(detail::exception_id::unflatten_value_not_primitive, "values in object must be primitive", &element.second));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& tokens = keys[key_index];
|
|
||||||
// number the prefixes of this key from its last one to the first
|
|
||||||
prefixes.resize(tokens.size());
|
|
||||||
for (std::size_t i = tokens.size(), id = leaves[key_index]; i > 0; --i)
|
|
||||||
{
|
|
||||||
id = tree.parent[id];
|
|
||||||
prefixes[i - 1] = id;
|
|
||||||
}
|
|
||||||
++key_index;
|
|
||||||
|
|
||||||
// Assign the value to the reference pointed to by JSON pointer. Note
|
// Assign the value to the reference pointed to by JSON pointer. Note
|
||||||
// that if the JSON pointer is "" (i.e., points to the whole value),
|
// that if the JSON pointer is "" (i.e., points to the whole value),
|
||||||
// function get_and_create returns a reference to the result itself.
|
// function get_and_create returns a reference to the result itself.
|
||||||
// An assignment will then create a primitive value.
|
// An assignment will then create a primitive value.
|
||||||
get_and_create(result, tokens, prefixes, tree) = element.second;
|
json_pointer(element.first).get_and_create(result, tree) = element.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -1424,7 +1166,7 @@ class json_pointer
|
|||||||
= delete;
|
= delete;
|
||||||
#else
|
#else
|
||||||
{
|
{
|
||||||
return equals_string(rhs);
|
return *this == json_pointer(rhs);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1508,7 +1250,7 @@ inline bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
|
|||||||
= delete;
|
= delete;
|
||||||
#else
|
#else
|
||||||
{
|
{
|
||||||
return lhs.equals_string(rhs);
|
return lhs == json_pointer<RefStringTypeLhs>(rhs);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1521,7 +1263,7 @@ inline bool operator==(const StringType& lhs,
|
|||||||
= delete;
|
= delete;
|
||||||
#else
|
#else
|
||||||
{
|
{
|
||||||
return rhs.equals_string(lhs);
|
return json_pointer<RefStringTypeRhs>(lhs) == rhs;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
#undef JSON_NO_UNIQUE_ADDRESS
|
#undef JSON_NO_UNIQUE_ADDRESS
|
||||||
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
||||||
#undef JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
#undef JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
||||||
#undef JSON_UNLOCKED_FILE_IO
|
|
||||||
#undef JSON_DTOA_SSE2
|
#undef JSON_DTOA_SSE2
|
||||||
#undef JSON_DTOA_NEON
|
#undef JSON_DTOA_NEON
|
||||||
|
|
||||||
@@ -48,8 +47,6 @@
|
|||||||
#undef JSON_STRICT_BINARY_UTF8
|
#undef JSON_STRICT_BINARY_UTF8
|
||||||
#undef JSON_DELETE_DEPRECATED_FUNCTIONS
|
#undef JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||||
#undef JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
#undef JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||||
#undef JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
#undef JSON_DISABLE_UNLOCKED_FILE_IO
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
|
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
|
||||||
@@ -9,11 +9,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <limits> // numeric_limits
|
#include <limits> // numeric_limits
|
||||||
#include <map> // map, multimap
|
|
||||||
#include <string> // char_traits
|
#include <string> // char_traits
|
||||||
#include <tuple> // tuple
|
#include <tuple> // tuple
|
||||||
#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
|
#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
|
||||||
#include <unordered_map> // unordered_map, unordered_multimap
|
|
||||||
#include <utility> // declval
|
#include <utility> // declval
|
||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L
|
#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L
|
||||||
@@ -685,22 +683,6 @@ struct is_compatible_integer_type
|
|||||||
: is_compatible_integer_type_impl<RealIntegerType,
|
: is_compatible_integer_type_impl<RealIntegerType,
|
||||||
CompatibleNumberIntegerType> {};
|
CompatibleNumberIntegerType> {};
|
||||||
|
|
||||||
// whether to_json() stores an integer of type CompatibleNumberIntegerType as
|
|
||||||
// number_integer
|
|
||||||
template<typename BasicJsonType, typename CompatibleNumberIntegerType, typename = void>
|
|
||||||
struct is_compatible_number_integer_type
|
|
||||||
: is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType> {};
|
|
||||||
|
|
||||||
// if number_unsigned_t is the same type as number_integer_t, unsigned integers
|
|
||||||
// are stored as number_integer as well (#2573)
|
|
||||||
template<typename BasicJsonType, typename CompatibleNumberIntegerType>
|
|
||||||
struct is_compatible_number_integer_type<BasicJsonType, CompatibleNumberIntegerType,
|
|
||||||
enable_if_t<conjunction<std::is_same<typename BasicJsonType::number_integer_t, typename BasicJsonType::number_unsigned_t>,
|
|
||||||
std::is_integral<CompatibleNumberIntegerType>,
|
|
||||||
std::is_unsigned<CompatibleNumberIntegerType>,
|
|
||||||
negation<std::is_same<bool, CompatibleNumberIntegerType>>>::value>>
|
|
||||||
: std::is_constructible<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType> {};
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename CompatibleType, typename = void>
|
template<typename BasicJsonType, typename CompatibleType, typename = void>
|
||||||
struct is_compatible_type_impl: std::false_type {};
|
struct is_compatible_type_impl: std::false_type {};
|
||||||
|
|
||||||
@@ -915,31 +897,6 @@ struct has_capacity : std::integral_constant<bool, is_detected<detect_capacity,
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
struct is_ordered_map : has_capacity<T> {};
|
struct is_ordered_map : has_capacity<T> {};
|
||||||
|
|
||||||
// whether T is a specialization of nlohmann::ordered_map, which only ever
|
|
||||||
// appends members, so they can only move when its capacity changes
|
|
||||||
template<typename T>
|
|
||||||
struct is_nlohmann_ordered_map : std::false_type {};
|
|
||||||
|
|
||||||
template<class Key, class T, class IgnoredLess, class Allocator>
|
|
||||||
struct is_nlohmann_ordered_map<ordered_map<Key, T, IgnoredLess, Allocator>> : std::true_type {};
|
|
||||||
|
|
||||||
// whether adding or erasing elements never moves the other elements of the
|
|
||||||
// container T; only node-based standard containers are known to guarantee this
|
|
||||||
template<typename T>
|
|
||||||
struct has_stable_elements : std::false_type {};
|
|
||||||
|
|
||||||
template<class Key, class T, class Compare, class Allocator>
|
|
||||||
struct has_stable_elements<std::map<Key, T, Compare, Allocator>> : std::true_type {};
|
|
||||||
|
|
||||||
template<class Key, class T, class Compare, class Allocator>
|
|
||||||
struct has_stable_elements<std::multimap<Key, T, Compare, Allocator>> : std::true_type {};
|
|
||||||
|
|
||||||
template<class Key, class T, class Hash, class KeyEqual, class Allocator>
|
|
||||||
struct has_stable_elements<std::unordered_map<Key, T, Hash, KeyEqual, Allocator>> : std::true_type {};
|
|
||||||
|
|
||||||
template<class Key, class T, class Hash, class KeyEqual, class Allocator>
|
|
||||||
struct has_stable_elements<std::unordered_multimap<Key, T, Hash, KeyEqual, Allocator>> : std::true_type {};
|
|
||||||
|
|
||||||
// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
|
// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
|
||||||
template < typename T, typename U, enable_if_t < !std::is_same<T, U>::value, int > = 0 >
|
template < typename T, typename U, enable_if_t < !std::is_same<T, U>::value, int > = 0 >
|
||||||
T conditional_static_cast(U value)
|
T conditional_static_cast(U value)
|
||||||
@@ -1063,25 +1020,6 @@ constexpr bool value_in_range_of(T val)
|
|||||||
template<bool Value>
|
template<bool Value>
|
||||||
using bool_constant = std::integral_constant<bool, Value>;
|
using bool_constant = std::integral_constant<bool, Value>;
|
||||||
|
|
||||||
// Stands in for number_unsigned_t in overload sets if it is the same type as
|
|
||||||
// number_integer_t, so that the unsigned overloads do not collide with the
|
|
||||||
// integer ones. Never constructed. Such a basic_json stores every integer as
|
|
||||||
// number_integer (#2573).
|
|
||||||
struct same_as_number_integer_type {};
|
|
||||||
|
|
||||||
// NumberUnsignedType, or same_as_number_integer_type if it is the same type as
|
|
||||||
// NumberIntegerType; basic_json uses this one, as it cannot name its own member
|
|
||||||
// types through an incomplete basic_json (MSVC reports a recursive alias, and
|
|
||||||
// old GCC versions crash)
|
|
||||||
template<typename NumberIntegerType, typename NumberUnsignedType>
|
|
||||||
using number_unsigned_overload_type = typename std::conditional<std::is_same<NumberIntegerType, NumberUnsignedType>::value,
|
|
||||||
same_as_number_integer_type, NumberUnsignedType>::type;
|
|
||||||
|
|
||||||
// number_unsigned_t of BasicJsonType, or same_as_number_integer_type if it is
|
|
||||||
// the same type as number_integer_t
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
using number_unsigned_overload_t = number_unsigned_overload_type<typename BasicJsonType::number_integer_t, typename BasicJsonType::number_unsigned_t>;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// is_c_string
|
// is_c_string
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
Loaded 100 of 182 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user