diff --git a/.github/workflows/check_amalgamation.yml b/.github/workflows/check_amalgamation.yml index baf4dbe31..f70ebfba0 100644 --- a/.github/workflows/check_amalgamation.yml +++ b/.github/workflows/check_amalgamation.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 with: egress-policy: audit @@ -34,7 +34,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 with: egress-policy: audit diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index a64b11537..2e50fee9c 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden Runner - uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 with: egress-policy: audit diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 6f2c060c2..c5497ef9c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 with: egress-policy: audit diff --git a/.github/workflows/comment_check_amalgamation.yml b/.github/workflows/comment_check_amalgamation.yml index 10ecb251c..788c1b8ce 100644 --- a/.github/workflows/comment_check_amalgamation.yml +++ b/.github/workflows/comment_check_amalgamation.yml @@ -19,7 +19,7 @@ jobs: pull-requests: write steps: - name: Harden Runner - uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 with: egress-policy: audit diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index a28624b77..aa09008a3 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 with: egress-policy: audit diff --git a/.github/workflows/flawfinder.yml b/.github/workflows/flawfinder.yml index 15f9f7825..7c4d22b3c 100644 --- a/.github/workflows/flawfinder.yml +++ b/.github/workflows/flawfinder.yml @@ -27,7 +27,7 @@ jobs: security-events: write steps: - name: Harden Runner - uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 with: egress-policy: audit diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 3fb2af82a..3b4571b95 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -17,7 +17,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 with: egress-policy: audit diff --git a/.github/workflows/publish_documentation.yml b/.github/workflows/publish_documentation.yml index 95790dcb9..189d95419 100644 --- a/.github/workflows/publish_documentation.yml +++ b/.github/workflows/publish_documentation.yml @@ -26,7 +26,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden Runner - uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 with: egress-policy: audit diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 0182fb062..de919b1ab 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -36,7 +36,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 with: egress-policy: audit diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index d2afcb42e..38de00932 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -32,7 +32,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 with: egress-policy: audit diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 1cea13636..1e690ccdd 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 with: egress-policy: audit diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 7ae29fffd..69a3cbc45 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -35,7 +35,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 with: egress-policy: audit @@ -60,7 +60,7 @@ jobs: target: [ci_test_amalgamation, ci_test_single_header, ci_cppcheck, ci_cpplint, ci_reproducible_tests, ci_non_git_tests, ci_offline_testdata, ci_reuse_compliance, ci_test_valgrind] steps: - name: Harden Runner - uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 with: egress-policy: audit @@ -118,7 +118,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 with: egress-policy: audit @@ -369,7 +369,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 with: egress-policy: audit @@ -392,7 +392,7 @@ jobs: target: [ci_test_examples, ci_test_build_documentation] steps: - name: Harden Runner - uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 with: egress-policy: audit diff --git a/Makefile b/Makefile index d99d6f5f3..e1a1d2b75 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: pretty clean ChangeLog.md release +.PHONY: pretty clean ChangeLog.md release update_hedley update_hedley_undef ########################################################################## # configuration @@ -41,6 +41,8 @@ all: @echo "fuzz_testing_ubjson - prepare fuzz testing of the UBJSON parser" @echo "pretty - beautify code with Artistic Style" @echo "run_benchmarks - build and run benchmarks" + @echo "update_hedley - download Hedley and regenerate hedley.hpp / hedley_undef.hpp" + @echo "update_hedley_undef - rebuild hedley_undef.hpp from the JSON_HEDLEY_* #define names in hedley.hpp" ########################################################################## @@ -241,11 +243,24 @@ update_hedley: rm -f include/nlohmann/thirdparty/hedley/hedley.hpp include/nlohmann/thirdparty/hedley/hedley_undef.hpp curl https://raw.githubusercontent.com/nemequ/hedley/master/hedley.h -o include/nlohmann/thirdparty/hedley/hedley.hpp $(SED) -i 's/HEDLEY_/JSON_HEDLEY_/g' include/nlohmann/thirdparty/hedley/hedley.hpp - grep "[[:blank:]]*#[[:blank:]]*undef" include/nlohmann/thirdparty/hedley/hedley.hpp | grep -v "__" | sort | uniq | $(SED) 's/ //g' | $(SED) 's/undef/undef /g' > include/nlohmann/thirdparty/hedley/hedley_undef.hpp $(SED) -i '1s/^/#pragma once\n\n/' include/nlohmann/thirdparty/hedley/hedley.hpp - $(SED) -i '1s/^/#pragma once\n\n/' include/nlohmann/thirdparty/hedley/hedley_undef.hpp + $(MAKE) update_hedley_undef $(MAKE) amalgamate +# Rebuild hedley_undef.hpp from every JSON_HEDLEY_* name that hedley.hpp +# #defines. Hedley does not #undef all of its public macros internally (see +# #5408), so grepping those #undef lines misses names such as +# JSON_HEDLEY_PRAGMA. cmake/scripts/gen_hedley_undef_check.cmake is the +# single source of truth for this extraction (tests/CMakeLists.txt uses the +# same script, in MODE=checks, to generate the matching leak-check test), so +# the vendored header, the generated #undef list, and the regression test +# cannot drift apart. +update_hedley_undef: + cmake -DHEDLEY_HPP=include/nlohmann/thirdparty/hedley/hedley.hpp \ + -DOUTPUT=include/nlohmann/thirdparty/hedley/hedley_undef.hpp \ + -DMODE=undef \ + -P cmake/scripts/gen_hedley_undef_check.cmake + ########################################################################## # serve_header.py ########################################################################## diff --git a/cmake/scripts/gen_hedley_undef_check.cmake b/cmake/scripts/gen_hedley_undef_check.cmake new file mode 100644 index 000000000..fc8cfec2c --- /dev/null +++ b/cmake/scripts/gen_hedley_undef_check.cmake @@ -0,0 +1,112 @@ +# Shared extractor for the JSON_HEDLEY_* macro names defined in hedley.hpp. +# +# Every macro that hedley.hpp #defines must be #undef-ed again once json.hpp +# has been fully processed (see include/nlohmann/detail/macro_unscope.hpp +# and https://github.com/nlohmann/json/issues/5408). Deriving the macro list +# straight from hedley.hpp here -- instead of hand-maintaining it in two +# places -- means hedley_undef.hpp and the regression test that checks for +# leaked macros can never drift apart, even after a future `make +# update_hedley` pulls in new macros from upstream Hedley. +# +# MODE=undef (default): write hedley_undef.hpp (SPDX header, #pragma once, +# one #undef per macro name) -- used by `make update_hedley_undef` +# MODE=checks: write one #ifdef/FAIL_CHECK/#endif per macro name, +# meant to be #include-d inside a TEST_CASE -- used by +# tests/CMakeLists.txt to (re)generate the include for +# tests/src/unit-no-macro-leak.cpp +# +# Required variables: +# HEDLEY_HPP path to include/nlohmann/thirdparty/hedley/hedley.hpp +# OUTPUT path of the file to (over)write +# Optional: +# MODE "undef" (default) or "checks" + +if(NOT DEFINED HEDLEY_HPP OR NOT DEFINED OUTPUT) + message(FATAL_ERROR "HEDLEY_HPP and OUTPUT must be set") +endif() + +if(NOT EXISTS "${HEDLEY_HPP}") + message(FATAL_ERROR "Hedley header not found: ${HEDLEY_HPP}") +endif() + +if(NOT DEFINED MODE) + set(MODE undef) +endif() + +if(NOT MODE STREQUAL "undef" AND NOT MODE STREQUAL "checks") + message(FATAL_ERROR "MODE must be undef or checks, got: ${MODE}") +endif() + +# Line-anchored, like `grep -oE "^[[:blank:]]*#[[:blank:]]*define[[:blank:]]+JSON_HEDLEY_[A-Za-z0-9_]+"`. +# Unanchored matching would also pick up JSON_HEDLEY_* mentions inside +# comments or string literals elsewhere in the file, which must not turn +# into #undef lines. +file(STRINGS "${HEDLEY_HPP}" hedley_lines) +set(macro_names) +foreach(line IN LISTS hedley_lines) + if("${line}" MATCHES "^[ \t]*#[ \t]*define[ \t]+(JSON_HEDLEY_[A-Za-z0-9_]+)") + list(APPEND macro_names "${CMAKE_MATCH_1}") + endif() +endforeach() + +if(NOT macro_names) + message(FATAL_ERROR "No JSON_HEDLEY_* macros found in ${HEDLEY_HPP}") +endif() + +list(REMOVE_DUPLICATES macro_names) +# Lexicographic, locale-independent (ASCII-only names) -- matches `LC_ALL=C sort`. +list(SORT macro_names COMPARE STRING) +list(LENGTH macro_names macro_count) + +set(generated "") +if(MODE STREQUAL "undef") + # Same banner `make update_hedley_undef` would stamp by hand, so the + # recipe is self-contained and its output is byte-stable across reruns. + # The embedded SPDX tags below are part of the *generated* file's + # content, not a REUSE header for this .cmake script itself (which is + # already covered by the blanket "Files: *" rule in .reuse/dep5) -- keep + # them wrapped in REUSE-IgnoreStart/End so `reuse lint` does not try to + # parse "MIT\n")" as this file's own SPDX-License-Identifier value. + # REUSE-IgnoreStart + string(APPEND generated "// __ _____ _____ _____\n") + string(APPEND generated "// __| | __| | | | JSON for Modern C++\n") + string(APPEND generated "// | | |__ | | | | | | version 3.12.0\n") + string(APPEND generated "// |_____|_____|_____|_|___| https://github.com/nlohmann/json\n") + string(APPEND generated "//\n") + string(APPEND generated "// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann \n") + string(APPEND generated "// SPDX-License-Identifier: MIT\n") + # REUSE-IgnoreEnd + string(APPEND generated "\n") + string(APPEND generated "#pragma once\n") + string(APPEND generated "\n") + foreach(name IN LISTS macro_names) + string(APPEND generated "#undef ${name}\n") + endforeach() +else() + string(APPEND generated "// This file is generated by cmake/scripts/gen_hedley_undef_check.cmake\n") + string(APPEND generated "// from include/nlohmann/thirdparty/hedley/hedley.hpp. Do not edit it by\n") + string(APPEND generated "// hand -- it is regenerated on every build. ${macro_count} macros checked.\n\n") + foreach(name IN LISTS macro_names) + string(APPEND generated "#ifdef ${name}\n") + string(APPEND generated " FAIL_CHECK(\"${name} leaked after including nlohmann/json.hpp\");\n") + string(APPEND generated "#endif\n") + endforeach() +endif() + +get_filename_component(output_dir "${OUTPUT}" DIRECTORY) +if(output_dir) + file(MAKE_DIRECTORY "${output_dir}") +endif() + +# Avoid rewriting the file (and busting downstream incremental rebuilds) +# when the content has not actually changed. +set(write_output TRUE) +if(EXISTS "${OUTPUT}") + file(READ "${OUTPUT}" existing_content) + if(existing_content STREQUAL generated) + set(write_output FALSE) + endif() +endif() +if(write_output) + file(WRITE "${OUTPUT}" "${generated}") +endif() diff --git a/docs/mkdocs/docs/api/basic_json/patch.md b/docs/mkdocs/docs/api/basic_json/patch.md index 432e7c128..fa25b2699 100644 --- a/docs/mkdocs/docs/api/basic_json/patch.md +++ b/docs/mkdocs/docs/api/basic_json/patch.md @@ -34,6 +34,10 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va ("add", "remove", "move") - Throws [`out_of_range.411`](../../home/exceptions.md#jsonexceptionout_of_range411) if an "add" operation's target location has a parent that is neither an object nor an array. +- Throws [`out_of_range.413`](../../home/exceptions.md#jsonexceptionout_of_range413) if a "remove" operation's target + location has a parent that is neither an object nor an array. +- Throws [`out_of_range.414`](../../home/exceptions.md#jsonexceptionout_of_range414) if a "move" operation's "from" + location is a proper prefix of its "path" location. - Throws [`other_error.501`](../../home/exceptions.md#jsonexceptionother_error501) if "test" operation was unsuccessful. @@ -75,3 +79,7 @@ is thrown. In any case, the original value is not changed: the patch is applied - Added in version 2.0.0. - Added [`out_of_range.411`](../../home/exceptions.md#jsonexceptionout_of_range411) and stopped relying on an internal assertion when an "add" operation's target location has a non-object/non-array parent in version 3.13.0. +- Added [`out_of_range.413`](../../home/exceptions.md#jsonexceptionout_of_range413) and stopped silently ignoring a "remove" operation whose target + location has a non-object/non-array parent in version 3.13.0. +- Added [`out_of_range.414`](../../home/exceptions.md#jsonexceptionout_of_range414) and rejected a "move" operation whose "from" location is a proper + prefix of its "path" location instead of silently producing a corrupted result in version 3.13.0. diff --git a/docs/mkdocs/docs/api/basic_json/patch_inplace.md b/docs/mkdocs/docs/api/basic_json/patch_inplace.md index ae2b5e6a9..7ae85aaaa 100644 --- a/docs/mkdocs/docs/api/basic_json/patch_inplace.md +++ b/docs/mkdocs/docs/api/basic_json/patch_inplace.md @@ -30,6 +30,10 @@ No guarantees, value may be corrupted by an unsuccessful patch operation. ("add", "remove", "move") - Throws [`out_of_range.411`](../../home/exceptions.md#jsonexceptionout_of_range411) if an "add" operation's target location has a parent that is neither an object nor an array. +- Throws [`out_of_range.413`](../../home/exceptions.md#jsonexceptionout_of_range413) if a "remove" operation's target + location has a parent that is neither an object nor an array. +- Throws [`out_of_range.414`](../../home/exceptions.md#jsonexceptionout_of_range414) if a "move" operation's "from" + location is a proper prefix of its "path" location. - Throws [`other_error.501`](../../home/exceptions.md#jsonexceptionother_error501) if "test" operation was unsuccessful. @@ -72,3 +76,7 @@ function throws an exception. - Added in version 3.11.0. - Added [`out_of_range.411`](../../home/exceptions.md#jsonexceptionout_of_range411) and stopped relying on an internal assertion when an "add" operation's target location has a non-object/non-array parent in version 3.13.0. +- Added [`out_of_range.413`](../../home/exceptions.md#jsonexceptionout_of_range413) and stopped silently ignoring a "remove" operation whose target + location has a non-object/non-array parent in version 3.13.0. +- Added [`out_of_range.414`](../../home/exceptions.md#jsonexceptionout_of_range414) and rejected a "move" operation whose "from" location is a proper + prefix of its "path" location instead of silently producing a corrupted result in version 3.13.0. diff --git a/docs/mkdocs/docs/examples/get__ValueType_const.output b/docs/mkdocs/docs/examples/get__ValueType_const.output index 5cd9cd3aa..e7e9b5d59 100644 --- a/docs/mkdocs/docs/examples/get__ValueType_const.output +++ b/docs/mkdocs/docs/examples/get__ValueType_const.output @@ -4,8 +4,8 @@ Hello, world! 1 2 3 4 5 -string: "Hello, world!" number: {"floating-point":17.23,"integer":42} null: null +string: "Hello, world!" boolean: true array: [1,2,3,4,5] diff --git a/docs/mkdocs/docs/examples/get_to.output b/docs/mkdocs/docs/examples/get_to.output index 5cd9cd3aa..e7e9b5d59 100644 --- a/docs/mkdocs/docs/examples/get_to.output +++ b/docs/mkdocs/docs/examples/get_to.output @@ -4,8 +4,8 @@ Hello, world! 1 2 3 4 5 -string: "Hello, world!" number: {"floating-point":17.23,"integer":42} null: null +string: "Hello, world!" boolean: true array: [1,2,3,4,5] diff --git a/docs/mkdocs/docs/examples/operator__ValueType.output b/docs/mkdocs/docs/examples/operator__ValueType.output index a3bd9fff4..de471ec02 100644 --- a/docs/mkdocs/docs/examples/operator__ValueType.output +++ b/docs/mkdocs/docs/examples/operator__ValueType.output @@ -4,9 +4,9 @@ Hello, world! 1 2 3 4 5 -string: "Hello, world!" number: {"floating-point":17.23,"integer":42} null: null +string: "Hello, world!" boolean: true array: [1,2,3,4,5] [json.exception.type_error.302] type must be boolean, but is string diff --git a/docs/mkdocs/docs/features/binary_formats/bjdata.md b/docs/mkdocs/docs/features/binary_formats/bjdata.md index faff64f16..d3f63a9b8 100644 --- a/docs/mkdocs/docs/features/binary_formats/bjdata.md +++ b/docs/mkdocs/docs/features/binary_formats/bjdata.md @@ -125,6 +125,7 @@ The library uses the following mapping from JSON values types to BJData types ac - `"_ArrayType_"` is one of `uint8`, `int8`, `uint16`, `int16`, `uint32`, `int32`, `uint64`, `int64`, `single`, `double`, `char`, or `byte`, + - `"_ArraySize_"` is an array, since the dimensions are written as the ND-array header's length, - every entry of `"_ArraySize_"` is a non-negative integer, and their product is representable as a `std::size_t`, - `"_ArrayData_"` holds exactly that many elements, and - every element of `"_ArrayData_"` is a number of the kind named by `"_ArrayType_"` (a floating-point number for diff --git a/docs/mkdocs/docs/home/customers.md b/docs/mkdocs/docs/home/customers.md index 73ac83ee8..72802ff17 100644 --- a/docs/mkdocs/docs/home/customers.md +++ b/docs/mkdocs/docs/home/customers.md @@ -8,41 +8,72 @@ the result of an internet search. If you know further customers of the library, ## Space Exploration - [**Peregrine Lunar Lander Flight 01**](https://en.wikipedia.org/wiki/Peregrine_Mission_One) - The library was used for payload management in the **Peregrine Moon Lander**, developed by **Astrobotic Technology** and launched as part of NASA's **Commercial Lunar Payload Services (CLPS)** program. After six days in orbit, the spacecraft was intentionally redirected into Earth's atmosphere, where it burned up over the Pacific Ocean on **January 18, 2024**. +- [**NASA Unsteady Pressure-Sensitive Paint Processing**](https://github.com/nasa/upsp-processing), NASA software for processing high-speed video recordings of wind tunnel tests on launch vehicle and aircraft models +- [**Terma TEMU**](https://temu.terma.com/docs/public/temu-release-notes/latest/copying/json-for-modern-cpp.html), an emulator of spacecraft on-board computers used to develop and validate flight software for European space missions ## Automotive - [**Alexa Auto SDK**](https://github.com/alexa/alexa-auto-sdk), a software development kit enabling the integration of Alexa into automotive systems - [**Apollo**](https://github.com/ApolloAuto/apollo), a framework for building autonomous driving systems - [**Automotive Grade Linux (AGL)**](https://download.automotivelinux.org/AGL/release/jellyfish/latest/qemux86-64/deploy/licenses/nlohmann-json/), a collaborative open-source platform for automotive software development +- [**Autoware**](https://github.com/autowarefoundation/autoware_universe), an open-source software stack for autonomous driving built on ROS 2 +- [**Eclipse S-CORE**](https://github.com/eclipse-score/nlohmann_json), an open-source software platform for the software-defined vehicle backed by major automotive manufacturers and suppliers - [**Genesis Motor** (infotainment)](http://webmanual.genesis.com/ccIC/AVNT/JW/KOR/English/reference010.html), a luxury automotive brand - [**Hyundai** (infotainment)](https://www.hyundai.com/wsvc/ww/download.file.do?id=/content/hyundai/ww/data/opensource/data/GN7-2022/licenseCode/info), a global automotive brand - [**Kia** (infotainment)](http://webmanual.kia.com/PREM_GEN6/AVNT/RJPE/KOR/Korean/reference010.html), a global automotive brand - [**Mercedes-Benz Operating System (MB.OS)**](https://group.mercedes-benz.com/careers/about-us/mercedes-benz-operating-system/), a core component of the vehicle software ecosystem from Mercedes-Benz +- [**NVIDIA DRIVE OS**](https://developer.nvidia.com/docs/drive/drive-os/6.0.5/public/driveworks-nvcgf/dwx_open_source_attribution.html), the operating system and DriveWorks SDK powering NVIDIA's platform for autonomous vehicles - [**Rivian** (infotainment)](https://assets.ctfassets.net/2md5qhoeajym/3cwyo4eoufk4yingUwusFt/ded2c47da620fdfc99c88c7156d2c1d8/In-Vehicle_OSS_Attribution_2024__11-24_.pdf), an electric vehicle manufacturer - [**Suzuki** (infotainment)](https://www.globalsuzuki.com/motorcycle/ipc/oss/oss_48KA_00.pdf), a global automotive and motorcycle manufacturer ## Gaming and Entertainment +- [**Anno 117: Pax Romana**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a city-building strategy game set in the Roman Empire - [**Assassin's Creed: Mirage**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a stealth-action game set in the Middle East, focusing on the journey of a young assassin with classic parkour and stealth mechanics +- [**Battlefield 6**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a military first-person shooter known for its large-scale multiplayer battles +- [**Battlefield: REDSEC**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a free-to-play battle royale experience set in the Battlefield universe +- [**BioMenace: Remastered**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a remaster of the classic side-scrolling platform shooter - [**Chasm: The Rift**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a first-person shooter blending horror and adventure, where players navigate dark realms and battle monsters - [**College Football 25**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a college football simulation game featuring gameplay that mimics real-life college teams and competitions +- [**College Football 26**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a college football simulation game featuring licensed teams and stadiums +- [**College Football 27**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), the latest installment of the college football simulation series - [**Concepts**](https://concepts.app/en/licenses), a digital sketching app designed for creative professionals, offering flexible drawing tools for illustration, design, and brainstorming - [**Depthkit**](https://www.depthkit.tv/third-party-licenses), a tool for creating and capturing volumetric video, enabling immersive 3D experiences and interactive content +- [**Dune: Awakening**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), an open-world survival MMO set on the desert planet Arrakis +- [**EA Sports FC 25**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), an association football simulation with club, career, and online modes +- [**EA Sports FC 26**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), the latest installment of the association football simulation series +- [**EA Sports UFC 6**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a mixed martial arts fighting simulation +- [**FiveM**](https://github.com/citizenfx/fivem), a modification framework for Grand Theft Auto V that powers custom multiplayer servers +- [**FLUX:: Immersive**](https://doc.flux.audio/syrah/Credits.html), a suite of professional audio processing and immersive mixing plugins used in music and post-production - [**IMG.LY**](https://img.ly/acknowledgements), a platform offering creative tools and SDKs for integrating advanced image and video editing in applications +- [**immersivetech**](https://immersitech.io/open-source-third-party-software/), a technology company focused on immersive experiences, providing tools and solutions for virtual and augmented reality applications +- [**Kodi**](https://github.com/xbmc/xbmc/blob/master/xbmc/utils/JSONVariantWriter.cpp), a home theater and media center application - [**LOOT**](https://loot.readthedocs.io/_/downloads/en/0.13.0/pdf/), a tool for optimizing the load order of game plugins, commonly used in The Elder Scrolls and Fallout series +- [**LunaTranslator**](https://github.com/HIllya51/LunaTranslator/blob/main/src/NativeImpl/LunaSubprocess/aspatch.cpp), a real-time translation tool for visual novels +- [**MaaAssistantArknights**](https://github.com/MaaAssistantArknights/MaaAssistantArknights/blob/dev-v2/src/MaaCore/Vision/Roguelike/BlackFlow/BlackFlowMapAnalyzer.cpp), an automation assistant for the mobile game Arknights - [**Madden NFL 25**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a sports simulation game capturing the excitement of American football with realistic gameplay and team management features +- [**Madden NFL 26**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), an American football simulation with franchise and team management modes +- [**Madden NFL 27**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), the latest installment of the American football simulation series - [**Marne**](https://marne.io/licenses), an unofficial private server platform for hosting custom Battlefield 1 game experiences - [**Minecraft**](https://www.minecraft.net/zh-hant/attribution), a popular sandbox video game +- [**Mumble**](https://github.com/mumble-voip/mumble), a low-latency, open-source voice chat application widely used by gaming communities - [**NHL 22**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a hockey simulation game offering realistic gameplay, team management, and various modes to enhance the hockey experience +- [**OBS Studio**](https://github.com/obsproject/obs-studio), a free and open-source suite for video recording and live streaming +- [**OpenRCT2**](https://github.com/OpenRCT2/OpenRCT2/blob/develop/src/openrct2/core/JsonFwd.hpp), an open source re-implementation of RollerCoaster Tycoon 2 - [**Pixelpart**](https://pixelpart.net/documentation/book/third-party.html), a 2D animation and video compositing software that allows users to create animated graphics and visual effects with a focus on simplicity and ease of use - [**Razer Cortex**](https://mysupport.razer.com/app/answers/detail/a_id/14146/~/open-source-software-for-razer-software), a gaming performance optimizer and system booster designed to enhance the gaming experience - [**Red Dead Redemption II**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), an open-world action-adventure game following an outlaw's story in the late 1800s, emphasizing deep storytelling and immersive gameplay +- [**RetroArch**](https://github.com/libretro/RetroArch), a frontend for emulators, game engines, and media players built on the libretro API +- [**shadPS4**](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/user_manager.h), a PlayStation 4 emulator for Windows, Linux and macOS +- [**skate.**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a free-to-play skateboarding game set in an open world - [**Snapchat**](https://www.snap.com/terms/license-android), a multimedia messaging and augmented reality app for communication and entertainment +- [**Steel Century Groove**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), an action game released in 2026 +- [**Sunshine**](https://github.com/LizardByte/Sunshine/blob/master/src/confighttp.h), a self-hosted game streaming host compatible with Moonlight clients - [**Tactics Ogre: Reborn**](https://www.square-enix-games.com/en_US/documents/tactics-ogre-reborn-pc-installer-software-and-associated-plug-ins-disclosure), a tactical role-playing game featuring strategic battles and deep storytelling elements - [**Throne and Liberty**](https://www.amazon.com/gp/help/customer/display.html?nodeId=T7fLNw5oAevCMtJFPj&pop-up=1), an MMORPG that offers an expansive fantasy world with dynamic gameplay and immersive storytelling - [**Unity Vivox**](https://docs.unity3d.com/Packages/com.unity.services.vivox@15.1/license/Third%20Party%20Notices.html), a communication service that enables voice and text chat functionality in multiplayer games developed with Unity +- [**xemu**](https://github.com/xemu-project/xemu), an emulator of the original Xbox console - [**Zool: Redimensioned**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a modern reimagining of the classic platformer featuring fast-paced gameplay and vibrant environments -- [**immersivetech**](https://immersitech.io/open-source-third-party-software/), a technology company focused on immersive experiences, providing tools and solutions for virtual and augmented reality applications ## Consumer Electronics @@ -50,109 +81,195 @@ the result of an internet search. If you know further customers of the library, - [**Canon CanoScan LIDE**](https://carolburo.com/wp-content/uploads/2024/06/LiDE400_OnlineManual_Win_FR_V02.pdf), a series of flatbed scanners offering high-resolution image scanning for home and office use - [**Canon PIXMA Printers**](https://www.mediaexpert.pl/products/files/73/7338196/Instrukcja-obslugi-CANON-Pixma-TS7450i.pdf), a line of all-in-one inkjet printers known for high-quality printing and wireless connectivity - [**Cisco Webex Desk Camera**](https://www.cisco.com/c/dam/en_us/about/doing_business/open_source/docs/CiscoWebexDeskCamera-23-1622100417.pdf), a video camera designed for professional-quality video conferencing and remote collaboration +- [**DJI Edge SDK**](https://github.com/dji-sdk/Edge-SDK-V2-Demo), the reference applications for DJI's Edge SDK, used to build edge computing services on DJI drone docks +- [**Elgato Stream Deck**](https://github.com/elgatosf/streamdeck-obs-plugin2), a family of programmable control surfaces for content creators and their plugin ecosystem +- [**Instagrid**](https://instagrid.co/intellectual-property/foss), a manufacturer of portable, high-performance battery systems for professional mobile power supply +- [**iRobot**](https://iot-content.irobot.com/iw/sfsites/c/cms/delivery/media/MCKRLTPDJSSJBNJKDA5SG5UVVIIQ), a manufacturer of autonomous home robots including the Roomba vacuum cleaner range +- [**Logitech Logi Bolt**](https://opensource.logitech.com/wiki/Logi_BoltApp/), the management application for Logitech's secure wireless connectivity technology +- [**Novitus**](https://novitus.pl/licencjepensource), a manufacturer of fiscal cash registers and point-of-sale devices - [**Philips Hue Personal Wireless Lighting**](http://2ak5ape.257.cz/), a smart lighting system for customizable and wireless home illumination - [**Ray-Ban Meta Smart glasses**](https://www.meta.com/de/en/legal/smart-glasses/third-party-notices-android/03/), a pair of smart glasses designed for capturing photos and videos with integrated connectivity and social features - [**Razer Synapse**](https://mysupport.razer.com/app/answers/detail/a_id/14146/~/open-source-software-for-razer-software), a unified configuration software enabling hardware customization for Razer devices +- [**Sharp Professional Displays**](https://jp.sharp/restricted/business/lcd-display/cms/images/source_pnla862/PN-LA652_752_862_LicenseInformation.pdf), a range of large-format interactive displays for business and education - [**Siemens SINEMA Remote Connect**](https://cache.industry.siemens.com/dl/files/790/109793790/att_1054961/v2/OSS_SINEMA-RC_86.pdf), a remote connectivity solution for monitoring and managing industrial networks and devices securely +- [**Skydio**](https://pages.skydio.com/rs/784-TUF-591/images/Open%20Source%20Software%20Notice%20v0.2.html), a manufacturer of autonomous drones for inspection, public safety, and defense applications - [**Sony PlayStation 4**](https://doc.dl.playstation.net/doc/ps4-oss/index.html), a gaming console developed by Sony that offers a wide range of games and multimedia entertainment features +- [**Sony Spatial Reality Display**](https://www.sony.co.jp/en/Products/Developer-Spatial-Reality-display/download/dcc-tools/blender-plugin/SpatiaRealityDisplayPluginforPreviewBL_Manual.pdf), a glasses-free stereoscopic 3D display and its plugins for Blender, 3ds Max, and ZBrush - [**Sony Virtual Webcam Driver for Remote Camera**](https://helpguide.sony.net/rc/vwd/v1/zh-cn/print.pdf), a software driver that enables the use of Sony cameras as virtual webcams for video conferencing and streaming +- [**Yamaha Clavinova**](https://usa.yamaha.com/files/download/other_assets/1/2298171/CLP-800_oss_license.pdf), a series of digital pianos combining acoustic piano feel with digital sound technology -## Operating Systems +## Operating Systems and Platforms - [**Apple iOS and macOS**](https://www.apple.com/macos), a family of operating systems developed by Apple, including iOS for mobile devices and macOS for desktop computers +- [**Chromium**](https://chromium.googlesource.com/chromium/src/+/main/third_party/nlohmann_json/), the open-source browser project that Google Chrome, Microsoft Edge, and many other browsers are built on, where the library is used as data container for on-device model execution - [**Google Fuchsia**](https://fuchsia.googlesource.com/third_party/json/), an open-source operating system developed by Google, designed to be secure, updatable, and adaptable across various devices +- [**LG webOS**](https://github.com/webosose/com.webos.service.camera), a Linux-based operating system used in LG smart TVs, signage, and embedded devices +- [**Microsoft Azure Linux**](https://github.com/microsoft/azurelinux), a Linux distribution developed by Microsoft for Azure infrastructure and edge workloads +- [**OpenHarmony**](https://github.com/openharmony/third_party_json), an open-source operating system for smart devices and the foundation of HarmonyOS - [**SerenityOS**](https://github.com/SerenityOS/serenity), an open-source operating system that aims to provide a simple and beautiful user experience with a focus on simplicity and elegance +- [**Windows Subsystem for Linux**](https://github.com/microsoft/WSL), a compatibility layer that runs Linux environments natively on Windows - [**Yocto**](http://ftp.emacinc.com/openembedded-sw/kirkstone-icop-5.15-kirkstone-6.0/archive-2024-10/pn8m-090t-ppc/licenses/nlohmann-json/), a Linux-based build system for creating custom operating systems and software distributions, tailored for embedded devices and IoT applications ## Development Tools and IDEs - [**Accentize SpectralBalance**](https://www.accentize.com/products/SpectralBalanceManual.pdf), an adaptive speech analysis tool designed to enhance audio quality by optimizing frequency balance in recordings +- [**Airbus Ghidralligator**](https://www.cyber.airbus.com/en/newsroom/stories/2025-06-ghidralligator), a Ghidra-based emulator from Airbus CyberSecurity used to fuzz and analyse embedded firmware +- [**Apache brpc**](https://github.com/apache/brpc/blob/master/src/butil/iobuf.h), an industrial-grade remote procedure call framework for C++ - [**Arm Compiler for Linux**](https://documentation-service.arm.com/static/66558e9d876c8d213b7843e4), a software development toolchain for compiling and optimizing applications on Arm-based Linux systems - [**BBEdit**](https://s3.amazonaws.com/BBSW-download/BBEdit_15.1.2_User_Manual.pdf), a professional text and code editor for macOS - [**CoderPad**](https://coderpad.io), a collaborative coding platform that enables real-time code interviews and assessments for developers; the library is included in every CoderPad instance and can be accessed with a simple `#include "json.hpp"` +- [**Codon**](https://github.com/exaloop/codon/blob/develop/jupyter/jupyter.h), an ahead-of-time compiler for a Python-like language - [**Compiler Explorer**](https://godbolt.org), a web-based tool that allows users to write, compile, and visualize the assembly output of code in various programming languages; the library is readily available and accessible with the directive `#include `. -- [**GitHub CodeQL**](https://github.com/github/codeql), a code analysis tool used for identifying security vulnerabilities and bugs in software through semantic queries +- [**Flutter**](https://github.com/flutter/flutter/blob/master/engine/src/flutter/impeller/compiler/reflector.cc), a UI toolkit for building natively compiled applications for mobile, web, and desktop from a single codebase +- [**Fraunhofer VVenC**](https://github.com/fraunhoferhhi/vvenc), a fast and efficient encoder for the Versatile Video Coding (H.266/VVC) standard +- [**GitHub CodeQL**](https://github.com/github/codeql/blob/main/shared/cpp/Diagnostics.h), a code analysis tool used for identifying security vulnerabilities and bugs in software through semantic queries +- [**GoPro ngfx**](https://github.com/gopro/ngfx), a low-level graphics abstraction and profiling framework developed by GoPro +- [**gRPC**](https://github.com/grpc/grpc/blob/master/tools/artifact_gen/utils.h), a high-performance universal remote procedure call framework - [**Hex-Rays**](https://docs.hex-rays.com/user-guide/user-interface/licenses), a reverse engineering toolset for analyzing and decompiling binaries, primarily used for security research and vulnerability analysis - [**ImHex**](https://github.com/WerWolv/ImHex), a hex editor designed for reverse engineering, providing advanced features for data analysis and manipulation +- [**Intel GITS**](https://github.com/intel/gits), a tool for capturing and replaying graphics API calls for debugging and performance analysis - [**Intel GPA Framework**](https://intel.github.io/gpasdk-doc/src/licenses.html), a suite of cross-platform tools for capturing, analyzing, and optimizing graphics applications across different APIs - [**Intopix**](https://www.intopix.com/software-licensing), a provider of advanced image processing and compression solutions used in software development and AV workflows - [**Java SE**](https://www.oracle.com/a/tech/docs/jdk8-lium.pdf), the core Java platform that provides the libraries and runtime needed to build and run general-purpose Java applications -- [**MKVToolNix**](https://mkvtoolnix.download/doc/README.md), a set of tools for creating, editing, and inspecting MKV (Matroska) multimedia container files - [**Meta Yoga**](https://github.com/facebook/yoga), a layout engine that facilitates flexible and efficient user interface design across multiple platforms -- [**NVIDIA Nsight Compute**](https://docs.nvidia.com/nsight-compute/2022.2/pdf/CopyrightAndLicenses.pdf), a performance analysis tool for CUDA applications that provides detailed insights into GPU performance metrics +- [**MKVToolNix**](https://mkvtoolnix.download/doc/README.md), a set of tools for creating, editing, and inspecting MKV (Matroska) multimedia container files +- [**MRTech IFF SDK**](https://mr-technologies.com/pub/iff-sdk-manual-2-0-1/iff-sdk-manual-2-0-1.pdf), an image processing SDK for machine vision applications with GPU-accelerated pipelines +- [**Nix**](https://github.com/NixOS/nix/blob/master/src/nix/build.cc), a purely functional package manager - [**Notepad++**](https://github.com/notepad-plus-plus/notepad-plus-plus), a free source code editor that supports various programming languages +- [**NVIDIA Nsight Compute**](https://docs.nvidia.com/nsight-compute/2022.2/pdf/CopyrightAndLicenses.pdf), a performance analysis tool for CUDA applications that provides detailed insights into GPU performance metrics +- [**openFrameworks**](https://github.com/openframeworks/openFrameworks/blob/master/libs/openFrameworks/utils/ofJson.h), a community-developed C++ toolkit for creative coding - [**OpenRGB**](https://gitlab.com/CalcProgrammer1/OpenRGB), an open source RGB lighting control that doesn't depend on manufacturer software - [**OpenTelemetry C++**](https://github.com/open-telemetry/opentelemetry-cpp), a library for collecting and exporting observability data in C++, enabling developers to implement distributed tracing and metrics in their application +- [**Oracle GraalVM**](https://docs.oracle.com/en/graalvm/jdk/21/docs/licensing-information/), a high-performance JDK distribution with ahead-of-time compilation and polyglot runtime support +- [**Philips amp-cucumber-cpp-runner**](https://github.com/philips-software/amp-cucumber-cpp-runner), a behaviour-driven development test runner for embedded C++ software developed at Philips - [**Qt Creator**](https://doc.qt.io/qtcreator/qtcreator-attribution-json-nlohmann.html), an IDE for developing applications using the Qt application framework +- [**Qt for MCUs**](https://doc.qt.io/QtForMCUs/quickultralite-attribution-nlohmann-json.html), a graphics framework for building fluid user interfaces on microcontrollers +- [**React Native**](https://github.com/react/react-native/blob/main/packages/react-native/ReactCxxPlatform/react/devsupport/PackagerConnection.cpp), a framework for building native mobile applications using React - [**Scanbot SDK**](https://docs.scanbot.io/barcode-scanner-sdk/web/third-party-libraries/), a software development kit (SDK) that provides tools for integrating advanced document scanning and barcode scanning capabilities into applications +- [**STMicroelectronics TouchGFX**](https://www.st.com/resource/en/additional_license_terms/additional-license-terms-x-cube-touchgfx.html), a graphical user interface framework shipped with STM32 microcontrollers for building embedded HMIs +- [**swagger-codegen**](https://github.com/swagger-api/swagger-codegen/blob/master/samples/server/petstore/pistache-server/model/Pet.h), a template-driven engine that generates API clients and server stubs from an OpenAPI specification +- [**Swoole**](https://github.com/swoole/swoole-src/blob/master/ext-src/swoole_admin_server.cc), a coroutine-based concurrency engine for PHP +- [**Tracy Profiler**](https://github.com/wolfpld/tracy/blob/master/profiler/src/profiler/TracyLlm.hpp), a real-time frame profiler for games and other applications +- [**WasmEdge**](https://github.com/WasmEdge/WasmEdge/blob/master/plugins/wasi_nn/GGML/tts/tts_core.cpp), a lightweight WebAssembly runtime for edge and cloud workloads +- [**x64dbg**](https://github.com/x64dbg/x64dbg/blob/development/src/cross/remote_table/TableRpcData.h), an open source user mode debugger for Windows, aimed at reverse engineering and malware analysis ## Machine Learning and AI +- [**Alibaba MNN**](https://github.com/alibaba/MNN), a lightweight deep learning inference engine for mobile and embedded devices +- [**AMD Gaia**](https://github.com/amd/gaia), an open-source framework for running generative AI applications locally on AMD hardware +- [**AMD Vitis AI (VAIP)**](https://github.com/amd/vaip), the execution provider stack that runs AI models on AMD Ryzen AI and adaptive computing devices - [**Apple Core ML Tools**](https://github.com/apple/coremltools), a set of tools for converting and configuring machine learning models for deployment in Apple's Core ML framework - [**Avular Mobile Robotics**](https://www.avular.com/licenses/nlohmann-json-3.9.1.txt), a platform for developing and deploying mobile robotics solutions +- [**FunASR**](https://github.com/modelscope/FunASR/blob/main/runtime/http/bin/asr_sessions.h), a speech recognition toolkit for training and deploying end-to-end models - [**Google gemma.cpp**](https://github.com/google/gemma.cpp), a lightweight C++ inference engine designed for running AI models from the Gemma family +- [**Google Magenta The Infinite Crate**](https://github.com/magenta/the-infinite-crate), an open-source generative AI plugin for digital audio workstations from Google's Magenta research team +- [**GPT4All**](https://github.com/nomic-ai/gpt4all/blob/main/gpt4all-chat/src/tool.h), a desktop application for running local large language models on consumer hardware +- [**Huawei MindSpore**](https://github.com/mindspore-ai/mindspore/blob/master/Third_Party_Open_Source_Software_Notice), a deep learning framework for training and inference across device, edge, and cloud +- [**KTransformers**](https://github.com/kvcache-ai/ktransformers/blob/main/archive/csrc/balance_serve/sched/model_config.h), a framework for heterogeneous large language model inference - [**llama.cpp**](https://github.com/ggerganov/llama.cpp), a C++ library designed for efficient inference of large language models (LLMs), enabling streamlined integration into applications +- [**LocalAI**](https://github.com/mudler/LocalAI/blob/master/backend/cpp/ds4/dsml_renderer.cpp), a self-hosted inference engine that exposes local models through an OpenAI-compatible API - [**MLX**](https://github.com/ml-explore/mlx), an array framework for machine learning on Apple Silicon - [**Mozilla llamafile**](https://github.com/Mozilla-Ocho/llamafile), a tool designed for distributing and executing large language models (LLMs) efficiently using a single file format - [**NVIDIA ACE**](https://docs.nvidia.com/ace/latest/index.html), a suite of real-time AI solutions designed for the development of interactive avatars and digital human applications, enabling scalable and sophisticated user interactions +- [**NVIDIA Instant NGP**](https://github.com/NVlabs/instant-ngp/blob/master/src/nerf_loader.cu), an implementation of instant neural graphics primitives for rapid scene reconstruction +- [**NVIDIA TensorRT**](https://github.com/NVIDIA/TensorRT), an SDK for high-performance deep learning inference, including its TensorRT-LLM extension for large language models +- [**NVIDIA TensorRT-LLM**](https://github.com/NVIDIA/TensorRT-LLM/blob/main/cpp/tensorrt_llm/common/safetensors.cpp), a toolkit for optimizing and serving large language model inference on GPUs +- [**ONNX Runtime**](https://github.com/microsoft/onnxruntime), a cross-platform inference and training accelerator for machine learning models +- [**OpenVINO**](https://github.com/openvinotoolkit/openvino), Intel's toolkit for optimizing and deploying deep learning inference across CPUs, GPUs, and NPUs +- [**PaddleOCR**](https://github.com/PaddlePaddle/PaddleOCR/blob/main/deploy/cpp_infer/src/modules/text_detection/result.cc), an optical character recognition toolkit that turns documents and images into structured data +- [**PaddlePaddle**](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/ap/src/axpr/anf_expr.cc), a deep learning framework for distributed training and inference - [**Peer**](https://support.peer.inc/hc/en-us/articles/17261335054235-Licenses), a platform offering personalized AI assistants for interactive learning and creative collaboration +- [**PyTorch**](https://github.com/pytorch/pytorch), a machine learning framework for building and training neural networks, widely used in research and production +- [**Qualcomm AI Engine Direct**](https://github.com/qualcomm/qai-appbuilder), a toolchain for building and running generative AI applications on Snapdragon devices +- [**sherpa-onnx**](https://github.com/k2-fsa/sherpa-onnx/blob/master/sherpa-onnx/csrc/sentence-piece-tokenizer.cc), a speech toolkit for on-device recognition, synthesis and speaker diarization - [**stable-diffusion.cpp**](https://github.com/leejet/stable-diffusion.cpp), a C++ implementation of the Stable Diffusion image generation model - [**TanvasTouch**](https://tanvas.co/tanvastouch-sdk-third-party-acknowledgments), a software development kit (SDK) that enables developers to create tactile experiences on touchscreens, allowing users to feel textures and physical sensations in a digital environment - [**TensorFlow**](https://github.com/tensorflow/tensorflow), a machine learning framework that facilitates the development and training of models, supporting data serialization and efficient data exchange between components +- [**whisper.cpp**](https://github.com/ggml-org/whisper.cpp), a C++ implementation of OpenAI's Whisper automatic speech recognition model ## Scientific Research and Analysis - [**BLACK**](https://www.black-sat.org/en/stable/installation/linux.html), a bounded linear temporal logic (LTL) satisfiability checker +- [**CERN ALICE O2**](https://github.com/AliceO2Group/AliceO2), the online-offline computing framework of the ALICE heavy-ion experiment at the Large Hadron Collider - [**CERN Atlas Athena**](https://gitlab.cern.ch/atlas/athena/-/blob/main/Control/PerformanceMonitoring/PerfMonComps/src/PerfMonMTSvc.h), a software framework used in the ATLAS experiment at the Large Hadron Collider (LHC) for performance monitoring +- [**CERN CMSSW**](https://github.com/cms-sw/cmssw), the offline software framework of the CMS experiment at the Large Hadron Collider +- [**CERN Gaudi**](https://gitlab.cern.ch/gaudi/Gaudi), the event-processing framework used by the LHCb and ATLAS experiments at the Large Hadron Collider - [**ICU**](https://github.com/unicode-org/icu), the International Components for Unicode, a mature library for software globalization and multilingual support - [**KAMERA**](https://github.com/Kitware/kamera), a platform for synchronized data collection and real-time deep learning to map marine species like polar bears and seals, aiding Arctic ecosystem research - [**KiCad**](https://gitlab.com/kicad/code/kicad/-/tree/master/thirdparty/nlohmann_json), a free and open-source software suite for electronic design automation +- [**LLNL ROSE**](https://github.com/llnl/rose), a compiler infrastructure from Lawrence Livermore National Laboratory for building source-to-source program analysis and transformation tools - [**Maple**](https://www.maplesoft.com/support/help/Maple/view.aspx?path=copyright), a symbolic and numeric computing environment for advanced mathematical modeling and analysis - [**MeVisLab**](https://mevislabdownloads.mevis.de/docs/current/MeVis/ThirdParty/Documentation/Publish/ThirdPartyReference/index.html), a software framework for medical image processing and visualization. +- [**MITK**](https://github.com/MITK/MITK), the Medical Imaging Interaction Toolkit, a framework for developing interactive medical image processing software - [**OpenPMD API**](https://openpmd-api.readthedocs.io/en/0.8.0-alpha/backends/json.html), a versatile programming interface for accessing and managing scientific data, designed to facilitate the efficient storage, retrieval, and sharing of simulation data across various applications and platforms +- [**ORNL DataFed**](https://github.com/ORNL/DataFed), a federated scientific data management system developed at Oak Ridge National Laboratory - [**ParaView**](https://github.com/Kitware/ParaView), an open-source tool for large-scale data visualization and analysis across various scientific domains - [**QGIS**](https://gitlab.b-data.ch/qgis/qgis/-/blob/backport-57658-to-release-3_34/external/nlohmann/json.hpp), a free and open-source geographic information system (GIS) application that allows users to create, edit, visualize, and analyze geospatial data across a variety of formats -- [**VTK**](https://github.com/Kitware/VTK), a software library for 3D computer graphics, image processing, and visualization +- [**Sandia InterSpec**](https://github.com/sandialabs/InterSpec), spectral radiation analysis software from Sandia National Laboratories for identifying radioactive isotopes - [**VolView**](https://github.com/Kitware/VolView), a lightweight application for interactive visualization and analysis of 3D medical imaging data. +- [**VTK**](https://github.com/Kitware/VTK), a software library for 3D computer graphics, image processing, and visualization ## Business and Productivity Software - [**ArcGIS PRO**](https://www.esri.com/content/dam/esrisites/en-us/media/legal/open-source-acknowledgements/arcgis-pro-2-8-attribution-report.html), a desktop geographic information system (GIS) application developed by Esri for mapping and spatial analysis - [**Autodesk Desktop**](https://damassets.autodesk.net/content/dam/autodesk/www/Company/legal-notices-trademarks/autodesk-desktop-platform-components/internal-autodesk-components-web-page-2023.pdf), a software platform developed by Autodesk for creating and managing desktop applications and services - [**Check Point**](https://www.checkpoint.com/about-us/copyright-and-trademarks/), a cybersecurity company specializing in threat prevention and network security solutions, offering a range of products designed to protect enterprises from cyber threats and ensure data integrity +- [**EasyEffects**](https://github.com/wwmm/easyeffects/blob/master/src/presets_manager.hpp), an audio effects processor for PipeWire offering limiting, compression and equalization +- [**espanso**](https://github.com/espanso/espanso/blob/dev/espanso-ui/src/win32/native.cpp), a cross-platform text expander +- [**Karabiner-Elements**](https://github.com/pqrs-org/Karabiner-Elements/blob/main/src/share/app_icon.hpp), a keyboard customizer for macOS +- [**MacType**](https://github.com/snowie2000/mactype/blob/directwrite/settings.h), a font rendering engine for Windows +- [**magicplan**](https://help.magicplan.app/acknowledgments), a mobile application for creating floor plans and interior designs using augmented reality - [**Microsoft Office for Mac**](https://officecdnmac.microsoft.com/pr/legal/mac/OfficeforMacAttributions.html), a suite of productivity applications developed by Microsoft for macOS, including tools for word processing, spreadsheets, and presentations - [**Microsoft Teams**](https://www.microsoft.com/microsoft-teams/), a team collaboration application offering workspace chat and video conferencing, file storage, and integration of proprietary and third-party applications and services +- [**MuseScore**](https://github.com/musescore/MuseScore), a free and open-source music notation and composition application +- [**NanaZip**](https://github.com/M2Team/NanaZip/blob/main/NanaZip.Codecs/NanaZip.Codecs.Archive.ElectronAsar.cpp), a 7-Zip derivative built for modern Windows - [**Nexthink Infinity**](https://docs.nexthink.com/legal/services-terms/experience-open-source-software-licenses/infinity-2022.8-software-licenses), a digital employee experience management platform for monitoring and improving IT performance - [**Sophos Connect Client**](https://docs.sophos.com/nsg/licenses/SophosConnect/SophosConnectAttribution.html), a secure VPN client from Sophos that allows remote users to connect to their corporate network, ensuring secure access to resources and data - [**Stonebranch**](https://stonebranchdocs.atlassian.net/wiki/spaces/UA77/pages/799545647/Licenses+for+Third-Party+Libraries), a cloud-based cybersecurity solution that integrates backup, disaster recovery, and cybersecurity features to protect data and ensure business continuity for organizations - [**Tablecruncher**](https://tablecruncher.com/), a data analysis tool that allows users to import, analyze, and visualize spreadsheet data, offering interactive features for better insights and decision-making -- [**magicplan**](https://help.magicplan.app/acknowledgments), a mobile application for creating floor plans and interior designs using augmented reality +- [**VNote**](https://github.com/vnotex/vnote/blob/master/src/core/services/notebookcoreservice.cpp), a Markdown-based note-taking application written in C++ ## Databases and Big Data - [**ADIOS2**](https://code.ornl.gov/ecpcitest/adios2/-/tree/pr4285_FFSUpstream/thirdparty/nlohmann_json?ref_type=heads), a data management framework designed for high-performance input and output operations +- [**Apache Doris**](https://github.com/apache/doris/blob/master/be/src/runtime/be_proc_monitor.cpp), a real-time analytical database for high-concurrency queries +- [**Claris FileMaker Server**](https://www.claris.com/company/legal/docs/acknowledgements/filemaker-server-macwin/claris_fms2025_acknowledgements_en.pdf), the server platform hosting FileMaker custom apps and databases, developed by Apple subsidiary Claris +- [**ClickHouse**](https://github.com/ClickHouse/ClickHouse), a column-oriented database management system for real-time analytical queries - [**Cribl Stream**](https://docs.cribl.io/stream/third-party-current-list/), a real-time data processing platform that enables organizations to collect, route, and transform observability data, enhancing visibility and insights into their systems - [**DB Browser for SQLite**](https://github.com/sqlitebrowser/sqlitebrowser), a visual open-source tool for creating, designing, and editing SQLite database files +- [**Manticore Search**](https://github.com/manticoresoftware/manticoresearch/blob/main/src/searchdhttpcompat.cpp), a database for search, offering full-text and vector queries +- [**Milvus**](https://github.com/milvus-io/milvus/blob/master/internal/core/src/query/PlanImpl.h), a cloud-native vector database built for embedding similarity search +- [**MongoDB**](https://github.com/mongodb/mongo/blob/master/src/mongo/replay/config_handler.cpp), a general-purpose document database - [**MySQL Connector/C++**](https://docs.oracle.com/cd/E17952_01/connector-cpp-9.1-license-com-en/license-opentelemetry-cpp-com.html), a C++ library for connecting and interacting with MySQL databases - [**MySQL NDB Cluster**](https://downloads.mysql.com/docs/licenses/cluster-9.0-com-en.pdf), a distributed database system that provides high availability and scalability for MySQL databases - [**MySQL Shell**](https://downloads.mysql.com/docs/licenses/mysql-shell-8.0-gpl-en.pdf), an advanced client and code editor for interacting with MySQL servers, supporting SQL, Python, and JavaScript -- [**PrestoDB**](https://github.com/prestodb/presto), a distributed SQL query engine designed for large-scale data analytics, originally developed by Facebook +- [**PrestoDB**](https://github.com/prestodb/presto/blob/master/presto-native-execution/presto_cpp/main/Announcer.cpp), a distributed SQL query engine designed for large-scale data analytics, originally developed by Facebook - [**ROOT Data Analysis Framework**](https://root.cern/doc/v614/classnlohmann_1_1basic__json.html), an open-source data analysis framework widely used in high-energy physics and other fields for data processing and visualization +- [**Typesense**](https://github.com/typesense/typesense/blob/v31/include/join.h), an open source typo-tolerant search engine +- [**Vearch**](https://github.com/jd-opensource/vearch), a distributed vector database developed at JD.com for similarity search and retrieval-augmented generation - [**WiredTiger**](https://github.com/wiredtiger/wiredtiger), a high-performance storage engine for databases, offering support for compression, concurrency, and checkpointing ## Simulation and Modeling +- [**Adobe Lagrange**](https://github.com/adobe/lagrange), a geometry processing library developed by Adobe for mesh manipulation and analysis - [**Arcturus HoloSuite**](https://www.datocms-assets.com/104353/1698904597-holosuite-third-party-software-credits-and-attributions-2.pdf), a software toolset for capturing, editing, and streaming volumetric video, featuring advanced compression technologies for high-quality 3D content creation - [**azul**](https://pure.tudelft.nl/ws/files/85338589/tgis.12673.pdf), a fast and efficient 3D city model viewer designed for visualizing urban environments and spatial data +- [**Bambu Studio**](https://github.com/bambulab/BambuStudio), a slicing and print management application for Bambu Lab 3D printers - [**Blender**](https://projects.blender.org/blender/blender/search?q=nlohmann), a free and open-source 3D creation suite for modeling, animation, rendering, and more - [**cpplot**](https://cpplot.readthedocs.io/en/latest/library_api/function_eigen_8h_1ac080eac0541014c5892a55e41bf785e6.html), a library for creating interactive graphs and charts in C++, which can be viewed in web browsers - [**Foundry Nuke**](https://learn.foundry.com/nuke/content/misc/studio_third_party_libraries.html), a powerful node-based digital compositing and visual effects application used in film and television post-production +- [**FreeCAD**](https://github.com/FreeCAD/FreeCAD), a free and open-source parametric 3D CAD modeler for product design and engineering - [**GAMS**](https://www.gams.com/47/docs/THIRDPARTY.html), a high-performance mathematical modeling system for optimization and decision support +- [**Keysight WirelessPro**](https://docs.keysight.com/display/engdocwirelesspro/WirelessPro+2026+Release+Notes), a simulation platform for 5G, 5G-Advanced, and 6G cellular network research - [**Kitware SMTK**](https://github.com/Kitware/SMTK), a software toolkit for managing simulation models and workflows in scientific and engineering applications - [**M-Star**](https://docs.mstarcfd.com/3_Licensing/thirdparty-licenses.html), a computational fluid dynamics software for simulating and analyzing fluid flow - [**MapleSim CAD Toolbox**](https://www.maplesoft.com/support/help/MapleSim/view.aspx?path=CADToolbox/copyright), a software extension for MapleSim that integrates CAD models, allowing users to import, manipulate, and analyze 3D CAD data within the MapleSim environment for enhanced modeling and simulation +- [**Microsoft AirSim**](https://github.com/microsoft/AirSim/blob/main/AirLib/include/common/Settings.hpp), a simulator for autonomous vehicles and drones built on Unreal Engine - [**NVIDIA Omniverse**](https://docs.omniverse.nvidia.com/composer/latest/common/product-licenses/usd-explorer/usd-explorer-2023.2.0-licenses-manifest.html), a platform for 3D content creation and collaboration that enables real-time simulations and interactive experiences across various industries +- [**OpenSCAD**](https://github.com/openscad/openscad/blob/master/src/core/AIClient.cc), a script-driven solid 3D CAD modeller +- [**OrcaSlicer**](https://github.com/SoftFever/OrcaSlicer), an open-source slicer supporting a wide range of consumer 3D printers - [**Pixar Renderman**](https://rmanwiki-26.pixar.com/space/REN26/19662083/Legal+Notice), a photorealistic 3D rendering software developed by Pixar, widely used in the film industry for creating high-quality visual effects and animations +- [**PrusaSlicer**](https://github.com/prusa3d/PrusaSlicer), the slicing software developed by Prusa Research for its 3D printers - [**ROS - Robot Operating System**](http://docs.ros.org/en/noetic/api/behaviortree_cpp/html/json_8hpp_source.html), a set of software libraries and tools that assist in developing robot applications - [**UBS**](https://www.ubs.com/), a multinational financial services and banking company @@ -161,18 +278,31 @@ the result of an internet search. If you know further customers of the library, - [**Acronis Cyber Protect Cloud**](https://care.acronis.com/s/article/59533-Third-party-software-used-in-Acronis-Cyber-Protect-Cloud?language=en_US), an all-in-one data protection solution that combines backup, disaster recovery, and cybersecurity to safeguard business data from threats like ransomware - [**Baereos**](https://gitlab.tiger-computing.co.uk/packages/bareos/-/blob/tiger/bullseye/third-party/CLI11/examples/json.cpp), a backup solution that provides data protection and recovery options for various environments, including physical and virtual systems - [**Bitdefender Home Scanner**](https://www.bitdefender.de/site/Main/view/home-scanner-open-source.html), a tool from Bitdefender that scans devices for malware and security threats, providing a safeguard against potential online dangers +- [**Cisco MLS++**](https://github.com/cisco/mlspp), an implementation of the Messaging Layer Security protocol for end-to-end encrypted group messaging - [**Citrix Provisioning**](https://docs.citrix.com/en-us/provisioning/2203-ltsr/downloads/pvs-third-party-notices-2203.pdf), a solution that streamlines the delivery of virtual desktops and applications by allowing administrators to manage and provision resources efficiently across multiple environments - [**Citrix Virtual Apps and Desktops**](https://docs.citrix.com/en-us/citrix-virtual-apps-desktops/2305/downloads/third-party-notices-apps-and-desktops.pdf), a solution from Citrix that delivers virtual apps and desktops - [**Cyberarc**](https://docs.cyberark.com/Downloads/Legal/Privileged%20Session%20Manager%20for%20SSH%20Third-Party%20Notices.pdf), a security solution that specializes in privileged access management, enabling organizations to control and monitor access to critical systems and data, thereby enhancing overall cybersecurity posture +- [**Deutsche Telekom sysrepo-plugins**](https://github.com/telekom/sysrepo-plugins), a collection of YANG datastore plugins used to manage network devices - [**Egnyte Desktop**](https://helpdesk.egnyte.com/hc/en-us/articles/360007071732-Third-Party-Software-Acknowledgements), a secure cloud storage solution designed for businesses, enabling file sharing, collaboration, and data management across teams while ensuring compliance and data protection - [**Elster**](https://www.secunet.com/en/about-us/press/article/elstersecure-bietet-komfortablen-login-ohne-passwort-dank-secunet-protect4use), a digital platform developed by German tax authorities for secure and efficient electronic tax filing and management using secunet protect4use +- [**Envoy**](https://github.com/envoyproxy/envoy), a cloud-native edge and service proxy that forms the data plane of many service meshes - [**Ethereum Solidity**](https://github.com/ethereum/solidity), a high-level, object-oriented programming language designed for implementing smart contracts on the Ethereum platform +- [**gVisor**](https://github.com/google/gvisor), an application kernel that provides a secure sandbox for running untrusted containers +- [**IBM Storage Virtualize**](https://public.dhe.ibm.com/systems/support/warranty/pdfs/stgoilc/SV_for_FS_7300_v8_7_0_Base_OILC.pdf), the software powering IBM FlashSystem enterprise storage arrays - [**Inciga**](https://fossies.org/linux/icinga2/third-party/nlohmann_json/json.hpp), a monitoring tool for IT infrastructure, designed to provide insights into system performance and availability through customizable dashboards and alerts - [**Intel Accelerator Management Daemon for VMware ESXi**](https://downloadmirror.intel.com/772507/THIRD-PARTY.txt), a management tool designed for monitoring and controlling Intel hardware accelerators within VMware ESXi environments, optimizing performance and resource allocation - [**Juniper Identity Management Service**](https://www.juniper.net/documentation/us/en/software/jims/jims-guide/jims-guide.pdf) +- [**Meta FBOSS**](https://github.com/facebook/fboss), the software stack that controls the network switches in Meta's data centers - [**Microsoft Azure IoT SDK**](https://library.e.abb.com/public/2779c5f85f30484192eb3cb3f666a201/IP%20Gateway%20Open%20License%20Declaration_9AKK108467A4095_Rev_C.pdf), a collection of tools and libraries to help developers connect, build, and deploy Internet of Things (IoT) solutions on the Azure cloud platform +- [**Microsoft Confidential Consortium Framework**](https://github.com/microsoft/CCF), a framework for building secure, highly available applications on trusted execution environments - [**Microsoft WinGet**](https://github.com/microsoft/winget-cli), a command-line utility included in the Windows Package Manager +- [**Mitsubishi Electric SECS/GEM**](https://dl.mitsubishielectric.com/dl/fa/document/manual/plc/sh082483eng/sh082483engi.pdf), the semiconductor equipment communication software running on Mitsubishi Electric C Controller and C intelligent function modules +- [**Moxa**](https://www.moxa.com/getmedia/fbe2a0c7-8dda-4b5b-a501-15e45adebb1f/moxa-foss-statement-for-da-720-series-win-10-ltsc-21h2-declaration-v1.0.pdf), a provider of industrial networking, computing, and automation infrastructure - [**plexusAV**](https://www.sisme.com/media/10994/manual_plexusav-p-avn-4-form8244-c.pdf), a high-performance AV-over-IP transceiver device capable of video encoding and decoding using the IPMX standard - [**Pointr**](https://docs-dev.pointr.tech/docs/8.x/Developer%20Portal/Open%20Source%20Licenses/), a platform for indoor positioning and navigation solutions, offering tools and SDKs for developers to create location-based applications - [**secunet protect4use**](https://www.secunet.com/en/about-us/press/article/elstersecure-bietet-komfortablen-login-ohne-passwort-dank-secunet-protect4use), a secure, passwordless multifactor authentication solution that transforms smartphones into digital keyrings, ensuring high security for online services and digital identities - [**Sencore MRD 7000**](https://www.foccusdigital.com/wp-content/uploads/2025/03/MRD-7000-Manual-8175V.pdf), a professional multi-channel receiver and decoder supporting UHD and HD stream decoding +- [**Siemens SINEC**](https://cache.industry.siemens.com/dl/files/917/109974917/att_1298783/v2/OSS_SINEC-NMS_99.pdf), a family of network management and infrastructure services for industrial networks +- [**Toshiba Industrial Servers**](https://www.global.toshiba/content/dam/toshiba/jp/products-solutions/industrial/computer/product/server/fs20000r/pdf/FS20000R_OSS_License_6E8C5817_rev0.pdf), the FS20000R series of industrial servers for factory automation and control systems +- [**Wazuh**](https://github.com/wazuh/wazuh/blob/main/src/data_provider/src/sysInfo.cpp), a security platform for threat detection, integrity monitoring and incident response +- [**ZeroTier**](https://github.com/zerotier/ZeroTierOne/blob/dev/osdep/OSUtils.hpp), a software-defined networking service that creates virtual Ethernet networks diff --git a/docs/mkdocs/docs/home/exceptions.md b/docs/mkdocs/docs/home/exceptions.md index 7c7d22e23..8c6649ef2 100644 --- a/docs/mkdocs/docs/home/exceptions.md +++ b/docs/mkdocs/docs/home/exceptions.md @@ -933,6 +933,34 @@ BSON stores the length of documents, arrays, strings, and binary values in a sig [`to_bson`](../api/basic_json/to_bson.md) produced documents with negative length prefixes that [`from_bson`](../api/basic_json/from_bson.md) rejected. +### json.exception.out_of_range.413 + +A JSON Patch `remove` operation cannot be applied because the target location's parent is neither an object nor an array. Per [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902), a `remove` target must reference a member of an existing object or an element of an existing array; a primitive value (string, number, boolean, etc.) or `null` has no members or elements to remove. + +!!! failure "Example message" + + ``` + cannot remove value: the JSON Patch 'remove' target's parent is of type number, but must be an object or array + ``` + +!!! note + + This exception was added in version 3.13.0. Before that, this situation was silently ignored (the `remove` operation had no effect). + +### json.exception.out_of_range.414 + +A JSON Patch `move` operation's `"from"` location is a proper prefix of its `"path"` location. Per [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902) (section 4.4), a location cannot be moved into one of its own children. + +!!! failure "Example message" + + ``` + cannot move value: 'from' path '/0' is a proper prefix of 'path' '/0/0' + ``` + +!!! note + + This exception was added in version 3.13.0. Before that, this situation could succeed with a corrupted result: for an array target, removing the "from" element before the "add" step shifted subsequent indices, so "path" silently re-resolved to a different element than intended. + ## Further exceptions This exception is thrown in case of errors that cannot be classified with the diff --git a/docs/mkdocs/docs/images/customers.png b/docs/mkdocs/docs/images/customers.png index dbfa19148..146056f20 100644 Binary files a/docs/mkdocs/docs/images/customers.png and b/docs/mkdocs/docs/images/customers.png differ diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index 6856a0965..11e40f5f4 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -398,6 +398,17 @@ inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin) } } +template +auto from_json_object_reserve(ConstructibleObjectType& obj, typename ConstructibleObjectType::size_type size, priority_tag<1> /*unused*/) +-> decltype(obj.reserve(size), void()) +{ + obj.reserve(size); +} + +template +inline void from_json_object_reserve(ConstructibleObjectType& /*obj*/, std::size_t /*size*/, priority_tag<0> /*unused*/) +{} + template::value, int> = 0> inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) @@ -409,6 +420,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) ConstructibleObjectType ret; const auto* inner_object = j.template get_ptr(); + from_json_object_reserve(ret, inner_object->size(), priority_tag<1> {}); for (const auto& p : *inner_object) { ret.emplace(p.first, p.second.template get()); diff --git a/include/nlohmann/detail/conversions/to_chars.hpp b/include/nlohmann/detail/conversions/to_chars.hpp index 70fb9b933..c0945ab9e 100644 --- a/include/nlohmann/detail/conversions/to_chars.hpp +++ b/include/nlohmann/detail/conversions/to_chars.hpp @@ -1075,8 +1075,8 @@ char* to_chars(char* first, const char* last, FloatType value) } #ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" + JSON_HEDLEY_DIAGNOSTIC_PUSH + JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal") #endif if (value == 0) // +-0 { @@ -1087,7 +1087,7 @@ char* to_chars(char* first, const char* last, FloatType value) return first; } #ifdef __GNUC__ -#pragma GCC diagnostic pop + JSON_HEDLEY_DIAGNOSTIC_POP #endif JSON_ASSERT(last - first >= std::numeric_limits::max_digits10); diff --git a/include/nlohmann/detail/exceptions.hpp b/include/nlohmann/detail/exceptions.hpp index cb87bb93e..3e5b45101 100644 --- a/include/nlohmann/detail/exceptions.hpp +++ b/include/nlohmann/detail/exceptions.hpp @@ -33,8 +33,8 @@ // code stumbling over this. See https://github.com/nlohmann/json/issues/4087 // for a discussion. #if defined(__clang__) - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wweak-vtables" + JSON_HEDLEY_DIAGNOSTIC_PUSH + JSON_HEDLEY_PRAGMA(clang diagnostic ignored "-Wweak-vtables") #endif NLOHMANN_JSON_NAMESPACE_BEGIN @@ -287,5 +287,5 @@ class other_error : public exception NLOHMANN_JSON_NAMESPACE_END #if defined(__clang__) - #pragma clang diagnostic pop + JSON_HEDLEY_DIAGNOSTIC_POP #endif diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index 0edd64288..bc31337f9 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -178,10 +178,11 @@ class lexer : public lexer_base public: using token_type = typename lexer_base::token_type; - explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept + explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false, bool discard_number_values_ = false) noexcept : ia(std::move(adapter)) , ignore_comments(ignore_comments_) , decimal_point_char(static_cast(get_decimal_point())) + , discard_number_values(discard_number_values_) {} // deleted because of pointer members @@ -1465,6 +1466,58 @@ scan_number_done: */ token_type convert_number(token_type number_type, std::size_t mantissa_end) { + // If the caller does not need the converted value (only whether the + // input is syntactically valid; see json_sax_acceptor/accept()), an + // unsigned/integer token can be reported without calling + // strtoull()/strtoll() at all, *provided* we can already tell from + // the digit count alone that the conversion cannot overflow 64 bits. + // Such tokens are always finite and are accepted unconditionally by + // the parser regardless of their actual value (parser::sax_parse_internal() + // never checks finiteness for value_unsigned/value_integer), so the + // classification below is all that is needed. + // + // A decimal number with up to 18 digits is always representable in + // both std::uint64_t and std::int64_t (18 nines is ~1e18, well below + // both UINT64_MAX ~1.8e19 and INT64_MAX ~9.2e18), so strtoull()/strtoll() + // could not have set errno to ERANGE for it. Numbers with more digits + // (rare in practice) fall through to the exact code below, unchanged, + // so their handling -- including reclassification to value_float when + // the value overflows 64 bits, and rejection when it is not even + // finite as a double -- is bit-for-bit identical to before this + // optimization. + // + // Note this reasons about std::uint64_t/std::int64_t, not about + // number_unsigned_t/number_integer_t (BasicJsonType's own, possibly + // narrower, template parameters -- e.g. std::uint32_t). That is fine + // *only* because discard_number_values is exclusively set by + // accept() (see json.hpp), and accept() always parses through the + // library's own json_sax_acceptor -- never a user-supplied SAX + // consumer -- whose number_unsigned()/number_integer()/number_float() + // callbacks unconditionally discard their argument and return true. + // So for every caller that can reach this branch, neither the token + // classification below nor the eventual (possibly narrowed, and on + // this fast path left stale/unset) value_unsigned/value_integer is + // ever consulted -- an unsigned/integer token is accepted outright, + // and even a >18-digit token that this fast path deliberately falls + // through for is, once reclassified to value_float, still finite + // (and thus accepted) for any digit count that fits in number_unsigned_t + // or number_integer_t regardless of that type's width. If this + // function is ever taught to run with discard_number_values true for + // a caller that *does* read the converted value, this reasoning (and + // the fast path below) would need to be revisited. + if (discard_number_values) + { + constexpr std::size_t safe_digit_count = 18; + if (number_type == token_type::value_unsigned && token_buffer.size() <= safe_digit_count) + { + return token_type::value_unsigned; + } + if (number_type == token_type::value_integer && token_buffer.size() - 1 <= safe_digit_count) + { + return token_type::value_integer; + } + } + const char* const num_begin = token_buffer.data(); const char* const num_end = num_begin + token_buffer.size(); @@ -1723,8 +1776,7 @@ scan_number_done: */ char_int_type get() { - ++position.chars_read_total; - ++position.chars_read_current_line; + advance_position(); if (next_unget) { @@ -1736,6 +1788,23 @@ scan_number_done: current = ia.get_character(); } + return track_after_read(); + } + + /// shared head of get() / get_ignoring_pending_unget(): bump the + /// per-character position counters (line-count-on-'\n' bookkeeping is + /// handled afterwards, in track_after_read(), once `current` is known) + void advance_position() noexcept + { + ++position.chars_read_total; + ++position.chars_read_current_line; + } + + /// shared tail of get() / get_ignoring_pending_unget(): capture the + /// character for error messages (if needed) and update line/column + /// bookkeeping for the character now in `current` + char_int_type track_after_read() + { // seekable adapters reconstruct the token lazily on error (see // get_token_string), so the eager per-character copy is skipped capture_char(std::integral_constant {}); @@ -1752,6 +1821,29 @@ scan_number_done: return current; } + /*! + @brief like get(), but for call sites that can prove no unget() is pending + + get() has to check the `next_unget` flag on every call, because a + previous token may have ended with unget() (e.g. scan_number() always + ungets the character that terminated the number, so the next call to + scan() can see it again). skip_whitespace() reads that first, + possibly-ungotten character via a plain get(), but every further + character it reads is guaranteed to be a fresh read: nothing between + those calls invokes unget(). This variant skips the (otherwise always + false) next_unget branch for those calls; it is not a general + replacement for get(). + */ + char_int_type get_ignoring_pending_unget() + { + JSON_ASSERT(!next_unget); + + advance_position(); + current = ia.get_character(); + + return track_after_read(); + } + /// seekable adapter: nothing to capture, the token is rebuilt on error void capture_char(std::true_type /*lazy*/) const noexcept {} @@ -1953,13 +2045,37 @@ scan_number_done: return true; } + /// whether `current` is one of the four JSON whitespace characters + bool current_is_whitespace() const noexcept + { + return current == ' ' || current == '\t' || current == '\n' || current == '\r'; + } + void skip_whitespace() { + // the first character may be a pending unget() left over from the + // previous token (see get_ignoring_pending_unget()); every + // subsequent character read by this loop is guaranteed fresh, since + // nothing below calls unget() + get(); + + if (!current_is_whitespace()) + { + return; + } + + // this is written as an if-guarded do-while (rather than a plain + // while loop) because that shape is what lets both GCC and Clang + // keep the input adapter's read pointer in a register across + // iterations; the equivalent while-loop measurably defeated that + // optimization in testing, turning long whitespace runs (e.g. the + // indentation of pretty-printed JSON) from a register-only loop + // into one that reloads the pointer from memory every character do { - get(); + get_ignoring_pending_unget(); } - while (current == ' ' || current == '\t' || current == '\n' || current == '\r'); + while (current_is_whitespace()); } token_type scan() @@ -2099,6 +2215,13 @@ scan_number_done: const char_int_type decimal_point_char = '.'; /// the position of the decimal point in the input std::size_t decimal_point_position = std::string::npos; + + /// whether the caller (e.g. accept()/json_sax_acceptor) only needs the + /// token classification and never looks at the converted numeric value; + /// when set, scan_number() may skip strtoull()/strtoll() for + /// value_unsigned/value_integer tokens whose digit count guarantees they + /// fit into 64 bits (see scan_number()) + const bool discard_number_values = false; }; } // namespace detail diff --git a/include/nlohmann/detail/input/parser.hpp b/include/nlohmann/detail/input/parser.hpp index ecc78eb72..a45ee4a0a 100644 --- a/include/nlohmann/detail/input/parser.hpp +++ b/include/nlohmann/detail/input/parser.hpp @@ -72,9 +72,10 @@ class parser parser_callback_t cb = nullptr, const bool allow_exceptions_ = true, const bool ignore_comments = false, - const bool ignore_trailing_commas_ = false) + const bool ignore_trailing_commas_ = false, + const bool discard_number_values_ = false) : callback(std::move(cb)) - , m_lexer(std::move(adapter), ignore_comments) + , m_lexer(std::move(adapter), ignore_comments, discard_number_values_) , allow_exceptions(allow_exceptions_) , ignore_trailing_commas(ignore_trailing_commas_) { diff --git a/include/nlohmann/detail/iterators/iteration_proxy.hpp b/include/nlohmann/detail/iterators/iteration_proxy.hpp index 99246d120..c8aa50dd2 100644 --- a/include/nlohmann/detail/iterators/iteration_proxy.hpp +++ b/include/nlohmann/detail/iterators/iteration_proxy.hpp @@ -18,6 +18,7 @@ #endif #include +#include #include #include #include @@ -206,10 +207,10 @@ NLOHMANN_JSON_NAMESPACE_END namespace std { +// Fix: https://github.com/nlohmann/json/issues/1401 #if defined(__clang__) - // Fix: https://github.com/nlohmann/json/issues/1401 - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wmismatched-tags" + JSON_HEDLEY_DIAGNOSTIC_PUSH + JSON_HEDLEY_PRAGMA(clang diagnostic ignored "-Wmismatched-tags") #endif template class tuple_size<::nlohmann::detail::iteration_proxy_value> // NOLINT(cert-dcl58-cpp) @@ -224,7 +225,7 @@ class tuple_element> ::nlohmann::detail::iteration_proxy_value> ())); }; #if defined(__clang__) - #pragma clang diagnostic pop + JSON_HEDLEY_DIAGNOSTIC_POP #endif } // namespace std diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index 576ba62cc..247c5babb 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -748,6 +748,20 @@ class json_pointer } } + // the reference token consists only of digits at this point (cf. checks + // above); however, its numeric value might not be representable, in which + // case array_index() would throw out_of_range.404/410 -- contains() must + // not throw (see #5395), so such a reference token is treated as "not found" + errno = 0; // strtoull() does not reset errno on success + char* p_end = nullptr; // NOLINT(misc-const-correctness) + const unsigned long long magnitude = std::strtoull(reference_token.c_str(), &p_end, 10); // NOLINT(runtime/int) + if (JSON_HEDLEY_UNLIKELY(errno == ERANGE // the value exceeds ULLONG_MAX + || magnitude >= static_cast((std::numeric_limits::max)()))) // NOLINT(runtime/int) + { + // the array index cannot be represented as size_type + return false; + } + const auto idx = array_index(reference_token); if (idx >= ptr->size()) { diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index e636c6d84..496c733d1 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -1668,6 +1668,15 @@ class binary_writer CharType dtype = it->second; key = "_ArraySize_"; + // the dimensions are written verbatim as the header length below, so a + // value that is not an array cannot produce a valid one: null emits 'Z' + // and an object emits '{', neither of which a reader accepts after '#'. + // Such an object is not a valid ndarray and falls back to a plain object. + if (!value.at(key).is_array()) + { + return true; + } + std::size_t len = (value.at(key).empty() ? 0 : 1); for (const auto& el : value.at(key)) { @@ -1841,8 +1850,8 @@ class binary_writer void write_compact_float(const number_float_t n, detail::input_format_t format) { #ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" + JSON_HEDLEY_DIAGNOSTIC_PUSH + JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal") #endif if (!std::isfinite(n) || ((static_cast(n) >= static_cast(std::numeric_limits::lowest()) && static_cast(n) <= static_cast((std::numeric_limits::max)()) && @@ -1861,7 +1870,7 @@ class binary_writer write_number(n); } #ifdef __GNUC__ -#pragma GCC diagnostic pop + JSON_HEDLEY_DIAGNOSTIC_POP #endif } diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index be4ccb90f..fea75d57a 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -164,11 +164,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::parser_callback_tcb = nullptr, const bool allow_exceptions = true, const bool ignore_comments = false, - const bool ignore_trailing_commas = false + const bool ignore_trailing_commas = false, + const bool discard_number_values = false ) { return ::nlohmann::detail::parser(std::move(adapter), - std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas); + std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas, discard_number_values); } private: @@ -1335,6 +1336,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief serialization /// @sa https://json.nlohmann.me/api/basic_json/dump/ + JSON_HEDLEY_WARN_UNUSED_RESULT string_t dump(const int indent = -1, const char indent_char = ' ', const bool ensure_ascii = false, @@ -1357,6 +1359,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return the type of the JSON value (explicit) /// @sa https://json.nlohmann.me/api/basic_json/type/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr value_t type() const noexcept { return m_data.m_type; @@ -1364,6 +1367,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether type is primitive /// @sa https://json.nlohmann.me/api/basic_json/is_primitive/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_primitive() const noexcept { return is_null() || is_string() || is_boolean() || is_number() || is_binary(); @@ -1371,6 +1375,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether type is structured /// @sa https://json.nlohmann.me/api/basic_json/is_structured/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_structured() const noexcept { return is_array() || is_object(); @@ -1378,6 +1383,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is null /// @sa https://json.nlohmann.me/api/basic_json/is_null/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_null() const noexcept { return m_data.m_type == value_t::null; @@ -1385,6 +1391,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is a boolean /// @sa https://json.nlohmann.me/api/basic_json/is_boolean/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_boolean() const noexcept { return m_data.m_type == value_t::boolean; @@ -1392,6 +1399,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is a number /// @sa https://json.nlohmann.me/api/basic_json/is_number/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_number() const noexcept { return is_number_integer() || is_number_float(); @@ -1399,6 +1407,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is an integer number /// @sa https://json.nlohmann.me/api/basic_json/is_number_integer/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_number_integer() const noexcept { return m_data.m_type == value_t::number_integer || m_data.m_type == value_t::number_unsigned; @@ -1406,6 +1415,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is an unsigned integer number /// @sa https://json.nlohmann.me/api/basic_json/is_number_unsigned/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_number_unsigned() const noexcept { return m_data.m_type == value_t::number_unsigned; @@ -1413,6 +1423,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is a floating-point number /// @sa https://json.nlohmann.me/api/basic_json/is_number_float/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_number_float() const noexcept { return m_data.m_type == value_t::number_float; @@ -1420,6 +1431,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is an object /// @sa https://json.nlohmann.me/api/basic_json/is_object/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_object() const noexcept { return m_data.m_type == value_t::object; @@ -1427,6 +1439,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is an array /// @sa https://json.nlohmann.me/api/basic_json/is_array/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_array() const noexcept { return m_data.m_type == value_t::array; @@ -1434,6 +1447,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is a string /// @sa https://json.nlohmann.me/api/basic_json/is_string/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_string() const noexcept { return m_data.m_type == value_t::string; @@ -1441,6 +1455,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is a binary array /// @sa https://json.nlohmann.me/api/basic_json/is_binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_binary() const noexcept { return m_data.m_type == value_t::binary; @@ -1448,6 +1463,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is discarded /// @sa https://json.nlohmann.me/api/basic_json/is_discarded/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_discarded() const noexcept { return m_data.m_type == value_t::discarded; @@ -2779,6 +2795,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief returns the number of occurrences of a key in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/count/ + JSON_HEDLEY_WARN_UNUSED_RESULT size_type count(const typename object_t::key_type& key) const { // return 0 for all nonobject types @@ -2789,6 +2806,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/count/ template::value, int> = 0> + JSON_HEDLEY_WARN_UNUSED_RESULT size_type count(KeyType && key) const { // return 0 for all nonobject types @@ -2797,6 +2815,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief check the existence of an element in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/contains/ + JSON_HEDLEY_WARN_UNUSED_RESULT bool contains(const typename object_t::key_type& key) const { return is_object() && m_data.m_value.object->find(key) != m_data.m_value.object->end(); @@ -2806,6 +2825,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/contains/ template::value, int> = 0> + JSON_HEDLEY_WARN_UNUSED_RESULT bool contains(KeyType && key) const { return is_object() && m_data.m_value.object->find(std::forward(key)) != m_data.m_value.object->end(); @@ -2813,12 +2833,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief check the existence of an element in a JSON object given a JSON pointer /// @sa https://json.nlohmann.me/api/basic_json/contains/ + JSON_HEDLEY_WARN_UNUSED_RESULT bool contains(const json_pointer& ptr) const { return ptr.contains(this); } template::value, int> = 0> + JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) bool contains(const typename ::nlohmann::json_pointer& ptr) const { @@ -2974,6 +2996,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief checks whether the container is empty. /// @sa https://json.nlohmann.me/api/basic_json/empty/ + JSON_HEDLEY_WARN_UNUSED_RESULT bool empty() const noexcept { switch (m_data.m_type) @@ -3013,6 +3036,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief returns the number of elements /// @sa https://json.nlohmann.me/api/basic_json/size/ + JSON_HEDLEY_WARN_UNUSED_RESULT size_type size() const noexcept { switch (m_data.m_type) @@ -3052,6 +3076,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief returns the maximum possible number of elements /// @sa https://json.nlohmann.me/api/basic_json/max_size/ + JSON_HEDLEY_WARN_UNUSED_RESULT size_type max_size() const noexcept { switch (m_data.m_type) @@ -3770,13 +3795,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec bool operator==(const_reference rhs) const noexcept { #ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" + JSON_HEDLEY_DIAGNOSTIC_PUSH + JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal") #endif const_reference lhs = *this; JSON_IMPLEMENT_OPERATOR( ==, true, false, false) #ifdef __GNUC__ -#pragma GCC diagnostic pop + JSON_HEDLEY_DIAGNOSTIC_POP #endif } @@ -3863,12 +3888,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec friend bool operator==(const_reference lhs, const_reference rhs) noexcept { #ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" + JSON_HEDLEY_DIAGNOSTIC_PUSH + JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal") #endif JSON_IMPLEMENT_OPERATOR( ==, true, false, false) #ifdef __GNUC__ -#pragma GCC diagnostic pop + JSON_HEDLEY_DIAGNOSTIC_POP #endif } @@ -4129,22 +4154,24 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief check if the input is valid JSON /// @sa https://json.nlohmann.me/api/basic_json/accept/ template + JSON_HEDLEY_WARN_UNUSED_RESULT static bool accept(InputType&& i, const bool ignore_comments = false, const bool ignore_trailing_commas = false) { - return parser(detail::input_adapter(std::forward(i)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true); + return parser(detail::input_adapter(std::forward(i)), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true); } /// @brief check if the input is valid JSON (iterator pair, or iterator+sentinel pair for C++20 ranges support) /// @sa https://json.nlohmann.me/api/basic_json/accept/ template::value, int> = 0> + JSON_HEDLEY_WARN_UNUSED_RESULT static bool accept(IteratorType first, SentinelType last, const bool ignore_comments = false, const bool ignore_trailing_commas = false) { - return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true); + return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true); } JSON_HEDLEY_WARN_UNUSED_RESULT @@ -4153,7 +4180,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool ignore_comments = false, const bool ignore_trailing_commas = false) { - return parser(i.get(), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true); + return parser(i.get(), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true); } /// @brief generate SAX events @@ -4239,6 +4266,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return the type as string /// @sa https://json.nlohmann.me/api/basic_json/type_name/ + JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_RETURNS_NON_NULL const char* type_name() const noexcept { @@ -4939,6 +4967,36 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // note erase performs range check parent.erase(json_pointer::template array_index(last_path)); } + else + { + // the parent of a "remove" target must be an object or array + // (see #5396) + JSON_THROW(out_of_range::create(413, detail::concat("cannot remove value: the JSON Patch 'remove' target's parent is of type ", parent.type_name(), ", but must be an object or array"), &parent)); + } + }; + + // RFC 6902 (section 4.4) forbids "from" from being a proper prefix + // of "path" for a "move" operation: a location cannot be moved into + // one of its own children. Compares reference tokens (already + // unescaped by json_pointer's parser) rather than the raw pointer + // strings, since a token may itself contain an escaped '/' or '~' + // that would defeat a naive string-prefix comparison. "from" equal + // to "path" is *not* a proper prefix and must return false. + const auto is_proper_prefix = [](const json_pointer & from, const json_pointer & to) + { + const auto from_size = from.reference_tokens.size(); + if (from_size >= to.reference_tokens.size()) + { + return false; + } + for (std::size_t i = 0; i < from_size; ++i) + { + if (!(from.reference_tokens[i] == to.reference_tokens[i])) + { + return false; + } + } + return true; }; // type check: top level value must be an array @@ -5016,6 +5074,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const auto from_path = get_value("move", "from", true).template get(); json_pointer from_ptr(from_path); + if (JSON_HEDLEY_UNLIKELY(is_proper_prefix(from_ptr, ptr))) + { + JSON_THROW(out_of_range::create(414, detail::concat("cannot move value: 'from' path '", from_path, "' is a proper prefix of 'path' '", path, "'"), &result)); + } + // the "from" location must exist - use at() basic_json const v = result.at(from_ptr); diff --git a/include/nlohmann/thirdparty/hedley/hedley_undef.hpp b/include/nlohmann/thirdparty/hedley/hedley_undef.hpp index 1b8bd4338..e4d9838cb 100644 --- a/include/nlohmann/thirdparty/hedley/hedley_undef.hpp +++ b/include/nlohmann/thirdparty/hedley/hedley_undef.hpp @@ -17,7 +17,7 @@ #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE #undef JSON_HEDLEY_CLANG_HAS_BUILTIN #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE -#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE #undef JSON_HEDLEY_CLANG_HAS_EXTENSION #undef JSON_HEDLEY_CLANG_HAS_FEATURE #undef JSON_HEDLEY_CLANG_HAS_WARNING @@ -108,7 +108,10 @@ #undef JSON_HEDLEY_PELLES_VERSION_CHECK #undef JSON_HEDLEY_PGI_VERSION #undef JSON_HEDLEY_PGI_VERSION_CHECK +#undef JSON_HEDLEY_PRAGMA #undef JSON_HEDLEY_PREDICT +#undef JSON_HEDLEY_PREDICT_FALSE +#undef JSON_HEDLEY_PREDICT_TRUE #undef JSON_HEDLEY_PRINTF_FORMAT #undef JSON_HEDLEY_PRIVATE #undef JSON_HEDLEY_PUBLIC diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 9db298733..96c342d42 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4945,8 +4945,8 @@ NLOHMANN_JSON_NAMESPACE_END // code stumbling over this. See https://github.com/nlohmann/json/issues/4087 // for a discussion. #if defined(__clang__) - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wweak-vtables" + JSON_HEDLEY_DIAGNOSTIC_PUSH + JSON_HEDLEY_PRAGMA(clang diagnostic ignored "-Wweak-vtables") #endif NLOHMANN_JSON_NAMESPACE_BEGIN @@ -5199,7 +5199,7 @@ class other_error : public exception NLOHMANN_JSON_NAMESPACE_END #if defined(__clang__) - #pragma clang diagnostic pop + JSON_HEDLEY_DIAGNOSTIC_POP #endif // #include @@ -5693,6 +5693,17 @@ inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin) } } +template +auto from_json_object_reserve(ConstructibleObjectType& obj, typename ConstructibleObjectType::size_type size, priority_tag<1> /*unused*/) +-> decltype(obj.reserve(size), void()) +{ + obj.reserve(size); +} + +template +inline void from_json_object_reserve(ConstructibleObjectType& /*obj*/, std::size_t /*size*/, priority_tag<0> /*unused*/) +{} + template::value, int> = 0> inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) @@ -5704,6 +5715,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) ConstructibleObjectType ret; const auto* inner_object = j.template get_ptr(); + from_json_object_reserve(ret, inner_object->size(), priority_tag<1> {}); for (const auto& p : *inner_object) { ret.emplace(p.first, p.second.template get()); @@ -5975,6 +5987,8 @@ NLOHMANN_JSON_NAMESPACE_END // #include +// #include + // #include // #include @@ -6204,10 +6218,10 @@ NLOHMANN_JSON_NAMESPACE_END namespace std { +// Fix: https://github.com/nlohmann/json/issues/1401 #if defined(__clang__) - // Fix: https://github.com/nlohmann/json/issues/1401 - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wmismatched-tags" + JSON_HEDLEY_DIAGNOSTIC_PUSH + JSON_HEDLEY_PRAGMA(clang diagnostic ignored "-Wmismatched-tags") #endif template class tuple_size<::nlohmann::detail::iteration_proxy_value> // NOLINT(cert-dcl58-cpp) @@ -6222,7 +6236,7 @@ class tuple_element> ::nlohmann::detail::iteration_proxy_value> ())); }; #if defined(__clang__) - #pragma clang diagnostic pop + JSON_HEDLEY_DIAGNOSTIC_POP #endif } // namespace std @@ -8618,10 +8632,11 @@ class lexer : public lexer_base public: using token_type = typename lexer_base::token_type; - explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept + explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false, bool discard_number_values_ = false) noexcept : ia(std::move(adapter)) , ignore_comments(ignore_comments_) , decimal_point_char(static_cast(get_decimal_point())) + , discard_number_values(discard_number_values_) {} // deleted because of pointer members @@ -9905,6 +9920,58 @@ scan_number_done: */ token_type convert_number(token_type number_type, std::size_t mantissa_end) { + // If the caller does not need the converted value (only whether the + // input is syntactically valid; see json_sax_acceptor/accept()), an + // unsigned/integer token can be reported without calling + // strtoull()/strtoll() at all, *provided* we can already tell from + // the digit count alone that the conversion cannot overflow 64 bits. + // Such tokens are always finite and are accepted unconditionally by + // the parser regardless of their actual value (parser::sax_parse_internal() + // never checks finiteness for value_unsigned/value_integer), so the + // classification below is all that is needed. + // + // A decimal number with up to 18 digits is always representable in + // both std::uint64_t and std::int64_t (18 nines is ~1e18, well below + // both UINT64_MAX ~1.8e19 and INT64_MAX ~9.2e18), so strtoull()/strtoll() + // could not have set errno to ERANGE for it. Numbers with more digits + // (rare in practice) fall through to the exact code below, unchanged, + // so their handling -- including reclassification to value_float when + // the value overflows 64 bits, and rejection when it is not even + // finite as a double -- is bit-for-bit identical to before this + // optimization. + // + // Note this reasons about std::uint64_t/std::int64_t, not about + // number_unsigned_t/number_integer_t (BasicJsonType's own, possibly + // narrower, template parameters -- e.g. std::uint32_t). That is fine + // *only* because discard_number_values is exclusively set by + // accept() (see json.hpp), and accept() always parses through the + // library's own json_sax_acceptor -- never a user-supplied SAX + // consumer -- whose number_unsigned()/number_integer()/number_float() + // callbacks unconditionally discard their argument and return true. + // So for every caller that can reach this branch, neither the token + // classification below nor the eventual (possibly narrowed, and on + // this fast path left stale/unset) value_unsigned/value_integer is + // ever consulted -- an unsigned/integer token is accepted outright, + // and even a >18-digit token that this fast path deliberately falls + // through for is, once reclassified to value_float, still finite + // (and thus accepted) for any digit count that fits in number_unsigned_t + // or number_integer_t regardless of that type's width. If this + // function is ever taught to run with discard_number_values true for + // a caller that *does* read the converted value, this reasoning (and + // the fast path below) would need to be revisited. + if (discard_number_values) + { + constexpr std::size_t safe_digit_count = 18; + if (number_type == token_type::value_unsigned && token_buffer.size() <= safe_digit_count) + { + return token_type::value_unsigned; + } + if (number_type == token_type::value_integer && token_buffer.size() - 1 <= safe_digit_count) + { + return token_type::value_integer; + } + } + const char* const num_begin = token_buffer.data(); const char* const num_end = num_begin + token_buffer.size(); @@ -10163,8 +10230,7 @@ scan_number_done: */ char_int_type get() { - ++position.chars_read_total; - ++position.chars_read_current_line; + advance_position(); if (next_unget) { @@ -10176,6 +10242,23 @@ scan_number_done: current = ia.get_character(); } + return track_after_read(); + } + + /// shared head of get() / get_ignoring_pending_unget(): bump the + /// per-character position counters (line-count-on-'\n' bookkeeping is + /// handled afterwards, in track_after_read(), once `current` is known) + void advance_position() noexcept + { + ++position.chars_read_total; + ++position.chars_read_current_line; + } + + /// shared tail of get() / get_ignoring_pending_unget(): capture the + /// character for error messages (if needed) and update line/column + /// bookkeeping for the character now in `current` + char_int_type track_after_read() + { // seekable adapters reconstruct the token lazily on error (see // get_token_string), so the eager per-character copy is skipped capture_char(std::integral_constant {}); @@ -10192,6 +10275,29 @@ scan_number_done: return current; } + /*! + @brief like get(), but for call sites that can prove no unget() is pending + + get() has to check the `next_unget` flag on every call, because a + previous token may have ended with unget() (e.g. scan_number() always + ungets the character that terminated the number, so the next call to + scan() can see it again). skip_whitespace() reads that first, + possibly-ungotten character via a plain get(), but every further + character it reads is guaranteed to be a fresh read: nothing between + those calls invokes unget(). This variant skips the (otherwise always + false) next_unget branch for those calls; it is not a general + replacement for get(). + */ + char_int_type get_ignoring_pending_unget() + { + JSON_ASSERT(!next_unget); + + advance_position(); + current = ia.get_character(); + + return track_after_read(); + } + /// seekable adapter: nothing to capture, the token is rebuilt on error void capture_char(std::true_type /*lazy*/) const noexcept {} @@ -10393,13 +10499,37 @@ scan_number_done: return true; } + /// whether `current` is one of the four JSON whitespace characters + bool current_is_whitespace() const noexcept + { + return current == ' ' || current == '\t' || current == '\n' || current == '\r'; + } + void skip_whitespace() { + // the first character may be a pending unget() left over from the + // previous token (see get_ignoring_pending_unget()); every + // subsequent character read by this loop is guaranteed fresh, since + // nothing below calls unget() + get(); + + if (!current_is_whitespace()) + { + return; + } + + // this is written as an if-guarded do-while (rather than a plain + // while loop) because that shape is what lets both GCC and Clang + // keep the input adapter's read pointer in a register across + // iterations; the equivalent while-loop measurably defeated that + // optimization in testing, turning long whitespace runs (e.g. the + // indentation of pretty-printed JSON) from a register-only loop + // into one that reloads the pointer from memory every character do { - get(); + get_ignoring_pending_unget(); } - while (current == ' ' || current == '\t' || current == '\n' || current == '\r'); + while (current_is_whitespace()); } token_type scan() @@ -10539,6 +10669,13 @@ scan_number_done: const char_int_type decimal_point_char = '.'; /// the position of the decimal point in the input std::size_t decimal_point_position = std::string::npos; + + /// whether the caller (e.g. accept()/json_sax_acceptor) only needs the + /// token classification and never looks at the converted numeric value; + /// when set, scan_number() may skip strtoull()/strtoll() for + /// value_unsigned/value_integer tokens whose digit count guarantees they + /// fit into 64 bits (see scan_number()) + const bool discard_number_values = false; }; } // namespace detail @@ -15045,9 +15182,10 @@ class parser parser_callback_t cb = nullptr, const bool allow_exceptions_ = true, const bool ignore_comments = false, - const bool ignore_trailing_commas_ = false) + const bool ignore_trailing_commas_ = false, + const bool discard_number_values_ = false) : callback(std::move(cb)) - , m_lexer(std::move(adapter), ignore_comments) + , m_lexer(std::move(adapter), ignore_comments, discard_number_values_) , allow_exceptions(allow_exceptions_) , ignore_trailing_commas(ignore_trailing_commas_) { @@ -17396,6 +17534,20 @@ class json_pointer } } + // the reference token consists only of digits at this point (cf. checks + // above); however, its numeric value might not be representable, in which + // case array_index() would throw out_of_range.404/410 -- contains() must + // not throw (see #5395), so such a reference token is treated as "not found" + errno = 0; // strtoull() does not reset errno on success + char* p_end = nullptr; // NOLINT(misc-const-correctness) + const unsigned long long magnitude = std::strtoull(reference_token.c_str(), &p_end, 10); // NOLINT(runtime/int) + if (JSON_HEDLEY_UNLIKELY(errno == ERANGE // the value exceeds ULLONG_MAX + || magnitude >= static_cast((std::numeric_limits::max)()))) // NOLINT(runtime/int) + { + // the array index cannot be represented as size_type + return false; + } + const auto idx = array_index(reference_token); if (idx >= ptr->size()) { @@ -19678,6 +19830,15 @@ class binary_writer CharType dtype = it->second; key = "_ArraySize_"; + // the dimensions are written verbatim as the header length below, so a + // value that is not an array cannot produce a valid one: null emits 'Z' + // and an object emits '{', neither of which a reader accepts after '#'. + // Such an object is not a valid ndarray and falls back to a plain object. + if (!value.at(key).is_array()) + { + return true; + } + std::size_t len = (value.at(key).empty() ? 0 : 1); for (const auto& el : value.at(key)) { @@ -19851,8 +20012,8 @@ class binary_writer void write_compact_float(const number_float_t n, detail::input_format_t format) { #ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" + JSON_HEDLEY_DIAGNOSTIC_PUSH + JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal") #endif if (!std::isfinite(n) || ((static_cast(n) >= static_cast(std::numeric_limits::lowest()) && static_cast(n) <= static_cast((std::numeric_limits::max)()) && @@ -19871,7 +20032,7 @@ class binary_writer write_number(n); } #ifdef __GNUC__ -#pragma GCC diagnostic pop + JSON_HEDLEY_DIAGNOSTIC_POP #endif } @@ -21046,8 +21207,8 @@ char* to_chars(char* first, const char* last, FloatType value) } #ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" + JSON_HEDLEY_DIAGNOSTIC_PUSH + JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal") #endif if (value == 0) // +-0 { @@ -21058,7 +21219,7 @@ char* to_chars(char* first, const char* last, FloatType value) return first; } #ifdef __GNUC__ -#pragma GCC diagnostic pop + JSON_HEDLEY_DIAGNOSTIC_POP #endif JSON_ASSERT(last - first >= std::numeric_limits::max_digits10); @@ -22585,11 +22746,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::parser_callback_tcb = nullptr, const bool allow_exceptions = true, const bool ignore_comments = false, - const bool ignore_trailing_commas = false + const bool ignore_trailing_commas = false, + const bool discard_number_values = false ) { return ::nlohmann::detail::parser(std::move(adapter), - std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas); + std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas, discard_number_values); } private: @@ -23756,6 +23918,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief serialization /// @sa https://json.nlohmann.me/api/basic_json/dump/ + JSON_HEDLEY_WARN_UNUSED_RESULT string_t dump(const int indent = -1, const char indent_char = ' ', const bool ensure_ascii = false, @@ -23778,6 +23941,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return the type of the JSON value (explicit) /// @sa https://json.nlohmann.me/api/basic_json/type/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr value_t type() const noexcept { return m_data.m_type; @@ -23785,6 +23949,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether type is primitive /// @sa https://json.nlohmann.me/api/basic_json/is_primitive/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_primitive() const noexcept { return is_null() || is_string() || is_boolean() || is_number() || is_binary(); @@ -23792,6 +23957,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether type is structured /// @sa https://json.nlohmann.me/api/basic_json/is_structured/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_structured() const noexcept { return is_array() || is_object(); @@ -23799,6 +23965,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is null /// @sa https://json.nlohmann.me/api/basic_json/is_null/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_null() const noexcept { return m_data.m_type == value_t::null; @@ -23806,6 +23973,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is a boolean /// @sa https://json.nlohmann.me/api/basic_json/is_boolean/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_boolean() const noexcept { return m_data.m_type == value_t::boolean; @@ -23813,6 +23981,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is a number /// @sa https://json.nlohmann.me/api/basic_json/is_number/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_number() const noexcept { return is_number_integer() || is_number_float(); @@ -23820,6 +23989,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is an integer number /// @sa https://json.nlohmann.me/api/basic_json/is_number_integer/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_number_integer() const noexcept { return m_data.m_type == value_t::number_integer || m_data.m_type == value_t::number_unsigned; @@ -23827,6 +23997,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is an unsigned integer number /// @sa https://json.nlohmann.me/api/basic_json/is_number_unsigned/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_number_unsigned() const noexcept { return m_data.m_type == value_t::number_unsigned; @@ -23834,6 +24005,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is a floating-point number /// @sa https://json.nlohmann.me/api/basic_json/is_number_float/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_number_float() const noexcept { return m_data.m_type == value_t::number_float; @@ -23841,6 +24013,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is an object /// @sa https://json.nlohmann.me/api/basic_json/is_object/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_object() const noexcept { return m_data.m_type == value_t::object; @@ -23848,6 +24021,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is an array /// @sa https://json.nlohmann.me/api/basic_json/is_array/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_array() const noexcept { return m_data.m_type == value_t::array; @@ -23855,6 +24029,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is a string /// @sa https://json.nlohmann.me/api/basic_json/is_string/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_string() const noexcept { return m_data.m_type == value_t::string; @@ -23862,6 +24037,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is a binary array /// @sa https://json.nlohmann.me/api/basic_json/is_binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_binary() const noexcept { return m_data.m_type == value_t::binary; @@ -23869,6 +24045,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is discarded /// @sa https://json.nlohmann.me/api/basic_json/is_discarded/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_discarded() const noexcept { return m_data.m_type == value_t::discarded; @@ -25200,6 +25377,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief returns the number of occurrences of a key in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/count/ + JSON_HEDLEY_WARN_UNUSED_RESULT size_type count(const typename object_t::key_type& key) const { // return 0 for all nonobject types @@ -25210,6 +25388,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/count/ template::value, int> = 0> + JSON_HEDLEY_WARN_UNUSED_RESULT size_type count(KeyType && key) const { // return 0 for all nonobject types @@ -25218,6 +25397,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief check the existence of an element in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/contains/ + JSON_HEDLEY_WARN_UNUSED_RESULT bool contains(const typename object_t::key_type& key) const { return is_object() && m_data.m_value.object->find(key) != m_data.m_value.object->end(); @@ -25227,6 +25407,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/contains/ template::value, int> = 0> + JSON_HEDLEY_WARN_UNUSED_RESULT bool contains(KeyType && key) const { return is_object() && m_data.m_value.object->find(std::forward(key)) != m_data.m_value.object->end(); @@ -25234,12 +25415,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief check the existence of an element in a JSON object given a JSON pointer /// @sa https://json.nlohmann.me/api/basic_json/contains/ + JSON_HEDLEY_WARN_UNUSED_RESULT bool contains(const json_pointer& ptr) const { return ptr.contains(this); } template::value, int> = 0> + JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) bool contains(const typename ::nlohmann::json_pointer& ptr) const { @@ -25395,6 +25578,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief checks whether the container is empty. /// @sa https://json.nlohmann.me/api/basic_json/empty/ + JSON_HEDLEY_WARN_UNUSED_RESULT bool empty() const noexcept { switch (m_data.m_type) @@ -25434,6 +25618,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief returns the number of elements /// @sa https://json.nlohmann.me/api/basic_json/size/ + JSON_HEDLEY_WARN_UNUSED_RESULT size_type size() const noexcept { switch (m_data.m_type) @@ -25473,6 +25658,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief returns the maximum possible number of elements /// @sa https://json.nlohmann.me/api/basic_json/max_size/ + JSON_HEDLEY_WARN_UNUSED_RESULT size_type max_size() const noexcept { switch (m_data.m_type) @@ -26191,13 +26377,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec bool operator==(const_reference rhs) const noexcept { #ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" + JSON_HEDLEY_DIAGNOSTIC_PUSH + JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal") #endif const_reference lhs = *this; JSON_IMPLEMENT_OPERATOR( ==, true, false, false) #ifdef __GNUC__ -#pragma GCC diagnostic pop + JSON_HEDLEY_DIAGNOSTIC_POP #endif } @@ -26284,12 +26470,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec friend bool operator==(const_reference lhs, const_reference rhs) noexcept { #ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" + JSON_HEDLEY_DIAGNOSTIC_PUSH + JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal") #endif JSON_IMPLEMENT_OPERATOR( ==, true, false, false) #ifdef __GNUC__ -#pragma GCC diagnostic pop + JSON_HEDLEY_DIAGNOSTIC_POP #endif } @@ -26550,22 +26736,24 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief check if the input is valid JSON /// @sa https://json.nlohmann.me/api/basic_json/accept/ template + JSON_HEDLEY_WARN_UNUSED_RESULT static bool accept(InputType&& i, const bool ignore_comments = false, const bool ignore_trailing_commas = false) { - return parser(detail::input_adapter(std::forward(i)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true); + return parser(detail::input_adapter(std::forward(i)), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true); } /// @brief check if the input is valid JSON (iterator pair, or iterator+sentinel pair for C++20 ranges support) /// @sa https://json.nlohmann.me/api/basic_json/accept/ template::value, int> = 0> + JSON_HEDLEY_WARN_UNUSED_RESULT static bool accept(IteratorType first, SentinelType last, const bool ignore_comments = false, const bool ignore_trailing_commas = false) { - return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true); + return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true); } JSON_HEDLEY_WARN_UNUSED_RESULT @@ -26574,7 +26762,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool ignore_comments = false, const bool ignore_trailing_commas = false) { - return parser(i.get(), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true); + return parser(i.get(), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true); } /// @brief generate SAX events @@ -26660,6 +26848,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return the type as string /// @sa https://json.nlohmann.me/api/basic_json/type_name/ + JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_RETURNS_NON_NULL const char* type_name() const noexcept { @@ -27360,6 +27549,36 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // note erase performs range check parent.erase(json_pointer::template array_index(last_path)); } + else + { + // the parent of a "remove" target must be an object or array + // (see #5396) + JSON_THROW(out_of_range::create(413, detail::concat("cannot remove value: the JSON Patch 'remove' target's parent is of type ", parent.type_name(), ", but must be an object or array"), &parent)); + } + }; + + // RFC 6902 (section 4.4) forbids "from" from being a proper prefix + // of "path" for a "move" operation: a location cannot be moved into + // one of its own children. Compares reference tokens (already + // unescaped by json_pointer's parser) rather than the raw pointer + // strings, since a token may itself contain an escaped '/' or '~' + // that would defeat a naive string-prefix comparison. "from" equal + // to "path" is *not* a proper prefix and must return false. + const auto is_proper_prefix = [](const json_pointer & from, const json_pointer & to) + { + const auto from_size = from.reference_tokens.size(); + if (from_size >= to.reference_tokens.size()) + { + return false; + } + for (std::size_t i = 0; i < from_size; ++i) + { + if (!(from.reference_tokens[i] == to.reference_tokens[i])) + { + return false; + } + } + return true; }; // type check: top level value must be an array @@ -27437,6 +27656,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const auto from_path = get_value("move", "from", true).template get(); json_pointer from_ptr(from_path); + if (JSON_HEDLEY_UNLIKELY(is_proper_prefix(from_ptr, ptr))) + { + JSON_THROW(out_of_range::create(414, detail::concat("cannot move value: 'from' path '", from_path, "' is a proper prefix of 'path' '", path, "'"), &result)); + } + // the "from" location must exist - use at() basic_json const v = result.at(from_ptr); @@ -27958,7 +28182,7 @@ struct formatter // NOLINT(cert-dcl58-c #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE #undef JSON_HEDLEY_CLANG_HAS_BUILTIN #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE -#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE #undef JSON_HEDLEY_CLANG_HAS_EXTENSION #undef JSON_HEDLEY_CLANG_HAS_FEATURE #undef JSON_HEDLEY_CLANG_HAS_WARNING @@ -28049,7 +28273,10 @@ struct formatter // NOLINT(cert-dcl58-c #undef JSON_HEDLEY_PELLES_VERSION_CHECK #undef JSON_HEDLEY_PGI_VERSION #undef JSON_HEDLEY_PGI_VERSION_CHECK +#undef JSON_HEDLEY_PRAGMA #undef JSON_HEDLEY_PREDICT +#undef JSON_HEDLEY_PREDICT_FALSE +#undef JSON_HEDLEY_PREDICT_TRUE #undef JSON_HEDLEY_PRINTF_FORMAT #undef JSON_HEDLEY_PRIVATE #undef JSON_HEDLEY_PUBLIC diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 48a4fd8b0..322e40d80 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -128,6 +128,51 @@ json_test_set_test_options(test-unicode4 TEST_PROPERTIES TIMEOUT 3000) # add unit tests ############################################################################# +# Generate the leak checks for every JSON_HEDLEY_* macro defined in +# hedley.hpp; tests/src/unit-no-macro-leak.cpp #include-s the result after +# nlohmann/json.hpp (see issue #5408). Using the shared +# cmake/scripts/gen_hedley_undef_check.cmake script (also used by `make +# update_hedley_undef`) instead of a hand-maintained list of macro names +# means this test can never go stale after a future `make update_hedley`. +set(hedley_hpp "${PROJECT_SOURCE_DIR}/include/nlohmann/thirdparty/hedley/hedley.hpp") +set(hedley_undef_check_script "${PROJECT_SOURCE_DIR}/cmake/scripts/gen_hedley_undef_check.cmake") +set(hedley_undef_checks "${PROJECT_BINARY_DIR}/include/hedley_undef_checks.inc") + +# Reconfigure whenever the vendored header or the generator script changes, +# so a `cmake --build` after `make update_hedley` does not silently keep a +# stale generated file around. +set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS + "${hedley_hpp}" + "${hedley_undef_check_script}") + +# Generate once at configure time, so the very first build (before any +# custom-command build step has run) already has an up-to-date file. +execute_process( + COMMAND ${CMAKE_COMMAND} + "-DHEDLEY_HPP=${hedley_hpp}" + "-DOUTPUT=${hedley_undef_checks}" + -DMODE=checks + -P "${hedley_undef_check_script}" + RESULT_VARIABLE hedley_undef_check_result +) +if(NOT hedley_undef_check_result EQUAL 0) + message(FATAL_ERROR "Failed to generate ${hedley_undef_checks}") +endif() + +# Also (re)generate as a build step, so an incremental build after editing +# hedley.hpp without a full reconfigure still picks up the change. +add_custom_command( + OUTPUT "${hedley_undef_checks}" + COMMAND ${CMAKE_COMMAND} + "-DHEDLEY_HPP=${hedley_hpp}" + "-DOUTPUT=${hedley_undef_checks}" + -DMODE=checks + -P "${hedley_undef_check_script}" + DEPENDS "${hedley_hpp}" "${hedley_undef_check_script}" + COMMENT "Generating Hedley undef leak checks" + VERBATIM) +add_custom_target(generate_hedley_undef_checks DEPENDS "${hedley_undef_checks}") + if("${JSON_TestStandards}" STREQUAL "") set(test_cxx_standards 11 14 17 20 23) unset(test_force) @@ -231,6 +276,14 @@ foreach(file ${files}) json_test_add_test_for(${file} MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force}) endforeach() +# tests/src/unit-no-macro-leak.cpp #include-s the generated leak-check file, +# so its test targets must be built after generate_hedley_undef_checks. +foreach(cxx_standard ${test_cxx_standards}) + if(TARGET test-no-macro-leak_cpp${cxx_standard}) + add_dependencies(test-no-macro-leak_cpp${cxx_standard} generate_hedley_undef_checks) + endif() +endforeach() + if(json_32bit_test_only) # Skip all other tests in this file return() diff --git a/tests/src/test_utils.hpp b/tests/src/test_utils.hpp index baa802f71..4c81a8ef4 100644 --- a/tests/src/test_utils.hpp +++ b/tests/src/test_utils.hpp @@ -15,6 +15,15 @@ namespace utils { +// Some tests intentionally discard the [[nodiscard]]/JSON_HEDLEY_WARN_UNUSED_RESULT +// return value of a call they only make to exercise its side effects (e.g. checking +// that it does not throw). A plain (void) cast on the call expression does not +// suppress GCC's warning for functions using the GNU __attribute__((warn_unused_result)) +// form (as opposed to the C++17 [[nodiscard]] attribute) -- passing the value into an +// ordinary function call does. +template +inline void ignore_return_value(T&& /*unused*/) noexcept {} + inline std::vector read_binary_file(const std::string& filename) { std::ifstream file(filename, std::ios::binary); diff --git a/tests/src/unit-bjdata.cpp b/tests/src/unit-bjdata.cpp index 41feb5288..7d0dd5ff2 100644 --- a/tests/src/unit-bjdata.cpp +++ b/tests/src/unit-bjdata.cpp @@ -2751,6 +2751,31 @@ TEST_CASE("BJData") CHECK(json::to_bjdata(j_ok) == std::vector({'[', '$', 'U', '#', '[', 'i', 2, 'i', 3, ']', 1, 2, 3, 4, 5, 6})); CHECK(json::from_bjdata(json::to_bjdata(j_ok), true, true) == j_ok); } + + SECTION("ndarray whose _ArraySize_ is not an array stays as object") + { + // the shape is written verbatim as the header length, so a + // value that is not an array cannot produce a valid one: null + // would emit 'Z' and an object '{', neither of which a reader + // accepts after '#'. Both have to stay plain objects. + json const j_null = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", nullptr}, {"_ArrayData_", json::array()}}); + const auto out_null = json::to_bjdata(j_null); + CHECK(out_null.at(0) == '{'); + CHECK(json::from_bjdata(out_null) == j_null); + + // an object shape passes the per-entry check by iterating its + // values rather than dimensions, so it needs rejecting too + json const j_obj = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", {{"a", 1}}}, {"_ArrayData_", {1}}}); + const auto out_obj = json::to_bjdata(j_obj); + CHECK(out_obj.at(0) == '{'); + CHECK(json::from_bjdata(out_obj) == j_obj); + + // a scalar shape is not a dimension list either + json const j_num = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", 1}, {"_ArrayData_", {1}}}); + const auto out_num = json::to_bjdata(j_num); + CHECK(out_num.at(0) == '{'); + CHECK(json::from_bjdata(out_num) == j_num); + } } } diff --git a/tests/src/unit-bson.cpp b/tests/src/unit-bson.cpp index 7896b9f18..f0b37ea3f 100644 --- a/tests/src/unit-bson.cpp +++ b/tests/src/unit-bson.cpp @@ -38,6 +38,54 @@ class huge_binary_t : public std::vector using huge_binary_json = nlohmann::basic_json < std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, double, std::allocator, nlohmann::adl_serializer, huge_binary_t, void >; + +// a string type that can be made to report a size beyond INT32_MAX without +// allocating that much memory, so BSON length overflow can be tested for +// strings and (embedded) documents as well, following the same idea as +// huge_binary_t. +// +// Unlike huge_binary_t (which is only ever used as the BSON *value* type), +// this type doubles as basic_json's StringType and is therefore also used +// for *object keys* (e.g. "s" or "nested" below). Only the designated test +// value is meant to lie about its size - if every huge_string_t (including +// keys) reported a huge size, the running totals computed while walking the +// BSON document (see calc_bson_object_size & friends in binary_writer.hpp) +// would need more than 32 bits, and on platforms where std::size_t is only +// 32 bits wide that arithmetic would silently wrap around, producing wrong +// (or even unguarded) lengths. The fake size is therefore opt-in via +// as_huge(), and plain strings - in particular object keys - keep reporting +// their real, small size. +class huge_string_t : public std::string +{ + public: + using std::string::string; + huge_string_t(const std::string& s) : std::string(s) {} // NOLINT(google-explicit-constructor,hicpp-explicit-conversions) + + // returns a copy of @a s whose size() pretends to be huge + static huge_string_t as_huge(const std::string& s) + { + huge_string_t result(s); + result.pretend_huge = true; + return result; + } + + size_type size() const noexcept + { + if (pretend_huge) + { + // one byte more than the BSON length field can represent + return static_cast((std::numeric_limits::max)()) + 1; + } + return std::string::size(); + } + + private: + bool pretend_huge = false; +}; + +using huge_string_json = nlohmann::basic_json < + std::map, std::vector, huge_string_t, bool, std::int64_t, std::uint64_t, + double, std::allocator, nlohmann::adl_serializer, std::vector, void >; } // namespace TEST_CASE("BSON") @@ -105,10 +153,36 @@ TEST_CASE("BSON") SECTION("lengths exceeding INT32_MAX cannot be serialized to BSON") { - huge_binary_json j; - j["b"] = huge_binary_json::binary(huge_binary_t{}); + // out_of_range.412 is thrown from a single shared helper + // (to_bson_length) that guards the BSON length fields of binary + // values, strings, and (embedded) documents alike + SECTION("binary") + { + huge_binary_json j; + j["b"] = huge_binary_json::binary(huge_binary_t{}); - CHECK_THROWS_WITH_AS(huge_binary_json::to_bson(j), "[json.exception.out_of_range.412] BSON length 2147483661 exceeds maximum of 2147483647", huge_binary_json::out_of_range&); + CHECK_THROWS_WITH_AS(huge_binary_json::to_bson(j), "[json.exception.out_of_range.412] BSON length 2147483661 exceeds maximum of 2147483647", huge_binary_json::out_of_range&); + } + + SECTION("string") + { + huge_string_json j; + j["s"] = huge_string_t::as_huge("value"); + + CHECK_THROWS_WITH_AS(huge_string_json::to_bson(j), "[json.exception.out_of_range.412] BSON length 2147483661 exceeds maximum of 2147483647", huge_string_json::out_of_range&); + } + + SECTION("document") + { + // an oversized string nested one level deep makes the + // *embedded* document's own length exceed INT32_MAX as well + huge_string_json nested; + nested["s"] = huge_string_t::as_huge("value"); + huge_string_json j; + j["nested"] = nested; + + CHECK_THROWS_WITH_AS(huge_string_json::to_bson(j), "[json.exception.out_of_range.412] BSON length 2147483674 exceeds maximum of 2147483647", huge_string_json::out_of_range&); + } } SECTION("string length must be at least 1") @@ -193,6 +267,23 @@ TEST_CASE("BSON") CHECK(json::from_bson(result, true, false) == j); } + SECTION("non-empty object with bool from a non-0/1 byte (lenient parsing)") + { + // documented lenient behavior (see gh-5333): any non-zero byte + // is accepted as `true`, not just 0x01 + std::vector const input = + { + 0x0D, 0x00, 0x00, 0x00, // size (little endian) + 0x08, // entry: boolean + 'e', 'n', 't', 'r', 'y', '\x00', + 0x02, // value = 0x02 (neither 0x00 nor 0x01) + 0x00 // end marker + }; + + const json expected = { { "entry", true } }; + CHECK(json::from_bson(input) == expected); + } + SECTION("non-empty object with double") { json const j = @@ -499,6 +590,29 @@ TEST_CASE("BSON") CHECK(json::from_bson(result, true, false) == j); } + SECTION("array elements with non-conforming keys (lenient parsing)") + { + // documented lenient behavior (see gh-5333): BSON array element + // keys are not checked against the required decimal sequence + // "0", "1", "2", ... - elements are taken in encoded order + std::vector const input = + { + 0x26, 0x00, 0x00, 0x00, // size (little endian) + 0x04, 'e', 'n', 't', 'r', 'y', '\x00', // entry: embedded array + + 0x1A, 0x00, 0x00, 0x00, // size (little endian) + 0x10, '5', 0x00, 0x0A, 0x00, 0x00, 0x00, // key "5" (bogus) -> 10 + 0x10, 'x', 0x00, 0x14, 0x00, 0x00, 0x00, // key "x" (non-numeric) -> 20 + 0x10, '1', 0x00, 0x1E, 0x00, 0x00, 0x00, // key "1" (out of order) -> 30 + 0x00, // end marker (embedded array) + + 0x00 // end marker + }; + + const json expected = { { "entry", json::array({10, 20, 30}) } }; + CHECK(json::from_bson(input) == expected); + } + SECTION("non-empty object with binary member") { const size_t N = 10; @@ -594,6 +708,31 @@ TEST_CASE("BSON") CHECK(json::from_bson(result, true, false) == j); } + SECTION("binary member with subtype 0x02 (old binary) keeps its inner length prefix (lenient parsing)") + { + // documented lenient behavior (see gh-5333): the payload for + // binary subtype 0x02 ("old binary") is returned as-is, + // including its own inner 4-byte length prefix; it is not + // stripped or reinterpreted + std::vector const input = + { + 0x17, 0x00, 0x00, 0x00, // size (little endian) + 0x05, 'e', 'n', 't', 'r', 'y', '\x00', // entry: binary + + 0x06, 0x00, 0x00, 0x00, // size of binary (little endian) + 0x02, // "old binary" subtype + 0x02, 0x00, 0x00, 0x00, // inner length prefix (part of the old-binary payload) + 0x68, 0x69, // payload ('h', 'i') + + 0x00 // end marker + }; + + // the inner length prefix is part of the (unmodified) payload + const std::vector expected_payload = {0x02, 0x00, 0x00, 0x00, 0x68, 0x69}; + const json expected = { { "entry", json::binary(expected_payload, 0x02) } }; + CHECK(json::from_bson(input) == expected); + } + SECTION("Some more complex document") { json const j = diff --git a/tests/src/unit-class_parser.cpp b/tests/src/unit-class_parser.cpp index 8b3ea660e..bb9cdcdc7 100644 --- a/tests/src/unit-class_parser.cpp +++ b/tests/src/unit-class_parser.cpp @@ -23,6 +23,8 @@ using nlohmann::json; #include #include +#include "test_utils.hpp" + namespace { class SaxEventLogger @@ -624,7 +626,8 @@ TEST_CASE("parser class") SECTION("overflow") { // overflows during parsing yield an exception - CHECK_THROWS_WITH_AS(parser_helper("1.18973e+4932").empty(), "[json.exception.out_of_range.406] number overflow parsing '1.18973e+4932'", json::out_of_range&); + // empty() is nodiscard; the exception is thrown by parser_helper() itself, before empty() would run + CHECK_THROWS_WITH_AS(utils::ignore_return_value(parser_helper("1.18973e+4932").empty()), "[json.exception.out_of_range.406] number overflow parsing '1.18973e+4932'", json::out_of_range&); } SECTION("invalid numbers") @@ -930,6 +933,98 @@ TEST_CASE("parser class") CHECK(accept_helper("+1") == false); CHECK(accept_helper("+0") == false); } + + SECTION("issue #5411 - skip conversion when accept() does not need the numeric value") + { + // lexer::scan_number() may skip strtoull()/strtoll() for + // value_unsigned/value_integer tokens when the caller (e.g. + // json::accept()) does not need the converted value, as long + // as the digit count alone guarantees no 64-bit overflow (see + // the "safe_digit_count" fast path in scan_number()). This + // differential test checks that json::accept() (which enables + // the fast path) and json::parse() (which never does) always + // agree, over a corpus that exercises both the fast path + // (<=18 digits) and the untouched, exact fallback path (>=19 + // digits) -- including reclassification of huge digit-only + // integers to a (possibly non-finite) floating-point value. + const std::vector> cases = + { + // normal small/large integers, both signs + {"0", true}, {"1", true}, {"-1", true}, {"42", true}, {"-42", true}, + {"123456789", true}, {"-123456789", true}, + + // digit-count boundary around the 18-digit safe cutoff (both signs) + {std::string(17, '9'), true}, + {std::string(18, '9'), true}, + {std::string(19, '9'), true}, + {std::string(20, '9'), true}, + {"-" + std::string(17, '9'), true}, + {"-" + std::string(18, '9'), true}, + {"-" + std::string(19, '9'), true}, + {"-" + std::string(20, '9'), true}, + + // 64-bit boundaries + {"9223372036854775807", true}, // INT64_MAX + {"-9223372036854775808", true}, // INT64_MIN + {"18446744073709551615", true}, // UINT64_MAX + {"18446744073709551616", true}, // UINT64_MAX + 1 (overflows uint64_t, finite double) + + // the 28-digit example from the issue: overflows uint64_t + // but is finite as a double, so the scanner reclassifies + // it to value_float and it is accepted + {"9999999999999999999999999999", true}, + + // huge digit-only integers that overflow even a double -> rejected + {std::string(309, '9'), false}, + {std::string(400, '9'), false}, + {"1" + std::string(400, '0'), false}, + + // 1e999 / 1e400 style overflow -> rejected + {"1e999", false}, + {"1e400", false}, + {"-1e999", false}, + {"1E999", false}, + + // values straddling DBL_MAX + {"1.7976931348623157e308", true}, // <= DBL_MAX, finite + {"1.7976931348623159e308", false}, // > DBL_MAX, overflows to inf + + // a mix of other valid/invalid numeric syntax + {"3.14159", true}, + {"-0.0", true}, + {"1.0e10", true}, + {"01", false}, + {"-", false}, + {"1.", false}, + {"1e", false}, + {"+1", false}, + }; + + for (const auto& c : cases) + { + const std::string& number = c.first; + const bool expected = c.second; + CAPTURE(number) + CAPTURE(expected) + + // accept() takes the fast path (skips conversion when possible) + CHECK(json::accept(number) == expected); + + // parse() always performs the full conversion; it must agree + json j; + CHECK_NOTHROW(json::parser(nlohmann::detail::input_adapter(number), nullptr, false).parse(true, j)); + CHECK(!j.is_discarded() == expected); + + // wrap in an array so get_token() is exercised beyond the + // very first (constructor-time) scan as well + std::string wrapped = "["; + wrapped += number; + wrapped += ","; + wrapped += number; + wrapped += "]"; + CHECK(json::accept(wrapped) == expected); + } + } } } @@ -1394,6 +1489,71 @@ TEST_CASE("parser class") CHECK(accept_helper("\"\\uD80C\\uFFFF\"") == false); } +#if !defined(JSON_NOEXCEPTION) + SECTION("issue #5412 - whitespace skipping bookkeeping (compact vs. pretty-printed)") + { + // lexer::skip_whitespace() reads its first character with get() (to + // honor a possibly pending unget() from the previous token) and every + // further whitespace character with get_ignoring_pending_unget() (a + // get() variant that skips the then-always-false next_unget check). + // This must not change the reported byte offset, line, or column of + // a syntax error, even when a long run of whitespace containing + // multiple newlines is skipped beforehand (as with pretty-printed + // input). The expected values below were captured from the + // unmodified do-while(get()) loop, so any regression that miscounts + // characters or newlines while skipping whitespace changes them. + const auto check_error = [](const std::string & input, std::size_t expected_byte, + const std::string & expected_what) + { + CAPTURE(input) + try + { + json _ = json::parse(input); + FAIL_CHECK("expected a parse_error, but parsing succeeded"); + } + catch (const json::parse_error& e) + { + CHECK(e.byte == expected_byte); + CHECK(std::string(e.what()) == expected_what); + } + }; + + // a nested document, serialized both compactly and pretty-printed + // (dump(4)), each truncated right before the final closing '}' so + // that the parser hits EOF after skipping all of the (in the + // pretty-printed case, substantial) indentation whitespace + const json doc = + { + {"a", 1}, + {"b", json::array({true, false, nullptr, "x"})}, + {"c", json::object({{"d", 3.14}, {"e", json::array({1, 2, 3})}})} + }; + + const std::string compact = doc.dump(); + const std::string pretty = doc.dump(4); + + check_error(compact.substr(0, compact.size() - 1), 60, + "[json.exception.parse_error.101] parse error at line 1, column 60: syntax error while parsing object - unexpected end of input; expected '}'"); + check_error(pretty.substr(0, pretty.size() - 1), 193, + "[json.exception.parse_error.101] parse error at line 17, column 1: syntax error while parsing object - unexpected end of input; expected '}'"); + + // an invalid token appearing after several indented, multi-line + // whitespace runs vs. the same document without any of that + // whitespace + check_error(R"({ + "a": 1, + "b": [ + true, + false + ], + "c": @ +})", 70, + "[json.exception.parse_error.101] parse error at line 7, column 10: syntax error while parsing value - invalid literal; last read: '\"c\": @'"); + check_error(R"({"a":1,"b":[true,false],"c":@})", 29, + "[json.exception.parse_error.101] parse error at line 1, column 29: syntax error while parsing value - invalid literal; last read: '\"c\":@'"); + } +#endif + SECTION("tests found by mutate++") { // test case to make sure no comma precedes the first key diff --git a/tests/src/unit-class_parser_diagnostic_positions.cpp b/tests/src/unit-class_parser_diagnostic_positions.cpp index 2697ecf8a..794182f30 100644 --- a/tests/src/unit-class_parser_diagnostic_positions.cpp +++ b/tests/src/unit-class_parser_diagnostic_positions.cpp @@ -22,6 +22,8 @@ using nlohmann::json; #include +#include "test_utils.hpp" + namespace { class SaxEventLogger @@ -629,7 +631,8 @@ TEST_CASE("parser class") SECTION("overflow") { // overflows during parsing yield an exception - CHECK_THROWS_WITH_AS(parser_helper("1.18973e+4932").empty(), "[json.exception.out_of_range.406] number overflow parsing '1.18973e+4932'", json::out_of_range&); + // empty() is nodiscard; the exception is thrown by parser_helper() itself, before empty() would run + CHECK_THROWS_WITH_AS(utils::ignore_return_value(parser_helper("1.18973e+4932").empty()), "[json.exception.out_of_range.406] number overflow parsing '1.18973e+4932'", json::out_of_range&); } SECTION("invalid numbers") diff --git a/tests/src/unit-conversions.cpp b/tests/src/unit-conversions.cpp index 1937affbb..4975854c0 100644 --- a/tests/src/unit-conversions.cpp +++ b/tests/src/unit-conversions.cpp @@ -1389,6 +1389,37 @@ TEST_CASE("value conversion") // CHECK(m5["one"] == "eins"); } + SECTION("reserve is called on containers that support it (#5406)") + { + // build a larger object so that a missing/incorrect reserve() + // call would be more likely to corrupt or drop elements + json j_large; + for (int i = 0; i < 100; ++i) + { + j_large[std::to_string(i)] = i; + } + + SECTION("std::unordered_map (supports reserve)") + { + const auto m = j_large.get>(); + CHECK(m.size() == 100); + for (int i = 0; i < 100; ++i) + { + CHECK(m.at(std::to_string(i)) == i); + } + } + + SECTION("std::map (no reserve, fallback path)") + { + const auto m = j_large.get>(); + CHECK(m.size() == 100); + for (int i = 0; i < 100; ++i) + { + CHECK(m.at(std::to_string(i)) == i); + } + } + } + SECTION("std::multimap") { j1.get>(); diff --git a/tests/src/unit-items-cpp17.cpp b/tests/src/unit-items-cpp17.cpp new file mode 100644 index 000000000..577dcea56 --- /dev/null +++ b/tests/src/unit-items-cpp17.cpp @@ -0,0 +1,42 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ (supporting code) +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +// This file contains the C++17-only part of unit-items.cpp (structured +// bindings support for json::items()). It is kept in a separate +// translation unit so the (much larger) unit-items.cpp does not need to +// be compiled a second time just for this one SECTION. + +#include "doctest_compatibility.h" + +#include +using nlohmann::json; + +#ifdef JSON_HAS_CPP_17 +#include +#include + +TEST_CASE("items()") +{ + SECTION("object") + { + SECTION("structured bindings") + { + json j = { {"A", 1}, {"B", 2} }; + + std::map m; + + for (auto const&[key, value] : j.items()) + { + m.emplace(key, value); + } + + CHECK(j.get() == m); + } + } +} +#endif diff --git a/tests/src/unit-items.cpp b/tests/src/unit-items.cpp index fa8948447..81959db8d 100644 --- a/tests/src/unit-items.cpp +++ b/tests/src/unit-items.cpp @@ -862,22 +862,6 @@ TEST_CASE("items()") CHECK(counter == 3); } - -#ifdef JSON_HAS_CPP_17 - SECTION("structured bindings") - { - json j = { {"A", 1}, {"B", 2} }; - - std::map m; - - for (auto const&[key, value] : j.items()) - { - m.emplace(key, value); - } - - CHECK(j.get() == m); - } -#endif } SECTION("const object") diff --git a/tests/src/unit-json_patch.cpp b/tests/src/unit-json_patch.cpp index 17d30f189..257e455aa 100644 --- a/tests/src/unit-json_patch.cpp +++ b/tests/src/unit-json_patch.cpp @@ -1389,6 +1389,192 @@ TEST_CASE("JSON patch - add to a primitive parent (regression #4292)") } } +TEST_CASE("JSON patch - remove with primitive or null parent (regression #5396)") +{ + // Regression test for https://github.com/nlohmann/json/issues/5396 + // + // RFC 6902 (§4.2) requires the target location of a "remove" operation + // to exist. When the target's parent resolves to a primitive value or + // null, the operation must fail. Previously operation_remove silently + // did nothing in this case (neither the "is_object" nor the "is_array" + // branch matched, and there was no final "else"), so the patch appeared + // to succeed without changing the document. It now throws + // out_of_range.413. + + SECTION("parent is a primitive (number)") + { + json const doc = {{"a", 1}}; + json const patch = {{{"op", "remove"}, {"path", "/a/b"}}}; +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH_AS(doc.patch(patch), "[json.exception.out_of_range.413] (/a) cannot remove value: the JSON Patch 'remove' target's parent is of type number, but must be an object or array", json::out_of_range&); +#else + CHECK_THROWS_WITH_AS(doc.patch(patch), "[json.exception.out_of_range.413] cannot remove value: the JSON Patch 'remove' target's parent is of type number, but must be an object or array", json::out_of_range&); +#endif + } + + SECTION("parent is a primitive (string)") + { + json const doc = {{"foo", {{"bar", "a string"}}}}; + json const patch = {{{"op", "remove"}, {"path", "/foo/bar/baz"}}}; +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH_AS(doc.patch(patch), "[json.exception.out_of_range.413] (/foo/bar) cannot remove value: the JSON Patch 'remove' target's parent is of type string, but must be an object or array", json::out_of_range&); +#else + CHECK_THROWS_WITH_AS(doc.patch(patch), "[json.exception.out_of_range.413] cannot remove value: the JSON Patch 'remove' target's parent is of type string, but must be an object or array", json::out_of_range&); +#endif + } + + SECTION("top-level document is null") + { + json const doc = nullptr; + json const patch = {{{"op", "remove"}, {"path", "/a"}}}; + CHECK_THROWS_WITH_AS(doc.patch(patch), "[json.exception.out_of_range.413] cannot remove value: the JSON Patch 'remove' target's parent is of type null, but must be an object or array", json::out_of_range&); + } + + SECTION("legitimate removes still work") + { + // object member + json const doc1 = {{"a", 1}, {"b", 2}}; + json const patch1 = {{{"op", "remove"}, {"path", "/a"}}}; + CHECK(doc1.patch(patch1) == json({{"b", 2}})); + + // array element + json const doc2 = R"([1, 2, 3])"_json; + json const patch2 = {{{"op", "remove"}, {"path", "/1"}}}; + CHECK(doc2.patch(patch2) == R"([1, 3])"_json); + } +} + +TEST_CASE("JSON patch - move where 'from' is a proper prefix of 'path' (regression #5397)") +{ + // Regression test for https://github.com/nlohmann/json/issues/5397 + // + // RFC 6902 (§4.4) forbids "from" from being a proper prefix of "path" + // for a "move" operation: "a location cannot be moved into one of its + // children." "move" is implemented as remove-then-add; for an object + // target this happened to throw anyway as a side effect of the "add" + // step re-resolving through the now-removed parent, but for an array + // target the removal shifted subsequent indices, so "path" silently + // re-resolved to a different element and the operation "succeeded" + // with a corrupted result. It now throws out_of_range.414 for both + // object and array targets. + + SECTION("array target (from the issue)") + { + json const doc = R"([[1,2],[3]])"_json; + json const patch = {{{"op", "move"}, {"from", "/0"}, {"path", "/0/0"}}}; +#if JSON_DIAGNOSTIC_POSITIONS + CHECK_THROWS_WITH_AS(doc.patch(patch), "[json.exception.out_of_range.414] (bytes 0-11) cannot move value: 'from' path '/0' is a proper prefix of 'path' '/0/0'", json::out_of_range&); +#else + CHECK_THROWS_WITH_AS(doc.patch(patch), "[json.exception.out_of_range.414] cannot move value: 'from' path '/0' is a proper prefix of 'path' '/0/0'", json::out_of_range&); +#endif + } + + SECTION("object target") + { + json const doc = R"({"a": {"b": 1}})"_json; + json const patch = {{{"op", "move"}, {"from", "/a"}, {"path", "/a/b"}}}; +#if JSON_DIAGNOSTIC_POSITIONS + CHECK_THROWS_WITH_AS(doc.patch(patch), "[json.exception.out_of_range.414] (bytes 0-15) cannot move value: 'from' path '/a' is a proper prefix of 'path' '/a/b'", json::out_of_range&); +#else + CHECK_THROWS_WITH_AS(doc.patch(patch), "[json.exception.out_of_range.414] cannot move value: 'from' path '/a' is a proper prefix of 'path' '/a/b'", json::out_of_range&); +#endif + } + + SECTION("from == path is not a proper prefix and must not be rejected") + { + // "from" equal to "path" is a no-op move; it is not a *proper* + // prefix relationship, so this new check must not reject it. + json const doc = R"({"a": 1, "b": 2})"_json; + json const patch = {{{"op", "move"}, {"from", "/a"}, {"path", "/a"}}}; + CHECK(doc.patch(patch) == doc); + } + + SECTION("raw string prefix that is not a pointer-token prefix must be allowed") + { + // "/ab" is a string-prefix of "/abc/x" as raw text, but "ab" and + // "abc" are different reference tokens, so this is NOT a + // pointer-token prefix relationship and the move must succeed. + // This is the key case proving the check compares tokens, not + // raw pointer text (a naive std::string prefix/rfind check on + // the undecoded pointer would wrongly reject this). + json const doc = R"({"ab": 1, "abc": {"x": 2}})"_json; + json const patch = {{{"op", "move"}, {"from", "/ab"}, {"path", "/abc/x"}}}; + json const result = R"({"abc": {"x": 1}})"_json; + CHECK(doc.patch(patch) == result); + } + + SECTION("escaped reference tokens are compared unescaped") + { + // "from" is the single token "a/b" (escaped as "a~1b"); "path" + // addresses member "x" of that same value, so "from" is a + // proper (token-level) prefix of "path" and must be rejected. + json const doc = R"({"a/b": {"x": 1}})"_json; + json const patch = {{{"op", "move"}, {"from", "/a~1b"}, {"path", "/a~1b/x"}}}; +#if JSON_DIAGNOSTIC_POSITIONS + CHECK_THROWS_WITH_AS(doc.patch(patch), "[json.exception.out_of_range.414] (bytes 0-17) cannot move value: 'from' path '/a~1b' is a proper prefix of 'path' '/a~1b/x'", json::out_of_range&); +#else + CHECK_THROWS_WITH_AS(doc.patch(patch), "[json.exception.out_of_range.414] cannot move value: 'from' path '/a~1b' is a proper prefix of 'path' '/a~1b/x'", json::out_of_range&); +#endif + } + + SECTION("ordinary valid moves still work") + { + // unrelated top-level members + json const doc1 = R"({"a": 1, "b": 2})"_json; + json const patch1 = {{{"op", "move"}, {"from", "/a"}, {"path", "/c"}}}; + CHECK(doc1.patch(patch1) == R"({"b": 2, "c": 1})"_json); + + // sibling paths that share a textual prefix but are unrelated + json const doc2 = R"({"a": {"x": 1}, "b": {"y": 2}})"_json; + json const patch2 = {{{"op", "move"}, {"from", "/a/x"}, {"path", "/b/z"}}}; + CHECK(doc2.patch(patch2) == R"({"a": {}, "b": {"y": 2, "z": 1}})"_json); + + // "path" is a proper prefix of "from" (the reverse relationship, + // which RFC 6902 does not forbid) + json const doc3 = R"({"a": {"b": 1}})"_json; + json const patch3 = {{{"op", "move"}, {"from", "/a/b"}, {"path", "/a"}}}; + CHECK(doc3.patch(patch3) == R"({"a": 1})"_json); + } + + SECTION("root 'from' is a proper prefix of every non-root 'path'") + { + // the whole document is a proper prefix of any location inside it + json const doc = R"({"a": 1})"_json; + json const patch = {{{"op", "move"}, {"from", ""}, {"path", "/a"}}}; +#if JSON_DIAGNOSTIC_POSITIONS + CHECK_THROWS_WITH_AS(doc.patch(patch), "[json.exception.out_of_range.414] (bytes 0-8) cannot move value: 'from' path '' is a proper prefix of 'path' '/a'", json::out_of_range&); +#else + CHECK_THROWS_WITH_AS(doc.patch(patch), "[json.exception.out_of_range.414] cannot move value: 'from' path '' is a proper prefix of 'path' '/a'", json::out_of_range&); +#endif + } + + SECTION("root 'path' is never a proper prefix violation for a non-root 'from'") + { + // the reverse of the above: moving a non-root location to the root + // is the "path is a prefix of from" relationship, which RFC 6902 + // permits (already covered generally above; this pins the root + // case specifically, since root is the one path with no reference + // tokens at all) + json const doc = R"({"a": {"b": 1}})"_json; + json const patch = {{{"op", "move"}, {"from", "/a"}, {"path", ""}}}; + CHECK(doc.patch(patch) == R"({"b": 1})"_json); + } + + SECTION("the array-append token '-' is an ordinary child token") + { + // "-" (append-to-array) addresses a location *inside* the array, + // so "from" pointing at the array is still a proper prefix of + // "path" ending in "-" and must be rejected like any other child. + json const doc = R"({"a": [1, 2]})"_json; + json const patch = {{{"op", "move"}, {"from", "/a"}, {"path", "/a/-"}}}; +#if JSON_DIAGNOSTIC_POSITIONS + CHECK_THROWS_WITH_AS(doc.patch(patch), "[json.exception.out_of_range.414] (bytes 0-13) cannot move value: 'from' path '/a' is a proper prefix of 'path' '/a/-'", json::out_of_range&); +#else + CHECK_THROWS_WITH_AS(doc.patch(patch), "[json.exception.out_of_range.414] cannot move value: 'from' path '/a' is a proper prefix of 'path' '/a/-'", json::out_of_range&); +#endif + } +} + TEST_CASE("JSON patch - diff emits array removals in descending index order") { SECTION("array shrunk to empty") diff --git a/tests/src/unit-json_pointer.cpp b/tests/src/unit-json_pointer.cpp index a8ed4a89e..4082de45c 100644 --- a/tests/src/unit-json_pointer.cpp +++ b/tests/src/unit-json_pointer.cpp @@ -319,6 +319,44 @@ TEST_CASE("JSON pointers") CHECK_THROWS_WITH_AS(j[jp] = 1, throw_msg.c_str(), json::out_of_range&); CHECK_THROWS_WITH_AS(j_const[jp] == 1, throw_msg.c_str(), json::out_of_range&); + + // #5395: contains() must not throw for a reference token that is a + // syntactically valid array index but numerically exceeds ULLONG_MAX + // (causing strtoull() to set errno to ERANGE) -- it should just report + // that the pointer does not resolve to an element + CHECK(!j.contains(jp)); + CHECK(!j_const.contains(jp)); + } + + { + // #5395: same as above, but using the exact reproduction from the issue + json::json_pointer const jp("/99999999999999999999"); + std::string const throw_msg = "[json.exception.out_of_range.404] unresolved reference token '99999999999999999999'"; + + CHECK_THROWS_WITH_AS(j[jp] = 1, throw_msg.c_str(), json::out_of_range&); + CHECK_THROWS_WITH_AS(j_const[jp] == 1, throw_msg.c_str(), json::out_of_range&); + CHECK_THROWS_WITH_AS(j.at(jp) = 1, throw_msg.c_str(), json::out_of_range&); + CHECK_THROWS_WITH_AS(j_const.at(jp) == 1, throw_msg.c_str(), json::out_of_range&); + + CHECK(!j.contains(jp)); + CHECK(!j_const.contains(jp)); + } + + { + // #5395: a reference token that is numerically representable in + // unsigned long long but exceeds size_type's max (e.g. ULLONG_MAX + // itself on typical 64-bit platforms, where size_type's max equals + // ULLONG_MAX) must not make contains() throw either + json::json_pointer const jp("/18446744073709551615"); + std::string const throw_msg = "[json.exception.out_of_range.410] array index 18446744073709551615 exceeds size_type"; + + CHECK_THROWS_WITH_AS(j[jp] = 1, throw_msg.c_str(), json::out_of_range&); + CHECK_THROWS_WITH_AS(j_const[jp] == 1, throw_msg.c_str(), json::out_of_range&); + CHECK_THROWS_WITH_AS(j.at(jp) = 1, throw_msg.c_str(), json::out_of_range&); + CHECK_THROWS_WITH_AS(j_const.at(jp) == 1, throw_msg.c_str(), json::out_of_range&); + + CHECK(!j.contains(jp)); + CHECK(!j_const.contains(jp)); } // on some machines, the check below is not constant @@ -334,6 +372,10 @@ TEST_CASE("JSON pointers") CHECK_THROWS_WITH_AS(j[jp] = 1, throw_msg.c_str(), json::out_of_range&); CHECK_THROWS_WITH_AS(j_const[jp] == 1, throw_msg.c_str(), json::out_of_range&); + + // #5395: contains() must not throw for a reference token exceeding size_type's max + CHECK(!j.contains(jp)); + CHECK(!j_const.contains(jp)); } DOCTEST_MSVC_SUPPRESS_WARNING_POP diff --git a/tests/src/unit-no-macro-leak.cpp b/tests/src/unit-no-macro-leak.cpp new file mode 100644 index 000000000..c5184c52f --- /dev/null +++ b/tests/src/unit-no-macro-leak.cpp @@ -0,0 +1,34 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +// This file makes sure that none of the internal JSON_HEDLEY_* macros (vendored +// from https://nemequ.github.io/hedley/, see +// include/nlohmann/thirdparty/hedley/hedley.hpp) leak into the including +// translation unit. include/nlohmann/detail/macro_unscope.hpp is supposed to +// #undef every JSON_HEDLEY_* macro (via hedley_undef.hpp) once json.hpp has +// been fully processed. See https://github.com/nlohmann/json/issues/5408, +// where JSON_HEDLEY_PRAGMA, JSON_HEDLEY_PREDICT_TRUE, JSON_HEDLEY_PREDICT_FALSE, +// and JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE escaped this cleanup because +// hedley_undef.hpp had no matching #undef for them. +// +// hedley_undef_checks.inc (included below) is generated at CMake configure/ +// build time by cmake/scripts/gen_hedley_undef_check.cmake, which derives the +// full list of JSON_HEDLEY_* macro names directly from hedley.hpp. That way +// this test covers every macro Hedley actually defines -- not a hardcoded +// snapshot that would silently go stale the next time `make update_hedley` +// runs -- and can never drift from the vendored header. + +#include "doctest_compatibility.h" + +#include + +TEST_CASE("JSON_HEDLEY macros do not leak after including json.hpp") +{ +#include "hedley_undef_checks.inc" + CHECK(true); // keep an assertion when nothing leaked +} diff --git a/tests/src/unit-regression1.cpp b/tests/src/unit-regression1.cpp index 475ef511f..0529f83dd 100644 --- a/tests/src/unit-regression1.cpp +++ b/tests/src/unit-regression1.cpp @@ -29,10 +29,7 @@ using nlohmann::json; #include #include #include "make_test_data_available.hpp" - -#ifdef JSON_HAS_CPP_17 - #include -#endif +#include "test_utils.hpp" #include "fifo_map.hpp" @@ -1373,7 +1370,8 @@ TEST_CASE("regression tests 1") std::array key1 = {{ 103, 92, 117, 48, 48, 48, 55, 92, 114, 215, 126, 214, 95, 92, 34, 174, 40, 71, 38, 174, 40, 71, 38, 223, 134, 247, 127, 0 }}; std::string const key1_str(reinterpret_cast(key1.data())); json const j = key1_str; - CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] invalid UTF-8 byte at index 10: 0x7E", json::type_error&); + // dump() is nodiscard; the exception is thrown by dump() itself before it would return + CHECK_THROWS_WITH_AS(utils::ignore_return_value(j.dump()), "[json.exception.type_error.316] invalid UTF-8 byte at index 10: 0x7E", json::type_error&); } #if JSON_USE_IMPLICIT_CONVERSIONS diff --git a/tests/src/unit-regression2.cpp b/tests/src/unit-regression2.cpp index 2e7450e2e..6c30e3503 100644 --- a/tests/src/unit-regression2.cpp +++ b/tests/src/unit-regression2.cpp @@ -31,6 +31,8 @@ using ordered_json = nlohmann::ordered_json; #include #include +#include "test_utils.hpp" + #ifdef JSON_HAS_CPP_17 #include #include @@ -639,7 +641,8 @@ TEST_CASE("regression tests 2") s += static_cast(i); } dump_test["1"] = s; - dump_test.dump(-1, ' ', true, nlohmann::json::error_handler_t::replace); + // dump() is nodiscard; this only checks that dumping does not throw/crash + utils::ignore_return_value(dump_test.dump(-1, ' ', true, nlohmann::json::error_handler_t::replace)); } } @@ -731,12 +734,14 @@ TEST_CASE("regression tests 2") { const std::array data = {{0x81, 0xA4, 0x64, 0x61, 0x74, 0x61, 0xC4, 0x0F, 0x33, 0x30, 0x30, 0x32, 0x33, 0x34, 0x30, 0x31, 0x30, 0x37, 0x30, 0x35, 0x30, 0x31, 0x30}}; const json j = json::from_msgpack(data.data(), data.size()); + // dump() is nodiscard; this only checks that dumping does not throw CHECK_NOTHROW( - j.dump(4, // Indent - ' ', // Indent char - false, // Ensure ascii - json::error_handler_t::strict // Error - )); + utils::ignore_return_value( + j.dump(4, // Indent + ' ', // Indent char + false, // Ensure ascii + json::error_handler_t::strict // Error + ))); } SECTION("PR #2181 - regression bug with lvalue") diff --git a/tests/src/unit-serialization.cpp b/tests/src/unit-serialization.cpp index f55ed8470..caf720671 100644 --- a/tests/src/unit-serialization.cpp +++ b/tests/src/unit-serialization.cpp @@ -15,6 +15,8 @@ using nlohmann::json; #include #include +#include "test_utils.hpp" + TEST_CASE("serialization") { SECTION("operator<<") @@ -84,8 +86,9 @@ TEST_CASE("serialization") { const json j = "ä\xA9ü"; - CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9", json::type_error&); - CHECK_THROWS_WITH_AS(j.dump(1, ' ', false, json::error_handler_t::strict), "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9", json::type_error&); + // dump() is nodiscard; the exception is thrown by dump() itself before it would return + CHECK_THROWS_WITH_AS(utils::ignore_return_value(j.dump()), "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9", json::type_error&); + CHECK_THROWS_WITH_AS(utils::ignore_return_value(j.dump(1, ' ', false, json::error_handler_t::strict)), "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9", json::type_error&); CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"äü\""); CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"ä\xEF\xBF\xBDü\""); CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"\\u00e4\\ufffd\\u00fc\""); @@ -95,8 +98,9 @@ TEST_CASE("serialization") { const json j = "123\xC2"; - CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] incomplete UTF-8 string; last byte: 0xC2", json::type_error&); - CHECK_THROWS_AS(j.dump(1, ' ', false, json::error_handler_t::strict), json::type_error&); + // dump() is nodiscard; the exception is thrown by dump() itself before it would return + CHECK_THROWS_WITH_AS(utils::ignore_return_value(j.dump()), "[json.exception.type_error.316] incomplete UTF-8 string; last byte: 0xC2", json::type_error&); + CHECK_THROWS_AS(utils::ignore_return_value(j.dump(1, ' ', false, json::error_handler_t::strict)), json::type_error&); CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"123\""); CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"123\xEF\xBF\xBD\""); CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"123\\ufffd\""); @@ -106,8 +110,9 @@ TEST_CASE("serialization") { const json j = "123\xF1\xB0\x34\x35\x36"; - CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] invalid UTF-8 byte at index 5: 0x34", json::type_error&); - CHECK_THROWS_AS(j.dump(1, ' ', false, json::error_handler_t::strict), json::type_error&); + // dump() is nodiscard; the exception is thrown by dump() itself before it would return + CHECK_THROWS_WITH_AS(utils::ignore_return_value(j.dump()), "[json.exception.type_error.316] invalid UTF-8 byte at index 5: 0x34", json::type_error&); + CHECK_THROWS_AS(utils::ignore_return_value(j.dump(1, ' ', false, json::error_handler_t::strict)), json::type_error&); CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"123456\""); CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"123\xEF\xBF\xBD\x34\x35\x36\""); CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"123\\ufffd456\""); diff --git a/tests/src/unit-unicode1.cpp b/tests/src/unit-unicode1.cpp index 174ce1395..2d744003a 100644 --- a/tests/src/unit-unicode1.cpp +++ b/tests/src/unit-unicode1.cpp @@ -17,6 +17,7 @@ using nlohmann::json; #include #include #include "make_test_data_available.hpp" +#include "test_utils.hpp" TEST_CASE("Unicode (1/5)" * doctest::skip()) { @@ -240,7 +241,8 @@ void roundtrip(bool success_expected, const std::string& s) if (success_expected) { // serialization succeeds - CHECK_NOTHROW(j.dump()); + // dump() is nodiscard; this only checks that dumping does not throw + CHECK_NOTHROW(utils::ignore_return_value(j.dump())); // exclude parse test for U+0000 if (s[0] != '\0') @@ -259,7 +261,8 @@ void roundtrip(bool success_expected, const std::string& s) else { // serialization fails - CHECK_THROWS_AS(j.dump(), json::type_error&); + // dump() is nodiscard; the exception is thrown by dump() itself before it would return + CHECK_THROWS_AS(utils::ignore_return_value(j.dump()), json::type_error&); // parsing JSON text fails CHECK_THROWS_AS(_ = json::parse(ps), json::parse_error&); diff --git a/tests/src/unit-unicode2.cpp b/tests/src/unit-unicode2.cpp index fb68815ba..a9649b4de 100644 --- a/tests/src/unit-unicode2.cpp +++ b/tests/src/unit-unicode2.cpp @@ -19,6 +19,7 @@ using nlohmann::json; #include #include #include "make_test_data_available.hpp" +#include "test_utils.hpp" // this test suite uses static variables with non-trivial destructors DOCTEST_CLANG_SUPPRESS_WARNING_PUSH @@ -97,7 +98,8 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 else { // strict mode must throw if success is not expected - CHECK_THROWS_AS(j.dump(), json::type_error&); + // dump() is nodiscard; the exception is thrown by dump() itself before it would return + CHECK_THROWS_AS(utils::ignore_return_value(j.dump()), json::type_error&); // ignore and replace must create different dumps CHECK(s_ignored != s_replaced); diff --git a/tests/src/unit-unicode3.cpp b/tests/src/unit-unicode3.cpp index 739a3dad3..d5627d8cc 100644 --- a/tests/src/unit-unicode3.cpp +++ b/tests/src/unit-unicode3.cpp @@ -19,6 +19,7 @@ using nlohmann::json; #include #include #include "make_test_data_available.hpp" +#include "test_utils.hpp" // this test suite uses static variables with non-trivial destructors DOCTEST_CLANG_SUPPRESS_WARNING_PUSH @@ -97,7 +98,8 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 else { // strict mode must throw if success is not expected - CHECK_THROWS_AS(j.dump(), json::type_error&); + // dump() is nodiscard; the exception is thrown by dump() itself before it would return + CHECK_THROWS_AS(utils::ignore_return_value(j.dump()), json::type_error&); // ignore and replace must create different dumps CHECK(s_ignored != s_replaced); diff --git a/tests/src/unit-unicode4.cpp b/tests/src/unit-unicode4.cpp index f7047201c..f15a1499f 100644 --- a/tests/src/unit-unicode4.cpp +++ b/tests/src/unit-unicode4.cpp @@ -19,6 +19,7 @@ using nlohmann::json; #include #include #include "make_test_data_available.hpp" +#include "test_utils.hpp" // this test suite uses static variables with non-trivial destructors DOCTEST_CLANG_SUPPRESS_WARNING_PUSH @@ -97,7 +98,8 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 else { // strict mode must throw if success is not expected - CHECK_THROWS_AS(j.dump(), json::type_error&); + // dump() is nodiscard; the exception is thrown by dump() itself before it would return + CHECK_THROWS_AS(utils::ignore_return_value(j.dump()), json::type_error&); // ignore and replace must create different dumps CHECK(s_ignored != s_replaced); diff --git a/tests/src/unit-unicode5.cpp b/tests/src/unit-unicode5.cpp index e4dcc2131..e35801823 100644 --- a/tests/src/unit-unicode5.cpp +++ b/tests/src/unit-unicode5.cpp @@ -19,6 +19,7 @@ using nlohmann::json; #include #include #include "make_test_data_available.hpp" +#include "test_utils.hpp" // this test suite uses static variables with non-trivial destructors DOCTEST_CLANG_SUPPRESS_WARNING_PUSH @@ -97,7 +98,8 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 else { // strict mode must throw if success is not expected - CHECK_THROWS_AS(j.dump(), json::type_error&); + // dump() is nodiscard; the exception is thrown by dump() itself before it would return + CHECK_THROWS_AS(utils::ignore_return_value(j.dump()), json::type_error&); // ignore and replace must create different dumps CHECK(s_ignored != s_replaced);