mirror of
https://github.com/nlohmann/json.git
synced 2026-09-25 09:20:32 +00:00
Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bba3fdbb65 | ||
|
|
448a130c54 | ||
|
|
83dcfd6f91 | ||
|
|
4daca40d7b | ||
|
|
7c90ec2323 | ||
|
|
305ca7dadd | ||
|
|
2e91641de2 | ||
|
|
8699de3064 | ||
|
|
5f659c881a | ||
|
|
36c079149a | ||
|
|
770f62dda9 | ||
|
|
bc066c1838 | ||
|
|
918da64657 | ||
|
|
f3768d6868 | ||
|
|
56b3ee566c |
@@ -158,6 +158,15 @@ make amalgamate
|
|||||||
Running `make amalgamate` will also apply automatic formatting to the source files using
|
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.
|
[`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
|
## Recommended documentation
|
||||||
|
|
||||||
- The library’s [README file](https://github.com/nlohmann/json/blob/master/README.md) is an excellent starting point to
|
- The library’s [README file](https://github.com/nlohmann/json/blob/master/README.md) is an excellent starting point to
|
||||||
|
|||||||
@@ -29,6 +29,27 @@ labels:
|
|||||||
files:
|
files:
|
||||||
- ".github/external_ci/.*"
|
- ".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|binary_formats)"
|
||||||
|
- "tests/src/fuzzer-parse_(bson|cbor|msgpack|ubjson|bjdata)"
|
||||||
|
- "docs/mkdocs/docs/features/binary_formats/"
|
||||||
|
- "docs/mkdocs/docs/(api/basic_json|examples)/(to|from)_(bson|cbor|msgpack|ubjson|bjdata)"
|
||||||
|
|
||||||
|
- label: "aspect: binary formats"
|
||||||
|
title: "(?i)(bson|cbor|msgpack|messagepack|ubjson|bjdata|binary format)"
|
||||||
|
|
||||||
|
- label: "python"
|
||||||
|
files:
|
||||||
|
- "\\.py$"
|
||||||
|
- "requirements[^/]*\\.txt$"
|
||||||
|
|
||||||
- label: "S"
|
- label: "S"
|
||||||
size-below: 10
|
size-below: 10
|
||||||
- label: "M"
|
- label: "M"
|
||||||
|
|||||||
@@ -57,13 +57,16 @@ jobs:
|
|||||||
python3 -mvenv venv
|
python3 -mvenv venv
|
||||||
venv/bin/pip3 install -r $MAIN_DIR/tools/astyle/requirements.txt
|
venv/bin/pip3 install -r $MAIN_DIR/tools/astyle/requirements.txt
|
||||||
|
|
||||||
- name: Regenerate amalgamation and formatting
|
- name: Regenerate amalgamation, formatting, and BUILD.bazel
|
||||||
run: |
|
run: |
|
||||||
cd $MAIN_DIR
|
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.json -s .
|
||||||
python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json_fwd.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 \
|
${{ github.workspace }}/venv/bin/astyle --project=tools/astyle/.astylerc --suffix=none --quiet \
|
||||||
$INCLUDE_DIR/json.hpp $INCLUDE_DIR/json_fwd.hpp
|
$INCLUDE_DIR/json.hpp $INCLUDE_DIR/json_fwd.hpp
|
||||||
|
|
||||||
@@ -87,7 +90,7 @@ jobs:
|
|||||||
mkdir -p ${{ github.workspace }}/patch
|
mkdir -p ${{ github.workspace }}/patch
|
||||||
git diff --patch --no-color > ${{ github.workspace }}/patch/amalgamation.patch
|
git diff --patch --no-color > ${{ github.workspace }}/patch/amalgamation.patch
|
||||||
if [ -s ${{ github.workspace }}/patch/amalgamation.patch ]; then
|
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
|
cat ${{ github.workspace }}/patch/amalgamation.patch
|
||||||
echo "has_diff=true" >> "$GITHUB_OUTPUT"
|
echo "has_diff=true" >> "$GITHUB_OUTPUT"
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -95,13 +95,13 @@ jobs:
|
|||||||
issue_number: issue_number,
|
issue_number: issue_number,
|
||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
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.'
|
+ (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:'
|
+ ' Download it, then apply it locally from the repository root with:'
|
||||||
+ '\n\n```shell\ngit apply amalgamation.patch\n```\n\n'
|
+ '\n\n```shell\ngit apply amalgamation.patch\n```\n\n'
|
||||||
+ 'This does not require installing astyle yourself.'
|
+ 'This does not require installing astyle yourself.'
|
||||||
: '')
|
: '')
|
||||||
+ (first ? '\n\n@' + author + ' Please read and follow the [Contribution Guidelines]'
|
+ (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).'
|
||||||
: '')
|
: '')
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ jobs:
|
|||||||
container: ubuntu:focal
|
container: ubuntu:focal
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
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_disableenumserialization, ci_test_skiplibraryversioncheck, ci_test_simdutf, ci_test_strict_nul_handling]
|
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:
|
steps:
|
||||||
- name: Install build-essential
|
- name: Install build-essential
|
||||||
run: apt-get update ; apt-get install -y build-essential unzip wget git libssl-dev
|
run: apt-get update ; apt-get install -y build-essential unzip wget git libssl-dev
|
||||||
|
|||||||
@@ -158,6 +158,10 @@ jobs:
|
|||||||
# to fit: IMAGE_REL_AMD64_SECREL against `.debug_line'" because the
|
# to fit: IMAGE_REL_AMD64_SECREL against `.debug_line'" because the
|
||||||
# MinGW linker cannot relocate the debug sections this test produces.
|
# 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.
|
# 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
|
- name: Run CMake
|
||||||
run: cmake -S . -B build ^
|
run: cmake -S . -B build ^
|
||||||
-DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang++.exe" ^
|
-DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang++.exe" ^
|
||||||
|
|||||||
@@ -30,8 +30,10 @@ cc_library(
|
|||||||
"include/nlohmann/detail/input/input_adapters.hpp",
|
"include/nlohmann/detail/input/input_adapters.hpp",
|
||||||
"include/nlohmann/detail/input/json_sax.hpp",
|
"include/nlohmann/detail/input/json_sax.hpp",
|
||||||
"include/nlohmann/detail/input/lexer.hpp",
|
"include/nlohmann/detail/input/lexer.hpp",
|
||||||
|
"include/nlohmann/detail/input/number_parse.hpp",
|
||||||
"include/nlohmann/detail/input/parser.hpp",
|
"include/nlohmann/detail/input/parser.hpp",
|
||||||
"include/nlohmann/detail/input/position_t.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/internal_iterator.hpp",
|
||||||
"include/nlohmann/detail/iterators/iter_impl.hpp",
|
"include/nlohmann/detail/iterators/iter_impl.hpp",
|
||||||
"include/nlohmann/detail/iterators/iteration_proxy.hpp",
|
"include/nlohmann/detail/iterators/iteration_proxy.hpp",
|
||||||
@@ -49,12 +51,14 @@ cc_library(
|
|||||||
"include/nlohmann/detail/meta/detected.hpp",
|
"include/nlohmann/detail/meta/detected.hpp",
|
||||||
"include/nlohmann/detail/meta/identity_tag.hpp",
|
"include/nlohmann/detail/meta/identity_tag.hpp",
|
||||||
"include/nlohmann/detail/meta/is_sax.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/std_fs.hpp",
|
||||||
"include/nlohmann/detail/meta/type_traits.hpp",
|
"include/nlohmann/detail/meta/type_traits.hpp",
|
||||||
"include/nlohmann/detail/meta/void_t.hpp",
|
"include/nlohmann/detail/meta/void_t.hpp",
|
||||||
"include/nlohmann/detail/output/binary_writer.hpp",
|
"include/nlohmann/detail/output/binary_writer.hpp",
|
||||||
"include/nlohmann/detail/output/output_adapters.hpp",
|
"include/nlohmann/detail/output/output_adapters.hpp",
|
||||||
"include/nlohmann/detail/output/serializer.hpp",
|
"include/nlohmann/detail/output/serializer.hpp",
|
||||||
|
"include/nlohmann/detail/recursion_depth_limit.hpp",
|
||||||
"include/nlohmann/detail/string_concat.hpp",
|
"include/nlohmann/detail/string_concat.hpp",
|
||||||
"include/nlohmann/detail/string_escape.hpp",
|
"include/nlohmann/detail/string_escape.hpp",
|
||||||
"include/nlohmann/detail/string_utils.hpp",
|
"include/nlohmann/detail/string_utils.hpp",
|
||||||
|
|||||||
@@ -250,12 +250,16 @@ Further documentation:
|
|||||||
|
|
||||||
### `BUILD.bazel`
|
### `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
|
```shell
|
||||||
make BUILD.bazel
|
make BUILD.bazel
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The "Check amalgamation" workflow fails if the file is out of date.
|
||||||
|
|
||||||
### `meson.build`
|
### `meson.build`
|
||||||
|
|
||||||
The build definition for the [Meson](https://mesonbuild.com) build system.
|
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
|
# configuration
|
||||||
@@ -30,8 +30,9 @@ AMALGAMATED_FWD_FILE=single_include/nlohmann/json_fwd.hpp
|
|||||||
# main target
|
# main target
|
||||||
all:
|
all:
|
||||||
@echo "amalgamate - amalgamate files single_include/nlohmann/json{,_fwd}.hpp from the include/nlohmann sources"
|
@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 "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 "clean - remove built files"
|
||||||
@echo "doctest - compile example files and check their output"
|
@echo "doctest - compile example files and check their output"
|
||||||
@echo "fuzz_testing - prepare fuzz testing of the JSON parser"
|
@echo "fuzz_testing - prepare fuzz testing of the JSON parser"
|
||||||
@@ -172,8 +173,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)
|
@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_FILE)~ $(AMALGAMATED_FILE)
|
||||||
@mv $(AMALGAMATED_FWD_FILE)~ $(AMALGAMATED_FWD_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
|
cmake -P cmake/scripts/gen_bazel_build_file.cmake
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|||||||
+19
-15
@@ -230,21 +230,6 @@ add_custom_target(ci_test_simdutf
|
|||||||
COMMENT "Compile and test with simdutf UTF-8 validation enabled"
|
COMMENT "Compile and test with simdutf UTF-8 validation enabled"
|
||||||
)
|
)
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Enable brace-init copy semantics.
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
add_custom_target(ci_test_brace_init_copy_semantics
|
|
||||||
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"
|
|
||||||
)
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Enable strict NUL-byte handling.
|
# Enable strict NUL-byte handling.
|
||||||
###############################################################################
|
###############################################################################
|
||||||
@@ -311,6 +296,25 @@ add_custom_target(ci_test_skiplibraryversioncheck
|
|||||||
COMMENT "Compile and run a translation unit simulating a mismatched library version, with JSON_SKIP_LIBRARY_VERSION_CHECK defined"
|
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, the copy constructor cannot bound its descent
|
||||||
|
# and copies every object and array without the call stack. That path is
|
||||||
|
# otherwise only reached by values nested deeper than the bound, so this target
|
||||||
|
# is what runs the whole test suite through it.
|
||||||
|
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.
|
# Coverage.
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|||||||
@@ -1,24 +1,58 @@
|
|||||||
# generate Bazel BUILD file
|
# 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")
|
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(
|
cc_library(
|
||||||
name = "json",
|
name = "json",
|
||||||
hdrs = [
|
hdrs = [
|
||||||
]=])
|
]=])
|
||||||
|
|
||||||
foreach(header ${HEADERS})
|
foreach(header ${HEADERS})
|
||||||
file(APPEND "${BUILD_FILE}" " \"${header}\",\n")
|
string(APPEND CONTENT " \"${header}\",\n")
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
file(APPEND "${BUILD_FILE}" [=[
|
string(APPEND CONTENT [=[
|
||||||
],
|
],
|
||||||
includes = ["include"],
|
includes = ["include"],
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
alwayslink = True,
|
alwayslink = True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "singleheader-json",
|
||||||
|
hdrs = [
|
||||||
|
"single_include/nlohmann/json.hpp",
|
||||||
|
],
|
||||||
|
includes = ["single_include"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
]=])
|
]=])
|
||||||
|
|
||||||
|
file(WRITE "${BUILD_FILE}" "${CONTENT}")
|
||||||
|
|||||||
@@ -46,9 +46,8 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
Proportional to the size of the JSON value `j` multiplied by its maximum nesting
|
Linear in the size of the JSON value `j`. The length prefixes of all nested documents and arrays are computed in one
|
||||||
depth, `O(n × d)`. BSON length prefixes are computed recursively before nested
|
pass before anything is written.
|
||||||
values are written.
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
@@ -77,3 +76,4 @@ values are written.
|
|||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 3.4.0.
|
- Added in version 3.4.0.
|
||||||
|
- Linear in the size of `j`, and no longer limited by the call stack for deeply nested values, since version 3.13.0.
|
||||||
|
|||||||
@@ -27,6 +27,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_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_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_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_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_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
|
- [**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.
|
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"
|
!!! tip "Workaround without the macro"
|
||||||
|
|
||||||
To explicitly create a single-element array without enabling this macro, use `json::array()`:
|
To explicitly create a single-element array without enabling this macro, use `json::array()`:
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
The copy constructor copies the first levels of a value by copying the containers, which copy their elements, and
|
||||||
|
completes whatever is nested deeper than that without the call stack, so that copying a value cannot exhaust the stack
|
||||||
|
however deeply it is nested. It counts the levels it has descended into in a `#!cpp thread_local` variable, as a counter
|
||||||
|
shared between threads would be raced.
|
||||||
|
|
||||||
|
Without that counter, no descent can be bounded safely, so objects and arrays are copied without the call stack right
|
||||||
|
away. Copying keeps working exactly as it does otherwise - the same values come out, and deeply nested values are copied
|
||||||
|
just as safely - but copying is slower, because the containers no longer copy themselves. Copying the benchmark
|
||||||
|
documents takes 9% (`canada.json`) to 34% (`twitter.json`) longer; values built mostly from objects are affected the
|
||||||
|
most.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## 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.
|
||||||
@@ -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
|
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).
|
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"
|
!!! hint "CMake option"
|
||||||
|
|
||||||
Implicit conversions can also be controlled with the CMake option
|
Implicit conversions can also be controlled with the CMake option
|
||||||
|
|||||||
@@ -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
|
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
|
[`to_bjdata`](../../api/basic_json/to_bjdata.md), it is automatically converted into a compact BJData ND-array.
|
||||||
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.
|
|
||||||
|
|
||||||
An object is only converted if the annotation actually describes a packed array; otherwise it is serialized as a
|
When parsing, an ND-array whose dimension vector is empty, contains a single integer, contains two integers with the
|
||||||
regular JSON object. This requires all of the following:
|
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`,
|
- `"_ArrayType_"` is one of `uint8`, `int8`, `uint16`, `int16`, `uint32`, `int32`, `uint64`, `int64`, `single`,
|
||||||
`double`, `char`, or `byte`,
|
`double`, `char`, or `byte`,
|
||||||
- `"_ArraySize_"` is an array, since the dimensions are written as the ND-array header's length,
|
- `"_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`,
|
- `"_ArraySize_"` has at least two entries and is not a 1×N row vector (first entry 1), since other shapes are
|
||||||
- `"_ArrayData_"` holds exactly that many elements, and
|
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
|
- every element of `"_ArrayData_"` is a number of the kind named by `"_ArrayType_"` (a floating-point number for
|
||||||
`single` and `double`, an integer otherwise).
|
`single` and `double`, an integer otherwise).
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,13 @@ security reasons (e.g., Intel Software Guard Extensions (SGX)).
|
|||||||
|
|
||||||
See [full documentation of `JSON_NO_IO`](../api/macros/json_no_io.md).
|
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 then always avoids the call stack
|
||||||
|
rather than descending into a bounded number of levels first, which is slower but yields the same values.
|
||||||
|
|
||||||
|
See [full documentation of `JSON_NO_THREAD_LOCAL`](../api/macros/json_no_thread_local.md).
|
||||||
|
|
||||||
## `JSON_SKIP_LIBRARY_VERSION_CHECK`
|
## `JSON_SKIP_LIBRARY_VERSION_CHECK`
|
||||||
|
|
||||||
When defined, the library will not create a compiler warning when a different version of the library was already
|
When defined, the library will not create a compiler warning when a different version of the library was already
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ The complete default namespace name is derived as follows:
|
|||||||
- [`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md) defined non-zero appends `_diag`.
|
- [`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)
|
- [`JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON`](../api/macros/json_use_legacy_discarded_value_comparison.md)
|
||||||
defined non-zero appends `_ldvcmp`.
|
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`.
|
||||||
- The inline namespace ends with the suffix `_v` followed by the 3 components of the version number separated by
|
- The inline namespace ends with the suffix `_v` followed by the 3 components of the version number separated by
|
||||||
underscores. To omit the version component, see [Disabling the version component](#disabling-the-version-component)
|
underscores. To omit the version component, see [Disabling the version component](#disabling-the-version-component)
|
||||||
below.
|
below.
|
||||||
|
|||||||
@@ -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),
|
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).
|
[`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"
|
=== "Deprecated"
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
|||||||
@@ -292,6 +292,7 @@ nav:
|
|||||||
- 'JSON_HAS_THREE_WAY_COMPARISON': api/macros/json_has_three_way_comparison.md
|
- 'JSON_HAS_THREE_WAY_COMPARISON': api/macros/json_has_three_way_comparison.md
|
||||||
- 'JSON_NOEXCEPTION': api/macros/json_noexception.md
|
- 'JSON_NOEXCEPTION': api/macros/json_noexception.md
|
||||||
- 'JSON_NO_IO': api/macros/json_no_io.md
|
- 'JSON_NO_IO': api/macros/json_no_io.md
|
||||||
|
- 'JSON_NO_THREAD_LOCAL': api/macros/json_no_thread_local.md
|
||||||
- 'JSON_SKIP_LIBRARY_VERSION_CHECK': api/macros/json_skip_library_version_check.md
|
- 'JSON_SKIP_LIBRARY_VERSION_CHECK': api/macros/json_skip_library_version_check.md
|
||||||
- 'JSON_SKIP_UNSUPPORTED_COMPILER_CHECK': api/macros/json_skip_unsupported_compiler_check.md
|
- 'JSON_SKIP_UNSUPPORTED_COMPILER_CHECK': api/macros/json_skip_unsupported_compiler_check.md
|
||||||
- 'JSON_STRICT_NUL_HANDLING': api/macros/json_strict_nul_handling.md
|
- 'JSON_STRICT_NUL_HANDLING': api/macros/json_strict_nul_handling.md
|
||||||
|
|||||||
@@ -34,6 +34,10 @@
|
|||||||
#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
|
#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef JSON_BRACE_INIT_COPY_SEMANTICS
|
||||||
|
#define JSON_BRACE_INIT_COPY_SEMANTICS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#if JSON_DIAGNOSTICS
|
#if JSON_DIAGNOSTICS
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||||
#else
|
#else
|
||||||
@@ -52,20 +56,27 @@
|
|||||||
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
|
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||||
#endif
|
#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
|
||||||
|
|
||||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||||
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Construct the namespace ABI tags component
|
// Construct the namespace ABI tags component
|
||||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) json_abi ## a ## b ## c
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d) json_abi ## a ## b ## c ## d
|
||||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c) \
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d) \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c)
|
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d)
|
||||||
|
|
||||||
#define NLOHMANN_JSON_ABI_TAGS \
|
#define NLOHMANN_JSON_ABI_TAGS \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
|
NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
|
||||||
NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \
|
NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \
|
||||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS)
|
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS, \
|
||||||
|
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS)
|
||||||
|
|
||||||
// Construct the namespace version component
|
// Construct the namespace version component
|
||||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||||
|
|||||||
@@ -471,6 +471,30 @@ inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<
|
|||||||
j = { std::get<Idx>(t)... };
|
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>
|
template<typename BasicJsonType, typename Tuple>
|
||||||
inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& /*unused*/, index_sequence<> /*unused*/)
|
inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& /*unused*/, index_sequence<> /*unused*/)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,8 +11,10 @@
|
|||||||
#include <cstdint> // uint8_t
|
#include <cstdint> // uint8_t
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <functional> // hash
|
#include <functional> // hash
|
||||||
|
#include <vector> // vector
|
||||||
|
|
||||||
#include <nlohmann/detail/abi_macros.hpp>
|
#include <nlohmann/detail/abi_macros.hpp>
|
||||||
|
#include <nlohmann/detail/recursion_depth_limit.hpp>
|
||||||
#include <nlohmann/detail/value_t.hpp>
|
#include <nlohmann/detail/value_t.hpp>
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
@@ -26,6 +28,9 @@ inline std::size_t combine(std::size_t seed, std::size_t h) noexcept
|
|||||||
return seed;
|
return seed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
std::size_t hash_iteratively(const BasicJsonType& j);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief hash a JSON value
|
@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
|
type of the JSON value is taken into account to have different hash values for
|
||||||
null, 0, 0U, and false, etc.
|
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
|
@tparam BasicJsonType basic_json specialization
|
||||||
@param j JSON value to hash
|
@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
|
@return hash value of j
|
||||||
*/
|
*/
|
||||||
template<typename BasicJsonType>
|
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 string_t = typename BasicJsonType::string_t;
|
||||||
using number_integer_t = typename BasicJsonType::number_integer_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:
|
case BasicJsonType::value_t::object:
|
||||||
{
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(depth >= recursion_depth_limit()))
|
||||||
|
{
|
||||||
|
return hash_iteratively(j);
|
||||||
|
}
|
||||||
|
|
||||||
auto seed = combine(type, j.size());
|
auto seed = combine(type, j.size());
|
||||||
for (const auto& element : j.items())
|
for (const auto& element : j.items())
|
||||||
{
|
{
|
||||||
const auto h = std::hash<string_t> {}(element.key());
|
const auto h = std::hash<string_t> {}(element.key());
|
||||||
seed = combine(seed, h);
|
seed = combine(seed, h);
|
||||||
seed = combine(seed, hash(element.value()));
|
seed = combine(seed, hash(element.value(), depth + 1));
|
||||||
}
|
}
|
||||||
return seed;
|
return seed;
|
||||||
}
|
}
|
||||||
|
|
||||||
case BasicJsonType::value_t::array:
|
case BasicJsonType::value_t::array:
|
||||||
{
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(depth >= recursion_depth_limit()))
|
||||||
|
{
|
||||||
|
return hash_iteratively(j);
|
||||||
|
}
|
||||||
|
|
||||||
auto seed = combine(type, j.size());
|
auto seed = combine(type, j.size());
|
||||||
for (const auto& element : j)
|
for (const auto& element : j)
|
||||||
{
|
{
|
||||||
seed = combine(seed, hash(element));
|
seed = combine(seed, hash(element, depth + 1));
|
||||||
}
|
}
|
||||||
return seed;
|
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
|
} // namespace detail
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|||||||
@@ -186,6 +186,15 @@
|
|||||||
#define JSON_NO_UNIQUE_ADDRESS
|
#define JSON_NO_UNIQUE_ADDRESS
|
||||||
#endif
|
#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
|
// disable documentation warnings on clang
|
||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
@@ -804,10 +813,6 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
#define JSON_USE_GLOBAL_UDLS 1
|
#define JSON_USE_GLOBAL_UDLS 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef JSON_BRACE_INIT_COPY_SEMANTICS
|
|
||||||
#define JSON_BRACE_INIT_COPY_SEMANTICS 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef JSON_STRICT_NUL_HANDLING
|
#ifndef JSON_STRICT_NUL_HANDLING
|
||||||
#define JSON_STRICT_NUL_HANDLING 0
|
#define JSON_STRICT_NUL_HANDLING 0
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
#undef JSON_NO_UNIQUE_ADDRESS
|
#undef JSON_NO_UNIQUE_ADDRESS
|
||||||
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
||||||
#undef JSON_USE_GLOBAL_UDLS
|
#undef JSON_USE_GLOBAL_UDLS
|
||||||
#undef JSON_BRACE_INIT_COPY_SEMANTICS
|
|
||||||
#undef JSON_STRICT_NUL_HANDLING
|
#undef JSON_STRICT_NUL_HANDLING
|
||||||
|
|
||||||
#ifndef JSON_TEST_KEEP_MACROS
|
#ifndef JSON_TEST_KEEP_MACROS
|
||||||
@@ -45,6 +44,7 @@
|
|||||||
#undef JSON_HAS_STD_FORMAT
|
#undef JSON_HAS_STD_FORMAT
|
||||||
#undef JSON_HAS_STATIC_RTTI
|
#undef JSON_HAS_STATIC_RTTI
|
||||||
#undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
#undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||||
|
#undef JSON_BRACE_INIT_COPY_SEMANTICS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
|
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ class binary_writer
|
|||||||
{
|
{
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
{
|
{
|
||||||
write_bson_object(*j.m_data.m_value.object);
|
write_bson_document(j);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -881,8 +881,6 @@ class binary_writer
|
|||||||
return ubjson_prefix(v, use_bjdata) == first_prefix;
|
return ubjson_prefix(v, use_bjdata) == first_prefix;
|
||||||
});
|
});
|
||||||
|
|
||||||
std::vector<CharType> bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type
|
|
||||||
|
|
||||||
// an optimized array of a valueless type carries no payload, so a
|
// an optimized array of a valueless type carries no payload, so a
|
||||||
// reader has nothing but the declared count to bound the allocation
|
// reader has nothing but the declared count to bound the allocation
|
||||||
// by and refuses an excessive one. Write the unoptimized form for
|
// by and refuses an excessive one. Write the unoptimized form for
|
||||||
@@ -893,7 +891,7 @@ class binary_writer
|
|||||||
&& j.m_data.m_value.array->size() > detail::max_valueless_container_size;
|
&& j.m_data.m_value.array->size() > detail::max_valueless_container_size;
|
||||||
|
|
||||||
if (same_prefix && !excessive_valueless
|
if (same_prefix && !excessive_valueless
|
||||||
&& !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end()))
|
&& !(use_bjdata && is_bjdata_excluded_type_marker(first_prefix)))
|
||||||
{
|
{
|
||||||
prefix_required = false;
|
prefix_required = false;
|
||||||
oa.write_character(to_char_type('$'));
|
oa.write_character(to_char_type('$'));
|
||||||
@@ -997,9 +995,7 @@ class binary_writer
|
|||||||
return ubjson_prefix(v, use_bjdata) == first_prefix;
|
return ubjson_prefix(v, use_bjdata) == first_prefix;
|
||||||
});
|
});
|
||||||
|
|
||||||
std::vector<CharType> bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type
|
if (same_prefix && !(use_bjdata && is_bjdata_excluded_type_marker(first_prefix)))
|
||||||
|
|
||||||
if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end()))
|
|
||||||
{
|
{
|
||||||
prefix_required = false;
|
prefix_required = false;
|
||||||
oa.write_character(to_char_type('$'));
|
oa.write_character(to_char_type('$'));
|
||||||
@@ -1201,35 +1197,6 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Writes a BSON element with key @a name and object @a value
|
|
||||||
*/
|
|
||||||
void write_bson_object_entry(const string_t& name,
|
|
||||||
const typename BasicJsonType::object_t& value)
|
|
||||||
{
|
|
||||||
write_bson_entry_header(name, 0x03); // object
|
|
||||||
write_bson_object(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@return The size of the BSON-encoded array @a value
|
|
||||||
*/
|
|
||||||
static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value)
|
|
||||||
{
|
|
||||||
std::size_t array_index = 0ul;
|
|
||||||
|
|
||||||
const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), static_cast<std::size_t>(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el)
|
|
||||||
{
|
|
||||||
// the index is built as a std::string, while calc_bson_element_size
|
|
||||||
// takes a string_t; convert explicitly, as the two are only
|
|
||||||
// implicitly convertible for some string types
|
|
||||||
const auto key = std::to_string(array_index++);
|
|
||||||
return result + calc_bson_element_size(string_t(key.data(), key.size()), el);
|
|
||||||
});
|
|
||||||
|
|
||||||
return sizeof(std::int32_t) + embedded_document_size + 1ul;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@return The size of the BSON-encoded binary array @a value
|
@return The size of the BSON-encoded binary array @a value
|
||||||
*/
|
*/
|
||||||
@@ -1238,29 +1205,6 @@ class binary_writer
|
|||||||
return sizeof(std::int32_t) + value.size() + 1ul;
|
return sizeof(std::int32_t) + value.size() + 1ul;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Writes a BSON element with key @a name and array @a value
|
|
||||||
*/
|
|
||||||
void write_bson_array(const string_t& name,
|
|
||||||
const typename BasicJsonType::array_t& value)
|
|
||||||
{
|
|
||||||
write_bson_entry_header(name, 0x04); // array
|
|
||||||
write_number<std::int32_t>(to_bson_length(calc_bson_array_size(value)), true);
|
|
||||||
|
|
||||||
std::size_t array_index = 0ul;
|
|
||||||
|
|
||||||
for (const auto& el : value)
|
|
||||||
{
|
|
||||||
// the index is built as a std::string, while write_bson_element takes
|
|
||||||
// a string_t; convert explicitly, as the two are only implicitly
|
|
||||||
// convertible for some string types
|
|
||||||
const auto key = std::to_string(array_index++);
|
|
||||||
write_bson_element(string_t(key.data(), key.size()), el);
|
|
||||||
}
|
|
||||||
|
|
||||||
oa.write_character(to_char_type(0x00));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Writes a BSON element with key @a name and binary value @a value
|
@brief Writes a BSON element with key @a name and binary value @a value
|
||||||
*/
|
*/
|
||||||
@@ -1282,43 +1226,37 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Calculates the size necessary to serialize the JSON value @a j with its @a name
|
@return The size of the value of the BSON document entry for @a j, which
|
||||||
@return The calculated size for the BSON document entry for @a j with the given @a name.
|
is neither an object nor an array
|
||||||
*/
|
*/
|
||||||
static std::size_t calc_bson_element_size(const string_t& name,
|
static std::size_t calc_bson_value_size(const BasicJsonType& j)
|
||||||
const BasicJsonType& j)
|
|
||||||
{
|
{
|
||||||
const auto header_size = calc_bson_entry_header_size(name, j);
|
|
||||||
switch (j.type())
|
switch (j.type())
|
||||||
{
|
{
|
||||||
case value_t::object:
|
|
||||||
return header_size + calc_bson_object_size(*j.m_data.m_value.object);
|
|
||||||
|
|
||||||
case value_t::array:
|
|
||||||
return header_size + calc_bson_array_size(*j.m_data.m_value.array);
|
|
||||||
|
|
||||||
case value_t::binary:
|
case value_t::binary:
|
||||||
return header_size + calc_bson_binary_size(*j.m_data.m_value.binary);
|
return calc_bson_binary_size(*j.m_data.m_value.binary);
|
||||||
|
|
||||||
case value_t::boolean:
|
case value_t::boolean:
|
||||||
return header_size + 1ul;
|
return 1ul;
|
||||||
|
|
||||||
case value_t::number_float:
|
case value_t::number_float:
|
||||||
return header_size + 8ul;
|
return 8ul;
|
||||||
|
|
||||||
case value_t::number_integer:
|
case value_t::number_integer:
|
||||||
return header_size + calc_bson_integer_size(j.m_data.m_value.number_integer);
|
return calc_bson_integer_size(j.m_data.m_value.number_integer);
|
||||||
|
|
||||||
case value_t::number_unsigned:
|
case value_t::number_unsigned:
|
||||||
return header_size + calc_bson_unsigned_size(j.m_data.m_value.number_unsigned);
|
return calc_bson_unsigned_size(j.m_data.m_value.number_unsigned);
|
||||||
|
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
return header_size + calc_bson_string_size(*j.m_data.m_value.string);
|
return calc_bson_string_size(*j.m_data.m_value.string);
|
||||||
|
|
||||||
case value_t::null:
|
case value_t::null:
|
||||||
return header_size + 0ul;
|
return 0ul;
|
||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
|
case value_t::object:
|
||||||
|
case value_t::array:
|
||||||
case value_t::discarded:
|
case value_t::discarded:
|
||||||
default:
|
default:
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
|
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
|
||||||
@@ -1328,22 +1266,13 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Serializes the JSON value @a j to BSON and associates it with the
|
@brief Writes the BSON document entry with key @a name for @a j, which is
|
||||||
key @a name.
|
neither an object nor an array
|
||||||
@param name The name to associate with the JSON entity @a j within the
|
|
||||||
current BSON document
|
|
||||||
*/
|
*/
|
||||||
void write_bson_element(const string_t& name,
|
void write_bson_value(const string_t& name, const BasicJsonType& j)
|
||||||
const BasicJsonType& j)
|
|
||||||
{
|
{
|
||||||
switch (j.type())
|
switch (j.type())
|
||||||
{
|
{
|
||||||
case value_t::object:
|
|
||||||
return write_bson_object_entry(name, *j.m_data.m_value.object);
|
|
||||||
|
|
||||||
case value_t::array:
|
|
||||||
return write_bson_array(name, *j.m_data.m_value.array);
|
|
||||||
|
|
||||||
case value_t::binary:
|
case value_t::binary:
|
||||||
return write_bson_binary(name, *j.m_data.m_value.binary);
|
return write_bson_binary(name, *j.m_data.m_value.binary);
|
||||||
|
|
||||||
@@ -1366,6 +1295,8 @@ class binary_writer
|
|||||||
return write_bson_null(name);
|
return write_bson_null(name);
|
||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
|
case value_t::object:
|
||||||
|
case value_t::array:
|
||||||
case value_t::discarded:
|
case value_t::discarded:
|
||||||
default:
|
default:
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
|
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
|
||||||
@@ -1374,37 +1305,221 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/// @brief an object or array of the BSON document being sized or written
|
||||||
@brief Calculates the size of the BSON serialization of the given
|
struct bson_frame
|
||||||
JSON-object @a j.
|
|
||||||
@param[in] value JSON value to serialize
|
|
||||||
@pre value.type() == value_t::object
|
|
||||||
*/
|
|
||||||
static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value)
|
|
||||||
{
|
{
|
||||||
const std::size_t document_size = std::accumulate(value.begin(), value.end(), static_cast<std::size_t>(0),
|
explicit bson_frame(const BasicJsonType* value_, const std::size_t size_slot_ = 0)
|
||||||
[](size_t result, const typename BasicJsonType::object_t::value_type & el)
|
: value(value_)
|
||||||
|
, size_slot(size_slot_)
|
||||||
{
|
{
|
||||||
return result += calc_bson_element_size(el.first, el.second);
|
if (value->is_object())
|
||||||
});
|
{
|
||||||
|
member = value->m_data.m_value.object->cbegin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return sizeof(std::int32_t) + document_size + 1ul;
|
/// the object or array
|
||||||
|
const BasicJsonType* value;
|
||||||
|
/// objects: the next member
|
||||||
|
typename BasicJsonType::object_t::const_iterator member{};
|
||||||
|
/// arrays: the index of the next element
|
||||||
|
std::size_t index = 0;
|
||||||
|
/// @ref calc_bson_sizes only: where its size goes in the table
|
||||||
|
std::size_t size_slot;
|
||||||
|
/// @ref calc_bson_sizes only: the size of its entries seen so far
|
||||||
|
std::size_t entries_size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief creates the name BSON gives the array element with index @a index
|
||||||
|
@param[out] name receives the decimal index
|
||||||
|
*/
|
||||||
|
static void create_bson_index_name(const std::size_t index, string_t& name)
|
||||||
|
{
|
||||||
|
// the index is built as a std::string; convert explicitly, as the
|
||||||
|
// two are only implicitly convertible for some string types
|
||||||
|
const auto key = std::to_string(index);
|
||||||
|
name = string_t(key.data(), key.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[in] value JSON value to serialize
|
@brief Calculates the size of every object and array in the BSON document
|
||||||
@pre value.type() == value_t::object
|
@a document, including the document itself.
|
||||||
|
|
||||||
|
BSON prefixes every document and array with its size, so all of them have
|
||||||
|
to be known before the first byte is written. They are computed in a
|
||||||
|
single pass, each one from the sizes of its entries, which keeps
|
||||||
|
serializing linear in the size of the document; computing each size by
|
||||||
|
walking the entire value below it made it quadratic in the nesting depth.
|
||||||
|
The pass keeps the objects and arrays it has entered on an explicit stack,
|
||||||
|
so a deeply nested value cannot exhaust the call stack.
|
||||||
|
|
||||||
|
@param[in] document the JSON object to serialize
|
||||||
|
@param[out] nested_sizes the sizes of the objects and arrays in
|
||||||
|
@a document, in the order they are written
|
||||||
|
@return the size of @a document
|
||||||
|
@throw out_of_range.409 if a key contains U+0000, before anything is
|
||||||
|
written
|
||||||
*/
|
*/
|
||||||
void write_bson_object(const typename BasicJsonType::object_t& value)
|
static std::size_t calc_bson_sizes(const BasicJsonType& document, std::vector<std::size_t>& nested_sizes)
|
||||||
{
|
{
|
||||||
write_number<std::int32_t>(to_bson_length(calc_bson_object_size(value)), true);
|
// the object or array whose entries are being sized, and the ones it
|
||||||
|
// is in; nothing is allocated unless the document nests
|
||||||
|
bson_frame current(&document);
|
||||||
|
std::vector<bson_frame> parents;
|
||||||
|
// string_t need not be default constructible
|
||||||
|
string_t index_name("", 0);
|
||||||
|
|
||||||
for (const auto& el : value)
|
while (true)
|
||||||
{
|
{
|
||||||
write_bson_element(el.first, el.second);
|
// size entries until the current object or array is done, or an
|
||||||
}
|
// entry is an object or array itself
|
||||||
|
const BasicJsonType* nested = nullptr;
|
||||||
|
if (current.value->is_object())
|
||||||
|
{
|
||||||
|
const auto& object = *current.value->m_data.m_value.object;
|
||||||
|
while (nested == nullptr && current.member != object.cend())
|
||||||
|
{
|
||||||
|
const auto& el = *current.member;
|
||||||
|
++current.member;
|
||||||
|
current.entries_size += calc_bson_entry_header_size(el.first, el.second);
|
||||||
|
if (el.second.is_structured())
|
||||||
|
{
|
||||||
|
nested = &el.second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
current.entries_size += calc_bson_value_size(el.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const auto& array = *current.value->m_data.m_value.array;
|
||||||
|
while (nested == nullptr && current.index < array.size())
|
||||||
|
{
|
||||||
|
const BasicJsonType& el = array[current.index];
|
||||||
|
create_bson_index_name(current.index, index_name);
|
||||||
|
current.entries_size += calc_bson_entry_header_size(index_name, el);
|
||||||
|
++current.index;
|
||||||
|
if (el.is_structured())
|
||||||
|
{
|
||||||
|
nested = ⪙
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
current.entries_size += calc_bson_value_size(el);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
oa.write_character(to_char_type(0x00));
|
if (nested != nullptr)
|
||||||
|
{
|
||||||
|
// its size is added to the current one's once it is done
|
||||||
|
nested_sizes.push_back(0);
|
||||||
|
parents.push_back(std::move(current));
|
||||||
|
current = bson_frame(nested, nested_sizes.size() - 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the int32 size, the entries, and the terminating null byte
|
||||||
|
const std::size_t size = sizeof(std::int32_t) + current.entries_size + 1ul;
|
||||||
|
if (parents.empty())
|
||||||
|
{
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
nested_sizes[current.size_slot] = size;
|
||||||
|
current = std::move(parents.back());
|
||||||
|
parents.pop_back();
|
||||||
|
current.entries_size += size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Serializes the JSON object @a document as a BSON document
|
||||||
|
|
||||||
|
Writes the objects and arrays in it without the call stack, keeping the
|
||||||
|
ones it has entered on an explicit stack, so a deeply nested value
|
||||||
|
cannot exhaust the call stack.
|
||||||
|
|
||||||
|
@param[in] document the JSON object to serialize
|
||||||
|
@pre document.type() == value_t::object
|
||||||
|
*/
|
||||||
|
void write_bson_document(const BasicJsonType& document)
|
||||||
|
{
|
||||||
|
std::vector<std::size_t> nested_sizes;
|
||||||
|
const std::size_t document_size = calc_bson_sizes(document, nested_sizes);
|
||||||
|
write_number<std::int32_t>(to_bson_length(document_size), true);
|
||||||
|
|
||||||
|
// the object or array whose entries are being written, and the ones
|
||||||
|
// it is in
|
||||||
|
bson_frame current(&document);
|
||||||
|
std::vector<bson_frame> parents;
|
||||||
|
std::size_t next_size = 0;
|
||||||
|
// string_t need not be default constructible
|
||||||
|
string_t index_name("", 0);
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// write entries until the current object or array is done, or an
|
||||||
|
// entry is an object or array itself
|
||||||
|
const string_t* nested_name = nullptr;
|
||||||
|
const BasicJsonType* nested = nullptr;
|
||||||
|
if (current.value->is_object())
|
||||||
|
{
|
||||||
|
const auto& object = *current.value->m_data.m_value.object;
|
||||||
|
while (nested == nullptr && current.member != object.cend())
|
||||||
|
{
|
||||||
|
const auto& el = *current.member;
|
||||||
|
++current.member;
|
||||||
|
if (el.second.is_structured())
|
||||||
|
{
|
||||||
|
nested_name = &el.first;
|
||||||
|
nested = &el.second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
write_bson_value(el.first, el.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const auto& array = *current.value->m_data.m_value.array;
|
||||||
|
while (nested == nullptr && current.index < array.size())
|
||||||
|
{
|
||||||
|
const BasicJsonType& el = array[current.index];
|
||||||
|
create_bson_index_name(current.index, index_name);
|
||||||
|
++current.index;
|
||||||
|
if (el.is_structured())
|
||||||
|
{
|
||||||
|
nested_name = &index_name;
|
||||||
|
nested = ⪙
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
write_bson_value(index_name, el);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nested != nullptr)
|
||||||
|
{
|
||||||
|
write_bson_entry_header(*nested_name, nested->is_object() ? 0x03 : 0x04);
|
||||||
|
write_number<std::int32_t>(to_bson_length(nested_sizes[next_size++]), true);
|
||||||
|
parents.push_back(std::move(current));
|
||||||
|
current = bson_frame(nested);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa.write_character(to_char_type(0x00));
|
||||||
|
if (parents.empty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
current = std::move(parents.back());
|
||||||
|
parents.pop_back();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////
|
//////////
|
||||||
@@ -1726,6 +1841,21 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief whether BJData forbids @a marker as the type of an optimized array
|
||||||
|
or object
|
||||||
|
|
||||||
|
Containers, strings, high-precision numbers, booleans and null cannot be
|
||||||
|
declared as the single type of an optimized container in BJData; such a
|
||||||
|
container is written unoptimized. The reader rejects them with the same
|
||||||
|
list (binary_reader::bjd_optimized_type_markers).
|
||||||
|
*/
|
||||||
|
static constexpr bool is_bjdata_excluded_type_marker(const CharType marker) noexcept
|
||||||
|
{
|
||||||
|
return marker == '[' || marker == '{' || marker == 'S' || marker == 'H'
|
||||||
|
|| marker == 'T' || marker == 'F' || marker == 'N' || marker == 'Z';
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr CharType get_ubjson_float_prefix(float /*unused*/)
|
static constexpr CharType get_ubjson_float_prefix(float /*unused*/)
|
||||||
{
|
{
|
||||||
return 'd'; // float 32
|
return 'd'; // float 32
|
||||||
@@ -1800,8 +1930,19 @@ class binary_writer
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t len = (value.at(key).empty() ? 0 : 1);
|
// the reader only restores an annotated object from an ND-array header
|
||||||
for (const auto& el : value.at(key))
|
// with at least two dimensions: an empty dimension vector, a single
|
||||||
|
// dimension, or a 1xN row vector is read back as a plain array, which
|
||||||
|
// would silently drop the annotation, so such an object falls back to
|
||||||
|
// a plain object encoding instead
|
||||||
|
const auto& dims = value.at(key);
|
||||||
|
if (dims.size() < 2 || (dims.size() == 2 && dims.at(0).is_number_integer() && dims.at(0).template get<std::int64_t>() == 1))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t len = 1;
|
||||||
|
for (const auto& el : dims)
|
||||||
{
|
{
|
||||||
// a dimension is read as an unsigned value below, so anything that
|
// a dimension is read as an unsigned value below, so anything that
|
||||||
// is not a non-negative integer is rejected: a non-integer entry
|
// is not a non-negative integer is rejected: a non-integer entry
|
||||||
@@ -1823,15 +1964,26 @@ class binary_writer
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const auto dim_size = static_cast<std::size_t>(dim);
|
const auto dim_size = static_cast<std::size_t>(dim);
|
||||||
if (dim_size != 0 && len > (std::numeric_limits<std::size_t>::max)() / dim_size)
|
|
||||||
|
// the reader turns an ND-array with any zero dimension into an
|
||||||
|
// empty plain array, dropping the annotation, so keep the object
|
||||||
|
if (dim_size == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (len > (std::numeric_limits<std::size_t>::max)() / dim_size)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
len *= dim_size;
|
len *= dim_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the elements are written from _ArrayData_ as a flat list, so it has
|
||||||
|
// to be an array: size() is 0 for null and 1 for any other scalar, and
|
||||||
|
// iterating an object visits its values, so any of these could match
|
||||||
|
// the dimensions by accident and be encoded as an unrelated ND-array
|
||||||
key = "_ArrayData_";
|
key = "_ArrayData_";
|
||||||
if (value.at(key).size() != len)
|
if (!value.at(key).is_array() || value.at(key).size() != len)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
#include <nlohmann/detail/meta/cpp_future.hpp>
|
||||||
#include <nlohmann/detail/output/binary_writer.hpp>
|
#include <nlohmann/detail/output/binary_writer.hpp>
|
||||||
#include <nlohmann/detail/output/output_adapters.hpp>
|
#include <nlohmann/detail/output/output_adapters.hpp>
|
||||||
|
#include <nlohmann/detail/recursion_depth_limit.hpp>
|
||||||
#include <nlohmann/detail/string_concat.hpp>
|
#include <nlohmann/detail/string_concat.hpp>
|
||||||
#include <nlohmann/detail/value_t.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
|
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
|
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
|
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
|
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.
|
vanishing minority - is written by exactly the code that always wrote it.
|
||||||
@@ -148,7 +149,7 @@ class serializer
|
|||||||
{
|
{
|
||||||
case value_t::object:
|
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);
|
dump_iteratively(val, current_indent);
|
||||||
return;
|
return;
|
||||||
@@ -223,7 +224,7 @@ class serializer
|
|||||||
|
|
||||||
case value_t::array:
|
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);
|
dump_iteratively(val, current_indent);
|
||||||
return;
|
return;
|
||||||
@@ -408,19 +409,12 @@ class serializer
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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
|
@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
|
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
|
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
|
written for speed: walking every value this way measured up to 20% slower on
|
||||||
object-heavy documents than letting the compiler drive the descent.
|
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
|
||||||
+532
-58
@@ -28,14 +28,14 @@
|
|||||||
#pragma GCC diagnostic ignored "-Wignored-attributes"
|
#pragma GCC diagnostic ignored "-Wignored-attributes"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <algorithm> // all_of, find, for_each
|
#include <algorithm> // all_of, find, for_each, none_of
|
||||||
#include <cstddef> // nullptr_t, ptrdiff_t, size_t
|
#include <cstddef> // nullptr_t, ptrdiff_t, size_t
|
||||||
#include <functional> // hash, less
|
#include <functional> // hash, less
|
||||||
#include <initializer_list> // initializer_list
|
#include <initializer_list> // initializer_list
|
||||||
#ifndef JSON_NO_IO
|
#ifndef JSON_NO_IO
|
||||||
#include <iosfwd> // istream, ostream
|
#include <iosfwd> // istream, ostream
|
||||||
#endif // JSON_NO_IO
|
#endif // JSON_NO_IO
|
||||||
#include <iterator> // random_access_iterator_tag
|
#include <iterator> // make_move_iterator, random_access_iterator_tag
|
||||||
#include <memory> // unique_ptr
|
#include <memory> // unique_ptr
|
||||||
#include <string> // string, stoi, to_string
|
#include <string> // string, stoi, to_string
|
||||||
#include <utility> // declval, forward, move, pair, swap
|
#include <utility> // declval, forward, move, pair, swap
|
||||||
@@ -68,6 +68,7 @@
|
|||||||
#include <nlohmann/detail/output/binary_writer.hpp>
|
#include <nlohmann/detail/output/binary_writer.hpp>
|
||||||
#include <nlohmann/detail/output/output_adapters.hpp>
|
#include <nlohmann/detail/output/output_adapters.hpp>
|
||||||
#include <nlohmann/detail/output/serializer.hpp>
|
#include <nlohmann/detail/output/serializer.hpp>
|
||||||
|
#include <nlohmann/detail/recursion_depth_limit.hpp>
|
||||||
#include <nlohmann/detail/value_t.hpp>
|
#include <nlohmann/detail/value_t.hpp>
|
||||||
#include <nlohmann/json_fwd.hpp>
|
#include <nlohmann/json_fwd.hpp>
|
||||||
#include <nlohmann/ordered_map.hpp>
|
#include <nlohmann/ordered_map.hpp>
|
||||||
@@ -896,6 +897,352 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return j;
|
return j;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef JSON_NO_THREAD_LOCAL
|
||||||
|
/// the number of levels an operation descends into before it finishes the
|
||||||
|
/// value below it without the call stack
|
||||||
|
static constexpr std::uint8_t nesting_depth_limit()
|
||||||
|
{
|
||||||
|
return 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief how many levels the operation going on in this thread has descended into
|
||||||
|
|
||||||
|
Copying a value and comparing two values share this count. The library never
|
||||||
|
nests one inside the other - copying a value does not compare one, and
|
||||||
|
comparing two values does not copy them - and where user code nests them
|
||||||
|
anyway, sharing the count only ends a descent sooner than it had to, which
|
||||||
|
costs a little speed and is never wrong.
|
||||||
|
|
||||||
|
A byte is enough: the count never exceeds the limit by more than the single
|
||||||
|
level that notices the limit has been reached.
|
||||||
|
*/
|
||||||
|
static std::uint8_t& nesting_depth() noexcept
|
||||||
|
{
|
||||||
|
static thread_local std::uint8_t depth = 0; // NOLINT(misc-use-internal-linkage)
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief counts one level of a bounded descent for as long as it runs, and
|
||||||
|
reports whether the descent was still within the limit when it began
|
||||||
|
|
||||||
|
Looks the count up and tests it against the limit itself, rather than
|
||||||
|
leaving that to the caller: either way it is reached exactly once, so
|
||||||
|
there is nothing to be gained by making the caller do it.
|
||||||
|
|
||||||
|
Does nothing and is never @ref okay without thread-local storage, where no
|
||||||
|
descent can be bounded at all: a caller that only descends while this says
|
||||||
|
it may always ends up finishing without the call stack, exactly as if every
|
||||||
|
value were nested past the limit.
|
||||||
|
*/
|
||||||
|
class nesting_depth_guard
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
nesting_depth_guard() noexcept
|
||||||
|
#ifdef JSON_NO_THREAD_LOCAL
|
||||||
|
: m_okay(false)
|
||||||
|
#else
|
||||||
|
: m_okay(nesting_depth() < nesting_depth_limit())
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
#ifndef JSON_NO_THREAD_LOCAL
|
||||||
|
++nesting_depth();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
~nesting_depth_guard()
|
||||||
|
{
|
||||||
|
#ifndef JSON_NO_THREAD_LOCAL
|
||||||
|
--nesting_depth();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
nesting_depth_guard(const nesting_depth_guard&) = delete;
|
||||||
|
nesting_depth_guard& operator=(const nesting_depth_guard&) = delete;
|
||||||
|
nesting_depth_guard(nesting_depth_guard&&) = delete;
|
||||||
|
nesting_depth_guard& operator=(nesting_depth_guard&&) = delete;
|
||||||
|
|
||||||
|
bool okay() const noexcept
|
||||||
|
{
|
||||||
|
return m_okay;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_okay;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// an entry of the iterative deep copy's worklist: a structured value and
|
||||||
|
/// the value that is to become its copy
|
||||||
|
using copy_worklist_t = std::vector<std::pair<const basic_json*, basic_json*>>;
|
||||||
|
|
||||||
|
/// scratch space to build the key skeleton of an object copy in one go
|
||||||
|
using copy_scratch_t = std::vector<std::pair<typename object_t::key_type, basic_json>>;
|
||||||
|
|
||||||
|
/// @brief copy everything of @a src into @a dst but its type and value
|
||||||
|
static void copy_metadata(const basic_json& src, basic_json& dst)
|
||||||
|
{
|
||||||
|
// a custom base class is only required to be copy-constructible and
|
||||||
|
// move-assignable, so the copy has to go through a temporary
|
||||||
|
static_cast<json_base_class_t&>(dst) = json_base_class_t(static_cast<const json_base_class_t&>(src));
|
||||||
|
|
||||||
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
|
dst.start_position = src.start_position;
|
||||||
|
dst.end_position = src.end_position;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief copy the value of @a src into @a dst, which must not be structured
|
||||||
|
|
||||||
|
Objects and arrays are left alone: creating those is the one thing the copy
|
||||||
|
constructor and @ref copy_shallow do differently from one another, and it is
|
||||||
|
the reason copying a value can descend at all.
|
||||||
|
*/
|
||||||
|
/// @note inlined on purpose: both callers have already told an object or an
|
||||||
|
/// array apart from the rest, and letting the compiler fold that test
|
||||||
|
/// into this switch is worth a few percent when copying a value made
|
||||||
|
/// mostly of numbers
|
||||||
|
JSON_HEDLEY_ALWAYS_INLINE
|
||||||
|
static void copy_leaf_value(const basic_json& src, basic_json& dst)
|
||||||
|
{
|
||||||
|
switch (src.m_data.m_type)
|
||||||
|
{
|
||||||
|
case value_t::string:
|
||||||
|
{
|
||||||
|
dst.m_data.m_value = *src.m_data.m_value.string;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::binary:
|
||||||
|
{
|
||||||
|
dst.m_data.m_value = *src.m_data.m_value.binary;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::boolean:
|
||||||
|
{
|
||||||
|
dst.m_data.m_value = src.m_data.m_value.boolean;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::number_integer:
|
||||||
|
{
|
||||||
|
dst.m_data.m_value = src.m_data.m_value.number_integer;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
{
|
||||||
|
dst.m_data.m_value = src.m_data.m_value.number_unsigned;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::number_float:
|
||||||
|
{
|
||||||
|
dst.m_data.m_value = src.m_data.m_value.number_float;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::object:
|
||||||
|
case value_t::array:
|
||||||
|
case value_t::null:
|
||||||
|
case value_t::discarded:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief copy everything of @a src into the null value @a dst but the children
|
||||||
|
|
||||||
|
Objects and arrays are not copied here; they are appended to @a worklist to
|
||||||
|
be created later by @ref copy_iteratively. Until that happens, @a dst remains
|
||||||
|
a null value, so that a partially built copy can be destroyed at any point
|
||||||
|
without ever violating the class invariants.
|
||||||
|
*/
|
||||||
|
static void copy_shallow(const basic_json& src, basic_json& dst, copy_worklist_t& worklist)
|
||||||
|
{
|
||||||
|
copy_metadata(src, dst);
|
||||||
|
|
||||||
|
if (src.m_data.m_type == value_t::object || src.m_data.m_type == value_t::array)
|
||||||
|
{
|
||||||
|
// defer: dst stays a null value until its container exists
|
||||||
|
worklist.emplace_back(&src, &dst);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_leaf_value(src, dst);
|
||||||
|
|
||||||
|
// only now that the value exists may the type be set: had the creation
|
||||||
|
// of the value thrown, dst would have been left as a valid null value
|
||||||
|
dst.m_data.m_type = src.m_data.m_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief create the copy of the array @a src in @a dst
|
||||||
|
/// @note structured elements are appended to @a worklist instead
|
||||||
|
static void copy_array_level(const basic_json& src, basic_json& dst, copy_worklist_t& worklist)
|
||||||
|
{
|
||||||
|
const array_t& src_array = *src.m_data.m_value.array;
|
||||||
|
|
||||||
|
// create all elements up front: growing the array afterwards could
|
||||||
|
// invalidate the pointers that are handed to the worklist; resize()
|
||||||
|
// rather than the fill constructor, because not every array type
|
||||||
|
// provides the latter (e.g., ones without a matching allocator-aware
|
||||||
|
// fill constructor)
|
||||||
|
dst.m_data.m_value.array = create<array_t>();
|
||||||
|
dst.m_data.m_value.array->resize(src_array.size());
|
||||||
|
|
||||||
|
auto dst_it = dst.m_data.m_value.array->begin();
|
||||||
|
for (auto src_it = src_array.cbegin(); src_it != src_array.cend(); ++src_it, ++dst_it)
|
||||||
|
{
|
||||||
|
copy_shallow(*src_it, *dst_it, worklist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief create the copy of the object @a src in @a dst
|
||||||
|
/// @note structured values are appended to @a worklist instead
|
||||||
|
static void copy_object_level(const basic_json& src, basic_json& dst,
|
||||||
|
copy_worklist_t& worklist, copy_scratch_t& scratch)
|
||||||
|
{
|
||||||
|
const object_t& src_object = *src.m_data.m_value.object;
|
||||||
|
|
||||||
|
// build the complete key skeleton and hand it to the object's range
|
||||||
|
// constructor: adding the keys one by one would be quadratic for object
|
||||||
|
// types that are backed by a vector, such as nlohmann::ordered_map
|
||||||
|
scratch.clear();
|
||||||
|
scratch.reserve(src_object.size());
|
||||||
|
for (const auto& element : src_object)
|
||||||
|
{
|
||||||
|
scratch.emplace_back(element.first, basic_json());
|
||||||
|
}
|
||||||
|
|
||||||
|
dst.m_data.m_value.object = create<object_t>(std::make_move_iterator(scratch.begin()),
|
||||||
|
std::make_move_iterator(scratch.end()));
|
||||||
|
scratch.clear();
|
||||||
|
|
||||||
|
// pair every value of the copy with its counterpart in the original;
|
||||||
|
// both are enumerated in the same order for every object type with a
|
||||||
|
// deterministic order, so the lookup is only needed for exotic ones
|
||||||
|
auto src_it = src_object.cbegin();
|
||||||
|
for (auto& element : *dst.m_data.m_value.object)
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_LIKELY(src_it != src_object.cend() && src_it->first == element.first))
|
||||||
|
{
|
||||||
|
copy_shallow(src_it->second, element.second, worklist);
|
||||||
|
++src_it;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const auto found = src_object.find(element.first);
|
||||||
|
JSON_ASSERT(found != src_object.cend());
|
||||||
|
copy_shallow(found->second, element.second, worklist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief deep-copy the object or array @a src into this value without recursing
|
||||||
|
|
||||||
|
The values whose copy has not been created yet are kept on an explicit
|
||||||
|
worklist rather than on the call stack. This is only reached for values
|
||||||
|
nested deeper than @ref nesting_depth_limit levels, which is why it copies
|
||||||
|
every container by hand instead of letting the container do it: the fast
|
||||||
|
ways of doing so would descend into the elements and defeat the purpose.
|
||||||
|
*/
|
||||||
|
void copy_iteratively(const basic_json& src)
|
||||||
|
{
|
||||||
|
copy_worklist_t worklist;
|
||||||
|
copy_scratch_t scratch;
|
||||||
|
|
||||||
|
const basic_json* src_value = &src;
|
||||||
|
basic_json* dst_value = this;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (src_value->m_data.m_type == value_t::array)
|
||||||
|
{
|
||||||
|
copy_array_level(*src_value, *dst_value, worklist);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
copy_object_level(*src_value, *dst_value, worklist, scratch);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the container is complete and will not be modified again
|
||||||
|
dst_value->set_parents();
|
||||||
|
|
||||||
|
if (worklist.empty())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& next = worklist.back();
|
||||||
|
src_value = next.first;
|
||||||
|
dst_value = next.second;
|
||||||
|
worklist.pop_back();
|
||||||
|
|
||||||
|
// the value stops being a null value exactly here
|
||||||
|
dst_value->m_data.m_type = src_value->m_data.m_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief copy one level of the object or array @a src into this value
|
||||||
|
|
||||||
|
The container copies its own elements, which is the fastest way to fill it.
|
||||||
|
Every element that is structured itself comes back to @ref copy_structured.
|
||||||
|
*/
|
||||||
|
void copy_level(const basic_json& src)
|
||||||
|
{
|
||||||
|
if (m_data.m_type == value_t::object)
|
||||||
|
{
|
||||||
|
m_data.m_value = *src.m_data.m_value.object;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_data.m_value = *src.m_data.m_value.array;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_parents();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief deep-copy the object or array @a src into this value
|
||||||
|
|
||||||
|
Copying a container copies its elements, so a value nested deeply enough
|
||||||
|
used to exhaust the call stack. The descent is bounded here: the first
|
||||||
|
@ref nesting_depth_limit levels are copied by the containers themselves, just
|
||||||
|
as they always were, and anything below that is copied without the call
|
||||||
|
stack by @ref copy_iteratively. Copying a value can therefore no longer
|
||||||
|
exhaust the stack, however deeply it is nested, just like destroying one
|
||||||
|
cannot since #1436.
|
||||||
|
|
||||||
|
Nothing has to be scanned or built by hand to reach that: a value that is
|
||||||
|
not nested deeper than the limit - all but a vanishing minority - is copied
|
||||||
|
exactly as it was before, and this whole detour costs it one counter.
|
||||||
|
|
||||||
|
@sa https://github.com/nlohmann/json/issues/5387
|
||||||
|
*/
|
||||||
|
void copy_structured(const basic_json& src)
|
||||||
|
{
|
||||||
|
const nesting_depth_guard guard;
|
||||||
|
|
||||||
|
if (JSON_HEDLEY_LIKELY(guard.okay()))
|
||||||
|
{
|
||||||
|
copy_level(src);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finish this value without descending any further. It is completed
|
||||||
|
// before this returns, so a copy made by a custom base class - or by
|
||||||
|
// anything else that runs while a copy is going on - is unaffected by
|
||||||
|
// the copy it is nested in.
|
||||||
|
copy_iteratively(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
// JSON parser callback //
|
// JSON parser callback //
|
||||||
@@ -1275,60 +1622,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// check of passed value is valid
|
// check of passed value is valid
|
||||||
other.assert_invariant();
|
other.assert_invariant();
|
||||||
|
|
||||||
switch (m_data.m_type)
|
if (m_data.m_type == value_t::object || m_data.m_type == value_t::array)
|
||||||
{
|
{
|
||||||
case value_t::object:
|
// copying the container directly would call this constructor again
|
||||||
{
|
// for every element, once per nesting level
|
||||||
m_data.m_value = *other.m_data.m_value.object;
|
copy_structured(other);
|
||||||
break;
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
case value_t::array:
|
copy_leaf_value(other, *this);
|
||||||
{
|
|
||||||
m_data.m_value = *other.m_data.m_value.array;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::string:
|
|
||||||
{
|
|
||||||
m_data.m_value = *other.m_data.m_value.string;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::boolean:
|
|
||||||
{
|
|
||||||
m_data.m_value = other.m_data.m_value.boolean;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::number_integer:
|
|
||||||
{
|
|
||||||
m_data.m_value = other.m_data.m_value.number_integer;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
{
|
|
||||||
m_data.m_value = other.m_data.m_value.number_unsigned;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::number_float:
|
|
||||||
{
|
|
||||||
m_data.m_value = other.m_data.m_value.number_float;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::binary:
|
|
||||||
{
|
|
||||||
m_data.m_value = *other.m_data.m_value.binary;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::null:
|
|
||||||
case value_t::discarded:
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set_parents();
|
set_parents();
|
||||||
@@ -3611,17 +3913,54 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", first.m_object->type_name()), first.m_object));
|
JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", first.m_object->type_name()), first.m_object));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update_members(first, last, merge_objects, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// @brief an object @ref update_members_iteratively or @ref
|
||||||
|
/// merge_patch_iteratively is merging into, and the members still to merge
|
||||||
|
struct merge_frame
|
||||||
|
{
|
||||||
|
merge_frame(basic_json* target_, const_iterator position_, const_iterator last_) noexcept
|
||||||
|
: target(target_), position(std::move(position_)), last(std::move(last_))
|
||||||
|
{}
|
||||||
|
|
||||||
|
basic_json* target;
|
||||||
|
const_iterator position;
|
||||||
|
const_iterator last;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief the members loop of @ref update, for this object and range
|
||||||
|
|
||||||
|
Merging a nested object calls this function again, once per nesting
|
||||||
|
level, so a value nested deeply enough used to exhaust the call stack and
|
||||||
|
terminate the process. The descent is bounded here: once @ref
|
||||||
|
detail::recursion_depth_limit levels have been entered, @ref
|
||||||
|
update_members_iteratively merges what is left without the call stack.
|
||||||
|
|
||||||
|
@param[in] depth nesting level of this object, counted from the object
|
||||||
|
@ref update was called on
|
||||||
|
*/
|
||||||
|
void update_members(const const_iterator& first, const const_iterator& last, const bool merge_objects, const std::size_t depth)
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(depth >= detail::recursion_depth_limit()))
|
||||||
|
{
|
||||||
|
update_members_iteratively(first, last);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (auto it = first; it != last; ++it)
|
for (auto it = first; it != last; ++it)
|
||||||
{
|
{
|
||||||
if (merge_objects && it.value().is_object())
|
if (merge_objects && it.value().is_object())
|
||||||
{
|
{
|
||||||
auto it2 = m_data.m_value.object->find(it.key());
|
const auto it2 = m_data.m_value.object->find(it.key());
|
||||||
// Only recurse when the existing value is itself an object.
|
// Only recurse when the existing value is itself an object.
|
||||||
// Otherwise overwrite, matching the documented "all other values
|
// Otherwise overwrite, matching the documented "all other values
|
||||||
// are overwritten as usual" behavior (see #5402).
|
// are overwritten as usual" behavior (see #5402).
|
||||||
if (it2 != m_data.m_value.object->end() && it2->second.is_object())
|
if (it2 != m_data.m_value.object->end() && it2->second.is_object())
|
||||||
{
|
{
|
||||||
it2->second.update(it.value(), true);
|
it2->second.update_members(it.value().cbegin(), it.value().cend(), true, depth + 1);
|
||||||
#if JSON_DIAGNOSTICS
|
#if JSON_DIAGNOSTICS
|
||||||
it2->second.set_parents();
|
it2->second.set_parents();
|
||||||
#endif
|
#endif
|
||||||
@@ -3635,6 +3974,64 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief merge @a first to @a last into this object without the call stack
|
||||||
|
|
||||||
|
Does the same as @ref update_members with `merge_objects` set, keeping the
|
||||||
|
objects whose merge was interrupted by a nested one on an explicit stack
|
||||||
|
instead of descending into them. A nested object is still merged
|
||||||
|
completely before the next member, in the same order as the recursive
|
||||||
|
version. Only reached for values nested deeper than @ref
|
||||||
|
detail::recursion_depth_limit.
|
||||||
|
*/
|
||||||
|
void update_members_iteratively(const_iterator first, const_iterator last)
|
||||||
|
{
|
||||||
|
std::vector<merge_frame> stack;
|
||||||
|
|
||||||
|
basic_json* target = this;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (first == last)
|
||||||
|
{
|
||||||
|
if (stack.empty())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// a nested object is merged: continue with its parent
|
||||||
|
#if JSON_DIAGNOSTICS
|
||||||
|
target->set_parents();
|
||||||
|
#endif
|
||||||
|
target = stack.back().target;
|
||||||
|
first = stack.back().position;
|
||||||
|
last = stack.back().last;
|
||||||
|
stack.pop_back();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first.value().is_object())
|
||||||
|
{
|
||||||
|
const auto it2 = target->m_data.m_value.object->find(first.key());
|
||||||
|
if (it2 != target->m_data.m_value.object->end() && it2->second.is_object())
|
||||||
|
{
|
||||||
|
const basic_json& source = first.value();
|
||||||
|
++first;
|
||||||
|
stack.emplace_back(target, first, last);
|
||||||
|
target = &it2->second;
|
||||||
|
first = source.cbegin();
|
||||||
|
last = source.cend();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target->m_data.m_value.object->operator[](first.key()) = first.value();
|
||||||
|
#if JSON_DIAGNOSTICS
|
||||||
|
target->m_data.m_value.object->operator[](first.key()).m_parent = target;
|
||||||
|
#endif
|
||||||
|
++first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
/// @brief exchanges the values
|
/// @brief exchanges the values
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/swap/
|
/// @sa https://json.nlohmann.me/api/basic_json/swap/
|
||||||
void swap(reference other) noexcept (
|
void swap(reference other) noexcept (
|
||||||
@@ -5529,9 +5926,30 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
/// @brief applies a JSON Merge Patch
|
/// @brief applies a JSON Merge Patch
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/merge_patch/
|
/// @sa https://json.nlohmann.me/api/basic_json/merge_patch/
|
||||||
void merge_patch(const basic_json& apply_patch)
|
void merge_patch(const basic_json& apply_patch)
|
||||||
|
{
|
||||||
|
apply_merge_patch(apply_patch, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*!
|
||||||
|
@brief @ref merge_patch, for a patch at nesting level @a depth
|
||||||
|
|
||||||
|
Applying a nested object calls this function again, once per nesting
|
||||||
|
level, so a patch nested deeply enough used to exhaust the call stack and
|
||||||
|
terminate the process. The descent is bounded here: once @ref
|
||||||
|
detail::recursion_depth_limit levels have been entered, @ref
|
||||||
|
merge_patch_iteratively applies what is left without the call stack.
|
||||||
|
*/
|
||||||
|
void apply_merge_patch(const basic_json& apply_patch, const std::size_t depth)
|
||||||
{
|
{
|
||||||
if (apply_patch.is_object())
|
if (apply_patch.is_object())
|
||||||
{
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(depth >= detail::recursion_depth_limit()))
|
||||||
|
{
|
||||||
|
merge_patch_iteratively(apply_patch);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!is_object())
|
if (!is_object())
|
||||||
{
|
{
|
||||||
*this = object();
|
*this = object();
|
||||||
@@ -5544,7 +5962,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
operator[](it.key()).merge_patch(it.value());
|
operator[](it.key()).apply_merge_patch(it.value(), depth + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5554,6 +5972,62 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief apply @a apply_patch to this value without the call stack
|
||||||
|
|
||||||
|
Does the same as @ref merge_patch, keeping the objects being patched on an
|
||||||
|
explicit stack instead of descending into them. A nested object is still
|
||||||
|
patched completely before the next member, in the same order as the
|
||||||
|
recursive version. Only reached for patches nested deeper than @ref
|
||||||
|
detail::recursion_depth_limit.
|
||||||
|
*/
|
||||||
|
void merge_patch_iteratively(const basic_json& apply_patch)
|
||||||
|
{
|
||||||
|
std::vector<merge_frame> stack;
|
||||||
|
|
||||||
|
// patch `target` with `patch`, or start patching it member by member
|
||||||
|
const auto apply = [&stack](basic_json & target, const basic_json & patch)
|
||||||
|
{
|
||||||
|
if (patch.is_object())
|
||||||
|
{
|
||||||
|
if (!target.is_object())
|
||||||
|
{
|
||||||
|
target = basic_json::object();
|
||||||
|
}
|
||||||
|
stack.emplace_back(&target, patch.cbegin(), patch.cend());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
target = patch;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
apply(*this, apply_patch);
|
||||||
|
while (!stack.empty())
|
||||||
|
{
|
||||||
|
// a copy, as applying a member below can reallocate the stack;
|
||||||
|
// the frame itself is only changed through stack.back()
|
||||||
|
const merge_frame frame = stack.back();
|
||||||
|
if (frame.position == frame.last)
|
||||||
|
{
|
||||||
|
stack.pop_back();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const const_iterator member = frame.position;
|
||||||
|
++stack.back().position;
|
||||||
|
if (member.value().is_null())
|
||||||
|
{
|
||||||
|
frame.target->erase(member.key());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
apply(frame.target->operator[](member.key()), member.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
/// @}
|
/// @}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -215,6 +215,126 @@
|
|||||||
</Expand>
|
</Expand>
|
||||||
</Type>
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_dp -->
|
||||||
|
<Type Name="nlohmann::json_abi_dp::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_dp::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_dp::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_dp::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_dp_v3_12_0 -->
|
||||||
|
<Type Name="nlohmann::json_abi_dp_v3_12_0::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_v3_12_0::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_v3_12_0::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_v3_12_0::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_v3_12_0::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_v3_12_0::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_v3_12_0::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_v3_12_0::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_v3_12_0::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_v3_12_0::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_dp_v3_12_0::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_dp_v3_12_0::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_dp_v3_12_0::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_bics -->
|
||||||
|
<Type Name="nlohmann::json_abi_bics::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_bics::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_bics::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_bics::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_bics::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_bics::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_bics::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_bics::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_bics::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_bics::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_bics::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_bics::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_bics::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_bics_v3_12_0 -->
|
||||||
|
<Type Name="nlohmann::json_abi_bics_v3_12_0::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_bics_v3_12_0::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_bics_v3_12_0::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_bics_v3_12_0::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_bics_v3_12_0::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_bics_v3_12_0::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_bics_v3_12_0::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_bics_v3_12_0::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_bics_v3_12_0::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_bics_v3_12_0::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_bics_v3_12_0::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_bics_v3_12_0::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_bics_v3_12_0::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
<!-- Namespace nlohmann::json_abi_diag_ldvcmp -->
|
<!-- Namespace nlohmann::json_abi_diag_ldvcmp -->
|
||||||
<Type Name="nlohmann::json_abi_diag_ldvcmp::basic_json<*>">
|
<Type Name="nlohmann::json_abi_diag_ldvcmp::basic_json<*>">
|
||||||
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp::detail::value_t::null">null</DisplayString>
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp::detail::value_t::null">null</DisplayString>
|
||||||
@@ -275,4 +395,604 @@
|
|||||||
</Expand>
|
</Expand>
|
||||||
</Type>
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_diag_dp -->
|
||||||
|
<Type Name="nlohmann::json_abi_diag_dp::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_dp::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_dp::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_diag_dp::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_diag_dp_v3_12_0 -->
|
||||||
|
<Type Name="nlohmann::json_abi_diag_dp_v3_12_0::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_v3_12_0::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_v3_12_0::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_v3_12_0::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_v3_12_0::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_v3_12_0::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_v3_12_0::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_v3_12_0::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_v3_12_0::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_v3_12_0::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_dp_v3_12_0::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_dp_v3_12_0::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_diag_dp_v3_12_0::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_diag_bics -->
|
||||||
|
<Type Name="nlohmann::json_abi_diag_bics::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_bics::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_bics::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_bics::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_bics::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_bics::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_bics::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_bics::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_bics::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_bics::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_bics::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_bics::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_diag_bics::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_diag_bics_v3_12_0 -->
|
||||||
|
<Type Name="nlohmann::json_abi_diag_bics_v3_12_0::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_bics_v3_12_0::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_bics_v3_12_0::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_bics_v3_12_0::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_bics_v3_12_0::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_bics_v3_12_0::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_bics_v3_12_0::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_bics_v3_12_0::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_bics_v3_12_0::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_bics_v3_12_0::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_bics_v3_12_0::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_bics_v3_12_0::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_diag_bics_v3_12_0::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_ldvcmp_dp -->
|
||||||
|
<Type Name="nlohmann::json_abi_ldvcmp_dp::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_ldvcmp_dp::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_ldvcmp_dp_v3_12_0 -->
|
||||||
|
<Type Name="nlohmann::json_abi_ldvcmp_dp_v3_12_0::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_v3_12_0::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_v3_12_0::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_v3_12_0::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_v3_12_0::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_v3_12_0::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_v3_12_0::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_v3_12_0::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_v3_12_0::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_v3_12_0::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_v3_12_0::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_v3_12_0::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_ldvcmp_dp_v3_12_0::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_ldvcmp_bics -->
|
||||||
|
<Type Name="nlohmann::json_abi_ldvcmp_bics::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_ldvcmp_bics::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_ldvcmp_bics_v3_12_0 -->
|
||||||
|
<Type Name="nlohmann::json_abi_ldvcmp_bics_v3_12_0::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics_v3_12_0::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics_v3_12_0::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics_v3_12_0::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics_v3_12_0::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics_v3_12_0::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics_v3_12_0::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics_v3_12_0::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics_v3_12_0::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics_v3_12_0::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics_v3_12_0::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_bics_v3_12_0::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_ldvcmp_bics_v3_12_0::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_dp_bics -->
|
||||||
|
<Type Name="nlohmann::json_abi_dp_bics::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_bics::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_bics::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_bics::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_bics::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_bics::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_bics::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_bics::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_bics::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_bics::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_dp_bics::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_dp_bics::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_dp_bics::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_dp_bics_v3_12_0 -->
|
||||||
|
<Type Name="nlohmann::json_abi_dp_bics_v3_12_0::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_bics_v3_12_0::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_bics_v3_12_0::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_bics_v3_12_0::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_bics_v3_12_0::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_bics_v3_12_0::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_bics_v3_12_0::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_bics_v3_12_0::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_bics_v3_12_0::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_dp_bics_v3_12_0::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_dp_bics_v3_12_0::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_dp_bics_v3_12_0::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_dp_bics_v3_12_0::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_diag_ldvcmp_dp -->
|
||||||
|
<Type Name="nlohmann::json_abi_diag_ldvcmp_dp::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_diag_ldvcmp_dp::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_diag_ldvcmp_dp_v3_12_0 -->
|
||||||
|
<Type Name="nlohmann::json_abi_diag_ldvcmp_dp_v3_12_0::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_v3_12_0::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_v3_12_0::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_v3_12_0::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_v3_12_0::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_v3_12_0::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_v3_12_0::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_v3_12_0::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_v3_12_0::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_v3_12_0::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_v3_12_0::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_v3_12_0::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_diag_ldvcmp_dp_v3_12_0::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_diag_ldvcmp_bics -->
|
||||||
|
<Type Name="nlohmann::json_abi_diag_ldvcmp_bics::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_diag_ldvcmp_bics::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_diag_ldvcmp_bics_v3_12_0 -->
|
||||||
|
<Type Name="nlohmann::json_abi_diag_ldvcmp_bics_v3_12_0::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics_v3_12_0::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics_v3_12_0::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics_v3_12_0::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics_v3_12_0::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics_v3_12_0::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics_v3_12_0::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics_v3_12_0::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics_v3_12_0::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics_v3_12_0::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics_v3_12_0::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_bics_v3_12_0::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_diag_ldvcmp_bics_v3_12_0::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_diag_dp_bics -->
|
||||||
|
<Type Name="nlohmann::json_abi_diag_dp_bics::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_diag_dp_bics::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_diag_dp_bics_v3_12_0 -->
|
||||||
|
<Type Name="nlohmann::json_abi_diag_dp_bics_v3_12_0::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics_v3_12_0::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics_v3_12_0::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics_v3_12_0::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics_v3_12_0::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics_v3_12_0::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics_v3_12_0::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics_v3_12_0::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics_v3_12_0::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics_v3_12_0::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics_v3_12_0::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_dp_bics_v3_12_0::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_diag_dp_bics_v3_12_0::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_ldvcmp_dp_bics -->
|
||||||
|
<Type Name="nlohmann::json_abi_ldvcmp_dp_bics::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_ldvcmp_dp_bics::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_ldvcmp_dp_bics_v3_12_0 -->
|
||||||
|
<Type Name="nlohmann::json_abi_ldvcmp_dp_bics_v3_12_0::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics_v3_12_0::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics_v3_12_0::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics_v3_12_0::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics_v3_12_0::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics_v3_12_0::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics_v3_12_0::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics_v3_12_0::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics_v3_12_0::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics_v3_12_0::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics_v3_12_0::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_ldvcmp_dp_bics_v3_12_0::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_ldvcmp_dp_bics_v3_12_0::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_diag_ldvcmp_dp_bics -->
|
||||||
|
<Type Name="nlohmann::json_abi_diag_ldvcmp_dp_bics::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_diag_ldvcmp_dp_bics::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Namespace nlohmann::json_abi_diag_ldvcmp_dp_bics_v3_12_0 -->
|
||||||
|
<Type Name="nlohmann::json_abi_diag_ldvcmp_dp_bics_v3_12_0::basic_json<*>">
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics_v3_12_0::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics_v3_12_0::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics_v3_12_0::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics_v3_12_0::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics_v3_12_0::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics_v3_12_0::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics_v3_12_0::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics_v3_12_0::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics_v3_12_0::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics_v3_12_0::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == nlohmann::json_abi_diag_ldvcmp_dp_bics_v3_12_0::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<!-- Skip the pair first/second members in the treeview while traversing a map.
|
||||||
|
Only works in VS 2015 Update 2 and beyond using the new visualization -->
|
||||||
|
<Type Name="std::pair<*, nlohmann::json_abi_diag_ldvcmp_dp_bics_v3_12_0::basic_json<*>>" IncludeView="MapHelper">
|
||||||
|
<DisplayString>{second}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>second</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
</AutoVisualizer>
|
</AutoVisualizer>
|
||||||
|
|||||||
+989
-194
File diff suppressed because it is too large
Load Diff
@@ -52,6 +52,10 @@
|
|||||||
#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
|
#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef JSON_BRACE_INIT_COPY_SEMANTICS
|
||||||
|
#define JSON_BRACE_INIT_COPY_SEMANTICS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#if JSON_DIAGNOSTICS
|
#if JSON_DIAGNOSTICS
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||||
#else
|
#else
|
||||||
@@ -70,20 +74,27 @@
|
|||||||
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
|
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||||
#endif
|
#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
|
||||||
|
|
||||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||||
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Construct the namespace ABI tags component
|
// Construct the namespace ABI tags component
|
||||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) json_abi ## a ## b ## c
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d) json_abi ## a ## b ## c ## d
|
||||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c) \
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d) \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c)
|
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d)
|
||||||
|
|
||||||
#define NLOHMANN_JSON_ABI_TAGS \
|
#define NLOHMANN_JSON_ABI_TAGS \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
|
NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
|
||||||
NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \
|
NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \
|
||||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS)
|
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS, \
|
||||||
|
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS)
|
||||||
|
|
||||||
// Construct the namespace version component
|
// Construct the namespace version component
|
||||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||||
|
|||||||
@@ -75,7 +75,12 @@ target_compile_options(test_main PUBLIC
|
|||||||
# is annotated JSON_HEDLEY_NO_RETURN (it always throws), which
|
# is annotated JSON_HEDLEY_NO_RETURN (it always throws), which
|
||||||
# makes MSVC flag the code following its call in binary_reader.hpp
|
# makes MSVC flag the code following its call in binary_reader.hpp
|
||||||
# as unreachable for that instantiation, in both Debug and Release
|
# 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.
|
||||||
|
$<$<CXX_COMPILER_ID:MSVC>:/W4;/wd4566;/wd4996;/wd4702;/wd4503>
|
||||||
# https://github.com/nlohmann/json/issues/1114
|
# https://github.com/nlohmann/json/issues/1114
|
||||||
$<$<CXX_COMPILER_ID:MSVC>:/bigobj> $<$<BOOL:${MINGW}>:-Wa,-mbig-obj>
|
$<$<CXX_COMPILER_ID:MSVC>:/bigobj> $<$<BOOL:${MINGW}>:-Wa,-mbig-obj>
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,20 @@ add_test(
|
|||||||
NAME test-abi_config_noversion
|
NAME test-abi_config_noversion
|
||||||
COMMAND abi_config_noversion ${DOCTEST_TEST_FILTER})
|
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
|
# test custom namespace
|
||||||
add_executable(abi_config_custom custom.cpp)
|
add_executable(abi_config_custom custom.cpp)
|
||||||
target_link_libraries(abi_config_custom PRIVATE abi_compat_main)
|
target_link_libraries(abi_config_custom PRIVATE abi_compat_main)
|
||||||
|
|||||||
@@ -24,12 +24,16 @@ TEST_CASE("default namespace")
|
|||||||
expected += "_diag";
|
expected += "_diag";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||||
|
expected += "_ldvcmp";
|
||||||
|
#endif
|
||||||
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
expected += "_dp";
|
expected += "_dp";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
#if JSON_BRACE_INIT_COPY_SEMANTICS
|
||||||
expected += "_ldvcmp";
|
expected += "_bics";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
expected += "_v" STRINGIZE(NLOHMANN_JSON_VERSION_MAJOR);
|
expected += "_v" STRINGIZE(NLOHMANN_JSON_VERSION_MAJOR);
|
||||||
|
|||||||
@@ -25,12 +25,16 @@ TEST_CASE("default namespace without version component")
|
|||||||
expected += "_diag";
|
expected += "_diag";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||||
|
expected += "_ldvcmp";
|
||||||
|
#endif
|
||||||
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
expected += "_dp";
|
expected += "_dp";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
#if JSON_BRACE_INIT_COPY_SEMANTICS
|
||||||
expected += "_ldvcmp";
|
expected += "_bics";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
expected += "::basic_json";
|
expected += "::basic_json";
|
||||||
|
|||||||
@@ -46,10 +46,16 @@ The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
|||||||
drivers.
|
drivers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <nlohmann/json.hpp>
|
#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;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
// value-stable comparison for the round-trip checks below; see the note
|
// value-stable comparison for the round-trip checks below; see the note
|
||||||
|
|||||||
@@ -19,10 +19,16 @@ The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
|||||||
drivers.
|
drivers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <nlohmann/json.hpp>
|
#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;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
// see http://llvm.org/docs/LibFuzzer.html
|
// see http://llvm.org/docs/LibFuzzer.html
|
||||||
|
|||||||
@@ -19,10 +19,16 @@ The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
|||||||
drivers.
|
drivers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <nlohmann/json.hpp>
|
#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;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
// see http://llvm.org/docs/LibFuzzer.html
|
// see http://llvm.org/docs/LibFuzzer.html
|
||||||
|
|||||||
@@ -20,10 +20,16 @@ The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
|||||||
drivers.
|
drivers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <nlohmann/json.hpp>
|
#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;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
// see http://llvm.org/docs/LibFuzzer.html
|
// see http://llvm.org/docs/LibFuzzer.html
|
||||||
|
|||||||
@@ -19,10 +19,16 @@ The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
|||||||
drivers.
|
drivers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <nlohmann/json.hpp>
|
#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;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
// see http://llvm.org/docs/LibFuzzer.html
|
// see http://llvm.org/docs/LibFuzzer.html
|
||||||
|
|||||||
@@ -25,10 +25,16 @@ The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
|||||||
drivers.
|
drivers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <nlohmann/json.hpp>
|
#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;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
// see http://llvm.org/docs/LibFuzzer.html
|
// see http://llvm.org/docs/LibFuzzer.html
|
||||||
|
|||||||
@@ -216,6 +216,57 @@ TEST_CASE("controlled bad_alloc")
|
|||||||
CHECK_THROWS_AS(my_json(s), std::bad_alloc&);
|
CHECK_THROWS_AS(my_json(s), std::bad_alloc&);
|
||||||
next_construct_fails = false;
|
next_construct_fails = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("basic_json(const basic_json&) of a deeply nested value (#5387)")
|
||||||
|
{
|
||||||
|
// Copying a value nested deeper than the descent bound builds the
|
||||||
|
// copy from the top down: every value whose own copy has not been
|
||||||
|
// made yet stays a null value until it is. Failing an allocation
|
||||||
|
// part-way through is what proves such a half-built copy can still
|
||||||
|
// be destroyed.
|
||||||
|
//
|
||||||
|
// Which path the failure lands in depends on the build: the first
|
||||||
|
// allocation of a copy belongs to the outermost level, so here it
|
||||||
|
// is the descending one. Built with JSON_NO_THREAD_LOCAL - as the
|
||||||
|
// ci_test_no_thread_local target builds the whole suite - no
|
||||||
|
// descent is made at all and the very same failure lands in the
|
||||||
|
// iterative path instead, part-way through its worklist.
|
||||||
|
const auto check_deep_copy = [](bool objects)
|
||||||
|
{
|
||||||
|
CAPTURE(objects);
|
||||||
|
|
||||||
|
next_construct_fails = false;
|
||||||
|
|
||||||
|
// deeper than the 128 levels the copy constructor descends into
|
||||||
|
const std::size_t depth = 300;
|
||||||
|
|
||||||
|
my_json j = 1;
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
if (objects)
|
||||||
|
{
|
||||||
|
my_json wrapper = my_json::object();
|
||||||
|
wrapper["a"] = std::move(j);
|
||||||
|
j = std::move(wrapper);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
j = my_json::array({std::move(j)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(performance-unnecessary-copy-initialization): the copy is what is tested
|
||||||
|
CHECK_NOTHROW(my_json(j));
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
// NOLINTNEXTLINE(performance-unnecessary-copy-initialization): the copy is what is tested
|
||||||
|
CHECK_THROWS_AS(my_json(j), std::bad_alloc&);
|
||||||
|
next_construct_fails = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
check_deep_copy(false);
|
||||||
|
check_deep_copy(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+93
-19
@@ -2604,25 +2604,25 @@ TEST_CASE("BJData")
|
|||||||
// that still round-trips.
|
// that still round-trips.
|
||||||
|
|
||||||
// string data declared as a uint64 array
|
// string data declared as a uint64 array
|
||||||
json const j_str = json({{"_ArrayType_", "uint64"}, {"_ArraySize_", {1}}, {"_ArrayData_", {"pointer"}}});
|
json const j_str = json({{"_ArrayType_", "uint64"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {"pointer", "value"}}});
|
||||||
const auto out_str = json::to_bjdata(j_str);
|
const auto out_str = json::to_bjdata(j_str);
|
||||||
CHECK(out_str.at(0) == '{');
|
CHECK(out_str.at(0) == '{');
|
||||||
CHECK(json::from_bjdata(out_str) == j_str);
|
CHECK(json::from_bjdata(out_str) == j_str);
|
||||||
|
|
||||||
// integer data declared as a double array
|
// integer data declared as a double array
|
||||||
json const j_float = json({{"_ArrayType_", "double"}, {"_ArraySize_", {2}}, {"_ArrayData_", {1, 2}}});
|
json const j_float = json({{"_ArrayType_", "double"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1, 2}}});
|
||||||
const auto out_float = json::to_bjdata(j_float);
|
const auto out_float = json::to_bjdata(j_float);
|
||||||
CHECK(out_float.at(0) == '{');
|
CHECK(out_float.at(0) == '{');
|
||||||
CHECK(json::from_bjdata(out_float) == j_float);
|
CHECK(json::from_bjdata(out_float) == j_float);
|
||||||
|
|
||||||
// a non-integer shape entry is likewise not treated as an ndarray
|
// a non-integer shape entry is likewise not treated as an ndarray
|
||||||
json const j_size = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", {"x"}}, {"_ArrayData_", {1}}});
|
json const j_size = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", {"x", 1}}, {"_ArrayData_", {1}}});
|
||||||
const auto out_size = json::to_bjdata(j_size);
|
const auto out_size = json::to_bjdata(j_size);
|
||||||
CHECK(out_size.at(0) == '{');
|
CHECK(out_size.at(0) == '{');
|
||||||
CHECK(json::from_bjdata(out_size) == j_size);
|
CHECK(json::from_bjdata(out_size) == j_size);
|
||||||
|
|
||||||
// a negative shape entry is not a usable dimension either
|
// a negative shape entry is not a usable dimension either
|
||||||
json const j_neg = json::parse(R"({"_ArrayType_":"uint8","_ArraySize_":[-1],"_ArrayData_":[1]})");
|
json const j_neg = json::parse(R"({"_ArrayType_":"uint8","_ArraySize_":[-1,1],"_ArrayData_":[1]})");
|
||||||
const auto out_neg = json::to_bjdata(j_neg);
|
const auto out_neg = json::to_bjdata(j_neg);
|
||||||
CHECK(out_neg.at(0) == '{');
|
CHECK(out_neg.at(0) == '{');
|
||||||
CHECK(json::from_bjdata(out_neg) == j_neg);
|
CHECK(json::from_bjdata(out_neg) == j_neg);
|
||||||
@@ -2657,14 +2657,14 @@ TEST_CASE("BJData")
|
|||||||
}
|
}
|
||||||
|
|
||||||
// negative values under a signed type behave the same way
|
// negative values under a signed type behave the same way
|
||||||
const auto from_neg = json::to_bjdata(json::parse(R"({"_ArrayType_":"int32","_ArraySize_":[2],"_ArrayData_":[-5,7]})"));
|
const auto from_neg = json::to_bjdata(json::parse(R"({"_ArrayType_":"int32","_ArraySize_":[2,1],"_ArrayData_":[-5,7]})"));
|
||||||
CHECK(from_neg.at(0) == '[');
|
CHECK(from_neg.at(0) == '[');
|
||||||
CHECK(from_neg == json::to_bjdata(json({{"_ArrayType_", "int32"}, {"_ArraySize_", {2}}, {"_ArrayData_", {-5, 7}}})));
|
CHECK(from_neg == json::to_bjdata(json({{"_ArrayType_", "int32"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {-5, 7}}})));
|
||||||
|
|
||||||
// and so do the floating point types
|
// and so do the floating point types
|
||||||
const auto from_float = json::to_bjdata(json::parse(R"({"_ArrayType_":"double","_ArraySize_":[2],"_ArrayData_":[1.5,2.5]})"));
|
const auto from_float = json::to_bjdata(json::parse(R"({"_ArrayType_":"double","_ArraySize_":[2,1],"_ArrayData_":[1.5,2.5]})"));
|
||||||
CHECK(from_float.at(0) == '[');
|
CHECK(from_float.at(0) == '[');
|
||||||
CHECK(from_float == json::to_bjdata(json({{"_ArrayType_", "double"}, {"_ArraySize_", {2}}, {"_ArrayData_", {1.5, 2.5}}})));
|
CHECK(from_float == json::to_bjdata(json({{"_ArrayType_", "double"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1.5, 2.5}}})));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("optimized ndarray (type and vector-size as 1D array)")
|
SECTION("optimized ndarray (type and vector-size as 1D array)")
|
||||||
@@ -2835,7 +2835,7 @@ TEST_CASE("BJData")
|
|||||||
// a single dimension that does not fit into std::size_t is
|
// a single dimension that does not fit into std::size_t is
|
||||||
// rejected for the same reason (only observable where
|
// rejected for the same reason (only observable where
|
||||||
// std::size_t is narrower than 64 bit)
|
// std::size_t is narrower than 64 bit)
|
||||||
json j_huge = json({{"_ArrayData_", json::array()}, {"_ArraySize_", {18446744073709551615ull}}, {"_ArrayType_", "uint8"}});
|
json j_huge = json({{"_ArrayData_", json::array()}, {"_ArraySize_", {18446744073709551615ull, 2}}, {"_ArrayType_", "uint8"}});
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(j_huge), true, true) == j_huge);
|
CHECK(json::from_bjdata(json::to_bjdata(j_huge), true, true) == j_huge);
|
||||||
|
|
||||||
// a well-formed ndarray is still encoded as one
|
// a well-formed ndarray is still encoded as one
|
||||||
@@ -2878,42 +2878,116 @@ TEST_CASE("BJData")
|
|||||||
// object encoding that still round-trips (see GitHub issue #5403)
|
// object encoding that still round-trips (see GitHub issue #5403)
|
||||||
|
|
||||||
// an unsigned element that does not fit uint8
|
// an unsigned element that does not fit uint8
|
||||||
json const j_uint8 = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", {2}}, {"_ArrayData_", {1, 256}}});
|
json const j_uint8 = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1, 256}}});
|
||||||
const auto out_uint8 = json::to_bjdata(j_uint8);
|
const auto out_uint8 = json::to_bjdata(j_uint8);
|
||||||
CHECK(out_uint8.at(0) == '{');
|
CHECK(out_uint8.at(0) == '{');
|
||||||
CHECK(json::from_bjdata(out_uint8) == j_uint8);
|
CHECK(json::from_bjdata(out_uint8) == j_uint8);
|
||||||
|
|
||||||
// a signed element that does not fit int8
|
// a signed element that does not fit int8
|
||||||
json const j_int8 = json({{"_ArrayType_", "int8"}, {"_ArraySize_", {2}}, {"_ArrayData_", {1, 200}}});
|
json const j_int8 = json({{"_ArrayType_", "int8"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1, 200}}});
|
||||||
const auto out_int8 = json::to_bjdata(j_int8);
|
const auto out_int8 = json::to_bjdata(j_int8);
|
||||||
CHECK(out_int8.at(0) == '{');
|
CHECK(out_int8.at(0) == '{');
|
||||||
CHECK(json::from_bjdata(out_int8) == j_int8);
|
CHECK(json::from_bjdata(out_int8) == j_int8);
|
||||||
|
|
||||||
// a negative element is likewise out of range for an
|
// a negative element is likewise out of range for an
|
||||||
// unsigned _ArrayType_
|
// unsigned _ArrayType_
|
||||||
json const j_uint16_neg = json({{"_ArrayType_", "uint16"}, {"_ArraySize_", {2}}, {"_ArrayData_", {1, -1}}});
|
json const j_uint16_neg = json({{"_ArrayType_", "uint16"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1, -1}}});
|
||||||
const auto out_uint16_neg = json::to_bjdata(j_uint16_neg);
|
const auto out_uint16_neg = json::to_bjdata(j_uint16_neg);
|
||||||
CHECK(out_uint16_neg.at(0) == '{');
|
CHECK(out_uint16_neg.at(0) == '{');
|
||||||
CHECK(json::from_bjdata(out_uint16_neg) == j_uint16_neg);
|
CHECK(json::from_bjdata(out_uint16_neg) == j_uint16_neg);
|
||||||
|
|
||||||
// a double element that overflows to infinity when narrowed
|
// a double element that overflows to infinity when narrowed
|
||||||
// to the "single" (float) precision named by _ArrayType_
|
// to the "single" (float) precision named by _ArrayType_
|
||||||
json const j_single = json({{"_ArrayType_", "single"}, {"_ArraySize_", {2}}, {"_ArrayData_", {1.5, 1e40}}});
|
json const j_single = json({{"_ArrayType_", "single"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1.5, 1e40}}});
|
||||||
const auto out_single = json::to_bjdata(j_single);
|
const auto out_single = json::to_bjdata(j_single);
|
||||||
CHECK(out_single.at(0) == '{');
|
CHECK(out_single.at(0) == '{');
|
||||||
CHECK(json::from_bjdata(out_single) == j_single);
|
CHECK(json::from_bjdata(out_single) == j_single);
|
||||||
|
|
||||||
// in-range boundary values still use the compact ndarray encoding
|
// in-range boundary values still use the compact ndarray encoding
|
||||||
json const j_uint8_ok = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", {2}}, {"_ArrayData_", {0, 255}}});
|
json const j_uint8_ok = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {0, 255}}});
|
||||||
CHECK(json::to_bjdata(j_uint8_ok) == std::vector<uint8_t>({'[', '$', 'U', '#', '[', 'i', 2, ']', 0, 255}));
|
CHECK(json::to_bjdata(j_uint8_ok) == std::vector<uint8_t>({'[', '$', 'U', '#', '[', 'i', 2, 'i', 1, ']', 0, 255}));
|
||||||
|
|
||||||
json const j_int8_ok = json({{"_ArrayType_", "int8"}, {"_ArraySize_", {2}}, {"_ArrayData_", {-128, 127}}});
|
json const j_int8_ok = json({{"_ArrayType_", "int8"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {-128, 127}}});
|
||||||
CHECK(json::to_bjdata(j_int8_ok) == std::vector<uint8_t>({'[', '$', 'i', '#', '[', 'i', 2, ']', 0x80, 0x7F}));
|
CHECK(json::to_bjdata(j_int8_ok) == std::vector<uint8_t>({'[', '$', 'i', '#', '[', 'i', 2, 'i', 1, ']', 0x80, 0x7F}));
|
||||||
|
|
||||||
json const j_single_ok = json({{"_ArrayType_", "single"}, {"_ArraySize_", {1}}, {"_ArrayData_", {1.5}}});
|
json const j_single_ok = json({{"_ArrayType_", "single"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1.5, -1.5}}});
|
||||||
const auto out_single_ok = json::to_bjdata(j_single_ok);
|
const auto out_single_ok = json::to_bjdata(j_single_ok);
|
||||||
CHECK(out_single_ok.at(0) == '[');
|
CHECK(out_single_ok.at(0) == '[');
|
||||||
CHECK(json::from_bjdata(out_single_ok) == json({1.5f}));
|
CHECK(json::from_bjdata(out_single_ok) == json({{"_ArrayType_", "single"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1.5f, -1.5f}}}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("ndarray that would not be read back as an annotated object stays as object")
|
||||||
|
{
|
||||||
|
// the reader only restores an annotated object from an ND-array
|
||||||
|
// with at least two non-zero dimensions that is not a 1xN row
|
||||||
|
// vector; any other shape is read back as a plain array. Writing
|
||||||
|
// such an object as an ND-array would drop its annotation, so it
|
||||||
|
// falls back to a plain object encoding that round-trips.
|
||||||
|
for (const char* text :
|
||||||
|
{
|
||||||
|
R"({"_ArrayType_":"int16","_ArraySize_":[],"_ArrayData_":[]})",
|
||||||
|
R"({"_ArrayType_":"int16","_ArraySize_":[2],"_ArrayData_":[1,2]})",
|
||||||
|
R"({"_ArrayType_":"int16","_ArraySize_":[1,2],"_ArrayData_":[1,2]})",
|
||||||
|
R"({"_ArrayType_":"int16","_ArraySize_":[0],"_ArrayData_":[]})",
|
||||||
|
R"({"_ArrayType_":"int16","_ArraySize_":[2,0],"_ArrayData_":[]})",
|
||||||
|
R"({"_ArrayType_":"int16","_ArraySize_":[0,2],"_ArrayData_":[]})"
|
||||||
|
})
|
||||||
|
{
|
||||||
|
CAPTURE(text);
|
||||||
|
const json j = json::parse(text);
|
||||||
|
for (const bool use_size :
|
||||||
|
{
|
||||||
|
false, true
|
||||||
|
})
|
||||||
|
{
|
||||||
|
const auto out = json::to_bjdata(j, use_size, use_size);
|
||||||
|
CHECK(out.at(0) == '{');
|
||||||
|
CHECK(json::from_bjdata(out) == j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// a genuine ND-array still uses the compact encoding and round-trips
|
||||||
|
const json j_2d = json::parse(R"({"_ArrayType_":"int16","_ArraySize_":[2,1],"_ArrayData_":[1,2]})");
|
||||||
|
const auto out_2d = json::to_bjdata(j_2d);
|
||||||
|
CHECK(out_2d.at(0) == '[');
|
||||||
|
CHECK(json::from_bjdata(out_2d) == j_2d);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("ndarray with non-array _ArrayData_ stays as object")
|
||||||
|
{
|
||||||
|
// the elements are written from _ArrayData_ as a flat list, so it
|
||||||
|
// has to be an array: null has size 0, any other scalar has size 1,
|
||||||
|
// and iterating an object visits its values, so each of these could
|
||||||
|
// match the dimensions and be encoded as an unrelated ND-array
|
||||||
|
for (const char* text :
|
||||||
|
{
|
||||||
|
R"({"_ArrayType_":"int16","_ArraySize_":[2,1],"_ArrayData_":null})",
|
||||||
|
R"({"_ArrayType_":"int16","_ArraySize_":[2,1],"_ArrayData_":{"a":1,"b":2}})",
|
||||||
|
R"({"_ArrayType_":"int16","_ArraySize_":[1],"_ArrayData_":5})",
|
||||||
|
R"({"_ArrayType_":"int16","_ArraySize_":[],"_ArrayData_":null})"
|
||||||
|
})
|
||||||
|
{
|
||||||
|
CAPTURE(text);
|
||||||
|
const json j = json::parse(text);
|
||||||
|
const auto out = json::to_bjdata(j);
|
||||||
|
CHECK(out.at(0) == '{');
|
||||||
|
CHECK(json::from_bjdata(out) == j);
|
||||||
|
}
|
||||||
|
|
||||||
|
// OSS-Fuzz issue 563659413: an empty binary _ArraySize_ is written
|
||||||
|
// as a plain object and read back as an empty array, after which
|
||||||
|
// the object with a null _ArrayData_ was encoded as an empty
|
||||||
|
// ND-array and re-read as [], so a second round trip lost the value
|
||||||
|
const std::vector<uint8_t> input =
|
||||||
|
{
|
||||||
|
'{', 'U', 11, '_', 'A', 'r', 'r', 'a', 'y', 'D', 'a', 't', 'a', '_', 'Z',
|
||||||
|
'U', 11, '_', 'A', 'r', 'r', 'a', 'y', 'T', 'y', 'p', 'e', '_', 'S', 'i', 5, 'i', 'n', 't', '1', '6',
|
||||||
|
'U', 11, '_', 'A', 'r', 'r', 'a', 'y', 'S', 'i', 'z', 'e', '_', '[', '$', 'B', '#', '[', ']', '}'
|
||||||
|
};
|
||||||
|
const json j1 = json::from_bjdata(input);
|
||||||
|
const json j2 = json::from_bjdata(json::to_bjdata(j1, false, false));
|
||||||
|
CHECK(j2 == json::parse(R"({"_ArrayType_":"int16","_ArraySize_":[],"_ArrayData_":null})"));
|
||||||
|
CHECK(json::from_bjdata(json::to_bjdata(j2, false, false)) == j2);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("ndarray with _ArrayType_ \"byte\" is gated by the BJData draft version")
|
SECTION("ndarray with _ArrayType_ \"byte\" is gated by the BJData draft version")
|
||||||
|
|||||||
@@ -0,0 +1,167 @@
|
|||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | JSON for Modern C++ (supporting code)
|
||||||
|
// | | |__ | | | | | | version 3.12.0
|
||||||
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include "doctest_compatibility.h"
|
||||||
|
|
||||||
|
// This file tests the opt-in JSON_BRACE_INIT_COPY_SEMANTICS, so it defines the
|
||||||
|
// macro itself rather than relying on a -D flag, and runs in every build.
|
||||||
|
#ifdef JSON_BRACE_INIT_COPY_SEMANTICS
|
||||||
|
#undef JSON_BRACE_INIT_COPY_SEMANTICS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define JSON_BRACE_INIT_COPY_SEMANTICS 1
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
using nlohmann::json;
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define STRINGIZE_EX(x) #x
|
||||||
|
#define STRINGIZE(x) STRINGIZE_EX(x)
|
||||||
|
|
||||||
|
TEST_CASE("JSON_BRACE_INIT_COPY_SEMANTICS")
|
||||||
|
{
|
||||||
|
SECTION("the macro is part of the ABI tag")
|
||||||
|
{
|
||||||
|
const std::string ns = STRINGIZE(NLOHMANN_JSON_NAMESPACE);
|
||||||
|
// other tags may come before it, e.g. json_abi_ldvcmp_bics
|
||||||
|
CHECK(ns.find("_bics") != std::string::npos);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("single-element brace initialization copies the element (#5074)")
|
||||||
|
{
|
||||||
|
json const j_obj = {{"key", "value"}, {"num", 42}};
|
||||||
|
json const j_arr = {1, 2, 3};
|
||||||
|
|
||||||
|
// object: brace init copies instead of wrapping
|
||||||
|
json const j1{j_obj};
|
||||||
|
CHECK(j1.is_object());
|
||||||
|
CHECK(j1 == j_obj);
|
||||||
|
|
||||||
|
// array: brace init copies instead of wrapping
|
||||||
|
json const j2{j_arr};
|
||||||
|
CHECK(j2.is_array());
|
||||||
|
CHECK(j2.size() == 3);
|
||||||
|
CHECK(j2 == j_arr);
|
||||||
|
|
||||||
|
// this applies to any single element, not only to JSON values
|
||||||
|
json const j3{true};
|
||||||
|
CHECK(j3.is_boolean());
|
||||||
|
|
||||||
|
json const j4{42};
|
||||||
|
CHECK(j4.is_number_integer());
|
||||||
|
|
||||||
|
json const j5 = {1};
|
||||||
|
CHECK(j5 == 1);
|
||||||
|
|
||||||
|
json const j6 = {"text"};
|
||||||
|
CHECK(j6 == "text");
|
||||||
|
|
||||||
|
json const j7 = {{1, 2}};
|
||||||
|
CHECK(j7 == json::array({1, 2}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("what the macro does not change")
|
||||||
|
{
|
||||||
|
// lists with more than one element are unaffected
|
||||||
|
json const j1 = {1, 2};
|
||||||
|
CHECK(j1.is_array());
|
||||||
|
CHECK(j1.size() == 2);
|
||||||
|
|
||||||
|
// a single [string, value] pair still describes an object
|
||||||
|
json const j2 = {{"key", "value"}};
|
||||||
|
CHECK(j2.is_object());
|
||||||
|
CHECK(j2["key"] == "value");
|
||||||
|
|
||||||
|
// json::array() always creates an array
|
||||||
|
json const j3 = json::array({1});
|
||||||
|
CHECK(j3.is_array());
|
||||||
|
CHECK(j3.size() == 1);
|
||||||
|
CHECK(j3[0] == 1);
|
||||||
|
|
||||||
|
json const j_obj = {{"key", "value"}};
|
||||||
|
json const j4 = json::array({j_obj});
|
||||||
|
CHECK(j4.is_array());
|
||||||
|
CHECK(j4.size() == 1);
|
||||||
|
CHECK(j4[0] == j_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("conversions build the same values as without the macro")
|
||||||
|
{
|
||||||
|
SECTION("one-element std::tuple")
|
||||||
|
{
|
||||||
|
json const j1 = std::tuple<int> {5};
|
||||||
|
CHECK(j1.dump() == "[5]");
|
||||||
|
CHECK(std::get<0>(j1.get<std::tuple<int>>()) == 5);
|
||||||
|
|
||||||
|
json const j2 = std::tuple<std::string> {"text"};
|
||||||
|
CHECK(j2.dump() == "[\"text\"]");
|
||||||
|
CHECK(std::get<0>(j2.get<std::tuple<std::string>>()) == "text");
|
||||||
|
|
||||||
|
json const j3 = std::tuple<json> {json::array({1, 2})};
|
||||||
|
CHECK(j3.dump() == "[[1,2]]");
|
||||||
|
|
||||||
|
// as without the macro, a [string, value] pair becomes an object
|
||||||
|
// member (see the known limitation documented for std::pair)
|
||||||
|
json const j4 = std::tuple<std::pair<std::string, int>> {{"a", 1}};
|
||||||
|
CHECK(j4.dump() == "{\"a\":1}");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("tuples with more elements")
|
||||||
|
{
|
||||||
|
json const j1 = std::tuple<int, std::string> {1, "a"};
|
||||||
|
CHECK(j1.dump() == "[1,\"a\"]");
|
||||||
|
|
||||||
|
json const j2 = std::tuple<> {};
|
||||||
|
CHECK(j2.dump() == "[]");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("one-element containers")
|
||||||
|
{
|
||||||
|
json const j1 = std::vector<int> {1};
|
||||||
|
CHECK(j1.dump() == "[1]");
|
||||||
|
CHECK(j1.get<std::vector<int>>() == std::vector<int> {1});
|
||||||
|
|
||||||
|
std::array<int, 1> const arr = {{1}};
|
||||||
|
json const j2 = arr;
|
||||||
|
CHECK(j2.dump() == "[1]");
|
||||||
|
|
||||||
|
json const j3 = std::list<std::string> {"a"};
|
||||||
|
CHECK(j3.dump() == "[\"a\"]");
|
||||||
|
|
||||||
|
json const j4 = std::map<std::string, int> {{"a", 1}};
|
||||||
|
CHECK(j4.dump() == "{\"a\":1}");
|
||||||
|
|
||||||
|
json const j5 = std::map<int, int> {{1, 2}};
|
||||||
|
CHECK(j5.dump() == "[[1,2]]");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::pair")
|
||||||
|
{
|
||||||
|
json const j = std::pair<int, int> {1, 2};
|
||||||
|
CHECK(j.dump() == "[1,2]");
|
||||||
|
CHECK((j.get<std::pair<int, int>>() == std::pair<int, int> {1, 2}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("items()")
|
||||||
|
{
|
||||||
|
json j_obj = {{"key", 1}};
|
||||||
|
for (const auto& el : j_obj.items())
|
||||||
|
{
|
||||||
|
json const j = el;
|
||||||
|
CHECK(j.dump() == "{\"key\":1}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+65
-1
@@ -49,7 +49,7 @@ using huge_binary_json = nlohmann::basic_json <
|
|||||||
// for *object keys* (e.g. "s" or "nested" below). Only the designated test
|
// for *object keys* (e.g. "s" or "nested" below). Only the designated test
|
||||||
// value is meant to lie about its size - if every huge_string_t (including
|
// value is meant to lie about its size - if every huge_string_t (including
|
||||||
// keys) reported a huge size, the running totals computed while walking the
|
// keys) reported a huge size, the running totals computed while walking the
|
||||||
// BSON document (see calc_bson_object_size & friends in binary_writer.hpp)
|
// BSON document (see calc_bson_sizes in binary_writer.hpp)
|
||||||
// would need more than 32 bits, and on platforms where std::size_t is only
|
// would need more than 32 bits, and on platforms where std::size_t is only
|
||||||
// 32 bits wide that arithmetic would silently wrap around, producing wrong
|
// 32 bits wide that arithmetic would silently wrap around, producing wrong
|
||||||
// (or even unguarded) lengths. The fake size is therefore opt-in via
|
// (or even unguarded) lengths. The fake size is therefore opt-in via
|
||||||
@@ -1697,3 +1697,67 @@ TEST_CASE("BSON roundtrips" * doctest::skip())
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("BSON: deeply nested values")
|
||||||
|
{
|
||||||
|
SECTION("documents and arrays round-trip at every depth")
|
||||||
|
{
|
||||||
|
// nested documents and arrays, with siblings on every level, so
|
||||||
|
// every length prefix covers entries of both kinds
|
||||||
|
json value = "leaf";
|
||||||
|
for (std::size_t depth = 0; depth <= 300; ++depth)
|
||||||
|
{
|
||||||
|
CAPTURE(depth);
|
||||||
|
const json document = {{"value", value}, {"n", depth}};
|
||||||
|
CHECK(json::from_bson(json::to_bson(document)) == document);
|
||||||
|
|
||||||
|
value = depth % 2 == 0 ? json{{"a", std::move(value)}, {"b", {1, "x"}}} :
|
||||||
|
json::array({std::move(value), depth, json::object()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("a key containing U+0000 is rejected before anything is written")
|
||||||
|
{
|
||||||
|
json value = json::object({{std::string("bad\0key", 7), 1}});
|
||||||
|
for (std::size_t depth = 0; depth < 200; ++depth)
|
||||||
|
{
|
||||||
|
value = json{{"a", {{"b", 1}}}, {"z", std::move(value)}};
|
||||||
|
}
|
||||||
|
std::vector<std::uint8_t> output;
|
||||||
|
CHECK_THROWS_AS(json::to_bson(value, output), json::out_of_range&);
|
||||||
|
CHECK(output.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("values nested too deeply for the call stack (#5392)")
|
||||||
|
{
|
||||||
|
// serializing recursed once per nesting level, and computed every
|
||||||
|
// nested document's length by walking everything below it again.
|
||||||
|
// The values are only parsed, serialized and walked, never copied or
|
||||||
|
// compared, since those recurse too.
|
||||||
|
const std::size_t depth = 100000;
|
||||||
|
for (const bool objects :
|
||||||
|
{
|
||||||
|
false, true
|
||||||
|
})
|
||||||
|
{
|
||||||
|
CAPTURE(objects);
|
||||||
|
std::string text = "{\"a\":";
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
text += objects ? "{\"a\":" : "[";
|
||||||
|
}
|
||||||
|
text += "1";
|
||||||
|
text.append(depth, objects ? '}' : ']');
|
||||||
|
text += "}";
|
||||||
|
|
||||||
|
const auto bson = json::to_bson(json::parse(text));
|
||||||
|
const auto result = json::from_bson(bson);
|
||||||
|
const json* p = &result.at("a");
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
p = objects ? &p->at("a") : &p->at(0);
|
||||||
|
}
|
||||||
|
CHECK(*p == 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -75,6 +75,72 @@ TEST_CASE("Better diagnostics with positions")
|
|||||||
CHECK(j.end_pos() == root.size());
|
CHECK(j.end_pos() == root.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("copying keeps the positions of nested values (#5387)")
|
||||||
|
{
|
||||||
|
// Values nested deeper than the copy constructor's descent bound are
|
||||||
|
// copied without the call stack, on a path that has to carry the
|
||||||
|
// positions over itself; shallower ones copy their containers, which
|
||||||
|
// bring the positions along. Both sides of the bound are checked here.
|
||||||
|
const auto check_copy = [](std::size_t depth, bool objects)
|
||||||
|
{
|
||||||
|
CAPTURE(depth)
|
||||||
|
CAPTURE(objects)
|
||||||
|
|
||||||
|
const std::string opening = objects ? R"({"a":)" : "[";
|
||||||
|
const std::string closing = objects ? "}" : "]";
|
||||||
|
|
||||||
|
std::string text;
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
text += opening;
|
||||||
|
}
|
||||||
|
text += "12";
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
text += closing;
|
||||||
|
}
|
||||||
|
|
||||||
|
const json original = json::parse(text);
|
||||||
|
const json copy(original); // NOLINT(performance-unnecessary-copy-initialization)
|
||||||
|
|
||||||
|
const json* o = &original;
|
||||||
|
const json* c = ©
|
||||||
|
for (std::size_t level = 0; level <= depth; ++level)
|
||||||
|
{
|
||||||
|
CAPTURE(level)
|
||||||
|
REQUIRE(c->start_pos() == o->start_pos());
|
||||||
|
REQUIRE(c->end_pos() == o->end_pos());
|
||||||
|
|
||||||
|
if (level < depth)
|
||||||
|
{
|
||||||
|
o = objects ? &o->at("a") : &o->at(0);
|
||||||
|
c = objects ? &c->at("a") : &c->at(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto check_arrays = [&check_copy](std::size_t depth)
|
||||||
|
{
|
||||||
|
check_copy(depth, false);
|
||||||
|
};
|
||||||
|
const auto check_objects = [&check_copy](std::size_t depth)
|
||||||
|
{
|
||||||
|
check_copy(depth, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
check_arrays(1);
|
||||||
|
check_arrays(127);
|
||||||
|
check_arrays(128);
|
||||||
|
check_arrays(129);
|
||||||
|
check_arrays(300);
|
||||||
|
|
||||||
|
check_objects(1);
|
||||||
|
check_objects(127);
|
||||||
|
check_objects(128);
|
||||||
|
check_objects(129);
|
||||||
|
check_objects(300);
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("JSON patch add to primitive parent (#4292)")
|
SECTION("JSON patch add to primitive parent (#4292)")
|
||||||
{
|
{
|
||||||
// the JSON Patch "add" target /foo/bar/baz has a string parent
|
// the JSON Patch "add" target /foo/bar/baz has a string parent
|
||||||
|
|||||||
@@ -274,6 +274,63 @@ TEST_CASE("Regression tests for extended diagnostics")
|
|||||||
CHECK(j1["string"] == "t");
|
CHECK(j1["string"] == "t");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("Regression test for issue #5387 - copying keeps the parents of nested values")
|
||||||
|
{
|
||||||
|
// A value nested deeper than the copy constructor's descent bound is
|
||||||
|
// copied without the call stack. Every container that path creates has
|
||||||
|
// to have the parents of its children set, or the JSON Pointer in the
|
||||||
|
// diagnostic is cut short.
|
||||||
|
const std::size_t depth = 300;
|
||||||
|
|
||||||
|
SECTION("objects")
|
||||||
|
{
|
||||||
|
json j = "not a number";
|
||||||
|
std::string pointer;
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
j = json{{"a", j}};
|
||||||
|
pointer += "/a";
|
||||||
|
}
|
||||||
|
|
||||||
|
json const copy(j); // NOLINT(performance-unnecessary-copy-initialization)
|
||||||
|
|
||||||
|
const json* inner = ©
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
inner = &inner->at("a");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string const expected = "[json.exception.type_error.302] (" + pointer + ") type must be number, but is string";
|
||||||
|
int i = 0;
|
||||||
|
CHECK_THROWS_WITH_AS(i = inner->get<int>(), expected.c_str(), json::type_error);
|
||||||
|
CHECK(i == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("arrays")
|
||||||
|
{
|
||||||
|
json j = "not a number";
|
||||||
|
std::string pointer;
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
j = json::array({j});
|
||||||
|
pointer += "/0";
|
||||||
|
}
|
||||||
|
|
||||||
|
json const copy(j); // NOLINT(performance-unnecessary-copy-initialization)
|
||||||
|
|
||||||
|
const json* inner = ©
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
inner = &inner->at(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string const expected = "[json.exception.type_error.302] (" + pointer + ") type must be number, but is string";
|
||||||
|
int i = 0;
|
||||||
|
CHECK_THROWS_WITH_AS(i = inner->get<int>(), expected.c_str(), json::type_error);
|
||||||
|
CHECK(i == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("Regression test - swap(array_t&)/swap(object_t&) must update JSON_DIAGNOSTICS parent pointers")
|
SECTION("Regression test - swap(array_t&)/swap(object_t&) must update JSON_DIAGNOSTICS parent pointers")
|
||||||
{
|
{
|
||||||
// swap(array_t&)
|
// swap(array_t&)
|
||||||
@@ -306,3 +363,52 @@ TEST_CASE("Regression tests for extended diagnostics")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Better diagnostics past the descent bound of update() and merge_patch()")
|
||||||
|
{
|
||||||
|
// Both merge objects nested more than detail::recursion_depth_limit()
|
||||||
|
// (128) levels deep without recursing; the values they add or replace
|
||||||
|
// there must still know their parents.
|
||||||
|
// The values are built rather than parsed, so that the expected messages
|
||||||
|
// carry no byte positions under JSON_DIAGNOSTIC_POSITIONS.
|
||||||
|
const std::size_t depth = 200;
|
||||||
|
json target = {{"x", 1}};
|
||||||
|
json patch = {{"y", 2}};
|
||||||
|
std::string path;
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
target = json{{"a", std::move(target)}};
|
||||||
|
patch = json{{"a", std::move(patch)}};
|
||||||
|
path += "/a";
|
||||||
|
}
|
||||||
|
const std::string expected_x = "[json.exception.type_error.304] (" + path + "/x) cannot use at() with number";
|
||||||
|
const std::string expected_y = "[json.exception.type_error.304] (" + path + "/y) cannot use at() with number";
|
||||||
|
|
||||||
|
SECTION("update()")
|
||||||
|
{
|
||||||
|
json j = target;
|
||||||
|
j.update(patch, true);
|
||||||
|
|
||||||
|
// walk down through const references, which leave m_parent alone
|
||||||
|
const json* p = &j;
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
p = &p->at("a");
|
||||||
|
}
|
||||||
|
CHECK_THROWS_WITH_AS(p->at("x").at(0), expected_x.c_str(), json::type_error);
|
||||||
|
CHECK_THROWS_WITH_AS(p->at("y").at(0), expected_y.c_str(), json::type_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("merge_patch()")
|
||||||
|
{
|
||||||
|
json j = target;
|
||||||
|
j.merge_patch(patch);
|
||||||
|
|
||||||
|
const json* p = &j;
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
p = &p->at("a");
|
||||||
|
}
|
||||||
|
CHECK_THROWS_WITH_AS(p->at("x").at(0), expected_x.c_str(), json::type_error);
|
||||||
|
CHECK_THROWS_WITH_AS(p->at("y").at(0), expected_y.c_str(), json::type_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,6 +13,78 @@ using json = nlohmann::json;
|
|||||||
using ordered_json = nlohmann::ordered_json;
|
using ordered_json = nlohmann::ordered_json;
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// how detail::hash defines the hash of an array or object: the seeds of the
|
||||||
|
// elements, combined in order. Recursive, so only usable on values nested a
|
||||||
|
// few hundred levels deep - which is exactly what is needed to check that the
|
||||||
|
// iterative path taken below detail::recursion_depth_limit() computes the same.
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
std::size_t reference_hash(const BasicJsonType& j)
|
||||||
|
{
|
||||||
|
using nlohmann::detail::combine;
|
||||||
|
using string_t = typename BasicJsonType::string_t;
|
||||||
|
|
||||||
|
if (!j.is_structured())
|
||||||
|
{
|
||||||
|
return std::hash<BasicJsonType> {}(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto seed = combine(static_cast<std::size_t>(j.type()), j.size());
|
||||||
|
for (const auto& element : j.items())
|
||||||
|
{
|
||||||
|
if (j.is_object())
|
||||||
|
{
|
||||||
|
seed = combine(seed, std::hash<string_t> {}(element.key()));
|
||||||
|
}
|
||||||
|
seed = combine(seed, reference_hash(element.value()));
|
||||||
|
}
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// a value nested `depth` levels deep, with siblings on every level
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
BasicJsonType nested(const std::size_t depth, const bool objects)
|
||||||
|
{
|
||||||
|
BasicJsonType value = "leaf";
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
if (objects)
|
||||||
|
{
|
||||||
|
value = BasicJsonType{{"before", i}, {"nested", std::move(value)}, {"after", {i, "x"}}};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = BasicJsonType::array({i, std::move(value), BasicJsonType::object({{"k", i}})});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string nested_text(const std::size_t depth, const bool objects)
|
||||||
|
{
|
||||||
|
std::string text;
|
||||||
|
if (objects)
|
||||||
|
{
|
||||||
|
text.reserve((6 * depth) + 1);
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
text += "{\"a\":";
|
||||||
|
}
|
||||||
|
text += "1";
|
||||||
|
text.append(depth, '}');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
text.assign(depth, '[');
|
||||||
|
text += "1";
|
||||||
|
text.append(depth, ']');
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
TEST_CASE("hash<nlohmann::json>")
|
TEST_CASE("hash<nlohmann::json>")
|
||||||
{
|
{
|
||||||
@@ -111,3 +183,44 @@ TEST_CASE("hash<nlohmann::ordered_json>")
|
|||||||
|
|
||||||
CHECK(hashes.size() == 21);
|
CHECK(hashes.size() == 21);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("hash of deeply nested values")
|
||||||
|
{
|
||||||
|
SECTION("hashing past the descent bound computes the same values")
|
||||||
|
{
|
||||||
|
// every depth on either side of where the iterative path takes over
|
||||||
|
for (std::size_t depth = 0; depth <= (2 * nlohmann::detail::recursion_depth_limit()) + 10; ++depth)
|
||||||
|
{
|
||||||
|
CAPTURE(depth);
|
||||||
|
const auto arrays = nested<json>(depth, false);
|
||||||
|
const auto objects = nested<json>(depth, true);
|
||||||
|
const auto ordered = nested<ordered_json>(depth, true);
|
||||||
|
CHECK(std::hash<json> {}(arrays) == reference_hash(arrays));
|
||||||
|
CHECK(std::hash<json> {}(objects) == reference_hash(objects));
|
||||||
|
CHECK(std::hash<ordered_json> {}(ordered) == reference_hash(ordered));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("values nested too deeply for the call stack (#5545)")
|
||||||
|
{
|
||||||
|
// recursing once per level used to exhaust the call stack here; the
|
||||||
|
// values are only parsed and hashed, never copied or compared, since
|
||||||
|
// those recurse as well
|
||||||
|
const std::size_t depth = 100000;
|
||||||
|
for (const bool objects :
|
||||||
|
{
|
||||||
|
false, true
|
||||||
|
})
|
||||||
|
{
|
||||||
|
CAPTURE(objects);
|
||||||
|
const auto text = nested_text(depth, objects);
|
||||||
|
const auto a = json::parse(text);
|
||||||
|
const auto b = json::parse(text);
|
||||||
|
CHECK(std::hash<json> {}(a) == std::hash<json> {}(b));
|
||||||
|
|
||||||
|
const auto c = ordered_json::parse(text);
|
||||||
|
const auto d = ordered_json::parse(text);
|
||||||
|
CHECK(std::hash<ordered_json> {}(c) == std::hash<ordered_json> {}(d));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
TEST_CASE("tests on very large JSONs")
|
TEST_CASE("tests on very large JSONs")
|
||||||
{
|
{
|
||||||
@@ -27,3 +28,153 @@ TEST_CASE("tests on very large JSONs")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
// Descend a chain of single-element containers and return the value at its end,
|
||||||
|
// reporting the number of levels traversed in @a depth.
|
||||||
|
//
|
||||||
|
// The values in the test case below are nested far deeper than the call stack
|
||||||
|
// can follow, so they must not be inspected with operator== or dump(): both are
|
||||||
|
// still recursive and would overflow the stack themselves.
|
||||||
|
const json* innermost_value(const json& j, std::size_t& depth)
|
||||||
|
{
|
||||||
|
const json* current = &j;
|
||||||
|
depth = 0;
|
||||||
|
|
||||||
|
while ((current->is_array() || current->is_object()) && !current->empty())
|
||||||
|
{
|
||||||
|
current = current->is_array()
|
||||||
|
? ¤t->front()
|
||||||
|
: ¤t->begin().value();
|
||||||
|
++depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("tests on deeply nested JSONs")
|
||||||
|
{
|
||||||
|
// deep enough to exhaust the call stack, but small enough to stay cheap:
|
||||||
|
// parsing is iterative, so building the values below costs little
|
||||||
|
const std::size_t depth = 100000;
|
||||||
|
|
||||||
|
SECTION("issue #5387 - stack overflow in the copy constructor")
|
||||||
|
{
|
||||||
|
SECTION("array")
|
||||||
|
{
|
||||||
|
const json j = json::parse(std::string(depth, '[') + '0' + std::string(depth, ']'));
|
||||||
|
|
||||||
|
const json copy(j); // NOLINT(performance-unnecessary-copy-initialization): the copy is what is tested
|
||||||
|
|
||||||
|
std::size_t copy_depth = 0;
|
||||||
|
CHECK(*innermost_value(copy, copy_depth) == 0);
|
||||||
|
CHECK(copy_depth == depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("object")
|
||||||
|
{
|
||||||
|
std::string s;
|
||||||
|
s.reserve((6 * depth) + 1);
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
s += "{\"a\":";
|
||||||
|
}
|
||||||
|
s += '1';
|
||||||
|
s.append(depth, '}');
|
||||||
|
|
||||||
|
const json j = json::parse(s);
|
||||||
|
|
||||||
|
const json copy(j); // NOLINT(performance-unnecessary-copy-initialization): the copy is what is tested
|
||||||
|
|
||||||
|
std::size_t copy_depth = 0;
|
||||||
|
CHECK(*innermost_value(copy, copy_depth) == 1);
|
||||||
|
CHECK(copy_depth == depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("copy assignment")
|
||||||
|
{
|
||||||
|
// operator=(basic_json) takes its argument by value, so the deep
|
||||||
|
// copy happens in the copy constructor
|
||||||
|
const json j = json::parse(std::string(depth, '[') + '0' + std::string(depth, ']'));
|
||||||
|
|
||||||
|
json target;
|
||||||
|
target = j;
|
||||||
|
|
||||||
|
std::size_t target_depth = 0;
|
||||||
|
CHECK(*innermost_value(target, target_depth) == 0);
|
||||||
|
CHECK(target_depth == depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("depths around the bound of the recursive descent")
|
||||||
|
{
|
||||||
|
// The copy constructor descends into a bounded number of levels and
|
||||||
|
// completes whatever is below that without the call stack. Cover
|
||||||
|
// every depth around that bound, so that the two ways of copying
|
||||||
|
// are known to meet cleanly - wherever the bound is set.
|
||||||
|
for (std::size_t d = 1; d <= 300; ++d)
|
||||||
|
{
|
||||||
|
CAPTURE(d);
|
||||||
|
|
||||||
|
const json array = json::parse(std::string(d, '[') + '0' + std::string(d, ']'));
|
||||||
|
const json array_copy(array); // NOLINT(performance-unnecessary-copy-initialization): the copy is what is tested
|
||||||
|
std::size_t array_depth = 0;
|
||||||
|
CHECK(*innermost_value(array_copy, array_depth) == 0);
|
||||||
|
CHECK(array_depth == d);
|
||||||
|
|
||||||
|
std::string object_text;
|
||||||
|
for (std::size_t i = 0; i < d; ++i)
|
||||||
|
{
|
||||||
|
object_text += "{\"a\":";
|
||||||
|
}
|
||||||
|
object_text += '1';
|
||||||
|
object_text.append(d, '}');
|
||||||
|
|
||||||
|
const json object = json::parse(object_text);
|
||||||
|
const json object_copy(object); // NOLINT(performance-unnecessary-copy-initialization): the copy is what is tested
|
||||||
|
std::size_t object_depth = 0;
|
||||||
|
CHECK(*innermost_value(object_copy, object_depth) == 1);
|
||||||
|
CHECK(object_depth == d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("a value that is deep in one place only")
|
||||||
|
{
|
||||||
|
json j = json::object();
|
||||||
|
j["shallow"] = 1;
|
||||||
|
j["deep"] = json::parse(std::string(depth, '[') + '0' + std::string(depth, ']'));
|
||||||
|
j["also_shallow"] = json::array({1, 2, 3});
|
||||||
|
|
||||||
|
const json copy(j);
|
||||||
|
|
||||||
|
CHECK(copy["shallow"] == 1);
|
||||||
|
CHECK(copy["also_shallow"] == json::array({1, 2, 3}));
|
||||||
|
|
||||||
|
std::size_t deep_depth = 0;
|
||||||
|
CHECK(*innermost_value(copy["deep"], deep_depth) == 0);
|
||||||
|
CHECK(deep_depth == depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("the copy is independent of the original")
|
||||||
|
{
|
||||||
|
const json j = json::parse(std::string(depth, '[') + '0' + std::string(depth, ']'));
|
||||||
|
|
||||||
|
json copy(j);
|
||||||
|
|
||||||
|
// reach the innermost value without recursing and replace it
|
||||||
|
json* current = ©
|
||||||
|
while (current->is_array() && !current->empty())
|
||||||
|
{
|
||||||
|
current = ¤t->front();
|
||||||
|
}
|
||||||
|
*current = 42;
|
||||||
|
|
||||||
|
std::size_t unused = 0;
|
||||||
|
CHECK(*innermost_value(copy, unused) == 42);
|
||||||
|
CHECK(*innermost_value(j, unused) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,60 @@ using nlohmann::json;
|
|||||||
using namespace nlohmann::literals; // NOLINT(google-build-using-namespace)
|
using namespace nlohmann::literals; // NOLINT(google-build-using-namespace)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// RFC 7396's MergePatch, written recursively as in the RFC; only usable on
|
||||||
|
// values nested a few hundred levels deep
|
||||||
|
void reference_merge_patch(json& target, const json& patch)
|
||||||
|
{
|
||||||
|
if (!patch.is_object())
|
||||||
|
{
|
||||||
|
target = patch;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!target.is_object())
|
||||||
|
{
|
||||||
|
target = json::object();
|
||||||
|
}
|
||||||
|
for (auto it = patch.begin(); it != patch.end(); ++it)
|
||||||
|
{
|
||||||
|
if (it.value().is_null())
|
||||||
|
{
|
||||||
|
target.erase(it.key());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reference_merge_patch(target[it.key()], it.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// objects nested `depth` levels deep under the key "a", with members that
|
||||||
|
// differ by `variant` on the way down
|
||||||
|
std::string nested_objects(const std::size_t depth, const int variant)
|
||||||
|
{
|
||||||
|
std::string text;
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
text += "{";
|
||||||
|
if ((i + static_cast<std::size_t>(variant)) % 3 == 0)
|
||||||
|
{
|
||||||
|
text += "\"s" + std::to_string(variant) + "\":" + std::to_string(i) + ",";
|
||||||
|
}
|
||||||
|
if (variant == 2 && i % 5 == 0)
|
||||||
|
{
|
||||||
|
text += "\"s0\":null,";
|
||||||
|
}
|
||||||
|
text += "\"a\":";
|
||||||
|
}
|
||||||
|
text += variant == 1 ? "{\"x\":1,\"y\":null}" : "{\"y\":2}";
|
||||||
|
text.append(depth, '}');
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
TEST_CASE("JSON Merge Patch")
|
TEST_CASE("JSON Merge Patch")
|
||||||
{
|
{
|
||||||
SECTION("examples from RFC 7396")
|
SECTION("examples from RFC 7396")
|
||||||
@@ -242,3 +296,52 @@ TEST_CASE("JSON Merge Patch")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("JSON Merge Patch on deeply nested values")
|
||||||
|
{
|
||||||
|
SECTION("patching past the descent bound gives the same result")
|
||||||
|
{
|
||||||
|
// every depth on either side of where the iterative version takes
|
||||||
|
// over (detail::recursion_depth_limit(), 128)
|
||||||
|
for (std::size_t depth = 0; depth <= 300; ++depth)
|
||||||
|
{
|
||||||
|
CAPTURE(depth);
|
||||||
|
for (int variant = 0; variant < 3; ++variant)
|
||||||
|
{
|
||||||
|
CAPTURE(variant);
|
||||||
|
const json patch = json::parse(nested_objects(depth, variant));
|
||||||
|
|
||||||
|
json result = json::parse(nested_objects(depth, (variant + 1) % 3));
|
||||||
|
json expected = result;
|
||||||
|
result.merge_patch(patch);
|
||||||
|
reference_merge_patch(expected, patch);
|
||||||
|
CHECK(result == expected);
|
||||||
|
|
||||||
|
// a target that is not an object, and an empty one
|
||||||
|
json from_null;
|
||||||
|
from_null.merge_patch(patch);
|
||||||
|
json expected_from_null;
|
||||||
|
reference_merge_patch(expected_from_null, patch);
|
||||||
|
CHECK(from_null == expected_from_null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("patches nested too deeply for the call stack (#5393)")
|
||||||
|
{
|
||||||
|
// applying a patch used to recurse once per nesting level. The result
|
||||||
|
// is only walked, never copied or compared, since those recurse too.
|
||||||
|
const std::size_t depth = 100000;
|
||||||
|
json target = json::parse(nested_objects(depth, 0));
|
||||||
|
target.merge_patch(json::parse(nested_objects(depth, 1)));
|
||||||
|
|
||||||
|
const json* p = ⌖
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
p = &p->at("a");
|
||||||
|
}
|
||||||
|
// {"y":2} patched with {"x":1,"y":null}
|
||||||
|
CHECK(p->size() == 1);
|
||||||
|
CHECK(p->at("x") == 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,53 @@
|
|||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// update(source, true) as documented, written recursively; only usable on
|
||||||
|
// values nested a few hundred levels deep
|
||||||
|
void reference_update(json& target, const json& source)
|
||||||
|
{
|
||||||
|
for (auto it = source.begin(); it != source.end(); ++it)
|
||||||
|
{
|
||||||
|
const auto existing = target.find(it.key());
|
||||||
|
if (it.value().is_object() && existing != target.end() && existing->is_object())
|
||||||
|
{
|
||||||
|
reference_update(*existing, it.value());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
target[it.key()] = it.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// objects nested `depth` levels deep under the key "a", with members that
|
||||||
|
// differ by `variant` on the way down
|
||||||
|
std::string nested_objects(const std::size_t depth, const int variant)
|
||||||
|
{
|
||||||
|
std::string text;
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
text += "{";
|
||||||
|
if ((i + static_cast<std::size_t>(variant)) % 3 == 0)
|
||||||
|
{
|
||||||
|
text += "\"s" + std::to_string(variant) + "\":" + std::to_string(i) + ",";
|
||||||
|
}
|
||||||
|
if (variant == 2 && i % 5 == 0)
|
||||||
|
{
|
||||||
|
// an object replacing a primitive, which is not merged
|
||||||
|
text += "\"s0\":{\"o\":1},";
|
||||||
|
}
|
||||||
|
text += "\"a\":";
|
||||||
|
}
|
||||||
|
text += variant == 1 ? "{\"x\":1}" : "{\"y\":2}";
|
||||||
|
text.append(depth, '}');
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
TEST_CASE("modifiers")
|
TEST_CASE("modifiers")
|
||||||
{
|
{
|
||||||
SECTION("clear()")
|
SECTION("clear()")
|
||||||
@@ -988,3 +1035,44 @@ TEST_CASE("modifiers")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("update() on deeply nested values")
|
||||||
|
{
|
||||||
|
SECTION("merging past the descent bound gives the same result")
|
||||||
|
{
|
||||||
|
// every depth on either side of where the iterative version takes
|
||||||
|
// over (detail::recursion_depth_limit(), 128)
|
||||||
|
for (std::size_t depth = 0; depth <= 300; ++depth)
|
||||||
|
{
|
||||||
|
CAPTURE(depth);
|
||||||
|
for (int variant = 0; variant < 3; ++variant)
|
||||||
|
{
|
||||||
|
CAPTURE(variant);
|
||||||
|
const json source = json::parse(nested_objects(depth, variant));
|
||||||
|
json result = json::parse(nested_objects(depth, (variant + 1) % 3));
|
||||||
|
json expected = result;
|
||||||
|
result.update(source, true);
|
||||||
|
reference_update(expected, source);
|
||||||
|
CHECK(result == expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("objects nested too deeply for the call stack (#5545)")
|
||||||
|
{
|
||||||
|
// merging used to recurse once per nesting level. The result is only
|
||||||
|
// walked, never copied or compared, since those recurse too.
|
||||||
|
const std::size_t depth = 100000;
|
||||||
|
json target = json::parse(nested_objects(depth, 0));
|
||||||
|
target.update(json::parse(nested_objects(depth, 1)), true);
|
||||||
|
|
||||||
|
const json* p = ⌖
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
p = &p->at("a");
|
||||||
|
}
|
||||||
|
CHECK(p->size() == 2);
|
||||||
|
CHECK(p->at("x") == 1);
|
||||||
|
CHECK(p->at("y") == 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -82,6 +82,40 @@ TEST_CASE("regression test for issue #3732 - iteration_proxy_value<iter_impl<ord
|
|||||||
static_cast<void>(fn);
|
static_cast<void>(fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("copying an ordered_json with nested values")
|
||||||
|
{
|
||||||
|
// ordered_map is backed by a vector, so copying an object that has
|
||||||
|
// structured values takes a different route than copying a std::map-backed
|
||||||
|
// one; see https://github.com/nlohmann/json/issues/5387
|
||||||
|
ordered_json oj;
|
||||||
|
oj["z"] = 1;
|
||||||
|
oj["a"]["y"] = 2;
|
||||||
|
oj["a"]["b"]["x"] = 3;
|
||||||
|
oj["m"] = {1, 2, {{"w", 4}}};
|
||||||
|
|
||||||
|
const ordered_json copy(oj);
|
||||||
|
|
||||||
|
SECTION("the copy is equal to the original")
|
||||||
|
{
|
||||||
|
CHECK(copy == oj);
|
||||||
|
CHECK(copy.dump() == oj.dump());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("the key order is preserved at every level")
|
||||||
|
{
|
||||||
|
CHECK(copy.dump() == R"({"z":1,"a":{"y":2,"b":{"x":3}},"m":[1,2,{"w":4}]})");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("the copy is independent of the original")
|
||||||
|
{
|
||||||
|
ordered_json mutated(oj);
|
||||||
|
mutated["a"]["b"]["x"] = 99;
|
||||||
|
|
||||||
|
CHECK(oj["a"]["b"]["x"] == 3);
|
||||||
|
CHECK(mutated["a"]["b"]["x"] == 99);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("regression test - diff() must account for ordered_json member order")
|
TEST_CASE("regression test - diff() must account for ordered_json member order")
|
||||||
{
|
{
|
||||||
SECTION("pure reorder, no value changes")
|
SECTION("pure reorder, no value changes")
|
||||||
|
|||||||
@@ -658,33 +658,6 @@ TEST_CASE("regression test #5074 - portable workaround for single-element brace
|
|||||||
CHECK(j[0] == j_obj);
|
CHECK(j[0] == j_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(JSON_BRACE_INIT_COPY_SEMANTICS) && (JSON_BRACE_INIT_COPY_SEMANTICS == 1)
|
|
||||||
TEST_CASE("regression test #5074 - single-element brace init with JSON_BRACE_INIT_COPY_SEMANTICS")
|
|
||||||
{
|
|
||||||
// with JSON_BRACE_INIT_COPY_SEMANTICS: single-element brace init copies/moves
|
|
||||||
json const j_obj = {{"key", "value"}, {"num", 42}};
|
|
||||||
json const j_arr = {1, 2, 3};
|
|
||||||
|
|
||||||
// object: brace init copies instead of wrapping
|
|
||||||
json const j1{j_obj};
|
|
||||||
CHECK(j1.is_object());
|
|
||||||
CHECK(j1 == j_obj);
|
|
||||||
|
|
||||||
// array: brace init copies instead of wrapping
|
|
||||||
json const j2{j_arr};
|
|
||||||
CHECK(j2.is_array());
|
|
||||||
CHECK(j2.size() == 3);
|
|
||||||
CHECK(j2 == j_arr);
|
|
||||||
|
|
||||||
// primitives still work as initializer lists
|
|
||||||
json const j3{true};
|
|
||||||
CHECK(j3.is_boolean());
|
|
||||||
|
|
||||||
json const j4{42};
|
|
||||||
CHECK(j4.is_number_integer());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct Example_5122
|
struct Example_5122
|
||||||
{
|
{
|
||||||
float b = 2;
|
float b = 2;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
namespaces = ['nlohmann']
|
namespaces = ['nlohmann']
|
||||||
abi_prefix = 'json_abi'
|
abi_prefix = 'json_abi'
|
||||||
abi_tags = ['_diag', '_ldvcmp']
|
abi_tags = ['_diag', '_ldvcmp', '_dp', '_bics']
|
||||||
version = '_v' + args.version.replace('.', '_')
|
version = '_v' + args.version.replace('.', '_')
|
||||||
inline_namespaces = []
|
inline_namespaces = []
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,10 @@ int main() {
|
|||||||
`serve_header.py` will try to read a configuration file `serve_header.yml` in the top level or project root directory, and will fall back on built-in defaults if the file cannot be read.
|
`serve_header.py` will try to read a configuration file `serve_header.yml` in the top level or project root directory, and will fall back on built-in defaults if the file cannot be read.
|
||||||
An annotated example configuration can be found in `tools/serve_header/serve_header.yml.example`.
|
An annotated example configuration can be found in `tools/serve_header/serve_header.yml.example`.
|
||||||
|
|
||||||
|
By default, the server listens on `localhost` only, and only web pages from Compiler Explorer (`https://godbolt.org` and `https://compiler-explorer.com`) may read the header.
|
||||||
|
Set `bind` to serve other machines as well; anyone who can reach the server can then trigger `make` runs in your working trees.
|
||||||
|
Set `cors_origins` to allow other web pages.
|
||||||
|
|
||||||
## Serving `json.hpp` from multiple project directory instances or working trees
|
## Serving `json.hpp` from multiple project directory instances or working trees
|
||||||
|
|
||||||
`serve_header.py` was designed with the goal of supporting multiple project roots or working trees at the same time.
|
`serve_header.py` was designed with the goal of supporting multiple project roots or working trees at the same time.
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ HEADER = 'json.hpp'
|
|||||||
|
|
||||||
DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'
|
DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'
|
||||||
|
|
||||||
|
# origins whose pages may read the served header from a browser; Compiler
|
||||||
|
# Explorer downloads #include <https://...> headers client-side
|
||||||
|
DEFAULT_CORS_ORIGINS = ['https://godbolt.org', 'https://compiler-explorer.com']
|
||||||
|
|
||||||
JSON_VERSION_RE = re.compile(r'\s*#\s*define\s+NLOHMANN_JSON_VERSION_MAJOR\s+')
|
JSON_VERSION_RE = re.compile(r'\s*#\s*define\s+NLOHMANN_JSON_VERSION_MAJOR\s+')
|
||||||
|
|
||||||
class ExitHandler(logging.StreamHandler):
|
class ExitHandler(logging.StreamHandler):
|
||||||
@@ -247,6 +251,8 @@ class WorkTrees(FileSystemEventHandler):
|
|||||||
self.observer.join()
|
self.observer.join()
|
||||||
|
|
||||||
class HeaderRequestHandler(SimpleHTTPRequestHandler): # lgtm[py/missing-call-to-init]
|
class HeaderRequestHandler(SimpleHTTPRequestHandler): # lgtm[py/missing-call-to-init]
|
||||||
|
cors_origins = DEFAULT_CORS_ORIGINS
|
||||||
|
|
||||||
def __init__(self, request, client_address, server):
|
def __init__(self, request, client_address, server):
|
||||||
"""."""
|
"""."""
|
||||||
self.worktrees = server.worktrees
|
self.worktrees = server.worktrees
|
||||||
@@ -310,8 +316,11 @@ class HeaderRequestHandler(SimpleHTTPRequestHandler): # lgtm[py/missing-call-to-
|
|||||||
|
|
||||||
# set content length
|
# set content length
|
||||||
super().send_header('Content-Length', length)
|
super().send_header('Content-Length', length)
|
||||||
# CORS header
|
# CORS header; only for the configured origins
|
||||||
self.send_header('Access-Control-Allow-Origin', '*')
|
origin = self.headers.get('Origin')
|
||||||
|
if origin in self.cors_origins:
|
||||||
|
self.send_header('Access-Control-Allow-Origin', origin)
|
||||||
|
self.send_header('Vary', 'Origin')
|
||||||
# prevent caching
|
# prevent caching
|
||||||
self.send_header('Cache-Control', 'no-cache, no-store, must-revalidate')
|
self.send_header('Cache-Control', 'no-cache, no-store, must-revalidate')
|
||||||
self.send_header('Pragma', 'no-cache')
|
self.send_header('Pragma', 'no-cache')
|
||||||
@@ -383,8 +392,15 @@ if __name__ == '__main__':
|
|||||||
# find and monitor working trees
|
# find and monitor working trees
|
||||||
worktrees = WorkTrees(config.get('root', '.'))
|
worktrees = WorkTrees(config.get('root', '.'))
|
||||||
|
|
||||||
# start web server
|
# origins allowed to read the header from a browser
|
||||||
infos = socket.getaddrinfo(config.get('bind', None), config.get('port', 8443),
|
cors_origins = config.get('cors_origins', DEFAULT_CORS_ORIGINS)
|
||||||
|
if isinstance(cors_origins, str):
|
||||||
|
cors_origins = [cors_origins]
|
||||||
|
HeaderRequestHandler.cors_origins = cors_origins
|
||||||
|
|
||||||
|
# start web server; only reachable from this machine unless configured
|
||||||
|
# otherwise (bind: null listens on all interfaces)
|
||||||
|
infos = socket.getaddrinfo(config.get('bind', 'localhost'), config.get('port', 8443),
|
||||||
type=socket.SOCK_STREAM, flags=socket.AI_PASSIVE)
|
type=socket.SOCK_STREAM, flags=socket.AI_PASSIVE)
|
||||||
DualStackServer.address_family = infos[0][0]
|
DualStackServer.address_family = infos[0][0]
|
||||||
HeaderRequestHandler.protocol_version = 'HTTP/1.0'
|
HeaderRequestHandler.protocol_version = 'HTTP/1.0'
|
||||||
|
|||||||
@@ -10,6 +10,13 @@
|
|||||||
# cert_file: localhost.pem
|
# cert_file: localhost.pem
|
||||||
# key_file: localhost-key.pem
|
# key_file: localhost-key.pem
|
||||||
|
|
||||||
# address and port for the server to listen on
|
# address and port for the server to listen on; by default, only this machine
|
||||||
# bind: null
|
# can connect. Binding to a network address, or to null for all interfaces,
|
||||||
|
# lets other machines connect, and every request runs make in a working tree.
|
||||||
|
# bind: localhost
|
||||||
# port: 8443
|
# port: 8443
|
||||||
|
|
||||||
|
# origins whose web pages may read the header (CORS)
|
||||||
|
# cors_origins:
|
||||||
|
# - https://godbolt.org
|
||||||
|
# - https://compiler-explorer.com
|
||||||
|
|||||||
Reference in New Issue
Block a user