mirror of
https://github.com/nlohmann/json.git
synced 2026-11-06 12:39:22 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7a7534872 | ||
|
|
eaee875178 | ||
|
|
3724295333 |
No files matched your search
@@ -28,7 +28,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
runs-on: ubuntu-26.04
|
runs-on: ubuntu-26.04
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ jobs:
|
|||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
runs-on: ubuntu-26.04
|
runs-on: ubuntu-26.04
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
security-events: write
|
security-events: write
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ jobs:
|
|||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
runs-on: ubuntu-26.04
|
runs-on: ubuntu-26.04
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
runs-on: ubuntu-26.04
|
runs-on: ubuntu-26.04
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@f86dd60acae0d132056913e248bdabf0e0580b7a # v4.4.5
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -38,7 +38,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@f86dd60acae0d132056913e248bdabf0e0580b7a # v4.4.5
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
- name: Check that Meson and pkg-config offer the CMake options
|
- name: Check that Meson and pkg-config offer the CMake options
|
||||||
run: make check_build_options
|
run: make check_build_options
|
||||||
- name: Install Meson
|
- name: Install Meson
|
||||||
@@ -80,7 +80,7 @@ jobs:
|
|||||||
runs-on: ubuntu-26.04
|
runs-on: ubuntu-26.04
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@f86dd60acae0d132056913e248bdabf0e0580b7a # v4.4.5
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -112,7 +112,7 @@ jobs:
|
|||||||
target: [ci_test_single_header, ci_cppcheck, ci_cpplint, ci_reproducible_tests, ci_non_git_tests, ci_offline_testdata, ci_reuse_compliance, ci_test_valgrind]
|
target: [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:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@f86dd60acae0d132056913e248bdabf0e0580b7a # v4.4.5
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -142,7 +142,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@f86dd60acae0d132056913e248bdabf0e0580b7a # v4.4.5
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -168,7 +168,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@f86dd60acae0d132056913e248bdabf0e0580b7a # v4.4.5
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -178,7 +178,7 @@ jobs:
|
|||||||
runs-on: ubuntu-26.04
|
runs-on: ubuntu-26.04
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -244,7 +244,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@f86dd60acae0d132056913e248bdabf0e0580b7a # v4.4.5
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: CXX=g++-${{ matrix.compiler }} cmake -S . -B build -DJSON_CI=On
|
run: CXX=g++-${{ matrix.compiler }} cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -262,7 +262,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@f86dd60acae0d132056913e248bdabf0e0580b7a # v4.4.5
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -279,7 +279,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@f86dd60acae0d132056913e248bdabf0e0580b7a # v4.4.5
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
- name: Disable filesystem support (std::filesystem is unusable) for clang 7 / 8 / 9 / 10
|
- name: Disable filesystem support (std::filesystem is unusable) for clang 7 / 8 / 9 / 10
|
||||||
run: echo "JSON_FORCED_GLOBAL_COMPILE_OPTIONS=-DJSON_HAS_FILESYSTEM=0;-DJSON_HAS_EXPERIMENTAL_FILESYSTEM=0" >> "$GITHUB_ENV"
|
run: echo "JSON_FORCED_GLOBAL_COMPILE_OPTIONS=-DJSON_HAS_FILESYSTEM=0;-DJSON_HAS_EXPERIMENTAL_FILESYSTEM=0" >> "$GITHUB_ENV"
|
||||||
if: ${{ matrix.compiler == '7' || matrix.compiler == '8' || matrix.compiler == '9' || matrix.compiler == '10' }}
|
if: ${{ matrix.compiler == '7' || matrix.compiler == '8' || matrix.compiler == '9' || matrix.compiler == '10' }}
|
||||||
@@ -299,7 +299,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@f86dd60acae0d132056913e248bdabf0e0580b7a # v4.4.5
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -319,7 +319,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@f86dd60acae0d132056913e248bdabf0e0580b7a # v4.4.5
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build with libc++
|
- name: Build with libc++
|
||||||
@@ -346,7 +346,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@f86dd60acae0d132056913e248bdabf0e0580b7a # v4.4.5
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -366,7 +366,7 @@ jobs:
|
|||||||
# import-std support. Its opt-in token is CMake-version-specific, so pin
|
# import-std support. Its opt-in token is CMake-version-specific, so pin
|
||||||
# CMake to the version whose token is set in tests/module_cpp20/CMakeLists.txt.
|
# CMake to the version whose token is set in tests/module_cpp20/CMakeLists.txt.
|
||||||
- name: Get pinned CMake and ninja
|
- name: Get pinned CMake and ninja
|
||||||
uses: lukka/get-cmake@f86dd60acae0d132056913e248bdabf0e0580b7a # v4.4.5
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
with:
|
with:
|
||||||
cmakeVersion: 4.3.4
|
cmakeVersion: 4.3.4
|
||||||
# Clang: the std library module is provided by libc++ (the image's libstdc++
|
# Clang: the std library module is provided by libc++ (the image's libstdc++
|
||||||
@@ -392,7 +392,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@f86dd60acae0d132056913e248bdabf0e0580b7a # v4.4.5
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -409,7 +409,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@f86dd60acae0d132056913e248bdabf0e0580b7a # v4.4.5
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -423,7 +423,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@f86dd60acae0d132056913e248bdabf0e0580b7a # v4.4.5
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -433,7 +433,7 @@ jobs:
|
|||||||
runs-on: ubuntu-26.04
|
runs-on: ubuntu-26.04
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -443,7 +443,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@f86dd60acae0d132056913e248bdabf0e0580b7a # v4.4.5
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -GNinja
|
run: cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -GNinja
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -456,7 +456,7 @@ jobs:
|
|||||||
target: [ci_test_examples, ci_test_build_documentation, ci_test_documentation_mermaid]
|
target: [ci_test_examples, ci_test_build_documentation, ci_test_documentation_mermaid]
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3
|
uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
@@ -484,7 +484,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@f86dd60acae0d132056913e248bdabf0e0580b7a # v4.4.5
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
- name: Check that the job runs natively on arm64
|
- name: Check that the job runs natively on arm64
|
||||||
run: uname -m && test "$(uname -m)" = aarch64
|
run: uname -m && test "$(uname -m)" = aarch64
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@f86dd60acae0d132056913e248bdabf0e0580b7a # v4.4.5
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
- name: Set extra CXX_FLAGS for latest std_version
|
- name: Set extra CXX_FLAGS for latest std_version
|
||||||
# /wd5285 silences C5285 emitted by the bundled third-party doctest.h, which
|
# /wd5285 silences C5285 emitted by the bundled third-party doctest.h, which
|
||||||
# specializes std::tuple (newly diagnosed by the VS2026 v145 toolset)
|
# specializes std::tuple (newly diagnosed by the VS2026 v145 toolset)
|
||||||
@@ -216,7 +216,7 @@ jobs:
|
|||||||
# import-std support. Its opt-in token is CMake-version-specific, so pin
|
# import-std support. Its opt-in token is CMake-version-specific, so pin
|
||||||
# CMake to the version whose token is set in tests/module_cpp20/CMakeLists.txt.
|
# CMake to the version whose token is set in tests/module_cpp20/CMakeLists.txt.
|
||||||
- name: Get pinned CMake and ninja
|
- name: Get pinned CMake and ninja
|
||||||
uses: lukka/get-cmake@f86dd60acae0d132056913e248bdabf0e0580b7a # v4.4.5
|
uses: lukka/get-cmake@a89e43e22a96abafcd7a62db652a65ecd33052c9 # v4.4.4
|
||||||
with:
|
with:
|
||||||
cmakeVersion: 4.3.4
|
cmakeVersion: 4.3.4
|
||||||
# CMAKE_CXX_FLAGS replaces CMake's MSVC defaults (which include /EHsc), and
|
# CMAKE_CXX_FLAGS replaces CMake's MSVC defaults (which include /EHsc), and
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ cc_library(
|
|||||||
"include/nlohmann/detail/exceptions.hpp",
|
"include/nlohmann/detail/exceptions.hpp",
|
||||||
"include/nlohmann/detail/hash.hpp",
|
"include/nlohmann/detail/hash.hpp",
|
||||||
"include/nlohmann/detail/input/binary_reader.hpp",
|
"include/nlohmann/detail/input/binary_reader.hpp",
|
||||||
"include/nlohmann/detail/input/contiguous_parser.hpp",
|
|
||||||
"include/nlohmann/detail/input/input_adapters.hpp",
|
"include/nlohmann/detail/input/input_adapters.hpp",
|
||||||
"include/nlohmann/detail/input/json_sax.hpp",
|
"include/nlohmann/detail/input/json_sax.hpp",
|
||||||
"include/nlohmann/detail/input/lexer.hpp",
|
"include/nlohmann/detail/input/lexer.hpp",
|
||||||
|
|||||||
@@ -15,9 +15,7 @@ the patch. This function applies a JSON patch in place and returns void.
|
|||||||
|
|
||||||
## Exception safety
|
## Exception safety
|
||||||
|
|
||||||
No guarantees: if an operation fails, the operations before it stay applied, and a failing `move` operation may already
|
No guarantees, value may be corrupted by an unsuccessful patch operation.
|
||||||
have removed the value at its "from" location. Use [`patch`](patch.md) if the value must be unchanged after a failed
|
|
||||||
patch.
|
|
||||||
|
|
||||||
## Exceptions
|
## Exceptions
|
||||||
|
|
||||||
@@ -64,19 +62,8 @@ the JSON value, the complexity can usually be neglected.
|
|||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
Unlike [`patch`](patch.md), `patch_inplace` applies the operation "in place" and no copy of the JSON value is created.
|
Unlike [`patch`](patch.md), `patch_inplace` applies the operation "in place" and no copy of the JSON value is created.
|
||||||
That makes it faster for large documents by avoiding the copy.
|
That makes it faster for large documents by avoiding the copy. However, the JSON value might be corrupted if the
|
||||||
|
function throws an exception.
|
||||||
!!! warning "Not atomic"
|
|
||||||
|
|
||||||
`patch_inplace` applies the operations one after the other and does not undo them when one fails. After an
|
|
||||||
exception, the value holds the result of the operations before the failing one, and a failing `move` operation
|
|
||||||
(a "remove" followed by an "add") may already have removed the value at its "from" location; see the second example
|
|
||||||
below. [RFC 6902](https://tools.ietf.org/html/rfc6902#section-5) requires that a patch with a failing operation is
|
|
||||||
not deemed successful, but leaves open what happens to the document.
|
|
||||||
|
|
||||||
If the value must stay unchanged when the patch fails, use [`patch`](patch.md), which applies the patch to a copy:
|
|
||||||
`#!cpp j = j.patch(json_patch);`. Validating the patch beforehand is not enough, as "test" operations and missing
|
|
||||||
paths are only detected while the patch is applied.
|
|
||||||
|
|
||||||
Duplicate member names inside an operation object are resolved by the parser before the patch is applied (the last one
|
Duplicate member names inside an operation object are resolved by the parser before the patch is applied (the last one
|
||||||
is kept, see [`object_t`](object_t.md#behavior)), so `#!json {"op": "add", "op": "remove", "path": "/a"}` is a
|
is kept, see [`object_t`](object_t.md#behavior)), so `#!json {"op": "add", "op": "remove", "path": "/a"}` is a
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ The default value is `0`: streams are scanned in bulk.
|
|||||||
!!! note "Performance"
|
!!! note "Performance"
|
||||||
|
|
||||||
Parsing from a `std::ifstream` or `std::istringstream` is typically 1.5 to 2 times as fast as before, and reading
|
Parsing from a `std::ifstream` or `std::istringstream` is typically 1.5 to 2 times as fast as before, and reading
|
||||||
many small values from one stream with `operator>>` 1.4 to 4 times as fast. Parsing from a stream then takes about
|
many small values from one stream with `operator>>` 1.4 to 4 times as fast. Parsing from a stream is then within
|
||||||
1.15 to 1.45 times as long as first reading the whole file into a `std::string` and parsing that.
|
about 10 to 20 % of first reading the whole file into a `std::string` and parsing that.
|
||||||
|
|
||||||
A stream buffer without a get area (one that only overrides `underflow()` and `uflow()` and hands out every
|
A stream buffer without a get area (one that only overrides `underflow()` and `uflow()` and hands out every
|
||||||
character from there) offers nothing to scan in bulk, so it is read one character at a time as before.
|
character from there) offers nothing to scan in bulk, so it is read one character at a time as before.
|
||||||
|
|||||||
@@ -1,130 +0,0 @@
|
|||||||
# Reading and writing files
|
|
||||||
|
|
||||||
The library has no functions that take a file name. It reads from and writes to streams, so files are handled with
|
|
||||||
`#!cpp std::ifstream` and `#!cpp std::ofstream` (or a `#!cpp FILE*` when reading). This page collects the recipes for
|
|
||||||
the common cases and the mistakes that show up again and again.
|
|
||||||
|
|
||||||
## Reading a JSON file
|
|
||||||
|
|
||||||
Pass the stream to [`parse`](../api/basic_json/parse.md) directly, and check that the file was opened:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#include <fstream>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
json read_json_file(const std::string& path)
|
|
||||||
{
|
|
||||||
std::ifstream f(path);
|
|
||||||
if (!f.is_open())
|
|
||||||
{
|
|
||||||
throw std::runtime_error("cannot open " + path);
|
|
||||||
}
|
|
||||||
return json::parse(f);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- **Check that the file is open.** A stream that failed to open looks like an empty input, so `parse` reports
|
|
||||||
[`parse_error.101`](../home/exceptions.md#jsonexceptionparse_error101) ("unexpected end of input") at line 1,
|
|
||||||
column 1 instead of saying that the file is missing. A relative path is resolved against the working directory of the
|
|
||||||
running program, which is often not the directory of the source file (see the
|
|
||||||
[FAQ](../home/faq.md#parsing-a-file-fails-with-unexpected-end-of-input)).
|
|
||||||
- **Read the file into a string only if the memory is no concern.** Parsing from a `#!cpp std::ifstream` takes about
|
|
||||||
1.15 to 1.45 times as long as reading the whole file into a string with one `read()` and parsing that, but it needs no
|
|
||||||
memory for the copy (see [input sources and performance](parsing/index.md#input-sources-and-performance)). The common
|
|
||||||
way to read a file with `#!cpp std::istreambuf_iterator<char>` is so slow that it takes about twice as long as parsing
|
|
||||||
from the stream. This advice is for
|
|
||||||
`#!cpp json::parse`. [`json_document`](json_view.md) reads a stream into a buffer that it owns anyway, so there is no
|
|
||||||
copy to save: reading the file into a `#!cpp std::string` yourself and passing that as an lvalue, which the document
|
|
||||||
borrows, was about 6 to 12 % faster than parsing from the stream, with the same single buffer.
|
|
||||||
- `parse` requires the file to hold exactly one JSON value. Files with one value per line are read with
|
|
||||||
[JSON Lines](parsing/json_lines.md); files too large for memory are covered in
|
|
||||||
[Large, streamed, and partial input](parsing/large_and_partial_input.md).
|
|
||||||
|
|
||||||
## Writing a JSON file
|
|
||||||
|
|
||||||
[`dump`](../api/basic_json/dump.md) the value into a string first, then write the string:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
void write_json_file(const std::string& path, const json& j)
|
|
||||||
{
|
|
||||||
const std::string text = j.dump(4) + '\n'; // may throw: nothing is written yet
|
|
||||||
|
|
||||||
std::ofstream f(path, std::ios::binary);
|
|
||||||
if (!f.is_open())
|
|
||||||
{
|
|
||||||
throw std::runtime_error("cannot open " + path);
|
|
||||||
}
|
|
||||||
f << text;
|
|
||||||
f.close();
|
|
||||||
if (f.fail())
|
|
||||||
{
|
|
||||||
throw std::runtime_error("cannot write " + path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- **Serialize before opening the file.** Opening a `#!cpp std::ofstream` truncates the file. `dump` and
|
|
||||||
[`operator<<`](../api/operator_ltlt.md) throw [`type_error.316`](../home/exceptions.md#jsonexceptiontype_error316)
|
|
||||||
when a string is not valid UTF-8 (see [handling invalid UTF-8](serialization.md#handling-invalid-utf-8)), and
|
|
||||||
`#!cpp f << std::setw(4) << j` may already have written part of the value when it throws. Serializing into a string
|
|
||||||
first leaves the old file untouched in that case.
|
|
||||||
- **Check the stream after closing it.** A full disk or a missing permission sets the stream's error state; it does not
|
|
||||||
throw unless you enabled exceptions on the stream with `exceptions()`.
|
|
||||||
- `std::ios::binary` writes the text as it is. Without it, Windows writes `\r\n` for every newline of a pretty-printed
|
|
||||||
value.
|
|
||||||
|
|
||||||
## Replacing a file without corrupting it
|
|
||||||
|
|
||||||
The library never writes a file itself, so this section is not about the library. It is general C++ advice for a
|
|
||||||
problem users of the library keep running into.
|
|
||||||
|
|
||||||
A program that crashes or is killed while it writes leaves a truncated file behind. To make sure readers see either the
|
|
||||||
old or the new contents, write to a temporary file in the same directory and rename it over the original
|
|
||||||
(C++17, `#!cpp <filesystem>`):
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#include <filesystem>
|
|
||||||
|
|
||||||
void replace_json_file(const std::filesystem::path& path, const json& j)
|
|
||||||
{
|
|
||||||
std::filesystem::path tmp = path;
|
|
||||||
tmp += ".tmp"; // use a unique name if several processes may write the file
|
|
||||||
|
|
||||||
write_json_file(tmp.string(), j);
|
|
||||||
std::filesystem::rename(tmp, path); // replaces path
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The temporary file must be in the same directory (more precisely, on the same file system) as the target: a rename
|
|
||||||
across file systems does not fall back to a copy, it fails and throws `#!cpp std::filesystem::filesystem_error`. On
|
|
||||||
POSIX systems, the rename atomically replaces the file. Standard C++ has no way to flush a file to the disk (`fsync`),
|
|
||||||
so this protects against a crashed program, but not necessarily against a power loss.
|
|
||||||
|
|
||||||
## Binary formats
|
|
||||||
|
|
||||||
The [binary formats](binary_formats/index.md) work the same way, with the streams opened in binary mode:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// read
|
|
||||||
std::ifstream in("data.cbor", std::ios::binary);
|
|
||||||
json j = json::from_cbor(in);
|
|
||||||
|
|
||||||
// write
|
|
||||||
const std::vector<std::uint8_t> bytes = json::to_cbor(j);
|
|
||||||
std::ofstream out("data.cbor", std::ios::binary);
|
|
||||||
out.write(reinterpret_cast<const char*>(bytes.data()), static_cast<std::streamsize>(bytes.size()));
|
|
||||||
```
|
|
||||||
|
|
||||||
Without `std::ios::binary`, Windows turns the bytes `0x0D 0x0A` into `0x0A` and may stop at a byte `0x1A` while reading,
|
|
||||||
and turns `0x0A` into `0x0D 0x0A` while writing, which corrupts the data.
|
|
||||||
|
|
||||||
## See also
|
|
||||||
|
|
||||||
- [Parsing](parsing/index.md) - the inputs `parse` accepts
|
|
||||||
- [Serialization](serialization.md) - the options of `dump`
|
|
||||||
- [`parse`](../api/basic_json/parse.md), [`dump`](../api/basic_json/dump.md)
|
|
||||||
- [`operator>>`](../api/operator_gtgt.md), [`operator<<`](../api/operator_ltlt.md)
|
|
||||||
@@ -46,8 +46,6 @@ C++ types, and finally serialize it again.
|
|||||||
[BJData](binary_formats/bjdata.md), [BON8](binary_formats/bon8.md), [BSON](binary_formats/bson.md),
|
[BJData](binary_formats/bjdata.md), [BON8](binary_formats/bon8.md), [BSON](binary_formats/bson.md),
|
||||||
[CBOR](binary_formats/cbor.md), [MessagePack](binary_formats/messagepack.md), or [UBJSON](binary_formats/ubjson.md).
|
[CBOR](binary_formats/cbor.md), [MessagePack](binary_formats/messagepack.md), or [UBJSON](binary_formats/ubjson.md).
|
||||||
- [Binary values](binary_values.md) — store and exchange raw byte sequences.
|
- [Binary values](binary_values.md) — store and exchange raw byte sequences.
|
||||||
- [Reading and writing files](files.md) — read and write files safely, including replacing a file without
|
|
||||||
corrupting it.
|
|
||||||
|
|
||||||
## How values are stored and configured
|
## How values are stored and configured
|
||||||
|
|
||||||
|
|||||||
@@ -29,8 +29,7 @@ other three continue on its result.
|
|||||||
|
|
||||||
[`patch`](../api/basic_json/patch.md) leaves the original value unchanged and returns the patched result as a copy.
|
[`patch`](../api/basic_json/patch.md) leaves the original value unchanged and returns the patched result as a copy.
|
||||||
If the document is large and the original value is no longer needed,
|
If the document is large and the original value is no longer needed,
|
||||||
[`patch_inplace`](../api/basic_json/patch_inplace.md) applies the same operations in place instead. Unlike
|
[`patch_inplace`](../api/basic_json/patch_inplace.md) applies the same operations in place instead.
|
||||||
`patch`, it is not atomic: if an operation fails, the operations before it stay applied.
|
|
||||||
|
|
||||||
!!! note "Duplicate member names"
|
!!! note "Duplicate member names"
|
||||||
|
|
||||||
|
|||||||
@@ -160,9 +160,7 @@ document: `#!cpp auto v = json_document::parse(text).root();` does not compile.
|
|||||||
wide. The limit is 16 bytes below 2^32, because the parser reads in 16-byte steps, and a position one step past the
|
wide. The limit is 16 bytes below 2^32, because the parser reads in 16-byte steps, and a position one step past the
|
||||||
end must still fit.
|
end must still fit.
|
||||||
- **A stream is always read to its end.** There is no partial/streaming read of an `#!cpp std::istream`: the stream
|
- **A stream is always read to its end.** There is no partial/streaming read of an `#!cpp std::istream`: the stream
|
||||||
is read to its end into a buffer the document owns, and parsing starts after that. For a file, reading it into a
|
is read to its end into a buffer the document owns, and parsing starts after that.
|
||||||
`#!cpp std::string` yourself and parsing that (an lvalue is borrowed) is therefore not more expensive in memory, and
|
|
||||||
was about 6 to 12 % faster than parsing from an `#!cpp std::ifstream`.
|
|
||||||
- **Fewer source positions after edits.** With
|
- **Fewer source positions after edits.** With
|
||||||
[`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md) enabled,
|
[`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md) enabled,
|
||||||
[`materialize()`](../api/basic_json_view/materialize.md) sets the same positions as `#!cpp basic_json::parse()`.
|
[`materialize()`](../api/basic_json_view/materialize.md) sets the same positions as `#!cpp basic_json::parse()`.
|
||||||
|
|||||||
@@ -35,8 +35,6 @@ std::ifstream f("example.json");
|
|||||||
json data = json::parse(f);
|
json data = json::parse(f);
|
||||||
```
|
```
|
||||||
|
|
||||||
See [Reading and writing files](../files.md) for checking that the file was opened and for writing files safely.
|
|
||||||
|
|
||||||
The input must be encoded in UTF-8; other encodings are not supported. The exception is wide-character input, which
|
The input must be encoded in UTF-8; other encodings are not supported. The exception is wide-character input, which
|
||||||
is read as UTF-16 or UTF-32 — depending on the character type — and converted to UTF-8; see the
|
is read as UTF-16 or UTF-32 — depending on the character type — and converted to UTF-8; see the
|
||||||
[`parse`](../../api/basic_json/parse.md) page for an example. A single input may contain only one JSON value.
|
[`parse`](../../api/basic_json/parse.md) page for an example. A single input may contain only one JSON value.
|
||||||
@@ -80,20 +78,12 @@ them faster than others:
|
|||||||
| `#!cpp FILE*` | one character at a time, with the file locked once for the whole parse |
|
| `#!cpp FILE*` | one character at a time, with the file locked once for the whole parse |
|
||||||
| other iterator ranges (e.g., `#!cpp std::list<char>`, `#!cpp std::istreambuf_iterator<char>`), wide strings | one character at a time |
|
| other iterator ranges (e.g., `#!cpp std::list<char>`, `#!cpp std::istreambuf_iterator<char>`), wide strings | one character at a time |
|
||||||
|
|
||||||
Input in one block of memory is read even faster with the default options (no
|
Parsing a file from a `#!cpp std::ifstream` is therefore only about 10 to 20 % slower than reading the whole file into a
|
||||||
[parser callback](../../api/basic_json/parser_callback_t.md), no comments, no trailing commas, and for `sax_parse()`
|
`#!cpp std::string` first and parsing that, and it needs no memory for a copy of the file. The stream is never read
|
||||||
`strict = true`): `parse()`, `accept()`, and `sax_parse()` then read the grammar and the tokens in one loop over the
|
|
||||||
memory. Only after an error do `parse()` and `sax_parse()` read the input again, to report the error exactly as before.
|
|
||||||
|
|
||||||
Parsing a file from a `#!cpp std::ifstream` therefore takes about 1.15 to 1.45 times as long as reading the whole file
|
|
||||||
into a `#!cpp std::string` first and parsing that, but it needs no memory for a copy of the file. The stream is never read
|
|
||||||
ahead: after parsing, it is positioned exactly as if every character had been read one at a time, so
|
ahead: after parsing, it is positioned exactly as if every character had been read one at a time, so
|
||||||
[`operator>>`](../../api/operator_gtgt.md) can still read several values from one stream (see also
|
[`operator>>`](../../api/operator_gtgt.md) can still read several values from one stream (see also
|
||||||
[JSON Lines](json_lines.md)).
|
[JSON Lines](json_lines.md)).
|
||||||
|
|
||||||
This does not hold for [`json_document`](../json_view.md): it reads a stream into a buffer it owns, so the stream saves
|
|
||||||
no memory, and reading the file into a `#!cpp std::string` yourself and borrowing it is about 6 to 12 % faster.
|
|
||||||
|
|
||||||
Two macros switch these optimizations off without changing any result:
|
Two macros switch these optimizations off without changing any result:
|
||||||
[`JSON_DISABLE_STREAM_BULK_SCAN`](../../api/macros/json_disable_stream_bulk_scan.md) for stream buffers that do not
|
[`JSON_DISABLE_STREAM_BULK_SCAN`](../../api/macros/json_disable_stream_bulk_scan.md) for stream buffers that do not
|
||||||
follow the usual `std::streambuf` contract, and
|
follow the usual `std::streambuf` contract, and
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ up. Nothing is read ahead, so the stream is left exactly where it would be after
|
|||||||
[`operator>>`](../api/operator_gtgt.md) can still read several values from one stream. A stream buffer without a get
|
[`operator>>`](../api/operator_gtgt.md) can still read several values from one stream. A stream buffer without a get
|
||||||
area is read one character at a time. A `#!cpp FILE*` is still read one character at a time, but `file_input_adapter`
|
area is read one character at a time. A `#!cpp FILE*` is still read one character at a time, but `file_input_adapter`
|
||||||
locks the file once per parse instead of once per character. See
|
locks the file once per parse instead of once per character. See
|
||||||
[input sources and performance](parsing/index.md#input-sources-and-performance) for the full table. Parsing from a
|
[input sources and performance](parsing/index.md#input-sources-and-performance) for the full table. According to the
|
||||||
stream takes about 1.15 to 1.45 times as long as parsing an equivalent `#!cpp std::string`, which `parse()` reads with
|
description of [#5828](https://github.com/nlohmann/json/pull/5828), parsing from a stream is within about 10 to 20 % of
|
||||||
a parser of its own (see there).
|
parsing an equivalent `#!cpp std::string`.
|
||||||
|
|
||||||
Inputs that are still read one character at a time: iterator ranges that are not contiguous (e.g.
|
Inputs that are still read one character at a time: iterator ranges that are not contiguous (e.g.
|
||||||
`#!cpp std::list<char>` or `#!cpp std::istreambuf_iterator<char>`), wide strings, and user-defined adapters. An
|
`#!cpp std::list<char>` or `#!cpp std::istreambuf_iterator<char>`), wide strings, and user-defined adapters. An
|
||||||
@@ -39,9 +39,7 @@ iterator pair over non-contiguous but random-access storage (e.g. `#!cpp std::de
|
|||||||
optimization but not the second, since the byte-scanning fast path requires contiguous storage.
|
optimization but not the second, since the byte-scanning fast path requires contiguous storage.
|
||||||
|
|
||||||
Practically: a file can be passed directly to `parse`, which also avoids the memory for a copy of the file. Reading it
|
Practically: a file can be passed directly to `parse`, which also avoids the memory for a copy of the file. Reading it
|
||||||
into a string first is no longer necessary for speed (this is for `#!cpp json::parse`; a
|
into a string first is no longer necessary for speed:
|
||||||
[`json_document`](json_view.md) reads a stream into an owned buffer anyway, so for it reading the file into a string
|
|
||||||
and borrowing that is the faster way):
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// fast: the stream buffer's get area is scanned in bulk
|
// fast: the stream buffer's get area is scanned in bulk
|
||||||
|
|||||||
@@ -17,9 +17,6 @@ To write a value directly to a stream (for example, a file or `#!cpp std::cout`)
|
|||||||
std::cout << j << std::endl;
|
std::cout << j << std::endl;
|
||||||
```
|
```
|
||||||
|
|
||||||
To write a file without leaving a truncated file behind when serialization fails, see
|
|
||||||
[Reading and writing files](files.md#writing-a-json-file).
|
|
||||||
|
|
||||||
!!! note "String, not raw value"
|
!!! note "String, not raw value"
|
||||||
|
|
||||||
`dump` always returns a **JSON text**. Serializing a JSON string therefore includes the surrounding quotes and
|
`dump` always returns a **JSON text**. Serializing a JSON string therefore includes the surrounding quotes and
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ in the source files that need it, instantiate `basic_json` once, and consider pr
|
|||||||
The parser did not see any character: the file was most likely not opened. A relative path is resolved against the
|
The parser did not see any character: the file was most likely not opened. A relative path is resolved against the
|
||||||
working directory of the running program, which is often the build directory of your IDE, not the source directory.
|
working directory of the running program, which is often the build directory of your IDE, not the source directory.
|
||||||
Check `is_open()` and try an absolute path. Other causes are an empty file and a stream that was already read. See
|
Check `is_open()` and try an absolute path. Other causes are an empty file and a stream that was already read. See
|
||||||
[`parse_error.101`](exceptions.md#jsonexceptionparse_error101) and [Reading and writing files](../features/files.md).
|
[`parse_error.101`](exceptions.md#jsonexceptionparse_error101).
|
||||||
|
|
||||||
### Input that arrives in chunks
|
### Input that arrives in chunks
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
<svg class="nj-fig nj-charts nj-charts--compile" viewBox="0 0 470 620" role="img" aria-labelledby="nj-charts-ct nj-charts-ct-d" xmlns="http://www.w3.org/2000/svg">
|
<svg class="nj-fig nj-charts nj-charts--compile" viewBox="0 0 470 516" role="img" aria-labelledby="nj-charts-ct nj-charts-ct-d" xmlns="http://www.w3.org/2000/svg">
|
||||||
<title id="nj-charts-ct">Change in compile time against the default configuration at -O0, Apple clang and G​CC 16</title>
|
<title id="nj-charts-ct">Change in compile time against the default configuration at -O0, Apple clang and G​CC 16</title>
|
||||||
<desc id="nj-charts-ct-d">Grouped horizontal bars, negative is faster. json_fwd.hpp instead of json.hpp on a header-only translation unit: Apple clang -62 %, G​CC 16 -82 %; extern template on a parsing translation unit: Apple clang -7 %, G​CC 16 -21 %; JSON_NO_AUTOMATIC_UDLS on a model translation unit: Apple clang -11 %, G​CC 16 -11 %; JSON_NO_AUTOMATIC_UDLS on a parsing translation unit: Apple clang -1 %, G​CC 16 -2 %; JSON_HAS_FILESYSTEM=0 + JSON_NO_IO on a model translation unit: Apple clang -8 %, G​CC 16 -11 %; JSON_HAS_FILESYSTEM=0 on a model translation unit: Apple clang -5 %, G​CC 16 -9 %; JSON_HAS_STD_FORMAT=0 on a model translation unit: Apple clang -1 %, G​CC 16 -4 %; JSON_HAS_RANGES=0 on a model translation unit: Apple clang -4 %, G​CC 16 -1 %; JSON_NO_IO on a model translation unit: Apple clang -2 %, G​CC 16 -2 %; JSON_USE_GLOBAL_UDLS=0 on a model translation unit: Apple clang 0 %, G​CC 16 -1 %.</desc>
|
<desc id="nj-charts-ct-d">Grouped horizontal bars, negative is faster. json_fwd.hpp instead of json.hpp on a header-only translation unit: Apple clang -62 %, G​CC 16 -82 %; extern template on a parsing translation unit: Apple clang -7 %, G​CC 16 -21 %; JSON_NO_AUTOMATIC_UDLS on a model translation unit: Apple clang -11 %, G​CC 16 -11 %; JSON_NO_AUTOMATIC_UDLS on a parsing translation unit: Apple clang -1 %, G​CC 16 -2 %; JSON_HAS_FILESYSTEM=0 + JSON_NO_IO on a model translation unit: Apple clang -8 %, G​CC 16 -11 %; JSON_HAS_FILESYSTEM=0 on a model translation unit: Apple clang -5 %, G​CC 16 -9 %; JSON_NO_IO on a model translation unit: Apple clang -2 %, G​CC 16 -2 %; JSON_USE_GLOBAL_UDLS=0 on a model translation unit: Apple clang 0 %, G​CC 16 -1 %.</desc>
|
||||||
<rect class="nj-charts-clang" x="8" y="10" width="12" height="12" rx="3"/>
|
<rect class="nj-charts-clang" x="8" y="10" width="12" height="12" rx="3"/>
|
||||||
<text class="nj-charts-leg" x="26" y="20.5">Apple clang</text>
|
<text class="nj-charts-leg" x="26" y="20.5">Apple clang</text>
|
||||||
<rect class="nj-charts-gcc" x="126" y="10" width="12" height="12" rx="3"/>
|
<rect class="nj-charts-gcc" x="126" y="10" width="12" height="12" rx="3"/>
|
||||||
<text class="nj-charts-leg" x="144" y="20.5">G​CC 16</text>
|
<text class="nj-charts-leg" x="144" y="20.5">G​CC 16</text>
|
||||||
<line class="nj-charts-grid" x1="47.4" y1="58" x2="47.4" y2="576"/>
|
<line class="nj-charts-grid" x1="47.4" y1="58" x2="47.4" y2="472"/>
|
||||||
<text class="nj-charts-tick" x="47.4" y="592">−80 %</text>
|
<text class="nj-charts-tick" x="47.4" y="488">−80 %</text>
|
||||||
<line class="nj-charts-grid" x1="126.2" y1="58" x2="126.2" y2="576"/>
|
<line class="nj-charts-grid" x1="126.2" y1="58" x2="126.2" y2="472"/>
|
||||||
<text class="nj-charts-tick" x="126.2" y="592">−60 %</text>
|
<text class="nj-charts-tick" x="126.2" y="488">−60 %</text>
|
||||||
<line class="nj-charts-grid" x1="205" y1="58" x2="205" y2="576"/>
|
<line class="nj-charts-grid" x1="205" y1="58" x2="205" y2="472"/>
|
||||||
<text class="nj-charts-tick" x="205" y="592">−40 %</text>
|
<text class="nj-charts-tick" x="205" y="488">−40 %</text>
|
||||||
<line class="nj-charts-grid" x1="283.8" y1="58" x2="283.8" y2="576"/>
|
<line class="nj-charts-grid" x1="283.8" y1="58" x2="283.8" y2="472"/>
|
||||||
<text class="nj-charts-tick" x="283.8" y="592">−20 %</text>
|
<text class="nj-charts-tick" x="283.8" y="488">−20 %</text>
|
||||||
<text class="nj-charts-tick" x="362.6" y="592">0 %</text>
|
<text class="nj-charts-tick" x="362.6" y="488">0 %</text>
|
||||||
<text class="nj-charts-tick nj-charts-tick--l" x="8" y="610">← faster</text>
|
<text class="nj-charts-tick nj-charts-tick--l" x="8" y="506">← faster</text>
|
||||||
<text class="nj-charts-tick nj-charts-tick--r" x="462" y="610">slower →</text>
|
<text class="nj-charts-tick nj-charts-tick--r" x="462" y="506">slower →</text>
|
||||||
<text class="nj-charts-rowlabel" x="8" y="62"><tspan class="nj-charts-code">json_fwd.hpp instead of json.hpp</tspan><tspan class="nj-charts-sub"> · header-only TU</tspan></text>
|
<text class="nj-charts-rowlabel" x="8" y="62"><tspan class="nj-charts-code">json_fwd.hpp instead of json.hpp</tspan><tspan class="nj-charts-sub"> · header-only TU</tspan></text>
|
||||||
<path class="nj-charts-clang" d="M362.6 70 H122.32 Q118.32 70 118.32 74 V78 Q118.32 82 122.32 82 H362.6 Z"><title>json_fwd.hpp instead of json.hpp, header-only TU (Apple clang): −62 %</title></path>
|
<path class="nj-charts-clang" d="M362.6 70 H122.32 Q118.32 70 118.32 74 V78 Q118.32 82 122.32 82 H362.6 Z"><title>json_fwd.hpp instead of json.hpp, header-only TU (Apple clang): −62 %</title></path>
|
||||||
<text class="nj-charts-val" x="367.6" y="80.5">−62 %</text>
|
<text class="nj-charts-val" x="367.6" y="80.5">−62 %</text>
|
||||||
@@ -46,25 +46,15 @@
|
|||||||
<text class="nj-charts-val" x="367.6" y="340.5">−5 %</text>
|
<text class="nj-charts-val" x="367.6" y="340.5">−5 %</text>
|
||||||
<path class="nj-charts-gcc" d="M362.6 344 H331.14 Q327.14 344 327.14 348 V352 Q327.14 356 331.14 356 H362.6 Z"><title>JSON_HAS_FILESYSTEM=0, model TU (G​CC 16): −9 %</title></path>
|
<path class="nj-charts-gcc" d="M362.6 344 H331.14 Q327.14 344 327.14 348 V352 Q327.14 356 331.14 356 H362.6 Z"><title>JSON_HAS_FILESYSTEM=0, model TU (G​CC 16): −9 %</title></path>
|
||||||
<text class="nj-charts-val" x="367.6" y="354.5">−9 %</text>
|
<text class="nj-charts-val" x="367.6" y="354.5">−9 %</text>
|
||||||
<text class="nj-charts-rowlabel" x="8" y="374"><tspan class="nj-charts-code">JSON_HAS_STD_FORMAT=0</tspan><tspan class="nj-charts-sub"> · model TU</tspan></text>
|
<text class="nj-charts-rowlabel" x="8" y="374"><tspan class="nj-charts-code">JSON_NO_IO</tspan><tspan class="nj-charts-sub"> · model TU*</tspan></text>
|
||||||
<path class="nj-charts-clang" d="M362.6 382 H362.6 Q358.66 382 358.66 385.94 V390.06 Q358.66 394 362.6 394 H362.6 Z"><title>JSON_HAS_STD_FORMAT=0, model TU (Apple clang): −1 %</title></path>
|
<path class="nj-charts-clang" d="M362.6 382 H358.72 Q354.72 382 354.72 386 V390 Q354.72 394 358.72 394 H362.6 Z"><title>JSON_NO_IO, model TU (Apple clang): −2 %</title></path>
|
||||||
<text class="nj-charts-val" x="367.6" y="392.5">−1 %</text>
|
<text class="nj-charts-val" x="367.6" y="392.5">−2 %</text>
|
||||||
<path class="nj-charts-gcc" d="M362.6 396 H350.84 Q346.84 396 346.84 400 V404 Q346.84 408 350.84 408 H362.6 Z"><title>JSON_HAS_STD_FORMAT=0, model TU (G​CC 16): −4 %</title></path>
|
<path class="nj-charts-gcc" d="M362.6 396 H358.72 Q354.72 396 354.72 400 V404 Q354.72 408 358.72 408 H362.6 Z"><title>JSON_NO_IO, model TU (G​CC 16): −2 %</title></path>
|
||||||
<text class="nj-charts-val" x="367.6" y="406.5">−4 %</text>
|
<text class="nj-charts-val" x="367.6" y="406.5">−2 %</text>
|
||||||
<text class="nj-charts-rowlabel" x="8" y="426"><tspan class="nj-charts-code">JSON_HAS_RANGES=0</tspan><tspan class="nj-charts-sub"> · model TU</tspan></text>
|
<text class="nj-charts-rowlabel" x="8" y="426"><tspan class="nj-charts-code">JSON_USE_GLOBAL_UDLS=0</tspan><tspan class="nj-charts-sub"> · model TU*</tspan></text>
|
||||||
<path class="nj-charts-clang" d="M362.6 434 H350.84 Q346.84 434 346.84 438 V442 Q346.84 446 350.84 446 H362.6 Z"><title>JSON_HAS_RANGES=0, model TU (Apple clang): −4 %</title></path>
|
<path class="nj-charts-clang" d="M362.6 434 h0.5 v12 h-0.5 Z"><title>JSON_USE_GLOBAL_UDLS=0, model TU (Apple clang): 0 %</title></path>
|
||||||
<text class="nj-charts-val" x="367.6" y="444.5">−4 %</text>
|
<text class="nj-charts-val" x="367.6" y="444.5">0 %</text>
|
||||||
<path class="nj-charts-gcc" d="M362.6 448 H362.6 Q358.66 448 358.66 451.94 V456.06 Q358.66 460 362.6 460 H362.6 Z"><title>JSON_HAS_RANGES=0, model TU (G​CC 16): −1 %</title></path>
|
<path class="nj-charts-gcc" d="M362.6 448 H362.6 Q358.66 448 358.66 451.94 V456.06 Q358.66 460 362.6 460 H362.6 Z"><title>JSON_USE_GLOBAL_UDLS=0, model TU (G​CC 16): −1 %</title></path>
|
||||||
<text class="nj-charts-val" x="367.6" y="458.5">−1 %</text>
|
<text class="nj-charts-val" x="367.6" y="458.5">−1 %</text>
|
||||||
<text class="nj-charts-rowlabel" x="8" y="478"><tspan class="nj-charts-code">JSON_NO_IO</tspan><tspan class="nj-charts-sub"> · model TU*</tspan></text>
|
<line class="nj-charts-zero" x1="362.6" y1="58" x2="362.6" y2="472"/>
|
||||||
<path class="nj-charts-clang" d="M362.6 486 H358.72 Q354.72 486 354.72 490 V494 Q354.72 498 358.72 498 H362.6 Z"><title>JSON_NO_IO, model TU (Apple clang): −2 %</title></path>
|
|
||||||
<text class="nj-charts-val" x="367.6" y="496.5">−2 %</text>
|
|
||||||
<path class="nj-charts-gcc" d="M362.6 500 H358.72 Q354.72 500 354.72 504 V508 Q354.72 512 358.72 512 H362.6 Z"><title>JSON_NO_IO, model TU (G​CC 16): −2 %</title></path>
|
|
||||||
<text class="nj-charts-val" x="367.6" y="510.5">−2 %</text>
|
|
||||||
<text class="nj-charts-rowlabel" x="8" y="530"><tspan class="nj-charts-code">JSON_USE_GLOBAL_UDLS=0</tspan><tspan class="nj-charts-sub"> · model TU*</tspan></text>
|
|
||||||
<path class="nj-charts-clang" d="M362.6 538 h0.5 v12 h-0.5 Z"><title>JSON_USE_GLOBAL_UDLS=0, model TU (Apple clang): 0 %</title></path>
|
|
||||||
<text class="nj-charts-val" x="367.6" y="548.5">0 %</text>
|
|
||||||
<path class="nj-charts-gcc" d="M362.6 552 H362.6 Q358.66 552 358.66 555.94 V560.06 Q358.66 564 362.6 564 H362.6 Z"><title>JSON_USE_GLOBAL_UDLS=0, model TU (G​CC 16): −1 %</title></path>
|
|
||||||
<text class="nj-charts-val" x="367.6" y="562.5">−1 %</text>
|
|
||||||
<line class="nj-charts-zero" x1="362.6" y1="58" x2="362.6" y2="576"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 7.5 KiB |
@@ -11,11 +11,6 @@ that cost, ordered by how much they typically save.
|
|||||||
16.2.0 on macOS (Apple M1 Max). All configurations were run interleaved, in alternating order. They show the order
|
16.2.0 on macOS (Apple M1 Max). All configurations were run interleaved, in alternating order. They show the order
|
||||||
of magnitude to expect; measure your own code before and after a change.
|
of magnitude to expect; measure your own code before and after a change.
|
||||||
|
|
||||||
The two sections on `std::format` and ranges apply to C++20 only and were measured with `-std=c++20`, so their
|
|
||||||
baselines are slower than the others. Their differences are mostly within the run-to-run noise of a few percent:
|
|
||||||
two complete runs of the same configuration differed by up to 10% in the median. The tables show the later run (one
|
|
||||||
outlier cell was repeated).
|
|
||||||
|
|
||||||
The translation units are listed below. They replace the ones used for earlier versions of this page, whose
|
The translation units are listed below. They replace the ones used for earlier versions of this page, whose
|
||||||
sources were not published, so the numbers differ from earlier versions.
|
sources were not published, so the numbers differ from earlier versions.
|
||||||
|
|
||||||
@@ -71,8 +66,7 @@ that cost, ordered by how much they typically save.
|
|||||||
--8<-- "images/compile_times.svg"
|
--8<-- "images/compile_times.svg"
|
||||||
</div>
|
</div>
|
||||||
<p class="nj-charts-caption" markdown>Change in compile time at `-O0` against the default configuration of the same
|
<p class="nj-charts-caption" markdown>Change in compile time at `-O0` against the default configuration of the same
|
||||||
translation unit, taken from the tables below (medians of nine runs, see above; the `std::format` and ranges rows are
|
translation unit, taken from the tables below (medians of nine runs, see above). * computed from the milliseconds in
|
||||||
measured with `-std=c++20`). * computed from the milliseconds in
|
|
||||||
[Options without effect on compile times](#options-without-effect-on-compile-times).</p>
|
[Options without effect on compile times](#options-without-effect-on-compile-times).</p>
|
||||||
|
|
||||||
## Include `json_fwd.hpp` in headers
|
## Include `json_fwd.hpp` in headers
|
||||||
@@ -220,88 +214,6 @@ from and to `std::string`; whether that fallback exists depends on the platform'
|
|||||||
(`std::wstring` on Windows), so check code that converts paths. `JSON_NO_IO` additionally removes all stream
|
(`std::wstring` on Windows), so check code that converts paths. `JSON_NO_IO` additionally removes all stream
|
||||||
functions (see [Options without effect](#options-without-effect-on-compile-times)).
|
functions (see [Options without effect](#options-without-effect-on-compile-times)).
|
||||||
|
|
||||||
## Switch off `std::format` support
|
|
||||||
|
|
||||||
With C++20 and a standard library that provides `<format>`, the library includes it to provide a
|
|
||||||
[`std::formatter<basic_json>`](../api/basic_json/std_formatter.md) specialization (see
|
|
||||||
[`JSON_HAS_STD_FORMAT`](../api/macros/json_has_std_format.md)). If you do not format JSON values with `std::format`,
|
|
||||||
switch the detection off for the whole project:
|
|
||||||
|
|
||||||
```cmake
|
|
||||||
target_compile_definitions(my_target PRIVATE JSON_HAS_STD_FORMAT=0)
|
|
||||||
```
|
|
||||||
|
|
||||||
| Compiler | Translation unit | Default (`-O0` / `-O2`) | `std::format` off (`-O0` / `-O2`) | Change |
|
|
||||||
|-------------|------------------|------------------------:|----------------------------------:|-----------:|
|
|
||||||
| Apple clang | model | 1283 ms / 1544 ms | 1269 ms / 1539 ms | −1% / 0% |
|
|
||||||
| GCC 16 | model | 2026 ms / 2482 ms | 1944 ms / 2416 ms | −4% / −3% |
|
|
||||||
| Apple clang | parsing | 1350 ms / 2278 ms | 1347 ms / 2300 ms | 0% / +1% |
|
|
||||||
| GCC 16 | parsing | 2715 ms / 4351 ms | 2644 ms / 4287 ms | −3% / −1% |
|
|
||||||
|
|
||||||
<p class="nj-table-caption" markdown>Compile time with `JSON_HAS_STD_FORMAT=0` against the default configuration, both with `-std=c++20`, at `-O0` and `-O2`.</p>
|
|
||||||
|
|
||||||
The saving is small, and only GCC shows it: libstdc++'s `<format>` is an additional header that nothing else in the
|
|
||||||
translation unit includes, so the macro removes it (and the headers it pulls in). With libc++ (Apple clang), other
|
|
||||||
standard headers include `<format>` anyway, so the macro only removes the library's own formatter, and the difference is
|
|
||||||
within the noise.
|
|
||||||
|
|
||||||
The trade-off: the `std::formatter<basic_json>` specialization is removed, so `#!cpp std::format("{}", j)`,
|
|
||||||
and `#!cpp std::format_to` with a JSON value no longer compile (in a test with both compilers on
|
|
||||||
macOS, the error was that `std::formatter` must be specialized for the type). Use [`dump()`](../api/basic_json/dump.md),
|
|
||||||
or the stream operators, instead.
|
|
||||||
|
|
||||||
## Switch off ranges support
|
|
||||||
|
|
||||||
With C++20 and a standard library that provides `<ranges>`, the library includes it to declare the
|
|
||||||
[`items()`](../api/basic_json/items.md) proxy a *borrowed range*, and uses the `std::ranges` concepts and algorithms for
|
|
||||||
construction from range views and for iterator input (see [`JSON_HAS_RANGES`](../api/macros/json_has_ranges.md)). If you
|
|
||||||
do not need these, switch the detection off for the whole project:
|
|
||||||
|
|
||||||
```cmake
|
|
||||||
target_compile_definitions(my_target PRIVATE JSON_HAS_RANGES=0)
|
|
||||||
```
|
|
||||||
|
|
||||||
| Compiler | Translation unit | Default (`-O0` / `-O2`) | Ranges off (`-O0` / `-O2`) | Change |
|
|
||||||
|-------------|------------------|------------------------:|---------------------------:|-----------:|
|
|
||||||
| Apple clang | model | 1283 ms / 1544 ms | 1237 ms / 1495 ms | −4% / −3% |
|
|
||||||
| GCC 16 | model | 2026 ms / 2482 ms | 2004 ms / 2464 ms | −1% / −1% |
|
|
||||||
| Apple clang | parsing | 1350 ms / 2278 ms | 1311 ms / 2284 ms | −3% / 0% |
|
|
||||||
| GCC 16 | parsing | 2715 ms / 4351 ms | 2705 ms / 4427 ms | 0% / +2% |
|
|
||||||
|
|
||||||
<p class="nj-table-caption" markdown>Compile time with `JSON_HAS_RANGES=0` against the default configuration, both with `-std=c++20`, at `-O0` and `-O2`.</p>
|
|
||||||
|
|
||||||
The saving is small as well, and with GCC it is not distinguishable from the noise: its `<format>` includes `<span>`,
|
|
||||||
so with `std::format` support left on, part of the ranges machinery is still parsed.
|
|
||||||
Both macros together save more than either alone:
|
|
||||||
|
|
||||||
```cmake
|
|
||||||
target_compile_definitions(my_target PRIVATE JSON_HAS_STD_FORMAT=0 JSON_HAS_RANGES=0)
|
|
||||||
```
|
|
||||||
|
|
||||||
| Compiler | Translation unit | Default (`-O0` / `-O2`) | Both off (`-O0` / `-O2`) | Change |
|
|
||||||
|-------------|------------------|------------------------:|-------------------------:|-----------:|
|
|
||||||
| Apple clang | model | 1283 ms / 1544 ms | 1223 ms / 1504 ms | −5% / −3% |
|
|
||||||
| GCC 16 | model | 2026 ms / 2482 ms | 1914 ms / 2378 ms | −6% / −4% |
|
|
||||||
| Apple clang | parsing | 1350 ms / 2278 ms | 1311 ms / 2271 ms | −3% / 0% |
|
|
||||||
| GCC 16 | parsing | 2715 ms / 4351 ms | 2635 ms / 4260 ms | −3% / −2% |
|
|
||||||
|
|
||||||
<p class="nj-table-caption" markdown>Compile time with `JSON_HAS_STD_FORMAT=0` and `JSON_HAS_RANGES=0` against the default configuration, both with `-std=c++20`, at `-O0` and `-O2`.</p>
|
|
||||||
|
|
||||||
The trade-offs of `JSON_HAS_RANGES=0` are larger than those of the other options on this page:
|
|
||||||
|
|
||||||
- `items()` is no longer a borrowed range. On a temporary, `#!cpp std::ranges::begin(make_json().items())` no longer
|
|
||||||
compiles, and `std::ranges` algorithms such as `#!cpp std::ranges::find_if(make_json().items(), pred)` return
|
|
||||||
`std::ranges::dangling` instead of an iterator, so code that uses the result no longer compiles. Calling them on
|
|
||||||
`#!cpp j.items()` for a named `j` is not affected.
|
|
||||||
- [`JSON_HAS_RANGE_VIEW_CONVERSION`](../api/macros/json_has_range_view_conversion.md) becomes `0`, so a JSON array can no
|
|
||||||
longer be constructed from a `std::ranges::view` such as the result of `std::views::filter`. The assignment
|
|
||||||
`#!cpp json j = v | std::views::filter(pred);` no longer compiles.
|
|
||||||
- Parsing from an iterator pair loses the C++20 fast path for contiguous iterators that are not pointers (for
|
|
||||||
example those of `std::vector`) and falls back to byte-at-a-time scanning. The result is the same; parsing may be
|
|
||||||
slower (not measured here).
|
|
||||||
|
|
||||||
The first two were checked with both compilers on macOS; the third is read from the library's source.
|
|
||||||
|
|
||||||
## Use C++20 modules
|
## Use C++20 modules
|
||||||
|
|
||||||
With a toolchain that supports named modules, `import nlohmann.json;` compiles the library once into a module and
|
With a toolchain that supports named modules, `import nlohmann.json;` compiles the library once into a module and
|
||||||
@@ -431,8 +343,6 @@ and −2% / 0% (GCC). To get a saving, combine it with
|
|||||||
|
|
||||||
- [`JSON_HAS_FILESYSTEM`](../api/macros/json_has_filesystem.md) - control the conversions from and to
|
- [`JSON_HAS_FILESYSTEM`](../api/macros/json_has_filesystem.md) - control the conversions from and to
|
||||||
`std::filesystem::path`
|
`std::filesystem::path`
|
||||||
- [`JSON_HAS_STD_FORMAT`](../api/macros/json_has_std_format.md) - control the `std::formatter` specialization
|
|
||||||
- [`JSON_HAS_RANGES`](../api/macros/json_has_ranges.md) - control the use of `std::ranges`
|
|
||||||
- [`JSON_NO_AUTOMATIC_UDLS`](../api/macros/json_no_automatic_udls.md) - do not include the user-defined string
|
- [`JSON_NO_AUTOMATIC_UDLS`](../api/macros/json_no_automatic_udls.md) - do not include the user-defined string
|
||||||
literals automatically
|
literals automatically
|
||||||
- [Modules](../features/modules.md) - C++20 module support
|
- [Modules](../features/modules.md) - C++20 module support
|
||||||
|
|||||||
@@ -130,7 +130,6 @@ nav:
|
|||||||
- features/element_access/unchecked_access.md
|
- features/element_access/unchecked_access.md
|
||||||
- features/element_access/checked_access.md
|
- features/element_access/checked_access.md
|
||||||
- features/element_access/default_value.md
|
- features/element_access/default_value.md
|
||||||
- features/files.md
|
|
||||||
- features/iterators.md
|
- features/iterators.md
|
||||||
- features/merge_patch.md
|
- features/merge_patch.md
|
||||||
- features/json_patch.md
|
- features/json_patch.md
|
||||||
|
|||||||
@@ -73,8 +73,6 @@ COMPILE = [
|
|||||||
("JSON_NO_AUTOMATIC_UDLS", "parsing TU", -1, -2, False),
|
("JSON_NO_AUTOMATIC_UDLS", "parsing TU", -1, -2, False),
|
||||||
("JSON_HAS_FILESYSTEM=0 + JSON_NO_IO", "model TU", -8, -11, False),
|
("JSON_HAS_FILESYSTEM=0 + JSON_NO_IO", "model TU", -8, -11, False),
|
||||||
("JSON_HAS_FILESYSTEM=0", "model TU", -5, -9, False),
|
("JSON_HAS_FILESYSTEM=0", "model TU", -5, -9, False),
|
||||||
("JSON_HAS_STD_FORMAT=0", "model TU", -1, -4, False),
|
|
||||||
("JSON_HAS_RANGES=0", "model TU", -4, -1, False),
|
|
||||||
# computed from the ms in "Options without effect on compile times": 1034/1051, 1756/1801, 1055/1051, 1783/1801
|
# computed from the ms in "Options without effect on compile times": 1034/1051, 1756/1801, 1055/1051, 1783/1801
|
||||||
("JSON_NO_IO", "model TU", round((1034 / 1051 - 1) * 100), round((1756 / 1801 - 1) * 100), True),
|
("JSON_NO_IO", "model TU", round((1034 / 1051 - 1) * 100), round((1756 / 1801 - 1) * 100), True),
|
||||||
("JSON_USE_GLOBAL_UDLS=0", "model TU", round((1055 / 1051 - 1) * 100), round((1783 / 1801 - 1) * 100), True),
|
("JSON_USE_GLOBAL_UDLS=0", "model TU", round((1055 / 1051 - 1) * 100), round((1783 / 1801 - 1) * 100), True),
|
||||||
|
|||||||
@@ -344,8 +344,8 @@ class binary_reader
|
|||||||
nested document could declare a length that disagrees with where its
|
nested document could declare a length that disagrees with where its
|
||||||
terminator actually falls and quietly hand the bytes in between to the
|
terminator actually falls and quietly hand the bytes in between to the
|
||||||
enclosing document. A well-formed document is at least 5 bytes (the prefix
|
enclosing document. A well-formed document is at least 5 bytes (the prefix
|
||||||
plus the terminator); open_bson_document() rejects a smaller size before
|
plus the terminator); the equality also rejects those impossible sizes,
|
||||||
it is stored, so @a document_size is never negative here.
|
since at least 5 bytes are always consumed.
|
||||||
|
|
||||||
@param[in] document_start value of chars_read before the size prefix
|
@param[in] document_start value of chars_read before the size prefix
|
||||||
@param[in] document_size the declared document size
|
@param[in] document_size the declared document size
|
||||||
@@ -353,7 +353,7 @@ class binary_reader
|
|||||||
*/
|
*/
|
||||||
bool check_bson_document_size(const std::size_t document_start, const std::int32_t document_size)
|
bool check_bson_document_size(const std::size_t document_start, const std::int32_t document_size)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(static_cast<std::size_t>(document_size) != chars_read - document_start))
|
if (JSON_HEDLEY_UNLIKELY(document_size < 0 || static_cast<std::size_t>(document_size) != chars_read - document_start))
|
||||||
{
|
{
|
||||||
return bson_document_size_error(document_size, chars_read - document_start);
|
return bson_document_size_error(document_size, chars_read - document_start);
|
||||||
}
|
}
|
||||||
@@ -1024,7 +1024,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
std::uint64_t len{};
|
std::uint64_t len{};
|
||||||
std::size_t size{};
|
std::size_t size{};
|
||||||
return get_cbor_argument(len) && get_container_size(len, size, "array") && enter_cbor_array(size);
|
return get_cbor_argument(len) && get_cbor_container_size(len, size, "array") && enter_cbor_array(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x9F: // array (indefinite length)
|
case 0x9F: // array (indefinite length)
|
||||||
@@ -1064,7 +1064,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
std::uint64_t len{};
|
std::uint64_t len{};
|
||||||
std::size_t size{};
|
std::size_t size{};
|
||||||
return get_cbor_argument(len) && get_container_size(len, size, "map") && enter_object(size);
|
return get_cbor_argument(len) && get_cbor_container_size(len, size, "map") && enter_object(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xBF: // map (indefinite length)
|
case 0xBF: // map (indefinite length)
|
||||||
@@ -1700,7 +1700,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief narrow a definite CBOR or MessagePack array/map length to std::size_t
|
@brief narrow a definite CBOR array/map length to std::size_t
|
||||||
|
|
||||||
A definite length is rejected if it does not fit in std::size_t or if it
|
A definite length is rejected if it does not fit in std::size_t or if it
|
||||||
equals detail::unknown_size(), which is reserved to mark an indefinite-
|
equals detail::unknown_size(), which is reserved to mark an indefinite-
|
||||||
@@ -1713,7 +1713,7 @@ class binary_reader
|
|||||||
@param[in] context "array" or "map", for the error message
|
@param[in] context "array" or "map", for the error message
|
||||||
@return whether the length is usable
|
@return whether the length is usable
|
||||||
*/
|
*/
|
||||||
bool get_container_size(const std::uint64_t len, std::size_t& result, const char* context)
|
bool get_cbor_container_size(const std::uint64_t len, std::size_t& result, const char* context)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(len) || len == detail::unknown_size()))
|
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(len) || len == detail::unknown_size()))
|
||||||
{
|
{
|
||||||
@@ -2309,8 +2309,7 @@ class binary_reader
|
|||||||
case 0xDD: // array 32
|
case 0xDD: // array 32
|
||||||
{
|
{
|
||||||
std::uint32_t len{};
|
std::uint32_t len{};
|
||||||
std::size_t size{};
|
return get_number(len) && enter_msgpack_array(conditional_static_cast<std::size_t>(len));
|
||||||
return get_number(len) && get_container_size(len, size, "array") && enter_msgpack_array(size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xDE: // map 16
|
case 0xDE: // map 16
|
||||||
@@ -2322,8 +2321,7 @@ class binary_reader
|
|||||||
case 0xDF: // map 32
|
case 0xDF: // map 32
|
||||||
{
|
{
|
||||||
std::uint32_t len{};
|
std::uint32_t len{};
|
||||||
std::size_t size{};
|
return get_number(len) && enter_object(conditional_static_cast<std::size_t>(len));
|
||||||
return get_number(len) && get_container_size(len, size, "map") && enter_object(size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// negative fixint
|
// negative fixint
|
||||||
@@ -3094,13 +3092,16 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
if (size_and_type.second != 0)
|
if (size_and_type.second != 0)
|
||||||
{
|
{
|
||||||
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
if (size_and_type.second != 'N')
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, size_and_type.second)))
|
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
||||||
{
|
{
|
||||||
return false;
|
if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, size_and_type.second)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
dim.push_back(dimlen);
|
||||||
}
|
}
|
||||||
dim.push_back(dimlen);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -3130,27 +3131,6 @@ class binary_reader
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief store the count of an optimized container (or the length of a string)
|
|
||||||
|
|
||||||
A count equal to npos is rejected: npos marks a container whose count is
|
|
||||||
not known, so a count of that value would be read as "no count" (compare
|
|
||||||
get_container_size()). No container can hold that many elements.
|
|
||||||
|
|
||||||
@param[in] count the count read from the input
|
|
||||||
@param[out] result set to @a count unless it is rejected
|
|
||||||
@return whether @a count is usable
|
|
||||||
*/
|
|
||||||
bool store_ubjson_count(const std::size_t count, std::size_t& result)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(count == npos))
|
|
||||||
{
|
|
||||||
return out_of_range_error(exception_id::container_too_large, "integer value overflow", "size");
|
|
||||||
}
|
|
||||||
result = count;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief read a UBJSON/BJData optimized-container count of a signed marker
|
@brief read a UBJSON/BJData optimized-container count of a signed marker
|
||||||
type ('i', 'I', 'l', 'L') and narrow it to std::size_t
|
type ('i', 'I', 'l', 'L') and narrow it to std::size_t
|
||||||
@@ -3181,7 +3161,8 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
return out_of_range_error(exception_id::container_too_large, "integer value overflow", "size");
|
return out_of_range_error(exception_id::container_too_large, "integer value overflow", "size");
|
||||||
}
|
}
|
||||||
return store_ubjson_count(static_cast<std::size_t>(number), result); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
|
result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -3258,7 +3239,8 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return store_ubjson_count(conditional_static_cast<std::size_t>(number), result);
|
result = conditional_static_cast<std::size_t>(number);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'M':
|
case 'M':
|
||||||
@@ -3276,7 +3258,8 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
return out_of_range_error(exception_id::container_too_large, "integer value overflow", "size");
|
return out_of_range_error(exception_id::container_too_large, "integer value overflow", "size");
|
||||||
}
|
}
|
||||||
return store_ubjson_count(detail::conditional_static_cast<std::size_t>(number), result);
|
result = detail::conditional_static_cast<std::size_t>(number);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case '[':
|
case '[':
|
||||||
@@ -3320,9 +3303,11 @@ class binary_reader
|
|||||||
// follow the documented _ArrayType_, _ArraySize_, _ArrayData_ order
|
// follow the documented _ArrayType_, _ArraySize_, _ArrayData_ order
|
||||||
if (ndarray_dtype != 0)
|
if (ndarray_dtype != 0)
|
||||||
{
|
{
|
||||||
// every optimized type marker is_valid_optimized_type() accepts for BJData has a name
|
|
||||||
const char* type_name = bjd_type_name(ndarray_dtype);
|
const char* type_name = bjd_type_name(ndarray_dtype);
|
||||||
JSON_ASSERT(type_name != nullptr);
|
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
|
||||||
|
{
|
||||||
|
return unexpected_byte("invalid byte", "type");
|
||||||
|
}
|
||||||
|
|
||||||
string_t type_key = "_ArrayType_";
|
string_t type_key = "_ArrayType_";
|
||||||
string_t type = type_name; // sax->string() takes a reference
|
string_t type = type_name; // sax->string() takes a reference
|
||||||
@@ -3405,6 +3390,12 @@ class binary_reader
|
|||||||
if (current == '$')
|
if (current == '$')
|
||||||
{
|
{
|
||||||
result.second = get(); // must not ignore 'N', because 'N' maybe the type
|
result.second = get(); // must not ignore 'N', because 'N' maybe the type
|
||||||
|
if (input_format == input_format_t::bjdata
|
||||||
|
&& JSON_HEDLEY_UNLIKELY(is_bjd_excluded_optimized_type(result.second)))
|
||||||
|
{
|
||||||
|
return optimized_type_error();
|
||||||
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("type")))
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("type")))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -3432,7 +3423,7 @@ class binary_reader
|
|||||||
// an ndarray was read here only if the flag flipped; when it was
|
// an ndarray was read here only if the flag flipped; when it was
|
||||||
// seeded true, get_ubjson_size_value() already rejected the nested
|
// seeded true, get_ubjson_size_value() already rejected the nested
|
||||||
// dimension vector
|
// dimension vector
|
||||||
if (is_ndarray && !inside_ndarray)
|
if (input_format == input_format_t::bjdata && is_ndarray && !inside_ndarray)
|
||||||
{
|
{
|
||||||
result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters
|
result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters
|
||||||
}
|
}
|
||||||
@@ -3442,7 +3433,7 @@ class binary_reader
|
|||||||
if (current == '#')
|
if (current == '#')
|
||||||
{
|
{
|
||||||
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
||||||
if (is_ndarray && !inside_ndarray)
|
if (input_format == input_format_t::bjdata && is_ndarray && !inside_ndarray)
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::unexpected_byte, "ndarray requires both type and size", "size");
|
return last_byte_error(exception_id::unexpected_byte, "ndarray requires both type and size", "size");
|
||||||
}
|
}
|
||||||
@@ -3616,6 +3607,7 @@ class binary_reader
|
|||||||
JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
bool parse_ubjson_typed_run(std::size_t& remaining, const char_int_type type, std::true_type /*bulk*/)
|
bool parse_ubjson_typed_run(std::size_t& remaining, const char_int_type type, std::true_type /*bulk*/)
|
||||||
{
|
{
|
||||||
|
const bool bjdata = input_format == input_format_t::bjdata;
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case 'U':
|
case 'U':
|
||||||
@@ -3629,11 +3621,11 @@ class binary_reader
|
|||||||
case 'L':
|
case 'L':
|
||||||
return parse_ubjson_typed_run<std::int64_t>(remaining);
|
return parse_ubjson_typed_run<std::int64_t>(remaining);
|
||||||
case 'u':
|
case 'u':
|
||||||
return parse_ubjson_typed_run<std::uint16_t>(remaining);
|
return !bjdata || parse_ubjson_typed_run<std::uint16_t>(remaining);
|
||||||
case 'm':
|
case 'm':
|
||||||
return parse_ubjson_typed_run<std::uint32_t>(remaining);
|
return !bjdata || parse_ubjson_typed_run<std::uint32_t>(remaining);
|
||||||
case 'M':
|
case 'M':
|
||||||
return parse_ubjson_typed_run<std::uint64_t>(remaining);
|
return !bjdata || parse_ubjson_typed_run<std::uint64_t>(remaining);
|
||||||
case 'd':
|
case 'd':
|
||||||
return parse_ubjson_typed_run<float>(remaining);
|
return parse_ubjson_typed_run<float>(remaining);
|
||||||
case 'D':
|
case 'D':
|
||||||
@@ -3714,7 +3706,7 @@ class binary_reader
|
|||||||
// if bit-8 of size_and_type.second is set to 1, encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata):
|
// if bit-8 of size_and_type.second is set to 1, encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata):
|
||||||
// {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]}
|
// {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]}
|
||||||
|
|
||||||
if ((size_and_type.second & (1 << 8)) != 0)
|
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
||||||
{
|
{
|
||||||
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
||||||
|
|
||||||
@@ -3773,7 +3765,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If BJData type marker is 'B' decode as binary
|
// If BJData type marker is 'B' decode as binary
|
||||||
if (size_and_type.first != npos && size_and_type.second == 'B')
|
if (input_format == input_format_t::bjdata && size_and_type.first != npos && size_and_type.second == 'B')
|
||||||
{
|
{
|
||||||
binary_t result;
|
binary_t result;
|
||||||
return get_binary(size_and_type.first, result) && sax->binary(result);
|
return get_binary(size_and_type.first, result) && sax->binary(result);
|
||||||
@@ -3822,7 +3814,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
// do not accept ND-array size in objects in BJData
|
// do not accept ND-array size in objects in BJData
|
||||||
if ((size_and_type.second & (1 << 8)) != 0)
|
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::unexpected_byte, "BJData object does not support ND-array size in optimized format", "object");
|
return last_byte_error(exception_id::unexpected_byte, "BJData object does not support ND-array size in optimized format", "object");
|
||||||
}
|
}
|
||||||
@@ -5103,9 +5095,8 @@ class binary_reader
|
|||||||
JSON_PRIVATE_UNLESS_TESTED:
|
JSON_PRIVATE_UNLESS_TESTED:
|
||||||
/*!
|
/*!
|
||||||
@brief check whether @a marker is a type marker that may follow '$'
|
@brief check whether @a marker is a type marker that may follow '$'
|
||||||
UBJSON: Z N T F i U I l L d D H C S [ {; BJData: the numeric markers and C.
|
UBJSON: Z N T F i U I l L d D H C S [ {; BJData: the numeric markers and C
|
||||||
The markers BJData excludes (F H N S T Z [ {) are the ones
|
(BJData's excluded markers are rejected separately).
|
||||||
binary_writer::is_bjdata_excluded_type_marker() lists; keep the two in sync.
|
|
||||||
*/
|
*/
|
||||||
bool is_valid_optimized_type(const char_int_type marker) const noexcept
|
bool is_valid_optimized_type(const char_int_type marker) const noexcept
|
||||||
{
|
{
|
||||||
@@ -5140,6 +5131,20 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief whether @a marker is excluded from BJData's optimized ND-array types
|
||||||
|
@return whether @a marker is one of 'F', 'H', 'N', 'S', 'T', 'Z', '[', '{'
|
||||||
|
|
||||||
|
Mirrors binary_writer's @ref binary_writer::is_bjdata_excluded_type_marker
|
||||||
|
"is_bjdata_excluded_type_marker()`, which encodes the same list the other
|
||||||
|
way; keep the two in sync.
|
||||||
|
*/
|
||||||
|
static constexpr bool is_bjd_excluded_optimized_type(const char_int_type marker) noexcept
|
||||||
|
{
|
||||||
|
return marker == '[' || marker == '{' || marker == 'S' || marker == 'H'
|
||||||
|
|| marker == 'T' || marker == 'F' || marker == 'N' || marker == 'Z';
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief look up the ND-array element type name for a BJData dtype marker
|
@brief look up the ND-array element type name for a BJData dtype marker
|
||||||
@return the type name ("uint8", "int8", ...), or nullptr if @a marker does
|
@return the type name ("uint8", "int8", ...), or nullptr if @a marker does
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -904,8 +904,7 @@ class json_sax_dom_parser : public json_sax_dom_error_state
|
|||||||
{
|
{
|
||||||
hashes.push_back(hasher(element.first));
|
hashes.push_back(hasher(element.first));
|
||||||
}
|
}
|
||||||
std::unique_ptr<object_key_index> index(new object_key_index(std::move(hashes))); // NOLINT(cppcoreguidelines-owning-memory,modernize-make-unique): std::make_unique is C++14
|
ordered_data.key_indexes.emplace_back(new object_key_index(std::move(hashes)));
|
||||||
ordered_data.key_indexes.push_back(std::move(index));
|
|
||||||
f.ordered.index = ordered_data.key_indexes.back().get();
|
f.ordered.index = ordered_data.key_indexes.back().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -316,30 +316,6 @@ JSON_HEDLEY_ALWAYS_INLINE std::size_t accumulate_digits(const char* data, std::s
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// the index of the first byte at or after @a i that is not a digit: the
|
|
||||||
/// digits of accumulate_digits() without their value
|
|
||||||
JSON_HEDLEY_ALWAYS_INLINE std::size_t skip_digits(const char* data, std::size_t i, std::size_t avail) noexcept
|
|
||||||
{
|
|
||||||
while (avail - i >= 8)
|
|
||||||
{
|
|
||||||
if (!is_eight_digits(read_eight_bytes(data + i)))
|
|
||||||
{
|
|
||||||
// a byte of the word is not a digit: no bounds checks before it
|
|
||||||
while (static_cast<unsigned char>(data[i] - '0') <= 9)
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
i += 8;
|
|
||||||
}
|
|
||||||
while (i < avail && data[i] >= '0' && data[i] <= '9')
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// whether [first, last) contains a digit other than '0'; a plain loop, as
|
/// whether [first, last) contains a digit other than '0'; a plain loop, as
|
||||||
/// GCC calls std::any_of out of line even for the usual empty range
|
/// GCC calls std::any_of out of line even for the usual empty range
|
||||||
JSON_HEDLEY_ALWAYS_INLINE bool has_nonzero_digit(const char* first, const char* last) noexcept
|
JSON_HEDLEY_ALWAYS_INLINE bool has_nonzero_digit(const char* first, const char* last) noexcept
|
||||||
|
|||||||
@@ -520,10 +520,7 @@ JSON_HEDLEY_NEVER_INLINE inline std::size_t string_bulk_run_from(const unsigned
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// string_bulk_run() for the lexer. The run must be the longest one: the lexer
|
// string_bulk_run() for the lexer. With NEON, only the first four words are
|
||||||
// would only lose speed with a shorter run, but contiguous_parser rejects a
|
|
||||||
// string whose run ends at a byte other than a quote or a backslash, so a
|
|
||||||
// shorter run would reject valid input there. With NEON, only the first four words are
|
|
||||||
// checked inline (most keys and short values end there), and longer runs and
|
// checked inline (most keys and short values end there), and longer runs and
|
||||||
// non-ASCII text continue out of line. The lexer's scan function runs once per
|
// non-ASCII text continue out of line. The lexer's scan function runs once per
|
||||||
// token, and the vector code inlined into it needs more registers, which every
|
// token, and the vector code inlined into it needs more registers, which every
|
||||||
|
|||||||
@@ -3126,19 +3126,22 @@ class binary_writer
|
|||||||
// negative fixnum
|
// negative fixnum
|
||||||
write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
|
write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
|
||||||
}
|
}
|
||||||
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)())
|
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() &&
|
||||||
|
j.m_data.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
|
||||||
{
|
{
|
||||||
// int 8
|
// int 8
|
||||||
oa.write_character(to_char_type(0xD0));
|
oa.write_character(to_char_type(0xD0));
|
||||||
write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
|
write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
|
||||||
}
|
}
|
||||||
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)())
|
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() &&
|
||||||
|
j.m_data.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
|
||||||
{
|
{
|
||||||
// int 16
|
// int 16
|
||||||
oa.write_character(to_char_type(0xD1));
|
oa.write_character(to_char_type(0xD1));
|
||||||
write_number(static_cast<std::int16_t>(j.m_data.m_value.number_integer));
|
write_number(static_cast<std::int16_t>(j.m_data.m_value.number_integer));
|
||||||
}
|
}
|
||||||
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)())
|
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() &&
|
||||||
|
j.m_data.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
|
||||||
{
|
{
|
||||||
// int 32
|
// int 32
|
||||||
oa.write_character(to_char_type(0xD2));
|
oa.write_character(to_char_type(0xD2));
|
||||||
@@ -4054,7 +4057,7 @@ class binary_writer
|
|||||||
Containers, strings, high-precision numbers, booleans and null cannot be
|
Containers, strings, high-precision numbers, booleans and null cannot be
|
||||||
declared as the single type of an optimized container in BJData; such a
|
declared as the single type of an optimized container in BJData; such a
|
||||||
container is written unoptimized. The reader rejects them with the same
|
container is written unoptimized. The reader rejects them with the same
|
||||||
list (binary_reader::is_valid_optimized_type()).
|
list (binary_reader::is_bjd_excluded_optimized_type()).
|
||||||
*/
|
*/
|
||||||
static constexpr bool is_bjdata_excluded_type_marker(const CharType marker) noexcept
|
static constexpr bool is_bjdata_excluded_type_marker(const CharType marker) noexcept
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -116,10 +116,6 @@ inline void copy_short(CharType* dst, const CharType* src, const std::size_t len
|
|||||||
/// the number of characters an empty vector is grown to by output_vector_sink
|
/// the number of characters an empty vector is grown to by output_vector_sink
|
||||||
constexpr std::size_t min_vector_sink_capacity = 64;
|
constexpr std::size_t min_vector_sink_capacity = 64;
|
||||||
|
|
||||||
/// the number of characters output_vector_sink makes writable ahead of its
|
|
||||||
/// position at a time: 64 KiB of the vector's capacity, not all of it
|
|
||||||
constexpr std::size_t vector_sink_window = 65536;
|
|
||||||
|
|
||||||
/// @brief non-virtual output sink writing into a std::vector
|
/// @brief non-virtual output sink writing into a std::vector
|
||||||
///
|
///
|
||||||
/// This sink is not part of the virtual output_adapter_protocol hierarchy: it is
|
/// This sink is not part of the virtual output_adapter_protocol hierarchy: it is
|
||||||
@@ -133,8 +129,7 @@ constexpr std::size_t vector_sink_window = 65536;
|
|||||||
/// encoding them: insert() is an out-of-line call into a routine that can insert
|
/// encoding them: insert() is an out-of-line call into a routine that can insert
|
||||||
/// anywhere, and resize() is one as well with libstdc++. The sink therefore keeps
|
/// anywhere, and resize() is one as well with libstdc++. The sink therefore keeps
|
||||||
/// its own write position: it grows the vector geometrically ahead of the
|
/// its own write position: it grows the vector geometrically ahead of the
|
||||||
/// position (making @ref vector_sink_window characters of it writable at a
|
/// position and copies each write to it, so a write is a capacity check and a
|
||||||
/// time) and copies each write to it, so a write is a capacity check and a
|
|
||||||
/// short copy. The destructor shrinks the vector to the bytes actually written,
|
/// short copy. The destructor shrinks the vector to the bytes actually written,
|
||||||
/// also when a write throws; shrinking a vector never allocates. The sink owns
|
/// also when a write throws; shrinking a vector never allocates. The sink owns
|
||||||
/// that trim, so it can be moved but not copied, and the vector must not be used
|
/// that trim, so it can be moved but not copied, and the vector must not be used
|
||||||
@@ -282,17 +277,9 @@ class output_vector_sink
|
|||||||
// reallocate for every few bytes
|
// reallocate for every few bytes
|
||||||
const std::size_t written = position();
|
const std::size_t written = position();
|
||||||
const std::size_t needed = written + length;
|
const std::size_t needed = written + length;
|
||||||
const std::size_t capacity = (std::max)(min_vector_sink_capacity, needed <= v->capacity()
|
v->resize((std::max)(min_vector_sink_capacity, needed <= v->capacity()
|
||||||
? v->capacity()
|
? v->capacity()
|
||||||
: (std::max)(needed, v->capacity() * 2));
|
: (std::max)(needed, v->capacity() * 2)));
|
||||||
v->reserve(capacity);
|
|
||||||
// resize() zeroes the characters it adds. Zeroing all of a doubled
|
|
||||||
// capacity, megabytes of a fresh allocation, at once faults in its
|
|
||||||
// pages and evicts them from the cache before the writer gets there,
|
|
||||||
// so its stores miss again; a window ahead of the position is still
|
|
||||||
// cached when they come (the binary writers on a long array of
|
|
||||||
// numbers: 4-26 % faster on x86, 1-2.5 % on Apple M1)
|
|
||||||
v->resize((std::min)(capacity, (std::max)(needed, written + vector_sink_window)));
|
|
||||||
cursor = v->data() + written;
|
cursor = v->data() + written;
|
||||||
limit = v->data() + v->size();
|
limit = v->data() + v->size();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <algorithm> // copy, none_of, min, search
|
#include <algorithm> // remove, fill, find, none_of, min
|
||||||
#include <array> // array
|
#include <array> // array
|
||||||
#include <clocale> // localeconv, lconv
|
#include <clocale> // localeconv, lconv
|
||||||
#include <cmath> // isfinite
|
#include <cmath> // isfinite
|
||||||
@@ -2080,35 +2080,31 @@ class serializer
|
|||||||
// check if the buffer was large enough
|
// check if the buffer was large enough
|
||||||
JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
|
JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
|
||||||
|
|
||||||
// look up the locale's decimal point now, matching what
|
// look up the locale's thousands separator and decimal point now,
|
||||||
// snprintf_float() just used (see get_decimal_point() in
|
// matching what snprintf_float() just used (see lexer::get_decimal_point())
|
||||||
// number_parse.hpp); it is a string, which can be longer than one
|
|
||||||
// byte (U+066B in ps_AF, #5993).
|
|
||||||
// A thousands separator needs no handling: %g never groups digits.
|
|
||||||
const auto* loc = std::localeconv();
|
const auto* loc = std::localeconv();
|
||||||
JSON_ASSERT(loc != nullptr);
|
JSON_ASSERT(loc != nullptr);
|
||||||
const char* const decimal_point = (loc->decimal_point == nullptr) ? "" : loc->decimal_point;
|
const char thousands_sep = (loc->thousands_sep == nullptr) ? '\0' : *loc->thousands_sep;
|
||||||
const std::size_t decimal_point_len = std::char_traits<char>::length(decimal_point);
|
const char decimal_point = (loc->decimal_point == nullptr) ? '\0' : *loc->decimal_point;
|
||||||
|
|
||||||
|
// erase thousands separators
|
||||||
|
if (thousands_sep != '\0')
|
||||||
|
{
|
||||||
|
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081
|
||||||
|
const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep);
|
||||||
|
std::fill(end, number_buffer.end(), '\0');
|
||||||
|
JSON_ASSERT((end - number_buffer.begin()) <= len);
|
||||||
|
len = (end - number_buffer.begin());
|
||||||
|
}
|
||||||
|
|
||||||
// convert decimal point to '.'
|
// convert decimal point to '.'
|
||||||
if ((decimal_point_len > 1) || (decimal_point_len == 1 && decimal_point[0] != '.'))
|
if (decimal_point != '\0' && decimal_point != '.')
|
||||||
{
|
{
|
||||||
const auto first = number_buffer.begin();
|
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081
|
||||||
const auto last = first + len;
|
const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
|
||||||
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::search returns an iterator, see https://github.com/nlohmann/json/issues/3081
|
if (dec_pos != number_buffer.end())
|
||||||
const auto dec_pos = std::search(first, last, decimal_point, decimal_point + decimal_point_len);
|
|
||||||
if (dec_pos != last)
|
|
||||||
{
|
{
|
||||||
*dec_pos = '.';
|
*dec_pos = '.';
|
||||||
if (decimal_point_len > 1)
|
|
||||||
{
|
|
||||||
// move the fraction and exponent behind the '.' (std::copy
|
|
||||||
// allows the overlap, as the destination is before the source)
|
|
||||||
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::copy returns an iterator, see https://github.com/nlohmann/json/issues/3081
|
|
||||||
const auto end = std::copy(dec_pos + static_cast<std::ptrdiff_t>(decimal_point_len), last, dec_pos + 1);
|
|
||||||
*end = '\0';
|
|
||||||
len = end - first;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1168,20 +1168,19 @@ indent_done:
|
|||||||
if (negative)
|
if (negative)
|
||||||
{
|
{
|
||||||
++p;
|
++p;
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(cur() < '0' || cur() > '9'))
|
|
||||||
{
|
|
||||||
return fail(error_code::number_after_minus);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const unsigned char* const int_start = p;
|
const unsigned char* const int_start = p;
|
||||||
if (cur() == '0')
|
if (cur() == '0')
|
||||||
{
|
{
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
|
else if (NLOHMANN_VIEW_LIKELY(cur() >= '1' && cur() <= '9'))
|
||||||
|
{
|
||||||
|
p = skip_digits(p + 1, e);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// (a digit: the dispatch or the check after the sign saw it)
|
return fail(error_code::number_after_minus);
|
||||||
p = skip_digits(p + 1, e);
|
|
||||||
}
|
}
|
||||||
const auto int_digits = static_cast<std::size_t>(p - int_start);
|
const auto int_digits = static_cast<std::size_t>(p - int_start);
|
||||||
std::size_t frac_digits = 0;
|
std::size_t frac_digits = 0;
|
||||||
|
|||||||
@@ -58,7 +58,6 @@
|
|||||||
#include <nlohmann/detail/exceptions.hpp> // IWYU pragma: export
|
#include <nlohmann/detail/exceptions.hpp> // IWYU pragma: export
|
||||||
#include <nlohmann/detail/hash.hpp>
|
#include <nlohmann/detail/hash.hpp>
|
||||||
#include <nlohmann/detail/input/binary_reader.hpp>
|
#include <nlohmann/detail/input/binary_reader.hpp>
|
||||||
#include <nlohmann/detail/input/contiguous_parser.hpp>
|
|
||||||
#include <nlohmann/detail/input/input_adapters.hpp>
|
#include <nlohmann/detail/input/input_adapters.hpp>
|
||||||
#include <nlohmann/detail/input/json_sax.hpp>
|
#include <nlohmann/detail/input/json_sax.hpp>
|
||||||
#include <nlohmann/detail/input/lexer.hpp>
|
#include <nlohmann/detail/input/lexer.hpp>
|
||||||
@@ -3110,15 +3109,6 @@ public:
|
|||||||
const bool ignore_trailing_commas)
|
const bool ignore_trailing_commas)
|
||||||
{
|
{
|
||||||
basic_json result;
|
basic_json result;
|
||||||
// JSON in one block of memory, with the default options, is read by
|
|
||||||
// the faster contiguous_parser; on an error, the library parser reads
|
|
||||||
// it again to report it (see contiguous_parser)
|
|
||||||
using contiguous = std::integral_constant<bool, detail::can_parse_contiguous<basic_json, typename std::decay<InputAdapterType>::type>::value>;
|
|
||||||
if (cb == nullptr && !ignore_comments && !ignore_trailing_commas
|
|
||||||
&& detail::parse_contiguous(adapter, result, allow_exceptions, contiguous {}))
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
parser(std::forward<InputAdapterType>(adapter), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result);
|
parser(std::forward<InputAdapterType>(adapter), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -3129,17 +3119,8 @@ public:
|
|||||||
const bool ignore_comments,
|
const bool ignore_comments,
|
||||||
const bool ignore_trailing_commas)
|
const bool ignore_trailing_commas)
|
||||||
{
|
{
|
||||||
// JSON in one block of memory is checked by the faster
|
|
||||||
// contiguous_parser (see parse_impl())
|
|
||||||
using adapter_t = typename std::decay<InputAdapterType>::type;
|
|
||||||
bool accepted = false;
|
|
||||||
if (!ignore_comments && !ignore_trailing_commas
|
|
||||||
&& detail::accept_contiguous<basic_json>(adapter, accepted, std::integral_constant<bool, detail::can_parse_contiguous<basic_json, adapter_t>::value> {}))
|
|
||||||
{
|
|
||||||
return accepted;
|
|
||||||
}
|
|
||||||
// the parser and lexer that discard the values of tokens (see lexer)
|
// the parser and lexer that discard the values of tokens (see lexer)
|
||||||
return ::nlohmann::detail::parser<basic_json, adapter_t, true>(std::forward<InputAdapterType>(adapter),
|
return ::nlohmann::detail::parser<basic_json, typename std::decay<InputAdapterType>::type, true>(std::forward<InputAdapterType>(adapter),
|
||||||
nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);
|
nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3154,14 +3135,6 @@ public:
|
|||||||
{
|
{
|
||||||
if (format == input_format_t::json)
|
if (format == input_format_t::json)
|
||||||
{
|
{
|
||||||
// JSON in one block of memory is read by the faster
|
|
||||||
// contiguous_parser, with the default options (see parse_impl())
|
|
||||||
bool result = false;
|
|
||||||
if (strict && !ignore_comments && !ignore_trailing_commas
|
|
||||||
&& detail::sax_parse_contiguous<basic_json>(ia, sax, result, std::integral_constant<bool, detail::can_parse_contiguous<basic_json, InputAdapterType>::value> {}))
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict);
|
return parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict);
|
||||||
}
|
}
|
||||||
return detail::binary_reader<basic_json, InputAdapterType, SAX>(std::move(ia), format).sax_parse(sax, strict, tag_handler);
|
return detail::binary_reader<basic_json, InputAdapterType, SAX>(std::move(ia), format).sax_parse(sax, strict, tag_handler);
|
||||||
|
|||||||
Generated
+90
-1191
File diff suppressed because it is too large.
Load diff
Generated
+5
-6
@@ -2473,20 +2473,19 @@ indent_done:
|
|||||||
if (negative)
|
if (negative)
|
||||||
{
|
{
|
||||||
++p;
|
++p;
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(cur() < '0' || cur() > '9'))
|
|
||||||
{
|
|
||||||
return fail(error_code::number_after_minus);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const unsigned char* const int_start = p;
|
const unsigned char* const int_start = p;
|
||||||
if (cur() == '0')
|
if (cur() == '0')
|
||||||
{
|
{
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
|
else if (NLOHMANN_VIEW_LIKELY(cur() >= '1' && cur() <= '9'))
|
||||||
|
{
|
||||||
|
p = skip_digits(p + 1, e);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// (a digit: the dispatch or the check after the sign saw it)
|
return fail(error_code::number_after_minus);
|
||||||
p = skip_digits(p + 1, e);
|
|
||||||
}
|
}
|
||||||
const auto int_digits = static_cast<std::size_t>(p - int_start);
|
const auto int_digits = static_cast<std::size_t>(p - int_start);
|
||||||
std::size_t frac_digits = 0;
|
std::size_t frac_digits = 0;
|
||||||
|
|||||||
@@ -97,23 +97,3 @@ TEST_CASE("CBOR")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("MessagePack")
|
|
||||||
{
|
|
||||||
SECTION("parse errors")
|
|
||||||
{
|
|
||||||
SECTION("array/map size equal to the unknown-size sentinel")
|
|
||||||
{
|
|
||||||
// on 32-bit platforms an array 32/map 32 length of 0xFFFFFFFF aliases unknown_size()
|
|
||||||
std::vector<uint8_t> const varr = {0xDD, 0xFF, 0xFF, 0xFF, 0xFF};
|
|
||||||
std::vector<uint8_t> const vmap = {0xDF, 0xFF, 0xFF, 0xFF, 0xFF};
|
|
||||||
|
|
||||||
json _;
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(varr), "[json.exception.out_of_range.408] syntax error while parsing MessagePack size: excessive array size", json::out_of_range&);
|
|
||||||
CHECK(json::from_msgpack(varr, true, false).is_discarded());
|
|
||||||
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(vmap), "[json.exception.out_of_range.408] syntax error while parsing MessagePack size: excessive map size", json::out_of_range&);
|
|
||||||
CHECK(json::from_msgpack(vmap, true, false).is_discarded());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1026,7 +1026,10 @@ TEST_CASE("insert() provides its documented exception guarantees (#5986)")
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(JSON_NOEXCEPTION)
|
// the no-exceptions CI job has no std::bad_alloc to catch; with iterator
|
||||||
|
// debugging (MSVC STL debug builds) containers allocate inside noexcept
|
||||||
|
// functions, so failing an allocation there terminates the program
|
||||||
|
#if !defined(JSON_NOEXCEPTION) && !(defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL > 0)
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
// the allocation numbered sweep_fail_at (counted from 1) throws; 0 never fails
|
// the allocation numbered sweep_fail_at (counted from 1) throws; 0 never fails
|
||||||
@@ -1038,14 +1041,6 @@ template<class T>
|
|||||||
struct sweep_allocator
|
struct sweep_allocator
|
||||||
{
|
{
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
// GCC 4.8's std::map takes these from the allocator instead of from
|
|
||||||
// std::allocator_traits
|
|
||||||
using pointer = T*;
|
|
||||||
using const_pointer = const T*;
|
|
||||||
using reference = T&;
|
|
||||||
using const_reference = const T&;
|
|
||||||
using size_type = std::size_t;
|
|
||||||
using difference_type = std::ptrdiff_t;
|
|
||||||
|
|
||||||
sweep_allocator() = default;
|
sweep_allocator() = default;
|
||||||
template<class U> sweep_allocator(const sweep_allocator<U>& /*unused*/) noexcept {}
|
template<class U> sweep_allocator(const sweep_allocator<U>& /*unused*/) noexcept {}
|
||||||
@@ -1163,14 +1158,9 @@ TEST_CASE("parse() survives a failing allocation at every point (#5985)")
|
|||||||
|
|
||||||
// With iterator debugging (MSVC STL debug builds, also used by clang-cl),
|
// With iterator debugging (MSVC STL debug builds, also used by clang-cl),
|
||||||
// containers allocate a debug proxy through the allocator inside their
|
// containers allocate a debug proxy through the allocator inside their
|
||||||
// noexcept default and move constructors, so failing that allocation
|
// noexcept move constructors, so failing that allocation terminates the
|
||||||
// terminates the program instead of throwing std::bad_alloc. Nothing to
|
// program instead of throwing std::bad_alloc. Nothing to check there.
|
||||||
// check there.
|
|
||||||
#if !(defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL > 0)
|
#if !(defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL > 0)
|
||||||
// Clang 3.4 and 3.5 leak memory when an element constructor throws
|
|
||||||
// inside libstdc++'s containers (a std::map copy, a std::vector
|
|
||||||
// reallocation), without any json value involved.
|
|
||||||
#if !(defined(__clang__) && !defined(__apple_build_version__) && __clang_major__ == 3 && __clang_minor__ < 6)
|
|
||||||
SECTION("parser callback")
|
SECTION("parser callback")
|
||||||
{
|
{
|
||||||
const auto keep_all = [](int /*depth*/, sweep_json::parse_event_t /*event*/, sweep_json& /*parsed*/) noexcept
|
const auto keep_all = [](int /*depth*/, sweep_json::parse_event_t /*event*/, sweep_json& /*parsed*/) noexcept
|
||||||
@@ -1191,6 +1181,7 @@ TEST_CASE("parse() survives a failing allocation at every point (#5985)")
|
|||||||
return sweep_json::parse(text, drop_d);
|
return sweep_json::parse(text, drop_d);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
SECTION("sax_parse into an existing value")
|
SECTION("sax_parse into an existing value")
|
||||||
{
|
{
|
||||||
@@ -1204,7 +1195,5 @@ TEST_CASE("parse() survives a failing allocation at every point (#5985)")
|
|||||||
return value;
|
return value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -50,14 +50,6 @@ std::vector<json> test_values()
|
|||||||
large_payloads.push_back(json::binary(std::vector<std::uint8_t>(static_cast<std::size_t>(100 * i), static_cast<std::uint8_t>(i))));
|
large_payloads.push_back(json::binary(std::vector<std::uint8_t>(static_cast<std::size_t>(100 * i), static_cast<std::uint8_t>(i))));
|
||||||
}
|
}
|
||||||
|
|
||||||
// several times vector_sink_window characters in every format: the vector
|
|
||||||
// sink makes the capacity it grows to writable a window at a time
|
|
||||||
json many_windows = json::array();
|
|
||||||
for (std::uint64_t i = 0; i < 30000; ++i)
|
|
||||||
{
|
|
||||||
many_windows.push_back((i * 0x9E3779B97F4A7C15u) >> 1u);
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
{
|
{
|
||||||
json(nullptr), json(true), json(false),
|
json(nullptr), json(true), json(false),
|
||||||
@@ -69,7 +61,7 @@ std::vector<json> test_values()
|
|||||||
json::array(), json::object(),
|
json::array(), json::object(),
|
||||||
json::array({1, 2, 3}), json({{"a", 1}, {"b", nullptr}}),
|
json::array({1, 2, 3}), json({{"a", 1}, {"b", nullptr}}),
|
||||||
json({{"nested", {{"deep", json::array({1, "two", 3.0, nullptr})}}}}),
|
json({{"nested", {{"deep", json::array({1, "two", 3.0, nullptr})}}}}),
|
||||||
big_array, big_object, large_payloads, many_windows,
|
big_array, big_object, large_payloads,
|
||||||
json::binary(std::vector<std::uint8_t>(5000, 0x42))
|
json::binary(std::vector<std::uint8_t>(5000, 0x42))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-32
@@ -78,25 +78,23 @@ TEST_CASE("BJData")
|
|||||||
{
|
{
|
||||||
SECTION("binary_reader BJData lookup tables")
|
SECTION("binary_reader BJData lookup tables")
|
||||||
{
|
{
|
||||||
// bjd_type_name() is static; is_valid_optimized_type() needs a BJData reader
|
// both lookups are static member functions
|
||||||
std::vector<std::uint8_t> const data;
|
std::vector<std::uint8_t> const data;
|
||||||
using reader_t = nlohmann::detail::binary_reader<json, decltype(nlohmann::detail::input_adapter(data))>;
|
using reader_t = nlohmann::detail::binary_reader<json, decltype(nlohmann::detail::input_adapter(data))>;
|
||||||
|
|
||||||
// the markers BJData excludes as optimized types must match
|
// the excluded optimized-type markers must match binary_writer's
|
||||||
// binary_writer's is_bjdata_excluded_type_marker(), which encodes the
|
// is_bjdata_excluded_type_marker(), which encodes the same 8 markers
|
||||||
// same 8 markers
|
|
||||||
const reader_t reader(nlohmann::detail::input_adapter(data), json::input_format_t::bjdata);
|
|
||||||
for (const char marker :
|
for (const char marker :
|
||||||
{'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z', 'x'
|
{'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
CHECK(!reader.is_valid_optimized_type(static_cast<unsigned char>(marker)));
|
CHECK(reader_t::is_bjd_excluded_optimized_type(static_cast<unsigned char>(marker)));
|
||||||
}
|
}
|
||||||
for (const char marker :
|
for (const char marker :
|
||||||
{'U', 'i', 'u', 'I', 'm', 'l', 'M', 'L', 'd', 'D', 'C', 'B', 'h'
|
{'U', 'i', 'u', 'I', 'm', 'l', 'M', 'L', 'd', 'D', 'C', 'B', 'x'
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
CHECK(reader.is_valid_optimized_type(static_cast<unsigned char>(marker)));
|
CHECK(!reader_t::is_bjd_excluded_optimized_type(static_cast<unsigned char>(marker)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// every dtype marker must round-trip to its ND-array type name
|
// every dtype marker must round-trip to its ND-array type name
|
||||||
@@ -3239,32 +3237,12 @@ TEST_CASE("BJData")
|
|||||||
#endif
|
#endif
|
||||||
CHECK(json::from_bjdata(vM, true, false).is_discarded());
|
CHECK(json::from_bjdata(vM, true, false).is_discarded());
|
||||||
|
|
||||||
// the first dimension is 2^64-1, which is rejected as a count (see below)
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vMX), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
|
|
||||||
CHECK(json::from_bjdata(vMX, true, false).is_discarded());
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("optimized array: count equal to SIZE_MAX")
|
|
||||||
{
|
|
||||||
// the reader marks a container without a count by SIZE_MAX, so
|
|
||||||
// a count of that value must not be read as "no count"
|
|
||||||
// (it used to parse as [1])
|
|
||||||
json _;
|
|
||||||
#if SIZE_MAX != 0xffffffff
|
#if SIZE_MAX != 0xffffffff
|
||||||
std::vector<uint8_t> const vM = {'[', '#', 'M', 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 'i', 0x01, ']'};
|
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vMX), "[json.exception.out_of_range.408] syntax error while parsing BJData size: excessive ndarray size caused overflow", json::out_of_range&);
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vM), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
|
|
||||||
CHECK(json::from_bjdata(vM, true, false).is_discarded());
|
|
||||||
// one less is an ordinary excessive count
|
|
||||||
std::vector<uint8_t> const vM1 = {'[', '#', 'M', 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 'i', 0x01, ']'};
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vM1), "[json.exception.out_of_range.408] excessive array size: 18446744073709551614", json::out_of_range&);
|
|
||||||
#else
|
#else
|
||||||
std::vector<uint8_t> const vm = {'[', '#', 'm', 0xFF, 0xFF, 0xFF, 0xFF, 'i', 0x01, ']'};
|
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vMX), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vm), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
|
|
||||||
CHECK(json::from_bjdata(vm, true, false).is_discarded());
|
|
||||||
std::vector<uint8_t> const vL = {'[', '#', 'L', 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 'i', 0x01, ']'};
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vL), "[json.exception.out_of_range.408] syntax error while parsing BJData size: integer value overflow", json::out_of_range&);
|
|
||||||
CHECK(json::from_bjdata(vL, true, false).is_discarded());
|
|
||||||
#endif
|
#endif
|
||||||
|
CHECK(json::from_bjdata(vMX, true, false).is_discarded());
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("optimized array: integer value overflow")
|
SECTION("optimized array: integer value overflow")
|
||||||
|
|||||||
@@ -2222,10 +2222,9 @@ TEST_CASE("serialization of short strings")
|
|||||||
{
|
{
|
||||||
CAPTURE(s)
|
CAPTURE(s)
|
||||||
const json j = s;
|
const json j = s;
|
||||||
const std::string quoted = "\"" + s + "\""; // NOLINT(performance-inefficient-string-concatenation)
|
CHECK(j.dump() == "\"" + s + "\"");
|
||||||
CHECK(j.dump() == quoted);
|
CHECK(j.dump(-1, ' ', true) == "\"" + s + "\"");
|
||||||
CHECK(j.dump(-1, ' ', true) == quoted);
|
CHECK(json({{s, s}}).dump() == "{\"" + s + "\":\"" + s + "\"}");
|
||||||
CHECK(json({{s, s}}).dump() == "{" + quoted + ":" + quoted + "}"); // NOLINT(performance-inefficient-string-concatenation)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// a character that needs an escape
|
// a character that needs an escape
|
||||||
|
|||||||
@@ -1,564 +0,0 @@
|
|||||||
// __ _____ _____ _____
|
|
||||||
// __| | __| | | | JSON for Modern C++ (supporting code)
|
|
||||||
// | | |__ | | | | | | version 3.12.0
|
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
||||||
//
|
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
// parse() and accept() read text in one block of memory with
|
|
||||||
// detail::contiguous_parser, and everything else (here: std::list iterators)
|
|
||||||
// with the lexer's byte path. These tests check that the two agree on every
|
|
||||||
// input: the same value, with the same number types and float bits, or the
|
|
||||||
// same exception with the same message.
|
|
||||||
|
|
||||||
#include "doctest_compatibility.h"
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
using nlohmann::json;
|
|
||||||
using nlohmann::ordered_json;
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
#include <list>
|
|
||||||
#include <map>
|
|
||||||
#include <random>
|
|
||||||
#include <string>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
/// the value with its number types and float bits; below 100 levels, the
|
|
||||||
/// library's dump() (whose recursion is bounded) describes the rest
|
|
||||||
template<typename Json>
|
|
||||||
std::string describe_value(const Json& j, int depth = 0)
|
|
||||||
{
|
|
||||||
std::string out;
|
|
||||||
if (depth >= 100)
|
|
||||||
{
|
|
||||||
out = j.dump();
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
switch (j.type())
|
|
||||||
{
|
|
||||||
case Json::value_t::number_float:
|
|
||||||
{
|
|
||||||
const double d = j.template get<double>();
|
|
||||||
std::uint64_t bits = 0;
|
|
||||||
std::memcpy(&bits, &d, sizeof(bits));
|
|
||||||
out += "f" + std::to_string(bits);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Json::value_t::number_integer:
|
|
||||||
out += "i" + j.dump();
|
|
||||||
break;
|
|
||||||
case Json::value_t::number_unsigned:
|
|
||||||
out += "u" + j.dump();
|
|
||||||
break;
|
|
||||||
case Json::value_t::array:
|
|
||||||
out += "[";
|
|
||||||
for (const auto& e : j)
|
|
||||||
{
|
|
||||||
out += describe_value(e, depth + 1) + ",";
|
|
||||||
}
|
|
||||||
out += "]";
|
|
||||||
break;
|
|
||||||
case Json::value_t::object:
|
|
||||||
out += "{";
|
|
||||||
for (auto it = j.begin(); it != j.end(); ++it)
|
|
||||||
{
|
|
||||||
out += Json(it.key()).dump() + ":" + describe_value(it.value(), depth + 1) + ",";
|
|
||||||
}
|
|
||||||
out += "}";
|
|
||||||
break;
|
|
||||||
case Json::value_t::null:
|
|
||||||
case Json::value_t::boolean:
|
|
||||||
case Json::value_t::string:
|
|
||||||
case Json::value_t::binary:
|
|
||||||
case Json::value_t::discarded:
|
|
||||||
default:
|
|
||||||
out += j.dump();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(JSON_NOEXCEPTION)
|
|
||||||
template<typename Json, typename Parse>
|
|
||||||
std::string outcome(Parse parse)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return describe_value(parse());
|
|
||||||
}
|
|
||||||
catch (const typename Json::exception& e)
|
|
||||||
{
|
|
||||||
return std::string("exception: ") + e.what();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// every SAX event with its values, the text of float tokens, and the parse
|
|
||||||
/// error with its position, token and message; returns false after
|
|
||||||
/// @a stop_after events (to stop the parse)
|
|
||||||
class event_log : public nlohmann::json_sax<json>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit event_log(std::size_t stop_after_ = static_cast<std::size_t>(-1), bool continue_after_error_ = false)
|
|
||||||
: stop_after(stop_after_), continue_after_error(continue_after_error_)
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool null() override
|
|
||||||
{
|
|
||||||
return add("null");
|
|
||||||
}
|
|
||||||
bool boolean(bool val) override
|
|
||||||
{
|
|
||||||
return add(val ? "true" : "false");
|
|
||||||
}
|
|
||||||
bool number_integer(number_integer_t val) override
|
|
||||||
{
|
|
||||||
return add("i" + std::to_string(val));
|
|
||||||
}
|
|
||||||
bool number_unsigned(number_unsigned_t val) override
|
|
||||||
{
|
|
||||||
return add("u" + std::to_string(val));
|
|
||||||
}
|
|
||||||
bool number_float(number_float_t val, const string_t& s) override
|
|
||||||
{
|
|
||||||
std::uint64_t bits = 0;
|
|
||||||
std::memcpy(&bits, &val, sizeof(bits));
|
|
||||||
return add("f" + std::to_string(bits) + " " + s);
|
|
||||||
}
|
|
||||||
bool string(string_t& val) override
|
|
||||||
{
|
|
||||||
return add("s" + val);
|
|
||||||
}
|
|
||||||
bool binary(binary_t& /*val*/) override
|
|
||||||
{
|
|
||||||
return add("binary");
|
|
||||||
}
|
|
||||||
bool start_object(std::size_t elements) override
|
|
||||||
{
|
|
||||||
return add("{" + std::to_string(elements));
|
|
||||||
}
|
|
||||||
bool key(string_t& val) override
|
|
||||||
{
|
|
||||||
return add("k" + val);
|
|
||||||
}
|
|
||||||
bool end_object() override
|
|
||||||
{
|
|
||||||
return add("}");
|
|
||||||
}
|
|
||||||
bool start_array(std::size_t elements) override
|
|
||||||
{
|
|
||||||
return add("[" + std::to_string(elements));
|
|
||||||
}
|
|
||||||
bool end_array() override
|
|
||||||
{
|
|
||||||
return add("]");
|
|
||||||
}
|
|
||||||
bool parse_error(std::size_t position, const std::string& last_token, const nlohmann::detail::exception& ex) override
|
|
||||||
{
|
|
||||||
events.push_back("error " + std::to_string(position) + " " + last_token + " " + ex.what());
|
|
||||||
return continue_after_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<std::string>& log() const noexcept
|
|
||||||
{
|
|
||||||
return events;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool add(const std::string& event)
|
|
||||||
{
|
|
||||||
events.push_back(event);
|
|
||||||
return events.size() != stop_after;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> events {}; // NOLINT(readability-redundant-member-init)
|
|
||||||
std::size_t stop_after;
|
|
||||||
bool continue_after_error;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// sax_parse() of the contiguous path against the byte path, also with a
|
|
||||||
/// handler that stops the parse
|
|
||||||
void check_same_events(const std::string& text, const std::list<char>& bytes)
|
|
||||||
{
|
|
||||||
for (const std::size_t stop_after :
|
|
||||||
{
|
|
||||||
static_cast<std::size_t>(-1), std::size_t{1}, std::size_t{3}
|
|
||||||
})
|
|
||||||
{
|
|
||||||
CAPTURE(stop_after)
|
|
||||||
event_log contiguous(stop_after);
|
|
||||||
event_log byte_path(stop_after);
|
|
||||||
const bool r1 = json::sax_parse(text, &contiguous);
|
|
||||||
const bool r2 = json::sax_parse(bytes.begin(), bytes.end(), &byte_path);
|
|
||||||
CHECK(r1 == r2);
|
|
||||||
CHECK(contiguous.log() == byte_path.log());
|
|
||||||
}
|
|
||||||
// a handler that asks to go on after an error (the library parser then
|
|
||||||
// reports what follows, e.g., the missing end of the input)
|
|
||||||
event_log contiguous(static_cast<std::size_t>(-1), true);
|
|
||||||
event_log byte_path(static_cast<std::size_t>(-1), true);
|
|
||||||
const bool r1 = json::sax_parse(text, &contiguous);
|
|
||||||
const bool r2 = json::sax_parse(bytes.begin(), bytes.end(), &byte_path);
|
|
||||||
CHECK(r1 == r2);
|
|
||||||
CHECK(contiguous.log() == byte_path.log());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// the contiguous path (a std::string) against the byte path (std::list iterators)
|
|
||||||
template<typename Json>
|
|
||||||
void check_same(const std::string& text)
|
|
||||||
{
|
|
||||||
CAPTURE(text)
|
|
||||||
const std::list<char> bytes(text.begin(), text.end());
|
|
||||||
|
|
||||||
#if !defined(JSON_NOEXCEPTION)
|
|
||||||
// with exceptions: the value, or the same exception for both
|
|
||||||
const std::string contiguous = outcome<Json>([&]
|
|
||||||
{
|
|
||||||
return Json::parse(text);
|
|
||||||
});
|
|
||||||
const std::string byte_path = outcome<Json>([&]
|
|
||||||
{
|
|
||||||
return Json::parse(bytes.begin(), bytes.end());
|
|
||||||
});
|
|
||||||
CHECK(contiguous == byte_path);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// without exceptions: the value, or discarded for both
|
|
||||||
const Json c = Json::parse(text, nullptr, false);
|
|
||||||
const Json b = Json::parse(bytes.begin(), bytes.end(), nullptr, false);
|
|
||||||
CHECK(c.is_discarded() == b.is_discarded());
|
|
||||||
|
|
||||||
CHECK(Json::accept(text) == Json::accept(bytes.begin(), bytes.end()));
|
|
||||||
if (std::is_same<Json, json>::value)
|
|
||||||
{
|
|
||||||
check_same_events(text, bytes);
|
|
||||||
}
|
|
||||||
// a pointer range without a terminating NUL
|
|
||||||
std::vector<char> exact(text.begin(), text.end());
|
|
||||||
if (!exact.empty())
|
|
||||||
{
|
|
||||||
CHECK(Json::accept(exact.data(), exact.data() + exact.size()) == Json::accept(bytes.begin(), bytes.end()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void check_both(const std::string& text)
|
|
||||||
{
|
|
||||||
check_same<json>(text);
|
|
||||||
check_same<ordered_json>(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// a random JSON text, mostly valid, with the corner cases of the parser
|
|
||||||
class generator
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit generator(std::uint32_t seed) : rng(seed) {}
|
|
||||||
|
|
||||||
std::string document(int depth)
|
|
||||||
{
|
|
||||||
std::string out;
|
|
||||||
value(out, depth);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::uint32_t pick(std::uint32_t n)
|
|
||||||
{
|
|
||||||
return static_cast<std::uint32_t>(rng() % n);
|
|
||||||
}
|
|
||||||
|
|
||||||
void whitespace(std::string& out)
|
|
||||||
{
|
|
||||||
static const char* const choices[] = {"", "", "", " ", "\n", "\n ", "\t", "\r\n ", "\n ", " "}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
||||||
out += choices[pick(10)];
|
|
||||||
}
|
|
||||||
|
|
||||||
void number(std::string& out)
|
|
||||||
{
|
|
||||||
static const char* const choices[] = // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
||||||
{
|
|
||||||
"0", "-0", "1", "-1", "42", "123456789012345678", "-123456789012345678", "1234567890123456789",
|
|
||||||
"-1234567890123456789", "9223372036854775807", "-9223372036854775808", "-9223372036854775809",
|
|
||||||
"18446744073709551615", "18446744073709551616", "123456789012345678901234567890", "0.0", "-0.0",
|
|
||||||
"1.5", "-65.613616999999977", "1e10", "1E+10", "1e-10", "2.2250738585072014e-308", "4.9e-324",
|
|
||||||
"1e-400", "1.7976931348623157e308", "1e308", "1e309", "-1e309", "123.456e-789", "0.1e1",
|
|
||||||
"12345678901234567890.5", "1.00000000000000011102230246251565404236316680908203125",
|
|
||||||
"9007199254740993", "0.30000000000000004", "1e22", "1e23", "100000000000000000000000",
|
|
||||||
"00", "01", "-", "1.", ".5", "1e", "1e+", "--1", "+1", "1.5e3.2", "0x10", "1_000"
|
|
||||||
};
|
|
||||||
out += choices[pick(sizeof(choices) / sizeof(choices[0]))];
|
|
||||||
}
|
|
||||||
|
|
||||||
void string(std::string& out)
|
|
||||||
{
|
|
||||||
static const char* const pieces[] = // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
||||||
{
|
|
||||||
"a", "key", "a longer piece of plain ASCII text for the vector paths", "\\n", "\\t", "\\\"",
|
|
||||||
"\\\\", "\\/", "\\b", "\\f", "\\r", "\\u0000", "\\u00e4", "\\u20AC", "\\uD83D\\uDE00",
|
|
||||||
"\\uD83D", "\\uDE00", "\\uD83Dx", "\\u12", "\\x", "\xC3\xA4", "\xE2\x82\xAC", "\xF0\x9F\x98\x80",
|
|
||||||
"\xC0\xAF", "\xED\xA0\x80", "\xF4\x90\x80\x80", "\x80", "\xFF", "\x01", "\x1F", "\x7F", "\t"
|
|
||||||
};
|
|
||||||
out += '"';
|
|
||||||
const auto n = pick(6);
|
|
||||||
for (std::uint32_t i = 0; i < n; ++i)
|
|
||||||
{
|
|
||||||
// mostly well-formed pieces
|
|
||||||
out += pieces[pick(5) == 0 ? pick(sizeof(pieces) / sizeof(pieces[0])) : pick(20)];
|
|
||||||
}
|
|
||||||
out += '"';
|
|
||||||
}
|
|
||||||
|
|
||||||
void value(std::string& out, int depth)
|
|
||||||
{
|
|
||||||
const auto kind = depth > 0 ? pick(9) : 3 + pick(6);
|
|
||||||
switch (kind)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
case 1:
|
|
||||||
{
|
|
||||||
out += '[';
|
|
||||||
whitespace(out);
|
|
||||||
const auto n = pick(6);
|
|
||||||
for (std::uint32_t i = 0; i < n; ++i)
|
|
||||||
{
|
|
||||||
if (i != 0)
|
|
||||||
{
|
|
||||||
out += ',';
|
|
||||||
whitespace(out);
|
|
||||||
}
|
|
||||||
value(out, depth - 1);
|
|
||||||
whitespace(out);
|
|
||||||
}
|
|
||||||
out += ']';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 2:
|
|
||||||
{
|
|
||||||
out += '{';
|
|
||||||
whitespace(out);
|
|
||||||
const auto n = pick(6);
|
|
||||||
for (std::uint32_t i = 0; i < n; ++i)
|
|
||||||
{
|
|
||||||
if (i != 0)
|
|
||||||
{
|
|
||||||
out += ',';
|
|
||||||
whitespace(out);
|
|
||||||
}
|
|
||||||
// a few duplicate keys
|
|
||||||
if (pick(4) == 0)
|
|
||||||
{
|
|
||||||
out += "\"dup\"";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
string(out);
|
|
||||||
}
|
|
||||||
whitespace(out);
|
|
||||||
out += pick(2) == 0 ? ":" : ": ";
|
|
||||||
whitespace(out);
|
|
||||||
value(out, depth - 1);
|
|
||||||
whitespace(out);
|
|
||||||
}
|
|
||||||
out += '}';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 3:
|
|
||||||
case 4:
|
|
||||||
number(out);
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
case 6:
|
|
||||||
string(out);
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
out += pick(2) == 0 ? "true" : "false";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
out += "null";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::mt19937 rng;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_CASE("contiguous parser")
|
|
||||||
{
|
|
||||||
SECTION("fixed inputs")
|
|
||||||
{
|
|
||||||
const char* const inputs[] = // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
||||||
{
|
|
||||||
"", " ", "\n", "null", "true", "false", "nul", "tru", "fals", "nullx", "[]", "{}", "[", "{", "]", "}",
|
|
||||||
"[1,]", "[,1]", R"({"a":1,})", R"({"a" 1})", R"({"a":})", "{1:2}", "[1 2]", R"("")", R"("abc)", R"("\")",
|
|
||||||
"\xEF\xBB\xBF[1]", "\xEF\xBB[1]", "\xEF[1]", "\xEF\xBB\xBF", " [1] ", "[1]x", "[1] [2]", "1 2",
|
|
||||||
R"({"a":1,"a":2})", R"({"b":1,"a":2,"b":3})", R"(["\u0000"])", "[1e400]", "[-1e400]",
|
|
||||||
R"({"a": [1, 2, {"b": null}], "c": "d"})", "[true,false,null]", "[[[[[]]]]]",
|
|
||||||
};
|
|
||||||
for (const char* input : inputs)
|
|
||||||
{
|
|
||||||
check_both(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NUL bytes: by default the end of the input (between values), else an error
|
|
||||||
check_both(std::string("[1]\0garbage", 11));
|
|
||||||
check_both(std::string("[1,\0]", 5));
|
|
||||||
check_both(std::string("\"a\0b\"", 5));
|
|
||||||
check_both(std::string("\0", 1));
|
|
||||||
check_both(std::string("1\0", 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("deep nesting")
|
|
||||||
{
|
|
||||||
// across the 64-level words of the nesting stack
|
|
||||||
const std::vector<std::size_t> depths = {63, 64, 65, 127, 128, 129, 200, 1000, 10000};
|
|
||||||
for (const std::size_t depth : depths)
|
|
||||||
{
|
|
||||||
check_both(std::string(depth, '[') + std::string(depth, ']'));
|
|
||||||
std::string objects;
|
|
||||||
for (std::size_t i = 0; i < depth; ++i)
|
|
||||||
{
|
|
||||||
objects += (i % 2 == 0) ? "{\"k\":" : "[";
|
|
||||||
}
|
|
||||||
objects += "1";
|
|
||||||
for (std::size_t i = depth; i-- > 0;)
|
|
||||||
{
|
|
||||||
objects += (i % 2 == 0) ? "}" : "]";
|
|
||||||
}
|
|
||||||
check_both(objects);
|
|
||||||
// unbalanced
|
|
||||||
check_both(std::string(depth, '[') + std::string(depth - 1, ']'));
|
|
||||||
check_both(std::string(depth, '[') + std::string(depth + 1, ']'));
|
|
||||||
}
|
|
||||||
// a value after a container that closes a 64-level word
|
|
||||||
std::string mixed = std::string(64, '[') + "1" + std::string(63, ']') + ",{\"a\":2}]";
|
|
||||||
check_both(mixed);
|
|
||||||
mixed = "{\"x\":" + std::string(64, '[') + std::string(64, ']') + ",\"y\":[1]}";
|
|
||||||
check_both(mixed);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("generated documents and their mutations")
|
|
||||||
{
|
|
||||||
generator gen(20261105u);
|
|
||||||
std::mt19937 rng(42u); // NOLINT(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed): reproducible
|
|
||||||
const char alphabet[] = "{}[]:,\"\\ \t\n\r0123456789-+.eEtrufalsn/u\x80\xbf\xc3\xe2\xed\xf0\xff"; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
||||||
for (int round = 0; round < 1500; ++round)
|
|
||||||
{
|
|
||||||
const std::string text = gen.document(1 + (round % 5));
|
|
||||||
check_both(text);
|
|
||||||
// every truncation of short documents, and a few mutations
|
|
||||||
if (text.size() < 60)
|
|
||||||
{
|
|
||||||
for (std::size_t n = 0; n < text.size(); ++n)
|
|
||||||
{
|
|
||||||
check_same<json>(text.substr(0, n));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int m = 0; m < 3 && !text.empty(); ++m)
|
|
||||||
{
|
|
||||||
std::string mutated = text;
|
|
||||||
const std::size_t pos = rng() % mutated.size();
|
|
||||||
switch (rng() % 3)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
mutated[pos] = alphabet[rng() % (sizeof(alphabet) - 1)];
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
mutated.insert(pos, 1, alphabet[rng() % (sizeof(alphabet) - 1)]);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
mutated.erase(pos, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
check_same<json>(mutated);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("narrow number types")
|
|
||||||
{
|
|
||||||
// integers that do not fit become floats, floats are converted to float
|
|
||||||
using narrow_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int32_t, std::uint32_t, float>;
|
|
||||||
const std::vector<std::string> inputs =
|
|
||||||
{
|
|
||||||
"[2147483647, 2147483648, -2147483648, -2147483649, 4294967295, 4294967296, 18446744073709551615]",
|
|
||||||
"[1.5, 3.4028234663852886e38, 3.4028235677973366e38, 1e39, -1e39, 1e-46, 0.1, 16777217]",
|
|
||||||
"[-0, 0.0, 123456789012345678901234567890, -9223372036854775809]", "1e39", "4294967296", "-2147483649"
|
|
||||||
};
|
|
||||||
for (const auto& input : inputs)
|
|
||||||
{
|
|
||||||
check_same<narrow_json>(input);
|
|
||||||
}
|
|
||||||
generator gen(17u);
|
|
||||||
for (int round = 0; round < 200; ++round)
|
|
||||||
{
|
|
||||||
check_same<narrow_json>(gen.document(1 + (round % 4)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("number corner cases at the end of the input")
|
|
||||||
{
|
|
||||||
// the number ends at the end of the block (no closing bracket after it)
|
|
||||||
for (const char* number :
|
|
||||||
{"0", "-0", "1", "12345678", "123456789", "1234567890123456789", "18446744073709551616",
|
|
||||||
"1.5", "1e5", "1e", "1.", "-", "1.5e-3", "123456789012345678901234567890e-10"
|
|
||||||
})
|
|
||||||
{
|
|
||||||
check_both(number);
|
|
||||||
check_both(std::string("[") + number + "]");
|
|
||||||
check_both(std::string("{\"n\":") + number + "}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("accept() of floats near the largest value")
|
|
||||||
{
|
|
||||||
// accept() skips digits without their value and computes it only for
|
|
||||||
// a float whose exponent may reach the largest value
|
|
||||||
for (const char* number :
|
|
||||||
{"17976931348623158e292", "17976931348623159e292", "1797693134862315.8e293", "1797693134862315.9e293",
|
|
||||||
"0.17976931348623158e309", "0.17976931348623159e309", "-17976931348623159e292", "9999999999999999999e289"
|
|
||||||
})
|
|
||||||
{
|
|
||||||
check_both(number);
|
|
||||||
check_both(std::string("[") + number + "]");
|
|
||||||
}
|
|
||||||
CHECK(json::accept("17976931348623158e292"));
|
|
||||||
CHECK_FALSE(json::accept("17976931348623159e292"));
|
|
||||||
CHECK(json::accept("[1797693134862315.8e293]"));
|
|
||||||
CHECK_FALSE(json::accept("[1797693134862315.9e293]"));
|
|
||||||
CHECK(json::accept("0.17976931348623158e309"));
|
|
||||||
CHECK_FALSE(json::accept("0.17976931348623159e309"));
|
|
||||||
CHECK_FALSE(json::accept("-17976931348623159e292"));
|
|
||||||
|
|
||||||
using narrow_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int32_t, std::uint32_t, float>;
|
|
||||||
check_same<narrow_json>("34028235e31");
|
|
||||||
check_same<narrow_json>("34028236e31");
|
|
||||||
CHECK(narrow_json::accept("34028235e31"));
|
|
||||||
CHECK_FALSE(narrow_json::accept("34028236e31"));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("callbacks, comments and trailing commas take the library parser")
|
|
||||||
{
|
|
||||||
// (the results are those of the options; the contiguous parser does not support them)
|
|
||||||
CHECK(json::parse("[1,]", nullptr, true, false, true) == json({1}));
|
|
||||||
CHECK(json::parse("[1/*c*/]", nullptr, true, true) == json({1}));
|
|
||||||
CHECK(json::accept("[1,]", false, true));
|
|
||||||
CHECK(json::accept("// c\n[1]", true));
|
|
||||||
int calls = 0;
|
|
||||||
const json j = json::parse("[1,2]", [&calls](int /*depth*/, json::parse_event_t /*event*/, json& /*parsed*/)
|
|
||||||
{
|
|
||||||
++calls;
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
CHECK(j == json({1, 2}));
|
|
||||||
CHECK(calls == 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -160,12 +160,12 @@ class strict_key
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_value;
|
std::string m_value{};
|
||||||
};
|
};
|
||||||
|
|
||||||
strict_key make_strict_key(const std::string& key)
|
strict_key make_strict_key(const std::string& key)
|
||||||
{
|
{
|
||||||
return {key.c_str()};
|
return strict_key(key.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Key, class Value, class Compare, class Allocator>
|
template<class Key, class Value, class Compare, class Allocator>
|
||||||
@@ -224,9 +224,7 @@ TEST_CASE("converting a value with keys that cannot be made from a std::string n
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// direct-initialization: a copy-initialization makes GCC 4.8 instantiate the
|
const strict_json converted = value;
|
||||||
// copy constructor, which needs a range constructor strict_object lacks
|
|
||||||
const strict_json converted(value);
|
|
||||||
|
|
||||||
// at() needs to make a std::string of a key for its error message
|
// at() needs to make a std::string of a key for its error message
|
||||||
const auto member = [](const strict_json & object, const char* name) -> const strict_json &
|
const auto member = [](const strict_json & object, const char* name) -> const strict_json &
|
||||||
|
|||||||
@@ -105,53 +105,19 @@ template<typename T>
|
|||||||
struct failing_allocator
|
struct failing_allocator
|
||||||
{
|
{
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
// GCC 4.8's std::map takes these from the allocator instead of from
|
|
||||||
// std::allocator_traits
|
|
||||||
using pointer = T*;
|
|
||||||
using const_pointer = const T*;
|
|
||||||
using reference = T&;
|
|
||||||
using const_reference = const T&;
|
|
||||||
using size_type = std::size_t;
|
|
||||||
using difference_type = std::ptrdiff_t;
|
|
||||||
|
|
||||||
template<typename U>
|
|
||||||
struct rebind
|
|
||||||
{
|
|
||||||
using other = failing_allocator<U>;
|
|
||||||
};
|
|
||||||
|
|
||||||
failing_allocator() = default;
|
failing_allocator() = default;
|
||||||
|
|
||||||
template<typename U>
|
template<typename U>
|
||||||
explicit failing_allocator(const failing_allocator<U>& /*other*/) noexcept {}
|
explicit failing_allocator(const failing_allocator<U>& /*other*/) noexcept {}
|
||||||
|
|
||||||
[[noreturn]] T* allocate(std::size_t /*n*/) // NOLINT(readability-convert-member-functions-to-static)
|
T* allocate(std::size_t /*n*/) // NOLINT(readability-convert-member-functions-to-static)
|
||||||
{
|
{
|
||||||
throw std::bad_alloc();
|
throw std::bad_alloc();
|
||||||
}
|
}
|
||||||
|
|
||||||
void deallocate(T* /*p*/, std::size_t /*n*/) noexcept {} // NOLINT(readability-convert-member-functions-to-static)
|
void deallocate(T* /*p*/, std::size_t /*n*/) noexcept {} // NOLINT(readability-convert-member-functions-to-static)
|
||||||
|
|
||||||
// GCC 4.8's std::map calls these on the allocator directly instead of
|
|
||||||
// going through std::allocator_traits
|
|
||||||
std::size_t max_size() const noexcept // NOLINT(readability-convert-member-functions-to-static)
|
|
||||||
{
|
|
||||||
return (std::numeric_limits<std::size_t>::max)() / sizeof(T);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename U, typename... Args>
|
|
||||||
void construct(U* p, Args&& ... args) // NOLINT(readability-convert-member-functions-to-static)
|
|
||||||
{
|
|
||||||
::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename U>
|
|
||||||
void destroy(U* p) noexcept // NOLINT(readability-convert-member-functions-to-static)
|
|
||||||
{
|
|
||||||
static_cast<void>(p); // unused for trivially destructible types
|
|
||||||
p->~U();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename U>
|
template<typename U>
|
||||||
bool operator==(const failing_allocator<U>& /*other*/) const noexcept
|
bool operator==(const failing_allocator<U>& /*other*/) const noexcept
|
||||||
{
|
{
|
||||||
@@ -304,7 +270,7 @@ TEST_CASE("json_view")
|
|||||||
"\"", "\"abc", "\"\\x\"", "\"\\u12\"", "\"\\ud800\"", "\"\\udc00\"", "\"\x01\"", "\"\xff\"", "\"\xc3\"", "[1]x", "/", "/*", "[\n 1,\n x\n]", // NOLINT(modernize-raw-string-literal)
|
"\"", "\"abc", "\"\\x\"", "\"\\u12\"", "\"\\ud800\"", "\"\\udc00\"", "\"\x01\"", "\"\xff\"", "\"\xc3\"", "[1]x", "/", "/*", "[\n 1,\n x\n]", // NOLINT(modernize-raw-string-literal)
|
||||||
"1e400", "-1e400", "[1.7976931348623159e308]", "{\"a\":\n{\"b\": [1, 2,\n 3 x]}}",
|
"1e400", "-1e400", "[1.7976931348623159e308]", "{\"a\":\n{\"b\": [1, 2,\n 3 x]}}",
|
||||||
// (an error after every kind of value: the error is found by replaying the parser)
|
// (an error after every kind of value: the error is found by replaying the parser)
|
||||||
R"([null, true, -1, 1, 1.5, "s", {"a": 1} 2])", R"({"a": null, "b": -1, "c": 1.5, "d": "s", "e": {}, 1})"
|
"[null, true, -1, 1, 1.5, \"s\", {\"a\": 1} 2]", "{\"a\": null, \"b\": -1, \"c\": 1.5, \"d\": \"s\", \"e\": {}, 1}"
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
CAPTURE(text)
|
CAPTURE(text)
|
||||||
@@ -1590,7 +1556,7 @@ TEST_CASE("json_view values")
|
|||||||
CHECK_THROWS_WITH_AS(not_an_object.root().get<view_map>(), json_view_test::message("type_error.302", "", 0, 5, "type must be object, but is array").c_str(), json::type_error&);
|
CHECK_THROWS_WITH_AS(not_an_object.root().get<view_map>(), json_view_test::message("type_error.302", "", 0, 5, "type must be object, but is array").c_str(), json::type_error&);
|
||||||
#if !defined(JSON_NOEXCEPTION)
|
#if !defined(JSON_NOEXCEPTION)
|
||||||
// (and one that cannot allocate its nodes)
|
// (and one that cannot allocate its nodes)
|
||||||
using failing_map = std::map<std::string, json_view, std::less<std::string>, failing_allocator<std::pair<const std::string, json_view>>>; // NOLINT(modernize-use-transparent-functors)
|
using failing_map = std::map<std::string, json_view, std::less<std::string>, failing_allocator<std::pair<const std::string, json_view>>>;
|
||||||
const json_document one_member = json_document::parse(R"({"a":1})");
|
const json_document one_member = json_document::parse(R"({"a":1})");
|
||||||
CHECK_THROWS_AS(one_member.root().get<failing_map>(), std::bad_alloc&);
|
CHECK_THROWS_AS(one_member.root().get<failing_map>(), std::bad_alloc&);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -397,14 +397,6 @@ TEST_CASE("locale with a multi-byte decimal point")
|
|||||||
|
|
||||||
// a value the locale-independent paths convert is not affected
|
// a value the locale-independent paths convert is not affected
|
||||||
CHECK(json::parse("12.5") == 12.5);
|
CHECK(json::parse("12.5") == 12.5);
|
||||||
|
|
||||||
// dump() of a long double that takes the snprintf path replaces the
|
|
||||||
// whole decimal point, not only its first byte (#5993)
|
|
||||||
CHECK(long_double_json(1.5L).dump() == "1.5");
|
|
||||||
CHECK(long_double_json(2.0L).dump() == "2.0");
|
|
||||||
CHECK(long_double_json(-0.25L).dump() == "-0.25");
|
|
||||||
CHECK(long_double_json::parse(long_double_json(1.5e-7L).dump()) == long_double_json(1.5e-7L));
|
|
||||||
CHECK(long_double_json::parse(ld.dump()) == expected_long_double);
|
|
||||||
}
|
}
|
||||||
if (!tested)
|
if (!tested)
|
||||||
{
|
{
|
||||||
@@ -513,8 +505,8 @@ struct locale_switching_token
|
|||||||
{
|
{
|
||||||
using value_type = char;
|
using value_type = char;
|
||||||
|
|
||||||
locale_switching_token(std::string text_, std::string switch_to_)
|
locale_switching_token(std::string text_, const char* switch_to_)
|
||||||
: text(std::move(text_)), switch_to(std::move(switch_to_))
|
: text(std::move(text_)), switch_to(switch_to_)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
std::size_t size() const
|
std::size_t size() const
|
||||||
@@ -531,13 +523,13 @@ struct locale_switching_token
|
|||||||
{
|
{
|
||||||
if (!switched)
|
if (!switched)
|
||||||
{
|
{
|
||||||
switched = std::setlocale(LC_NUMERIC, switch_to.c_str()) != nullptr;
|
switched = std::setlocale(LC_NUMERIC, switch_to) != nullptr;
|
||||||
}
|
}
|
||||||
return &text[0]; // NOLINT(readability-container-data-pointer): std::string::data() is const before C++17
|
return &text[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string text;
|
std::string text;
|
||||||
std::string switch_to;
|
const char* switch_to;
|
||||||
bool switched = false;
|
bool switched = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -655,7 +647,7 @@ TEST_CASE("convert_float_locale_aware")
|
|||||||
|
|
||||||
// "C" -> comma locale: strtod stops at the '.', so the conversion is repeated
|
// "C" -> comma locale: strtod stops at the '.', so the conversion is repeated
|
||||||
{
|
{
|
||||||
locale_switching_token token("1.5", comma_locale);
|
locale_switching_token token("1.5", comma_locale.c_str());
|
||||||
double value = 0;
|
double value = 0;
|
||||||
nlohmann::detail::convert_float_locale_aware(token, 1, value);
|
nlohmann::detail::convert_float_locale_aware(token, 1, value);
|
||||||
if (token.switched)
|
if (token.switched)
|
||||||
@@ -668,7 +660,7 @@ TEST_CASE("convert_float_locale_aware")
|
|||||||
// the same for the other floating-point types
|
// the same for the other floating-point types
|
||||||
REQUIRE(std::setlocale(LC_NUMERIC, "C") != nullptr);
|
REQUIRE(std::setlocale(LC_NUMERIC, "C") != nullptr);
|
||||||
{
|
{
|
||||||
locale_switching_token token("2.25", comma_locale);
|
locale_switching_token token("2.25", comma_locale.c_str());
|
||||||
float value = 0;
|
float value = 0;
|
||||||
nlohmann::detail::convert_float_locale_aware(token, 1, value);
|
nlohmann::detail::convert_float_locale_aware(token, 1, value);
|
||||||
if (token.switched)
|
if (token.switched)
|
||||||
@@ -678,7 +670,7 @@ TEST_CASE("convert_float_locale_aware")
|
|||||||
}
|
}
|
||||||
REQUIRE(std::setlocale(LC_NUMERIC, "C") != nullptr);
|
REQUIRE(std::setlocale(LC_NUMERIC, "C") != nullptr);
|
||||||
{
|
{
|
||||||
locale_switching_token token("2.25", comma_locale);
|
locale_switching_token token("2.25", comma_locale.c_str());
|
||||||
long double value = 0;
|
long double value = 0;
|
||||||
nlohmann::detail::convert_float_locale_aware(token, 1, value);
|
nlohmann::detail::convert_float_locale_aware(token, 1, value);
|
||||||
if (token.switched)
|
if (token.switched)
|
||||||
|
|||||||
@@ -122,8 +122,7 @@ TEST_CASE("JSON text with std::byte")
|
|||||||
SECTION("invalid text")
|
SECTION("invalid text")
|
||||||
{
|
{
|
||||||
CHECK_FALSE(json::accept(to_bytes("[1,\n2,")));
|
CHECK_FALSE(json::accept(to_bytes("[1,\n2,")));
|
||||||
json _;
|
CHECK_THROWS_WITH_AS(json::parse(to_bytes("[1,\n2,")),
|
||||||
CHECK_THROWS_WITH_AS(_ = json::parse(to_bytes("[1,\n2,")),
|
|
||||||
"[json.exception.parse_error.101] parse error at line 2, column 3: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&);
|
"[json.exception.parse_error.101] parse error at line 2, column 3: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -541,7 +541,6 @@ TEST_CASE("object_key_index")
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::vector<std::size_t> hashes;
|
std::vector<std::size_t> hashes;
|
||||||
hashes.reserve(initial);
|
|
||||||
for (std::size_t i = 0; i < initial; ++i)
|
for (std::size_t i = 0; i < initial; ++i)
|
||||||
{
|
{
|
||||||
hashes.push_back(hash_of(i));
|
hashes.push_back(hash_of(i));
|
||||||
|
|||||||
@@ -1533,7 +1533,7 @@ TEST_CASE("regression tests 1")
|
|||||||
{
|
{
|
||||||
my_json::object_t top = orig.get_ref<const my_json::object_t&>();
|
my_json::object_t top = orig.get_ref<const my_json::object_t&>();
|
||||||
top.at("x") = my_json::parse(R"({"a":{"b":1},"n":null,"arr":[10,20],"objs":[{"m":5},{"m":6}]})");
|
top.at("x") = my_json::parse(R"({"a":{"b":1},"n":null,"arr":[10,20],"objs":[{"m":5},{"m":6}]})");
|
||||||
return my_json(std::move(top)); // NOLINT(modernize-return-braced-init-list): {top} would be an array
|
return my_json(std::move(top));
|
||||||
};
|
};
|
||||||
|
|
||||||
SECTION("existing members")
|
SECTION("existing members")
|
||||||
|
|||||||
@@ -1009,7 +1009,7 @@ TEST_CASE("regression test - excessive binary container size honors allow_except
|
|||||||
// allow_exceptions=true (the default) must still throw exactly as before.
|
// allow_exceptions=true (the default) must still throw exactly as before.
|
||||||
// The exact message text is not checked here: on platforms where
|
// The exact message text is not checked here: on platforms where
|
||||||
// std::size_t is 32-bit, the CBOR reader's own length-narrowing check
|
// std::size_t is 32-bit, the CBOR reader's own length-narrowing check
|
||||||
// (get_container_size(), unrelated to this fix) intercepts a
|
// (get_cbor_container_size(), unrelated to this fix) intercepts a
|
||||||
// declared length of 2^63 before it ever reaches the check this test
|
// declared length of 2^63 before it ever reaches the check this test
|
||||||
// targets, with different (but equally valid, and already correct)
|
// targets, with different (but equally valid, and already correct)
|
||||||
// wording -- see unit-cbor.cpp for coverage of that message.
|
// wording -- see unit-cbor.cpp for coverage of that message.
|
||||||
|
|||||||
@@ -120,7 +120,6 @@ class oversized_get_area_streambuf : public std::streambuf
|
|||||||
private:
|
private:
|
||||||
std::array<char, 8> m_data{{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}};
|
std::array<char, 8> m_data{{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// how a test reads from the stream
|
// how a test reads from the stream
|
||||||
@@ -464,18 +463,15 @@ TEST_CASE("parsing from a std::istream scans the get area in bulk")
|
|||||||
CHECK(token.empty());
|
CHECK(token.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SIZE_MAX > UINT32_MAX
|
|
||||||
// a get area larger than an int needs a 64-bit std::size_t (checked by the
|
|
||||||
// preprocessor, as a constant condition triggers MSVC's C4127)
|
|
||||||
SECTION("skipping more characters than gbump() takes at once")
|
SECTION("skipping more characters than gbump() takes at once")
|
||||||
{
|
{
|
||||||
// gbump() takes an int, but a get area can be larger
|
// gbump() takes an int, but a get area can be larger
|
||||||
const std::size_t claimed = std::size_t{3} << 30u;
|
const std::size_t claimed = sizeof(std::size_t) < 8 ? 0 : std::size_t{3} << 30u;
|
||||||
oversized_get_area_streambuf buf(claimed);
|
oversized_get_area_streambuf buf(claimed);
|
||||||
if (static_cast<std::size_t>(buf.in_avail()) != claimed)
|
if (claimed == 0 || static_cast<std::size_t>(buf.in_avail()) != claimed)
|
||||||
{
|
{
|
||||||
// a standard library whose streambuf stores the size of the get
|
// a 32-bit size_t, or a standard library whose streambuf stores
|
||||||
// area in an int (MSVC)
|
// the size of the get area in an int (MSVC)
|
||||||
MESSAGE("a get area larger than an int is not supported");
|
MESSAGE("a get area larger than an int is not supported");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -495,7 +491,6 @@ TEST_CASE("parsing from a std::istream scans the get area in bulk")
|
|||||||
buf.restore_get_area();
|
buf.restore_get_area();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SECTION("SAX parsing from a stream")
|
SECTION("SAX parsing from a stream")
|
||||||
|
|||||||
@@ -2385,18 +2385,6 @@ TEST_CASE("UBJSON optimized arrays of a valueless type are bounded")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("a count equal to SIZE_MAX is rejected")
|
|
||||||
{
|
|
||||||
// the reader marks a container without a count by SIZE_MAX, so a
|
|
||||||
// count of that value must not be read as "no count"; UBJSON's
|
|
||||||
// largest count type (int64) can only reach it where size_t is 32 bits
|
|
||||||
#if SIZE_MAX == 0xffffffff
|
|
||||||
const std::vector<uint8_t> input = {'[', '#', 'L', 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 'i', 0x01, ']'};
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(input), "[json.exception.out_of_range.408] syntax error while parsing UBJSON size: integer value overflow", json::out_of_range&);
|
|
||||||
CHECK(json::from_ubjson(input, true, false).is_discarded());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("ordinary counts are unaffected")
|
SECTION("ordinary counts are unaffected")
|
||||||
{
|
{
|
||||||
CHECK(json::from_ubjson(std::vector<uint8_t>({'[', '$', 'Z', '#', 'i', 3})) == json({nullptr, nullptr, nullptr}));
|
CHECK(json::from_ubjson(std::vector<uint8_t>({'[', '$', 'Z', '#', 'i', 3})) == json({nullptr, nullptr, nullptr}));
|
||||||
|
|||||||
Reference in new issue
Block a user