mirror of
https://github.com/nlohmann/json.git
synced 2026-10-06 22:47:13 +00:00
Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d26cbef976 | ||
|
|
0adb7783a4 | ||
|
|
4303ba674d | ||
|
|
17842694f6 | ||
|
|
953d74ddcd | ||
|
|
5ecb704f6b | ||
|
|
0490778fc3 | ||
|
|
0a365865f9 | ||
|
|
5379e04ce4 | ||
|
|
8a142bc436 | ||
|
|
23d3b373e1 | ||
|
|
c5a7a4b46d | ||
|
|
4f69be80d6 | ||
|
|
8c1f60a45e | ||
|
|
6b4b825af2 |
No files matched your search
@@ -16,6 +16,9 @@ only_commits:
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
# The Visual Studio 2017 jobs compile everything with /std:c++17, so they
|
||||
# only build the C++17 variant of each test, split into two jobs each to
|
||||
# stay below AppVeyor's 60-minute limit per job.
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
configuration: Debug
|
||||
platform: x86
|
||||
@@ -34,7 +37,13 @@ environment:
|
||||
configuration: Release
|
||||
platform: x86
|
||||
CXX_FLAGS: "/permissive- /std:c++17 /utf-8 /W4 /WX"
|
||||
CMAKE_OPTIONS: ""
|
||||
CMAKE_OPTIONS: "-DJSON_TestStandards=17 -DJSON_TestShard=0/2"
|
||||
GENERATOR: Visual Studio 15 2017
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
configuration: Release
|
||||
platform: x86
|
||||
CXX_FLAGS: "/permissive- /std:c++17 /utf-8 /W4 /WX"
|
||||
CMAKE_OPTIONS: "-DJSON_TestStandards=17 -DJSON_TestShard=1/2"
|
||||
GENERATOR: Visual Studio 15 2017
|
||||
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
@@ -55,7 +64,13 @@ environment:
|
||||
configuration: Release
|
||||
platform: x64
|
||||
CXX_FLAGS: "/permissive- /std:c++17 /Zc:__cplusplus /utf-8 /W4 /WX"
|
||||
CMAKE_OPTIONS: ""
|
||||
CMAKE_OPTIONS: "-DJSON_TestStandards=17 -DJSON_TestShard=0/2"
|
||||
GENERATOR: Visual Studio 15 2017
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
configuration: Release
|
||||
platform: x64
|
||||
CXX_FLAGS: "/permissive- /std:c++17 /Zc:__cplusplus /utf-8 /W4 /WX"
|
||||
CMAKE_OPTIONS: "-DJSON_TestStandards=17 -DJSON_TestShard=1/2"
|
||||
GENERATOR: Visual Studio 15 2017
|
||||
|
||||
init:
|
||||
@@ -66,7 +81,7 @@ install:
|
||||
- if "%platform%"=="x86" set GENERATOR_PLATFORM=Win32
|
||||
|
||||
before_build:
|
||||
- cmake . -G "%GENERATOR%" -A "%GENERATOR_PLATFORM%" -DCMAKE_CXX_FLAGS="%CXX_FLAGS%" -DCMAKE_IGNORE_PATH="C:/Program Files/Git/usr/bin" -DJSON_BuildTests=On "%CMAKE_OPTIONS%"
|
||||
- cmake . -G "%GENERATOR%" -A "%GENERATOR_PLATFORM%" -DCMAKE_CXX_FLAGS="%CXX_FLAGS%" -DCMAKE_IGNORE_PATH="C:/Program Files/Git/usr/bin" -DJSON_BuildTests=On %CMAKE_OPTIONS%
|
||||
|
||||
build_script:
|
||||
- cmake --build . --config "%configuration%" --parallel 2
|
||||
|
||||
+3
-4
@@ -51,13 +51,12 @@ labels:
|
||||
- "include/nlohmann/detail/view/.*"
|
||||
- "single_include/nlohmann/json_view\\.hpp"
|
||||
- "tests/src/unit-json_view.*"
|
||||
- "tests/src/fuzzer-(parse_json_view|json_view_image)\\.cpp"
|
||||
- "tests/benchmarks/json_view/.*"
|
||||
- "tests/src/fuzzer-parse_json_view\\.cpp"
|
||||
- "tools/amalgamate/config_json_view\\.json"
|
||||
- "docs/mkdocs/docs/features/json_view\\.md"
|
||||
- "docs/mkdocs/docs/api/basic_json_(document|view)/.*"
|
||||
- "docs/mkdocs/docs/api/(ordered_)?json_(editable_)?(document|view)\\.md"
|
||||
- "docs/mkdocs/docs/examples/(basic_json_(document|view)__|(ordered_)?json_(editable_)?(document|view)).*"
|
||||
- "docs/mkdocs/docs/api/(ordered_)?json_(document|view)\\.md"
|
||||
- "docs/mkdocs/docs/examples/(basic_json_(document|view)__|(ordered_)?json_(document|view)).*"
|
||||
- "tests/benchmarks/src/benchmarks_view\\.cpp"
|
||||
|
||||
- label: "aspect: json_view"
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
name: "json_view benchmarks"
|
||||
|
||||
# On demand only: runs the comparison of json_view with yyjson, simdjson, and
|
||||
# Boost.JSON (tests/benchmarks/json_view/compare.py) on GitHub-hosted runners,
|
||||
# for numbers from x86-64 and AArch64 Linux. It runs when started by hand, or
|
||||
# when a pull request gets the label "benchmark" (on both architectures, with
|
||||
# GCC and the default settings). Shared runners are noisy: the results show
|
||||
# where json_view stands, but published numbers need a quiet machine (see
|
||||
# tests/benchmarks/json_view/README.md).
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
runner:
|
||||
description: "Runner image"
|
||||
type: choice
|
||||
options:
|
||||
- ubuntu-24.04
|
||||
- ubuntu-24.04-arm
|
||||
default: ubuntu-24.04
|
||||
compiler:
|
||||
description: "Compiler"
|
||||
type: choice
|
||||
options:
|
||||
- g++
|
||||
- clang++
|
||||
default: g++
|
||||
native:
|
||||
description: "Compile for the runner's CPU (-march=native)"
|
||||
type: boolean
|
||||
default: false
|
||||
rounds:
|
||||
description: "Rounds of bench_view"
|
||||
type: number
|
||||
default: 30
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
compare:
|
||||
if: github.event_name == 'workflow_dispatch' || github.event.label.name == 'benchmark'
|
||||
strategy:
|
||||
matrix:
|
||||
runner: ${{ fromJSON(github.event_name == 'workflow_dispatch' && format('["{0}"]', inputs.runner) || '["ubuntu-24.04", "ubuntu-24.04-arm"]') }}
|
||||
runs-on: ${{ matrix.runner }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Download test data
|
||||
run: |
|
||||
cmake -S . -B build -DJSON_BuildTests=On
|
||||
cmake --build build --target download_test_data
|
||||
|
||||
- name: Run the comparison
|
||||
env:
|
||||
CXX: ${{ inputs.compiler || 'g++' }}
|
||||
CC: ${{ inputs.compiler == 'clang++' && 'clang' || 'gcc' }}
|
||||
ROUNDS: ${{ inputs.rounds || 30 }}
|
||||
NATIVE: ${{ inputs.native && '--native' || '' }}
|
||||
run: python3 tests/benchmarks/json_view/compare.py --data build/test_files --download --rounds "$ROUNDS" $NATIVE
|
||||
|
||||
- name: Summary
|
||||
run: cat tests/benchmarks/json_view/results/*.md >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: json_view-benchmarks-${{ matrix.runner }}-${{ inputs.compiler || 'g++' }}
|
||||
path: tests/benchmarks/json_view/results/
|
||||
@@ -17,11 +17,11 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
macos-14:
|
||||
runs-on: macos-14 # https://github.com/actions/runner-images/blob/main/images/macos/macos-14-Readme.md
|
||||
macos-15:
|
||||
runs-on: macos-15 # https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md
|
||||
strategy:
|
||||
matrix:
|
||||
xcode: ['15.0.1', '15.1', '15.2', '15.3', '15.4']
|
||||
xcode: ['16.0', '16.1', '16.2', '16.3', '16.4', '26.0.1', '26.1.1', '26.2', '26.3']
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
|
||||
|
||||
@@ -36,11 +36,11 @@ jobs:
|
||||
- name: Test
|
||||
run: cd build ; ctest -j 10 --output-on-failure
|
||||
|
||||
macos-15:
|
||||
runs-on: macos-15 # https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md
|
||||
macos-26:
|
||||
runs-on: macos-26 # https://github.com/actions/runner-images/blob/main/images/macos/macos-26-arm64-Readme.md
|
||||
strategy:
|
||||
matrix:
|
||||
xcode: ['16.0', '16.1', '16.2', '16.3', '16.4', '26.0.1']
|
||||
xcode: ['26.4.1', '26.5', '26.6']
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
container: ubuntu:24.04
|
||||
strategy:
|
||||
matrix:
|
||||
target: [ci_cmake_flags, ci_test_diagnostics, ci_test_diagnostic_positions, ci_test_noexceptions, ci_test_noimplicitconversions, ci_test_legacycomparison, ci_test_noglobaludls, ci_test_disableenumserialization, ci_test_disabletuplereferenceconversion, ci_test_skiplibraryversioncheck, ci_test_simdutf, ci_test_strict_nul_handling, ci_test_no_thread_local]
|
||||
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_disabletuplereferenceconversion, ci_test_skiplibraryversioncheck, ci_test_simdutf, ci_test_strict_nul_handling, ci_test_delete_deprecated_functions, ci_test_no_thread_local]
|
||||
steps:
|
||||
- name: Install build-essential
|
||||
run: apt-get update ; apt-get install -y build-essential unzip wget git
|
||||
@@ -209,7 +209,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
# older GCC docker images (4, 5, 6) fail to check out code
|
||||
compiler: ['7', '8', '9', '10', '11', '12', '13', '14', '15', 'latest']
|
||||
compiler: ['7', '8', '9', '10', '11', '12', '13', '14', '15', '16', 'latest']
|
||||
container: gcc:${{ matrix.compiler }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
@@ -70,10 +70,7 @@ cc_library(
|
||||
"include/nlohmann/detail/view/builder.hpp",
|
||||
"include/nlohmann/detail/view/compare.hpp",
|
||||
"include/nlohmann/detail/view/document_data.hpp",
|
||||
"include/nlohmann/detail/view/edit.hpp",
|
||||
"include/nlohmann/detail/view/edit_storage.hpp",
|
||||
"include/nlohmann/detail/view/errors.hpp",
|
||||
"include/nlohmann/detail/view/image.hpp",
|
||||
"include/nlohmann/detail/view/input.hpp",
|
||||
"include/nlohmann/detail/view/iterator.hpp",
|
||||
"include/nlohmann/detail/view/lookup.hpp",
|
||||
@@ -82,11 +79,9 @@ cc_library(
|
||||
"include/nlohmann/detail/view/materialize.hpp",
|
||||
"include/nlohmann/detail/view/node.hpp",
|
||||
"include/nlohmann/detail/view/number.hpp",
|
||||
"include/nlohmann/detail/view/object_index.hpp",
|
||||
"include/nlohmann/detail/view/pointer.hpp",
|
||||
"include/nlohmann/detail/view/scan.hpp",
|
||||
"include/nlohmann/detail/view/serializer.hpp",
|
||||
"include/nlohmann/detail/view/simd.hpp",
|
||||
"include/nlohmann/detail/view/string_ref.hpp",
|
||||
"include/nlohmann/detail/view/value.hpp",
|
||||
"include/nlohmann/json.hpp",
|
||||
|
||||
@@ -62,6 +62,7 @@ option(JSON_MultipleHeaders "Use non-amalgamated version of the l
|
||||
option(JSON_SystemInclude "Include as system headers (skip for clang-tidy)." OFF)
|
||||
option(JSON_StrictNulHandling "Build with strict NUL-byte handling enabled." OFF)
|
||||
option(JSON_StrictBinaryUTF8 "Build with UTF-8 checks in the CBOR, UBJSON, BJData, and BSON writers enabled." OFF)
|
||||
option(JSON_DeleteDeprecatedFunctions "Delete the deprecated functions instead of only deprecating them." OFF)
|
||||
|
||||
if (JSON_CI)
|
||||
include(ci)
|
||||
@@ -123,6 +124,10 @@ if (JSON_StrictBinaryUTF8)
|
||||
message(STATUS "Strict UTF-8 checks in binary writers enabled (JSON_STRICT_BINARY_UTF8=1)")
|
||||
endif()
|
||||
|
||||
if (JSON_DeleteDeprecatedFunctions)
|
||||
message(STATUS "Deprecated functions are deleted (JSON_DELETE_DEPRECATED_FUNCTIONS=1)")
|
||||
endif()
|
||||
|
||||
if (JSON_Diagnostic_Positions)
|
||||
message(STATUS "Diagnostic positions enabled (JSON_DIAGNOSTIC_POSITIONS=1)")
|
||||
endif()
|
||||
@@ -159,6 +164,7 @@ target_compile_definitions(
|
||||
$<$<BOOL:${JSON_LegacyDiscardedValueComparison}>:JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1>
|
||||
$<$<BOOL:${JSON_StrictNulHandling}>:JSON_STRICT_NUL_HANDLING=1>
|
||||
$<$<BOOL:${JSON_StrictBinaryUTF8}>:JSON_STRICT_BINARY_UTF8=1>
|
||||
$<$<BOOL:${JSON_DeleteDeprecatedFunctions}>:JSON_DELETE_DEPRECATED_FUNCTIONS=1>
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
|
||||
@@ -1404,7 +1404,6 @@ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR I
|
||||
- The class contains parts of [Google Abseil](https://github.com/abseil/abseil-cpp) which is licensed under the [Apache 2.0 License](https://opensource.org/licenses/Apache-2.0).
|
||||
- The class contains an adapted version of the Eisel-Lemire algorithm, its table of powers of five, and its digit comparison for long numbers from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
||||
- The view's parser (`<nlohmann/json_view.hpp>`) contains techniques and code adapted from [yyjson](https://github.com/ibireme/yyjson) by YaoYuan, which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above): table-driven decoding of `\u` escapes and fixed-offset unrolled checks.
|
||||
- The view's parser (`<nlohmann/json_view.hpp>`) validates non-ASCII strings with the vector UTF-8 check of [simdjson](https://github.com/simdjson/simdjson) by Daniel Lemire, Geoff Langdale, John Keiser, and contributors (its "lookup4" algorithm and tables, after J. Keiser and D. Lemire, "Validating UTF-8 In Less Than One Instruction Per Byte", 2021), which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here) and the Apache 2.0 License. Copyright © 2018-2025 The simdjson authors
|
||||
|
||||
<img align="right" src="https://git.fsfe.org/reuse/reuse-ci/raw/branch/master/reuse-horizontal.png" alt="REUSE Software">
|
||||
|
||||
|
||||
+16
-2
@@ -249,6 +249,20 @@ add_custom_target(ci_test_strict_nul_handling
|
||||
COMMENT "Compile and test with strict NUL-byte handling enabled"
|
||||
)
|
||||
|
||||
###############################################################################
|
||||
# Delete the deprecated functions.
|
||||
###############################################################################
|
||||
|
||||
add_custom_target(ci_test_delete_deprecated_functions
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
||||
-DJSON_BuildTests=ON -DJSON_FastTests=ON -DJSON_DeleteDeprecatedFunctions=ON
|
||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_delete_deprecated_functions
|
||||
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_delete_deprecated_functions
|
||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_delete_deprecated_functions && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
|
||||
COMMENT "Compile and test with the deprecated functions deleted"
|
||||
)
|
||||
|
||||
###############################################################################
|
||||
# Disable global UDLs.
|
||||
###############################################################################
|
||||
@@ -362,7 +376,7 @@ add_custom_target(ci_test_coverage
|
||||
# Sanitizers.
|
||||
###############################################################################
|
||||
|
||||
set(CLANG_CXX_FLAGS_SANITIZER "-g -O1 -fsanitize=address -fsanitize=undefined -fsanitize=integer -fsanitize=nullability -fno-omit-frame-pointer -fno-sanitize-recover=all -fno-sanitize=unsigned-integer-overflow -fno-sanitize=unsigned-shift-base")
|
||||
set(CLANG_CXX_FLAGS_SANITIZER "-g -O1 -fsanitize=address -fsanitize=undefined -fsanitize=integer -fsanitize=nullability -fno-omit-frame-pointer -fno-sanitize-recover=all -fno-sanitize=unsigned-integer-overflow -fno-sanitize=unsigned-shift-base -fsanitize-ignorelist=${PROJECT_SOURCE_DIR}/cmake/clang_sanitizer_ignorelist.txt")
|
||||
|
||||
add_custom_target(ci_test_clang_sanitizer
|
||||
COMMAND CXX=${CLANG_TOOL} CXXFLAGS=${CLANG_CXX_FLAGS_SANITIZER} ${CMAKE_COMMAND}
|
||||
@@ -708,7 +722,7 @@ ci_get_cmake(4.0.0 CMAKE_4_0_0_BINARY)
|
||||
# the tests require CMake 3.13 or later, so they are excluded for CMake 3.5.0
|
||||
set(JSON_CMAKE_FLAGS_3_5_0 JSON_Diagnostics JSON_Diagnostic_Positions JSON_GlobalUDLs JSON_ImplicitConversions JSON_DisableEnumSerialization
|
||||
JSON_LegacyDiscardedValueComparison JSON_Install JSON_MultipleHeaders JSON_SystemInclude JSON_Valgrind
|
||||
JSON_StrictNulHandling JSON_StrictBinaryUTF8)
|
||||
JSON_StrictNulHandling JSON_StrictBinaryUTF8 JSON_DeleteDeprecatedFunctions)
|
||||
set(JSON_CMAKE_FLAGS_3_31_6 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
|
||||
set(JSON_CMAKE_FLAGS_4_0_0 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# Sanitizer ignore list for ci_test_clang_sanitizer (-fsanitize-ignorelist).
|
||||
#
|
||||
# libstdc++ 14's <format> declares `_Scanner(basic_string_view<_CharT>, size_t __nargs = -1)`, so every std::format
|
||||
# call converts -1 to size_t, which -fsanitize=integer reports as implicit-integer-sign-change. This is
|
||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119429, not a bug in this library. Only that check and only <format> are
|
||||
# excluded, so implicit sign changes in the library and the tests are still reported.
|
||||
[implicit-integer-sign-change]
|
||||
src:*/include/c++/*/format
|
||||
+2
-12
@@ -135,20 +135,14 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::~basic_json', 'Me
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document', 'Class', 'api/basic_json_document/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::basic_json_document', 'Constructor', 'api/basic_json_document/basic_json_document/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::accept', 'Function', 'api/basic_json_document/accept/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::erase', 'Method', 'api/basic_json_document/erase/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::insert', 'Method', 'api/basic_json_document/insert/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::is_discarded', 'Method', 'api/basic_json_document/is_discarded/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::load', 'Function', 'api/basic_json_document/load/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::memory_usage', 'Method', 'api/basic_json_document/memory_usage/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::node_count', 'Method', 'api/basic_json_document/node_count/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::owns_source', 'Method', 'api/basic_json_document/owns_source/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::parse', 'Function', 'api/basic_json_document/parse/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::parse_copy', 'Function', 'api/basic_json_document/parse_copy/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::push_back', 'Method', 'api/basic_json_document/push_back/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::read', 'Method', 'api/basic_json_document/read/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::root', 'Method', 'api/basic_json_document/root/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::save', 'Method', 'api/basic_json_document/save/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::set', 'Method', 'api/basic_json_document/set/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::shrink_to_fit', 'Method', 'api/basic_json_document/shrink_to_fit/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::source', 'Method', 'api/basic_json_document/source/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view', 'Class', 'api/basic_json_view/index.html');
|
||||
@@ -197,8 +191,6 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::type_name',
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::value', 'Method', 'api/basic_json_view/value/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('json', 'Class', 'api/json/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_document', 'Class', 'api/json_document/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_editable_document', 'Class', 'api/json_editable_document/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_editable_view', 'Class', 'api/json_editable_view/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_view', 'Class', 'api/json_view/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer', 'Class', 'api/json_pointer/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::back', 'Method', 'api/json_pointer/back/index.html');
|
||||
@@ -238,8 +230,6 @@ INSERT INTO searchIndex(name, type, path) VALUES ('operator<<', 'Operator', 'api
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('operator>>', 'Operator', 'api/operator_gtgt/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_json', 'Class', 'api/ordered_json/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_json_document', 'Class', 'api/ordered_json_document/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_json_editable_document', 'Class', 'api/ordered_json_editable_document/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_json_editable_view', 'Class', 'api/ordered_json_editable_view/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_json_view', 'Class', 'api/ordered_json_view/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map', 'Class', 'api/ordered_map/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('std::formatter<basic_json>', 'Class', 'api/basic_json/std_formatter/index.html');
|
||||
@@ -291,6 +281,7 @@ INSERT INTO searchIndex(name, type, path) VALUES ('Supported Macros', 'Guide', '
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_ASSERT', 'Macro', 'api/macros/json_assert/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_BRACE_INIT_COPY_SEMANTICS', 'Macro', 'api/macros/json_brace_init_copy_semantics/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_CATCH_USER', 'Macro', 'api/macros/json_throw_user/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DELETE_DEPRECATED_FUNCTIONS', 'Macro', 'api/macros/json_delete_deprecated_functions/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DIAGNOSTICS', 'Macro', 'api/macros/json_diagnostics/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DIAGNOSTIC_POSITIONS', 'Macro', 'api/macros/json_diagnostic_positions/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DISABLE_ENUM_SERIALIZATION', 'Macro', 'api/macros/json_disable_enum_serialization/index.html');
|
||||
@@ -321,6 +312,7 @@ INSERT INTO searchIndex(name, type, path) VALUES ('JSON_TRY_USER', 'Macro', 'api
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_GLOBAL_UDLS', 'Macro', 'api/macros/json_use_global_udls/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_IMPLICIT_CONVERSIONS', 'Macro', 'api/macros/json_use_implicit_conversions/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON', 'Macro', 'api/macros/json_use_legacy_discarded_value_comparison/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS', 'Macro', 'api/macros/json_use_objects_for_enum_keyed_maps/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_SIMDUTF', 'Macro', 'api/macros/json_use_simdutf/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('Macros', 'Macro', 'api/macros/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
|
||||
@@ -356,5 +348,3 @@ INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_SERIALIZE_ENUM_
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_VERSION_MAJOR', 'Macro', 'api/macros/nlohmann_json_version_major/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_VERSION_MINOR', 'Macro', 'api/macros/nlohmann_json_version_major/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_VERSION_PATCH', 'Macro', 'api/macros/nlohmann_json_version_major/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_VIEW_NO_SIMD', 'Macro', 'api/macros/json_view_no_simd/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_VIEW_USE_SSSE3', 'Macro', 'api/macros/json_view_use_ssse3/index.html');
|
||||
@@ -8,7 +8,7 @@ static basic_json diff(const basic_json& source,
|
||||
Creates a [JSON Patch](http://jsonpatch.com) so that value `source` can be changed into the value `target` by calling
|
||||
[`patch`](patch.md) function.
|
||||
|
||||
For two JSON values `source` and `target`, the following code yields always `#!cpp true`:
|
||||
For two JSON values `source` and `target`, the following code always yields `#!cpp true`:
|
||||
```cpp
|
||||
source.patch(diff(source, target)) == target;
|
||||
```
|
||||
@@ -27,7 +27,7 @@ a JSON patch to convert the `source` to `target`
|
||||
|
||||
## Exception safety
|
||||
|
||||
Strong guarantee: if an exception is thrown, there are no changes in the JSON value.
|
||||
Strong guarantee: `source` and `target` are never modified.
|
||||
|
||||
## Complexity
|
||||
|
||||
|
||||
@@ -120,3 +120,12 @@ Linear in the size of the input.
|
||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||
- Added `error_handler` parameter in version 3.13.0.
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
- Overload (2) replaces calls to `from_bjdata` with a pointer and a length as first two parameters, which has been
|
||||
deprecated in version 3.13.0. This overload will be removed in version 4.0.0. Please replace all calls like
|
||||
`#!cpp from_bjdata(ptr, len, ...);` with `#!cpp from_bjdata(ptr, ptr+len, ...);`.
|
||||
|
||||
You should be warned by your compiler with a `-Wdeprecated-declarations` warning if you are using a deprecated
|
||||
function.
|
||||
@@ -106,3 +106,12 @@ Linear in the size of the input.
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
- Overload (2) replaces calls to `from_bon8` with a pointer and a length as first two parameters, which has been
|
||||
deprecated in version 3.13.0. This overload will be removed in version 4.0.0. Please replace all calls like
|
||||
`#!cpp from_bon8(ptr, len, ...);` with `#!cpp from_bon8(ptr, ptr+len, ...);`.
|
||||
|
||||
You should be warned by your compiler with a `-Wdeprecated-declarations` warning if you are using a deprecated
|
||||
function.
|
||||
@@ -56,6 +56,10 @@ This implementation does exactly follow this approach, as it uses double precisi
|
||||
smaller than `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` will be stored as NaN internally
|
||||
and be serialized to `null`.
|
||||
|
||||
During deserialization (from JSON text or any of the binary formats), a finite number that does not fit into
|
||||
`number_float_t` is rejected with [`out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406), for
|
||||
example a double-precision number in a binary format when `number_float_t` is `#!cpp float`.
|
||||
|
||||
### Storage
|
||||
|
||||
Floating-point number values are stored directly inside a `basic_json` type.
|
||||
|
||||
@@ -47,8 +47,9 @@ With the default values for `NumberIntegerType` (`std::int64_t`), the default va
|
||||
|
||||
When the default type is used, the maximal integer number that can be stored is `9223372036854775807` (INT64_MAX) and
|
||||
the minimal integer number that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers that are out of
|
||||
range will yield over/underflow when used in a constructor. During deserialization, too large or small integer numbers
|
||||
will automatically be stored as [`number_unsigned_t`](number_unsigned_t.md) or [`number_float_t`](number_float_t.md).
|
||||
range will yield over/underflow when used in a constructor. During deserialization (from JSON text or any of the binary
|
||||
formats), too large or small integer numbers will automatically be stored as [`number_unsigned_t`](number_unsigned_t.md)
|
||||
or [`number_float_t`](number_float_t.md).
|
||||
|
||||
[RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
|
||||
> Note that when such software is used, numbers that are integers and are in the range [-2<sup>53</sup>+1, 2<sup>53</sup>-1] are
|
||||
|
||||
@@ -48,8 +48,9 @@ With the default values for `NumberUnsignedType` (`std::uint64_t`), the default
|
||||
|
||||
When the default type is used, the maximal integer number that can be stored is `18446744073709551615` (UINT64_MAX) and
|
||||
the minimal integer number that can be stored is `0`. Integer numbers that are out of range will yield over/underflow
|
||||
when used in a constructor. During deserialization, too large or small integer numbers will automatically be stored
|
||||
as [`number_integer_t`](number_integer_t.md) or [`number_float_t`](number_float_t.md).
|
||||
when used in a constructor. During deserialization (from JSON text or any of the binary formats), too large or small
|
||||
integer numbers will automatically be stored as [`number_integer_t`](number_integer_t.md) or
|
||||
[`number_float_t`](number_float_t.md).
|
||||
|
||||
[RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
|
||||
> Note that when such software is used, numbers that are integers and are in the range [-2<sup>53</sup>+1, 2<sup>53</sup>-1] are
|
||||
|
||||
@@ -68,6 +68,8 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
||||
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is
|
||||
not valid UTF-8 and `error_handler` is `strict` (the default only if
|
||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled)
|
||||
- Throws [type_error.321](../../home/exceptions.md#jsonexceptiontype_error321) if `j` or a value nested in it is
|
||||
discarded; example: `"cannot serialize discarded value to BJData"`
|
||||
|
||||
## Complexity
|
||||
|
||||
@@ -119,4 +121,6 @@ Linear in the size of the JSON value `j`.
|
||||
- BJData version parameter (for draft3 binary encoding) added in version 3.12.0.
|
||||
- Added `error_handler` parameter in version 3.13.0. Its default, `keep`, writes the bytes of a string or object key
|
||||
that is not valid UTF-8 unchanged, as before; `strict` (the default if
|
||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316`.
|
||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316`.
|
||||
- Throws `type_error.321` for a discarded value since version 3.13.0; previously, a discarded value nested in an
|
||||
array or object was silently skipped, producing invalid BJData.
|
||||
@@ -58,6 +58,9 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
||||
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key is
|
||||
not valid UTF-8 and `error_handler` is `strict` (the default only if
|
||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled)
|
||||
- Throws [type_error.321](../../home/exceptions.md#jsonexceptiontype_error321) if a value nested in `j` is discarded
|
||||
(the top-level value itself is covered by `type_error.317` above, since it must be an object); example:
|
||||
`"cannot serialize discarded value to BSON"`
|
||||
|
||||
## Complexity
|
||||
|
||||
@@ -110,6 +113,8 @@ pass before anything is written.
|
||||
- Throws `out_of_range.412` and `out_of_range.415` since version 3.13.0.
|
||||
- Linear in the size of `j`, and no longer limited by the call stack for deeply nested values, since version 3.13.0.
|
||||
- `out_of_range.415` is now detected before anything is written, like the other exceptions above, since version 3.13.0.
|
||||
- Throws `type_error.321` for a discarded value nested in `j` since version 3.13.0; previously, it was silently
|
||||
skipped, producing a document whose declared size did not match what was actually written.
|
||||
- Added `error_handler` parameter in version 3.13.0. Its default, `keep`, writes the bytes of a string or object key
|
||||
that is not valid UTF-8 unchanged, as before; `strict` (the default if
|
||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316` before anything
|
||||
|
||||
@@ -49,6 +49,8 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
||||
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is
|
||||
not valid UTF-8 and `error_handler` is `strict` (the default only if
|
||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled)
|
||||
- Throws [type_error.321](../../home/exceptions.md#jsonexceptiontype_error321) if `j` or a value nested in it is
|
||||
discarded; example: `"cannot serialize discarded value to CBOR"`
|
||||
|
||||
## Complexity
|
||||
|
||||
@@ -86,3 +88,5 @@ Linear in the size of the JSON value `j`.
|
||||
- Added `error_handler` parameter in version 3.13.0. Its default, `keep`, writes the bytes of a string or object key
|
||||
that is not valid UTF-8 unchanged, as before; `strict` (the default if
|
||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316`.
|
||||
- Throws `type_error.321` for a discarded value since version 3.13.0; previously, a discarded value nested in an
|
||||
array or object was silently skipped, producing invalid CBOR.
|
||||
@@ -54,6 +54,8 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
||||
`"subtype 70000 is too large for the MessagePack ext type (max 255)"`
|
||||
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is
|
||||
not valid UTF-8 and `error_handler` is `strict`
|
||||
- Throws [type_error.321](../../home/exceptions.md#jsonexceptiontype_error321) if `j` or a value nested in it is
|
||||
discarded; example: `"cannot serialize discarded value to MessagePack"`
|
||||
|
||||
## Complexity
|
||||
|
||||
@@ -108,3 +110,5 @@ Linear in the size of the JSON value `j`.
|
||||
- Fixed in version 3.13.0 to serialize `number_integer_t`/`number_unsigned_t` pairs of different width correctly;
|
||||
before, integers could be serialized with the wrong value if `number_integer_t` was narrower than
|
||||
`number_unsigned_t`.
|
||||
- Throws `type_error.321` for a discarded value since version 3.13.0; previously, a discarded value nested in an
|
||||
array or object was silently skipped, producing invalid MessagePack.
|
||||
@@ -61,6 +61,8 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
||||
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is
|
||||
not valid UTF-8 and `error_handler` is `strict` (the default only if
|
||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled)
|
||||
- Throws [type_error.321](../../home/exceptions.md#jsonexceptiontype_error321) if `j` or a value nested in it is
|
||||
discarded; example: `"cannot serialize discarded value to UBJSON"`
|
||||
|
||||
## Complexity
|
||||
|
||||
@@ -112,3 +114,5 @@ Linear in the size of the JSON value `j`.
|
||||
- Added `error_handler` parameter in version 3.13.0. Its default, `keep`, writes the bytes of a string or object key
|
||||
that is not valid UTF-8 unchanged, as before; `strict` (the default if
|
||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316`.
|
||||
- Throws `type_error.321` for a discarded value since version 3.13.0; previously, a discarded value nested in an
|
||||
array or object was silently skipped, producing invalid UBJSON.
|
||||
@@ -1,128 +0,0 @@
|
||||
# <small>nlohmann::basic_json_document::</small>erase
|
||||
|
||||
```cpp
|
||||
// (1)
|
||||
std::size_t erase(view_type object, string_view_t key);
|
||||
|
||||
// (2)
|
||||
template<typename I>
|
||||
void erase(view_type array, I idx);
|
||||
|
||||
// (3)
|
||||
std::size_t erase(const json_pointer& ptr);
|
||||
```
|
||||
|
||||
Only an **editable** document (`#!cpp Editable == true`, e.g. [`json_editable_document`](../json_editable_document.md))
|
||||
has `erase`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`).
|
||||
|
||||
1. Removes every member of `object` whose key is `key` (see [Notes](#notes) on duplicate keys) and returns how many
|
||||
were removed; `#!cpp 0` if `object` has no member with this key.
|
||||
2. Removes the element at index `idx` of `array`, which must already exist (`#!cpp idx < array.size()`).
|
||||
3. Removes the value the JSON pointer `ptr` refers to, relative to [`root()`](root.md), and returns how many values
|
||||
were removed: the *parent* of the target must already exist, and the target itself is removed as in 1. (an object
|
||||
member; `#!cpp 0` or more) or 2. (an array element; always `#!cpp 1`). `ptr` must not be empty -- [`root()`](root.md)
|
||||
itself cannot be erased.
|
||||
|
||||
## Template parameters
|
||||
|
||||
`I`
|
||||
: an integral type other than `#!cpp bool`, deduced (overloads taking a `#!cpp bool` or a non-integral type for
|
||||
`idx` do not participate in overload resolution).
|
||||
|
||||
## Parameters
|
||||
|
||||
`object` (in)
|
||||
: the object to remove a member of
|
||||
|
||||
`array` (in)
|
||||
: the array to remove an element of
|
||||
|
||||
`key` (in)
|
||||
: the key of the member(s) to remove
|
||||
|
||||
`idx` (in)
|
||||
: the index of the element to remove; a negative value throws (see [Exceptions](#exceptions))
|
||||
|
||||
`ptr` (in)
|
||||
: a JSON pointer to the value to remove, relative to `root()`
|
||||
|
||||
## Return value
|
||||
|
||||
1. the number of removed members (`#!cpp 0` if `object` had none with this `key`)
|
||||
2. (nothing)
|
||||
3. the number of removed values (`#!cpp 0` or more for an object member, always `#!cpp 1` for an array element)
|
||||
|
||||
## Exceptions
|
||||
|
||||
1. Throws [`type_error.307`](../../home/exceptions.md#jsonexceptiontype_error307) if `object` is not an object -- the
|
||||
same message [`BasicJsonType::erase`](../basic_json/erase.md) throws for the same type.
|
||||
2. Throws `type_error.307` if `array` is not an array. Throws
|
||||
[`out_of_range.401`](../../home/exceptions.md#jsonexceptionout_of_range401) if `idx` is negative, or if
|
||||
`#!cpp idx >= array.size()`.
|
||||
3. Throws [`out_of_range.405`](../../home/exceptions.md#jsonexceptionout_of_range405) ("JSON pointer has no parent")
|
||||
if `ptr` is empty. Throws what [`at`](../basic_json_view/at.md) throws (overload 3) for resolving `ptr`'s parent.
|
||||
For the last reference token itself: if the parent is an array, throws what 2. throws for an index that is out of
|
||||
range, or, for a token that is not a valid array index,
|
||||
[`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) (a leading `#!cpp '0'`),
|
||||
[`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) (not a number),
|
||||
[`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) (too large for `size_type`), or
|
||||
[`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) (an empty token); otherwise (an
|
||||
object, or a primitive value the pointer's parent resolves to) throws what 1. throws.
|
||||
|
||||
Every overload also throws [`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) ("view
|
||||
does not belong to this document") if `object`/`array` is a [discarded](../basic_json_view/is_discarded.md) view or a
|
||||
view of a *different* document (overloads 1-2 only; overload 3 always starts from this document's own
|
||||
[`root()`](root.md)).
|
||||
|
||||
## Complexity
|
||||
|
||||
1. Linear in the number of members of `object`.
|
||||
2. Linear in the number of elements of `array` at or after `idx` (they move one slot over).
|
||||
3. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
||||
that level or the index into the array (as [`at`](../basic_json_view/at.md)), plus the complexity of 1. or 2. for
|
||||
the last token.
|
||||
|
||||
## Notes
|
||||
|
||||
!!! info "Duplicate keys"
|
||||
|
||||
Overload 1. removes *every* member with `key`, not just the first -- unlike [`set`](set.md), which assigns the
|
||||
first occurrence and drops the rest. This is why it returns a count rather than a single view: there may be
|
||||
more than one member removed, or none.
|
||||
|
||||
Like [`set`](set.md) and [`push_back`](push_back.md), `erase` never moves an element's *value*: a view still
|
||||
referring to a removed member or element keeps showing what it last held (see [Edits](index.md#edits)) -- it just no
|
||||
longer appears when `array`/`object` is read, dumped, or iterated. Removing an element of `array` (2.) does shift the
|
||||
*links* to the elements after it, the same way `insert`, `set`, or `push_back` on the same array would; any iterator
|
||||
already taken over `array`/`object` is invalidated by an erase, since it was walking the old layout.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below drops a deprecated field and a decommissioned entry from a configuration document -- using all
|
||||
three overloads -- and shows what stays intact that would not with a plain `json`/`ordered_json` value: the order
|
||||
of the fields around the ones removed, and the exact spelling of a number that was never touched.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_document__erase.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_document__erase.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [insert](insert.md) - insert an element into an array
|
||||
- [set](set.md) - replace a value, or set an object member, an array element, or the value a JSON pointer refers to
|
||||
- [push_back](push_back.md) - append to an array
|
||||
- [root](root.md) - the view of the root value, the starting point of overload 3
|
||||
- [`BasicJsonType::erase`](../basic_json/erase.md) - the corresponding function of `basic_json`
|
||||
- [Edits](index.md#edits) - what an edit guarantees, for every overload
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -3,7 +3,7 @@
|
||||
<small>Defined in header `<nlohmann/json_view.hpp>`</small>
|
||||
|
||||
```cpp
|
||||
template<typename BasicJsonType, bool Editable = false>
|
||||
template<typename BasicJsonType>
|
||||
class basic_json_document;
|
||||
```
|
||||
|
||||
@@ -19,11 +19,6 @@ it (a copy, or an rvalue `#!cpp std::string` that was moved in); see [`owns_sour
|
||||
is move-only: copying a document would either duplicate a potentially large index and text, or leave two documents
|
||||
claiming to borrow the same buffer, so it is disabled.
|
||||
|
||||
With `#!cpp Editable == true`, the document also offers [`set`](set.md), [`push_back`](push_back.md),
|
||||
[`insert`](insert.md), and [`erase`](erase.md) to change values in place, see [Edits](#edits) below. The source text
|
||||
itself is never written; a read-only document (`#!cpp Editable == false`, the default) does not carry any of the
|
||||
bookkeeping edits need, and calling any of them on one fails to compile (`#!cpp static_assert`).
|
||||
|
||||
## Template parameters
|
||||
|
||||
`BasicJsonType`
|
||||
@@ -31,37 +26,23 @@ bookkeeping edits need, and calling any of them on one fails to compile (`#!cpp
|
||||
[`ordered_json`](../ordered_json.md). Only 64-bit `number_integer_t`/`number_unsigned_t` types are supported; this
|
||||
is checked with a `static_assert`.
|
||||
|
||||
`Editable`
|
||||
: whether the document supports [`set`](set.md), [`push_back`](push_back.md), [`insert`](insert.md), and
|
||||
[`erase`](erase.md) (optional, `#!cpp false` by default). See [Edits](#edits) below.
|
||||
|
||||
## Specializations
|
||||
|
||||
- [**json_document**](../json_document.md) - read-only documents of the default specialization [`json`](../json.md)
|
||||
- [**ordered_json_document**](../ordered_json_document.md) - read-only documents of
|
||||
[`ordered_json`](../ordered_json.md)
|
||||
- [**json_editable_document**](../json_editable_document.md) - editable documents of [`json`](../json.md)
|
||||
- [**ordered_json_editable_document**](../ordered_json_editable_document.md) - editable documents of
|
||||
[`ordered_json`](../ordered_json.md)
|
||||
- [**json_document**](../json_document.md) - documents of the default specialization [`json`](../json.md)
|
||||
- [**ordered_json_document**](../ordered_json_document.md) - documents of [`ordered_json`](../ordered_json.md)
|
||||
|
||||
## Member types
|
||||
|
||||
- **view_type** - the type of view returned by [`root()`](root.md) (`#!cpp basic_json_view<BasicJsonType, Editable>`)
|
||||
- **view_type** - the type of view returned by [`root()`](root.md) (`#!cpp basic_json_view<BasicJsonType>`)
|
||||
- **value_t** - the JSON type enumeration, see [`basic_json::value_t`](../basic_json/value_t.md)
|
||||
|
||||
## Member functions
|
||||
|
||||
- [(constructor)](basic_json_document.md)
|
||||
|
||||
### Parsing
|
||||
|
||||
- [**parse**](parse.md) (_static_) - deserialize from a compatible input, borrowing or owning it as appropriate
|
||||
- [**parse_copy**](parse_copy.md) (_static_) - deserialize a copy of a compatible input
|
||||
- [**accept**](accept.md) (_static_) - check whether the input is valid JSON
|
||||
- [**read**](read.md) - (re-)parse into this document, reusing its memory
|
||||
|
||||
### Access
|
||||
|
||||
- [**root**](root.md) - the view of the root value
|
||||
- [**is_discarded**](is_discarded.md) - return whether the last parse failed
|
||||
- [**source**](source.md) - the parsed text
|
||||
@@ -70,49 +51,6 @@ bookkeeping edits need, and calling any of them on one fails to compile (`#!cpp
|
||||
- [**memory_usage**](memory_usage.md) - the number of bytes held by the document
|
||||
- [**shrink_to_fit**](shrink_to_fit.md) - release unused index capacity
|
||||
|
||||
### Images
|
||||
|
||||
- [**save**](save.md) - the document as an image that `load()` reads without parsing
|
||||
- [**load**](load.md) (_static_) - read an image written by `save()`
|
||||
|
||||
### Edits
|
||||
|
||||
- [**set**](set.md) - replace a value, or set an object member, an array element, or the value a JSON pointer refers
|
||||
to (`#!cpp Editable` documents only)
|
||||
- [**push_back**](push_back.md) - append to an array (`#!cpp Editable` documents only)
|
||||
- [**insert**](insert.md) - insert an element into an array before a given position (`#!cpp Editable` documents only)
|
||||
- [**erase**](erase.md) - remove an object member, an array element, or the value a JSON pointer refers to
|
||||
(`#!cpp Editable` documents only)
|
||||
|
||||
## Edits
|
||||
|
||||
An editable document (`#!cpp Editable == true`) can be changed after parsing, with [`set`](set.md),
|
||||
[`push_back`](push_back.md), [`insert`](insert.md), and [`erase`](erase.md);
|
||||
[`json_editable_document`](../json_editable_document.md) and
|
||||
[`ordered_json_editable_document`](../ordered_json_editable_document.md) are the corresponding specializations. A few
|
||||
points apply to every edit:
|
||||
|
||||
- **The source text is never written**, and the parsed index never moves: every value keeps the node it was parsed
|
||||
into, so [views](../basic_json_view/index.md) taken before an edit stay valid, including
|
||||
[`root()`](root.md). New values (and the element sequences of an edited array/object) go to storage owned by the
|
||||
document, allocated on demand.
|
||||
- **A view keeps referring to the same value.** After [`set`](set.md) replaces the value a view refers to, that view
|
||||
sees the new value; a view of a value that a later edit replaces or drops keeps showing what it last held. An edit
|
||||
of an array or object, however, **invalidates the iterators taken over it** (its members may now live in a
|
||||
different sequence), and a string obtained with [`get_string()`](../basic_json_view/get_string.md) stays valid even
|
||||
as further edits happen (earlier buffers of edited text are kept alive, not overwritten).
|
||||
- **Values are accepted three ways:** a [`basic_json_view`](../basic_json_view/index.md) of *any* document
|
||||
(read-only or editable; it is copied, nothing is shared with the source document), a `BasicJsonType` value, or
|
||||
anything `BasicJsonType` can be constructed from (numbers, strings, `#!cpp bool`, `#!cpp nullptr`, containers, ...).
|
||||
- [`dump()`](../basic_json_view/dump.md) writes an edited document with members in document order, new members at
|
||||
the end, and, with [`number_format::source`](../basic_json_view/number_format.md), keeps the spelling of every
|
||||
number that was not itself edited -- see [Editing a document](../../features/json_view.md#editing-a-document) for
|
||||
why this matters.
|
||||
- [`read()`](read.md) discards all edits, [`shrink_to_fit()`](shrink_to_fit.md) does not move the node index once
|
||||
there are edits, and [`memory_usage()`](memory_usage.md) includes the memory edits use.
|
||||
[`source_offset()`](../basic_json_view/source_offset.md) of a value introduced by an edit is
|
||||
`#!cpp static_cast<std::size_t>(-1)`, the same value it reports for a decoded string.
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -1,114 +0,0 @@
|
||||
# <small>nlohmann::basic_json_document::</small>insert
|
||||
|
||||
```cpp
|
||||
template<typename I, typename V>
|
||||
view_type insert(view_type array, I idx, V&& value);
|
||||
```
|
||||
|
||||
Only an **editable** document (`#!cpp Editable == true`, e.g. [`json_editable_document`](../json_editable_document.md))
|
||||
has `insert`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`).
|
||||
|
||||
Inserts `value` into `array` as a new element before position `idx`, which must not be past the end
|
||||
(`#!cpp idx <= array.size()`; `#!cpp idx == array.size()` appends, like [`push_back`](push_back.md)). Unlike
|
||||
[`push_back`](push_back.md), a [null](../basic_json_view/is_null.md) `array` does *not* first become an empty array:
|
||||
`array` must already be an array.
|
||||
|
||||
`value` is accepted three ways: a [`basic_json_view`](../basic_json_view/index.md) of *any* document -- read-only or
|
||||
editable, and it does not have to be `array`'s own document -- which is copied so that nothing is shared with the
|
||||
source document afterward; a `BasicJsonType` value; or anything `BasicJsonType` can be constructed from (numbers,
|
||||
strings, `#!cpp bool`, `#!cpp nullptr`, containers, ...).
|
||||
|
||||
## Template parameters
|
||||
|
||||
`I`
|
||||
: an integral type other than `#!cpp bool`, deduced (overloads taking a `#!cpp bool` or a non-integral type for
|
||||
`idx` do not participate in overload resolution).
|
||||
|
||||
`V`
|
||||
: the type of `value`, deduced; see above for what is accepted.
|
||||
|
||||
## Parameters
|
||||
|
||||
`array` (in)
|
||||
: the array to insert into
|
||||
|
||||
`idx` (in)
|
||||
: the position to insert `value` before; a negative value throws (see [Exceptions](#exceptions))
|
||||
|
||||
`value` (in)
|
||||
: the value to insert
|
||||
|
||||
## Return value
|
||||
|
||||
a view of the new element, now holding `value`
|
||||
|
||||
## Exception safety
|
||||
|
||||
Basic exception safety: `value` is fully encoded -- including the checks below -- into storage owned by the document
|
||||
before anything already reachable from [`root()`](root.md) is touched, so a failure while encoding `value` (an
|
||||
invalid argument, or `#!cpp std::bad_alloc`) leaves the document completely unchanged, other than memory allocated
|
||||
for the encoding that is not reclaimed. A failure of a later allocation -- while `array` switches from its parsed
|
||||
layout to a growable block, or while that block grows, see [Notes](#notes) -- can still leave `array` already
|
||||
switched to that layout even though `value` itself was not inserted.
|
||||
|
||||
## Exceptions
|
||||
|
||||
Throws [`type_error.309`](../../home/exceptions.md#jsonexceptiontype_error309) if `array` is not an array -- the same
|
||||
message [`BasicJsonType::insert`](../basic_json/insert.md) throws for the same type; a null `array` throws this too
|
||||
(see above). Throws [`out_of_range.401`](../../home/exceptions.md#jsonexceptionout_of_range401) if `idx` is negative,
|
||||
or if `#!cpp idx > array.size()`. Throws
|
||||
[`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) ("view does not belong to this
|
||||
document") if `array` is a [discarded](../basic_json_view/is_discarded.md) view or a view of a *different* document.
|
||||
Throws [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if `value` is a
|
||||
[discarded](../basic_json_view/is_discarded.md) view or a [discarded](../basic_json/is_discarded.md) `BasicJsonType`
|
||||
value, and [`type_error.319`](../../home/exceptions.md#jsonexceptiontype_error319) if `value` is (or contains) a
|
||||
binary value -- `BasicJsonType` can hold one, but a `json_document` cannot. Throws
|
||||
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) if `value` is (or contains) a string that is
|
||||
not valid UTF-8, with the same message [`BasicJsonType::dump()`](../basic_json/dump.md) gives for that string.
|
||||
|
||||
## Complexity
|
||||
|
||||
Linear in the number of elements of `array` at or after `idx` (they move one slot over), plus time linear in the
|
||||
size of `value` to encode it into the document's storage (constant for a scalar, linear in the number of nested
|
||||
values for an array or object): like [`push_back`](push_back.md), the elements of `array` move to a growable block
|
||||
of links the first time it is inserted into (or [`set`](set.md)/[`push_back`](push_back.md) on), and that block
|
||||
grows in amortized constant time; inserting before the end within that block still shifts every later element.
|
||||
|
||||
## Notes
|
||||
|
||||
Like [`set`](set.md) on a member or an element, `insert` never moves an existing *element's value* -- only where
|
||||
`array`'s *links* to its elements live -- so a view of an existing element of `array` stays valid across an
|
||||
`insert`, and keeps referring to the same element even though its index shifts. Any iterator already taken over
|
||||
`array` is invalidated, since it was walking the old layout. See [Edits](index.md#edits) for what stays valid across
|
||||
an edit in general.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below inserts a step into the middle of a deployment plan, without touching the steps that come
|
||||
after it, and shows that a view taken before the insert keeps referring to the same element even though its
|
||||
index shifts -- something a plain `json`/`ordered_json` array, or its `std::vector`-based storage, has no
|
||||
equivalent for.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_document__insert.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_document__insert.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [push_back](push_back.md) - append to an array
|
||||
- [erase](erase.md) - remove an object member, an array element, or the value a JSON pointer refers to
|
||||
- [set](set.md) - replace a value, or set an object member, an array element, or the value a JSON pointer refers to
|
||||
- [`BasicJsonType::insert`](../basic_json/insert.md) - the corresponding function of `basic_json`
|
||||
- [Edits](index.md#edits) - what an edit guarantees, for every overload
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -1,169 +0,0 @@
|
||||
# <small>nlohmann::basic_json_document::</small>load
|
||||
|
||||
```cpp
|
||||
// (1)
|
||||
static basic_json_document load(const std::uint8_t* image, std::size_t size,
|
||||
const image_check check = image_check::full);
|
||||
|
||||
// (2)
|
||||
static basic_json_document load(const std::vector<std::uint8_t>& image,
|
||||
const image_check check = image_check::full);
|
||||
|
||||
// (3)
|
||||
static basic_json_document load(std::vector<std::uint8_t>&& image,
|
||||
const image_check check = image_check::full);
|
||||
```
|
||||
|
||||
1. Reads an image [`save()`](save.md) wrote, from a pointer and a byte count. The image is **borrowed**: `image`
|
||||
must stay alive and unchanged for as long as the returned document, and any view taken from it, is used.
|
||||
2. Reads an image from a `#!cpp std::vector`. Also **borrowed** -- equivalent to overload 1 called with
|
||||
`#!cpp image.data()` and `#!cpp image.size()`.
|
||||
3. Reads an image, keeping the vector instead of copying it: `image` is moved into the document (no copy), which
|
||||
then owns it for as long as it needs the text and the decoded strings. [`owns_source()`](owns_source.md) is
|
||||
`#!cpp true` afterward.
|
||||
|
||||
In every overload, the node index is copied into storage the document itself owns -- so that it is properly aligned,
|
||||
and, for an [editable](index.md#edits) document, can be edited -- while the text and the decoded strings stay in
|
||||
`image`. The hash indexes [large objects](../../features/json_view.md) use for lookup are rebuilt, exactly as after
|
||||
parsing.
|
||||
|
||||
## Parameters
|
||||
|
||||
`image` (in)
|
||||
: the image [`save()`](save.md) wrote (overloads 1 and 2), or one to take ownership of (overload 3)
|
||||
|
||||
`size` (in)
|
||||
: the number of bytes at `image` (overload 1)
|
||||
|
||||
`check` (in)
|
||||
: how thoroughly to validate `image` before trusting it; see [`image_check`](#image_check) below (optional,
|
||||
`#!cpp image_check::full` by default)
|
||||
|
||||
## Return value
|
||||
|
||||
The document read from the image.
|
||||
|
||||
## Exception safety
|
||||
|
||||
Overloads 1 and 2 give the strong guarantee: `image` is only read, never written, so a thrown exception leaves the
|
||||
caller's buffer untouched.
|
||||
|
||||
Overload 3 moves `image` into the document *before* validating it, so that a good image is kept without a copy. If
|
||||
loading then fails, the partially built document -- and the vector now inside it -- is discarded along with the
|
||||
exception, and `image` itself is left **empty**, not restored to what was passed in. Move a copy in instead, or
|
||||
validate with overload 2 first, if the original vector must survive a failed load.
|
||||
|
||||
## Exceptions
|
||||
|
||||
On a big-endian target, throws [`type_error.320`](../../home/exceptions.md#jsonexceptiontype_error320) -- the same
|
||||
exception [`save()`](save.md#exceptions) throws there, since the image format is little-endian only.
|
||||
|
||||
Otherwise throws [`parse_error.116`](../../home/exceptions.md#jsonexceptionparse_error116) if `image` is not one
|
||||
`save()` could have written, or fails the requested `check`:
|
||||
|
||||
| message | when |
|
||||
|------------------------|------------------------------------------------------------------------------------------------------|
|
||||
| `too short` | `image` is `#!cpp nullptr`, or `size` is smaller than the 64-byte header |
|
||||
| `unknown format` | the header's magic bytes or version do not match, or a reserved header field is not zero |
|
||||
| `sizes out of range` | the node count, text size, or decoded-string size the header describes does not fit `size`, or the `#!cpp '\0'` after the text or after the decoded strings is missing |
|
||||
| `the check failed` | `check` is not `#!cpp image_check::none`, and the image fails it -- see [`image_check`](#image_check) |
|
||||
|
||||
!!! failure "Example messages"
|
||||
|
||||
```
|
||||
[json.exception.parse_error.116] parse error: invalid json_document image: too short
|
||||
```
|
||||
```
|
||||
[json.exception.parse_error.116] parse error: invalid json_document image: unknown format
|
||||
```
|
||||
```
|
||||
[json.exception.parse_error.116] parse error: invalid json_document image: sizes out of range
|
||||
```
|
||||
```
|
||||
[json.exception.parse_error.116] parse error: invalid json_document image: the check failed
|
||||
```
|
||||
|
||||
## Complexity
|
||||
|
||||
Linear in the number of nodes, which are always copied into the document. With `#!cpp check == image_check::full`,
|
||||
additionally linear in the combined length of the text and the decoded strings; `#!cpp image_check::bounds` and
|
||||
`#!cpp image_check::none` do not read them.
|
||||
|
||||
## Notes
|
||||
|
||||
**The `image_check` modes.**
|
||||
|
||||
```cpp
|
||||
using image_check = detail::view::image_check;
|
||||
|
||||
enum class image_check
|
||||
{
|
||||
full,
|
||||
bounds,
|
||||
none
|
||||
};
|
||||
```
|
||||
|
||||
How thoroughly `load()` validates `image` before trusting it.
|
||||
|
||||
| value | checks | guarantees |
|
||||
|----------|--------------------------------------------------------------------------------------------------------------|------------|
|
||||
| `full` | everything the parser itself guarantees: structure and bounds; that every string is valid UTF-8 (and, for a string still in the source text, that it contains no quote, backslash, or control character); and that every number token is well-formed and matches the value stored for it | reading and serializing a checked image is safe and always produces valid JSON, exactly as for a parsed document |
|
||||
| `bounds` | structure and bounds only -- that every offset and count in the node index stays inside the image | reading and serializing stay memory-safe, but a crafted image can hold strings that are not valid UTF-8 or that serialize to invalid JSON ([`dump()`](../basic_json_view/dump.md) writes them unchanged or throws [`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316)), and numbers whose values differ from their text |
|
||||
| `none` | nothing | images from a trusted source only -- reading a damaged image is undefined behavior |
|
||||
|
||||
`full` is the default and the right choice for an image from anything you do not fully control -- a file, a cache
|
||||
shared with other processes, a peer on the network. `bounds` skips scanning the text and the decoded strings, so it
|
||||
fits a cache your own process just wrote and reads straight back, where damage would mean a bug or a hardware fault
|
||||
rather than adversarial input; it still cannot crash or read out of bounds. `none` skips validation entirely and
|
||||
should only be used for an image you trust as much as your own memory.
|
||||
|
||||
**Lifetime.** Overloads 1 and 2 borrow `image`: it must stay alive and byte-for-byte unchanged for as long as the
|
||||
returned document, and any [view](../basic_json_view/index.md) taken from it, is used -- exactly like a document
|
||||
[`parse()`](parse.md) borrowed its input for. Overload 3 avoids this by keeping the vector itself; see
|
||||
[`owns_source`](owns_source.md).
|
||||
|
||||
!!! warning "Experimental"
|
||||
|
||||
The image format is versioned but not yet stable, and may change in an incompatible way before it is declared
|
||||
stable; `load()` already rejects an image written by a different format version with `parse_error.116`
|
||||
("unknown format"). Use images to cache a document within one build of the library, or to hand one to another
|
||||
process running the *same* build on the *same* (little-endian) machine -- not as a long-term storage format.
|
||||
|
||||
**What `image_check::bounds` does not guarantee.** A bounds-checked image can never make `load()`,
|
||||
[`root()`](root.md), element access, or [`materialize()`](../basic_json_view/materialize.md) read outside the image,
|
||||
so those stay safe on a damaged one. It does *not* guarantee that the image describes valid JSON: a string
|
||||
that a `full` check would have rejected can make [`dump()`](../basic_json_view/dump.md) write invalid UTF-8 or invalid
|
||||
JSON, or throw `type_error.316`, and a number can read back with a value that does not match how it is spelled.
|
||||
Reserve `bounds` for images you already trust to be well-formed, and use it only to skip the extra scan.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example "Caching a document, ownership, and a rejected image"
|
||||
|
||||
The example below saves a parsed document as an image, checks that `load()` reproduces the original
|
||||
[`dump()`](../basic_json_view/dump.md) without parsing, and shows the difference between
|
||||
`load(std::move(image))` (owned) and `load(image)` (borrowed). It then damages one byte of the image and shows
|
||||
`image_check::full` rejecting it with `parse_error.116`, while `image_check::bounds` -- meant for a cache the
|
||||
process already trusts -- still reads it without going out of bounds.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_document__load.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_document__load.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [save](save.md) - write the document as an image
|
||||
- [owns_source](owns_source.md) - return whether the document holds its own copy of the text
|
||||
- [parse](parse.md) - deserialize from JSON text instead of an image
|
||||
- [Images](../../features/json_view.md#images) - why and when to use images
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -132,7 +132,6 @@ integer type becomes a floating-point value.
|
||||
- [accept](accept.md) - check whether the input is valid JSON
|
||||
- [read](read.md) - (re-)parse into this document, reusing its memory
|
||||
- [owns_source](owns_source.md) - return whether the document holds its own copy of the text
|
||||
- [load](load.md) - read a document from an image instead of parsing JSON text
|
||||
- [`BasicJsonType::parse`](../basic_json/parse.md) - the corresponding function of `basic_json`
|
||||
|
||||
## Version history
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
# <small>nlohmann::basic_json_document::</small>push_back
|
||||
|
||||
```cpp
|
||||
template<typename V>
|
||||
view_type push_back(view_type array, V&& value);
|
||||
```
|
||||
|
||||
Appends `value` as a new last element of `array`. A [null](../basic_json_view/is_null.md) `array` first becomes an
|
||||
empty array, the same way [`set`](set.md) turns a null `object` into an empty object.
|
||||
|
||||
`value` is accepted three ways: a [`basic_json_view`](../basic_json_view/index.md) of *any* document -- read-only or
|
||||
editable, and it does not have to be `array`'s own document -- which is copied so that nothing is shared with the
|
||||
source document afterward; a `BasicJsonType` value; or anything `BasicJsonType` can be constructed from (numbers,
|
||||
strings, `#!cpp bool`, `#!cpp nullptr`, containers, ...).
|
||||
|
||||
Only an **editable** document (`#!cpp Editable == true`, e.g. [`json_editable_document`](../json_editable_document.md))
|
||||
has `push_back`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`).
|
||||
|
||||
## Template parameters
|
||||
|
||||
`V`
|
||||
: the type of `value`, deduced; see above for what is accepted.
|
||||
|
||||
## Parameters
|
||||
|
||||
`array` (in)
|
||||
: the array (or null value) to append to
|
||||
|
||||
`value` (in)
|
||||
: the value to append
|
||||
|
||||
## Return value
|
||||
|
||||
a view of the new last element of `array`, now holding `value`
|
||||
|
||||
## Exception safety
|
||||
|
||||
Basic exception safety: `value` is fully encoded -- including the checks below -- into storage owned by the document
|
||||
before anything already reachable from [`root()`](root.md) is touched, so a failure while encoding `value` (an
|
||||
invalid argument, or `#!cpp std::bad_alloc`) leaves the document completely unchanged, other than memory allocated
|
||||
for the encoding that is not reclaimed. A failure of a later allocation -- while `array` switches from its parsed
|
||||
layout to a growable block, or while that block grows, see [Notes](#notes) -- can still leave a partial effect, such
|
||||
as a null `array` argument already turned into an empty array even though `value` itself was not appended.
|
||||
|
||||
## Exceptions
|
||||
|
||||
Throws [`type_error.308`](../../home/exceptions.md#jsonexceptiontype_error308) if `array` is neither an array nor
|
||||
null -- the same message [`BasicJsonType::push_back`](../basic_json/push_back.md) throws for the same type. Throws
|
||||
[`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) ("view does not belong to this
|
||||
document") if `array` is a [discarded](../basic_json_view/is_discarded.md) view or a view of a *different* document.
|
||||
Throws [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if `value` is a
|
||||
[discarded](../basic_json_view/is_discarded.md) view or a [discarded](../basic_json/is_discarded.md) `BasicJsonType`
|
||||
value, and [`type_error.319`](../../home/exceptions.md#jsonexceptiontype_error319) if `value` is (or contains) a
|
||||
binary value -- `BasicJsonType` can hold one, but a `json_document` cannot. Throws
|
||||
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) if `value` is (or contains) a string that is
|
||||
not valid UTF-8, with the same message [`BasicJsonType::dump()`](../basic_json/dump.md) gives for that string.
|
||||
|
||||
## Complexity
|
||||
|
||||
Amortized constant, plus time linear in the size of `value` to encode it into the document's storage (constant for
|
||||
a scalar, linear in the number of nested values for an array or object): the elements of `array` move to a growable
|
||||
block of links the first time it is appended to (or [`set`](set.md) on), and that block itself grows -- doubling its
|
||||
capacity, so the cost of growing it amortizes to constant per element -- only once it runs out of room. See
|
||||
[Notes](#notes).
|
||||
|
||||
## Notes
|
||||
|
||||
Like [`set`](set.md) on a member or an element, `push_back` never moves an existing element itself -- only where
|
||||
`array`'s *links* to its elements live -- so a view of an existing element of `array` stays valid across a
|
||||
`push_back`, but any iterator already taken over `array` is invalidated, since it was walking the old layout. See
|
||||
[Edits](index.md#edits) for what stays valid across an edit in general.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below appends records to an array one at a time, as they might arrive from a stream of events,
|
||||
without ever building a `BasicJsonType` value for the array or for the records already in it, and shows that a
|
||||
view taken from an earlier `push_back` still refers to the same element once later ones have run.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_document__push_back.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_document__push_back.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [set](set.md) - replace a value, or set an object member, an array element, or the value a JSON pointer refers to
|
||||
- [insert](insert.md) - insert an element into an array before a given position
|
||||
- [erase](erase.md) - remove an object member, an array element, or the value a JSON pointer refers to
|
||||
- [root](root.md) - the view of the root value
|
||||
- [`BasicJsonType::push_back`](../basic_json/push_back.md) - the corresponding function of `basic_json`
|
||||
- [Edits](index.md#edits) - what an edit guarantees, for every overload
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -49,11 +49,6 @@ whether or not the new parse succeeds; take fresh views from [`root()`](root.md)
|
||||
`input` is borrowed or owned by the same rules as [`parse()`](parse.md#notes); a document can borrow on one call and
|
||||
own on the next, since ownership is decided freshly each time.
|
||||
|
||||
Reusing a document matters most for large inputs: the operating system provides the memory of a fresh node index one
|
||||
page at a time, and every page costs a page fault the first time it is written. On x86-64 Linux (4 KiB pages), parsing
|
||||
a 55 MB document into a reused document took about 40 % less time than parsing it into a fresh one. Programs that parse
|
||||
many documents of similar size should therefore keep one document and call `read()`.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
@@ -75,7 +70,6 @@ many documents of similar size should therefore keep one document and call `read
|
||||
|
||||
- [parse](parse.md) - deserialize from a compatible input
|
||||
- [root](root.md) - the view of the root value
|
||||
- [load](load.md) - read a document from an image instead of parsing JSON text
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
# <small>nlohmann::basic_json_document::</small>save
|
||||
|
||||
```cpp
|
||||
std::vector<std::uint8_t> save() const;
|
||||
```
|
||||
|
||||
Writes the document as an *image*: a byte buffer that [`load`](load.md) reads back without parsing. The image holds
|
||||
the node index, the source text (plus, for an edited document, the number tokens edits wrote), and the decoded
|
||||
strings (plus the strings edits wrote) -- everything [`root()`](root.md) needs, with nothing left to parse.
|
||||
|
||||
An edited document is written in its *current* state, with its values in document order, the way the library's own
|
||||
parser would have produced them for that JSON text: a member [`set`](set.md) added goes at the end, an
|
||||
[`erase`](erase.md)d member leaves no trace, and a float that is not finite (NaN or positive/negative infinity)
|
||||
becomes null, the same substitution [`dump()`](../basic_json_view/dump.md) makes. The same document always saves to
|
||||
the same bytes -- also across `BasicJsonType` and `#!cpp Editable`, since the image reflects document order and
|
||||
values only, not which specialization produced them.
|
||||
|
||||
## Return value
|
||||
|
||||
The image, as a `#!cpp std::vector<std::uint8_t>`. Pass it, or a pointer to its data together with its size, to
|
||||
[`load`](load.md) to read the document back.
|
||||
|
||||
## Exception safety
|
||||
|
||||
Strong guarantee: `save()` does not modify `#!cpp *this` (it is `#!cpp const`), so if it throws, the document is left
|
||||
exactly as it was, and the partially built image is discarded with the exception.
|
||||
|
||||
## Exceptions
|
||||
|
||||
Throws [`type_error.320`](../../home/exceptions.md#jsonexceptiontype_error320) if the document is
|
||||
[discarded](is_discarded.md) -- a default-constructed document, or one a failed [`parse()`](parse.md)/
|
||||
[`read()`](read.md) with `allow_exceptions == false` left discarded.
|
||||
|
||||
On a big-endian target, throws `type_error.320` with a different message instead: the image format is little-endian
|
||||
only (see [Notes](#notes)).
|
||||
|
||||
Throws [`out_of_range.416`](../../home/exceptions.md#jsonexceptionout_of_range416) if the node count, the text, or
|
||||
the decoded strings of the image would individually reach 4 GiB -- the same 32-bit offsets
|
||||
[`parse()`](parse.md#exceptions) and, for edits, [`set`](set.md)/[`push_back`](push_back.md) are already limited to.
|
||||
|
||||
!!! failure "Example messages"
|
||||
|
||||
```
|
||||
[json.exception.type_error.320] cannot save a discarded json_document
|
||||
```
|
||||
```
|
||||
[json.exception.type_error.320] json_document images need a little-endian target
|
||||
```
|
||||
```
|
||||
[json.exception.out_of_range.416] images of 4 GiB or more are not supported by json_document
|
||||
```
|
||||
|
||||
## Complexity
|
||||
|
||||
Linear in the size of the document: the number of nodes, plus the length of the text and the decoded strings that end
|
||||
up in the image.
|
||||
|
||||
## Notes
|
||||
|
||||
**Format.** The image begins with a 64-byte header (the magic bytes `#!cpp "NJVI"`, a version number, the node count,
|
||||
and the sizes of the text and the decoded strings, all little-endian), followed by the nodes
|
||||
([16 bytes each](../../home/architecture.md#node-index-of-json-views)), the text and a `#!cpp '\0'`, and the decoded
|
||||
strings and a `#!cpp '\0'`. [`load`](load.md) checks the header, and the sizes it describes, before reading anything
|
||||
else -- see [`load`'s Exceptions](load.md#exceptions).
|
||||
|
||||
!!! warning "Experimental"
|
||||
|
||||
The image format is versioned but not yet stable: it may change in an incompatible way before it is declared
|
||||
stable. Use images to cache a document within one build of the library, or to hand one to another process running
|
||||
the *same* build on the *same* (little-endian) machine -- not as a long-term storage format. Keep the original
|
||||
JSON text if you need to read a saved document back with a future library version.
|
||||
|
||||
**Little-endian only.** The image is written as raw little-endian bytes, with no byte-swapping. `save()` (and
|
||||
[`load`](load.md)) throw `type_error.320` on a big-endian target rather than silently produce bytes a big-endian
|
||||
reader could not interpret correctly.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example "Caching a parsed document as an image"
|
||||
|
||||
The example below saves a parsed configuration as an image -- the way a service might cache one to answer later
|
||||
requests without parsing the text again -- and confirms that loading it back gives exactly the same result as
|
||||
parsing did, and that saving is deterministic.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_document__save.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_document__save.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [load](load.md) - read an image written by `save()`
|
||||
- [owns_source](owns_source.md) - return whether the document holds its own copy of the text
|
||||
- [`basic_json_view::dump`](../basic_json_view/dump.md) - serialize the document to JSON text instead of an image
|
||||
- [Images](../../features/json_view.md#images) - why and when to use images
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -1,194 +0,0 @@
|
||||
# <small>nlohmann::basic_json_document::</small>set
|
||||
|
||||
```cpp
|
||||
// (1)
|
||||
template<typename V>
|
||||
view_type set(view_type target, V&& value);
|
||||
|
||||
// (2)
|
||||
template<typename V>
|
||||
view_type set(view_type object, string_view_t key, V&& value);
|
||||
|
||||
// (3)
|
||||
template<typename I, typename V>
|
||||
view_type set(view_type array, I idx, V&& value);
|
||||
|
||||
// (4)
|
||||
template<typename V>
|
||||
view_type set(const json_pointer& ptr, V&& value);
|
||||
```
|
||||
|
||||
Only an **editable** document (`#!cpp Editable == true`, e.g. [`json_editable_document`](../json_editable_document.md))
|
||||
has `set`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`).
|
||||
|
||||
1. Replaces the value `target` refers to with `value`.
|
||||
2. Sets the member `key` of the object `object` to `value`: assigns it if `object` already has a member with this
|
||||
key -- the first one, should the key occur more than once, and the later duplicates are then dropped (see the
|
||||
[Notes](#notes) below) -- or appends a new member at the end otherwise. A [null](../basic_json_view/is_null.md)
|
||||
`object` first becomes an empty object.
|
||||
3. Assigns `value` to the element at index `idx` of the array `array`, which must already exist (`#!cpp idx <
|
||||
array.size()`).
|
||||
4. Sets the value the JSON pointer `ptr` refers to, relative to [`root()`](root.md), to `value`. The *parent* of the
|
||||
target must already exist: an object member is set as in 2. (added if it does not exist yet), an array element is
|
||||
assigned as in 3., and a last reference token of `#!cpp "-"`, or equal to the size of the array, appends `value`
|
||||
instead, exactly as [`push_back`](push_back.md) would. An empty `ptr` sets [`root()`](root.md) itself, as in 1.
|
||||
|
||||
In every overload, `value` is accepted three ways: a [`basic_json_view`](../basic_json_view/index.md) of *any*
|
||||
document -- read-only or editable, and it does not have to be `target`'s/`object`'s/`array`'s own document -- which
|
||||
is copied so that nothing is shared with the source document afterward; a `BasicJsonType` value; or anything
|
||||
`BasicJsonType` can be constructed from (numbers, strings, `#!cpp bool`, `#!cpp nullptr`, containers, ...).
|
||||
|
||||
## Template parameters
|
||||
|
||||
`V`
|
||||
: the type of `value`, deduced; see above for what is accepted.
|
||||
|
||||
`I`
|
||||
: an integral type other than `#!cpp bool`, deduced (overloads taking a `#!cpp bool` or a non-integral type for
|
||||
`idx` do not participate in overload resolution).
|
||||
|
||||
## Parameters
|
||||
|
||||
`target` (in)
|
||||
: the value to replace
|
||||
|
||||
`object` (in)
|
||||
: the object (or null value) whose member to set
|
||||
|
||||
`array` (in)
|
||||
: the array whose element to assign
|
||||
|
||||
`key` (in)
|
||||
: the key of the member to set
|
||||
|
||||
`idx` (in)
|
||||
: the index of the element to assign; a negative value throws (see [Exceptions](#exceptions))
|
||||
|
||||
`ptr` (in)
|
||||
: a JSON pointer to the value to set, relative to `root()`
|
||||
|
||||
`value` (in)
|
||||
: the new value
|
||||
|
||||
## Return value
|
||||
|
||||
1. a view of `target`, now holding `value`
|
||||
2. a view of the member `key` of `object`, now holding `value`
|
||||
3. a view of the element `idx` of `array`, now holding `value`
|
||||
4. a view of the value `ptr` refers to, now holding `value`
|
||||
|
||||
## Exception safety
|
||||
|
||||
Basic exception safety: `value` is fully encoded -- including the checks below -- into storage owned by the document
|
||||
before anything already reachable from [`root()`](root.md) is touched, so a failure while encoding `value` (an
|
||||
invalid argument, or `#!cpp std::bad_alloc`) leaves the document completely unchanged, other than memory allocated
|
||||
for the encoding that is not reclaimed. A failure of a later allocation -- while an edited array or object switches
|
||||
from its parsed layout to a growable block, see [Notes](#notes) -- can still leave a partial effect, such as a
|
||||
[null](../basic_json_view/is_null.md) `object`/`array` argument already turned into an empty object/array even
|
||||
though `value` itself was not linked in.
|
||||
|
||||
## Exceptions
|
||||
|
||||
1. Throws [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if `value` is a
|
||||
[discarded](../basic_json_view/is_discarded.md) view, or a [discarded](../basic_json/is_discarded.md)
|
||||
`BasicJsonType` value (e.g. `#!cpp BasicJsonType(value_t::discarded)`) -- an object or array `value`, of either
|
||||
kind, is fine and is encoded as a whole subtree.
|
||||
2. Throws [`type_error.305`](../../home/exceptions.md#jsonexceptiontype_error305) if `object` is neither an object
|
||||
nor null -- the same message [`operator[]`](../basic_json_view/operator%5B%5D.md) throws for a string argument on
|
||||
such a value. Throws [`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) if `key` is not
|
||||
valid UTF-8, with the same message [`BasicJsonType::dump()`](../basic_json/dump.md) gives for that string.
|
||||
Also throws what 1. throws for `value`.
|
||||
3. Throws `type_error.305` if `array` is not an array -- the same message `operator[]` throws for a numeric argument
|
||||
on such a value. Throws [`out_of_range.401`](../../home/exceptions.md#jsonexceptionout_of_range401) if `idx` is
|
||||
negative, or if `#!cpp idx >= array.size()`. Also throws what 1. throws for `value`.
|
||||
4. Throws what [`at`](../basic_json_view/at.md) throws (overload 3) for resolving `ptr`'s parent, except that a
|
||||
missing object member or an array index equal to the array's size at the very last reference token is not an
|
||||
error there (it becomes a new member or an appended element) instead of
|
||||
[`out_of_range.403`](../../home/exceptions.md#jsonexceptionout_of_range403)/[`out_of_range.402`](../../home/exceptions.md#jsonexceptionout_of_range402).
|
||||
For the last reference token itself: if the parent is an object (or a primitive value, where it throws
|
||||
`type_error.305`), throws what 2. throws; if the parent is an array, throws what 3. throws for an index that is
|
||||
out of range, or, for a token that is not a valid array index,
|
||||
[`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) (a leading `#!cpp '0'`),
|
||||
[`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) (not a number),
|
||||
[`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) (too large for `size_type`), or
|
||||
[`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) (an empty token). Also throws what 1.
|
||||
throws for `value`.
|
||||
|
||||
Every overload also throws [`type_error.319`](../../home/exceptions.md#jsonexceptiontype_error319) if `value` is (or
|
||||
contains) a binary value -- `BasicJsonType` can hold one, but a `json_document` cannot -- and
|
||||
[`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) ("view does not belong to this
|
||||
document") if `target`/`object`/`array` is a [discarded](../basic_json_view/is_discarded.md) view or a view of a
|
||||
*different* document (overloads 1-3 only; overload 4 always starts from this document's own [`root()`](root.md)).
|
||||
|
||||
## Complexity
|
||||
|
||||
1. Linear in the size of `value` (encoding it into the document's storage): constant for a scalar, linear in the
|
||||
number of nested values for an array or object. If `target` is itself an array or object that spans more than one
|
||||
node in its parent's original, unedited layout, and `value` is a scalar, replacing it additionally costs time
|
||||
linear in the number of elements of that parent, the *first* time -- see [Notes](#notes).
|
||||
2. Linear in the number of members of `object`, to find an existing member with `key`, plus the complexity of 1. for
|
||||
`value`.
|
||||
3. Constant, plus the complexity of 1. for `value`.
|
||||
4. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
||||
that level or the index into the array (as [`at`](../basic_json_view/at.md)), plus the complexity of 2. or 3. for
|
||||
the last token.
|
||||
|
||||
## Notes
|
||||
|
||||
!!! info "Duplicate keys"
|
||||
|
||||
If `object` already has more than one member with `key` (2.), the *first* one is assigned `value` and every
|
||||
later member with the same key is removed -- so that a lookup, an iteration, and
|
||||
[`materialize()`](../basic_json_view/materialize.md) of `object` afterward all agree on a single value for
|
||||
`key`, the same way [`operator[]`](../basic_json_view/operator%5B%5D.md) already picks the first occurrence of a
|
||||
duplicate key for reading. See the [Notes on duplicate keys](../basic_json_view/operator%5B%5D.md#notes) of
|
||||
`operator[]`.
|
||||
|
||||
Setting a member (2.) or an element (3., through 4.) of an array or object whose elements have not been edited
|
||||
before switches it from its parsed layout to a growable block holding links to its elements; a later
|
||||
[`push_back`](push_back.md) or `set` on the same container reuses that block, growing it (amortized constant time)
|
||||
only once it runs out of room. This never moves an element itself -- only where the container's *links* to its
|
||||
elements live -- so a view of an element stays valid, but any iterator already taken over the container is
|
||||
invalidated, since it was walking the old layout. See [Edits](index.md#edits) for what stays valid across an edit in
|
||||
general.
|
||||
|
||||
The same switch happens, for the same reason, when overload 1. replaces a multi-node array/object value with a
|
||||
scalar: the *parent's* element sequence is what has to switch to links, not `target` itself, because the parent
|
||||
originally stepped over `target`'s whole subtree by its node count, which no longer applies once `target` is a
|
||||
one-node scalar.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example "Example: (1)/(2)/(3)/(4) replace a value, set a member, assign an element, set via a JSON pointer"
|
||||
|
||||
The example below edits a small configuration document -- replacing a value, adding an object member, assigning
|
||||
an array element, and reaching a field through a JSON pointer -- and shows what
|
||||
[`dump()`](../basic_json_view/dump.md) preserves that is lost once the same edits are made on a `BasicJsonType`
|
||||
value instead: the order object members were written in, and the exact spelling of a number that was never
|
||||
touched.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_document__set.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_document__set.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [push_back](push_back.md) - append to an array
|
||||
- [insert](insert.md) - insert an element into an array
|
||||
- [erase](erase.md) - remove an object member, an array element, or the value a JSON pointer refers to
|
||||
- [root](root.md) - the view of the root value, the starting point of overload 4
|
||||
- [`basic_json_view::dump`](../basic_json_view/dump.md) - serialize the document, keeping an untouched number's
|
||||
spelling with `#!cpp number_format::source`
|
||||
- [Edits](index.md#edits) - what an edit guarantees, for every overload
|
||||
- [Editing a document](../../features/json_view.md#editing-a-document) - why editable documents keep the source
|
||||
text's order and number spelling
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -74,8 +74,6 @@ None of these exceptions carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics
|
||||
1. Linear in the number of members: as for [`ordered_json`](../ordered_json.md), members are compared one after
|
||||
another, in document order, stopping at the first match. Each comparison first checks the key's length --
|
||||
already known from the index, without reading the key bytes -- before comparing its content.
|
||||
Objects with 128 or more members get a hash index while parsing, so that a lookup in them takes constant time
|
||||
on average.
|
||||
2. Linear in `idx`: elements are skipped one at a time from the first one, since they are not a fixed size in the
|
||||
index (unlike `BasicJsonType`'s array, which is random-access).
|
||||
3. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
||||
|
||||
@@ -35,8 +35,6 @@ No-throw guarantee: this function never throws exceptions.
|
||||
1. Linear in the number of members: as for [`ordered_json`](../ordered_json.md), members are compared one after
|
||||
another, in document order, stopping at the first match. Each comparison first checks the key's length -- already
|
||||
known from the index, without reading the key bytes -- before comparing its content.
|
||||
Objects with 128 or more members get a hash index while parsing, so that a lookup in them takes constant time
|
||||
on average.
|
||||
2. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
||||
that level or the index into the array -- as for [`operator[]`](operator[].md#complexity) and
|
||||
[`at`](at.md#complexity) with a JSON pointer.
|
||||
|
||||
@@ -26,8 +26,6 @@ No-throw guarantee: this function never throws exceptions.
|
||||
Linear in the number of members: as for [`ordered_json`](../ordered_json.md), members are compared one after
|
||||
another, in document order, stopping at the first match. Each comparison first checks the key's length -- already
|
||||
known from the index, without reading the key bytes -- before comparing its content.
|
||||
Objects with 128 or more members get a hash index while parsing, so that a lookup in them takes constant time on
|
||||
average.
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
@@ -28,8 +28,6 @@ No-throw guarantee: this function never throws exceptions.
|
||||
Linear in the number of members: as for [`ordered_json`](../ordered_json.md), members are compared one after
|
||||
another, in document order, stopping at the first match. Each comparison first checks the key's length -- already
|
||||
known from the index, without reading the key bytes -- before comparing its content.
|
||||
Objects with 128 or more members get a hash index while parsing, so that a lookup in them takes constant time on
|
||||
average.
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<small>Defined in header `<nlohmann/json_view.hpp>`</small>
|
||||
|
||||
```cpp
|
||||
template<typename BasicJsonType, bool Editable = false>
|
||||
template<typename BasicJsonType>
|
||||
class basic_json_view;
|
||||
```
|
||||
|
||||
@@ -27,30 +27,16 @@ subtree on demand. [`operator[]`](operator%5B%5D.md), [`at`](at.md), [`contains`
|
||||
[`operator!=`](operator_ne.md) compare two views, or a view and a `BasicJsonType` value, without ever building a
|
||||
`BasicJsonType` value for a view; no ordering comparison (`#!cpp operator<`) is provided.
|
||||
|
||||
`basic_json_view` itself is always read-only -- it never has a `set` or `push_back` of its own. A view of an
|
||||
**editable** document (`#!cpp Editable == true`) sees every edit made through
|
||||
[`basic_json_document::set`](../basic_json_document/set.md) and
|
||||
[`basic_json_document::push_back`](../basic_json_document/push_back.md): once a value is changed, every view that
|
||||
still refers to it -- including ones taken before the change -- reads the new value. See
|
||||
[Edits](../basic_json_document/index.md#edits).
|
||||
|
||||
## Template parameters
|
||||
|
||||
`BasicJsonType`
|
||||
: a specialization of [`basic_json`](../basic_json/index.md), matching the
|
||||
[`basic_json_document`](../basic_json_document/index.md) the view was taken from.
|
||||
|
||||
`Editable`
|
||||
: whether the view is of an editable document, matching the [`basic_json_document`](../basic_json_document/index.md)
|
||||
it was taken from (optional, `#!cpp false` by default). See [Edits](../basic_json_document/index.md#edits).
|
||||
|
||||
## Specializations
|
||||
|
||||
- [**json_view**](../json_view.md) - views of a [`json_document`](../json_document.md)
|
||||
- [**ordered_json_view**](../ordered_json_view.md) - views of an [`ordered_json_document`](../ordered_json_document.md)
|
||||
- [**json_editable_view**](../json_editable_view.md) - views of a [`json_editable_document`](../json_editable_document.md)
|
||||
- [**ordered_json_editable_view**](../ordered_json_editable_view.md) - views of an
|
||||
[`ordered_json_editable_document`](../ordered_json_editable_document.md)
|
||||
|
||||
## Member types
|
||||
|
||||
|
||||
@@ -76,8 +76,6 @@ None of these exceptions carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics
|
||||
another, in document order, stopping at the first match. Each comparison first checks the key's length --
|
||||
already known from the index, without reading the key bytes -- before comparing its content, so a key of a
|
||||
different length than `key` is rejected without touching the source text.
|
||||
Objects with 128 or more members get a hash index while parsing, so that a lookup in them takes constant time
|
||||
on average.
|
||||
2. Linear in `idx`: elements are skipped one at a time from the first one, since they are not a fixed size in the
|
||||
index (unlike `BasicJsonType`'s array, which is random-access).
|
||||
3. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
||||
|
||||
@@ -70,8 +70,6 @@ None of these exceptions carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics
|
||||
1. Linear in the number of members: as for [`operator[]`](operator[].md#complexity), members are compared one after
|
||||
another, in document order, stopping at the first match. Plus the complexity of converting the found member to
|
||||
`T` (see [`get`](get.md)).
|
||||
Objects with 128 or more members get a hash index while parsing, so that a lookup in them takes constant time
|
||||
on average.
|
||||
2. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
||||
that level or the index into the array -- as for the [`operator[]`](operator[].md#complexity) and
|
||||
[`at`](at.md#complexity) overloads that take a JSON pointer. Plus the complexity of converting the resolved value
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
# <small>nlohmann::</small>json_editable_document
|
||||
|
||||
<small>Defined in header `<nlohmann/json_view.hpp>`</small>
|
||||
|
||||
```cpp
|
||||
using json_editable_document = basic_json_document<json, true>;
|
||||
```
|
||||
|
||||
This type is an **editable** [`basic_json_document`](basic_json_document/index.md) of the default
|
||||
[`json`](json.md) specialization: in addition to everything [`json_document`](json_document.md) offers,
|
||||
[`set`](basic_json_document/set.md) and [`push_back`](basic_json_document/push_back.md) change values after
|
||||
parsing, without ever rewriting the source text -- see [Edits](basic_json_document/index.md#edits) and
|
||||
[Editing a document](../features/json_view.md#editing-a-document).
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below patches two fields of a small configuration document -- changing one and adding another --
|
||||
and dumps it back out with the member order and the spelling of the untouched number preserved, something a
|
||||
plain [`json`](json.md) value cannot do.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/json_editable_document.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/json_editable_document.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [json_editable_view](json_editable_view.md) - a view of a value of a `json_editable_document`
|
||||
- [json_document](json_document.md) - the read-only document this type adds edits to
|
||||
- [ordered_json_editable_document](ordered_json_editable_document.md) - the corresponding editable document for
|
||||
`ordered_json`
|
||||
- [Edits](basic_json_document/index.md#edits) - what an edit guarantees
|
||||
|
||||
## Version history
|
||||
|
||||
Since version 3.13.0.
|
||||
@@ -1,41 +0,0 @@
|
||||
# <small>nlohmann::</small>json_editable_view
|
||||
|
||||
<small>Defined in header `<nlohmann/json_view.hpp>`</small>
|
||||
|
||||
```cpp
|
||||
using json_editable_view = basic_json_view<json, true>;
|
||||
```
|
||||
|
||||
This type is a [`basic_json_view`](basic_json_view/index.md) of a value of a
|
||||
[`json_editable_document`](json_editable_document.md). It offers the same read-only interface as
|
||||
[`json_view`](json_view.md); what is different is what it can be a view *of* -- a value that
|
||||
[`set`](basic_json_document/set.md) and [`push_back`](basic_json_document/push_back.md) can change, with every view
|
||||
still referring to it seeing the change, see [Edits](basic_json_document/index.md#edits).
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below is the same as [`json_editable_document`'s](json_editable_document.md): every view read back
|
||||
out of the document -- `#!cpp doc.root()` and the views nested under it -- sees the edits made through `set`.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/json_editable_document.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/json_editable_document.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [json_editable_document](json_editable_document.md) - the document type this view refers into
|
||||
- [json_view](json_view.md) - the corresponding read-only view
|
||||
- [ordered_json_editable_view](ordered_json_editable_view.md) - the corresponding view for
|
||||
`ordered_json_editable_document`
|
||||
|
||||
## Version history
|
||||
|
||||
Since version 3.13.0.
|
||||
@@ -37,8 +37,6 @@ header. See also the [macro overview page](../../features/macros.md).
|
||||
- [**JSON_SKIP_UNSUPPORTED_COMPILER_CHECK**](json_skip_unsupported_compiler_check.md) - do not warn about unsupported compilers
|
||||
- [**JSON_USE_GLOBAL_UDLS**](json_use_global_udls.md) - place user-defined string literals (UDLs) into the global namespace
|
||||
- [**JSON_USE_SIMDUTF**](json_use_simdutf.md) - use the simdutf library to accelerate UTF-8 validation
|
||||
- [**JSON_VIEW_NO_SIMD**](json_view_no_simd.md) - use only portable code in the parser of `json_view.hpp`
|
||||
- [**JSON_VIEW_USE_SSSE3**](json_view_use_ssse3.md) - validate non-ASCII strings with SSSE3 in the parser of `json_view.hpp`
|
||||
|
||||
## Library version
|
||||
|
||||
@@ -60,6 +58,13 @@ header. See also the [macro overview page](../../features/macros.md).
|
||||
- [**JSON_DISABLE_ENUM_SERIALIZATION**](json_disable_enum_serialization.md) - switch off default serialization/deserialization functions for enums
|
||||
- [**JSON_DISABLE_TUPLE_REFERENCE_CONVERSION**](json_disable_tuple_reference_conversion.md) - switch off conversion from a one-element tuple of a JSON reference
|
||||
- [**JSON_USE_IMPLICIT_CONVERSIONS**](json_use_implicit_conversions.md) - control implicit conversions
|
||||
- [**JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS**](json_use_objects_for_enum_keyed_maps.md) - opt in to storing maps with enum
|
||||
keys as objects
|
||||
|
||||
## Deprecated functions
|
||||
|
||||
- [**JSON_DELETE_DEPRECATED_FUNCTIONS**](json_delete_deprecated_functions.md) - opt in to deleting the deprecated
|
||||
functions ahead of their removal in version 4.0.0
|
||||
|
||||
## Comparison behavior
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
# JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
|
||||
```cpp
|
||||
#define JSON_DELETE_DEPRECATED_FUNCTIONS /* value */
|
||||
```
|
||||
|
||||
When defined to `1`, all [deprecated functions](../../community/roadmap.md#removal-of-deprecated-functions) of the
|
||||
library are declared as deleted (`= delete`) instead of only being marked as deprecated. Code that still calls one of
|
||||
them no longer compiles. This way, you can find all calls that need to be replaced before version 4.0.0 removes these
|
||||
functions; the [migration guide](../../integration/migration_guide.md#replace-deprecated-functions) describes how.
|
||||
|
||||
A deleted function, unlike a removed one, still takes part in overload resolution. A call that would select it
|
||||
therefore fails to compile instead of silently selecting another overload. This matters for the deprecated
|
||||
`from_*(ptr, len)` overloads of [`from_cbor`](../basic_json/from_cbor.md), [`from_msgpack`](../basic_json/from_msgpack.md),
|
||||
[`from_ubjson`](../basic_json/from_ubjson.md), [`from_bjdata`](../basic_json/from_bjdata.md),
|
||||
[`from_bon8`](../basic_json/from_bon8.md), and [`from_bson`](../basic_json/from_bson.md): without them, a call like
|
||||
`from_cbor(ptr, len)` would compile, read `ptr` as a NUL-terminated string, and convert `len` to the `strict` parameter.
|
||||
|
||||
The macro does not affect the deprecated legacy comparison of discarded values, which is controlled by
|
||||
[`JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON`](json_use_legacy_discarded_value_comparison.md).
|
||||
|
||||
## Default definition
|
||||
|
||||
The default value is `0` (disabled, the deprecated functions can still be called, and the compiler warns about it).
|
||||
|
||||
```cpp
|
||||
#define JSON_DELETE_DEPRECATED_FUNCTIONS 0
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
!!! info "CMake option"
|
||||
|
||||
The macro can also be set with the CMake option
|
||||
[`JSON_DeleteDeprecatedFunctions`](../../integration/cmake.md#json_deletedeprecatedfunctions) (`OFF` by default).
|
||||
|
||||
!!! warning "Opt-in only"
|
||||
|
||||
This macro must be defined **before** including `<nlohmann/json.hpp>`. Defining it after the include has no
|
||||
effect. Define it for the whole project to avoid different declarations of the same class in different
|
||||
translation units.
|
||||
|
||||
!!! note "ABI compatibility"
|
||||
|
||||
The macro only turns calls that compile into calls that do not; it does not change the layout or the behavior of
|
||||
any type. Its value is therefore not encoded in the [namespace](../../features/namespace.md).
|
||||
|
||||
## Examples
|
||||
|
||||
??? example "Example: default behavior (macro not defined)"
|
||||
|
||||
Without the macro, the deprecated overload is called, and the compiler warns about it:
|
||||
|
||||
```cpp
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
int main()
|
||||
{
|
||||
const std::vector<std::uint8_t> v = {0x82, 0x01, 0x02};
|
||||
auto j = json::from_cbor(v.data(), v.size());
|
||||
// warning: 'from_cbor' is deprecated: Since 3.8.0; use from_cbor(ptr, ptr + len)
|
||||
}
|
||||
```
|
||||
|
||||
??? example "Example: deleted deprecated functions (macro defined to 1)"
|
||||
|
||||
With the macro, the call does not compile:
|
||||
|
||||
```cpp
|
||||
#define JSON_DELETE_DEPRECATED_FUNCTIONS 1
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
int main()
|
||||
{
|
||||
const std::vector<std::uint8_t> v = {0x82, 0x01, 0x02};
|
||||
auto j = json::from_cbor(v.data(), v.size());
|
||||
// error: call to deleted function 'from_cbor'
|
||||
}
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Roadmap: removal of deprecated functions](../../community/roadmap.md#removal-of-deprecated-functions) - the
|
||||
deprecated functions and the version they were deprecated in
|
||||
- [Migration guide: replace deprecated functions](../../integration/migration_guide.md#replace-deprecated-functions) -
|
||||
how to replace each deprecated function
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
- Planned to be removed in version 4.0.0, which removes the deprecated functions. The deprecated `from_*(ptr, len)`
|
||||
overloads stay deleted in version 4.0.0.
|
||||
@@ -112,3 +112,4 @@ The default value is `0` (disabled — existing behavior is preserved).
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
- Planned to become the default (with the macro removed) in version 4.0.0.
|
||||
@@ -44,7 +44,7 @@ By default, implicit conversions are enabled.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example "Example: implicit conversion"
|
||||
??? example "Example: implicit and explicit conversions"
|
||||
|
||||
This is an example for an implicit conversion:
|
||||
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
# JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
|
||||
```cpp
|
||||
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS /* value */
|
||||
```
|
||||
|
||||
When defined to `1`, maps whose keys are enums (such as `std::map<E, T>` or `std::unordered_map<E, T>`) are stored as
|
||||
JSON objects, using the enum's own conversion for the keys. By default, they are stored as arrays of `[key, value]`
|
||||
pairs.
|
||||
|
||||
## Default definition
|
||||
|
||||
The default value is `0` (disabled — existing behavior is preserved).
|
||||
|
||||
```cpp
|
||||
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 0
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
!!! note "Background"
|
||||
|
||||
JSON object keys are strings, so a map is only stored as an object if its keys can be converted to a string type.
|
||||
Enums are not, even if [`NLOHMANN_JSON_SERIALIZE_ENUM`](nlohmann_json_serialize_enum.md) maps them to strings, so a
|
||||
map with enum keys becomes an array of `[key, value]` pairs:
|
||||
|
||||
```json
|
||||
[["stopped", "aa"], ["completed", "bb"]]
|
||||
```
|
||||
|
||||
With this macro, the same map becomes an object
|
||||
(see [#4378](https://github.com/nlohmann/json/issues/4378)):
|
||||
|
||||
```json
|
||||
{"completed": "bb", "stopped": "aa"}
|
||||
```
|
||||
|
||||
!!! note "Maps with non-unique keys"
|
||||
|
||||
Maps that allow duplicate keys, such as `std::multimap<E, T>` or `std::unordered_multimap<E, T>`, are not affected
|
||||
by the macro and are still stored as arrays of `[key, value]` pairs, as an object cannot hold duplicate keys.
|
||||
|
||||
!!! note "Reading"
|
||||
|
||||
Reading is not affected by the macro: a map with enum keys can always be read from both an array of pairs and an
|
||||
object. For the latter, each key is converted to the enum with its `from_json` function, e.g., the one defined by
|
||||
[`NLOHMANN_JSON_SERIALIZE_ENUM`](nlohmann_json_serialize_enum.md). Data written without the macro can therefore
|
||||
still be read after enabling it.
|
||||
|
||||
!!! warning "Keys must serialize to distinct strings"
|
||||
|
||||
Each key is converted with the enum's `to_json` function. If a key is not converted to a string (for instance, an
|
||||
enum without [`NLOHMANN_JSON_SERIALIZE_ENUM`](nlohmann_json_serialize_enum.md), which is stored as an integer, or an
|
||||
enumerator mapped to `nullptr`), [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) is thrown.
|
||||
If two keys are converted to the same string (for instance, because
|
||||
[`NLOHMANN_JSON_SERIALIZE_ENUM`](nlohmann_json_serialize_enum.md) maps an unlisted enumerator to the first entry),
|
||||
[`type_error.318`](../../home/exceptions.md#jsonexceptiontype_error318) is thrown. In both cases, the target value
|
||||
is not changed.
|
||||
|
||||
!!! warning "Opt-in only"
|
||||
|
||||
This macro must be defined **before** including `<nlohmann/json.hpp>`. Defining it after the include has no effect.
|
||||
|
||||
!!! note "ABI compatibility"
|
||||
|
||||
The value of this macro is encoded in the [namespace](../../features/namespace.md) (tag `_ekmo`), 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.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example "Example: default behavior (macro not defined)"
|
||||
|
||||
Without the macro, a map with enum keys is stored as an array of pairs:
|
||||
|
||||
```cpp
|
||||
#include <map>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
enum TaskState { TS_STOPPED, TS_RUNNING, TS_COMPLETED };
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(TaskState, {
|
||||
{TS_STOPPED, "stopped"},
|
||||
{TS_RUNNING, "running"},
|
||||
{TS_COMPLETED, "completed"},
|
||||
})
|
||||
|
||||
int main()
|
||||
{
|
||||
std::map<TaskState, std::string> m = {{TS_STOPPED, "aa"}, {TS_COMPLETED, "bb"}};
|
||||
|
||||
json j = m;
|
||||
// j is [["stopped","aa"],["completed","bb"]]
|
||||
}
|
||||
```
|
||||
|
||||
??? example "Example: objects for enum-keyed maps (macro defined to 1)"
|
||||
|
||||
With the macro, the same map is stored as an object:
|
||||
|
||||
```cpp
|
||||
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 1
|
||||
#include <map>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
enum TaskState { TS_STOPPED, TS_RUNNING, TS_COMPLETED };
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(TaskState, {
|
||||
{TS_STOPPED, "stopped"},
|
||||
{TS_RUNNING, "running"},
|
||||
{TS_COMPLETED, "completed"},
|
||||
})
|
||||
|
||||
int main()
|
||||
{
|
||||
std::map<TaskState, std::string> m = {{TS_STOPPED, "aa"}, {TS_COMPLETED, "bb"}};
|
||||
|
||||
json j = m;
|
||||
// j is {"completed":"bb","stopped":"aa"}
|
||||
|
||||
auto m2 = j.get<std::map<TaskState, std::string>>();
|
||||
// m2 == m
|
||||
}
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Specializing enum conversion](../../features/enum_conversion.md)
|
||||
- [**NLOHMANN_JSON_SERIALIZE_ENUM**](nlohmann_json_serialize_enum.md) - serialize/deserialize an enum
|
||||
- [**NLOHMANN_JSON_SERIALIZE_ENUM_STRICT**](nlohmann_json_serialize_enum_strict.md) - serialize/deserialize an enum with
|
||||
exceptions
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -1,50 +0,0 @@
|
||||
# JSON_VIEW_NO_SIMD
|
||||
|
||||
```cpp
|
||||
#define JSON_VIEW_NO_SIMD
|
||||
```
|
||||
|
||||
When defined, the parser of [`basic_json_document`](../basic_json_document/index.md) (`<nlohmann/json_view.hpp>`)
|
||||
uses only portable C++ to scan strings. By default, it scans long runs of string bytes 16 at a time with NEON on
|
||||
AArch64 (with GCC and Clang) and SSE2 on x86-64, which are part of the baseline instruction sets of these
|
||||
architectures, and validates non-ASCII text with NEON (or SSSE3, see
|
||||
[`JSON_VIEW_USE_SSSE3`](json_view_use_ssse3.md)).
|
||||
|
||||
The same input is accepted or rejected either way, with the same values, and errors are reported the same way; only
|
||||
the speed differs. The macro exists for platforms whose compilers lack the intrinsics headers, and to test the portable
|
||||
code.
|
||||
|
||||
!!! warning "Define consistently"
|
||||
|
||||
The macro selects between two definitions of the same inline functions. It must therefore be defined identically for
|
||||
**every** translation unit that includes `<nlohmann/json_view.hpp>`; prefer a compile definition on the target.
|
||||
|
||||
## Default definition
|
||||
|
||||
By default, `#!cpp JSON_VIEW_NO_SIMD` is not defined, and the vector code is used where available.
|
||||
|
||||
```cpp
|
||||
#undef JSON_VIEW_NO_SIMD
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The code below uses the portable string scanning of the view.
|
||||
|
||||
```cpp
|
||||
#define JSON_VIEW_NO_SIMD
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [JSON_VIEW_USE_SSSE3](json_view_use_ssse3.md) - validate non-ASCII strings with SSSE3 on x86-64
|
||||
- [json_view](../../features/json_view.md) - the zero-copy view
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -1,53 +0,0 @@
|
||||
# JSON_VIEW_USE_SSSE3
|
||||
|
||||
```cpp
|
||||
#define JSON_VIEW_USE_SSSE3
|
||||
```
|
||||
|
||||
When defined on x86-64, the parser of [`basic_json_document`](../basic_json_document/index.md)
|
||||
(`<nlohmann/json_view.hpp>`) validates non-ASCII text in strings with SSSE3 without asking the CPU first.
|
||||
|
||||
By default, the parser checks once at run time whether the CPU has SSSE3 (all x86-64 CPUs since about 2011 have it)
|
||||
and then validates non-ASCII text 16 bytes at a time, using the "lookup4" algorithm of
|
||||
[simdjson](https://github.com/simdjson/simdjson); on CPUs without SSSE3, it validates one UTF-8 sequence at a time.
|
||||
The vector check is compiled for SSSE3 with a function attribute (GCC 4.9 and later, Clang), so this needs no compiler
|
||||
option. With MSVC, the check uses `__cpuid`. On AArch64, the vector check uses NEON and is always on.
|
||||
|
||||
Define the macro only together with a compiler option that enables SSSE3 (e.g. `-mssse3`, or `-march=` with a CPU that
|
||||
has it), and only for programs that run on such CPUs. It saves the check of the CPU, which costs little. The same
|
||||
input is accepted or rejected either way; only the speed of non-ASCII text differs.
|
||||
|
||||
!!! warning "Define consistently"
|
||||
|
||||
The macro selects between two definitions of the same inline functions. It must therefore be defined identically,
|
||||
with the same compiler options, for **every** translation unit that includes `<nlohmann/json_view.hpp>`; mixing
|
||||
translation units that define it with ones that do not is an ODR violation. Prefer a compile definition on the
|
||||
target.
|
||||
|
||||
## Default definition
|
||||
|
||||
By default, `#!cpp JSON_VIEW_USE_SSSE3` is not defined.
|
||||
|
||||
```cpp
|
||||
#undef JSON_VIEW_USE_SSSE3
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
With CMake, for a program that only runs on CPUs with SSSE3:
|
||||
|
||||
```cmake
|
||||
target_compile_definitions(your_target PRIVATE JSON_VIEW_USE_SSSE3)
|
||||
target_compile_options(your_target PRIVATE -mssse3)
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [JSON_VIEW_NO_SIMD](json_view_no_simd.md) - use only portable code in the view's parser
|
||||
- [JSON_USE_SIMDUTF](json_use_simdutf.md) - validate UTF-8 with simdutf in `basic_json`'s parser
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -41,6 +41,9 @@ inline void from_json(const BasicJsonType& j, type& e);
|
||||
conversion. Select this default pair carefully. See example 1 below.
|
||||
- If an enum or JSON value is specified in multiple conversions, the first matching conversion from the top of the
|
||||
list will be returned when converting to or from JSON. See example 2 below.
|
||||
- Maps with enum keys (e.g., `std::map<ENUM_TYPE, T>`) are stored as arrays of `[key, value]` pairs by default.
|
||||
Define [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](json_use_objects_for_enum_keyed_maps.md) to store them as objects
|
||||
with the converted keys. Such maps can be read from both forms.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -80,6 +83,7 @@ inline void from_json(const BasicJsonType& j, type& e);
|
||||
- [Specializing enum conversion](../../features/enum_conversion.md)
|
||||
- [`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`](./nlohmann_json_serialize_enum_strict.md)
|
||||
- [`JSON_DISABLE_ENUM_SERIALIZATION`](json_disable_enum_serialization.md)
|
||||
- [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](json_use_objects_for_enum_keyed_maps.md)
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -44,6 +44,9 @@ inline void from_json(const BasicJsonType& j, type& e);
|
||||
`"enum value out of range for <type>"`.
|
||||
- If an enum or JSON value is specified in multiple conversions, the first matching conversion from the top of the
|
||||
list will be returned when converting to or from JSON. See example 2 below.
|
||||
- Maps with enum keys (e.g., `std::map<ENUM_TYPE, T>`) are stored as arrays of `[key, value]` pairs by default.
|
||||
Define [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](json_use_objects_for_enum_keyed_maps.md) to store them as objects
|
||||
with the converted keys. Such maps can be read from both forms.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -99,6 +102,7 @@ inline void from_json(const BasicJsonType& j, type& e);
|
||||
- [Specializing enum conversion](../../features/enum_conversion.md)
|
||||
- [`NLOHMANN_JSON_SERIALIZE_ENUM`](./nlohmann_json_serialize_enum.md)
|
||||
- [`JSON_DISABLE_ENUM_SERIALIZATION`](json_disable_enum_serialization.md)
|
||||
- [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](json_use_objects_for_enum_keyed_maps.md)
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
# <small>nlohmann::</small>ordered_json_editable_document
|
||||
|
||||
<small>Defined in header `<nlohmann/json_view.hpp>`</small>
|
||||
|
||||
```cpp
|
||||
using ordered_json_editable_document = basic_json_document<ordered_json, true>;
|
||||
```
|
||||
|
||||
This type is an **editable** [`basic_json_document`](basic_json_document/index.md) of the
|
||||
[`ordered_json`](ordered_json.md) specialization: [`set`](basic_json_document/set.md) and
|
||||
[`push_back`](basic_json_document/push_back.md) change values after parsing, as for
|
||||
[`json_editable_document`](json_editable_document.md), and
|
||||
[`materialize()`](basic_json_view/materialize.md) preserves the document order of object members -- including
|
||||
members [`set`](basic_json_document/set.md) added -- instead of sorting them like
|
||||
[`json_editable_document`](json_editable_document.md) does.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below edits a document with `set`, then shows that `materialize()` keeps the member order of the
|
||||
source text (with the new member at the end) for `ordered_json_editable_document`, where it would sort the
|
||||
members alphabetically for [`json_editable_document`](json_editable_document.md).
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/ordered_json_editable_document.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/ordered_json_editable_document.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [ordered_json_editable_view](ordered_json_editable_view.md) - a view of a value of an
|
||||
`ordered_json_editable_document`
|
||||
- [ordered_json_document](ordered_json_document.md) - the read-only document this type adds edits to
|
||||
- [json_editable_document](json_editable_document.md) - the corresponding editable document for the default `json`
|
||||
specialization
|
||||
- [Object Order](../features/object_order.md)
|
||||
- [Edits](basic_json_document/index.md#edits) - what an edit guarantees
|
||||
|
||||
## Version history
|
||||
|
||||
Since version 3.13.0.
|
||||
@@ -1,40 +0,0 @@
|
||||
# <small>nlohmann::</small>ordered_json_editable_view
|
||||
|
||||
<small>Defined in header `<nlohmann/json_view.hpp>`</small>
|
||||
|
||||
```cpp
|
||||
using ordered_json_editable_view = basic_json_view<ordered_json, true>;
|
||||
```
|
||||
|
||||
This type is a [`basic_json_view`](basic_json_view/index.md) of a value of an
|
||||
[`ordered_json_editable_document`](ordered_json_editable_document.md), the corresponding view for
|
||||
[`ordered_json_view`](ordered_json_view.md) the way [`json_editable_view`](json_editable_view.md) is for
|
||||
[`json_view`](json_view.md).
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below is the same as [`ordered_json_editable_document`'s](ordered_json_editable_document.md): the
|
||||
views `set` returns see the document's member order preserved on `materialize()`, unlike for a
|
||||
[`json_editable_document`](json_editable_document.md).
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/ordered_json_editable_document.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/ordered_json_editable_document.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [ordered_json_editable_document](ordered_json_editable_document.md) - the document type this view refers into
|
||||
- [ordered_json_view](ordered_json_view.md) - the corresponding read-only view
|
||||
- [json_editable_view](json_editable_view.md) - the corresponding view for `json_editable_document`
|
||||
|
||||
## Version history
|
||||
|
||||
Since version 3.13.0.
|
||||
@@ -21,17 +21,18 @@ Note: Some modern features (like C++20 ranges or filesystem support) may be disa
|
||||
|
||||
| Compiler | Architecture | Operating System | CI |
|
||||
|----------------------------------------------|--------------|-----------------------------------|-----------|
|
||||
| AppleClang 15.0.0.15000040; Xcode 15.0.1 | arm64 | macOS 14.7.2 (Sonoma) | GitHub |
|
||||
| AppleClang 15.0.0.15000100; Xcode 15.1 | arm64 | macOS 14.7.2 (Sonoma) | GitHub |
|
||||
| AppleClang 15.0.0.15000100; Xcode 15.2 | arm64 | macOS 14.7.2 (Sonoma) | GitHub |
|
||||
| AppleClang 15.0.0.15000309; Xcode 15.3 | arm64 | macOS 14.7.2 (Sonoma) | GitHub |
|
||||
| AppleClang 15.0.0.15000309; Xcode 15.4 | arm64 | macOS 14.7.2 (Sonoma) | GitHub |
|
||||
| AppleClang 16.0.0.16000026; Xcode 16 | arm64 | macOS 15.2 (Sequoia) | GitHub |
|
||||
| AppleClang 16.0.0.16000026; Xcode 16.1 | arm64 | macOS 15.2 (Sequoia) | GitHub |
|
||||
| AppleClang 16.0.0.16000026; Xcode 16.2 | arm64 | macOS 15.2 (Sequoia) | GitHub |
|
||||
| AppleClang 17.0.0.17000013; Xcode 16.3 | arm64 | macOS 15.5 (Sequoia) | GitHub |
|
||||
| AppleClang 17.0.0.17000013; Xcode 16.4 | arm64 | macOS 15.5 (Sequoia) | GitHub |
|
||||
| AppleClang 17.0.0.17000319; Xcode 26.0.1 | arm64 | macOS 15.5 (Sequoia) | GitHub |
|
||||
| AppleClang 17.0.0.17000404; Xcode 26.1.1 | arm64 | macOS 15.7.9 (Sequoia) | GitHub |
|
||||
| AppleClang 17.0.0.17000603; Xcode 26.2 | arm64 | macOS 15.7.9 (Sequoia) | GitHub |
|
||||
| AppleClang 17.0.0.17000604; Xcode 26.3 | arm64 | macOS 15.7.9 (Sequoia) | GitHub |
|
||||
| AppleClang 21.0.0.21000099; Xcode 26.4.1 | arm64 | macOS 26.6.2 (Tahoe) | GitHub |
|
||||
| AppleClang 21.0.0.21000101; Xcode 26.5 | arm64 | macOS 26.6.2 (Tahoe) | GitHub |
|
||||
| AppleClang 21.0.0.21000101; Xcode 26.6 | arm64 | macOS 26.6.2 (Tahoe) | GitHub |
|
||||
| Clang 3.4.2 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||
| Clang 3.5.2 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||
| Clang 3.6.2 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||
@@ -89,7 +90,7 @@ Note: Some modern features (like C++20 ranges or filesystem support) may be disa
|
||||
| GNU 13.3.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||
| GNU 14.2.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||
| GNU 15.1.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||
| GNU 16.1.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||
| GNU 16.2.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||
| GNU 16.1.0 | arm64 | Ubuntu 24.04 | GitHub |
|
||||
| icpc (ICC) 2021.10.0 20230609 | x86_64 | Ubuntu 22.04 LTS | GitHub |
|
||||
| icpx (Intel oneAPI DPC++/C++) 2025.3.2 | x86_64 | Ubuntu 24.04 LTS | GitHub |
|
||||
|
||||
@@ -64,6 +64,8 @@ The following macros guard changes that are planned to become the default in ver
|
||||
| [`JSON_PRECISE_STREAM_POSITION`](../api/macros/json_precise_stream_position.md) | `0` | `1`: reading from a stream does not consume the character after a number | – | 3.13.0 |
|
||||
| [`JSON_STRICT_NUL_HANDLING`](../api/macros/json_strict_nul_handling.md) | `0` | `1`: a NUL byte in the input is a parse error instead of the end of input | [`JSON_StrictNulHandling`](../integration/cmake.md#json_strictnulhandling) | 3.13.0 |
|
||||
| [`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md) | `0` | `1`: `to_cbor`, `to_ubjson`, `to_bjdata`, and `to_bson` throw for strings that are not valid UTF-8 by default | [`JSON_StrictBinaryUTF8`](../integration/cmake.md#json_strictbinaryutf8) | 3.13.0 |
|
||||
| [`JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`](../api/macros/json_disable_tuple_reference_conversion.md) | `0` | `1`: a `basic_json` value can no longer be created from a one-element tuple of a reference to it, such as `std::forward_as_tuple(j)`| [`JSON_DisableTupleReferenceConversion`](../integration/cmake.md#json_disabletuplereferenceconversion) | 3.13.0 |
|
||||
| [`JSON_DELETE_DEPRECATED_FUNCTIONS`](../api/macros/json_delete_deprecated_functions.md) | `0` | removed: the deprecated functions are removed (see below); the `from_*(ptr, len)` overloads stay deleted | [`JSON_DeleteDeprecatedFunctions`](../integration/cmake.md#json_deletedeprecatedfunctions) | 3.13.0 |
|
||||
|
||||
For example, the following makes a 3.x release behave like version 4.0 with respect to these changes:
|
||||
|
||||
@@ -75,6 +77,8 @@ For example, the following makes a 3.x release behave like version 4.0 with resp
|
||||
#define JSON_PRECISE_STREAM_POSITION 1
|
||||
#define JSON_STRICT_NUL_HANDLING 1
|
||||
#define JSON_STRICT_BINARY_UTF8 1
|
||||
#define JSON_DISABLE_TUPLE_REFERENCE_CONVERSION 1
|
||||
#define JSON_DELETE_DEPRECATED_FUNCTIONS 1
|
||||
#include <nlohmann/json.hpp>
|
||||
```
|
||||
|
||||
@@ -84,8 +88,13 @@ way to achieve this.
|
||||
### Removal of deprecated functions
|
||||
|
||||
Version 4.0 will remove all deprecated functions. Compiling with deprecation warnings enabled shows which of them your
|
||||
code still uses. The [migration guide](../integration/migration_guide.md#replace-deprecated-functions) shows how to
|
||||
replace each of them.
|
||||
code still uses. Defining [`JSON_DELETE_DEPRECATED_FUNCTIONS`](../api/macros/json_delete_deprecated_functions.md) to
|
||||
`1` turns these warnings into errors, as the deprecated functions are then deleted. The
|
||||
[migration guide](../integration/migration_guide.md#replace-deprecated-functions) shows how to replace each of them.
|
||||
|
||||
The `from_*` overloads taking a pointer and a length are not removed in version 4.0, but stay deleted. Without them, a
|
||||
call like `from_cbor(ptr, len)` would still compile: it would read `ptr` as a NUL-terminated string and convert `len`
|
||||
to the `strict` parameter.
|
||||
|
||||
| Deprecated | Since | Migration |
|
||||
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------------------------------------------------------------------------------|
|
||||
@@ -97,6 +106,7 @@ replace each of them.
|
||||
| [`json_pointer::operator string_t`](../api/json_pointer/operator_string_t.md) | 3.11.0 | [JSON Pointers](../integration/migration_guide.md#json-pointers) |
|
||||
| [`json_pointer`](../api/json_pointer/index.md) with a `basic_json` type as template argument, and the overloads of `value`, `contains`, `operator[]`, and `at` accepting such a pointer | 3.11.0 | [JSON Pointers](../integration/migration_guide.md#json-pointers) |
|
||||
| Comparing a [`json_pointer`](../api/json_pointer/index.md) with a string via [`operator==`](../api/json_pointer/operator_eq.md) or [`operator!=`](../api/json_pointer/operator_ne.md) | 3.11.2 | [JSON Pointers](../integration/migration_guide.md#json-pointers) |
|
||||
| [`from_bjdata`](../api/basic_json/from_bjdata.md) and [`from_bon8`](../api/basic_json/from_bon8.md) with `(ptr, len)` | 3.13.0 | [Parsing](../integration/migration_guide.md#parsing) |
|
||||
|
||||
The deprecated legacy comparison of discarded values is controlled by a macro and therefore listed in the table above.
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
using json_editable_document = nlohmann::json_editable_document;
|
||||
using json_editable_view = nlohmann::json_editable_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
// a deprecated field is dropped from a configuration file, and a
|
||||
// decommissioned replica is removed from the list -- "price" keeps its
|
||||
// trailing zero, and the fields around the removed ones keep their order
|
||||
const std::string text = R"({
|
||||
"name": "cache",
|
||||
"legacy_host": "db0",
|
||||
"host": "db1",
|
||||
"price": 19.90,
|
||||
"replicas": ["db2", "db3", "db4"]
|
||||
})";
|
||||
|
||||
json_editable_document doc = json_editable_document::parse(text);
|
||||
|
||||
doc.erase(doc.root(), "legacy_host"); // (1) an object member
|
||||
doc.erase(doc.root()["replicas"], 1); // (2) an array element ("db3")
|
||||
const std::size_t removed = doc.erase(json::json_pointer("/replicas/0")); // (3) via a JSON pointer
|
||||
|
||||
std::cout << removed << '\n';
|
||||
std::cout << doc.root().dump(2, ' ', false, json_editable_view::number_format::source) << "\n\n";
|
||||
|
||||
// the same edits on a plain json value: object_t is a std::map, so
|
||||
// parsing already sorted the keys, and dump() rewrites every number to
|
||||
// its shortest form, even "price", which was never touched
|
||||
json plain = json::parse(text);
|
||||
plain.erase("legacy_host");
|
||||
plain["replicas"].erase(1);
|
||||
plain["replicas"].erase(0);
|
||||
std::cout << plain.dump(2) << '\n';
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
1
|
||||
{
|
||||
"name": "cache",
|
||||
"host": "db1",
|
||||
"price": 19.90,
|
||||
"replicas": [
|
||||
"db4"
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
"host": "db1",
|
||||
"name": "cache",
|
||||
"price": 19.9,
|
||||
"replicas": [
|
||||
"db4"
|
||||
]
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
using json_editable_document = nlohmann::json_editable_document;
|
||||
using json_editable_view = nlohmann::json_editable_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
// a deployment plan -- "budget" is written with a trailing zero that has
|
||||
// no effect on its value
|
||||
const std::string text = R"({
|
||||
"release": "2026.09",
|
||||
"steps": ["build", "test", "deploy"],
|
||||
"budget": 19.90
|
||||
})";
|
||||
|
||||
json_editable_document doc = json_editable_document::parse(text);
|
||||
|
||||
const std::size_t deploy_index = 2;
|
||||
const auto deploy = doc.root()["steps"][deploy_index]; // held across the insert
|
||||
|
||||
doc.insert(doc.root()["steps"], deploy_index, "smoke-test"); // insert before "deploy"
|
||||
|
||||
// the held view still refers to "deploy", even though its index moved
|
||||
// from 2 to 3, and nothing else in the document was touched
|
||||
std::cout << deploy.dump() << '\n';
|
||||
std::cout << doc.root().dump(2, ' ', false, json_editable_view::number_format::source) << "\n\n";
|
||||
|
||||
// the same edit on a plain json value: an index held from before the
|
||||
// insert now refers to whatever moved into that slot, and dump()
|
||||
// rewrites "budget" to its shortest form even though it was never
|
||||
// touched
|
||||
json plain = json::parse(text);
|
||||
plain["steps"].insert(plain["steps"].begin() + static_cast<std::ptrdiff_t>(deploy_index), "smoke-test");
|
||||
std::cout << plain["steps"][deploy_index].dump() << '\n';
|
||||
std::cout << plain.dump(2) << '\n';
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
"deploy"
|
||||
{
|
||||
"release": "2026.09",
|
||||
"steps": [
|
||||
"build",
|
||||
"test",
|
||||
"smoke-test",
|
||||
"deploy"
|
||||
],
|
||||
"budget": 19.90
|
||||
}
|
||||
|
||||
"smoke-test"
|
||||
{
|
||||
"budget": 19.9,
|
||||
"release": "2026.09",
|
||||
"steps": [
|
||||
"build",
|
||||
"test",
|
||||
"smoke-test",
|
||||
"deploy"
|
||||
]
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
using json_document = nlohmann::json_document;
|
||||
using image_check = json_document::image_check;
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << std::boolalpha;
|
||||
|
||||
// the image of a parsed document -- as if read back from a cache file or
|
||||
// received from another process running the same build of the library
|
||||
const std::string text = R"({"name": "cache", "note": "caf\u00e9", "replicas": ["db2", "db3"]})";
|
||||
const json_document parsed = json_document::parse(text);
|
||||
const std::vector<std::uint8_t> image = parsed.save();
|
||||
|
||||
// (1)/(2) load() needs no parsing, yet dumps exactly what parsing did
|
||||
const json_document borrowed = json_document::load(image);
|
||||
std::cout << (borrowed.root().dump() == parsed.root().dump()) << '\n';
|
||||
std::cout << borrowed.owns_source() << '\n'; // borrowed: still points into `image`
|
||||
|
||||
// (3) load(std::move(image)) keeps the vector instead of copying it
|
||||
std::vector<std::uint8_t> to_move = image;
|
||||
const json_document owned = json_document::load(std::move(to_move));
|
||||
std::cout << owned.owns_source() << '\n';
|
||||
|
||||
// a damaged image -- the last byte of the decoded string "note" holds
|
||||
// (an escape sequence, so it was unescaped into the document's own
|
||||
// buffer), flipped, as storage or transport corruption might do
|
||||
std::vector<std::uint8_t> damaged = image;
|
||||
damaged[damaged.size() - 2] = 0xFF;
|
||||
|
||||
// image_check::full inspects strings and numbers, so it catches the damage
|
||||
try
|
||||
{
|
||||
static_cast<void>(json_document::load(damaged, image_check::full));
|
||||
}
|
||||
catch (const json::parse_error& e)
|
||||
{
|
||||
std::cout << e.id << '\n';
|
||||
}
|
||||
|
||||
// image_check::bounds only checks structure and bounds, so a cache the
|
||||
// process already trusts loads without the extra scan -- reading a value
|
||||
// the damage did not touch is still safe
|
||||
const json_document trusted = json_document::load(damaged, image_check::bounds);
|
||||
std::cout << trusted.root()["name"].get<std::string>() << '\n';
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
true
|
||||
false
|
||||
true
|
||||
116
|
||||
cache
|
||||
@@ -1,24 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
using json_editable_document = nlohmann::json_editable_document;
|
||||
|
||||
int main()
|
||||
{
|
||||
// "events" starts out null -- the first push_back() turns it into an
|
||||
// array, exactly like set() turns a null object member into an object
|
||||
json_editable_document doc = json_editable_document::parse(R"({"source": "sensor-1", "events": null})");
|
||||
|
||||
const auto first = doc.push_back(doc.root()["events"], json{{"type", "start"}, {"t", 0}});
|
||||
for (int t = 1; t <= 3; ++t)
|
||||
{
|
||||
doc.push_back(doc.root()["events"], json{{"type", "tick"}, {"t", t}});
|
||||
}
|
||||
|
||||
// push_back() never moves an existing element: a view taken from an
|
||||
// earlier call still refers to the same element after later ones
|
||||
std::cout << first.dump() << '\n';
|
||||
std::cout << doc.root()["events"].size() << '\n';
|
||||
std::cout << doc.root().dump(2) << '\n';
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{"t":0,"type":"start"}
|
||||
4
|
||||
{
|
||||
"source": "sensor-1",
|
||||
"events": [
|
||||
{
|
||||
"t": 0,
|
||||
"type": "start"
|
||||
},
|
||||
{
|
||||
"t": 1,
|
||||
"type": "tick"
|
||||
},
|
||||
{
|
||||
"t": 2,
|
||||
"type": "tick"
|
||||
},
|
||||
{
|
||||
"t": 3,
|
||||
"type": "tick"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << std::boolalpha;
|
||||
|
||||
// a configuration a service parses once and then caches as an image, so
|
||||
// that later requests can load() it instead of parsing the text again
|
||||
const std::string text = R"({"name": "cache", "host": "db1", "port": 6379, "replicas": ["db2", "db3"]})";
|
||||
const json_document config = json_document::parse(text);
|
||||
|
||||
// save() turns the parsed document into a byte buffer: a 64-byte header,
|
||||
// the node index, the source text, and the decoded strings
|
||||
const std::vector<std::uint8_t> image = config.save();
|
||||
std::cout << image.size() << '\n';
|
||||
|
||||
// the same document always saves to the same bytes
|
||||
std::cout << (image == json_document::parse(text).save()) << '\n';
|
||||
|
||||
// loading the image back needs no parsing, yet dumps exactly what
|
||||
// parsing the text produced
|
||||
const json_document reloaded = json_document::load(image);
|
||||
std::cout << (reloaded.root().dump() == config.root().dump()) << '\n';
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
316
|
||||
true
|
||||
true
|
||||
@@ -1,41 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
using json_editable_document = nlohmann::json_editable_document;
|
||||
using json_editable_view = nlohmann::json_editable_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
// a configuration file, as it might be read from disk -- "price" is
|
||||
// written with a trailing zero that has no effect on its value
|
||||
const std::string text = R"({
|
||||
"name": "cache",
|
||||
"host": "db1",
|
||||
"port": 6379,
|
||||
"price": 19.90,
|
||||
"replicas": ["db2", "db3"],
|
||||
"timeout": 30
|
||||
})";
|
||||
|
||||
json_editable_document doc = json_editable_document::parse(text);
|
||||
|
||||
doc.set(doc.root()["port"], 6380); // (1) replace a value
|
||||
doc.set(doc.root(), "region", "us-east"); // (2) add a member
|
||||
doc.set(doc.root()["replicas"], 0, "db4"); // (3) assign an element
|
||||
doc.set(json::json_pointer("/timeout"), 45); // (4) via a JSON pointer
|
||||
|
||||
// members stay in document order (the new one at the end), and a number
|
||||
// that was not itself edited keeps its exact spelling
|
||||
std::cout << doc.root().dump(2, ' ', false, json_editable_view::number_format::source) << "\n\n";
|
||||
|
||||
// the same edits on a plain json value: object_t is a std::map, so
|
||||
// parsing already sorted the keys, and dump() rewrites every number to
|
||||
// its shortest form, even "price", which was never touched
|
||||
json plain = json::parse(text);
|
||||
plain["port"] = 6380;
|
||||
plain["region"] = "us-east";
|
||||
plain["replicas"][0] = "db4";
|
||||
plain[json::json_pointer("/timeout")] = 45;
|
||||
std::cout << plain.dump(2) << '\n';
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"name": "cache",
|
||||
"host": "db1",
|
||||
"port": 6380,
|
||||
"price": 19.90,
|
||||
"replicas": [
|
||||
"db4",
|
||||
"db3"
|
||||
],
|
||||
"timeout": 45,
|
||||
"region": "us-east"
|
||||
}
|
||||
|
||||
{
|
||||
"host": "db1",
|
||||
"name": "cache",
|
||||
"port": 6380,
|
||||
"price": 19.9,
|
||||
"region": "us-east",
|
||||
"replicas": [
|
||||
"db4",
|
||||
"db3"
|
||||
],
|
||||
"timeout": 45
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
using json_editable_document = nlohmann::json_editable_document;
|
||||
using json_editable_view = nlohmann::json_editable_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
// a configuration file, as it might be read from disk
|
||||
const std::string text = R"({"name": "cache", "host": "db1", "port": 6379, "price": 19.90})";
|
||||
std::cout << text << "\n\n";
|
||||
|
||||
// patch two fields -- "price" is never touched
|
||||
json_editable_document doc = json_editable_document::parse(text);
|
||||
doc.set(doc.root(), "host", "db2");
|
||||
doc.set(doc.root(), "retries", 3);
|
||||
|
||||
// member order (the new member at the end) and the untouched number's
|
||||
// exact spelling survive
|
||||
std::cout << doc.root().dump(-1, ' ', false, json_editable_view::number_format::source) << '\n';
|
||||
|
||||
// the same patch on a plain json value: keys are sorted (object_t is a
|
||||
// std::map), and "price" is rewritten even though the patch never
|
||||
// touched it
|
||||
json plain = json::parse(text);
|
||||
plain["host"] = "db2";
|
||||
plain["retries"] = 3;
|
||||
std::cout << plain.dump() << '\n';
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
{"name": "cache", "host": "db1", "port": 6379, "price": 19.90}
|
||||
|
||||
{"name":"cache","host":"db2","port":6379,"price":19.90,"retries":3}
|
||||
{"host":"db2","name":"cache","port":6379,"price":19.9,"retries":3}
|
||||
@@ -1,15 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using ordered_json_editable_document = nlohmann::ordered_json_editable_document;
|
||||
|
||||
int main()
|
||||
{
|
||||
// ordered_json_editable_document is basic_json_document<nlohmann::ordered_json, true>
|
||||
ordered_json_editable_document doc = ordered_json_editable_document::parse(R"({"z": 1, "a": 2, "m": 3})");
|
||||
doc.set(doc.root(), "b", 4); // set() always appends a new member at the end
|
||||
|
||||
// materialize() preserves the document order (with "b" at the end),
|
||||
// instead of sorting the keys the way json_editable_document does
|
||||
std::cout << doc.root().materialize().dump() << '\n';
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{"z":1,"a":2,"m":3,"b":4}
|
||||
@@ -168,9 +168,9 @@ The library maps CBOR types to JSON value types as follows:
|
||||
!!! warning "Negative integer overflow"
|
||||
|
||||
CBOR negative integers (major type 1) are decoded as `-1 - n`. If the encoded magnitude `n` is too large for the
|
||||
result to fit into `number_integer_t` (`std::int64_t` by default), parsing fails with a
|
||||
[`parse_error.112`](../../home/exceptions.md#jsonexceptionparse_error112) exception rather than overflowing
|
||||
silently.
|
||||
result to fit into `number_integer_t` (`std::int64_t` by default), the result is stored as `number_float_t`, like
|
||||
a too small integer in JSON text. For example, `-18446744073709551616` (`0x3B` followed by eight `0xFF` bytes) is
|
||||
stored as `-1.8446744073709552e+19`.
|
||||
|
||||
!!! warning "Object keys"
|
||||
|
||||
|
||||
@@ -58,6 +58,23 @@ assert(jPi.get<TaskState>() == TS_INVALID );
|
||||
--8<-- "examples/nlohmann_json_serialize_enum.output"
|
||||
```
|
||||
|
||||
## Maps with enum keys
|
||||
|
||||
By default, maps with enum keys, such as `std::map<TaskState, std::string>`, are stored as arrays of `[key, value]`
|
||||
pairs, because JSON object keys must be strings. Define
|
||||
[`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md) before including the
|
||||
library to store them as objects, with the keys converted by the enum's `to_json()` function:
|
||||
|
||||
```cpp
|
||||
std::map<TaskState, std::string> m = {{TS_STOPPED, "aa"}, {TS_COMPLETED, "bb"}};
|
||||
|
||||
json j = m;
|
||||
// default: [["stopped","aa"],["completed","bb"]]
|
||||
// with JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS: {"completed":"bb","stopped":"aa"}
|
||||
```
|
||||
|
||||
Either form can be read back, with or without the macro.
|
||||
|
||||
## Notes
|
||||
|
||||
Just as in [Arbitrary Type Conversions](arbitrary_types.md) above,
|
||||
|
||||
@@ -14,8 +14,6 @@ C++ types, and finally serialize it again.
|
||||
[parsing untrusted input](parsing/untrusted_input.md).
|
||||
- [Zero-copy JSON views](json_view.md) — read a JSON text through a flat index instead of building a `json` tree;
|
||||
strings and numbers stay in the input and are only decoded when needed.
|
||||
[Editable documents](json_view.md#editing-a-document) can also be modified, and [images](json_view.md#images) load a
|
||||
parsed document again without parsing it.
|
||||
- [Comments](comments.md) and [trailing commas](trailing_commas.md) — opt-in relaxations of the JSON grammar.
|
||||
|
||||
## Accessing and modifying values
|
||||
|
||||
@@ -187,129 +187,14 @@ a number to its parsed `#!cpp double`/`#!cpp int64_t` value.
|
||||
[`operator<<`](../api/basic_json_view/operator_ltlt.md) writes a view to a stream the way `basic_json`'s does, using
|
||||
the stream's `width`/`fill` for indentation.
|
||||
|
||||
## Editing a document
|
||||
|
||||
Everything above is read-only: a `json_document`/`json_view` lets you look at a parsed text without copying it, but
|
||||
not change it. [`basic_json_document<BasicJsonType, true>`](../api/basic_json_document/index.md) -- more conveniently
|
||||
spelled [`json_editable_document`](../api/json_editable_document.md) or
|
||||
[`ordered_json_editable_document`](../api/ordered_json_editable_document.md) -- also lets you
|
||||
[`set`](../api/basic_json_document/set.md) a value, [`push_back`](../api/basic_json_document/push_back.md) onto or
|
||||
[`insert`](../api/basic_json_document/insert.md) into an array, and [`erase`](../api/basic_json_document/erase.md)
|
||||
an object member or an array element, still without ever building a `basic_json` tree for parts you do not touch.
|
||||
|
||||
`#!cpp Editable` defaults to `#!cpp false`, so `json_document`/`ordered_json_document` are unaffected -- they carry
|
||||
none of the bookkeeping edits need, and calling `set`/`push_back`/`insert`/`erase` on one is a compile error, not a
|
||||
runtime one.
|
||||
|
||||
### Why: editing without reformatting
|
||||
|
||||
The `#!cpp 19.90` price from [above](#writing-a-view-back) is exactly the kind of value that makes editing a `json`
|
||||
or `ordered_json` value in place lossy. Say you parse a configuration file, patch one field, and write it back:
|
||||
|
||||
- **`json`** re-sorts every key on the way in (`object_t` is a `#!cpp std::map`) and rewrites every number to its
|
||||
shortest round-trip form on the way out -- a one-field patch turns into a diff that reorders the whole file and
|
||||
rewrites `#!cpp 19.90` to `#!cpp 19.9`.
|
||||
- **`ordered_json`** keeps the key order, but still rewrites every number the same way: parsing has already reduced
|
||||
it to a `#!cpp double`/`#!cpp int64_t`, and there is no way back to how it was spelled in the source text.
|
||||
|
||||
An editable document keeps both. [`dump()`](../api/basic_json_view/dump.md) of an edited document writes members in
|
||||
document order -- a member [`set`](../api/basic_json_document/set.md) added goes at the end, exactly where it was
|
||||
inserted, and an [`erase`](../api/basic_json_document/erase.md)d member simply leaves a gap: everything around it
|
||||
keeps its place -- and [`number_format::source`](../api/basic_json_view/number_format.md) keeps the exact spelling
|
||||
of every number an edit did not itself touch; a number an edit *did* touch is written the way
|
||||
[`BasicJsonType::dump()`](../api/basic_json/dump.md) would write it, since there is no source spelling for a brand
|
||||
new value.
|
||||
|
||||
??? example "Example: patch a configuration, keeping member order and number spellings"
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/json_editable_document.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/json_editable_document.output"
|
||||
```
|
||||
|
||||
### What stays valid, and what an edit costs
|
||||
|
||||
The source text itself is **never written**, and the parsed index never moves -- a value keeps the node it was
|
||||
parsed into for as long as it is not itself replaced. So every [view](../api/basic_json_view/index.md) taken before
|
||||
an edit, including a previously obtained [`root()`](../api/basic_json_document/root.md), stays valid and, if it
|
||||
still refers to the edited value, sees the edit; a view of a value a later edit drops or replaces just keeps showing
|
||||
what it last held. New values go to storage the document allocates and owns on demand. The one thing an edit does
|
||||
invalidate is the **iterators** taken over an edited array or object: the first time one of its elements is set,
|
||||
appended to, inserted into, or erased, its elements move from the parsed, fixed layout to a growable block of links
|
||||
so that [`push_back`](../api/basic_json_document/push_back.md) can later grow it in amortized constant time --
|
||||
existing elements are not touched, but an iterator that was walking the old layout no longer matches. A string
|
||||
obtained with
|
||||
[`get_string()`](../api/basic_json_view/get_string.md) is unaffected either way and stays valid across further
|
||||
edits. See [`basic_json_document`'s Edits](../api/basic_json_document/index.md#edits) for the details, and
|
||||
[`set`'s Exception safety](../api/basic_json_document/set.md#exception-safety) for what an edit guarantees if it
|
||||
throws (the *basic* guarantee, not the strong one `dump()` and the read-only functions provide). How edits are kept in
|
||||
the index is described in the [architecture overview](../home/architecture.md#node-index-of-json-views).
|
||||
|
||||
## Images
|
||||
|
||||
[`save()`](../api/basic_json_document/save.md) writes a document as an *image*: a byte buffer that
|
||||
[`load()`](../api/basic_json_document/load.md) reads back into a document without parsing -- no lexing, no building
|
||||
the node index, nothing but copying the nodes and pointing the text and the decoded strings at the image. Where
|
||||
[`parse_copy()`](../api/basic_json_document/parse_copy.md) still has to scan the whole input,
|
||||
[`load()`](../api/basic_json_document/load.md) turns that scan into a copy of the node index alone.
|
||||
|
||||
**Why.** A document that is parsed once and then read many times -- a configuration loaded at startup, a template
|
||||
rendered on every request, a large reference dataset a worker process needs in memory -- pays for parsing once but
|
||||
can amortize [`save()`](../api/basic_json_document/save.md)'s cost across every later load. That makes images useful
|
||||
for a cache: save a document the first time it is parsed (to a file, a shared-memory segment, an in-process cache),
|
||||
and [`load()`](../api/basic_json_document/load.md) it on every later use instead of parsing the source text again.
|
||||
They are just as useful for handing a parsed document to another process (or a forked worker) running the same build
|
||||
of the library, since [`load()`](../api/basic_json_document/load.md) turns the transfer into a copy of the node index
|
||||
plus pointers into the received bytes, not a re-parse.
|
||||
|
||||
**Choosing a check.** [`load()`](../api/basic_json_document/load.md) takes an
|
||||
[`image_check`](../api/basic_json_document/load.md#image_check) that trades validation against speed:
|
||||
`image_check::full` (the default) checks everything the parser itself guarantees, so a checked image is exactly as
|
||||
safe to read and serialize as a freshly parsed document -- the right choice whenever the image did not come straight
|
||||
from this process's own [`save()`](../api/basic_json_document/save.md), such as a file or a network peer.
|
||||
`image_check::bounds` only checks structure and bounds -- cheaper, since it skips scanning the text and the decoded
|
||||
strings -- and fits a cache the process trusts, one it wrote and reads back itself. `image_check::none` skips
|
||||
validation entirely, for an image trusted as much as the process's own memory. See
|
||||
[`load()`'s Notes](../api/basic_json_document/load.md#notes) for exactly what each level does and does not guarantee.
|
||||
|
||||
??? example "Example: cache a parsed configuration as an image"
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_document__save.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_document__save.output"
|
||||
```
|
||||
|
||||
!!! warning "Experimental"
|
||||
|
||||
The image format is versioned but not yet stable, and may change in an incompatible way before it is declared
|
||||
stable. It is little-endian only, and tied to the library build that wrote it -- use it to cache a document or to
|
||||
hand one to another process running the *same* build, not as a long-term storage format; keep the original JSON
|
||||
text if a saved document needs to be readable by a future library version.
|
||||
|
||||
The idea of a document you can read without parsing comes from zero-copy formats such as
|
||||
[FlatBuffers](https://github.com/google/flatbuffers) and [YaFF](https://github.com/yandex/yaff); the check
|
||||
[`load()`](../api/basic_json_document/load.md) runs follows the idea of FlatBuffers' Verifier. No code is taken from
|
||||
either.
|
||||
|
||||
## Choosing between `json`, `ordered_json`, the SAX interface, and `json_view`
|
||||
|
||||
| | [`json`](../api/json.md) / [`ordered_json`](../api/ordered_json.md) | [SAX interface](parsing/sax_interface.md) | [`json_document`](../api/json_document.md) / [`json_view`](../api/json_view.md) | [`json_editable_document`](../api/json_editable_document.md) / [`json_editable_view`](../api/json_editable_view.md) |
|
||||
|---|---|---|---|---|
|
||||
| **Ownership** | owns every value | owns nothing; you decide what to keep, in your handler | borrows or owns the *text*; the index is always owned by the document | same as `json_document`; edits go to storage the document owns |
|
||||
| **Mutability** | freely mutable | not applicable (a one-shot event stream) | read-only | [`set`](../api/basic_json_document/set.md)/[`push_back`](../api/basic_json_document/push_back.md)/[`insert`](../api/basic_json_document/insert.md)/[`erase`](../api/basic_json_document/erase.md) edit in place; the source text is never rewritten |
|
||||
| **What you get** | a full tree you can read, write, and keep as long as you like | a sequence of callbacks; whatever your handler builds from them | a flat index plus, on demand, [`materialize()`](../api/basic_json_view/materialize.md)d `json`/`ordered_json` values for the parts you actually use | the same, plus [`dump()`](../api/basic_json_view/dump.md) of an edited document that keeps the member order and, with [`number_format::source`](../api/basic_json_view/number_format.md), the spelling of every untouched number |
|
||||
| **Typical use** | general-purpose JSON handling: config, request/response bodies you build or modify, anything you hold onto | validating or projecting a text into your own data structure without ever holding the whole thing as JSON | large or high-volume input where you only need part of it, or need it repeatedly, and can keep the source text (or a copy) alive for as long as the document lives | a document you read, patch a few fields of, and write back -- a configuration file, for instance -- where the rest of it should come back exactly as it was |
|
||||
| **Caching/reload** | not applicable -- re-parse, or roll your own serialization | not applicable | [`save()`](../api/basic_json_document/save.md)/[`load()`](../api/basic_json_document/load.md): cache the parsed index as an image and reload it without parsing | same, saving the document's current -- possibly edited -- state |
|
||||
| | [`json`](../api/json.md) / [`ordered_json`](../api/ordered_json.md) | [SAX interface](parsing/sax_interface.md) | [`json_document`](../api/json_document.md) / [`json_view`](../api/json_view.md) |
|
||||
|---|---|---|---|
|
||||
| **Ownership** | owns every value | owns nothing; you decide what to keep, in your handler | borrows or owns the *text*; the index is always owned by the document |
|
||||
| **Mutability** | freely mutable | not applicable (a one-shot event stream) | read-only |
|
||||
| **What you get** | a full tree you can read, write, and keep as long as you like | a sequence of callbacks; whatever your handler builds from them | a flat index plus, on demand, [`materialize()`](../api/basic_json_view/materialize.md)d `json`/`ordered_json` values for the parts you actually use |
|
||||
| **Typical use** | general-purpose JSON handling: config, request/response bodies you build or modify, anything you hold onto | validating or projecting a text into your own data structure without ever holding the whole thing as JSON | large or high-volume input where you only need part of it, or need it repeatedly, and can keep the source text (or a copy) alive for as long as the document lives |
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -23,6 +23,17 @@ This macro overrides [`#!cpp catch`](https://en.cppreference.com/w/cpp/language/
|
||||
|
||||
See [full documentation of `JSON_CATCH_USER(exception)`](../api/macros/json_throw_user.md).
|
||||
|
||||
## `JSON_DELETE_DEPRECATED_FUNCTIONS`
|
||||
|
||||
When defined to `1`, all deprecated functions are declared as deleted instead of only being marked as deprecated, so
|
||||
code that still calls them no longer compiles. This way, you can find all calls that need to be replaced before version
|
||||
4.0.0 removes these functions.
|
||||
|
||||
The macro can also be set with the CMake option
|
||||
[`JSON_DeleteDeprecatedFunctions`](../integration/cmake.md#json_deletedeprecatedfunctions) (`OFF` by default).
|
||||
|
||||
See [full documentation of `JSON_DELETE_DEPRECATED_FUNCTIONS`](../api/macros/json_delete_deprecated_functions.md).
|
||||
|
||||
## `JSON_DIAGNOSTICS`
|
||||
|
||||
This macro enables extended diagnostics for exception messages. Possible values are `1` to enable or `0` to disable
|
||||
@@ -86,7 +97,8 @@ See [full documentation of `JSON_DISABLE_ENUM_SERIALIZATION`](../api/macros/json
|
||||
## `JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`
|
||||
|
||||
When defined to `1`, a JSON value can no longer be created from a one-element `std::tuple` holding a reference to a JSON
|
||||
value, such as the result of `std::forward_as_tuple(j)`. This lets `std::tuple` convert such tuples element-wise.
|
||||
value, such as the result of `std::forward_as_tuple(j)`. This lets `std::tuple` convert such tuples element-wise. This
|
||||
is planned to become the default in version 4.0.0.
|
||||
|
||||
See [full documentation of `JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`](../api/macros/json_disable_tuple_reference_conversion.md).
|
||||
|
||||
@@ -198,6 +210,13 @@ default.
|
||||
|
||||
See [full documentation of `JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON`](../api/macros/json_use_legacy_discarded_value_comparison.md).
|
||||
|
||||
## `JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`
|
||||
|
||||
When defined to `1`, maps with enum keys (e.g., `std::map<E, T>`) are stored as objects, using the enum's conversion for
|
||||
the keys, instead of arrays of `[key, value]` pairs. It is switched off (`0`) by default.
|
||||
|
||||
See [full documentation of `JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md).
|
||||
|
||||
## `JSON_USE_SIMDUTF`
|
||||
|
||||
When defined, UTF-8 validation of JSON strings read from contiguous byte input is delegated to the
|
||||
|
||||
@@ -21,6 +21,8 @@ The complete default namespace name is derived as follows:
|
||||
- [`JSON_PRECISE_STREAM_POSITION`](../api/macros/json_precise_stream_position.md) defined non-zero appends `_psp`.
|
||||
- [`JSON_STRICT_NUL_HANDLING`](../api/macros/json_strict_nul_handling.md) defined non-zero appends `_snul`.
|
||||
- [`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md) defined non-zero appends `_sbu8`.
|
||||
- [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md) defined non-zero
|
||||
appends `_ekmo`.
|
||||
- The inline namespace ends with the suffix `_v` followed by the 3 components of the version number separated by
|
||||
underscores. To omit the version component, see [Disabling the version component](#disabling-the-version-component)
|
||||
below.
|
||||
|
||||
@@ -55,7 +55,7 @@ always use the scalar path regardless of this macro.
|
||||
|
||||
Parsing always produces SAX events internally; [`parse`](../api/basic_json/parse.md) simply feeds them to a consumer
|
||||
that builds a complete `basic_json` value tree (a DOM) in memory. For documents too large to comfortably hold as
|
||||
a DOM, two alternatives avoid building it:
|
||||
a DOM, three alternatives avoid building it:
|
||||
|
||||
- Implement the [SAX interface](parsing/sax_interface.md) directly and pass it to
|
||||
[`sax_parse`](../api/basic_json/sax_parse.md); only the parts of the input you choose to keep ever become
|
||||
@@ -64,6 +64,12 @@ a DOM, two alternatives avoid building it:
|
||||
discard finished elements as soon as they are handled, so memory usage stays bounded by one element (plus the
|
||||
unparsed remainder of the input) instead of the whole document -- see the [recipe for streaming a large homogeneous
|
||||
array](parsing/parser_callbacks.md#recipe-streaming-a-large-homogeneous-array).
|
||||
- Parse into a [`json_document`](json_view.md) (`#!cpp <nlohmann/json_view.hpp>`) instead of a `basic_json`. It keeps
|
||||
the input text and builds a flat index of 16 bytes per value; strings and numbers are not copied, but read from the
|
||||
text when needed. Read-only [views](../api/basic_json_view/index.md) give the familiar element access, and only the
|
||||
parts you [`materialize()`](../api/basic_json_view/materialize.md) become `basic_json` values. A document that
|
||||
borrows the text instead of owning a copy needs the text to outlive it; see
|
||||
[choosing between `json`, the SAX interface, and `json_view`](json_view.md#choosing-between-json-ordered_json-the-sax-interface-and-json_view).
|
||||
|
||||
If the data is naturally record-oriented, consider [JSON Lines](parsing/json_lines.md) instead of one large JSON
|
||||
document: reading and parsing it line by line with `#!cpp std::getline` means only one line's value is ever in memory
|
||||
@@ -209,6 +215,7 @@ those headers are then never processed by the compiler at all.
|
||||
- [Architecture](../home/architecture.md) - how input adapters, the lexer, and the serializer fit together
|
||||
- [Parsing](parsing/index.md) - the available parsing functions and inputs
|
||||
- [SAX interface](parsing/sax_interface.md) - parse without building a DOM
|
||||
- [Zero-copy JSON views](json_view.md) - parse into a flat index of the text and read it without building a DOM
|
||||
- [Binary formats](binary_formats/index.md) - compact alternatives to JSON text
|
||||
- [Object Order](object_order.md) - `json` vs. `ordered_json` and other `ObjectType` choices
|
||||
- [Template Parameter Requirements](types/template_parameters.md) - custom container and allocator types
|
||||
|
||||
@@ -194,9 +194,9 @@ packet-beta
|
||||
|
||||
| Bytes | Field | Type | Contents |
|
||||
|-------|---------|------------|-------------------------------------------------------------------------------------------------------------------------------|
|
||||
| 0 | `kind` | `uint8_t` | the type, numbered as [`value_t`](../api/basic_json/value_t.md): 0 null, 1 object, 2 array, 3 string, 4 boolean, 5 signed integer, 6 unsigned integer, 7 float; 10 for a link (see below) |
|
||||
| 1 | `flags` | `uint8_t` | bits 0-1: where a string's bytes (or a number's token) are (0: the source text, 1: the buffer of decoded strings, for strings with escapes, 2: the edit buffer); bit 2: the value of a boolean; bits 3 and 4: moved and new (see below) |
|
||||
| 2-3 | `extra` | `uint16_t` | numbers: the number of integer digits (low byte) and fraction digits (high byte), 255 for more; objects: the number of their hash index (1-based), or 0; otherwise 0 |
|
||||
| 0 | `kind` | `uint8_t` | the type, numbered as [`value_t`](../api/basic_json/value_t.md): 0 null, 1 object, 2 array, 3 string, 4 boolean, 5 signed integer, 6 unsigned integer, 7 float |
|
||||
| 1 | `flags` | `uint8_t` | bits 0-1: where a string's bytes are (0: the source text, 1: the buffer of decoded strings, for strings with escapes); bit 2: the value of a boolean |
|
||||
| 2-3 | `extra` | `uint16_t` | numbers: the number of integer digits (low byte) and fraction digits (high byte), 255 for more; otherwise 0 |
|
||||
| 4-7 | `off` | `uint32_t` | where the value starts: the first byte after a string's opening quote (or its position in the buffer of decoded strings), the first byte of a number or literal, the bracket of an array or object |
|
||||
| 8-11 | `len` | `uint32_t` | strings: the length after decoding; floats and literals: the length of the token; arrays and objects: the number of elements |
|
||||
| 12-15 | `next` | `uint32_t` | arrays and objects: the number of nodes of the subtree, including the node itself |
|
||||
@@ -211,11 +211,6 @@ packet-beta
|
||||
subtree is `next` nodes further for an array or object, and the next node otherwise (`document_data::after`). Views
|
||||
step from element to element this way and skip whole subtrees in constant time.
|
||||
- **Offsets** are 32 bits wide, so a document is limited to 4 GiB (`out_of_range.416`).
|
||||
- **Large objects** (128 members or more) get a hash index after parsing
|
||||
([`detail/view/object_index.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/view/object_index.hpp)):
|
||||
an open-addressing table whose slots hold the distance from the object's node to a key's node, so that a lookup does
|
||||
not compare every key. The object's `extra` holds the number of its table. Only 65,535 tables fit into `extra`;
|
||||
objects beyond them are searched linearly.
|
||||
|
||||
For example, `#!json {"a": [1, 2.5]}` becomes five nodes. Each node's elements follow it, and `next` leads from an
|
||||
array or object past its subtree:
|
||||
@@ -244,29 +239,6 @@ flowchart LR
|
||||
All `flags` are 0. The integer's bytes 8-15 hold its value, 1; its `extra` says it has one digit. The float's `extra`
|
||||
says it has one integer and one fraction digit, and its `len` is that of the token `2.5`.
|
||||
|
||||
Editable documents ([`json_editable_document`](../api/json_editable_document.md),
|
||||
[`detail/view/edit.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/view/edit.hpp) and
|
||||
[`detail/view/edit_storage.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/view/edit_storage.hpp))
|
||||
never write the source text and never move or resize the parsed index, so views stay valid while the document is
|
||||
edited:
|
||||
|
||||
- A new scalar is written over its node. Its text (a string, or the token of a number as `dump()` writes it) goes to
|
||||
the edit buffer, which `flags` bits 0-1 then name.
|
||||
- An array or object whose elements change gets the flag *moved* (bit 3): its elements then live in a separate
|
||||
sequence (a header node, then the entries), whose number is in `off`. The entries are links (`kind` 10), whose bytes
|
||||
8-15 hold the address of the value's node, so values never move.
|
||||
- A node written by an edit gets the flag *new* (bit 4): it has no position in the source text.
|
||||
- Views of read-only documents compile without any of this: how views walk the index is a template parameter
|
||||
(`navigation<Editable>`).
|
||||
|
||||
Images ([`save`](../api/basic_json_document/save.md) and [`load`](../api/basic_json_document/load.md),
|
||||
[`detail/view/image.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/view/image.hpp)) store
|
||||
the nodes as they are: a 64-byte header (the magic bytes `NJVI`, a format version, the sizes, and reserved bytes that
|
||||
must be zero), the nodes, the text, and the decoded strings. An edited document is first written in document order, as
|
||||
the parser would have written it (without links), and the numbers of the hash indexes are cleared, since `load`
|
||||
rebuilds the indexes. So a change of the node layout is a change of the image format: it must raise `image_version`,
|
||||
and `load` then rejects images of other versions (`parse_error.116`) instead of misreading them.
|
||||
|
||||
## Input adapters
|
||||
|
||||
Input is read via **input adapters** that abstract a source. Every input adapter provides this interface:
|
||||
|
||||
@@ -331,9 +331,6 @@ An unexpected byte was read in a [binary format](../features/binary_formats/inde
|
||||
[json.exception.parse_error.112] parse error at byte 15: syntax error while parsing BSON binary: byte array length cannot be negative, is -1
|
||||
```
|
||||
```
|
||||
[json.exception.parse_error.112] parse error at byte 9: syntax error while parsing CBOR value: negative integer overflow
|
||||
```
|
||||
```
|
||||
[json.exception.parse_error.112] parse error at byte 5: syntax error while parsing BSON document: document size 6 does not match the number of bytes read (5)
|
||||
```
|
||||
|
||||
@@ -391,23 +388,6 @@ A UBJSON high-precision number could not be parsed.
|
||||
[json.exception.parse_error.115] parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A
|
||||
```
|
||||
|
||||
### json.exception.parse_error.116
|
||||
|
||||
[`basic_json_document::load()`](../api/basic_json_document/load.md) rejected an
|
||||
[image](../features/json_view.md#images): either the bytes are not one [`save()`](../api/basic_json_document/save.md)
|
||||
could have written (too short, an unknown magic number or format version, or sizes that do not fit the buffer), or
|
||||
they are, but fail the requested [`image_check`](../api/basic_json_document/load.md#image_check).
|
||||
|
||||
!!! failure "Example message"
|
||||
|
||||
```
|
||||
[json.exception.parse_error.116] parse error: invalid json_document image: the check failed
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
This exception was added in version 3.13.0, together with [images](../features/json_view.md#images).
|
||||
|
||||
## Iterator errors
|
||||
|
||||
This exception is thrown if iterators passed to a library function do not match
|
||||
@@ -616,6 +596,9 @@ During implicit or explicit value conversion, the JSON type must be compatible w
|
||||
[json.exception.type_error.302] type must be string, but is object
|
||||
```
|
||||
|
||||
This exception is also thrown with [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md)
|
||||
if a key of a map with enum keys is not converted to a string, for instance, because the enum is stored as an integer.
|
||||
|
||||
### json.exception.type_error.303
|
||||
|
||||
To retrieve a reference to a value stored in a `basic_json` object with `get_ref`, the type of the reference must match the value type. For instance, for a JSON array, the `ReferenceType` must be `array_t &`.
|
||||
@@ -808,43 +791,32 @@ The dynamic type of the object cannot be represented in the requested serializat
|
||||
|
||||
Encapsulate the JSON value in an object. That is, instead of serializing `#!json true`, serialize `#!json {"value": true}`
|
||||
|
||||
### json.exception.type_error.319
|
||||
### json.exception.type_error.318
|
||||
|
||||
[`basic_json_document::set`](../api/basic_json_document/set.md) and
|
||||
[`basic_json_document::push_back`](../api/basic_json_document/push_back.md) can store any `basic_json` value except
|
||||
a binary one: a `json_document` has no representation for [binary values](../features/binary_values.md), which only
|
||||
ever arise from parsing a binary format or from an explicit [`json::binary`](../api/basic_json/binary.md) value, not
|
||||
from JSON text.
|
||||
With [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md), a map with enum
|
||||
keys is stored as an object. This exception is thrown if two of its keys are converted to the same string, so one of the
|
||||
entries would be lost. This happens, for instance, if [`NLOHMANN_JSON_SERIALIZE_ENUM`](../api/macros/nlohmann_json_serialize_enum.md)
|
||||
does not list an enumerator and it is therefore converted like the first listed one.
|
||||
|
||||
!!! failure "Example message"
|
||||
|
||||
```
|
||||
[json.exception.type_error.319] cannot store a binary value in a json_document
|
||||
[json.exception.type_error.318] duplicate object key 'red'
|
||||
```
|
||||
|
||||
!!! note
|
||||
### json.exception.type_error.321
|
||||
|
||||
This exception was added in version 3.13.0, together with editable [`json_document`s](../features/json_view.md).
|
||||
A discarded value (one created by [`parse()`](../api/basic_json/parse.md) with a callback that returns `false` for the
|
||||
value, or by default-constructing a [`basic_json`](../api/basic_json/index.md) with
|
||||
[`value_t::discarded`](../api/basic_json/value_t.md)) was passed to a binary serialization function, either directly or
|
||||
nested in an array or object. There is no way to represent a discarded value in CBOR, MessagePack, UBJSON, BJData, or BSON.
|
||||
|
||||
### json.exception.type_error.320
|
||||
|
||||
[`basic_json_document::save()`](../api/basic_json_document/save.md) cannot write an
|
||||
[image](../features/json_view.md#images) of a [discarded](../api/basic_json_document/is_discarded.md) document.
|
||||
[`save()`](../api/basic_json_document/save.md) and [`load()`](../api/basic_json_document/load.md) also throw this
|
||||
exception on a big-endian target, since the image format is little-endian only.
|
||||
|
||||
!!! failure "Example messages"
|
||||
!!! failure "Example message"
|
||||
|
||||
Serializing `#!json [1, 2]` to CBOR, where the second element was discarded by a parser callback:
|
||||
```
|
||||
[json.exception.type_error.320] cannot save a discarded json_document
|
||||
[json.exception.type_error.321] cannot serialize discarded value to CBOR
|
||||
```
|
||||
```
|
||||
[json.exception.type_error.320] json_document images need a little-endian target
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
This exception was added in version 3.13.0, together with [images](../features/json_view.md#images).
|
||||
|
||||
## Out of range
|
||||
|
||||
@@ -918,13 +890,18 @@ The JSON Patch operations 'remove' and 'add' cannot be applied to the root eleme
|
||||
|
||||
### json.exception.out_of_range.406
|
||||
|
||||
A parsed number could not be stored as without changing it to NaN or INF.
|
||||
A parsed number could not be stored without changing it to NaN or INF. For the binary formats, this happens when a
|
||||
finite floating-point number does not fit into [`number_float_t`](../api/basic_json/number_float_t.md), for example a
|
||||
double-precision number when `number_float_t` is `#!cpp float`.
|
||||
|
||||
!!! failure "Example message"
|
||||
!!! failure "Example messages"
|
||||
|
||||
```
|
||||
number overflow parsing '10E1000'
|
||||
```
|
||||
```
|
||||
[json.exception.out_of_range.406] syntax error while parsing CBOR value: number overflow
|
||||
```
|
||||
|
||||
### json.exception.out_of_range.407
|
||||
|
||||
@@ -1070,24 +1047,13 @@ MessagePack's ext type and BSON's binary subtype are each stored in a single byt
|
||||
|
||||
[`basic_json_document::parse()`](../api/basic_json_document/parse.md) and the other parsing functions of
|
||||
[`basic_json_document`](../api/basic_json_document/index.md) index a value's position in the source text in 32 bits,
|
||||
so they do not support an input of 4 GiB or more. The same 32-bit limit applies to an **editable** document's own
|
||||
storage: [`set`](../api/basic_json_document/set.md) and [`push_back`](../api/basic_json_document/push_back.md) throw
|
||||
this exception once the strings and number tokens written by edits reach 4 GiB in total, or once more than
|
||||
4294967295 arrays/objects have had an element set or appended to them. The same limit applies to an
|
||||
[image](../features/json_view.md#images): [`save()`](../api/basic_json_document/save.md) throws it if the node
|
||||
count, the text, or the decoded strings it would write would individually reach 4 GiB.
|
||||
so they do not support an input of 4 GiB or more.
|
||||
|
||||
!!! failure "Example messages"
|
||||
!!! failure "Example message"
|
||||
|
||||
```
|
||||
[json.exception.out_of_range.416] input of 4 GiB or more is not supported by json_document
|
||||
```
|
||||
```
|
||||
[json.exception.out_of_range.416] edits of 4 GiB or more are not supported by json_document
|
||||
```
|
||||
```
|
||||
[json.exception.out_of_range.416] images of 4 GiB or more are not supported by json_document
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
|
||||
@@ -25,5 +25,3 @@ The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Eva
|
||||
The class contains an adapted version of the Eisel-Lemire algorithm, its table of powers of five, and its digit comparison for long numbers from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
||||
|
||||
The view's parser (`<nlohmann/json_view.hpp>`) contains techniques and code adapted from [yyjson](https://github.com/ibireme/yyjson) by YaoYuan, which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above): table-driven decoding of `\u` escapes and fixed-offset unrolled checks.
|
||||
|
||||
The view's parser (`<nlohmann/json_view.hpp>`) validates non-ASCII strings with the vector UTF-8 check of [simdjson](https://github.com/simdjson/simdjson) by Daniel Lemire, Geoff Langdale, John Keiser, and contributors (its "lookup4" algorithm and tables, after J. Keiser and D. Lemire, "Validating UTF-8 In Less Than One Instruction Per Byte", 2021), which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here) and the Apache 2.0 License. Copyright © 2018-2025 The simdjson authors
|
||||
@@ -132,6 +132,12 @@ Build the unit tests when [`BUILD_TESTING`](https://cmake.org/cmake/help/latest/
|
||||
|
||||
Enable CI build targets. The exact targets are used during the several CI steps and are subject to change without notice. This option is `OFF` by default.
|
||||
|
||||
### `JSON_DeleteDeprecatedFunctions`
|
||||
|
||||
Delete the deprecated functions instead of only deprecating them by defining the macro
|
||||
[`JSON_DELETE_DEPRECATED_FUNCTIONS`](../api/macros/json_delete_deprecated_functions.md). This option is `OFF` by
|
||||
default.
|
||||
|
||||
### `JSON_Diagnostics`
|
||||
|
||||
Enable [extended diagnostic messages](../home/exceptions.md#extended-diagnostic-messages) by defining macro [`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md). This option is `OFF` by default.
|
||||
|
||||
@@ -14,6 +14,13 @@ deprecations are annotated with
|
||||
[`HEDLEY_DEPRECATED_FOR`](https://nemequ.github.io/hedley/api-reference.html#HEDLEY_DEPRECATED_FOR) to report which
|
||||
function to use instead.
|
||||
|
||||
!!! tip "Find all calls of deprecated functions"
|
||||
|
||||
Define [`JSON_DELETE_DEPRECATED_FUNCTIONS`](../api/macros/json_delete_deprecated_functions.md) to `1` (or set the
|
||||
CMake option [`JSON_DeleteDeprecatedFunctions`](cmake.md#json_deletedeprecatedfunctions)) to delete all deprecated
|
||||
functions. Every remaining call then fails to compile, even if deprecation warnings are disabled, so your code is
|
||||
ready for version 4.0.0 once it compiles with the macro.
|
||||
|
||||
### Parsing
|
||||
|
||||
- Function `friend std::istream& operator<<(basic_json&, std::istream&)` is deprecated since 3.0.0. Please use
|
||||
@@ -41,8 +48,10 @@ function to use instead.
|
||||
[`from_ubjson`](../api/basic_json/from_ubjson.md), and [`from_bson`](../api/basic_json/from_bson.md)) via initializer
|
||||
lists is deprecated since 3.8.0. Instead, pass two iterators; for instance, call `from_cbor(ptr, ptr+len)` instead of
|
||||
`from_cbor({ptr, len})`. Likewise, passing a pointer and a length as two separate arguments to `from_cbor`,
|
||||
`from_msgpack`, `from_ubjson`, and `from_bson` is deprecated since 3.8.0; call `from_cbor(ptr, ptr+len)` instead of
|
||||
`from_cbor(ptr, len)`.
|
||||
`from_msgpack`, `from_ubjson`, and `from_bson` is deprecated since 3.8.0, and to
|
||||
[`from_bjdata`](../api/basic_json/from_bjdata.md) and [`from_bon8`](../api/basic_json/from_bon8.md) since 3.13.0; call
|
||||
`from_cbor(ptr, ptr+len)` instead of `from_cbor(ptr, len)`. These overloads will not be removed in version 4.0.0, but
|
||||
deleted, so a call like `from_cbor(ptr, len)` cannot compile and convert `len` to the `strict` parameter.
|
||||
|
||||
=== "Deprecated"
|
||||
|
||||
|
||||
+2
-12
@@ -237,20 +237,14 @@ nav:
|
||||
- 'Overview': api/basic_json_document/index.md
|
||||
- '(Constructor)': api/basic_json_document/basic_json_document.md
|
||||
- 'accept': api/basic_json_document/accept.md
|
||||
- 'erase': api/basic_json_document/erase.md
|
||||
- 'insert': api/basic_json_document/insert.md
|
||||
- 'is_discarded': api/basic_json_document/is_discarded.md
|
||||
- 'load': api/basic_json_document/load.md
|
||||
- 'memory_usage': api/basic_json_document/memory_usage.md
|
||||
- 'node_count': api/basic_json_document/node_count.md
|
||||
- 'owns_source': api/basic_json_document/owns_source.md
|
||||
- 'parse': api/basic_json_document/parse.md
|
||||
- 'parse_copy': api/basic_json_document/parse_copy.md
|
||||
- 'push_back': api/basic_json_document/push_back.md
|
||||
- 'read': api/basic_json_document/read.md
|
||||
- 'root': api/basic_json_document/root.md
|
||||
- 'save': api/basic_json_document/save.md
|
||||
- 'set': api/basic_json_document/set.md
|
||||
- 'shrink_to_fit': api/basic_json_document/shrink_to_fit.md
|
||||
- 'source': api/basic_json_document/source.md
|
||||
- basic_json_view:
|
||||
@@ -313,8 +307,6 @@ nav:
|
||||
- 'to_json': api/adl_serializer/to_json.md
|
||||
- 'json': api/json.md
|
||||
- 'json_document': api/json_document.md
|
||||
- 'json_editable_document': api/json_editable_document.md
|
||||
- 'json_editable_view': api/json_editable_view.md
|
||||
- json_pointer:
|
||||
- 'Overview': api/json_pointer/index.md
|
||||
- '(Constructor)': api/json_pointer/json_pointer.md
|
||||
@@ -356,8 +348,6 @@ nav:
|
||||
- 'operator""_json_pointer': api/operator_literal_json_pointer.md
|
||||
- 'ordered_json': api/ordered_json.md
|
||||
- 'ordered_json_document': api/ordered_json_document.md
|
||||
- 'ordered_json_editable_document': api/ordered_json_editable_document.md
|
||||
- 'ordered_json_editable_view': api/ordered_json_editable_view.md
|
||||
- 'ordered_json_view': api/ordered_json_view.md
|
||||
- 'ordered_map': api/ordered_map.md
|
||||
- macros:
|
||||
@@ -366,6 +356,7 @@ nav:
|
||||
- 'JSON_BRACE_INIT_COPY_SEMANTICS': api/macros/json_brace_init_copy_semantics.md
|
||||
- 'JSON_CATCH_USER, JSON_THROW_USER, JSON_TRY_USER': api/macros/json_throw_user.md
|
||||
- 'JSON_DIAGNOSTICS': api/macros/json_diagnostics.md
|
||||
- 'JSON_DELETE_DEPRECATED_FUNCTIONS': api/macros/json_delete_deprecated_functions.md
|
||||
- 'JSON_DIAGNOSTIC_POSITIONS': api/macros/json_diagnostic_positions.md
|
||||
- 'JSON_DISABLE_ENUM_SERIALIZATION': api/macros/json_disable_enum_serialization.md
|
||||
- 'JSON_DISABLE_TUPLE_REFERENCE_CONVERSION': api/macros/json_disable_tuple_reference_conversion.md
|
||||
@@ -387,9 +378,8 @@ nav:
|
||||
- 'JSON_USE_GLOBAL_UDLS': api/macros/json_use_global_udls.md
|
||||
- 'JSON_USE_IMPLICIT_CONVERSIONS': api/macros/json_use_implicit_conversions.md
|
||||
- 'JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON': api/macros/json_use_legacy_discarded_value_comparison.md
|
||||
- 'JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS': api/macros/json_use_objects_for_enum_keyed_maps.md
|
||||
- 'JSON_USE_SIMDUTF': api/macros/json_use_simdutf.md
|
||||
- 'JSON_VIEW_NO_SIMD': api/macros/json_view_no_simd.md
|
||||
- 'JSON_VIEW_USE_SSSE3': api/macros/json_view_use_ssse3.md
|
||||
- 'NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE, NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_derived_type.md
|
||||
- 'NLOHMANN_DEFINE_TYPE_INTRUSIVE, NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_type_intrusive.md
|
||||
- 'NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE, NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_type_non_intrusive.md
|
||||
|
||||
@@ -50,6 +50,10 @@
|
||||
#define JSON_STRICT_BINARY_UTF8 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 0
|
||||
#endif
|
||||
|
||||
#if JSON_DIAGNOSTICS
|
||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||
#else
|
||||
@@ -92,14 +96,20 @@
|
||||
#define NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8
|
||||
#endif
|
||||
|
||||
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
#define NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS _ekmo
|
||||
#else
|
||||
#define NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
#endif
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||
#endif
|
||||
|
||||
// Construct the namespace ABI tags component
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g) json_abi ## a ## b ## c ## d ## e ## f ## g
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f, g) \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g)
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h) json_abi ## a ## b ## c ## d ## e ## f ## g ## h
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f, g, h) \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h)
|
||||
|
||||
#define NLOHMANN_JSON_ABI_TAGS \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||
@@ -109,7 +119,8 @@
|
||||
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS, \
|
||||
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
||||
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING, \
|
||||
NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8)
|
||||
NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8, \
|
||||
NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS)
|
||||
|
||||
// Construct the namespace version component
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||
|
||||
@@ -172,7 +172,7 @@ inline void from_json(const BasicJsonType& j, EnumType& e)
|
||||
typename BasicJsonType::number_unsigned_t, underlying_type>::type;
|
||||
value_type val;
|
||||
get_arithmetic_value(j, val);
|
||||
e = static_cast<EnumType>(static_cast<underlying_type>(val));
|
||||
e = static_cast<EnumType>(bool_aware_static_cast<underlying_type>(val));
|
||||
}
|
||||
#endif // JSON_DISABLE_ENUM_SERIALIZATION
|
||||
|
||||
@@ -530,11 +530,40 @@ void from_json_pair_array_to_map(const BasicJsonType& j, MapType& m)
|
||||
}
|
||||
}
|
||||
|
||||
// read a map with enum keys from an object, using the enum's own from_json for
|
||||
// the keys (e.g., from NLOHMANN_JSON_SERIALIZE_ENUM); this is the form written
|
||||
// with JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
template<typename BasicJsonType, typename Map>
|
||||
inline bool from_json_enum_keyed_object(const BasicJsonType& j, Map& m, std::true_type /*key is enum*/)
|
||||
{
|
||||
if (!j.is_object())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m.clear();
|
||||
for (const auto& p : *j.template get_ptr<const typename BasicJsonType::object_t*>())
|
||||
{
|
||||
m.emplace(BasicJsonType(p.first).template get<typename Map::key_type>(), p.second.template get<typename Map::mapped_type>());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename Map>
|
||||
inline bool from_json_enum_keyed_object(const BasicJsonType& /*j*/, Map& /*m*/, std::false_type /*key is enum*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
|
||||
typename = enable_if_t < !std::is_constructible <
|
||||
typename BasicJsonType::string_t, Key >::value >>
|
||||
void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
|
||||
{
|
||||
// NOLINTNEXTLINE(modernize-type-traits) we use C++11
|
||||
if (from_json_enum_keyed_object(j, m, std::is_enum<Key> {}))
|
||||
{
|
||||
return;
|
||||
}
|
||||
from_json_pair_array_to_map(j, m);
|
||||
}
|
||||
|
||||
@@ -543,6 +572,11 @@ template < typename BasicJsonType, typename Key, typename Value, typename Hash,
|
||||
typename BasicJsonType::string_t, Key >::value >>
|
||||
void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
|
||||
{
|
||||
// NOLINTNEXTLINE(modernize-type-traits) we use C++11
|
||||
if (from_json_enum_keyed_object(j, m, std::is_enum<Key> {}))
|
||||
{
|
||||
return;
|
||||
}
|
||||
from_json_pair_array_to_map(j, m);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <valarray> // valarray
|
||||
#include <vector> // vector
|
||||
|
||||
#include <nlohmann/detail/exceptions.hpp>
|
||||
#include <nlohmann/detail/iterators/iteration_proxy.hpp>
|
||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
||||
#include <nlohmann/detail/meta/std_fs.hpp>
|
||||
@@ -286,9 +287,20 @@ struct external_constructor<value_t::object>
|
||||
/////////////
|
||||
|
||||
#ifdef JSON_HAS_CPP_17
|
||||
// whether storing the value of a std::optional<T> cannot throw; MSVC 2017
|
||||
// evaluates std::is_nothrow_assignable as true even if T's to_json throws, so
|
||||
// the exception would call std::terminate (#5642)
|
||||
#if defined(_MSC_VER) && !defined(__clang__) && _MSC_VER < 1920
|
||||
template<typename BasicJsonType, typename T>
|
||||
using is_nothrow_optional_to_json = std::false_type;
|
||||
#else
|
||||
template<typename BasicJsonType, typename T>
|
||||
using is_nothrow_optional_to_json = std::is_nothrow_assignable<BasicJsonType&, const T&>;
|
||||
#endif
|
||||
|
||||
template<typename BasicJsonType, typename T,
|
||||
enable_if_t<std::is_constructible<BasicJsonType, T>::value, int> = 0>
|
||||
void to_json(BasicJsonType& j, const std::optional<T>& opt) noexcept(std::is_nothrow_assignable<BasicJsonType&, const T&>::value)
|
||||
void to_json(BasicJsonType& j, const std::optional<T>& opt) noexcept(is_nothrow_optional_to_json<BasicJsonType, T>::value)
|
||||
{
|
||||
if (opt.has_value())
|
||||
{
|
||||
@@ -364,7 +376,7 @@ inline void to_json(BasicJsonType& j, EnumType e) noexcept
|
||||
{
|
||||
using underlying_type = typename std::underlying_type<EnumType>::type;
|
||||
static constexpr value_t integral_value_t = std::is_unsigned<underlying_type>::value ? value_t::number_unsigned : value_t::number_integer;
|
||||
external_constructor<integral_value_t>::construct(j, static_cast<underlying_type>(e));
|
||||
external_constructor<integral_value_t>::construct(j, bool_aware_static_cast<underlying_type>(e));
|
||||
}
|
||||
#endif // JSON_DISABLE_ENUM_SERIALIZATION
|
||||
|
||||
@@ -384,6 +396,9 @@ template < typename BasicJsonType, typename CompatibleArrayType,
|
||||
!is_basic_json<CompatibleArrayType>::value
|
||||
#if JSON_HAS_RANGE_VIEW_CONVERSION
|
||||
&& !is_compatible_range_view<CompatibleArrayType>::value
|
||||
#endif
|
||||
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
&& !is_enum_keyed_map<CompatibleArrayType>::value
|
||||
#endif
|
||||
,
|
||||
int > = 0 >
|
||||
@@ -438,6 +453,33 @@ inline void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
|
||||
external_constructor<value_t::object>::construct(j, obj);
|
||||
}
|
||||
|
||||
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
// store a map with enum keys as an object, using the enum's own to_json for the
|
||||
// keys (e.g., from NLOHMANN_JSON_SERIALIZE_ENUM); without the macro, such maps
|
||||
// are stored as arrays of [key, value] pairs
|
||||
template < typename BasicJsonType, typename EnumKeyedMap,
|
||||
enable_if_t < is_enum_keyed_map<EnumKeyedMap>::value&& !is_basic_json<EnumKeyedMap>::value, int > = 0 >
|
||||
inline void to_json(BasicJsonType& j, const EnumKeyedMap& map)
|
||||
{
|
||||
typename BasicJsonType::object_t obj;
|
||||
for (const auto& p : map)
|
||||
{
|
||||
BasicJsonType key = p.first;
|
||||
if (JSON_HEDLEY_UNLIKELY(!key.is_string()))
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be string, but is ", key.type_name()), &key));
|
||||
}
|
||||
|
||||
auto& key_string = *key.template get_ptr<typename BasicJsonType::string_t*>();
|
||||
if (JSON_HEDLEY_UNLIKELY(!obj.emplace(key_string, BasicJsonType(p.second)).second))
|
||||
{
|
||||
JSON_THROW(type_error::create(318, concat("duplicate object key '", key_string, "'"), &key));
|
||||
}
|
||||
}
|
||||
external_constructor<value_t::object>::construct(j, std::move(obj));
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename BasicJsonType>
|
||||
inline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -221,7 +221,7 @@ class lexer : public lexer_base<BasicJsonType>
|
||||
// scan functions
|
||||
/////////////////////
|
||||
|
||||
/// contiguous input: try to decode the 4 hex digits following `\u`
|
||||
/// contiguous input: try to decode the 4 hex digits following `\\u`
|
||||
/// directly from the input buffer via hex_codepoint(), instead of 4 calls
|
||||
/// to get(). On success, advances the adapter and the position counters
|
||||
/// exactly as those 4 get() calls would (a hex digit is never '\n', so
|
||||
@@ -2070,6 +2070,39 @@ scan_number_done:
|
||||
// read the next character and ignore whitespace
|
||||
skip_whitespace();
|
||||
|
||||
return scan_after_whitespace();
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief scan the next token when the caller expects a separator (':' or
|
||||
',') most of the time
|
||||
|
||||
After an object key the next token is almost always ':', after a value
|
||||
inside an object or array almost always ','. Testing for that character
|
||||
first is a compare and a well-predicted branch, where the switch in
|
||||
scan_after_whitespace() is an indirect jump through a table. Anything else
|
||||
goes through the switch, so the result is the same as scan()'s.
|
||||
|
||||
May only be called after scan() has run once (the BOM check is skipped).
|
||||
*/
|
||||
token_type scan_expecting(token_type expected_type)
|
||||
{
|
||||
JSON_ASSERT(expected_type == token_type::name_separator || expected_type == token_type::value_separator);
|
||||
JSON_ASSERT(position.chars_read_total > 0);
|
||||
const char_int_type expected_char = static_cast<unsigned char>((expected_type == token_type::name_separator) ? ':' : ',');
|
||||
skip_whitespace();
|
||||
if (JSON_HEDLEY_LIKELY(current == expected_char))
|
||||
{
|
||||
return expected_type;
|
||||
}
|
||||
return scan_after_whitespace();
|
||||
}
|
||||
|
||||
private:
|
||||
/// the part of scan() after the leading whitespace: skip comments and
|
||||
/// scan the token that starts with current
|
||||
token_type scan_after_whitespace()
|
||||
{
|
||||
// ignore comments
|
||||
while (ignore_comments && current == '/')
|
||||
{
|
||||
@@ -2149,7 +2182,6 @@ scan_number_done:
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/// input adapter
|
||||
InputAdapterType ia;
|
||||
|
||||
|
||||
@@ -260,7 +260,7 @@ class parser
|
||||
}
|
||||
|
||||
// parse separator (:)
|
||||
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_token_expecting(token_type::name_separator)))
|
||||
{
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
@@ -423,7 +423,7 @@ class parser
|
||||
{
|
||||
// comma -> next value
|
||||
// or end of array (ignore_trailing_commas = true)
|
||||
if (get_token() == token_type::value_separator)
|
||||
if (get_token_expecting(token_type::value_separator))
|
||||
{
|
||||
// parse a new value
|
||||
get_token();
|
||||
@@ -463,7 +463,7 @@ class parser
|
||||
|
||||
// comma -> next value
|
||||
// or end of object (ignore_trailing_commas = true)
|
||||
if (get_token() == token_type::value_separator)
|
||||
if (get_token_expecting(token_type::value_separator))
|
||||
{
|
||||
get_token();
|
||||
|
||||
@@ -484,7 +484,7 @@ class parser
|
||||
}
|
||||
|
||||
// parse separator (:)
|
||||
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_token_expecting(token_type::name_separator)))
|
||||
{
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
@@ -528,6 +528,13 @@ class parser
|
||||
return last_token = m_lexer.scan();
|
||||
}
|
||||
|
||||
/// get next token from lexer; true if it is the separator @a expected_type
|
||||
/// (name_separator or value_separator), which it usually is
|
||||
bool get_token_expecting(token_type expected_type)
|
||||
{
|
||||
return (last_token = m_lexer.scan_expecting(expected_type)) == expected_type;
|
||||
}
|
||||
|
||||
std::string exception_message(const token_type expected, const std::string& context)
|
||||
{
|
||||
std::string error_msg = "syntax error ";
|
||||
|
||||
@@ -88,9 +88,13 @@ class json_pointer
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_string_t/
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, to_string())
|
||||
operator string_t() const
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return to_string();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef JSON_NO_IO
|
||||
/// @brief write string representation of the JSON pointer to stream
|
||||
@@ -1029,9 +1033,13 @@ class json_pointer
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer))
|
||||
bool operator==(const string_t& rhs) const
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return *this == json_pointer(rhs);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief 3-way compares two JSON pointers
|
||||
template<typename RefStringTypeRhs>
|
||||
@@ -1109,18 +1117,26 @@ template<typename RefStringTypeLhs,
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer))
|
||||
inline bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
|
||||
const StringType& rhs)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return lhs == json_pointer<RefStringTypeLhs>(rhs);
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename RefStringTypeRhs,
|
||||
typename StringType = typename json_pointer<RefStringTypeRhs>::string_t>
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer))
|
||||
inline bool operator==(const StringType& lhs,
|
||||
const json_pointer<RefStringTypeRhs>& rhs)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return json_pointer<RefStringTypeRhs>(lhs) == rhs;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename RefStringTypeLhs, typename RefStringTypeRhs>
|
||||
inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
|
||||
@@ -1134,18 +1150,26 @@ template<typename RefStringTypeLhs,
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer))
|
||||
inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
|
||||
const StringType& rhs)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename RefStringTypeRhs,
|
||||
typename StringType = typename json_pointer<RefStringTypeRhs>::string_t>
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer))
|
||||
inline bool operator!=(const StringType& lhs,
|
||||
const json_pointer<RefStringTypeRhs>& rhs)
|
||||
#if JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename RefStringTypeLhs, typename RefStringTypeRhs>
|
||||
inline bool operator<(const json_pointer<RefStringTypeLhs>& lhs,
|
||||
|
||||
@@ -888,6 +888,10 @@
|
||||
#define JSON_DISABLE_ENUM_SERIALIZATION 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
#define JSON_DELETE_DEPRECATED_FUNCTIONS 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
||||
#define JSON_DISABLE_TUPLE_REFERENCE_CONVERSION 0
|
||||
#endif
|
||||
@@ -45,6 +45,8 @@
|
||||
#undef JSON_PRECISE_STREAM_POSITION
|
||||
#undef JSON_STRICT_NUL_HANDLING
|
||||
#undef JSON_STRICT_BINARY_UTF8
|
||||
#undef JSON_DELETE_DEPRECATED_FUNCTIONS
|
||||
#undef JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
#endif
|
||||
|
||||
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
|
||||
@@ -438,6 +438,30 @@ template<typename BasicJsonType, typename CompatibleObjectType>
|
||||
struct is_compatible_object_type
|
||||
: is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};
|
||||
|
||||
template<typename T>
|
||||
using insert_result_t = decltype(std::declval<T&>().insert(std::declval<const value_type_t<T>&>()));
|
||||
|
||||
template<typename T>
|
||||
using insert_result_second_t = decltype(std::declval<T&>().insert(std::declval<const value_type_t<T>&>()).second);
|
||||
|
||||
// a map-like type (std::map, std::unordered_map, ...) whose keys are enums; see
|
||||
// JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
||||
template<typename T, typename = void>
|
||||
struct is_enum_keyed_map : std::false_type {};
|
||||
|
||||
template<typename T>
|
||||
struct is_enum_keyed_map <
|
||||
T, enable_if_t < is_detected<mapped_type_t, T>::value&&
|
||||
is_detected<key_type_t, T>::value >>
|
||||
{
|
||||
// maps with non-unique keys (std::multimap, std::unordered_multimap, ...)
|
||||
// are excluded, because an object cannot hold duplicate keys; they are
|
||||
// detected by insert() returning an iterator instead of a pair<iterator, bool>
|
||||
// NOLINTNEXTLINE(modernize-type-traits) we use C++11
|
||||
static constexpr bool value = std::is_enum<typename T::key_type>::value &&
|
||||
!(is_detected<insert_result_t, T>::value && !is_detected<insert_result_second_t, T>::value);
|
||||
};
|
||||
|
||||
template<typename BasicJsonType, typename ConstructibleObjectType,
|
||||
typename = void>
|
||||
struct is_constructible_object_type_impl : std::false_type {};
|
||||
@@ -882,6 +906,21 @@ T conditional_static_cast(U value)
|
||||
return value;
|
||||
}
|
||||
|
||||
// like conditional_static_cast, but converts to bool by comparing with zero,
|
||||
// because MSVC 2015 warns about any conversion to bool (C4800), even with an
|
||||
// explicit cast; used for enums whose underlying type is bool
|
||||
template < typename T, typename U, enable_if_t < !std::is_same<T, bool>::value, int > = 0 >
|
||||
T bool_aware_static_cast(U value)
|
||||
{
|
||||
return conditional_static_cast<T>(value);
|
||||
}
|
||||
|
||||
template<typename T, typename U, enable_if_t<std::is_same<T, bool>::value, int> = 0>
|
||||
bool bool_aware_static_cast(U value)
|
||||
{
|
||||
return value != U();
|
||||
}
|
||||
|
||||
template<typename... Types>
|
||||
using all_integral = conjunction<std::is_integral<Types>...>;
|
||||
|
||||
|
||||
@@ -127,6 +127,7 @@ class binary_writer
|
||||
@throw type_error.316 if a string value or an object key is not valid
|
||||
UTF-8
|
||||
@throw type_error.317 if @a j is not an object
|
||||
@throw type_error.321 if a value nested in @a j is discarded
|
||||
*/
|
||||
void write_bson(const BasicJsonType& j)
|
||||
{
|
||||
@@ -158,6 +159,7 @@ class binary_writer
|
||||
@param[in] j JSON value to serialize
|
||||
@throw type_error.316 if a string value or an object key is not valid
|
||||
UTF-8
|
||||
@throw type_error.321 if @a j or a value nested in it is discarded
|
||||
*/
|
||||
void write_cbor(const BasicJsonType& j)
|
||||
{
|
||||
@@ -322,7 +324,7 @@ class binary_writer
|
||||
|
||||
case value_t::discarded:
|
||||
default:
|
||||
break;
|
||||
throw_on_discarded(j, "CBOR");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,6 +384,7 @@ class binary_writer
|
||||
|
||||
/*!
|
||||
@param[in] j JSON value to serialize
|
||||
@throw type_error.321 if @a j or a value nested in it is discarded
|
||||
*/
|
||||
void write_msgpack(const BasicJsonType& j)
|
||||
{
|
||||
@@ -655,7 +658,7 @@ class binary_writer
|
||||
|
||||
case value_t::discarded:
|
||||
default:
|
||||
break;
|
||||
throw_on_discarded(j, "MessagePack");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -668,6 +671,7 @@ class binary_writer
|
||||
@param[in] bjdata_version which BJData version to use, default is draft2
|
||||
@throw type_error.316 if a string value or an object key is not valid
|
||||
UTF-8
|
||||
@throw type_error.321 if @a j or a value nested in it is discarded
|
||||
*/
|
||||
void write_ubjson(const BasicJsonType& j, const bool use_count,
|
||||
const bool use_type, const bool add_prefix = true,
|
||||
@@ -901,7 +905,7 @@ class binary_writer
|
||||
|
||||
case value_t::discarded:
|
||||
default:
|
||||
break;
|
||||
throw_on_discarded(j, use_bjdata ? "BJData" : "UBJSON");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -921,6 +925,15 @@ class binary_writer
|
||||
}
|
||||
|
||||
private:
|
||||
/*!
|
||||
@brief throws because @a j is discarded and cannot be serialized
|
||||
@throw type_error.321 always
|
||||
*/
|
||||
JSON_HEDLEY_NO_RETURN static void throw_on_discarded(const BasicJsonType& j, const char* format_name)
|
||||
{
|
||||
JSON_THROW(type_error::create(321, concat("cannot serialize discarded value to ", format_name), &j));
|
||||
}
|
||||
|
||||
//////////
|
||||
// BSON //
|
||||
//////////
|
||||
@@ -1172,6 +1185,7 @@ class binary_writer
|
||||
into a byte, before anything is written
|
||||
@throw type_error.316 if @a j is a string that is not valid UTF-8, before
|
||||
anything is written
|
||||
@throw type_error.321 if @a j is discarded
|
||||
*/
|
||||
std::size_t calc_bson_value_size(const BasicJsonType& j)
|
||||
{
|
||||
@@ -1198,10 +1212,12 @@ class binary_writer
|
||||
case value_t::null:
|
||||
return 0ul;
|
||||
|
||||
case value_t::discarded:
|
||||
throw_on_discarded(j, "BSON");
|
||||
|
||||
// LCOV_EXCL_START
|
||||
case value_t::object:
|
||||
case value_t::array:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
|
||||
return 0ul;
|
||||
@@ -1238,10 +1254,12 @@ class binary_writer
|
||||
case value_t::null:
|
||||
return write_bson_null(name);
|
||||
|
||||
case value_t::discarded:
|
||||
throw_on_discarded(j, "BSON");
|
||||
|
||||
// LCOV_EXCL_START
|
||||
case value_t::object:
|
||||
case value_t::array:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
|
||||
return;
|
||||
@@ -1308,6 +1326,8 @@ class binary_writer
|
||||
byte, before anything is written
|
||||
@throw type_error.316 if a string value or a key is not valid UTF-8,
|
||||
before anything is written
|
||||
@throw type_error.321 if a value nested in @a document is discarded,
|
||||
before anything is written
|
||||
*/
|
||||
std::size_t calc_bson_sizes(const BasicJsonType& document, std::vector<std::size_t>& nested_sizes)
|
||||
{
|
||||
|
||||
@@ -116,13 +116,6 @@ class builder
|
||||
frame shallow[64]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays): not initialized on purpose; filled as containers open
|
||||
std::vector<frame> deep{};
|
||||
|
||||
/// remember an object to index after parsing (out of line, so that the
|
||||
/// parse loop only has a call for it)
|
||||
NLOHMANN_VIEW_NOINLINE void note_large_object(std::uint32_t idx)
|
||||
{
|
||||
doc.large_objects.push_back(idx);
|
||||
}
|
||||
|
||||
NLOHMANN_VIEW_NOINLINE bool fail(error_code c, const unsigned char* at) noexcept
|
||||
{
|
||||
m_failure.code = c;
|
||||
@@ -164,14 +157,12 @@ class builder
|
||||
if (p[1] == '/')
|
||||
{
|
||||
p += 2;
|
||||
// (as in parse(), a null byte is the end of the input, so it is
|
||||
// left for the caller to see)
|
||||
while (p != e && *p != '\n' && *p != '\r' && !(NulIsEnd && *p == 0))
|
||||
{
|
||||
++p;
|
||||
}
|
||||
if (NulIsEnd && p != e && *p == 0)
|
||||
{
|
||||
++p; // as in parse(), a null byte ends the comment like a line break
|
||||
}
|
||||
return p;
|
||||
}
|
||||
if (p[1] == '*')
|
||||
@@ -510,7 +501,7 @@ class builder
|
||||
switch (cur()) \
|
||||
{ \
|
||||
case '"': \
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!string<true>())) { return false; } \
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!string())) { return false; } \
|
||||
goto NEXT; \
|
||||
case '{': \
|
||||
open(value_t::object); \
|
||||
@@ -594,7 +585,7 @@ obj_key:
|
||||
{
|
||||
return fail(error_code::expected_key);
|
||||
}
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!string<false>()))
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!string()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -635,27 +626,19 @@ obj_next:
|
||||
if (enabled(TrailingCommas) && cur() == '}')
|
||||
{
|
||||
++p;
|
||||
goto close_object;
|
||||
goto close_container;
|
||||
}
|
||||
goto obj_key;
|
||||
}
|
||||
if (cur() == '}')
|
||||
{
|
||||
++p;
|
||||
goto close_object;
|
||||
goto close_container;
|
||||
}
|
||||
return fail(error_code::expected_object_end);
|
||||
|
||||
#undef NLOHMANN_VIEW_VALUE
|
||||
|
||||
close_object:
|
||||
// a large object gets a hash index (objects only, so that closing
|
||||
// an array pays nothing for this)
|
||||
if (NLOHMANN_VIEW_UNLIKELY(cur_count >= document_data::index_min_members))
|
||||
{
|
||||
cold.note_large_object(cur_idx);
|
||||
}
|
||||
|
||||
close_container:
|
||||
close();
|
||||
if (NLOHMANN_VIEW_UNLIKELY(depth == 0))
|
||||
@@ -705,7 +688,7 @@ root_done:
|
||||
switch (cur())
|
||||
{
|
||||
case '"':
|
||||
return string<true>();
|
||||
return string();
|
||||
case 't':
|
||||
return literal("true", 4, value_t::boolean, node_flags::is_true);
|
||||
case 'f':
|
||||
@@ -828,19 +811,14 @@ indent_done:
|
||||
const auto idx = static_cast<std::uint32_t>(emit(k, 0, 0, static_cast<std::size_t>(p - b), 0) - base);
|
||||
if (depth != 0)
|
||||
{
|
||||
const frame f = {cur_idx, cur_count, cur_is_object};
|
||||
if (NLOHMANN_VIEW_LIKELY(depth <= 64))
|
||||
{
|
||||
// field by field: a frame put together on the stack and
|
||||
// copied would be read back wider than it was written,
|
||||
// and that load waits until the stores are done
|
||||
frame& f = cold.shallow[depth - 1];
|
||||
f.idx = cur_idx;
|
||||
f.count = cur_count;
|
||||
f.is_object = cur_is_object;
|
||||
cold.shallow[depth - 1] = f;
|
||||
}
|
||||
else
|
||||
{
|
||||
cold.deep.push_back(frame{cur_idx, cur_count, cur_is_object});
|
||||
cold.deep.push_back(f);
|
||||
}
|
||||
}
|
||||
++depth;
|
||||
@@ -856,21 +834,19 @@ indent_done:
|
||||
n.next = static_cast<std::uint32_t>(out - base) - cur_idx;
|
||||
if (--depth != 0)
|
||||
{
|
||||
frame f{};
|
||||
if (NLOHMANN_VIEW_LIKELY(depth <= 64))
|
||||
{
|
||||
const frame& f = cold.shallow[depth - 1];
|
||||
cur_idx = f.idx;
|
||||
cur_count = f.count;
|
||||
cur_is_object = f.is_object;
|
||||
f = cold.shallow[depth - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
const frame f = cold.deep.back();
|
||||
f = cold.deep.back();
|
||||
cold.deep.pop_back();
|
||||
cur_idx = f.idx;
|
||||
cur_count = f.count;
|
||||
cur_is_object = f.is_object;
|
||||
}
|
||||
cur_idx = f.idx;
|
||||
cur_count = f.count;
|
||||
cur_is_object = f.is_object;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -999,13 +975,12 @@ indent_done:
|
||||
return true;
|
||||
}
|
||||
|
||||
/// a string at p: a value (Value) or a key
|
||||
template<bool Value>
|
||||
/// a string (value or key) at p
|
||||
NLOHMANN_VIEW_ALWAYS_INLINE bool string()
|
||||
{
|
||||
++p; // opening quote
|
||||
const unsigned char* const s = p;
|
||||
p = scan_string_run<Value>(p, e);
|
||||
p = scan_string_run(p, e);
|
||||
if (NLOHMANN_VIEW_LIKELY(p != e && *p == '"'))
|
||||
{
|
||||
emit(value_t::string, 0, 0, static_cast<std::size_t>(s - b), static_cast<std::uint64_t>(p - s));
|
||||
|
||||
@@ -10,14 +10,9 @@
|
||||
|
||||
#include <array> // array
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // uint8_t, uint32_t
|
||||
#include <cstring> // memcpy
|
||||
#include <functional> // less
|
||||
#include <map> // map
|
||||
#include <memory> // unique_ptr
|
||||
#include <new> // operator new, placement new
|
||||
#include <string> // string
|
||||
#include <vector> // vector
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <nlohmann/detail/view/macro_scope.hpp>
|
||||
@@ -41,44 +36,10 @@ struct document_data
|
||||
node* inline_tape = nullptr; ///< node array allocated together with this header
|
||||
std::size_t inline_cap = 0;
|
||||
std::string arena{}; ///< decoded strings that contained escapes // NOLINT(readability-redundant-member-init)
|
||||
std::size_t arena_size = 0; ///< bytes of decoded strings at base[1] (the arena, or those of a loaded image)
|
||||
std::string owned{}; ///< owned copy of the input, if any // NOLINT(readability-redundant-member-init)
|
||||
std::vector<std::uint8_t> owned_image{}; ///< a loaded image the document owns (the text and the decoded strings point into it) // NOLINT(readability-redundant-member-init)
|
||||
|
||||
// hash indexes of large objects (see object_index.hpp)
|
||||
static constexpr std::uint32_t index_min_members = 128;
|
||||
struct object_index
|
||||
{
|
||||
std::size_t start; ///< first slot in index_slots
|
||||
std::uint32_t mask; ///< slot count - 1 (a power of two minus one)
|
||||
};
|
||||
std::vector<object_index> indexes{}; // NOLINT(readability-redundant-member-init)
|
||||
std::vector<std::uint32_t> index_slots{}; // NOLINT(readability-redundant-member-init)
|
||||
std::vector<std::uint32_t> large_objects{}; ///< positions of the objects to index (noted while parsing) // NOLINT(readability-redundant-member-init)
|
||||
std::array<const char*, 4> base = {{nullptr, nullptr, nullptr, nullptr}}; ///< string bases: source, arena, edit arena (indexed by flags & node_flags::storage)
|
||||
std::array<const char*, 4> base = {{nullptr, nullptr, nullptr, nullptr}}; ///< string bases: source, arena (indexed by flags & node_flags::storage)
|
||||
bool discarded = true;
|
||||
|
||||
/// The storage of edits (editable documents only; see edit_storage.hpp).
|
||||
/// Edits never move or resize the parsed index, so views stay valid: an
|
||||
/// array/object whose elements change gets node_flags::moved, and its
|
||||
/// elements then live in a separate sequence (a header node, then the
|
||||
/// entries), whose entries link to the values.
|
||||
struct edit_state
|
||||
{
|
||||
std::vector<node*> moved{}; ///< element sequences of moved arrays/objects (header node first) // NOLINT(readability-redundant-member-init)
|
||||
std::vector<std::size_t> moved_cap{}; ///< capacity in nodes of a growable block; 0: a fixed sequence (a new value) // NOLINT(readability-redundant-member-init)
|
||||
std::vector<std::unique_ptr<node[]>> chunks{}; ///< storage of new values and blocks; never moved // NOLINT(readability-redundant-member-init,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
std::map<const node*, node*, std::less<const node*>> regions{}; ///< new arrays/objects: root -> container that uses it as its element sequence (nullptr: linked from a block) // NOLINT(readability-redundant-member-init)
|
||||
node* chunk_cur = nullptr;
|
||||
node* chunk_end = nullptr;
|
||||
std::size_t chunk_next = 64;
|
||||
std::vector<std::unique_ptr<char[]>> texts{}; ///< edit arena, the current buffer last; earlier ones stay alive for string views // NOLINT(readability-redundant-member-init,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
std::size_t text_used = 0;
|
||||
std::size_t text_cap = 0;
|
||||
std::size_t bytes = 0; ///< memory held by edits
|
||||
};
|
||||
std::unique_ptr<edit_state> edits{}; ///< created by the first edit // NOLINT(readability-redundant-member-init)
|
||||
|
||||
/// one allocation for the header and room for `nodes` nodes; large
|
||||
/// documents get a separate node array instead (so it can be trimmed)
|
||||
static document_data* create(std::size_t nodes)
|
||||
@@ -103,7 +64,7 @@ struct document_data
|
||||
}
|
||||
};
|
||||
|
||||
document_data() = default;
|
||||
document_data() noexcept = default;
|
||||
document_data(const document_data&) = delete;
|
||||
document_data(document_data&&) = delete;
|
||||
document_data& operator=(const document_data&) = delete;
|
||||
@@ -162,78 +123,6 @@ struct document_data
|
||||
{
|
||||
return n + n->next;
|
||||
}
|
||||
|
||||
/// (editable documents) first element or key, also of a moved container
|
||||
NLOHMANN_VIEW_ALWAYS_INLINE const node* first_child_edited(const node* n) const noexcept
|
||||
{
|
||||
return NLOHMANN_VIEW_LIKELY((n->flags & node_flags::moved) == 0) ? n + 1 : edits->moved[n->off] + 1;
|
||||
}
|
||||
|
||||
/// (editable documents) end of the elements, also of a moved container
|
||||
NLOHMANN_VIEW_ALWAYS_INLINE const node* child_end_edited(const node* n) const noexcept
|
||||
{
|
||||
if (NLOHMANN_VIEW_LIKELY((n->flags & node_flags::moved) == 0))
|
||||
{
|
||||
return n + n->next;
|
||||
}
|
||||
const node* const h = edits->moved[n->off];
|
||||
return h + h->next;
|
||||
}
|
||||
|
||||
/// (editable documents) the value at an element position: entries of
|
||||
/// moved sequences are links. The link case is out of line, so that this
|
||||
/// compiles to a predicted branch rather than a select that delays the
|
||||
/// following loads.
|
||||
static NLOHMANN_VIEW_ALWAYS_INLINE const node* deref(const node* n) noexcept
|
||||
{
|
||||
return NLOHMANN_VIEW_LIKELY(n->kind != kind_link) ? n : follow_link(n);
|
||||
}
|
||||
|
||||
static NLOHMANN_VIEW_NOINLINE const node* follow_link(const node* n) noexcept
|
||||
{
|
||||
return link_target(*n);
|
||||
}
|
||||
};
|
||||
|
||||
/// How the index is walked: views of read-only documents follow the node
|
||||
/// array alone and compile without any of the edit handling; views of
|
||||
/// editable documents also follow moved element sequences and links.
|
||||
template<bool Editable>
|
||||
struct navigation
|
||||
{
|
||||
static NLOHMANN_VIEW_ALWAYS_INLINE const node* first(const document_data& /*d*/, const node* n) noexcept
|
||||
{
|
||||
return n + 1;
|
||||
}
|
||||
|
||||
static NLOHMANN_VIEW_ALWAYS_INLINE const node* end(const document_data& /*d*/, const node* n) noexcept
|
||||
{
|
||||
return n + n->next;
|
||||
}
|
||||
|
||||
static NLOHMANN_VIEW_ALWAYS_INLINE const node* value(const node* n) noexcept
|
||||
{
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct navigation<true>
|
||||
{
|
||||
static NLOHMANN_VIEW_ALWAYS_INLINE const node* first(const document_data& d, const node* n) noexcept
|
||||
{
|
||||
return d.first_child_edited(n);
|
||||
}
|
||||
|
||||
static NLOHMANN_VIEW_ALWAYS_INLINE const node* end(const document_data& d, const node* n) noexcept
|
||||
{
|
||||
return d.child_end_edited(n);
|
||||
}
|
||||
|
||||
static NLOHMANN_VIEW_ALWAYS_INLINE const node* value(const node* n) noexcept
|
||||
{
|
||||
return document_data::deref(n);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace view
|
||||
|
||||
@@ -1,767 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | 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 <array> // array
|
||||
#include <cmath> // isinf, isnan
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // int64_t, uint8_t, uint32_t, uint64_t
|
||||
#include <cstring> // memcmp, memmove
|
||||
#include <limits> // numeric_limits
|
||||
#include <string> // string, to_string
|
||||
#include <type_traits> // decay, enable_if, integral_constant, is_arithmetic, is_convertible, is_floating_point, is_same, is_signed
|
||||
#include <utility> // forward
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <nlohmann/detail/view/document_data.hpp>
|
||||
#include <nlohmann/detail/view/edit_storage.hpp>
|
||||
#include <nlohmann/detail/view/errors.hpp>
|
||||
#include <nlohmann/detail/view/lookup.hpp>
|
||||
#include <nlohmann/detail/view/macro_scope.hpp>
|
||||
#include <nlohmann/detail/view/node.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
template<typename BasicJsonType, bool Editable>
|
||||
class basic_json_view;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
namespace view
|
||||
{
|
||||
|
||||
/// the index of the first true condition (the number of conditions if none is)
|
||||
template<bool... Conditions>
|
||||
struct first_true : std::integral_constant<int, 0> {};
|
||||
|
||||
template<bool... Conditions>
|
||||
struct first_true<false, Conditions...> : std::integral_constant < int, 1 + first_true<Conditions...>::value > {};
|
||||
|
||||
/// Checks a string the way basic_json's serializer does when it writes it
|
||||
/// (type_error.316 with the same message), so that an editable document
|
||||
/// only holds valid UTF-8: the error is at the first byte that no
|
||||
/// well-formed sequence can continue with (Unicode, Table 3-7).
|
||||
inline void check_utf8(const char* s, std::size_t n)
|
||||
{
|
||||
const auto* const p = reinterpret_cast<const unsigned char*>(s); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
const auto hex = [](unsigned char c)
|
||||
{
|
||||
constexpr const char* digits = "0123456789ABCDEF";
|
||||
return std::string{digits[c >> 4u], digits[c & 0xFu]};
|
||||
};
|
||||
for (std::size_t i = 0; i < n;)
|
||||
{
|
||||
const unsigned char c = p[i];
|
||||
if (c < 0x80)
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
std::size_t len = 0;
|
||||
unsigned char lo = 0x80;
|
||||
unsigned char hi = 0xBF;
|
||||
if (c >= 0xC2 && c <= 0xDF)
|
||||
{
|
||||
len = 2;
|
||||
}
|
||||
else if (c >= 0xE0 && c <= 0xEF)
|
||||
{
|
||||
len = 3;
|
||||
lo = c == 0xE0 ? 0xA0 : 0x80;
|
||||
hi = c == 0xED ? 0x9F : 0xBF;
|
||||
}
|
||||
else if (c >= 0xF0 && c <= 0xF4)
|
||||
{
|
||||
len = 4;
|
||||
lo = c == 0xF0 ? 0x90 : 0x80;
|
||||
hi = c == 0xF4 ? 0x8F : 0xBF;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw_type_error(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex(c)));
|
||||
}
|
||||
for (std::size_t k = 1; k < len; ++k)
|
||||
{
|
||||
if (i + k == n)
|
||||
{
|
||||
throw_type_error(316, concat("incomplete UTF-8 string; last byte: 0x", hex(p[n - 1])));
|
||||
}
|
||||
const unsigned char b = p[i + k];
|
||||
if (b < (k == 1 ? lo : 0x80) || b > (k == 1 ? hi : 0xBF))
|
||||
{
|
||||
throw_type_error(316, concat("invalid UTF-8 byte at index ", std::to_string(i + k), ": 0x", hex(b)));
|
||||
}
|
||||
}
|
||||
i += len;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief the edits of an editable basic_json_document
|
||||
|
||||
Values are accepted as views (of any document), BasicJsonType values, and
|
||||
everything BasicJsonType can be constructed from. The source text is never
|
||||
written: new values go to storage owned by the document (see
|
||||
edit_storage.hpp).
|
||||
*/
|
||||
template<typename BasicJsonType, typename View>
|
||||
class editor
|
||||
{
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
using string_view_t = typename View::string_view_t;
|
||||
using nav = navigation<true>;
|
||||
|
||||
public:
|
||||
explicit editor(document_data& d) noexcept
|
||||
: m_doc(d)
|
||||
{}
|
||||
|
||||
/// replace a value; returns its view
|
||||
template<typename V>
|
||||
View set(const View& target, V&& value)
|
||||
{
|
||||
node* const slot = own(target);
|
||||
const encoded e = encode(std::forward<V>(value));
|
||||
assign(slot, e, nullptr, false);
|
||||
return View(&m_doc, slot);
|
||||
}
|
||||
|
||||
/// set a member (appended if missing; a null value becomes an object);
|
||||
/// returns a view of the member value
|
||||
template<typename V>
|
||||
View set(const View& object, string_view_t key, V&& value)
|
||||
{
|
||||
node* const o = own(object);
|
||||
if (o->kind != static_cast<std::uint8_t>(value_t::object) && o->kind != static_cast<std::uint8_t>(value_t::null))
|
||||
{
|
||||
throw_type_error(305, "cannot use operator[] with a string argument with ", object.type_name());
|
||||
}
|
||||
check_utf8(key.data(), key.size());
|
||||
const encoded e = encode(std::forward<V>(value));
|
||||
if (o->kind == static_cast<std::uint8_t>(value_t::null))
|
||||
{
|
||||
become_empty(o, value_t::object);
|
||||
}
|
||||
// an existing member: assign it (and drop later duplicates, so that
|
||||
// lookups, iteration, and materialize() agree)
|
||||
node* slot = nullptr;
|
||||
bool duplicates = false;
|
||||
for (const node* k = nav::first(m_doc, o), *end = nav::end(m_doc, o); k != end; k = document_data::after(k + 1))
|
||||
{
|
||||
if (key_equals(*k, key))
|
||||
{
|
||||
if (slot != nullptr)
|
||||
{
|
||||
duplicates = true;
|
||||
break;
|
||||
}
|
||||
slot = const_cast<node*>(nav::value(k + 1)); // NOLINT(cppcoreguidelines-pro-type-const-cast): the nodes belong to this document
|
||||
}
|
||||
}
|
||||
if (slot != nullptr)
|
||||
{
|
||||
if (duplicates)
|
||||
{
|
||||
erase_members(o, key, true);
|
||||
}
|
||||
assign(slot, e, o, true);
|
||||
return View(&m_doc, slot);
|
||||
}
|
||||
const node k = string_node(key.data(), key.size());
|
||||
slot = new_slot(e);
|
||||
node* const h = block_of(m_doc, o, 2);
|
||||
h[h->next] = k;
|
||||
make_link(h[h->next + 1], slot);
|
||||
h->next += 2;
|
||||
++h->len;
|
||||
++o->len;
|
||||
return View(&m_doc, slot);
|
||||
}
|
||||
|
||||
/// assign an existing array element; returns a view of it
|
||||
template<typename V>
|
||||
View set(const View& array, std::size_t idx, V&& value)
|
||||
{
|
||||
node* const a = own(array);
|
||||
if (a->kind != static_cast<std::uint8_t>(value_t::array))
|
||||
{
|
||||
throw_type_error(305, "cannot use operator[] with a numeric argument with ", array.type_name());
|
||||
}
|
||||
check_index(idx, a->len);
|
||||
const encoded e = encode(std::forward<V>(value));
|
||||
node* const slot = const_cast<node*>(nav::value(element_at<true>(m_doc, a, idx))); // NOLINT(cppcoreguidelines-pro-type-const-cast)
|
||||
assign(slot, e, a, true);
|
||||
return View(&m_doc, slot);
|
||||
}
|
||||
|
||||
/// append to an array (a null value becomes an array); returns a view of
|
||||
/// the new element
|
||||
template<typename V>
|
||||
View push_back(const View& array, V&& value)
|
||||
{
|
||||
node* const a = own(array);
|
||||
if (a->kind != static_cast<std::uint8_t>(value_t::array) && a->kind != static_cast<std::uint8_t>(value_t::null))
|
||||
{
|
||||
throw_type_error(308, "cannot use push_back() with ", array.type_name());
|
||||
}
|
||||
const encoded e = encode(std::forward<V>(value));
|
||||
if (a->kind == static_cast<std::uint8_t>(value_t::null))
|
||||
{
|
||||
become_empty(a, value_t::array);
|
||||
}
|
||||
node* const slot = new_slot(e);
|
||||
node* const h = block_of(m_doc, a, 1);
|
||||
make_link(h[h->next], slot);
|
||||
++h->next;
|
||||
++h->len;
|
||||
++a->len;
|
||||
return View(&m_doc, slot);
|
||||
}
|
||||
|
||||
/// insert into an array before position idx (idx <= size()); returns a
|
||||
/// view of the new element
|
||||
template<typename V>
|
||||
View insert(const View& array, std::size_t idx, V&& value)
|
||||
{
|
||||
node* const a = own(array);
|
||||
if (a->kind != static_cast<std::uint8_t>(value_t::array))
|
||||
{
|
||||
throw_type_error(309, "cannot use insert() with ", array.type_name());
|
||||
}
|
||||
check_index(idx, a->len + 1);
|
||||
const encoded e = encode(std::forward<V>(value));
|
||||
node* const slot = new_slot(e);
|
||||
node* const h = block_of(m_doc, a, 1);
|
||||
std::memmove(h + 2 + idx, h + 1 + idx, (h->next - 1 - idx) * sizeof(node));
|
||||
make_link(h[1 + idx], slot);
|
||||
++h->next;
|
||||
++h->len;
|
||||
++a->len;
|
||||
return View(&m_doc, slot);
|
||||
}
|
||||
|
||||
/// remove all members with this key; returns their number
|
||||
std::size_t erase(const View& object, string_view_t key)
|
||||
{
|
||||
node* const o = own(object);
|
||||
if (o->kind != static_cast<std::uint8_t>(value_t::object))
|
||||
{
|
||||
throw_type_error(307, "cannot use erase() with ", object.type_name());
|
||||
}
|
||||
for (const node* k = nav::first(m_doc, o), *end = nav::end(m_doc, o); k != end; k = document_data::after(k + 1))
|
||||
{
|
||||
if (key_equals(*k, key))
|
||||
{
|
||||
return erase_members(o, key, false);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// remove an array element
|
||||
void erase(const View& array, std::size_t idx)
|
||||
{
|
||||
node* const a = own(array);
|
||||
if (a->kind != static_cast<std::uint8_t>(value_t::array))
|
||||
{
|
||||
throw_type_error(307, "cannot use erase() with ", array.type_name());
|
||||
}
|
||||
check_index(idx, a->len);
|
||||
node* const h = block_of(m_doc, a, 0);
|
||||
std::memmove(h + 1 + idx, h + 2 + idx, (h->next - 2 - idx) * sizeof(node));
|
||||
--h->next;
|
||||
--h->len;
|
||||
--a->len;
|
||||
}
|
||||
|
||||
private:
|
||||
/// an encoded value: a scalar node, or the root of a new array/object
|
||||
struct encoded
|
||||
{
|
||||
node scalar{};
|
||||
node* region = nullptr;
|
||||
};
|
||||
|
||||
/// the node of a view of this document
|
||||
node* own(const View& v)
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(v.m_doc != &m_doc || v.m_node == nullptr))
|
||||
{
|
||||
throw_invalid_iterator(202, "view does not belong to this document");
|
||||
}
|
||||
edit_state_of(m_doc);
|
||||
return const_cast<node*>(v.m_node); // NOLINT(cppcoreguidelines-pro-type-const-cast): the nodes belong to this document
|
||||
}
|
||||
|
||||
static void check_index(std::size_t idx, std::size_t limit)
|
||||
{
|
||||
if (idx >= limit)
|
||||
{
|
||||
throw_out_of_range(401, concat("array index ", std::to_string(idx), " is out of range"));
|
||||
}
|
||||
}
|
||||
|
||||
bool key_equals(const node& k, string_view_t key) const noexcept
|
||||
{
|
||||
return k.len == key.size() && (key.size() == 0 || std::memcmp(m_doc.str(k), key.data(), key.size()) == 0);
|
||||
}
|
||||
|
||||
/// remove the members with this key (all, or all but the first) from an object
|
||||
std::size_t erase_members(node* o, string_view_t key, bool keep_first)
|
||||
{
|
||||
node* const h = block_of(m_doc, o, 0);
|
||||
node* w = h + 1;
|
||||
std::size_t erased = 0;
|
||||
bool kept = false;
|
||||
for (node* r = h + 1, *end = h + h->next; r != end; r += 2)
|
||||
{
|
||||
const bool match = key_equals(*r, key);
|
||||
if (match && (kept || !keep_first))
|
||||
{
|
||||
++erased;
|
||||
continue;
|
||||
}
|
||||
kept = kept || match;
|
||||
if (w != r)
|
||||
{
|
||||
w[0] = r[0];
|
||||
w[1] = r[1];
|
||||
}
|
||||
w += 2;
|
||||
}
|
||||
h->next = static_cast<std::uint32_t>(w - h);
|
||||
h->len -= static_cast<std::uint32_t>(erased);
|
||||
o->len -= static_cast<std::uint32_t>(erased);
|
||||
return erased;
|
||||
}
|
||||
|
||||
/// turn a null into an empty array/object in place
|
||||
static void become_empty(node* n, value_t k) noexcept
|
||||
{
|
||||
*n = node{};
|
||||
n->kind = static_cast<std::uint8_t>(k);
|
||||
n->flags = node_flags::is_new;
|
||||
n->next = 1;
|
||||
}
|
||||
|
||||
/// Replace the value at slot; `parent` is the container whose elements
|
||||
/// include slot (if known).
|
||||
void assign(node* slot, const encoded& e, node* parent, bool parent_known)
|
||||
{
|
||||
if (e.region == nullptr)
|
||||
{
|
||||
if (is_container(*slot) && slot->next > 1 && slot != m_doc.tape)
|
||||
{
|
||||
// The slot spans its old elements in the enclosing sequence, but
|
||||
// a scalar is one node: the enclosing container first switches to
|
||||
// links (then the extent of the slot no longer matters).
|
||||
node* const p = parent_known ? parent : find_parent(m_doc, slot);
|
||||
if (p != nullptr && ((p->flags & node_flags::moved) == 0 || moved_capacity(m_doc, p) == 0))
|
||||
{
|
||||
block_of(m_doc, p, 0);
|
||||
}
|
||||
}
|
||||
*slot = e.scalar;
|
||||
return;
|
||||
}
|
||||
// an array/object: the slot keeps its extent (so that the enclosing
|
||||
// sequence still steps over it), and the elements come from the new
|
||||
// sequence
|
||||
const node* const r = e.region;
|
||||
const std::uint32_t extent = is_container(*slot) ? slot->next : 1;
|
||||
const bool was_moved = (slot->flags & node_flags::moved) != 0;
|
||||
slot->kind = r->kind;
|
||||
slot->extra = 0;
|
||||
slot->len = r->len;
|
||||
slot->next = extent;
|
||||
slot->flags = was_moved ? static_cast<std::uint8_t>(node_flags::moved | node_flags::is_new) : std::uint8_t{0};
|
||||
set_moved(m_doc, slot, e.region, 0);
|
||||
edit_state_of(m_doc).regions[e.region] = slot;
|
||||
}
|
||||
|
||||
/// a node for a new element (links point to it; it never moves)
|
||||
node* new_slot(const encoded& e)
|
||||
{
|
||||
if (e.region != nullptr)
|
||||
{
|
||||
return e.region;
|
||||
}
|
||||
node* const s = alloc_nodes(m_doc, 1);
|
||||
*s = e.scalar;
|
||||
return s;
|
||||
}
|
||||
|
||||
//////////////
|
||||
// encoding //
|
||||
//////////////
|
||||
|
||||
template<int N>
|
||||
using encode_tag = std::integral_constant<int, N>;
|
||||
|
||||
template<typename T>
|
||||
struct is_view : std::false_type {};
|
||||
|
||||
template<typename J, bool E>
|
||||
struct is_view<basic_json_view<J, E>> : std::true_type {};
|
||||
|
||||
template<typename V>
|
||||
encoded encode(V&& v)
|
||||
{
|
||||
using D = typename std::decay<V>::type;
|
||||
return encode_impl(std::forward<V>(v), encode_tag<first_true<is_view<D>::value,
|
||||
std::is_same<D, BasicJsonType>::value,
|
||||
std::is_same<D, std::nullptr_t>::value,
|
||||
std::is_same<D, bool>::value,
|
||||
std::is_arithmetic<D>::value,
|
||||
std::is_convertible<const D&, string_view_t>::value>::value> {});
|
||||
}
|
||||
|
||||
/// a view of any document (copied; nothing is shared with it)
|
||||
template<typename J, bool E>
|
||||
encoded encode_impl(const basic_json_view<J, E>& v, encode_tag<0> /*view*/)
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(v.m_node == nullptr))
|
||||
{
|
||||
throw_type_error(302, "type must be a value, but is ", "discarded");
|
||||
}
|
||||
encoded r;
|
||||
if (!is_container(*v.m_node))
|
||||
{
|
||||
r.scalar = copy_scalar(*v.m_doc, *v.m_node);
|
||||
return r;
|
||||
}
|
||||
r.region = alloc_nodes(m_doc, count_nodes<E>(*v.m_doc, v.m_node));
|
||||
fill_nodes<E>(*v.m_doc, v.m_node, r.region);
|
||||
edit_state_of(m_doc).regions.emplace(r.region, nullptr);
|
||||
return r;
|
||||
}
|
||||
|
||||
encoded encode_impl(const BasicJsonType& j, encode_tag<1> /*json*/)
|
||||
{
|
||||
encoded r;
|
||||
if (!j.is_structured())
|
||||
{
|
||||
r.scalar = json_scalar(j);
|
||||
return r;
|
||||
}
|
||||
r.region = alloc_nodes(m_doc, count_nodes(j));
|
||||
fill_nodes(j, r.region);
|
||||
edit_state_of(m_doc).regions.emplace(r.region, nullptr);
|
||||
return r;
|
||||
}
|
||||
|
||||
encoded encode_impl(std::nullptr_t /*unused*/, encode_tag<2> /*null*/)
|
||||
{
|
||||
encoded r;
|
||||
r.scalar = plain_node(value_t::null);
|
||||
return r;
|
||||
}
|
||||
|
||||
encoded encode_impl(bool b, encode_tag<3> /*boolean*/)
|
||||
{
|
||||
encoded r;
|
||||
r.scalar = plain_node(value_t::boolean);
|
||||
r.scalar.flags = static_cast<std::uint8_t>(r.scalar.flags | (b ? node_flags::is_true : 0));
|
||||
return r;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
encoded encode_impl(T x, encode_tag<4> /*number*/)
|
||||
{
|
||||
encoded r;
|
||||
r.scalar = number_node(x, std::integral_constant<int, first_true<std::is_floating_point<T>::value, std::is_signed<T>::value>::value> {});
|
||||
return r;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
encoded encode_impl(const T& s, encode_tag<5> /*string*/)
|
||||
{
|
||||
const string_view_t sv(s);
|
||||
check_utf8(sv.data(), sv.size());
|
||||
encoded r;
|
||||
r.scalar = string_node(sv.data(), sv.size());
|
||||
return r;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
encoded encode_impl(T&& x, encode_tag<6> /*other*/)
|
||||
{
|
||||
return encode_impl(BasicJsonType(std::forward<T>(x)), encode_tag<1> {});
|
||||
}
|
||||
|
||||
static node plain_node(value_t k) noexcept
|
||||
{
|
||||
node n{};
|
||||
n.kind = static_cast<std::uint8_t>(k);
|
||||
n.flags = node_flags::is_new;
|
||||
return n;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
node number_node(T x, std::integral_constant<int, 0> /*floating-point*/)
|
||||
{
|
||||
return float_node(static_cast<number_float_t>(x));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
node number_node(T x, std::integral_constant<int, 1> /*signed*/)
|
||||
{
|
||||
return integer_node(static_cast<std::uint64_t>(static_cast<std::int64_t>(x)), value_t::number_integer);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
node number_node(T x, std::integral_constant<int, 2> /*unsigned*/)
|
||||
{
|
||||
return integer_node(static_cast<std::uint64_t>(x), value_t::number_unsigned);
|
||||
}
|
||||
|
||||
/// an integer with its canonical token in the edit arena
|
||||
node integer_node(std::uint64_t bits, value_t k)
|
||||
{
|
||||
const bool negative = k == value_t::number_integer && static_cast<std::int64_t>(bits) < 0;
|
||||
std::uint64_t magnitude = negative ? 0 - bits : bits;
|
||||
std::array<char, 24> buf{};
|
||||
char* p = buf.data() + buf.size();
|
||||
do
|
||||
{
|
||||
*--p = static_cast<char>('0' + (magnitude % 10));
|
||||
magnitude /= 10;
|
||||
}
|
||||
while (magnitude != 0);
|
||||
if (negative)
|
||||
{
|
||||
*--p = '-';
|
||||
}
|
||||
const auto len = static_cast<std::size_t>(buf.data() + buf.size() - p);
|
||||
node n = plain_node(k);
|
||||
n.flags = static_cast<std::uint8_t>(n.flags | node_flags::edited);
|
||||
n.off = append_text(m_doc, p, len);
|
||||
// number_length() adds one for the sign of number_integer nodes
|
||||
n.extra = static_cast<std::uint16_t>(k == value_t::number_integer ? len - 1 : len);
|
||||
set_integer_bits(n, bits);
|
||||
return n;
|
||||
}
|
||||
|
||||
/// a float with its shortest round-trip token (as basic_json::dump()
|
||||
/// writes it), or nan, inf, -inf, in the edit arena
|
||||
node float_node(number_float_t x)
|
||||
{
|
||||
string_t text;
|
||||
if (std::isnan(x))
|
||||
{
|
||||
text = "nan";
|
||||
}
|
||||
else if (std::isinf(x))
|
||||
{
|
||||
text = x > 0 ? "inf" : "-inf";
|
||||
}
|
||||
else
|
||||
{
|
||||
text = BasicJsonType(x).dump();
|
||||
}
|
||||
node n = plain_node(value_t::number_float);
|
||||
n.flags = static_cast<std::uint8_t>(n.flags | node_flags::edited);
|
||||
n.extra = 0xFFFFu; // (the digit layout is not recorded)
|
||||
n.off = append_text(m_doc, text.data(), text.size());
|
||||
n.len = static_cast<std::uint32_t>(text.size());
|
||||
return n;
|
||||
}
|
||||
|
||||
/// a string (or key) in the edit arena
|
||||
node string_node(const char* s, std::size_t len)
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(len >= 0xFFFFFFFFu))
|
||||
{
|
||||
throw_out_of_range(416, "strings of 4 GiB or more are not supported by json_document"); // LCOV_EXCL_LINE
|
||||
}
|
||||
node n = plain_node(value_t::string);
|
||||
n.flags = static_cast<std::uint8_t>(n.flags | node_flags::edited);
|
||||
n.off = append_text(m_doc, s, len);
|
||||
n.len = static_cast<std::uint32_t>(len);
|
||||
return n;
|
||||
}
|
||||
|
||||
/// a scalar of a view (of any document) as a node of this document
|
||||
node copy_scalar(const document_data& from, const node& n)
|
||||
{
|
||||
if (&from == &m_doc)
|
||||
{
|
||||
return n; // the same storage
|
||||
}
|
||||
switch (static_cast<value_t>(n.kind))
|
||||
{
|
||||
case value_t::string:
|
||||
return string_node(from.str(n), n.len);
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
return integer_node(integer_bits(n), static_cast<value_t>(n.kind));
|
||||
case value_t::number_float:
|
||||
{
|
||||
node r = plain_node(value_t::number_float);
|
||||
r.flags = static_cast<std::uint8_t>(r.flags | node_flags::edited);
|
||||
r.off = append_text(m_doc, from.str(n), n.len);
|
||||
r.len = n.len;
|
||||
r.extra = n.extra;
|
||||
return r;
|
||||
}
|
||||
case value_t::boolean:
|
||||
{
|
||||
node r = plain_node(value_t::boolean);
|
||||
r.flags = static_cast<std::uint8_t>(r.flags | (n.flags & node_flags::is_true));
|
||||
return r;
|
||||
}
|
||||
case value_t::null:
|
||||
case value_t::object:
|
||||
case value_t::array:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
return plain_node(value_t::null);
|
||||
}
|
||||
}
|
||||
|
||||
node json_scalar(const BasicJsonType& j)
|
||||
{
|
||||
switch (j.type())
|
||||
{
|
||||
case value_t::null:
|
||||
return plain_node(value_t::null);
|
||||
case value_t::boolean:
|
||||
{
|
||||
node r = plain_node(value_t::boolean);
|
||||
r.flags = static_cast<std::uint8_t>(r.flags | (j.template get<bool>() ? node_flags::is_true : 0));
|
||||
return r;
|
||||
}
|
||||
case value_t::number_integer:
|
||||
return integer_node(static_cast<std::uint64_t>(static_cast<std::int64_t>(j.template get<number_integer_t>())), value_t::number_integer);
|
||||
case value_t::number_unsigned:
|
||||
return integer_node(static_cast<std::uint64_t>(j.template get<number_unsigned_t>()), value_t::number_unsigned);
|
||||
case value_t::number_float:
|
||||
return float_node(j.template get<number_float_t>());
|
||||
case value_t::string:
|
||||
{
|
||||
const auto& s = j.template get_ref<const string_t&>();
|
||||
check_utf8(s.data(), s.size());
|
||||
return string_node(s.data(), s.size());
|
||||
}
|
||||
case value_t::binary:
|
||||
throw_type_error(319, "cannot store a binary value in a json_document", "");
|
||||
case value_t::discarded:
|
||||
case value_t::object:
|
||||
case value_t::array:
|
||||
default:
|
||||
throw_type_error(302, "type must be a value, but is ", "discarded");
|
||||
}
|
||||
}
|
||||
|
||||
/// number of nodes of a subtree (containers, keys, scalars)
|
||||
template<bool E>
|
||||
static std::size_t count_nodes(const document_data& d, const node* n)
|
||||
{
|
||||
if (!is_container(*n))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
const bool object = n->kind == static_cast<std::uint8_t>(value_t::object);
|
||||
std::size_t r = 1;
|
||||
for (const node* c = navigation<E>::first(d, n), *end = navigation<E>::end(d, n); c != end;)
|
||||
{
|
||||
const node* const v = object ? c + 1 : c;
|
||||
r += (object ? 1 : 0) + count_nodes<E>(d, navigation<E>::value(v));
|
||||
c = document_data::after(v);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/// copy a subtree (of any document) as a contiguous sequence; returns its end
|
||||
template<bool E>
|
||||
node* fill_nodes(const document_data& d, const node* n, node* out)
|
||||
{
|
||||
if (!is_container(*n))
|
||||
{
|
||||
*out = copy_scalar(d, *n);
|
||||
return out + 1;
|
||||
}
|
||||
node* const self = out++;
|
||||
*self = plain_node(static_cast<value_t>(n->kind));
|
||||
self->len = n->len;
|
||||
const bool object = n->kind == static_cast<std::uint8_t>(value_t::object);
|
||||
for (const node* c = navigation<E>::first(d, n), *end = navigation<E>::end(d, n); c != end;)
|
||||
{
|
||||
if (object)
|
||||
{
|
||||
*out++ = copy_scalar(d, *c);
|
||||
++c;
|
||||
}
|
||||
out = fill_nodes<E>(d, navigation<E>::value(c), out);
|
||||
c = document_data::after(c);
|
||||
}
|
||||
self->next = static_cast<std::uint32_t>(out - self);
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::size_t count_nodes(const BasicJsonType& j)
|
||||
{
|
||||
std::size_t r = 1;
|
||||
if (j.is_object())
|
||||
{
|
||||
for (const auto& member : j.items())
|
||||
{
|
||||
r += 1 + count_nodes(member.value());
|
||||
}
|
||||
}
|
||||
else if (j.is_array())
|
||||
{
|
||||
for (const auto& e : j)
|
||||
{
|
||||
r += count_nodes(e);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
node* fill_nodes(const BasicJsonType& j, node* out)
|
||||
{
|
||||
if (!j.is_structured())
|
||||
{
|
||||
*out = json_scalar(j);
|
||||
return out + 1;
|
||||
}
|
||||
node* const self = out++;
|
||||
*self = plain_node(j.type());
|
||||
self->len = static_cast<std::uint32_t>(j.size());
|
||||
if (j.is_object())
|
||||
{
|
||||
for (const auto& member : j.items())
|
||||
{
|
||||
check_utf8(member.key().data(), member.key().size());
|
||||
*out++ = string_node(member.key().data(), member.key().size());
|
||||
out = fill_nodes(member.value(), out);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& e : j)
|
||||
{
|
||||
out = fill_nodes(e, out);
|
||||
}
|
||||
}
|
||||
self->next = static_cast<std::uint32_t>(out - self);
|
||||
return out;
|
||||
}
|
||||
|
||||
document_data& m_doc;
|
||||
};
|
||||
|
||||
} // namespace view
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,242 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | 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 <algorithm> // max, min
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // uint8_t, uint32_t
|
||||
#include <cstring> // memcpy
|
||||
#include <functional> // less
|
||||
#include <memory> // unique_ptr
|
||||
#include <utility> // move
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <nlohmann/detail/view/document_data.hpp>
|
||||
#include <nlohmann/detail/view/errors.hpp>
|
||||
#include <nlohmann/detail/view/macro_scope.hpp>
|
||||
#include <nlohmann/detail/view/node.hpp>
|
||||
|
||||
// The storage of edits. Edits never move or resize the parsed index: every
|
||||
// value keeps its node, so views stay valid. New values and element sequences
|
||||
// live in chunks that never move; strings and number tokens written by edits
|
||||
// live in the edit arena. An array/object whose elements change gets
|
||||
// node_flags::moved: its elements then live in a separate sequence (a header
|
||||
// node, then the entries), whose entries link to the values (kind_link).
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
namespace view
|
||||
{
|
||||
|
||||
inline document_data::edit_state& edit_state_of(document_data& d)
|
||||
{
|
||||
if (!d.edits)
|
||||
{
|
||||
d.edits.reset(new document_data::edit_state()); // NOLINT(cppcoreguidelines-owning-memory): owned by the unique_ptr
|
||||
}
|
||||
return *d.edits;
|
||||
}
|
||||
|
||||
/// k consecutive nodes that never move (new values and blocks)
|
||||
inline node* alloc_nodes(document_data& d, std::size_t k)
|
||||
{
|
||||
document_data::edit_state& e = edit_state_of(d);
|
||||
if (NLOHMANN_VIEW_UNLIKELY(static_cast<std::size_t>(e.chunk_end - e.chunk_cur) < k))
|
||||
{
|
||||
const std::size_t count = (std::max)(k, e.chunk_next);
|
||||
std::unique_ptr<node[]> fresh(new node[count]()); // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
e.chunks.push_back(std::move(fresh));
|
||||
e.chunk_cur = e.chunks.back().get();
|
||||
e.chunk_end = e.chunk_cur + count;
|
||||
e.chunk_next = (std::min)(e.chunk_next * 2, std::size_t{65536});
|
||||
e.bytes += count * sizeof(node);
|
||||
}
|
||||
node* const r = e.chunk_cur;
|
||||
e.chunk_cur += k;
|
||||
return r;
|
||||
}
|
||||
|
||||
/// copy n bytes into the edit arena and return their offset; a new buffer
|
||||
/// leaves the old one alive, so that string views into it remain valid
|
||||
inline std::uint32_t append_text(document_data& d, const char* s, std::size_t n)
|
||||
{
|
||||
document_data::edit_state& e = edit_state_of(d);
|
||||
if (NLOHMANN_VIEW_UNLIKELY(e.text_cap - e.text_used < n))
|
||||
{
|
||||
const std::size_t cap = (std::max)(e.text_cap * 2, e.text_used + n + 256);
|
||||
if (cap > 0xFFFFFFFFu)
|
||||
{
|
||||
throw_out_of_range(416, "edits of 4 GiB or more are not supported by json_document"); // LCOV_EXCL_LINE (4 GiB)
|
||||
}
|
||||
std::unique_ptr<char[]> fresh(new char[cap]); // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
if (e.text_used != 0)
|
||||
{
|
||||
std::memcpy(fresh.get(), e.texts.back().get(), e.text_used);
|
||||
}
|
||||
e.texts.push_back(std::move(fresh));
|
||||
e.text_cap = cap;
|
||||
e.bytes += cap;
|
||||
d.base[2] = e.texts.back().get();
|
||||
}
|
||||
const auto off = static_cast<std::uint32_t>(e.text_used);
|
||||
if (n != 0)
|
||||
{
|
||||
std::memcpy(e.texts.back().get() + e.text_used, s, n);
|
||||
}
|
||||
e.text_used += n;
|
||||
return off;
|
||||
}
|
||||
|
||||
/// the capacity in nodes of the block of a moved container (0: a fixed
|
||||
/// sequence, the elements of a new value)
|
||||
inline std::size_t moved_capacity(const document_data& d, const node* n) noexcept
|
||||
{
|
||||
return d.edits->moved_cap[n->off];
|
||||
}
|
||||
|
||||
/// let container n take its elements from `seq` (header node first)
|
||||
inline void set_moved(document_data& d, node* n, node* seq, std::size_t cap)
|
||||
{
|
||||
document_data::edit_state& e = edit_state_of(d);
|
||||
if ((n->flags & node_flags::moved) != 0)
|
||||
{
|
||||
e.moved[n->off] = seq;
|
||||
e.moved_cap[n->off] = cap;
|
||||
return;
|
||||
}
|
||||
if (e.moved.size() >= 0xFFFFFFFFu)
|
||||
{
|
||||
throw_out_of_range(416, "more than 4294967295 edited arrays and objects are not supported by json_document"); // LCOV_EXCL_LINE
|
||||
}
|
||||
if (e.moved.size() == e.moved.capacity() || e.moved_cap.size() == e.moved_cap.capacity())
|
||||
{
|
||||
// both grow before either changes, so that the push_backs cannot throw
|
||||
e.moved.reserve((2 * e.moved.size()) + 16);
|
||||
e.moved_cap.reserve((2 * e.moved.size()) + 16);
|
||||
}
|
||||
e.moved.push_back(seq);
|
||||
e.moved_cap.push_back(cap);
|
||||
n->off = static_cast<std::uint32_t>(e.moved.size() - 1);
|
||||
n->flags = static_cast<std::uint8_t>(n->flags | node_flags::moved | node_flags::is_new);
|
||||
}
|
||||
|
||||
/// Make the elements of container n a growable block with room for `extra`
|
||||
/// more nodes, and return its header. The entries link to the existing
|
||||
/// values, which stay where they are. A block that grows is copied (its old
|
||||
/// space is not reused).
|
||||
inline node* block_of(document_data& d, node* n, std::size_t extra)
|
||||
{
|
||||
if ((n->flags & node_flags::moved) != 0 && moved_capacity(d, n) != 0)
|
||||
{
|
||||
node* const h = d.edits->moved[n->off];
|
||||
if (h->next + extra <= moved_capacity(d, n))
|
||||
{
|
||||
return h;
|
||||
}
|
||||
const std::size_t cap = (std::max)(2 * moved_capacity(d, n), h->next + extra);
|
||||
node* const nh = alloc_nodes(d, cap);
|
||||
std::memcpy(nh, h, h->next * sizeof(node));
|
||||
set_moved(d, n, nh, cap);
|
||||
return nh;
|
||||
}
|
||||
const bool object = n->kind == static_cast<std::uint8_t>(value_t::object);
|
||||
const std::size_t used = 1 + (static_cast<std::size_t>(n->len) * (object ? 2 : 1));
|
||||
const std::size_t cap = used + extra;
|
||||
node* const h = alloc_nodes(d, cap);
|
||||
*h = node{};
|
||||
h->kind = n->kind;
|
||||
h->len = n->len;
|
||||
h->next = static_cast<std::uint32_t>(used);
|
||||
node* o = h + 1;
|
||||
for (const node* c = d.first_child_edited(n), *e = d.child_end_edited(n); c != e;)
|
||||
{
|
||||
if (object)
|
||||
{
|
||||
*o++ = *c++; // the key
|
||||
}
|
||||
make_link(*o, document_data::deref(c));
|
||||
++o;
|
||||
c = document_data::after(c);
|
||||
}
|
||||
set_moved(d, n, h, cap);
|
||||
return h;
|
||||
}
|
||||
|
||||
/// The container whose elements include `target`; nullptr for the root, for
|
||||
/// a value that is no longer part of the document, and for a value that is
|
||||
/// only reached through a link. Values never move between allocations, so
|
||||
/// the path to `target` stays inside the allocation that holds it (the parsed
|
||||
/// index, or one new value), where the extent of each container (`next`)
|
||||
/// still covers its original subtree.
|
||||
inline node* find_parent(const document_data& d, const node* target)
|
||||
{
|
||||
const std::less<const node*> lt;
|
||||
const node* lo = d.tape;
|
||||
const node* hi = d.tape + d.tape_size;
|
||||
const node* c = d.tape;
|
||||
if (lt(target, lo) || !lt(target, hi))
|
||||
{
|
||||
if (!d.edits)
|
||||
{
|
||||
return nullptr; // LCOV_EXCL_LINE (nodes outside the index exist only after edits)
|
||||
}
|
||||
auto it = d.edits->regions.upper_bound(target);
|
||||
if (it == d.edits->regions.begin())
|
||||
{
|
||||
return nullptr; // LCOV_EXCL_LINE (an array/object with elements is in the index or a new value)
|
||||
}
|
||||
--it;
|
||||
lo = it->first;
|
||||
hi = lo + lo->next;
|
||||
if (!lt(target, hi))
|
||||
{
|
||||
return nullptr; // LCOV_EXCL_LINE (a single-node value, reached through a link)
|
||||
}
|
||||
// the root of a new value is the element sequence of its owner, or a linked value
|
||||
c = it->second != nullptr ? it->second : lo;
|
||||
}
|
||||
if (target == lo)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
if (!is_container(*c))
|
||||
{
|
||||
return nullptr; // LCOV_EXCL_LINE (the value is inside c)
|
||||
}
|
||||
const bool object = c->kind == static_cast<std::uint8_t>(value_t::object);
|
||||
const node* down = nullptr;
|
||||
for (const node* p = d.first_child_edited(c), *e = d.child_end_edited(c); p != e;)
|
||||
{
|
||||
const node* const at = object ? p + 1 : p;
|
||||
const node* const v = document_data::deref(at);
|
||||
if (v == target)
|
||||
{
|
||||
return const_cast<node*>(c); // NOLINT(cppcoreguidelines-pro-type-const-cast): the nodes belong to the document
|
||||
}
|
||||
if (is_container(*v) && !lt(v, lo) && lt(v, target) && lt(target, v + v->next))
|
||||
{
|
||||
down = v;
|
||||
break;
|
||||
}
|
||||
p = document_data::after(at);
|
||||
}
|
||||
if (down == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
c = down;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace view
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -30,11 +30,6 @@ namespace view
|
||||
NLOHMANN_VIEW_THROW(type_error::create(id, concat(prefix, type), nullptr));
|
||||
}
|
||||
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_type_error(int id, const std::string& msg)
|
||||
{
|
||||
NLOHMANN_VIEW_THROW(type_error::create(id, msg, nullptr));
|
||||
}
|
||||
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_out_of_range(int id, const std::string& msg)
|
||||
{
|
||||
NLOHMANN_VIEW_THROW(out_of_range::create(id, msg, nullptr));
|
||||
|
||||
@@ -1,602 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | 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 <array> // array
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // int64_t, uint8_t, uint16_t, uint32_t, uint64_t
|
||||
#include <cstring> // memcmp, memcpy
|
||||
#include <limits> // numeric_limits
|
||||
#include <string> // string
|
||||
#include <vector> // vector
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <nlohmann/detail/view/document_data.hpp>
|
||||
#include <nlohmann/detail/view/errors.hpp>
|
||||
#include <nlohmann/detail/view/macro_scope.hpp>
|
||||
#include <nlohmann/detail/view/node.hpp>
|
||||
#include <nlohmann/detail/view/number.hpp>
|
||||
#include <nlohmann/detail/view/object_index.hpp>
|
||||
#include <nlohmann/detail/view/scan.hpp>
|
||||
|
||||
// Images: a document stored so that loading it needs no parsing.
|
||||
//
|
||||
// Layout (little-endian): a 64-byte header, the nodes, the text (the source,
|
||||
// followed by the number tokens written by edits), a NUL, the decoded strings
|
||||
// (followed by the strings written by edits), a NUL. The idea is that of
|
||||
// zero-copy formats such as FlatBuffers (https://github.com/google/flatbuffers)
|
||||
// and YaFF (https://github.com/yandex/yaff); no code is taken from them.
|
||||
// check_image follows the idea of FlatBuffers' Verifier (bounds and
|
||||
// structure) and also checks what the parser guarantees about strings and
|
||||
// numbers, so that reading and serializing a checked image is safe and yields
|
||||
// valid JSON.
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
namespace view
|
||||
{
|
||||
|
||||
/// how load() checks an image
|
||||
enum class image_check
|
||||
{
|
||||
/// everything the parser guarantees: structure and bounds, strings (valid
|
||||
/// UTF-8; source strings without quotes, backslashes, and control
|
||||
/// characters), and numbers (well-formed, matching the stored values)
|
||||
full,
|
||||
/// structure and bounds only: reading and serializing are safe, but a
|
||||
/// crafted image can yield invalid UTF-8, strings that serialize to
|
||||
/// invalid JSON, or numbers that differ from their text
|
||||
bounds,
|
||||
/// none: for images from a trusted source only (a damaged image is
|
||||
/// undefined behavior)
|
||||
none,
|
||||
};
|
||||
|
||||
struct image_header
|
||||
{
|
||||
std::array<char, 4> magic; ///< "NJVI"
|
||||
std::uint32_t version; ///< 1
|
||||
std::uint64_t node_count;
|
||||
std::uint64_t text_size;
|
||||
std::uint64_t arena_size;
|
||||
std::array<std::uint64_t, 4> reserved; ///< zero (for later versions)
|
||||
};
|
||||
static_assert(sizeof(image_header) == 64, "the image header must be 64 bytes");
|
||||
|
||||
constexpr std::uint32_t image_version = 1;
|
||||
|
||||
/// the largest node count and text or string size of an image (as for parsed
|
||||
/// documents, offsets and counts must fit 32 bits)
|
||||
constexpr std::uint64_t image_limit = 0xFFFFFFF0u;
|
||||
|
||||
/// Copy the current structure of an edited document into nodes in document
|
||||
/// order, as the parser would have written them. Text written by edits is
|
||||
/// appended to text_tail (number tokens) and arena_tail (strings); floats that
|
||||
/// are not finite become null, as dump() writes them.
|
||||
inline void compact_nodes(const document_data& d, std::size_t arena_size, std::vector<node>& out, std::string& text_tail, std::string& arena_tail)
|
||||
{
|
||||
struct frame
|
||||
{
|
||||
const node* cur;
|
||||
const node* end;
|
||||
std::size_t index; ///< the container's node in out
|
||||
std::uint32_t count;
|
||||
bool object;
|
||||
};
|
||||
std::vector<frame> stack;
|
||||
const auto string_node = [&](const node & s)
|
||||
{
|
||||
node r = s;
|
||||
r.extra = 0;
|
||||
r.flags = static_cast<std::uint8_t>(s.flags & node_flags::storage);
|
||||
if (r.flags == node_flags::edited)
|
||||
{
|
||||
r.off = static_cast<std::uint32_t>(arena_size + arena_tail.size());
|
||||
arena_tail.append(d.str(s), s.len);
|
||||
r.flags = node_flags::escaped;
|
||||
}
|
||||
return r;
|
||||
};
|
||||
const auto emit = [&](const node * v)
|
||||
{
|
||||
node r = *v;
|
||||
switch (static_cast<value_t>(v->kind))
|
||||
{
|
||||
case value_t::object:
|
||||
case value_t::array:
|
||||
r.flags = 0;
|
||||
r.extra = 0;
|
||||
r.off = (v->flags & (node_flags::moved | node_flags::is_new)) != 0 ? 0 : v->off;
|
||||
r.len = 0; // counted below
|
||||
r.next = 0; // set when the container is complete
|
||||
stack.push_back(frame{d.first_child_edited(v), d.child_end_edited(v), out.size(), 0, v->kind == static_cast<std::uint8_t>(value_t::object)});
|
||||
break;
|
||||
case value_t::string:
|
||||
r = string_node(*v);
|
||||
break;
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
if ((v->flags & node_flags::storage) == node_flags::edited)
|
||||
{
|
||||
r.off = static_cast<std::uint32_t>(d.size + text_tail.size());
|
||||
text_tail.append(d.str(*v), number_length(*v));
|
||||
}
|
||||
r.flags = 0;
|
||||
break;
|
||||
case value_t::number_float:
|
||||
if ((v->flags & node_flags::storage) == node_flags::edited)
|
||||
{
|
||||
const char* const t = d.str(*v);
|
||||
if (t[0] == 'n' || t[0] == 'i' || (v->len > 1 && t[1] == 'i'))
|
||||
{
|
||||
r = node{}; // nan and infinity: null, as dump() writes them
|
||||
r.kind = static_cast<std::uint8_t>(value_t::null);
|
||||
break;
|
||||
}
|
||||
r.off = static_cast<std::uint32_t>(d.size + text_tail.size());
|
||||
text_tail.append(t, v->len);
|
||||
r.extra = 0xFFFFu; // the digit layout is not recorded
|
||||
}
|
||||
r.flags = 0;
|
||||
break;
|
||||
case value_t::boolean:
|
||||
r.flags = static_cast<std::uint8_t>(v->flags & node_flags::is_true);
|
||||
break;
|
||||
case value_t::null:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
r.flags = 0;
|
||||
break;
|
||||
}
|
||||
out.push_back(r);
|
||||
};
|
||||
emit(d.tape);
|
||||
while (!stack.empty())
|
||||
{
|
||||
frame& top = stack.back();
|
||||
if (top.cur == top.end)
|
||||
{
|
||||
node& c = out[top.index];
|
||||
c.len = top.count;
|
||||
c.next = static_cast<std::uint32_t>(out.size() - top.index);
|
||||
stack.pop_back();
|
||||
continue;
|
||||
}
|
||||
++top.count;
|
||||
const node* v = nullptr;
|
||||
if (top.object)
|
||||
{
|
||||
out.push_back(string_node(*top.cur));
|
||||
v = document_data::deref(top.cur + 1);
|
||||
top.cur = document_data::after(top.cur + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
v = document_data::deref(top.cur);
|
||||
top.cur = document_data::after(top.cur);
|
||||
}
|
||||
emit(v); // may grow the stack (top is not used afterwards)
|
||||
}
|
||||
}
|
||||
|
||||
/// the document as an image
|
||||
inline std::vector<std::uint8_t> save_image(const document_data& d)
|
||||
{
|
||||
#if !NLOHMANN_VIEW_LITTLE_ENDIAN
|
||||
throw_type_error(320, "json_document images need a little-endian target"); // LCOV_EXCL_LINE
|
||||
#endif
|
||||
const std::size_t arena_size = d.arena_size;
|
||||
const node* nodes = d.tape;
|
||||
std::size_t count = d.tape_size;
|
||||
std::vector<node> compacted;
|
||||
std::string text_tail;
|
||||
std::string arena_tail;
|
||||
if (d.edits)
|
||||
{
|
||||
compact_nodes(d, arena_size, compacted, text_tail, arena_tail);
|
||||
nodes = compacted.data();
|
||||
count = compacted.size();
|
||||
}
|
||||
const std::size_t text_size = d.size + text_tail.size();
|
||||
const std::size_t total_arena = arena_size + arena_tail.size();
|
||||
if (NLOHMANN_VIEW_UNLIKELY(text_size >= image_limit || total_arena >= image_limit || count >= image_limit))
|
||||
{
|
||||
// LCOV_EXCL_START (4 GiB)
|
||||
throw_out_of_range(416, "images of 4 GiB or more are not supported by json_document");
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
image_header h{};
|
||||
h.magic = {{'N', 'J', 'V', 'I'}};
|
||||
h.version = image_version;
|
||||
h.node_count = count;
|
||||
h.text_size = text_size;
|
||||
h.arena_size = total_arena;
|
||||
std::vector<std::uint8_t> image(sizeof(h) + (count * sizeof(node)) + text_size + 1 + total_arena + 1);
|
||||
std::uint8_t* o = image.data();
|
||||
std::memcpy(o, &h, sizeof(h));
|
||||
o += sizeof(h);
|
||||
std::memcpy(o, nodes, count * sizeof(node));
|
||||
// the hash indexes are rebuilt by load()
|
||||
for (std::size_t i = 0; i < count; ++i)
|
||||
{
|
||||
if (nodes[i].kind == static_cast<std::uint8_t>(value_t::object) && nodes[i].extra != 0)
|
||||
{
|
||||
node n = nodes[i];
|
||||
n.extra = 0;
|
||||
std::memcpy(o + (i * sizeof(node)), &n, sizeof(node));
|
||||
}
|
||||
}
|
||||
o += count * sizeof(node);
|
||||
const auto append = [&o](const char* s, std::size_t n)
|
||||
{
|
||||
if (n != 0)
|
||||
{
|
||||
std::memcpy(o, s, n);
|
||||
o += n;
|
||||
}
|
||||
};
|
||||
append(d.src, d.size);
|
||||
append(text_tail.data(), text_tail.size());
|
||||
*o++ = 0;
|
||||
append(d.base[1], arena_size);
|
||||
append(arena_tail.data(), arena_tail.size());
|
||||
*o = 0;
|
||||
return image;
|
||||
}
|
||||
|
||||
/// whether a number node matches its token the way the parser records it
|
||||
/// (after the bounds check)
|
||||
inline bool check_number(const node& n, const unsigned char* text)
|
||||
{
|
||||
const std::size_t len = number_length(n);
|
||||
const unsigned char* const s = text + n.off;
|
||||
const unsigned char* const e = s + len;
|
||||
const unsigned char* p = s;
|
||||
const bool negative = *p == '-';
|
||||
p += negative ? 1 : 0;
|
||||
const unsigned char* const int_start = p;
|
||||
if (p == e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (*p == '0')
|
||||
{
|
||||
++p;
|
||||
}
|
||||
else if (*p >= '1' && *p <= '9')
|
||||
{
|
||||
while (p != e && is_digit(*p))
|
||||
{
|
||||
++p;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto int_digits = static_cast<std::size_t>(p - int_start);
|
||||
std::size_t frac_digits = 0;
|
||||
bool is_float = false;
|
||||
if (p != e && *p == '.')
|
||||
{
|
||||
const unsigned char* const f0 = ++p;
|
||||
while (p != e && is_digit(*p))
|
||||
{
|
||||
++p;
|
||||
}
|
||||
if (p == f0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
frac_digits = static_cast<std::size_t>(p - f0);
|
||||
is_float = true;
|
||||
}
|
||||
std::int64_t exponent = 0;
|
||||
if (p != e && (*p | 0x20u) == 'e')
|
||||
{
|
||||
++p;
|
||||
const bool exp_negative = p != e && *p == '-';
|
||||
p += (p != e && (*p == '+' || *p == '-')) ? 1 : 0;
|
||||
if (p == e || !is_digit(*p))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
while (p != e && is_digit(*p))
|
||||
{
|
||||
exponent = exponent < 100000 ? (exponent * 10) + (*p - '0') : exponent;
|
||||
++p;
|
||||
}
|
||||
exponent = exp_negative ? -exponent : exponent;
|
||||
is_float = true;
|
||||
}
|
||||
if (p != e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (n.kind == static_cast<std::uint8_t>(value_t::number_float))
|
||||
{
|
||||
// the digit layout the parser records (or "many", as compaction
|
||||
// writes it), and a finite value
|
||||
const auto layout = static_cast<std::uint16_t>((int_digits < 255 ? int_digits : 255) | ((frac_digits < 255 ? frac_digits : 255) << 8u));
|
||||
if (n.extra != layout && n.extra != 0xFFFFu)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// parse() rejects floats that overflow; as there, only a number whose
|
||||
// magnitude could reach 1e308 needs the conversion
|
||||
if (static_cast<std::int64_t>(int_digits) + exponent > 300)
|
||||
{
|
||||
const auto v = float_value<double>(reinterpret_cast<const char*>(s), n); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
return v <= (std::numeric_limits<double>::max)() && v >= -(std::numeric_limits<double>::max)();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// integers: the token's value is the stored one; number_integer nodes of
|
||||
// edits can be non-negative (as basic_json keeps the type of a value)
|
||||
const bool integer = n.kind == static_cast<std::uint8_t>(value_t::number_integer);
|
||||
if (is_float || int_digits > 20 || (negative && !integer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// (at most 19 digits cannot overflow; 20 digits are compared with 2^64 - 1)
|
||||
if (int_digits == 20 && std::memcmp(int_start, "18446744073709551615", 20) > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
std::uint64_t m = 0;
|
||||
for (const unsigned char* d = int_start; d != int_start + int_digits; ++d)
|
||||
{
|
||||
m = (m * 10) + static_cast<std::uint64_t>(*d - '0');
|
||||
}
|
||||
if (integer && m > (negative ? std::uint64_t{1} << 63u : (std::uint64_t{1} << 63u) - 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return integer_bits(n) == (negative ? 0 - m : m);
|
||||
}
|
||||
|
||||
/// Check the nodes of a loaded image against its text and decoded strings:
|
||||
/// kinds, flags, and `extra`; extents and element counts of arrays and
|
||||
/// objects; keys; bounds; string contents (source strings as the parser
|
||||
/// leaves them: no quotes, backslashes, or control characters; all strings
|
||||
/// valid UTF-8); and number tokens.
|
||||
inline bool check_image(const node* nodes, std::size_t count, const unsigned char* text, std::size_t text_size,
|
||||
const unsigned char* arena, std::size_t arena_size, bool full)
|
||||
{
|
||||
struct frame
|
||||
{
|
||||
std::size_t end;
|
||||
std::uint32_t len;
|
||||
std::uint32_t seen;
|
||||
bool object;
|
||||
bool expect_key;
|
||||
};
|
||||
std::vector<frame> stack;
|
||||
const auto check_string = [&](const node & n) -> bool
|
||||
{
|
||||
if ((n.flags & ~node_flags::escaped) != 0 || n.extra != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const bool decoded = (n.flags & node_flags::escaped) != 0;
|
||||
const unsigned char* const base = decoded ? arena : text;
|
||||
const std::size_t limit = decoded ? arena_size : text_size;
|
||||
if (n.off > limit || n.len > limit - n.off)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!full)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
const unsigned char* const b = base + n.off;
|
||||
return decoded ? valid_utf8_prefix(b, n.len) == n.len : scan_string_run(b, b + n.len) == b + n.len;
|
||||
};
|
||||
// bounds of a number token; the recorded digit layout must lie within it
|
||||
const auto number_in_bounds = [&](const node & n) -> bool
|
||||
{
|
||||
const std::size_t len = number_length(n);
|
||||
if (len == 0 || n.off > text_size || len > text_size - n.off)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (n.kind != static_cast<std::uint8_t>(value_t::number_float))
|
||||
{
|
||||
return (n.extra >> 8u) == 0;
|
||||
}
|
||||
// float_value() reads the sign, the integer digits, and the point and
|
||||
// fraction digits the layout records (a layout of more than 19 digits
|
||||
// means the general conversion, which stays within the token)
|
||||
const std::size_t int_digits = n.extra & 0xFFu;
|
||||
const std::size_t frac_digits = n.extra >> 8u;
|
||||
const std::size_t need = (text[n.off] == '-' ? 1u : 0u) + int_digits + (frac_digits != 0 ? frac_digits + 1 : 0);
|
||||
return int_digits + frac_digits > 19 || need <= len;
|
||||
};
|
||||
std::size_t i = 0;
|
||||
for (;;)
|
||||
{
|
||||
// close finished arrays and objects
|
||||
while (!stack.empty() && i == stack.back().end)
|
||||
{
|
||||
const frame f = stack.back();
|
||||
if (f.seen != f.len || (f.object && !f.expect_key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
stack.pop_back();
|
||||
if (!stack.empty())
|
||||
{
|
||||
++stack.back().seen;
|
||||
stack.back().expect_key = true;
|
||||
}
|
||||
}
|
||||
if (i == count)
|
||||
{
|
||||
return stack.empty();
|
||||
}
|
||||
if (i != 0 && stack.empty())
|
||||
{
|
||||
return false; // nodes after the root
|
||||
}
|
||||
const node& n = nodes[i];
|
||||
if (!stack.empty() && stack.back().object && stack.back().expect_key)
|
||||
{
|
||||
if (n.kind != static_cast<std::uint8_t>(value_t::string) || !check_string(n))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
stack.back().expect_key = false;
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
bool complete = true;
|
||||
switch (static_cast<value_t>(n.kind))
|
||||
{
|
||||
case value_t::null:
|
||||
// (the offset of a literal is read to size the output of dump())
|
||||
if (n.flags != 0 || n.extra != 0 || n.off > text_size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case value_t::boolean:
|
||||
if ((n.flags & ~node_flags::is_true) != 0 || n.extra != 0 || n.off > text_size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case value_t::string:
|
||||
if (!check_string(n))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
if (n.flags != 0 || !number_in_bounds(n) || (full && !check_number(n, text)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case value_t::array:
|
||||
case value_t::object:
|
||||
{
|
||||
const std::size_t limit = stack.empty() ? count : stack.back().end;
|
||||
if (n.flags != 0 || n.extra != 0 || n.next == 0 || n.next > limit - i || n.off > text_size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
stack.push_back(frame{i + n.next, n.len, 0, n.kind == static_cast<std::uint8_t>(value_t::object), true});
|
||||
complete = false;
|
||||
break;
|
||||
}
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
++i;
|
||||
if (complete && !stack.empty())
|
||||
{
|
||||
++stack.back().seen;
|
||||
stack.back().expect_key = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_invalid_image(const char* what)
|
||||
{
|
||||
throw_parse_error(116, concat("invalid json_document image: ", what));
|
||||
}
|
||||
|
||||
/// Read an image into d. The text and the decoded strings stay in the image;
|
||||
/// the nodes are copied (so that they are aligned, and edits can change them).
|
||||
inline void load_image(document_data& d, const std::uint8_t* image, std::size_t size, image_check check)
|
||||
{
|
||||
#if !NLOHMANN_VIEW_LITTLE_ENDIAN
|
||||
throw_type_error(320, "json_document images need a little-endian target"); // LCOV_EXCL_LINE
|
||||
#endif
|
||||
if (image == nullptr || size < sizeof(image_header))
|
||||
{
|
||||
throw_invalid_image("too short");
|
||||
}
|
||||
image_header h{};
|
||||
std::memcpy(&h, image, sizeof(h));
|
||||
// (the reserved fields are for later versions)
|
||||
if (std::memcmp(h.magic.data(), "NJVI", 4) != 0 || h.version != image_version
|
||||
|| (h.reserved[0] | h.reserved[1] | h.reserved[2] | h.reserved[3]) != 0)
|
||||
{
|
||||
throw_invalid_image("unknown format");
|
||||
}
|
||||
const std::size_t room = size - sizeof(h);
|
||||
if (h.node_count == 0 || h.node_count > room / sizeof(node) || h.node_count >= image_limit || h.text_size >= image_limit || h.arena_size >= image_limit)
|
||||
{
|
||||
throw_invalid_image("sizes out of range");
|
||||
}
|
||||
const auto count = static_cast<std::size_t>(h.node_count);
|
||||
const auto text_size = static_cast<std::size_t>(h.text_size);
|
||||
const auto arena_size = static_cast<std::size_t>(h.arena_size);
|
||||
const std::size_t text_at = sizeof(h) + (count * sizeof(node));
|
||||
// the text, a NUL, the decoded strings, a NUL, and nothing after them
|
||||
if (size - text_at < 2 || text_size > size - text_at - 2 || arena_size != size - text_at - text_size - 2
|
||||
|| image[text_at + text_size] != 0 || image[size - 1] != 0)
|
||||
{
|
||||
throw_invalid_image("sizes out of range");
|
||||
}
|
||||
|
||||
d.discarded = true;
|
||||
d.edits.reset();
|
||||
d.base[2] = nullptr;
|
||||
d.owned.clear();
|
||||
if (d.owned_image.empty() || image != d.owned_image.data())
|
||||
{
|
||||
d.owned_image.clear();
|
||||
}
|
||||
d.arena.clear();
|
||||
d.indexes.clear();
|
||||
d.index_slots.clear();
|
||||
d.large_objects.clear();
|
||||
d.tape_size = 0;
|
||||
d.reserve(count);
|
||||
std::memcpy(d.tape, image + sizeof(h), count * sizeof(node));
|
||||
d.tape_size = count;
|
||||
const std::uint8_t* const text = image + text_at;
|
||||
const std::uint8_t* const arena = text + text_size + 1;
|
||||
d.src = reinterpret_cast<const char*>(text); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
d.size = text_size;
|
||||
d.base[0] = d.src;
|
||||
d.base[1] = reinterpret_cast<const char*>(arena); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
d.arena_size = arena_size;
|
||||
if (check != image_check::none && !check_image(d.tape, count, text, text_size, arena, arena_size, check == image_check::full))
|
||||
{
|
||||
throw_invalid_image("the check failed");
|
||||
}
|
||||
// the hash indexes of large objects, as after parsing
|
||||
for (std::size_t i = 0; i < count; ++i)
|
||||
{
|
||||
node& n = d.tape[i];
|
||||
if (n.kind == static_cast<std::uint8_t>(value_t::object))
|
||||
{
|
||||
n.extra = 0;
|
||||
if (n.len >= document_data::index_min_members)
|
||||
{
|
||||
d.large_objects.push_back(static_cast<std::uint32_t>(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
build_object_indexes(d);
|
||||
d.discarded = false;
|
||||
}
|
||||
|
||||
} // namespace view
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -73,7 +73,7 @@ class view_iterator
|
||||
|
||||
NLOHMANN_VIEW_ALWAYS_INLINE View operator*() const noexcept
|
||||
{
|
||||
return View(m_doc, View::navigation::value(m_pos + m_value_offset));
|
||||
return View(m_doc, m_pos + m_value_offset);
|
||||
}
|
||||
|
||||
pointer operator->() const noexcept
|
||||
|
||||
Loaded 100 of 152 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user