mirror of
https://github.com/nlohmann/json.git
synced 2026-09-26 09:50:30 +00:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f971ae520 | ||
|
|
3395089b5f | ||
|
|
519094151e | ||
|
|
e9af5cbd11 | ||
|
|
21c626c641 | ||
|
|
00bfe6808b | ||
|
|
9be5018748 |
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
- [ ] The changes are described in detail, both the what and why.
|
- [ ] The changes are described in detail, both the what and why.
|
||||||
- [ ] If applicable, an [existing issue](https://github.com/nlohmann/json/issues) is referenced.
|
- [ ] If applicable, an [existing issue](https://github.com/nlohmann/json/issues) is referenced.
|
||||||
- [ ] If applicable, a fixed [OSS-Fuzz](https://issues.oss-fuzz.com) issue is referenced as `OSS-Fuzz: <id>` (see [fuzz testing](https://github.com/nlohmann/json/blob/develop/tests/fuzzing.md#handling-oss-fuzz-reports)).
|
|
||||||
- [ ] The [Code coverage](https://coveralls.io/github/nlohmann/json) remained at 100%. A test case for every new line of code.
|
- [ ] The [Code coverage](https://coveralls.io/github/nlohmann/json) remained at 100%. A test case for every new line of code.
|
||||||
- [ ] If applicable, the [documentation](https://json.nlohmann.me) is updated.
|
- [ ] If applicable, the [documentation](https://json.nlohmann.me) is updated.
|
||||||
- [ ] The source code is amalgamated by running `make amalgamate`.
|
- [ ] The source code is amalgamated by running `make amalgamate`.
|
||||||
|
|||||||
+3
-14
@@ -9,23 +9,12 @@ identified a security vulnerability in this repository, please use the GitHub Se
|
|||||||
Until it is published, this draft security advisory will only be visible to the maintainers of this project. Other
|
Until it is published, this draft security advisory will only be visible to the maintainers of this project. Other
|
||||||
users and teams may be added once the advisory is created.
|
users and teams may be added once the advisory is created.
|
||||||
|
|
||||||
We will send a first response within 14 days, indicating the next steps in handling your report. After the initial
|
We will send a response indicating the next steps in handling your report. After the initial reply to your report, we
|
||||||
reply to your report, we will keep you informed of the progress towards a fix and full announcement and may ask for
|
will keep you informed of the progress towards a fix and full announcement and may ask for additional information or
|
||||||
additional information or guidance.
|
guidance.
|
||||||
|
|
||||||
For vulnerabilities in third-party dependencies or modules, please report them directly to the respective maintainers.
|
For vulnerabilities in third-party dependencies or modules, please report them directly to the respective maintainers.
|
||||||
|
|
||||||
## Disclosure and credit
|
|
||||||
|
|
||||||
Once a fix is released, we publish the security advisory and list the fixed vulnerability in the release notes. We
|
|
||||||
credit the reporter in both, unless they ask not to be named.
|
|
||||||
|
|
||||||
## Supported versions
|
|
||||||
|
|
||||||
Security fixes are made on the `develop` branch and shipped with the next release. Only the latest release receives
|
|
||||||
security fixes; they are not backported to older releases. A release stops receiving security fixes when the next
|
|
||||||
release is published, so please update to the latest release to get them.
|
|
||||||
|
|
||||||
## Unofficial packages
|
## Unofficial packages
|
||||||
|
|
||||||
This project does not publish an official npm package. The npm package
|
This project does not publish an official npm package. The npm package
|
||||||
|
|||||||
+4
-4
@@ -37,13 +37,13 @@ labels:
|
|||||||
files:
|
files:
|
||||||
- "include/nlohmann/detail/input/binary_reader\\.hpp"
|
- "include/nlohmann/detail/input/binary_reader\\.hpp"
|
||||||
- "include/nlohmann/detail/output/binary_writer\\.hpp"
|
- "include/nlohmann/detail/output/binary_writer\\.hpp"
|
||||||
- "tests/src/unit-(bson|cbor|msgpack|ubjson|bjdata|bon8|binary_formats)"
|
- "tests/src/unit-(bson|cbor|msgpack|ubjson|bjdata|binary_formats)"
|
||||||
- "tests/src/fuzzer-parse_(bson|cbor|msgpack|ubjson|bjdata|bon8)"
|
- "tests/src/fuzzer-parse_(bson|cbor|msgpack|ubjson|bjdata)"
|
||||||
- "docs/mkdocs/docs/features/binary_formats/"
|
- "docs/mkdocs/docs/features/binary_formats/"
|
||||||
- "docs/mkdocs/docs/(api/basic_json|examples)/(to|from)_(bson|cbor|msgpack|ubjson|bjdata|bon8)"
|
- "docs/mkdocs/docs/(api/basic_json|examples)/(to|from)_(bson|cbor|msgpack|ubjson|bjdata)"
|
||||||
|
|
||||||
- label: "aspect: binary formats"
|
- label: "aspect: binary formats"
|
||||||
title: "(?i)(bson|cbor|msgpack|messagepack|ubjson|bjdata|bon8|binary format)"
|
title: "(?i)(bson|cbor|msgpack|messagepack|ubjson|bjdata|binary format)"
|
||||||
|
|
||||||
- label: "python"
|
- label: "python"
|
||||||
files:
|
files:
|
||||||
|
|||||||
@@ -3,10 +3,6 @@ name: "Check amalgamation"
|
|||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
name: CIFuzz
|
name: CIFuzz
|
||||||
on: [pull_request]
|
on: [pull_request]
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
|
|||||||
@@ -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@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
|
uses: github/codeql-action/init@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
|
||||||
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@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
|
uses: github/codeql-action/autobuild@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
|
uses: github/codeql-action/analyze@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
|
||||||
|
|||||||
@@ -9,10 +9,6 @@
|
|||||||
name: 'Dependency Review'
|
name: 'Dependency Review'
|
||||||
on: [pull_request]
|
on: [pull_request]
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,6 @@
|
|||||||
|
|
||||||
name: flawfinder
|
name: flawfinder
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
@@ -47,6 +43,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@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
|
uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
|
||||||
with:
|
with:
|
||||||
sarif_file: ${{github.workspace}}/flawfinder_results.sarif
|
sarif_file: ${{github.workspace}}/flawfinder_results.sarif
|
||||||
|
|||||||
@@ -4,12 +4,6 @@ on:
|
|||||||
pull_request_target:
|
pull_request_target:
|
||||||
types: [opened, synchronize]
|
types: [opened, synchronize]
|
||||||
|
|
||||||
# pull_request_target runs on the base branch, so github.ref would put all pull
|
|
||||||
# requests into one group; group by pull request number instead
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
|
|||||||
@@ -14,10 +14,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: ["develop"]
|
branches: ["develop"]
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
@@ -80,6 +76,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@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
|
uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
|
||||||
with:
|
with:
|
||||||
sarif_file: results.sarif
|
sarif_file: results.sarif
|
||||||
|
|||||||
@@ -19,10 +19,6 @@ on:
|
|||||||
schedule:
|
schedule:
|
||||||
- cron: '23 2 * * 4'
|
- cron: '23 2 * * 4'
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
@@ -65,7 +61,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@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
|
uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
|
||||||
with:
|
with:
|
||||||
sarif_file: semgrep.sarif
|
sarif_file: semgrep.sarif
|
||||||
if: always()
|
if: always()
|
||||||
|
|||||||
@@ -124,11 +124,11 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
- name: Run CMake (Release)
|
- name: Run CMake (Release)
|
||||||
run: cmake -S . -B build -G "Visual Studio 18 2026" -A ARM64 -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/W4 /WX"
|
run: cmake -S . -B build -G "Visual Studio 17 2022" -A ARM64 -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/W4 /WX"
|
||||||
if: matrix.build_type == 'Release'
|
if: matrix.build_type == 'Release'
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
- name: Run CMake (Debug)
|
- name: Run CMake (Debug)
|
||||||
run: cmake -S . -B build -G "Visual Studio 18 2026" -A ARM64 -DJSON_BuildTests=On -DJSON_FastTests=ON -DCMAKE_CXX_FLAGS="/W4 /WX"
|
run: cmake -S . -B build -G "Visual Studio 17 2022" -A ARM64 -DJSON_BuildTests=On -DJSON_FastTests=ON -DCMAKE_CXX_FLAGS="/W4 /WX"
|
||||||
if: matrix.build_type == 'Debug'
|
if: matrix.build_type == 'Debug'
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
- name: Build
|
- name: Build
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ cc_library(
|
|||||||
],
|
],
|
||||||
includes = ["include"],
|
includes = ["include"],
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
|
alwayslink = True,
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ all:
|
|||||||
@echo "clean - remove built files"
|
@echo "clean - remove built files"
|
||||||
@echo "doctest - compile example files and check their output"
|
@echo "doctest - compile example files and check their output"
|
||||||
@echo "fuzz_testing - prepare fuzz testing of the JSON parser"
|
@echo "fuzz_testing - prepare fuzz testing of the JSON parser"
|
||||||
@echo "fuzz_testing_bon8 - prepare fuzz testing of the BON8 parser"
|
|
||||||
@echo "fuzz_testing_bson - prepare fuzz testing of the BSON parser"
|
@echo "fuzz_testing_bson - prepare fuzz testing of the BSON parser"
|
||||||
@echo "fuzz_testing_cbor - prepare fuzz testing of the CBOR parser"
|
@echo "fuzz_testing_cbor - prepare fuzz testing of the CBOR parser"
|
||||||
@echo "fuzz_testing_msgpack - prepare fuzz testing of the MessagePack parser"
|
@echo "fuzz_testing_msgpack - prepare fuzz testing of the MessagePack parser"
|
||||||
@@ -72,14 +71,6 @@ fuzz_testing:
|
|||||||
find tests/data/json_tests -size -5k -name *json | xargs -I{} cp "{}" fuzz-testing/testcases
|
find tests/data/json_tests -size -5k -name *json | xargs -I{} cp "{}" fuzz-testing/testcases
|
||||||
@echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer"
|
@echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer"
|
||||||
|
|
||||||
fuzz_testing_bon8:
|
|
||||||
rm -fr fuzz-testing
|
|
||||||
mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out
|
|
||||||
$(MAKE) parse_bon8_fuzzer -C tests CXX=afl-clang++
|
|
||||||
mv tests/parse_bon8_fuzzer fuzz-testing/fuzzer
|
|
||||||
find tests/data -size -5k -name *.bon8 | xargs -I{} cp "{}" fuzz-testing/testcases
|
|
||||||
@echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer"
|
|
||||||
|
|
||||||
fuzz_testing_bson:
|
fuzz_testing_bson:
|
||||||
rm -fr fuzz-testing
|
rm -fr fuzz-testing
|
||||||
mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out
|
mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
[](https://github.com/nlohmann/json/releases)
|
[](https://github.com/nlohmann/json/releases)
|
||||||
[](https://github.com/nlohmann/json/issues)
|
[](https://github.com/nlohmann/json/issues)
|
||||||
[](https://isitmaintained.com/project/nlohmann/json "Average time to resolve an issue")
|
[](https://isitmaintained.com/project/nlohmann/json "Average time to resolve an issue")
|
||||||
[](https://www.bestpractices.dev/projects/289)
|
[](https://bestpractices.coreinfrastructure.org/projects/289)
|
||||||
[](https://scorecard.dev/viewer/?uri=github.com/nlohmann/json)
|
[](https://scorecard.dev/viewer/?uri=github.com/nlohmann/json)
|
||||||
[](https://cloudback.it)
|
[](https://cloudback.it)
|
||||||
[](https://github.com/sponsors/nlohmann)
|
[](https://github.com/sponsors/nlohmann)
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
- [Implicit conversions](#implicit-conversions)
|
- [Implicit conversions](#implicit-conversions)
|
||||||
- [Conversions to/from arbitrary types](#arbitrary-types-conversions)
|
- [Conversions to/from arbitrary types](#arbitrary-types-conversions)
|
||||||
- [Specializing enum conversion](#specializing-enum-conversion)
|
- [Specializing enum conversion](#specializing-enum-conversion)
|
||||||
- [Binary formats (BSON, CBOR, MessagePack, UBJSON, BJData, and BON8)](#binary-formats-bson-cbor-messagepack-ubjson-bjdata-and-bon8)
|
- [Binary formats (BSON, CBOR, MessagePack, UBJSON, and BJData)](#binary-formats-bson-cbor-messagepack-ubjson-and-bjdata)
|
||||||
- [Customers](#customers)
|
- [Customers](#customers)
|
||||||
- [Ecosystem](#ecosystem)
|
- [Ecosystem](#ecosystem)
|
||||||
- [Supported compilers](#supported-compilers)
|
- [Supported compilers](#supported-compilers)
|
||||||
@@ -63,7 +63,7 @@ There are myriads of [JSON](https://json.org) libraries out there, and each may
|
|||||||
|
|
||||||
- **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings. The library is also included in all popular [package managers](https://json.nlohmann.me/integration/package_managers/).
|
- **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings. The library is also included in all popular [package managers](https://json.nlohmann.me/integration/package_managers/).
|
||||||
|
|
||||||
- **Serious testing**. Our code is heavily [unit-tested](https://github.com/nlohmann/json/tree/develop/tests/src) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](https://valgrind.org) and the [Clang Sanitizers](https://clang.llvm.org/docs/index.html) that there are no memory leaks. [Google OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/json) additionally runs fuzz tests against all parsers 24/7, effectively executing billions of tests so far. To maintain high quality, the project is following the [OpenSSF Best Practices](https://www.bestpractices.dev/projects/289). See the [quality assurance](https://json.nlohmann.me/community/quality_assurance) overview documentation.
|
- **Serious testing**. Our code is heavily [unit-tested](https://github.com/nlohmann/json/tree/develop/tests/src) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](https://valgrind.org) and the [Clang Sanitizers](https://clang.llvm.org/docs/index.html) that there are no memory leaks. [Google OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/json) additionally runs fuzz tests against all parsers 24/7, effectively executing billions of tests so far. To maintain high quality, the project is following the [Core Infrastructure Initiative (CII) best practices](https://bestpractices.coreinfrastructure.org/projects/289). See the [quality assurance](https://json.nlohmann.me/community/quality_assurance) overview documentation.
|
||||||
|
|
||||||
Other aspects were not so important to us:
|
Other aspects were not so important to us:
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ There is also a [**docset**](https://github.com/Kapeli/Dash-User-Contributions/t
|
|||||||
- **JSON Pointer functions**: [flatten](https://json.nlohmann.me/api/basic_json/flatten), [unflatten](https://json.nlohmann.me/api/basic_json/unflatten)
|
- **JSON Pointer functions**: [flatten](https://json.nlohmann.me/api/basic_json/flatten), [unflatten](https://json.nlohmann.me/api/basic_json/unflatten)
|
||||||
- **JSON Patch functions**: [patch](https://json.nlohmann.me/api/basic_json/patch), [patch_inplace](https://json.nlohmann.me/api/basic_json/patch_inplace), [diff](https://json.nlohmann.me/api/basic_json/diff), [merge_patch](https://json.nlohmann.me/api/basic_json/merge_patch)
|
- **JSON Patch functions**: [patch](https://json.nlohmann.me/api/basic_json/patch), [patch_inplace](https://json.nlohmann.me/api/basic_json/patch_inplace), [diff](https://json.nlohmann.me/api/basic_json/diff), [merge_patch](https://json.nlohmann.me/api/basic_json/merge_patch)
|
||||||
- **Static functions**: [meta](https://json.nlohmann.me/api/basic_json/meta), [get_allocator](https://json.nlohmann.me/api/basic_json/get_allocator)
|
- **Static functions**: [meta](https://json.nlohmann.me/api/basic_json/meta), [get_allocator](https://json.nlohmann.me/api/basic_json/get_allocator)
|
||||||
- **Binary formats**: [from_bjdata](https://json.nlohmann.me/api/basic_json/from_bjdata), [from_bon8](https://json.nlohmann.me/api/basic_json/from_bon8), [from_bson](https://json.nlohmann.me/api/basic_json/from_bson), [from_cbor](https://json.nlohmann.me/api/basic_json/from_cbor), [from_msgpack](https://json.nlohmann.me/api/basic_json/from_msgpack), [from_ubjson](https://json.nlohmann.me/api/basic_json/from_ubjson), [to_bjdata](https://json.nlohmann.me/api/basic_json/to_bjdata), [to_bon8](https://json.nlohmann.me/api/basic_json/to_bon8), [to_bson](https://json.nlohmann.me/api/basic_json/to_bson), [to_cbor](https://json.nlohmann.me/api/basic_json/to_cbor), [to_msgpack](https://json.nlohmann.me/api/basic_json/to_msgpack), [to_ubjson](https://json.nlohmann.me/api/basic_json/to_ubjson)
|
- **Binary formats**: [from_bjdata](https://json.nlohmann.me/api/basic_json/from_bjdata), [from_bson](https://json.nlohmann.me/api/basic_json/from_bson), [from_cbor](https://json.nlohmann.me/api/basic_json/from_cbor), [from_msgpack](https://json.nlohmann.me/api/basic_json/from_msgpack), [from_ubjson](https://json.nlohmann.me/api/basic_json/from_ubjson), [to_bjdata](https://json.nlohmann.me/api/basic_json/to_bjdata), [to_bson](https://json.nlohmann.me/api/basic_json/to_bson), [to_cbor](https://json.nlohmann.me/api/basic_json/to_cbor), [to_msgpack](https://json.nlohmann.me/api/basic_json/to_msgpack), [to_ubjson](https://json.nlohmann.me/api/basic_json/to_ubjson)
|
||||||
- **Non-member functions**: [operator<<](https://json.nlohmann.me/api/operator_ltlt/), [operator>>](https://json.nlohmann.me/api/operator_gtgt/), [to_string](https://json.nlohmann.me/api/basic_json/to_string)
|
- **Non-member functions**: [operator<<](https://json.nlohmann.me/api/operator_ltlt/), [operator>>](https://json.nlohmann.me/api/operator_gtgt/), [to_string](https://json.nlohmann.me/api/basic_json/to_string)
|
||||||
- **Literals**: [operator""_json](https://json.nlohmann.me/api/operator_literal_json)
|
- **Literals**: [operator""_json](https://json.nlohmann.me/api/operator_literal_json)
|
||||||
- **Helper classes**: [std::hash<basic_json>](https://json.nlohmann.me/api/basic_json/std_hash), [std::swap<basic_json>](https://json.nlohmann.me/api/basic_json/std_swap)
|
- **Helper classes**: [std::hash<basic_json>](https://json.nlohmann.me/api/basic_json/std_hash), [std::swap<basic_json>](https://json.nlohmann.me/api/basic_json/std_swap)
|
||||||
@@ -1110,9 +1110,9 @@ Other Important points:
|
|||||||
- When using `get<ENUM_TYPE>()`, undefined JSON values will default to the first pair specified in your map. Select this default pair carefully. If you desire an exception in this circumstance use `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT()` which behaves identically except for throwing an exception on unrecognized values.
|
- When using `get<ENUM_TYPE>()`, undefined JSON values will default to the first pair specified in your map. Select this default pair carefully. If you desire an exception in this circumstance use `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT()` which behaves identically except for throwing an exception on unrecognized values.
|
||||||
- If an enum or JSON value is specified more than once in your map, the first matching occurrence from the top of the map will be returned when converting to or from JSON.
|
- If an enum or JSON value is specified more than once in your map, the first matching occurrence from the top of the map will be returned when converting to or from JSON.
|
||||||
|
|
||||||
### Binary formats (BSON, CBOR, MessagePack, UBJSON, BJData, and BON8)
|
### Binary formats (BSON, CBOR, MessagePack, UBJSON, and BJData)
|
||||||
|
|
||||||
Though JSON is a ubiquitous data format, it is not a very compact format suitable for data exchange, for instance over a network. Hence, the library supports [BSON](https://bsonspec.org) (Binary JSON), [CBOR](https://cbor.io) (Concise Binary Object Representation), [MessagePack](https://msgpack.org), [UBJSON](https://ubjson.org) (Universal Binary JSON Specification), [BJData](https://neurojson.org/bjdata) (Binary JData), and [BON8](https://github.com/hikoworks/hikogui/blob/main/docs/BON8.md) (Binary Object Notation 8) to efficiently encode JSON values to byte vectors and to decode such vectors.
|
Though JSON is a ubiquitous data format, it is not a very compact format suitable for data exchange, for instance over a network. Hence, the library supports [BSON](https://bsonspec.org) (Binary JSON), [CBOR](https://cbor.io) (Concise Binary Object Representation), [MessagePack](https://msgpack.org), [UBJSON](https://ubjson.org) (Universal Binary JSON Specification) and [BJData](https://neurojson.org/bjdata) (Binary JData) to efficiently encode JSON values to byte vectors and to decode such vectors.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// create a JSON value
|
// create a JSON value
|
||||||
@@ -1149,14 +1149,6 @@ std::vector<std::uint8_t> v_ubjson = json::to_ubjson(j);
|
|||||||
|
|
||||||
// roundtrip
|
// roundtrip
|
||||||
json j_from_ubjson = json::from_ubjson(v_ubjson);
|
json j_from_ubjson = json::from_ubjson(v_ubjson);
|
||||||
|
|
||||||
// serialize to BON8
|
|
||||||
std::vector<std::uint8_t> v_bon8 = json::to_bon8(j);
|
|
||||||
|
|
||||||
// 0x88, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0xF9, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x90
|
|
||||||
|
|
||||||
// roundtrip
|
|
||||||
json j_from_bon8 = json::from_bon8(v_bon8);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The library also supports binary types from BSON, CBOR (byte strings), and MessagePack (bin, ext, fixext). They are stored by default as `std::vector<std::uint8_t>` to be processed outside the library.
|
The library also supports binary types from BSON, CBOR (byte strings), and MessagePack (bin, ext, fixext). They are stored by default as `std::vector<std::uint8_t>` to be processed outside the library.
|
||||||
|
|||||||
+6
-6
@@ -300,10 +300,10 @@ add_custom_target(ci_test_skiplibraryversioncheck
|
|||||||
# Disable thread-local storage.
|
# Disable thread-local storage.
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
# Without thread-local storage, copying and comparing cannot bound their
|
# Without thread-local storage, the copy constructor cannot bound its descent
|
||||||
# descent and handle every object and array without the call stack. Those paths
|
# and copies every object and array without the call stack. That path is
|
||||||
# are otherwise only reached by values nested deeper than the bound, so this
|
# otherwise only reached by values nested deeper than the bound, so this target
|
||||||
# target is what runs the whole test suite through them.
|
# is what runs the whole test suite through it.
|
||||||
add_custom_target(ci_test_no_thread_local
|
add_custom_target(ci_test_no_thread_local
|
||||||
COMMAND ${CMAKE_COMMAND}
|
COMMAND ${CMAKE_COMMAND}
|
||||||
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
||||||
@@ -542,7 +542,7 @@ add_custom_target(ci_infer
|
|||||||
|
|
||||||
add_custom_target(ci_offline_testdata
|
add_custom_target(ci_offline_testdata
|
||||||
COMMAND mkdir -p ${PROJECT_BINARY_DIR}/build_offline_testdata/test_data
|
COMMAND mkdir -p ${PROJECT_BINARY_DIR}/build_offline_testdata/test_data
|
||||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_offline_testdata/test_data && ${GIT_TOOL} clone -c advice.detachedHead=false --branch v3.2.0 https://github.com/nlohmann/json_test_data.git --quiet --depth 1
|
COMMAND cd ${PROJECT_BINARY_DIR}/build_offline_testdata/test_data && ${GIT_TOOL} clone -c advice.detachedHead=false --branch v3.1.0 https://github.com/nlohmann/json_test_data.git --quiet --depth 1
|
||||||
COMMAND ${CMAKE_COMMAND}
|
COMMAND ${CMAKE_COMMAND}
|
||||||
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
||||||
-DJSON_BuildTests=ON -DJSON_FastTests=ON -DJSON_TestDataDirectory=${PROJECT_BINARY_DIR}/build_offline_testdata/test_data/json_test_data
|
-DJSON_BuildTests=ON -DJSON_FastTests=ON -DJSON_TestDataDirectory=${PROJECT_BINARY_DIR}/build_offline_testdata/test_data/json_test_data
|
||||||
@@ -619,7 +619,7 @@ add_custom_target(ci_single_binaries
|
|||||||
add_custom_target(ci_benchmarks
|
add_custom_target(ci_benchmarks
|
||||||
COMMAND ${CMAKE_COMMAND}
|
COMMAND ${CMAKE_COMMAND}
|
||||||
-DCMAKE_BUILD_TYPE=Release -GNinja
|
-DCMAKE_BUILD_TYPE=Release -GNinja
|
||||||
-S${PROJECT_SOURCE_DIR}/tests/benchmarks -B${PROJECT_BINARY_DIR}/build_benchmarks
|
-S${PROJECT_SOURCE_DIR}/benchmarks -B${PROJECT_BINARY_DIR}/build_benchmarks
|
||||||
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_benchmarks --target json_benchmarks
|
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_benchmarks --target json_benchmarks
|
||||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_benchmarks && ./json_benchmarks
|
COMMAND cd ${PROJECT_BINARY_DIR}/build_benchmarks && ./json_benchmarks
|
||||||
COMMENT "Run benchmarks"
|
COMMENT "Run benchmarks"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
set(JSON_TEST_DATA_URL https://github.com/nlohmann/json_test_data)
|
set(JSON_TEST_DATA_URL https://github.com/nlohmann/json_test_data)
|
||||||
set(JSON_TEST_DATA_VERSION 3.2.0)
|
set(JSON_TEST_DATA_VERSION 3.1.0)
|
||||||
|
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ if(CMAKE_CROSSCOMPILING)
|
|||||||
endif()
|
endif()
|
||||||
if(NOT DEFINED LIBCPP_VERSION_OUTPUT_CACHED)
|
if(NOT DEFINED LIBCPP_VERSION_OUTPUT_CACHED)
|
||||||
try_run(RUN_RESULT_VAR COMPILE_RESULT_VAR
|
try_run(RUN_RESULT_VAR COMPILE_RESULT_VAR
|
||||||
"${CMAKE_BINARY_DIR}" SOURCES "${CMAKE_CURRENT_LIST_DIR}/detect_libcpp_version.cpp"
|
"${CMAKE_BINARY_DIR}" SOURCES "${CMAKE_SOURCE_DIR}/cmake/detect_libcpp_version.cpp"
|
||||||
RUN_OUTPUT_VARIABLE LIBCPP_VERSION_OUTPUT
|
RUN_OUTPUT_VARIABLE LIBCPP_VERSION_OUTPUT
|
||||||
COMPILE_OUTPUT_VARIABLE LIBCPP_VERSION_COMPILE_OUTPUT
|
COMPILE_OUTPUT_VARIABLE LIBCPP_VERSION_COMPILE_OUTPUT
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ string(APPEND CONTENT [=[
|
|||||||
],
|
],
|
||||||
includes = ["include"],
|
includes = ["include"],
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
|
alwayslink = True,
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_bjdata', 'Fu
|
|||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_bson', 'Function', 'api/basic_json/from_bson/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_bson', 'Function', 'api/basic_json/from_bson/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_cbor', 'Function', 'api/basic_json/from_cbor/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_cbor', 'Function', 'api/basic_json/from_cbor/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_msgpack', 'Function', 'api/basic_json/from_msgpack/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_msgpack', 'Function', 'api/basic_json/from_msgpack/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_bon8', 'Function', 'api/basic_json/from_bon8/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_ubjson', 'Function', 'api/basic_json/from_ubjson/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_ubjson', 'Function', 'api/basic_json/from_ubjson/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::front', 'Method', 'api/basic_json/front/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::front', 'Method', 'api/basic_json/front/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get', 'Method', 'api/basic_json/get/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get', 'Method', 'api/basic_json/get/index.html');
|
||||||
@@ -122,7 +121,6 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_bjdata', 'Func
|
|||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_bson', 'Function', 'api/basic_json/to_bson/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_bson', 'Function', 'api/basic_json/to_bson/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_cbor', 'Function', 'api/basic_json/to_cbor/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_cbor', 'Function', 'api/basic_json/to_cbor/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_msgpack', 'Function', 'api/basic_json/to_msgpack/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_msgpack', 'Function', 'api/basic_json/to_msgpack/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_bon8', 'Function', 'api/basic_json/to_bon8/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_string', 'Method', 'api/basic_json/to_string/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_string', 'Method', 'api/basic_json/to_string/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_ubjson', 'Function', 'api/basic_json/to_ubjson/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_ubjson', 'Function', 'api/basic_json/to_ubjson/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::value', 'Method', 'api/basic_json/value/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::value', 'Method', 'api/basic_json/value/index.html');
|
||||||
@@ -173,7 +171,6 @@ INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: BJData', 'Gui
|
|||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: BSON', 'Guide', 'features/binary_formats/bson/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: BSON', 'Guide', 'features/binary_formats/bson/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: CBOR', 'Guide', 'features/binary_formats/cbor/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: CBOR', 'Guide', 'features/binary_formats/cbor/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: MessagePack', 'Guide', 'features/binary_formats/messagepack/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: MessagePack', 'Guide', 'features/binary_formats/messagepack/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: BON8', 'Guide', 'features/binary_formats/bon8/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: UBJSON', 'Guide', 'features/binary_formats/ubjson/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: UBJSON', 'Guide', 'features/binary_formats/ubjson/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Values', 'Guide', 'features/binary_values/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Values', 'Guide', 'features/binary_values/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('Comments', 'Guide', 'features/comments/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('Comments', 'Guide', 'features/comments/index.html');
|
||||||
|
|||||||
@@ -104,7 +104,6 @@ Linear in the size of the input.
|
|||||||
- [from_msgpack](from_msgpack.md) create a JSON value from an input in MessagePack format
|
- [from_msgpack](from_msgpack.md) create a JSON value from an input in MessagePack format
|
||||||
- [from_bson](from_bson.md) create a JSON value from an input in BSON format
|
- [from_bson](from_bson.md) create a JSON value from an input in BSON format
|
||||||
- [from_ubjson](from_ubjson.md) create a JSON value from an input in UBJSON format
|
- [from_ubjson](from_ubjson.md) create a JSON value from an input in UBJSON format
|
||||||
- [from_bon8](from_bon8.md) create a JSON value from an input in BON8 format
|
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -1,108 +0,0 @@
|
|||||||
# <small>nlohmann::basic_json::</small>from_bon8
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// (1)
|
|
||||||
template<typename InputType>
|
|
||||||
static basic_json from_bon8(InputType&& i,
|
|
||||||
const bool strict = true,
|
|
||||||
const bool allow_exceptions = true);
|
|
||||||
// (2)
|
|
||||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
|
||||||
static basic_json from_bon8(IteratorType first, SentinelType last,
|
|
||||||
const bool strict = true,
|
|
||||||
const bool allow_exceptions = true);
|
|
||||||
```
|
|
||||||
|
|
||||||
Deserializes a given input to a JSON value using the BON8 (Binary Object Notation 8) serialization format.
|
|
||||||
|
|
||||||
1. Reads from a compatible input.
|
|
||||||
2. Reads from an iterator range, or an iterator and a sentinel of a different type (C++20 ranges support).
|
|
||||||
|
|
||||||
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/bon8.md).
|
|
||||||
|
|
||||||
## Template parameters
|
|
||||||
|
|
||||||
`InputType`
|
|
||||||
: A compatible input, for instance:
|
|
||||||
|
|
||||||
- an `std::istream` object
|
|
||||||
- a `FILE` pointer
|
|
||||||
- a C-style array of characters
|
|
||||||
- a pointer to a null-terminated string of single byte characters
|
|
||||||
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
|
|
||||||
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
|
|
||||||
|
|
||||||
`IteratorType`
|
|
||||||
: a compatible iterator type
|
|
||||||
|
|
||||||
`SentinelType`
|
|
||||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
|
||||||
|
|
||||||
- a custom sentinel type for C++20 ranges
|
|
||||||
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
|
||||||
|
|
||||||
## Parameters
|
|
||||||
|
|
||||||
`i` (in)
|
|
||||||
: an input in BON8 format convertible to an input adapter
|
|
||||||
|
|
||||||
`first` (in)
|
|
||||||
: iterator to the start of the input
|
|
||||||
|
|
||||||
`last` (in)
|
|
||||||
: iterator to the end of the input, or a sentinel value that compares equal to the end iterator with `operator!=`
|
|
||||||
|
|
||||||
`strict` (in)
|
|
||||||
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
|
|
||||||
|
|
||||||
`allow_exceptions` (in)
|
|
||||||
: whether to throw exceptions in case of a parse error (optional, `#!cpp true` by default)
|
|
||||||
|
|
||||||
## Return value
|
|
||||||
|
|
||||||
deserialized JSON value; in case of a parse error and `allow_exceptions` set to `#!cpp false`, the return value will be
|
|
||||||
`value_t::discarded`. The latter can be checked with [`is_discarded`](is_discarded.md).
|
|
||||||
|
|
||||||
## Exception safety
|
|
||||||
|
|
||||||
Strong guarantee: if an exception is thrown, there are no changes in the JSON value.
|
|
||||||
|
|
||||||
## Exceptions
|
|
||||||
|
|
||||||
- Throws [parse_error.110](../../home/exceptions.md#jsonexceptionparse_error110) if the given input ends prematurely or
|
|
||||||
the end of the file was not reached when `strict` was set to true
|
|
||||||
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if a parse error occurs, for instance
|
|
||||||
an invalid byte, a string that is not valid UTF-8, or an object key that is not a string
|
|
||||||
|
|
||||||
## Complexity
|
|
||||||
|
|
||||||
Linear in the size of the input.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
??? example
|
|
||||||
|
|
||||||
The example shows the deserialization of a byte vector in BON8 format to a JSON value.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
--8<-- "examples/from_bon8.cpp"
|
|
||||||
```
|
|
||||||
|
|
||||||
Output:
|
|
||||||
|
|
||||||
```json
|
|
||||||
--8<-- "examples/from_bon8.output"
|
|
||||||
```
|
|
||||||
|
|
||||||
## See also
|
|
||||||
|
|
||||||
- [to_bon8](to_bon8.md) create a BON8 serialization of a JSON value
|
|
||||||
- [from_cbor](from_cbor.md) create a JSON value from an input in CBOR format
|
|
||||||
- [from_msgpack](from_msgpack.md) create a JSON value from an input in MessagePack format
|
|
||||||
- [from_bson](from_bson.md) create a JSON value from an input in BSON format
|
|
||||||
- [from_ubjson](from_ubjson.md) create a JSON value from an input in UBJSON format
|
|
||||||
- [from_bjdata](from_bjdata.md) create a JSON value from an input in BJData format
|
|
||||||
|
|
||||||
## Version history
|
|
||||||
|
|
||||||
- Added in version 3.13.0.
|
|
||||||
@@ -104,7 +104,6 @@ Linear in the size of the input.
|
|||||||
- [from_msgpack](from_msgpack.md) for the related MessagePack format
|
- [from_msgpack](from_msgpack.md) for the related MessagePack format
|
||||||
- [from_ubjson](from_ubjson.md) for the related UBJSON format
|
- [from_ubjson](from_ubjson.md) for the related UBJSON format
|
||||||
- [from_bjdata](from_bjdata.md) for the related BJData format
|
- [from_bjdata](from_bjdata.md) for the related BJData format
|
||||||
- [from_bon8](from_bon8.md) for the related BON8 format
|
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,6 @@ Linear in the size of the input.
|
|||||||
- [from_bson](from_bson.md) create a JSON value from an input in BSON format
|
- [from_bson](from_bson.md) create a JSON value from an input in BSON format
|
||||||
- [from_ubjson](from_ubjson.md) create a JSON value from an input in UBJSON format
|
- [from_ubjson](from_ubjson.md) create a JSON value from an input in UBJSON format
|
||||||
- [from_bjdata](from_bjdata.md) create a JSON value from an input in BJData format
|
- [from_bjdata](from_bjdata.md) create a JSON value from an input in BJData format
|
||||||
- [from_bon8](from_bon8.md) create a JSON value from an input in BON8 format
|
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -103,7 +103,6 @@ Linear in the size of the input.
|
|||||||
- [from_bson](from_bson.md) create a JSON value from an input in BSON format
|
- [from_bson](from_bson.md) create a JSON value from an input in BSON format
|
||||||
- [from_ubjson](from_ubjson.md) create a JSON value from an input in UBJSON format
|
- [from_ubjson](from_ubjson.md) create a JSON value from an input in UBJSON format
|
||||||
- [from_bjdata](from_bjdata.md) create a JSON value from an input in BJData format
|
- [from_bjdata](from_bjdata.md) create a JSON value from an input in BJData format
|
||||||
- [from_bon8](from_bon8.md) create a JSON value from an input in BON8 format
|
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -104,7 +104,6 @@ Linear in the size of the input.
|
|||||||
- [from_msgpack](from_msgpack.md) create a JSON value from an input in MessagePack format
|
- [from_msgpack](from_msgpack.md) create a JSON value from an input in MessagePack format
|
||||||
- [from_bson](from_bson.md) create a JSON value from an input in BSON format
|
- [from_bson](from_bson.md) create a JSON value from an input in BSON format
|
||||||
- [from_bjdata](from_bjdata.md) create a JSON value from an input in BJData format
|
- [from_bjdata](from_bjdata.md) create a JSON value from an input in BJData format
|
||||||
- [from_bon8](from_bon8.md) create a JSON value from an input in BON8 format
|
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -21,10 +21,6 @@ This overload is chosen if:
|
|||||||
- `ValueType` is not `basic_json`,
|
- `ValueType` is not `basic_json`,
|
||||||
- `json_serializer<ValueType>` has a `from_json()` method of the form `void from_json(const basic_json&, ValueType&)`
|
- `json_serializer<ValueType>` has a `from_json()` method of the form `void from_json(const basic_json&, ValueType&)`
|
||||||
|
|
||||||
`v` must not be `const`. Passing a `const` object is a compile-time error. For types such as arithmetic types, enums,
|
|
||||||
and C arrays, the error is a `static_assert` that names the problem. For other types, the overload is not viable, and
|
|
||||||
the compiler reports that no matching `get_to` was found.
|
|
||||||
|
|
||||||
## Template parameters
|
## Template parameters
|
||||||
|
|
||||||
`ValueType`
|
`ValueType`
|
||||||
@@ -71,4 +67,3 @@ Depends on the `json_serializer<ValueType>::from_json()` implementation.
|
|||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Since version 3.3.0.
|
- Since version 3.3.0.
|
||||||
- Added a `static_assert` with a clear message for `const` arguments in version 3.13.0.
|
|
||||||
|
|||||||
@@ -290,13 +290,11 @@ Access to the JSON value
|
|||||||
### Binary formats
|
### Binary formats
|
||||||
|
|
||||||
- [**from_bjdata**](from_bjdata.md) (_static_) - create a JSON value from an input in BJData format
|
- [**from_bjdata**](from_bjdata.md) (_static_) - create a JSON value from an input in BJData format
|
||||||
- [**from_bon8**](from_bon8.md) (_static_) - create a JSON value from an input in BON8 format
|
|
||||||
- [**from_bson**](from_bson.md) (_static_) - create a JSON value from an input in BSON format
|
- [**from_bson**](from_bson.md) (_static_) - create a JSON value from an input in BSON format
|
||||||
- [**from_cbor**](from_cbor.md) (_static_) - create a JSON value from an input in CBOR format
|
- [**from_cbor**](from_cbor.md) (_static_) - create a JSON value from an input in CBOR format
|
||||||
- [**from_msgpack**](from_msgpack.md) (_static_) - create a JSON value from an input in MessagePack format
|
- [**from_msgpack**](from_msgpack.md) (_static_) - create a JSON value from an input in MessagePack format
|
||||||
- [**from_ubjson**](from_ubjson.md) (_static_) - create a JSON value from an input in UBJSON format
|
- [**from_ubjson**](from_ubjson.md) (_static_) - create a JSON value from an input in UBJSON format
|
||||||
- [**to_bjdata**](to_bjdata.md) (_static_) - create a BJData serialization of a given JSON value
|
- [**to_bjdata**](to_bjdata.md) (_static_) - create a BJData serialization of a given JSON value
|
||||||
- [**to_bon8**](to_bon8.md) (_static_) - create a BON8 serialization of a given JSON value
|
|
||||||
- [**to_bson**](to_bson.md) (_static_) - create a BSON serialization of a given JSON value
|
- [**to_bson**](to_bson.md) (_static_) - create a BSON serialization of a given JSON value
|
||||||
- [**to_cbor**](to_cbor.md) (_static_) - create a CBOR serialization of a given JSON value
|
- [**to_cbor**](to_cbor.md) (_static_) - create a CBOR serialization of a given JSON value
|
||||||
- [**to_msgpack**](to_msgpack.md) (_static_) - create a MessagePack serialization of a given JSON value
|
- [**to_msgpack**](to_msgpack.md) (_static_) - create a MessagePack serialization of a given JSON value
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ enum class input_format_t {
|
|||||||
msgpack,
|
msgpack,
|
||||||
ubjson,
|
ubjson,
|
||||||
bson,
|
bson,
|
||||||
bjdata,
|
bjdata
|
||||||
bon8
|
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -32,9 +31,6 @@ bson
|
|||||||
bjdata
|
bjdata
|
||||||
: BJData (Binary JData)
|
: BJData (Binary JData)
|
||||||
|
|
||||||
bon8
|
|
||||||
: BON8 (Binary Object Notation 8)
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ class parse_error : public exception;
|
|||||||
```
|
```
|
||||||
|
|
||||||
The library throws this exception when a parse error occurs. Parse errors can occur during the deserialization of
|
The library throws this exception when a parse error occurs. Parse errors can occur during the deserialization of
|
||||||
JSON text, BJData, BON8, BSON, CBOR, MessagePack, UBJSON, as well as when using JSON Patch.
|
JSON text, BSON, CBOR, MessagePack, UBJSON, as well as when using JSON Patch.
|
||||||
|
|
||||||
Member `byte` holds the byte index of the last read character in the input file (see note below).
|
Member `byte` holds the byte index of the last read character in the input file (see note below).
|
||||||
|
|
||||||
|
|||||||
@@ -65,14 +65,11 @@ The SAX event lister must follow the interface of [`json_sax`](../json_sax/index
|
|||||||
: SAX event listener (must not be null)
|
: SAX event listener (must not be null)
|
||||||
|
|
||||||
`format` (in)
|
`format` (in)
|
||||||
: the format to parse (JSON, BJData, BON8, BSON, CBOR, MessagePack, or UBJSON) (optional, `input_format_t::json` by
|
: the format to parse (JSON, CBOR, MessagePack, or UBJSON) (optional, `input_format_t::json` by default), see
|
||||||
default), see
|
|
||||||
[`input_format_t`](input_format_t.md) for more information
|
[`input_format_t`](input_format_t.md) for more information
|
||||||
|
|
||||||
`strict` (in)
|
`strict` (in)
|
||||||
: whether the input has to be consumed completely (optional, `#!cpp true` by default); when `#!cpp false` and the
|
: whether the input has to be consumed completely (optional, `#!cpp true` by default)
|
||||||
input is a `#!cpp std::istream`, the character that terminates a number is consumed unless
|
|
||||||
[`JSON_PRECISE_STREAM_POSITION`](../macros/json_precise_stream_position.md) is defined to `1`; see [`operator>>`](../operator_gtgt.md#notes)
|
|
||||||
|
|
||||||
`ignore_comments` (in)
|
`ignore_comments` (in)
|
||||||
: whether comments should be ignored and treated like whitespace (`#!cpp true`) or yield a parse error
|
: whether comments should be ignored and treated like whitespace (`#!cpp true`) or yield a parse error
|
||||||
@@ -139,8 +136,6 @@ A UTF-8 byte order mark is silently ignored.
|
|||||||
- Added `ignore_trailing_commas` in version 3.13.0.
|
- Added `ignore_trailing_commas` in version 3.13.0.
|
||||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||||
- 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
|
|
||||||
after the parsed value when `strict` is `#!cpp false`.
|
|
||||||
|
|
||||||
!!! warning "Deprecation"
|
!!! warning "Deprecation"
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,6 @@ Linear in the size of the JSON value `j`.
|
|||||||
- [to_msgpack](to_msgpack.md) create a MessagePack serialization of a JSON value
|
- [to_msgpack](to_msgpack.md) create a MessagePack serialization of a JSON value
|
||||||
- [to_bson](to_bson.md) create a BSON serialization of a JSON value
|
- [to_bson](to_bson.md) create a BSON serialization of a JSON value
|
||||||
- [to_ubjson](to_ubjson.md) create a UBJSON serialization of a JSON value
|
- [to_ubjson](to_ubjson.md) create a UBJSON serialization of a JSON value
|
||||||
- [to_bon8](to_bon8.md) create a BON8 serialization of a JSON value
|
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
# <small>nlohmann::basic_json::</small>to_bon8
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// (1)
|
|
||||||
static std::vector<std::uint8_t> to_bon8(const basic_json& j);
|
|
||||||
|
|
||||||
// (2)
|
|
||||||
static void to_bon8(const basic_json& j, detail::output_adapter<std::uint8_t> o);
|
|
||||||
static void to_bon8(const basic_json& j, detail::output_adapter<char> o);
|
|
||||||
```
|
|
||||||
|
|
||||||
Serializes a given JSON value `j` to a byte vector using the BON8 (Binary Object Notation 8) serialization format. BON8
|
|
||||||
is a compact binary serialization format that stores strings as UTF-8 without a length prefix.
|
|
||||||
|
|
||||||
1. Returns a byte vector containing the BON8 serialization.
|
|
||||||
2. Writes the BON8 serialization to an output adapter.
|
|
||||||
|
|
||||||
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/bon8.md).
|
|
||||||
|
|
||||||
## Parameters
|
|
||||||
|
|
||||||
`j` (in)
|
|
||||||
: JSON value to serialize
|
|
||||||
|
|
||||||
`o` (in)
|
|
||||||
: output adapter to write serialization to
|
|
||||||
|
|
||||||
## Return value
|
|
||||||
|
|
||||||
1. BON8 serialization as a byte vector
|
|
||||||
2. (none)
|
|
||||||
|
|
||||||
## Exception safety
|
|
||||||
|
|
||||||
Strong guarantee: if an exception is thrown, there are no changes in the JSON value `j`, which is never modified.
|
|
||||||
With (2), the bytes written before the exception remain in the output adapter.
|
|
||||||
|
|
||||||
## Exceptions
|
|
||||||
|
|
||||||
- Throws [out_of_range.407](../../home/exceptions.md#jsonexceptionout_of_range407) if `j` contains an unsigned integer
|
|
||||||
above 9223372036854775807, which BON8 cannot represent
|
|
||||||
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if `j` contains a string that is not
|
|
||||||
valid UTF-8
|
|
||||||
|
|
||||||
## Complexity
|
|
||||||
|
|
||||||
Linear in the size of the JSON value `j`.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
??? example
|
|
||||||
|
|
||||||
The example shows the serialization of a JSON value to a byte vector in BON8 format.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
--8<-- "examples/to_bon8.cpp"
|
|
||||||
```
|
|
||||||
|
|
||||||
Output:
|
|
||||||
|
|
||||||
```json
|
|
||||||
--8<-- "examples/to_bon8.output"
|
|
||||||
```
|
|
||||||
|
|
||||||
## See also
|
|
||||||
|
|
||||||
- [from_bon8](from_bon8.md) create a JSON value from an input in BON8 format
|
|
||||||
- [to_cbor](to_cbor.md) create a CBOR serialization of a JSON value
|
|
||||||
- [to_msgpack](to_msgpack.md) create a MessagePack serialization of a JSON value
|
|
||||||
- [to_bson](to_bson.md) create a BSON serialization of a JSON value
|
|
||||||
- [to_ubjson](to_ubjson.md) create a UBJSON serialization of a JSON value
|
|
||||||
- [to_bjdata](to_bjdata.md) create a BJData serialization of a JSON value
|
|
||||||
|
|
||||||
## Version history
|
|
||||||
|
|
||||||
- Added in version 3.13.0.
|
|
||||||
@@ -46,8 +46,9 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
Linear in the size of the JSON value `j`. The length prefixes of all nested documents and arrays are computed in one
|
Proportional to the size of the JSON value `j` multiplied by its maximum nesting
|
||||||
pass before anything is written.
|
depth, `O(n × d)`. BSON length prefixes are computed recursively before nested
|
||||||
|
values are written.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
@@ -72,9 +73,7 @@ pass before anything is written.
|
|||||||
- [to_msgpack](to_msgpack.md) create a MessagePack serialization of a JSON value
|
- [to_msgpack](to_msgpack.md) create a MessagePack serialization of a JSON value
|
||||||
- [to_ubjson](to_ubjson.md) create a UBJSON serialization of a JSON value
|
- [to_ubjson](to_ubjson.md) create a UBJSON serialization of a JSON value
|
||||||
- [to_bjdata](to_bjdata.md) create a BJData serialization of a JSON value
|
- [to_bjdata](to_bjdata.md) create a BJData serialization of a JSON value
|
||||||
- [to_bon8](to_bon8.md) create a BON8 serialization of a JSON value
|
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 3.4.0.
|
- Added in version 3.4.0.
|
||||||
- Linear in the size of `j`, and no longer limited by the call stack for deeply nested values, since version 3.13.0.
|
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ Linear in the size of the JSON value `j`.
|
|||||||
- [to_bson](to_bson.md) create a BSON serialization of a JSON value
|
- [to_bson](to_bson.md) create a BSON serialization of a JSON value
|
||||||
- [to_ubjson](to_ubjson.md) create a UBJSON serialization of a JSON value
|
- [to_ubjson](to_ubjson.md) create a UBJSON serialization of a JSON value
|
||||||
- [to_bjdata](to_bjdata.md) create a BJData serialization of a JSON value
|
- [to_bjdata](to_bjdata.md) create a BJData serialization of a JSON value
|
||||||
- [to_bon8](to_bon8.md) create a BON8 serialization of a JSON value
|
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ Linear in the size of the JSON value `j`.
|
|||||||
- [to_bson](to_bson.md) create a BSON serialization of a JSON value
|
- [to_bson](to_bson.md) create a BSON serialization of a JSON value
|
||||||
- [to_ubjson](to_ubjson.md) create a UBJSON serialization of a JSON value
|
- [to_ubjson](to_ubjson.md) create a UBJSON serialization of a JSON value
|
||||||
- [to_bjdata](to_bjdata.md) create a BJData serialization of a JSON value
|
- [to_bjdata](to_bjdata.md) create a BJData serialization of a JSON value
|
||||||
- [to_bon8](to_bon8.md) create a BON8 serialization of a JSON value
|
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,6 @@ Linear in the size of the JSON value `j`.
|
|||||||
- [to_msgpack](to_msgpack.md) create a MessagePack serialization of a JSON value
|
- [to_msgpack](to_msgpack.md) create a MessagePack serialization of a JSON value
|
||||||
- [to_bson](to_bson.md) create a BSON serialization of a JSON value
|
- [to_bson](to_bson.md) create a BSON serialization of a JSON value
|
||||||
- [to_bjdata](to_bjdata.md) create a BJData serialization of a JSON value
|
- [to_bjdata](to_bjdata.md) create a BJData serialization of a JSON value
|
||||||
- [to_bon8](to_bon8.md) create a BON8 serialization of a JSON value
|
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ header. See also the [macro overview page](../../features/macros.md).
|
|||||||
|
|
||||||
## Parsing
|
## Parsing
|
||||||
|
|
||||||
- [**JSON_PRECISE_STREAM_POSITION**](json_precise_stream_position.md) - opt in to leaving an input stream positioned
|
|
||||||
right after a parsed number
|
|
||||||
- [**JSON_STRICT_NUL_HANDLING**](json_strict_nul_handling.md) - opt in to rejecting a NUL byte in the input instead of
|
- [**JSON_STRICT_NUL_HANDLING**](json_strict_nul_handling.md) - opt in to rejecting a NUL byte in the input instead of
|
||||||
treating it as end of input
|
treating it as end of input
|
||||||
|
|
||||||
|
|||||||
@@ -7,16 +7,16 @@
|
|||||||
When defined, the library does not use `#!cpp thread_local` storage. This is relevant for the few environments whose
|
When defined, the library does not use `#!cpp thread_local` storage. This is relevant for the few environments whose
|
||||||
toolchain does not support it.
|
toolchain does not support it.
|
||||||
|
|
||||||
Copying a value and comparing two values both descend into the first levels by letting the containers copy or compare
|
The copy constructor copies the first levels of a value by copying the containers, which copy their elements, and
|
||||||
themselves, and finish whatever is nested deeper than that without the call stack, so that neither can exhaust the stack
|
completes whatever is nested deeper than that without the call stack, so that copying a value cannot exhaust the stack
|
||||||
however deeply the values are nested. Each counts the levels it has descended into in a `#!cpp thread_local` variable, as
|
however deeply it is nested. It counts the levels it has descended into in a `#!cpp thread_local` variable, as a counter
|
||||||
a counter shared between threads would be raced.
|
shared between threads would be raced.
|
||||||
|
|
||||||
Without those counters, no descent can be bounded safely, so objects and arrays are copied and compared without the call
|
Without that counter, no descent can be bounded safely, so objects and arrays are copied without the call stack right
|
||||||
stack right away. Both keep working exactly as they do otherwise - the same values come out, the same comparisons hold,
|
away. Copying keeps working exactly as it does otherwise - the same values come out, and deeply nested values are copied
|
||||||
and deeply nested values are handled just as safely - but both are slower, because the containers no longer copy or
|
just as safely - but copying is slower, because the containers no longer copy themselves. Copying the benchmark
|
||||||
compare themselves. Copying the benchmark documents takes 9% (`canada.json`) to 34% (`twitter.json`) longer, and
|
documents takes 9% (`canada.json`) to 34% (`twitter.json`) longer; values built mostly from objects are affected the
|
||||||
comparing two equal ones 10% (`citm_catalog.json`) to 90% (`canada.json`) longer.
|
most.
|
||||||
|
|
||||||
## Default definition
|
## Default definition
|
||||||
|
|
||||||
@@ -28,7 +28,6 @@ By default, `#!cpp JSON_NO_THREAD_LOCAL` is not defined.
|
|||||||
|
|
||||||
The library defines it by itself for Clang targeting MinGW, which does not survive the `#!cpp thread_local` storage:
|
The library defines it by itself for Clang targeting MinGW, which does not survive the `#!cpp thread_local` storage:
|
||||||
copying a value segfaults there, with both old and current Clang versions, while GCC targeting MinGW is unaffected.
|
copying a value segfaults there, with both old and current Clang versions, while GCC targeting MinGW is unaffected.
|
||||||
Copying and comparing fall back to working without the call stack there, as they do whenever the macro is defined.
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|||||||
@@ -1,131 +0,0 @@
|
|||||||
# JSON_PRECISE_STREAM_POSITION
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#define JSON_PRECISE_STREAM_POSITION /* value */
|
|
||||||
```
|
|
||||||
|
|
||||||
When defined to `1`, [`operator>>`](../operator_gtgt.md) and [`sax_parse`](../basic_json/sax_parse.md) with
|
|
||||||
`strict = false` leave a `#!cpp std::istream` positioned right after the parsed value for every value type. By default,
|
|
||||||
the character that terminates a number is consumed as well.
|
|
||||||
|
|
||||||
The macro only affects reading from a `#!cpp std::istream` when the rest of the stream is not required to be consumed.
|
|
||||||
[`parse`](../basic_json/parse.md), [`accept`](../basic_json/accept.md), and all other inputs (strings, iterators,
|
|
||||||
containers, `#!cpp FILE*`) are never affected.
|
|
||||||
|
|
||||||
## Default definition
|
|
||||||
|
|
||||||
The default value is `0` (disabled — existing behavior is preserved).
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#define JSON_PRECISE_STREAM_POSITION 0
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
!!! note "Background"
|
|
||||||
|
|
||||||
A number is the only JSON value whose end can be detected solely by reading the character that follows it. By
|
|
||||||
default, that character is consumed and not put back, so the stream is left one byte too far after a number, and
|
|
||||||
only after a number:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
std::istringstream input("1true");
|
|
||||||
json j;
|
|
||||||
input >> j; // j == 1, but the stream now starts at "rue"
|
|
||||||
```
|
|
||||||
|
|
||||||
With this macro, the character is only looked at and left in the stream, so the stream starts at `true`. This
|
|
||||||
does not require the stream buffer to support putting a character back.
|
|
||||||
|
|
||||||
This was not changed unconditionally, because code can depend on the consumed character, even unknowingly (see
|
|
||||||
[#5340](https://github.com/nlohmann/json/issues/5340)). Both of the following work by default only because the
|
|
||||||
character after each number is swallowed, and behave differently with this macro:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
std::istringstream input("1,2,3");
|
|
||||||
json j1, j2, j3;
|
|
||||||
input >> j1 >> j2 >> j3; // default: 1, 2, 3
|
|
||||||
// with the macro: throws parse_error.101 at the ','
|
|
||||||
```
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
std::istringstream input("42\nfoo");
|
|
||||||
json j;
|
|
||||||
std::string line;
|
|
||||||
input >> j;
|
|
||||||
std::getline(input, line); // default: "foo"
|
|
||||||
// with the macro: "" (like after reading an int with >>)
|
|
||||||
```
|
|
||||||
|
|
||||||
In both cases, the behavior with the macro is what you already get today when the value is not a number: `"a","b"`
|
|
||||||
fails at the `,`, and `std::getline` after `{}` returns an empty string. This macro offers an opt-in path to
|
|
||||||
the consistent behavior ahead of version 4.0.0, where it is planned to become the default.
|
|
||||||
|
|
||||||
!!! warning "Opt-in only"
|
|
||||||
|
|
||||||
This macro must be defined **before** including `<nlohmann/json.hpp>`. Defining it after the include has no
|
|
||||||
effect.
|
|
||||||
|
|
||||||
!!! note "ABI compatibility"
|
|
||||||
|
|
||||||
The value of this macro is encoded in the [namespace](../../features/namespace.md) (tag `_psp`), 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.
|
|
||||||
|
|
||||||
!!! tip "Workaround without the macro"
|
|
||||||
|
|
||||||
Separate the values in the stream with whitespace. The character consumed after a number is then the separator,
|
|
||||||
and whitespace before the next value is skipped anyway.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
??? example "Default behavior (macro not defined)"
|
|
||||||
|
|
||||||
Without the macro, the character after a number is consumed:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
std::istringstream input("1true");
|
|
||||||
json j1, j2;
|
|
||||||
input >> j1; // j1 == 1
|
|
||||||
input >> j2; // throws parse_error.101: the stream now starts at "rue"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
??? example "Opt-in precise stream position (macro defined to 1)"
|
|
||||||
|
|
||||||
With the macro, the stream is positioned right after the number:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#define JSON_PRECISE_STREAM_POSITION 1
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
std::istringstream input("1true");
|
|
||||||
json j1, j2;
|
|
||||||
input >> j1; // j1 == 1
|
|
||||||
input >> j2; // j2 == true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## See also
|
|
||||||
|
|
||||||
- [**operator>>**](../operator_gtgt.md) - deserialize from stream
|
|
||||||
- [**sax_parse**](../basic_json/sax_parse.md) - generate SAX events
|
|
||||||
|
|
||||||
## Version history
|
|
||||||
|
|
||||||
- Added in version 3.13.0.
|
|
||||||
- Planned to become the default (with the macro removed) in version 4.0.0.
|
|
||||||
@@ -11,9 +11,9 @@ The macro only affects the JSON text parser ([`parse`](../basic_json/parse.md),
|
|||||||
[`sax_parse`](../basic_json/sax_parse.md), and [`operator>>`](../operator_gtgt.md)). There are three cases where a NUL
|
[`sax_parse`](../basic_json/sax_parse.md), and [`operator>>`](../operator_gtgt.md)). There are three cases where a NUL
|
||||||
byte is still not rejected:
|
byte is still not rejected:
|
||||||
|
|
||||||
- The binary formats ([`from_bjdata`](../basic_json/from_bjdata.md), [`from_bon8`](../basic_json/from_bon8.md),
|
- The binary formats ([`from_bjdata`](../basic_json/from_bjdata.md), [`from_bson`](../basic_json/from_bson.md),
|
||||||
[`from_bson`](../basic_json/from_bson.md), [`from_cbor`](../basic_json/from_cbor.md),
|
[`from_cbor`](../basic_json/from_cbor.md), [`from_msgpack`](../basic_json/from_msgpack.md),
|
||||||
[`from_msgpack`](../basic_json/from_msgpack.md), [`from_ubjson`](../basic_json/from_ubjson.md)) are never affected: there, `0x00` is ordinary data.
|
[`from_ubjson`](../basic_json/from_ubjson.md)) are never affected: there, `0x00` is ordinary data.
|
||||||
- A bare `const char*` pointer has no length of its own, so its length is still determined with `strlen()`. The first
|
- A bare `const char*` pointer has no length of its own, so its length is still determined with `strlen()`. The first
|
||||||
NUL byte therefore still marks the end of the input, and nothing after it is read.
|
NUL byte therefore still marks the end of the input, and nothing after it is read.
|
||||||
- One trailing `'\0'` at the end of a `char` array (e.g., a string literal) is trimmed; see the warning below.
|
- One trailing `'\0'` at the end of a `char` array (e.g., a string literal) is trimmed; see the warning below.
|
||||||
@@ -65,12 +65,6 @@ The default value is `0` (disabled — existing behavior is preserved).
|
|||||||
for CBOR or MessagePack, are never affected by this trimming; their full extent - including a genuine trailing
|
for CBOR or MessagePack, are never affected by this trimming; their full extent - including a genuine trailing
|
||||||
`0x00` - is always preserved, in both states of this macro.
|
`0x00` - is always preserved, in both states of this macro.
|
||||||
|
|
||||||
!!! note "ABI compatibility"
|
|
||||||
|
|
||||||
The value of this macro is encoded in the [namespace](../../features/namespace.md) (tag `_snul`), 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.
|
|
||||||
|
|
||||||
!!! tip "Workaround without the macro"
|
!!! tip "Workaround without the macro"
|
||||||
|
|
||||||
To reject a NUL byte without enabling this macro, trim your input yourself before calling `parse()`:
|
To reject a NUL byte without enabling this macro, trim your input yourself before calling `parse()`:
|
||||||
|
|||||||
@@ -57,8 +57,7 @@ Summary:
|
|||||||
: name of the base type (class, struct) `type` is derived from
|
: name of the base type (class, struct) `type` is derived from
|
||||||
|
|
||||||
`member` (in)
|
`member` (in)
|
||||||
: name of the member variable to serialize/deserialize; up to 63 members can be given as a comma-separated
|
: name of the member variable to serialize/deserialize; up to 63 members can be given as a comma-separated list
|
||||||
list, which may also be empty
|
|
||||||
|
|
||||||
## Default definition
|
## Default definition
|
||||||
|
|
||||||
@@ -128,20 +127,6 @@ void to_json(BasicJsonType& j, const B& b) {
|
|||||||
- Macros 4, 5, and 6 have the same prerequisites of [NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE](nlohmann_define_type_non_intrusive.md).
|
- Macros 4, 5, and 6 have the same prerequisites of [NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE](nlohmann_define_type_non_intrusive.md).
|
||||||
- Serialization/deserialization of base types must be defined.
|
- Serialization/deserialization of base types must be defined.
|
||||||
|
|
||||||
!!! info "Derived types without own members"
|
|
||||||
|
|
||||||
The member list may be empty. The macro then generates a `to_json`/`from_json` pair that only delegates to
|
|
||||||
the base type, so `type` serializes exactly like `base_type`:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
struct derived : base
|
|
||||||
{
|
|
||||||
NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(derived, base)
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
The `WITH_NAMES` variants do not support this.
|
|
||||||
|
|
||||||
!!! warning "Implementation limits"
|
!!! warning "Implementation limits"
|
||||||
|
|
||||||
See Implementation limits for [NLOHMANN_DEFINE_TYPE_INTRUSIVE](nlohmann_define_type_intrusive.md) and
|
See Implementation limits for [NLOHMANN_DEFINE_TYPE_INTRUSIVE](nlohmann_define_type_intrusive.md) and
|
||||||
|
|||||||
@@ -33,8 +33,7 @@ Summary:
|
|||||||
: name of the type (class, struct) to serialize/deserialize
|
: name of the type (class, struct) to serialize/deserialize
|
||||||
|
|
||||||
`member` (in)
|
`member` (in)
|
||||||
: name of the member variable to serialize/deserialize; up to 63 members can be given as a comma-separated
|
: name of the member variable to serialize/deserialize; up to 63 members can be given as a comma-separated list
|
||||||
list, which may also be empty
|
|
||||||
|
|
||||||
## Default definition
|
## Default definition
|
||||||
|
|
||||||
@@ -59,20 +58,6 @@ See the examples below for the concrete generated code.
|
|||||||
|
|
||||||
[GetNonDefNonCopy]: ../../features/arbitrary_types.md#how-can-i-use-get-for-non-default-constructiblenon-copyable-types
|
[GetNonDefNonCopy]: ../../features/arbitrary_types.md#how-can-i-use-get-for-non-default-constructiblenon-copyable-types
|
||||||
|
|
||||||
!!! info "Types without members"
|
|
||||||
|
|
||||||
The member list may be empty. The macro then generates a `to_json` that produces an empty JSON object
|
|
||||||
`#!json {}`, and a `from_json` that reads no members:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
struct marker
|
|
||||||
{
|
|
||||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(marker)
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
The `WITH_NAMES` variants do not support this.
|
|
||||||
|
|
||||||
!!! warning "Implementation limits"
|
!!! warning "Implementation limits"
|
||||||
|
|
||||||
- The current implementation is limited to at most 63 member variables. If you want to serialize/deserialize types
|
- The current implementation is limited to at most 63 member variables. If you want to serialize/deserialize types
|
||||||
|
|||||||
@@ -33,8 +33,7 @@ Summary:
|
|||||||
: name of the type (class, struct) to serialize/deserialize
|
: name of the type (class, struct) to serialize/deserialize
|
||||||
|
|
||||||
`member` (in)
|
`member` (in)
|
||||||
: name of the (public) member variable to serialize/deserialize; up to 63 members can be given as a
|
: name of the (public) member variable to serialize/deserialize; up to 63 members can be given as a comma-separated list
|
||||||
comma-separated list, which may also be empty
|
|
||||||
|
|
||||||
## Default definition
|
## Default definition
|
||||||
|
|
||||||
@@ -60,18 +59,6 @@ See the examples below for the concrete generated code.
|
|||||||
|
|
||||||
[GetNonDefNonCopy]: ../../features/arbitrary_types.md#how-can-i-use-get-for-non-default-constructiblenon-copyable-types
|
[GetNonDefNonCopy]: ../../features/arbitrary_types.md#how-can-i-use-get-for-non-default-constructiblenon-copyable-types
|
||||||
|
|
||||||
!!! info "Types without members"
|
|
||||||
|
|
||||||
The member list may be empty. The macro then generates a `to_json` that produces an empty JSON object
|
|
||||||
`#!json {}`, and a `from_json` that reads no members:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
struct marker {};
|
|
||||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(marker)
|
|
||||||
```
|
|
||||||
|
|
||||||
The `WITH_NAMES` variants do not support this.
|
|
||||||
|
|
||||||
!!! warning "Implementation limits"
|
!!! warning "Implementation limits"
|
||||||
|
|
||||||
- The current implementation is limited to at most 63 member variables. If you want to serialize/deserialize types
|
- The current implementation is limited to at most 63 member variables. If you want to serialize/deserialize types
|
||||||
|
|||||||
@@ -67,9 +67,7 @@ input >> j2; // parses the next value
|
|||||||
Only numbers are affected. Values ending in a self-delimiting character do not read past themselves, so
|
Only numbers are affected. Values ending in a self-delimiting character do not read past themselves, so
|
||||||
`truefalse`, `[1][2]`, `{"a":1}{"b":2}`, and `"a""b"` can be read back to back without a separator.
|
`truefalse`, `[1][2]`, `{"a":1}{"b":2}`, and `"a""b"` can be read back to back without a separator.
|
||||||
|
|
||||||
Define [`JSON_PRECISE_STREAM_POSITION`](macros/json_precise_stream_position.md) to `1` to leave the terminating character in the stream
|
This is tracked in [#5340](https://github.com/nlohmann/json/issues/5340).
|
||||||
instead, so that the stream is positioned right after the value for every value type and no separator is
|
|
||||||
needed. This is tracked in [#5340](https://github.com/nlohmann/json/issues/5340).
|
|
||||||
|
|
||||||
Note that reading concatenated values does **not** work for [JSON Lines](../features/parsing/json_lines.md)
|
Note that reading concatenated values does **not** work for [JSON Lines](../features/parsing/json_lines.md)
|
||||||
(newline-delimited JSON) input -- see that page for why and for the recommended alternative.
|
(newline-delimited JSON) input -- see that page for why and for the recommended alternative.
|
||||||
@@ -109,12 +107,9 @@ being read.
|
|||||||
- [parse](basic_json/parse.md) - deserialize from a compatible input
|
- [parse](basic_json/parse.md) - deserialize from a compatible input
|
||||||
- [`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
|
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 1.0.0.
|
- Added in version 1.0.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.
|
||||||
- `JSON_PRECISE_STREAM_POSITION` added in version 3.13.0 to optionally leave the character that terminates a number in
|
|
||||||
the stream; planned to become the default in version 4.0.0.
|
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
# Assurance case
|
|
||||||
|
|
||||||
This page argues why the library meets its security requirements. It describes the threats the library faces, where the
|
|
||||||
trust boundaries lie, and how the library's design and the [quality assurance](quality_assurance.md) counter these
|
|
||||||
threats. To report a vulnerability, see the [security policy](security_policy.md).
|
|
||||||
|
|
||||||
## Threat model
|
|
||||||
|
|
||||||
The library parses, stores, and serializes JSON values in memory. It does not open network connections, does not open
|
|
||||||
files (it only reads from streams or `std::FILE*` handles that the caller has already opened), does not read environment
|
|
||||||
variables, and does not implement cryptography or handle credentials.
|
|
||||||
|
|
||||||
The primary threat is therefore **untrusted input**: JSON text or binary data (BJData, BSON, CBOR, MessagePack, UBJSON)
|
|
||||||
that an attacker controls, passed to [`parse`](../api/basic_json/parse.md), [`accept`](../api/basic_json/accept.md),
|
|
||||||
[`sax_parse`](../api/basic_json/sax_parse.md), or one of the `from_*` functions such as
|
|
||||||
[`from_cbor`](../api/basic_json/from_cbor.md). Such input may try to
|
|
||||||
|
|
||||||
- make the library read or write out of bounds (malformed lengths, truncated input, invalid UTF-8),
|
|
||||||
- trigger undefined behavior (integer overflow in sizes or numbers, invalid casts),
|
|
||||||
- exhaust memory (huge announced sizes), or
|
|
||||||
- exhaust the call stack (deeply nested arrays and objects).
|
|
||||||
|
|
||||||
## Trust boundaries
|
|
||||||
|
|
||||||
- **Untrusted:** all serialized input read by the parser, the SAX interface, and the binary readers. The library must
|
|
||||||
handle every possible input by either producing a value or throwing a [`parse_error`](../home/exceptions.md#parse-errors)
|
|
||||||
(or returning `false` when exceptions are disabled for the call).
|
|
||||||
- **Trusted:** the C++ code that calls the library. Calling a function with violated preconditions, for instance
|
|
||||||
accessing an array with [`operator[]`](../api/basic_json/operator%5B%5D.md) out of range, is a programming error and
|
|
||||||
not a security boundary. Such preconditions are checked with [runtime assertions](../features/assertions.md) in debug
|
|
||||||
builds; functions such as [`at`](../api/basic_json/at.md) offer checked access with exceptions.
|
|
||||||
|
|
||||||
## Secure design
|
|
||||||
|
|
||||||
- **Strict parsing.** The parser accepts exactly the JSON grammar of [RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259).
|
|
||||||
Extensions such as [comments](../features/comments.md) and [trailing commas](../features/trailing_commas.md) must be
|
|
||||||
enabled explicitly. Invalid UTF-8 is rejected.
|
|
||||||
- **Errors are reported, not ignored.** Malformed input results in a [`parse_error`](../home/exceptions.md#parse-errors)
|
|
||||||
with the byte position of the error. Binary readers do not trust announced sizes: strings and binary values grow
|
|
||||||
only as bytes are actually read, arrays reserve at most a fixed number of elements up front, and sizes that no
|
|
||||||
container can hold are rejected.
|
|
||||||
- **Memory is owned by values.** Each `basic_json` value owns its content, and there is no manual memory management in
|
|
||||||
user code. The destructor does not recurse, so destroying a deeply nested value does not exhaust the stack.
|
|
||||||
- **Bounded recursion.** The JSON parser and the binary readers keep their state in explicit stacks instead of
|
|
||||||
recursing per nesting level. Operations that walk a value, such as [`dump`](../api/basic_json/dump.md), copying,
|
|
||||||
comparison, hashing, and [`merge_patch`](../api/basic_json/merge_patch.md), recurse only up to a fixed depth and
|
|
||||||
continue with an explicit stack below it. Some operations, such as [`diff`](../api/basic_json/diff.md),
|
|
||||||
[`flatten`](../api/basic_json/flatten.md), and the binary writers, still recurse once per nesting level; work on them
|
|
||||||
is in progress. Applications that process untrusted input can limit its nesting depth with a
|
|
||||||
[parser callback](../features/parsing/parser_callbacks.md).
|
|
||||||
- **Invariants are checked.** The class invariant (for instance, that the pointer for the stored type is never null) is
|
|
||||||
checked with runtime assertions throughout the test suite.
|
|
||||||
|
|
||||||
## Common weaknesses
|
|
||||||
|
|
||||||
The following table maps the relevant classes of the [Common Weakness Enumeration](https://cwe.mitre.org) to the
|
|
||||||
measures that counter them. The measures are described in detail in [Quality assurance](quality_assurance.md).
|
|
||||||
|
|
||||||
| Weakness | Countermeasures |
|
|
||||||
|---------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|
|
|
||||||
| Out-of-bounds read/write ([CWE-125](https://cwe.mitre.org/data/definitions/125.html), [CWE-787](https://cwe.mitre.org/data/definitions/787.html)) | bounds checks on all reads from the input; AddressSanitizer and Valgrind on the test suite; OSS-Fuzz |
|
|
||||||
| Integer overflow ([CWE-190](https://cwe.mitre.org/data/definitions/190.html)) | UndefinedBehaviorSanitizer with integer overflow detection; Clang-Tidy; Cppcheck |
|
|
||||||
| Use after free, double free ([CWE-416](https://cwe.mitre.org/data/definitions/416.html), [CWE-415](https://cwe.mitre.org/data/definitions/415.html)) | ownership of all memory by values; AddressSanitizer and Valgrind; Clang Static Analyzer |
|
|
||||||
| Memory leaks ([CWE-401](https://cwe.mitre.org/data/definitions/401.html)) | Valgrind (Memcheck) on the test suite |
|
|
||||||
| Uncontrolled recursion ([CWE-674](https://cwe.mitre.org/data/definitions/674.html)) | iterative parser, binary readers, and destructor; bounded recursion in value operations; tests with deeply nested inputs |
|
|
||||||
| Uncontrolled resource consumption ([CWE-400](https://cwe.mitre.org/data/definitions/400.html)) | allocations based on announced sizes are capped; OSS-Fuzz with memory limits |
|
|
||||||
| Undefined behavior in general ([CWE-758](https://cwe.mitre.org/data/definitions/758.html)) | UndefinedBehaviorSanitizer; runtime assertions; Clang-Tidy, Cppcheck, Clang Static Analyzer, Infer |
|
|
||||||
|
|
||||||
In addition, every line of the library is covered by the unit tests, and all parsers are fuzz-tested around the clock
|
|
||||||
by [OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/json).
|
|
||||||
@@ -91,31 +91,6 @@ activities include (but are not limited to):
|
|||||||
Users who continue to engage with the project and its community will often find themselves becoming more and more
|
Users who continue to engage with the project and its community will often find themselves becoming more and more
|
||||||
involved. Such users may then go on to become contributors, as described above.
|
involved. Such users may then go on to become contributors, as described above.
|
||||||
|
|
||||||
## Access to project resources
|
|
||||||
|
|
||||||
The project's resources are the [GitHub repository](https://github.com/nlohmann/json) with its settings, CI workflows
|
|
||||||
and secrets, and the documentation at [json.nlohmann.me](https://json.nlohmann.me), which is built and deployed from
|
|
||||||
the repository. Currently, the project lead is the only person with write or admin access to them.
|
|
||||||
|
|
||||||
### Granting access
|
|
||||||
|
|
||||||
Write or admin access is only granted by the project lead, and only to a contributor whose track record in the project
|
|
||||||
the project lead has reviewed first. The role is assigned manually and is the lowest one that is needed for the task.
|
|
||||||
Access is removed when it is no longer needed. GitHub requires two-factor authentication for everyone who can modify the
|
|
||||||
repository.
|
|
||||||
|
|
||||||
### Secrets
|
|
||||||
|
|
||||||
The CI workflows mostly use the token that GitHub creates for each workflow run. It is read-only by default, and each
|
|
||||||
workflow requests only the additional permissions it needs. The few other credentials, such as the token for
|
|
||||||
[Semgrep](https://semgrep.dev), are stored as encrypted GitHub Actions secrets:
|
|
||||||
|
|
||||||
- Only people with admin access can create, change, or delete them. Their values cannot be read back, not even by
|
|
||||||
admins.
|
|
||||||
- They are not passed to workflows that run for pull requests from forks.
|
|
||||||
- They must never be committed to the repository or printed in logs.
|
|
||||||
- They are rotated whenever someone with admin access leaves the project, and immediately if a leak is suspected.
|
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
All participants in the community are encouraged to provide support for new users within the project management
|
All participants in the community are encouraged to provide support for new users within the project management
|
||||||
|
|||||||
@@ -5,6 +5,4 @@
|
|||||||
- [Contribution Guidelines](contribution_guidelines.md) - guidelines how to contribute to this project
|
- [Contribution Guidelines](contribution_guidelines.md) - guidelines how to contribute to this project
|
||||||
- [Governance](governance.md) - the governance model of this project
|
- [Governance](governance.md) - the governance model of this project
|
||||||
- [Quality Assurance](quality_assurance.md) - how the quality of this project is assured
|
- [Quality Assurance](quality_assurance.md) - how the quality of this project is assured
|
||||||
- [Roadmap](roadmap.md) - what the project will and will not do
|
|
||||||
- [Security Policy](security_policy.md) - the security policy of the project
|
- [Security Policy](security_policy.md) - the security policy of the project
|
||||||
- [Assurance Case](assurance_case.md) - why the library meets its security requirements
|
|
||||||
|
|||||||
@@ -164,9 +164,6 @@ Note: Some modern features (like C++20 ranges or filesystem support) may be disa
|
|||||||
- [x] The parser is tested against extensive correctness suites for JSON compliance.
|
- [x] The parser is tested against extensive correctness suites for JSON compliance.
|
||||||
- [x] In addition, the library is continuously fuzz-tested at [OSS-Fuzz](https://google.github.io/oss-fuzz/) where the
|
- [x] In addition, the library is continuously fuzz-tested at [OSS-Fuzz](https://google.github.io/oss-fuzz/) where the
|
||||||
library is checked against billions of inputs.
|
library is checked against billions of inputs.
|
||||||
- [x] Every crash reported by OSS-Fuzz is fixed together with a unit test that reproduces it, and the fix references
|
|
||||||
the OSS-Fuzz issue. The round-trip checks of the fuzzer drivers are also part of the unit tests. See the
|
|
||||||
[fuzz testing documentation](https://github.com/nlohmann/json/blob/develop/tests/fuzzing.md#handling-oss-fuzz-reports).
|
|
||||||
|
|
||||||
## Static analysis
|
## Static analysis
|
||||||
|
|
||||||
@@ -200,25 +197,6 @@ Note: Some modern features (like C++20 ranges or filesystem support) may be disa
|
|||||||
- [x] The test suite is executed with [Sanitizers](https://github.com/google/sanitizers) (address sanitizer, undefined
|
- [x] The test suite is executed with [Sanitizers](https://github.com/google/sanitizers) (address sanitizer, undefined
|
||||||
behavior sanitizer, integer overflow detection, nullability violations).
|
behavior sanitizer, integer overflow detection, nullability violations).
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
!!! success "Requirement: No vulnerable dependencies"
|
|
||||||
|
|
||||||
The library has no dependencies besides the C++ standard library. The tools used to build, test, and document it
|
|
||||||
are kept free of known vulnerabilities.
|
|
||||||
|
|
||||||
- [x] GitHub Actions are pinned to a commit hash, and the Python packages used by the documentation and the tools are
|
|
||||||
pinned to exact versions.
|
|
||||||
- [x] [Dependabot](https://docs.github.com/en/code-security/dependabot) checks these dependencies daily and proposes
|
|
||||||
updates as pull requests.
|
|
||||||
- [x] Every pull request is checked with the
|
|
||||||
[dependency review action](https://github.com/actions/dependency-review-action). A pull request that adds a
|
|
||||||
dependency with a known vulnerability of any severity fails this check and is not merged.
|
|
||||||
- [x] Vulnerability alerts for dependencies are fixed or dismissed with a documented reason before the next release.
|
|
||||||
No release is made while such an alert is open.
|
|
||||||
- [x] Third-party code included in the repository for testing, such as [doctest](https://github.com/doctest/doctest),
|
|
||||||
is updated manually.
|
|
||||||
|
|
||||||
## Style check
|
## Style check
|
||||||
|
|
||||||
!!! success "Requirement: Common code style"
|
!!! success "Requirement: Common code style"
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
# Roadmap
|
|
||||||
|
|
||||||
This page describes what the project intends to do, and what it does not intend to do, over the next year. Concrete
|
|
||||||
work items are tracked in the [GitHub milestones](https://github.com/nlohmann/json/milestones) and the
|
|
||||||
[issue tracker](https://github.com/nlohmann/json/issues).
|
|
||||||
|
|
||||||
## What the project will do
|
|
||||||
|
|
||||||
- **Keep the C++11 baseline.** The library will continue to compile with every
|
|
||||||
[supported C++11 compiler](https://github.com/nlohmann/json/blob/develop/README.md#supported-compilers). Features of
|
|
||||||
later standards are only used when they are guarded by the `JSON_HAS_CPP_*` macros.
|
|
||||||
- **Stay conformant to JSON.** The parser and serializer follow [RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259).
|
|
||||||
Extensions such as [comments](../features/comments.md) or [trailing commas](../features/trailing_commas.md) remain
|
|
||||||
opt-in.
|
|
||||||
- **Keep the 3.x public API stable.** Releases follow [semantic versioning](https://semver.org). Changes that would
|
|
||||||
break existing code are only added behind a feature macro, so users can opt in and test their code before a next
|
|
||||||
major release.
|
|
||||||
- **Support a broad range of compilers and platforms.** The [CI](quality_assurance.md) keeps testing old and new
|
|
||||||
versions of GCC, Clang, MSVC, and other compilers on Linux, macOS, and Windows.
|
|
||||||
- **Keep the quality assurance up.** Every change keeps the test coverage at 100%, passes the static and dynamic
|
|
||||||
analysis, and is fuzz-tested by OSS-Fuzz, see [Quality assurance](quality_assurance.md).
|
|
||||||
- **Harden the library against hostile input.** Handling deeply nested values without exhausting the call stack is
|
|
||||||
ongoing work.
|
|
||||||
- **Fix bugs and security issues** reported through the issue tracker and the [security policy](security_policy.md).
|
|
||||||
|
|
||||||
## What the project will not do
|
|
||||||
|
|
||||||
- **Break the public API of version 3.x.** See the
|
|
||||||
[contribution guidelines](https://github.com/nlohmann/json/blob/develop/.github/CONTRIBUTING.md#break-the-public-api)
|
|
||||||
for what counts as a breaking change.
|
|
||||||
- **Require a newer C++ standard than C++11.**
|
|
||||||
- **Break JSON conformance** or enable non-standard extensions by default.
|
|
||||||
- **Add dependencies** or require a build step. The library remains header-only, and the single header
|
|
||||||
`json.hpp` remains a complete distribution.
|
|
||||||
- **Trade simplicity for speed or memory efficiency.** Performance improvements are welcome, but the library is not
|
|
||||||
meant to compete with the fastest JSON libraries, see [Design goals](../home/design_goals.md).
|
|
||||||
|
|
||||||
## Version 4.0
|
|
||||||
|
|
||||||
There is no decision yet on whether or when a version 4.0 with breaking changes will be released. Proposals that need
|
|
||||||
a major version, for instance stricter type conversions, are collected in issue
|
|
||||||
[#3453](https://github.com/nlohmann/json/issues/3453). Until then, such changes are only added as opt-in behavior
|
|
||||||
behind feature macros.
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
// create byte vector
|
|
||||||
std::vector<std::uint8_t> v = {0x89, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74,
|
|
||||||
0xf9, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0xff,
|
|
||||||
0x42, 0x4f, 0x4e, 0x38, 0xff, 0x73, 0x63, 0x68,
|
|
||||||
0x65, 0x6d, 0x61, 0x90
|
|
||||||
};
|
|
||||||
|
|
||||||
// deserialize it with BON8
|
|
||||||
json j = json::from_bon8(v);
|
|
||||||
|
|
||||||
// print the deserialized JSON value
|
|
||||||
std::cout << std::setw(2) << j << std::endl;
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"compact": true,
|
|
||||||
"format": "BON8",
|
|
||||||
"schema": 0
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
|
||||||
using namespace nlohmann::literals;
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
// create a JSON value
|
|
||||||
json j = R"({"compact": true, "format": "BON8", "schema": 0})"_json;
|
|
||||||
|
|
||||||
// serialize it to BON8
|
|
||||||
std::vector<std::uint8_t> v = json::to_bon8(j);
|
|
||||||
|
|
||||||
// print the vector content
|
|
||||||
for (auto& byte : v)
|
|
||||||
{
|
|
||||||
std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)byte << " ";
|
|
||||||
}
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
0x89 0x63 0x6f 0x6d 0x70 0x61 0x63 0x74 0xf9 0x66 0x6f 0x72 0x6d 0x61 0x74 0xff 0x42 0x4f 0x4e 0x38 0xff 0x73 0x63 0x68 0x65 0x6d 0x61 0x90
|
|
||||||
@@ -215,24 +215,6 @@ For _derived_ classes and structs, use the following macros
|
|||||||
nlohmann::ordered_json j = p; // keys appear in declaration order: name, address, age
|
nlohmann::ordered_json j = p; // keys appear in declaration order: name, address, age
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! note "Zero-member types"
|
|
||||||
|
|
||||||
All 12 `NLOHMANN_DEFINE_TYPE_*`/`NLOHMANN_DEFINE_DERIVED_TYPE_*` macros (excluding the `WITH_NAMES` variants)
|
|
||||||
also accept types with no member variables to serialize, producing/accepting an empty JSON object `{}`
|
|
||||||
(or, for the derived-type macros, just the base class's own JSON representation):
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
namespace ns {
|
|
||||||
struct marker {
|
|
||||||
bool operator==(const marker&) const { return true; }
|
|
||||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(marker)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ns::marker m{};
|
|
||||||
nlohmann::json j = m; // {}
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! note "No macro for non-default-constructible types"
|
!!! note "No macro for non-default-constructible types"
|
||||||
|
|
||||||
There is currently no `NLOHMANN_DEFINE_TYPE_*`-style macro for types that are not
|
There is currently no `NLOHMANN_DEFINE_TYPE_*`-style macro for types that are not
|
||||||
|
|||||||
@@ -208,16 +208,6 @@ The library maps BJData types to JSON value types as follows:
|
|||||||
|
|
||||||
The mapping is **complete** in the sense that any BJData value can be converted to a JSON value.
|
The mapping is **complete** in the sense that any BJData value can be converted to a JSON value.
|
||||||
|
|
||||||
!!! info "Round trips"
|
|
||||||
|
|
||||||
A value returned by [`from_bjdata`](../../api/basic_json/from_bjdata.md) can be serialized with
|
|
||||||
[`to_bjdata`](../../api/basic_json/to_bjdata.md) using any combination of options and parsed back into an equal
|
|
||||||
value, and serializing that value again with the same options produces the same bytes. The exception is binary
|
|
||||||
values: they are only written as an optimized binary array (`[$B`) if Draft 3 is enabled and both `use_size` and
|
|
||||||
`use_type` are set. Otherwise, they are written as arrays of integers and parsed back as such (see the notes on
|
|
||||||
binary values above), and serializing such an array again may choose different, but equally valid, type markers.
|
|
||||||
The bytes can then differ, but parsing them again yields the same value.
|
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
|||||||
@@ -1,159 +0,0 @@
|
|||||||
# BON8
|
|
||||||
|
|
||||||
BON8 (Binary Object Notation 8) is a compact binary serialization format for JSON values. It uses the byte values that
|
|
||||||
cannot begin a UTF-8 character as type markers, so strings are stored as plain UTF-8 without a length prefix: a string
|
|
||||||
ends at the first byte that cannot continue it. Integers from -10 to 39, `true`, `false`, `null`, and the floating-point
|
|
||||||
values -1.0, 0.0, and 1.0 take a single byte, and arrays and objects with up to four elements need no terminator.
|
|
||||||
|
|
||||||
!!! abstract "References"
|
|
||||||
|
|
||||||
- [BON8 specification](https://github.com/hikoworks/hikogui/blob/main/docs/BON8.md)
|
|
||||||
- [Reference implementation](https://github.com/hikoworks/hikogui/blob/main/src/hikogui/codec/BON8.hpp) in HikoGUI
|
|
||||||
|
|
||||||
## Serialization
|
|
||||||
|
|
||||||
The library uses the following mapping from JSON values types to BON8 types according to the BON8 specification:
|
|
||||||
|
|
||||||
| JSON value type | value/range | BON8 type | first byte |
|
|
||||||
|-----------------|----------------------------------------------|-------------------------------|------------|
|
|
||||||
| null | `null` | null | 0xFA |
|
|
||||||
| boolean | `true` | true | 0xF9 |
|
|
||||||
| boolean | `false` | false | 0xF8 |
|
|
||||||
| number_integer | -9223372036854775808..-2147483649 | int64 | 0x8D |
|
|
||||||
| number_integer | -2147483648..-33818507 | int32 | 0x8C |
|
|
||||||
| number_integer | -33818506..-264075 | 4-byte negative integer | 0xF0..0xF7 |
|
|
||||||
| number_integer | -264074..-1931 | 3-byte negative integer | 0xE0..0xEF |
|
|
||||||
| number_integer | -1930..-11 | 2-byte negative integer | 0xC2..0xDF |
|
|
||||||
| number_integer | -10..-1 | 1-byte negative integer | 0xB8..0xC1 |
|
|
||||||
| number_integer | 0..39 | 1-byte positive integer | 0x90..0xB7 |
|
|
||||||
| number_integer | 40..3879 | 2-byte positive integer | 0xC2..0xDF |
|
|
||||||
| number_integer | 3880..528167 | 3-byte positive integer | 0xE0..0xEF |
|
|
||||||
| number_integer | 528168..67637031 | 4-byte positive integer | 0xF0..0xF7 |
|
|
||||||
| number_integer | 67637032..2147483647 | int32 | 0x8C |
|
|
||||||
| number_integer | 2147483648..9223372036854775807 | int64 | 0x8D |
|
|
||||||
| number_unsigned | 0..39 | 1-byte positive integer | 0x90..0xB7 |
|
|
||||||
| number_unsigned | 40..3879 | 2-byte positive integer | 0xC2..0xDF |
|
|
||||||
| number_unsigned | 3880..528167 | 3-byte positive integer | 0xE0..0xEF |
|
|
||||||
| number_unsigned | 528168..67637031 | 4-byte positive integer | 0xF0..0xF7 |
|
|
||||||
| number_unsigned | 67637032..2147483647 | int32 | 0x8C |
|
|
||||||
| number_unsigned | 2147483648..9223372036854775807 | int64 | 0x8D |
|
|
||||||
| number_float | `-1.0` | -1.0 | 0xFB |
|
|
||||||
| number_float | `0.0` | 0.0 | 0xFC |
|
|
||||||
| number_float | `1.0` | 1.0 | 0xFD |
|
|
||||||
| number_float | *any other value representable by a float* | binary32 | 0x8E |
|
|
||||||
| number_float | *any value NOT representable by a float* | binary64 | 0x8F |
|
|
||||||
| string | *empty* | end of string | 0xFF |
|
|
||||||
| string | *non-empty* | UTF-8 string | 0x00..0x7F, 0xC2..0xF4 |
|
|
||||||
| array | *size*: 0..4 | array with count | 0x80..0x84 |
|
|
||||||
| array | *size*: 5 or more | array (terminated by 0xFE) | 0x85 |
|
|
||||||
| object | *size*: 0..4 | object with count | 0x86..0x8A |
|
|
||||||
| object | *size*: 5 or more | object (terminated by 0xFE) | 0x8B |
|
|
||||||
| binary | *size*: 0..4 | array with count | 0x80..0x84 |
|
|
||||||
| binary | *size*: 5 or more | array (terminated by 0xFE) | 0x85 |
|
|
||||||
|
|
||||||
An integer that takes 2 to 4 bytes starts with a UTF-8 lead byte (0xC2..0xF7) that is followed by a byte that cannot
|
|
||||||
continue a UTF-8 character: 0x00..0x7F for positive and 0xC0..0xFF for negative integers. A string is terminated by
|
|
||||||
0xFF only if it is empty, if another string follows it, or if it is the last value of the message; otherwise, the first
|
|
||||||
byte of the next value ends it.
|
|
||||||
|
|
||||||
!!! success "Complete mapping"
|
|
||||||
|
|
||||||
Except for the values listed below, any JSON value can be converted to a BON8 value.
|
|
||||||
|
|
||||||
Any BON8 output created by `to_bon8` can be successfully parsed by `from_bon8`.
|
|
||||||
|
|
||||||
!!! warning "Unsupported values"
|
|
||||||
|
|
||||||
The following values can **not** be converted to a BON8 value:
|
|
||||||
|
|
||||||
- unsigned integers above 9223372036854775807, because BON8 has no unsigned 64-bit integer type
|
|
||||||
([out_of_range.407](../../home/exceptions.md#jsonexceptionout_of_range407))
|
|
||||||
- strings that are not valid UTF-8, because the end of a string is determined from its encoding
|
|
||||||
([type_error.316](../../home/exceptions.md#jsonexceptiontype_error316))
|
|
||||||
|
|
||||||
!!! info "NaN/infinity handling"
|
|
||||||
|
|
||||||
`-0.0`, `Infinity`, and `-Infinity` are serialized as binary32 (type 0x8E, 5 bytes total). `NaN` is serialized as
|
|
||||||
the binary32 value 0x7F800001 that the specification recommends. This is in contrast to the
|
|
||||||
[dump](../../api/basic_json/dump.md) function which serializes NaN or Infinity to `null`.
|
|
||||||
|
|
||||||
!!! warning "Binary values"
|
|
||||||
|
|
||||||
BON8 has no binary type. Binary values are serialized as arrays of integers (0..255), so they are read back as
|
|
||||||
arrays. The subtype is not serialized.
|
|
||||||
|
|
||||||
!!! info "Canonical representation"
|
|
||||||
|
|
||||||
The output follows the specification's canonical representation rules: every value uses the shortest encoding,
|
|
||||||
floating-point numbers use binary32 whenever that loses no precision, and object keys are sorted by their UTF-8
|
|
||||||
code units. There are two exceptions:
|
|
||||||
|
|
||||||
- Strings are not normalized to Unicode Normalization Form C (NFC).
|
|
||||||
- Object keys are written in the order of the object type, which is sorted for `json`, but not for
|
|
||||||
[`ordered_json`](../../api/ordered_json.md).
|
|
||||||
|
|
||||||
??? example
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
--8<-- "examples/to_bon8.cpp"
|
|
||||||
```
|
|
||||||
|
|
||||||
Output:
|
|
||||||
|
|
||||||
```c
|
|
||||||
--8<-- "examples/to_bon8.output"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Deserialization
|
|
||||||
|
|
||||||
The library maps BON8 types to JSON value types as follows:
|
|
||||||
|
|
||||||
| BON8 type | JSON value type | first byte |
|
|
||||||
|-------------------------------|-----------------|------------------------|
|
|
||||||
| UTF-8 string | string | 0x00..0x7F |
|
|
||||||
| array with count | array | 0x80..0x84 |
|
|
||||||
| array (terminated by 0xFE) | array | 0x85 |
|
|
||||||
| object with count | object | 0x86..0x8A |
|
|
||||||
| object (terminated by 0xFE) | object | 0x8B |
|
|
||||||
| int32 | number_unsigned or number_integer | 0x8C |
|
|
||||||
| int64 | number_unsigned or number_integer | 0x8D |
|
|
||||||
| binary32 | number_float | 0x8E |
|
|
||||||
| binary64 | number_float | 0x8F |
|
|
||||||
| 1-byte positive integer | number_unsigned | 0x90..0xB7 |
|
|
||||||
| 1-byte negative integer | number_integer | 0xB8..0xC1 |
|
|
||||||
| UTF-8 string | string | 0xC2..0xF4, followed by 0x80..0xBF |
|
|
||||||
| 2- to 4-byte positive integer | number_unsigned | 0xC2..0xF7, followed by 0x00..0x7F |
|
|
||||||
| 2- to 4-byte negative integer | number_integer | 0xC2..0xF7, followed by 0xC0..0xFF |
|
|
||||||
| false | `false` | 0xF8 |
|
|
||||||
| true | `true` | 0xF9 |
|
|
||||||
| null | `null` | 0xFA |
|
|
||||||
| -1.0 | number_float | 0xFB |
|
|
||||||
| 0.0 | number_float | 0xFC |
|
|
||||||
| 1.0 | number_float | 0xFD |
|
|
||||||
| empty string | string | 0xFF |
|
|
||||||
|
|
||||||
Non-negative integers are read as number_unsigned, negative integers as number_integer.
|
|
||||||
|
|
||||||
!!! info
|
|
||||||
|
|
||||||
Values that do not use the canonical representation, such as integers with a longer encoding than necessary,
|
|
||||||
arrays and objects with up to four elements that are terminated by 0xFE, unsorted object keys, or a 0xFF after a
|
|
||||||
string that would also end without it, are accepted. A second 0xFF is not a terminator but an empty string.
|
|
||||||
|
|
||||||
Strings must be valid UTF-8, and the last string of a message must be terminated by 0xFF.
|
|
||||||
|
|
||||||
!!! info
|
|
||||||
|
|
||||||
Any BON8 output created by `to_bon8` can be successfully parsed by `from_bon8`.
|
|
||||||
|
|
||||||
??? example
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
--8<-- "examples/from_bon8.cpp"
|
|
||||||
```
|
|
||||||
|
|
||||||
Output:
|
|
||||||
|
|
||||||
```json
|
|
||||||
--8<-- "examples/from_bon8.output"
|
|
||||||
```
|
|
||||||
@@ -109,15 +109,6 @@ The library maps BSON record types to JSON value types as follows:
|
|||||||
If BSON input must be validated for strict specification compliance, validate it separately before passing it to
|
If BSON input must be validated for strict specification compliance, validate it separately before passing it to
|
||||||
`from_bson()`.
|
`from_bson()`.
|
||||||
|
|
||||||
!!! warning "UTF-8 validation of string values"
|
|
||||||
|
|
||||||
The BSON specification requires `string` values (type `0x02`) to be valid UTF-8. This library validates the
|
|
||||||
bytes of every such string at decode time and rejects ill-formed UTF-8 with a
|
|
||||||
[`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) exception (or, with `allow_exceptions`
|
|
||||||
set to `false`, a discarded value), rather than only failing later when the resulting value is dumped. Element
|
|
||||||
(key) names and `binary` values (type `0x05`) are unaffected and are never validated, since they are read
|
|
||||||
byte-by-byte as a C string, or are not required to hold text, respectively.
|
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
|||||||
@@ -176,16 +176,6 @@ The library maps CBOR types to JSON value types as follows:
|
|||||||
|
|
||||||
CBOR allows map keys of any type, whereas JSON only allows strings as keys in object values. Therefore, CBOR maps with keys other than UTF-8 strings are rejected.
|
CBOR allows map keys of any type, whereas JSON only allows strings as keys in object values. Therefore, CBOR maps with keys other than UTF-8 strings are rejected.
|
||||||
|
|
||||||
!!! warning "UTF-8 validation of text strings"
|
|
||||||
|
|
||||||
[RFC 8949, Section 3.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-3.1) requires CBOR text strings
|
|
||||||
(major type 3) to be valid UTF-8. This library validates the bytes of every text string (object keys included) at
|
|
||||||
decode time and rejects ill-formed UTF-8 with a
|
|
||||||
[`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) exception (or, with
|
|
||||||
`allow_exceptions` set to `false`, a discarded value), rather than only failing later when the resulting value is
|
|
||||||
dumped. Byte strings (major type 2) are unaffected and are never validated, since they are not required to hold
|
|
||||||
text.
|
|
||||||
|
|
||||||
!!! warning "Tagged items"
|
!!! warning "Tagged items"
|
||||||
|
|
||||||
Tagged items (0xC0..0xDB) will throw a parse error by default. They can be ignored by passing `cbor_tag_handler_t::ignore` to function `from_cbor`, in which case the tag is skipped and the enclosed data item is parsed on its own. They can be stored by passing `cbor_tag_handler_t::store` to function `from_cbor`. Note that no tag is ever interpreted: for instance, a text string tagged with tag 0 (date/time) stays a string.
|
Tagged items (0xC0..0xDB) will throw a parse error by default. They can be ignored by passing `cbor_tag_handler_t::ignore` to function `from_cbor`, in which case the tag is skipped and the enclosed data item is parsed on its own. They can be stored by passing `cbor_tag_handler_t::store` to function `from_cbor`. Note that no tag is ever interpreted: for instance, a text string tagged with tag 0 (date/time) stays a string.
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ Though JSON is a ubiquitous data format, it is not a very compact format suitabl
|
|||||||
a network. Hence, the library supports
|
a network. Hence, the library supports
|
||||||
|
|
||||||
- [BJData](bjdata.md) (Binary JData),
|
- [BJData](bjdata.md) (Binary JData),
|
||||||
- [BON8](bon8.md) (Binary Object Notation 8),
|
|
||||||
- [BSON](bson.md) (Binary JSON),
|
- [BSON](bson.md) (Binary JSON),
|
||||||
- [CBOR](cbor.md) (Concise Binary Object Representation),
|
- [CBOR](cbor.md) (Concise Binary Object Representation),
|
||||||
- [MessagePack](messagepack.md), and
|
- [MessagePack](messagepack.md), and
|
||||||
@@ -19,7 +18,6 @@ to efficiently encode JSON values to byte vectors and to decode such vectors.
|
|||||||
| Format | Serialization | Deserialization |
|
| Format | Serialization | Deserialization |
|
||||||
|-------------|-----------------------------------------------|----------------------------------------------|
|
|-------------|-----------------------------------------------|----------------------------------------------|
|
||||||
| BJData | complete | complete |
|
| BJData | complete | complete |
|
||||||
| BON8 | incomplete: no unsigned integers above int64 | complete |
|
|
||||||
| BSON | incomplete: top-level value must be an object | incomplete, but all JSON types are supported |
|
| BSON | incomplete: top-level value must be an object | incomplete, but all JSON types are supported |
|
||||||
| CBOR | complete | incomplete, but all JSON types are supported |
|
| CBOR | complete | incomplete, but all JSON types are supported |
|
||||||
| MessagePack | complete | complete |
|
| MessagePack | complete | complete |
|
||||||
@@ -30,7 +28,6 @@ to efficiently encode JSON values to byte vectors and to decode such vectors.
|
|||||||
| Format | Binary values | Binary subtypes |
|
| Format | Binary values | Binary subtypes |
|
||||||
|-------------|---------------|-----------------|
|
|-------------|---------------|-----------------|
|
||||||
| BJData | not supported | not supported |
|
| BJData | not supported | not supported |
|
||||||
| BON8 | not supported | not supported |
|
|
||||||
| BSON | supported | supported |
|
| BSON | supported | supported |
|
||||||
| CBOR | supported | supported |
|
| CBOR | supported | supported |
|
||||||
| MessagePack | supported | supported |
|
| MessagePack | supported | supported |
|
||||||
@@ -45,7 +42,6 @@ See [binary values](../binary_values.md) for more information.
|
|||||||
| BJData | 53.2 % | 91.1 % | 78.1 % | 96.6 % |
|
| BJData | 53.2 % | 91.1 % | 78.1 % | 96.6 % |
|
||||||
| BJData (size) | 58.6 % | 92.1 % | 86.7 % | 97.4 % |
|
| BJData (size) | 58.6 % | 92.1 % | 86.7 % | 97.4 % |
|
||||||
| BJData (size+type) | 58.6 % | 92.1 % | 86.5 % | 97.4 % |
|
| BJData (size+type) | 58.6 % | 92.1 % | 86.5 % | 97.4 % |
|
||||||
| BON8 | 50.5 % | 83.8 % | 63.5 % | 87.5 % |
|
|
||||||
| BSON | 85.8 % | 95.2 % | 95.8 % | 106.7 % |
|
| BSON | 85.8 % | 95.2 % | 95.8 % | 106.7 % |
|
||||||
| CBOR | 50.5 % | 86.3 % | 68.4 % | 88.0 % |
|
| CBOR | 50.5 % | 86.3 % | 68.4 % | 88.0 % |
|
||||||
| MessagePack | 50.5 % | 86.0 % | 68.5 % | 87.9 % |
|
| MessagePack | 50.5 % | 86.0 % | 68.5 % | 87.9 % |
|
||||||
|
|||||||
@@ -136,14 +136,6 @@ The library maps MessagePack types to JSON value types as follows:
|
|||||||
|
|
||||||
Any MessagePack output created by `to_msgpack` can be successfully parsed by `from_msgpack`.
|
Any MessagePack output created by `to_msgpack` can be successfully parsed by `from_msgpack`.
|
||||||
|
|
||||||
!!! warning "UTF-8 validation of string values"
|
|
||||||
|
|
||||||
The MessagePack specification requires `str` values (`fixstr`, `str 8`, `str 16`, `str 32`) to be valid UTF-8.
|
|
||||||
This library validates the bytes of every such string (object keys included) at decode time and rejects
|
|
||||||
ill-formed UTF-8 with a [`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) exception (or,
|
|
||||||
with `allow_exceptions` set to `false`, a discarded value), rather than only failing later when the resulting
|
|
||||||
value is dumped. `bin`/`ext`/`fixext` values are unaffected and are never validated, since they are not required
|
|
||||||
to hold text.
|
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
|
|
||||||
|
|||||||
@@ -187,41 +187,6 @@ as an array of uint8 values. The library implements this translation.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### BON8
|
|
||||||
|
|
||||||
[BON8](binary_formats/bon8.md) neither supports binary values nor subtypes. The library serializes binary values as an
|
|
||||||
array of integers.
|
|
||||||
|
|
||||||
??? example
|
|
||||||
|
|
||||||
Code:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// create a binary value of subtype 42 (will be ignored in BON8)
|
|
||||||
json j;
|
|
||||||
j["binary"] = json::binary({0xCA, 0xFE, 0xBA, 0xBE}, 42);
|
|
||||||
|
|
||||||
// convert to BON8
|
|
||||||
auto v = json::to_bon8(j);
|
|
||||||
```
|
|
||||||
|
|
||||||
`v` is a `std::vector<std::uint8_t>` with the following 16 elements:
|
|
||||||
|
|
||||||
```c
|
|
||||||
0x87 // object with 1 member
|
|
||||||
0x62 0x69 0x6E 0x61 0x72 0x79 // "binary"
|
|
||||||
0x84 // array with 4 elements
|
|
||||||
0xC3 0x22 0xC3 0x56 0xC3 0x12 0xC3 0x16 // content (each byte as a 2-byte integer)
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that the subtype is lost, and deserializing `v` would yield the following value:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"binary": [202, 254, 186, 190]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### BSON
|
### BSON
|
||||||
|
|
||||||
[BSON](binary_formats/bson.md) supports binary values and subtypes. If a subtype is given, it is used and added as an
|
[BSON](binary_formats/bson.md) supports binary values and subtypes. If a subtype is given, it is used and added as an
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ C++ types, and finally serialize it again.
|
|||||||
- [Serialization](serialization.md) — turn a value back into JSON text with [`dump`](../api/basic_json/dump.md),
|
- [Serialization](serialization.md) — turn a value back into JSON text with [`dump`](../api/basic_json/dump.md),
|
||||||
including pretty-printing and handling of non-ASCII and invalid UTF-8.
|
including pretty-printing and handling of non-ASCII and invalid UTF-8.
|
||||||
- [Binary formats](binary_formats/index.md) — encode values more compactly as
|
- [Binary formats](binary_formats/index.md) — encode values more compactly as
|
||||||
[BJData](binary_formats/bjdata.md), [BON8](binary_formats/bon8.md), [BSON](binary_formats/bson.md),
|
[BJData](binary_formats/bjdata.md), [BSON](binary_formats/bson.md), [CBOR](binary_formats/cbor.md),
|
||||||
[CBOR](binary_formats/cbor.md), [MessagePack](binary_formats/messagepack.md), or [UBJSON](binary_formats/ubjson.md).
|
[MessagePack](binary_formats/messagepack.md), or [UBJSON](binary_formats/ubjson.md).
|
||||||
- [Binary values](binary_values.md) — store and exchange raw byte sequences.
|
- [Binary values](binary_values.md) — store and exchange raw byte sequences.
|
||||||
|
|
||||||
## How values are stored and configured
|
## How values are stored and configured
|
||||||
|
|||||||
@@ -93,21 +93,11 @@ See [full documentation of `JSON_NO_IO`](../api/macros/json_no_io.md).
|
|||||||
|
|
||||||
## `JSON_NO_THREAD_LOCAL`
|
## `JSON_NO_THREAD_LOCAL`
|
||||||
|
|
||||||
When defined, the library does not use `#!cpp thread_local` storage. Copying a value and comparing two values then
|
When defined, the library does not use `#!cpp thread_local` storage. Copying a value then always avoids the call stack
|
||||||
always avoid the call stack rather than descending into a bounded number of levels first, which is slower but yields the
|
rather than descending into a bounded number of levels first, which is slower but yields the same values.
|
||||||
same values and the same comparisons.
|
|
||||||
|
|
||||||
See [full documentation of `JSON_NO_THREAD_LOCAL`](../api/macros/json_no_thread_local.md).
|
See [full documentation of `JSON_NO_THREAD_LOCAL`](../api/macros/json_no_thread_local.md).
|
||||||
|
|
||||||
## `JSON_PRECISE_STREAM_POSITION`
|
|
||||||
|
|
||||||
When defined to `1`, [`operator>>`](../api/operator_gtgt.md) and non-strict
|
|
||||||
[`sax_parse`](../api/basic_json/sax_parse.md) leave an input stream positioned right after the parsed value, instead of
|
|
||||||
also consuming the character that terminates a number. The default value is `0`, which preserves the existing behavior;
|
|
||||||
this is planned to become the default in version 4.0.0.
|
|
||||||
|
|
||||||
See [full documentation of `JSON_PRECISE_STREAM_POSITION`](../api/macros/json_precise_stream_position.md).
|
|
||||||
|
|
||||||
## `JSON_SKIP_LIBRARY_VERSION_CHECK`
|
## `JSON_SKIP_LIBRARY_VERSION_CHECK`
|
||||||
|
|
||||||
When defined, the library will not create a compiler warning when a different version of the library was already
|
When defined, the library will not create a compiler warning when a different version of the library was already
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ The complete default namespace name is derived as follows:
|
|||||||
- [`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md) defined non-zero appends `_dp`.
|
- [`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md) defined non-zero appends `_dp`.
|
||||||
- [`JSON_BRACE_INIT_COPY_SEMANTICS`](../api/macros/json_brace_init_copy_semantics.md) defined non-zero appends
|
- [`JSON_BRACE_INIT_COPY_SEMANTICS`](../api/macros/json_brace_init_copy_semantics.md) defined non-zero appends
|
||||||
`_bics`.
|
`_bics`.
|
||||||
- [`JSON_PRECISE_STREAM_POSITION`](../api/macros/json_precise_stream_position.md) defined non-zero appends `_psp`.
|
|
||||||
- [`JSON_STRICT_NUL_HANDLING`](../api/macros/json_strict_nul_handling.md) defined non-zero appends `_snul`.
|
|
||||||
- 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.
|
||||||
|
|||||||
@@ -41,8 +41,7 @@ document followed by trailing bytes" is accepted rather than rejected. If you ar
|
|||||||
reject any input that is not exactly one JSON document, prefer `parse`.
|
reject any input that is not exactly one JSON document, prefer `parse`.
|
||||||
|
|
||||||
When using `operator>>` to read several concatenated values this way, a value that is a number must be followed by
|
When using `operator>>` to read several concatenated values this way, a value that is a number must be followed by
|
||||||
whitespace, because `operator>>` consumes the character that terminates a number, unless
|
whitespace, because `operator>>` consumes the character that terminates a number — 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.
|
||||||
|
|
||||||
## SAX vs. DOM parsing
|
## SAX vs. DOM parsing
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ For the [{fmt}](https://github.com/fmtlib/fmt) library, the library ships a
|
|||||||
## Serializing to other formats
|
## Serializing to other formats
|
||||||
|
|
||||||
Besides JSON text, a value can also be serialized to the more compact [binary formats](binary_formats/index.md)
|
Besides JSON text, a value can also be serialized to the more compact [binary formats](binary_formats/index.md)
|
||||||
(BJData, BON8, BSON, CBOR, MessagePack, UBJSON).
|
(BJData, BSON, CBOR, MessagePack, UBJSON).
|
||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
|
|||||||
@@ -547,7 +547,7 @@ Grisu2 algorithm, which produces the shortest representation that round-trips. O
|
|||||||
### Required for the binary formats
|
### Required for the binary formats
|
||||||
|
|
||||||
`NumberFloatType` must be `#!cpp float` or `#!cpp double`. The writers for
|
`NumberFloatType` must be `#!cpp float` or `#!cpp double`. The writers for
|
||||||
[CBOR, MessagePack, UBJSON, BJData, BON8, and BSON](../binary_formats/index.md) map a floating-point value onto an IEEE 754
|
[CBOR, MessagePack, UBJSON, BJData, and BSON](../binary_formats/index.md) map a floating-point value onto an IEEE 754
|
||||||
binary32 or binary64 field and have no encoding for `#!cpp long double`.
|
binary32 or binary64 field and have no encoding for `#!cpp long double`.
|
||||||
|
|
||||||
### Compatible types
|
### Compatible types
|
||||||
@@ -562,11 +562,7 @@ binary32 or binary64 field and have no encoding for `#!cpp long double`.
|
|||||||
## `AllocatorType`
|
## `AllocatorType`
|
||||||
|
|
||||||
`AllocatorType` is instantiated with **one** argument, for each of `object_t`, `array_t`, `string_t`, `binary_t`,
|
`AllocatorType` is instantiated with **one** argument, for each of `object_t`, `array_t`, `string_t`, `binary_t`,
|
||||||
`basic_json`, `#!cpp std::pair<const StringType, basic_json>`, and `#!cpp std::pair<StringType, basic_json>`.
|
`basic_json`, and `#!cpp std::pair<const StringType, basic_json>`.
|
||||||
|
|
||||||
`AllocatorType` is not the only allocator a `basic_json` uses. It allocates the JSON values themselves, but most
|
|
||||||
temporary storage is allocated with `#!cpp std::allocator`. This includes the parser's stacks and the stacks that
|
|
||||||
process deeply nested values without recursion.
|
|
||||||
|
|
||||||
### Always required
|
### Always required
|
||||||
|
|
||||||
|
|||||||
@@ -1,125 +1,34 @@
|
|||||||
# Architecture
|
# Architecture
|
||||||
|
|
||||||
This page gives a high-level overview of the library's architecture. It should help new contributors to get an idea of
|
!!! info
|
||||||
the used concepts and where to make changes.
|
|
||||||
|
This page is still under construction. Its goal is to provide a high-level overview of the library's architecture.
|
||||||
|
This should help new contributors to get an idea of the used concepts and where to make changes.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The library is built around a single class template, [`nlohmann::basic_json`](../api/basic_json/index.md). A
|
The main structure is class [nlohmann::basic_json](../api/basic_json/index.md).
|
||||||
`basic_json` value is a node in a tree of JSON values. All other components either create such a tree from an input
|
|
||||||
(parsing), write a tree to an output (serialization), or give access to it (iterators, JSON Pointer, conversions).
|
|
||||||
|
|
||||||
```mermaid
|
- public API
|
||||||
flowchart LR
|
- container interface
|
||||||
input[/"input<br>(string, stream,<br>iterator range, file)"/]
|
- iterators
|
||||||
ia["input adapter"]
|
|
||||||
lexer["lexer"]
|
|
||||||
parser["parser"]
|
|
||||||
breader["binary_reader"]
|
|
||||||
sax["SAX interface"]
|
|
||||||
value[("basic_json<br>value tree")]
|
|
||||||
serializer["serializer"]
|
|
||||||
bwriter["binary_writer"]
|
|
||||||
oa["output adapter"]
|
|
||||||
output[/"output<br>(string, stream,<br>vector)"/]
|
|
||||||
|
|
||||||
input --> ia
|
## Template specializations
|
||||||
ia --> lexer --> parser --> sax
|
|
||||||
ia --> breader --> sax
|
|
||||||
sax --> value
|
|
||||||
value --> serializer --> oa
|
|
||||||
value --> bwriter --> oa
|
|
||||||
oa --> output
|
|
||||||
```
|
|
||||||
|
|
||||||
- **JSON text** is read by an [input adapter](#input-adapters), tokenized by the lexer, and turned into SAX events by
|
- describe template parameters of `basic_json`
|
||||||
the parser.
|
- [`json`](../api/json.md)
|
||||||
- **Binary formats** (BJData, BSON, CBOR, MessagePack, UBJSON) are read by an input adapter and turned into the same SAX
|
- [`ordered_json`](../api/ordered_json.md) via [`ordered_map`](../api/ordered_map.md)
|
||||||
events by the `binary_reader`.
|
|
||||||
- A [SAX consumer](#sax-interface) receives the events. The one used by [`parse`](../api/basic_json/parse.md) builds a
|
|
||||||
`basic_json` value tree.
|
|
||||||
- The `serializer` (JSON text) or the `binary_writer` (binary formats) writes a value tree to an
|
|
||||||
[output adapter](#output-adapters).
|
|
||||||
|
|
||||||
## Source layout
|
|
||||||
|
|
||||||
The public headers are in [`include/nlohmann`](https://github.com/nlohmann/json/tree/develop/include/nlohmann):
|
|
||||||
|
|
||||||
- [`json.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/json.hpp) defines class [`basic_json`](../api/basic_json/index.md).
|
|
||||||
- [`json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/json_fwd.hpp) contains forward declarations.
|
|
||||||
- [`adl_serializer.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/adl_serializer.hpp), [`byte_container_with_subtype.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/byte_container_with_subtype.hpp), and [`ordered_map.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/ordered_map.hpp) define
|
|
||||||
[`adl_serializer`](../api/adl_serializer/index.md),
|
|
||||||
[`byte_container_with_subtype`](../api/byte_container_with_subtype/index.md), and
|
|
||||||
[`ordered_map`](../api/ordered_map.md).
|
|
||||||
|
|
||||||
Everything else lives in [`detail/`](https://github.com/nlohmann/json/tree/develop/include/nlohmann/detail) and namespace `nlohmann::detail`, which is not part of the public API. Paths
|
|
||||||
below are relative to `include/nlohmann`.
|
|
||||||
|
|
||||||
| Component | Location |
|
|
||||||
|-----------|----------|
|
|
||||||
| Value type enumeration | [`detail/value_t.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/value_t.hpp) |
|
|
||||||
| Input adapters | [`detail/input/input_adapters.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/input_adapters.hpp) |
|
|
||||||
| Lexer | [`detail/input/lexer.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/lexer.hpp), [`detail/input/number_parse.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/number_parse.hpp), [`detail/input/string_scan.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/string_scan.hpp) |
|
|
||||||
| Parser | [`detail/input/parser.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/parser.hpp) |
|
|
||||||
| SAX interface and DOM builders | [`detail/input/json_sax.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/json_sax.hpp) |
|
|
||||||
| Binary format readers | [`detail/input/binary_reader.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/binary_reader.hpp) |
|
|
||||||
| JSON serializer | [`detail/output/serializer.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/output/serializer.hpp), [`detail/conversions/to_chars.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/conversions/to_chars.hpp) |
|
|
||||||
| Binary format writers | [`detail/output/binary_writer.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/output/binary_writer.hpp) |
|
|
||||||
| Output adapters | [`detail/output/output_adapters.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/output/output_adapters.hpp) |
|
|
||||||
| Iterators | [`detail/iterators/`](https://github.com/nlohmann/json/tree/develop/include/nlohmann/detail/iterators) |
|
|
||||||
| Conversions from/to arbitrary types | [`detail/conversions/from_json.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/conversions/from_json.hpp), [`detail/conversions/to_json.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/conversions/to_json.hpp) |
|
|
||||||
| JSON Pointer | [`detail/json_pointer.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/json_pointer.hpp) |
|
|
||||||
| Exceptions | [`detail/exceptions.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/exceptions.hpp) |
|
|
||||||
| Type traits and C++ feature backports | [`detail/meta/`](https://github.com/nlohmann/json/tree/develop/include/nlohmann/detail/meta) |
|
|
||||||
| Macros | [`detail/macro_scope.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/macro_scope.hpp), [`detail/macro_unscope.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/macro_unscope.hpp), [`detail/abi_macros.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/abi_macros.hpp) |
|
|
||||||
|
|
||||||
The single-header version [`single_include/nlohmann/json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp)
|
|
||||||
is generated from these files with `make amalgamate` and must not be edited by hand.
|
|
||||||
|
|
||||||
## Template parameters
|
|
||||||
|
|
||||||
[`basic_json`](../api/basic_json/index.md) is parameterized by the types it uses to store values and to convert from and to other types:
|
|
||||||
|
|
||||||
| Template parameter | Default | Used for |
|
|
||||||
|----------------------|-----------------------------|-------------------------------------------------------------------|
|
|
||||||
| `ObjectType` | `std::map` | objects, see [`object_t`](../api/basic_json/object_t.md) |
|
|
||||||
| `ArrayType` | `std::vector` | arrays, see [`array_t`](../api/basic_json/array_t.md) |
|
|
||||||
| `StringType` | `std::string` | strings and object keys, see [`string_t`](../api/basic_json/string_t.md) |
|
|
||||||
| `BooleanType` | `bool` | Booleans, see [`boolean_t`](../api/basic_json/boolean_t.md) |
|
|
||||||
| `NumberIntegerType` | `std::int64_t` | signed integers, see [`number_integer_t`](../api/basic_json/number_integer_t.md) |
|
|
||||||
| `NumberUnsignedType` | `std::uint64_t` | unsigned integers, see [`number_unsigned_t`](../api/basic_json/number_unsigned_t.md) |
|
|
||||||
| `NumberFloatType` | `double` | floating-point numbers, see [`number_float_t`](../api/basic_json/number_float_t.md) |
|
|
||||||
| `AllocatorType` | `std::allocator` | allocating objects, arrays, strings, and binary values |
|
|
||||||
| `JSONSerializer` | `adl_serializer` | conversions from/to other types, see [`adl_serializer`](../api/adl_serializer/index.md) |
|
|
||||||
| `BinaryType` | `std::vector<std::uint8_t>` | binary values, see [`binary_t`](../api/basic_json/binary_t.md) |
|
|
||||||
| `CustomBaseClass` | `void` | an optional base class, see [`json_base_class_t`](../api/basic_json/json_base_class_t.md) |
|
|
||||||
|
|
||||||
The library provides two specializations:
|
|
||||||
|
|
||||||
- [`json`](../api/json.md) uses all default template arguments.
|
|
||||||
- [`ordered_json`](../api/ordered_json.md) uses [`ordered_map`](../api/ordered_map.md) as `ObjectType` to keep the
|
|
||||||
insertion order of object keys.
|
|
||||||
|
|
||||||
The requirements on the template arguments are listed in
|
|
||||||
[Template Parameter Requirements](../features/types/template_parameters.md).
|
|
||||||
|
|
||||||
## Value storage
|
## Value storage
|
||||||
|
|
||||||
Each [`basic_json`](../api/basic_json/index.md) value stores its content as a tagged union: an enumeration [`value_t`](../api/basic_json/value_t.md)
|
Values are stored as a tagged union of [value_t](../api/basic_json/value_t.md) and json_value.
|
||||||
names the type of the value, and a union `json_value` holds the value itself. Both are members of the nested struct
|
|
||||||
`data`, which is the only data member `m_data` of `basic_json`:
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
struct data
|
/// the type of the current element
|
||||||
{
|
value_t m_type = value_t::null;
|
||||||
/// the type of the current element
|
|
||||||
value_t m_type = value_t::null;
|
|
||||||
|
|
||||||
/// the value of the current element
|
/// the value of the current element
|
||||||
json_value m_value = {};
|
json_value m_value = {};
|
||||||
};
|
|
||||||
|
|
||||||
data m_data = {};
|
|
||||||
```
|
```
|
||||||
|
|
||||||
with
|
with
|
||||||
@@ -159,83 +68,42 @@ union json_value {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Objects, arrays, strings, and binary values are allocated on the heap with `AllocatorType`, and the union only stores a
|
## Parsing inputs (deserialization)
|
||||||
pointer to them. This keeps a `basic_json` value small: one pointer-sized union and one byte for the type. The class
|
|
||||||
maintains the invariant that the pointer matching `m_type` is never null; `assert_invariant()` checks it with
|
|
||||||
[runtime assertions](../features/assertions.md).
|
|
||||||
|
|
||||||
## Input adapters
|
Input is read via **input adapters** that abstract a source with a common interface:
|
||||||
|
|
||||||
Input is read via **input adapters** that abstract a source. Every input adapter provides this interface:
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
/// the type of the characters in the input
|
/// read a single character
|
||||||
using char_type = ...;
|
std::char_traits<char>::int_type get_character() noexcept;
|
||||||
|
|
||||||
/// read a single character; returns std::char_traits<char_type>::eof() at the end of the input
|
/// read multiple characters to a destination buffer and
|
||||||
typename std::char_traits<char_type>::int_type get_character();
|
/// returns the number of characters successfully read
|
||||||
|
|
||||||
/// read up to count * sizeof(T) bytes into dest and return the number of bytes read
|
|
||||||
/// (used by the binary readers)
|
|
||||||
template<class T>
|
template<class T>
|
||||||
std::size_t get_elements(T* dest, std::size_t count = 1);
|
std::size_t get_elements(T* dest, std::size_t count = 1);
|
||||||
```
|
```
|
||||||
|
|
||||||
The lexer detects two optional extensions at compile time. Only `iterator_input_adapter` provides them, and only for
|
List examples of input adapters.
|
||||||
random-access input of single-byte characters:
|
|
||||||
|
|
||||||
- `supports_seek`, `get_consumed_count()`, and `copy_consumed_range()` let the lexer reconstruct already consumed input
|
## SAX Interface
|
||||||
for error messages instead of copying every character it reads.
|
|
||||||
- `supports_bulk_scan`, `bulk_data()`, `bulk_remaining()`, and `bulk_skip()` let the lexer scan strings directly in
|
|
||||||
contiguous memory, several bytes at a time.
|
|
||||||
|
|
||||||
The function `input_adapter` picks the right adapter for the argument passed to `parse`, `accept`, `sax_parse`, or the
|
TODO
|
||||||
`from_*` functions:
|
|
||||||
|
|
||||||
- `iterator_input_adapter` reads from an iterator range, which also covers strings, containers, and pointers.
|
## Writing outputs (serialization)
|
||||||
- `wide_string_input_adapter` reads from ranges of `wchar_t`, `char16_t`, or `char32_t` and converts them to UTF-8.
|
|
||||||
It cannot be used for binary formats; its `get_elements()` throws.
|
|
||||||
- `input_stream_adapter` reads from a `std::istream`.
|
|
||||||
- `file_input_adapter` reads from a `std::FILE*`.
|
|
||||||
|
|
||||||
## SAX interface
|
|
||||||
|
|
||||||
The parser does not build values itself. It reports what it reads as events to a [SAX](../features/parsing/sax_interface.md)
|
|
||||||
consumer, which implements the interface [`json_sax`](../api/json_sax/index.md): `null`, `boolean`, `number_integer`,
|
|
||||||
`number_unsigned`, `number_float`, `string`, `binary`, `start_object`, `key`, `end_object`, `start_array`, `end_array`,
|
|
||||||
and `parse_error`.
|
|
||||||
|
|
||||||
The library comes with two consumers in `detail/input/json_sax.hpp`:
|
|
||||||
|
|
||||||
- `json_sax_dom_parser` builds a [`basic_json`](../api/basic_json/index.md) value tree. [`parse`](../api/basic_json/parse.md) uses it.
|
|
||||||
- `json_sax_dom_callback_parser` does the same, but calls a [parser callback](../features/parsing/parser_callbacks.md)
|
|
||||||
for each event, which can skip values. `parse` uses it when a callback is given.
|
|
||||||
|
|
||||||
The `binary_reader` emits the same events for binary formats, so [`sax_parse`](../api/basic_json/sax_parse.md) works
|
|
||||||
with a user-defined consumer for JSON and for all binary formats alike.
|
|
||||||
|
|
||||||
## Output adapters
|
|
||||||
|
|
||||||
Output is written via **output adapters**:
|
Output is written via **output adapters**:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
template<typename T>
|
||||||
void write_character(CharType c);
|
void write_character(CharType c);
|
||||||
|
|
||||||
|
template<typename CharType>
|
||||||
void write_characters(const CharType* s, std::size_t length);
|
void write_characters(const CharType* s, std::size_t length);
|
||||||
```
|
```
|
||||||
|
|
||||||
The `serializer` (used by [`dump`](../api/basic_json/dump.md) and [`operator<<`](../api/operator_ltlt.md)) and the
|
List examples of output adapters.
|
||||||
`binary_writer` (used by the `to_*` functions) write to one of these adapters:
|
|
||||||
|
|
||||||
- `output_vector_adapter` appends to a `std::vector`.
|
|
||||||
- `output_stream_adapter` writes to a `std::ostream`.
|
|
||||||
- `output_string_adapter` appends to a string.
|
|
||||||
|
|
||||||
## Value conversion
|
## Value conversion
|
||||||
|
|
||||||
Values are converted from and to other types with the `JSONSerializer` template parameter. The default,
|
|
||||||
[`adl_serializer`](../api/adl_serializer/index.md), calls the free functions
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
template<class T>
|
template<class T>
|
||||||
void to_json(basic_json& j, const T& t);
|
void to_json(basic_json& j, const T& t);
|
||||||
@@ -244,23 +112,13 @@ template<class T>
|
|||||||
void from_json(const basic_json& j, T& t);
|
void from_json(const basic_json& j, T& t);
|
||||||
```
|
```
|
||||||
|
|
||||||
found by argument-dependent lookup. The library defines them for standard types in `detail/conversions`; users add them
|
|
||||||
for their own types, see [Arbitrary Type Conversions](../features/arbitrary_types.md). The
|
|
||||||
[serialization macros](../features/macros.md) generate these functions.
|
|
||||||
|
|
||||||
## Additional features
|
## Additional features
|
||||||
|
|
||||||
- [JSON Pointer](../features/json_pointer.md) (class `json_pointer`) addresses values inside a tree. It is also the
|
- JSON Pointers
|
||||||
basis of [JSON Patch](../features/json_patch.md).
|
- Binary formats
|
||||||
- [Binary formats](../features/binary_formats/index.md) are read by `binary_reader` and written by `binary_writer`.
|
- Custom base class
|
||||||
- A [custom base class](../api/basic_json/json_base_class_t.md) can add members to every [`basic_json`](../api/basic_json/index.md) value.
|
- Conversion macros
|
||||||
- [Serialization macros](../features/macros.md) generate `to_json` and `from_json` functions for user-defined types.
|
|
||||||
|
|
||||||
## Details namespace
|
## Details namespace
|
||||||
|
|
||||||
Namespace `nlohmann::detail` contains all implementation details. It is not part of the public API and may change in any
|
- C++ feature backports
|
||||||
release. Besides the components above, it contains:
|
|
||||||
|
|
||||||
- type traits to detect the capabilities of user-defined types (`detail/meta/type_traits.hpp`),
|
|
||||||
- backports of C++14/17 features to C++11 (`detail/meta/cpp_future.hpp`), and
|
|
||||||
- helpers such as `string_concat` and `string_escape`.
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ There are myriads of [JSON](https://json.org) libraries out there, and each may
|
|||||||
|
|
||||||
- **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings.
|
- **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings.
|
||||||
|
|
||||||
- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/tree/develop/tests/src) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) and the [Clang Sanitizers](https://clang.llvm.org/docs/index.html) that there are no memory leaks. [Google OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/json) additionally runs fuzz tests against all parsers 24/7, effectively executing billions of tests so far. To maintain high quality, the project is following the [OpenSSF Best Practices](https://www.bestpractices.dev/projects/289).
|
- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/tree/develop/tests/src) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) and the [Clang Sanitizers](https://clang.llvm.org/docs/index.html) that there are no memory leaks. [Google OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/json) additionally runs fuzz tests against all parsers 24/7, effectively executing billions of tests so far. To maintain high quality, the project is following the [Core Infrastructure Initiative (CII) best practices](https://bestpractices.coreinfrastructure.org/projects/289).
|
||||||
|
|
||||||
Other aspects were not so important to us:
|
Other aspects were not so important to us:
|
||||||
|
|
||||||
|
|||||||
@@ -340,8 +340,7 @@ An unexpected byte was read in a [binary format](../features/binary_formats/inde
|
|||||||
### json.exception.parse_error.113
|
### json.exception.parse_error.113
|
||||||
|
|
||||||
A string could not be read from a [binary format](../features/binary_formats/index.md): either a value that is not a
|
A string could not be read from a [binary format](../features/binary_formats/index.md): either a value that is not a
|
||||||
string was read where one was required (for instance as a map key), the string's length specification is invalid, or
|
string was read where one was required (for instance as a map key), or the string's length specification is invalid.
|
||||||
the string's bytes are not valid UTF-8.
|
|
||||||
|
|
||||||
!!! failure "Example messages"
|
!!! failure "Example messages"
|
||||||
|
|
||||||
@@ -357,9 +356,6 @@ the string's bytes are not valid UTF-8.
|
|||||||
```
|
```
|
||||||
[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing BJData string: string length must not be negative
|
[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing BJData string: string length must not be negative
|
||||||
```
|
```
|
||||||
```
|
|
||||||
[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing CBOR string: invalid string: ill-formed UTF-8 byte
|
|
||||||
```
|
|
||||||
|
|
||||||
### json.exception.parse_error.114
|
### json.exception.parse_error.114
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
*[ASCII]: American Standard Code for Information Interchange
|
*[ASCII]: American Standard Code for Information Interchange
|
||||||
*[BDFL]: Benevolent Dictator for Life
|
*[BDFL]: Benevolent Dictator for Life
|
||||||
*[BJData]: Binary JData
|
*[BJData]: Binary JData
|
||||||
*[BON8]: Binary Object Notation 8
|
|
||||||
*[BSON]: Binary JSON
|
*[BSON]: Binary JSON
|
||||||
*[CBOR]: Concise Binary Object Representation
|
*[CBOR]: Concise Binary Object Representation
|
||||||
*[CC0]: Creative Commons Zero
|
*[CC0]: Creative Commons Zero
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ nav:
|
|||||||
- Binary Formats:
|
- Binary Formats:
|
||||||
- features/binary_formats/index.md
|
- features/binary_formats/index.md
|
||||||
- features/binary_formats/bjdata.md
|
- features/binary_formats/bjdata.md
|
||||||
- features/binary_formats/bon8.md
|
|
||||||
- features/binary_formats/bson.md
|
- features/binary_formats/bson.md
|
||||||
- features/binary_formats/cbor.md
|
- features/binary_formats/cbor.md
|
||||||
- features/binary_formats/messagepack.md
|
- features/binary_formats/messagepack.md
|
||||||
@@ -143,7 +142,6 @@ nav:
|
|||||||
- 'flatten': api/basic_json/flatten.md
|
- 'flatten': api/basic_json/flatten.md
|
||||||
- 'format_as': api/basic_json/format_as.md
|
- 'format_as': api/basic_json/format_as.md
|
||||||
- 'from_bjdata': api/basic_json/from_bjdata.md
|
- 'from_bjdata': api/basic_json/from_bjdata.md
|
||||||
- 'from_bon8': api/basic_json/from_bon8.md
|
|
||||||
- 'from_bson': api/basic_json/from_bson.md
|
- 'from_bson': api/basic_json/from_bson.md
|
||||||
- 'from_cbor': api/basic_json/from_cbor.md
|
- 'from_cbor': api/basic_json/from_cbor.md
|
||||||
- 'from_msgpack': api/basic_json/from_msgpack.md
|
- 'from_msgpack': api/basic_json/from_msgpack.md
|
||||||
@@ -215,7 +213,6 @@ nav:
|
|||||||
- 'swap': api/basic_json/swap.md
|
- 'swap': api/basic_json/swap.md
|
||||||
- 'std::swap<basic_json>': api/basic_json/std_swap.md
|
- 'std::swap<basic_json>': api/basic_json/std_swap.md
|
||||||
- 'to_bjdata': api/basic_json/to_bjdata.md
|
- 'to_bjdata': api/basic_json/to_bjdata.md
|
||||||
- 'to_bon8': api/basic_json/to_bon8.md
|
|
||||||
- 'to_bson': api/basic_json/to_bson.md
|
- 'to_bson': api/basic_json/to_bson.md
|
||||||
- 'to_cbor': api/basic_json/to_cbor.md
|
- 'to_cbor': api/basic_json/to_cbor.md
|
||||||
- 'to_msgpack': api/basic_json/to_msgpack.md
|
- 'to_msgpack': api/basic_json/to_msgpack.md
|
||||||
@@ -296,7 +293,6 @@ nav:
|
|||||||
- 'JSON_NOEXCEPTION': api/macros/json_noexception.md
|
- 'JSON_NOEXCEPTION': api/macros/json_noexception.md
|
||||||
- 'JSON_NO_IO': api/macros/json_no_io.md
|
- 'JSON_NO_IO': api/macros/json_no_io.md
|
||||||
- 'JSON_NO_THREAD_LOCAL': api/macros/json_no_thread_local.md
|
- 'JSON_NO_THREAD_LOCAL': api/macros/json_no_thread_local.md
|
||||||
- 'JSON_PRECISE_STREAM_POSITION': api/macros/json_precise_stream_position.md
|
|
||||||
- 'JSON_SKIP_LIBRARY_VERSION_CHECK': api/macros/json_skip_library_version_check.md
|
- 'JSON_SKIP_LIBRARY_VERSION_CHECK': api/macros/json_skip_library_version_check.md
|
||||||
- 'JSON_SKIP_UNSUPPORTED_COMPILER_CHECK': api/macros/json_skip_unsupported_compiler_check.md
|
- 'JSON_SKIP_UNSUPPORTED_COMPILER_CHECK': api/macros/json_skip_unsupported_compiler_check.md
|
||||||
- 'JSON_STRICT_NUL_HANDLING': api/macros/json_strict_nul_handling.md
|
- 'JSON_STRICT_NUL_HANDLING': api/macros/json_strict_nul_handling.md
|
||||||
@@ -321,9 +317,7 @@ nav:
|
|||||||
- community/contribution_guidelines.md
|
- community/contribution_guidelines.md
|
||||||
- community/quality_assurance.md
|
- community/quality_assurance.md
|
||||||
- community/governance.md
|
- community/governance.md
|
||||||
- community/roadmap.md
|
|
||||||
- community/security_policy.md
|
- community/security_policy.md
|
||||||
- community/assurance_case.md
|
|
||||||
|
|
||||||
# Extras
|
# Extras
|
||||||
extra:
|
extra:
|
||||||
@@ -415,7 +409,7 @@ plugins:
|
|||||||
markdown_description: >
|
markdown_description: >
|
||||||
JSON for Modern C++ is a C++11 header-only library implementing a JSON
|
JSON for Modern C++ is a C++11 header-only library implementing a JSON
|
||||||
value type with an STL-like API, JSON Pointer/Patch, CBOR/MessagePack/
|
value type with an STL-like API, JSON Pointer/Patch, CBOR/MessagePack/
|
||||||
BSON/UBJSON/BJData/BON8 binary format support, and a SAX-style parser interface.
|
BSON/UBJSON/BJData binary format support, and a SAX-style parser interface.
|
||||||
sections:
|
sections:
|
||||||
Home:
|
Home:
|
||||||
- index.md
|
- index.md
|
||||||
|
|||||||
@@ -38,14 +38,6 @@
|
|||||||
#define JSON_BRACE_INIT_COPY_SEMANTICS 0
|
#define JSON_BRACE_INIT_COPY_SEMANTICS 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef JSON_PRECISE_STREAM_POSITION
|
|
||||||
#define JSON_PRECISE_STREAM_POSITION 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef JSON_STRICT_NUL_HANDLING
|
|
||||||
#define JSON_STRICT_NUL_HANDLING 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_DIAGNOSTICS
|
#if JSON_DIAGNOSTICS
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||||
#else
|
#else
|
||||||
@@ -70,35 +62,21 @@
|
|||||||
#define NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS
|
#define NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if JSON_PRECISE_STREAM_POSITION
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION _psp
|
|
||||||
#else
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_STRICT_NUL_HANDLING
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING _snul
|
|
||||||
#else
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING
|
|
||||||
#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) json_abi ## a ## b ## c ## d ## e ## f
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d) json_abi ## a ## b ## c ## d
|
||||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f) \
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d) \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f)
|
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d)
|
||||||
|
|
||||||
#define NLOHMANN_JSON_ABI_TAGS \
|
#define NLOHMANN_JSON_ABI_TAGS \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
|
NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
|
||||||
NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \
|
NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \
|
||||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS, \
|
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS, \
|
||||||
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS, \
|
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS)
|
||||||
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
|
||||||
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING)
|
|
||||||
|
|
||||||
// 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) \
|
||||||
|
|||||||
@@ -28,12 +28,10 @@
|
|||||||
#include <nlohmann/detail/input/input_adapters.hpp>
|
#include <nlohmann/detail/input/input_adapters.hpp>
|
||||||
#include <nlohmann/detail/input/json_sax.hpp>
|
#include <nlohmann/detail/input/json_sax.hpp>
|
||||||
#include <nlohmann/detail/input/lexer.hpp>
|
#include <nlohmann/detail/input/lexer.hpp>
|
||||||
#include <nlohmann/detail/input/string_scan.hpp>
|
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
#include <nlohmann/detail/meta/is_sax.hpp>
|
#include <nlohmann/detail/meta/is_sax.hpp>
|
||||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||||
#include <nlohmann/detail/string_concat.hpp>
|
#include <nlohmann/detail/string_concat.hpp>
|
||||||
#include <nlohmann/detail/string_utils.hpp>
|
|
||||||
#include <nlohmann/detail/value_t.hpp>
|
#include <nlohmann/detail/value_t.hpp>
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
@@ -85,7 +83,7 @@ JSON_INLINE_VARIABLE constexpr std::size_t max_valueless_container_size = 1 << 2
|
|||||||
///////////////////
|
///////////////////
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief deserialization of BJData, BON8, BSON, CBOR, MessagePack, and UBJSON values
|
@brief deserialization of CBOR, MessagePack, and UBJSON values
|
||||||
*/
|
*/
|
||||||
template<typename BasicJsonType, typename InputAdapterType, typename SAX = json_sax_dom_parser<BasicJsonType, InputAdapterType>>
|
template<typename BasicJsonType, typename InputAdapterType, typename SAX = json_sax_dom_parser<BasicJsonType, InputAdapterType>>
|
||||||
class binary_reader
|
class binary_reader
|
||||||
@@ -99,11 +97,6 @@ class binary_reader
|
|||||||
using char_type = typename InputAdapterType::char_type;
|
using char_type = typename InputAdapterType::char_type;
|
||||||
using char_int_type = typename char_traits<char_type>::int_type;
|
using char_int_type = typename char_traits<char_type>::int_type;
|
||||||
|
|
||||||
/// whether the input is a contiguous block of bytes that can be inspected
|
|
||||||
/// and consumed in bulk (as in the lexer); used by @ref get_bon8_string_bulk
|
|
||||||
static constexpr bool bulk_scan =
|
|
||||||
input_adapter_supports_bulk_scan<InputAdapterType>(is_detected<detect_supports_bulk_scan, InputAdapterType> {});
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/*!
|
/*!
|
||||||
@brief create a binary reader
|
@brief create a binary reader
|
||||||
@@ -138,7 +131,6 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
sax = sax_;
|
sax = sax_;
|
||||||
container_stack.clear();
|
container_stack.clear();
|
||||||
bon8_pushback_size = 0;
|
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
switch (format)
|
switch (format)
|
||||||
@@ -160,10 +152,6 @@ class binary_reader
|
|||||||
result = parse_ubjson_internal();
|
result = parse_ubjson_internal();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case input_format_t::bon8:
|
|
||||||
result = parse_bon8_internal();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case input_format_t::json: // LCOV_EXCL_LINE
|
case input_format_t::json: // LCOV_EXCL_LINE
|
||||||
default: // LCOV_EXCL_LINE
|
default: // LCOV_EXCL_LINE
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||||
@@ -176,11 +164,6 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
get_ignore_noop();
|
get_ignore_noop();
|
||||||
}
|
}
|
||||||
else if (input_format == input_format_t::bon8)
|
|
||||||
{
|
|
||||||
// a string that ends a container hands back the byte after it
|
|
||||||
get_bon8();
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
get();
|
get();
|
||||||
@@ -412,11 +395,6 @@ class binary_reader
|
|||||||
*/
|
*/
|
||||||
bool get_bson_cstr(string_t& result)
|
bool get_bson_cstr(string_t& result)
|
||||||
{
|
{
|
||||||
if (get_bson_cstr_bulk(result, std::integral_constant<bool, bulk_scan> {}))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto out = std::back_inserter(result);
|
auto out = std::back_inserter(result);
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
@@ -433,46 +411,6 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief read a C-style string from contiguous input in one step
|
|
||||||
|
|
||||||
@param[in,out] result the string to append to
|
|
||||||
@return whether the string was read; if the input has no \x00-byte, nothing
|
|
||||||
is read, and @ref get_bson_cstr reports the end of the input
|
|
||||||
*/
|
|
||||||
bool get_bson_cstr_bulk(string_t& result, std::true_type /*bulk*/)
|
|
||||||
{
|
|
||||||
const std::size_t remaining = ia.bulk_remaining();
|
|
||||||
if (remaining == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto* const data = reinterpret_cast<const unsigned char*>(ia.bulk_data());
|
|
||||||
// a plain loop rather than std::memchr: most keys are short (array
|
|
||||||
// indices are keys, too), and the call would cost more than it saves
|
|
||||||
std::size_t length = 0;
|
|
||||||
while (length < remaining && data[length] != 0x00)
|
|
||||||
{
|
|
||||||
++length;
|
|
||||||
}
|
|
||||||
if (length == remaining)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
result.append(reinterpret_cast<const typename string_t::value_type*>(data), length);
|
|
||||||
// consume the string and its \x00-byte, as the byte-wise path does
|
|
||||||
ia.bulk_skip(length + 1);
|
|
||||||
chars_read += length + 1;
|
|
||||||
current = 0x00;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// input that is not contiguous: C-style strings are read byte by byte
|
|
||||||
bool get_bson_cstr_bulk(string_t& /*result*/, std::false_type /*bulk*/) const noexcept
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Parses a zero-terminated string of length @a len from the BSON
|
@brief Parses a zero-terminated string of length @a len from the BSON
|
||||||
input.
|
input.
|
||||||
@@ -494,21 +432,7 @@ class binary_reader
|
|||||||
exception_message(input_format_t::bson, concat("string length must be at least 1, is ", std::to_string(len)), "string"), nullptr));
|
exception_message(input_format_t::bson, concat("string length must be at least 1, is ", std::to_string(len)), "string"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_string(input_format_t::bson, len - static_cast<NumberType>(1), result)))
|
return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) && get() != char_traits<char_type>::eof();
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(get() != 0x00))
|
|
||||||
{
|
|
||||||
auto last_token = get_token_string();
|
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
|
||||||
exception_message(input_format_t::bson,
|
|
||||||
"BSON string is not null-terminated",
|
|
||||||
"string"), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -626,6 +550,8 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//////////
|
//////////
|
||||||
// CBOR //
|
// CBOR //
|
||||||
//////////
|
//////////
|
||||||
@@ -3245,549 +3171,6 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////
|
|
||||||
// BON8 //
|
|
||||||
//////////
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief get the next byte of a BON8 value
|
|
||||||
|
|
||||||
A BON8 string has no length prefix and no mandatory terminator: it ends at
|
|
||||||
the first byte that cannot continue it, which is already the first byte (or,
|
|
||||||
for an integer that begins with a UTF-8 lead byte, the first two bytes) of
|
|
||||||
whatever follows. The string reader hands those bytes back with
|
|
||||||
@ref unget_bon8, and every BON8 read goes through this function so that
|
|
||||||
they are seen again.
|
|
||||||
|
|
||||||
@return character read from the input
|
|
||||||
*/
|
|
||||||
char_int_type get_bon8()
|
|
||||||
{
|
|
||||||
if (bon8_pushback_size != 0)
|
|
||||||
{
|
|
||||||
++chars_read;
|
|
||||||
return current = bon8_pushback[--bon8_pushback_size];
|
|
||||||
}
|
|
||||||
return get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief hand a byte back so that the next @ref get_bon8 returns it again
|
|
||||||
|
|
||||||
@param[in] c the byte to hand back; bytes handed back are returned in
|
|
||||||
reverse order
|
|
||||||
*/
|
|
||||||
void unget_bon8(const char_int_type c)
|
|
||||||
{
|
|
||||||
// At most two bytes are ever handed back: a byte is only handed back
|
|
||||||
// right after it was read with get_bon8(), and the only place that
|
|
||||||
// hands back two bytes (a lead byte and the byte after it) read both
|
|
||||||
// of them in a row, which emptied the buffer first. This is an
|
|
||||||
// invariant of the reader rather than a property of the input, so
|
|
||||||
// an assertion suffices (the fuzzers are built with assertions).
|
|
||||||
JSON_ASSERT(bon8_pushback_size < bon8_pushback.size());
|
|
||||||
bon8_pushback[bon8_pushback_size++] = c;
|
|
||||||
--chars_read;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@param[in] c a byte
|
|
||||||
@return whether @a c is a UTF-8 continuation byte (0x80..0xBF)
|
|
||||||
*/
|
|
||||||
static constexpr bool is_bon8_continuation(const char_int_type c) noexcept
|
|
||||||
{
|
|
||||||
return 0x80 <= c && c <= 0xBF;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief report a parse error at the last read byte
|
|
||||||
|
|
||||||
@param[in] detail a detailed error message
|
|
||||||
@param[in] context further context information
|
|
||||||
@return false
|
|
||||||
*/
|
|
||||||
bool bon8_error(const std::string& detail, const char* context)
|
|
||||||
{
|
|
||||||
auto last_token = get_token_string();
|
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
|
||||||
exception_message(input_format_t::bon8, concat(detail, ": 0x", last_token), context), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief read a BON8 value and everything nested inside it
|
|
||||||
|
|
||||||
Reads values until the one that was begun here is complete, resuming the
|
|
||||||
enclosing container after each element, so that the nesting depth of the
|
|
||||||
input costs heap rather than native stack (see #5104).
|
|
||||||
|
|
||||||
@return whether reading the value succeeded
|
|
||||||
*/
|
|
||||||
bool parse_bon8_internal()
|
|
||||||
{
|
|
||||||
// the key currently being read; hoisted out of the loop so that its
|
|
||||||
// capacity is reused across elements and across nesting levels
|
|
||||||
string_t key;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (!container_stack.empty())
|
|
||||||
{
|
|
||||||
// a copy, not a reference: it must stay valid across the
|
|
||||||
// pop_back() below, which destroys the container_stack element
|
|
||||||
// it would otherwise alias
|
|
||||||
const container_frame top = container_stack.back();
|
|
||||||
bool at_end = false;
|
|
||||||
|
|
||||||
if (top.remaining != npos)
|
|
||||||
{
|
|
||||||
// counted container (0x80..0x84, 0x86..0x8A): it ends once
|
|
||||||
// its elements have been read
|
|
||||||
at_end = (top.remaining == 0);
|
|
||||||
if (!at_end)
|
|
||||||
{
|
|
||||||
// claim the element about to be read
|
|
||||||
--container_stack.back().remaining;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// container 0x85 or 0x8B: it ends at an end-of-container
|
|
||||||
// marker (0xFE); any other byte begins the next element
|
|
||||||
at_end = (get_bon8() == 0xFE);
|
|
||||||
if (!at_end)
|
|
||||||
{
|
|
||||||
unget_bon8(current);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (at_end)
|
|
||||||
{
|
|
||||||
container_stack.pop_back();
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(top.is_object ? !sax->end_object() : !sax->end_array()))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// the value begun here is complete once its container is
|
|
||||||
if (container_stack.empty())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (top.is_object)
|
|
||||||
{
|
|
||||||
key.clear();
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_bon8_key(key) || !sax->key(key)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!parse_bon8_value()))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// a value that opened a container left it on the stack; one that
|
|
||||||
// did not, and that was not inside a container, was the whole value
|
|
||||||
if (container_stack.empty())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief read one BON8 value
|
|
||||||
|
|
||||||
Reads a single value and passes it to the SAX parser. A value that begins
|
|
||||||
a container is not read to its end: the container is opened with
|
|
||||||
@ref enter_container and its elements are read by
|
|
||||||
@ref parse_bon8_internal, so that nesting does not consume native stack.
|
|
||||||
|
|
||||||
@return whether reading the value succeeded
|
|
||||||
*/
|
|
||||||
bool parse_bon8_value()
|
|
||||||
{
|
|
||||||
const auto byte = get_bon8();
|
|
||||||
|
|
||||||
if (byte == char_traits<char_type>::eof())
|
|
||||||
{
|
|
||||||
return unexpect_eof(input_format_t::bon8, "value");
|
|
||||||
}
|
|
||||||
|
|
||||||
// string: ASCII character
|
|
||||||
if (byte <= 0x7F)
|
|
||||||
{
|
|
||||||
string_t s;
|
|
||||||
unget_bon8(byte);
|
|
||||||
return get_bon8_string(s) && sax->string(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// array with 0..4 elements
|
|
||||||
if (byte <= 0x84)
|
|
||||||
{
|
|
||||||
return enter_array(static_cast<std::size_t>(byte - 0x80));
|
|
||||||
}
|
|
||||||
|
|
||||||
// array terminated by 0xFE
|
|
||||||
if (byte == 0x85)
|
|
||||||
{
|
|
||||||
return enter_array(npos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// object with 0..4 members
|
|
||||||
if (byte <= 0x8A)
|
|
||||||
{
|
|
||||||
return enter_object(static_cast<std::size_t>(byte - 0x86));
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (byte)
|
|
||||||
{
|
|
||||||
case 0x8B: // object terminated by 0xFE
|
|
||||||
return enter_object(npos);
|
|
||||||
|
|
||||||
case 0x8C: // int32
|
|
||||||
{
|
|
||||||
std::int32_t number{};
|
|
||||||
return get_number(input_format_t::bon8, number) && emit_bon8_integer(number);
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0x8D: // int64
|
|
||||||
{
|
|
||||||
std::int64_t number{};
|
|
||||||
return get_number(input_format_t::bon8, number) && emit_bon8_integer(number);
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0x8E: // binary32
|
|
||||||
{
|
|
||||||
float number{};
|
|
||||||
return get_number(input_format_t::bon8, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0x8F: // binary64
|
|
||||||
{
|
|
||||||
double number{};
|
|
||||||
return get_number(input_format_t::bon8, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0xF8:
|
|
||||||
return sax->boolean(false);
|
|
||||||
|
|
||||||
case 0xF9:
|
|
||||||
return sax->boolean(true);
|
|
||||||
|
|
||||||
case 0xFA:
|
|
||||||
return sax->null();
|
|
||||||
|
|
||||||
case 0xFB:
|
|
||||||
return sax->number_float(static_cast<number_float_t>(-1.0), "");
|
|
||||||
|
|
||||||
case 0xFC:
|
|
||||||
return sax->number_float(static_cast<number_float_t>(0.0), "");
|
|
||||||
|
|
||||||
case 0xFD:
|
|
||||||
return sax->number_float(static_cast<number_float_t>(1.0), "");
|
|
||||||
|
|
||||||
case 0xFF: // empty string
|
|
||||||
{
|
|
||||||
string_t s;
|
|
||||||
return sax->string(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// integer 0..39
|
|
||||||
if (byte <= 0xB7)
|
|
||||||
{
|
|
||||||
return sax->number_unsigned(static_cast<number_unsigned_t>(byte - 0x90));
|
|
||||||
}
|
|
||||||
|
|
||||||
// integer -1..-10
|
|
||||||
if (byte <= 0xC1)
|
|
||||||
{
|
|
||||||
return sax->number_integer(-1 - static_cast<number_integer_t>(byte - 0xB8));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 0xC2..0xF7: a UTF-8 lead byte begins a string if a continuation
|
|
||||||
// byte follows and an integer otherwise
|
|
||||||
if (byte <= 0xF7)
|
|
||||||
{
|
|
||||||
const auto second = get_bon8();
|
|
||||||
if (is_bon8_continuation(second))
|
|
||||||
{
|
|
||||||
string_t s;
|
|
||||||
unget_bon8(second);
|
|
||||||
unget_bon8(byte);
|
|
||||||
return get_bon8_string(s) && sax->string(s);
|
|
||||||
}
|
|
||||||
return get_bon8_integer(byte, second);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 0xFE: end of container where a value is expected
|
|
||||||
return bon8_error("invalid byte", "value");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief pass an integer to the SAX parser
|
|
||||||
|
|
||||||
Non-negative integers are passed as unsigned, negative integers as signed
|
|
||||||
numbers, like the other binary formats do.
|
|
||||||
|
|
||||||
@param[in] number the integer
|
|
||||||
@return whether the SAX parser accepted the value
|
|
||||||
*/
|
|
||||||
bool emit_bon8_integer(const std::int64_t number)
|
|
||||||
{
|
|
||||||
if (number >= 0)
|
|
||||||
{
|
|
||||||
return sax->number_unsigned(static_cast<number_unsigned_t>(number));
|
|
||||||
}
|
|
||||||
return sax->number_integer(static_cast<number_integer_t>(number));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief read an integer encoded in 2..4 bytes
|
|
||||||
|
|
||||||
The first byte is a UTF-8 lead byte (0xC2..0xF7) that is followed by a
|
|
||||||
byte that is not a continuation byte: 0x00..0x7F for positive and
|
|
||||||
0xC0..0xFF for negative integers. The lead byte's low bits and the second
|
|
||||||
byte's low 7 (positive) or 6 (negative) bits are the most significant bits
|
|
||||||
of the value; 3- and 4-byte integers add one or two full bytes. Each range
|
|
||||||
starts where the shorter one ends, so no value has two encodings of the
|
|
||||||
same length.
|
|
||||||
|
|
||||||
@param[in] lead the first byte (0xC2..0xF7)
|
|
||||||
@param[in] second the second byte
|
|
||||||
@return whether reading the integer succeeded
|
|
||||||
*/
|
|
||||||
bool get_bon8_integer(const char_int_type lead, const char_int_type second)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bon8, "number")))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool negative = second >= 0xC0;
|
|
||||||
auto value = static_cast<std::int64_t>(negative ? (second & 0x3F) : second);
|
|
||||||
std::int64_t offset = 0;
|
|
||||||
int extra_bytes = 0;
|
|
||||||
|
|
||||||
if (lead <= 0xDF)
|
|
||||||
{
|
|
||||||
value |= static_cast<std::int64_t>(lead - 0xC2) << (negative ? 6 : 7);
|
|
||||||
offset = negative ? 11 : 40;
|
|
||||||
}
|
|
||||||
else if (lead <= 0xEF)
|
|
||||||
{
|
|
||||||
value |= static_cast<std::int64_t>(lead & 0x0F) << (negative ? 6 : 7);
|
|
||||||
offset = negative ? 1931 : 3880;
|
|
||||||
extra_bytes = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
value |= static_cast<std::int64_t>(lead & 0x07) << (negative ? 6 : 7);
|
|
||||||
offset = negative ? 264075 : 528168;
|
|
||||||
extra_bytes = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < extra_bytes; ++i)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(get_bon8() == char_traits<char_type>::eof()))
|
|
||||||
{
|
|
||||||
return unexpect_eof(input_format_t::bon8, "number");
|
|
||||||
}
|
|
||||||
value = (value << 8) | static_cast<std::int64_t>(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
return negative ? sax->number_integer(static_cast<number_integer_t>(-(value + offset)))
|
|
||||||
: sax->number_unsigned(static_cast<number_unsigned_t>(value + offset));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief read an object key
|
|
||||||
|
|
||||||
A key must be a string, so its first byte must be an ASCII character, a
|
|
||||||
UTF-8 lead byte followed by a continuation byte, or 0xFF (empty string).
|
|
||||||
|
|
||||||
@param[out] result the key
|
|
||||||
@return whether reading the key succeeded
|
|
||||||
*/
|
|
||||||
bool get_bon8_key(string_t& result)
|
|
||||||
{
|
|
||||||
const auto byte = get_bon8();
|
|
||||||
|
|
||||||
if (byte == char_traits<char_type>::eof())
|
|
||||||
{
|
|
||||||
return unexpect_eof(input_format_t::bon8, "key");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (byte == 0xFF)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (byte <= 0x7F)
|
|
||||||
{
|
|
||||||
unget_bon8(byte);
|
|
||||||
return get_bon8_string(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (0xC2 <= byte && byte <= 0xF7)
|
|
||||||
{
|
|
||||||
const auto second = get_bon8();
|
|
||||||
unget_bon8(second);
|
|
||||||
if (is_bon8_continuation(second))
|
|
||||||
{
|
|
||||||
unget_bon8(byte);
|
|
||||||
return get_bon8_string(result);
|
|
||||||
}
|
|
||||||
// an integer: report its first byte rather than the one after it
|
|
||||||
current = byte;
|
|
||||||
}
|
|
||||||
|
|
||||||
return bon8_error("expected a string; last byte", "key");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief append the run of valid UTF-8 at the read position to a string
|
|
||||||
|
|
||||||
For contiguous input, the ASCII characters and complete well-formed UTF-8
|
|
||||||
sequences at the read position are appended to @a result in one step. The
|
|
||||||
byte that stops the run (an end-of-string marker, the first byte of the
|
|
||||||
next value, or an ill-formed byte) is left for @ref get_bon8_string, so
|
|
||||||
that strings end and errors are reported exactly as without this step.
|
|
||||||
|
|
||||||
@param[in,out] result the string to append to
|
|
||||||
*/
|
|
||||||
void get_bon8_string_bulk(string_t& result, std::true_type /*bulk*/)
|
|
||||||
{
|
|
||||||
// bytes handed back must be read through get_bon8() first
|
|
||||||
if (bon8_pushback_size != 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const std::size_t remaining = ia.bulk_remaining();
|
|
||||||
if (remaining == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto* const data = reinterpret_cast<const unsigned char*>(ia.bulk_data());
|
|
||||||
const std::size_t length = valid_utf8_prefix(data, remaining);
|
|
||||||
if (length != 0)
|
|
||||||
{
|
|
||||||
result.append(reinterpret_cast<const typename string_t::value_type*>(data), length);
|
|
||||||
ia.bulk_skip(length);
|
|
||||||
chars_read += length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// input that is not contiguous: strings are read byte by byte
|
|
||||||
void get_bon8_string_bulk(string_t& /*result*/, std::false_type /*bulk*/) const noexcept {}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief read a string
|
|
||||||
|
|
||||||
Reads UTF-8 characters until an end-of-string marker (0xFF), which is
|
|
||||||
consumed, or a byte that cannot continue the string, which is handed back
|
|
||||||
to be read as the start of the next value. The string must be valid UTF-8,
|
|
||||||
and it must not end at the end of the input: the last string of a message
|
|
||||||
is always terminated by 0xFF.
|
|
||||||
|
|
||||||
@param[out] result the string
|
|
||||||
@return whether reading the string succeeded
|
|
||||||
*/
|
|
||||||
bool get_bon8_string(string_t& result)
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
get_bon8_string_bulk(result, std::integral_constant<bool, bulk_scan> {});
|
|
||||||
|
|
||||||
const auto byte = get_bon8();
|
|
||||||
|
|
||||||
if (byte == char_traits<char_type>::eof())
|
|
||||||
{
|
|
||||||
return unexpect_eof(input_format_t::bon8, "string");
|
|
||||||
}
|
|
||||||
|
|
||||||
// end of string
|
|
||||||
if (byte == 0xFF)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ASCII character
|
|
||||||
if (byte <= 0x7F)
|
|
||||||
{
|
|
||||||
result.push_back(static_cast<typename string_t::value_type>(byte));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// a byte that cannot begin a character ends the string and begins
|
|
||||||
// the next value
|
|
||||||
if (byte < 0xC2 || byte > 0xF7)
|
|
||||||
{
|
|
||||||
unget_bon8(byte);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// a lead byte ends the string if no continuation byte follows: it
|
|
||||||
// is then the first byte of an integer
|
|
||||||
const auto second = get_bon8();
|
|
||||||
if (!is_bon8_continuation(second))
|
|
||||||
{
|
|
||||||
unget_bon8(second);
|
|
||||||
unget_bon8(byte);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// the valid range of the second byte excludes overlong forms,
|
|
||||||
// surrogates, and code points above U+10FFFF
|
|
||||||
// (RFC 3629, section 4)
|
|
||||||
int continuation_bytes = 0;
|
|
||||||
bool valid_second = true;
|
|
||||||
if (byte <= 0xDF)
|
|
||||||
{
|
|
||||||
continuation_bytes = 1;
|
|
||||||
}
|
|
||||||
else if (byte <= 0xEF)
|
|
||||||
{
|
|
||||||
continuation_bytes = 2;
|
|
||||||
valid_second = (byte != 0xE0 || second >= 0xA0) && (byte != 0xED || second <= 0x9F);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
continuation_bytes = 3;
|
|
||||||
valid_second = byte <= 0xF4 && (byte != 0xF0 || second >= 0x90) && (byte != 0xF4 || second <= 0x8F);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!valid_second))
|
|
||||||
{
|
|
||||||
return bon8_error("invalid UTF-8 byte", "string");
|
|
||||||
}
|
|
||||||
|
|
||||||
result.push_back(static_cast<typename string_t::value_type>(byte));
|
|
||||||
result.push_back(static_cast<typename string_t::value_type>(second));
|
|
||||||
|
|
||||||
for (int i = 1; i < continuation_bytes; ++i)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(get_bon8() == char_traits<char_type>::eof()))
|
|
||||||
{
|
|
||||||
return unexpect_eof(input_format_t::bon8, "string");
|
|
||||||
}
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!is_bon8_continuation(current)))
|
|
||||||
{
|
|
||||||
return bon8_error("invalid UTF-8 byte", "string");
|
|
||||||
}
|
|
||||||
result.push_back(static_cast<typename string_t::value_type>(current));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////
|
///////////////////////
|
||||||
// Utility functions //
|
// Utility functions //
|
||||||
///////////////////////
|
///////////////////////
|
||||||
@@ -3921,28 +3304,7 @@ class binary_reader
|
|||||||
const NumberType len,
|
const NumberType len,
|
||||||
string_t& result)
|
string_t& result)
|
||||||
{
|
{
|
||||||
// get_bytes() appends to result, and CBOR indefinite-length strings
|
return get_bytes(format, len, "string", result);
|
||||||
// collect all their chunks in the same result; validating only the
|
|
||||||
// newly read bytes keeps the check linear in the input size
|
|
||||||
const std::size_t old_size = result.size();
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_bytes(format, len, "string", result)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// RFC 8949 (CBOR) §3.1 and the MessagePack/BSON/UBJSON specifications
|
|
||||||
// all require text strings to be valid UTF-8; reject anything else
|
|
||||||
// right here so malformed input is caught at decode time instead of
|
|
||||||
// only surfacing later as a type_error.316 when the value is dumped
|
|
||||||
// (which would defeat allow_exceptions=false / strict discarding).
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!is_valid_utf8(result, old_size)))
|
|
||||||
{
|
|
||||||
return sax->parse_error(chars_read, get_token_string(),
|
|
||||||
parse_error::create(113, chars_read,
|
|
||||||
exception_message(format, "invalid string: ill-formed UTF-8 byte", "string"), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -4086,10 +3448,6 @@ class binary_reader
|
|||||||
error_msg += "BJData";
|
error_msg += "BJData";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case input_format_t::bon8:
|
|
||||||
error_msg += "BON8";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case input_format_t::json: // LCOV_EXCL_LINE
|
case input_format_t::json: // LCOV_EXCL_LINE
|
||||||
default: // LCOV_EXCL_LINE
|
default: // LCOV_EXCL_LINE
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||||
@@ -4122,11 +3480,6 @@ class binary_reader
|
|||||||
/// the containers that have been opened and not closed yet; see @ref container_frame
|
/// the containers that have been opened and not closed yet; see @ref container_frame
|
||||||
std::vector<container_frame> container_stack{};
|
std::vector<container_frame> container_stack{};
|
||||||
|
|
||||||
/// BON8: bytes read past the end of a string, returned again by @ref get_bon8
|
|
||||||
std::array<char_int_type, 2> bon8_pushback{{}};
|
|
||||||
/// BON8: number of bytes in @ref bon8_pushback
|
|
||||||
std::size_t bon8_pushback_size = 0;
|
|
||||||
|
|
||||||
// excluded markers in bjdata optimized type
|
// excluded markers in bjdata optimized type
|
||||||
#define JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_ \
|
#define JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_ \
|
||||||
make_array<char_int_type>('F', 'H', 'N', 'S', 'T', 'Z', '[', '{')
|
make_array<char_int_type>('F', 'H', 'N', 'S', 'T', 'Z', '[', '{')
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace detail
|
|||||||
{
|
{
|
||||||
|
|
||||||
/// the supported input formats
|
/// the supported input formats
|
||||||
enum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata, bon8 };
|
enum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata };
|
||||||
|
|
||||||
////////////////////
|
////////////////////
|
||||||
// input adapters //
|
// input adapters //
|
||||||
@@ -101,11 +101,6 @@ class input_stream_adapter
|
|||||||
// maintain ifstream flags, except eof
|
// maintain ifstream flags, except eof
|
||||||
if (is != nullptr)
|
if (is != nullptr)
|
||||||
{
|
{
|
||||||
#if JSON_PRECISE_STREAM_POSITION
|
|
||||||
// consume the character last returned by get_character() unless it
|
|
||||||
// was given back with release_lookahead()
|
|
||||||
commit_lookahead();
|
|
||||||
#endif
|
|
||||||
is->clear(is->rdstate() & std::ios::eofbit);
|
is->clear(is->rdstate() & std::ios::eofbit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,58 +114,6 @@ 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;
|
||||||
|
|
||||||
#if JSON_PRECISE_STREAM_POSITION
|
|
||||||
input_stream_adapter(input_stream_adapter&& rhs) noexcept
|
|
||||||
: is(rhs.is), sb(rhs.sb), lookahead(rhs.lookahead)
|
|
||||||
{
|
|
||||||
rhs.is = nullptr;
|
|
||||||
rhs.sb = nullptr;
|
|
||||||
rhs.lookahead = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Whether the character last returned by get_character() can be given back
|
|
||||||
// to the input with release_lookahead().
|
|
||||||
static constexpr bool supports_lookahead = true;
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// end up as the same value, e.g., 0xFFFFFFFF.
|
|
||||||
//
|
|
||||||
// The character is peeked rather than consumed: it is only stepped over
|
|
||||||
// once the next character is requested, or when the adapter is destroyed.
|
|
||||||
// Until then, release_lookahead() can leave it in the input.
|
|
||||||
std::char_traits<char>::int_type get_character()
|
|
||||||
{
|
|
||||||
if (lookahead)
|
|
||||||
{
|
|
||||||
// step over the character returned by the previous call
|
|
||||||
sb->sbumpc();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto res = sb->sgetc();
|
|
||||||
// set eof manually, as we don't use the istream interface.
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))
|
|
||||||
{
|
|
||||||
// there is nothing to step over next time
|
|
||||||
lookahead = false;
|
|
||||||
is->clear(is->rdstate() | std::ios::eofbit);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lookahead = true;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Leave the character last returned by get_character() in the input, so
|
|
||||||
// that the next read from the stream - by this adapter or by the caller
|
|
||||||
// once parsing is done - sees it again. Unlike putting a consumed
|
|
||||||
// character back, this cannot fail.
|
|
||||||
void release_lookahead() noexcept
|
|
||||||
{
|
|
||||||
lookahead = false;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
input_stream_adapter(input_stream_adapter&& rhs) noexcept
|
input_stream_adapter(input_stream_adapter&& rhs) noexcept
|
||||||
: is(rhs.is), sb(rhs.sb)
|
: is(rhs.is), sb(rhs.sb)
|
||||||
{
|
{
|
||||||
@@ -181,9 +124,6 @@ class input_stream_adapter
|
|||||||
// 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.
|
||||||
//
|
|
||||||
// The character is consumed, so the character that terminates a number
|
|
||||||
// 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()
|
||||||
{
|
{
|
||||||
auto res = sb->sbumpc();
|
auto res = sb->sbumpc();
|
||||||
@@ -194,14 +134,10 @@ class input_stream_adapter
|
|||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
std::size_t get_elements(T* dest, std::size_t count = 1)
|
std::size_t get_elements(T* dest, std::size_t count = 1)
|
||||||
{
|
{
|
||||||
#if JSON_PRECISE_STREAM_POSITION
|
|
||||||
commit_lookahead();
|
|
||||||
#endif
|
|
||||||
auto res = static_cast<std::size_t>(sb->sgetn(reinterpret_cast<char*>(dest), static_cast<std::streamsize>(count * sizeof(T))));
|
auto res = static_cast<std::size_t>(sb->sgetn(reinterpret_cast<char*>(dest), static_cast<std::streamsize>(count * sizeof(T))));
|
||||||
if (JSON_HEDLEY_UNLIKELY(res < count * sizeof(T)))
|
if (JSON_HEDLEY_UNLIKELY(res < count * sizeof(T)))
|
||||||
{
|
{
|
||||||
@@ -211,27 +147,9 @@ class input_stream_adapter
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#if JSON_PRECISE_STREAM_POSITION
|
|
||||||
// Step over the character last returned by get_character(). The character
|
|
||||||
// has already been peeked successfully, so for every streambuf with a get
|
|
||||||
// area this is a pointer increment that cannot fail.
|
|
||||||
void commit_lookahead()
|
|
||||||
{
|
|
||||||
if (lookahead)
|
|
||||||
{
|
|
||||||
lookahead = false;
|
|
||||||
sb->sbumpc();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// the associated input stream
|
/// the associated input stream
|
||||||
std::istream* is = nullptr;
|
std::istream* is = nullptr;
|
||||||
std::streambuf* sb = nullptr;
|
std::streambuf* sb = nullptr;
|
||||||
#if JSON_PRECISE_STREAM_POSITION
|
|
||||||
/// whether get_character() peeked a character that is not consumed yet
|
|
||||||
bool lookahead = false;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
#endif // JSON_NO_IO
|
#endif // JSON_NO_IO
|
||||||
|
|
||||||
|
|||||||
@@ -127,25 +127,6 @@ constexpr bool input_adapter_supports_seek(std::false_type /*detected*/)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect whether an input adapter reads with one character of lookahead that
|
|
||||||
// can be left in the input (see input_stream_adapter::supports_lookahead,
|
|
||||||
// which is only defined with JSON_PRECISE_STREAM_POSITION), detected like
|
|
||||||
// supports_seek above.
|
|
||||||
template<typename InputAdapterType>
|
|
||||||
using detect_supports_lookahead = decltype(InputAdapterType::supports_lookahead);
|
|
||||||
|
|
||||||
template<typename InputAdapterType>
|
|
||||||
constexpr bool input_adapter_supports_lookahead(std::true_type /*detected*/)
|
|
||||||
{
|
|
||||||
return InputAdapterType::supports_lookahead;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename InputAdapterType>
|
|
||||||
constexpr bool input_adapter_supports_lookahead(std::false_type /*detected*/)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect whether an input adapter exposes a contiguous byte block that the
|
// Detect whether an input adapter exposes a contiguous byte block that the
|
||||||
// lexer can scan directly (see iterator_input_adapter::supports_bulk_scan).
|
// lexer can scan directly (see iterator_input_adapter::supports_bulk_scan).
|
||||||
// Adapters without the flag - file, stream, wide-string, user-defined - fall
|
// Adapters without the flag - file, stream, wide-string, user-defined - fall
|
||||||
@@ -186,12 +167,6 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
static constexpr bool lazy_token_string =
|
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 a simulated unget can be passed on to the input adapter, which
|
|
||||||
/// then leaves the character in the input; see
|
|
||||||
/// input_adapter_supports_lookahead
|
|
||||||
static constexpr bool can_release_lookahead =
|
|
||||||
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). This requires
|
/// directly from a contiguous input buffer (SWAR fast path). This requires
|
||||||
/// the token to be reconstructible lazily (lazy_token_string), so bypassing
|
/// the token to be reconstructible lazily (lazy_token_string), so bypassing
|
||||||
@@ -1923,21 +1898,6 @@ scan_number_done:
|
|||||||
uncapture_char(std::integral_constant<bool, lazy_token_string> {});
|
uncapture_char(std::integral_constant<bool, lazy_token_string> {});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// adapter without lookahead: nothing to do (see release_lookahead)
|
|
||||||
void release_lookahead_impl(std::false_type /*can_release*/) const noexcept {}
|
|
||||||
|
|
||||||
/// adapter with lookahead: leave the character in the input instead
|
|
||||||
void release_lookahead_impl(std::true_type /*can_release*/)
|
|
||||||
{
|
|
||||||
if (next_unget)
|
|
||||||
{
|
|
||||||
// the character is read from the input again rather than replayed
|
|
||||||
// from current, so the adapter must not step over it
|
|
||||||
next_unget = false;
|
|
||||||
ia.release_lookahead();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// seekable adapter: nothing was captured, so nothing to undo
|
/// seekable adapter: nothing was captured, so nothing to undo
|
||||||
void uncapture_char(std::true_type /*lazy*/) const noexcept {}
|
void uncapture_char(std::true_type /*lazy*/) const noexcept {}
|
||||||
|
|
||||||
@@ -2001,31 +1961,6 @@ scan_number_done:
|
|||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief pass a pending simulated unget on to the input
|
|
||||||
|
|
||||||
unget() only rewinds the lexer's own bookkeeping, so the character that
|
|
||||||
terminated the last token (e.g. the character after a number) would still
|
|
||||||
be stepped over when the input adapter is done. Callers that hand the
|
|
||||||
input back to the user afterwards - operator>> and non-strict sax_parse -
|
|
||||||
call this once when scanning is done, so that the input is positioned
|
|
||||||
right after the value.
|
|
||||||
|
|
||||||
Adapters without lookahead (see input_adapter_supports_lookahead) are not
|
|
||||||
handed back to the user, so this is a no-op for them. Without
|
|
||||||
JSON_PRECISE_STREAM_POSITION, no adapter has lookahead, so this is always a
|
|
||||||
no-op and the terminating character stays consumed.
|
|
||||||
|
|
||||||
Scanning may continue after this call: @a next_unget is cleared, and the
|
|
||||||
character is read from the input again instead of being replayed from
|
|
||||||
@a current. A pending unget of EOF needs no special case, because reaching
|
|
||||||
EOF leaves no lookahead to release.
|
|
||||||
*/
|
|
||||||
void release_lookahead()
|
|
||||||
{
|
|
||||||
release_lookahead_impl(std::integral_constant<bool, can_release_lookahead> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
/// return the offset of the first character of the last read token; unlike
|
/// return the offset of the first character of the last read token; unlike
|
||||||
/// the token's parsed value, this accounts for escape sequences
|
/// the token's parsed value, this accounts for escape sequences
|
||||||
|
|||||||
@@ -100,22 +100,13 @@ class parser
|
|||||||
json_sax_dom_callback_parser<BasicJsonType, InputAdapterType> sdp(result, callback, allow_exceptions, &m_lexer);
|
json_sax_dom_callback_parser<BasicJsonType, InputAdapterType> sdp(result, callback, allow_exceptions, &m_lexer);
|
||||||
sax_parse_internal(&sdp);
|
sax_parse_internal(&sdp);
|
||||||
|
|
||||||
if (strict)
|
// in strict mode, input must be completely read
|
||||||
|
if (strict && (get_token() != token_type::end_of_input))
|
||||||
{
|
{
|
||||||
// in strict mode, input must be completely read
|
sdp.parse_error(m_lexer.get_position(),
|
||||||
if (get_token() != token_type::end_of_input)
|
m_lexer.get_token_string(),
|
||||||
{
|
parse_error::create(101, m_lexer.get_position(),
|
||||||
sdp.parse_error(m_lexer.get_position(),
|
exception_message(token_type::end_of_input, "value"), nullptr));
|
||||||
m_lexer.get_token_string(),
|
|
||||||
parse_error::create(101, m_lexer.get_position(),
|
|
||||||
exception_message(token_type::end_of_input, "value"), nullptr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// the caller keeps using the input: position it right after
|
|
||||||
// the value by leaving the character that terminated it
|
|
||||||
m_lexer.release_lookahead();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// in case of an error, return a discarded value
|
// in case of an error, return a discarded value
|
||||||
@@ -137,20 +128,12 @@ class parser
|
|||||||
json_sax_dom_parser<BasicJsonType, InputAdapterType> sdp(result, allow_exceptions, &m_lexer);
|
json_sax_dom_parser<BasicJsonType, InputAdapterType> sdp(result, allow_exceptions, &m_lexer);
|
||||||
sax_parse_internal(&sdp);
|
sax_parse_internal(&sdp);
|
||||||
|
|
||||||
if (strict)
|
// in strict mode, input must be completely read
|
||||||
|
if (strict && (get_token() != token_type::end_of_input))
|
||||||
{
|
{
|
||||||
// in strict mode, input must be completely read
|
sdp.parse_error(m_lexer.get_position(),
|
||||||
if (get_token() != token_type::end_of_input)
|
m_lexer.get_token_string(),
|
||||||
{
|
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
|
||||||
sdp.parse_error(m_lexer.get_position(),
|
|
||||||
m_lexer.get_token_string(),
|
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// see above
|
|
||||||
m_lexer.release_lookahead();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// in case of an error, return a discarded value
|
// in case of an error, return a discarded value
|
||||||
@@ -183,24 +166,12 @@ class parser
|
|||||||
(void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
|
(void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
|
||||||
const bool result = sax_parse_internal(sax);
|
const bool result = sax_parse_internal(sax);
|
||||||
|
|
||||||
if (result)
|
// strict mode: next byte must be EOF
|
||||||
|
if (result && strict && (get_token() != token_type::end_of_input))
|
||||||
{
|
{
|
||||||
if (strict)
|
return sax->parse_error(m_lexer.get_position(),
|
||||||
{
|
m_lexer.get_token_string(),
|
||||||
// strict mode: next byte must be EOF
|
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
|
||||||
if (get_token() != token_type::end_of_input)
|
|
||||||
{
|
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
|
||||||
m_lexer.get_token_string(),
|
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// the caller keeps using the input: position it right after
|
|
||||||
// the value by leaving the character that terminated it
|
|
||||||
m_lexer.release_lookahead();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -201,43 +201,6 @@ inline std::size_t validate_one_utf8(const unsigned char* data, std::size_t avai
|
|||||||
return 0; // invalid, incomplete, or must be diagnosed by the byte path
|
return 0; // invalid, incomplete, or must be diagnosed by the byte path
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the length of the longest prefix of [data, data+n) that consists of
|
|
||||||
// ASCII characters and complete well-formed UTF-8 sequences; n if all of it is
|
|
||||||
// valid UTF-8. Unlike scalar_string_bulk_run(), quotes, escapes, and control
|
|
||||||
// characters are ordinary characters here. ASCII is skipped 8 bytes at a time.
|
|
||||||
inline std::size_t valid_utf8_prefix(const unsigned char* data, std::size_t n) noexcept
|
|
||||||
{
|
|
||||||
constexpr std::uint64_t high = 0x8080808080808080ull;
|
|
||||||
std::size_t pos = 0;
|
|
||||||
while (pos < n)
|
|
||||||
{
|
|
||||||
if (pos + 8 <= n)
|
|
||||||
{
|
|
||||||
std::uint64_t word = 0;
|
|
||||||
std::memcpy(&word, data + pos, sizeof(word));
|
|
||||||
if ((word & high) == 0)
|
|
||||||
{
|
|
||||||
pos += 8;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data[pos] < 0x80u)
|
|
||||||
{
|
|
||||||
++pos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::size_t seq = validate_one_utf8(data + pos, n - pos);
|
|
||||||
if (seq == 0)
|
|
||||||
{
|
|
||||||
break; // ill-formed or truncated
|
|
||||||
}
|
|
||||||
pos += seq;
|
|
||||||
}
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scalar (C++11) computation of the bulk run length: the number of leading
|
// Scalar (C++11) computation of the bulk run length: the number of leading
|
||||||
// bytes in [data, data+n) that are ordinary ASCII or complete well-formed UTF-8
|
// bytes in [data, data+n) that are ordinary ASCII or complete well-formed UTF-8
|
||||||
// sequences, stopping before the first byte that needs individual handling (the
|
// sequences, stopping before the first byte that needs individual handling (the
|
||||||
|
|||||||
@@ -596,62 +596,16 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
|
|
||||||
#define NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(...) template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> __VA_ARGS__
|
#define NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(...) template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> __VA_ARGS__
|
||||||
|
|
||||||
// Helpers used to dispatch the NLOHMANN_DEFINE_TYPE_*/NLOHMANN_DEFINE_DERIVED_TYPE_*
|
|
||||||
// macros below between a zero-member and a one-or-more-member implementation
|
|
||||||
// (issue #4041, e.g. NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type) with no further
|
|
||||||
// arguments). NLOHMANN_JSON_TYPE_BODY(Prefix, ...) expands to the macro name
|
|
||||||
// Prefix##EMPTY when __VA_ARGS__ is a single argument (Type alone) and to
|
|
||||||
// Prefix##MEMBERS for two or more (Type, member...). It reuses the existing
|
|
||||||
// 64-slot NLOHMANN_JSON_GET_MACRO dispatch with one extra trailing sentinel
|
|
||||||
// token appended so its own trailing "..." is never left completely empty at
|
|
||||||
// the lowest supported argument count -- invoking a variadic macro so that
|
|
||||||
// "..." matches nothing is only granted unconditionally by the standard since
|
|
||||||
// C++20, and pre-C++20 compilers may reject it under -pedantic regardless of
|
|
||||||
// what the macro body does.
|
|
||||||
//
|
|
||||||
// The EMPTY/MEMBERS suffixes are pasted onto Prefix right in the slot table:
|
|
||||||
// operands of ## are not macro-expanded, so the dispatch keeps working even if
|
|
||||||
// user code defines macros named EMPTY or MEMBERS. Producing the bare suffix
|
|
||||||
// first and pasting it later would let such a macro replace it.
|
|
||||||
//
|
|
||||||
// NLOHMANN_JSON_DERIVED_TYPE_BODY(Prefix, ...) answers the same question for
|
|
||||||
// the derived-type macros, whose fixed prefix is Type,BaseType. It drops the
|
|
||||||
// leading Type and defers to NLOHMANN_JSON_TYPE_BODY rather than shifting the
|
|
||||||
// slot table by one: NLOHMANN_JSON_GET_MACRO only resolves 64 positional
|
|
||||||
// arguments, so dispatching on Type,BaseType,member... directly would run out
|
|
||||||
// one slot early and cap the derived-type macros at 62 members instead of the
|
|
||||||
// 63 that NLOHMANN_JSON_PASTE supports.
|
|
||||||
#define NLOHMANN_JSON_TYPE_BODY(Prefix, ...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \
|
|
||||||
Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, \
|
|
||||||
Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, \
|
|
||||||
Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, \
|
|
||||||
Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, \
|
|
||||||
Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, \
|
|
||||||
Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, \
|
|
||||||
Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, \
|
|
||||||
Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## EMPTY, \
|
|
||||||
NLOHMANN_JSON_TYPE_BODY_SENTINEL))
|
|
||||||
|
|
||||||
#define NLOHMANN_JSON_DERIVED_TYPE_BODY_(Prefix, Type, ...) NLOHMANN_JSON_TYPE_BODY(Prefix, __VA_ARGS__)
|
|
||||||
#define NLOHMANN_JSON_DERIVED_TYPE_BODY(Prefix, ...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DERIVED_TYPE_BODY_(Prefix, __VA_ARGS__))
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief macro
|
@brief macro
|
||||||
@def NLOHMANN_DEFINE_TYPE_INTRUSIVE
|
@def NLOHMANN_DEFINE_TYPE_INTRUSIVE
|
||||||
@since version 3.9.0
|
@since version 3.9.0
|
||||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
|
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
|
||||||
*/
|
*/
|
||||||
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_MEMBERS(Type, ...) \
|
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) })
|
||||||
|
|
||||||
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_EMPTY(Type) \
|
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type&) { nlohmann_json_j = BasicJsonType::object(); }) \
|
|
||||||
/* NOLINTNEXTLINE(bugprone-macro-parentheses) Type is used as a declarator type, not in an expression */ \
|
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType&, Type&) noexcept { })
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_TYPE_BODY(NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_, __VA_ARGS__)(__VA_ARGS__))
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_NAMES(Type, ...) \
|
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_NAMES(Type, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) })
|
||||||
@@ -662,15 +616,10 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
@since version 3.11.0
|
@since version 3.11.0
|
||||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
|
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
|
||||||
*/
|
*/
|
||||||
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, ...) \
|
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) })
|
||||||
|
|
||||||
// identical to NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_EMPTY: with no members there is nothing to default
|
|
||||||
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_EMPTY(Type) NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_EMPTY(Type)
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_TYPE_BODY(NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_, __VA_ARGS__)(__VA_ARGS__))
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, ...) \
|
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) })
|
||||||
@@ -681,14 +630,9 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
@since version 3.11.3
|
@since version 3.11.3
|
||||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
|
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
|
||||||
*/
|
*/
|
||||||
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_MEMBERS(Type, ...) \
|
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) })
|
||||||
|
|
||||||
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_EMPTY(Type) \
|
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type&) { nlohmann_json_j = BasicJsonType::object(); })
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_TYPE_BODY(NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_, __VA_ARGS__)(__VA_ARGS__))
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(Type, ...) \
|
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(Type, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) })
|
||||||
|
|
||||||
@@ -698,17 +642,10 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
@since version 3.9.0
|
@since version 3.9.0
|
||||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
|
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
|
||||||
*/
|
*/
|
||||||
#define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_MEMBERS(Type, ...) \
|
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) })
|
||||||
|
|
||||||
#define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_EMPTY(Type) \
|
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type&) { nlohmann_json_j = BasicJsonType::object(); }) \
|
|
||||||
/* NOLINTNEXTLINE(bugprone-macro-parentheses) Type is used as a declarator type, not in an expression */ \
|
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType&, Type&) noexcept { })
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_TYPE_BODY(NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_, __VA_ARGS__)(__VA_ARGS__))
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_NAMES(Type, ...) \
|
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_NAMES(Type, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) })
|
||||||
@@ -719,15 +656,10 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
@since version 3.11.0
|
@since version 3.11.0
|
||||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
|
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
|
||||||
*/
|
*/
|
||||||
#define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, ...) \
|
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) })
|
||||||
|
|
||||||
// identical to NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_EMPTY: with no members there is nothing to default
|
|
||||||
#define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_EMPTY(Type) NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_EMPTY(Type)
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_TYPE_BODY(NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_, __VA_ARGS__)(__VA_ARGS__))
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, ...) \
|
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) })
|
||||||
@@ -738,14 +670,9 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
@since version 3.11.3
|
@since version 3.11.3
|
||||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
|
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
|
||||||
*/
|
*/
|
||||||
#define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_MEMBERS(Type, ...) \
|
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) })
|
||||||
|
|
||||||
#define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_EMPTY(Type) \
|
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type&) { nlohmann_json_j = BasicJsonType::object(); })
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_TYPE_BODY(NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_, __VA_ARGS__)(__VA_ARGS__))
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(Type, ...) \
|
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(Type, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) })
|
||||||
|
|
||||||
@@ -755,17 +682,10 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
@since version 3.12.0
|
@since version 3.12.0
|
||||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
||||||
*/
|
*/
|
||||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_MEMBERS(Type, BaseType, ...) \
|
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(Type, BaseType, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) })
|
||||||
|
|
||||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_EMPTY(Type, BaseType) \
|
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); }) \
|
|
||||||
/* NOLINTNEXTLINE(bugprone-macro-parentheses) Type/BaseType are used as declarator types, not in expressions */ \
|
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); })
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DERIVED_TYPE_BODY(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_, __VA_ARGS__)(__VA_ARGS__))
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_NAMES(Type, BaseType, ...) \
|
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_NAMES(Type, BaseType, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) })
|
||||||
@@ -776,15 +696,10 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
@since version 3.12.0
|
@since version 3.12.0
|
||||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
||||||
*/
|
*/
|
||||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, BaseType, ...) \
|
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) })
|
||||||
|
|
||||||
// identical to NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_EMPTY: with no members there is nothing to default
|
|
||||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_EMPTY(Type, BaseType) NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_EMPTY(Type, BaseType)
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DERIVED_TYPE_BODY(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_, __VA_ARGS__)(__VA_ARGS__))
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, BaseType, ...) \
|
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, BaseType, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) })
|
||||||
@@ -795,14 +710,9 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
@since version 3.12.0
|
@since version 3.12.0
|
||||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
||||||
*/
|
*/
|
||||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_MEMBERS(Type, BaseType, ...) \
|
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) })
|
||||||
|
|
||||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_EMPTY(Type, BaseType) \
|
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); })
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DERIVED_TYPE_BODY(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_, __VA_ARGS__)(__VA_ARGS__))
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(Type, BaseType, ...) \
|
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(Type, BaseType, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) })
|
||||||
|
|
||||||
@@ -813,17 +723,10 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
@since version 3.12.0
|
@since version 3.12.0
|
||||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
||||||
*/
|
*/
|
||||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_MEMBERS(Type, BaseType, ...) \
|
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(Type, BaseType, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) })
|
||||||
|
|
||||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_EMPTY(Type, BaseType) \
|
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); }) \
|
|
||||||
/* NOLINTNEXTLINE(bugprone-macro-parentheses) Type/BaseType are used as declarator types, not in expressions */ \
|
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); })
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DERIVED_TYPE_BODY(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_, __VA_ARGS__)(__VA_ARGS__))
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_NAMES(Type, BaseType, ...) \
|
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_NAMES(Type, BaseType, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) })
|
||||||
@@ -834,15 +737,10 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
@since version 3.12.0
|
@since version 3.12.0
|
||||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
||||||
*/
|
*/
|
||||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, BaseType, ...) \
|
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) })
|
||||||
|
|
||||||
// identical to NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_EMPTY: with no members there is nothing to default
|
|
||||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_EMPTY(Type, BaseType) NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_EMPTY(Type, BaseType)
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DERIVED_TYPE_BODY(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_, __VA_ARGS__)(__VA_ARGS__))
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, BaseType, ...) \
|
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, BaseType, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) })
|
||||||
@@ -853,14 +751,9 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
@since version 3.12.0
|
@since version 3.12.0
|
||||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
||||||
*/
|
*/
|
||||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_MEMBERS(Type, BaseType, ...) \
|
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) })
|
||||||
|
|
||||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_EMPTY(Type, BaseType) \
|
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); })
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DERIVED_TYPE_BODY(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_, __VA_ARGS__)(__VA_ARGS__))
|
|
||||||
|
|
||||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(Type, BaseType, ...) \
|
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(Type, BaseType, ...) \
|
||||||
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) })
|
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) })
|
||||||
|
|
||||||
@@ -919,3 +812,7 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
#ifndef JSON_USE_GLOBAL_UDLS
|
#ifndef JSON_USE_GLOBAL_UDLS
|
||||||
#define JSON_USE_GLOBAL_UDLS 1
|
#define JSON_USE_GLOBAL_UDLS 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef JSON_STRICT_NUL_HANDLING
|
||||||
|
#define JSON_STRICT_NUL_HANDLING 0
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#undef JSON_NO_UNIQUE_ADDRESS
|
#undef JSON_NO_UNIQUE_ADDRESS
|
||||||
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
||||||
#undef JSON_USE_GLOBAL_UDLS
|
#undef JSON_USE_GLOBAL_UDLS
|
||||||
|
#undef JSON_STRICT_NUL_HANDLING
|
||||||
|
|
||||||
#ifndef JSON_TEST_KEEP_MACROS
|
#ifndef JSON_TEST_KEEP_MACROS
|
||||||
#undef JSON_CATCH
|
#undef JSON_CATCH
|
||||||
@@ -44,8 +45,6 @@
|
|||||||
#undef JSON_HAS_STATIC_RTTI
|
#undef JSON_HAS_STATIC_RTTI
|
||||||
#undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
#undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||||
#undef JSON_BRACE_INIT_COPY_SEMANTICS
|
#undef JSON_BRACE_INIT_COPY_SEMANTICS
|
||||||
#undef JSON_PRECISE_STREAM_POSITION
|
|
||||||
#undef JSON_STRICT_NUL_HANDLING
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
|
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <nlohmann/detail/input/binary_reader.hpp>
|
#include <nlohmann/detail/input/binary_reader.hpp>
|
||||||
#include <nlohmann/detail/input/string_scan.hpp>
|
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
#include <nlohmann/detail/output/output_adapters.hpp>
|
#include <nlohmann/detail/output/output_adapters.hpp>
|
||||||
#include <nlohmann/detail/string_concat.hpp>
|
#include <nlohmann/detail/string_concat.hpp>
|
||||||
@@ -77,7 +76,7 @@ std::size_t binary_reserve_hint(const BasicJsonType& j)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief serialization to BJData, BON8, BSON, CBOR, MessagePack, and UBJSON values
|
@brief serialization to CBOR and MessagePack values
|
||||||
*/
|
*/
|
||||||
template<typename BasicJsonType, typename CharType, typename OutputSinkType = output_adapter_sink<CharType>>
|
template<typename BasicJsonType, typename CharType, typename OutputSinkType = output_adapter_sink<CharType>>
|
||||||
class binary_writer
|
class binary_writer
|
||||||
@@ -123,7 +122,7 @@ class binary_writer
|
|||||||
{
|
{
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
{
|
{
|
||||||
write_bson_document(j);
|
write_bson_object(*j.m_data.m_value.object);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1033,21 +1032,6 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@param[in] j JSON value to serialize
|
|
||||||
*/
|
|
||||||
void write_bon8(const BasicJsonType& j)
|
|
||||||
{
|
|
||||||
bool string_open = false;
|
|
||||||
write_bon8_value(j, string_open);
|
|
||||||
|
|
||||||
// the last string of a message must be terminated
|
|
||||||
if (string_open)
|
|
||||||
{
|
|
||||||
oa.write_character(to_char_type(0xFF));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//////////
|
//////////
|
||||||
// BSON //
|
// BSON //
|
||||||
@@ -1213,6 +1197,35 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Writes a BSON element with key @a name and object @a value
|
||||||
|
*/
|
||||||
|
void write_bson_object_entry(const string_t& name,
|
||||||
|
const typename BasicJsonType::object_t& value)
|
||||||
|
{
|
||||||
|
write_bson_entry_header(name, 0x03); // object
|
||||||
|
write_bson_object(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@return The size of the BSON-encoded array @a value
|
||||||
|
*/
|
||||||
|
static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value)
|
||||||
|
{
|
||||||
|
std::size_t array_index = 0ul;
|
||||||
|
|
||||||
|
const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), static_cast<std::size_t>(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el)
|
||||||
|
{
|
||||||
|
// the index is built as a std::string, while calc_bson_element_size
|
||||||
|
// takes a string_t; convert explicitly, as the two are only
|
||||||
|
// implicitly convertible for some string types
|
||||||
|
const auto key = std::to_string(array_index++);
|
||||||
|
return result + calc_bson_element_size(string_t(key.data(), key.size()), el);
|
||||||
|
});
|
||||||
|
|
||||||
|
return sizeof(std::int32_t) + embedded_document_size + 1ul;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@return The size of the BSON-encoded binary array @a value
|
@return The size of the BSON-encoded binary array @a value
|
||||||
*/
|
*/
|
||||||
@@ -1221,6 +1234,29 @@ class binary_writer
|
|||||||
return sizeof(std::int32_t) + value.size() + 1ul;
|
return sizeof(std::int32_t) + value.size() + 1ul;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Writes a BSON element with key @a name and array @a value
|
||||||
|
*/
|
||||||
|
void write_bson_array(const string_t& name,
|
||||||
|
const typename BasicJsonType::array_t& value)
|
||||||
|
{
|
||||||
|
write_bson_entry_header(name, 0x04); // array
|
||||||
|
write_number<std::int32_t>(to_bson_length(calc_bson_array_size(value)), true);
|
||||||
|
|
||||||
|
std::size_t array_index = 0ul;
|
||||||
|
|
||||||
|
for (const auto& el : value)
|
||||||
|
{
|
||||||
|
// the index is built as a std::string, while write_bson_element takes
|
||||||
|
// a string_t; convert explicitly, as the two are only implicitly
|
||||||
|
// convertible for some string types
|
||||||
|
const auto key = std::to_string(array_index++);
|
||||||
|
write_bson_element(string_t(key.data(), key.size()), el);
|
||||||
|
}
|
||||||
|
|
||||||
|
oa.write_character(to_char_type(0x00));
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Writes a BSON element with key @a name and binary value @a value
|
@brief Writes a BSON element with key @a name and binary value @a value
|
||||||
*/
|
*/
|
||||||
@@ -1242,37 +1278,43 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@return The size of the value of the BSON document entry for @a j, which
|
@brief Calculates the size necessary to serialize the JSON value @a j with its @a name
|
||||||
is neither an object nor an array
|
@return The calculated size for the BSON document entry for @a j with the given @a name.
|
||||||
*/
|
*/
|
||||||
static std::size_t calc_bson_value_size(const BasicJsonType& j)
|
static std::size_t calc_bson_element_size(const string_t& name,
|
||||||
|
const BasicJsonType& j)
|
||||||
{
|
{
|
||||||
|
const auto header_size = calc_bson_entry_header_size(name, j);
|
||||||
switch (j.type())
|
switch (j.type())
|
||||||
{
|
{
|
||||||
|
case value_t::object:
|
||||||
|
return header_size + calc_bson_object_size(*j.m_data.m_value.object);
|
||||||
|
|
||||||
|
case value_t::array:
|
||||||
|
return header_size + calc_bson_array_size(*j.m_data.m_value.array);
|
||||||
|
|
||||||
case value_t::binary:
|
case value_t::binary:
|
||||||
return calc_bson_binary_size(*j.m_data.m_value.binary);
|
return header_size + calc_bson_binary_size(*j.m_data.m_value.binary);
|
||||||
|
|
||||||
case value_t::boolean:
|
case value_t::boolean:
|
||||||
return 1ul;
|
return header_size + 1ul;
|
||||||
|
|
||||||
case value_t::number_float:
|
case value_t::number_float:
|
||||||
return 8ul;
|
return header_size + 8ul;
|
||||||
|
|
||||||
case value_t::number_integer:
|
case value_t::number_integer:
|
||||||
return calc_bson_integer_size(j.m_data.m_value.number_integer);
|
return header_size + calc_bson_integer_size(j.m_data.m_value.number_integer);
|
||||||
|
|
||||||
case value_t::number_unsigned:
|
case value_t::number_unsigned:
|
||||||
return calc_bson_unsigned_size(j.m_data.m_value.number_unsigned);
|
return header_size + calc_bson_unsigned_size(j.m_data.m_value.number_unsigned);
|
||||||
|
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
return calc_bson_string_size(*j.m_data.m_value.string);
|
return header_size + calc_bson_string_size(*j.m_data.m_value.string);
|
||||||
|
|
||||||
case value_t::null:
|
case value_t::null:
|
||||||
return 0ul;
|
return header_size + 0ul;
|
||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
case value_t::object:
|
|
||||||
case value_t::array:
|
|
||||||
case value_t::discarded:
|
case value_t::discarded:
|
||||||
default:
|
default:
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
|
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
|
||||||
@@ -1282,13 +1324,22 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Writes the BSON document entry with key @a name for @a j, which is
|
@brief Serializes the JSON value @a j to BSON and associates it with the
|
||||||
neither an object nor an array
|
key @a name.
|
||||||
|
@param name The name to associate with the JSON entity @a j within the
|
||||||
|
current BSON document
|
||||||
*/
|
*/
|
||||||
void write_bson_value(const string_t& name, const BasicJsonType& j)
|
void write_bson_element(const string_t& name,
|
||||||
|
const BasicJsonType& j)
|
||||||
{
|
{
|
||||||
switch (j.type())
|
switch (j.type())
|
||||||
{
|
{
|
||||||
|
case value_t::object:
|
||||||
|
return write_bson_object_entry(name, *j.m_data.m_value.object);
|
||||||
|
|
||||||
|
case value_t::array:
|
||||||
|
return write_bson_array(name, *j.m_data.m_value.array);
|
||||||
|
|
||||||
case value_t::binary:
|
case value_t::binary:
|
||||||
return write_bson_binary(name, *j.m_data.m_value.binary);
|
return write_bson_binary(name, *j.m_data.m_value.binary);
|
||||||
|
|
||||||
@@ -1311,8 +1362,6 @@ class binary_writer
|
|||||||
return write_bson_null(name);
|
return write_bson_null(name);
|
||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
case value_t::object:
|
|
||||||
case value_t::array:
|
|
||||||
case value_t::discarded:
|
case value_t::discarded:
|
||||||
default:
|
default:
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
|
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
|
||||||
@@ -1321,221 +1370,37 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief an object or array of the BSON document being sized or written
|
|
||||||
struct bson_frame
|
|
||||||
{
|
|
||||||
explicit bson_frame(const BasicJsonType* value_, const std::size_t size_slot_ = 0)
|
|
||||||
: value(value_)
|
|
||||||
, size_slot(size_slot_)
|
|
||||||
{
|
|
||||||
if (value->is_object())
|
|
||||||
{
|
|
||||||
member = value->m_data.m_value.object->cbegin();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// the object or array
|
|
||||||
const BasicJsonType* value;
|
|
||||||
/// objects: the next member
|
|
||||||
typename BasicJsonType::object_t::const_iterator member{};
|
|
||||||
/// arrays: the index of the next element
|
|
||||||
std::size_t index = 0;
|
|
||||||
/// @ref calc_bson_sizes only: where its size goes in the table
|
|
||||||
std::size_t size_slot;
|
|
||||||
/// @ref calc_bson_sizes only: the size of its entries seen so far
|
|
||||||
std::size_t entries_size = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief creates the name BSON gives the array element with index @a index
|
@brief Calculates the size of the BSON serialization of the given
|
||||||
@param[out] name receives the decimal index
|
JSON-object @a j.
|
||||||
|
@param[in] value JSON value to serialize
|
||||||
|
@pre value.type() == value_t::object
|
||||||
*/
|
*/
|
||||||
static void create_bson_index_name(const std::size_t index, string_t& name)
|
static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value)
|
||||||
{
|
{
|
||||||
// the index is built as a std::string; convert explicitly, as the
|
const std::size_t document_size = std::accumulate(value.begin(), value.end(), static_cast<std::size_t>(0),
|
||||||
// two are only implicitly convertible for some string types
|
[](size_t result, const typename BasicJsonType::object_t::value_type & el)
|
||||||
const auto key = std::to_string(index);
|
{
|
||||||
name = string_t(key.data(), key.size());
|
return result += calc_bson_element_size(el.first, el.second);
|
||||||
|
});
|
||||||
|
|
||||||
|
return sizeof(std::int32_t) + document_size + 1ul;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Calculates the size of every object and array in the BSON document
|
@param[in] value JSON value to serialize
|
||||||
@a document, including the document itself.
|
@pre value.type() == value_t::object
|
||||||
|
|
||||||
BSON prefixes every document and array with its size, so all of them have
|
|
||||||
to be known before the first byte is written. They are computed in a
|
|
||||||
single pass, each one from the sizes of its entries, which keeps
|
|
||||||
serializing linear in the size of the document; computing each size by
|
|
||||||
walking the entire value below it made it quadratic in the nesting depth.
|
|
||||||
The pass keeps the objects and arrays it has entered on an explicit stack,
|
|
||||||
so a deeply nested value cannot exhaust the call stack.
|
|
||||||
|
|
||||||
@param[in] document the JSON object to serialize
|
|
||||||
@param[out] nested_sizes the sizes of the objects and arrays in
|
|
||||||
@a document, in the order they are written
|
|
||||||
@return the size of @a document
|
|
||||||
@throw out_of_range.409 if a key contains U+0000, before anything is
|
|
||||||
written
|
|
||||||
*/
|
*/
|
||||||
static std::size_t calc_bson_sizes(const BasicJsonType& document, std::vector<std::size_t>& nested_sizes)
|
void write_bson_object(const typename BasicJsonType::object_t& value)
|
||||||
{
|
{
|
||||||
// the object or array whose entries are being sized, and the ones it
|
write_number<std::int32_t>(to_bson_length(calc_bson_object_size(value)), true);
|
||||||
// is in; nothing is allocated unless the document nests
|
|
||||||
bson_frame current(&document);
|
|
||||||
std::vector<bson_frame> parents;
|
|
||||||
// string_t need not be default constructible
|
|
||||||
string_t index_name("", 0);
|
|
||||||
|
|
||||||
while (true)
|
for (const auto& el : value)
|
||||||
{
|
{
|
||||||
// size entries until the current object or array is done, or an
|
write_bson_element(el.first, el.second);
|
||||||
// entry is an object or array itself
|
|
||||||
const BasicJsonType* nested = nullptr;
|
|
||||||
if (current.value->is_object())
|
|
||||||
{
|
|
||||||
const auto& object = *current.value->m_data.m_value.object;
|
|
||||||
while (nested == nullptr && current.member != object.cend())
|
|
||||||
{
|
|
||||||
const auto& el = *current.member;
|
|
||||||
++current.member;
|
|
||||||
current.entries_size += calc_bson_entry_header_size(el.first, el.second);
|
|
||||||
if (el.second.is_structured())
|
|
||||||
{
|
|
||||||
nested = &el.second;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
current.entries_size += calc_bson_value_size(el.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto& array = *current.value->m_data.m_value.array;
|
|
||||||
while (nested == nullptr && current.index < array.size())
|
|
||||||
{
|
|
||||||
const BasicJsonType& el = array[current.index];
|
|
||||||
create_bson_index_name(current.index, index_name);
|
|
||||||
current.entries_size += calc_bson_entry_header_size(index_name, el);
|
|
||||||
++current.index;
|
|
||||||
if (el.is_structured())
|
|
||||||
{
|
|
||||||
nested = ⪙
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
current.entries_size += calc_bson_value_size(el);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nested != nullptr)
|
|
||||||
{
|
|
||||||
// its size is added to the current one's once it is done
|
|
||||||
nested_sizes.push_back(0);
|
|
||||||
parents.push_back(std::move(current));
|
|
||||||
current = bson_frame(nested, nested_sizes.size() - 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// the int32 size, the entries, and the terminating null byte
|
|
||||||
const std::size_t size = sizeof(std::int32_t) + current.entries_size + 1ul;
|
|
||||||
if (parents.empty())
|
|
||||||
{
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
nested_sizes[current.size_slot] = size;
|
|
||||||
current = std::move(parents.back());
|
|
||||||
parents.pop_back();
|
|
||||||
current.entries_size += size;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
oa.write_character(to_char_type(0x00));
|
||||||
@brief Serializes the JSON object @a document as a BSON document
|
|
||||||
|
|
||||||
Writes the objects and arrays in it without the call stack, keeping the
|
|
||||||
ones it has entered on an explicit stack, so a deeply nested value
|
|
||||||
cannot exhaust the call stack.
|
|
||||||
|
|
||||||
@param[in] document the JSON object to serialize
|
|
||||||
@pre document.type() == value_t::object
|
|
||||||
*/
|
|
||||||
void write_bson_document(const BasicJsonType& document)
|
|
||||||
{
|
|
||||||
std::vector<std::size_t> nested_sizes;
|
|
||||||
const std::size_t document_size = calc_bson_sizes(document, nested_sizes);
|
|
||||||
write_number<std::int32_t>(to_bson_length(document_size), true);
|
|
||||||
|
|
||||||
// the object or array whose entries are being written, and the ones
|
|
||||||
// it is in
|
|
||||||
bson_frame current(&document);
|
|
||||||
std::vector<bson_frame> parents;
|
|
||||||
std::size_t next_size = 0;
|
|
||||||
// string_t need not be default constructible
|
|
||||||
string_t index_name("", 0);
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
// write entries until the current object or array is done, or an
|
|
||||||
// entry is an object or array itself
|
|
||||||
const string_t* nested_name = nullptr;
|
|
||||||
const BasicJsonType* nested = nullptr;
|
|
||||||
if (current.value->is_object())
|
|
||||||
{
|
|
||||||
const auto& object = *current.value->m_data.m_value.object;
|
|
||||||
while (nested == nullptr && current.member != object.cend())
|
|
||||||
{
|
|
||||||
const auto& el = *current.member;
|
|
||||||
++current.member;
|
|
||||||
if (el.second.is_structured())
|
|
||||||
{
|
|
||||||
nested_name = &el.first;
|
|
||||||
nested = &el.second;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
write_bson_value(el.first, el.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto& array = *current.value->m_data.m_value.array;
|
|
||||||
while (nested == nullptr && current.index < array.size())
|
|
||||||
{
|
|
||||||
const BasicJsonType& el = array[current.index];
|
|
||||||
create_bson_index_name(current.index, index_name);
|
|
||||||
++current.index;
|
|
||||||
if (el.is_structured())
|
|
||||||
{
|
|
||||||
nested_name = &index_name;
|
|
||||||
nested = ⪙
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
write_bson_value(index_name, el);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nested != nullptr)
|
|
||||||
{
|
|
||||||
write_bson_entry_header(*nested_name, nested->is_object() ? 0x03 : 0x04);
|
|
||||||
write_number<std::int32_t>(to_bson_length(nested_sizes[next_size++]), true);
|
|
||||||
parents.push_back(std::move(current));
|
|
||||||
current = bson_frame(nested);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
oa.write_character(to_char_type(0x00));
|
|
||||||
if (parents.empty())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
current = std::move(parents.back());
|
|
||||||
parents.pop_back();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////
|
//////////
|
||||||
@@ -1566,28 +1431,6 @@ class binary_writer
|
|||||||
return to_char_type(0xCB); // float 64
|
return to_char_type(0xCB); // float 64
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @return the BON8 type marker for binary32 (float) or binary64 (double)
|
|
||||||
template<typename FloatType>
|
|
||||||
static constexpr CharType get_bon8_float_prefix()
|
|
||||||
{
|
|
||||||
return to_char_type(std::is_same<FloatType, float>::value ? 0x8E : 0x8F);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @return the type marker for a FloatType value in @a format (CBOR, MessagePack, or BON8)
|
|
||||||
template<typename FloatType>
|
|
||||||
static CharType get_compact_float_prefix(const detail::input_format_t format)
|
|
||||||
{
|
|
||||||
if (format == detail::input_format_t::cbor)
|
|
||||||
{
|
|
||||||
return get_cbor_float_prefix(FloatType{});
|
|
||||||
}
|
|
||||||
if (format == detail::input_format_t::bon8)
|
|
||||||
{
|
|
||||||
return get_bon8_float_prefix<FloatType>();
|
|
||||||
}
|
|
||||||
return get_msgpack_float_prefix(FloatType{});
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////
|
////////////
|
||||||
// UBJSON //
|
// UBJSON //
|
||||||
////////////
|
////////////
|
||||||
@@ -2180,322 +2023,6 @@ class binary_writer
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////
|
|
||||||
// BON8 //
|
|
||||||
//////////
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief write a BON8 value
|
|
||||||
|
|
||||||
A string is written without length or terminator: it ends at the first
|
|
||||||
byte that cannot continue it, which is the first byte of any non-string
|
|
||||||
value and of the end-of-container marker 0xFE. It only needs an explicit
|
|
||||||
end-of-string marker (0xFF) when it is empty, when another string follows,
|
|
||||||
or when it is the last thing in the message.
|
|
||||||
|
|
||||||
@param[in] j JSON value to serialize
|
|
||||||
@param[in,out] string_open whether the output ends with a non-empty
|
|
||||||
string that has not been terminated with 0xFF
|
|
||||||
*/
|
|
||||||
void write_bon8_value(const BasicJsonType& j, bool& string_open)
|
|
||||||
{
|
|
||||||
switch (j.type())
|
|
||||||
{
|
|
||||||
case value_t::null:
|
|
||||||
{
|
|
||||||
write_bon8_marker(0xFA, string_open);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::boolean:
|
|
||||||
{
|
|
||||||
write_bon8_marker(j.m_data.m_value.boolean ? 0xF9 : 0xF8, string_open);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
{
|
|
||||||
if (j.m_data.m_value.number_unsigned > static_cast<typename BasicJsonType::number_unsigned_t>((std::numeric_limits<std::int64_t>::max)()))
|
|
||||||
{
|
|
||||||
JSON_THROW(out_of_range::create(407, concat("integer number ", std::to_string(j.m_data.m_value.number_unsigned), " cannot be represented by BON8 as it does not fit int64"), &j));
|
|
||||||
}
|
|
||||||
write_bon8_integer(static_cast<std::int64_t>(j.m_data.m_value.number_unsigned));
|
|
||||||
string_open = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::number_integer:
|
|
||||||
{
|
|
||||||
write_bon8_integer(static_cast<std::int64_t>(j.m_data.m_value.number_integer));
|
|
||||||
string_open = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::number_float:
|
|
||||||
{
|
|
||||||
write_bon8_float(j.m_data.m_value.number_float);
|
|
||||||
string_open = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::string:
|
|
||||||
{
|
|
||||||
write_bon8_string(*j.m_data.m_value.string, string_open, j);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::array:
|
|
||||||
{
|
|
||||||
const auto N = j.m_data.m_value.array->size();
|
|
||||||
// 0x80..0x84: array with 0..4 elements; 0x85: array ended by 0xFE
|
|
||||||
write_bon8_marker(static_cast<std::uint8_t>(N <= 4 ? 0x80 + N : 0x85), string_open);
|
|
||||||
|
|
||||||
for (const auto& el : *j.m_data.m_value.array)
|
|
||||||
{
|
|
||||||
write_bon8_value(el, string_open);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (N > 4)
|
|
||||||
{
|
|
||||||
write_bon8_marker(0xFE, string_open);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::object:
|
|
||||||
{
|
|
||||||
const auto N = j.m_data.m_value.object->size();
|
|
||||||
// 0x86..0x8A: object with 0..4 members; 0x8B: object ended by 0xFE
|
|
||||||
write_bon8_marker(static_cast<std::uint8_t>(N <= 4 ? 0x86 + N : 0x8B), string_open);
|
|
||||||
|
|
||||||
for (const auto& el : *j.m_data.m_value.object)
|
|
||||||
{
|
|
||||||
write_bon8_string(el.first, string_open, j);
|
|
||||||
write_bon8_value(el.second, string_open);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (N > 4)
|
|
||||||
{
|
|
||||||
write_bon8_marker(0xFE, string_open);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::binary:
|
|
||||||
{
|
|
||||||
// BON8 has no binary type: write the bytes as an array of
|
|
||||||
// integers, like UBJSON and BJData do
|
|
||||||
const auto N = j.m_data.m_value.binary->size();
|
|
||||||
write_bon8_marker(static_cast<std::uint8_t>(N <= 4 ? 0x80 + N : 0x85), string_open);
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < N; ++i)
|
|
||||||
{
|
|
||||||
// the cast is needed for binary types whose value type
|
|
||||||
// is not an integer (e.g., std::byte)
|
|
||||||
write_bon8_integer(static_cast<std::uint8_t>(j.m_data.m_value.binary->data()[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (N > 4)
|
|
||||||
{
|
|
||||||
write_bon8_marker(0xFE, string_open);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::discarded:
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief write a single byte that is not part of a string
|
|
||||||
|
|
||||||
@param[in] marker the byte to write
|
|
||||||
@param[out] string_open set to false, because the output no longer ends
|
|
||||||
with a string; see @ref write_bon8_value
|
|
||||||
*/
|
|
||||||
void write_bon8_marker(const std::uint8_t marker, bool& string_open)
|
|
||||||
{
|
|
||||||
oa.write_character(to_char_type(marker));
|
|
||||||
string_open = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief write a string
|
|
||||||
|
|
||||||
@param[in] s the string to write
|
|
||||||
@param[in,out] string_open see @ref write_bon8_value
|
|
||||||
@param[in] context the value the string belongs to (for diagnostics)
|
|
||||||
|
|
||||||
@throw type_error.316 if @a s is not valid UTF-8, because the end of a
|
|
||||||
string is determined from its encoding
|
|
||||||
*/
|
|
||||||
void write_bon8_string(const string_t& s, bool& string_open, const BasicJsonType& context)
|
|
||||||
{
|
|
||||||
check_bon8_utf8(s, context);
|
|
||||||
|
|
||||||
// a string that follows another string terminates it
|
|
||||||
if (string_open)
|
|
||||||
{
|
|
||||||
oa.write_character(to_char_type(0xFF));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s.empty())
|
|
||||||
{
|
|
||||||
// the empty string is just the end-of-string marker
|
|
||||||
oa.write_character(to_char_type(0xFF));
|
|
||||||
string_open = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
oa.write_characters(reinterpret_cast<const CharType*>(s.data()), s.size());
|
|
||||||
string_open = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief check that a string is valid UTF-8 (RFC 3629)
|
|
||||||
|
|
||||||
@param[in] s the string to check
|
|
||||||
@param[in] context the value the string belongs to (for diagnostics)
|
|
||||||
|
|
||||||
@throw type_error.316 if @a s is not valid UTF-8; the message names the
|
|
||||||
first byte of the first invalid or incomplete sequence
|
|
||||||
*/
|
|
||||||
static void check_bon8_utf8(const string_t& s, const BasicJsonType& context)
|
|
||||||
{
|
|
||||||
static_cast<void>(context); // only used when exceptions are enabled
|
|
||||||
const auto* data = reinterpret_cast<const unsigned char*>(s.data());
|
|
||||||
const std::size_t valid = valid_utf8_prefix(data, s.size());
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(valid != s.size()))
|
|
||||||
{
|
|
||||||
JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(valid), ": 0x", hex_byte(data[valid])), &context));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @return a byte as two uppercase hexadecimal digits
|
|
||||||
static std::string hex_byte(const std::uint8_t byte)
|
|
||||||
{
|
|
||||||
std::string result = "00";
|
|
||||||
constexpr const char* nibble_to_hex = "0123456789ABCDEF";
|
|
||||||
result[0] = nibble_to_hex[byte / 16];
|
|
||||||
result[1] = nibble_to_hex[byte % 16];
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief write an integer in the shortest encoding
|
|
||||||
|
|
||||||
Integers from -10 to 39 take one byte. Up to -33818506 and 67637031, an
|
|
||||||
integer takes 2 to 4 bytes that begin with a UTF-8 lead byte (0xC2..0xF7)
|
|
||||||
followed by a byte that is not a continuation byte: 0x00..0x7F for
|
|
||||||
positive and 0xC0..0xFF for negative integers. Each range starts where the
|
|
||||||
shorter one ends. Larger integers are written as int32 (0x8C) or int64
|
|
||||||
(0x8D) in big-endian byte order.
|
|
||||||
|
|
||||||
@param[in] value the integer to write
|
|
||||||
*/
|
|
||||||
void write_bon8_integer(std::int64_t value)
|
|
||||||
{
|
|
||||||
if (value < (std::numeric_limits<std::int32_t>::min)() || value > (std::numeric_limits<std::int32_t>::max)())
|
|
||||||
{
|
|
||||||
oa.write_character(to_char_type(0x8D));
|
|
||||||
write_number(value);
|
|
||||||
}
|
|
||||||
else if (value < -33818506 || value > 67637031)
|
|
||||||
{
|
|
||||||
oa.write_character(to_char_type(0x8C));
|
|
||||||
write_number(static_cast<std::int32_t>(value));
|
|
||||||
}
|
|
||||||
else if (value <= -264075)
|
|
||||||
{
|
|
||||||
value = -(value + 264075);
|
|
||||||
write_bon8_bytes(0xF0 + ((value >> 22) & 0x07), 0xC0 + ((value >> 16) & 0x3F), value >> 8, value);
|
|
||||||
}
|
|
||||||
else if (value <= -1931)
|
|
||||||
{
|
|
||||||
value = -(value + 1931);
|
|
||||||
write_bon8_bytes(0xE0 + ((value >> 14) & 0x0F), 0xC0 + ((value >> 8) & 0x3F), value);
|
|
||||||
}
|
|
||||||
else if (value <= -11)
|
|
||||||
{
|
|
||||||
value = -(value + 11);
|
|
||||||
write_bon8_bytes(0xC2 + ((value >> 6) & 0x1F), 0xC0 + (value & 0x3F));
|
|
||||||
}
|
|
||||||
else if (value <= -1)
|
|
||||||
{
|
|
||||||
write_bon8_bytes(0xB8 - (value + 1));
|
|
||||||
}
|
|
||||||
else if (value <= 39)
|
|
||||||
{
|
|
||||||
write_bon8_bytes(0x90 + value);
|
|
||||||
}
|
|
||||||
else if (value <= 3879)
|
|
||||||
{
|
|
||||||
value -= 40;
|
|
||||||
write_bon8_bytes(0xC2 + ((value >> 7) & 0x1F), value & 0x7F);
|
|
||||||
}
|
|
||||||
else if (value <= 528167)
|
|
||||||
{
|
|
||||||
value -= 3880;
|
|
||||||
write_bon8_bytes(0xE0 + ((value >> 15) & 0x0F), (value >> 8) & 0x7F, value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
value -= 528168;
|
|
||||||
write_bon8_bytes(0xF0 + ((value >> 23) & 0x07), (value >> 16) & 0x7F, value >> 8, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// write the low byte of each argument
|
|
||||||
template<typename... Bytes>
|
|
||||||
void write_bon8_bytes(const Bytes... bytes)
|
|
||||||
{
|
|
||||||
const std::array<CharType, sizeof...(Bytes)> buffer{{to_char_type(static_cast<std::uint8_t>(bytes & 0xFF))...}};
|
|
||||||
oa.write_characters(buffer.data(), buffer.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief write a floating-point number
|
|
||||||
|
|
||||||
-1.0, +0.0, and 1.0 take one byte. Other numbers are written as binary32
|
|
||||||
(0x8E) if that loses no precision, and as binary64 (0x8F) otherwise; -0.0,
|
|
||||||
infinities, and NaN are always written as binary32, NaN as 0x7F800001.
|
|
||||||
|
|
||||||
@param[in] n the number to write
|
|
||||||
*/
|
|
||||||
void write_bon8_float(const number_float_t n)
|
|
||||||
{
|
|
||||||
#ifdef __GNUC__
|
|
||||||
JSON_HEDLEY_DIAGNOSTIC_PUSH
|
|
||||||
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
|
|
||||||
#endif
|
|
||||||
if (n == static_cast<number_float_t>(-1))
|
|
||||||
{
|
|
||||||
oa.write_character(to_char_type(0xFB));
|
|
||||||
}
|
|
||||||
else if (n == static_cast<number_float_t>(0) && !std::signbit(n))
|
|
||||||
{
|
|
||||||
oa.write_character(to_char_type(0xFC));
|
|
||||||
}
|
|
||||||
else if (n == static_cast<number_float_t>(1))
|
|
||||||
{
|
|
||||||
oa.write_character(to_char_type(0xFD));
|
|
||||||
}
|
|
||||||
else if (std::isnan(n))
|
|
||||||
{
|
|
||||||
write_bon8_bytes(0x8E, 0x7F, 0x80, 0x00, 0x01);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
write_compact_float(n, detail::input_format_t::bon8);
|
|
||||||
}
|
|
||||||
#ifdef __GNUC__
|
|
||||||
JSON_HEDLEY_DIAGNOSTIC_POP
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////
|
///////////////////////
|
||||||
// Utility functions //
|
// Utility functions //
|
||||||
///////////////////////
|
///////////////////////
|
||||||
@@ -2630,12 +2157,16 @@ class binary_writer
|
|||||||
static_cast<double>(n) <= static_cast<double>((std::numeric_limits<float>::max)()) &&
|
static_cast<double>(n) <= static_cast<double>((std::numeric_limits<float>::max)()) &&
|
||||||
static_cast<double>(static_cast<float>(n)) == static_cast<double>(n))))
|
static_cast<double>(static_cast<float>(n)) == static_cast<double>(n))))
|
||||||
{
|
{
|
||||||
oa.write_character(get_compact_float_prefix<float>(format));
|
oa.write_character(format == detail::input_format_t::cbor
|
||||||
|
? get_cbor_float_prefix(static_cast<float>(n))
|
||||||
|
: get_msgpack_float_prefix(static_cast<float>(n)));
|
||||||
write_number(static_cast<float>(n));
|
write_number(static_cast<float>(n));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
oa.write_character(get_compact_float_prefix<number_float_t>(format));
|
oa.write_character(format == detail::input_format_t::cbor
|
||||||
|
? get_cbor_float_prefix(n)
|
||||||
|
: get_msgpack_float_prefix(n));
|
||||||
write_number(n);
|
write_number(n);
|
||||||
}
|
}
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
|
|||||||
@@ -32,7 +32,6 @@
|
|||||||
#include <nlohmann/detail/output/output_adapters.hpp>
|
#include <nlohmann/detail/output/output_adapters.hpp>
|
||||||
#include <nlohmann/detail/recursion_depth_limit.hpp>
|
#include <nlohmann/detail/recursion_depth_limit.hpp>
|
||||||
#include <nlohmann/detail/string_concat.hpp>
|
#include <nlohmann/detail/string_concat.hpp>
|
||||||
#include <nlohmann/detail/string_utils.hpp>
|
|
||||||
#include <nlohmann/detail/value_t.hpp>
|
#include <nlohmann/detail/value_t.hpp>
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
@@ -59,6 +58,8 @@ class serializer
|
|||||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||||
using binary_char_t = typename BasicJsonType::binary_t::value_type;
|
using binary_char_t = typename BasicJsonType::binary_t::value_type;
|
||||||
|
static constexpr std::uint8_t UTF8_ACCEPT = 0;
|
||||||
|
static constexpr std::uint8_t UTF8_REJECT = 1;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/*!
|
/*!
|
||||||
@@ -1591,6 +1592,62 @@ class serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief check whether a string is UTF-8 encoded
|
||||||
|
|
||||||
|
The function checks each byte of a string whether it is UTF-8 encoded. The
|
||||||
|
result of the check is stored in the @a state parameter. The function must
|
||||||
|
be called initially with state 0 (accept). State 1 means the string must
|
||||||
|
be rejected, because the current byte is not allowed. If the string is
|
||||||
|
completely processed, but the state is non-zero, the string ended
|
||||||
|
prematurely; that is, the last byte indicated more bytes should have
|
||||||
|
followed.
|
||||||
|
|
||||||
|
@param[in,out] state the state of the decoding
|
||||||
|
@param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT)
|
||||||
|
@param[in] byte next byte to decode
|
||||||
|
@return new state
|
||||||
|
|
||||||
|
@note The function has been edited: a std::array is used.
|
||||||
|
|
||||||
|
@copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
||||||
|
@sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
|
||||||
|
*/
|
||||||
|
static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept
|
||||||
|
{
|
||||||
|
static const std::array<std::uint8_t, 400> utf8d =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F
|
||||||
|
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF
|
||||||
|
8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF
|
||||||
|
0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
|
||||||
|
0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
|
||||||
|
0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
|
||||||
|
1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
|
||||||
|
1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
|
||||||
|
1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
JSON_ASSERT(static_cast<std::size_t>(byte) < utf8d.size());
|
||||||
|
const std::uint8_t type = utf8d[byte];
|
||||||
|
|
||||||
|
codep = (state != UTF8_ACCEPT)
|
||||||
|
? (byte & 0x3fu) | (codep << 6u)
|
||||||
|
: (0xFFu >> type) & (byte);
|
||||||
|
|
||||||
|
const std::size_t index = 256u + (static_cast<size_t>(state) * 16u) + static_cast<size_t>(type);
|
||||||
|
JSON_ASSERT(index < utf8d.size());
|
||||||
|
state = utf8d[index];
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Overload to make the compiler happy while it is instantiating
|
* Overload to make the compiler happy while it is instantiating
|
||||||
* dump_integer for number_unsigned_t.
|
* dump_integer for number_unsigned_t.
|
||||||
|
|||||||
@@ -8,13 +8,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array> // array
|
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstdint> // uint8_t, uint32_t
|
|
||||||
#include <string> // string, to_string
|
#include <string> // string, to_string
|
||||||
|
|
||||||
#include <nlohmann/detail/abi_macros.hpp>
|
#include <nlohmann/detail/abi_macros.hpp>
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
namespace detail
|
namespace detail
|
||||||
@@ -36,102 +33,5 @@ StringType to_string(std::size_t value)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////
|
|
||||||
// UTF-8 decoding //
|
|
||||||
///////////////////
|
|
||||||
|
|
||||||
// UTF-8 decoder states used by decode() below
|
|
||||||
static constexpr std::uint8_t UTF8_ACCEPT = 0;
|
|
||||||
static constexpr std::uint8_t UTF8_REJECT = 1;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief process a byte of a UTF-8 sequence
|
|
||||||
|
|
||||||
This is a single-byte step of a "shift-based" UTF-8 decoder originally
|
|
||||||
written by Björn Hoehrmann. See
|
|
||||||
http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
|
|
||||||
|
|
||||||
This decoder is the single source of truth for UTF-8 validation in this
|
|
||||||
library: it is used both by the serializer (to escape and, in strict mode,
|
|
||||||
reject ill-formed UTF-8 when dumping a string) and by the binary readers
|
|
||||||
(to reject ill-formed UTF-8 in CBOR/MessagePack/BSON/UBJSON text strings at
|
|
||||||
decode time; see @ref is_valid_utf8 below).
|
|
||||||
|
|
||||||
@param[in,out] state the current decoder state
|
|
||||||
@param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT)
|
|
||||||
@param[in] byte next byte to decode
|
|
||||||
@return new state
|
|
||||||
|
|
||||||
@note Original source: http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
|
|
||||||
@sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
|
|
||||||
*/
|
|
||||||
inline std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept
|
|
||||||
{
|
|
||||||
static const std::array<std::uint8_t, 400> utf8d =
|
|
||||||
{
|
|
||||||
{
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F
|
|
||||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF
|
|
||||||
8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF
|
|
||||||
0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
|
|
||||||
0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
|
|
||||||
0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
|
|
||||||
1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
|
|
||||||
1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
|
|
||||||
1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
JSON_ASSERT(static_cast<std::size_t>(byte) < utf8d.size());
|
|
||||||
const std::uint8_t type = utf8d[byte];
|
|
||||||
|
|
||||||
codep = (state != UTF8_ACCEPT)
|
|
||||||
? (byte & 0x3fu) | (codep << 6u)
|
|
||||||
: (0xFFu >> type) & (byte);
|
|
||||||
|
|
||||||
const std::size_t index = 256u + (static_cast<std::size_t>(state) * 16u) + static_cast<std::size_t>(type);
|
|
||||||
JSON_ASSERT(index < utf8d.size());
|
|
||||||
state = utf8d[index];
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief check whether a string consists solely of valid UTF-8
|
|
||||||
|
|
||||||
Used by the CBOR/MessagePack/BSON/UBJSON binary readers to reject text
|
|
||||||
strings that are not valid UTF-8 at decode time (RFC 8949 §3.1 and the
|
|
||||||
MessagePack/BSON specifications all require text strings to be UTF-8), so
|
|
||||||
that malformed input is caught immediately instead of only surfacing later
|
|
||||||
as a type_error.316 when the resulting value is dumped.
|
|
||||||
|
|
||||||
@param[in] s the string to check
|
|
||||||
@param[in] first index of the first byte to check; the bytes before it are
|
|
||||||
assumed to have been validated already and to end on a
|
|
||||||
code point boundary
|
|
||||||
@return whether @a s (from index @a first on) is valid UTF-8
|
|
||||||
*/
|
|
||||||
template<typename StringType>
|
|
||||||
inline bool is_valid_utf8(const StringType& s, const std::size_t first = 0) noexcept
|
|
||||||
{
|
|
||||||
std::uint8_t state = UTF8_ACCEPT;
|
|
||||||
std::uint32_t codepoint = 0;
|
|
||||||
|
|
||||||
for (std::size_t i = first; i < s.size(); ++i)
|
|
||||||
{
|
|
||||||
decode(state, codepoint, static_cast<std::uint8_t>(s[i]));
|
|
||||||
if (state == UTF8_REJECT)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return state == UTF8_ACCEPT;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|||||||
+23
-395
@@ -924,31 +924,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief whether a descent must stop here and finish without the call stack
|
|
||||||
|
|
||||||
@a may_descend says whether the operator descends at all; it is a constant
|
|
||||||
at every call site, and is passed rather than tested by the caller so that
|
|
||||||
the test does not become a constant condition there, which MSVC reports as
|
|
||||||
C4127.
|
|
||||||
|
|
||||||
The comparison operators use this rather than @ref nesting_depth_guard::okay,
|
|
||||||
because they are written as a macro and a macro cannot use the preprocessor
|
|
||||||
the way the guard's constructor does; @ref copy_structured, which can, asks
|
|
||||||
the guard instead and never calls this.
|
|
||||||
*/
|
|
||||||
static bool nesting_depth_exhausted(bool may_descend = true) noexcept
|
|
||||||
{
|
|
||||||
#ifdef JSON_NO_THREAD_LOCAL
|
|
||||||
// without a count of its own per thread, a descent cannot be bounded
|
|
||||||
// without racing another one, so none is made
|
|
||||||
static_cast<void>(may_descend);
|
|
||||||
return true;
|
|
||||||
#else
|
|
||||||
return !may_descend || nesting_depth() >= nesting_depth_limit();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief counts one level of a bounded descent for as long as it runs, and
|
@brief counts one level of a bounded descent for as long as it runs, and
|
||||||
reports whether the descent was still within the limit when it began
|
reports whether the descent was still within the limit when it began
|
||||||
@@ -1003,8 +978,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
using copy_worklist_t = std::vector<std::pair<const basic_json*, basic_json*>>;
|
using copy_worklist_t = std::vector<std::pair<const basic_json*, basic_json*>>;
|
||||||
|
|
||||||
/// scratch space to build the key skeleton of an object copy in one go
|
/// scratch space to build the key skeleton of an object copy in one go
|
||||||
using copy_scratch_value_t = std::pair<typename object_t::key_type, basic_json>;
|
using copy_scratch_t = std::vector<std::pair<typename object_t::key_type, basic_json>>;
|
||||||
using copy_scratch_t = std::vector<copy_scratch_value_t, AllocatorType<copy_scratch_value_t>>;
|
|
||||||
|
|
||||||
/// @brief copy everything of @a src into @a dst but its type and value
|
/// @brief copy everything of @a src into @a dst but its type and value
|
||||||
static void copy_metadata(const basic_json& src, basic_json& dst)
|
static void copy_metadata(const basic_json& src, basic_json& dst)
|
||||||
@@ -1269,274 +1243,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// the result of comparing two values, including values that cannot be
|
|
||||||
/// ordered at all, such as a discarded value or a NaN
|
|
||||||
enum class compare_result { less, equal, greater, unordered };
|
|
||||||
|
|
||||||
#if JSON_HAS_THREE_WAY_COMPARISON
|
|
||||||
/// @brief the ordering that @a result stands for
|
|
||||||
static std::partial_ordering to_partial_ordering(compare_result result) noexcept // *NOPAD*
|
|
||||||
{
|
|
||||||
switch (result)
|
|
||||||
{
|
|
||||||
case compare_result::less:
|
|
||||||
return std::partial_ordering::less;
|
|
||||||
case compare_result::greater:
|
|
||||||
return std::partial_ordering::greater;
|
|
||||||
case compare_result::equal:
|
|
||||||
return std::partial_ordering::equivalent;
|
|
||||||
case compare_result::unordered:
|
|
||||||
default:
|
|
||||||
return std::partial_ordering::unordered;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief compare two values that are not both an array or both an object
|
|
||||||
|
|
||||||
Such a pair is compared by the operators themselves, which cannot descend
|
|
||||||
into it and therefore cannot recurse.
|
|
||||||
|
|
||||||
That holds for a pair whose types differ as much as for a pair of leaves: an
|
|
||||||
array and an object are told apart by their types alone, because an operator
|
|
||||||
only ever descends into two values of the same type. So `==` reports them as
|
|
||||||
unequal without looking inside either, and an ordering falls back to the
|
|
||||||
order of the types - an object sorts before an array - exactly as it does
|
|
||||||
for a value that is not nested deeply enough to get here.
|
|
||||||
*/
|
|
||||||
template<bool Ordered>
|
|
||||||
static compare_result compare_leaves(const_reference lhs, const_reference rhs) noexcept
|
|
||||||
{
|
|
||||||
if (lhs == rhs)
|
|
||||||
{
|
|
||||||
return compare_result::equal;
|
|
||||||
}
|
|
||||||
|
|
||||||
return order_leaves(lhs, rhs, std::integral_constant<bool, Ordered> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief compare two object keys
|
|
||||||
|
|
||||||
An object compares its entries as pairs of a key and a value, so its keys
|
|
||||||
are compared exactly as std::pair compares them: with < where the objects
|
|
||||||
are being ordered, and with == where they are only checked for equality.
|
|
||||||
Note that this is not the object's own comparator, which for a vector-backed
|
|
||||||
object type such as nlohmann::ordered_map tells equality rather than order.
|
|
||||||
*/
|
|
||||||
static compare_result compare_keys(const typename object_t::key_type& lhs,
|
|
||||||
const typename object_t::key_type& rhs,
|
|
||||||
std::true_type /*ordered*/)
|
|
||||||
{
|
|
||||||
if (lhs < rhs)
|
|
||||||
{
|
|
||||||
return compare_result::less;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rhs < lhs)
|
|
||||||
{
|
|
||||||
return compare_result::greater;
|
|
||||||
}
|
|
||||||
|
|
||||||
return compare_result::equal;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief check two object keys for equality
|
|
||||||
static compare_result compare_keys(const typename object_t::key_type& lhs,
|
|
||||||
const typename object_t::key_type& rhs,
|
|
||||||
std::false_type /*ordered*/)
|
|
||||||
{
|
|
||||||
return lhs == rhs ? compare_result::equal : compare_result::unordered;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief tell apart two values that are not equal
|
|
||||||
/// @note only instantiated where the values are being ordered, as a key or
|
|
||||||
/// string type is not required to be ordered to be compared for equality
|
|
||||||
static compare_result order_leaves(const_reference lhs, const_reference rhs, std::true_type /*ordered*/) noexcept
|
|
||||||
{
|
|
||||||
if (lhs < rhs)
|
|
||||||
{
|
|
||||||
return compare_result::less;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rhs < lhs)
|
|
||||||
{
|
|
||||||
return compare_result::greater;
|
|
||||||
}
|
|
||||||
|
|
||||||
return compare_result::unordered;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief report two values as not equal without ordering them
|
|
||||||
static compare_result order_leaves(const_reference /*lhs*/, const_reference /*rhs*/, std::false_type /*ordered*/) noexcept
|
|
||||||
{
|
|
||||||
return compare_result::unordered;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief compare @a lhs and @a rhs without descending into them
|
|
||||||
|
|
||||||
Reached once a comparison has descended @ref nesting_depth_limit levels, so
|
|
||||||
that comparing values cannot exhaust the call stack however deeply they are
|
|
||||||
nested. The two values are walked in lockstep on an explicit stack and
|
|
||||||
compared lexicographically, element by element in the order the containers
|
|
||||||
enumerate them - which is how the container types this library ships compare
|
|
||||||
themselves: a std::map enumerates its entries in key order, and
|
|
||||||
nlohmann::ordered_map in insertion order. An object type that enumerates its
|
|
||||||
entries in an unspecified order, such as std::unordered_map, compares them
|
|
||||||
pairwise instead; the difference could only ever show below the bound.
|
|
||||||
|
|
||||||
Note that the stack this walks with is allocated, while the comparison
|
|
||||||
operators are noexcept and the container comparison this replaces allocated
|
|
||||||
nothing. Failing that allocation therefore ends the process rather than
|
|
||||||
throwing. It only arises for values nested past the bound, and only when
|
|
||||||
memory has run out - where the same comparison used to exhaust the call
|
|
||||||
stack instead - but it is a way to fail that the operators did not have.
|
|
||||||
*/
|
|
||||||
template<bool Ordered>
|
|
||||||
static compare_result compare_iteratively(const_reference lhs, const_reference rhs,
|
|
||||||
const bool unordered_compares_equal) noexcept
|
|
||||||
{
|
|
||||||
/// a pair of containers being compared in lockstep
|
|
||||||
struct frame
|
|
||||||
{
|
|
||||||
const basic_json* lhs_value{nullptr};
|
|
||||||
const basic_json* rhs_value{nullptr};
|
|
||||||
typename array_t::const_iterator lhs_array_it{};
|
|
||||||
typename array_t::const_iterator rhs_array_it{};
|
|
||||||
typename object_t::const_iterator lhs_object_it{};
|
|
||||||
typename object_t::const_iterator rhs_object_it{};
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<frame> stack;
|
|
||||||
const basic_json* left = &lhs;
|
|
||||||
const basic_json* right = &rhs;
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
const auto type = left->m_data.m_type;
|
|
||||||
|
|
||||||
if (type == right->m_data.m_type && (type == value_t::array || type == value_t::object))
|
|
||||||
{
|
|
||||||
// descend: the elements decide, and are compared further down
|
|
||||||
stack.emplace_back();
|
|
||||||
frame& pushed = stack.back();
|
|
||||||
pushed.lhs_value = left;
|
|
||||||
pushed.rhs_value = right;
|
|
||||||
|
|
||||||
if (type == value_t::array)
|
|
||||||
{
|
|
||||||
pushed.lhs_array_it = left->m_data.m_value.array->cbegin();
|
|
||||||
pushed.rhs_array_it = right->m_data.m_value.array->cbegin();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pushed.lhs_object_it = left->m_data.m_value.object->cbegin();
|
|
||||||
pushed.rhs_object_it = right->m_data.m_value.object->cbegin();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const compare_result result = compare_leaves<Ordered>(*left, *right);
|
|
||||||
|
|
||||||
// Values that cannot be ordered - a NaN, say - end an ordered
|
|
||||||
// comparison for std::lexicographical_compare_three_way, but
|
|
||||||
// std::lexicographical_compare treats them as equivalent and
|
|
||||||
// carries on with the next element. Both are reproduced here,
|
|
||||||
// so that a value nested too deeply to descend into compares
|
|
||||||
// exactly as one that is not.
|
|
||||||
if (result != compare_result::equal &&
|
|
||||||
!(unordered_compares_equal && result == compare_result::unordered))
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// walk back up past the containers that are exhausted, then take the
|
|
||||||
// next pair of elements from the innermost one that is not
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
if (stack.empty())
|
|
||||||
{
|
|
||||||
return compare_result::equal;
|
|
||||||
}
|
|
||||||
|
|
||||||
frame& current = stack.back();
|
|
||||||
const bool is_object = current.lhs_value->m_data.m_type == value_t::object;
|
|
||||||
|
|
||||||
const bool lhs_done = is_object
|
|
||||||
? current.lhs_object_it == current.lhs_value->m_data.m_value.object->cend()
|
|
||||||
: current.lhs_array_it == current.lhs_value->m_data.m_value.array->cend();
|
|
||||||
const bool rhs_done = is_object
|
|
||||||
? current.rhs_object_it == current.rhs_value->m_data.m_value.object->cend()
|
|
||||||
: current.rhs_array_it == current.rhs_value->m_data.m_value.array->cend();
|
|
||||||
|
|
||||||
if (lhs_done || rhs_done)
|
|
||||||
{
|
|
||||||
// whichever ran out first holds the smaller container; if
|
|
||||||
// both did, they are equal and the container above decides
|
|
||||||
if (lhs_done != rhs_done)
|
|
||||||
{
|
|
||||||
return lhs_done ? compare_result::less : compare_result::greater;
|
|
||||||
}
|
|
||||||
|
|
||||||
stack.pop_back();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_object)
|
|
||||||
{
|
|
||||||
// an entry is a key and a value, and the key decides first
|
|
||||||
const compare_result key_result =
|
|
||||||
compare_keys(current.lhs_object_it->first, current.rhs_object_it->first,
|
|
||||||
std::integral_constant<bool, Ordered> {});
|
|
||||||
|
|
||||||
if (key_result != compare_result::equal)
|
|
||||||
{
|
|
||||||
return key_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
left = &(current.lhs_object_it->second);
|
|
||||||
right = &(current.rhs_object_it->second);
|
|
||||||
++current.lhs_object_it;
|
|
||||||
++current.rhs_object_it;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
left = &(*current.lhs_array_it);
|
|
||||||
right = &(*current.rhs_array_it);
|
|
||||||
++current.lhs_array_it;
|
|
||||||
++current.rhs_array_it;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief restore the parent pointers after erasing from an object
|
|
||||||
/// ordered_json keeps its members in a vector, and erasing a member
|
|
||||||
/// re-constructs every member after it in place, which resets their
|
|
||||||
/// parent pointers
|
|
||||||
void set_parents_after_object_erase()
|
|
||||||
{
|
|
||||||
#if JSON_DIAGNOSTICS
|
|
||||||
#ifdef JSON_HEDLEY_MSVC_VERSION
|
|
||||||
#pragma warning(push )
|
|
||||||
#pragma warning(disable : 4127) // ignore warning to replace if with if constexpr
|
|
||||||
#endif
|
|
||||||
if (detail::is_ordered_map<object_t>::value)
|
|
||||||
{
|
|
||||||
set_parents();
|
|
||||||
}
|
|
||||||
#ifdef JSON_HEDLEY_MSVC_VERSION
|
|
||||||
#pragma warning( pop )
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
// JSON parser callback //
|
// JSON parser callback //
|
||||||
@@ -2554,7 +2260,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
ValueType & get_to(ValueType& v) const noexcept(noexcept(
|
ValueType & get_to(ValueType& v) const noexcept(noexcept(
|
||||||
JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))
|
JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))
|
||||||
{
|
{
|
||||||
static_assert(!std::is_const<ValueType>::value, "get_to() cannot deserialize into a const value");
|
|
||||||
JSONSerializer<ValueType>::from_json(*this, v);
|
JSONSerializer<ValueType>::from_json(*this, v);
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
@@ -2580,7 +2285,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
noexcept(noexcept(JSONSerializer<Array>::from_json(
|
noexcept(noexcept(JSONSerializer<Array>::from_json(
|
||||||
std::declval<const basic_json_t&>(), v)))
|
std::declval<const basic_json_t&>(), v)))
|
||||||
{
|
{
|
||||||
static_assert(!std::is_const<T>::value, "get_to() cannot deserialize into a const value");
|
|
||||||
JSONSerializer<Array>::from_json(*this, v);
|
JSONSerializer<Array>::from_json(*this, v);
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
@@ -3227,7 +2931,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
case value_t::object:
|
case value_t::object:
|
||||||
{
|
{
|
||||||
result.m_it.object_iterator = erase_from_object(pos.m_it.object_iterator);
|
result.m_it.object_iterator = erase_from_object(pos.m_it.object_iterator);
|
||||||
set_parents_after_object_erase();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3300,7 +3003,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
{
|
{
|
||||||
result.m_it.object_iterator = m_data.m_value.object->erase(first.m_it.object_iterator,
|
result.m_it.object_iterator = m_data.m_value.object->erase(first.m_it.object_iterator,
|
||||||
last.m_it.object_iterator);
|
last.m_it.object_iterator);
|
||||||
set_parents_after_object_erase();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3331,9 +3033,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
|
JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto erased = m_data.m_value.object->erase(std::forward<KeyType>(key));
|
return m_data.m_value.object->erase(std::forward<KeyType>(key));
|
||||||
set_parents_after_object_erase();
|
|
||||||
return erased;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename KeyType, detail::enable_if_t <
|
template < typename KeyType, detail::enable_if_t <
|
||||||
@@ -3350,7 +3050,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
if (it != m_data.m_value.object->end())
|
if (it != m_data.m_value.object->end())
|
||||||
{
|
{
|
||||||
m_data.m_value.object->erase(it);
|
m_data.m_value.object->erase(it);
|
||||||
set_parents_after_object_erase();
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -4262,12 +3961,16 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
if (it2 != m_data.m_value.object->end() && it2->second.is_object())
|
if (it2 != m_data.m_value.object->end() && it2->second.is_object())
|
||||||
{
|
{
|
||||||
it2->second.update_members(it.value().cbegin(), it.value().cend(), true, depth + 1);
|
it2->second.update_members(it.value().cbegin(), it.value().cend(), true, depth + 1);
|
||||||
|
#if JSON_DIAGNOSTICS
|
||||||
|
it2->second.set_parents();
|
||||||
|
#endif
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// set_parent() also repairs the other members, which ordered_json
|
m_data.m_value.object->operator[](it.key()) = it.value();
|
||||||
// relocates when adding a key makes its vector grow
|
#if JSON_DIAGNOSTICS
|
||||||
set_parent(m_data.m_value.object->operator[](it.key()) = it.value());
|
m_data.m_value.object->operator[](it.key()).m_parent = this;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4296,6 +3999,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
}
|
}
|
||||||
|
|
||||||
// a nested object is merged: continue with its parent
|
// a nested object is merged: continue with its parent
|
||||||
|
#if JSON_DIAGNOSTICS
|
||||||
|
target->set_parents();
|
||||||
|
#endif
|
||||||
target = stack.back().target;
|
target = stack.back().target;
|
||||||
first = stack.back().position;
|
first = stack.back().position;
|
||||||
last = stack.back().last;
|
last = stack.back().last;
|
||||||
@@ -4317,9 +4023,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// set_parent() also repairs the other members, which ordered_json
|
target->m_data.m_value.object->operator[](first.key()) = first.value();
|
||||||
// relocates when adding a key makes its vector grow
|
#if JSON_DIAGNOSTICS
|
||||||
target->set_parent(target->m_data.m_value.object->operator[](first.key()) = first.value());
|
target->m_data.m_value.object->operator[](first.key()).m_parent = target;
|
||||||
|
#endif
|
||||||
++first;
|
++first;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4458,7 +4165,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// because any negative signed value is smaller than any unsigned value.
|
// because any negative signed value is smaller than any unsigned value.
|
||||||
// Otherwise, the non-negative signed value is cast to unsigned before the
|
// Otherwise, the non-negative signed value is cast to unsigned before the
|
||||||
// comparison to avoid wraparound.
|
// comparison to avoid wraparound.
|
||||||
#define JSON_IMPLEMENT_OPERATOR(op, null_result, unordered_result, default_result, deep_result, may_descend) \
|
#define JSON_IMPLEMENT_OPERATOR(op, null_result, unordered_result, default_result) \
|
||||||
const auto lhs_type = lhs.type(); \
|
const auto lhs_type = lhs.type(); \
|
||||||
const auto rhs_type = rhs.type(); \
|
const auto rhs_type = rhs.type(); \
|
||||||
\
|
\
|
||||||
@@ -4467,25 +4174,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
switch (lhs_type) \
|
switch (lhs_type) \
|
||||||
{ \
|
{ \
|
||||||
case value_t::array: \
|
case value_t::array: \
|
||||||
{ \
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(nesting_depth_exhausted(may_descend))) \
|
|
||||||
{ \
|
|
||||||
return (deep_result); \
|
|
||||||
} \
|
|
||||||
const nesting_depth_guard guard; \
|
|
||||||
return (*lhs.m_data.m_value.array) op (*rhs.m_data.m_value.array); \
|
return (*lhs.m_data.m_value.array) op (*rhs.m_data.m_value.array); \
|
||||||
} \
|
\
|
||||||
\
|
|
||||||
case value_t::object: \
|
case value_t::object: \
|
||||||
{ \
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(nesting_depth_exhausted(may_descend))) \
|
|
||||||
{ \
|
|
||||||
return (deep_result); \
|
|
||||||
} \
|
|
||||||
const nesting_depth_guard guard; \
|
|
||||||
return (*lhs.m_data.m_value.object) op (*rhs.m_data.m_value.object); \
|
return (*lhs.m_data.m_value.object) op (*rhs.m_data.m_value.object); \
|
||||||
} \
|
\
|
||||||
\
|
|
||||||
case value_t::null: \
|
case value_t::null: \
|
||||||
return (null_result); \
|
return (null_result); \
|
||||||
\
|
\
|
||||||
@@ -4585,8 +4278,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
|
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
|
||||||
#endif
|
#endif
|
||||||
const_reference lhs = *this;
|
const_reference lhs = *this;
|
||||||
JSON_IMPLEMENT_OPERATOR( ==, true, false, false,
|
JSON_IMPLEMENT_OPERATOR( ==, true, false, false)
|
||||||
compare_iteratively<false>(lhs, rhs, false) == compare_result::equal, true)
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
JSON_HEDLEY_DIAGNOSTIC_POP
|
JSON_HEDLEY_DIAGNOSTIC_POP
|
||||||
#endif
|
#endif
|
||||||
@@ -4611,8 +4303,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
JSON_IMPLEMENT_OPERATOR(<=>, // *NOPAD*
|
JSON_IMPLEMENT_OPERATOR(<=>, // *NOPAD*
|
||||||
std::partial_ordering::equivalent,
|
std::partial_ordering::equivalent,
|
||||||
std::partial_ordering::unordered,
|
std::partial_ordering::unordered,
|
||||||
lhs_type <=> rhs_type, // *NOPAD*
|
lhs_type <=> rhs_type) // *NOPAD*
|
||||||
to_partial_ordering(compare_iteratively<true>(lhs, rhs, false)), true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief comparison: 3-way
|
/// @brief comparison: 3-way
|
||||||
@@ -4679,8 +4370,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
JSON_HEDLEY_DIAGNOSTIC_PUSH
|
JSON_HEDLEY_DIAGNOSTIC_PUSH
|
||||||
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
|
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
|
||||||
#endif
|
#endif
|
||||||
JSON_IMPLEMENT_OPERATOR( ==, true, false, false,
|
JSON_IMPLEMENT_OPERATOR( ==, true, false, false)
|
||||||
compare_iteratively<false>(lhs, rhs, false) == compare_result::equal, true)
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
JSON_HEDLEY_DIAGNOSTIC_POP
|
JSON_HEDLEY_DIAGNOSTIC_POP
|
||||||
#endif
|
#endif
|
||||||
@@ -4736,8 +4426,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// default_result is used if we cannot compare values. In that case,
|
// default_result is used if we cannot compare values. In that case,
|
||||||
// we compare types. Note we have to call the operator explicitly,
|
// we compare types. Note we have to call the operator explicitly,
|
||||||
// because MSVC has problems otherwise.
|
// because MSVC has problems otherwise.
|
||||||
JSON_IMPLEMENT_OPERATOR( <, false, false, operator<(lhs_type, rhs_type),
|
JSON_IMPLEMENT_OPERATOR( <, false, false, operator<(lhs_type, rhs_type))
|
||||||
compare_iteratively<true>(lhs, rhs, true) == compare_result::less, false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief comparison: less than
|
/// @brief comparison: less than
|
||||||
@@ -5286,30 +4975,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
binary_writer<char>(o).write_bson(j);
|
binary_writer<char>(o).write_bson(j);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a BON8 serialization of a given JSON value
|
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/to_bon8/
|
|
||||||
static std::vector<std::uint8_t> to_bon8(const basic_json& j)
|
|
||||||
{
|
|
||||||
std::vector<std::uint8_t> result;
|
|
||||||
result.reserve(detail::binary_reserve_hint(j));
|
|
||||||
vector_writer(result).write_bon8(j);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief create a BON8 serialization of a given JSON value
|
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/to_bon8/
|
|
||||||
static void to_bon8(const basic_json& j, detail::output_adapter<std::uint8_t> o)
|
|
||||||
{
|
|
||||||
binary_writer<std::uint8_t>(o).write_bon8(j);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief create a BON8 serialization of a given JSON value
|
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/to_bon8/
|
|
||||||
static void to_bon8(const basic_json& j, detail::output_adapter<char> o)
|
|
||||||
{
|
|
||||||
binary_writer<char>(o).write_bon8(j);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief create a JSON value from an input in CBOR format
|
/// @brief create a JSON value from an input in CBOR format
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
|
/// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
|
||||||
template<typename InputType>
|
template<typename InputType>
|
||||||
@@ -5543,43 +5208,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a JSON value from an input in BON8 format
|
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bon8/
|
|
||||||
template<typename InputType>
|
|
||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
|
||||||
static basic_json from_bon8(InputType&& i,
|
|
||||||
const bool strict = true,
|
|
||||||
const bool allow_exceptions = true)
|
|
||||||
{
|
|
||||||
basic_json result;
|
|
||||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bon8).sax_parse(input_format_t::bon8, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
|
||||||
{
|
|
||||||
result = value_t::discarded;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief create a JSON value from an input in BON8 format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bon8/
|
|
||||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
|
||||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
|
||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
|
||||||
static basic_json from_bon8(IteratorType first, SentinelType last,
|
|
||||||
const bool strict = true,
|
|
||||||
const bool allow_exceptions = true)
|
|
||||||
{
|
|
||||||
basic_json result;
|
|
||||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bon8).sax_parse(input_format_t::bon8, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
|
||||||
{
|
|
||||||
result = value_t::discarded;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief create a JSON value from an input in BSON format
|
/// @brief create a JSON value from an input in BSON format
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
|
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
|
||||||
template<typename InputType>
|
template<typename InputType>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+233
-2108
File diff suppressed because it is too large
Load Diff
@@ -56,14 +56,6 @@
|
|||||||
#define JSON_BRACE_INIT_COPY_SEMANTICS 0
|
#define JSON_BRACE_INIT_COPY_SEMANTICS 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef JSON_PRECISE_STREAM_POSITION
|
|
||||||
#define JSON_PRECISE_STREAM_POSITION 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef JSON_STRICT_NUL_HANDLING
|
|
||||||
#define JSON_STRICT_NUL_HANDLING 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_DIAGNOSTICS
|
#if JSON_DIAGNOSTICS
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||||
#else
|
#else
|
||||||
@@ -88,35 +80,21 @@
|
|||||||
#define NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS
|
#define NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if JSON_PRECISE_STREAM_POSITION
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION _psp
|
|
||||||
#else
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_STRICT_NUL_HANDLING
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING _snul
|
|
||||||
#else
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING
|
|
||||||
#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) json_abi ## a ## b ## c ## d ## e ## f
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d) json_abi ## a ## b ## c ## d
|
||||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f) \
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d) \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f)
|
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d)
|
||||||
|
|
||||||
#define NLOHMANN_JSON_ABI_TAGS \
|
#define NLOHMANN_JSON_ABI_TAGS \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
|
NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
|
||||||
NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \
|
NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \
|
||||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS, \
|
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS, \
|
||||||
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS, \
|
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS)
|
||||||
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
|
||||||
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING)
|
|
||||||
|
|
||||||
// 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,10 +80,7 @@ target_compile_options(test_main PUBLIC
|
|||||||
# std::allocator_traits<...>::construct for the custom-base-class
|
# std::allocator_traits<...>::construct for the custom-base-class
|
||||||
# test's map type past VS2015's limit. The name is only used for
|
# test's map type past VS2015's limit. The name is only used for
|
||||||
# debug info, so truncation does not affect the build.
|
# debug info, so truncation does not affect the build.
|
||||||
# Disable warning C5285: cannot declare a specialization for 'std::tuple'; MSVC 19.51
|
$<$<CXX_COMPILER_ID:MSVC>:/W4;/wd4566;/wd4996;/wd4702;/wd4503>
|
||||||
# reports the forward declarations of standard library
|
|
||||||
# templates in the vendored doctest.h
|
|
||||||
$<$<CXX_COMPILER_ID:MSVC>:/W4;/wd4566;/wd4996;/wd4702;/wd4503;/wd5285>
|
|
||||||
# https://github.com/nlohmann/json/issues/1114
|
# https://github.com/nlohmann/json/issues/1114
|
||||||
$<$<CXX_COMPILER_ID:MSVC>:/bigobj> $<$<BOOL:${MINGW}>:-Wa,-mbig-obj>
|
$<$<CXX_COMPILER_ID:MSVC>:/bigobj> $<$<BOOL:${MINGW}>:-Wa,-mbig-obj>
|
||||||
|
|
||||||
@@ -112,7 +109,7 @@ endif()
|
|||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||||
# avoid stack overflow, see https://github.com/nlohmann/json/issues/2955
|
# avoid stack overflow, see https://github.com/nlohmann/json/issues/2955
|
||||||
json_test_set_test_options("test-bon8;test-cbor;test-msgpack;test-ubjson;test-bjdata;test-binary_formats" LINK_OPTIONS /STACK:4000000)
|
json_test_set_test_options("test-cbor;test-msgpack;test-ubjson;test-bjdata;test-binary_formats" LINK_OPTIONS /STACK:4000000)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# disable exceptions for test-disabled_exceptions
|
# disable exceptions for test-disabled_exceptions
|
||||||
|
|||||||
+1
-4
@@ -10,7 +10,7 @@ CXXFLAGS += -std=c++11
|
|||||||
CPPFLAGS += -I ../single_include
|
CPPFLAGS += -I ../single_include
|
||||||
|
|
||||||
FUZZER_ENGINE = src/fuzzer-driver_afl.cpp
|
FUZZER_ENGINE = src/fuzzer-driver_afl.cpp
|
||||||
FUZZERS = parse_afl_fuzzer parse_bson_fuzzer parse_cbor_fuzzer parse_msgpack_fuzzer parse_ubjson_fuzzer parse_bjdata_fuzzer parse_bon8_fuzzer
|
FUZZERS = parse_afl_fuzzer parse_bson_fuzzer parse_cbor_fuzzer parse_msgpack_fuzzer parse_ubjson_fuzzer parse_bjdata_fuzzer
|
||||||
fuzzers: $(FUZZERS)
|
fuzzers: $(FUZZERS)
|
||||||
|
|
||||||
parse_afl_fuzzer:
|
parse_afl_fuzzer:
|
||||||
@@ -30,6 +30,3 @@ parse_ubjson_fuzzer:
|
|||||||
|
|
||||||
parse_bjdata_fuzzer:
|
parse_bjdata_fuzzer:
|
||||||
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(FUZZER_ENGINE) src/fuzzer-parse_bjdata.cpp -o $@
|
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(FUZZER_ENGINE) src/fuzzer-parse_bjdata.cpp -o $@
|
||||||
|
|
||||||
parse_bon8_fuzzer:
|
|
||||||
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(FUZZER_ENGINE) src/fuzzer-parse_bon8.cpp -o $@
|
|
||||||
|
|||||||
@@ -36,14 +36,6 @@ TEST_CASE("default namespace")
|
|||||||
expected += "_bics";
|
expected += "_bics";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if JSON_PRECISE_STREAM_POSITION
|
|
||||||
expected += "_psp";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_STRICT_NUL_HANDLING
|
|
||||||
expected += "_snul";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
expected += "_v" STRINGIZE(NLOHMANN_JSON_VERSION_MAJOR);
|
expected += "_v" STRINGIZE(NLOHMANN_JSON_VERSION_MAJOR);
|
||||||
expected += "_" STRINGIZE(NLOHMANN_JSON_VERSION_MINOR);
|
expected += "_" STRINGIZE(NLOHMANN_JSON_VERSION_MINOR);
|
||||||
expected += "_" STRINGIZE(NLOHMANN_JSON_VERSION_PATCH) "::basic_json";
|
expected += "_" STRINGIZE(NLOHMANN_JSON_VERSION_PATCH) "::basic_json";
|
||||||
|
|||||||
@@ -37,14 +37,6 @@ TEST_CASE("default namespace without version component")
|
|||||||
expected += "_bics";
|
expected += "_bics";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if JSON_PRECISE_STREAM_POSITION
|
|
||||||
expected += "_psp";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_STRICT_NUL_HANDLING
|
|
||||||
expected += "_snul";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
expected += "::basic_json";
|
expected += "::basic_json";
|
||||||
|
|
||||||
// fallback for Clang
|
// fallback for Clang
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.14)
|
cmake_minimum_required(VERSION 3.11...3.14)
|
||||||
project(JSON_Benchmarks LANGUAGES CXX)
|
project(JSON_Benchmarks LANGUAGES CXX)
|
||||||
|
|
||||||
# set compiler flags
|
# set compiler flags
|
||||||
@@ -6,35 +6,29 @@ if((CMAKE_CXX_COMPILER_ID MATCHES GNU) OR (CMAKE_CXX_COMPILER_ID MATCHES Clang))
|
|||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto -DNDEBUG -O3")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto -DNDEBUG -O3")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# configure Google Benchmark; a fixed release, so that results stay comparable
|
# configure Google Benchmarks
|
||||||
set(JSON_GOOGLE_BENCHMARK_VERSION 1.9.5)
|
|
||||||
include(FetchContent)
|
include(FetchContent)
|
||||||
|
FetchContent_Declare(
|
||||||
# only the library is needed; -Werror would break the pinned release as soon as
|
benchmark
|
||||||
# a newer compiler adds a warning
|
GIT_REPOSITORY https://github.com/google/benchmark.git
|
||||||
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
|
GIT_TAG origin/main
|
||||||
set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL "" FORCE)
|
GIT_SHALLOW TRUE
|
||||||
set(BENCHMARK_ENABLE_WERROR OFF CACHE BOOL "" FORCE)
|
|
||||||
|
|
||||||
FetchContent_Declare(benchmark
|
|
||||||
URL https://github.com/google/benchmark/archive/refs/tags/v${JSON_GOOGLE_BENCHMARK_VERSION}.tar.gz
|
|
||||||
URL_HASH SHA256=9631341c82bac4a288bef951f8b26b41f69021794184ece969f8473977eaa340
|
|
||||||
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
|
|
||||||
)
|
)
|
||||||
FetchContent_MakeAvailable(benchmark)
|
|
||||||
|
FetchContent_GetProperties(benchmark)
|
||||||
|
if(NOT benchmark_POPULATED)
|
||||||
|
FetchContent_Populate(benchmark)
|
||||||
|
set(BENCHMARK_ENABLE_TESTING OFF CACHE INTERNAL "" FORCE)
|
||||||
|
add_subdirectory(${benchmark_SOURCE_DIR} ${benchmark_BINARY_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
# download test data
|
# download test data
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../cmake ${CMAKE_MODULE_PATH})
|
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../cmake ${CMAKE_MODULE_PATH})
|
||||||
include(download_test_data)
|
include(download_test_data)
|
||||||
|
|
||||||
# the header to benchmark; point this at a directory holding another version's
|
|
||||||
# nlohmann/json.hpp to compare versions (see README.md)
|
|
||||||
set(JSON_BENCHMARK_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../single_include" CACHE PATH
|
|
||||||
"directory containing the nlohmann/json.hpp to benchmark")
|
|
||||||
|
|
||||||
# benchmark binary
|
# benchmark binary
|
||||||
add_executable(json_benchmarks src/benchmarks.cpp)
|
add_executable(json_benchmarks src/benchmarks.cpp)
|
||||||
target_compile_features(json_benchmarks PRIVATE cxx_std_11)
|
target_compile_features(json_benchmarks PRIVATE cxx_std_11)
|
||||||
target_link_libraries(json_benchmarks benchmark ${CMAKE_THREAD_LIBS_INIT})
|
target_link_libraries(json_benchmarks benchmark ${CMAKE_THREAD_LIBS_INIT})
|
||||||
add_dependencies(json_benchmarks download_test_data)
|
add_dependencies(json_benchmarks download_test_data)
|
||||||
target_include_directories(json_benchmarks PRIVATE ${JSON_BENCHMARK_INCLUDE_DIR} ${CMAKE_BINARY_DIR}/include)
|
target_include_directories(json_benchmarks PRIVATE ${CMAKE_SOURCE_DIR}/../../single_include ${CMAKE_BINARY_DIR}/include)
|
||||||
|
|||||||
@@ -1,130 +0,0 @@
|
|||||||
# Benchmarks
|
|
||||||
|
|
||||||
Micro-benchmarks for parsing, serialization and the binary formats, written with
|
|
||||||
[Google Benchmark](https://github.com/google/benchmark). They are not run by CI; see
|
|
||||||
[When to run them](#when-to-run-them).
|
|
||||||
|
|
||||||
## What is measured
|
|
||||||
|
|
||||||
| benchmark | what it does |
|
|
||||||
|---|---|
|
|
||||||
| `ParseFile`, `ParseString` | parse JSON from a file stream or a string |
|
|
||||||
| `ParseIndented` | parse the large files re-indented by 4 spaces, for the lexer's whitespace handling |
|
|
||||||
| `Dump` | serialize, compact (`-`) and indented (`4`) |
|
|
||||||
| `ToCbor`, `BinaryToCbor` | write CBOR; `BinaryToCbor` writes binary values of growing size |
|
|
||||||
| `FromMsgpack` | read MessagePack; unchanged over the years, so its numbers stay comparable across releases |
|
|
||||||
| `FromBinaryBuffer`, `FromBinaryFile` | read CBOR, MessagePack, UBJSON, BJData and BSON from a buffer or a `FILE*` |
|
|
||||||
| `FromBinaryShape` | read deeply nested, container-heavy and scalar-heavy documents in every binary format |
|
|
||||||
| `FromCborChunkedString` | read CBOR strings split into indefinite-length chunks |
|
|
||||||
|
|
||||||
The input files are those of [nativejson-benchmark](https://github.com/miloyip/nativejson-benchmark) (`canada`,
|
|
||||||
`citm_catalog`, `twitter`), a large `jeopardy` file, and number-heavy files (`floats`, `signed_ints`, ...).
|
|
||||||
`bytes_per_second` counts the bytes read or written: the JSON text when parsing, the output when serializing.
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- CMake 3.14 or later, a C++11 compiler, and Ninja for the `make` target.
|
|
||||||
- Network access on the first configure: CMake downloads Google Benchmark and the
|
|
||||||
[test data](https://github.com/nlohmann/json_test_data) into the build directory. To reuse a download of the test
|
|
||||||
data, pass `-DJSON_TestDataDirectory=<build directory>/test_files`.
|
|
||||||
- Google Benchmark is pinned to a release (1.9.5), so that results from different days stay comparable. To update it,
|
|
||||||
change `JSON_GOOGLE_BENCHMARK_VERSION` and the archive's `URL_HASH` in `CMakeLists.txt` together.
|
|
||||||
- The benchmarks include `single_include/nlohmann/json.hpp`, so run `make amalgamate` after changing anything in
|
|
||||||
`include/`.
|
|
||||||
|
|
||||||
GCC and Clang builds use `-O3 -flto -DNDEBUG`.
|
|
||||||
|
|
||||||
## Running them
|
|
||||||
|
|
||||||
From the repository root, this builds everything from scratch in `cmake-build-benchmarks` and runs all benchmarks:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
make run_benchmarks
|
|
||||||
```
|
|
||||||
|
|
||||||
To build once and run selectively:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cmake -S tests/benchmarks -B build-benchmarks -G Ninja -DCMAKE_BUILD_TYPE=Release
|
|
||||||
cmake --build build-benchmarks
|
|
||||||
build-benchmarks/json_benchmarks --benchmark_filter='ParseString|Dump'
|
|
||||||
```
|
|
||||||
|
|
||||||
Useful options of `json_benchmarks`:
|
|
||||||
|
|
||||||
| option | effect |
|
|
||||||
|---|---|
|
|
||||||
| `--benchmark_list_tests` | list the benchmarks instead of running them |
|
|
||||||
| `--benchmark_filter=<regex>` | run only the benchmarks whose names match |
|
|
||||||
| `--benchmark_repetitions=<n>` | run every benchmark `n` times and add mean, median, standard deviation and coefficient of variation |
|
|
||||||
| `--benchmark_enable_random_interleaving=true` | run the repetitions in random order, which spreads out drifts such as thermal throttling |
|
|
||||||
| `--benchmark_min_time=<seconds>s` | run each benchmark at least this long (e.g. `2s`) |
|
|
||||||
| `--benchmark_out=<file> --benchmark_out_format=json` | also write the results to a file, e.g. for `compare.py` |
|
|
||||||
|
|
||||||
## Reading the output
|
|
||||||
|
|
||||||
Each line shows the wall-clock `Time` and the `CPU` time per iteration, the number of `Iterations` Google Benchmark
|
|
||||||
chose, and the throughput in `bytes_per_second`. With repetitions, the lines ending in `_median` are the ones to
|
|
||||||
compare. A `_cv` (coefficient of variation) above a few percent means the machine was too noisy for small
|
|
||||||
differences to mean anything.
|
|
||||||
|
|
||||||
## Comparing two versions
|
|
||||||
|
|
||||||
To see what a change or a release did, build the same benchmarks twice: once against the header of the version to
|
|
||||||
compare with, and once against the current one. `JSON_BENCHMARK_INCLUDE_DIR` names the directory holding the
|
|
||||||
`nlohmann/json.hpp` to benchmark. For example, to compare the current checkout with 3.12.0:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# the header of the version to compare with
|
|
||||||
mkdir -p build-baseline-header/nlohmann
|
|
||||||
git show v3.12.0:single_include/nlohmann/json.hpp > build-baseline-header/nlohmann/json.hpp
|
|
||||||
|
|
||||||
# the same benchmarks, built against either header
|
|
||||||
cmake -S tests/benchmarks -B build-baseline -G Ninja -DCMAKE_BUILD_TYPE=Release \
|
|
||||||
-DJSON_BENCHMARK_INCLUDE_DIR="$PWD/build-baseline-header"
|
|
||||||
cmake -S tests/benchmarks -B build-current -G Ninja -DCMAKE_BUILD_TYPE=Release
|
|
||||||
cmake --build build-baseline
|
|
||||||
cmake --build build-current
|
|
||||||
|
|
||||||
# run both, back to back
|
|
||||||
build-baseline/json_benchmarks --benchmark_repetitions=10 --benchmark_enable_random_interleaving=true \
|
|
||||||
--benchmark_out=build-baseline/results.json --benchmark_out_format=json
|
|
||||||
build-current/json_benchmarks --benchmark_repetitions=10 --benchmark_enable_random_interleaving=true \
|
|
||||||
--benchmark_out=build-current/results.json --benchmark_out_format=json
|
|
||||||
```
|
|
||||||
|
|
||||||
Google Benchmark ships a tool to compare the two result files. It needs NumPy and SciPy:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
python3 -m venv build-venv
|
|
||||||
build-venv/bin/pip install numpy scipy
|
|
||||||
build-venv/bin/python build-current/_deps/benchmark-src/tools/compare.py -a benchmarks build-baseline/results.json build-current/results.json
|
|
||||||
```
|
|
||||||
|
|
||||||
The tool's own `tools/requirements.txt` pins NumPy and SciPy versions that need Python 3.11 or later; with an older
|
|
||||||
Python, unpinned versions work as well. In its output:
|
|
||||||
|
|
||||||
- the `Time` and `CPU` columns are relative changes: `-0.35` means 35% faster, `+0.10` means 10% slower;
|
|
||||||
- `_pvalue` lines report a Mann-Whitney U test of whether the two versions differ. It needs at least 9
|
|
||||||
repetitions, and a p-value below 0.05 means the difference is unlikely to be noise;
|
|
||||||
- `OVERALL_GEOMEAN` summarizes all benchmarks;
|
|
||||||
- `-a` shows only the aggregates, not every repetition.
|
|
||||||
|
|
||||||
The header you compare with must support everything the benchmarks use. The current benchmarks build against 3.12.0.
|
|
||||||
Only benchmarks present in both result files are compared, so for older releases, either filter the benchmarks or
|
|
||||||
build that release's own `tests/benchmarks` against its own header.
|
|
||||||
|
|
||||||
## Getting stable numbers
|
|
||||||
|
|
||||||
- Build and run both versions on the same machine, one right after the other.
|
|
||||||
- Keep the machine otherwise idle: no builds, no browser, and a laptop plugged in.
|
|
||||||
- On Linux, set the CPU frequency governor to `performance`, e.g. `sudo cpupower frequency-set --governor performance`.
|
|
||||||
Google Benchmark prints a warning when frequency scaling is enabled. Pinning the process to a core
|
|
||||||
(`taskset -c 2 ...`) helps as well.
|
|
||||||
- Use 10 or more repetitions with random interleaving, compare medians, and treat changes within the `_cv` as noise.
|
|
||||||
|
|
||||||
## When to run them
|
|
||||||
|
|
||||||
They are a manual step, not part of CI: shared CI runners vary more between runs than most of the effects measured.
|
|
||||||
Run the comparison above before a release, comparing the previous release tag with `develop`, and for pull requests
|
|
||||||
that claim to change performance.
|
|
||||||
@@ -131,8 +131,7 @@ static void Dump(benchmark::State& state, const char* filename, int indent)
|
|||||||
|
|
||||||
while (state.KeepRunning())
|
while (state.KeepRunning())
|
||||||
{
|
{
|
||||||
std::string output = j.dump(indent);
|
j.dump(indent);
|
||||||
benchmark::DoNotOptimize(output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state.SetBytesProcessed(state.iterations() * j.dump(indent).size());
|
state.SetBytesProcessed(state.iterations() * j.dump(indent).size());
|
||||||
@@ -274,8 +273,7 @@ enum class binary_format
|
|||||||
ubjson_optimized,
|
ubjson_optimized,
|
||||||
bjdata,
|
bjdata,
|
||||||
bjdata_optimized,
|
bjdata_optimized,
|
||||||
bson,
|
bson
|
||||||
bon8
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::vector<std::uint8_t> to_binary(const json& j, const binary_format format)
|
static std::vector<std::uint8_t> to_binary(const json& j, const binary_format format)
|
||||||
@@ -294,8 +292,6 @@ static std::vector<std::uint8_t> to_binary(const json& j, const binary_format fo
|
|||||||
return json::to_bjdata(j);
|
return json::to_bjdata(j);
|
||||||
case binary_format::bjdata_optimized:
|
case binary_format::bjdata_optimized:
|
||||||
return json::to_bjdata(j, true, true);
|
return json::to_bjdata(j, true, true);
|
||||||
case binary_format::bon8:
|
|
||||||
return json::to_bon8(j);
|
|
||||||
case binary_format::bson:
|
case binary_format::bson:
|
||||||
default:
|
default:
|
||||||
return json::to_bson(j);
|
return json::to_bson(j);
|
||||||
@@ -316,8 +312,6 @@ static json from_binary(const std::vector<std::uint8_t>& bytes, const binary_for
|
|||||||
case binary_format::bjdata:
|
case binary_format::bjdata:
|
||||||
case binary_format::bjdata_optimized:
|
case binary_format::bjdata_optimized:
|
||||||
return json::from_bjdata(bytes);
|
return json::from_bjdata(bytes);
|
||||||
case binary_format::bon8:
|
|
||||||
return json::from_bon8(bytes);
|
|
||||||
case binary_format::bson:
|
case binary_format::bson:
|
||||||
default:
|
default:
|
||||||
return json::from_bson(bytes);
|
return json::from_bson(bytes);
|
||||||
@@ -338,8 +332,6 @@ static json from_binary(std::FILE* file, const binary_format format)
|
|||||||
case binary_format::bjdata:
|
case binary_format::bjdata:
|
||||||
case binary_format::bjdata_optimized:
|
case binary_format::bjdata_optimized:
|
||||||
return json::from_bjdata(file);
|
return json::from_bjdata(file);
|
||||||
case binary_format::bon8:
|
|
||||||
return json::from_bon8(file);
|
|
||||||
case binary_format::bson:
|
case binary_format::bson:
|
||||||
default:
|
default:
|
||||||
return json::from_bson(file);
|
return json::from_bson(file);
|
||||||
@@ -414,10 +406,6 @@ BENCHMARK_CAPTURE(FromBinaryBuffer, bjdata / canada, TEST_DATA_DIRECTORY "/nativ
|
|||||||
BENCHMARK_CAPTURE(FromBinaryBuffer, bjdata / twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json", binary_format::bjdata);
|
BENCHMARK_CAPTURE(FromBinaryBuffer, bjdata / twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json", binary_format::bjdata);
|
||||||
BENCHMARK_CAPTURE(FromBinaryBuffer, bjdata_optimized / canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json", binary_format::bjdata_optimized);
|
BENCHMARK_CAPTURE(FromBinaryBuffer, bjdata_optimized / canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json", binary_format::bjdata_optimized);
|
||||||
BENCHMARK_CAPTURE(FromBinaryBuffer, bjdata_optimized / twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json", binary_format::bjdata_optimized);
|
BENCHMARK_CAPTURE(FromBinaryBuffer, bjdata_optimized / twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json", binary_format::bjdata_optimized);
|
||||||
BENCHMARK_CAPTURE(FromBinaryBuffer, bon8 / jeopardy, TEST_DATA_DIRECTORY "/jeopardy/jeopardy.json", binary_format::bon8);
|
|
||||||
BENCHMARK_CAPTURE(FromBinaryBuffer, bon8 / canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json", binary_format::bon8);
|
|
||||||
BENCHMARK_CAPTURE(FromBinaryBuffer, bon8 / citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json", binary_format::bon8);
|
|
||||||
BENCHMARK_CAPTURE(FromBinaryBuffer, bon8 / twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json", binary_format::bon8);
|
|
||||||
// BSON requires an object at the top level, so the array-rooted test files
|
// BSON requires an object at the top level, so the array-rooted test files
|
||||||
// (jeopardy and the regression files) cannot be captured here
|
// (jeopardy and the regression files) cannot be captured here
|
||||||
BENCHMARK_CAPTURE(FromBinaryBuffer, bson / canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json", binary_format::bson);
|
BENCHMARK_CAPTURE(FromBinaryBuffer, bson / canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json", binary_format::bson);
|
||||||
@@ -461,8 +449,6 @@ BENCHMARK_CAPTURE(FromBinaryFile, cbor / twitter, TEST_DATA_DIRECTORY "/nativejs
|
|||||||
BENCHMARK_CAPTURE(FromBinaryFile, ubjson / canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json", binary_format::ubjson);
|
BENCHMARK_CAPTURE(FromBinaryFile, ubjson / canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json", binary_format::ubjson);
|
||||||
BENCHMARK_CAPTURE(FromBinaryFile, ubjson / twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json", binary_format::ubjson);
|
BENCHMARK_CAPTURE(FromBinaryFile, ubjson / twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json", binary_format::ubjson);
|
||||||
BENCHMARK_CAPTURE(FromBinaryFile, bjdata / twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json", binary_format::bjdata);
|
BENCHMARK_CAPTURE(FromBinaryFile, bjdata / twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json", binary_format::bjdata);
|
||||||
BENCHMARK_CAPTURE(FromBinaryFile, bon8 / canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json", binary_format::bon8);
|
|
||||||
BENCHMARK_CAPTURE(FromBinaryFile, bon8 / twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json", binary_format::bon8);
|
|
||||||
BENCHMARK_CAPTURE(FromBinaryFile, bson / twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json", binary_format::bson);
|
BENCHMARK_CAPTURE(FromBinaryFile, bson / twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json", binary_format::bson);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -543,21 +529,18 @@ BENCHMARK_CAPTURE(FromBinaryShape, nested / msgpack, make_nested, binary_format:
|
|||||||
BENCHMARK_CAPTURE(FromBinaryShape, nested / ubjson, make_nested, binary_format::ubjson);
|
BENCHMARK_CAPTURE(FromBinaryShape, nested / ubjson, make_nested, binary_format::ubjson);
|
||||||
BENCHMARK_CAPTURE(FromBinaryShape, nested / bjdata, make_nested, binary_format::bjdata);
|
BENCHMARK_CAPTURE(FromBinaryShape, nested / bjdata, make_nested, binary_format::bjdata);
|
||||||
BENCHMARK_CAPTURE(FromBinaryShape, nested / bson, make_nested, binary_format::bson);
|
BENCHMARK_CAPTURE(FromBinaryShape, nested / bson, make_nested, binary_format::bson);
|
||||||
BENCHMARK_CAPTURE(FromBinaryShape, nested / bon8, make_nested, binary_format::bon8);
|
|
||||||
BENCHMARK_CAPTURE(FromBinaryShape, containers / cbor, make_containers, binary_format::cbor);
|
BENCHMARK_CAPTURE(FromBinaryShape, containers / cbor, make_containers, binary_format::cbor);
|
||||||
BENCHMARK_CAPTURE(FromBinaryShape, containers / msgpack, make_containers, binary_format::msgpack);
|
BENCHMARK_CAPTURE(FromBinaryShape, containers / msgpack, make_containers, binary_format::msgpack);
|
||||||
BENCHMARK_CAPTURE(FromBinaryShape, containers / ubjson, make_containers, binary_format::ubjson);
|
BENCHMARK_CAPTURE(FromBinaryShape, containers / ubjson, make_containers, binary_format::ubjson);
|
||||||
BENCHMARK_CAPTURE(FromBinaryShape, containers / ubjson_optimized, make_containers, binary_format::ubjson_optimized);
|
BENCHMARK_CAPTURE(FromBinaryShape, containers / ubjson_optimized, make_containers, binary_format::ubjson_optimized);
|
||||||
BENCHMARK_CAPTURE(FromBinaryShape, containers / bjdata, make_containers, binary_format::bjdata);
|
BENCHMARK_CAPTURE(FromBinaryShape, containers / bjdata, make_containers, binary_format::bjdata);
|
||||||
BENCHMARK_CAPTURE(FromBinaryShape, containers / bson, make_containers, binary_format::bson);
|
BENCHMARK_CAPTURE(FromBinaryShape, containers / bson, make_containers, binary_format::bson);
|
||||||
BENCHMARK_CAPTURE(FromBinaryShape, containers / bon8, make_containers, binary_format::bon8);
|
|
||||||
// BSON names every array element, so a large array measures key generation
|
// BSON names every array element, so a large array measures key generation
|
||||||
// rather than scalar decoding and is left out here
|
// rather than scalar decoding and is left out here
|
||||||
BENCHMARK_CAPTURE(FromBinaryShape, scalars / cbor, make_scalars, binary_format::cbor);
|
BENCHMARK_CAPTURE(FromBinaryShape, scalars / cbor, make_scalars, binary_format::cbor);
|
||||||
BENCHMARK_CAPTURE(FromBinaryShape, scalars / msgpack, make_scalars, binary_format::msgpack);
|
BENCHMARK_CAPTURE(FromBinaryShape, scalars / msgpack, make_scalars, binary_format::msgpack);
|
||||||
BENCHMARK_CAPTURE(FromBinaryShape, scalars / ubjson, make_scalars, binary_format::ubjson);
|
BENCHMARK_CAPTURE(FromBinaryShape, scalars / ubjson, make_scalars, binary_format::ubjson);
|
||||||
BENCHMARK_CAPTURE(FromBinaryShape, scalars / bjdata, make_scalars, binary_format::bjdata);
|
BENCHMARK_CAPTURE(FromBinaryShape, scalars / bjdata, make_scalars, binary_format::bjdata);
|
||||||
BENCHMARK_CAPTURE(FromBinaryShape, scalars / bon8, make_scalars, binary_format::bon8);
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief parse an indefinite-length CBOR string
|
@brief parse an indefinite-length CBOR string
|
||||||
|
|||||||
+3
-26
@@ -1,6 +1,6 @@
|
|||||||
# Fuzz testing
|
# Fuzz testing
|
||||||
|
|
||||||
Each parser of the library (JSON, BJData, BON8, BSON, CBOR, MessagePack, and UBJSON) can be fuzz tested. Currently,
|
Each parser of the library (JSON, BJData, BSON, CBOR, MessagePack, and UBJSON) can be fuzz tested. Currently,
|
||||||
[libFuzzer](https://llvm.org/docs/LibFuzzer.html) and [afl++](https://github.com/AFLplusplus/AFLplusplus) are supported.
|
[libFuzzer](https://llvm.org/docs/LibFuzzer.html) and [afl++](https://github.com/AFLplusplus/AFLplusplus) are supported.
|
||||||
|
|
||||||
## Corpus creation
|
## Corpus creation
|
||||||
@@ -10,11 +10,11 @@ directory with some simple input files that cover several features of the parser
|
|||||||
for mutations.
|
for mutations.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
TEST_DATA_VERSION=3.2.0
|
TEST_DATA_VERSION=3.1.0
|
||||||
wget https://github.com/nlohmann/json_test_data/archive/refs/tags/v$TEST_DATA_VERSION.zip
|
wget https://github.com/nlohmann/json_test_data/archive/refs/tags/v$TEST_DATA_VERSION.zip
|
||||||
unzip v$TEST_DATA_VERSION.zip
|
unzip v$TEST_DATA_VERSION.zip
|
||||||
rm v$TEST_DATA_VERSION.zip
|
rm v$TEST_DATA_VERSION.zip
|
||||||
for FORMAT in json bjdata bon8 bson cbor msgpack ubjson
|
for FORMAT in json bjdata bson cbor msgpack ubjson
|
||||||
do
|
do
|
||||||
rm -fr corpus_$FORMAT
|
rm -fr corpus_$FORMAT
|
||||||
mkdir corpus_$FORMAT
|
mkdir corpus_$FORMAT
|
||||||
@@ -79,26 +79,3 @@ the same `fuzzers` target as above and also relies on the `FUZZER_ENGINE` variab
|
|||||||
[build script](https://github.com/google/oss-fuzz/blob/master/projects/json/build.sh) for more information.
|
[build script](https://github.com/google/oss-fuzz/blob/master/projects/json/build.sh) for more information.
|
||||||
|
|
||||||
In case the build at OSS-Fuzz fails, an issue will be created automatically.
|
In case the build at OSS-Fuzz fails, an issue will be created automatically.
|
||||||
|
|
||||||
### Handling OSS-Fuzz reports
|
|
||||||
|
|
||||||
OSS-Fuzz files the crashes it finds in its own [issue tracker](https://issues.oss-fuzz.com), not on GitHub. So that
|
|
||||||
each report can be traced to the change that fixed it, and each fix to the report it answers, fixes follow these
|
|
||||||
conventions:
|
|
||||||
|
|
||||||
- **Reference the OSS-Fuzz issue in the pull request**, next to any GitHub issue it closes, as `OSS-Fuzz: <id>` (for
|
|
||||||
example, `OSS-Fuzz: 563659413`), and in the commit message. The ID alone does not disclose the crash. If the report
|
|
||||||
was triaged into a GitHub issue, link the OSS-Fuzz issue there too.
|
|
||||||
- **Turn the reproducer into a unit test.** Download the testcase from the OSS-Fuzz report, reduce it if possible, and
|
|
||||||
add it as a regression test to the unit test of the affected format (e.g., `tests/src/unit-bjdata.cpp`), with a
|
|
||||||
comment naming the OSS-Fuzz issue. This way the input is checked by every CI run rather than only by OSS-Fuzz, and
|
|
||||||
it stays covered even if OSS-Fuzz later closes the report as not reproducible.
|
|
||||||
- **Keep the fuzzer drivers and the unit tests in sync.** The round-trip checks of the UBJSON and BJData drivers are
|
|
||||||
also run on a fixed corpus in the unit tests (see `tests/src/round_trip_corpus.hpp` and the "round-trip invariants"
|
|
||||||
test cases), so a regression shows up in CI first. When a driver's checks change, change the unit tests with them.
|
|
||||||
- **Record in the report whether the bug shipped.** OSS-Fuzz asks whether a crash was a short-lived regression or
|
|
||||||
affects a released version; answer it when the fix is merged, as it decides whether the fix needs a release note or
|
|
||||||
a security advisory (see the [security policy](../.github/SECURITY.md)).
|
|
||||||
|
|
||||||
After the fix is merged, OSS-Fuzz re-runs the reproducer on its next build and marks the report as verified and
|
|
||||||
closed. If it does not, the fix is incomplete.
|
|
||||||
|
|||||||
@@ -42,9 +42,6 @@ dump() serializes any non-finite double the same deterministic way (as JSON
|
|||||||
`null`, since JSON itself cannot represent NaN/Infinity), so comparing
|
`null`, since JSON itself cannot represent NaN/Infinity), so comparing
|
||||||
dumps is stable under exactly the same values that break operator==.
|
dumps is stable under exactly the same values that break operator==.
|
||||||
|
|
||||||
The unit tests run the same checks on a fixed corpus (see the "BJData round-trip
|
|
||||||
invariants" test case), so keep both in sync.
|
|
||||||
|
|
||||||
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
||||||
drivers.
|
drivers.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
// __ _____ _____ _____
|
|
||||||
// __| | __| | | | JSON for Modern C++ (supporting code)
|
|
||||||
// | | |__ | | | | | | version 3.12.0
|
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
||||||
//
|
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
/*
|
|
||||||
This file implements a parser test suitable for fuzz testing. Given a byte
|
|
||||||
array data, it performs the following steps:
|
|
||||||
|
|
||||||
- j1 = from_bon8(data)
|
|
||||||
- vec = to_bon8(j1)
|
|
||||||
- j2 = from_bon8(vec)
|
|
||||||
- assert(j1 == j2)
|
|
||||||
|
|
||||||
It also checks that reading the data from a stream, which reads strings byte by
|
|
||||||
byte, gives the same value or error as reading it from contiguous memory, which
|
|
||||||
copies strings in bulk.
|
|
||||||
|
|
||||||
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
|
||||||
drivers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
// the round-trip checks below are assertions; NDEBUG would compile them away
|
|
||||||
#ifdef NDEBUG
|
|
||||||
#error "the fuzzer drivers must be built without NDEBUG"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
// the serialization of the value read from @a input, or the error message
|
|
||||||
template<typename InputType>
|
|
||||||
std::string read_bon8(InputType&& input)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const auto vec = json::to_bon8(json::from_bon8(std::forward<InputType>(input)));
|
|
||||||
return {vec.begin(), vec.end()};
|
|
||||||
}
|
|
||||||
catch (const json::exception& e)
|
|
||||||
{
|
|
||||||
return e.what();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
// see http://llvm.org/docs/LibFuzzer.html
|
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
|
||||||
{
|
|
||||||
// contiguous and stream input must be read alike
|
|
||||||
{
|
|
||||||
std::istringstream stream(std::string(reinterpret_cast<const char*>(data), size));
|
|
||||||
assert(read_bon8(std::vector<uint8_t>(data, data + size)) == read_bon8(stream));
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// step 1: parse input
|
|
||||||
std::vector<uint8_t> const vec1(data, data + size);
|
|
||||||
json const j1 = json::from_bon8(vec1);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// step 2: round trip
|
|
||||||
std::vector<uint8_t> const vec2 = json::to_bon8(j1);
|
|
||||||
|
|
||||||
// parse serialization
|
|
||||||
json const j2 = json::from_bon8(vec2);
|
|
||||||
|
|
||||||
// serializations must match
|
|
||||||
assert(json::to_bon8(j2) == vec2);
|
|
||||||
}
|
|
||||||
catch (const json::parse_error&)
|
|
||||||
{
|
|
||||||
// parsing a BON8 serialization must not fail
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const json::parse_error&)
|
|
||||||
{
|
|
||||||
// parse errors are ok, because input may be random bytes
|
|
||||||
}
|
|
||||||
catch (const json::type_error&)
|
|
||||||
{
|
|
||||||
// type errors can occur during parsing, too
|
|
||||||
}
|
|
||||||
catch (const json::out_of_range&)
|
|
||||||
{
|
|
||||||
// out of range errors may happen if provided sizes are excessive
|
|
||||||
}
|
|
||||||
|
|
||||||
// return 0 - non-zero return values are reserved for future use
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -21,9 +21,6 @@ array data, it performs the following steps:
|
|||||||
- j4 = from_ubjson(vec3)
|
- j4 = from_ubjson(vec3)
|
||||||
- assert(j1 == j4)
|
- assert(j1 == j4)
|
||||||
|
|
||||||
The unit tests run the same checks on a fixed corpus (see the "UBJSON round-trip
|
|
||||||
invariants" test case), so keep both in sync.
|
|
||||||
|
|
||||||
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
||||||
drivers.
|
drivers.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,213 +0,0 @@
|
|||||||
// __ _____ _____ _____
|
|
||||||
// __| | __| | | | JSON for Modern C++ (supporting code)
|
|
||||||
// | | |__ | | | | | | version 3.12.0
|
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
||||||
//
|
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cmath> // nan
|
|
||||||
#include <cstddef> // size_t
|
|
||||||
#include <cstdint> // int32_t, int64_t, uint32_t, uint64_t
|
|
||||||
#include <limits> // numeric_limits
|
|
||||||
#include <random> // mt19937
|
|
||||||
#include <string> // string, to_string
|
|
||||||
#include <utility> // move
|
|
||||||
#include <vector> // vector
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
// Values for the round-trip property tests of the UBJSON and BJData writers.
|
|
||||||
//
|
|
||||||
// The fuzzer drivers (tests/src/fuzzer-parse_ubjson.cpp and
|
|
||||||
// fuzzer-parse_bjdata.cpp) check that anything the library parses can be
|
|
||||||
// serialized, parsed back, and serialized again without loss. Those checks
|
|
||||||
// only run at OSS-Fuzz, so a regression used to surface days later as an
|
|
||||||
// external report. The unit tests run the same checks on this corpus in CI.
|
|
||||||
//
|
|
||||||
// The corpus is deterministic: std::mt19937's output sequence is fixed by
|
|
||||||
// the standard, and it is used directly rather than through a distribution
|
|
||||||
// (whose results are implementation-defined).
|
|
||||||
namespace utils
|
|
||||||
{
|
|
||||||
|
|
||||||
class round_trip_corpus
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
static std::vector<json> values()
|
|
||||||
{
|
|
||||||
round_trip_corpus corpus;
|
|
||||||
return corpus.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
// whether a value contains a binary value, which a BJData or UBJSON round
|
|
||||||
// trip may turn into an array of integers
|
|
||||||
static bool contains_binary(const json& j)
|
|
||||||
{
|
|
||||||
if (j.is_binary())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (j.is_structured())
|
|
||||||
{
|
|
||||||
for (const auto& element : j)
|
|
||||||
{
|
|
||||||
if (contains_binary(element))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<json> atoms;
|
|
||||||
// a fixed seed is the point: the corpus must be the same in every run
|
|
||||||
std::mt19937 generator{42}; // NOLINT(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed)
|
|
||||||
|
|
||||||
round_trip_corpus()
|
|
||||||
: atoms
|
|
||||||
{
|
|
||||||
nullptr, true, false,
|
|
||||||
// integers at the boundaries of every UBJSON/BJData integer type
|
|
||||||
0, 1, -1, 127, 128, 255, 256, -128, -129,
|
|
||||||
32767, 32768, 65535, 65536, -32768, -32769,
|
|
||||||
(std::numeric_limits<std::int32_t>::min)(), (std::numeric_limits<std::int32_t>::max)(),
|
|
||||||
(std::numeric_limits<std::uint32_t>::max)(),
|
|
||||||
(std::numeric_limits<std::int64_t>::min)(), (std::numeric_limits<std::int64_t>::max)(),
|
|
||||||
static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()) + 1u,
|
|
||||||
(std::numeric_limits<std::uint64_t>::max)(),
|
|
||||||
// floating-point numbers, including non-finite ones
|
|
||||||
0.0, -0.0, 1.5, -2.25, 3.4e38, (std::numeric_limits<double>::max)(),
|
|
||||||
std::nan(""), std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity(),
|
|
||||||
// strings, including a non-ASCII one and one longer than 255 bytes
|
|
||||||
"", "a", "\xC3\xA4", std::string(300, 'x'),
|
|
||||||
// binary values with and without subtype
|
|
||||||
json::binary({}), json::binary({1, 2, 255}), json::binary({0x80, 0x7F}, 42), json::binary({1}, 0)
|
|
||||||
}
|
|
||||||
{}
|
|
||||||
|
|
||||||
std::vector<json> build()
|
|
||||||
{
|
|
||||||
std::vector<json> result = atoms;
|
|
||||||
|
|
||||||
// each atom inside containers, including homogeneous ones that the
|
|
||||||
// writers encode as optimized (typed) containers
|
|
||||||
result.emplace_back(json::array());
|
|
||||||
result.emplace_back(json::object());
|
|
||||||
for (const auto& atom : atoms)
|
|
||||||
{
|
|
||||||
result.push_back(json::array({atom}));
|
|
||||||
result.push_back(json::array({atom, atom, atom}));
|
|
||||||
result.push_back(json::array({json::array({atom})}));
|
|
||||||
result.push_back(json::object({{"key", atom}}));
|
|
||||||
}
|
|
||||||
result.push_back(json::array({1, 1.5}));
|
|
||||||
result.push_back(json::array({-1, 255}));
|
|
||||||
result.push_back(json::array({"a", "b"}));
|
|
||||||
|
|
||||||
// deep, but well below any recursion or depth limit
|
|
||||||
json nested_array = 1;
|
|
||||||
json nested_object = 1;
|
|
||||||
for (int i = 0; i < 300; ++i)
|
|
||||||
{
|
|
||||||
nested_array = json::array({nested_array});
|
|
||||||
nested_object = json::object({{"key", nested_object}});
|
|
||||||
}
|
|
||||||
result.push_back(nested_array);
|
|
||||||
result.push_back(nested_object);
|
|
||||||
|
|
||||||
add_annotated_arrays(result);
|
|
||||||
add_random_values(result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// objects in the JData annotated array format, which the BJData writer
|
|
||||||
// encodes as ND-arrays when the annotation describes a packed array, and
|
|
||||||
// as plain objects otherwise (see #5398, #5399, #5403, #5404, and #5542)
|
|
||||||
static void add_annotated_arrays(std::vector<json>& result)
|
|
||||||
{
|
|
||||||
const std::vector<json> types =
|
|
||||||
{
|
|
||||||
"uint8", "int8", "uint16", "int16", "uint32", "int32", "uint64", "int64",
|
|
||||||
"single", "double", "char", "byte", "bool", "unknown", 5, nullptr
|
|
||||||
};
|
|
||||||
const std::vector<json> sizes =
|
|
||||||
{
|
|
||||||
json::array(), {3}, {1, 3}, {3, 1}, {2, 3}, {2, 0}, {0, 2}, {2, 2, 2}, {-1, 2}, {2, 1.5},
|
|
||||||
"3", 3, nullptr, json::binary({})
|
|
||||||
};
|
|
||||||
const std::vector<json> data =
|
|
||||||
{
|
|
||||||
nullptr, 5, "s", json::object({{"a", 1}}), json::array(),
|
|
||||||
{1, 2, 3}, {1, 2, 3, 4, 5, 6}, {1, 2, 3, 4, 5, 6, 7, 8},
|
|
||||||
{1.5, 2.5, 3.5, 4.5, 5.5, 6.5}, {300, -300, 70000, -70000, 1, 2},
|
|
||||||
{"a", "b", "c", "d", "e", "f"}, {json::array({1, 2, 3}), json::array({4, 5, 6})}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const auto& type : types)
|
|
||||||
{
|
|
||||||
for (const auto& size : sizes)
|
|
||||||
{
|
|
||||||
for (const auto& d : data)
|
|
||||||
{
|
|
||||||
result.push_back({{"_ArrayType_", type}, {"_ArraySize_", size}, {"_ArrayData_", d}});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// incomplete annotations and annotations with an extra key
|
|
||||||
result.push_back({{"_ArraySize_", {2, 3}}, {"_ArrayData_", {1, 2, 3, 4, 5, 6}}});
|
|
||||||
result.push_back({{"_ArrayType_", "uint8"}, {"_ArrayData_", {1, 2, 3, 4, 5, 6}}});
|
|
||||||
result.push_back({{"_ArrayType_", "uint8"}, {"_ArraySize_", {2, 3}}});
|
|
||||||
result.push_back({{"_ArrayType_", "uint8"}, {"_ArraySize_", {2, 3}}, {"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"extra", 1}});
|
|
||||||
}
|
|
||||||
|
|
||||||
// random containers of atoms, both homogeneous and mixed
|
|
||||||
void add_random_values(std::vector<json>& result)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 1000; ++i)
|
|
||||||
{
|
|
||||||
result.push_back(random_value(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t random_below(std::size_t bound)
|
|
||||||
{
|
|
||||||
return generator() % bound;
|
|
||||||
}
|
|
||||||
|
|
||||||
json random_value(int depth)
|
|
||||||
{
|
|
||||||
const auto kind = random_below(10);
|
|
||||||
if (depth > 3 || kind < 5)
|
|
||||||
{
|
|
||||||
return atoms[random_below(atoms.size())];
|
|
||||||
}
|
|
||||||
|
|
||||||
json result = kind < 8 ? json::array() : json::object();
|
|
||||||
const auto count = random_below(5);
|
|
||||||
const bool homogeneous = random_below(2) == 0;
|
|
||||||
const json fixed = atoms[random_below(atoms.size())];
|
|
||||||
for (std::size_t i = 0; i < count; ++i)
|
|
||||||
{
|
|
||||||
json element = homogeneous ? fixed : random_value(depth + 1);
|
|
||||||
if (result.is_array())
|
|
||||||
{
|
|
||||||
result.push_back(std::move(element));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result[std::to_string(i)] = std::move(element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace utils
|
|
||||||
@@ -270,82 +270,6 @@ TEST_CASE("controlled bad_alloc")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
// counts the allocations of pairs with a non-const first member: the object
|
|
||||||
// types store std::pair<const Key, T>, so only the scratch space of the
|
|
||||||
// iterative deep copy allocates std::pair<Key, T>
|
|
||||||
std::size_t scratch_pair_allocations = 0;
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
struct is_scratch_pair : std::false_type {};
|
|
||||||
|
|
||||||
template<class K, class V>
|
|
||||||
struct is_scratch_pair<std::pair<K, V>> : std::integral_constant < bool, !std::is_const<K>::value > {};
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
struct scratch_counting_allocator : std::allocator<T>
|
|
||||||
{
|
|
||||||
using std::allocator<T>::allocator;
|
|
||||||
|
|
||||||
T* allocate(std::size_t n)
|
|
||||||
{
|
|
||||||
if (is_scratch_pair<T>::value)
|
|
||||||
{
|
|
||||||
++scratch_pair_allocations;
|
|
||||||
}
|
|
||||||
return std::allocator<T>::allocate(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __cpp_lib_allocate_at_least
|
|
||||||
// std::allocator<T>::allocate_at_least would bypass the counting, and
|
|
||||||
// libc++'s containers prefer it over allocate from C++23 on
|
|
||||||
auto allocate_at_least(std::size_t n)
|
|
||||||
{
|
|
||||||
if (is_scratch_pair<T>::value)
|
|
||||||
{
|
|
||||||
++scratch_pair_allocations;
|
|
||||||
}
|
|
||||||
return std::allocator<T>::allocate_at_least(n);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <class U>
|
|
||||||
struct rebind
|
|
||||||
{
|
|
||||||
using other = scratch_counting_allocator<U>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_CASE("deep copy uses the provided allocator")
|
|
||||||
{
|
|
||||||
using counting_json = nlohmann::basic_json<std::map,
|
|
||||||
std::vector,
|
|
||||||
std::string,
|
|
||||||
bool,
|
|
||||||
std::int64_t,
|
|
||||||
std::uint64_t,
|
|
||||||
double,
|
|
||||||
scratch_counting_allocator>;
|
|
||||||
|
|
||||||
// deeper than the 128 levels the copy constructor descends into, so the
|
|
||||||
// innermost objects are copied by the iterative deep copy
|
|
||||||
counting_json j = 1;
|
|
||||||
for (std::size_t i = 0; i < 300; ++i)
|
|
||||||
{
|
|
||||||
counting_json wrapper = counting_json::object();
|
|
||||||
wrapper["a"] = std::move(j);
|
|
||||||
j = std::move(wrapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
scratch_pair_allocations = 0;
|
|
||||||
// NOLINTNEXTLINE(performance-unnecessary-copy-initialization): the copy is what is tested
|
|
||||||
const counting_json copy(j);
|
|
||||||
CHECK(scratch_pair_allocations > 0);
|
|
||||||
CHECK(copy == j);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
template<class T>
|
template<class T>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user