mirror of
https://github.com/nlohmann/json.git
synced 2026-09-26 01:40:32 +00:00
Compare commits
54
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1e83fae94 | ||
|
|
43a346cf99 | ||
|
|
31db00f2b4 | ||
|
|
e1310ad43c | ||
|
|
f6d7eaf280 | ||
|
|
d3be1bd74d | ||
|
|
344148dc33 | ||
|
|
465407f3ce | ||
|
|
02dd3e67f2 | ||
|
|
64119e9b5b | ||
|
|
abbe52d6de | ||
|
|
2994bcd9e2 | ||
|
|
98278dc3f6 | ||
|
|
6c8ea0a6d1 | ||
|
|
01b53c8c15 | ||
|
|
cc472af13f | ||
|
|
80bf54a5a2 | ||
|
|
aada27405d | ||
|
|
3901b223e5 | ||
|
|
9f7c0f3ea7 | ||
|
|
f290b36ad2 | ||
|
|
5f673b0eae | ||
|
|
e94e164b07 | ||
|
|
4daca40d7b | ||
|
|
7c90ec2323 | ||
|
|
305ca7dadd | ||
|
|
2e91641de2 | ||
|
|
8699de3064 | ||
|
|
5f659c881a | ||
|
|
36c079149a | ||
|
|
770f62dda9 | ||
|
|
bc066c1838 | ||
|
|
918da64657 | ||
|
|
f3768d6868 | ||
|
|
56b3ee566c | ||
|
|
ed513715a8 | ||
|
|
f751547a81 | ||
|
|
06b0452189 | ||
|
|
1054b2097e | ||
|
|
f92024b317 | ||
|
|
0b20b7e622 | ||
|
|
d2c1a6a272 | ||
|
|
a2b19d6158 | ||
|
|
e485441123 | ||
|
|
e564136c22 | ||
|
|
663013ce64 | ||
|
|
c41152e620 | ||
|
|
502e9d66f6 | ||
|
|
e8e1ba0db9 | ||
|
|
29ba5973b6 | ||
|
|
75efd6b1c3 | ||
|
|
f58db1c9e8 | ||
|
|
ff65f688f7 | ||
|
|
af91eee2cc |
@@ -158,6 +158,15 @@ make amalgamate
|
||||
Running `make amalgamate` will also apply automatic formatting to the source files using
|
||||
[`Artistic Style`](https://astyle.sourceforge.net/). This formatting may modify your source files in-place. Be certain to review and commit any changes to avoid unintended formatting diffs in commits.
|
||||
|
||||
If you add, rename, or remove a header in `include/nlohmann`, also regenerate the header list in
|
||||
[`BUILD.bazel`](https://github.com/nlohmann/json/blob/develop/BUILD.bazel) (requires CMake) by executing:
|
||||
|
||||
```shell
|
||||
make BUILD.bazel
|
||||
```
|
||||
|
||||
The amalgamation check in CI fails if any of these generated files is out of date.
|
||||
|
||||
## Recommended documentation
|
||||
|
||||
- The library’s [README file](https://github.com/nlohmann/json/blob/master/README.md) is an excellent starting point to
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
- [ ] 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, 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.
|
||||
- [ ] If applicable, the [documentation](https://json.nlohmann.me) is updated.
|
||||
- [ ] The source code is amalgamated by running `make amalgamate`.
|
||||
|
||||
@@ -29,6 +29,27 @@ labels:
|
||||
files:
|
||||
- ".github/external_ci/.*"
|
||||
|
||||
- label: "CI"
|
||||
files:
|
||||
- ".github/(dependabot|labeler)\\.yml"
|
||||
|
||||
- label: "aspect: binary formats"
|
||||
files:
|
||||
- "include/nlohmann/detail/input/binary_reader\\.hpp"
|
||||
- "include/nlohmann/detail/output/binary_writer\\.hpp"
|
||||
- "tests/src/unit-(bson|cbor|msgpack|ubjson|bjdata|bon8|binary_formats)"
|
||||
- "tests/src/fuzzer-parse_(bson|cbor|msgpack|ubjson|bjdata|bon8)"
|
||||
- "docs/mkdocs/docs/features/binary_formats/"
|
||||
- "docs/mkdocs/docs/(api/basic_json|examples)/(to|from)_(bson|cbor|msgpack|ubjson|bjdata|bon8)"
|
||||
|
||||
- label: "aspect: binary formats"
|
||||
title: "(?i)(bson|cbor|msgpack|messagepack|ubjson|bjdata|bon8|binary format)"
|
||||
|
||||
- label: "python"
|
||||
files:
|
||||
- "\\.py$"
|
||||
- "requirements[^/]*\\.txt$"
|
||||
|
||||
- label: "S"
|
||||
size-below: 10
|
||||
- label: "M"
|
||||
|
||||
@@ -57,13 +57,16 @@ jobs:
|
||||
python3 -mvenv venv
|
||||
venv/bin/pip3 install -r $MAIN_DIR/tools/astyle/requirements.txt
|
||||
|
||||
- name: Regenerate amalgamation and formatting
|
||||
- name: Regenerate amalgamation, formatting, and BUILD.bazel
|
||||
run: |
|
||||
cd $MAIN_DIR
|
||||
|
||||
python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json.json -s .
|
||||
python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json_fwd.json -s .
|
||||
|
||||
# the header list of the Bazel "json" target must match the files in include/
|
||||
cmake -P cmake/scripts/gen_bazel_build_file.cmake
|
||||
|
||||
${{ github.workspace }}/venv/bin/astyle --project=tools/astyle/.astylerc --suffix=none --quiet \
|
||||
$INCLUDE_DIR/json.hpp $INCLUDE_DIR/json_fwd.hpp
|
||||
|
||||
@@ -87,7 +90,7 @@ jobs:
|
||||
mkdir -p ${{ github.workspace }}/patch
|
||||
git diff --patch --no-color > ${{ github.workspace }}/patch/amalgamation.patch
|
||||
if [ -s ${{ github.workspace }}/patch/amalgamation.patch ]; then
|
||||
echo "The source code has not been amalgamated/formatted correctly. Diff:"
|
||||
echo "The source code has not been amalgamated/formatted correctly or BUILD.bazel is out of date. Diff:"
|
||||
cat ${{ github.workspace }}/patch/amalgamation.patch
|
||||
echo "has_diff=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
|
||||
@@ -38,14 +38,14 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
|
||||
uses: github/codeql-action/init@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
|
||||
with:
|
||||
languages: c-cpp
|
||||
|
||||
# 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)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
|
||||
uses: github/codeql-action/autobuild@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
|
||||
uses: github/codeql-action/analyze@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
|
||||
|
||||
@@ -95,13 +95,13 @@ jobs:
|
||||
issue_number: issue_number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: '## 🔴 Amalgamation check failed! 🔴\nThe source code has not been amalgamated and/or formatted correctly.'
|
||||
body: '## 🔴 Amalgamation check failed! 🔴\nThe source code has not been amalgamated and/or formatted correctly, or `BUILD.bazel` is out of date.'
|
||||
+ (hasPatch ? '\n\n📎 A ready-to-apply patch is attached to the [failed workflow run](' + runUrl + ') as the `amalgamation-patch` artifact.'
|
||||
+ ' Download it, then apply it locally from the repository root with:'
|
||||
+ '\n\n```shell\ngit apply amalgamation.patch\n```\n\n'
|
||||
+ 'This does not require installing astyle yourself.'
|
||||
: '')
|
||||
+ (first ? '\n\n@' + author + ' Please read and follow the [Contribution Guidelines]'
|
||||
+ '(https://github.com/nlohmann/json/blob/develop/.github/CONTRIBUTING.md#files-to-change).'
|
||||
+ '(https://github.com/nlohmann/json/blob/develop/.github/CONTRIBUTING.md#amalgamate-the-source-code).'
|
||||
: '')
|
||||
})
|
||||
|
||||
@@ -43,6 +43,6 @@ jobs:
|
||||
output: 'flawfinder_results.sarif'
|
||||
|
||||
- name: Upload analysis results to GitHub Security tab
|
||||
uses: github/codeql-action/upload-sarif@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
|
||||
uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
|
||||
with:
|
||||
sarif_file: ${{github.workspace}}/flawfinder_results.sarif
|
||||
|
||||
@@ -76,6 +76,6 @@ jobs:
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
|
||||
uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
|
||||
# Upload SARIF file generated in previous step
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
|
||||
uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
|
||||
with:
|
||||
sarif_file: semgrep.sarif
|
||||
if: always()
|
||||
|
||||
@@ -100,7 +100,7 @@ jobs:
|
||||
container: ubuntu:focal
|
||||
strategy:
|
||||
matrix:
|
||||
target: [ci_cmake_flags, ci_test_diagnostics, ci_test_diagnostic_positions, ci_test_noexceptions, ci_test_noimplicitconversions, ci_test_legacycomparison, ci_test_noglobaludls, ci_test_simdutf]
|
||||
target: [ci_cmake_flags, ci_test_diagnostics, ci_test_diagnostic_positions, ci_test_noexceptions, ci_test_noimplicitconversions, ci_test_legacycomparison, ci_test_noglobaludls, ci_test_disableenumserialization, ci_test_skiplibraryversioncheck, ci_test_simdutf, ci_test_strict_nul_handling, ci_test_no_thread_local]
|
||||
steps:
|
||||
- name: Install build-essential
|
||||
run: apt-get update ; apt-get install -y build-essential unzip wget git libssl-dev
|
||||
|
||||
@@ -124,11 +124,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Run CMake (Release)
|
||||
run: cmake -S . -B build -G "Visual Studio 17 2022" -A ARM64 -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/W4 /WX"
|
||||
run: cmake -S . -B build -G "Visual Studio 18 2026" -A ARM64 -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/W4 /WX"
|
||||
if: matrix.build_type == 'Release'
|
||||
shell: pwsh
|
||||
- name: Run CMake (Debug)
|
||||
run: cmake -S . -B build -G "Visual Studio 17 2022" -A ARM64 -DJSON_BuildTests=On -DJSON_FastTests=ON -DCMAKE_CXX_FLAGS="/W4 /WX"
|
||||
run: cmake -S . -B build -G "Visual Studio 18 2026" -A ARM64 -DJSON_BuildTests=On -DJSON_FastTests=ON -DCMAKE_CXX_FLAGS="/W4 /WX"
|
||||
if: matrix.build_type == 'Debug'
|
||||
shell: pwsh
|
||||
- name: Build
|
||||
@@ -158,6 +158,10 @@ jobs:
|
||||
# to fit: IMAGE_REL_AMD64_SECREL against `.debug_line'" because the
|
||||
# MinGW linker cannot relocate the debug sections this test produces.
|
||||
# The tests are only built and run here, so the debug info is not used.
|
||||
# Do not add -O1 here to shrink the objects further: it does make them
|
||||
# link, but the binaries clang 11.0.1 and clang 18.1.8 then produce crash
|
||||
# before doctest prints its first line - 39 of 102 tests on clang 18.
|
||||
# Keep the objects small by splitting the test files instead.
|
||||
- name: Run CMake
|
||||
run: cmake -S . -B build ^
|
||||
-DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang++.exe" ^
|
||||
|
||||
+4
-1
@@ -30,8 +30,10 @@ cc_library(
|
||||
"include/nlohmann/detail/input/input_adapters.hpp",
|
||||
"include/nlohmann/detail/input/json_sax.hpp",
|
||||
"include/nlohmann/detail/input/lexer.hpp",
|
||||
"include/nlohmann/detail/input/number_parse.hpp",
|
||||
"include/nlohmann/detail/input/parser.hpp",
|
||||
"include/nlohmann/detail/input/position_t.hpp",
|
||||
"include/nlohmann/detail/input/string_scan.hpp",
|
||||
"include/nlohmann/detail/iterators/internal_iterator.hpp",
|
||||
"include/nlohmann/detail/iterators/iter_impl.hpp",
|
||||
"include/nlohmann/detail/iterators/iteration_proxy.hpp",
|
||||
@@ -49,12 +51,14 @@ cc_library(
|
||||
"include/nlohmann/detail/meta/detected.hpp",
|
||||
"include/nlohmann/detail/meta/identity_tag.hpp",
|
||||
"include/nlohmann/detail/meta/is_sax.hpp",
|
||||
"include/nlohmann/detail/meta/logic.hpp",
|
||||
"include/nlohmann/detail/meta/std_fs.hpp",
|
||||
"include/nlohmann/detail/meta/type_traits.hpp",
|
||||
"include/nlohmann/detail/meta/void_t.hpp",
|
||||
"include/nlohmann/detail/output/binary_writer.hpp",
|
||||
"include/nlohmann/detail/output/output_adapters.hpp",
|
||||
"include/nlohmann/detail/output/serializer.hpp",
|
||||
"include/nlohmann/detail/recursion_depth_limit.hpp",
|
||||
"include/nlohmann/detail/string_concat.hpp",
|
||||
"include/nlohmann/detail/string_escape.hpp",
|
||||
"include/nlohmann/detail/string_utils.hpp",
|
||||
@@ -67,7 +71,6 @@ cc_library(
|
||||
],
|
||||
includes = ["include"],
|
||||
visibility = ["//visibility:public"],
|
||||
alwayslink = True,
|
||||
)
|
||||
|
||||
cc_library(
|
||||
|
||||
@@ -59,6 +59,7 @@ option(JSON_LegacyDiscardedValueComparison "Enable legacy discarded value compar
|
||||
option(JSON_Install "Install CMake targets during install step." ${MAIN_PROJECT})
|
||||
option(JSON_MultipleHeaders "Use non-amalgamated version of the library." ON)
|
||||
option(JSON_SystemInclude "Include as system headers (skip for clang-tidy)." OFF)
|
||||
option(JSON_StrictNulHandling "Build with strict NUL-byte handling enabled." OFF)
|
||||
|
||||
if (JSON_CI)
|
||||
include(ci)
|
||||
@@ -108,6 +109,10 @@ if (JSON_Diagnostics)
|
||||
message(STATUS "Diagnostics enabled (JSON_DIAGNOSTICS=1)")
|
||||
endif()
|
||||
|
||||
if (JSON_StrictNulHandling)
|
||||
message(STATUS "Strict NUL-byte handling enabled (JSON_STRICT_NUL_HANDLING=1)")
|
||||
endif()
|
||||
|
||||
if (JSON_Diagnostic_Positions)
|
||||
message(STATUS "Diagnostic positions enabled (JSON_DIAGNOSTIC_POSITIONS=1)")
|
||||
endif()
|
||||
@@ -141,6 +146,7 @@ target_compile_definitions(
|
||||
$<$<BOOL:${JSON_Diagnostics}>:JSON_DIAGNOSTICS=1>
|
||||
$<$<BOOL:${JSON_Diagnostic_Positions}>:JSON_DIAGNOSTIC_POSITIONS=1>
|
||||
$<$<BOOL:${JSON_LegacyDiscardedValueComparison}>:JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1>
|
||||
$<$<BOOL:${JSON_StrictNulHandling}>:JSON_STRICT_NUL_HANDLING=1>
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
|
||||
@@ -250,12 +250,16 @@ Further documentation:
|
||||
|
||||
### `BUILD.bazel`
|
||||
|
||||
The file can be updated by calling
|
||||
The build definition for [Bazel](https://bazel.build). The file is generated by
|
||||
`cmake/scripts/gen_bazel_build_file.cmake`, which derives the header list from the files in `include`; change the
|
||||
script rather than editing the file by hand. The file can be updated by calling
|
||||
|
||||
```shell
|
||||
make BUILD.bazel
|
||||
```
|
||||
|
||||
The "Check amalgamation" workflow fails if the file is out of date.
|
||||
|
||||
### `meson.build`
|
||||
|
||||
The build definition for the [Meson](https://mesonbuild.com) build system.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.PHONY: pretty clean ChangeLog.md release update_hedley update_hedley_undef
|
||||
.PHONY: pretty clean ChangeLog.md release update_hedley update_hedley_undef BUILD.bazel
|
||||
|
||||
##########################################################################
|
||||
# configuration
|
||||
@@ -30,11 +30,13 @@ AMALGAMATED_FWD_FILE=single_include/nlohmann/json_fwd.hpp
|
||||
# main target
|
||||
all:
|
||||
@echo "amalgamate - amalgamate files single_include/nlohmann/json{,_fwd}.hpp from the include/nlohmann sources"
|
||||
@echo "BUILD.bazel - regenerate the Bazel BUILD file from the include/nlohmann sources"
|
||||
@echo "ChangeLog.md - generate ChangeLog file"
|
||||
@echo "check-amalgamation - check whether sources have been amalgamated"
|
||||
@echo "check-amalgamation - check whether sources have been amalgamated and BUILD.bazel is up to date"
|
||||
@echo "clean - remove built files"
|
||||
@echo "doctest - compile example files and check their output"
|
||||
@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_cbor - prepare fuzz testing of the CBOR parser"
|
||||
@echo "fuzz_testing_msgpack - prepare fuzz testing of the MessagePack parser"
|
||||
@@ -70,6 +72,14 @@ fuzz_testing:
|
||||
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"
|
||||
|
||||
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:
|
||||
rm -fr fuzz-testing
|
||||
mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out
|
||||
@@ -172,8 +182,13 @@ check-amalgamation:
|
||||
@diff $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_FWD_FILE)~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv $(AMALGAMATED_FWD_FILE)~ $(AMALGAMATED_FWD_FILE) ; false)
|
||||
@mv $(AMALGAMATED_FILE)~ $(AMALGAMATED_FILE)
|
||||
@mv $(AMALGAMATED_FWD_FILE)~ $(AMALGAMATED_FWD_FILE)
|
||||
@mv BUILD.bazel BUILD.bazel~
|
||||
@$(MAKE) BUILD.bazel
|
||||
@diff BUILD.bazel BUILD.bazel~ || (echo "===================================================================\n BUILD.bazel is out of date! Please run 'make BUILD.bazel'.\n===================================================================" ; mv BUILD.bazel~ BUILD.bazel ; false)
|
||||
@mv BUILD.bazel~ BUILD.bazel
|
||||
|
||||
BUILD.bazel: $(SRCS)
|
||||
# generate the Bazel BUILD file; phony, because a removed header would not trigger a rebuild
|
||||
BUILD.bazel:
|
||||
cmake -P cmake/scripts/gen_bazel_build_file.cmake
|
||||
|
||||
##########################################################################
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
- [Implicit conversions](#implicit-conversions)
|
||||
- [Conversions to/from arbitrary types](#arbitrary-types-conversions)
|
||||
- [Specializing enum conversion](#specializing-enum-conversion)
|
||||
- [Binary formats (BSON, CBOR, MessagePack, UBJSON, and BJData)](#binary-formats-bson-cbor-messagepack-ubjson-and-bjdata)
|
||||
- [Binary formats (BSON, CBOR, MessagePack, UBJSON, BJData, and BON8)](#binary-formats-bson-cbor-messagepack-ubjson-bjdata-and-bon8)
|
||||
- [Customers](#customers)
|
||||
- [Ecosystem](#ecosystem)
|
||||
- [Supported compilers](#supported-compilers)
|
||||
@@ -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 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)
|
||||
- **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)
|
||||
- **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)
|
||||
- **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)
|
||||
- **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.
|
||||
- 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, and BJData)
|
||||
### Binary formats (BSON, CBOR, MessagePack, UBJSON, BJData, and BON8)
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
```cpp
|
||||
// create a JSON value
|
||||
@@ -1149,6 +1149,14 @@ std::vector<std::uint8_t> v_ubjson = json::to_ubjson(j);
|
||||
|
||||
// roundtrip
|
||||
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.
|
||||
@@ -1421,7 +1429,7 @@ I deeply appreciate the help of the following people.
|
||||
6. [Joshua C. Randall](https://github.com/jrandall) fixed a bug in the floating-point serialization.
|
||||
7. [Aaron Burghardt](https://github.com/aburgh) implemented code to parse streams incrementally. Furthermore, he greatly improved the parser class by allowing the definition of a filter function to discard undesired elements while parsing.
|
||||
8. [Daniel Kopeček](https://github.com/dkopecek) fixed a bug in the compilation with GCC 5.0.
|
||||
9. [Florian Weber](https://github.com/Florianjw) fixed a bug in and improved the performance of the comparison operators.
|
||||
9. [Fiona Johanna Weber](https://github.com/Fiona-J-W) fixed a bug in and improved the performance of the comparison operators.
|
||||
10. [Eric Cornelius](https://github.com/EricMCornelius) pointed out a bug in the handling with NaN and infinity values. He also improved the performance of the string escaping.
|
||||
11. [易思龙](https://github.com/likebeta) implemented a conversion from anonymous enums.
|
||||
12. [kepkin](https://github.com/kepkin) patiently pushed forward the support for Microsoft Visual Studio.
|
||||
@@ -1523,14 +1531,14 @@ I deeply appreciate the help of the following people.
|
||||
108. [Kevin Tonon](https://github.com/ktonon) overworked the C++11 compiler checks in CMake.
|
||||
109. [Axel Huebl](https://github.com/ax3l) simplified a CMake check and added support for the [Spack package manager](https://spack.io).
|
||||
110. [Carlos O'Ryan](https://github.com/coryan) fixed a typo.
|
||||
111. [James Upjohn](https://github.com/jammehcow) fixed a version number in the compilers section.
|
||||
111. [James Upjohn](https://github.com/jupjohn) fixed a version number in the compilers section.
|
||||
112. [Chuck Atkins](https://github.com/chuckatkins) adjusted the CMake files to the CMake packaging guidelines and provided documentation for the CMake integration.
|
||||
113. [Jan Schöppach](https://github.com/dns13) fixed a typo.
|
||||
114. [martin-mfg](https://github.com/martin-mfg) fixed a typo.
|
||||
115. [Matthias Möller](https://github.com/TinyTinni) removed the dependency from `std::stringstream`.
|
||||
116. [agrianius](https://github.com/agrianius) added code to use alternative string implementations.
|
||||
117. [Daniel599](https://github.com/Daniel599) allowed to use more algorithms with the `items()` function.
|
||||
118. [Julius Rakow](https://github.com/jrakow) fixed the Meson include directory and fixed the links to [cppreference.com](https://cppreference.com).
|
||||
118. [Julius Rakow](https://github.com/juliusrakow) fixed the Meson include directory and fixed the links to [cppreference.com](https://cppreference.com).
|
||||
119. [Sonu Lohani](https://github.com/sonulohani) fixed the compilation with MSVC 2015 in debug mode.
|
||||
120. [grembo](https://github.com/grembo) fixed the test suite and re-enabled several test cases.
|
||||
121. [Hyeon Kim](https://github.com/simnalamburt) introduced the macro `JSON_INTERNAL_CATCH` to control the exception handling inside the library.
|
||||
@@ -1581,7 +1589,7 @@ I deeply appreciate the help of the following people.
|
||||
166. [Mark Beckwith](https://github.com/wythe) fixed a typo.
|
||||
167. [yann-morin-1998](https://github.com/yann-morin-1998) helped to reduce the CMake requirement to version 3.1.
|
||||
168. [Konstantin Podsvirov](https://github.com/podsvirov) maintains a package for the MSYS2 software distro.
|
||||
169. [remyabel](https://github.com/remyabel) added GNUInstallDirs to the CMake files.
|
||||
169. [remyabel](https://github.com/remyabel2) added GNUInstallDirs to the CMake files.
|
||||
170. [Taylor Howard](https://github.com/taylorhoward92) fixed a unit test.
|
||||
171. [Gabe Ron](https://github.com/Macr0Nerd) implemented the `to_string` method.
|
||||
172. [Watal M. Iwasaki](https://github.com/heavywatal) fixed a Clang warning.
|
||||
@@ -1608,7 +1616,7 @@ I deeply appreciate the help of the following people.
|
||||
193. [Hubert Chathi](https://github.com/uhoreg) made CMake's version config file architecture-independent.
|
||||
194. [OmnipotentEntity](https://github.com/OmnipotentEntity) implemented the binary values for CBOR, MessagePack, BSON, and UBJSON.
|
||||
195. [ArtemSarmini](https://github.com/ArtemSarmini) fixed a compilation issue with GCC 10 and fixed a leak.
|
||||
196. [Evgenii Sopov](https://github.com/sea-kg) integrated the library to the wsjcpp package manager.
|
||||
196. [Evgenii Sopov](https://github.com/sea5kg) integrated the library to the wsjcpp package manager.
|
||||
197. [Sergey Linev](https://github.com/linev) fixed a compiler warning.
|
||||
198. [Miguel Magalhães](https://github.com/magamig) fixed the year in the copyright.
|
||||
199. [Gareth Sylvester-Bradley](https://github.com/garethsb-sony) fixed a compilation issue with MSVC.
|
||||
@@ -1702,7 +1710,7 @@ I deeply appreciate the help of the following people.
|
||||
287. [NN](https://github.com/NN---) added the Visual Studio output directory to `.gitignore`.
|
||||
288. [Romain Reignier](https://github.com/romainreignier) improved the performance of the vector output adapter.
|
||||
289. [Mike](https://github.com/Mike-Leo-Smith) fixed the `std::iterator_traits`.
|
||||
290. [Richard Hozák](https://github.com/zxey) added macro `JSON_NO_ENUM` to disable default enum conversions.
|
||||
290. [Richard Hozák](https://github.com/richardhozak) added macro `JSON_NO_ENUM` to disable default enum conversions.
|
||||
291. [vakokako](https://github.com/vakokako) fixed tests when compiling with C++20.
|
||||
292. [Alexander “weej” Jones](https://github.com/alexweej) fixed an example in the README.
|
||||
293. [Eli Schwartz](https://github.com/eli-schwartz) added more files to the `include.zip` archive.
|
||||
@@ -1727,7 +1735,7 @@ I deeply appreciate the help of the following people.
|
||||
312. [Gareth Sylvester-Bradley](https://github.com/garethsb) added `operator/=` and `operator/` to construct JSON pointers.
|
||||
313. [Michael Macnair](https://github.com/mykter) added support for afl-fuzz testing.
|
||||
314. [Berkus Decker](https://github.com/berkus) fixed a typo in the README.
|
||||
315. [Illia Polishchuk](https://github.com/effolkronium) improved the CMake testing.
|
||||
315. [Illia Polishchuk](https://github.com/ilqvya) improved the CMake testing.
|
||||
316. [Ikko Ashimine](https://github.com/eltociear) fixed a typo.
|
||||
317. [Raphael Grimm](https://github.com/barcode) added the possibility to define a custom base class.
|
||||
318. [tocic](https://github.com/tocic) fixed typos in the documentation.
|
||||
@@ -1797,6 +1805,66 @@ I deeply appreciate the help of the following people.
|
||||
382. [bitFiedler](https://github.com/bitFiedler) made GDB pretty printer work with Python 3.8.
|
||||
383. [Gianfranco Costamagna](https://github.com/LocutusOfBorg) fixed a compiler warning.
|
||||
384. [risa2000](https://github.com/risa2000) made `std::filesystem::path` conversion to/from UTF-8 encoded string explicit.
|
||||
385. [AM](https://github.com/maqnouch) fixed typos in the README.
|
||||
386. [dmenendez-gruposantander](https://github.com/dmenendez-gruposantander) fixed typos in the comments of the examples.
|
||||
387. [Mihai Stan](https://github.com/mstan-xx) fixed comparisons against the literal `0`.
|
||||
388. [Matt Gumbel](https://github.com/intelmatt) fixed some `-Weffc++` warnings.
|
||||
389. [vimpunk](https://github.com/vimpunk) moved a lambda out of an unevaluated context to support older compilers.
|
||||
390. [Chris Harris](https://github.com/cjh1) fixed the compilation with GCC 4.8.
|
||||
391. [Palmer Dabbelt](https://github.com/palmer-dabbelt) generated and installed a pkg-config file.
|
||||
392. [Gus Pozuelo](https://github.com/ap-viavi) made `ordered_map` compatible with GCC 5.5, Clang 3.6, and Xcode 9.
|
||||
393. [AK](https://github.com/Lioncky) fixed an MSVC build error caused by the `min`/`max` macros from `windows.h`.
|
||||
394. [Sergiu Deitsch](https://github.com/sergiud) provided a fallback for missing `char8_t` support.
|
||||
395. [Xiaochuan Ye](https://github.com/XueSongTap) fixed `from_msgpack` for `std::byte` input by specializing `std::char_traits`.
|
||||
396. [Ville Vesilehto](https://github.com/thevilledev) fixed an overflow in the BJData size calculation and rejected overflowing negative integers in CBOR.
|
||||
397. [NmPassTHFan](https://github.com/nmpassthf) replaced the deprecated `std::is_trivial` for C++26.
|
||||
398. [Chris Ever](https://github.com/chirsz-ever) added the `ignore_trailing_commas` parser option.
|
||||
399. [Kuan-Fu Wu](https://github.com/kfwu1999) fixed the example code for `json_pointer` initialization.
|
||||
400. [David Kilzer](https://github.com/ddkilzer) added a missing header to the input adapters.
|
||||
401. [Miko](https://github.com/mikomikotaishi) added proper C++20 module support, simplified the module API, and fixed missing exports.
|
||||
402. [hitgirl](https://github.com/hitgil) fixed the CMake configuration when cross-compiling.
|
||||
403. [Devon Thomas](https://github.com/ThomaDevOSU) mentioned the Artistic Style formatting in the contribution guidelines.
|
||||
404. [Erik Hu](https://github.com/Erikhu1) made Coveralls upload errors non-fatal in the CI.
|
||||
405. [co63oc](https://github.com/co63oc) fixed typos.
|
||||
406. [DmitriBogdanov](https://github.com/DmitriBogdanov) fixed broken package manager links in the documentation.
|
||||
407. [Bander](https://github.com/banderzhm) improved the MSVC compatibility of the C++ modules.
|
||||
408. [Andy Choi](https://github.com/ccpong) removed an unnecessary `template` keyword before `get` in the README and the documentation.
|
||||
409. [SamareshSingh](https://github.com/ssam18) fixed single-element brace initialization to copy/move instead of wrapping in an array, fixed the `WITH_DEFAULT` macros for `ordered_map`, and handled moved events in `serve_header.py`.
|
||||
410. [Aditya](https://github.com/Lumowhisp) improved the documentation of the documentation generation.
|
||||
411. [cheese1](https://github.com/cheese1) clarified the README.
|
||||
412. [KhloodElhossiny](https://github.com/khloodelhossiny) enabled `std::string_view` keys in `operator[]`.
|
||||
413. [Charles Cabergs](https://github.com/cacharle) fixed a `-Wtautological-constant-out-of-range-compare` warning.
|
||||
414. [EALePain](https://github.com/EALePain) made the `std::tuple` conversion work with reference types such as `std::tie`.
|
||||
415. [koala_oishi](https://github.com/chibi-dogs) fixed grammatical wording in the README.
|
||||
416. [riccardoori11](https://github.com/riccardoori11) fixed a typo in the documentation.
|
||||
417. [Swastik Bose](https://github.com/VasuBhakt) fixed the parent pointers after `update()` with `JSON_DIAGNOSTICS` and fixed the Doxygen autolinking of requirements.
|
||||
418. [trdesilva](https://github.com/trdesilva) added `front`, `pop_front`, and `push_front` to `json_pointer`.
|
||||
419. [Akhilesh Arora](https://github.com/akhilesharora) fixed an incomplete-type error with `ordered_json`.
|
||||
420. [Hariom Phulre](https://github.com/hariomphulre) fixed the C++20 modules compilation with GCC.
|
||||
421. [Kirill Lokotkov](https://github.com/RUSLoker) fixed printing `long double` values.
|
||||
422. [George Sedov](https://github.com/radistmorse) added the `NLOHMANN_DEFINE_TYPE_*_WITH_NAMES` macros.
|
||||
423. [Caillin Nugent](https://github.com/nugentcaillin) added the `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT` macro.
|
||||
424. [Cosmin D.](https://github.com/drcosmin) fixed `std::filesystem::path` conversions and added an MSVC workaround for `std::unique_ptr`.
|
||||
425. [Paul Dreik](https://github.com/pauldreik) fixed a test relying on implementation-specific behavior.
|
||||
426. [Daniel Falk](https://github.com/daniel-falk) added missing copyright notices to the SBOM.
|
||||
427. [Federico Sfriso](https://github.com/federicosfriso05-dotcom) added support for constructing JSON values from C++20 range views.
|
||||
428. [Luke Banicevic](https://github.com/banaboi) fixed corrupt BSON output for lengths exceeding `INT32_MAX`, cleaned up the BSON writer, and improved the documentation.
|
||||
429. [Patrick Armstrong](https://github.com/Patrick10199) updated the CBOR references and the half-precision float assertions.
|
||||
430. [Yash Bavadiya](https://github.com/xevrion) added checks to all BSON reads.
|
||||
431. [hum4nBeing](https://github.com/hum4nBeing) fixed the overflow handling of high-precision numbers in UBJSON.
|
||||
432. [tomatotomata](https://github.com/tomatotomata) added checks for reading CBOR tagged subtypes.
|
||||
433. [YingqiDuan](https://github.com/YingqiDuan) documented the BSON interoperability.
|
||||
434. [KBS](https://github.com/youdie006) documented the standards compliance and the strictness of `parse()` and `operator>>`.
|
||||
435. [Petr Bělohlávek](https://github.com/petrbel) added Clang 21 and 22 to the CI.
|
||||
436. [Dmitry Rantovov](https://github.com/darkdi) fixed the placement of a CBOR documentation block.
|
||||
437. [ljcjclljc](https://github.com/ljcjclljc) fixed the comparison of large unsigned integers with signed integers.
|
||||
438. [Sahil Kamate](https://github.com/sahilkamate03) fixed the handling of CBOR tags 0-5 and 21-23.
|
||||
439. [Krishnanand G](https://github.com/Krishnanand-G) made the UBJSON writer reject `use_type` without `use_size`.
|
||||
440. [whn](https://github.com/Whning0513) documented the lenient BSON input handling and corrected the complexity of `to_bson`.
|
||||
441. [elix3r](https://github.com/22elix3r) fixed `update()` with `merge_objects` when merging a primitive into an object.
|
||||
442. [Avionic Harshit](https://github.com/avionicharshit-byte) made `diff()` linear when an array shrinks.
|
||||
443. [Qatadaha Bin Matloob](https://github.com/qatcod) fixed comparisons between integers and floats and fixed unparsable BJData output.
|
||||
444. [Wu Shuwen](https://github.com/dajiaohuang) removed an unused include.
|
||||
|
||||
Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone.
|
||||
|
||||
|
||||
+64
-9
@@ -231,18 +231,20 @@ add_custom_target(ci_test_simdutf
|
||||
)
|
||||
|
||||
###############################################################################
|
||||
# Enable brace-init copy semantics.
|
||||
# Enable strict NUL-byte handling.
|
||||
###############################################################################
|
||||
|
||||
add_custom_target(ci_test_brace_init_copy_semantics
|
||||
add_custom_target(ci_test_strict_nul_handling
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
||||
-DJSON_BuildTests=ON -DJSON_FastTests=ON
|
||||
-DCMAKE_CXX_FLAGS=-DJSON_BRACE_INIT_COPY_SEMANTICS=1
|
||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_brace_init_copy_semantics
|
||||
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_brace_init_copy_semantics
|
||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_brace_init_copy_semantics && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
|
||||
COMMENT "Compile and test with brace-init copy semantics enabled"
|
||||
-DJSON_BuildTests=ON -DJSON_FastTests=ON -DJSON_StrictNulHandling=ON
|
||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_strict_nul_handling
|
||||
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_strict_nul_handling
|
||||
# unit-testsuites contains a fixture (a "1e308" test value) that relies on the
|
||||
# legacy NUL-as-end-of-input behavior this macro disables; exclude it here, as
|
||||
# it is expected to fail under strict NUL handling and is out of scope for it
|
||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_strict_nul_handling && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure -E "test-testsuites"
|
||||
COMMENT "Compile and test with strict NUL-byte handling enabled"
|
||||
)
|
||||
|
||||
###############################################################################
|
||||
@@ -260,6 +262,59 @@ add_custom_target(ci_test_noglobaludls
|
||||
COMMENT "Compile and test with global UDLs disabled"
|
||||
)
|
||||
|
||||
###############################################################################
|
||||
# Disable enum serialization.
|
||||
###############################################################################
|
||||
|
||||
add_custom_target(ci_test_disableenumserialization
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
||||
-DJSON_BuildTests=ON -DJSON_FastTests=ON -DJSON_DisableEnumSerialization=ON
|
||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_disableenumserialization
|
||||
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_disableenumserialization
|
||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_disableenumserialization && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
|
||||
COMMENT "Compile and test with enum serialization disabled"
|
||||
)
|
||||
|
||||
###############################################################################
|
||||
# Skip the multiple-inclusion library version check.
|
||||
###############################################################################
|
||||
|
||||
# tests/src/skip_library_version_check.cpp deliberately simulates a scenario
|
||||
# (mixing two differently-versioned inclusions of the library in one
|
||||
# translation unit) that unavoidably triggers the compiler's own "macro
|
||||
# redefined" warning, so -- unlike the ci_test_* targets above -- it is
|
||||
# compiled directly here, with a modest warning set, instead of being folded
|
||||
# into the library's own -Weverything/-Werror unit test matrix.
|
||||
add_custom_target(ci_test_skiplibraryversioncheck
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/skip_library_version_check
|
||||
COMMAND ${CMAKE_CXX_COMPILER} -std=c++11 -Wall -Wextra
|
||||
-I${PROJECT_SOURCE_DIR}/include
|
||||
${PROJECT_SOURCE_DIR}/tests/src/skip_library_version_check.cpp
|
||||
-o ${PROJECT_BINARY_DIR}/skip_library_version_check/skip_library_version_check
|
||||
COMMAND ${PROJECT_BINARY_DIR}/skip_library_version_check/skip_library_version_check
|
||||
COMMENT "Compile and run a translation unit simulating a mismatched library version, with JSON_SKIP_LIBRARY_VERSION_CHECK defined"
|
||||
)
|
||||
|
||||
###############################################################################
|
||||
# Disable thread-local storage.
|
||||
###############################################################################
|
||||
|
||||
# Without thread-local storage, copying and comparing cannot bound their
|
||||
# descent and handle every object and array without the call stack. Those paths
|
||||
# are otherwise only reached by values nested deeper than the bound, so this
|
||||
# target is what runs the whole test suite through them.
|
||||
add_custom_target(ci_test_no_thread_local
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
||||
-DJSON_BuildTests=ON
|
||||
-DCMAKE_CXX_FLAGS=-DJSON_NO_THREAD_LOCAL
|
||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_no_thread_local
|
||||
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_no_thread_local
|
||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_no_thread_local && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
|
||||
COMMENT "Compile and test without thread-local storage"
|
||||
)
|
||||
|
||||
###############################################################################
|
||||
# Coverage.
|
||||
###############################################################################
|
||||
@@ -487,7 +542,7 @@ add_custom_target(ci_infer
|
||||
|
||||
add_custom_target(ci_offline_testdata
|
||||
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.1.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.2.0 https://github.com/nlohmann/json_test_data.git --quiet --depth 1
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
||||
-DJSON_BuildTests=ON -DJSON_FastTests=ON -DJSON_TestDataDirectory=${PROJECT_BINARY_DIR}/build_offline_testdata/test_data/json_test_data
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
set(JSON_TEST_DATA_URL https://github.com/nlohmann/json_test_data)
|
||||
set(JSON_TEST_DATA_VERSION 3.1.0)
|
||||
set(JSON_TEST_DATA_VERSION 3.2.0)
|
||||
|
||||
include(ExternalProject)
|
||||
|
||||
|
||||
@@ -1,24 +1,57 @@
|
||||
# generate Bazel BUILD file
|
||||
#
|
||||
# usage: cmake -P cmake/scripts/gen_bazel_build_file.cmake (or: make BUILD.bazel)
|
||||
#
|
||||
# The header list of the "json" target is derived from the files in include/. Everything else is fixed text below,
|
||||
# so edit this script rather than BUILD.bazel.
|
||||
|
||||
set(PROJECT_ROOT "${CMAKE_CURRENT_LIST_DIR}/../..")
|
||||
get_filename_component(PROJECT_ROOT "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE)
|
||||
set(BUILD_FILE "${PROJECT_ROOT}/BUILD.bazel")
|
||||
|
||||
file(GLOB_RECURSE HEADERS LIST_DIRECTORIES false RELATIVE "${PROJECT_ROOT}" "include/*.hpp")
|
||||
file(GLOB_RECURSE HEADERS LIST_DIRECTORIES false RELATIVE "${PROJECT_ROOT}" "${PROJECT_ROOT}/include/*.hpp")
|
||||
list(SORT HEADERS)
|
||||
|
||||
set(CONTENT [=[
|
||||
load("@rules_cc//cc:cc_library.bzl", "cc_library")
|
||||
load("@rules_license//rules:license.bzl", "license")
|
||||
|
||||
package(
|
||||
default_applicable_licenses = [":license"],
|
||||
)
|
||||
|
||||
exports_files([
|
||||
"LICENSE.MIT",
|
||||
])
|
||||
|
||||
license(
|
||||
name = "license",
|
||||
license_kinds = ["@rules_license//licenses/spdx:MIT"],
|
||||
license_text = "LICENSE.MIT",
|
||||
)
|
||||
|
||||
file(WRITE "${BUILD_FILE}" [=[
|
||||
cc_library(
|
||||
name = "json",
|
||||
hdrs = [
|
||||
]=])
|
||||
|
||||
foreach(header ${HEADERS})
|
||||
file(APPEND "${BUILD_FILE}" " \"${header}\",\n")
|
||||
string(APPEND CONTENT " \"${header}\",\n")
|
||||
endforeach()
|
||||
|
||||
file(APPEND "${BUILD_FILE}" [=[
|
||||
string(APPEND CONTENT [=[
|
||||
],
|
||||
includes = ["include"],
|
||||
visibility = ["//visibility:public"],
|
||||
alwayslink = True,
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "singleheader-json",
|
||||
hdrs = [
|
||||
"single_include/nlohmann/json.hpp",
|
||||
],
|
||||
includes = ["single_include"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
]=])
|
||||
|
||||
file(WRITE "${BUILD_FILE}" "${CONTENT}")
|
||||
|
||||
@@ -48,6 +48,7 @@ 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_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_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::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');
|
||||
@@ -121,6 +122,7 @@ 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_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_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_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');
|
||||
@@ -171,6 +173,7 @@ 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: 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: 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 Values', 'Guide', 'features/binary_values/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('Comments', 'Guide', 'features/comments/index.html');
|
||||
|
||||
@@ -90,6 +90,10 @@ Linear in the length of the input. The parser is a predictive LL(1) parser.
|
||||
|
||||
A UTF-8 byte order mark is silently ignored.
|
||||
|
||||
By default, a `'\0'` (NUL) byte anywhere in the input is treated as end of input, rather than as an ordinary (and,
|
||||
outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-bytes-in-the-input) for details and the
|
||||
[`JSON_STRICT_NUL_HANDLING`](../macros/json_strict_nul_handling.md) macro to opt into rejecting it instead.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
@@ -111,6 +115,8 @@ A UTF-8 byte order mark is silently ignored.
|
||||
- [parse](parse.md) - deserialize from a compatible input
|
||||
- [sax_parse](sax_parse.md) - parse input using the SAX interface
|
||||
- [operator>>](../operator_gtgt.md) - deserialize from stream
|
||||
- [`JSON_STRICT_NUL_HANDLING`](../macros/json_strict_nul_handling.md) - opt in to rejecting a NUL byte in the input
|
||||
instead of treating it as end of input
|
||||
|
||||
## Version history
|
||||
|
||||
@@ -120,6 +126,8 @@ A UTF-8 byte order mark is silently ignored.
|
||||
- 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 overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||
- `JSON_STRICT_NUL_HANDLING` added in version 3.13.0 to optionally reject a NUL byte in the input instead of treating
|
||||
it as end of input; planned to become the default in version 4.0.0.
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
|
||||
@@ -104,6 +104,7 @@ Linear in the size of the input.
|
||||
- [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_bon8](from_bon8.md) create a JSON value from an input in BON8 format
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
# <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,6 +104,7 @@ Linear in the size of the input.
|
||||
- [from_msgpack](from_msgpack.md) for the related MessagePack format
|
||||
- [from_ubjson](from_ubjson.md) for the related UBJSON format
|
||||
- [from_bjdata](from_bjdata.md) for the related BJData format
|
||||
- [from_bon8](from_bon8.md) for the related BON8 format
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -110,6 +110,7 @@ Linear in the size of the input.
|
||||
- [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
|
||||
- [from_bon8](from_bon8.md) create a JSON value from an input in BON8 format
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -103,6 +103,7 @@ Linear in the size of the input.
|
||||
- [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
|
||||
- [from_bon8](from_bon8.md) create a JSON value from an input in BON8 format
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -104,6 +104,7 @@ Linear in the size of the input.
|
||||
- [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_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
|
||||
|
||||
|
||||
@@ -21,6 +21,10 @@ This overload is chosen if:
|
||||
- `ValueType` is not `basic_json`,
|
||||
- `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
|
||||
|
||||
`ValueType`
|
||||
@@ -67,3 +71,4 @@ Depends on the `json_serializer<ValueType>::from_json()` implementation.
|
||||
## Version history
|
||||
|
||||
- Since version 3.3.0.
|
||||
- Added a `static_assert` with a clear message for `const` arguments in version 3.13.0.
|
||||
|
||||
@@ -290,11 +290,13 @@ Access to the JSON value
|
||||
### Binary formats
|
||||
|
||||
- [**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_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_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_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_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
|
||||
|
||||
@@ -7,7 +7,8 @@ enum class input_format_t {
|
||||
msgpack,
|
||||
ubjson,
|
||||
bson,
|
||||
bjdata
|
||||
bjdata,
|
||||
bon8
|
||||
};
|
||||
```
|
||||
|
||||
@@ -31,6 +32,9 @@ bson
|
||||
bjdata
|
||||
: BJData (Binary JData)
|
||||
|
||||
bon8
|
||||
: BON8 (Binary Object Notation 8)
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
@@ -88,6 +88,8 @@ Strong exception safety: if an exception occurs, the original value stays intact
|
||||
do not belong to the same JSON value; example: `"iterators do not fit"`
|
||||
- Throws [`invalid_iterator.211`](../../home/exceptions.md#jsonexceptioninvalid_iterator211) if `first` or `last`
|
||||
are iterators into container for which insert is called; example: `"passed iterators may not belong to container"`
|
||||
- Throws [`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) if `first` or `last`
|
||||
do not point to an array; example: `"iterators first and last must point to arrays"`
|
||||
4. The function can throw the following exceptions:
|
||||
- Throws [`type_error.309`](../../home/exceptions.md#jsonexceptiontype_error309) if called on JSON values other than
|
||||
arrays; example: `"cannot use insert() with string"`
|
||||
|
||||
@@ -103,6 +103,10 @@ A UTF-8 byte order mark is silently ignored.
|
||||
Invalid Unicode escapes and unpaired surrogates in the input are reported as
|
||||
[`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101) with a detailed message.
|
||||
|
||||
By default, a `'\0'` (NUL) byte anywhere in the input is treated as end of input, rather than as an ordinary (and,
|
||||
outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-bytes-in-the-input) for details and the
|
||||
[`JSON_STRICT_NUL_HANDLING`](../macros/json_strict_nul_handling.md) macro to opt into rejecting it instead.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example "Parsing from a character array"
|
||||
@@ -236,6 +240,8 @@ Invalid Unicode escapes and unpaired surrogates in the input are reported as
|
||||
- [accept](accept.md) - check if the input is valid JSON
|
||||
- [sax_parse](sax_parse.md) - parse input using the SAX interface
|
||||
- [operator>>](../operator_gtgt.md) - deserialize from stream
|
||||
- [`JSON_STRICT_NUL_HANDLING`](../macros/json_strict_nul_handling.md) - opt in to rejecting a NUL byte in the input
|
||||
instead of treating it as end of input
|
||||
|
||||
## Version history
|
||||
|
||||
@@ -246,6 +252,8 @@ Invalid Unicode escapes and unpaired surrogates in the input are reported as
|
||||
- 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 overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||
- `JSON_STRICT_NUL_HANDLING` added in version 3.13.0 to optionally reject a NUL byte in the input instead of treating
|
||||
it as end of input; planned to become the default in version 4.0.0.
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
|
||||
@@ -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
|
||||
JSON text, BSON, CBOR, MessagePack, UBJSON, as well as when using JSON Patch.
|
||||
JSON text, BJData, BON8, 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).
|
||||
|
||||
|
||||
@@ -65,11 +65,14 @@ The SAX event lister must follow the interface of [`json_sax`](../json_sax/index
|
||||
: SAX event listener (must not be null)
|
||||
|
||||
`format` (in)
|
||||
: the format to parse (JSON, CBOR, MessagePack, or UBJSON) (optional, `input_format_t::json` by default), see
|
||||
: the format to parse (JSON, BJData, BON8, BSON, CBOR, MessagePack, or UBJSON) (optional, `input_format_t::json` by
|
||||
default), see
|
||||
[`input_format_t`](input_format_t.md) for more information
|
||||
|
||||
`strict` (in)
|
||||
: whether the input has to be consumed completely (optional, `#!cpp true` by default)
|
||||
: whether the input has to be consumed completely (optional, `#!cpp true` by default); when `#!cpp false` and the
|
||||
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)
|
||||
: whether comments should be ignored and treated like whitespace (`#!cpp true`) or yield a parse error
|
||||
@@ -136,6 +139,8 @@ A UTF-8 byte order mark is silently ignored.
|
||||
- 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 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"
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ Linear in the size of the JSON value `j`.
|
||||
- [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_bon8](to_bon8.md) create a BON8 serialization of a JSON value
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# <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.
|
||||
@@ -73,6 +73,7 @@ values are written.
|
||||
- [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_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
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ Linear in the size of the JSON value `j`.
|
||||
- [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
|
||||
- [to_bon8](to_bon8.md) create a BON8 serialization of a JSON value
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ Linear in the size of the JSON value `j`.
|
||||
- [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
|
||||
- [to_bon8](to_bon8.md) create a BON8 serialization of a JSON value
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ Linear in the size of the JSON value `j`.
|
||||
- [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_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
|
||||
|
||||
|
||||
@@ -14,6 +14,13 @@ header. See also the [macro overview page](../../features/macros.md).
|
||||
- [**JSON_DIAGNOSTIC_POSITIONS**](json_diagnostic_positions.md) - access positions of elements
|
||||
- [**JSON_NOEXCEPTION**](json_noexception.md) - switch off exceptions
|
||||
|
||||
## 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
|
||||
treating it as end of input
|
||||
|
||||
## Language support
|
||||
|
||||
- [**JSON_HAS_CPP_11**<br>**JSON_HAS_CPP_14**<br>**JSON_HAS_CPP_17**<br>**JSON_HAS_CPP_20**](json_has_cpp_11.md) - set supported C++ standard
|
||||
@@ -22,6 +29,7 @@ header. See also the [macro overview page](../../features/macros.md).
|
||||
- [**JSON_HAS_STD_FORMAT**](json_has_std_format.md) - control `std::format`/`std::formatter` support
|
||||
- [**JSON_HAS_THREE_WAY_COMPARISON**](json_has_three_way_comparison.md) - control 3-way comparison support
|
||||
- [**JSON_NO_IO**](json_no_io.md) - switch off functions relying on certain C++ I/O headers
|
||||
- [**JSON_NO_THREAD_LOCAL**](json_no_thread_local.md) - switch off the use of `thread_local` storage
|
||||
- [**JSON_SKIP_UNSUPPORTED_COMPILER_CHECK**](json_skip_unsupported_compiler_check.md) - do not warn about unsupported compilers
|
||||
- [**JSON_USE_GLOBAL_UDLS**](json_use_global_udls.md) - place user-defined string literals (UDLs) into the global namespace
|
||||
- [**JSON_USE_SIMDUTF**](json_use_simdutf.md) - use the simdutf library to accelerate UTF-8 validation
|
||||
|
||||
@@ -38,6 +38,28 @@ The default value is `0` (disabled — existing behavior is preserved).
|
||||
|
||||
This macro must be defined **before** including `<nlohmann/json.hpp>`. Defining it after the include has no effect.
|
||||
|
||||
!!! warning "Applies to every single-element list"
|
||||
|
||||
The macro does not only affect a single JSON value in braces. **Any** single-element braced list is treated as its
|
||||
element, so it no longer creates a one-element array:
|
||||
|
||||
```cpp
|
||||
json j1 = {1}; // 1, not [1]
|
||||
json j2 = {"text"}; // "text", not ["text"]
|
||||
json j3 = {{1, 2}}; // [1,2], not [[1,2]]
|
||||
```
|
||||
|
||||
Code that relies on these producing arrays must use `json::array()` instead (see below). Lists with more than one
|
||||
element, and a single `[string, value]` pair such as `{{"key", "value"}}`, which still creates an object, are not
|
||||
affected. The library's own conversions are not affected either: for example, `std::tuple<int>{5}` still becomes
|
||||
`[5]`.
|
||||
|
||||
!!! note "ABI compatibility"
|
||||
|
||||
The value of this macro is encoded in the [namespace](../../features/namespace.md) (tag `_bics`), 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"
|
||||
|
||||
To explicitly create a single-element array without enabling this macro, use `json::array()`:
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
# JSON_NO_THREAD_LOCAL
|
||||
|
||||
```cpp
|
||||
#define JSON_NO_THREAD_LOCAL
|
||||
```
|
||||
|
||||
When defined, the library does not use `#!cpp thread_local` storage. This is relevant for the few environments whose
|
||||
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
|
||||
themselves, and finish whatever is nested deeper than that without the call stack, so that neither can exhaust the stack
|
||||
however deeply the values are nested. Each counts the levels it has descended into in a `#!cpp thread_local` variable, as
|
||||
a counter 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
|
||||
stack right away. Both keep working exactly as they do otherwise - the same values come out, the same comparisons hold,
|
||||
and deeply nested values are handled just as safely - but both are slower, because the containers no longer copy or
|
||||
compare themselves. Copying the benchmark documents takes 9% (`canada.json`) to 34% (`twitter.json`) longer, and
|
||||
comparing two equal ones 10% (`citm_catalog.json`) to 90% (`canada.json`) longer.
|
||||
|
||||
## Default definition
|
||||
|
||||
By default, `#!cpp JSON_NO_THREAD_LOCAL` is not defined.
|
||||
|
||||
```cpp
|
||||
#undef JSON_NO_THREAD_LOCAL
|
||||
```
|
||||
|
||||
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 and comparing fall back to working without the call stack there, as they do whenever the macro is defined.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The code below forces the library not to use `#!cpp thread_local` storage.
|
||||
|
||||
```cpp
|
||||
#define JSON_NO_THREAD_LOCAL 1
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.12.1.
|
||||
@@ -0,0 +1,131 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,126 @@
|
||||
# JSON_STRICT_NUL_HANDLING
|
||||
|
||||
```cpp
|
||||
#define JSON_STRICT_NUL_HANDLING /* value */
|
||||
```
|
||||
|
||||
When defined to `1`, a `'\0'` (NUL) byte in JSON text input is rejected with `parse_error.101`, like any other
|
||||
unexpected byte, instead of being silently treated as end of input.
|
||||
|
||||
The macro only affects the JSON text parser ([`parse`](../basic_json/parse.md), [`accept`](../basic_json/accept.md),
|
||||
[`sax_parse`](../basic_json/sax_parse.md), and [`operator>>`](../operator_gtgt.md)). There are three cases where a NUL
|
||||
byte is still not rejected:
|
||||
|
||||
- The binary formats ([`from_bjdata`](../basic_json/from_bjdata.md), [`from_bon8`](../basic_json/from_bon8.md),
|
||||
[`from_bson`](../basic_json/from_bson.md), [`from_cbor`](../basic_json/from_cbor.md),
|
||||
[`from_msgpack`](../basic_json/from_msgpack.md), [`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
|
||||
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.
|
||||
|
||||
## Default definition
|
||||
|
||||
The default value is `0` (disabled — existing behavior is preserved).
|
||||
|
||||
```cpp
|
||||
#define JSON_STRICT_NUL_HANDLING 0
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
!!! note "Background"
|
||||
|
||||
By default, a `'\0'` byte anywhere in the input is treated the same as the real end of the input, rather than as
|
||||
an ordinary (and, outside of a string, invalid) byte. Everything from that byte onward is silently ignored,
|
||||
without a parse error - including further, otherwise well-formed JSON:
|
||||
|
||||
```cpp
|
||||
json::parse(std::string("123") + '\0'); // == 123, no error
|
||||
json::parse(std::string("123") + '\0' + "true"); // == 123, the "true" is silently ignored too
|
||||
```
|
||||
|
||||
This falls out of the same convention used when no explicit input length is given at all: parsing from a
|
||||
`const char*` already stops at the first NUL byte via `strlen()`, since a bare pointer has no length of its own.
|
||||
The library applies that same NUL-terminated-C-string convention uniformly, rather than only when a length is
|
||||
genuinely unavailable - so a `std::string`, iterator range, or container whose content happens to include a NUL
|
||||
byte is affected the same way a raw `const char*` would be (see the
|
||||
[FAQ entry](../../home/faq.md#nul-bytes-in-the-input) for a fuller explanation).
|
||||
|
||||
This was not fixed unconditionally, because doing so is backwards-incompatible for any caller who happens to
|
||||
depend on the current behavior - even unknowingly, for instance because their input already contains trailing
|
||||
padding they never noticed was being discarded (see [#5530](https://github.com/nlohmann/json/issues/5530)).
|
||||
This macro instead offers an opt-in path to the corrected 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.
|
||||
|
||||
Enabling it also changes how a `char` array (including a string literal, e.g. `json::parse("123")`) is read: such
|
||||
an array normally carries a trailing `'\0'` contributed by the compiler, not by the source text. With this macro
|
||||
enabled, that one trailing byte is trimmed if present so that parsing a string literal keeps working; every other
|
||||
byte in the array - including any `'\0'` that is not the very last element - is read as real data and rejected
|
||||
like any other unexpected byte. Arrays of any other element type (`unsigned char`, `std::uint8_t`, ...), as used
|
||||
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.
|
||||
|
||||
!!! tip "Workaround without the macro"
|
||||
|
||||
To reject a NUL byte without enabling this macro, trim your input yourself before calling `parse()`:
|
||||
|
||||
```cpp
|
||||
s.resize(s.find('\0')); // drop everything from the first NUL onward, if any
|
||||
json::parse(s);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
??? example "Default behavior (macro not defined)"
|
||||
|
||||
Without the macro, a NUL byte silently ends parsing at that point:
|
||||
|
||||
```cpp
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
int main()
|
||||
{
|
||||
json j = json::parse(std::string("123") + '\0' + "true");
|
||||
// j is 123 -- the '\0' and everything after it is silently ignored
|
||||
}
|
||||
```
|
||||
|
||||
??? example "Opt-in strict handling (macro defined to 1)"
|
||||
|
||||
With the macro, a NUL byte is rejected like any other unexpected byte:
|
||||
|
||||
```cpp
|
||||
#define JSON_STRICT_NUL_HANDLING 1
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
int main()
|
||||
{
|
||||
json j = json::parse(std::string("123") + '\0' + "true");
|
||||
// throws parse_error.101 -- the NUL byte is now invalid input,
|
||||
// exactly like any other unexpected trailing byte
|
||||
|
||||
json ok = json::parse("123");
|
||||
// ok is 123 -- parsing from a string literal still works
|
||||
}
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [FAQ: NUL bytes in the input](../../home/faq.md#nul-bytes-in-the-input)
|
||||
- [**parse**](../basic_json/parse.md) - deserialize from a compatible input
|
||||
- [**accept**](../basic_json/accept.md) - check if the input is valid JSON
|
||||
- [**operator>>**](../operator_gtgt.md) - deserialize from stream
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
- Planned to become the default (with the macro removed) in version 4.0.0.
|
||||
@@ -24,6 +24,14 @@ By default, implicit conversions are enabled.
|
||||
You can prepare existing code by already defining `JSON_USE_IMPLICIT_CONVERSIONS` to `0` and replace any implicit
|
||||
conversions with calls to [`get`](../basic_json/get.md).
|
||||
|
||||
!!! tip "Automatic migration"
|
||||
|
||||
The community-maintained clang-tidy check `modernize-nlohmann-json-explicit-conversions` rewrites implicit
|
||||
conversions into explicit calls to [`get`](../basic_json/get.md); for example, `#!cpp int i = j;` becomes
|
||||
`#!cpp int i = j.get<int>();`. The check is not part of clang-tidy itself, and it does not catch every case (for
|
||||
example, constructing a `std::optional` from a JSON value), so review the result. See
|
||||
[discussion #4610](https://github.com/nlohmann/json/discussions/4610) for how to build and use it.
|
||||
|
||||
!!! hint "CMake option"
|
||||
|
||||
Implicit conversions can also be controlled with the CMake option
|
||||
|
||||
@@ -67,11 +67,20 @@ input >> j2; // parses the next value
|
||||
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.
|
||||
|
||||
This is tracked in [#5340](https://github.com/nlohmann/json/issues/5340).
|
||||
Define [`JSON_PRECISE_STREAM_POSITION`](macros/json_precise_stream_position.md) to `1` to leave the terminating character in the stream
|
||||
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)
|
||||
(newline-delimited JSON) input -- see that page for why and for the recommended alternative.
|
||||
|
||||
By default, a `'\0'` (NUL) byte encountered while reading a value is treated as end of input, rather than as an
|
||||
ordinary (and, outside of a string, invalid) byte; see the [FAQ entry](../home/faq.md#nul-bytes-in-the-input) for
|
||||
details and the [`JSON_STRICT_NUL_HANDLING`](macros/json_strict_nul_handling.md) macro to opt into rejecting it
|
||||
instead. Because `operator>>` only parses a single value and does not require the rest of the stream to be consumed,
|
||||
a NUL byte *after* a complete value has no effect on `operator>>` either way; it only matters while a value is still
|
||||
being read.
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
This function replaces function `#!cpp std::istream& operator<<(basic_json& j, std::istream& i)` which has
|
||||
@@ -98,7 +107,14 @@ Note that reading concatenated values does **not** work for [JSON Lines](../feat
|
||||
|
||||
- [accept](basic_json/accept.md) - check if the input is valid JSON
|
||||
- [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
|
||||
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
|
||||
|
||||
- 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
|
||||
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.
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# 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,
|
||||
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 comparison, [`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).
|
||||
@@ -5,4 +5,6 @@
|
||||
- [Contribution Guidelines](contribution_guidelines.md) - guidelines how to contribute to this project
|
||||
- [Governance](governance.md) - the governance model of this project
|
||||
- [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
|
||||
- [Assurance Case](assurance_case.md) - why the library meets its security requirements
|
||||
|
||||
@@ -164,6 +164,9 @@ 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] 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.
|
||||
- [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
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,21 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"compact": true,
|
||||
"format": "BON8",
|
||||
"schema": 0
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
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
|
||||
@@ -116,18 +116,22 @@ The library uses the following mapping from JSON values types to BJData types ac
|
||||
```
|
||||
|
||||
Likewise, when a JSON object in the above form is serialized using
|
||||
[`to_bjdata`](../../api/basic_json/to_bjdata.md), it is automatically converted into a compact BJData ND-array. When
|
||||
the 1-dimensional vector stored in `"_ArraySize_"` contains a single integer or two integers with one being 1, a
|
||||
regular 1-D optimized array is generated instead.
|
||||
[`to_bjdata`](../../api/basic_json/to_bjdata.md), it is automatically converted into a compact BJData ND-array.
|
||||
|
||||
An object is only converted if the annotation actually describes a packed array; otherwise it is serialized as a
|
||||
regular JSON object. This requires all of the following:
|
||||
When parsing, an ND-array whose dimension vector is empty, contains a single integer, contains two integers with the
|
||||
first being 1, or contains a 0 is returned as a regular (possibly empty) array rather than an annotated object.
|
||||
|
||||
An object is only converted if the annotation describes a packed array that is parsed back into the same annotated
|
||||
object; otherwise it is serialized as a regular JSON object, so the annotation is never lost in a round trip. This requires
|
||||
all of the following:
|
||||
|
||||
- `"_ArrayType_"` is one of `uint8`, `int8`, `uint16`, `int16`, `uint32`, `int32`, `uint64`, `int64`, `single`,
|
||||
`double`, `char`, or `byte`,
|
||||
- `"_ArraySize_"` is an array, since the dimensions are written as the ND-array header's length,
|
||||
- every entry of `"_ArraySize_"` is a non-negative integer, and their product is representable as a `std::size_t`,
|
||||
- `"_ArrayData_"` holds exactly that many elements, and
|
||||
- `"_ArraySize_"` has at least two entries and is not a 1×N row vector (first entry 1), since other shapes are
|
||||
parsed back as a regular array,
|
||||
- every entry of `"_ArraySize_"` is a positive integer, and their product is representable as a `std::size_t`,
|
||||
- `"_ArrayData_"` is an array holding exactly that many elements, and
|
||||
- every element of `"_ArrayData_"` is a number of the kind named by `"_ArrayType_"` (a floating-point number for
|
||||
`single` and `double`, an integer otherwise).
|
||||
|
||||
@@ -204,6 +208,16 @@ 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.
|
||||
|
||||
!!! 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
|
||||
|
||||
```cpp
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
# 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"
|
||||
```
|
||||
@@ -4,6 +4,7 @@ Though JSON is a ubiquitous data format, it is not a very compact format suitabl
|
||||
a network. Hence, the library supports
|
||||
|
||||
- [BJData](bjdata.md) (Binary JData),
|
||||
- [BON8](bon8.md) (Binary Object Notation 8),
|
||||
- [BSON](bson.md) (Binary JSON),
|
||||
- [CBOR](cbor.md) (Concise Binary Object Representation),
|
||||
- [MessagePack](messagepack.md), and
|
||||
@@ -18,6 +19,7 @@ to efficiently encode JSON values to byte vectors and to decode such vectors.
|
||||
| Format | Serialization | Deserialization |
|
||||
|-------------|-----------------------------------------------|----------------------------------------------|
|
||||
| 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 |
|
||||
| CBOR | complete | incomplete, but all JSON types are supported |
|
||||
| MessagePack | complete | complete |
|
||||
@@ -28,6 +30,7 @@ to efficiently encode JSON values to byte vectors and to decode such vectors.
|
||||
| Format | Binary values | Binary subtypes |
|
||||
|-------------|---------------|-----------------|
|
||||
| BJData | not supported | not supported |
|
||||
| BON8 | not supported | not supported |
|
||||
| BSON | supported | supported |
|
||||
| CBOR | supported | supported |
|
||||
| MessagePack | supported | supported |
|
||||
@@ -42,6 +45,7 @@ See [binary values](../binary_values.md) for more information.
|
||||
| BJData | 53.2 % | 91.1 % | 78.1 % | 96.6 % |
|
||||
| BJData (size) | 58.6 % | 92.1 % | 86.7 % | 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 % |
|
||||
| CBOR | 50.5 % | 86.3 % | 68.4 % | 88.0 % |
|
||||
| MessagePack | 50.5 % | 86.0 % | 68.5 % | 87.9 % |
|
||||
|
||||
@@ -187,6 +187,41 @@ 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](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),
|
||||
including pretty-printing and handling of non-ASCII and invalid UTF-8.
|
||||
- [Binary formats](binary_formats/index.md) — encode values more compactly as
|
||||
[BJData](binary_formats/bjdata.md), [BSON](binary_formats/bson.md), [CBOR](binary_formats/cbor.md),
|
||||
[MessagePack](binary_formats/messagepack.md), or [UBJSON](binary_formats/ubjson.md).
|
||||
[BJData](binary_formats/bjdata.md), [BON8](binary_formats/bon8.md), [BSON](binary_formats/bson.md),
|
||||
[CBOR](binary_formats/cbor.md), [MessagePack](binary_formats/messagepack.md), or [UBJSON](binary_formats/ubjson.md).
|
||||
- [Binary values](binary_values.md) — store and exchange raw byte sequences.
|
||||
|
||||
## How values are stored and configured
|
||||
|
||||
@@ -91,6 +91,23 @@ security reasons (e.g., Intel Software Guard Extensions (SGX)).
|
||||
|
||||
See [full documentation of `JSON_NO_IO`](../api/macros/json_no_io.md).
|
||||
|
||||
## `JSON_NO_THREAD_LOCAL`
|
||||
|
||||
When defined, the library does not use `#!cpp thread_local` storage. Copying a value and comparing two values then
|
||||
always avoid the call stack rather than descending into a bounded number of levels first, which is slower but yields the
|
||||
same values and the same comparisons.
|
||||
|
||||
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`
|
||||
|
||||
When defined, the library will not create a compiler warning when a different version of the library was already
|
||||
@@ -105,6 +122,19 @@ using the library with compilers that do not fully support C++11 and may only wo
|
||||
|
||||
See [full documentation of `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK`](../api/macros/json_skip_unsupported_compiler_check.md).
|
||||
|
||||
## `JSON_STRICT_NUL_HANDLING`
|
||||
|
||||
When defined to `1`, a `'\0'` (NUL) byte anywhere in the input is rejected with `parse_error.101`, like any other
|
||||
unexpected byte, instead of being silently treated as end of input (see the
|
||||
[FAQ entry](../home/faq.md#nul-bytes-in-the-input) for background). The default value is `0`, which preserves the
|
||||
existing behavior; this is planned to become the default in version 4.0.0.
|
||||
|
||||
The strict handling can also be enabled with the CMake option
|
||||
[`JSON_StrictNulHandling`](../integration/cmake.md#json_strictnulhandling) (`OFF` by default) which sets
|
||||
`JSON_STRICT_NUL_HANDLING` accordingly.
|
||||
|
||||
See [full documentation of `JSON_STRICT_NUL_HANDLING`](../api/macros/json_strict_nul_handling.md).
|
||||
|
||||
## `JSON_THROW_USER(exception)`
|
||||
|
||||
This macro overrides `#!cpp throw` calls inside the library. The argument is the exception to be thrown.
|
||||
|
||||
@@ -15,6 +15,10 @@ The complete default namespace name is derived as follows:
|
||||
- [`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md) defined non-zero appends `_diag`.
|
||||
- [`JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON`](../api/macros/json_use_legacy_discarded_value_comparison.md)
|
||||
defined non-zero appends `_ldvcmp`.
|
||||
- [`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
|
||||
`_bics`.
|
||||
- [`JSON_PRECISE_STREAM_POSITION`](../api/macros/json_precise_stream_position.md) defined non-zero appends `_psp`.
|
||||
- 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)
|
||||
below.
|
||||
|
||||
@@ -41,7 +41,8 @@ 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`.
|
||||
|
||||
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 — see the
|
||||
whitespace, because `operator>>` consumes the character that terminates a number, unless
|
||||
[`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.
|
||||
|
||||
## 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
|
||||
|
||||
Besides JSON text, a value can also be serialized to the more compact [binary formats](binary_formats/index.md)
|
||||
(BJData, BSON, CBOR, MessagePack, UBJSON).
|
||||
(BJData, BON8, BSON, CBOR, MessagePack, UBJSON).
|
||||
|
||||
## See also
|
||||
|
||||
|
||||
@@ -547,7 +547,7 @@ Grisu2 algorithm, which produces the shortest representation that round-trips. O
|
||||
### Required for the binary formats
|
||||
|
||||
`NumberFloatType` must be `#!cpp float` or `#!cpp double`. The writers for
|
||||
[CBOR, MessagePack, UBJSON, BJData, and BSON](../binary_formats/index.md) map a floating-point value onto an IEEE 754
|
||||
[CBOR, MessagePack, UBJSON, BJData, BON8, 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`.
|
||||
|
||||
### Compatible types
|
||||
|
||||
@@ -1,34 +1,125 @@
|
||||
# Architecture
|
||||
|
||||
!!! info
|
||||
|
||||
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.
|
||||
This page gives a high-level overview of the library's architecture. It should help new contributors to get an idea of
|
||||
the used concepts and where to make changes.
|
||||
|
||||
## Overview
|
||||
|
||||
The main structure is class [nlohmann::basic_json](../api/basic_json/index.md).
|
||||
The library is built around a single class template, [`nlohmann::basic_json`](../api/basic_json/index.md). A
|
||||
`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).
|
||||
|
||||
- public API
|
||||
- container interface
|
||||
- iterators
|
||||
```mermaid
|
||||
flowchart LR
|
||||
input[/"input<br>(string, stream,<br>iterator range, file)"/]
|
||||
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)"/]
|
||||
|
||||
## Template specializations
|
||||
input --> ia
|
||||
ia --> lexer --> parser --> sax
|
||||
ia --> breader --> sax
|
||||
sax --> value
|
||||
value --> serializer --> oa
|
||||
value --> bwriter --> oa
|
||||
oa --> output
|
||||
```
|
||||
|
||||
- describe template parameters of `basic_json`
|
||||
- [`json`](../api/json.md)
|
||||
- [`ordered_json`](../api/ordered_json.md) via [`ordered_map`](../api/ordered_map.md)
|
||||
- **JSON text** is read by an [input adapter](#input-adapters), tokenized by the lexer, and turned into SAX events by
|
||||
the parser.
|
||||
- **Binary formats** (BJData, BSON, CBOR, MessagePack, UBJSON) are read by an input adapter and turned into the same SAX
|
||||
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
|
||||
|
||||
Values are stored as a tagged union of [value_t](../api/basic_json/value_t.md) and json_value.
|
||||
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)
|
||||
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
|
||||
/// the type of the current element
|
||||
value_t m_type = value_t::null;
|
||||
struct data
|
||||
{
|
||||
/// the type of the current element
|
||||
value_t m_type = value_t::null;
|
||||
|
||||
/// the value of the current element
|
||||
json_value m_value = {};
|
||||
/// the value of the current element
|
||||
json_value m_value = {};
|
||||
};
|
||||
|
||||
data m_data = {};
|
||||
```
|
||||
|
||||
with
|
||||
@@ -68,42 +159,83 @@ union json_value {
|
||||
};
|
||||
```
|
||||
|
||||
## Parsing inputs (deserialization)
|
||||
Objects, arrays, strings, and binary values are allocated on the heap with `AllocatorType`, and the union only stores a
|
||||
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 is read via **input adapters** that abstract a source with a common interface:
|
||||
## Input adapters
|
||||
|
||||
Input is read via **input adapters** that abstract a source. Every input adapter provides this interface:
|
||||
|
||||
```cpp
|
||||
/// read a single character
|
||||
std::char_traits<char>::int_type get_character() noexcept;
|
||||
/// the type of the characters in the input
|
||||
using char_type = ...;
|
||||
|
||||
/// read multiple characters to a destination buffer and
|
||||
/// returns the number of characters successfully read
|
||||
/// read a single character; returns std::char_traits<char_type>::eof() at the end of the input
|
||||
typename std::char_traits<char_type>::int_type get_character();
|
||||
|
||||
/// read up to count * sizeof(T) bytes into dest and return the number of bytes read
|
||||
/// (used by the binary readers)
|
||||
template<class T>
|
||||
std::size_t get_elements(T* dest, std::size_t count = 1);
|
||||
```
|
||||
|
||||
List examples of input adapters.
|
||||
The lexer detects two optional extensions at compile time. Only `iterator_input_adapter` provides them, and only for
|
||||
random-access input of single-byte characters:
|
||||
|
||||
## SAX Interface
|
||||
- `supports_seek`, `get_consumed_count()`, and `copy_consumed_range()` let the lexer reconstruct already consumed input
|
||||
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.
|
||||
|
||||
TODO
|
||||
The function `input_adapter` picks the right adapter for the argument passed to `parse`, `accept`, `sax_parse`, or the
|
||||
`from_*` functions:
|
||||
|
||||
## Writing outputs (serialization)
|
||||
- `iterator_input_adapter` reads from an iterator range, which also covers strings, containers, and pointers.
|
||||
- `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**:
|
||||
|
||||
```cpp
|
||||
template<typename T>
|
||||
void write_character(CharType c);
|
||||
|
||||
template<typename CharType>
|
||||
void write_characters(const CharType* s, std::size_t length);
|
||||
```
|
||||
|
||||
List examples of output adapters.
|
||||
The `serializer` (used by [`dump`](../api/basic_json/dump.md) and [`operator<<`](../api/operator_ltlt.md)) and the
|
||||
`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
|
||||
|
||||
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
|
||||
template<class T>
|
||||
void to_json(basic_json& j, const T& t);
|
||||
@@ -112,13 +244,23 @@ template<class 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
|
||||
|
||||
- JSON Pointers
|
||||
- Binary formats
|
||||
- Custom base class
|
||||
- Conversion macros
|
||||
- [JSON Pointer](../features/json_pointer.md) (class `json_pointer`) addresses values inside a tree. It is also the
|
||||
basis of [JSON Patch](../features/json_patch.md).
|
||||
- [Binary formats](../features/binary_formats/index.md) are read by `binary_reader` and written by `binary_writer`.
|
||||
- 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.
|
||||
- [Serialization macros](../features/macros.md) generate `to_json` and `from_json` functions for user-defined types.
|
||||
|
||||
## Details namespace
|
||||
|
||||
- C++ feature backports
|
||||
Namespace `nlohmann::detail` contains all implementation details. It is not part of the public API and may change in any
|
||||
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`.
|
||||
|
||||
@@ -970,6 +970,21 @@ A JSON Patch `move` operation's `"from"` location is a proper prefix of its `"pa
|
||||
|
||||
This exception was added in version 3.13.0. Before that, this situation could succeed with a corrupted result: for an array target, removing the "from" element before the "add" step shifted subsequent indices, so "path" silently re-resolved to a different element than intended.
|
||||
|
||||
### json.exception.out_of_range.415
|
||||
|
||||
MessagePack's ext type and BSON's binary subtype are each stored in a single byte. This exception is thrown when serializing a
|
||||
[`byte_container_with_subtype`](../api/byte_container_with_subtype/index.md) whose subtype exceeds 255.
|
||||
|
||||
!!! failure "Example message"
|
||||
|
||||
```
|
||||
[json.exception.out_of_range.415] subtype 70000 is too large for the MessagePack ext type (max 255)
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
This exception was added in version 3.13.0. Before that, subtypes above 255 were silently truncated modulo 256 instead of raising an error.
|
||||
|
||||
## Further exceptions
|
||||
|
||||
This exception is thrown in case of errors that cannot be classified with the
|
||||
|
||||
@@ -90,6 +90,54 @@ The library supports **Unicode input** as follows:
|
||||
In most cases, the parser is right to complain, because the input is not UTF-8 encoded. This is especially true for Microsoft Windows, where Latin-1 or ISO 8859-1 is often the standard encoding.
|
||||
|
||||
|
||||
### NUL bytes in the input
|
||||
|
||||
!!! question "Questions"
|
||||
|
||||
- Why does `json::parse()` silently ignore part of my input?
|
||||
- Why does a `std::string`/buffer with extra data after the JSON text parse without error, while a similar-looking string with extra text does not?
|
||||
|
||||
A `'\0'` (NUL) byte anywhere in the input is treated the same as the real end of the input, rather than as an ordinary (and, outside of a string, invalid) byte. Everything from that byte onward is silently ignored, without a parse error — including further, otherwise well-formed JSON:
|
||||
|
||||
```cpp
|
||||
json::parse(std::string("123") + '\0'); // == 123, no error
|
||||
json::parse(std::string("123") + '\0' + "true"); // == 123, the "true" is silently ignored too
|
||||
```
|
||||
|
||||
This is different from any other unexpected trailing byte, which *does* raise [`parse_error.101`](../home/exceptions.md#jsonexceptionparse_error101):
|
||||
|
||||
```cpp
|
||||
json::parse("123x"); // throws parse_error.101: unexpected additional data
|
||||
```
|
||||
|
||||
This falls out of the same convention used when no explicit input length is given at all: `json::parse(const char*)` already stops at the first NUL byte via `strlen()`, since a bare pointer has no length of its own. The library applies that same NUL-terminated-C-string convention uniformly, rather than only when a length is genuinely unavailable — so a `std::string`, iterator range, or container whose content happens to include a NUL byte is affected the same way a raw `const char*` would be.
|
||||
|
||||
If your input may contain a trailing or embedded NUL that is **not** meant to signal the end of the JSON text — for instance, a fixed-size, zero-padded buffer — trim it yourself before calling `parse()`, since the library will otherwise silently stop there instead of raising an error:
|
||||
|
||||
```cpp
|
||||
s.resize(s.find('\0')); // drop everything from the first NUL onward, if any
|
||||
json::parse(s);
|
||||
```
|
||||
|
||||
**Opt-in strict handling (since version 3.13.0)**
|
||||
|
||||
Manually trimming every input is easy to forget. If you define [`JSON_STRICT_NUL_HANDLING`](../api/macros/json_strict_nul_handling.md) to `1` before including the library, a `'\0'` byte is instead rejected like any other unexpected byte and raises `parse_error.101`, instead of being treated as end of input:
|
||||
|
||||
```cpp
|
||||
#define JSON_STRICT_NUL_HANDLING 1
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
json::parse(std::string("123") + '\0'); // throws parse_error.101 instead of silently returning 123
|
||||
```
|
||||
|
||||
This macro defaults to `0` (disabled, preserving the behavior described above) to avoid breaking existing code that may depend on it, even unknowingly; it is planned to become the default in version 4.0.0. See [its documentation](../api/macros/json_strict_nul_handling.md) for details, including how it also affects `char` arrays such as string literals.
|
||||
|
||||
Note that this is unrelated to an *unescaped* NUL byte occurring **inside** a quoted JSON string, which is a different, already-invalid case and is correctly rejected either way:
|
||||
|
||||
```cpp
|
||||
json::parse(std::string("\"") + '\0' + "\""); // throws parse_error.101: control character U+0000 (NUL) must be escaped to \u0000
|
||||
```
|
||||
|
||||
### Wide string handling
|
||||
|
||||
!!! question
|
||||
|
||||
@@ -198,6 +198,11 @@ Use the non-amalgamated version of the library. This option is `ON` by default.
|
||||
|
||||
Treat the library headers like system headers (i.e., adding `SYSTEM` to the [`target_include_directories`](https://cmake.org/cmake/help/latest/command/target_include_directories.html) call) to check for this library by tools like Clang-Tidy. This option is `OFF` by default.
|
||||
|
||||
### `JSON_StrictNulHandling`
|
||||
|
||||
Reject a `'\0'` (NUL) byte in the input instead of treating it as end of input, by defining the macro
|
||||
[`JSON_STRICT_NUL_HANDLING`](../api/macros/json_strict_nul_handling.md). This option is `OFF` by default.
|
||||
|
||||
### `JSON_Valgrind`
|
||||
|
||||
Execute the test suite with [Valgrind](https://valgrind.org). This option is `OFF` by default. Depends on `JSON_BuildTests`.
|
||||
|
||||
@@ -176,6 +176,12 @@ You can prepare existing code by already defining
|
||||
conversions with calls to [`get`](../api/basic_json/get.md), [`get_to`](../api/basic_json/get_to.md),
|
||||
[`get_ref`](../api/basic_json/get_ref.md), or [`get_ptr`](../api/basic_json/get_ptr.md).
|
||||
|
||||
!!! tip "Automatic migration"
|
||||
|
||||
The community-maintained clang-tidy check `modernize-nlohmann-json-explicit-conversions` rewrites most implicit
|
||||
conversions into calls to [`get`](../api/basic_json/get.md). It is not part of clang-tidy itself; see
|
||||
[discussion #4610](https://github.com/nlohmann/json/discussions/4610) for how to build and use it.
|
||||
|
||||
=== "Deprecated"
|
||||
|
||||
```cpp
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*[ASCII]: American Standard Code for Information Interchange
|
||||
*[BDFL]: Benevolent Dictator for Life
|
||||
*[BJData]: Binary JData
|
||||
*[BON8]: Binary Object Notation 8
|
||||
*[BSON]: Binary JSON
|
||||
*[CBOR]: Concise Binary Object Representation
|
||||
*[CC0]: Creative Commons Zero
|
||||
|
||||
@@ -63,6 +63,7 @@ nav:
|
||||
- Binary Formats:
|
||||
- features/binary_formats/index.md
|
||||
- features/binary_formats/bjdata.md
|
||||
- features/binary_formats/bon8.md
|
||||
- features/binary_formats/bson.md
|
||||
- features/binary_formats/cbor.md
|
||||
- features/binary_formats/messagepack.md
|
||||
@@ -142,6 +143,7 @@ nav:
|
||||
- 'flatten': api/basic_json/flatten.md
|
||||
- 'format_as': api/basic_json/format_as.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_cbor': api/basic_json/from_cbor.md
|
||||
- 'from_msgpack': api/basic_json/from_msgpack.md
|
||||
@@ -213,6 +215,7 @@ nav:
|
||||
- 'swap': api/basic_json/swap.md
|
||||
- 'std::swap<basic_json>': api/basic_json/std_swap.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_cbor': api/basic_json/to_cbor.md
|
||||
- 'to_msgpack': api/basic_json/to_msgpack.md
|
||||
@@ -292,8 +295,11 @@ nav:
|
||||
- 'JSON_HAS_THREE_WAY_COMPARISON': api/macros/json_has_three_way_comparison.md
|
||||
- 'JSON_NOEXCEPTION': api/macros/json_noexception.md
|
||||
- 'JSON_NO_IO': api/macros/json_no_io.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_UNSUPPORTED_COMPILER_CHECK': api/macros/json_skip_unsupported_compiler_check.md
|
||||
- 'JSON_STRICT_NUL_HANDLING': api/macros/json_strict_nul_handling.md
|
||||
- 'JSON_USE_GLOBAL_UDLS': api/macros/json_use_global_udls.md
|
||||
- 'JSON_USE_IMPLICIT_CONVERSIONS': api/macros/json_use_implicit_conversions.md
|
||||
- 'JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON': api/macros/json_use_legacy_discarded_value_comparison.md
|
||||
@@ -315,7 +321,9 @@ nav:
|
||||
- community/contribution_guidelines.md
|
||||
- community/quality_assurance.md
|
||||
- community/governance.md
|
||||
- community/roadmap.md
|
||||
- community/security_policy.md
|
||||
- community/assurance_case.md
|
||||
|
||||
# Extras
|
||||
extra:
|
||||
@@ -407,7 +415,7 @@ plugins:
|
||||
markdown_description: >
|
||||
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/
|
||||
BSON/UBJSON/BJData binary format support, and a SAX-style parser interface.
|
||||
BSON/UBJSON/BJData/BON8 binary format support, and a SAX-style parser interface.
|
||||
sections:
|
||||
Home:
|
||||
- index.md
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
wheel==0.48.0
|
||||
|
||||
mkdocs==1.6.1 # documentation framework
|
||||
mkdocs-git-revision-date-localized-plugin==1.5.4 # plugin "git-revision-date-localized"
|
||||
mkdocs-git-revision-date-localized-plugin==1.6.0 # plugin "git-revision-date-localized"
|
||||
mkdocs-material==9.7.7 # theme for mkdocs
|
||||
mkdocs-material-extensions==1.3.1 # extensions
|
||||
mkdocs-minify-plugin==0.8.0 # plugin "minify"
|
||||
|
||||
@@ -34,6 +34,14 @@
|
||||
#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_BRACE_INIT_COPY_SEMANTICS
|
||||
#define JSON_BRACE_INIT_COPY_SEMANTICS 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_PRECISE_STREAM_POSITION
|
||||
#define JSON_PRECISE_STREAM_POSITION 0
|
||||
#endif
|
||||
|
||||
#if JSON_DIAGNOSTICS
|
||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||
#else
|
||||
@@ -52,20 +60,34 @@
|
||||
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
#endif
|
||||
|
||||
#if JSON_BRACE_INIT_COPY_SEMANTICS
|
||||
#define NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS _bics
|
||||
#else
|
||||
#define NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS
|
||||
#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
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||
#endif
|
||||
|
||||
// Construct the namespace ABI tags component
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) json_abi ## a ## b ## c
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c) \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c)
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e) json_abi ## a ## b ## c ## d ## e
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e) \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e)
|
||||
|
||||
#define NLOHMANN_JSON_ABI_TAGS \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
|
||||
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_PRECISE_STREAM_POSITION)
|
||||
|
||||
// Construct the namespace version component
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||
|
||||
@@ -471,6 +471,30 @@ inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<
|
||||
j = { std::get<Idx>(t)... };
|
||||
}
|
||||
|
||||
#if JSON_BRACE_INIT_COPY_SEMANTICS
|
||||
// JSON_BRACE_INIT_COPY_SEMANTICS makes a one-element braced list copy its
|
||||
// element instead of wrapping it, which would serialize std::tuple<int>{5} as 5
|
||||
// rather than [5]. Build what the default deduction builds instead: an object
|
||||
// if the element is a [string, value] pair, a one-element array otherwise.
|
||||
template<typename BasicJsonType, typename Tuple>
|
||||
inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<0> /*unused*/)
|
||||
{
|
||||
BasicJsonType element(std::get<0>(t));
|
||||
// same test as the initializer-list constructor, including the cast that
|
||||
// keeps a string type constructible from 0 from selecting operator[](key)
|
||||
const bool is_member = element.is_array() && element.size() == 2
|
||||
&& element[static_cast<typename BasicJsonType::size_type>(0)].is_string();
|
||||
if (is_member)
|
||||
{
|
||||
j = BasicJsonType::object({std::move(element)});
|
||||
}
|
||||
else
|
||||
{
|
||||
j = BasicJsonType::array({std::move(element)});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename BasicJsonType, typename Tuple>
|
||||
inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& /*unused*/, index_sequence<> /*unused*/)
|
||||
{
|
||||
|
||||
@@ -11,8 +11,10 @@
|
||||
#include <cstdint> // uint8_t
|
||||
#include <cstddef> // size_t
|
||||
#include <functional> // hash
|
||||
#include <vector> // vector
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
#include <nlohmann/detail/recursion_depth_limit.hpp>
|
||||
#include <nlohmann/detail/value_t.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
@@ -26,6 +28,9 @@ inline std::size_t combine(std::size_t seed, std::size_t h) noexcept
|
||||
return seed;
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
std::size_t hash_iteratively(const BasicJsonType& j);
|
||||
|
||||
/*!
|
||||
@brief hash a JSON value
|
||||
|
||||
@@ -33,12 +38,21 @@ The hash function tries to rely on std::hash where possible. Furthermore, the
|
||||
type of the JSON value is taken into account to have different hash values for
|
||||
null, 0, 0U, and false, etc.
|
||||
|
||||
Hashing an array or an object hashes its elements, which used to call this
|
||||
function again once per nesting level, so a value nested deeply enough
|
||||
exhausted the call stack and terminated the process. The descent is bounded
|
||||
here: once @ref recursion_depth_limit levels have been entered, @ref
|
||||
hash_iteratively hashes what is left without the call stack. A value nested
|
||||
less deeply than that - all but a vanishing minority - is hashed exactly as
|
||||
before, without allocating.
|
||||
|
||||
@tparam BasicJsonType basic_json specialization
|
||||
@param j JSON value to hash
|
||||
@param depth nesting level of @a j, counted from the value passed by the caller
|
||||
@return hash value of j
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
std::size_t hash(const BasicJsonType& j)
|
||||
std::size_t hash(const BasicJsonType& j, const std::size_t depth = 0)
|
||||
{
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
@@ -56,22 +70,32 @@ std::size_t hash(const BasicJsonType& j)
|
||||
|
||||
case BasicJsonType::value_t::object:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(depth >= recursion_depth_limit()))
|
||||
{
|
||||
return hash_iteratively(j);
|
||||
}
|
||||
|
||||
auto seed = combine(type, j.size());
|
||||
for (const auto& element : j.items())
|
||||
{
|
||||
const auto h = std::hash<string_t> {}(element.key());
|
||||
seed = combine(seed, h);
|
||||
seed = combine(seed, hash(element.value()));
|
||||
seed = combine(seed, hash(element.value(), depth + 1));
|
||||
}
|
||||
return seed;
|
||||
}
|
||||
|
||||
case BasicJsonType::value_t::array:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(depth >= recursion_depth_limit()))
|
||||
{
|
||||
return hash_iteratively(j);
|
||||
}
|
||||
|
||||
auto seed = combine(type, j.size());
|
||||
for (const auto& element : j)
|
||||
{
|
||||
seed = combine(seed, hash(element));
|
||||
seed = combine(seed, hash(element, depth + 1));
|
||||
}
|
||||
return seed;
|
||||
}
|
||||
@@ -127,5 +151,77 @@ std::size_t hash(const BasicJsonType& j)
|
||||
}
|
||||
}
|
||||
|
||||
/// an array or object whose elements @ref hash_iteratively is hashing
|
||||
template<typename BasicJsonType>
|
||||
struct hash_frame
|
||||
{
|
||||
hash_frame(const BasicJsonType* value_, std::size_t seed_) noexcept
|
||||
: value(value_), position(value_->cbegin()), seed(seed_)
|
||||
{}
|
||||
|
||||
const BasicJsonType* value;
|
||||
typename BasicJsonType::const_iterator position;
|
||||
std::size_t seed;
|
||||
};
|
||||
|
||||
/*!
|
||||
@brief hash the array or object @a j without the call stack
|
||||
|
||||
Computes the same value as @ref hash, keeping the arrays and objects it has
|
||||
entered on an explicit stack instead of descending into them. Only reached for
|
||||
values nested deeper than @ref recursion_depth_limit.
|
||||
|
||||
@tparam BasicJsonType basic_json specialization
|
||||
@param j array or object to hash
|
||||
@return hash value of j
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
std::size_t hash_iteratively(const BasicJsonType& j)
|
||||
{
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
|
||||
std::vector<hash_frame<BasicJsonType>> stack;
|
||||
stack.emplace_back(&j, combine(static_cast<std::size_t>(j.type()), j.size()));
|
||||
|
||||
while (true)
|
||||
{
|
||||
// a copy, as entering an element below can reallocate the stack; the
|
||||
// frame itself is only changed through stack.back()
|
||||
const hash_frame<BasicJsonType> frame = stack.back();
|
||||
|
||||
if (frame.position == frame.value->cend())
|
||||
{
|
||||
// all elements are hashed: fold this value's hash into its parent's
|
||||
// seed, exactly where the recursive version returns it
|
||||
const std::size_t h = frame.seed;
|
||||
stack.pop_back();
|
||||
if (stack.empty())
|
||||
{
|
||||
return h;
|
||||
}
|
||||
stack.back().seed = combine(stack.back().seed, h);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (frame.value->is_object())
|
||||
{
|
||||
stack.back().seed = combine(stack.back().seed, std::hash<string_t> {}(frame.position.key()));
|
||||
}
|
||||
|
||||
// advance before entering the element, which pushes onto the stack
|
||||
const BasicJsonType& element = *frame.position;
|
||||
++stack.back().position;
|
||||
|
||||
if (element.is_structured())
|
||||
{
|
||||
stack.emplace_back(&element, combine(static_cast<std::size_t>(element.type()), element.size()));
|
||||
}
|
||||
else
|
||||
{
|
||||
stack.back().seed = combine(stack.back().seed, hash(element));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <nlohmann/detail/input/input_adapters.hpp>
|
||||
#include <nlohmann/detail/input/json_sax.hpp>
|
||||
#include <nlohmann/detail/input/lexer.hpp>
|
||||
#include <nlohmann/detail/input/string_scan.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#include <nlohmann/detail/meta/is_sax.hpp>
|
||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||
@@ -83,7 +84,7 @@ JSON_INLINE_VARIABLE constexpr std::size_t max_valueless_container_size = 1 << 2
|
||||
///////////////////
|
||||
|
||||
/*!
|
||||
@brief deserialization of CBOR, MessagePack, and UBJSON values
|
||||
@brief deserialization of BJData, BON8, BSON, CBOR, MessagePack, and UBJSON values
|
||||
*/
|
||||
template<typename BasicJsonType, typename InputAdapterType, typename SAX = json_sax_dom_parser<BasicJsonType, InputAdapterType>>
|
||||
class binary_reader
|
||||
@@ -97,6 +98,11 @@ class binary_reader
|
||||
using char_type = typename InputAdapterType::char_type;
|
||||
using char_int_type = typename char_traits<char_type>::int_type;
|
||||
|
||||
/// whether the input is a contiguous block of bytes that BON8 strings can
|
||||
/// be copied from in bulk; see @ref get_bon8_string_bulk
|
||||
static constexpr bool bon8_bulk_scan =
|
||||
input_adapter_supports_bulk_scan<InputAdapterType>(is_detected<detect_supports_bulk_scan, InputAdapterType> {});
|
||||
|
||||
public:
|
||||
/*!
|
||||
@brief create a binary reader
|
||||
@@ -131,6 +137,7 @@ class binary_reader
|
||||
{
|
||||
sax = sax_;
|
||||
container_stack.clear();
|
||||
bon8_pushback_size = 0;
|
||||
bool result = false;
|
||||
|
||||
switch (format)
|
||||
@@ -152,6 +159,10 @@ class binary_reader
|
||||
result = parse_ubjson_internal();
|
||||
break;
|
||||
|
||||
case input_format_t::bon8:
|
||||
result = parse_bon8_internal();
|
||||
break;
|
||||
|
||||
case input_format_t::json: // LCOV_EXCL_LINE
|
||||
default: // LCOV_EXCL_LINE
|
||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||
@@ -164,6 +175,11 @@ class binary_reader
|
||||
{
|
||||
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
|
||||
{
|
||||
get();
|
||||
@@ -3171,6 +3187,549 @@ 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, bon8_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 //
|
||||
///////////////////////
|
||||
@@ -3448,6 +4007,10 @@ class binary_reader
|
||||
error_msg += "BJData";
|
||||
break;
|
||||
|
||||
case input_format_t::bon8:
|
||||
error_msg += "BON8";
|
||||
break;
|
||||
|
||||
case input_format_t::json: // LCOV_EXCL_LINE
|
||||
default: // LCOV_EXCL_LINE
|
||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||
@@ -3480,6 +4043,11 @@ class binary_reader
|
||||
/// the containers that have been opened and not closed yet; see @ref container_frame
|
||||
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
|
||||
#define JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_ \
|
||||
make_array<char_int_type>('F', 'H', 'N', 'S', 'T', 'Z', '[', '{')
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace detail
|
||||
{
|
||||
|
||||
/// the supported input formats
|
||||
enum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata };
|
||||
enum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata, bon8 };
|
||||
|
||||
////////////////////
|
||||
// input adapters //
|
||||
@@ -101,6 +101,11 @@ class input_stream_adapter
|
||||
// maintain ifstream flags, except eof
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -114,6 +119,58 @@ class input_stream_adapter
|
||||
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
|
||||
: is(rhs.is), sb(rhs.sb)
|
||||
{
|
||||
@@ -124,6 +181,9 @@ class input_stream_adapter
|
||||
// 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 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()
|
||||
{
|
||||
auto res = sb->sbumpc();
|
||||
@@ -134,10 +194,14 @@ class input_stream_adapter
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<class T>
|
||||
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))));
|
||||
if (JSON_HEDLEY_UNLIKELY(res < count * sizeof(T)))
|
||||
{
|
||||
@@ -147,9 +211,27 @@ class input_stream_adapter
|
||||
}
|
||||
|
||||
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
|
||||
std::istream* is = 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
|
||||
|
||||
@@ -762,6 +844,21 @@ contiguous_bytes_input_adapter input_adapter(CharT b)
|
||||
template<typename T, std::size_t N>
|
||||
auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
{
|
||||
#if JSON_STRICT_NUL_HANDLING
|
||||
// A `char` array from string-literal initialization (e.g. json::parse("123"))
|
||||
// carries a trailing '\0' contributed by the compiler, not by the source
|
||||
// text; drop exactly that one byte so it is not mistaken for real trailing
|
||||
// data. Every other element type (unsigned char, std::uint8_t, ...) keeps
|
||||
// the full extent unconditionally, since a trailing zero byte there is
|
||||
// genuine data (e.g. CBOR/MessagePack). This intentionally does not
|
||||
// strlen()-scan the array (as the pointer overload above does for a
|
||||
// null-delimited string): for a `char` array that is not NUL-terminated
|
||||
// within its bounds, that would read past the end of the array.
|
||||
if (std::is_same<typename std::remove_cv<T>::type, char>::value && N > 0 && array[N - 1] == 0)
|
||||
{
|
||||
return input_adapter(array, array + N - 1);
|
||||
}
|
||||
#endif
|
||||
return input_adapter(array, array + N);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm> // min
|
||||
#include <algorithm> // find_if, min
|
||||
#include <cstddef>
|
||||
#include <string> // string
|
||||
#include <type_traits> // enable_if_t
|
||||
#include <utility> // move
|
||||
#include <utility> // move, pair
|
||||
#include <vector> // vector
|
||||
|
||||
#include <nlohmann/detail/exceptions.hpp>
|
||||
@@ -278,7 +278,7 @@ class json_sax_dom_parser
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
|
||||
{
|
||||
JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
|
||||
return parse_error(0, "", out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -327,7 +327,7 @@ class json_sax_dom_parser
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
|
||||
{
|
||||
JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
|
||||
return parse_error(0, "", out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
|
||||
}
|
||||
|
||||
if (len != detail::unknown_size())
|
||||
@@ -611,7 +611,7 @@ class json_sax_dom_callback_parser
|
||||
// check object limit
|
||||
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
|
||||
{
|
||||
JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
|
||||
return parse_error(0, "", out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -631,7 +631,17 @@ class json_sax_dom_callback_parser
|
||||
// add discarded value at the given key and store the reference for later
|
||||
if (keep && ref_stack.back())
|
||||
{
|
||||
object_element = &(ref_stack.back()->m_data.m_value.object->operator[](val) = discarded);
|
||||
auto& obj = *ref_stack.back()->m_data.m_value.object;
|
||||
const auto it = obj.find(val);
|
||||
if (it != obj.end())
|
||||
{
|
||||
// this is a duplicate key (legal in JSON); remember its
|
||||
// current value so it can be restored later if the new
|
||||
// value is rejected by the callback, instead of being
|
||||
// erased together with the discarded placeholder
|
||||
duplicate_key_stash.emplace_back(&(it->second), it->second);
|
||||
}
|
||||
object_element = &(obj[val] = discarded);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -643,13 +653,18 @@ class json_sax_dom_callback_parser
|
||||
{
|
||||
if (!callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))
|
||||
{
|
||||
// discard object
|
||||
*ref_stack.back() = discarded;
|
||||
// discard object, unless this slot holds a duplicate key's
|
||||
// previous value pending restoration, in which case that
|
||||
// value is restored instead of being discarded
|
||||
if (!resolve_duplicate_key_stash(ref_stack.back(), true))
|
||||
{
|
||||
*ref_stack.back() = discarded;
|
||||
|
||||
#if JSON_DIAGNOSTIC_POSITIONS
|
||||
// Set start/end positions for discarded object.
|
||||
handle_diagnostic_positions_for_json_value(*ref_stack.back());
|
||||
// Set start/end positions for discarded object.
|
||||
handle_diagnostic_positions_for_json_value(*ref_stack.back());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -663,6 +678,10 @@ class json_sax_dom_callback_parser
|
||||
#endif
|
||||
|
||||
ref_stack.back()->set_parents();
|
||||
// this object is finally, definitively kept; drop any
|
||||
// pending duplicate-key stash entry for its slot since it
|
||||
// can no longer be restored
|
||||
resolve_duplicate_key_stash(ref_stack.back(), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -711,7 +730,7 @@ class json_sax_dom_callback_parser
|
||||
// check array limit
|
||||
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
|
||||
{
|
||||
JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
|
||||
return parse_error(0, "", out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
|
||||
}
|
||||
|
||||
if (len != detail::unknown_size())
|
||||
@@ -743,16 +762,25 @@ class json_sax_dom_callback_parser
|
||||
#endif
|
||||
|
||||
ref_stack.back()->set_parents();
|
||||
// this array is finally, definitively kept; drop any
|
||||
// pending duplicate-key stash entry for its slot since it
|
||||
// can no longer be restored
|
||||
resolve_duplicate_key_stash(ref_stack.back(), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// discard array
|
||||
*ref_stack.back() = discarded;
|
||||
// discard array, unless this slot holds a duplicate key's
|
||||
// previous value pending restoration, in which case that
|
||||
// value is restored instead of being discarded
|
||||
if (!resolve_duplicate_key_stash(ref_stack.back(), true))
|
||||
{
|
||||
*ref_stack.back() = discarded;
|
||||
|
||||
#if JSON_DIAGNOSTIC_POSITIONS
|
||||
// Set start/end positions for discarded array.
|
||||
handle_diagnostic_positions_for_json_value(*ref_stack.back());
|
||||
// Set start/end positions for discarded array.
|
||||
handle_diagnostic_positions_for_json_value(*ref_stack.back());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -869,6 +897,35 @@ class json_sax_dom_callback_parser
|
||||
}
|
||||
#endif
|
||||
|
||||
/// if there is a pending duplicate-key stash entry for this exact slot,
|
||||
/// remove it from the stash; if restore_value is true, the stashed
|
||||
/// previous value is moved back into the slot first (use this when the
|
||||
/// new value at that slot was rejected); otherwise the stash entry is
|
||||
/// simply dropped (use this when the new value was accepted, so it
|
||||
/// correctly supersedes the old one and no restore should ever happen
|
||||
/// for this slot again)
|
||||
/// @return whether a matching stash entry was found (and processed)
|
||||
bool resolve_duplicate_key_stash(BasicJsonType* slot, bool restore_value)
|
||||
{
|
||||
const auto it = std::find_if(duplicate_key_stash.begin(), duplicate_key_stash.end(),
|
||||
[slot](const std::pair<BasicJsonType*, BasicJsonType>& entry)
|
||||
{
|
||||
return entry.first == slot;
|
||||
});
|
||||
|
||||
if (it == duplicate_key_stash.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (restore_value)
|
||||
{
|
||||
*slot = std::move(it->second);
|
||||
}
|
||||
duplicate_key_stash.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief the key the value now being handled will be stored under
|
||||
|
||||
@@ -887,7 +944,9 @@ class json_sax_dom_callback_parser
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief remove the discarded value the callback rejected from its parent
|
||||
@brief remove the discarded value the callback rejected from its parent,
|
||||
unless it is a duplicate key's slot with a stashed previous value, in
|
||||
which case that previous value is restored instead
|
||||
|
||||
A rejected value can only ever be the one most recently added to @a parent:
|
||||
the last element of an array, or the placeholder key() stored under @a key
|
||||
@@ -902,7 +961,7 @@ class json_sax_dom_callback_parser
|
||||
@param[in,out] parent the container to remove the rejected value from
|
||||
@param[in] key the key the value was stored under; unused for arrays
|
||||
*/
|
||||
static void remove_discarded_value(BasicJsonType& parent, const string_t& key)
|
||||
void remove_discarded_value(BasicJsonType& parent, const string_t& key)
|
||||
{
|
||||
if (parent.is_array())
|
||||
{
|
||||
@@ -918,7 +977,12 @@ class json_sax_dom_callback_parser
|
||||
const auto it = object.find(key);
|
||||
if (it != object.end() && it->second.is_discarded())
|
||||
{
|
||||
object.erase(it);
|
||||
// a duplicate key's slot has a stashed previous value that
|
||||
// must be restored instead of being erased
|
||||
if (!resolve_duplicate_key_stash(&it->second, true))
|
||||
{
|
||||
object.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1020,6 +1084,16 @@ class json_sax_dom_callback_parser
|
||||
|
||||
JSON_ASSERT(object_element);
|
||||
*object_element = std::move(value);
|
||||
if (!skip_callback)
|
||||
{
|
||||
// this scalar value finally, definitively replaces whatever was
|
||||
// at this slot; drop any pending duplicate-key stash entry for
|
||||
// it since it can no longer be restored (a container value at
|
||||
// this slot is resolved later, in end_object()/end_array(),
|
||||
// since skip_callback is true for the placeholder handling that
|
||||
// happens here for those)
|
||||
resolve_duplicate_key_stash(object_element, false);
|
||||
}
|
||||
return {true, object_element};
|
||||
}
|
||||
|
||||
@@ -1039,6 +1113,12 @@ class json_sax_dom_callback_parser
|
||||
std::vector<string_t> container_key_stack {}; // NOLINT(readability-redundant-member-init)
|
||||
/// helper to hold the reference for the next object element
|
||||
BasicJsonType* object_element = nullptr;
|
||||
/// stash of (slot pointer, previous value) for object members that
|
||||
/// already existed when key() was called again for the same key
|
||||
/// (duplicate keys); used to restore the previous value if the new
|
||||
/// value is later rejected by the callback, instead of erasing the
|
||||
/// member entirely
|
||||
std::vector<std::pair<BasicJsonType*, BasicJsonType>> duplicate_key_stash {};
|
||||
/// whether a syntax error occurred
|
||||
bool errored = false;
|
||||
/// callback function
|
||||
|
||||
@@ -127,6 +127,25 @@ constexpr bool input_adapter_supports_seek(std::false_type /*detected*/)
|
||||
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
|
||||
// lexer can scan directly (see iterator_input_adapter::supports_bulk_scan).
|
||||
// Adapters without the flag - file, stream, wide-string, user-defined - fall
|
||||
@@ -167,6 +186,12 @@ class lexer : public lexer_base<BasicJsonType>
|
||||
static constexpr bool lazy_token_string =
|
||||
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
|
||||
/// directly from a contiguous input buffer (SWAR fast path). This requires
|
||||
/// the token to be reconstructible lazily (lazy_token_string), so bypassing
|
||||
@@ -952,7 +977,9 @@ class lexer : public lexer_base<BasicJsonType>
|
||||
case '\n':
|
||||
case '\r':
|
||||
case char_traits<char_type>::eof():
|
||||
#if !JSON_STRICT_NUL_HANDLING
|
||||
case '\0':
|
||||
#endif
|
||||
return true;
|
||||
|
||||
default:
|
||||
@@ -970,8 +997,10 @@ class lexer : public lexer_base<BasicJsonType>
|
||||
{
|
||||
switch (get())
|
||||
{
|
||||
case char_traits<char_type>::eof():
|
||||
#if !JSON_STRICT_NUL_HANDLING
|
||||
case '\0':
|
||||
#endif
|
||||
case char_traits<char_type>::eof():
|
||||
{
|
||||
error_message = "invalid comment; missing closing '*/'";
|
||||
return false;
|
||||
@@ -1894,6 +1923,21 @@ scan_number_done:
|
||||
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
|
||||
void uncapture_char(std::true_type /*lazy*/) const noexcept {}
|
||||
|
||||
@@ -1957,6 +2001,31 @@ scan_number_done:
|
||||
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
|
||||
/// return the offset of the first character of the last read token; unlike
|
||||
/// the token's parsed value, this accounts for escape sequences
|
||||
@@ -2153,9 +2222,12 @@ scan_number_done:
|
||||
case '9':
|
||||
return scan_number_dispatch(std::integral_constant<bool, bulk_scan> {});
|
||||
|
||||
// end of input (the null byte is needed when parsing from
|
||||
// string literals)
|
||||
#if !JSON_STRICT_NUL_HANDLING
|
||||
case '\0':
|
||||
#endif
|
||||
// end of input; by default, a null byte is also treated as end of
|
||||
// input for backwards compatibility (see JSON_STRICT_NUL_HANDLING
|
||||
// to opt into rejecting a null byte in the input instead)
|
||||
case char_traits<char_type>::eof():
|
||||
return token_type::end_of_input;
|
||||
|
||||
|
||||
@@ -100,13 +100,22 @@ class parser
|
||||
json_sax_dom_callback_parser<BasicJsonType, InputAdapterType> sdp(result, callback, allow_exceptions, &m_lexer);
|
||||
sax_parse_internal(&sdp);
|
||||
|
||||
// in strict mode, input must be completely read
|
||||
if (strict && (get_token() != token_type::end_of_input))
|
||||
if (strict)
|
||||
{
|
||||
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));
|
||||
// in strict mode, input must be completely read
|
||||
if (get_token() != token_type::end_of_input)
|
||||
{
|
||||
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
|
||||
{
|
||||
// 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
|
||||
@@ -128,12 +137,20 @@ class parser
|
||||
json_sax_dom_parser<BasicJsonType, InputAdapterType> sdp(result, allow_exceptions, &m_lexer);
|
||||
sax_parse_internal(&sdp);
|
||||
|
||||
// in strict mode, input must be completely read
|
||||
if (strict && (get_token() != token_type::end_of_input))
|
||||
if (strict)
|
||||
{
|
||||
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));
|
||||
// in strict mode, input must be completely read
|
||||
if (get_token() != token_type::end_of_input)
|
||||
{
|
||||
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
|
||||
@@ -166,12 +183,24 @@ class parser
|
||||
(void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
|
||||
const bool result = sax_parse_internal(sax);
|
||||
|
||||
// strict mode: next byte must be EOF
|
||||
if (result && strict && (get_token() != token_type::end_of_input))
|
||||
if (result)
|
||||
{
|
||||
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));
|
||||
if (strict)
|
||||
{
|
||||
// strict mode: next byte must be EOF
|
||||
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;
|
||||
|
||||
@@ -201,6 +201,43 @@ 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 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
|
||||
// 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
|
||||
|
||||
@@ -186,6 +186,15 @@
|
||||
#define JSON_NO_UNIQUE_ADDRESS
|
||||
#endif
|
||||
|
||||
// Clang targeting MinGW does not survive the thread_local storage the copy
|
||||
// constructor uses to bound its descent: every test that copies a value
|
||||
// segfaults with clang 11.0.1 and clang 18.1.8, while the same tests pass with
|
||||
// GCC targeting MinGW and with every other toolchain the library is tested on.
|
||||
// Copying works the same way without the counter, only more slowly.
|
||||
#if !defined(JSON_NO_THREAD_LOCAL) && defined(__clang__) && defined(__MINGW32__)
|
||||
#define JSON_NO_THREAD_LOCAL 1
|
||||
#endif
|
||||
|
||||
// disable documentation warnings on clang
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
@@ -804,6 +813,6 @@ void templated_json_throw(ExceptionType exception)
|
||||
#define JSON_USE_GLOBAL_UDLS 1
|
||||
#endif
|
||||
|
||||
#ifndef JSON_BRACE_INIT_COPY_SEMANTICS
|
||||
#define JSON_BRACE_INIT_COPY_SEMANTICS 0
|
||||
#ifndef JSON_STRICT_NUL_HANDLING
|
||||
#define JSON_STRICT_NUL_HANDLING 0
|
||||
#endif
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#undef JSON_NO_UNIQUE_ADDRESS
|
||||
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
||||
#undef JSON_USE_GLOBAL_UDLS
|
||||
#undef JSON_BRACE_INIT_COPY_SEMANTICS
|
||||
#undef JSON_STRICT_NUL_HANDLING
|
||||
|
||||
#ifndef JSON_TEST_KEEP_MACROS
|
||||
#undef JSON_CATCH
|
||||
@@ -44,6 +44,8 @@
|
||||
#undef JSON_HAS_STD_FORMAT
|
||||
#undef JSON_HAS_STATIC_RTTI
|
||||
#undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
#undef JSON_BRACE_INIT_COPY_SEMANTICS
|
||||
#undef JSON_PRECISE_STREAM_POSITION
|
||||
#endif
|
||||
|
||||
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,7 @@
|
||||
#include <iterator> // back_inserter
|
||||
#include <memory> // shared_ptr, make_shared
|
||||
#include <string> // basic_string
|
||||
#include <utility> // move
|
||||
#include <vector> // vector
|
||||
|
||||
#ifndef JSON_NO_IO
|
||||
@@ -44,22 +45,32 @@ template<typename CharType> struct output_adapter_protocol
|
||||
template<typename CharType>
|
||||
using output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;
|
||||
|
||||
/// output adapter for byte vectors
|
||||
/// @brief non-virtual output sink writing into a std::vector
|
||||
///
|
||||
/// This sink is not part of the virtual output_adapter_protocol hierarchy: it is
|
||||
/// passed to binary_writer by value as a template parameter, so
|
||||
/// write_character()/write_characters() are ordinary (inlinable) calls with no
|
||||
/// vtable lookup and no shared_ptr. It is used for the common
|
||||
/// `to_cbor`/`to_msgpack`/... into a std::vector. output_vector_adapter below
|
||||
/// wraps this same sink to provide the virtual interface.
|
||||
template<typename CharType, typename AllocatorType = std::allocator<CharType>>
|
||||
class output_vector_adapter : public output_adapter_protocol<CharType>
|
||||
class output_vector_sink
|
||||
{
|
||||
public:
|
||||
explicit output_vector_adapter(std::vector<CharType, AllocatorType>& vec) noexcept
|
||||
explicit output_vector_sink(std::vector<CharType, AllocatorType>& vec) noexcept
|
||||
: v(vec)
|
||||
{}
|
||||
|
||||
void write_character(CharType c) override
|
||||
void write_character(CharType c)
|
||||
{
|
||||
v.push_back(c);
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NON_NULL(2)
|
||||
void write_characters(const CharType* s, std::size_t length) override
|
||||
// no JSON_HEDLEY_NON_NULL here: binary_writer legitimately passes a null
|
||||
// pointer with length 0 for empty strings/binary values. Appending an empty
|
||||
// range is a no-op; the type-erased path tolerates this via the (unattributed)
|
||||
// virtual base, and the concrete sink must do the same.
|
||||
void write_characters(const CharType* s, std::size_t length)
|
||||
{
|
||||
v.insert(v.end(), s, s + length);
|
||||
}
|
||||
@@ -68,6 +79,34 @@ class output_vector_adapter : public output_adapter_protocol<CharType>
|
||||
std::vector<CharType, AllocatorType>& v;
|
||||
};
|
||||
|
||||
/// output adapter for byte vectors
|
||||
///
|
||||
/// The appending itself lives in output_vector_sink; this class only adds the
|
||||
/// virtual output_adapter_protocol interface on top of it, so both the
|
||||
/// type-erased and the templated path share one implementation.
|
||||
template<typename CharType, typename AllocatorType = std::allocator<CharType>>
|
||||
class output_vector_adapter : public output_adapter_protocol<CharType>
|
||||
{
|
||||
public:
|
||||
explicit output_vector_adapter(std::vector<CharType, AllocatorType>& vec) noexcept
|
||||
: sink(vec)
|
||||
{}
|
||||
|
||||
void write_character(CharType c) override
|
||||
{
|
||||
sink.write_character(c);
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NON_NULL(2)
|
||||
void write_characters(const CharType* s, std::size_t length) override
|
||||
{
|
||||
sink.write_characters(s, length);
|
||||
}
|
||||
|
||||
private:
|
||||
output_vector_sink<CharType, AllocatorType> sink;
|
||||
};
|
||||
|
||||
#ifndef JSON_NO_IO
|
||||
/// output adapter for output streams
|
||||
template<typename CharType>
|
||||
@@ -118,6 +157,39 @@ class output_string_adapter : public output_adapter_protocol<CharType>
|
||||
StringType& str;
|
||||
};
|
||||
|
||||
/// @brief output sink forwarding to a type-erased output adapter
|
||||
///
|
||||
/// Wraps the polymorphic output_adapter_t so the same binary_writer template can
|
||||
/// also target arbitrary adapters (output streams, strings, user-provided
|
||||
/// adapters) via the `output_adapter`-based overloads. Each write still goes
|
||||
/// through one virtual call, exactly as before; only the concrete sinks above
|
||||
/// avoid it.
|
||||
template<typename CharType>
|
||||
class output_adapter_sink
|
||||
{
|
||||
public:
|
||||
explicit output_adapter_sink(output_adapter_t<CharType> adapter)
|
||||
: oa(std::move(adapter))
|
||||
{
|
||||
JSON_ASSERT(oa);
|
||||
}
|
||||
|
||||
void write_character(CharType c)
|
||||
{
|
||||
oa->write_character(c);
|
||||
}
|
||||
|
||||
// no JSON_HEDLEY_NON_NULL: forwards (null, 0) for empty payloads, exactly as
|
||||
// the type-erased path already did before this sink existed
|
||||
void write_characters(const CharType* s, std::size_t length)
|
||||
{
|
||||
oa->write_characters(s, length);
|
||||
}
|
||||
|
||||
private:
|
||||
output_adapter_t<CharType> oa;
|
||||
};
|
||||
|
||||
template<typename CharType, typename StringType = std::basic_string<CharType>>
|
||||
class output_adapter
|
||||
{
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
||||
#include <nlohmann/detail/output/binary_writer.hpp>
|
||||
#include <nlohmann/detail/output/output_adapters.hpp>
|
||||
#include <nlohmann/detail/recursion_depth_limit.hpp>
|
||||
#include <nlohmann/detail/string_concat.hpp>
|
||||
#include <nlohmann/detail/value_t.hpp>
|
||||
|
||||
@@ -133,7 +134,7 @@ class serializer
|
||||
|
||||
Serializing a container descends into its elements, so a value nested deeply
|
||||
enough used to exhaust the call stack and terminate the process with no
|
||||
exception to catch. The descent is bounded here: once @ref dump_depth_limit
|
||||
exception to catch. The descent is bounded here: once @ref recursion_depth_limit
|
||||
levels have been entered, @ref dump_iteratively writes out what is left
|
||||
without the call stack. A value nested less deeply than that - all but a
|
||||
vanishing minority - is written by exactly the code that always wrote it.
|
||||
@@ -148,7 +149,7 @@ class serializer
|
||||
{
|
||||
case value_t::object:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(depth >= dump_depth_limit()))
|
||||
if (JSON_HEDLEY_UNLIKELY(depth >= recursion_depth_limit()))
|
||||
{
|
||||
dump_iteratively(val, current_indent);
|
||||
return;
|
||||
@@ -223,7 +224,7 @@ class serializer
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(depth >= dump_depth_limit()))
|
||||
if (JSON_HEDLEY_UNLIKELY(depth >= recursion_depth_limit()))
|
||||
{
|
||||
dump_iteratively(val, current_indent);
|
||||
return;
|
||||
@@ -408,19 +409,12 @@ class serializer
|
||||
}
|
||||
|
||||
private:
|
||||
/// the number of levels @ref dump_internal descends into before it hands
|
||||
/// over to @ref dump_iteratively
|
||||
static constexpr std::size_t dump_depth_limit()
|
||||
{
|
||||
return 128;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief write out @a val and everything below it without the call stack
|
||||
|
||||
Emits the same bytes as @ref dump_internal, keeping the containers it has
|
||||
entered on an explicit stack instead of descending into them. Only reached
|
||||
for values nested deeper than @ref dump_depth_limit, which is why it is not
|
||||
for values nested deeper than @ref recursion_depth_limit, which is why it is not
|
||||
written for speed: walking every value this way measured up to 20% slower on
|
||||
object-heavy documents than letting the compiler drive the descent.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | 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 <cstddef> // size_t
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/*!
|
||||
@brief the number of nesting levels an operation recurses into
|
||||
|
||||
Operations that walk a value (serializing, hashing, merging, ...) recurse once
|
||||
per nesting level, which is fastest, but a value nested deeply enough would
|
||||
exhaust the call stack. So they recurse only this many levels deep and finish
|
||||
whatever lies below with an explicit stack. All of them share this limit.
|
||||
|
||||
@sa https://github.com/nlohmann/json/issues/5387
|
||||
*/
|
||||
constexpr std::size_t recursion_depth_limit() noexcept
|
||||
{
|
||||
return 128;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
+1062
-93
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+2996
-328
File diff suppressed because it is too large
Load Diff
@@ -52,6 +52,14 @@
|
||||
#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_BRACE_INIT_COPY_SEMANTICS
|
||||
#define JSON_BRACE_INIT_COPY_SEMANTICS 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_PRECISE_STREAM_POSITION
|
||||
#define JSON_PRECISE_STREAM_POSITION 0
|
||||
#endif
|
||||
|
||||
#if JSON_DIAGNOSTICS
|
||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||
#else
|
||||
@@ -70,20 +78,34 @@
|
||||
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
#endif
|
||||
|
||||
#if JSON_BRACE_INIT_COPY_SEMANTICS
|
||||
#define NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS _bics
|
||||
#else
|
||||
#define NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS
|
||||
#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
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||
#endif
|
||||
|
||||
// Construct the namespace ABI tags component
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) json_abi ## a ## b ## c
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c) \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c)
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e) json_abi ## a ## b ## c ## d ## e
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e) \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e)
|
||||
|
||||
#define NLOHMANN_JSON_ABI_TAGS \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
|
||||
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_PRECISE_STREAM_POSITION)
|
||||
|
||||
// Construct the namespace version component
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||
|
||||
+10
-2
@@ -75,7 +75,15 @@ target_compile_options(test_main PUBLIC
|
||||
# is annotated JSON_HEDLEY_NO_RETURN (it always throws), which
|
||||
# makes MSVC flag the code following its call in binary_reader.hpp
|
||||
# as unreachable for that instantiation, in both Debug and Release
|
||||
$<$<CXX_COMPILER_ID:MSVC>:/W4;/wd4566;/wd4996;/wd4702>
|
||||
# Disable warning C4503: decorated name length exceeded, name was truncated; the deep
|
||||
# copy support added for #5387 pushes the mangled name of
|
||||
# std::allocator_traits<...>::construct for the custom-base-class
|
||||
# test's map type past VS2015's limit. The name is only used for
|
||||
# debug info, so truncation does not affect the build.
|
||||
# Disable warning C5285: cannot declare a specialization for 'std::tuple'; MSVC 19.51
|
||||
# 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
|
||||
$<$<CXX_COMPILER_ID:MSVC>:/bigobj> $<$<BOOL:${MINGW}>:-Wa,-mbig-obj>
|
||||
|
||||
@@ -104,7 +112,7 @@ endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
# avoid stack overflow, see https://github.com/nlohmann/json/issues/2955
|
||||
json_test_set_test_options("test-cbor;test-msgpack;test-ubjson;test-bjdata;test-binary_formats" LINK_OPTIONS /STACK:4000000)
|
||||
json_test_set_test_options("test-bon8;test-cbor;test-msgpack;test-ubjson;test-bjdata;test-binary_formats" LINK_OPTIONS /STACK:4000000)
|
||||
endif()
|
||||
|
||||
# disable exceptions for test-disabled_exceptions
|
||||
|
||||
+4
-1
@@ -10,7 +10,7 @@ CXXFLAGS += -std=c++11
|
||||
CPPFLAGS += -I ../single_include
|
||||
|
||||
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
|
||||
FUZZERS = parse_afl_fuzzer parse_bson_fuzzer parse_cbor_fuzzer parse_msgpack_fuzzer parse_ubjson_fuzzer parse_bjdata_fuzzer parse_bon8_fuzzer
|
||||
fuzzers: $(FUZZERS)
|
||||
|
||||
parse_afl_fuzzer:
|
||||
@@ -30,3 +30,6 @@ parse_ubjson_fuzzer:
|
||||
|
||||
parse_bjdata_fuzzer:
|
||||
$(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 $@
|
||||
|
||||
@@ -14,6 +14,20 @@ add_test(
|
||||
NAME test-abi_config_noversion
|
||||
COMMAND abi_config_noversion ${DOCTEST_TEST_FILTER})
|
||||
|
||||
# test default and no version namespace with all ABI tags enabled, so the
|
||||
# expected tag order is checked regardless of the JSON_* CMake options
|
||||
foreach(test default noversion)
|
||||
add_executable(abi_config_${test}_all_tags ${test}.cpp)
|
||||
target_compile_definitions(abi_config_${test}_all_tags PRIVATE
|
||||
JSON_DIAGNOSTICS=1
|
||||
JSON_DIAGNOSTIC_POSITIONS=1
|
||||
JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1)
|
||||
target_link_libraries(abi_config_${test}_all_tags PRIVATE abi_compat_main)
|
||||
add_test(
|
||||
NAME test-abi_config_${test}_all_tags
|
||||
COMMAND abi_config_${test}_all_tags ${DOCTEST_TEST_FILTER})
|
||||
endforeach()
|
||||
|
||||
# test custom namespace
|
||||
add_executable(abi_config_custom custom.cpp)
|
||||
target_link_libraries(abi_config_custom PRIVATE abi_compat_main)
|
||||
|
||||
@@ -24,12 +24,20 @@ TEST_CASE("default namespace")
|
||||
expected += "_diag";
|
||||
#endif
|
||||
|
||||
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
expected += "_ldvcmp";
|
||||
#endif
|
||||
|
||||
#if JSON_DIAGNOSTIC_POSITIONS
|
||||
expected += "_dp";
|
||||
#endif
|
||||
|
||||
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
expected += "_ldvcmp";
|
||||
#if JSON_BRACE_INIT_COPY_SEMANTICS
|
||||
expected += "_bics";
|
||||
#endif
|
||||
|
||||
#if JSON_PRECISE_STREAM_POSITION
|
||||
expected += "_psp";
|
||||
#endif
|
||||
|
||||
expected += "_v" STRINGIZE(NLOHMANN_JSON_VERSION_MAJOR);
|
||||
|
||||
@@ -25,12 +25,20 @@ TEST_CASE("default namespace without version component")
|
||||
expected += "_diag";
|
||||
#endif
|
||||
|
||||
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
expected += "_ldvcmp";
|
||||
#endif
|
||||
|
||||
#if JSON_DIAGNOSTIC_POSITIONS
|
||||
expected += "_dp";
|
||||
#endif
|
||||
|
||||
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
expected += "_ldvcmp";
|
||||
#if JSON_BRACE_INIT_COPY_SEMANTICS
|
||||
expected += "_bics";
|
||||
#endif
|
||||
|
||||
#if JSON_PRECISE_STREAM_POSITION
|
||||
expected += "_psp";
|
||||
#endif
|
||||
|
||||
expected += "::basic_json";
|
||||
|
||||
@@ -273,7 +273,8 @@ enum class binary_format
|
||||
ubjson_optimized,
|
||||
bjdata,
|
||||
bjdata_optimized,
|
||||
bson
|
||||
bson,
|
||||
bon8
|
||||
};
|
||||
|
||||
static std::vector<std::uint8_t> to_binary(const json& j, const binary_format format)
|
||||
@@ -292,6 +293,8 @@ static std::vector<std::uint8_t> to_binary(const json& j, const binary_format fo
|
||||
return json::to_bjdata(j);
|
||||
case binary_format::bjdata_optimized:
|
||||
return json::to_bjdata(j, true, true);
|
||||
case binary_format::bon8:
|
||||
return json::to_bon8(j);
|
||||
case binary_format::bson:
|
||||
default:
|
||||
return json::to_bson(j);
|
||||
@@ -312,6 +315,8 @@ static json from_binary(const std::vector<std::uint8_t>& bytes, const binary_for
|
||||
case binary_format::bjdata:
|
||||
case binary_format::bjdata_optimized:
|
||||
return json::from_bjdata(bytes);
|
||||
case binary_format::bon8:
|
||||
return json::from_bon8(bytes);
|
||||
case binary_format::bson:
|
||||
default:
|
||||
return json::from_bson(bytes);
|
||||
@@ -332,6 +337,8 @@ static json from_binary(std::FILE* file, const binary_format format)
|
||||
case binary_format::bjdata:
|
||||
case binary_format::bjdata_optimized:
|
||||
return json::from_bjdata(file);
|
||||
case binary_format::bon8:
|
||||
return json::from_bon8(file);
|
||||
case binary_format::bson:
|
||||
default:
|
||||
return json::from_bson(file);
|
||||
@@ -406,6 +413,10 @@ 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_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, 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
|
||||
// (jeopardy and the regression files) cannot be captured here
|
||||
BENCHMARK_CAPTURE(FromBinaryBuffer, bson / canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json", binary_format::bson);
|
||||
@@ -449,6 +460,8 @@ 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 / 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, 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);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@@ -529,18 +542,21 @@ BENCHMARK_CAPTURE(FromBinaryShape, nested / msgpack, make_nested, binary_format:
|
||||
BENCHMARK_CAPTURE(FromBinaryShape, nested / ubjson, make_nested, binary_format::ubjson);
|
||||
BENCHMARK_CAPTURE(FromBinaryShape, nested / bjdata, make_nested, binary_format::bjdata);
|
||||
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 / msgpack, make_containers, binary_format::msgpack);
|
||||
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 / bjdata, make_containers, binary_format::bjdata);
|
||||
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
|
||||
// rather than scalar decoding and is left out here
|
||||
BENCHMARK_CAPTURE(FromBinaryShape, scalars / cbor, make_scalars, binary_format::cbor);
|
||||
BENCHMARK_CAPTURE(FromBinaryShape, scalars / msgpack, make_scalars, binary_format::msgpack);
|
||||
BENCHMARK_CAPTURE(FromBinaryShape, scalars / ubjson, make_scalars, binary_format::ubjson);
|
||||
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
|
||||
|
||||
+26
-3
@@ -1,6 +1,6 @@
|
||||
# Fuzz testing
|
||||
|
||||
Each parser of the library (JSON, BJData, BSON, CBOR, MessagePack, and UBJSON) can be fuzz tested. Currently,
|
||||
Each parser of the library (JSON, BJData, BON8, 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.
|
||||
|
||||
## Corpus creation
|
||||
@@ -10,11 +10,11 @@ directory with some simple input files that cover several features of the parser
|
||||
for mutations.
|
||||
|
||||
```shell
|
||||
TEST_DATA_VERSION=3.1.0
|
||||
TEST_DATA_VERSION=3.2.0
|
||||
wget https://github.com/nlohmann/json_test_data/archive/refs/tags/v$TEST_DATA_VERSION.zip
|
||||
unzip v$TEST_DATA_VERSION.zip
|
||||
rm v$TEST_DATA_VERSION.zip
|
||||
for FORMAT in json bjdata bson cbor msgpack ubjson
|
||||
for FORMAT in json bjdata bon8 bson cbor msgpack ubjson
|
||||
do
|
||||
rm -fr corpus_$FORMAT
|
||||
mkdir corpus_$FORMAT
|
||||
@@ -79,3 +79,26 @@ 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.
|
||||
|
||||
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.
|
||||
|
||||
@@ -21,16 +21,53 @@ array data, it performs the following steps:
|
||||
- j4 = from_bjdata(vec3)
|
||||
- assert(j1 == j4)
|
||||
|
||||
Re-serializing j2/j3/j4 with the same use_size/use_type settings is checked
|
||||
for value-stability rather than byte-exact stability: from_bjdata(to_bjdata(j2))
|
||||
must equal j2 (and likewise for j3, j4). Byte-exact stability does not hold in
|
||||
general, because a BJData value can lose type fidelity across a round trip
|
||||
(e.g. a binary_t value serialized without the optimized "$U#" array header is
|
||||
parsed back as a plain array of numbers, see #5398 and the discussion on
|
||||
PR #5494) - the numeric value is preserved, but the writer's smallest-type
|
||||
selection for the now-plain numbers may legitimately pick a different, but
|
||||
equally valid, single-byte type marker than the dedicated binary-data writer
|
||||
would have. Both encodings are valid BJData and both decode to the same
|
||||
value, so this is not treated as a round-trip failure here.
|
||||
|
||||
"Value-stable" is checked by comparing dump()s rather than with operator==
|
||||
directly: a BJData/UBJSON payload can decode to a non-finite double (NaN or
|
||||
+-Infinity), and IEEE 754 NaN is never equal to itself, so operator== would
|
||||
report two structurally-identical trees as different whenever a NaN is
|
||||
involved -- not a round-trip bug, just NaN's ordinary (non-)reflexivity.
|
||||
dump() serializes any non-finite double the same deterministic way (as JSON
|
||||
`null`, since JSON itself cannot represent NaN/Infinity), so comparing
|
||||
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
|
||||
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;
|
||||
|
||||
// value-stable comparison for the round-trip checks below; see the note
|
||||
// above on why this compares dump()s rather than the json values directly
|
||||
static bool is_value_stable(const json& lhs, const json& rhs)
|
||||
{
|
||||
return lhs.dump() == rhs.dump();
|
||||
}
|
||||
|
||||
// see http://llvm.org/docs/LibFuzzer.html
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||
{
|
||||
@@ -56,10 +93,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||
json const j3 = json::from_bjdata(vec3);
|
||||
json const j4 = json::from_bjdata(vec4);
|
||||
|
||||
// serializations must match
|
||||
assert(json::to_bjdata(j2, false, false) == vec2);
|
||||
assert(json::to_bjdata(j3, true, false) == vec3);
|
||||
assert(json::to_bjdata(j4, true, true) == vec4);
|
||||
// re-serializing must be value-stable (see the notes above on
|
||||
// why byte-exact stability is not guaranteed in general, and
|
||||
// why this compares dump()s rather than the values directly)
|
||||
assert(is_value_stable(json::from_bjdata(json::to_bjdata(j2, false, false)), j2));
|
||||
assert(is_value_stable(json::from_bjdata(json::to_bjdata(j3, true, false)), j3));
|
||||
assert(is_value_stable(json::from_bjdata(json::to_bjdata(j4, true, true)), j4));
|
||||
}
|
||||
catch (const json::parse_error&)
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user