mirror of
https://github.com/nlohmann/json.git
synced 2026-10-01 12:10:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e93415d91 | ||
|
|
b54ed188e6 | ||
|
|
cff0a61369 | ||
|
|
3926fcaac3 | ||
|
|
18dd5663b0 | ||
|
|
a32f61eb98 | ||
|
|
7e8e8e219b | ||
|
|
1d675cdb46 | ||
|
|
c261578431 | ||
|
|
35802e78d6 | ||
|
|
791cd88dfc | ||
|
|
7d7055ec50 | ||
|
|
3b6ae43c53 | ||
|
|
6a073dbae4 | ||
|
|
fdcc569eee | ||
|
|
5f1727cef2 | ||
|
|
6fc0d501f3 | ||
|
|
67435c9c7e | ||
|
|
2ea6d8c127 | ||
|
|
66877675b1 | ||
|
|
7fd6895788 | ||
|
|
6ae17630a4 | ||
|
|
5bd766aa50 | ||
|
|
4bb1b14b06 | ||
|
|
bfe0f32d71 | ||
|
|
44a88d85be | ||
|
|
5b11a0282c | ||
|
|
bfea6f36d3 | ||
|
|
e444a66276 | ||
|
|
1151826508 | ||
|
|
6218aa212b | ||
|
|
42f89e3130 | ||
|
|
6d7845d207 | ||
|
|
678fd3017b | ||
|
|
fc4c9c3446 | ||
|
|
68beba727c | ||
|
|
0050d0f7a4 | ||
|
|
6a8a7735ed | ||
|
|
65af260117 | ||
|
|
d268eaa693 | ||
|
|
5b9ffae8bb | ||
|
|
04f6ddd227 | ||
|
|
c16dd7e4f5 | ||
|
|
792853d725 | ||
|
|
4bdf1b7e74 | ||
|
|
b1e9d98e41 | ||
|
|
f855d257df | ||
|
|
3e683e9c04 | ||
|
|
d1d84ed9af | ||
|
|
de8529f99b | ||
|
|
677794f076 | ||
|
|
437a95cfdb | ||
|
|
c8735246d0 | ||
|
|
9adb510a0d |
-17
@@ -1,17 +0,0 @@
|
|||||||
arm_container:
|
|
||||||
image: gcc:latest
|
|
||||||
|
|
||||||
check_task:
|
|
||||||
check_script:
|
|
||||||
# the gcc image ships an outdated CMake, so fetch a recent prebuilt binary
|
|
||||||
# instead of compiling CMake from source
|
|
||||||
- wget -q https://github.com/Kitware/CMake/releases/download/v4.3.4/cmake-4.3.4-linux-aarch64.tar.gz
|
|
||||||
- tar xfz cmake-4.3.4-linux-aarch64.tar.gz
|
|
||||||
- export PATH="$(pwd)/cmake-4.3.4-linux-aarch64/bin:$PATH"
|
|
||||||
- cmake --version
|
|
||||||
- mkdir build
|
|
||||||
- cd build
|
|
||||||
- cmake .. -DJSON_FastTests=ON
|
|
||||||
- make -j4
|
|
||||||
- cd tests
|
|
||||||
- ctest -j4
|
|
||||||
@@ -24,6 +24,10 @@ updates:
|
|||||||
interval: daily
|
interval: daily
|
||||||
cooldown:
|
cooldown:
|
||||||
default-days: 7
|
default-days: 7
|
||||||
|
ignore:
|
||||||
|
# astyle is deliberately pinned (see tools/astyle/requirements.txt): newer versions
|
||||||
|
# reformat the code, and the pinned version defines the formatting CI enforces
|
||||||
|
- dependency-name: astyle
|
||||||
|
|
||||||
- package-ecosystem: pip
|
- package-ecosystem: pip
|
||||||
directory: /tools/generate_natvis
|
directory: /tools/generate_natvis
|
||||||
|
|||||||
@@ -30,14 +30,6 @@ environment:
|
|||||||
CMAKE_OPTIONS: ""
|
CMAKE_OPTIONS: ""
|
||||||
GENERATOR: Visual Studio 14 2015
|
GENERATOR: Visual Studio 14 2015
|
||||||
|
|
||||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
|
||||||
configuration: Release
|
|
||||||
platform: x86
|
|
||||||
name: with_win_header
|
|
||||||
CXX_FLAGS: "/W4 /WX"
|
|
||||||
CMAKE_OPTIONS: ""
|
|
||||||
GENERATOR: Visual Studio 14 2015
|
|
||||||
|
|
||||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||||
configuration: Release
|
configuration: Release
|
||||||
platform: x86
|
platform: x86
|
||||||
@@ -74,9 +66,6 @@ install:
|
|||||||
- if "%platform%"=="x86" set GENERATOR_PLATFORM=Win32
|
- if "%platform%"=="x86" set GENERATOR_PLATFORM=Win32
|
||||||
|
|
||||||
before_build:
|
before_build:
|
||||||
# for with_win_header build, inject the inclusion of Windows.h to the single-header library
|
|
||||||
- ps: if ($env:name -Eq "with_win_header") { $header_path = "single_include\nlohmann\json.hpp" }
|
|
||||||
- ps: if ($env:name -Eq "with_win_header") { "#include <Windows.h>`n" + (Get-Content $header_path | Out-String) | Set-Content $header_path }
|
|
||||||
- cmake . -G "%GENERATOR%" -A "%GENERATOR_PLATFORM%" -DCMAKE_CXX_FLAGS="%CXX_FLAGS%" -DCMAKE_IGNORE_PATH="C:/Program Files/Git/usr/bin" -DJSON_BuildTests=On "%CMAKE_OPTIONS%"
|
- cmake . -G "%GENERATOR%" -A "%GENERATOR_PLATFORM%" -DCMAKE_CXX_FLAGS="%CXX_FLAGS%" -DCMAKE_IGNORE_PATH="C:/Program Files/Git/usr/bin" -DJSON_BuildTests=On "%CMAKE_OPTIONS%"
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ jobs:
|
|||||||
MAIN_DIR: ${{ github.workspace }}/main
|
MAIN_DIR: ${{ github.workspace }}/main
|
||||||
INCLUDE_DIR: ${{ github.workspace }}/main/single_include/nlohmann
|
INCLUDE_DIR: ${{ github.workspace }}/main/single_include/nlohmann
|
||||||
TOOL_DIR: ${{ github.workspace }}/tools/tools/amalgamate
|
TOOL_DIR: ${{ github.workspace }}/tools/tools/amalgamate
|
||||||
|
NATVIS_TOOL_DIR: ${{ github.workspace }}/tools/tools/generate_natvis
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
@@ -61,12 +62,55 @@ jobs:
|
|||||||
python3 -mvenv venv
|
python3 -mvenv venv
|
||||||
venv/bin/pip3 install -r $MAIN_DIR/tools/astyle/requirements.txt
|
venv/bin/pip3 install -r $MAIN_DIR/tools/astyle/requirements.txt
|
||||||
|
|
||||||
|
- name: Install generate_natvis dependencies
|
||||||
|
run: pip3 install -r $NATVIS_TOOL_DIR/requirements.txt
|
||||||
|
|
||||||
|
- name: Regenerate the tools/macro_builder tables in macro_scope.hpp
|
||||||
|
run: |
|
||||||
|
cd $MAIN_DIR
|
||||||
|
|
||||||
|
# Built from this PR's own tools/macro_builder/main.cpp, not a
|
||||||
|
# develop checkout: unlike amalgamate.py and generate_natvis.py,
|
||||||
|
# this tool has no other source of truth to check against (its
|
||||||
|
# own README documents that it must reproduce macro_scope.hpp
|
||||||
|
# byte for byte), so there is nothing to gain from checking out a
|
||||||
|
# separate copy, and doing so would make this step fail on a PR
|
||||||
|
# that adds support for a new dispatch table until that PR itself
|
||||||
|
# merges to develop, the same way generate_natvis.py's --version
|
||||||
|
# requirement briefly did.
|
||||||
|
TMPDIR=$(mktemp -d ./macro_builder_check.XXXXXX)
|
||||||
|
c++ -std=c++11 tools/macro_builder/main.cpp -o "$TMPDIR/macro_builder"
|
||||||
|
"$TMPDIR/macro_builder" > "$TMPDIR/paste.hpp"
|
||||||
|
"$TMPDIR/macro_builder" type_body > "$TMPDIR/type_body.hpp"
|
||||||
|
|
||||||
|
# Splice the (still unindented) generated blocks back into
|
||||||
|
# macro_scope.hpp; the astyle pass below indents their
|
||||||
|
# continuation lines the same way it does for the rest of
|
||||||
|
# include/, so a correctly regenerated file comes out unchanged.
|
||||||
|
awk -v newfile="$TMPDIR/paste.hpp" '
|
||||||
|
BEGIN { while ((getline line < newfile) > 0) { new = new line "\n" } }
|
||||||
|
/^#define NLOHMANN_JSON_EXPAND\( x \) x$/ { printf "%s", new; skip=1 }
|
||||||
|
skip && /^#define NLOHMANN_JSON_DOUBLE_PASTE63\(/ { skip=0; next }
|
||||||
|
skip { next }
|
||||||
|
{ print }
|
||||||
|
' include/nlohmann/detail/macro_scope.hpp > "$TMPDIR/macro_scope_1.hpp"
|
||||||
|
awk -v newfile="$TMPDIR/type_body.hpp" '
|
||||||
|
BEGIN { while ((getline line < newfile) > 0) { new = new line "\n" } }
|
||||||
|
/^#define NLOHMANN_JSON_TYPE_BODY\(Prefix, \.\.\.\)/ { printf "%s", new; skip=1 }
|
||||||
|
skip && /^[[:space:]]*NLOHMANN_JSON_TYPE_BODY_SENTINEL\)\)$/ { skip=0; next }
|
||||||
|
skip { next }
|
||||||
|
{ print }
|
||||||
|
' "$TMPDIR/macro_scope_1.hpp" > "$TMPDIR/macro_scope_2.hpp"
|
||||||
|
mv "$TMPDIR/macro_scope_2.hpp" include/nlohmann/detail/macro_scope.hpp
|
||||||
|
rm -rf "$TMPDIR"
|
||||||
|
|
||||||
- name: Regenerate amalgamation, formatting, and BUILD.bazel
|
- name: Regenerate amalgamation, formatting, and BUILD.bazel
|
||||||
run: |
|
run: |
|
||||||
cd $MAIN_DIR
|
cd $MAIN_DIR
|
||||||
|
|
||||||
python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json.json -s .
|
python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json.json -s .
|
||||||
python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json_fwd.json -s .
|
python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json_fwd.json -s .
|
||||||
|
cp include/nlohmann/json_literals.hpp $INCLUDE_DIR/json_literals.hpp
|
||||||
|
|
||||||
# the header list of the Bazel "json" target must match the files in include/
|
# the header list of the Bazel "json" target must match the files in include/
|
||||||
cmake -P cmake/scripts/gen_bazel_build_file.cmake
|
cmake -P cmake/scripts/gen_bazel_build_file.cmake
|
||||||
@@ -87,6 +131,19 @@ jobs:
|
|||||||
${{ github.workspace }}/venv/bin/astyle --project=tools/astyle/.astylerc --suffix=none --quiet \
|
${{ github.workspace }}/venv/bin/astyle --project=tools/astyle/.astylerc --suffix=none --quiet \
|
||||||
$(find $SOURCE_DIRS -type f \( -name '*.hpp' -o -name '*.cpp' -o -name '*.cu' \) -not -path 'tests/thirdparty/*' -not -path 'tests/abi/include/nlohmann/*' | sort)
|
$(find $SOURCE_DIRS -type f \( -name '*.hpp' -o -name '*.cpp' -o -name '*.cu' \) -not -path 'tests/thirdparty/*' -not -path 'tests/abi/include/nlohmann/*' | sort)
|
||||||
|
|
||||||
|
- name: Regenerate nlohmann_json.natvis
|
||||||
|
run: |
|
||||||
|
cd $MAIN_DIR
|
||||||
|
# Pass --version explicitly so this step also works with the tool
|
||||||
|
# copy from develop before this repository's own generate_natvis.py
|
||||||
|
# learns to derive the version itself: the older script requires
|
||||||
|
# --version, and the newer one accepts it as an explicit override.
|
||||||
|
ABI_MACROS=include/nlohmann/detail/abi_macros.hpp
|
||||||
|
VERSION_MAJOR=$(grep -m1 'define NLOHMANN_JSON_VERSION_MAJOR' $ABI_MACROS | grep -o '[0-9]\+')
|
||||||
|
VERSION_MINOR=$(grep -m1 'define NLOHMANN_JSON_VERSION_MINOR' $ABI_MACROS | grep -o '[0-9]\+')
|
||||||
|
VERSION_PATCH=$(grep -m1 'define NLOHMANN_JSON_VERSION_PATCH' $ABI_MACROS | grep -o '[0-9]\+')
|
||||||
|
python3 $NATVIS_TOOL_DIR/generate_natvis.py --version "$VERSION_MAJOR.$VERSION_MINOR.$VERSION_PATCH" $MAIN_DIR
|
||||||
|
|
||||||
- name: Build patch and check for differences
|
- name: Build patch and check for differences
|
||||||
id: diff
|
id: diff
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -7,6 +7,16 @@ on:
|
|||||||
- develop
|
- develop
|
||||||
paths:
|
paths:
|
||||||
- docs/mkdocs/**
|
- docs/mkdocs/**
|
||||||
|
# the site also embeds these files via pymdownx.snippets
|
||||||
|
# (mkdocs.yml sets restrict_base_path: false for this)
|
||||||
|
- .clang-tidy
|
||||||
|
- .github/CODE_OF_CONDUCT.md
|
||||||
|
- .github/CONTRIBUTING.md
|
||||||
|
- .github/SECURITY.md
|
||||||
|
- cmake/clang_flags.cmake
|
||||||
|
- cmake/gcc_flags.cmake
|
||||||
|
- tests/fmt_formatter/project/main.cpp
|
||||||
|
- tools/astyle/.astylerc
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
# we don't want to have concurrent jobs, and we don't want to cancel running jobs to avoid broken publications
|
# we don't want to have concurrent jobs, and we don't want to cancel running jobs to avoid broken publications
|
||||||
@@ -23,7 +33,7 @@ jobs:
|
|||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
if: github.repository == 'nlohmann/json'
|
if: github.repository == 'nlohmann/json'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
||||||
@@ -31,6 +41,8 @@ jobs:
|
|||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Install virtual environment
|
- name: Install virtual environment
|
||||||
run: make install_venv -C docs/mkdocs
|
run: make install_venv -C docs/mkdocs
|
||||||
|
|||||||
@@ -52,6 +52,13 @@ jobs:
|
|||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build --target ci_infer
|
run: cmake --build build --target ci_infer
|
||||||
|
- name: Archive Infer report
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: infer-report
|
||||||
|
path: ${{ github.workspace }}/build/build_infer/infer-out/report.txt
|
||||||
|
if-no-files-found: ignore
|
||||||
|
|
||||||
ci_static_analysis_ubuntu:
|
ci_static_analysis_ubuntu:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -97,13 +104,13 @@ jobs:
|
|||||||
|
|
||||||
ci_cmake_options:
|
ci_cmake_options:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: ubuntu:focal
|
container: ubuntu:24.04
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
target: [ci_cmake_flags, ci_test_diagnostics, ci_test_diagnostic_positions, ci_test_noexceptions, ci_test_noimplicitconversions, ci_test_legacycomparison, ci_test_noglobaludls, ci_test_disableenumserialization, ci_test_skiplibraryversioncheck, ci_test_simdutf, ci_test_strict_nul_handling, ci_test_no_thread_local]
|
target: [ci_cmake_flags, ci_test_diagnostics, ci_test_diagnostic_positions, ci_test_noexceptions, ci_test_noimplicitconversions, ci_test_legacycomparison, ci_test_noglobaludls, ci_test_disableenumserialization, ci_test_disabletuplereferenceconversion, ci_test_skiplibraryversioncheck, ci_test_simdutf, ci_test_strict_nul_handling, ci_test_no_thread_local]
|
||||||
steps:
|
steps:
|
||||||
- name: Install build-essential
|
- name: Install build-essential
|
||||||
run: apt-get update ; apt-get install -y build-essential unzip wget git libssl-dev
|
run: apt-get update ; apt-get install -y build-essential unzip wget git
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
@@ -346,6 +353,8 @@ jobs:
|
|||||||
container: intel/oneapi-hpckit:latest
|
container: intel/oneapi-hpckit:latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
@@ -358,6 +367,8 @@ jobs:
|
|||||||
container: nvcr.io/nvidia/nvhpc:25.5-devel-cuda12.9-ubuntu22.04
|
container: nvcr.io/nvidia/nvhpc:25.5-devel-cuda12.9-ubuntu22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
@@ -403,3 +414,21 @@ jobs:
|
|||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build --target ${{ matrix.target }}
|
run: cmake --build build --target ${{ matrix.target }}
|
||||||
|
|
||||||
|
# Native Linux ARM64 runner with the latest GCC. The ubuntu-24.04-arm label is
|
||||||
|
# only available for public repositories.
|
||||||
|
ci_test_arm64:
|
||||||
|
runs-on: ubuntu-24.04-arm
|
||||||
|
container: gcc:latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Get latest CMake and ninja
|
||||||
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
|
- name: Check that the job runs natively on arm64
|
||||||
|
run: uname -m && test "$(uname -m)" = aarch64
|
||||||
|
- name: Run CMake
|
||||||
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build build --target ci_test_compiler_default
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Set extra CXX_FLAGS for latest std_version
|
- name: Set extra CXX_FLAGS for latest std_version
|
||||||
@@ -123,6 +125,8 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- name: Run CMake (Release)
|
- name: Run CMake (Release)
|
||||||
run: cmake -S . -B build -G "Visual Studio 18 2026" -A ARM64 -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/W4 /WX"
|
run: cmake -S . -B build -G "Visual Studio 18 2026" -A ARM64 -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/W4 /WX"
|
||||||
if: matrix.build_type == 'Release'
|
if: matrix.build_type == 'Release'
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"_comment": "Used by the ci_infer CMake target (#5715 item 4b). fail-on-issue makes CI fail on Infer findings; disable-issue-type is a type-level baseline for the ~174 pre-existing findings (all PULSE_UNNECESSARY_COPY*/PULSE_RESOURCE_LEAK/PULSE_CONST_REFABLE, mostly in test code) triaged in run https://github.com/nlohmann/json/actions/runs/35829411620 on commit 1054b2097, so CI fails only on a NEW issue type. Remove an entry here once its findings have been fixed or explicitly accepted.",
|
||||||
|
"fail-on-issue": true,
|
||||||
|
"disable-issue-type": [
|
||||||
|
"PULSE_UNNECESSARY_COPY_ASSIGNMENT",
|
||||||
|
"PULSE_UNNECESSARY_COPY",
|
||||||
|
"PULSE_UNNECESSARY_COPY_INTERMEDIATE",
|
||||||
|
"PULSE_UNNECESSARY_COPY_OPTIONAL",
|
||||||
|
"PULSE_RESOURCE_LEAK",
|
||||||
|
"PULSE_CONST_REFABLE"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -23,10 +23,6 @@ Files: tests/thirdparty/fifo_map/*
|
|||||||
Copyright: 2015-2017 Niels Lohmann
|
Copyright: 2015-2017 Niels Lohmann
|
||||||
License: MIT
|
License: MIT
|
||||||
|
|
||||||
Files: tests/thirdparty/Fuzzer/*
|
|
||||||
Copyright: 2003-2022 LLVM Project.
|
|
||||||
License: Apache-2.0
|
|
||||||
|
|
||||||
Files: tests/thirdparty/imapdl/*
|
Files: tests/thirdparty/imapdl/*
|
||||||
Copyright: 2017 Georg Sauthoff <mail@gms.tf>
|
Copyright: 2017 Georg Sauthoff <mail@gms.tf>
|
||||||
License: GPL-3.0-only
|
License: GPL-3.0-only
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ cc_library(
|
|||||||
"include/nlohmann/adl_serializer.hpp",
|
"include/nlohmann/adl_serializer.hpp",
|
||||||
"include/nlohmann/byte_container_with_subtype.hpp",
|
"include/nlohmann/byte_container_with_subtype.hpp",
|
||||||
"include/nlohmann/detail/abi_macros.hpp",
|
"include/nlohmann/detail/abi_macros.hpp",
|
||||||
|
"include/nlohmann/detail/bit_ops.hpp",
|
||||||
"include/nlohmann/detail/conversions/from_json.hpp",
|
"include/nlohmann/detail/conversions/from_json.hpp",
|
||||||
"include/nlohmann/detail/conversions/to_chars.hpp",
|
"include/nlohmann/detail/conversions/to_chars.hpp",
|
||||||
"include/nlohmann/detail/conversions/to_json.hpp",
|
"include/nlohmann/detail/conversions/to_json.hpp",
|
||||||
@@ -33,6 +34,7 @@ cc_library(
|
|||||||
"include/nlohmann/detail/input/number_parse.hpp",
|
"include/nlohmann/detail/input/number_parse.hpp",
|
||||||
"include/nlohmann/detail/input/parser.hpp",
|
"include/nlohmann/detail/input/parser.hpp",
|
||||||
"include/nlohmann/detail/input/position_t.hpp",
|
"include/nlohmann/detail/input/position_t.hpp",
|
||||||
|
"include/nlohmann/detail/input/pow5_table.hpp",
|
||||||
"include/nlohmann/detail/input/string_scan.hpp",
|
"include/nlohmann/detail/input/string_scan.hpp",
|
||||||
"include/nlohmann/detail/iterators/internal_iterator.hpp",
|
"include/nlohmann/detail/iterators/internal_iterator.hpp",
|
||||||
"include/nlohmann/detail/iterators/iter_impl.hpp",
|
"include/nlohmann/detail/iterators/iter_impl.hpp",
|
||||||
@@ -65,6 +67,7 @@ cc_library(
|
|||||||
"include/nlohmann/detail/value_t.hpp",
|
"include/nlohmann/detail/value_t.hpp",
|
||||||
"include/nlohmann/json.hpp",
|
"include/nlohmann/json.hpp",
|
||||||
"include/nlohmann/json_fwd.hpp",
|
"include/nlohmann/json_fwd.hpp",
|
||||||
|
"include/nlohmann/json_literals.hpp",
|
||||||
"include/nlohmann/ordered_map.hpp",
|
"include/nlohmann/ordered_map.hpp",
|
||||||
"include/nlohmann/thirdparty/hedley/hedley.hpp",
|
"include/nlohmann/thirdparty/hedley/hedley.hpp",
|
||||||
"include/nlohmann/thirdparty/hedley/hedley_undef.hpp",
|
"include/nlohmann/thirdparty/hedley/hedley_undef.hpp",
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ option(JSON_Diagnostic_Positions "Enable diagnostic positions." OFF)
|
|||||||
option(JSON_GlobalUDLs "Place user-defined string literals in the global namespace." ON)
|
option(JSON_GlobalUDLs "Place user-defined string literals in the global namespace." ON)
|
||||||
option(JSON_ImplicitConversions "Enable implicit conversions." ON)
|
option(JSON_ImplicitConversions "Enable implicit conversions." ON)
|
||||||
option(JSON_DisableEnumSerialization "Disable default integer enum serialization." OFF)
|
option(JSON_DisableEnumSerialization "Disable default integer enum serialization." OFF)
|
||||||
|
option(JSON_DisableTupleReferenceConversion "Disable conversion from a one-element tuple of a JSON reference." OFF)
|
||||||
option(JSON_LegacyDiscardedValueComparison "Enable legacy discarded value comparison." OFF)
|
option(JSON_LegacyDiscardedValueComparison "Enable legacy discarded value comparison." OFF)
|
||||||
option(JSON_Install "Install CMake targets during install step." ${MAIN_PROJECT})
|
option(JSON_Install "Install CMake targets during install step." ${MAIN_PROJECT})
|
||||||
option(JSON_MultipleHeaders "Use non-amalgamated version of the library." ON)
|
option(JSON_MultipleHeaders "Use non-amalgamated version of the library." ON)
|
||||||
@@ -101,6 +102,10 @@ if (JSON_DisableEnumSerialization)
|
|||||||
message(STATUS "Enum integer serialization is disabled (JSON_DISABLE_ENUM_SERIALIZATION=1)")
|
message(STATUS "Enum integer serialization is disabled (JSON_DISABLE_ENUM_SERIALIZATION=1)")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (JSON_DisableTupleReferenceConversion)
|
||||||
|
message(STATUS "Tuple reference conversion is disabled (JSON_DISABLE_TUPLE_REFERENCE_CONVERSION=1)")
|
||||||
|
endif()
|
||||||
|
|
||||||
if (JSON_LegacyDiscardedValueComparison)
|
if (JSON_LegacyDiscardedValueComparison)
|
||||||
message(STATUS "Legacy discarded value comparison enabled (JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1)")
|
message(STATUS "Legacy discarded value comparison enabled (JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1)")
|
||||||
endif()
|
endif()
|
||||||
@@ -143,6 +148,7 @@ target_compile_definitions(
|
|||||||
$<$<NOT:$<BOOL:${JSON_GlobalUDLs}>>:JSON_USE_GLOBAL_UDLS=0>
|
$<$<NOT:$<BOOL:${JSON_GlobalUDLs}>>:JSON_USE_GLOBAL_UDLS=0>
|
||||||
$<$<NOT:$<BOOL:${JSON_ImplicitConversions}>>:JSON_USE_IMPLICIT_CONVERSIONS=0>
|
$<$<NOT:$<BOOL:${JSON_ImplicitConversions}>>:JSON_USE_IMPLICIT_CONVERSIONS=0>
|
||||||
$<$<BOOL:${JSON_DisableEnumSerialization}>:JSON_DISABLE_ENUM_SERIALIZATION=1>
|
$<$<BOOL:${JSON_DisableEnumSerialization}>:JSON_DISABLE_ENUM_SERIALIZATION=1>
|
||||||
|
$<$<BOOL:${JSON_DisableTupleReferenceConversion}>:JSON_DISABLE_TUPLE_REFERENCE_CONVERSION=1>
|
||||||
$<$<BOOL:${JSON_Diagnostics}>:JSON_DIAGNOSTICS=1>
|
$<$<BOOL:${JSON_Diagnostics}>:JSON_DIAGNOSTICS=1>
|
||||||
$<$<BOOL:${JSON_Diagnostic_Positions}>:JSON_DIAGNOSTIC_POSITIONS=1>
|
$<$<BOOL:${JSON_Diagnostic_Positions}>:JSON_DIAGNOSTIC_POSITIONS=1>
|
||||||
$<$<BOOL:${JSON_LegacyDiscardedValueComparison}>:JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1>
|
$<$<BOOL:${JSON_LegacyDiscardedValueComparison}>:JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1>
|
||||||
|
|||||||
@@ -33,17 +33,6 @@ Further documentation:
|
|||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> The folder `.github/workflows` is predetermined by GitHub.
|
> The folder `.github/workflows` is predetermined by GitHub.
|
||||||
|
|
||||||
### `.cirrus.yml`
|
|
||||||
|
|
||||||
Configuration file for the pipeline at [Cirrus CI](https://cirrus-ci.com/github/nlohmann/json).
|
|
||||||
|
|
||||||
Further documentation:
|
|
||||||
|
|
||||||
- [Writing tasks](https://cirrus-ci.org/guide/writing-tasks/)
|
|
||||||
|
|
||||||
> [!IMPORTANT]
|
|
||||||
> The filename `.cirrus.yml` and position (root of the repository) are predetermined by Cirrus CI.
|
|
||||||
|
|
||||||
### `.github/external_ci/appveyor.yml`
|
### `.github/external_ci/appveyor.yml`
|
||||||
|
|
||||||
Configuration for the pipelines at [AppVeyor](https://ci.appveyor.com/project/nlohmann/json).
|
Configuration for the pipelines at [AppVeyor](https://ci.appveyor.com/project/nlohmann/json).
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.PHONY: pretty clean ChangeLog.md release update_hedley update_hedley_undef BUILD.bazel
|
.PHONY: pretty clean ChangeLog.md release update_hedley update_hedley_undef BUILD.bazel natvis macro_builder_check
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# configuration
|
# configuration
|
||||||
@@ -21,6 +21,11 @@ TESTS_SRCS=$(shell find tests -type f \( -name '*.hpp' -o -name '*.cpp' -o -name
|
|||||||
# the single headers (amalgamated from the source files)
|
# the single headers (amalgamated from the source files)
|
||||||
AMALGAMATED_FILE=single_include/nlohmann/json.hpp
|
AMALGAMATED_FILE=single_include/nlohmann/json.hpp
|
||||||
AMALGAMATED_FWD_FILE=single_include/nlohmann/json_fwd.hpp
|
AMALGAMATED_FWD_FILE=single_include/nlohmann/json_fwd.hpp
|
||||||
|
# json_literals.hpp only includes <nlohmann/json.hpp>, so it is copied verbatim
|
||||||
|
AMALGAMATED_LITERALS_FILE=single_include/nlohmann/json_literals.hpp
|
||||||
|
|
||||||
|
# the header with the argument-counting macros generated by tools/macro_builder
|
||||||
|
MACRO_SCOPE_HPP=include/nlohmann/detail/macro_scope.hpp
|
||||||
|
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
@@ -29,18 +34,14 @@ AMALGAMATED_FWD_FILE=single_include/nlohmann/json_fwd.hpp
|
|||||||
|
|
||||||
# main target
|
# main target
|
||||||
all:
|
all:
|
||||||
@echo "amalgamate - amalgamate files single_include/nlohmann/json{,_fwd}.hpp from the include/nlohmann sources"
|
@echo "amalgamate - amalgamate files single_include/nlohmann/json{,_fwd,_literals}.hpp from the include/nlohmann sources"
|
||||||
@echo "BUILD.bazel - regenerate the Bazel BUILD file from the include/nlohmann sources"
|
@echo "BUILD.bazel - regenerate the Bazel BUILD file from the include/nlohmann sources"
|
||||||
@echo "ChangeLog.md - generate ChangeLog file"
|
@echo "ChangeLog.md - generate ChangeLog file"
|
||||||
@echo "check-amalgamation - check whether sources have been amalgamated and BUILD.bazel is up to date"
|
@echo "check-amalgamation - check whether sources have been amalgamated and BUILD.bazel is up to date"
|
||||||
@echo "clean - remove built files"
|
@echo "clean - remove built files"
|
||||||
@echo "doctest - compile example files and check their output"
|
@echo "fuzzing - see tests/fuzzing.md for how to build and run the fuzzers"
|
||||||
@echo "fuzz_testing - prepare fuzz testing of the JSON parser"
|
@echo "macro_builder_check - check that macro_scope.hpp matches tools/macro_builder's output"
|
||||||
@echo "fuzz_testing_bon8 - prepare fuzz testing of the BON8 parser"
|
@echo "natvis - regenerate nlohmann_json.natvis from the current ABI tags and version"
|
||||||
@echo "fuzz_testing_bson - prepare fuzz testing of the BSON parser"
|
|
||||||
@echo "fuzz_testing_cbor - prepare fuzz testing of the CBOR parser"
|
|
||||||
@echo "fuzz_testing_msgpack - prepare fuzz testing of the MessagePack parser"
|
|
||||||
@echo "fuzz_testing_ubjson - prepare fuzz testing of the UBJSON parser"
|
|
||||||
@echo "pretty - beautify code with Artistic Style"
|
@echo "pretty - beautify code with Artistic Style"
|
||||||
@echo "run_benchmarks - build and run benchmarks"
|
@echo "run_benchmarks - build and run benchmarks"
|
||||||
@echo "update_hedley - download Hedley and regenerate hedley.hpp / hedley_undef.hpp"
|
@echo "update_hedley - download Hedley and regenerate hedley.hpp / hedley_undef.hpp"
|
||||||
@@ -59,74 +60,6 @@ run_benchmarks:
|
|||||||
cd cmake-build-benchmarks ; ./json_benchmarks
|
cd cmake-build-benchmarks ; ./json_benchmarks
|
||||||
|
|
||||||
|
|
||||||
##########################################################################
|
|
||||||
# fuzzing
|
|
||||||
##########################################################################
|
|
||||||
|
|
||||||
# the overall fuzz testing target
|
|
||||||
fuzz_testing:
|
|
||||||
rm -fr fuzz-testing
|
|
||||||
mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out
|
|
||||||
$(MAKE) parse_afl_fuzzer -C tests CXX=afl-clang++
|
|
||||||
mv tests/parse_afl_fuzzer fuzz-testing/fuzzer
|
|
||||||
find tests/data/json_tests -size -5k -name *json | xargs -I{} cp "{}" fuzz-testing/testcases
|
|
||||||
@echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer"
|
|
||||||
|
|
||||||
fuzz_testing_bon8:
|
|
||||||
rm -fr fuzz-testing
|
|
||||||
mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out
|
|
||||||
$(MAKE) parse_bon8_fuzzer -C tests CXX=afl-clang++
|
|
||||||
mv tests/parse_bon8_fuzzer fuzz-testing/fuzzer
|
|
||||||
find tests/data -size -5k -name *.bon8 | xargs -I{} cp "{}" fuzz-testing/testcases
|
|
||||||
@echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer"
|
|
||||||
|
|
||||||
fuzz_testing_bson:
|
|
||||||
rm -fr fuzz-testing
|
|
||||||
mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out
|
|
||||||
$(MAKE) parse_bson_fuzzer -C tests CXX=afl-clang++
|
|
||||||
mv tests/parse_bson_fuzzer fuzz-testing/fuzzer
|
|
||||||
find tests/data -size -5k -name *.bson | xargs -I{} cp "{}" fuzz-testing/testcases
|
|
||||||
@echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer"
|
|
||||||
|
|
||||||
fuzz_testing_cbor:
|
|
||||||
rm -fr fuzz-testing
|
|
||||||
mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out
|
|
||||||
$(MAKE) parse_cbor_fuzzer -C tests CXX=afl-clang++
|
|
||||||
mv tests/parse_cbor_fuzzer fuzz-testing/fuzzer
|
|
||||||
find tests/data -size -5k -name *.cbor | xargs -I{} cp "{}" fuzz-testing/testcases
|
|
||||||
@echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer"
|
|
||||||
|
|
||||||
fuzz_testing_msgpack:
|
|
||||||
rm -fr fuzz-testing
|
|
||||||
mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out
|
|
||||||
$(MAKE) parse_msgpack_fuzzer -C tests CXX=afl-clang++
|
|
||||||
mv tests/parse_msgpack_fuzzer fuzz-testing/fuzzer
|
|
||||||
find tests/data -size -5k -name *.msgpack | xargs -I{} cp "{}" fuzz-testing/testcases
|
|
||||||
@echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer"
|
|
||||||
|
|
||||||
fuzz_testing_ubjson:
|
|
||||||
rm -fr fuzz-testing
|
|
||||||
mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out
|
|
||||||
$(MAKE) parse_ubjson_fuzzer -C tests CXX=afl-clang++
|
|
||||||
mv tests/parse_ubjson_fuzzer fuzz-testing/fuzzer
|
|
||||||
find tests/data -size -5k -name *.ubjson | xargs -I{} cp "{}" fuzz-testing/testcases
|
|
||||||
@echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer"
|
|
||||||
|
|
||||||
fuzzing-start:
|
|
||||||
afl-fuzz -S fuzzer1 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null &
|
|
||||||
afl-fuzz -S fuzzer2 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null &
|
|
||||||
afl-fuzz -S fuzzer3 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null &
|
|
||||||
afl-fuzz -S fuzzer4 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null &
|
|
||||||
afl-fuzz -S fuzzer5 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null &
|
|
||||||
afl-fuzz -S fuzzer6 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null &
|
|
||||||
afl-fuzz -S fuzzer7 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null &
|
|
||||||
afl-fuzz -M fuzzer0 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer
|
|
||||||
|
|
||||||
fuzzing-stop:
|
|
||||||
-killall fuzzer
|
|
||||||
-killall afl-fuzz
|
|
||||||
|
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# Static analysis
|
# Static analysis
|
||||||
##########################################################################
|
##########################################################################
|
||||||
@@ -154,14 +87,10 @@ install_astyle:
|
|||||||
|
|
||||||
# call the Artistic Style pretty printer on all source files
|
# call the Artistic Style pretty printer on all source files
|
||||||
pretty: install_astyle
|
pretty: install_astyle
|
||||||
$(ASTYLE) --project=tools/astyle/.astylerc $(SRCS) $(TESTS_SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) docs/mkdocs/docs/examples/*.cpp
|
$(ASTYLE) --project=tools/astyle/.astylerc $(SRCS) $(TESTS_SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE) docs/mkdocs/docs/examples/*.cpp docs/mkdocs/docs/examples/*.hpp
|
||||||
|
|
||||||
# call the Clang-Format on all source files
|
|
||||||
pretty_format:
|
|
||||||
for FILE in $(SRCS) $(TESTS_SRCS) $(AMALGAMATED_FILE) docs/mkdocs/docs/examples/*.cpp; do echo $$FILE; clang-format -i $$FILE; done
|
|
||||||
|
|
||||||
# create single header files and pretty print
|
# create single header files and pretty print
|
||||||
amalgamate: $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE)
|
amalgamate: $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE)
|
||||||
$(MAKE) pretty
|
$(MAKE) pretty
|
||||||
|
|
||||||
# call the amalgamation tool for json.hpp
|
# call the amalgamation tool for json.hpp
|
||||||
@@ -172,20 +101,50 @@ $(AMALGAMATED_FILE): $(SRCS)
|
|||||||
$(AMALGAMATED_FWD_FILE): $(SRCS)
|
$(AMALGAMATED_FWD_FILE): $(SRCS)
|
||||||
tools/amalgamate/amalgamate.py -c tools/amalgamate/config_json_fwd.json -s . --verbose=yes
|
tools/amalgamate/amalgamate.py -c tools/amalgamate/config_json_fwd.json -s . --verbose=yes
|
||||||
|
|
||||||
|
# copy json_literals.hpp
|
||||||
|
$(AMALGAMATED_LITERALS_FILE): include/nlohmann/json_literals.hpp
|
||||||
|
cp include/nlohmann/json_literals.hpp $(AMALGAMATED_LITERALS_FILE)
|
||||||
|
|
||||||
|
# regenerate nlohmann_json.natvis from the ABI tags and version in include/nlohmann/detail/abi_macros.hpp
|
||||||
|
natvis:
|
||||||
|
python3 tools/generate_natvis/generate_natvis.py .
|
||||||
|
|
||||||
|
# regenerate the two tools/macro_builder blocks of $(MACRO_SCOPE_HPP) (see its README.md) and diff against the
|
||||||
|
# checked-in header; phony, because it never writes $(MACRO_SCOPE_HPP) itself
|
||||||
|
macro_builder_check:
|
||||||
|
@set -e; \
|
||||||
|
TMPDIR=$$(mktemp -d ./macro_builder_check.XXXXXX); \
|
||||||
|
trap 'rm -rf "$$TMPDIR"' EXIT; \
|
||||||
|
$(CXX) -std=c++11 tools/macro_builder/main.cpp -o "$$TMPDIR/macro_builder"; \
|
||||||
|
"$$TMPDIR/macro_builder" > "$$TMPDIR/paste.hpp"; \
|
||||||
|
"$$TMPDIR/macro_builder" type_body > "$$TMPDIR/type_body.hpp"; \
|
||||||
|
$(ASTYLE) --project=tools/astyle/.astylerc --suffix=none --quiet "$$TMPDIR/paste.hpp" "$$TMPDIR/type_body.hpp"; \
|
||||||
|
sed -n '/^#define NLOHMANN_JSON_EXPAND( x ) x$$/,/^#define NLOHMANN_JSON_DOUBLE_PASTE63(/p' $(MACRO_SCOPE_HPP) > "$$TMPDIR/paste_actual.hpp"; \
|
||||||
|
sed -n '/^#define NLOHMANN_JSON_TYPE_BODY(Prefix, \.\.\.)/,/^ NLOHMANN_JSON_TYPE_BODY_SENTINEL))$$/p' $(MACRO_SCOPE_HPP) > "$$TMPDIR/type_body_actual.hpp"; \
|
||||||
|
diff "$$TMPDIR/paste.hpp" "$$TMPDIR/paste_actual.hpp" || (echo "===================================================================\n $(MACRO_SCOPE_HPP) (NLOHMANN_JSON_EXPAND..NLOHMANN_JSON_DOUBLE_PASTE63) is out of date!\n Regenerate it, see tools/macro_builder/README.md.\n===================================================================" ; exit 1); \
|
||||||
|
diff "$$TMPDIR/type_body.hpp" "$$TMPDIR/type_body_actual.hpp" || (echo "===================================================================\n $(MACRO_SCOPE_HPP) (NLOHMANN_JSON_TYPE_BODY) is out of date!\n Regenerate it, see tools/macro_builder/README.md.\n===================================================================" ; exit 1)
|
||||||
|
|
||||||
# check if file single_include/nlohmann/json.hpp has been amalgamated from the nlohmann sources
|
# check if file single_include/nlohmann/json.hpp has been amalgamated from the nlohmann sources
|
||||||
# Note: this target is called by Travis
|
|
||||||
check-amalgamation:
|
check-amalgamation:
|
||||||
@mv $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~
|
@mv $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~
|
||||||
@mv $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_FWD_FILE)~
|
@mv $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_FWD_FILE)~
|
||||||
|
@mv $(AMALGAMATED_LITERALS_FILE) $(AMALGAMATED_LITERALS_FILE)~
|
||||||
@$(MAKE) amalgamate
|
@$(MAKE) amalgamate
|
||||||
@diff $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv $(AMALGAMATED_FILE)~ $(AMALGAMATED_FILE) ; false)
|
@diff $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv $(AMALGAMATED_FILE)~ $(AMALGAMATED_FILE) ; false)
|
||||||
@diff $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_FWD_FILE)~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv $(AMALGAMATED_FWD_FILE)~ $(AMALGAMATED_FWD_FILE) ; false)
|
@diff $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_FWD_FILE)~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv $(AMALGAMATED_FWD_FILE)~ $(AMALGAMATED_FWD_FILE) ; false)
|
||||||
|
@diff $(AMALGAMATED_LITERALS_FILE) $(AMALGAMATED_LITERALS_FILE)~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv $(AMALGAMATED_LITERALS_FILE)~ $(AMALGAMATED_LITERALS_FILE) ; false)
|
||||||
@mv $(AMALGAMATED_FILE)~ $(AMALGAMATED_FILE)
|
@mv $(AMALGAMATED_FILE)~ $(AMALGAMATED_FILE)
|
||||||
@mv $(AMALGAMATED_FWD_FILE)~ $(AMALGAMATED_FWD_FILE)
|
@mv $(AMALGAMATED_FWD_FILE)~ $(AMALGAMATED_FWD_FILE)
|
||||||
|
@mv $(AMALGAMATED_LITERALS_FILE)~ $(AMALGAMATED_LITERALS_FILE)
|
||||||
@mv BUILD.bazel BUILD.bazel~
|
@mv BUILD.bazel BUILD.bazel~
|
||||||
@$(MAKE) BUILD.bazel
|
@$(MAKE) BUILD.bazel
|
||||||
@diff BUILD.bazel BUILD.bazel~ || (echo "===================================================================\n BUILD.bazel is out of date! Please run 'make BUILD.bazel'.\n===================================================================" ; mv BUILD.bazel~ BUILD.bazel ; false)
|
@diff BUILD.bazel BUILD.bazel~ || (echo "===================================================================\n BUILD.bazel is out of date! Please run 'make BUILD.bazel'.\n===================================================================" ; mv BUILD.bazel~ BUILD.bazel ; false)
|
||||||
@mv BUILD.bazel~ BUILD.bazel
|
@mv BUILD.bazel~ BUILD.bazel
|
||||||
|
@mv nlohmann_json.natvis nlohmann_json.natvis~
|
||||||
|
@$(MAKE) natvis
|
||||||
|
@diff nlohmann_json.natvis nlohmann_json.natvis~ || (echo "===================================================================\n nlohmann_json.natvis is out of date! Please run 'make natvis'.\n===================================================================" ; mv nlohmann_json.natvis~ nlohmann_json.natvis ; false)
|
||||||
|
@mv nlohmann_json.natvis~ nlohmann_json.natvis
|
||||||
|
@$(MAKE) macro_builder_check
|
||||||
|
|
||||||
# generate the Bazel BUILD file; phony, because a removed header would not trigger a rebuild
|
# generate the Bazel BUILD file; phony, because a removed header would not trigger a rebuild
|
||||||
BUILD.bazel:
|
BUILD.bazel:
|
||||||
@@ -222,7 +181,7 @@ json.tar.xz:
|
|||||||
# We use `-X` to make the resulting ZIP file reproducible, see
|
# We use `-X` to make the resulting ZIP file reproducible, see
|
||||||
# <https://content.pivotal.io/blog/barriers-to-deterministic-reproducible-zip-files>.
|
# <https://content.pivotal.io/blog/barriers-to-deterministic-reproducible-zip-files>.
|
||||||
include.zip: BUILD.bazel
|
include.zip: BUILD.bazel
|
||||||
zip -9 --recurse-paths -X include.zip $(SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) BUILD.bazel MODULE.bazel meson.build LICENSE.MIT
|
zip -9 --recurse-paths -X include.zip $(SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE) BUILD.bazel MODULE.bazel meson.build LICENSE.MIT
|
||||||
|
|
||||||
# Create the files for a release and add signatures and hashes.
|
# Create the files for a release and add signatures and hashes.
|
||||||
release: include.zip json.tar.xz
|
release: include.zip json.tar.xz
|
||||||
@@ -231,11 +190,13 @@ release: include.zip json.tar.xz
|
|||||||
gpg --armor --detach-sig include.zip
|
gpg --armor --detach-sig include.zip
|
||||||
gpg --armor --detach-sig $(AMALGAMATED_FILE)
|
gpg --armor --detach-sig $(AMALGAMATED_FILE)
|
||||||
gpg --armor --detach-sig $(AMALGAMATED_FWD_FILE)
|
gpg --armor --detach-sig $(AMALGAMATED_FWD_FILE)
|
||||||
|
gpg --armor --detach-sig $(AMALGAMATED_LITERALS_FILE)
|
||||||
gpg --armor --detach-sig json.tar.xz
|
gpg --armor --detach-sig json.tar.xz
|
||||||
cp $(AMALGAMATED_FILE) release_files
|
cp $(AMALGAMATED_FILE) release_files
|
||||||
cp $(AMALGAMATED_FWD_FILE) release_files
|
cp $(AMALGAMATED_FWD_FILE) release_files
|
||||||
mv $(AMALGAMATED_FILE).asc $(AMALGAMATED_FWD_FILE).asc json.tar.xz json.tar.xz.asc include.zip include.zip.asc release_files
|
cp $(AMALGAMATED_LITERALS_FILE) release_files
|
||||||
cd release_files ; shasum -a 256 json.hpp include.zip json.tar.xz > hashes.txt
|
mv $(AMALGAMATED_FILE).asc $(AMALGAMATED_FWD_FILE).asc $(AMALGAMATED_LITERALS_FILE).asc json.tar.xz json.tar.xz.asc include.zip include.zip.asc release_files
|
||||||
|
cd release_files ; shasum -a 256 $$(find . -type f -not -name '*.asc' | sed 's|^\./||' | sort) > hashes.txt
|
||||||
|
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
@@ -245,7 +206,6 @@ release: include.zip json.tar.xz
|
|||||||
# clean up
|
# clean up
|
||||||
clean:
|
clean:
|
||||||
rm -fr fuzz fuzz-testing *.dSYM tests/*.dSYM
|
rm -fr fuzz fuzz-testing *.dSYM tests/*.dSYM
|
||||||
rm -fr benchmarks/files/numbers/*.json
|
|
||||||
rm -fr cmake-build-benchmarks fuzz-testing cmake-build-pvs-studio release_files
|
rm -fr cmake-build-benchmarks fuzz-testing cmake-build-pvs-studio release_files
|
||||||
$(MAKE) clean -Cdocs
|
$(MAKE) clean -Cdocs
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
[](https://coveralls.io/github/nlohmann/json?branch=develop)
|
[](https://coveralls.io/github/nlohmann/json?branch=develop)
|
||||||
[](https://scan.coverity.com/projects/nlohmann-json)
|
[](https://scan.coverity.com/projects/nlohmann-json)
|
||||||
[](https://app.codacy.com/gh/nlohmann/json/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
|
[](https://app.codacy.com/gh/nlohmann/json/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
|
||||||
[](https://cirrus-ci.com/github/nlohmann/json)
|
|
||||||
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:json)
|
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:json)
|
||||||
[](https://wandbox.org/permlink/1mp10JbaANo6FUc7)
|
[](https://wandbox.org/permlink/1mp10JbaANo6FUc7)
|
||||||
[](https://json.nlohmann.me)
|
[](https://json.nlohmann.me)
|
||||||
@@ -496,7 +495,7 @@ bool key(string_t& val);
|
|||||||
bool parse_error(std::size_t position, const std::string& last_token, const detail::exception& ex);
|
bool parse_error(std::size_t position, const std::string& last_token, const detail::exception& ex);
|
||||||
```
|
```
|
||||||
|
|
||||||
The return value of each function determines whether parsing should proceed.
|
The return value of each function determines whether parsing should proceed. For `parse_error`, returning `true` [recovers from the error](https://json.nlohmann.me/features/parsing/error_recovery/): the parser repairs the input and continues.
|
||||||
|
|
||||||
To implement your own SAX handler, proceed as follows:
|
To implement your own SAX handler, proceed as follows:
|
||||||
|
|
||||||
@@ -504,7 +503,7 @@ To implement your own SAX handler, proceed as follows:
|
|||||||
2. Create an object of your SAX interface class, e.g. `my_sax`.
|
2. Create an object of your SAX interface class, e.g. `my_sax`.
|
||||||
3. Call `bool json::sax_parse(input, &my_sax)`; where the first parameter can be any input like a string or an input stream and the second parameter is a pointer to your SAX interface.
|
3. Call `bool json::sax_parse(input, &my_sax)`; where the first parameter can be any input like a string or an input stream and the second parameter is a pointer to your SAX interface.
|
||||||
|
|
||||||
Note the `sax_parse` function only returns a `bool` indicating the result of the last executed SAX event. It does not return a `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error -- it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file [`json_sax.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/json_sax.hpp).
|
Note the `sax_parse` function only returns a `bool` indicating whether the input was parsed without errors and no SAX event returned `false`. It does not return a `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error -- it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file [`json_sax.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/json_sax.hpp).
|
||||||
|
|
||||||
### STL-like access
|
### STL-like access
|
||||||
|
|
||||||
@@ -1395,6 +1394,7 @@ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR I
|
|||||||
- The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2009 [Florian Loitsch](https://florian.loitsch.com/)
|
- The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2009 [Florian Loitsch](https://florian.loitsch.com/)
|
||||||
- The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/).
|
- The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||||
- The class contains parts of [Google Abseil](https://github.com/abseil/abseil-cpp) which is licensed under the [Apache 2.0 License](https://opensource.org/licenses/Apache-2.0).
|
- The class contains parts of [Google Abseil](https://github.com/abseil/abseil-cpp) which is licensed under the [Apache 2.0 License](https://opensource.org/licenses/Apache-2.0).
|
||||||
|
- The class contains an adapted version of the Eisel-Lemire algorithm and its table of powers of five from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
||||||
|
|
||||||
<img align="right" src="https://git.fsfe.org/reuse/reuse-ci/raw/branch/master/reuse-horizontal.png" alt="REUSE Software">
|
<img align="right" src="https://git.fsfe.org/reuse/reuse-ci/raw/branch/master/reuse-horizontal.png" alt="REUSE Software">
|
||||||
|
|
||||||
|
|||||||
+137
-95
@@ -40,6 +40,14 @@ execute_process(COMMAND ${IWYU_TOOL} --version OUTPUT_VARIABLE IWYU_TOOL_VERSION
|
|||||||
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" IWYU_TOOL_VERSION "${IWYU_TOOL_VERSION}")
|
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" IWYU_TOOL_VERSION "${IWYU_TOOL_VERSION}")
|
||||||
message(STATUS "🔖 include-what-you-use ${IWYU_TOOL_VERSION} (${IWYU_TOOL})")
|
message(STATUS "🔖 include-what-you-use ${IWYU_TOOL_VERSION} (${IWYU_TOOL})")
|
||||||
|
|
||||||
|
# CMake's CXX_INCLUDE_WHAT_YOU_USE launcher runs IWYU during the normal compile step (useful to see
|
||||||
|
# diagnostics inline), but CMake's own __run_co_compile wrapper does not propagate the launched
|
||||||
|
# tool's exit code to the build, so IWYU's own "-Xiwyu --error" cannot fail that step (verified: a
|
||||||
|
# deliberately-unused #include in a header still lets `cmake --build` finish with exit code 0).
|
||||||
|
# iwyu_tool.py, which ships with IWYU, reads compile_commands.json and does return a non-zero exit
|
||||||
|
# code for any analyzed file with findings; ci_single_binaries uses it to actually fail on findings.
|
||||||
|
find_program(IWYU_TOOL_PY NAMES iwyu_tool iwyu_tool.py iwyu-tool)
|
||||||
|
|
||||||
find_program(INFER_TOOL NAMES infer)
|
find_program(INFER_TOOL NAMES infer)
|
||||||
execute_process(COMMAND ${INFER_TOOL} --version OUTPUT_VARIABLE INFER_TOOL_VERSION ERROR_VARIABLE INFER_TOOL_VERSION)
|
execute_process(COMMAND ${INFER_TOOL} --version OUTPUT_VARIABLE INFER_TOOL_VERSION ERROR_VARIABLE INFER_TOOL_VERSION)
|
||||||
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" INFER_TOOL_VERSION "${INFER_TOOL_VERSION}")
|
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" INFER_TOOL_VERSION "${INFER_TOOL_VERSION}")
|
||||||
@@ -55,12 +63,6 @@ execute_process(COMMAND ${NINJA_TOOL} --version OUTPUT_VARIABLE NINJA_TOOL_VERSI
|
|||||||
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" NINJA_TOOL_VERSION "${NINJA_TOOL_VERSION}")
|
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" NINJA_TOOL_VERSION "${NINJA_TOOL_VERSION}")
|
||||||
message(STATUS "🔖 Ninja ${NINJA_TOOL_VERSION} (${NINJA_TOOL})")
|
message(STATUS "🔖 Ninja ${NINJA_TOOL_VERSION} (${NINJA_TOOL})")
|
||||||
|
|
||||||
find_program(OCLINT_TOOL NAMES oclint-json-compilation-database)
|
|
||||||
find_program(OCLINT_VERSION_TOOL NAMES oclint)
|
|
||||||
execute_process(COMMAND ${OCLINT_VERSION_TOOL} --version OUTPUT_VARIABLE OCLINT_TOOL_VERSION ERROR_VARIABLE OCLINT_TOOL_VERSION)
|
|
||||||
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" OCLINT_TOOL_VERSION "${OCLINT_TOOL_VERSION}")
|
|
||||||
message(STATUS "🔖 OCLint ${OCLINT_TOOL_VERSION} (${OCLINT_TOOL})")
|
|
||||||
|
|
||||||
find_program(VALGRIND_TOOL NAMES valgrind)
|
find_program(VALGRIND_TOOL NAMES valgrind)
|
||||||
execute_process(COMMAND ${VALGRIND_TOOL} --version OUTPUT_VARIABLE VALGRIND_TOOL_VERSION ERROR_VARIABLE VALGRIND_TOOL_VERSION)
|
execute_process(COMMAND ${VALGRIND_TOOL} --version OUTPUT_VARIABLE VALGRIND_TOOL_VERSION ERROR_VARIABLE VALGRIND_TOOL_VERSION)
|
||||||
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" VALGRIND_TOOL_VERSION "${VALGRIND_TOOL_VERSION}")
|
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" VALGRIND_TOOL_VERSION "${VALGRIND_TOOL_VERSION}")
|
||||||
@@ -128,16 +130,16 @@ foreach(CXX_STANDARD 11 14 17 20 23 26)
|
|||||||
COMMENT "Compile and test with Clang for C++${CXX_STANDARD}"
|
COMMENT "Compile and test with Clang for C++${CXX_STANDARD}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# pass -stdlib=libc++ through CXXFLAGS: an explicit -DCMAKE_CXX_FLAGS would make CMake ignore CXXFLAGS
|
||||||
add_custom_target(ci_test_clang_libcxx_cxx${CXX_STANDARD}
|
add_custom_target(ci_test_clang_libcxx_cxx${CXX_STANDARD}
|
||||||
COMMAND CXX=${CLANG_TOOL} CXXFLAGS="${CLANG_CXXFLAGS}" ${CMAKE_COMMAND}
|
COMMAND CXX=${CLANG_TOOL} CXXFLAGS="${CLANG_CXXFLAGS};-stdlib=libc++" ${CMAKE_COMMAND}
|
||||||
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
||||||
-DJSON_BuildTests=ON -DJSON_FastTests=ON
|
-DJSON_BuildTests=ON -DJSON_FastTests=ON
|
||||||
-DJSON_TestStandards=${CXX_STANDARD}
|
-DJSON_TestStandards=${CXX_STANDARD}
|
||||||
-DCMAKE_CXX_FLAGS="-stdlib=libc++"
|
|
||||||
-DCMAKE_EXE_LINKER_FLAGS="-lc++abi"
|
-DCMAKE_EXE_LINKER_FLAGS="-lc++abi"
|
||||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_clang_cxx${CXX_STANDARD}
|
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_clang_libcxx_cxx${CXX_STANDARD}
|
||||||
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_clang_cxx${CXX_STANDARD}
|
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_clang_libcxx_cxx${CXX_STANDARD}
|
||||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_clang_cxx${CXX_STANDARD} && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
|
COMMAND cd ${PROJECT_BINARY_DIR}/build_clang_libcxx_cxx${CXX_STANDARD} && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
|
||||||
COMMENT "Compile and test with Clang for C++${CXX_STANDARD} (libc++)"
|
COMMENT "Compile and test with Clang for C++${CXX_STANDARD} (libc++)"
|
||||||
)
|
)
|
||||||
endforeach()
|
endforeach()
|
||||||
@@ -276,6 +278,20 @@ add_custom_target(ci_test_disableenumserialization
|
|||||||
COMMENT "Compile and test with enum serialization disabled"
|
COMMENT "Compile and test with enum serialization disabled"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Disable conversion from a one-element tuple of a JSON reference.
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
add_custom_target(ci_test_disabletuplereferenceconversion
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
||||||
|
-DJSON_BuildTests=ON -DJSON_FastTests=ON -DJSON_DisableTupleReferenceConversion=ON
|
||||||
|
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_disabletuplereferenceconversion
|
||||||
|
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_disabletuplereferenceconversion
|
||||||
|
COMMAND cd ${PROJECT_BINARY_DIR}/build_disabletuplereferenceconversion && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
|
||||||
|
COMMENT "Compile and test with tuple reference conversion disabled"
|
||||||
|
)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Skip the multiple-inclusion library version check.
|
# Skip the multiple-inclusion library version check.
|
||||||
###############################################################################
|
###############################################################################
|
||||||
@@ -362,20 +378,29 @@ add_custom_target(ci_test_clang_sanitizer
|
|||||||
# Check if header is amalgamated and sources are properly indented.
|
# Check if header is amalgamated and sources are properly indented.
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
# Same file set as .github/workflows/check_amalgamation.yml, so a direct push to develop/master/release/*
|
||||||
|
# (which only this CMake target checks, not the pull_request-only workflow) is held to the same standard.
|
||||||
file(GLOB_RECURSE INDENT_FILES
|
file(GLOB_RECURSE INDENT_FILES
|
||||||
${PROJECT_SOURCE_DIR}/include/nlohmann/*.hpp
|
${PROJECT_SOURCE_DIR}/docs/mkdocs/docs/examples/*.hpp
|
||||||
${PROJECT_SOURCE_DIR}/tests/src/*.cpp
|
|
||||||
${PROJECT_SOURCE_DIR}/tests/src/*.hpp
|
|
||||||
${PROJECT_SOURCE_DIR}/tests/benchmarks/src/benchmarks.cpp
|
|
||||||
${PROJECT_SOURCE_DIR}/docs/mkdocs/docs/examples/*.cpp
|
${PROJECT_SOURCE_DIR}/docs/mkdocs/docs/examples/*.cpp
|
||||||
|
${PROJECT_SOURCE_DIR}/docs/mkdocs/docs/examples/*.cu
|
||||||
|
${PROJECT_SOURCE_DIR}/include/*.hpp
|
||||||
|
${PROJECT_SOURCE_DIR}/include/*.cpp
|
||||||
|
${PROJECT_SOURCE_DIR}/include/*.cu
|
||||||
|
${PROJECT_SOURCE_DIR}/tests/*.hpp
|
||||||
|
${PROJECT_SOURCE_DIR}/tests/*.cpp
|
||||||
|
${PROJECT_SOURCE_DIR}/tests/*.cu
|
||||||
)
|
)
|
||||||
|
list(FILTER INDENT_FILES EXCLUDE REGEX "/tests/thirdparty/|/tests/abi/include/nlohmann/")
|
||||||
|
|
||||||
set(include_dir ${PROJECT_SOURCE_DIR}/single_include/nlohmann)
|
set(include_dir ${PROJECT_SOURCE_DIR}/single_include/nlohmann)
|
||||||
set(tool_dir ${PROJECT_SOURCE_DIR}/tools/amalgamate)
|
set(tool_dir ${PROJECT_SOURCE_DIR}/tools/amalgamate)
|
||||||
add_custom_target(ci_test_amalgamation
|
add_custom_target(ci_test_amalgamation
|
||||||
COMMAND rm -fr ${include_dir}/json.hpp~ ${include_dir}/json_fwd.hpp~
|
COMMAND rm -fr ${include_dir}/json.hpp~ ${include_dir}/json_fwd.hpp~ ${include_dir}/json_literals.hpp~
|
||||||
COMMAND cp ${include_dir}/json.hpp ${include_dir}/json.hpp~
|
COMMAND cp ${include_dir}/json.hpp ${include_dir}/json.hpp~
|
||||||
COMMAND cp ${include_dir}/json_fwd.hpp ${include_dir}/json_fwd.hpp~
|
COMMAND cp ${include_dir}/json_fwd.hpp ${include_dir}/json_fwd.hpp~
|
||||||
|
COMMAND cp ${include_dir}/json_literals.hpp ${include_dir}/json_literals.hpp~
|
||||||
|
COMMAND cp ${PROJECT_SOURCE_DIR}/BUILD.bazel ${PROJECT_SOURCE_DIR}/BUILD.bazel~
|
||||||
|
|
||||||
COMMAND ${Python3_EXECUTABLE} -mvenv venv_astyle
|
COMMAND ${Python3_EXECUTABLE} -mvenv venv_astyle
|
||||||
COMMAND venv_astyle/bin/pip3 --quiet install -r ${CMAKE_SOURCE_DIR}/tools/astyle/requirements.txt
|
COMMAND venv_astyle/bin/pip3 --quiet install -r ${CMAKE_SOURCE_DIR}/tools/astyle/requirements.txt
|
||||||
@@ -383,16 +408,20 @@ add_custom_target(ci_test_amalgamation
|
|||||||
|
|
||||||
COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json.json -s .
|
COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json.json -s .
|
||||||
COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json_fwd.json -s .
|
COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json_fwd.json -s .
|
||||||
|
COMMAND cp ${PROJECT_SOURCE_DIR}/include/nlohmann/json_literals.hpp ${include_dir}/json_literals.hpp
|
||||||
COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=none ${include_dir}/json.hpp ${include_dir}/json_fwd.hpp
|
COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=none ${include_dir}/json.hpp ${include_dir}/json_fwd.hpp
|
||||||
|
COMMAND ${CMAKE_COMMAND} -P ${PROJECT_SOURCE_DIR}/cmake/scripts/gen_bazel_build_file.cmake
|
||||||
|
|
||||||
COMMAND diff ${include_dir}/json.hpp~ ${include_dir}/json.hpp
|
COMMAND diff ${include_dir}/json.hpp~ ${include_dir}/json.hpp
|
||||||
COMMAND diff ${include_dir}/json_fwd.hpp~ ${include_dir}/json_fwd.hpp
|
COMMAND diff ${include_dir}/json_fwd.hpp~ ${include_dir}/json_fwd.hpp
|
||||||
|
COMMAND diff ${include_dir}/json_literals.hpp~ ${include_dir}/json_literals.hpp
|
||||||
|
COMMAND diff ${PROJECT_SOURCE_DIR}/BUILD.bazel~ ${PROJECT_SOURCE_DIR}/BUILD.bazel
|
||||||
|
|
||||||
COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=orig ${INDENT_FILES}
|
COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=orig ${INDENT_FILES}
|
||||||
COMMAND for FILE in `find . -name '*.orig'`\; do false \; done
|
COMMAND for FILE in `find . -name '*.orig'`\; do false \; done
|
||||||
|
|
||||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
COMMENT "Check amalgamation and indentation"
|
COMMENT "Check amalgamation, formatting, and BUILD.bazel"
|
||||||
)
|
)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
@@ -427,14 +456,14 @@ add_custom_target(ci_test_valgrind
|
|||||||
# Check code with Clang Static Analyzer.
|
# Check code with Clang Static Analyzer.
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
set(CLANG_ANALYZER_CHECKS "fuchsia.HandleChecker,nullability.NullableDereferenced,nullability.NullablePassedToNonnull,nullability.NullableReturnedFromNonnull,optin.cplusplus.UninitializedObject,optin.cplusplus.VirtualCall,optin.mpi.MPI-Checker,optin.osx.OSObjectCStyleCast,optin.osx.cocoa.localizability.EmptyLocalizationContextChecker,optin.osx.cocoa.localizability.NonLocalizedStringChecker,optin.performance.GCDAntipattern,optin.performance.Padding,optin.portability.UnixAPI,security.FloatLoopCounter,security.insecureAPI.DeprecatedOrUnsafeBufferHandling,security.insecureAPI.bcmp,security.insecureAPI.bcopy,security.insecureAPI.bzero,security.insecureAPI.rand,security.insecureAPI.strcpy,valist.CopyToSelf,valist.Uninitialized,valist.Unterminated,webkit.NoUncountedMemberChecker,webkit.RefCntblBaseVirtualDtor,core.CallAndMessage,core.DivideZero,core.NonNullParamChecker,core.NullDereference,core.StackAddressEscape,core.UndefinedBinaryOperatorResult,core.VLASize,core.uninitialized.ArraySubscript,core.uninitialized.Assign,core.uninitialized.Branch,core.uninitialized.CapturedBlockVariable,core.uninitialized.UndefReturn,cplusplus.InnerPointer,cplusplus.Move,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,cplusplus.PlacementNew,cplusplus.PureVirtualCall,deadcode.DeadStores,nullability.NullPassedToNonnull,nullability.NullReturnedFromNonnull,osx.API,osx.MIG,osx.NumberObjectConversion,osx.OSObjectRetainCount,osx.ObjCProperty,osx.SecKeychainAPI,osx.cocoa.AtSync,osx.cocoa.AutoreleaseWrite,osx.cocoa.ClassRelease,osx.cocoa.Dealloc,osx.cocoa.IncompatibleMethodTypes,osx.cocoa.Loops,osx.cocoa.MissingSuperCall,osx.cocoa.NSAutoreleasePool,osx.cocoa.NSError,osx.cocoa.NilArg,osx.cocoa.NonNilReturnValue,osx.cocoa.ObjCGenerics,osx.cocoa.RetainCount,osx.cocoa.RunLoopAutoreleaseLeak,osx.cocoa.SelfInit,osx.cocoa.SuperDealloc,osx.cocoa.UnusedIvars,osx.cocoa.VariadicMethodTypes,osx.coreFoundation.CFError,osx.coreFoundation.CFNumber,osx.coreFoundation.CFRetainRelease,osx.coreFoundation.containers.OutOfBounds,osx.coreFoundation.containers.PointerSizedValues,security.insecureAPI.UncheckedReturn,security.insecureAPI.decodeValueOfObjCType,security.insecureAPI.getpw,security.insecureAPI.gets,security.insecureAPI.mkstemp,security.insecureAPI.mktemp,security.insecureAPI.vfork,unix.API,unix.Malloc,unix.MallocSizeof,unix.MismatchedDeallocator,unix.Vfork,unix.cstring.BadSizeArg,unix.cstring.NullArg")
|
set(CLANG_ANALYZER_CHECKS "nullability.NullableDereferenced,nullability.NullablePassedToNonnull,nullability.NullableReturnedFromNonnull,optin.cplusplus.UninitializedObject,optin.cplusplus.VirtualCall,optin.performance.Padding,optin.portability.UnixAPI,security.FloatLoopCounter,security.insecureAPI.DeprecatedOrUnsafeBufferHandling,security.insecureAPI.bcmp,security.insecureAPI.bcopy,security.insecureAPI.bzero,security.insecureAPI.rand,security.insecureAPI.strcpy,valist.CopyToSelf,valist.Uninitialized,valist.Unterminated,core.CallAndMessage,core.DivideZero,core.NonNullParamChecker,core.NullDereference,core.StackAddressEscape,core.UndefinedBinaryOperatorResult,core.VLASize,core.uninitialized.ArraySubscript,core.uninitialized.Assign,core.uninitialized.Branch,core.uninitialized.CapturedBlockVariable,core.uninitialized.UndefReturn,cplusplus.InnerPointer,cplusplus.Move,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,cplusplus.PlacementNew,cplusplus.PureVirtualCall,deadcode.DeadStores,nullability.NullPassedToNonnull,nullability.NullReturnedFromNonnull,security.insecureAPI.UncheckedReturn,security.insecureAPI.decodeValueOfObjCType,security.insecureAPI.getpw,security.insecureAPI.gets,security.insecureAPI.mkstemp,security.insecureAPI.mktemp,security.insecureAPI.vfork,unix.API,unix.Malloc,unix.MallocSizeof,unix.MismatchedDeallocator,unix.Vfork,unix.cstring.BadSizeArg,unix.cstring.NullArg")
|
||||||
|
|
||||||
add_custom_target(ci_clang_analyze
|
add_custom_target(ci_clang_analyze
|
||||||
COMMAND CXX=${CLANG_TOOL} ${CMAKE_COMMAND}
|
COMMAND CXX=${CLANG_TOOL} ${CMAKE_COMMAND}
|
||||||
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
||||||
-DJSON_BuildTests=ON
|
-DJSON_BuildTests=ON
|
||||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_clang_analyze
|
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_clang_analyze
|
||||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_clang_analyze && ${SCAN_BUILD_TOOL} -enable-checker ${CLANG_ANALYZER_CHECKS} --use-c++=${CLANG_TOOL} -analyze-headers -o ${PROJECT_BINARY_DIR}/report ninja
|
COMMAND cd ${PROJECT_BINARY_DIR}/build_clang_analyze && ${SCAN_BUILD_TOOL} -enable-checker ${CLANG_ANALYZER_CHECKS} --use-c++=${CLANG_TOOL} --use-analyzer=${CLANG_TOOL} -analyze-headers --status-bugs -o ${PROJECT_BINARY_DIR}/report ninja
|
||||||
COMMENT "Check code with Clang Analyzer"
|
COMMENT "Check code with Clang Analyzer"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -450,7 +479,7 @@ add_custom_target(ci_cppcheck
|
|||||||
COMMAND venv_cppcheck/bin/cppcheck --enable=warning --check-level=exhaustive --inline-suppr --inconclusive --force
|
COMMAND venv_cppcheck/bin/cppcheck --enable=warning --check-level=exhaustive --inline-suppr --inconclusive --force
|
||||||
--std=c++11 ${PROJECT_SOURCE_DIR}/include/nlohmann/json.hpp -I ${CMAKE_SOURCE_DIR}/include
|
--std=c++11 ${PROJECT_SOURCE_DIR}/include/nlohmann/json.hpp -I ${CMAKE_SOURCE_DIR}/include
|
||||||
--error-exitcode=1 --relative-paths=${PROJECT_SOURCE_DIR} -j ${N} --include=default_defines.hpp
|
--error-exitcode=1 --relative-paths=${PROJECT_SOURCE_DIR} -j ${N} --include=default_defines.hpp
|
||||||
--cppcheck-build-dir=cppcheck --check-level=exhaustive
|
--cppcheck-build-dir=cppcheck
|
||||||
-UJSON_CATCH_USER -UJSON_TRY_USER -UJSON_ASSERT -UJSON_INTERNAL_CATCH -UJSON_THROW
|
-UJSON_CATCH_USER -UJSON_TRY_USER -UJSON_ASSERT -UJSON_INTERNAL_CATCH -UJSON_THROW
|
||||||
-DJSON_HAS_CPP_11 -UJSON_HAS_CPP_14 -UJSON_HAS_CPP_17 -UJSON_HAS_CPP_20 -UJSON_HAS_THREE_WAY_COMPARISON
|
-DJSON_HAS_CPP_11 -UJSON_HAS_CPP_14 -UJSON_HAS_CPP_17 -UJSON_HAS_CPP_20 -UJSON_HAS_THREE_WAY_COMPARISON
|
||||||
COMMENT "Check code with Cppcheck"
|
COMMENT "Check code with Cppcheck"
|
||||||
@@ -468,34 +497,6 @@ add_custom_target(ci_cpplint
|
|||||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Check code with OCLint.
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
file(COPY ${PROJECT_SOURCE_DIR}/single_include/nlohmann/json.hpp DESTINATION ${PROJECT_BINARY_DIR}/src_single)
|
|
||||||
file(RENAME ${PROJECT_BINARY_DIR}/src_single/json.hpp ${PROJECT_BINARY_DIR}/src_single/all.cpp)
|
|
||||||
file(APPEND "${PROJECT_BINARY_DIR}/src_single/all.cpp" "\n\nint main()\n{}\n")
|
|
||||||
|
|
||||||
add_executable(single_all ${PROJECT_BINARY_DIR}/src_single/all.cpp)
|
|
||||||
target_compile_features(single_all PRIVATE cxx_std_11)
|
|
||||||
|
|
||||||
add_custom_target(ci_oclint
|
|
||||||
COMMAND ${CMAKE_COMMAND}
|
|
||||||
-DCMAKE_BUILD_TYPE=Debug
|
|
||||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
|
||||||
-DJSON_BuildTests=OFF -DJSON_CI=ON
|
|
||||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_oclint
|
|
||||||
COMMAND ${OCLINT_TOOL} -i ${PROJECT_BINARY_DIR}/build_oclint/src_single/all.cpp -p ${PROJECT_BINARY_DIR}/build_oclint --
|
|
||||||
-report-type html -enable-global-analysis --max-priority-1=0 --max-priority-2=1000 --max-priority-3=2000
|
|
||||||
--disable-rule=MultipleUnaryOperator
|
|
||||||
--disable-rule=DoubleNegative
|
|
||||||
--disable-rule=ShortVariableName
|
|
||||||
--disable-rule=GotoStatement
|
|
||||||
--disable-rule=LongLine
|
|
||||||
-o ${PROJECT_BINARY_DIR}/build_oclint/oclint_report.html
|
|
||||||
COMMENT "Check code with OCLint"
|
|
||||||
)
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Check code with Clang-Tidy.
|
# Check code with Clang-Tidy.
|
||||||
###############################################################################
|
###############################################################################
|
||||||
@@ -510,29 +511,16 @@ add_custom_target(ci_clang_tidy
|
|||||||
COMMENT "Check code with Clang-Tidy"
|
COMMENT "Check code with Clang-Tidy"
|
||||||
)
|
)
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Check code with PVS-Studio Analyzer <https://www.viva64.com/en/pvs-studio/>.
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
add_custom_target(ci_pvs_studio
|
|
||||||
COMMAND CXX=${CLANG_TOOL} ${CMAKE_COMMAND}
|
|
||||||
-DCMAKE_BUILD_TYPE=Debug
|
|
||||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
|
||||||
-DJSON_BuildTests=ON
|
|
||||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_pvs_studio
|
|
||||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_pvs_studio && ${PVS_STUDIO_ANALYZER_TOOL} analyze -j 10
|
|
||||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_pvs_studio && ${PLOG_CONVERTER_TOOL} -a'GA:1,2;64:1;CS' -t fullhtml PVS-Studio.log -o pvs
|
|
||||||
COMMENT "Check code with PVS Studio"
|
|
||||||
)
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Check code with Infer <https://fbinfer.com> static analyzer.
|
# Check code with Infer <https://fbinfer.com> static analyzer.
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
# .inferconfig (repository root) pins --fail-on-issue and the currently-triaged issue types that
|
||||||
|
# are disabled until they are addressed separately; see #5715 item 4b.
|
||||||
add_custom_target(ci_infer
|
add_custom_target(ci_infer
|
||||||
COMMAND mkdir -p ${PROJECT_BINARY_DIR}/build_infer
|
COMMAND mkdir -p ${PROJECT_BINARY_DIR}/build_infer
|
||||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_infer && ${INFER_TOOL} compile -- ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug ${PROJECT_SOURCE_DIR} -DJSON_BuildTests=ON
|
COMMAND cd ${PROJECT_BINARY_DIR}/build_infer && ${INFER_TOOL} compile -- ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug ${PROJECT_SOURCE_DIR} -DJSON_BuildTests=ON
|
||||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_infer && ${INFER_TOOL} run -- make
|
COMMAND cd ${PROJECT_BINARY_DIR}/build_infer && ${INFER_TOOL} run --project-root ${PROJECT_SOURCE_DIR} -- make
|
||||||
COMMENT "Check code with Infer"
|
COMMENT "Check code with Infer"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -588,7 +576,13 @@ add_custom_target(ci_reproducible_tests
|
|||||||
# be compiled individually.
|
# be compiled individually.
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
set(iwyu_path_and_options ${IWYU_TOOL} -Xiwyu --max_line_length=300)
|
set(iwyu_options -Xiwyu --error -Xiwyu --max_line_length=300)
|
||||||
|
set(iwyu_path_and_options ${IWYU_TOOL} ${iwyu_options})
|
||||||
|
|
||||||
|
# CMake needs to know the exact flags used to compile each src_single/*.cpp below to hand them to
|
||||||
|
# iwyu_tool.py; JSON_CI already implies a from-scratch configure, so enabling this project-wide has
|
||||||
|
# no downside here.
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
foreach(SRC_FILE ${SRC_FILES})
|
foreach(SRC_FILE ${SRC_FILES})
|
||||||
# get relative path of the header file
|
# get relative path of the header file
|
||||||
@@ -603,14 +597,34 @@ foreach(SRC_FILE ${SRC_FILES})
|
|||||||
target_include_directories(single_${RELATIVE_SRC_FILE} PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
target_include_directories(single_${RELATIVE_SRC_FILE} PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||||
target_compile_features(single_${RELATIVE_SRC_FILE} PRIVATE cxx_std_11)
|
target_compile_features(single_${RELATIVE_SRC_FILE} PRIVATE cxx_std_11)
|
||||||
set_property(TARGET single_${RELATIVE_SRC_FILE} PROPERTY CXX_INCLUDE_WHAT_YOU_USE "${iwyu_path_and_options}")
|
set_property(TARGET single_${RELATIVE_SRC_FILE} PROPERTY CXX_INCLUDE_WHAT_YOU_USE "${iwyu_path_and_options}")
|
||||||
# remember binary for ci_single_binaries target
|
# remember binary for ci_single_binaries
|
||||||
list(APPEND single_binaries single_${RELATIVE_SRC_FILE})
|
list(APPEND single_binaries single_${RELATIVE_SRC_FILE})
|
||||||
|
# json.hpp pulls together the whole library behind heavily templated, SFINAE-based code, and
|
||||||
|
# IWYU's suggestion for its one truly ambiguous symbol (a container-comparison "swap,
|
||||||
|
# operator!=") is not deterministic between runs (observed <set>, <unordered_map>, and <map>
|
||||||
|
# for the exact same source across otherwise-identical local and containerized builds). Keep
|
||||||
|
# reporting its diagnostics (informational, via CXX_INCLUDE_WHAT_YOU_USE above) but exclude it
|
||||||
|
# from the hard gate below so a fresh IWYU/compiler combination does not fail this target on a
|
||||||
|
# nondeterministic suggestion for a header that already re-exports everything on purpose.
|
||||||
|
if(NOT RELATIVE_SRC_FILE STREQUAL "json")
|
||||||
|
list(APPEND single_binaries_tus src_single/${RELATIVE_SRC_FILE}.cpp)
|
||||||
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
add_custom_target(ci_single_binaries
|
if(IWYU_TOOL_PY)
|
||||||
DEPENDS ${single_binaries}
|
add_custom_target(ci_single_binaries
|
||||||
COMMENT "Check if headers are self-contained"
|
DEPENDS ${single_binaries}
|
||||||
)
|
COMMAND ${IWYU_TOOL_PY} -p ${PROJECT_BINARY_DIR} ${single_binaries_tus} -- ${iwyu_options}
|
||||||
|
COMMENT "Check if headers are self-contained"
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
# iwyu_tool.py (ships with IWYU, e.g. as /usr/bin/iwyu_tool on Debian/Ubuntu) was not found;
|
||||||
|
# fall back to building the self-containment check without enforcing the IWYU findings.
|
||||||
|
add_custom_target(ci_single_binaries
|
||||||
|
DEPENDS ${single_binaries}
|
||||||
|
COMMENT "Check if headers are self-contained"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Benchmarks
|
# Benchmarks
|
||||||
@@ -632,21 +646,41 @@ add_custom_target(ci_benchmarks
|
|||||||
# we test the project with different CMake versions:
|
# we test the project with different CMake versions:
|
||||||
# - CMake 3.5 (the earliest supported)
|
# - CMake 3.5 (the earliest supported)
|
||||||
# - CMake 3.31.6 (the latest 3.x release)
|
# - CMake 3.31.6 (the latest 3.x release)
|
||||||
# - CMake 4.0.0 (the latest release)
|
# - CMake 4.0.0 (the first 4.x release)
|
||||||
|
# - the CMake version running this build (usually the latest release)
|
||||||
|
|
||||||
function(ci_get_cmake version var)
|
function(ci_get_cmake version var)
|
||||||
set(${var} ${PROJECT_BINARY_DIR}/cmake-${version}/bin/cmake)
|
set(${var} ${PROJECT_BINARY_DIR}/cmake-${version}/bin/cmake)
|
||||||
add_custom_command(
|
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(x86_64|amd64)$")
|
||||||
OUTPUT ${${var}}
|
# Kitware publishes a prebuilt Linux x86_64 archive for every release; unpacking it is far
|
||||||
COMMAND wget -nc https://github.com/Kitware/CMake/releases/download/v${version}/cmake-${version}.tar.gz
|
# cheaper than compiling all of CMake (including its own test helpers) from source.
|
||||||
COMMAND tar xfz cmake-${version}.tar.gz
|
add_custom_command(
|
||||||
COMMAND rm cmake-${version}.tar.gz
|
OUTPUT ${${var}}
|
||||||
# -DCMAKE_POLICY_VERSION_MINIMUM=3.5 required to compile older CMake versions with CMake 4.0.0
|
COMMAND wget -nc https://github.com/Kitware/CMake/releases/download/v${version}/cmake-${version}-linux-x86_64.tar.gz
|
||||||
COMMAND cmake -S cmake-${version} -B cmake-${version} -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
COMMAND wget -nc https://github.com/Kitware/CMake/releases/download/v${version}/cmake-${version}-SHA-256.txt
|
||||||
COMMAND cmake --build cmake-${version} --parallel 10
|
# verify the archive against Kitware's published SHA-256 sums before unpacking it
|
||||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
COMMAND sh -c "grep ' cmake-${version}-linux-x86_64[.]tar[.]gz$' cmake-${version}-SHA-256.txt | sha256sum -c -"
|
||||||
COMMENT "Download CMake ${version}"
|
COMMAND tar xfz cmake-${version}-linux-x86_64.tar.gz
|
||||||
)
|
COMMAND rm cmake-${version}-linux-x86_64.tar.gz cmake-${version}-SHA-256.txt
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E rm -rf cmake-${version}
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E rename cmake-${version}-linux-x86_64 cmake-${version}
|
||||||
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||||
|
COMMENT "Download prebuilt CMake ${version}"
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
# no prebuilt archive for this platform (e.g. macOS or Linux aarch64): build from source
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${${var}}
|
||||||
|
COMMAND wget -nc https://github.com/Kitware/CMake/releases/download/v${version}/cmake-${version}.tar.gz
|
||||||
|
COMMAND tar xfz cmake-${version}.tar.gz
|
||||||
|
COMMAND rm cmake-${version}.tar.gz
|
||||||
|
# -DCMAKE_POLICY_VERSION_MINIMUM=3.5 required to compile older CMake versions with CMake 4.0.0
|
||||||
|
COMMAND cmake -S cmake-${version} -B cmake-${version} -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||||
|
COMMAND cmake --build cmake-${version} --parallel 10
|
||||||
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||||
|
COMMENT "Download and build CMake ${version} from source"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
set(${var} ${${var}} PARENT_SCOPE)
|
set(${var} ${${var}} PARENT_SCOPE)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
@@ -656,30 +690,24 @@ ci_get_cmake(4.0.0 CMAKE_4_0_0_BINARY)
|
|||||||
|
|
||||||
# the tests require CMake 3.13 or later, so they are excluded for CMake 3.5.0
|
# the tests require CMake 3.13 or later, so they are excluded for CMake 3.5.0
|
||||||
set(JSON_CMAKE_FLAGS_3_5_0 JSON_Diagnostics JSON_Diagnostic_Positions JSON_GlobalUDLs JSON_ImplicitConversions JSON_DisableEnumSerialization
|
set(JSON_CMAKE_FLAGS_3_5_0 JSON_Diagnostics JSON_Diagnostic_Positions JSON_GlobalUDLs JSON_ImplicitConversions JSON_DisableEnumSerialization
|
||||||
JSON_LegacyDiscardedValueComparison JSON_Install JSON_MultipleHeaders JSON_SystemInclude JSON_Valgrind)
|
JSON_LegacyDiscardedValueComparison JSON_Install JSON_MultipleHeaders JSON_SystemInclude JSON_Valgrind
|
||||||
set(JSON_CMAKE_FLAGS_3_31_6 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_31_6})
|
JSON_StrictNulHandling)
|
||||||
|
set(JSON_CMAKE_FLAGS_3_31_6 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
|
||||||
set(JSON_CMAKE_FLAGS_4_0_0 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
|
set(JSON_CMAKE_FLAGS_4_0_0 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
|
||||||
|
|
||||||
function(ci_add_cmake_flags_targets flag min_version)
|
function(ci_add_cmake_flags_targets flag min_version)
|
||||||
string(TOLOWER "ci_cmake_flag_${flag}" flag_target)
|
string(TOLOWER "ci_cmake_flag_${flag}" flag_target)
|
||||||
string(REPLACE . _ min_version_var ${min_version})
|
string(REPLACE . _ min_version_var ${min_version})
|
||||||
set(cmake_binary ${CMAKE_${min_version_var}_BINARY})
|
set(cmake_binary ${CMAKE_${min_version_var}_BINARY})
|
||||||
add_custom_target(${flag_target}_${min_version}_2
|
|
||||||
COMMENT "Check CMake flag ${flag} (CMake ${CMAKE_VERSION})"
|
|
||||||
COMMAND ${CMAKE_COMMAND}
|
|
||||||
-Werror=dev
|
|
||||||
-D${flag}=ON
|
|
||||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_${flag_target}
|
|
||||||
)
|
|
||||||
add_custom_target(${flag_target}_${min_version_var}
|
add_custom_target(${flag_target}_${min_version_var}
|
||||||
COMMENT "Check CMake flag ${JSON_CMAKE_FLAG} (CMake ${min_version})"
|
COMMENT "Check CMake flag ${flag} (CMake ${min_version})"
|
||||||
COMMAND mkdir -pv ${PROJECT_BINARY_DIR}/build_${flag_target}_${min_version_var}
|
COMMAND mkdir -pv ${PROJECT_BINARY_DIR}/build_${flag_target}_${min_version_var}
|
||||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_${flag_target}_${min_version_var}
|
COMMAND cd ${PROJECT_BINARY_DIR}/build_${flag_target}_${min_version_var}
|
||||||
&& ${cmake_binary} -Werror=dev ${PROJECT_SOURCE_DIR} -D${flag}=ON
|
&& ${cmake_binary} -Werror=dev ${PROJECT_SOURCE_DIR} -D${flag}=ON
|
||||||
DEPENDS ${cmake_binary}
|
DEPENDS ${cmake_binary}
|
||||||
)
|
)
|
||||||
list(APPEND JSON_CMAKE_FLAG_TARGETS ${JSON_CMAKE_FLAG_TARGET} ${flag_target}_${min_version_var})
|
list(APPEND JSON_CMAKE_FLAG_TARGETS ${flag_target}_${min_version_var})
|
||||||
list(APPEND JSON_CMAKE_FLAG_BUILD_DIRS ${PROJECT_BINARY_DIR}/build_${flag_target} ${PROJECT_BINARY_DIR}/build_${flag_target}_${min_version_var})
|
list(APPEND JSON_CMAKE_FLAG_BUILD_DIRS ${PROJECT_BINARY_DIR}/build_${flag_target}_${min_version_var})
|
||||||
set(JSON_CMAKE_FLAG_TARGETS ${JSON_CMAKE_FLAG_TARGETS} PARENT_SCOPE)
|
set(JSON_CMAKE_FLAG_TARGETS ${JSON_CMAKE_FLAG_TARGETS} PARENT_SCOPE)
|
||||||
set(JSON_CMAKE_FLAG_BUILD_DIRS ${JSON_CMAKE_FLAG_BUILD_DIRS} PARENT_SCOPE)
|
set(JSON_CMAKE_FLAG_BUILD_DIRS ${JSON_CMAKE_FLAG_BUILD_DIRS} PARENT_SCOPE)
|
||||||
endfunction()
|
endfunction()
|
||||||
@@ -696,6 +724,20 @@ foreach(JSON_CMAKE_FLAG ${JSON_CMAKE_FLAGS_4_0_0})
|
|||||||
ci_add_cmake_flags_targets(${JSON_CMAKE_FLAG} 4.0.0)
|
ci_add_cmake_flags_targets(${JSON_CMAKE_FLAG} 4.0.0)
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
# check the same flags with the CMake version running this build
|
||||||
|
foreach(JSON_CMAKE_FLAG ${JSON_CMAKE_FLAGS_4_0_0})
|
||||||
|
string(TOLOWER "ci_cmake_flag_${JSON_CMAKE_FLAG}" flag_target)
|
||||||
|
add_custom_target(${flag_target}
|
||||||
|
COMMENT "Check CMake flag ${JSON_CMAKE_FLAG} (CMake ${CMAKE_VERSION})"
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
-Werror=dev
|
||||||
|
-D${JSON_CMAKE_FLAG}=ON
|
||||||
|
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_${flag_target}
|
||||||
|
)
|
||||||
|
list(APPEND JSON_CMAKE_FLAG_TARGETS ${flag_target})
|
||||||
|
list(APPEND JSON_CMAKE_FLAG_BUILD_DIRS ${PROJECT_BINARY_DIR}/build_${flag_target})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
add_custom_target(ci_cmake_flags
|
add_custom_target(ci_cmake_flags
|
||||||
DEPENDS ${JSON_CMAKE_FLAG_TARGETS}
|
DEPENDS ${JSON_CMAKE_FLAG_TARGETS}
|
||||||
COMMENT "Check CMake flags"
|
COMMENT "Check CMake flags"
|
||||||
@@ -854,6 +896,6 @@ add_custom_target(ci_test_build_documentation
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
add_custom_target(ci_clean
|
add_custom_target(ci_clean
|
||||||
COMMAND rm -fr ${PROJECT_BINARY_DIR}/build_* cmake-3.5.0-Darwin64 ${JSON_CMAKE_FLAG_BUILD_DIRS} ${single_binaries}
|
COMMAND rm -fr ${PROJECT_BINARY_DIR}/build_* ${PROJECT_BINARY_DIR}/cmake-3.5.0 ${PROJECT_BINARY_DIR}/cmake-3.31.6 ${PROJECT_BINARY_DIR}/cmake-4.0.0 ${JSON_CMAKE_FLAG_BUILD_DIRS} ${single_binaries}
|
||||||
COMMENT "Clean generated directories"
|
COMMENT "Clean generated directories"
|
||||||
)
|
)
|
||||||
|
|||||||
+16
-5
@@ -11,20 +11,31 @@ EXAMPLES = $(wildcard mkdocs/docs/examples/*.cpp)
|
|||||||
|
|
||||||
cxx_standard = $(lastword c++11 $(filter c++%, $(subst ., ,$1)))
|
cxx_standard = $(lastword c++11 $(filter c++%, $(subst ., ,$1)))
|
||||||
|
|
||||||
|
# common compile flags for the stand-alone example files
|
||||||
|
EXAMPLE_CPPFLAGS = -I $(SRCDIR) -DJSON_USE_GLOBAL_UDLS=0
|
||||||
|
EXAMPLE_WARNFLAGS = -Werror=deprecated-declarations
|
||||||
|
|
||||||
|
# examples that document deprecated API and are allowed to use it
|
||||||
|
DEPRECATED_EXAMPLES = $(addprefix mkdocs/docs/examples/, \
|
||||||
|
json_pointer__operator__equal_stringtype \
|
||||||
|
json_pointer__operator__notequal_stringtype \
|
||||||
|
json_pointer__operator_string_t)
|
||||||
|
$(DEPRECATED_EXAMPLES:=.output) $(DEPRECATED_EXAMPLES:=.test): EXAMPLE_WARNFLAGS = -Wno-deprecated-declarations
|
||||||
|
|
||||||
# create output from a stand-alone example file
|
# create output from a stand-alone example file
|
||||||
%.output: %.cpp
|
%.output: %.cpp
|
||||||
@echo "standard $(call cxx_standard $(<:.cpp=))"
|
@echo "standard $(call cxx_standard,$(<:.cpp=))"
|
||||||
$(MAKE) $(<:.cpp=) \
|
$(MAKE) $(<:.cpp=) \
|
||||||
CPPFLAGS="-I $(SRCDIR) -DJSON_USE_GLOBAL_UDLS=0" \
|
CPPFLAGS="$(EXAMPLE_CPPFLAGS)" \
|
||||||
CXXFLAGS="-std=$(call cxx_standard,$(<:.cpp=)) -Wno-deprecated-declarations"
|
CXXFLAGS="-std=$(call cxx_standard,$(<:.cpp=)) $(EXAMPLE_WARNFLAGS)"
|
||||||
./$(<:.cpp=) > $@
|
./$(<:.cpp=) > $@
|
||||||
rm $(<:.cpp=)
|
rm $(<:.cpp=)
|
||||||
|
|
||||||
# compare created output with current output of the example files
|
# compare created output with current output of the example files
|
||||||
%.test: %.cpp
|
%.test: %.cpp
|
||||||
$(MAKE) $(<:.cpp=) \
|
$(MAKE) $(<:.cpp=) \
|
||||||
CPPFLAGS="-I $(SRCDIR) -DJSON_USE_GLOBAL_UDLS=0" \
|
CPPFLAGS="$(EXAMPLE_CPPFLAGS)" \
|
||||||
CXXFLAGS="-std=$(call cxx_standard,$(<:.cpp=)) -Wno-deprecated-declarations"
|
CXXFLAGS="-std=$(call cxx_standard,$(<:.cpp=)) $(EXAMPLE_WARNFLAGS)"
|
||||||
./$(<:.cpp=) > $@
|
./$(<:.cpp=) > $@
|
||||||
diff $@ $(<:.cpp=.output)
|
diff $@ $(<:.cpp=.output)
|
||||||
rm $(<:.cpp=) $@
|
rm $(<:.cpp=) $@
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<key>dashIndexFilePath</key>
|
<key>dashIndexFilePath</key>
|
||||||
<string>index.html</string>
|
<string>index.html</string>
|
||||||
<key>DashDocSetFallbackURL</key>
|
<key>DashDocSetFallbackURL</key>
|
||||||
<string>https://nlohmann.github.io/json/</string>
|
<string>https://json.nlohmann.me/</string>
|
||||||
<key>isJavaScriptEnabled</key>
|
<key>isJavaScriptEnabled</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
|
|||||||
+12
-22
@@ -52,35 +52,25 @@ install_docset_zeal: JSON_for_Modern_C++.docset
|
|||||||
mkdir -p $$docset_root; \
|
mkdir -p $$docset_root; \
|
||||||
cp -r JSON_for_Modern_C++.docset $$docset_root/
|
cp -r JSON_for_Modern_C++.docset $$docset_root/
|
||||||
|
|
||||||
|
# both targets below compare the docset search index with the mkdocs page
|
||||||
|
# set. They share the same normalization (docs/foo/index.md and
|
||||||
|
# docs/foo.md both become foo/index.html, the URL mkdocs itself would
|
||||||
|
# give the page; the top-level index.md is excluded, as it is not part
|
||||||
|
# of the hand-curated docSet.sql) and use comm(1) on two sorted lists
|
||||||
|
# instead of running a sqlite3 query, or an O(n*m) nested shell loop,
|
||||||
|
# once per page.
|
||||||
|
DOCSET_INDEX_PATHS=$(shell sqlite3 docSet.dsidx "SELECT DISTINCT path FROM searchIndex" | sort)
|
||||||
|
DOCSET_PAGE_PATHS=$(shell echo '$(MKDOCS_PAGES)' | tr ' ' '\n' | grep -v '^index\.md$$' | $(SED) -E 's@/index\.md$$@/index.html@; s@\.md$$@/index.html@' | sort)
|
||||||
|
|
||||||
# list mkdocs pages missing from the docset index
|
# list mkdocs pages missing from the docset index
|
||||||
.PHONY: list_missing_pages
|
.PHONY: list_missing_pages
|
||||||
list_missing_pages: docSet.dsidx
|
list_missing_pages: docSet.dsidx
|
||||||
@for page in $(MKDOCS_PAGES); do \
|
@comm -23 <(echo '$(DOCSET_PAGE_PATHS)' | tr ' ' '\n') <(echo '$(DOCSET_INDEX_PATHS)' | tr ' ' '\n')
|
||||||
case "$$page" in \
|
|
||||||
*/index.md) path=$${page/\/index.md/} ;; \
|
|
||||||
*) path=$${page/.md/} ;; \
|
|
||||||
esac; \
|
|
||||||
if [ "x$$page" != "xindex.md" -a "x$$(sqlite3 docSet.dsidx "SELECT COUNT(*) FROM searchIndex WHERE path='$$path/index.html'")" = "x0" ]; then \
|
|
||||||
echo $$page; \
|
|
||||||
fi \
|
|
||||||
done
|
|
||||||
|
|
||||||
# list paths in the docset index without a corresponding mkdocs page
|
# list paths in the docset index without a corresponding mkdocs page
|
||||||
.PHONY: list_removed_paths
|
.PHONY: list_removed_paths
|
||||||
list_removed_paths: docSet.dsidx
|
list_removed_paths: docSet.dsidx
|
||||||
@for path in $$(sqlite3 docSet.dsidx "SELECT path FROM searchIndex"); do \
|
@comm -13 <(echo '$(DOCSET_PAGE_PATHS)' | tr ' ' '\n') <(echo '$(DOCSET_INDEX_PATHS)' | tr ' ' '\n')
|
||||||
page=$${path/\/index.html/.md}; \
|
|
||||||
page_index=$${path/index.html/index.md}; \
|
|
||||||
page_found=0; \
|
|
||||||
for p in $(MKDOCS_PAGES); do \
|
|
||||||
if [ "x$$p" = "x$$page" -o "x$$p" = "x$$page_index" ]; then \
|
|
||||||
page_found=1; \
|
|
||||||
fi \
|
|
||||||
done; \
|
|
||||||
if [ "x$$page_found" = "x0" ]; then \
|
|
||||||
echo $$path; \
|
|
||||||
fi \
|
|
||||||
done
|
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ documentation browsers like [Dash](https://kapeli.com/dash), [Velocity](https://
|
|||||||
The docset can be created with
|
The docset can be created with
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
make nlohmann_json.docset
|
make JSON_for_Modern_C++.docset
|
||||||
```
|
```
|
||||||
|
|
||||||
The generated folder `nlohmann_json.docset` can then be opened in the documentation browser.
|
The generated folder `JSON_for_Modern_C++.docset` can then be opened in the documentation browser. `make all` builds a
|
||||||
|
`JSON_for_Modern_C++.tgz` archive instead, and `make install_docset_zeal` installs the docset for Zeal directly.
|
||||||
|
|
||||||
A recent version is also part of the [Dash user contributions](https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/JSON_for_Modern_C%2B%2B).
|
A recent version is also part of the [Dash user contributions](https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/JSON_for_Modern_C%2B%2B).
|
||||||
|
|
||||||
|
|||||||
@@ -210,6 +210,7 @@ INSERT INTO searchIndex(name, type, path) VALUES ('JSON_CATCH_USER', 'Macro', 'a
|
|||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DIAGNOSTICS', 'Macro', 'api/macros/json_diagnostics/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DIAGNOSTICS', 'Macro', 'api/macros/json_diagnostics/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DIAGNOSTIC_POSITIONS', 'Macro', 'api/macros/json_diagnostic_positions/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DIAGNOSTIC_POSITIONS', 'Macro', 'api/macros/json_diagnostic_positions/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DISABLE_ENUM_SERIALIZATION', 'Macro', 'api/macros/json_disable_enum_serialization/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DISABLE_ENUM_SERIALIZATION', 'Macro', 'api/macros/json_disable_enum_serialization/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DISABLE_TUPLE_REFERENCE_CONVERSION', 'Macro', 'api/macros/json_disable_tuple_reference_conversion/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_11', 'Macro', 'api/macros/json_has_cpp_11/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_11', 'Macro', 'api/macros/json_has_cpp_11/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_14', 'Macro', 'api/macros/json_has_cpp_11/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_14', 'Macro', 'api/macros/json_has_cpp_11/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_17', 'Macro', 'api/macros/json_has_cpp_11/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_17', 'Macro', 'api/macros/json_has_cpp_11/index.html');
|
||||||
|
|||||||
@@ -139,8 +139,8 @@ basic_json(basic_json&& other) noexcept;
|
|||||||
|
|
||||||
- In case of a `#!json null` type, [invalid_iterator.206](../../home/exceptions.md#jsonexceptioninvalid_iterator206)
|
- In case of a `#!json null` type, [invalid_iterator.206](../../home/exceptions.md#jsonexceptioninvalid_iterator206)
|
||||||
is thrown.
|
is thrown.
|
||||||
- In case of other primitive types (number, boolean, or string), `first` must be `begin()` and `last` must be
|
- In case of other primitive types (number, boolean, string, or binary), `first` must be `begin()` and `last`
|
||||||
`end()`. In this case, the value is copied. Otherwise,
|
must be `end()`. In this case, the value is copied. Otherwise,
|
||||||
[`invalid_iterator.204`](../../home/exceptions.md#jsonexceptioninvalid_iterator204) is thrown.
|
[`invalid_iterator.204`](../../home/exceptions.md#jsonexceptioninvalid_iterator204) is thrown.
|
||||||
- In case of structured types (array, object), the constructor behaves as similar versions for `std::vector` or
|
- In case of structured types (array, object), the constructor behaves as similar versions for `std::vector` or
|
||||||
`std::map`; that is, a JSON array or object is constructed from the values in the range.
|
`std::map`; that is, a JSON array or object is constructed from the values in the range.
|
||||||
@@ -159,6 +159,8 @@ basic_json(basic_json&& other) noexcept;
|
|||||||
- `CompatibleType` is not `basic_json` (to avoid hijacking copy/move constructors),
|
- `CompatibleType` is not `basic_json` (to avoid hijacking copy/move constructors),
|
||||||
- `CompatibleType` is not a different `basic_json` type (i.e. with different template arguments)
|
- `CompatibleType` is not a different `basic_json` type (i.e. with different template arguments)
|
||||||
- `CompatibleType` is not a `basic_json` nested type (e.g., `json_pointer`, `iterator`, etc.)
|
- `CompatibleType` is not a `basic_json` nested type (e.g., `json_pointer`, `iterator`, etc.)
|
||||||
|
- if [`JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`](../macros/json_disable_tuple_reference_conversion.md) is defined
|
||||||
|
to `1`: `CompatibleType` is not a one-element `std::tuple` holding a reference to `basic_json`
|
||||||
- `json_serializer<U>` (with `U = uncvref_t<CompatibleType>`) has a `to_json(basic_json_t&, CompatibleType&&)`
|
- `json_serializer<U>` (with `U = uncvref_t<CompatibleType>`) has a `to_json(basic_json_t&, CompatibleType&&)`
|
||||||
method
|
method
|
||||||
|
|
||||||
@@ -242,8 +244,8 @@ basic_json(basic_json&& other) noexcept;
|
|||||||
and `last` are not compatible (i.e., do not belong to the same JSON value). In this case, the range
|
and `last` are not compatible (i.e., do not belong to the same JSON value). In this case, the range
|
||||||
`[first, last)` is undefined.
|
`[first, last)` is undefined.
|
||||||
- Throws [`invalid_iterator.204`](../../home/exceptions.md#jsonexceptioninvalid_iterator204) if iterators `first`
|
- Throws [`invalid_iterator.204`](../../home/exceptions.md#jsonexceptioninvalid_iterator204) if iterators `first`
|
||||||
and `last` belong to a primitive type (number, boolean, or string), but `first` does not point to the first
|
and `last` belong to a primitive type (number, boolean, string, or binary), but `first` does not point to the
|
||||||
element anymore. In this case, the range `[first, last)` is undefined. See the example code below.
|
first element anymore. In this case, the range `[first, last)` is undefined. See the example code below.
|
||||||
- Throws [`invalid_iterator.206`](../../home/exceptions.md#jsonexceptioninvalid_iterator206) if iterators `first`
|
- Throws [`invalid_iterator.206`](../../home/exceptions.md#jsonexceptioninvalid_iterator206) if iterators `first`
|
||||||
and `last` belong to a `#!json null` value. In this case, the range `[first, last)` is undefined.
|
and `last` belong to a `#!json null` value. In this case, the range `[first, last)` is undefined.
|
||||||
8. (none)
|
8. (none)
|
||||||
@@ -423,6 +425,8 @@ basic_json(basic_json&& other) noexcept;
|
|||||||
4. Since version 3.2.0.
|
4. Since version 3.2.0.
|
||||||
5. Since version 1.0.0.
|
5. Since version 1.0.0.
|
||||||
6. Since version 1.0.0.
|
6. Since version 1.0.0.
|
||||||
7. Since version 1.0.0.
|
7. Since version 1.0.0. Fixed in version 3.13.0 to also check the iterator range for binary values; before, a range
|
||||||
|
that did not cover the whole value (such as `(end(), end())`) was accepted and the whole binary value was copied,
|
||||||
|
unlike the other primitive types.
|
||||||
8. Since version 1.0.0.
|
8. Since version 1.0.0.
|
||||||
9. Since version 1.0.0.
|
9. Since version 1.0.0.
|
||||||
|
|||||||
@@ -7,15 +7,15 @@ void clear() noexcept;
|
|||||||
Clears the content of a JSON value and resets it to the default value as if [`basic_json(value_t)`](basic_json.md) would
|
Clears the content of a JSON value and resets it to the default value as if [`basic_json(value_t)`](basic_json.md) would
|
||||||
have been called with the current value type from [`type()`](type.md):
|
have been called with the current value type from [`type()`](type.md):
|
||||||
|
|
||||||
| Value type | initial value |
|
| Value type | initial value |
|
||||||
|------------|----------------------|
|
|------------|-----------------------------------------|
|
||||||
| null | `null` |
|
| null | `null` |
|
||||||
| boolean | `false` |
|
| boolean | `false` |
|
||||||
| string | `""` |
|
| string | `""` |
|
||||||
| number | `0` |
|
| number | `0` |
|
||||||
| binary | An empty byte vector |
|
| binary | An empty byte vector with no subtype |
|
||||||
| object | `{}` |
|
| object | `{}` |
|
||||||
| array | `[]` |
|
| array | `[]` |
|
||||||
|
|
||||||
Has the same effect as calling
|
Has the same effect as calling
|
||||||
|
|
||||||
@@ -56,3 +56,4 @@ All iterators, pointers, and references related to this container are invalidate
|
|||||||
|
|
||||||
- Added in version 1.0.0.
|
- Added in version 1.0.0.
|
||||||
- Added support for binary types in version 3.8.0.
|
- Added support for binary types in version 3.8.0.
|
||||||
|
- Fixed in version 3.13.0 to also clear the subtype of a binary value; before, the subtype was left unchanged.
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ Logarithmic in the size of the JSON object.
|
|||||||
|
|
||||||
- This method always returns `#!cpp false` when executed on a JSON type that is not an object.
|
- This method always returns `#!cpp false` when executed on a JSON type that is not an object.
|
||||||
- This method can be executed on any JSON value type.
|
- This method can be executed on any JSON value type.
|
||||||
|
- Calling this function with an integer argument (for example, `#!cpp contains(0)`) does not compile: such an argument
|
||||||
|
would otherwise implicitly convert to a null `#!cpp const char*` and, from there, cause undefined behavior when
|
||||||
|
constructing a `#!cpp std::string` for the object key. To check for an array element instead, use [`at`](at.md),
|
||||||
|
[`operator[]`](operator%5B%5D.md), or compare against [`size`](size.md).
|
||||||
|
|
||||||
!!! info "Postconditions"
|
!!! info "Postconditions"
|
||||||
|
|
||||||
@@ -117,3 +121,5 @@ Logarithmic in the size of the JSON object.
|
|||||||
1. Added in version 3.11.0.
|
1. Added in version 3.11.0.
|
||||||
2. Added in version 3.6.0. Extended template `KeyType` to support comparable types in version 3.11.0.
|
2. Added in version 3.6.0. Extended template `KeyType` to support comparable types in version 3.11.0.
|
||||||
3. Added in version 3.7.0.
|
3. Added in version 3.7.0.
|
||||||
|
4. Deleted overloads for integral key types added in version 3.13.0 to reject such calls at compile time instead of
|
||||||
|
causing undefined behavior at runtime.
|
||||||
|
|||||||
@@ -40,7 +40,11 @@ Logarithmic in the size of the JSON object.
|
|||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
This method always returns `0` when executed on a JSON type that is not an object.
|
- This method always returns `0` when executed on a JSON type that is not an object.
|
||||||
|
- Calling this function with an integer argument (for example, `#!cpp count(0)`) does not compile: such an argument
|
||||||
|
would otherwise implicitly convert to a null `#!cpp const char*` and, from there, cause undefined behavior when
|
||||||
|
constructing a `#!cpp std::string` for the object key. To check for an array element instead, use [`at`](at.md),
|
||||||
|
[`operator[]`](operator%5B%5D.md), or compare against [`size`](size.md).
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
@@ -81,3 +85,5 @@ This method always returns `0` when executed on a JSON type that is not an objec
|
|||||||
|
|
||||||
1. Added in version 3.11.0.
|
1. Added in version 3.11.0.
|
||||||
2. Added in version 1.0.0. Changed parameter `key` type to `KeyType&&` in version 3.11.0.
|
2. Added in version 1.0.0. Changed parameter `key` type to `KeyType&&` in version 3.11.0.
|
||||||
|
3. Deleted overload for integral key types added in version 3.13.0 to reject such calls at compile time instead of
|
||||||
|
causing undefined behavior at runtime.
|
||||||
|
|||||||
@@ -44,7 +44,11 @@ Logarithmic in the size of the JSON object.
|
|||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
This method always returns `end()` when executed on a JSON type that is not an object.
|
- This method always returns `end()` when executed on a JSON type that is not an object.
|
||||||
|
- Calling this function with an integer argument (for example, `#!cpp find(0)`) does not compile: such an argument
|
||||||
|
would otherwise implicitly convert to a null `#!cpp const char*` and, from there, cause undefined behavior when
|
||||||
|
constructing a `#!cpp std::string` for the object key. To access an array element instead, use [`at`](at.md),
|
||||||
|
[`operator[]`](operator%5B%5D.md), or compare against [`size`](size.md).
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
@@ -85,3 +89,5 @@ This method always returns `end()` when executed on a JSON type that is not an o
|
|||||||
|
|
||||||
1. Added in version 3.11.0.
|
1. Added in version 3.11.0.
|
||||||
2. Added in version 1.0.0. Changed to support comparable types in version 3.11.0.
|
2. Added in version 1.0.0. Changed to support comparable types in version 3.11.0.
|
||||||
|
3. Deleted overloads for integral key types added in version 3.13.0 to reject such calls at compile time instead of
|
||||||
|
causing undefined behavior at runtime.
|
||||||
|
|||||||
@@ -195,5 +195,7 @@ Strong exception safety: if an exception occurs, the original value stays intact
|
|||||||
1. Added in version 1.0.0.
|
1. Added in version 1.0.0.
|
||||||
2. Added in version 1.0.0.
|
2. Added in version 1.0.0.
|
||||||
3. Added in version 1.0.0.
|
3. Added in version 1.0.0.
|
||||||
4. Added in version 1.0.0.
|
4. Added in version 1.0.0. Fixed in version 3.13.0 to copy the values before inserting; before, an `ilist` that
|
||||||
|
referred to elements of the array being inserted into could insert wrong values, because the range insert could
|
||||||
|
move from or shift an element before it was copied.
|
||||||
5. Added in version 3.0.0.
|
5. Added in version 3.0.0.
|
||||||
|
|||||||
@@ -37,6 +37,12 @@ Thereby, `Target` is the current object; that is, the patch is applied to the cu
|
|||||||
|
|
||||||
Linear in the lengths of `apply_patch`.
|
Linear in the lengths of `apply_patch`.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
`apply_patch` may be `#!cpp *this` itself or refer to a value contained in `#!cpp *this` (for example, a subobject
|
||||||
|
returned by `#!cpp (*this)[key]`); it is read as it was when `merge_patch()` was called, before any modification of
|
||||||
|
`#!cpp *this`.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
@@ -61,3 +67,5 @@ Linear in the lengths of `apply_patch`.
|
|||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 3.0.0.
|
- Added in version 3.0.0.
|
||||||
|
- Fixed use of freed or relocated memory when `apply_patch` is `#!cpp *this` or refers to a value contained in
|
||||||
|
`#!cpp *this`, in version 3.13.0.
|
||||||
|
|||||||
@@ -55,10 +55,6 @@ This implementation does exactly follow this approach, as it uses double precisi
|
|||||||
smaller than `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` will be stored as NaN internally
|
smaller than `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` will be stored as NaN internally
|
||||||
and be serialized to `null`.
|
and be serialized to `null`.
|
||||||
|
|
||||||
During deserialization (from JSON text or any of the binary formats), a finite number that does not fit into
|
|
||||||
`number_float_t` is rejected with [`out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406), for
|
|
||||||
example a double-precision number in a binary format when `number_float_t` is `#!cpp float`.
|
|
||||||
|
|
||||||
#### Storage
|
#### Storage
|
||||||
|
|
||||||
Floating-point number values are stored directly inside a `basic_json` type.
|
Floating-point number values are stored directly inside a `basic_json` type.
|
||||||
|
|||||||
@@ -47,12 +47,11 @@ With the default values for `NumberIntegerType` (`std::int64_t`), the default va
|
|||||||
|
|
||||||
When the default type is used, the maximal integer number that can be stored is `9223372036854775807` (INT64_MAX) and
|
When the default type is used, the maximal integer number that can be stored is `9223372036854775807` (INT64_MAX) and
|
||||||
the minimal integer number that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers that are out of
|
the minimal integer number that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers that are out of
|
||||||
range will yield over/underflow when used in a constructor. During deserialization (from JSON text or any of the binary
|
range will yield over/underflow when used in a constructor. During deserialization, too large or small integer numbers
|
||||||
formats), too large or small integer numbers will automatically be stored as [`number_unsigned_t`](number_unsigned_t.md)
|
will automatically be stored as [`number_unsigned_t`](number_unsigned_t.md) or [`number_float_t`](number_float_t.md).
|
||||||
or [`number_float_t`](number_float_t.md).
|
|
||||||
|
|
||||||
[RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
|
[RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
|
||||||
> Note that when such software is used, numbers that are integers and are in the range $[-2^{53}+1, 2^{53}-1]$ are
|
> Note that when such software is used, numbers that are integers and are in the range [-2<sup>53</sup>+1, 2<sup>53</sup>-1] are
|
||||||
> interoperable in the sense that implementations will agree exactly on their numeric values.
|
> interoperable in the sense that implementations will agree exactly on their numeric values.
|
||||||
|
|
||||||
As this range is a subrange of the exactly supported range [INT64_MIN, INT64_MAX], this class's integer type is
|
As this range is a subrange of the exactly supported range [INT64_MIN, INT64_MAX], this class's integer type is
|
||||||
|
|||||||
@@ -48,12 +48,11 @@ With the default values for `NumberUnsignedType` (`std::uint64_t`), the default
|
|||||||
|
|
||||||
When the default type is used, the maximal integer number that can be stored is `18446744073709551615` (UINT64_MAX) and
|
When the default type is used, the maximal integer number that can be stored is `18446744073709551615` (UINT64_MAX) and
|
||||||
the minimal integer number that can be stored is `0`. Integer numbers that are out of range will yield over/underflow
|
the minimal integer number that can be stored is `0`. Integer numbers that are out of range will yield over/underflow
|
||||||
when used in a constructor. During deserialization (from JSON text or any of the binary formats), too large or small
|
when used in a constructor. During deserialization, too large or small integer numbers will automatically be stored
|
||||||
integer numbers will automatically be stored as [`number_integer_t`](number_integer_t.md) or
|
as [`number_integer_t`](number_integer_t.md) or [`number_float_t`](number_float_t.md).
|
||||||
[`number_float_t`](number_float_t.md).
|
|
||||||
|
|
||||||
[RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
|
[RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
|
||||||
> Note that when such software is used, numbers that are integers and are in the range $[-2^{53}+1, 2^{53}-1]$ are
|
> Note that when such software is used, numbers that are integers and are in the range [-2<sup>53</sup>+1, 2<sup>53</sup>-1] are
|
||||||
> interoperable in the sense that implementations will agree exactly on their numeric values.
|
> interoperable in the sense that implementations will agree exactly on their numeric values.
|
||||||
|
|
||||||
As this range is a subrange (when considered in conjunction with the `number_integer_t` type) of the exactly supported
|
As this range is a subrange (when considered in conjunction with the `number_integer_t` type) of the exactly supported
|
||||||
|
|||||||
@@ -70,6 +70,9 @@ Strong exception safety: if an exception occurs, the original value stays intact
|
|||||||
1. The function can throw the following exceptions:
|
1. The function can throw the following exceptions:
|
||||||
- Throws [`type_error.305`](../../home/exceptions.md#jsonexceptiontype_error305) if the JSON value is not an array
|
- Throws [`type_error.305`](../../home/exceptions.md#jsonexceptiontype_error305) if the JSON value is not an array
|
||||||
or null; in that case, using the `[]` operator with an index makes no sense.
|
or null; in that case, using the `[]` operator with an index makes no sense.
|
||||||
|
- Throws `#!cpp std::length_error` if `idx` equals the maximum value of `size_type`; the array is left unchanged.
|
||||||
|
(This is the one index for which growing the array to hold it cannot be expressed as a `size_type` size, the same
|
||||||
|
way an oversized [`resize`](https://en.cppreference.com/w/cpp/container/vector/resize) throws.)
|
||||||
2. The function can throw the following exceptions:
|
2. The function can throw the following exceptions:
|
||||||
- Throws [`type_error.305`](../../home/exceptions.md#jsonexceptiontype_error305) if the JSON value is not an object
|
- Throws [`type_error.305`](../../home/exceptions.md#jsonexceptiontype_error305) if the JSON value is not an object
|
||||||
or null; in that case, using the `[]` operator with a key makes no sense.
|
or null; in that case, using the `[]` operator with a key makes no sense.
|
||||||
@@ -257,7 +260,8 @@ Strong exception safety: if an exception occurs, the original value stays intact
|
|||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
1. Added in version 1.0.0.
|
1. Added in version 1.0.0. Fixed in version 3.13.0 to throw `#!cpp std::length_error` instead of emptying the array and
|
||||||
|
accessing it out of bounds when `idx` equals the maximum value of `size_type`.
|
||||||
2. Added in version 1.0.0. Added overloads for `T* key` in version 1.1.0. Removed overloads for `T* key` (replaced by 3)
|
2. Added in version 1.0.0. Added overloads for `T* key` in version 1.1.0. Removed overloads for `T* key` (replaced by 3)
|
||||||
in version 3.11.0.
|
in version 3.11.0.
|
||||||
3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as
|
3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as
|
||||||
|
|||||||
@@ -88,7 +88,12 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
## Exceptions
|
## Exceptions
|
||||||
|
|
||||||
- Throws [`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101) in case of an unexpected token, or
|
- Throws [`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101) in case of an unexpected token, or
|
||||||
empty input like a null `FILE*` or `char*` pointer.
|
empty input like a null `FILE*` or `char*` pointer, or an `std::istream` without a stream buffer
|
||||||
|
(`#!cpp i.rdbuf() == nullptr`, for instance `#!cpp std::istream(nullptr)`).
|
||||||
|
- If reading from an `std::istream` reaches the end of the input and `eofbit` is part of the stream's
|
||||||
|
[`exceptions()`](https://en.cppreference.com/w/cpp/io/basic_ios/exceptions) mask, the `std::ios_base::failure`
|
||||||
|
thrown by the stream itself propagates instead of a `parse_error`, the same as it would for the standard library's
|
||||||
|
own extraction operators.
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
@@ -254,6 +259,8 @@ outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-by
|
|||||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||||
- `JSON_STRICT_NUL_HANDLING` added in version 3.13.0 to optionally reject a NUL byte in the input instead of treating
|
- `JSON_STRICT_NUL_HANDLING` added in version 3.13.0 to optionally reject a NUL byte in the input instead of treating
|
||||||
it as end of input; planned to become the default in version 4.0.0.
|
it as end of input; planned to become the default in version 4.0.0.
|
||||||
|
- Extended empty-input detection to also cover an `std::istream` without a stream buffer, and fixed a crash
|
||||||
|
(`std::terminate`) when parsing from an `std::istream` with `eofbit` in its exception mask, in version 3.13.0.
|
||||||
|
|
||||||
!!! warning "Deprecation"
|
!!! warning "Deprecation"
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ classDiagram
|
|||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
For an input with $n$ bytes, 1 is the index of the first character and $n+1$ is the index of the terminating null byte
|
For an input with <i>n</i> bytes, 1 is the index of the first character and <i>n</i>+1 is the index of the terminating null byte
|
||||||
or the end of file. This also holds true when reading a byte vector for binary formats.
|
or the end of file. This also holds true when reading a byte vector for binary formats.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|||||||
@@ -100,3 +100,6 @@ the latter case, it is skipped completely, or replaced by `null` if it is the to
|
|||||||
- Added in version 1.0.0.
|
- Added in version 1.0.0.
|
||||||
- Fixed in version 3.13.0 to also remove discarded values from a parent object; before, discarding an array or a value
|
- Fixed in version 3.13.0 to also remove discarded values from a parent object; before, discarding an array or a value
|
||||||
stored under an object key left a discarded member behind, which made the parse result serialize to invalid JSON.
|
stored under an object key left a discarded member behind, which made the parse result serialize to invalid JSON.
|
||||||
|
- Fixed in version 3.13.0 so that discarding an array or object at its start event also hides its content from the
|
||||||
|
callback, as documented above; before, the callback was still called for the content, and the key of every member of
|
||||||
|
a discarded object was kept in memory until the parse ended.
|
||||||
|
|||||||
@@ -90,7 +90,9 @@ The SAX event lister must follow the interface of [`json_sax`](../json_sax/index
|
|||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
return value of the last processed SAX event
|
`#!cpp true` if the input was parsed without errors and no SAX event returned `#!cpp false`; `#!cpp false` otherwise.
|
||||||
|
In particular, the result is `#!cpp false` for input with errors, even if the SAX parser recovered from all of them
|
||||||
|
(see [error recovery](../../features/parsing/error_recovery.md)).
|
||||||
|
|
||||||
## Exception safety
|
## Exception safety
|
||||||
|
|
||||||
@@ -138,6 +140,7 @@ A UTF-8 byte order mark is silently ignored.
|
|||||||
- Ignoring comments via `ignore_comments` added in version 3.9.0.
|
- Ignoring comments via `ignore_comments` added in version 3.9.0.
|
||||||
- Added `ignore_trailing_commas` in version 3.13.0.
|
- Added `ignore_trailing_commas` in version 3.13.0.
|
||||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||||
|
- Recovering from parse errors (see [`parse_error`](../json_sax/parse_error.md)) added in version 3.13.0.
|
||||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||||
- `JSON_PRECISE_STREAM_POSITION` added in version 3.13.0 to optionally leave a `#!cpp std::istream` positioned right
|
- `JSON_PRECISE_STREAM_POSITION` added in version 3.13.0 to optionally leave a `#!cpp std::istream` positioned right
|
||||||
after the parsed value when `strict` is `#!cpp false`.
|
after the parsed value when `strict` is `#!cpp false`.
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ void swap(reference other) noexcept (
|
|||||||
std::is_nothrow_move_constructible<value_t>::value &&
|
std::is_nothrow_move_constructible<value_t>::value &&
|
||||||
std::is_nothrow_move_assignable<value_t>::value &&
|
std::is_nothrow_move_assignable<value_t>::value &&
|
||||||
std::is_nothrow_move_constructible<json_value>::value &&
|
std::is_nothrow_move_constructible<json_value>::value &&
|
||||||
std::is_nothrow_move_assignable<json_value>::value
|
std::is_nothrow_move_assignable<json_value>::value &&
|
||||||
|
std::is_nothrow_move_constructible<json_base_class_t>::value &&
|
||||||
|
std::is_nothrow_move_assignable<json_base_class_t>::value
|
||||||
);
|
);
|
||||||
|
|
||||||
// (2)
|
// (2)
|
||||||
@@ -14,7 +16,9 @@ friend void swap(reference left, reference right) noexcept (
|
|||||||
std::is_nothrow_move_constructible<value_t>::value &&
|
std::is_nothrow_move_constructible<value_t>::value &&
|
||||||
std::is_nothrow_move_assignable<value_t>::value &&
|
std::is_nothrow_move_assignable<value_t>::value &&
|
||||||
std::is_nothrow_move_constructible<json_value>::value &&
|
std::is_nothrow_move_constructible<json_value>::value &&
|
||||||
std::is_nothrow_move_assignable<json_value>::value
|
std::is_nothrow_move_assignable<json_value>::value &&
|
||||||
|
std::is_nothrow_move_constructible<json_base_class_t>::value &&
|
||||||
|
std::is_nothrow_move_assignable<json_base_class_t>::value
|
||||||
);
|
);
|
||||||
|
|
||||||
// (3)
|
// (3)
|
||||||
@@ -37,11 +41,15 @@ void swap(typename binary_t::container_type& other);
|
|||||||
individual elements. All iterators and references remain valid. The past-the-end iterator is invalidated. If macro
|
individual elements. All iterators and references remain valid. The past-the-end iterator is invalidated. If macro
|
||||||
[`JSON_DIAGNOSTIC_POSITIONS`](../macros/json_diagnostic_positions.md) is defined to `#!cpp 1`, the
|
[`JSON_DIAGNOSTIC_POSITIONS`](../macros/json_diagnostic_positions.md) is defined to `#!cpp 1`, the
|
||||||
[`start_pos()`](start_pos.md)/[`end_pos()`](end_pos.md) diagnostic positions are exchanged along with the value.
|
[`start_pos()`](start_pos.md)/[`end_pos()`](end_pos.md) diagnostic positions are exchanged along with the value.
|
||||||
|
The [`json_base_class_t`](json_base_class_t.md) subobject is exchanged along with the value as well, the same way it
|
||||||
|
is copied or moved by the copy/move constructors and assignment operators.
|
||||||
2. Exchanges the contents of the JSON value from `left` with those of `right`. Does not invoke any move, copy, or swap
|
2. Exchanges the contents of the JSON value from `left` with those of `right`. Does not invoke any move, copy, or swap
|
||||||
operations on individual elements. All iterators and references remain valid. The past-the-end iterator is
|
operations on individual elements. All iterators and references remain valid. The past-the-end iterator is
|
||||||
invalidated. Implemented as a friend function callable via ADL. If macro
|
invalidated. Implemented as a friend function callable via ADL. If macro
|
||||||
[`JSON_DIAGNOSTIC_POSITIONS`](../macros/json_diagnostic_positions.md) is defined to `#!cpp 1`, the
|
[`JSON_DIAGNOSTIC_POSITIONS`](../macros/json_diagnostic_positions.md) is defined to `#!cpp 1`, the
|
||||||
[`start_pos()`](start_pos.md)/[`end_pos()`](end_pos.md) diagnostic positions are exchanged along with the value.
|
[`start_pos()`](start_pos.md)/[`end_pos()`](end_pos.md) diagnostic positions are exchanged along with the value.
|
||||||
|
The [`json_base_class_t`](json_base_class_t.md) subobject is exchanged along with the value as well, the same way it
|
||||||
|
is copied or moved by the copy/move constructors and assignment operators.
|
||||||
3. Exchanges the contents of a JSON array with those of `other`. Does not invoke any move, copy, or swap operations on
|
3. Exchanges the contents of a JSON array with those of `other`. Does not invoke any move, copy, or swap operations on
|
||||||
individual elements. All iterators and references remain valid. The past-the-end iterator is invalidated.
|
individual elements. All iterators and references remain valid. The past-the-end iterator is invalidated.
|
||||||
4. Exchanges the contents of a JSON object with those of `other`. Does not invoke any move, copy, or swap operations on
|
4. Exchanges the contents of a JSON object with those of `other`. Does not invoke any move, copy, or swap operations on
|
||||||
@@ -164,8 +172,8 @@ Constant.
|
|||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
1. Since version 1.0.0.
|
1. Since version 1.0.0. Exchanges the `json_base_class_t` subobject along with the value since version 3.13.0.
|
||||||
2. Since version 1.0.0.
|
2. Since version 1.0.0. Exchanges the `json_base_class_t` subobject along with the value since version 3.13.0.
|
||||||
3. Since version 1.0.0.
|
3. Since version 1.0.0.
|
||||||
4. Since version 1.0.0.
|
4. Since version 1.0.0.
|
||||||
5. Since version 1.0.0.
|
5. Since version 1.0.0.
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
- Throws [`out_of_range.412`](../../home/exceptions.md#jsonexceptionout_of_range412) if the length of a document, array,
|
- Throws [`out_of_range.412`](../../home/exceptions.md#jsonexceptionout_of_range412) if the length of a document, array,
|
||||||
string, or binary value exceeds the range of the 32-bit BSON length field; example:
|
string, or binary value exceeds the range of the 32-bit BSON length field; example:
|
||||||
`"BSON length 2147483661 exceeds maximum of 2147483647"`
|
`"BSON length 2147483661 exceeds maximum of 2147483647"`
|
||||||
|
- Throws [`out_of_range.415`](../../home/exceptions.md#jsonexceptionout_of_range415) if the subtype of a binary value
|
||||||
|
exceeds 255, the maximum of the BSON binary subtype; example:
|
||||||
|
`"subtype 70000 is too large for the BSON binary subtype (max 255)"`
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
@@ -78,3 +81,4 @@ pass before anything is written.
|
|||||||
|
|
||||||
- Added in version 3.4.0.
|
- Added in version 3.4.0.
|
||||||
- Linear in the size of `j`, and no longer limited by the call stack for deeply nested values, since version 3.13.0.
|
- Linear in the size of `j`, and no longer limited by the call stack for deeply nested values, since version 3.13.0.
|
||||||
|
- `out_of_range.415` is now detected before anything is written, like the other exceptions above, since version 3.13.0.
|
||||||
|
|||||||
@@ -76,3 +76,6 @@ Linear in the size of the JSON value `j`.
|
|||||||
|
|
||||||
- Added in version 2.0.9.
|
- Added in version 2.0.9.
|
||||||
- Throws `out_of_range.412` and `out_of_range.415` since version 3.13.0.
|
- Throws `out_of_range.412` and `out_of_range.415` since version 3.13.0.
|
||||||
|
- Fixed in version 3.13.0 to serialize `number_integer_t`/`number_unsigned_t` pairs of different width correctly;
|
||||||
|
before, integers could be serialized with the wrong value if `number_integer_t` was narrower than
|
||||||
|
`number_unsigned_t`.
|
||||||
|
|||||||
@@ -59,6 +59,12 @@ Basic guarantee: if an exception is thrown during the operation, the JSON value
|
|||||||
1. O(N*log(size() + N)), where N is the number of elements to insert.
|
1. O(N*log(size() + N)), where N is the number of elements to insert.
|
||||||
2. O(N*log(size() + N)), where N is the number of elements to insert.
|
2. O(N*log(size() + N)), where N is the number of elements to insert.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
The argument `j` (or, for overload (2), the range `[first, last)`) may be `#!cpp *this` itself or refer to a value
|
||||||
|
contained in `#!cpp *this` (for example, a subobject returned by `#!cpp (*this)[key]`); it is read as it was when
|
||||||
|
`update()` was called, before any modification of `#!cpp *this`.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
@@ -155,3 +161,5 @@ Basic guarantee: if an exception is thrown during the operation, the JSON value
|
|||||||
|
|
||||||
- Added in version 3.0.0.
|
- Added in version 3.0.0.
|
||||||
- Added `merge_objects` parameter in 3.10.5.
|
- Added `merge_objects` parameter in 3.10.5.
|
||||||
|
- Fixed use of freed or relocated memory when the argument is `#!cpp *this` or refers to a value contained in
|
||||||
|
`#!cpp *this`, in version 3.13.0.
|
||||||
|
|||||||
@@ -52,6 +52,14 @@ This is equivalent to Python's `dict.get(key, default)`.
|
|||||||
- Unlike [`operator[]`](operator[].md), this function does not implicitly add an element to the position defined by
|
- Unlike [`operator[]`](operator[].md), this function does not implicitly add an element to the position defined by
|
||||||
`key`/`ptr` key. This function is furthermore also applicable to const objects.
|
`key`/`ptr` key. This function is furthermore also applicable to const objects.
|
||||||
|
|
||||||
|
!!! note "Integer keys"
|
||||||
|
|
||||||
|
Calling this function with an integer `key` argument (for example, `#!cpp value(0, 1)`) does not compile in
|
||||||
|
C++11, where `object_comparator_t` is not transparent: such an argument would otherwise implicitly convert to a
|
||||||
|
null `#!cpp const char*` and, from there, cause undefined behavior when constructing a `#!cpp std::string` for the
|
||||||
|
object key. To access an array element with a default value, use [`at`](at.md) together with a `#!cpp try`/`#!cpp
|
||||||
|
catch` block, or compare against [`size`](size.md) instead.
|
||||||
|
|
||||||
## Template parameters
|
## Template parameters
|
||||||
|
|
||||||
`KeyType`
|
`KeyType`
|
||||||
@@ -184,7 +192,9 @@ changes to any JSON value.
|
|||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
1. Added in version 1.0.0. Changed parameter `default_value` type from `const ValueType&` to `ValueType&&` in version 3.11.0.
|
1. Added in version 1.0.0. Changed parameter `default_value` type from `const ValueType&` to `ValueType&&` in version
|
||||||
|
3.11.0. Deleted overload for integral key types added in version 3.13.0 to reject such calls at compile time
|
||||||
|
instead of causing undefined behavior at runtime.
|
||||||
2. Added in version 3.11.0. Made `ValueType` the first template parameter in version 3.11.2.
|
2. Added in version 3.11.0. Made `ValueType` the first template parameter in version 3.11.2.
|
||||||
3. Added in version 2.0.2. Extended to work with arrays in version 3.13.0, including fixing an issue where resolving
|
3. Added in version 2.0.2. Extended to work with arrays in version 3.13.0, including fixing an issue where resolving
|
||||||
`ptr` through an array unexpectedly threw `out_of_range` instead of returning the resolved element (or
|
`ptr` through an array unexpectedly threw `out_of_range` instead of returning the resolved element (or
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ struct json_sax;
|
|||||||
|
|
||||||
This class describes the SAX interface used by [sax_parse](../basic_json/sax_parse.md). Each function is called in
|
This class describes the SAX interface used by [sax_parse](../basic_json/sax_parse.md). Each function is called in
|
||||||
different situations while the input is parsed. The boolean return value informs the parser whether to continue
|
different situations while the input is parsed. The boolean return value informs the parser whether to continue
|
||||||
processing the input.
|
processing the input; for [`parse_error`](parse_error.md), it decides whether to
|
||||||
|
[recover from the error](../../features/parsing/error_recovery.md).
|
||||||
|
|
||||||
## Template parameters
|
## Template parameters
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,14 @@ A parse error occurred.
|
|||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
Whether parsing should proceed (**must return `#!cpp false`**).
|
Whether to recover from the error:
|
||||||
|
|
||||||
|
- `#!cpp false` stops parsing.
|
||||||
|
- `#!cpp true` recovers from the error: the error is repaired and parsing continues. If that is not possible, which
|
||||||
|
happens in the binary formats when the end of the item with the error is unknown, the value read so far is completed
|
||||||
|
and parsing stops. See [error recovery](../../features/parsing/error_recovery.md) for how errors are repaired.
|
||||||
|
|
||||||
|
Either way, [`sax_parse`](../basic_json/sax_parse.md) returns `#!cpp false`.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
@@ -39,6 +46,22 @@ Whether parsing should proceed (**must return `#!cpp false`**).
|
|||||||
--8<-- "examples/sax_parse.output"
|
--8<-- "examples/sax_parse.output"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
The example below shows how a SAX parser recovers from errors.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/sax_parse__error_recovery.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```
|
||||||
|
--8<-- "examples/sax_parse__error_recovery.output"
|
||||||
|
```
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 3.2.0.
|
- Added in version 3.2.0.
|
||||||
|
- Returning `#!cpp true` recovers from the error since version 3.13.0; before, parsing stopped, but the result of
|
||||||
|
[`sax_parse`](../basic_json/sax_parse.md) could be wrong.
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ header. See also the [macro overview page](../../features/macros.md).
|
|||||||
- [**JSON_HAS_RANGES**](json_has_ranges.md) - control `std::ranges` support
|
- [**JSON_HAS_RANGES**](json_has_ranges.md) - control `std::ranges` support
|
||||||
- [**JSON_HAS_STD_FORMAT**](json_has_std_format.md) - control `std::format`/`std::formatter` support
|
- [**JSON_HAS_STD_FORMAT**](json_has_std_format.md) - control `std::format`/`std::formatter` support
|
||||||
- [**JSON_HAS_THREE_WAY_COMPARISON**](json_has_three_way_comparison.md) - control 3-way comparison support
|
- [**JSON_HAS_THREE_WAY_COMPARISON**](json_has_three_way_comparison.md) - control 3-way comparison support
|
||||||
|
- [**JSON_NO_AUTOMATIC_UDLS**](json_no_automatic_udls.md) - do not include the user-defined string literals (UDLs) automatically
|
||||||
- [**JSON_NO_IO**](json_no_io.md) - switch off functions relying on certain C++ I/O headers
|
- [**JSON_NO_IO**](json_no_io.md) - switch off functions relying on certain C++ I/O headers
|
||||||
- [**JSON_NO_THREAD_LOCAL**](json_no_thread_local.md) - switch off the use of `thread_local` storage
|
- [**JSON_NO_THREAD_LOCAL**](json_no_thread_local.md) - switch off the use of `thread_local` storage
|
||||||
- [**JSON_SKIP_UNSUPPORTED_COMPILER_CHECK**](json_skip_unsupported_compiler_check.md) - do not warn about unsupported compilers
|
- [**JSON_SKIP_UNSUPPORTED_COMPILER_CHECK**](json_skip_unsupported_compiler_check.md) - do not warn about unsupported compilers
|
||||||
@@ -52,6 +53,7 @@ header. See also the [macro overview page](../../features/macros.md).
|
|||||||
|
|
||||||
- [**JSON_BRACE_INIT_COPY_SEMANTICS**](json_brace_init_copy_semantics.md) - opt in to copy/move semantics for single-element brace initialization
|
- [**JSON_BRACE_INIT_COPY_SEMANTICS**](json_brace_init_copy_semantics.md) - opt in to copy/move semantics for single-element brace initialization
|
||||||
- [**JSON_DISABLE_ENUM_SERIALIZATION**](json_disable_enum_serialization.md) - switch off default serialization/deserialization functions for enums
|
- [**JSON_DISABLE_ENUM_SERIALIZATION**](json_disable_enum_serialization.md) - switch off default serialization/deserialization functions for enums
|
||||||
|
- [**JSON_DISABLE_TUPLE_REFERENCE_CONVERSION**](json_disable_tuple_reference_conversion.md) - switch off conversion from a one-element tuple of a JSON reference
|
||||||
- [**JSON_USE_IMPLICIT_CONVERSIONS**](json_use_implicit_conversions.md) - control implicit conversions
|
- [**JSON_USE_IMPLICIT_CONVERSIONS**](json_use_implicit_conversions.md) - control implicit conversions
|
||||||
|
|
||||||
## Comparison behavior
|
## Comparison behavior
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
# JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#define JSON_DISABLE_TUPLE_REFERENCE_CONVERSION /* value */
|
||||||
|
```
|
||||||
|
|
||||||
|
When defined to `1`, a `basic_json` value can no longer be constructed from a one-element `std::tuple` whose element is
|
||||||
|
a reference to that `basic_json` type, such as `std::tuple<json&>`, `std::tuple<const json&>`, or `std::tuple<json&&>`.
|
||||||
|
These are the tuples created by `std::forward_as_tuple(j)`.
|
||||||
|
|
||||||
|
## Default definition
|
||||||
|
|
||||||
|
The default value is `0` (disabled — existing behavior is preserved).
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#define JSON_DISABLE_TUPLE_REFERENCE_CONVERSION 0
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
!!! note "Background"
|
||||||
|
|
||||||
|
By default, `basic_json` can be constructed from any `std::tuple` whose elements can be converted to JSON; the result
|
||||||
|
is an array. This includes `std::tuple<json&>`, which becomes a one-element array.
|
||||||
|
|
||||||
|
`std::tuple` only converts another tuple element by element if its element type cannot be constructed from the whole
|
||||||
|
source tuple. Because `json` *can* be constructed from `std::tuple<json&>`, `std::tuple` instead converts the whole
|
||||||
|
tuple into a single `json` value. This has two surprising effects:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
json j = true;
|
||||||
|
|
||||||
|
// rejected by some standard libraries (e.g., libc++); with others, the
|
||||||
|
// reference binds to a temporary that is destroyed right away
|
||||||
|
std::tuple<const json&> t1(std::forward_as_tuple(j));
|
||||||
|
|
||||||
|
// compiles, but std::get<0>(t2) is [true], not true
|
||||||
|
std::tuple<json> t2(std::forward_as_tuple(j));
|
||||||
|
```
|
||||||
|
|
||||||
|
Enabling this macro removes the conversion, so both tuples are converted element by element: `std::get<0>(t1)`
|
||||||
|
refers to `j`, and `std::get<0>(t2)` is a copy of `j` (see [#2226](https://github.com/nlohmann/json/issues/2226)).
|
||||||
|
|
||||||
|
!!! warning "Opt-in only"
|
||||||
|
|
||||||
|
This macro must be defined **before** including `<nlohmann/json.hpp>`. Defining it after the include has no effect.
|
||||||
|
|
||||||
|
!!! note "Affected conversions"
|
||||||
|
|
||||||
|
Only one-element tuples holding a reference to the **same** `basic_json` type are affected. Constructing a JSON value
|
||||||
|
from them no longer compiles:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
json j = true;
|
||||||
|
json a = std::forward_as_tuple(j); // error with the macro enabled
|
||||||
|
json b = json::array({j}); // use this instead: [true]
|
||||||
|
```
|
||||||
|
|
||||||
|
Tuples holding a JSON value (`std::make_tuple(j)`), tuples with more than one element, and tuples holding references
|
||||||
|
to other types (including other `basic_json` specializations) are converted to arrays as before.
|
||||||
|
|
||||||
|
!!! hint "CMake option"
|
||||||
|
|
||||||
|
This behavior can also be controlled with the CMake option
|
||||||
|
[`JSON_DisableTupleReferenceConversion`](../../integration/cmake.md#json_disabletuplereferenceconversion)
|
||||||
|
(`OFF` by default) which defines `JSON_DISABLE_TUPLE_REFERENCE_CONVERSION` accordingly.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example "Default behavior (macro not defined)"
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
json j = true;
|
||||||
|
|
||||||
|
std::tuple<json> t(std::forward_as_tuple(j));
|
||||||
|
// std::get<0>(t) is [true] -- the whole tuple was converted
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
??? example "Conversion disabled (macro defined to 1)"
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#define JSON_DISABLE_TUPLE_REFERENCE_CONVERSION 1
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
json j = true;
|
||||||
|
|
||||||
|
std::tuple<json> t(std::forward_as_tuple(j));
|
||||||
|
// std::get<0>(t) is true -- a copy of j
|
||||||
|
|
||||||
|
std::tuple<const json&> r(std::forward_as_tuple(j));
|
||||||
|
// std::get<0>(r) refers to j
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [**basic_json(CompatibleType&&)**](../basic_json/basic_json.md) - the affected constructor
|
||||||
|
- [:simple-cmake: JSON_DisableTupleReferenceConversion](../../integration/cmake.md#json_disabletuplereferenceconversion) -
|
||||||
|
CMake option to control the macro
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# JSON_NO_AUTOMATIC_UDLS
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#define JSON_NO_AUTOMATIC_UDLS
|
||||||
|
```
|
||||||
|
|
||||||
|
When defined, `<nlohmann/json.hpp>` does not include `<nlohmann/json_literals.hpp>`, so the user-defined string
|
||||||
|
literals [`operator""_json`](../operator_literal_json.md) and
|
||||||
|
[`operator""_json_pointer`](../operator_literal_json_pointer.md) are not declared. Include
|
||||||
|
`<nlohmann/json_literals.hpp>` in the files that use them.
|
||||||
|
|
||||||
|
The literals are ordinary inline functions whose bodies call the parser, so every translation unit that includes them
|
||||||
|
instantiates the parser — even if it never parses anything itself. Defining `JSON_NO_AUTOMATIC_UDLS` for a whole project
|
||||||
|
avoids this cost in translation units that do not parse (e.g., ones that only define types and conversions or pass
|
||||||
|
`json` values around) and reduces their compile time.
|
||||||
|
|
||||||
|
## Default definition
|
||||||
|
|
||||||
|
By default, `#!cpp JSON_NO_AUTOMATIC_UDLS` is not defined, and `<nlohmann/json.hpp>` includes
|
||||||
|
`<nlohmann/json_literals.hpp>`.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#undef JSON_NO_AUTOMATIC_UDLS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
!!! info "Header `<nlohmann/json_literals.hpp>`"
|
||||||
|
|
||||||
|
The header includes `<nlohmann/json.hpp>` itself and places the literals according to
|
||||||
|
[`JSON_USE_GLOBAL_UDLS`](json_use_global_udls.md). It is part of the multi-header sources (`include/nlohmann`)
|
||||||
|
and of the single-header sources (`single_include/nlohmann`), next to `json.hpp`.
|
||||||
|
|
||||||
|
!!! info "C++ modules"
|
||||||
|
|
||||||
|
The `nlohmann.json` [module](../../features/modules.md) always exports the literals, regardless of this macro.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
The code below includes the library without the literals and adds them in a single translation unit.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// compiled with -DJSON_NO_AUTOMATIC_UDLS for the whole project
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
// this file uses the literals, so it includes them explicitly
|
||||||
|
#include <nlohmann/json_literals.hpp>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
auto j = R"({"foo": 42})"_json;
|
||||||
|
return j.at("/foo"_json_pointer) == 42 ? 0 : 1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Without the include of `<nlohmann/json_literals.hpp>`, the code would fail to compile.
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [`operator""_json`](../operator_literal_json.md)
|
||||||
|
- [`operator""_json_pointer`](../operator_literal_json_pointer.md)
|
||||||
|
- [`JSON_USE_GLOBAL_UDLS`](json_use_global_udls.md) - place user-defined string literals (UDLs) into the global namespace
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -16,7 +16,8 @@ byte is still not rejected:
|
|||||||
[`from_msgpack`](../basic_json/from_msgpack.md), [`from_ubjson`](../basic_json/from_ubjson.md)) are never affected: there, `0x00` is ordinary data.
|
[`from_msgpack`](../basic_json/from_msgpack.md), [`from_ubjson`](../basic_json/from_ubjson.md)) are never affected: there, `0x00` is ordinary data.
|
||||||
- A bare `const char*` pointer has no length of its own, so its length is still determined with `strlen()`. The first
|
- A bare `const char*` pointer has no length of its own, so its length is still determined with `strlen()`. The first
|
||||||
NUL byte therefore still marks the end of the input, and nothing after it is read.
|
NUL byte therefore still marks the end of the input, and nothing after it is read.
|
||||||
- One trailing `'\0'` at the end of a `char` array (e.g., a string literal) is trimmed; see the warning below.
|
- One trailing `'\0'` at the end of a `char`, `wchar_t`, `char16_t`, `char32_t`, or (C++20) `char8_t` array (e.g., a
|
||||||
|
string literal) is trimmed; see the warning below.
|
||||||
|
|
||||||
## Default definition
|
## Default definition
|
||||||
|
|
||||||
@@ -57,13 +58,15 @@ The default value is `0` (disabled — existing behavior is preserved).
|
|||||||
This macro must be defined **before** including `<nlohmann/json.hpp>`. Defining it after the include has no
|
This macro must be defined **before** including `<nlohmann/json.hpp>`. Defining it after the include has no
|
||||||
effect.
|
effect.
|
||||||
|
|
||||||
Enabling it also changes how a `char` array (including a string literal, e.g. `json::parse("123")`) is read: such
|
Enabling it also changes how an array of a text-literal element type (`char`, `wchar_t`, `char16_t`, `char32_t`,
|
||||||
an array normally carries a trailing `'\0'` contributed by the compiler, not by the source text. With this macro
|
or, since C++20, `char8_t` - including a string literal, e.g. `json::parse("123")` or `json::parse(L"123")`) is
|
||||||
enabled, that one trailing byte is trimmed if present so that parsing a string literal keeps working; every other
|
read: such an array normally carries a trailing `'\0'` contributed by the compiler, not by the source text. With
|
||||||
byte in the array - including any `'\0'` that is not the very last element - is read as real data and rejected
|
this macro enabled, that one trailing element is trimmed if present so that parsing a string literal keeps
|
||||||
like any other unexpected byte. Arrays of any other element type (`unsigned char`, `std::uint8_t`, ...), as used
|
working, for any of these character types; every other element in the array - including any `'\0'` that is not
|
||||||
for CBOR or MessagePack, are never affected by this trimming; their full extent - including a genuine trailing
|
the very last element - is read as real data and rejected like any other unexpected byte. Arrays of any other
|
||||||
`0x00` - is always preserved, in both states of this macro.
|
element type (`unsigned char`, `std::uint8_t`, ...), as used for CBOR or MessagePack, are never affected by this
|
||||||
|
trimming; their full extent - including a genuine trailing `0x00` - is always preserved, in both states of this
|
||||||
|
macro.
|
||||||
|
|
||||||
!!! note "ABI compatibility"
|
!!! note "ABI compatibility"
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ The default value is `1`.
|
|||||||
#define JSON_USE_GLOBAL_UDLS 1
|
#define JSON_USE_GLOBAL_UDLS 1
|
||||||
```
|
```
|
||||||
|
|
||||||
When the macro is not defined, the library will define it to its default value.
|
When the macro is not defined, the library behaves as if it were defined to its default value.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
@@ -32,6 +32,11 @@ When the macro is not defined, the library will define it to its default value.
|
|||||||
[`JSON_GlobalUDLs`](../../integration/cmake.md#json_globaludls) (`ON` by default) which defines
|
[`JSON_GlobalUDLs`](../../integration/cmake.md#json_globaludls) (`ON` by default) which defines
|
||||||
`JSON_USE_GLOBAL_UDLS` accordingly.
|
`JSON_USE_GLOBAL_UDLS` accordingly.
|
||||||
|
|
||||||
|
!!! info "Leaving out the literals"
|
||||||
|
|
||||||
|
If [`JSON_NO_AUTOMATIC_UDLS`](json_no_automatic_udls.md) is defined, the literals are only declared where
|
||||||
|
`<nlohmann/json_literals.hpp>` is included; this macro then applies to that header.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example "Example 1: Default behavior"
|
??? example "Example 1: Default behavior"
|
||||||
@@ -92,6 +97,7 @@ When the macro is not defined, the library will define it to its default value.
|
|||||||
|
|
||||||
- [`operator""_json`](../operator_literal_json.md)
|
- [`operator""_json`](../operator_literal_json.md)
|
||||||
- [`operator""_json_pointer`](../operator_literal_json_pointer.md)
|
- [`operator""_json_pointer`](../operator_literal_json_pointer.md)
|
||||||
|
- [`JSON_NO_AUTOMATIC_UDLS`](json_no_automatic_udls.md) - do not include the user-defined string literals automatically
|
||||||
- [:simple-cmake: JSON_GlobalUDLs](../../integration/cmake.md#json_globaludls) - CMake option to control the macro
|
- [:simple-cmake: JSON_GlobalUDLs](../../integration/cmake.md#json_globaludls) - CMake option to control the macro
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|||||||
@@ -18,9 +18,18 @@ Deserializes an input stream to a JSON value.
|
|||||||
|
|
||||||
the stream `i`
|
the stream `i`
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
Strong guarantee: if an exception is thrown, there are no changes in `j`.
|
||||||
|
|
||||||
## Exceptions
|
## Exceptions
|
||||||
|
|
||||||
- Throws [`parse_error.101`](../home/exceptions.md#jsonexceptionparse_error101) in case of an unexpected token.
|
- Throws [`parse_error.101`](../home/exceptions.md#jsonexceptionparse_error101) in case of an unexpected token, or if
|
||||||
|
`i` has no stream buffer (`#!cpp i.rdbuf() == nullptr`, for instance `#!cpp std::istream(nullptr)`).
|
||||||
|
- If reading from `i` reaches the end of the input and `eofbit` is part of `i`'s
|
||||||
|
[`exceptions()`](https://en.cppreference.com/w/cpp/io/basic_ios/exceptions) mask, the `std::ios_base::failure`
|
||||||
|
thrown by `i` itself propagates instead of a `parse_error`, the same as it would for the standard library's own
|
||||||
|
extraction operators.
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
@@ -118,3 +127,7 @@ being read.
|
|||||||
it as end of input; planned to become the default in version 4.0.0.
|
it as end of input; planned to become the default in version 4.0.0.
|
||||||
- `JSON_PRECISE_STREAM_POSITION` added in version 3.13.0 to optionally leave the character that terminates a number in
|
- `JSON_PRECISE_STREAM_POSITION` added in version 3.13.0 to optionally leave the character that terminates a number in
|
||||||
the stream; planned to become the default in version 4.0.0.
|
the stream; planned to become the default in version 4.0.0.
|
||||||
|
- Fixed a null pointer dereference for an `std::istream` without a stream buffer (now throws `parse_error.101`), and a
|
||||||
|
crash (`std::terminate`) when `i` has `eofbit` in its exception mask, in version 3.13.0.
|
||||||
|
- Changed to the strong exception safety guarantee in version 3.13.0: `j` is no longer left with a partially parsed
|
||||||
|
value if parsing throws.
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ using namespace nlohmann;
|
|||||||
```
|
```
|
||||||
|
|
||||||
This is suggested to ease migration to the next major version release of the library. See
|
This is suggested to ease migration to the next major version release of the library. See
|
||||||
[`JSON_USE_GLOBAL_UDLS`](macros/json_use_global_udls.md#notes) for details.
|
[`JSON_USE_GLOBAL_UDLS`](macros/json_use_global_udls.md#notes) for details. The operator is declared in header
|
||||||
|
`<nlohmann/json_literals.hpp>`, which `<nlohmann/json.hpp>` includes unless
|
||||||
|
[`JSON_NO_AUTOMATIC_UDLS`](macros/json_no_automatic_udls.md) is defined.
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
@@ -59,6 +61,8 @@ Linear.
|
|||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [Creating JSON values](../features/creating_values.md) - the article on creating JSON values
|
- [Creating JSON values](../features/creating_values.md) - the article on creating JSON values
|
||||||
|
- [JSON_NO_AUTOMATIC_UDLS](macros/json_no_automatic_udls.md) - do not include the user-defined string literals
|
||||||
|
automatically
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ using namespace nlohmann::literals::json_literals;
|
|||||||
using namespace nlohmann;
|
using namespace nlohmann;
|
||||||
```
|
```
|
||||||
This is suggested to ease migration to the next major version release of the library. See
|
This is suggested to ease migration to the next major version release of the library. See
|
||||||
[`JSON_USE_GLOBAL_UDLS`](macros/json_use_global_udls.md#notes) for details.
|
[`JSON_USE_GLOBAL_UDLS`](macros/json_use_global_udls.md#notes) for details. The operator is declared in header
|
||||||
|
`<nlohmann/json_literals.hpp>`, which `<nlohmann/json.hpp>` includes unless
|
||||||
|
[`JSON_NO_AUTOMATIC_UDLS`](macros/json_no_automatic_udls.md) is defined.
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
@@ -58,6 +60,8 @@ Linear.
|
|||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [json_pointer](json_pointer/index.md) - type to represent JSON Pointers
|
- [json_pointer](json_pointer/index.md) - type to represent JSON Pointers
|
||||||
|
- [JSON_NO_AUTOMATIC_UDLS](macros/json_no_automatic_udls.md) - do not include the user-defined string literals
|
||||||
|
automatically
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ A minimal map-like container that preserves insertion order for use within [`nlo
|
|||||||
The type uses a `std::vector` to store object elements. Therefore, adding elements can yield a reallocation in which
|
The type uses a `std::vector` to store object elements. Therefore, adding elements can yield a reallocation in which
|
||||||
case all iterators (including the `end()` iterator) and all references to the elements are invalidated.
|
case all iterators (including the `end()` iterator) and all references to the elements are invalidated.
|
||||||
|
|
||||||
|
When the storage grows, the keys are copied and the mapped values are moved to the new storage. A plain `std::vector`
|
||||||
|
would copy the whole elements instead, because their `#!cpp const` keys make them not nothrow move constructible; for
|
||||||
|
[`ordered_json`](ordered_json.md), this would be a deep copy of every nested value. The values are only copied if
|
||||||
|
`T` is not default constructible or not nothrow move assignable.
|
||||||
|
|
||||||
## Member types
|
## Member types
|
||||||
|
|
||||||
- **key_type** - key type (`Key`)
|
- **key_type** - key type (`Key`)
|
||||||
@@ -56,6 +61,11 @@ std::equal_to<> // since C++14
|
|||||||
- **find**
|
- **find**
|
||||||
- **insert**
|
- **insert**
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
**emplace**, **operator\[\]**, and **insert(value)** have the strong exception guarantee: if an exception is thrown (for
|
||||||
|
instance, because copying a key or allocating memory fails), the contents of the container are unchanged.
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
Because the elements are stored in a `std::vector` in insertion order, there is no index to look a key up by. Every
|
Because the elements are stored in a `std::vector` in insertion order, there is no index to look a key up by. Every
|
||||||
@@ -122,3 +132,4 @@ This differs from `#!cpp std::map`, where the same operations are O(log n).
|
|||||||
|
|
||||||
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](ordered_json.md).
|
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](ordered_json.md).
|
||||||
- Added **key_compare** member in version 3.11.0.
|
- Added **key_compare** member in version 3.11.0.
|
||||||
|
- Changed in version 3.13.0: growing the storage moves the mapped values instead of copying them.
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ Note: Some modern features (like C++20 ranges or filesystem support) may be disa
|
|||||||
| GNU 14.2.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
| GNU 14.2.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||||
| GNU 15.1.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
| GNU 15.1.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||||
| GNU 16.1.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
| GNU 16.1.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||||
| GNU 16.1.0 | arm64 | Linux 6.1.100 | Cirrus CI |
|
| GNU 16.1.0 | arm64 | Ubuntu 24.04 | GitHub |
|
||||||
| icpc (ICC) 2021.10.0 20230609 | x86_64 | Ubuntu 22.04 LTS | GitHub |
|
| icpc (ICC) 2021.10.0 20230609 | x86_64 | Ubuntu 22.04 LTS | GitHub |
|
||||||
| icpx (Intel oneAPI DPC++/C++) 2025.3.2 | x86_64 | Ubuntu 24.04 LTS | GitHub |
|
| icpx (Intel oneAPI DPC++/C++) 2025.3.2 | x86_64 | Ubuntu 24.04 LTS | GitHub |
|
||||||
| nvc++ (NVIDIA HPC SDK) 25.5-0 | x86_64 | Ubuntu 22.04 LTS | GitHub |
|
| nvc++ (NVIDIA HPC SDK) 25.5-0 | x86_64 | Ubuntu 22.04 LTS | GitHub |
|
||||||
@@ -132,7 +132,8 @@ Note: Some modern features (like C++20 ranges or filesystem support) may be disa
|
|||||||
[libstdc++](https://gcc.gnu.org/onlinedocs/libstdc++/) to detect subtle differences or incompatibilities.
|
[libstdc++](https://gcc.gnu.org/onlinedocs/libstdc++/) to detect subtle differences or incompatibilities.
|
||||||
- [x] The code checked with [Include What You Use (IWYU)](https://include-what-you-use.org) that all required standard
|
- [x] The code checked with [Include What You Use (IWYU)](https://include-what-you-use.org) that all required standard
|
||||||
headers are included.
|
headers are included.
|
||||||
- [x] On Windows, the library is compiled with `<Windows.h>` being included to detect and avoid common bugs.
|
- [x] On Windows, the library is compiled with `<Windows.h>` being included to detect and avoid common bugs (see
|
||||||
|
[`unit-windows_h.cpp`](https://github.com/nlohmann/json/blob/develop/tests/src/unit-windows_h.cpp)).
|
||||||
- [x] The library is compiled with exceptions disabled to support alternative means of error handling.
|
- [x] The library is compiled with exceptions disabled to support alternative means of error handling.
|
||||||
|
|
||||||
## Stable public API
|
## Stable public API
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
<a target="_blank" href="https://wandbox.org/permlink/hUJYo1HWmfTBLMGn"><b>online</b></a>
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<a target="_blank" href="https://wandbox.org/permlink/AWbpa8e1xRV3y4MM"><b>online</b></a>
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
// a SAX parser that creates a JSON value like json::parse does, but that
|
||||||
|
// recovers from parse errors instead of stopping at the first one
|
||||||
|
class recovering_parser : public nlohmann::detail::json_sax_dom_parser<json>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit recovering_parser(json& result)
|
||||||
|
: nlohmann::detail::json_sax_dom_parser<json>(result, false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool parse_error(std::size_t position,
|
||||||
|
const std::string& /*last_token*/,
|
||||||
|
const json::exception& ex)
|
||||||
|
{
|
||||||
|
std::cout << "byte " << position << ": " << ex.what() << '\n';
|
||||||
|
|
||||||
|
// repair the input and continue
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// JSON text with several mistakes that ends too early
|
||||||
|
const std::string text = R"({
|
||||||
|
"name": "Hello World",
|
||||||
|
"tags": ["a" "b",],
|
||||||
|
"valid": tru,
|
||||||
|
"size": 1.,
|
||||||
|
"nested": {"x": 1)";
|
||||||
|
|
||||||
|
json result;
|
||||||
|
recovering_parser sax(result);
|
||||||
|
const bool valid = json::sax_parse(text, &sax);
|
||||||
|
|
||||||
|
std::cout << "\nvalid JSON: " << std::boolalpha << valid << '\n'
|
||||||
|
<< std::setw(4) << result << std::endl;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
byte 49: [json.exception.parse_error.101] parse error at line 3, column 20: syntax error while parsing array - unexpected string literal; expected ']'
|
||||||
|
byte 51: [json.exception.parse_error.101] parse error at line 3, column 22: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal
|
||||||
|
byte 70: [json.exception.parse_error.101] parse error at line 4, column 17: syntax error while parsing value - invalid literal; last read: '"valid": tru,'
|
||||||
|
byte 86: [json.exception.parse_error.101] parse error at line 5, column 15: syntax error while parsing value - invalid number; expected digit after '.'; last read: '1.,'
|
||||||
|
byte 109: [json.exception.parse_error.101] parse error at line 6, column 22: syntax error while parsing object - unexpected end of input; expected '}'
|
||||||
|
|
||||||
|
valid JSON: false
|
||||||
|
{
|
||||||
|
"name": "Hello World",
|
||||||
|
"nested": {
|
||||||
|
"x": 1
|
||||||
|
},
|
||||||
|
"size": 1,
|
||||||
|
"tags": [
|
||||||
|
"a",
|
||||||
|
"b"
|
||||||
|
],
|
||||||
|
"valid": null
|
||||||
|
}
|
||||||
@@ -61,7 +61,7 @@ The library uses the following mapping from JSON values types to BJData types ac
|
|||||||
|
|
||||||
The following values can **not** be converted to a BJData value:
|
The following values can **not** be converted to a BJData value:
|
||||||
|
|
||||||
- strings with more than 18446744073709551615 bytes, i.e., $2^{64}-1$ bytes (theoretical)
|
- strings with more than 18446744073709551615 bytes, i.e., 2<sup>64</sup>-1 bytes (theoretical)
|
||||||
|
|
||||||
!!! info "Unused BJData markers"
|
!!! info "Unused BJData markers"
|
||||||
|
|
||||||
|
|||||||
@@ -168,9 +168,9 @@ The library maps CBOR types to JSON value types as follows:
|
|||||||
!!! warning "Negative integer overflow"
|
!!! warning "Negative integer overflow"
|
||||||
|
|
||||||
CBOR negative integers (major type 1) are decoded as `-1 - n`. If the encoded magnitude `n` is too large for the
|
CBOR negative integers (major type 1) are decoded as `-1 - n`. If the encoded magnitude `n` is too large for the
|
||||||
result to fit into `number_integer_t` (`std::int64_t` by default), the result is stored as `number_float_t`, like
|
result to fit into `number_integer_t` (`std::int64_t` by default), parsing fails with a
|
||||||
a too small integer in JSON text. For example, `-18446744073709551616` (`0x3B` followed by eight `0xFF` bytes) is
|
[`parse_error.112`](../../home/exceptions.md#jsonexceptionparse_error112) exception rather than overflowing
|
||||||
stored as `-1.8446744073709552e+19`.
|
silently.
|
||||||
|
|
||||||
!!! warning "Object keys"
|
!!! warning "Object keys"
|
||||||
|
|
||||||
|
|||||||
@@ -83,6 +83,22 @@ When defined, default parse and serialize functions for enums are excluded and h
|
|||||||
|
|
||||||
See [full documentation of `JSON_DISABLE_ENUM_SERIALIZATION`](../api/macros/json_disable_enum_serialization.md).
|
See [full documentation of `JSON_DISABLE_ENUM_SERIALIZATION`](../api/macros/json_disable_enum_serialization.md).
|
||||||
|
|
||||||
|
## `JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`
|
||||||
|
|
||||||
|
When defined to `1`, a JSON value can no longer be created from a one-element `std::tuple` holding a reference to a JSON
|
||||||
|
value, such as the result of `std::forward_as_tuple(j)`. This lets `std::tuple` convert such tuples element-wise.
|
||||||
|
|
||||||
|
See [full documentation of `JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`](../api/macros/json_disable_tuple_reference_conversion.md).
|
||||||
|
|
||||||
|
## `JSON_NO_AUTOMATIC_UDLS`
|
||||||
|
|
||||||
|
When defined, `<nlohmann/json.hpp>` does not include `<nlohmann/json_literals.hpp>` with the user-defined string literals
|
||||||
|
`operator""_json` and `operator""_json_pointer`. This reduces the compile time of translation units that do not use
|
||||||
|
them, because the literals instantiate the parser in every translation unit that includes them. Include
|
||||||
|
`<nlohmann/json_literals.hpp>` where the literals are needed.
|
||||||
|
|
||||||
|
See [full documentation of `JSON_NO_AUTOMATIC_UDLS`](../api/macros/json_no_automatic_udls.md).
|
||||||
|
|
||||||
## `JSON_NO_IO`
|
## `JSON_NO_IO`
|
||||||
|
|
||||||
When defined, headers `<cstdio>`, `<ios>`, `<iosfwd>`, `<istream>`, and `<ostream>` are not included and parse functions
|
When defined, headers `<cstdio>`, `<ios>`, `<iosfwd>`, `<istream>`, and `<ostream>` are not included and parse functions
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ Only the following symbols are exported from `nlohmann.json`:
|
|||||||
- `nlohmann::literals::json_literals::operator""_json`
|
- `nlohmann::literals::json_literals::operator""_json`
|
||||||
- `nlohmann::literals::json_literals::operator""_json_pointer`
|
- `nlohmann::literals::json_literals::operator""_json_pointer`
|
||||||
|
|
||||||
|
The module always exports the two user-defined string literals, even if
|
||||||
|
[`JSON_NO_AUTOMATIC_UDLS`](../api/macros/json_no_automatic_udls.md) is defined when building it.
|
||||||
|
|
||||||
Additionally, the following `nlohmann::detail` symbols are exported, solely to work around an MSVC compilation issue
|
Additionally, the following `nlohmann::detail` symbols are exported, solely to work around an MSVC compilation issue
|
||||||
([#3970](https://github.com/nlohmann/json/issues/3970)). They are implementation details, not part of the public API,
|
([#3970](https://github.com/nlohmann/json/issues/3970)). They are implementation details, not part of the public API,
|
||||||
and should not be used directly:
|
and should not be used directly:
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
# Error Recovery
|
||||||
|
|
||||||
|
By default, parsing stops at the first error. With the [SAX interface](sax_interface.md), you can instead ask the
|
||||||
|
parser to *recover*: to repair the error and continue, so that you get as much as possible out of malformed input, for
|
||||||
|
instance a file that was cut off, JSON edited by hand, or the output of a language model.
|
||||||
|
|
||||||
|
## Recovering from errors
|
||||||
|
|
||||||
|
The SAX parser's [`parse_error`](../../api/json_sax/parse_error.md) function is called for every error. Its return value
|
||||||
|
decides what happens next:
|
||||||
|
|
||||||
|
- `#!cpp false` stops parsing. This is what the SAX parsers of the library do, so [`parse`](../../api/basic_json/parse.md)
|
||||||
|
and [`accept`](../../api/basic_json/accept.md) never recover.
|
||||||
|
- `#!cpp true` repairs the error and continues parsing.
|
||||||
|
|
||||||
|
When recovering, the SAX parser still receives well-formed events: every `start_object` or `start_array` is followed by
|
||||||
|
the matching `end_object` or `end_array`, and every `key` is followed by exactly one value. A SAX parser that creates a
|
||||||
|
JSON value, such as the one in the example below, therefore gets a complete value. Parsing always ends, and
|
||||||
|
[`sax_parse`](../../api/basic_json/sax_parse.md) returns `#!cpp false` for input that is not valid JSON, even if every
|
||||||
|
error was repaired. Each token is reported at most once, and the SAX parser can stop at any error by returning
|
||||||
|
`#!cpp false`.
|
||||||
|
|
||||||
|
!!! example
|
||||||
|
|
||||||
|
The example below derives a SAX parser from the library's parser for `json` values (`json_sax_dom_parser`),
|
||||||
|
and recovers from all errors.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/sax_parse__error_recovery.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```
|
||||||
|
--8<-- "examples/sax_parse__error_recovery.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## How errors are repaired
|
||||||
|
|
||||||
|
Each error is repaired with the smallest local edit: a missing separator is inserted, a stray token is removed, what can
|
||||||
|
be read of a broken string or number is kept, and a value that cannot be read at all becomes `#!json null`.
|
||||||
|
|
||||||
|
| Mistake | Repair | Example | Result |
|
||||||
|
|---------------------------|--------------------------------------------------------------------------------|------------------------------------------|----------------------------|
|
||||||
|
| missing `,` or `:` | inserted | `#!json [1 2]`, `#!json {"a" 1}` | `[1,2]`, `{"a":1}` |
|
||||||
|
| missing value | `#!json null` for an object key or between commas in an array | `#!json {"a":}`, `#!json [1,,2]` | `{"a":null}`, `[1,null,2]` |
|
||||||
|
| trailing comma | removed | `#!json [1,2,]` | `[1,2]` |
|
||||||
|
| broken string | invalid escapes and bytes are replaced (see below); a line break ends the string | `#!json ["a\qb"]` | `["aqb"]` |
|
||||||
|
| broken number | the longest valid beginning is kept | `#!json [1., 2e+]` | `[1,2]` |
|
||||||
|
| unreadable value | `#!json null` | `#!json [1, NaN, tru]` | `[1,null,null]` |
|
||||||
|
| number too large | passed as infinity, together with its text | `#!json [1e999]` | infinity (see below) |
|
||||||
|
| stray `:` | removed | `#!json ["a":1]` | `["a",1]` |
|
||||||
|
| member without a key | skipped up to the next `,` or `}` | `#!json {1:2, "b":3}` | `{"b":3}` |
|
||||||
|
| wrong closing bracket | closes the innermost array or object | `#!json {"a":[1,2}, "b":3}` | `{"a":[1,2],"b":3}` |
|
||||||
|
| input ends too early | all open arrays and objects are closed | `#!json {"a":[1,2` | `{"a":[1,2]}` |
|
||||||
|
| text before the value | skipped | `#!json )]}'{"a":1}` | `{"a":1}` |
|
||||||
|
|
||||||
|
In a string, an unknown escape like `\q` stands for the escaped character (`q`), as in JavaScript. An invalid `\u`
|
||||||
|
escape, a lone surrogate, and ill-formed UTF-8 are each replaced by U+FFFD (REPLACEMENT CHARACTER), and control
|
||||||
|
characters are kept. A string without its closing quote ends at the next line break or at the end of the input.
|
||||||
|
|
||||||
|
The input after the top-level value is not repaired: as without recovery, it is reported as an error, and parsing stops.
|
||||||
|
|
||||||
|
## Binary formats
|
||||||
|
|
||||||
|
The binary formats ([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),
|
||||||
|
and [UBJSON](../binary_formats/ubjson.md)) have no delimiters to find the next value by. So what can be repaired depends
|
||||||
|
on whether the end of the item with the error is known, a distinction that
|
||||||
|
[RFC 8949, Section 5.3](https://www.rfc-editor.org/rfc/rfc8949.html#section-5.3) makes for CBOR, too.
|
||||||
|
|
||||||
|
If the item is complete, but cannot be passed on as it is, it is replaced, and parsing continues after it:
|
||||||
|
|
||||||
|
| Mistake | Formats | Repair |
|
||||||
|
|---------------------------------------------------------------------|-----------------------------------------|-------------------------------------------------------------------------|
|
||||||
|
| tag | CBOR | ignored |
|
||||||
|
| simple value other than `false`, `true`, and `null`, like undefined | CBOR | `#!json null` |
|
||||||
|
| negative integer below the range of `number_integer_t` | CBOR | the nearest floating-point number |
|
||||||
|
| string that is not valid UTF-8 | BJData, BSON, CBOR, MessagePack, UBJSON | each ill-formed sequence becomes U+FFFD |
|
||||||
|
| character (`C`) that is not ASCII | BJData, UBJSON | U+FFFD |
|
||||||
|
| invalid high-precision number (`H`) | BJData, UBJSON | the longest valid beginning is kept, as for JSON text, or `#!json null` |
|
||||||
|
| high-precision number too large | BJData, UBJSON | passed as infinity, together with its text |
|
||||||
|
| object key that is not a string | BON8, CBOR, MessagePack | the member is skipped |
|
||||||
|
| element of a type the library does not read, like ObjectId or date | BSON | `#!json null` |
|
||||||
|
| string without its terminator | BSON | kept |
|
||||||
|
| document whose size does not match its content | BSON | kept |
|
||||||
|
|
||||||
|
CBOR tags and simple values are repaired as [RFC 8949, Section 6.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-6.1)
|
||||||
|
suggests for converting CBOR to JSON. Note that [`sax_parse`](../../api/basic_json/sax_parse.md) has no parameter for
|
||||||
|
CBOR tags, so every tag is an error there; when recovering, tags are ignored like with
|
||||||
|
[`cbor_tag_handler_t::ignore`](../../api/basic_json/cbor_tag_handler_t.md).
|
||||||
|
|
||||||
|
After any other error, the end of the item is unknown: the input ended, a byte is not a valid type marker, or a size
|
||||||
|
cannot be right. Parsing then stops, and the value read so far is completed: a key that waits for its value gets
|
||||||
|
`#!json null`, and all open arrays and objects are closed. This keeps everything before the error of an input that was
|
||||||
|
cut off. The exception is BSON, which stores the size of every document: an element whose end is unknown gets
|
||||||
|
`#!json null`, the rest of its document is skipped, and parsing continues after the document.
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
|
||||||
|
- A repair is a guess. For example, `#!json {"a" "b": 1}` could be meant as `#!json {"a": "b"}` or as
|
||||||
|
`#!json {"a": null, "b": 1}`; it is repaired to the former. Treat recovered values as a best effort, and check the
|
||||||
|
reported errors.
|
||||||
|
- A closing bracket always closes the innermost array or object. If a bracket is missing rather than wrong, the
|
||||||
|
repair differs from the intention: `#!json {"a": {"b": [1, 2}, "c": 3}` is repaired to
|
||||||
|
`#!json {"a": {"b": [1, 2], "c": 3}}`, although `#!json {"a": {"b": [1, 2]}, "c": 3}` may have been meant.
|
||||||
|
- Keys without quotes, and strings in single quotes, are not supported; such members are skipped.
|
||||||
|
- In the binary formats, a member that is skipped because its key is not a string is lost, and so are the elements of a
|
||||||
|
BSON document after one whose end is unknown.
|
||||||
|
- A number that is too large for `number_float_t` is passed as positive or negative infinity. The SAX parser's
|
||||||
|
`number_float` also gets the number's text, but a JSON value cannot store it, and
|
||||||
|
[`dump`](../../api/basic_json/dump.md) serializes infinity as `#!json null`.
|
||||||
|
- When parsing is not strict (see [`sax_parse`](../../api/basic_json/sax_parse.md)), a repair may read parts of the
|
||||||
|
input after the value, for instance of the next value in a stream of concatenated values.
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [SAX interface](sax_interface.md) - implement a custom SAX handler
|
||||||
|
- [`parse_error`](../../api/json_sax/parse_error.md) - the SAX event for parse errors
|
||||||
|
- [`sax_parse`](../../api/basic_json/sax_parse.md) - generate SAX events
|
||||||
|
- [parsing and exceptions](parse_exceptions.md) - control error handling
|
||||||
@@ -65,7 +65,7 @@ You can influence a DOM parse without switching to the SAX interface by passing
|
|||||||
When the input is not valid JSON, the `parse` function throws an exception by default. If exceptions are undesired or
|
When the input is not valid JSON, the `parse` function throws an exception by default. If exceptions are undesired or
|
||||||
unavailable, the parser can instead return a discarded value, or [`accept`](../../api/basic_json/accept.md) can be used
|
unavailable, the parser can instead return a discarded value, or [`accept`](../../api/basic_json/accept.md) can be used
|
||||||
to only check whether an input is valid JSON. See [parsing and exceptions](parse_exceptions.md) for the available
|
to only check whether an input is valid JSON. See [parsing and exceptions](parse_exceptions.md) for the available
|
||||||
options.
|
options. To get as much as possible out of malformed input, a SAX parser can [recover from errors](error_recovery.md).
|
||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
@@ -76,3 +76,4 @@ options.
|
|||||||
- [parser callbacks](parser_callbacks.md) - influence the parsing by a callback function
|
- [parser callbacks](parser_callbacks.md) - influence the parsing by a callback function
|
||||||
- [SAX interface](sax_interface.md) - implement a custom SAX handler
|
- [SAX interface](sax_interface.md) - implement a custom SAX handler
|
||||||
- [parsing and exceptions](parse_exceptions.md) - control error handling
|
- [parsing and exceptions](parse_exceptions.md) - control error handling
|
||||||
|
- [error recovery](error_recovery.md) - get as much as possible out of malformed input
|
||||||
|
|||||||
@@ -64,7 +64,8 @@ bool parse_error(std::size_t position,
|
|||||||
const json::exception& ex);
|
const json::exception& ex);
|
||||||
```
|
```
|
||||||
|
|
||||||
The return value indicates whether the parsing should continue, so the function should usually return `#!cpp false`.
|
The return value decides whether to stop parsing (`#!cpp false`) or to repair the error and continue
|
||||||
|
(`#!cpp true`); see [error recovery](error_recovery.md) for the latter.
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,8 @@ bool key(string_t& val);
|
|||||||
bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex);
|
bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex);
|
||||||
```
|
```
|
||||||
|
|
||||||
The return value of each function determines whether parsing should proceed.
|
The return value of each function determines whether parsing should proceed. For `parse_error`, returning
|
||||||
|
`#!cpp true` [recovers from the error](error_recovery.md).
|
||||||
|
|
||||||
To implement your own SAX handler, proceed as follows:
|
To implement your own SAX handler, proceed as follows:
|
||||||
|
|
||||||
@@ -68,7 +69,7 @@ To implement your own SAX handler, proceed as follows:
|
|||||||
2. Create an object of your SAX interface class, e.g. `my_sax`.
|
2. Create an object of your SAX interface class, e.g. `my_sax`.
|
||||||
3. Call `#!cpp bool json::sax_parse(input, &my_sax);` where the first parameter can be any input like a string or an input stream and the second parameter is a pointer to your SAX interface.
|
3. Call `#!cpp bool json::sax_parse(input, &my_sax);` where the first parameter can be any input like a string or an input stream and the second parameter is a pointer to your SAX interface.
|
||||||
|
|
||||||
Note the `sax_parse` function only returns a `#!cpp bool` indicating the result of the last executed SAX event. It does not return `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error - it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file `json_sax.hpp`.
|
Note the `sax_parse` function only returns a `#!cpp bool` indicating whether the input was parsed without errors and no SAX event returned `#!cpp false`. It does not return `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error - it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file `json_sax.hpp`.
|
||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ When the default type is used, the maximal unsigned integer number that can be s
|
|||||||
|
|
||||||
[RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
|
[RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
|
||||||
|
|
||||||
> Note that when such software is used, numbers that are integers and are in the range $[-2^{53}+1, 2^{53}-1]$ are interoperable in the sense that implementations will agree exactly on their numeric values.
|
> Note that when such software is used, numbers that are integers and are in the range [-2<sup>53</sup>+1, 2<sup>53</sup>-1] are interoperable in the sense that implementations will agree exactly on their numeric values.
|
||||||
|
|
||||||
As this range is a subrange of the exactly supported range [`INT64_MIN`, `INT64_MAX`], this class's integer type is interoperable.
|
As this range is a subrange of the exactly supported range [`INT64_MIN`, `INT64_MAX`], this class's integer type is interoperable.
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ On number interoperability, the following remarks are made:
|
|||||||
for numeric magnitude and precision than is widely available.
|
for numeric magnitude and precision than is widely available.
|
||||||
|
|
||||||
Note that when such software is used, numbers that are integers and
|
Note that when such software is used, numbers that are integers and
|
||||||
are in the range $[-2^{53}+1, 2^{53}-1]$ are interoperable in the
|
are in the range [-2<sup>53</sup>+1, 2<sup>53</sup>-1] are interoperable in the
|
||||||
sense that implementations will agree exactly on their numeric
|
sense that implementations will agree exactly on their numeric
|
||||||
values.
|
values.
|
||||||
|
|
||||||
@@ -95,9 +95,9 @@ This is the same behavior as the code `#!c double x = 3.141592653589793238462643
|
|||||||
|
|
||||||
!!! success "Interoperability"
|
!!! success "Interoperability"
|
||||||
|
|
||||||
- The library is interoperable with respect to the specification, because its supported range $[-2^{63}, 2^{64}-1]$ is
|
- The library is interoperable with respect to the specification, because its supported range [-2<sup>63</sup>, 2<sup>64</sup>-1] is
|
||||||
larger than the described range $[-2^{53}+1, 2^{53}-1]$.
|
larger than the described range [-2<sup>53</sup>+1, 2<sup>53</sup>-1].
|
||||||
- All integers outside the range $[-2^{63}, 2^{64}-1]$, as well as floating-point numbers are stored as `double`.
|
- All integers outside the range [-2<sup>63</sup>, 2<sup>64</sup>-1], as well as floating-point numbers are stored as `double`.
|
||||||
This also concurs with the specification above.
|
This also concurs with the specification above.
|
||||||
|
|
||||||
### Zeros
|
### Zeros
|
||||||
|
|||||||
@@ -389,6 +389,7 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
|
|||||||
| Functionality | Additional requirement |
|
| Functionality | Additional requirement |
|
||||||
|-----------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|-----------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| [`diff`](../../api/basic_json/diff.md), [`items`](../../api/basic_json/items.md), [`std::hash`](../../api/basic_json/std_hash.md) | conversion of a `#!cpp std::size_t` to `StringType`: either assignability from the result of `#!cpp std::to_string`, or an ADL overload `#!cpp void int_to_string(StringType&, std::size_t)` |
|
| [`diff`](../../api/basic_json/diff.md), [`items`](../../api/basic_json/items.md), [`std::hash`](../../api/basic_json/std_hash.md) | conversion of a `#!cpp std::size_t` to `StringType`: either assignability from the result of `#!cpp std::to_string`, or an ADL overload `#!cpp void int_to_string(StringType&, std::size_t)` |
|
||||||
|
| [`operator/(std::size_t)`](../../api/json_pointer/operator_slash.md) | the same conversion of a `#!cpp std::size_t` to `StringType` as `diff`, `items`, and `std::hash` above |
|
||||||
| [`std::hash<basic_json>`](../../api/basic_json/std_hash.md) | additionally a specialization of `#!cpp std::hash<StringType>` |
|
| [`std::hash<basic_json>`](../../api/basic_json/std_hash.md) | additionally a specialization of `#!cpp std::hash<StringType>` |
|
||||||
| [`to_bson`](../../api/basic_json/to_bson.md) | `find(value_type)` and `npos` |
|
| [`to_bson`](../../api/basic_json/to_bson.md) | `find(value_type)` and `npos` |
|
||||||
| [`parse`](../../api/basic_json/parse.md) from a `string_t` | the input adapters must accept it; otherwise pass a character range |
|
| [`parse`](../../api/basic_json/parse.md) from a `string_t` | the input adapters must accept it; otherwise pass a character range |
|
||||||
|
|||||||
@@ -331,6 +331,9 @@ An unexpected byte was read in a [binary format](../features/binary_formats/inde
|
|||||||
[json.exception.parse_error.112] parse error at byte 15: syntax error while parsing BSON binary: byte array length cannot be negative, is -1
|
[json.exception.parse_error.112] parse error at byte 15: syntax error while parsing BSON binary: byte array length cannot be negative, is -1
|
||||||
```
|
```
|
||||||
```
|
```
|
||||||
|
[json.exception.parse_error.112] parse error at byte 9: syntax error while parsing CBOR value: negative integer overflow
|
||||||
|
```
|
||||||
|
```
|
||||||
[json.exception.parse_error.112] parse error at byte 5: syntax error while parsing BSON document: document size 6 does not match the number of bytes read (5)
|
[json.exception.parse_error.112] parse error at byte 5: syntax error while parsing BSON document: document size 6 does not match the number of bytes read (5)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -851,18 +854,13 @@ The JSON Patch operations 'remove' and 'add' cannot be applied to the root eleme
|
|||||||
|
|
||||||
### json.exception.out_of_range.406
|
### json.exception.out_of_range.406
|
||||||
|
|
||||||
A parsed number could not be stored without changing it to NaN or INF. For the binary formats, this happens when a
|
A parsed number could not be stored as without changing it to NaN or INF.
|
||||||
finite floating-point number does not fit into [`number_float_t`](../api/basic_json/number_float_t.md), for example a
|
|
||||||
double-precision number when `number_float_t` is `#!cpp float`.
|
|
||||||
|
|
||||||
!!! failure "Example messages"
|
!!! failure "Example message"
|
||||||
|
|
||||||
```
|
```
|
||||||
number overflow parsing '10E1000'
|
number overflow parsing '10E1000'
|
||||||
```
|
```
|
||||||
```
|
|
||||||
[json.exception.out_of_range.406] syntax error while parsing CBOR value: number overflow
|
|
||||||
```
|
|
||||||
|
|
||||||
### json.exception.out_of_range.407
|
### json.exception.out_of_range.407
|
||||||
|
|
||||||
|
|||||||
@@ -19,3 +19,5 @@ The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is licensed und
|
|||||||
The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2009 [Florian Loitsch](https://florian.loitsch.com/)
|
The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2009 [Florian Loitsch](https://florian.loitsch.com/)
|
||||||
|
|
||||||
The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/).
|
The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||||
|
|
||||||
|
The class contains an adapted version of the Eisel-Lemire algorithm and its table of powers of five from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
||||||
|
|||||||
@@ -169,6 +169,12 @@ Enable position diagnostics by defining macro [`JSON_DIAGNOSTIC_POSITIONS`](../a
|
|||||||
Disable default `enum` serialization by defining the macro
|
Disable default `enum` serialization by defining the macro
|
||||||
[`JSON_DISABLE_ENUM_SERIALIZATION`](../api/macros/json_disable_enum_serialization.md). This option is `OFF` by default.
|
[`JSON_DISABLE_ENUM_SERIALIZATION`](../api/macros/json_disable_enum_serialization.md). This option is `OFF` by default.
|
||||||
|
|
||||||
|
### `JSON_DisableTupleReferenceConversion`
|
||||||
|
|
||||||
|
Disable the conversion from a one-element `std::tuple` holding a reference to a JSON value by defining the macro
|
||||||
|
[`JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`](../api/macros/json_disable_tuple_reference_conversion.md). This option is
|
||||||
|
`OFF` by default.
|
||||||
|
|
||||||
### `JSON_FastTests`
|
### `JSON_FastTests`
|
||||||
|
|
||||||
Skip expensive/slow test suites. This option is `OFF` by default. Depends on `JSON_BuildTests`.
|
Skip expensive/slow test suites. This option is `OFF` by default. Depends on `JSON_BuildTests`.
|
||||||
|
|||||||
@@ -15,4 +15,7 @@ Clang).
|
|||||||
|
|
||||||
You can further use file
|
You can further use file
|
||||||
[`single_include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_fwd.hpp)
|
[`single_include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_fwd.hpp)
|
||||||
for forward declarations.
|
for forward declarations, and file
|
||||||
|
[`single_include/nlohmann/json_literals.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_literals.hpp)
|
||||||
|
for the user-defined string literals if you define
|
||||||
|
[`JSON_NO_AUTOMATIC_UDLS`](../api/macros/json_no_automatic_udls.md).
|
||||||
|
|||||||
@@ -678,11 +678,11 @@ to install the [nlohmann-json](https://ports.macports.org/port/nlohmann-json/) p
|
|||||||
1. Create the following files:
|
1. Create the following files:
|
||||||
|
|
||||||
```cpp title="example.cpp"
|
```cpp title="example.cpp"
|
||||||
--8<-- "integration/homebrew/example.cpp"
|
--8<-- "integration/macports/example.cpp"
|
||||||
```
|
```
|
||||||
|
|
||||||
```cmake title="CMakeLists.txt"
|
```cmake title="CMakeLists.txt"
|
||||||
--8<-- "integration/homebrew/CMakeLists.txt"
|
--8<-- "integration/macports/CMakeLists.txt"
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Install the package:
|
2. Install the package:
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ nav:
|
|||||||
- features/object_order.md
|
- features/object_order.md
|
||||||
- Parsing:
|
- Parsing:
|
||||||
- features/parsing/index.md
|
- features/parsing/index.md
|
||||||
|
- features/parsing/error_recovery.md
|
||||||
- features/parsing/json_lines.md
|
- features/parsing/json_lines.md
|
||||||
- features/parsing/parse_exceptions.md
|
- features/parsing/parse_exceptions.md
|
||||||
- features/parsing/parser_callbacks.md
|
- features/parsing/parser_callbacks.md
|
||||||
@@ -287,12 +288,14 @@ nav:
|
|||||||
- 'JSON_DIAGNOSTICS': api/macros/json_diagnostics.md
|
- 'JSON_DIAGNOSTICS': api/macros/json_diagnostics.md
|
||||||
- 'JSON_DIAGNOSTIC_POSITIONS': api/macros/json_diagnostic_positions.md
|
- 'JSON_DIAGNOSTIC_POSITIONS': api/macros/json_diagnostic_positions.md
|
||||||
- 'JSON_DISABLE_ENUM_SERIALIZATION': api/macros/json_disable_enum_serialization.md
|
- 'JSON_DISABLE_ENUM_SERIALIZATION': api/macros/json_disable_enum_serialization.md
|
||||||
|
- 'JSON_DISABLE_TUPLE_REFERENCE_CONVERSION': api/macros/json_disable_tuple_reference_conversion.md
|
||||||
- 'JSON_HAS_CPP_11, JSON_HAS_CPP_14, JSON_HAS_CPP_17, JSON_HAS_CPP_20': api/macros/json_has_cpp_11.md
|
- 'JSON_HAS_CPP_11, JSON_HAS_CPP_14, JSON_HAS_CPP_17, JSON_HAS_CPP_20': api/macros/json_has_cpp_11.md
|
||||||
- 'JSON_HAS_EXPERIMENTAL_FILESYSTEM, JSON_HAS_FILESYSTEM': api/macros/json_has_filesystem.md
|
- 'JSON_HAS_EXPERIMENTAL_FILESYSTEM, JSON_HAS_FILESYSTEM': api/macros/json_has_filesystem.md
|
||||||
- 'JSON_HAS_RANGES': api/macros/json_has_ranges.md
|
- 'JSON_HAS_RANGES': api/macros/json_has_ranges.md
|
||||||
- 'JSON_HAS_STATIC_RTTI': api/macros/json_has_static_rtti.md
|
- 'JSON_HAS_STATIC_RTTI': api/macros/json_has_static_rtti.md
|
||||||
- 'JSON_HAS_STD_FORMAT': api/macros/json_has_std_format.md
|
- 'JSON_HAS_STD_FORMAT': api/macros/json_has_std_format.md
|
||||||
- 'JSON_HAS_THREE_WAY_COMPARISON': api/macros/json_has_three_way_comparison.md
|
- 'JSON_HAS_THREE_WAY_COMPARISON': api/macros/json_has_three_way_comparison.md
|
||||||
|
- 'JSON_NO_AUTOMATIC_UDLS': api/macros/json_no_automatic_udls.md
|
||||||
- 'JSON_NOEXCEPTION': api/macros/json_noexception.md
|
- 'JSON_NOEXCEPTION': api/macros/json_noexception.md
|
||||||
- 'JSON_NO_IO': api/macros/json_no_io.md
|
- 'JSON_NO_IO': api/macros/json_no_io.md
|
||||||
- 'JSON_NO_THREAD_LOCAL': api/macros/json_no_thread_local.md
|
- 'JSON_NO_THREAD_LOCAL': api/macros/json_no_thread_local.md
|
||||||
@@ -349,7 +352,6 @@ markdown_extensions:
|
|||||||
- toc:
|
- toc:
|
||||||
permalink: true
|
permalink: true
|
||||||
- md_in_html
|
- md_in_html
|
||||||
- pymdownx.arithmatex
|
|
||||||
- pymdownx.betterem:
|
- pymdownx.betterem:
|
||||||
smart_enable: all
|
smart_enable: all
|
||||||
- pymdownx.caret
|
- pymdownx.caret
|
||||||
@@ -441,6 +443,3 @@ plugins:
|
|||||||
|
|
||||||
extra_css:
|
extra_css:
|
||||||
- css/custom.css
|
- css/custom.css
|
||||||
|
|
||||||
extra_javascript:
|
|
||||||
- https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-MML-AM_CHTML
|
|
||||||
|
|||||||
@@ -79,9 +79,9 @@ def check_structure() -> None:
|
|||||||
report("whitespace/line_length", f"{file}:{lineno+1} ({current_section})", f"line is too long ({len(line)} vs. 160 chars)")
|
report("whitespace/line_length", f"{file}:{lineno+1} ({current_section})", f"line is too long ({len(line)} vs. 160 chars)")
|
||||||
|
|
||||||
# sections in `<!-- NOLINT -->` comments are treated as present
|
# sections in `<!-- NOLINT -->` comments are treated as present
|
||||||
if line.startswith("<!-- NOLINT"):
|
nolint_match = re.match(r"<!--\s*NOLINT\s+(.*?)\s*-->", line)
|
||||||
current_section = line.strip("<!-- NOLINT")
|
if nolint_match:
|
||||||
current_section = current_section.strip(" -->")
|
current_section = nolint_match.group(1)
|
||||||
existing_sections.append(current_section)
|
existing_sections.append(current_section)
|
||||||
|
|
||||||
# check if sections are correct
|
# check if sections are correct
|
||||||
@@ -97,7 +97,7 @@ def check_structure() -> None:
|
|||||||
if len(unexpected):
|
if len(unexpected):
|
||||||
report("style/numbering", f"{file}:{lineno} ({current_section})", f'unexpected overloads: {", ".join([f"({x})" for x in unexpected])}')
|
report("style/numbering", f"{file}:{lineno} ({current_section})", f'unexpected overloads: {", ".join([f"({x})" for x in unexpected])}')
|
||||||
|
|
||||||
current_section = line.strip("## ")
|
current_section = line[3:]
|
||||||
existing_sections.append(current_section)
|
existing_sections.append(current_section)
|
||||||
|
|
||||||
if current_section in expected_sections:
|
if current_section in expected_sections:
|
||||||
@@ -141,7 +141,7 @@ def check_structure() -> None:
|
|||||||
# check that non-example admonitions have titles
|
# check that non-example admonitions have titles
|
||||||
untitled_admonition = re.match(r"^(\?\?\?|!!!) ([^ ]+)$", line)
|
untitled_admonition = re.match(r"^(\?\?\?|!!!) ([^ ]+)$", line)
|
||||||
if untitled_admonition and untitled_admonition.group(2) != "example":
|
if untitled_admonition and untitled_admonition.group(2) != "example":
|
||||||
report("style/admonition_title", f"{file}:{lineno} ({current_section})", f'"{untitled_admonition.group(2)}" admonitions should have a title')
|
report("style/admonition_title", f"{file}:{lineno+1} ({current_section})", f'"{untitled_admonition.group(2)}" admonitions should have a title')
|
||||||
|
|
||||||
previous_line = line
|
previous_line = line
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | JSON for Modern C++
|
||||||
|
// | | |__ | | | | | | version 3.12.0
|
||||||
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint> // uint64_t
|
||||||
|
|
||||||
|
#include <nlohmann/detail/abi_macros.hpp>
|
||||||
|
|
||||||
|
// Portable bit-level helpers for the number and string scanners. They use
|
||||||
|
// compiler builtins where available and plain C++ otherwise, so they need no
|
||||||
|
// platform headers and work regardless of byte order.
|
||||||
|
|
||||||
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
/// number of leading zero bits of x (x != 0)
|
||||||
|
inline int count_leading_zeros(std::uint64_t x) noexcept
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
return __builtin_clzll(x);
|
||||||
|
#else
|
||||||
|
int n = 0;
|
||||||
|
for (int shift = 32; shift != 0; shift >>= 1)
|
||||||
|
{
|
||||||
|
if ((x >> (64 - shift)) == 0)
|
||||||
|
{
|
||||||
|
n += shift;
|
||||||
|
x <<= shift;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the 128-bit product of two 64-bit numbers
|
||||||
|
struct uint128_parts
|
||||||
|
{
|
||||||
|
std::uint64_t low;
|
||||||
|
std::uint64_t high;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline uint128_parts full_multiplication(std::uint64_t a, std::uint64_t b) noexcept
|
||||||
|
{
|
||||||
|
#if defined(__SIZEOF_INT128__)
|
||||||
|
__extension__ using uint128 = unsigned __int128;
|
||||||
|
const uint128 r = static_cast<uint128>(a) * b;
|
||||||
|
return {static_cast<std::uint64_t>(r), static_cast<std::uint64_t>(r >> 64u)};
|
||||||
|
#else
|
||||||
|
const std::uint64_t a_lo = a & 0xFFFFFFFFu;
|
||||||
|
const std::uint64_t a_hi = a >> 32u;
|
||||||
|
const std::uint64_t b_lo = b & 0xFFFFFFFFu;
|
||||||
|
const std::uint64_t b_hi = b >> 32u;
|
||||||
|
const std::uint64_t lo_lo = a_lo * b_lo;
|
||||||
|
const std::uint64_t hi_lo = a_hi * b_lo;
|
||||||
|
const std::uint64_t lo_hi = a_lo * b_hi;
|
||||||
|
const std::uint64_t hi_hi = a_hi * b_hi;
|
||||||
|
const std::uint64_t cross = (lo_lo >> 32u) + (hi_lo & 0xFFFFFFFFu) + lo_hi;
|
||||||
|
return {(cross << 32u) | (lo_lo & 0xFFFFFFFFu), (hi_lo >> 32u) + (cross >> 32u) + hi_hi};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// eight bytes as a little-endian word (compilers fold this into one load on
|
||||||
|
/// little-endian targets)
|
||||||
|
inline std::uint64_t read_eight_bytes(const char* p) noexcept
|
||||||
|
{
|
||||||
|
const auto* b = reinterpret_cast<const unsigned char*>(p); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||||
|
return static_cast<std::uint64_t>(b[0]) | (static_cast<std::uint64_t>(b[1]) << 8u)
|
||||||
|
| (static_cast<std::uint64_t>(b[2]) << 16u) | (static_cast<std::uint64_t>(b[3]) << 24u)
|
||||||
|
| (static_cast<std::uint64_t>(b[4]) << 32u) | (static_cast<std::uint64_t>(b[5]) << 40u)
|
||||||
|
| (static_cast<std::uint64_t>(b[6]) << 48u) | (static_cast<std::uint64_t>(b[7]) << 56u);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
@@ -167,9 +167,13 @@ template<typename BasicJsonType, typename EnumType,
|
|||||||
enable_if_t<std::is_enum<EnumType>::value, int> = 0>
|
enable_if_t<std::is_enum<EnumType>::value, int> = 0>
|
||||||
inline void from_json(const BasicJsonType& j, EnumType& e)
|
inline void from_json(const BasicJsonType& j, EnumType& e)
|
||||||
{
|
{
|
||||||
typename std::underlying_type<EnumType>::type val;
|
using underlying_type = typename std::underlying_type<EnumType>::type;
|
||||||
|
// get_arithmetic_value() does not accept boolean_t; read the number that to_json() wrote instead
|
||||||
|
using value_type = typename std::conditional<std::is_same<underlying_type, typename BasicJsonType::boolean_t>::value,
|
||||||
|
typename BasicJsonType::number_unsigned_t, underlying_type>::type;
|
||||||
|
value_type val;
|
||||||
get_arithmetic_value(j, val);
|
get_arithmetic_value(j, val);
|
||||||
e = static_cast<EnumType>(val);
|
e = static_cast<EnumType>(static_cast<underlying_type>(val));
|
||||||
}
|
}
|
||||||
#endif // JSON_DISABLE_ENUM_SERIALIZATION
|
#endif // JSON_DISABLE_ENUM_SERIALIZATION
|
||||||
|
|
||||||
@@ -564,7 +568,7 @@ inline void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allo
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
|
if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
|
||||||
{
|
{
|
||||||
JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j));
|
JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &p));
|
||||||
}
|
}
|
||||||
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
|
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
|
||||||
}
|
}
|
||||||
@@ -584,7 +588,7 @@ inline void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Has
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
|
if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
|
||||||
{
|
{
|
||||||
JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j));
|
JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &p));
|
||||||
}
|
}
|
||||||
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
|
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -287,7 +287,7 @@ struct external_constructor<value_t::object>
|
|||||||
#ifdef JSON_HAS_CPP_17
|
#ifdef JSON_HAS_CPP_17
|
||||||
template<typename BasicJsonType, typename T,
|
template<typename BasicJsonType, typename T,
|
||||||
enable_if_t<std::is_constructible<BasicJsonType, T>::value, int> = 0>
|
enable_if_t<std::is_constructible<BasicJsonType, T>::value, int> = 0>
|
||||||
void to_json(BasicJsonType& j, const std::optional<T>& opt) noexcept
|
void to_json(BasicJsonType& j, const std::optional<T>& opt) noexcept(std::is_nothrow_assignable<BasicJsonType&, const T&>::value)
|
||||||
{
|
{
|
||||||
if (opt.has_value())
|
if (opt.has_value())
|
||||||
{
|
{
|
||||||
@@ -471,11 +471,13 @@ inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<
|
|||||||
j = { std::get<Idx>(t)... };
|
j = { std::get<Idx>(t)... };
|
||||||
}
|
}
|
||||||
|
|
||||||
#if JSON_BRACE_INIT_COPY_SEMANTICS
|
// A one-element braced list does not reliably wrap its element: with
|
||||||
// JSON_BRACE_INIT_COPY_SEMANTICS makes a one-element braced list copy its
|
// JSON_BRACE_INIT_COPY_SEMANTICS it copies it, which would serialize
|
||||||
// element instead of wrapping it, which would serialize std::tuple<int>{5} as 5
|
// std::tuple<int>{5} as 5 rather than [5], and some compilers (e.g., Apple clang
|
||||||
// rather than [5]. Build what the default deduction builds instead: an object
|
// 15 and 16) copy an element that is itself a basic_json even without it, so
|
||||||
// if the element is a [string, value] pair, a one-element array otherwise.
|
// std::tuple<json>{true} became true rather than [true]. Build what the default
|
||||||
|
// deduction builds instead: an object if the element is a [string, value] pair,
|
||||||
|
// a one-element array otherwise.
|
||||||
template<typename BasicJsonType, typename Tuple>
|
template<typename BasicJsonType, typename Tuple>
|
||||||
inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<0> /*unused*/)
|
inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<0> /*unused*/)
|
||||||
{
|
{
|
||||||
@@ -493,7 +495,6 @@ inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<
|
|||||||
j = BasicJsonType::array({std::move(element)});
|
j = BasicJsonType::array({std::move(element)});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename Tuple>
|
template<typename BasicJsonType, typename Tuple>
|
||||||
inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& /*unused*/, index_sequence<> /*unused*/)
|
inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& /*unused*/, index_sequence<> /*unused*/)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -8,12 +8,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm> // min
|
||||||
#include <array> // array
|
#include <array> // array
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
|
#include <cstdint> // uint32_t
|
||||||
#include <cstring> // strlen
|
#include <cstring> // strlen
|
||||||
#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next
|
#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next
|
||||||
#include <memory> // shared_ptr, make_shared, addressof
|
|
||||||
#include <numeric> // accumulate
|
|
||||||
#include <streambuf> // streambuf
|
#include <streambuf> // streambuf
|
||||||
#include <string> // string, char_traits
|
#include <string> // string, char_traits
|
||||||
#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
|
#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
#include <nlohmann/detail/iterators/iterator_traits.hpp>
|
#include <nlohmann/detail/iterators/iterator_traits.hpp>
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||||
|
#include <nlohmann/detail/string_utils.hpp>
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
namespace detail
|
namespace detail
|
||||||
@@ -82,8 +83,9 @@ class file_input_adapter
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at
|
Input adapter for a (caching) istream. Does not skip a UTF Byte Order Mark
|
||||||
beginning of input. Does not support changing the underlying std::streambuf
|
itself; that is done by the lexer's skip_bom(). Does not support changing
|
||||||
|
the underlying std::streambuf
|
||||||
in mid-input. Maintains underlying std::istream and std::streambuf to support
|
in mid-input. Maintains underlying std::istream and std::streambuf to support
|
||||||
subsequent use of standard std::istream operations to process any input
|
subsequent use of standard std::istream operations to process any input
|
||||||
characters following those used in parsing the JSON input. Clears the
|
characters following those used in parsing the JSON input. Clears the
|
||||||
@@ -106,7 +108,13 @@ class input_stream_adapter
|
|||||||
// was given back with release_lookahead()
|
// was given back with release_lookahead()
|
||||||
commit_lookahead();
|
commit_lookahead();
|
||||||
#endif
|
#endif
|
||||||
is->clear(is->rdstate() & std::ios::eofbit);
|
// only call clear() if there is something to clear: it throws
|
||||||
|
// std::ios_base::failure if the stream has exceptions() enabled
|
||||||
|
// for a state bit that remains set, and a destructor must not throw
|
||||||
|
if ((is->rdstate() & ~std::ios::eofbit) != 0)
|
||||||
|
{
|
||||||
|
is->clear(is->rdstate() & std::ios::eofbit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,32 +456,14 @@ struct wide_string_input_helper<BaseInputAdapter, 4>
|
|||||||
// get the current character
|
// get the current character
|
||||||
const auto wc = input.get_character();
|
const auto wc = input.get_character();
|
||||||
|
|
||||||
// UTF-32 to UTF-8 encoding
|
if (wc <= 0x10FFFF)
|
||||||
if (wc < 0x80)
|
|
||||||
{
|
{
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
|
// UTF-32 to UTF-8 encoding
|
||||||
utf8_bytes_filled = 1;
|
utf8_bytes_filled = 0;
|
||||||
}
|
encode_utf8(static_cast<std::uint32_t>(wc), [&utf8_bytes, &utf8_bytes_filled](std::uint32_t byte)
|
||||||
else if (wc <= 0x7FF)
|
{
|
||||||
{
|
utf8_bytes[utf8_bytes_filled++] = static_cast<std::char_traits<char>::int_type>(byte);
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u) & 0x1Fu));
|
});
|
||||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
|
|
||||||
utf8_bytes_filled = 2;
|
|
||||||
}
|
|
||||||
else if (wc <= 0xFFFF)
|
|
||||||
{
|
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u) & 0x0Fu));
|
|
||||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
|
|
||||||
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
|
|
||||||
utf8_bytes_filled = 3;
|
|
||||||
}
|
|
||||||
else if (wc <= 0x10FFFF)
|
|
||||||
{
|
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | ((static_cast<unsigned int>(wc) >> 18u) & 0x07u));
|
|
||||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 12u) & 0x3Fu));
|
|
||||||
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
|
|
||||||
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
|
|
||||||
utf8_bytes_filled = 4;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -510,24 +500,15 @@ struct wide_string_input_helper<BaseInputAdapter, 2>
|
|||||||
// get the current character
|
// get the current character
|
||||||
const auto wc = input.get_character();
|
const auto wc = input.get_character();
|
||||||
|
|
||||||
// UTF-16 to UTF-8 encoding
|
if (0xD800 > wc || wc >= 0xE000)
|
||||||
if (wc < 0x80)
|
|
||||||
{
|
{
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
|
// a UTF-16 code unit outside the surrogate range is a valid
|
||||||
utf8_bytes_filled = 1;
|
// code point (at most U+FFFF) on its own
|
||||||
}
|
utf8_bytes_filled = 0;
|
||||||
else if (wc <= 0x7FF)
|
encode_utf8(static_cast<std::uint32_t>(wc), [&utf8_bytes, &utf8_bytes_filled](std::uint32_t byte)
|
||||||
{
|
{
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u)));
|
utf8_bytes[utf8_bytes_filled++] = static_cast<std::char_traits<char>::int_type>(byte);
|
||||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
|
});
|
||||||
utf8_bytes_filled = 2;
|
|
||||||
}
|
|
||||||
else if (0xD800 > wc || wc >= 0xE000)
|
|
||||||
{
|
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u)));
|
|
||||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
|
|
||||||
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
|
|
||||||
utf8_bytes_filled = 3;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -545,11 +526,11 @@ struct wide_string_input_helper<BaseInputAdapter, 2>
|
|||||||
if (0xDC00 <= wc2 && wc2 <= 0xDFFF)
|
if (0xDC00 <= wc2 && wc2 <= 0xDFFF)
|
||||||
{
|
{
|
||||||
const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
|
const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
|
utf8_bytes_filled = 0;
|
||||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
|
encode_utf8(charcode, [&utf8_bytes, &utf8_bytes_filled](std::uint32_t byte)
|
||||||
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
|
{
|
||||||
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
|
utf8_bytes[utf8_bytes_filled++] = static_cast<std::char_traits<char>::int_type>(byte);
|
||||||
utf8_bytes_filled = 4;
|
});
|
||||||
valid_pair = true;
|
valid_pair = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -811,12 +792,16 @@ inline file_input_adapter input_adapter(std::FILE* file)
|
|||||||
|
|
||||||
inline input_stream_adapter input_adapter(std::istream& stream)
|
inline input_stream_adapter input_adapter(std::istream& stream)
|
||||||
{
|
{
|
||||||
|
if (stream.rdbuf() == nullptr)
|
||||||
|
{
|
||||||
|
JSON_THROW(parse_error::create(101, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
|
||||||
|
}
|
||||||
return input_stream_adapter(stream);
|
return input_stream_adapter(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline input_stream_adapter input_adapter(std::istream&& stream)
|
inline input_stream_adapter input_adapter(std::istream&& stream)
|
||||||
{
|
{
|
||||||
return input_stream_adapter(stream);
|
return input_adapter(stream);
|
||||||
}
|
}
|
||||||
#endif // JSON_NO_IO
|
#endif // JSON_NO_IO
|
||||||
|
|
||||||
@@ -845,16 +830,28 @@ template<typename T, std::size_t N>
|
|||||||
auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||||
{
|
{
|
||||||
#if JSON_STRICT_NUL_HANDLING
|
#if JSON_STRICT_NUL_HANDLING
|
||||||
// A `char` array from string-literal initialization (e.g. json::parse("123"))
|
// A text-literal array from string-literal initialization (e.g.
|
||||||
// carries a trailing '\0' contributed by the compiler, not by the source
|
// json::parse("123") or json::parse(L"123")) carries a trailing '\0'
|
||||||
// text; drop exactly that one byte so it is not mistaken for real trailing
|
// contributed by the compiler, not by the source text; drop exactly that
|
||||||
// data. Every other element type (unsigned char, std::uint8_t, ...) keeps
|
// one byte so it is not mistaken for real trailing data. This covers all
|
||||||
// the full extent unconditionally, since a trailing zero byte there is
|
// character types that string literals can use: char, wchar_t, char16_t,
|
||||||
// genuine data (e.g. CBOR/MessagePack). This intentionally does not
|
// char32_t, and (C++20) char8_t. Every other element type (unsigned char,
|
||||||
// strlen()-scan the array (as the pointer overload above does for a
|
// std::uint8_t, ...) keeps the full extent unconditionally, since a
|
||||||
// null-delimited string): for a `char` array that is not NUL-terminated
|
// trailing zero byte there is genuine data (e.g. CBOR/MessagePack). This
|
||||||
// within its bounds, that would read past the end of the array.
|
// intentionally does not strlen()-scan the array (as the pointer overload
|
||||||
if (std::is_same<typename std::remove_cv<T>::type, char>::value && N > 0 && array[N - 1] == 0)
|
// above does for a null-delimited string): for an array that is not
|
||||||
|
// NUL-terminated within its bounds, that would read past the end of the
|
||||||
|
// array.
|
||||||
|
using char_t = typename std::remove_cv<T>::type;
|
||||||
|
constexpr bool is_text_literal_type = std::is_same<char_t, char>::value
|
||||||
|
|| std::is_same<char_t, wchar_t>::value
|
||||||
|
|| std::is_same<char_t, char16_t>::value
|
||||||
|
|| std::is_same<char_t, char32_t>::value
|
||||||
|
#if defined(__cpp_char8_t)
|
||||||
|
|| std::is_same<char_t, char8_t>::value
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
if (is_text_literal_type && N > 0 && array[N - 1] == 0)
|
||||||
{
|
{
|
||||||
return input_adapter(array, array + N - 1);
|
return input_adapter(array, array + N - 1);
|
||||||
}
|
}
|
||||||
@@ -862,9 +859,9 @@ auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) /
|
|||||||
return input_adapter(array, array + N);
|
return input_adapter(array, array + N);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This class only handles inputs of input_buffer_adapter type.
|
// This class only handles inputs that construct a contiguous_bytes_input_adapter
|
||||||
// It's required so that expressions like {ptr, len} can be implicitly cast
|
// (e.g. span_input_adapter). It's required so that expressions like {ptr, len}
|
||||||
// to the correct adapter.
|
// can be implicitly cast to the correct adapter.
|
||||||
class span_input_adapter
|
class span_input_adapter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include <algorithm> // find_if, min
|
#include <algorithm> // find_if, min
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <limits> // numeric_limits
|
||||||
#include <string> // string
|
#include <string> // string
|
||||||
#include <type_traits> // enable_if_t
|
#include <type_traits> // enable_if_t
|
||||||
#include <utility> // move, pair
|
#include <utility> // move, pair
|
||||||
@@ -131,7 +132,9 @@ struct json_sax
|
|||||||
@param[in] position the position in the input where the error occurs
|
@param[in] position the position in the input where the error occurs
|
||||||
@param[in] last_token the last read token
|
@param[in] last_token the last read token
|
||||||
@param[in] ex an exception object describing the error
|
@param[in] ex an exception object describing the error
|
||||||
@return whether parsing should proceed (must return false)
|
@return whether to recover from the error: false stops parsing; true
|
||||||
|
repairs the error and continues, or, if that is not possible,
|
||||||
|
stops after completing the value read so far
|
||||||
*/
|
*/
|
||||||
virtual bool parse_error(std::size_t position,
|
virtual bool parse_error(std::size_t position,
|
||||||
const std::string& last_token,
|
const std::string& last_token,
|
||||||
@@ -175,6 +178,88 @@ template<typename ArrayType>
|
|||||||
inline void reserve_array(ArrayType& /*arr*/, std::size_t /*len*/, priority_tag<0> /*unused*/)
|
inline void reserve_array(ArrayType& /*arr*/, std::size_t /*len*/, priority_tag<0> /*unused*/)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
|
/*!
|
||||||
|
@brief set the diagnostic positions of a value the DOM SAX parsers just stored
|
||||||
|
|
||||||
|
Shared by json_sax_dom_parser and json_sax_dom_callback_parser. basic_json
|
||||||
|
befriends this struct, as the position members are private.
|
||||||
|
*/
|
||||||
|
struct diagnostic_positions
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
@param[in,out] v the value that was just parsed
|
||||||
|
@param[in] lexer the lexer that read it, or nullptr to leave @a v alone
|
||||||
|
*/
|
||||||
|
template<typename BasicJsonType, typename LexerType>
|
||||||
|
static void set_from_lexer(BasicJsonType& v, LexerType* lexer)
|
||||||
|
{
|
||||||
|
if (lexer)
|
||||||
|
{
|
||||||
|
// Lexer has read past the current field value, so set the end position to the current position.
|
||||||
|
// The start position will be set below based on the length of the string representation
|
||||||
|
// of the value.
|
||||||
|
v.end_position = lexer->get_position();
|
||||||
|
|
||||||
|
switch (v.type())
|
||||||
|
{
|
||||||
|
case value_t::boolean:
|
||||||
|
{
|
||||||
|
// 4 and 5 are the string length of "true" and "false"
|
||||||
|
v.start_position = v.end_position - (v.m_data.m_value.boolean ? 4 : 5);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::null:
|
||||||
|
{
|
||||||
|
// 4 is the string length of "null"
|
||||||
|
v.start_position = v.end_position - 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::string:
|
||||||
|
{
|
||||||
|
// escape sequences make the token longer than the value it
|
||||||
|
// parses to, so the start position cannot be derived from
|
||||||
|
// the value; use the offset the lexer recorded instead
|
||||||
|
v.start_position = lexer->get_token_start_position();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::discarded:
|
||||||
|
{
|
||||||
|
// an object or array the callback of
|
||||||
|
// json_sax_dom_callback_parser rejected has no position
|
||||||
|
v.end_position = std::string::npos;
|
||||||
|
v.start_position = v.end_position;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::binary:
|
||||||
|
case value_t::number_integer:
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
case value_t::number_float:
|
||||||
|
{
|
||||||
|
v.start_position = v.end_position - lexer->get_string().size();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::object:
|
||||||
|
case value_t::array:
|
||||||
|
{
|
||||||
|
// object and array are handled in start_object() and start_array() handlers
|
||||||
|
// skip setting the values here.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: // LCOV_EXCL_LINE
|
||||||
|
// Handle all possible types discretely, default handler should never be reached.
|
||||||
|
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief SAX implementation to create a JSON value from SAX events
|
@brief SAX implementation to create a JSON value from SAX events
|
||||||
|
|
||||||
@@ -186,9 +271,12 @@ a pointer to the respective array or object for each recursion depth.
|
|||||||
After successful parsing, the value that is passed by reference to the
|
After successful parsing, the value that is passed by reference to the
|
||||||
constructor contains the parsed value.
|
constructor contains the parsed value.
|
||||||
|
|
||||||
@tparam BasicJsonType the JSON type
|
@tparam BasicJsonType the JSON type
|
||||||
|
@tparam InputAdapterType the input adapter of the lexer that can be passed to
|
||||||
|
the constructor to record diagnostic positions; it
|
||||||
|
does not matter if no lexer is passed
|
||||||
*/
|
*/
|
||||||
template<typename BasicJsonType, typename InputAdapterType>
|
template<typename BasicJsonType, typename InputAdapterType = string_input_adapter_type>
|
||||||
class json_sax_dom_parser
|
class json_sax_dom_parser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -376,76 +464,6 @@ class json_sax_dom_parser
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
|
||||||
void handle_diagnostic_positions_for_json_value(BasicJsonType& v)
|
|
||||||
{
|
|
||||||
if (m_lexer_ref)
|
|
||||||
{
|
|
||||||
// Lexer has read past the current field value, so set the end position to the current position.
|
|
||||||
// The start position will be set below based on the length of the string representation
|
|
||||||
// of the value.
|
|
||||||
v.end_position = m_lexer_ref->get_position();
|
|
||||||
|
|
||||||
switch (v.type())
|
|
||||||
{
|
|
||||||
case value_t::boolean:
|
|
||||||
{
|
|
||||||
// 4 and 5 are the string length of "true" and "false"
|
|
||||||
v.start_position = v.end_position - (v.m_data.m_value.boolean ? 4 : 5);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::null:
|
|
||||||
{
|
|
||||||
// 4 is the string length of "null"
|
|
||||||
v.start_position = v.end_position - 4;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::string:
|
|
||||||
{
|
|
||||||
// escape sequences make the token longer than the value it
|
|
||||||
// parses to, so the start position cannot be derived from
|
|
||||||
// the value; use the offset the lexer recorded instead
|
|
||||||
v.start_position = m_lexer_ref->get_token_start_position();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// As we handle the start and end positions for values created during parsing,
|
|
||||||
// we do not expect the following value type to be called. Regardless, set the positions
|
|
||||||
// in case this is created manually or through a different constructor. Exclude from lcov
|
|
||||||
// since the exact condition of this switch is esoteric.
|
|
||||||
// LCOV_EXCL_START
|
|
||||||
case value_t::discarded:
|
|
||||||
{
|
|
||||||
v.end_position = std::string::npos;
|
|
||||||
v.start_position = v.end_position;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// LCOV_EXCL_STOP
|
|
||||||
case value_t::binary:
|
|
||||||
case value_t::number_integer:
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
case value_t::number_float:
|
|
||||||
{
|
|
||||||
v.start_position = v.end_position - m_lexer_ref->get_string().size();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case value_t::object:
|
|
||||||
case value_t::array:
|
|
||||||
{
|
|
||||||
// object and array are handled in start_object() and start_array() handlers
|
|
||||||
// skip setting the values here.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: // LCOV_EXCL_LINE
|
|
||||||
// Handle all possible types discretely, default handler should never be reached.
|
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert,-warnings-as-errors) LCOV_EXCL_LINE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@invariant If the ref stack is empty, then the passed value will be the new
|
@invariant If the ref stack is empty, then the passed value will be the new
|
||||||
root.
|
root.
|
||||||
@@ -461,7 +479,7 @@ class json_sax_dom_parser
|
|||||||
root = BasicJsonType(std::forward<Value>(v));
|
root = BasicJsonType(std::forward<Value>(v));
|
||||||
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
handle_diagnostic_positions_for_json_value(root);
|
diagnostic_positions::set_from_lexer(root, m_lexer_ref);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return &root;
|
return &root;
|
||||||
@@ -474,7 +492,7 @@ class json_sax_dom_parser
|
|||||||
ref_stack.back()->m_data.m_value.array->emplace_back(std::forward<Value>(v));
|
ref_stack.back()->m_data.m_value.array->emplace_back(std::forward<Value>(v));
|
||||||
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
handle_diagnostic_positions_for_json_value(ref_stack.back()->m_data.m_value.array->back());
|
diagnostic_positions::set_from_lexer(ref_stack.back()->m_data.m_value.array->back(), m_lexer_ref);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return &(ref_stack.back()->m_data.m_value.array->back());
|
return &(ref_stack.back()->m_data.m_value.array->back());
|
||||||
@@ -485,7 +503,7 @@ class json_sax_dom_parser
|
|||||||
*object_element = BasicJsonType(std::forward<Value>(v));
|
*object_element = BasicJsonType(std::forward<Value>(v));
|
||||||
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
handle_diagnostic_positions_for_json_value(*object_element);
|
diagnostic_positions::set_from_lexer(*object_element, m_lexer_ref);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return object_element;
|
return object_element;
|
||||||
@@ -505,7 +523,7 @@ class json_sax_dom_parser
|
|||||||
lexer_t* m_lexer_ref = nullptr;
|
lexer_t* m_lexer_ref = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename BasicJsonType, typename InputAdapterType>
|
template<typename BasicJsonType, typename InputAdapterType = string_input_adapter_type>
|
||||||
class json_sax_dom_callback_parser
|
class json_sax_dom_callback_parser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -582,8 +600,8 @@ class json_sax_dom_callback_parser
|
|||||||
|
|
||||||
bool start_object(std::size_t len)
|
bool start_object(std::size_t len)
|
||||||
{
|
{
|
||||||
// check callback for object start
|
// check callback for object start; not called inside a discarded container
|
||||||
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);
|
const bool keep = keep_stack.back() && callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);
|
||||||
keep_stack.push_back(keep);
|
keep_stack.push_back(keep);
|
||||||
|
|
||||||
// the key this object will be stored under, read before handle_value()
|
// the key this object will be stored under, read before handle_value()
|
||||||
@@ -619,6 +637,18 @@ class json_sax_dom_callback_parser
|
|||||||
|
|
||||||
bool key(string_t& val)
|
bool key(string_t& val)
|
||||||
{
|
{
|
||||||
|
if (!keep_stack.back() || !ref_stack.back())
|
||||||
|
{
|
||||||
|
// the object is not stored: the value of this key is dropped in
|
||||||
|
// handle_value() without touching the key stacks
|
||||||
|
if (keep_stack.back())
|
||||||
|
{
|
||||||
|
BasicJsonType k = BasicJsonType(val);
|
||||||
|
static_cast<void>(callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
BasicJsonType k = BasicJsonType(val);
|
BasicJsonType k = BasicJsonType(val);
|
||||||
|
|
||||||
// check callback for the key
|
// check callback for the key
|
||||||
@@ -662,7 +692,7 @@ class json_sax_dom_callback_parser
|
|||||||
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
// Set start/end positions for discarded object.
|
// Set start/end positions for discarded object.
|
||||||
handle_diagnostic_positions_for_json_value(*ref_stack.back());
|
diagnostic_positions::set_from_lexer(*ref_stack.back(), m_lexer_ref);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -704,7 +734,7 @@ class json_sax_dom_callback_parser
|
|||||||
|
|
||||||
bool start_array(std::size_t len)
|
bool start_array(std::size_t len)
|
||||||
{
|
{
|
||||||
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);
|
const bool keep = keep_stack.back() && callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);
|
||||||
keep_stack.push_back(keep);
|
keep_stack.push_back(keep);
|
||||||
|
|
||||||
// see start_object()
|
// see start_object()
|
||||||
@@ -778,7 +808,7 @@ class json_sax_dom_callback_parser
|
|||||||
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
// Set start/end positions for discarded array.
|
// Set start/end positions for discarded array.
|
||||||
handle_diagnostic_positions_for_json_value(*ref_stack.back());
|
diagnostic_positions::set_from_lexer(*ref_stack.back(), m_lexer_ref);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -831,72 +861,6 @@ class json_sax_dom_callback_parser
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
|
||||||
void handle_diagnostic_positions_for_json_value(BasicJsonType& v)
|
|
||||||
{
|
|
||||||
if (m_lexer_ref)
|
|
||||||
{
|
|
||||||
// Lexer has read past the current field value, so set the end position to the current position.
|
|
||||||
// The start position will be set below based on the length of the string representation
|
|
||||||
// of the value.
|
|
||||||
v.end_position = m_lexer_ref->get_position();
|
|
||||||
|
|
||||||
switch (v.type())
|
|
||||||
{
|
|
||||||
case value_t::boolean:
|
|
||||||
{
|
|
||||||
// 4 and 5 are the string length of "true" and "false"
|
|
||||||
v.start_position = v.end_position - (v.m_data.m_value.boolean ? 4 : 5);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::null:
|
|
||||||
{
|
|
||||||
// 4 is the string length of "null"
|
|
||||||
v.start_position = v.end_position - 4;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::string:
|
|
||||||
{
|
|
||||||
// escape sequences make the token longer than the value it
|
|
||||||
// parses to, so the start position cannot be derived from
|
|
||||||
// the value; use the offset the lexer recorded instead
|
|
||||||
v.start_position = m_lexer_ref->get_token_start_position();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::discarded:
|
|
||||||
{
|
|
||||||
v.end_position = std::string::npos;
|
|
||||||
v.start_position = v.end_position;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::binary:
|
|
||||||
case value_t::number_integer:
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
case value_t::number_float:
|
|
||||||
{
|
|
||||||
v.start_position = v.end_position - m_lexer_ref->get_string().size();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::object:
|
|
||||||
case value_t::array:
|
|
||||||
{
|
|
||||||
// object and array are handled in start_object() and start_array() handlers
|
|
||||||
// skip setting the values here.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: // LCOV_EXCL_LINE
|
|
||||||
// Handle all possible types discretely, default handler should never be reached.
|
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert,-warnings-as-errors) LCOV_EXCL_LINE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// if there is a pending duplicate-key stash entry for this exact slot,
|
/// if there is a pending duplicate-key stash entry for this exact slot,
|
||||||
/// remove it from the stash; if restore_value is true, the stashed
|
/// remove it from the stash; if restore_value is true, the stashed
|
||||||
/// previous value is moved back into the slot first (use this when the
|
/// previous value is moved back into the slot first (use this when the
|
||||||
@@ -1018,7 +982,7 @@ class json_sax_dom_callback_parser
|
|||||||
auto value = BasicJsonType(std::forward<Value>(v));
|
auto value = BasicJsonType(std::forward<Value>(v));
|
||||||
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
handle_diagnostic_positions_for_json_value(value);
|
diagnostic_positions::set_from_lexer(value, m_lexer_ref);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// check callback
|
// check callback
|
||||||
|
|||||||
@@ -9,10 +9,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array> // array
|
#include <array> // array
|
||||||
#include <clocale> // localeconv
|
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
|
#include <cstdint> // uint8_t, uint32_t
|
||||||
#include <cstdio> // snprintf
|
#include <cstdio> // snprintf
|
||||||
#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull
|
|
||||||
#include <initializer_list> // initializer_list
|
#include <initializer_list> // initializer_list
|
||||||
#include <string> // char_traits, string
|
#include <string> // char_traits, string
|
||||||
#include <utility> // move
|
#include <utility> // move
|
||||||
@@ -24,6 +23,7 @@
|
|||||||
#include <nlohmann/detail/input/string_scan.hpp>
|
#include <nlohmann/detail/input/string_scan.hpp>
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||||
|
#include <nlohmann/detail/string_utils.hpp>
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
namespace detail
|
namespace detail
|
||||||
@@ -217,18 +217,6 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
~lexer() = default;
|
~lexer() = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/////////////////////
|
|
||||||
// locales
|
|
||||||
/////////////////////
|
|
||||||
|
|
||||||
/// return the decimal point of the current locale
|
|
||||||
static char get_decimal_point() noexcept
|
|
||||||
{
|
|
||||||
const auto* loc = localeconv();
|
|
||||||
JSON_ASSERT(loc != nullptr);
|
|
||||||
return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////
|
/////////////////////
|
||||||
// scan functions
|
// scan functions
|
||||||
/////////////////////
|
/////////////////////
|
||||||
@@ -451,8 +439,16 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF)
|
if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF)
|
||||||
{
|
{
|
||||||
// expect next \uxxxx entry
|
// expect next \uxxxx entry
|
||||||
if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u'))
|
if (JSON_HEDLEY_LIKELY(get() == '\\'))
|
||||||
{
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(get() != 'u'))
|
||||||
|
{
|
||||||
|
// current is the character escaped by the backslash
|
||||||
|
error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF";
|
||||||
|
string_error_resume = resume_kind::escaped_character;
|
||||||
|
return token_type::parse_error;
|
||||||
|
}
|
||||||
|
|
||||||
const int codepoint2 = get_codepoint();
|
const int codepoint2 = get_codepoint();
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1))
|
if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1))
|
||||||
@@ -477,7 +473,11 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// the second escape was read completely and is a
|
||||||
|
// code point of its own
|
||||||
error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF";
|
error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF";
|
||||||
|
string_error_resume = resume_kind::after_escape;
|
||||||
|
string_error_codepoint = codepoint2;
|
||||||
return token_type::parse_error;
|
return token_type::parse_error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -491,7 +491,9 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF))
|
if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF))
|
||||||
{
|
{
|
||||||
|
// the escape was read completely
|
||||||
error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF";
|
error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF";
|
||||||
|
string_error_resume = resume_kind::after_escape;
|
||||||
return token_type::parse_error;
|
return token_type::parse_error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -500,32 +502,10 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF);
|
JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF);
|
||||||
|
|
||||||
// translate codepoint into bytes
|
// translate codepoint into bytes
|
||||||
if (codepoint < 0x80)
|
encode_utf8(static_cast<std::uint32_t>(codepoint), [this](std::uint32_t byte)
|
||||||
{
|
{
|
||||||
// 1-byte characters: 0xxxxxxx (ASCII)
|
add(static_cast<char_int_type>(byte));
|
||||||
add(static_cast<char_int_type>(codepoint));
|
});
|
||||||
}
|
|
||||||
else if (codepoint <= 0x7FF)
|
|
||||||
{
|
|
||||||
// 2-byte characters: 110xxxxx 10xxxxxx
|
|
||||||
add(static_cast<char_int_type>(0xC0u | (static_cast<unsigned int>(codepoint) >> 6u)));
|
|
||||||
add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
|
|
||||||
}
|
|
||||||
else if (codepoint <= 0xFFFF)
|
|
||||||
{
|
|
||||||
// 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx
|
|
||||||
add(static_cast<char_int_type>(0xE0u | (static_cast<unsigned int>(codepoint) >> 12u)));
|
|
||||||
add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));
|
|
||||||
add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
|
||||||
add(static_cast<char_int_type>(0xF0u | (static_cast<unsigned int>(codepoint) >> 18u)));
|
|
||||||
add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 12u) & 0x3Fu)));
|
|
||||||
add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));
|
|
||||||
add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1036,24 +1016,6 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
|
||||||
static void strtof(float& f, const char* str, char** endptr) noexcept
|
|
||||||
{
|
|
||||||
f = std::strtof(str, endptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
|
||||||
static void strtof(double& f, const char* str, char** endptr) noexcept
|
|
||||||
{
|
|
||||||
f = std::strtod(str, endptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
|
||||||
static void strtof(long double& f, const char* str, char** endptr) noexcept
|
|
||||||
{
|
|
||||||
f = std::strtold(str, endptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief scan a number literal
|
@brief scan a number literal
|
||||||
|
|
||||||
@@ -1093,7 +1055,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
@note The scanner is independent of the current locale: token_buffer
|
@note The scanner is independent of the current locale: token_buffer
|
||||||
always holds `.`. Only the std::strtod fallback of convert_number()
|
always holds `.`. Only the std::strtod fallback of convert_number()
|
||||||
depends on the locale, and it looks up the decimal point right
|
depends on the locale, and it looks up the decimal point right
|
||||||
before converting (see convert_float_locale_aware()).
|
before converting (see detail::convert_float_locale_aware()).
|
||||||
*/
|
*/
|
||||||
token_type scan_number() // lgtm [cpp/use-of-goto] `goto` is used in this function to implement the number-parsing state machine described above. By design, any finite input will eventually reach the "done" state or return token_type::parse_error. In each intermediate state, 1 byte of the input is appended to the token_buffer vector, and only the already initialized variables token_buffer, number_type, and error_message are manipulated.
|
token_type scan_number() // lgtm [cpp/use-of-goto] `goto` is used in this function to implement the number-parsing state machine described above. By design, any finite input will eventually reach the "done" state or return token_type::parse_error. In each intermediate state, 1 byte of the input is appended to the token_buffer vector, and only the already initialized variables token_buffer, number_type, and error_message are manipulated.
|
||||||
{
|
{
|
||||||
@@ -1424,59 +1386,6 @@ scan_number_done:
|
|||||||
return token_type::uninitialized;
|
return token_type::uninitialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief check whether Clinger's fast path can still succeed for this token
|
|
||||||
|
|
||||||
parse_float_fast() needs a significand below 2^53. A mantissa with 17 or
|
|
||||||
more significant digits is at least 10^16 and therefore always exceeds it,
|
|
||||||
so calling the fast path would walk the token one extra time only to
|
|
||||||
decline before strtod has to run anyway.
|
|
||||||
|
|
||||||
Significant digits are the mantissa's digits from the first nonzero one on;
|
|
||||||
the sign, the decimal point, leading zeros, and the exponent do not count.
|
|
||||||
The answer is derived from indices - the digits are not scanned again - so
|
|
||||||
this stays off the hot path of the number scanners.
|
|
||||||
|
|
||||||
@param[in] mantissa_end offset just past the last mantissa byte in
|
|
||||||
token_buffer
|
|
||||||
@return false if parse_float_fast() is guaranteed to decline
|
|
||||||
*/
|
|
||||||
bool mantissa_fits_clinger(std::size_t mantissa_end) const
|
|
||||||
{
|
|
||||||
// 10^16 already exceeds 2^53, so 17 digits can never fit
|
|
||||||
constexpr std::size_t limit = 17;
|
|
||||||
|
|
||||||
const std::size_t neg = (!token_buffer.empty() && token_buffer[0] == '-') ? 1u : 0u;
|
|
||||||
const std::size_t has_dot = (decimal_point_position != std::string::npos) ? 1u : 0u;
|
|
||||||
// the JSON grammar restricts the integer part to "0" or [1-9][0-9]*, so
|
|
||||||
// a leading zero can only be a lone "0", which is not significant
|
|
||||||
const std::size_t lead_zero = (token_buffer[neg] == '0') ? 1u : 0u;
|
|
||||||
JSON_ASSERT(mantissa_end >= neg + has_dot + lead_zero);
|
|
||||||
std::size_t digits = mantissa_end - neg - has_dot - lead_zero;
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_LIKELY(digits < limit))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only a number below 1 can carry further insignificant zeros, and only
|
|
||||||
// while the count stays at the limit does removing them change the
|
|
||||||
// answer - so this loop is skipped for all but a few tokens. The
|
|
||||||
// fraction is located through decimal_point_position rather than by
|
|
||||||
// searching '.'.
|
|
||||||
if (lead_zero != 0)
|
|
||||||
{
|
|
||||||
JSON_ASSERT(has_dot != 0); // an integer "0" cannot reach the limit
|
|
||||||
for (std::size_t i = decimal_point_position + 1;
|
|
||||||
digits >= limit && i < mantissa_end && token_buffer[i] == '0'; ++i)
|
|
||||||
{
|
|
||||||
--digits;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return digits < limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief convert the number text in token_buffer to its value and token type
|
@brief convert the number text in token_buffer to its value and token type
|
||||||
|
|
||||||
@@ -1490,49 +1399,34 @@ scan_number_done:
|
|||||||
token_buffer (the index of 'e'/'E', or
|
token_buffer (the index of 'e'/'E', or
|
||||||
token_buffer.size() when there is no exponent);
|
token_buffer.size() when there is no exponent);
|
||||||
used to skip Clinger's fast path when it cannot
|
used to skip Clinger's fast path when it cannot
|
||||||
possibly succeed - see mantissa_fits_clinger()
|
possibly succeed - see detail::mantissa_fits_clinger()
|
||||||
*/
|
*/
|
||||||
token_type convert_number(token_type number_type, std::size_t mantissa_end)
|
token_type convert_number(token_type number_type, std::size_t mantissa_end)
|
||||||
{
|
{
|
||||||
// If the caller does not need the converted value (only whether the
|
// accept() only needs to know whether the input is valid, so it sets
|
||||||
// input is syntactically valid; see json_sax_acceptor/accept()), an
|
// discard_number_values (see json.hpp), and an integer token whose
|
||||||
// unsigned/integer token can be reported without calling
|
// digit count shows that it fits is reported without calling
|
||||||
// strtoull()/strtoll() at all, *provided* we can already tell from
|
// convert_integer(). A number with up to 18 digits always fits into
|
||||||
// the digit count alone that the conversion cannot overflow 64 bits.
|
// both std::uint64_t and std::int64_t (18 nines is about 1e18, below
|
||||||
// Such tokens are always finite and are accepted unconditionally by
|
// INT64_MAX, which is about 9.2e18). Longer tokens take the exact path
|
||||||
// the parser regardless of their actual value (parser::sax_parse_internal()
|
// below, including the fallback to floating point when the value does
|
||||||
// never checks finiteness for value_unsigned/value_integer), so the
|
// not fit.
|
||||||
// classification below is all that is needed.
|
|
||||||
//
|
//
|
||||||
// A decimal number with up to 18 digits is always representable in
|
// With a narrower number_unsigned_t/number_integer_t (e.g.
|
||||||
// both std::uint64_t and std::int64_t (18 nines is ~1e18, well below
|
// std::uint32_t), the exact path would reclassify some of these tokens
|
||||||
// both UINT64_MAX ~1.8e19 and INT64_MAX ~9.2e18), so strtoull()/strtoll()
|
// as (finite) floats, while this check reports integers. That does not
|
||||||
// could not have set errno to ERANGE for it. Numbers with more digits
|
// change the result of accept(): it always parses through
|
||||||
// (rare in practice) fall through to the exact code below, unchanged,
|
// json_sax_acceptor, whose number callbacks discard their argument and
|
||||||
// so their handling -- including reclassification to value_float when
|
// return true, and the parser rejects neither integers nor finite
|
||||||
// the value overflows 64 bits, and rejection when it is not even
|
// floats. value_unsigned/value_integer are left unset here, so a caller
|
||||||
// finite as a double -- is bit-for-bit identical to before this
|
// that reads the converted value must not set discard_number_values.
|
||||||
// optimization.
|
|
||||||
//
|
//
|
||||||
// Note this reasons about std::uint64_t/std::int64_t, not about
|
// On contiguous input, scan_number_bulk_contiguous() converts integer
|
||||||
// number_unsigned_t/number_integer_t (BasicJsonType's own, possibly
|
// tokens itself and does not pass them to this function, unless
|
||||||
// narrower, template parameters -- e.g. std::uint32_t). That is fine
|
// JSON_DIAGNOSTIC_POSITIONS is enabled. This check is therefore only
|
||||||
// *only* because discard_number_values is exclusively set by
|
// reached for input without bulk access (e.g. streams), with
|
||||||
// accept() (see json.hpp), and accept() always parses through the
|
// JSON_DIAGNOSTIC_POSITIONS, or when scan_number_bulk_contiguous()
|
||||||
// library's own json_sax_acceptor -- never a user-supplied SAX
|
// falls back to scan_number().
|
||||||
// consumer -- whose number_unsigned()/number_integer()/number_float()
|
|
||||||
// callbacks unconditionally discard their argument and return true.
|
|
||||||
// So for every caller that can reach this branch, neither the token
|
|
||||||
// classification below nor the eventual (possibly narrowed, and on
|
|
||||||
// this fast path left stale/unset) value_unsigned/value_integer is
|
|
||||||
// ever consulted -- an unsigned/integer token is accepted outright,
|
|
||||||
// and even a >18-digit token that this fast path deliberately falls
|
|
||||||
// through for is, once reclassified to value_float, still finite
|
|
||||||
// (and thus accepted) for any digit count that fits in number_unsigned_t
|
|
||||||
// or number_integer_t regardless of that type's width. If this
|
|
||||||
// function is ever taught to run with discard_number_values true for
|
|
||||||
// a caller that *does* read the converted value, this reasoning (and
|
|
||||||
// the fast path below) would need to be revisited.
|
|
||||||
if (discard_number_values)
|
if (discard_number_values)
|
||||||
{
|
{
|
||||||
constexpr std::size_t safe_digit_count = 18;
|
constexpr std::size_t safe_digit_count = 18;
|
||||||
@@ -1563,77 +1457,15 @@ scan_number_done:
|
|||||||
// (Eisel-Lemire, locale-independent, correctly rounded) when available;
|
// (Eisel-Lemire, locale-independent, correctly rounded) when available;
|
||||||
// otherwise the exact Clinger fast path (double only); otherwise the
|
// otherwise the exact Clinger fast path (double only); otherwise the
|
||||||
// locale-aware strtof/strtod/strtold.
|
// locale-aware strtof/strtod/strtold.
|
||||||
if (parse_float_from_chars(num_begin, num_end, value_float))
|
if (convert_float_fast(num_begin, num_end, decimal_point_position, mantissa_end, value_float))
|
||||||
{
|
|
||||||
return token_type::value_float;
|
|
||||||
}
|
|
||||||
// Skipping a fast path that cannot succeed is lossless and saves a full
|
|
||||||
// extra pass over the token's bytes, which otherwise shows up on
|
|
||||||
// high-precision inputs such as canada.json
|
|
||||||
if (mantissa_fits_clinger(mantissa_end)
|
|
||||||
&& parse_float_fast(num_begin, num_end, value_float))
|
|
||||||
{
|
{
|
||||||
return token_type::value_float;
|
return token_type::value_float;
|
||||||
}
|
}
|
||||||
|
|
||||||
convert_float_locale_aware();
|
convert_float_locale_aware(token_buffer, decimal_point_position, value_float);
|
||||||
return token_type::value_float;
|
return token_type::value_float;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief convert the float in token_buffer with strtof/strtod/strtold
|
|
||||||
|
|
||||||
These functions expect the decimal point of the *current* locale, so it is
|
|
||||||
looked up right before the conversion instead of once when the lexer is
|
|
||||||
constructed: a locale change in between (by a parser callback, a SAX
|
|
||||||
handler, or another thread) must not truncate the value (#5198). The
|
|
||||||
token has been validated before, so if the conversion stops early and the
|
|
||||||
decimal point changed in the meantime, the locale changed between the
|
|
||||||
lookup and the call, and the conversion is repeated with the new decimal
|
|
||||||
point. If the decimal point did not change, a retry cannot succeed: the
|
|
||||||
locale's decimal point is not a single character (e.g., the two-byte
|
|
||||||
U+066B of ar_EG.UTF-8 or fa_IR.UTF-8) and cannot be substituted in place.
|
|
||||||
The value strtod parsed up to that point is kept, as before this change.
|
|
||||||
|
|
||||||
Note that changing the locale in another thread *while* strtod runs is
|
|
||||||
undefined behavior of the C library, which this function cannot prevent.
|
|
||||||
*/
|
|
||||||
void convert_float_locale_aware()
|
|
||||||
{
|
|
||||||
const bool has_dot = decimal_point_position != std::string::npos;
|
|
||||||
char decimal_point = get_decimal_point();
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
const bool substitute = has_dot && decimal_point != '.';
|
|
||||||
if (substitute)
|
|
||||||
{
|
|
||||||
token_buffer[decimal_point_position] = static_cast<typename string_t::value_type>(decimal_point);
|
|
||||||
}
|
|
||||||
|
|
||||||
char* endptr = nullptr; // NOLINT(misc-const-correctness,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
|
||||||
strtof(value_float, token_buffer.data(), &endptr);
|
|
||||||
|
|
||||||
if (substitute)
|
|
||||||
{
|
|
||||||
// get_string() hands the token to the SAX interface with '.'
|
|
||||||
token_buffer[decimal_point_position] = '.';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_LIKELY(endptr == token_buffer.data() + token_buffer.size()))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// retry only if the locale changed; otherwise, this would loop forever
|
|
||||||
const char current_decimal_point = get_decimal_point();
|
|
||||||
if (current_decimal_point == decimal_point)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
decimal_point = current_decimal_point;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief contiguous fast path for scanning a number
|
@brief contiguous fast path for scanning a number
|
||||||
|
|
||||||
@@ -2023,7 +1855,7 @@ scan_number_done:
|
|||||||
return value_float;
|
return value_float;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// return current string value (implicitly resets the token; useful only once)
|
/// return current string value
|
||||||
string_t& get_string()
|
string_t& get_string()
|
||||||
{
|
{
|
||||||
// a number token holds '.' regardless of the locale (#4084)
|
// a number token holds '.' regardless of the locale (#4084)
|
||||||
@@ -2277,6 +2109,573 @@ scan_number_done:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/////////////////////
|
||||||
|
// error recovery
|
||||||
|
/////////////////////
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief make the best of the token that scan() rejected
|
||||||
|
|
||||||
|
Called by the parser after scan() returned token_type::parse_error and the
|
||||||
|
SAX parser asked to recover from the error (see #3989). Keeps what can be
|
||||||
|
read of the token and skips the rest:
|
||||||
|
|
||||||
|
- A string keeps its characters. An unknown escape stands for the escaped
|
||||||
|
character itself (as in JavaScript), an invalid `\u` escape and ill-formed
|
||||||
|
UTF-8 become U+FFFD, and a control character is kept. A line break or the
|
||||||
|
end of the input ends a string that lacks its closing quote.
|
||||||
|
- A number keeps its longest valid prefix, e.g. `1` for `1.` or `1e+`.
|
||||||
|
- A block comment that is not closed runs to the end of the input.
|
||||||
|
- Anything else is skipped.
|
||||||
|
|
||||||
|
The rest of an invalid token is skipped up to the next delimiter
|
||||||
|
(whitespace, a structural character, or a quote). A delimiter that the
|
||||||
|
invalid token consumed is returned to the input, so that the next scan()
|
||||||
|
reads it.
|
||||||
|
|
||||||
|
@return token_type::value_string or a number token type if a string or a
|
||||||
|
number could be read, token_type::end_of_input for a block comment
|
||||||
|
that is not closed, token_type::uninitialized otherwise
|
||||||
|
*/
|
||||||
|
token_type recover_token()
|
||||||
|
{
|
||||||
|
const resume_kind resume = string_error_resume;
|
||||||
|
const int codepoint = string_error_codepoint;
|
||||||
|
string_error_resume = resume_kind::character;
|
||||||
|
string_error_codepoint = -1;
|
||||||
|
|
||||||
|
if (error_message_starts_with("invalid string"))
|
||||||
|
{
|
||||||
|
return recover_string(resume, codepoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error_message_starts_with("invalid number"))
|
||||||
|
{
|
||||||
|
return recover_number();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error_message_starts_with("invalid comment; missing"))
|
||||||
|
{
|
||||||
|
// the comment runs to the end of the input
|
||||||
|
return token_type::end_of_input;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_to_delimiter();
|
||||||
|
return token_type::uninitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief return the token that scan() read last to the input, so that the
|
||||||
|
next scan() reads it again
|
||||||
|
|
||||||
|
Called by the parser when recovering from an error. The token must be a
|
||||||
|
single character (',', ':', '[', ']', '{', or '}') or the end of the
|
||||||
|
input, and scan() must have read it last.
|
||||||
|
*/
|
||||||
|
void unget_token()
|
||||||
|
{
|
||||||
|
JSON_ASSERT(!next_unget);
|
||||||
|
unget();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief let the token string for the next error begin at the current character
|
||||||
|
|
||||||
|
The token string of an error reaches back to the beginning of the last
|
||||||
|
string or number. After an error, the parser calls this function so that
|
||||||
|
the next error does not report (and, with many errors, copy) everything
|
||||||
|
read since then.
|
||||||
|
*/
|
||||||
|
void restart_token_string()
|
||||||
|
{
|
||||||
|
restart_token_string_impl(std::integral_constant<bool, lazy_token_string> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// how recover_string() continues after the error scan_string() reported
|
||||||
|
enum class resume_kind : std::uint8_t
|
||||||
|
{
|
||||||
|
/// current is the next character of the string (or the end of input)
|
||||||
|
character,
|
||||||
|
/// current is the character escaped by the preceding backslash
|
||||||
|
escaped_character,
|
||||||
|
/// current is the last character of a complete escape
|
||||||
|
after_escape
|
||||||
|
};
|
||||||
|
|
||||||
|
/// whether error_message begins with @a prefix
|
||||||
|
bool error_message_starts_with(const char* prefix) const noexcept
|
||||||
|
{
|
||||||
|
const char* message = error_message;
|
||||||
|
while (*prefix != '\0')
|
||||||
|
{
|
||||||
|
if (*message++ != *prefix++)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// whether current ends an invalid token (see recover_token())
|
||||||
|
bool current_is_delimiter() const noexcept
|
||||||
|
{
|
||||||
|
switch (current)
|
||||||
|
{
|
||||||
|
case ' ':
|
||||||
|
case '\t':
|
||||||
|
case '\n':
|
||||||
|
case '\r':
|
||||||
|
case '[':
|
||||||
|
case ']':
|
||||||
|
case '{':
|
||||||
|
case '}':
|
||||||
|
case ',':
|
||||||
|
case ':':
|
||||||
|
case '\"':
|
||||||
|
#if !JSON_STRICT_NUL_HANDLING
|
||||||
|
case '\0':
|
||||||
|
#endif
|
||||||
|
case char_traits<char_type>::eof():
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case '/':
|
||||||
|
return ignore_comments;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// skip the rest of an invalid token and return its delimiter to the input
|
||||||
|
void skip_to_delimiter()
|
||||||
|
{
|
||||||
|
while (!current_is_delimiter())
|
||||||
|
{
|
||||||
|
get();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current != char_traits<char_type>::eof())
|
||||||
|
{
|
||||||
|
unget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// append U+FFFD REPLACEMENT CHARACTER to token_buffer
|
||||||
|
void add_replacement_character()
|
||||||
|
{
|
||||||
|
add(0xEF);
|
||||||
|
add(0xBF);
|
||||||
|
add(0xBD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// append the UTF-8 encoding of @a codepoint (not a surrogate) to token_buffer
|
||||||
|
void add_codepoint(const int codepoint)
|
||||||
|
{
|
||||||
|
JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF);
|
||||||
|
const auto cp = static_cast<unsigned int>(codepoint);
|
||||||
|
if (cp < 0x80)
|
||||||
|
{
|
||||||
|
add(static_cast<char_int_type>(cp));
|
||||||
|
}
|
||||||
|
else if (cp <= 0x7FF)
|
||||||
|
{
|
||||||
|
add(static_cast<char_int_type>(0xC0u | (cp >> 6u)));
|
||||||
|
add(static_cast<char_int_type>(0x80u | (cp & 0x3Fu)));
|
||||||
|
}
|
||||||
|
else if (cp <= 0xFFFF)
|
||||||
|
{
|
||||||
|
add(static_cast<char_int_type>(0xE0u | (cp >> 12u)));
|
||||||
|
add(static_cast<char_int_type>(0x80u | ((cp >> 6u) & 0x3Fu)));
|
||||||
|
add(static_cast<char_int_type>(0x80u | (cp & 0x3Fu)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
add(static_cast<char_int_type>(0xF0u | (cp >> 18u)));
|
||||||
|
add(static_cast<char_int_type>(0x80u | ((cp >> 12u) & 0x3Fu)));
|
||||||
|
add(static_cast<char_int_type>(0x80u | ((cp >> 6u) & 0x3Fu)));
|
||||||
|
add(static_cast<char_int_type>(0x80u | (cp & 0x3Fu)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// append a code point read from a `\u` escape; a surrogate becomes U+FFFD
|
||||||
|
void add_escaped_codepoint(const int codepoint)
|
||||||
|
{
|
||||||
|
if (0xD800 <= codepoint && codepoint <= 0xDFFF)
|
||||||
|
{
|
||||||
|
add_replacement_character();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
add_codepoint(codepoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief remove an incomplete UTF-8 sequence from the end of token_buffer
|
||||||
|
|
||||||
|
next_byte_in_range() adds the bytes of a sequence as it checks them, so
|
||||||
|
when it rejects a byte, the beginning of the sequence is already in
|
||||||
|
token_buffer, which otherwise holds only complete sequences.
|
||||||
|
|
||||||
|
@return whether an incomplete sequence was removed
|
||||||
|
*/
|
||||||
|
bool remove_incomplete_utf8_sequence()
|
||||||
|
{
|
||||||
|
std::size_t lead = token_buffer.size();
|
||||||
|
std::size_t continuation_bytes = 0;
|
||||||
|
while (lead > 0 && continuation_bytes < 3
|
||||||
|
&& (static_cast<unsigned char>(token_buffer[lead - 1]) & 0xC0u) == 0x80u)
|
||||||
|
{
|
||||||
|
--lead;
|
||||||
|
++continuation_bytes;
|
||||||
|
}
|
||||||
|
if (lead == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto lead_byte = static_cast<unsigned char>(token_buffer[lead - 1]);
|
||||||
|
std::size_t expected = 0;
|
||||||
|
if (lead_byte >= 0xF0)
|
||||||
|
{
|
||||||
|
expected = 3;
|
||||||
|
}
|
||||||
|
else if (lead_byte >= 0xE0)
|
||||||
|
{
|
||||||
|
expected = 2;
|
||||||
|
}
|
||||||
|
else if (lead_byte >= 0xC0)
|
||||||
|
{
|
||||||
|
expected = 1;
|
||||||
|
}
|
||||||
|
if (continuation_bytes >= expected)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
token_buffer.resize(lead - 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief read the UTF-8 sequence that begins with current, which is not ASCII
|
||||||
|
@return whether the next character must be read; false if current still
|
||||||
|
needs to be handled, because it does not belong to the sequence
|
||||||
|
*/
|
||||||
|
bool recover_utf8_sequence()
|
||||||
|
{
|
||||||
|
// the number of continuation bytes and the range of the first one;
|
||||||
|
// see the ranges in scan_string()
|
||||||
|
std::size_t count = 0;
|
||||||
|
char_int_type low = 0x80;
|
||||||
|
char_int_type high = 0xBF;
|
||||||
|
if (current >= 0xC2 && current <= 0xDF)
|
||||||
|
{
|
||||||
|
count = 1;
|
||||||
|
}
|
||||||
|
else if (current >= 0xE0 && current <= 0xEF)
|
||||||
|
{
|
||||||
|
count = 2;
|
||||||
|
low = (current == 0xE0) ? 0xA0 : 0x80;
|
||||||
|
high = (current == 0xED) ? 0x9F : 0xBF;
|
||||||
|
}
|
||||||
|
else if (current >= 0xF0 && current <= 0xF4)
|
||||||
|
{
|
||||||
|
count = 3;
|
||||||
|
low = (current == 0xF0) ? 0x90 : 0x80;
|
||||||
|
high = (current == 0xF4) ? 0x8F : 0xBF;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// an ill-formed byte
|
||||||
|
add_replacement_character();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::size_t start = token_buffer.size();
|
||||||
|
add(current);
|
||||||
|
for (std::size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
get();
|
||||||
|
if (current < low || current > high)
|
||||||
|
{
|
||||||
|
token_buffer.resize(start);
|
||||||
|
add_replacement_character();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
add(current);
|
||||||
|
low = 0x80;
|
||||||
|
high = 0xBF;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief read the low surrogate that must follow the high surrogate @a high
|
||||||
|
@return whether the next character must be read; false if current still
|
||||||
|
needs to be handled
|
||||||
|
*/
|
||||||
|
bool recover_low_surrogate(int high)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (get() != '\\')
|
||||||
|
{
|
||||||
|
add_replacement_character();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (get() != 'u')
|
||||||
|
{
|
||||||
|
add_replacement_character();
|
||||||
|
// not 'u', so this does not come back here
|
||||||
|
return recover_escape();
|
||||||
|
}
|
||||||
|
|
||||||
|
const int low = get_codepoint();
|
||||||
|
if (low == -1)
|
||||||
|
{
|
||||||
|
add_replacement_character();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (0xDC00 <= low && low <= 0xDFFF)
|
||||||
|
{
|
||||||
|
add_codepoint(static_cast<int>((static_cast<unsigned int>(high) << 10u)
|
||||||
|
+ static_cast<unsigned int>(low) - 0x35FDC00u));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// high has no low surrogate
|
||||||
|
add_replacement_character();
|
||||||
|
if (low < 0xD800 || low > 0xDBFF)
|
||||||
|
{
|
||||||
|
add_codepoint(low);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// another high surrogate
|
||||||
|
high = low;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief read the escape whose backslash was read; current is the escaped character
|
||||||
|
@return whether the next character must be read; false if current still
|
||||||
|
needs to be handled
|
||||||
|
*/
|
||||||
|
bool recover_escape()
|
||||||
|
{
|
||||||
|
switch (current)
|
||||||
|
{
|
||||||
|
case '\"':
|
||||||
|
add('\"');
|
||||||
|
return true;
|
||||||
|
case '\\':
|
||||||
|
add('\\');
|
||||||
|
return true;
|
||||||
|
case '/':
|
||||||
|
add('/');
|
||||||
|
return true;
|
||||||
|
case 'b':
|
||||||
|
add('\b');
|
||||||
|
return true;
|
||||||
|
case 'f':
|
||||||
|
add('\f');
|
||||||
|
return true;
|
||||||
|
case 'n':
|
||||||
|
add('\n');
|
||||||
|
return true;
|
||||||
|
case 'r':
|
||||||
|
add('\r');
|
||||||
|
return true;
|
||||||
|
case 't':
|
||||||
|
add('\t');
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case 'u':
|
||||||
|
{
|
||||||
|
const int codepoint = get_codepoint();
|
||||||
|
if (codepoint == -1)
|
||||||
|
{
|
||||||
|
add_replacement_character();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (0xD800 <= codepoint && codepoint <= 0xDBFF)
|
||||||
|
{
|
||||||
|
return recover_low_surrogate(codepoint);
|
||||||
|
}
|
||||||
|
add_escaped_codepoint(codepoint);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// an unknown escape stands for the escaped character
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief read the rest of a string after scan_string() rejected it
|
||||||
|
|
||||||
|
token_buffer holds what scan_string() read before the error. See
|
||||||
|
recover_token() for how errors are repaired.
|
||||||
|
|
||||||
|
@param[in] resume how to continue, see resume_kind
|
||||||
|
@param[in] codepoint for a high surrogate followed by an escape of another
|
||||||
|
code point: that code point; -1 otherwise
|
||||||
|
*/
|
||||||
|
token_type recover_string(const resume_kind resume, const int codepoint)
|
||||||
|
{
|
||||||
|
// whether the next character must be read before it can be handled
|
||||||
|
bool fetch = false;
|
||||||
|
|
||||||
|
if (error_message_starts_with("invalid string: surrogate")
|
||||||
|
|| error_message_starts_with("invalid string: '\\u'")
|
||||||
|
|| (error_message_starts_with("invalid string: ill-formed UTF-8")
|
||||||
|
&& remove_incomplete_utf8_sequence()))
|
||||||
|
{
|
||||||
|
add_replacement_character();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (resume)
|
||||||
|
{
|
||||||
|
case resume_kind::escaped_character:
|
||||||
|
fetch = recover_escape();
|
||||||
|
break;
|
||||||
|
case resume_kind::after_escape:
|
||||||
|
if (0xD800 <= codepoint && codepoint <= 0xDBFF)
|
||||||
|
{
|
||||||
|
fetch = recover_low_surrogate(codepoint);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (codepoint != -1)
|
||||||
|
{
|
||||||
|
add_escaped_codepoint(codepoint);
|
||||||
|
}
|
||||||
|
fetch = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case resume_kind::character:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (fetch)
|
||||||
|
{
|
||||||
|
get();
|
||||||
|
}
|
||||||
|
fetch = true;
|
||||||
|
|
||||||
|
switch (current)
|
||||||
|
{
|
||||||
|
case '\"':
|
||||||
|
// a line break or the end of the input ends a string that
|
||||||
|
// lacks its closing quote
|
||||||
|
case '\n':
|
||||||
|
case '\r':
|
||||||
|
case char_traits<char_type>::eof():
|
||||||
|
return token_type::value_string;
|
||||||
|
|
||||||
|
#if !JSON_STRICT_NUL_HANDLING
|
||||||
|
case '\0':
|
||||||
|
// the end of the input, see scan()
|
||||||
|
unget();
|
||||||
|
return token_type::value_string;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
case '\\':
|
||||||
|
get();
|
||||||
|
fetch = recover_escape();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (current < 0x80)
|
||||||
|
{
|
||||||
|
// including control characters
|
||||||
|
add(current);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fetch = recover_utf8_sequence();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief keep the longest valid prefix of a number that scan_number() rejected
|
||||||
|
|
||||||
|
token_buffer holds the characters scan_number() accepted before the error,
|
||||||
|
so the prefix ends at its last digit.
|
||||||
|
*/
|
||||||
|
token_type recover_number()
|
||||||
|
{
|
||||||
|
// only size(), operator[], and resize() are used, which every string
|
||||||
|
// type the library supports provides
|
||||||
|
std::size_t length = token_buffer.size();
|
||||||
|
while (length != 0 && (token_buffer[length - 1] < '0' || token_buffer[length - 1] > '9'))
|
||||||
|
{
|
||||||
|
--length;
|
||||||
|
}
|
||||||
|
token_buffer.resize(length);
|
||||||
|
|
||||||
|
if (length == 0)
|
||||||
|
{
|
||||||
|
skip_to_delimiter();
|
||||||
|
return token_type::uninitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decimal_point_position >= length)
|
||||||
|
{
|
||||||
|
decimal_point_position = std::string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t exponent = std::string::npos;
|
||||||
|
for (std::size_t i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
if (token_buffer[i] == 'e' || token_buffer[i] == 'E')
|
||||||
|
{
|
||||||
|
exponent = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const std::size_t mantissa_end = (exponent == std::string::npos) ? length : exponent;
|
||||||
|
token_type number_type = token_type::value_unsigned;
|
||||||
|
if (decimal_point_position != std::string::npos || exponent != std::string::npos)
|
||||||
|
{
|
||||||
|
number_type = token_type::value_float;
|
||||||
|
}
|
||||||
|
else if (token_buffer[0] == '-')
|
||||||
|
{
|
||||||
|
number_type = token_type::value_integer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const token_type result = convert_number(number_type, mantissa_end);
|
||||||
|
skip_to_delimiter();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// seekable adapter: the token string begins at current, which was consumed
|
||||||
|
void restart_token_string_impl(std::true_type /*lazy*/) noexcept
|
||||||
|
{
|
||||||
|
const std::size_t consumed = ia.get_consumed_count();
|
||||||
|
token_string_start = (consumed > 0 && current != char_traits<char_type>::eof()) ? consumed - 1 : consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// streaming adapter: the token string begins at current; a character
|
||||||
|
/// that was put back is copied again when it is read again
|
||||||
|
void restart_token_string_impl(std::false_type /*lazy*/)
|
||||||
|
{
|
||||||
|
token_string.clear();
|
||||||
|
if (!next_unget && current != char_traits<char_type>::eof())
|
||||||
|
{
|
||||||
|
token_string.push_back(char_traits<char_type>::to_char_type(current));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// input adapter
|
/// input adapter
|
||||||
InputAdapterType ia;
|
InputAdapterType ia;
|
||||||
@@ -2317,6 +2716,13 @@ scan_number_done:
|
|||||||
/// a description of occurred lexer errors
|
/// a description of occurred lexer errors
|
||||||
const char* error_message = "";
|
const char* error_message = "";
|
||||||
|
|
||||||
|
/// how recover_token() continues a string that scan_string() rejected;
|
||||||
|
/// set only on the error paths that need more than error_message
|
||||||
|
resume_kind string_error_resume = resume_kind::character;
|
||||||
|
/// the code point of the second escape when a high surrogate is followed
|
||||||
|
/// by an escape that is not a low surrogate; -1 otherwise
|
||||||
|
int string_error_codepoint = -1;
|
||||||
|
|
||||||
// number values
|
// number values
|
||||||
number_integer_t value_integer = 0;
|
number_integer_t value_integer = 0;
|
||||||
number_unsigned_t value_unsigned = 0;
|
number_unsigned_t value_unsigned = 0;
|
||||||
@@ -2325,11 +2731,11 @@ scan_number_done:
|
|||||||
/// the position of the decimal point in token_buffer
|
/// the position of the decimal point in token_buffer
|
||||||
std::size_t decimal_point_position = std::string::npos;
|
std::size_t decimal_point_position = std::string::npos;
|
||||||
|
|
||||||
/// whether the caller (e.g. accept()/json_sax_acceptor) only needs the
|
/// whether the caller only needs the token types and never looks at the
|
||||||
/// token classification and never looks at the converted numeric value;
|
/// converted numeric values; set only by accept(), which parses through
|
||||||
/// when set, scan_number() may skip strtoull()/strtoll() for
|
/// json_sax_acceptor. When set, convert_number() skips converting integer
|
||||||
/// value_unsigned/value_integer tokens whose digit count guarantees they
|
/// tokens whose digit count guarantees that they fit into 64 bits (see
|
||||||
/// fit into 64 bits (see scan_number())
|
/// there)
|
||||||
const bool discard_number_values = false;
|
const bool discard_number_values = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// | | |__ | | | | | | version 3.12.0
|
// | | |__ | | | | | | version 3.12.0
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
//
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2021 The fast_float authors <https://github.com/fastfloat/fast_float>
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
@@ -10,10 +11,16 @@
|
|||||||
|
|
||||||
#include <array> // array
|
#include <array> // array
|
||||||
#include <cfloat> // FLT_EVAL_METHOD
|
#include <cfloat> // FLT_EVAL_METHOD
|
||||||
|
#include <clocale> // localeconv
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstdint> // int64_t, uint64_t
|
#include <cstdint> // int64_t, uint64_t
|
||||||
|
#include <cstdlib> // strtof, strtod, strtold
|
||||||
|
#include <cstring> // memcpy
|
||||||
#include <limits> // numeric_limits
|
#include <limits> // numeric_limits
|
||||||
|
#include <string> // string
|
||||||
|
|
||||||
|
#include <nlohmann/detail/bit_ops.hpp>
|
||||||
|
#include <nlohmann/detail/input/pow5_table.hpp>
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
|
|
||||||
// std::from_chars lives in <charconv>, but being in C++17 mode does not
|
// std::from_chars lives in <charconv>, but being in C++17 mode does not
|
||||||
@@ -29,8 +36,9 @@
|
|||||||
|
|
||||||
// This file contains the value-conversion helpers used by the lexer to turn an
|
// This file contains the value-conversion helpers used by the lexer to turn an
|
||||||
// already-validated number token into a value, without the locale/errno
|
// already-validated number token into a value, without the locale/errno
|
||||||
// overhead of std::strtoull/std::strtod. They are free functions so the lexer
|
// overhead of std::strtoull/std::strtod where possible. They are free functions
|
||||||
// stays focused on scanning; see lexer::convert_number().
|
// so the lexer stays focused on scanning (see lexer::convert_number()) and so
|
||||||
|
// that other parsers of JSON text can convert tokens exactly like it does.
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
namespace detail
|
namespace detail
|
||||||
@@ -293,5 +301,415 @@ bool parse_float_from_chars(const char* first, const char* last, FloatType& out)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// whether the eight bytes of @a v (see read_eight_bytes()) are ASCII digits
|
||||||
|
/// (after fast_float's is_made_of_eight_digits_fast)
|
||||||
|
inline bool is_eight_digits(std::uint64_t v) noexcept
|
||||||
|
{
|
||||||
|
return ((v & 0xF0F0F0F0F0F0F0F0u) | (((v + 0x0606060606060606u) & 0xF0F0F0F0F0F0F0F0u) >> 4u)) == 0x3333333333333333u;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the value of the eight ASCII digits in @a v (see read_eight_bytes()), three
|
||||||
|
/// multiplications instead of eight (after simdjson and fast_float)
|
||||||
|
inline std::uint32_t parse_eight_digits(std::uint64_t v) noexcept
|
||||||
|
{
|
||||||
|
v = ((v & 0x0F0F0F0F0F0F0F0Fu) * 2561u) >> 8u;
|
||||||
|
v = ((v & 0x00FF00FF00FF00FFu) * 6553601u) >> 16u;
|
||||||
|
return static_cast<std::uint32_t>(((v & 0x0000FFFF0000FFFFu) * 42949672960001u) >> 32u);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief the double nearest to w * 10^q (Eisel-Lemire)
|
||||||
|
|
||||||
|
The algorithm of Daniel Lemire, "Number Parsing at a Gigabyte per Second"
|
||||||
|
(Software: Practice and Experience, 2021), after fast_float's compute_float
|
||||||
|
(used under the MIT license). With a 128-bit approximation of 5^q, the product
|
||||||
|
is always sufficient to round correctly for w with at most 19 digits (Noble
|
||||||
|
Mushtak and Daniel Lemire, "Fast number parsing without fallback", Software:
|
||||||
|
Practice and Experience, 2023). Only integer arithmetic is used, so the result
|
||||||
|
does not depend on the floating-point environment.
|
||||||
|
|
||||||
|
@param[in] q decimal exponent
|
||||||
|
@param[in] w significand, w != 0
|
||||||
|
@return the IEEE-754 bits of the positive result (0 for underflow, infinity
|
||||||
|
for overflow)
|
||||||
|
*/
|
||||||
|
inline std::uint64_t eisel_lemire(std::int64_t q, std::uint64_t w) noexcept
|
||||||
|
{
|
||||||
|
constexpr int mantissa_bits = 52;
|
||||||
|
constexpr std::uint64_t infinity = std::uint64_t{0x7FF} << mantissa_bits;
|
||||||
|
if (q < pow5_128_smallest_power)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (q > pow5_128_largest_power)
|
||||||
|
{
|
||||||
|
return infinity;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int lz = count_leading_zeros(w);
|
||||||
|
w <<= static_cast<unsigned>(lz);
|
||||||
|
const auto index = static_cast<std::size_t>(2 * (q - pow5_128_smallest_power));
|
||||||
|
uint128_parts product = full_multiplication(w, pow5_128()[index]);
|
||||||
|
constexpr std::uint64_t precision_mask = 0xFFFFFFFFFFFFFFFFu >> (mantissa_bits + 3);
|
||||||
|
if ((product.high & precision_mask) == precision_mask)
|
||||||
|
{
|
||||||
|
// the lower bits may carry into the result: use the next 64 bits of 5^q
|
||||||
|
const uint128_parts second = full_multiplication(w, pow5_128()[index + 1]);
|
||||||
|
product.low += second.high;
|
||||||
|
if (second.high > product.low)
|
||||||
|
{
|
||||||
|
++product.high;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto upperbit = static_cast<int>(product.high >> 63u);
|
||||||
|
const int shift = upperbit + 64 - mantissa_bits - 3;
|
||||||
|
std::uint64_t mantissa = product.high >> static_cast<unsigned>(shift);
|
||||||
|
// floor(log2(10^q)) + 63 + 1023, with log2(10) ~ 217706 / 2^16
|
||||||
|
std::int64_t power2 = (((152170 + 65536) * q) >> 16) + 63 + upperbit - lz + 1023;
|
||||||
|
|
||||||
|
if (power2 <= 0) // subnormal
|
||||||
|
{
|
||||||
|
if (-power2 + 1 >= 64)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
mantissa >>= static_cast<unsigned>(-power2 + 1);
|
||||||
|
mantissa += (mantissa & 1u);
|
||||||
|
mantissa >>= 1u;
|
||||||
|
// rounding up may produce the smallest normal number
|
||||||
|
power2 = (mantissa < (std::uint64_t{1} << mantissa_bits)) ? 0 : 1;
|
||||||
|
return mantissa | (static_cast<std::uint64_t>(power2) << mantissa_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
// a value exactly between two doubles rounds to even; this can only
|
||||||
|
// happen for small |q|, where 5^q is exact
|
||||||
|
if (product.low <= 1 && q >= -4 && q <= 23 && (mantissa & 3u) == 1
|
||||||
|
&& (mantissa << static_cast<unsigned>(shift)) == product.high)
|
||||||
|
{
|
||||||
|
mantissa &= ~std::uint64_t{1};
|
||||||
|
}
|
||||||
|
mantissa += (mantissa & 1u);
|
||||||
|
mantissa >>= 1u;
|
||||||
|
if (mantissa >= (std::uint64_t{2} << mantissa_bits))
|
||||||
|
{
|
||||||
|
mantissa = std::uint64_t{1} << mantissa_bits;
|
||||||
|
++power2;
|
||||||
|
}
|
||||||
|
mantissa &= ~(std::uint64_t{1} << mantissa_bits);
|
||||||
|
if (power2 >= 0x7FF)
|
||||||
|
{
|
||||||
|
return infinity;
|
||||||
|
}
|
||||||
|
return mantissa | (static_cast<std::uint64_t>(power2) << mantissa_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief parse a validated float token with the Eisel-Lemire algorithm
|
||||||
|
|
||||||
|
The significand is accumulated eight digits at a time where possible. A token
|
||||||
|
with more than 19 significant digits is truncated to w; the value then lies
|
||||||
|
in [w, w + 1) * 10^q, and it is only returned if both ends round to the same
|
||||||
|
double, which covers all but a few such tokens.
|
||||||
|
|
||||||
|
@param[in] first pointer to the first character of the token
|
||||||
|
@param[in] last pointer past the last character
|
||||||
|
@param[out] out the correctly rounded value on success (±infinity if it
|
||||||
|
overflows, like strtod)
|
||||||
|
@return true on success; false if strtod must decide
|
||||||
|
*/
|
||||||
|
inline bool parse_float_eisel_lemire(const char* first, const char* last, double& out) noexcept
|
||||||
|
{
|
||||||
|
const char* p = first;
|
||||||
|
const bool negative = (p != last && *p == '-');
|
||||||
|
if (negative)
|
||||||
|
{
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint64_t w = 0;
|
||||||
|
int digits = 0; // significant digits in w
|
||||||
|
std::int64_t exponent = 0;
|
||||||
|
bool truncated = false;
|
||||||
|
bool in_fraction = false;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
// eight digits at a time, as long as they fit into w
|
||||||
|
while (w != 0 && digits <= 19 - 8 && last - p >= 8)
|
||||||
|
{
|
||||||
|
const std::uint64_t v = read_eight_bytes(p);
|
||||||
|
if (!is_eight_digits(v))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
w = (w * 100000000u) + parse_eight_digits(v);
|
||||||
|
digits += 8;
|
||||||
|
exponent -= in_fraction ? 8 : 0;
|
||||||
|
p += 8;
|
||||||
|
}
|
||||||
|
if (p == last)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const char c = *p;
|
||||||
|
if (c >= '0' && c <= '9')
|
||||||
|
{
|
||||||
|
if (w == 0 && c == '0')
|
||||||
|
{
|
||||||
|
// leading zeros are not significant, but scale a fraction
|
||||||
|
exponent -= in_fraction ? 1 : 0;
|
||||||
|
}
|
||||||
|
else if (digits < 19)
|
||||||
|
{
|
||||||
|
w = (w * 10u) + static_cast<std::uint64_t>(c - '0');
|
||||||
|
++digits;
|
||||||
|
exponent -= in_fraction ? 1 : 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// dropped: the value lies between w and w + 1 (in units of
|
||||||
|
// the last kept digit) unless all dropped digits are zero
|
||||||
|
truncated = truncated || c != '0';
|
||||||
|
exponent += in_fraction ? 0 : 1;
|
||||||
|
}
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
else if (c == '.')
|
||||||
|
{
|
||||||
|
in_fraction = true;
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break; // 'e' or 'E'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p != last)
|
||||||
|
{
|
||||||
|
++p; // 'e' or 'E'
|
||||||
|
bool exp_negative = false;
|
||||||
|
if (p != last && (*p == '-' || *p == '+'))
|
||||||
|
{
|
||||||
|
exp_negative = (*p == '-');
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
std::int64_t exp_value = 0;
|
||||||
|
for (; p != last; ++p)
|
||||||
|
{
|
||||||
|
// saturate: any exponent beyond this under- or overflows anyway
|
||||||
|
if (exp_value < 100000)
|
||||||
|
{
|
||||||
|
exp_value = (exp_value * 10) + (*p - '0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exponent += exp_negative ? -exp_value : exp_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint64_t bits = 0;
|
||||||
|
if (w != 0)
|
||||||
|
{
|
||||||
|
bits = eisel_lemire(exponent, w);
|
||||||
|
if (truncated && (w + 1 == 0 || eisel_lemire(exponent, w + 1) != bits))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bits |= negative ? (std::uint64_t{1} << 63u) : 0u;
|
||||||
|
static_assert(sizeof(double) == sizeof(std::uint64_t), "double must have 64 bits");
|
||||||
|
std::memcpy(&out, &bits, sizeof(out));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Eisel-Lemire is only implemented for `double`
|
||||||
|
template<typename FloatType>
|
||||||
|
bool parse_float_eisel_lemire(const char* /*first*/, const char* /*last*/, FloatType& /*out*/) noexcept
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief check whether Clinger's fast path can still succeed for a float token
|
||||||
|
|
||||||
|
parse_float_fast() needs a significand below 2^53. A mantissa with 17 or
|
||||||
|
more significant digits is at least 10^16 and therefore always exceeds it,
|
||||||
|
so calling the fast path would walk the token one extra time only to
|
||||||
|
decline before strtod has to run anyway.
|
||||||
|
|
||||||
|
Significant digits are the mantissa's digits from the first nonzero one on;
|
||||||
|
the sign, the decimal point, leading zeros, and the exponent do not count.
|
||||||
|
The answer is derived from indices - the digits are not scanned again - so
|
||||||
|
this stays off the hot path of the number scanners.
|
||||||
|
|
||||||
|
@param[in] token the validated number token ('.' as decimal point)
|
||||||
|
@param[in] decimal_point_position index of the '.' in @a token, or
|
||||||
|
std::string::npos if there is none
|
||||||
|
@param[in] mantissa_end offset just past the last mantissa byte
|
||||||
|
@return false if parse_float_fast() is guaranteed to decline
|
||||||
|
*/
|
||||||
|
inline bool mantissa_fits_clinger(const char* token, std::size_t decimal_point_position, std::size_t mantissa_end) noexcept
|
||||||
|
{
|
||||||
|
// 10^16 already exceeds 2^53, so 17 digits can never fit
|
||||||
|
constexpr std::size_t limit = 17;
|
||||||
|
|
||||||
|
const std::size_t neg = (token[0] == '-') ? 1u : 0u;
|
||||||
|
const std::size_t has_dot = (decimal_point_position != std::string::npos) ? 1u : 0u;
|
||||||
|
// the JSON grammar restricts the integer part to "0" or [1-9][0-9]*, so
|
||||||
|
// a leading zero can only be a lone "0", which is not significant
|
||||||
|
const std::size_t lead_zero = (token[neg] == '0') ? 1u : 0u;
|
||||||
|
JSON_ASSERT(mantissa_end >= neg + has_dot + lead_zero);
|
||||||
|
std::size_t digits = mantissa_end - neg - has_dot - lead_zero;
|
||||||
|
|
||||||
|
if (JSON_HEDLEY_LIKELY(digits < limit))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only a number below 1 can carry further insignificant zeros, and only
|
||||||
|
// while the count stays at the limit does removing them change the
|
||||||
|
// answer - so this loop is skipped for all but a few tokens. The
|
||||||
|
// fraction is located through decimal_point_position rather than by
|
||||||
|
// searching '.'.
|
||||||
|
if (lead_zero != 0)
|
||||||
|
{
|
||||||
|
JSON_ASSERT(has_dot != 0); // an integer "0" cannot reach the limit
|
||||||
|
for (std::size_t i = decimal_point_position + 1;
|
||||||
|
digits >= limit && i < mantissa_end && token[i] == '0'; ++i)
|
||||||
|
{
|
||||||
|
--digits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return digits < limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief convert a validated float token without the C library, if possible
|
||||||
|
|
||||||
|
Tries std::from_chars (when available), Clinger's exact fast path (double
|
||||||
|
only, skipped when it cannot succeed), and the Eisel-Lemire algorithm (double
|
||||||
|
only).
|
||||||
|
|
||||||
|
@param[in] first pointer to the first character of the token
|
||||||
|
@param[in] last pointer past the last character
|
||||||
|
@param[in] decimal_point_position index of the '.' in the token, or
|
||||||
|
std::string::npos if there is none
|
||||||
|
@param[in] mantissa_end offset just past the last mantissa byte (the
|
||||||
|
index of 'e'/'E', or the token length)
|
||||||
|
@param[out] value the converted value on success
|
||||||
|
@return true if the value was converted; false if convert_float_locale_aware()
|
||||||
|
must convert it
|
||||||
|
*/
|
||||||
|
template<typename FloatType>
|
||||||
|
bool convert_float_fast(const char* first, const char* last, std::size_t decimal_point_position,
|
||||||
|
std::size_t mantissa_end, FloatType& value) noexcept
|
||||||
|
{
|
||||||
|
if (parse_float_from_chars(first, last, value))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Skipping a fast path that cannot succeed is lossless and saves a full
|
||||||
|
// extra pass over the token's bytes, which otherwise shows up on
|
||||||
|
// high-precision inputs such as canada.json
|
||||||
|
if (mantissa_fits_clinger(first, decimal_point_position, mantissa_end)
|
||||||
|
&& parse_float_fast(first, last, value))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return parse_float_eisel_lemire(first, last, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// std::strtof, std::strtod, or std::strtold, chosen by the type of @a f
|
||||||
|
JSON_HEDLEY_NON_NULL(2)
|
||||||
|
inline void strtof_by_type(float& f, const char* str, char** endptr) noexcept
|
||||||
|
{
|
||||||
|
f = std::strtof(str, endptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// std::strtof, std::strtod, or std::strtold, chosen by the type of @a f
|
||||||
|
JSON_HEDLEY_NON_NULL(2)
|
||||||
|
inline void strtof_by_type(double& f, const char* str, char** endptr) noexcept
|
||||||
|
{
|
||||||
|
f = std::strtod(str, endptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// std::strtof, std::strtod, or std::strtold, chosen by the type of @a f
|
||||||
|
JSON_HEDLEY_NON_NULL(2)
|
||||||
|
inline void strtof_by_type(long double& f, const char* str, char** endptr) noexcept
|
||||||
|
{
|
||||||
|
f = std::strtold(str, endptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return the decimal point of the current locale
|
||||||
|
inline char get_decimal_point() noexcept
|
||||||
|
{
|
||||||
|
const auto* loc = localeconv();
|
||||||
|
JSON_ASSERT(loc != nullptr);
|
||||||
|
return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief convert a validated float token with strtof/strtod/strtold
|
||||||
|
|
||||||
|
These functions expect the decimal point of the *current* locale, so it is
|
||||||
|
looked up right before the conversion instead of once when the lexer is
|
||||||
|
constructed: a locale change in between (by a parser callback, a SAX
|
||||||
|
handler, or another thread) must not truncate the value (#5198). The
|
||||||
|
token has been validated before, so if the conversion stops early and the
|
||||||
|
decimal point changed in the meantime, the locale changed between the
|
||||||
|
lookup and the call, and the conversion is repeated with the new decimal
|
||||||
|
point. If the decimal point did not change, a retry cannot succeed: the
|
||||||
|
locale's decimal point is not a single character (e.g., the two-byte
|
||||||
|
U+066B of ar_EG.UTF-8 or fa_IR.UTF-8) and cannot be substituted in place.
|
||||||
|
The value strtod parsed up to that point is kept, as before this change.
|
||||||
|
|
||||||
|
Note that changing the locale in another thread *while* strtod runs is
|
||||||
|
undefined behavior of the C library, which this function cannot prevent.
|
||||||
|
|
||||||
|
@param[in,out] token the token with '.' as decimal point; its
|
||||||
|
decimal point is replaced during the
|
||||||
|
conversion and restored afterwards
|
||||||
|
(data() must be NUL-terminated)
|
||||||
|
@param[in] decimal_point_position index of the '.' in @a token, or
|
||||||
|
std::string::npos if there is none
|
||||||
|
@param[out] value the converted value
|
||||||
|
*/
|
||||||
|
template<typename StringType, typename FloatType>
|
||||||
|
void convert_float_locale_aware(StringType& token, std::size_t decimal_point_position, FloatType& value)
|
||||||
|
{
|
||||||
|
const bool has_dot = decimal_point_position != std::string::npos;
|
||||||
|
char decimal_point = get_decimal_point();
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
const bool substitute = has_dot && decimal_point != '.';
|
||||||
|
if (substitute)
|
||||||
|
{
|
||||||
|
token[decimal_point_position] = static_cast<typename StringType::value_type>(decimal_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* endptr = nullptr; // NOLINT(misc-const-correctness,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||||
|
strtof_by_type(value, token.data(), &endptr);
|
||||||
|
|
||||||
|
if (substitute)
|
||||||
|
{
|
||||||
|
// the caller hands the token on (e.g. to the SAX interface) with '.'
|
||||||
|
token[decimal_point_position] = '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON_HEDLEY_LIKELY(endptr == token.data() + token.size()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// retry only if the locale changed; otherwise, this would loop forever
|
||||||
|
const char current_decimal_point = get_decimal_point();
|
||||||
|
if (current_decimal_point == decimal_point)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
decimal_point = current_decimal_point;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|||||||
@@ -54,7 +54,8 @@ using parser_callback_t =
|
|||||||
/*!
|
/*!
|
||||||
@brief syntax analysis
|
@brief syntax analysis
|
||||||
|
|
||||||
This class implements a recursive descent parser.
|
This class implements an iterative parser that keeps the open containers on
|
||||||
|
an explicit stack and reports what it reads as SAX events.
|
||||||
*/
|
*/
|
||||||
template<typename BasicJsonType, typename InputAdapterType>
|
template<typename BasicJsonType, typename InputAdapterType>
|
||||||
class parser
|
class parser
|
||||||
@@ -98,28 +99,9 @@ class parser
|
|||||||
if (callback)
|
if (callback)
|
||||||
{
|
{
|
||||||
json_sax_dom_callback_parser<BasicJsonType, InputAdapterType> sdp(result, callback, allow_exceptions, &m_lexer);
|
json_sax_dom_callback_parser<BasicJsonType, InputAdapterType> sdp(result, callback, allow_exceptions, &m_lexer);
|
||||||
sax_parse_internal(&sdp);
|
|
||||||
|
|
||||||
if (strict)
|
|
||||||
{
|
|
||||||
// in strict mode, input must be completely read
|
|
||||||
if (get_token() != token_type::end_of_input)
|
|
||||||
{
|
|
||||||
sdp.parse_error(m_lexer.get_position(),
|
|
||||||
m_lexer.get_token_string(),
|
|
||||||
parse_error::create(101, m_lexer.get_position(),
|
|
||||||
exception_message(token_type::end_of_input, "value"), nullptr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// the caller keeps using the input: position it right after
|
|
||||||
// the value by leaving the character that terminated it
|
|
||||||
m_lexer.release_lookahead();
|
|
||||||
}
|
|
||||||
|
|
||||||
// in case of an error, return a discarded value
|
// in case of an error, return a discarded value
|
||||||
if (sdp.is_errored())
|
if (!parse_dom(sdp, strict))
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
return;
|
return;
|
||||||
@@ -135,26 +117,9 @@ class parser
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
json_sax_dom_parser<BasicJsonType, InputAdapterType> sdp(result, allow_exceptions, &m_lexer);
|
json_sax_dom_parser<BasicJsonType, InputAdapterType> sdp(result, allow_exceptions, &m_lexer);
|
||||||
sax_parse_internal(&sdp);
|
|
||||||
|
|
||||||
if (strict)
|
|
||||||
{
|
|
||||||
// in strict mode, input must be completely read
|
|
||||||
if (get_token() != token_type::end_of_input)
|
|
||||||
{
|
|
||||||
sdp.parse_error(m_lexer.get_position(),
|
|
||||||
m_lexer.get_token_string(),
|
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// see above
|
|
||||||
m_lexer.release_lookahead();
|
|
||||||
}
|
|
||||||
|
|
||||||
// in case of an error, return a discarded value
|
// in case of an error, return a discarded value
|
||||||
if (sdp.is_errored())
|
if (!parse_dom(sdp, strict))
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
return;
|
return;
|
||||||
@@ -173,26 +138,59 @@ class parser
|
|||||||
bool accept(const bool strict = true)
|
bool accept(const bool strict = true)
|
||||||
{
|
{
|
||||||
json_sax_acceptor<BasicJsonType> sax_acceptor;
|
json_sax_acceptor<BasicJsonType> sax_acceptor;
|
||||||
return sax_parse(&sax_acceptor, strict);
|
return sax_parse_impl<false>(&sax_acceptor, strict);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief public SAX interface
|
||||||
|
|
||||||
|
If the SAX parser's parse_error() returns true, the parser recovers from
|
||||||
|
the error: it repairs the input and continues (see #3989).
|
||||||
|
|
||||||
|
@param[in] sax the SAX parser
|
||||||
|
@param[in] strict whether to expect the last token to be EOF
|
||||||
|
@return whether the input was parsed without errors and no SAX event
|
||||||
|
returned false
|
||||||
|
*/
|
||||||
template<typename SAX>
|
template<typename SAX>
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
JSON_HEDLEY_NON_NULL(2)
|
||||||
bool sax_parse(SAX* sax, const bool strict = true)
|
bool sax_parse(SAX* sax, const bool strict = true)
|
||||||
|
{
|
||||||
|
return sax_parse_impl<true>(sax, strict);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// what sax_parse_internal() does after an object key was expected
|
||||||
|
enum class next_step : std::uint8_t
|
||||||
|
{
|
||||||
|
/// stop parsing
|
||||||
|
stop,
|
||||||
|
/// parse a value that begins with last_token
|
||||||
|
parse_value,
|
||||||
|
/// evaluate the state of the innermost container, which reads
|
||||||
|
/// last_token again
|
||||||
|
evaluate_state
|
||||||
|
};
|
||||||
|
|
||||||
|
template<bool AllowRecovery, typename SAX>
|
||||||
|
JSON_HEDLEY_NON_NULL(2)
|
||||||
|
bool sax_parse_impl(SAX* sax, const bool strict)
|
||||||
{
|
{
|
||||||
(void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
|
(void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
|
||||||
const bool result = sax_parse_internal(sax);
|
const bool result = sax_parse_internal<AllowRecovery>(sax);
|
||||||
|
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
if (strict)
|
if (strict)
|
||||||
{
|
{
|
||||||
// strict mode: next byte must be EOF
|
// strict mode: next byte must be EOF; after recovering from an
|
||||||
if (get_token() != token_type::end_of_input)
|
// error, the end of the input may already have been read
|
||||||
|
if (last_token != token_type::end_of_input && get_token() != token_type::end_of_input)
|
||||||
{
|
{
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
// the value is complete, so there is nothing to recover
|
||||||
m_lexer.get_token_string(),
|
static_cast<void>(report_error(sax, parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr),
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
|
std::integral_constant<bool, AllowRecovery> {}));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -203,14 +201,63 @@ class parser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result && !error_reported;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
/*!
|
||||||
template<typename SAX>
|
@brief run a DOM SAX parser to completion and position the lexer
|
||||||
|
|
||||||
|
Shared by both branches of @ref parse(): builds no SAX parser itself,
|
||||||
|
but drives an already-constructed @a json_sax_dom_parser or
|
||||||
|
@ref json_sax_dom_callback_parser through @ref sax_parse_internal(),
|
||||||
|
then applies the strict-EOF check (reporting parse_error.101 through
|
||||||
|
@a sdp on failure) or, in non-strict mode, releases the lookahead so
|
||||||
|
the caller can keep reading the input right after the parsed value.
|
||||||
|
|
||||||
|
@param[in,out] sdp the DOM SAX parser to run
|
||||||
|
@param[in] strict whether to expect the last token to be EOF
|
||||||
|
@return whether @a sdp did not report an error
|
||||||
|
*/
|
||||||
|
template<typename DomSax>
|
||||||
|
bool parse_dom(DomSax& sdp, const bool strict)
|
||||||
|
{
|
||||||
|
sax_parse_internal<false>(&sdp);
|
||||||
|
|
||||||
|
if (strict)
|
||||||
|
{
|
||||||
|
// in strict mode, input must be completely read
|
||||||
|
if (get_token() != token_type::end_of_input)
|
||||||
|
{
|
||||||
|
sdp.parse_error(m_lexer.get_position(),
|
||||||
|
m_lexer.get_token_string(),
|
||||||
|
parse_error::create(101, m_lexer.get_position(),
|
||||||
|
exception_message(token_type::end_of_input, "value"), nullptr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// the caller keeps using the input: position it right after
|
||||||
|
// the value by leaving the character that terminated it
|
||||||
|
m_lexer.release_lookahead();
|
||||||
|
}
|
||||||
|
|
||||||
|
return !sdp.is_errored();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief parse a JSON value and pass it to a SAX parser
|
||||||
|
|
||||||
|
@tparam AllowRecovery whether to recover from an error if the SAX parser's
|
||||||
|
parse_error() returns true; false for the SAX parsers
|
||||||
|
of parse() and accept(), which never do, so that no
|
||||||
|
code for recovering is generated for them
|
||||||
|
*/
|
||||||
|
template<bool AllowRecovery, typename SAX>
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
JSON_HEDLEY_NON_NULL(2)
|
||||||
bool sax_parse_internal(SAX* sax)
|
bool sax_parse_internal(SAX* sax)
|
||||||
{
|
{
|
||||||
|
const std::integral_constant<bool, AllowRecovery> allow_recovery{};
|
||||||
|
|
||||||
// stack to remember the hierarchy of structured values we are parsing
|
// stack to remember the hierarchy of structured values we are parsing
|
||||||
// true = array; false = object
|
// true = array; false = object
|
||||||
std::vector<bool> states;
|
std::vector<bool> states;
|
||||||
@@ -241,12 +288,18 @@ class parser
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse key
|
// remember we are now inside an object
|
||||||
|
states.push_back(false);
|
||||||
|
|
||||||
|
// parse key (the steps of parse_key(), which are
|
||||||
|
// repeated here and below for speed)
|
||||||
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
|
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
|
||||||
{
|
{
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
if (!continue_after(key_error(sax, allow_recovery, false), skip_to_state_evaluation))
|
||||||
m_lexer.get_token_string(),
|
{
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
|
return false;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
|
||||||
{
|
{
|
||||||
@@ -256,14 +309,13 @@ class parser
|
|||||||
// parse separator (:)
|
// parse separator (:)
|
||||||
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
|
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
|
||||||
{
|
{
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
if (!continue_after(key_error(sax, allow_recovery, true), skip_to_state_evaluation))
|
||||||
m_lexer.get_token_string(),
|
{
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
|
return false;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// remember we are now inside an object
|
|
||||||
states.push_back(false);
|
|
||||||
|
|
||||||
// parse values
|
// parse values
|
||||||
get_token();
|
get_token();
|
||||||
continue;
|
continue;
|
||||||
@@ -299,9 +351,11 @@ class parser
|
|||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))
|
if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))
|
||||||
{
|
{
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
if (!overflow_error(sax, res, allow_recovery))
|
||||||
m_lexer.get_token_string(),
|
{
|
||||||
out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr));
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
|
if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
|
||||||
@@ -369,23 +423,63 @@ class parser
|
|||||||
case token_type::parse_error:
|
case token_type::parse_error:
|
||||||
{
|
{
|
||||||
// using "uninitialized" to avoid an "expected" message
|
// using "uninitialized" to avoid an "expected" message
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
if (!report_error(sax, parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr), allow_recovery))
|
||||||
m_lexer.get_token_string(),
|
{
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr));
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// recover: keep what can be read of the token
|
||||||
|
recover_token();
|
||||||
|
if (last_token != token_type::uninitialized)
|
||||||
|
{
|
||||||
|
// a string or a number
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (states.empty())
|
||||||
|
{
|
||||||
|
// look for the value after the garbage
|
||||||
|
if (!skip_to_value())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// nothing could be read
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!sax->null()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case token_type::end_of_input:
|
case token_type::end_of_input:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(m_lexer.get_position().chars_read_total == 1))
|
if (JSON_HEDLEY_UNLIKELY(m_lexer.get_position().chars_read_total == 1))
|
||||||
{
|
{
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
// there is nothing to recover
|
||||||
m_lexer.get_token_string(),
|
static_cast<void>(report_error(sax, parse_error::create(101, m_lexer.get_position(),
|
||||||
parse_error::create(101, m_lexer.get_position(),
|
"attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr), allow_recovery));
|
||||||
"attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
if (!report_error(sax, parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr), allow_recovery))
|
||||||
m_lexer.get_token_string(),
|
{
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// recover: the input ends where a value is missing
|
||||||
|
if (states.empty())
|
||||||
|
{
|
||||||
|
// there is no value
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!recover_missing_value(sax, states))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// the state evaluation reads the token again
|
||||||
|
m_lexer.unget_token();
|
||||||
|
skip_to_state_evaluation = true;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
case token_type::uninitialized:
|
case token_type::uninitialized:
|
||||||
case token_type::end_array:
|
case token_type::end_array:
|
||||||
@@ -395,9 +489,35 @@ class parser
|
|||||||
case token_type::literal_or_value:
|
case token_type::literal_or_value:
|
||||||
default: // the last token was unexpected
|
default: // the last token was unexpected
|
||||||
{
|
{
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
if (!report_error(sax, parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr), allow_recovery))
|
||||||
m_lexer.get_token_string(),
|
{
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// recover
|
||||||
|
if (states.empty())
|
||||||
|
{
|
||||||
|
// look for the value after the garbage
|
||||||
|
if (!skip_to_value())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (last_token == token_type::name_separator)
|
||||||
|
{
|
||||||
|
// a stray ':'; the value may follow
|
||||||
|
get_token();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!recover_missing_value(sax, states))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// the state evaluation reads the token again
|
||||||
|
m_lexer.unget_token();
|
||||||
|
skip_to_state_evaluation = true;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -439,17 +559,39 @@ class parser
|
|||||||
|
|
||||||
// We are done with this array. Before we can parse a
|
// We are done with this array. Before we can parse a
|
||||||
// new value, we need to evaluate the new state first.
|
// new value, we need to evaluate the new state first.
|
||||||
// By setting skip_to_state_evaluation to false, we
|
// By setting skip_to_state_evaluation to true, the next
|
||||||
// are effectively jumping to the beginning of this if.
|
// iteration skips parsing a value and evaluates the
|
||||||
|
// enclosing state directly.
|
||||||
JSON_ASSERT(!states.empty());
|
JSON_ASSERT(!states.empty());
|
||||||
states.pop_back();
|
states.pop_back();
|
||||||
skip_to_state_evaluation = true;
|
skip_to_state_evaluation = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
if (!report_error(sax, parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), nullptr), allow_recovery))
|
||||||
m_lexer.get_token_string(),
|
{
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), nullptr));
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// recover
|
||||||
|
if (last_token == token_type::end_of_input)
|
||||||
|
{
|
||||||
|
// the input ends inside the array
|
||||||
|
return close_containers(sax, states);
|
||||||
|
}
|
||||||
|
if (last_token == token_type::end_object)
|
||||||
|
{
|
||||||
|
// a wrong closing bracket closes the innermost container
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
states.pop_back();
|
||||||
|
skip_to_state_evaluation = true;
|
||||||
|
}
|
||||||
|
// otherwise, a missing ',' (or a stray ':', which value
|
||||||
|
// parsing drops): the next value begins here
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// states.back() is false -> object
|
// states.back() is false -> object
|
||||||
@@ -466,11 +608,12 @@ class parser
|
|||||||
// parse key
|
// parse key
|
||||||
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
|
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
|
||||||
{
|
{
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
if (!continue_after(key_error(sax, allow_recovery, false), skip_to_state_evaluation))
|
||||||
m_lexer.get_token_string(),
|
{
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
|
return false;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -479,9 +622,11 @@ class parser
|
|||||||
// parse separator (:)
|
// parse separator (:)
|
||||||
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
|
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
|
||||||
{
|
{
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
if (!continue_after(key_error(sax, allow_recovery, true), skip_to_state_evaluation))
|
||||||
m_lexer.get_token_string(),
|
{
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
|
return false;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse values
|
// parse values
|
||||||
@@ -500,20 +645,488 @@ class parser
|
|||||||
|
|
||||||
// We are done with this object. Before we can parse a
|
// We are done with this object. Before we can parse a
|
||||||
// new value, we need to evaluate the new state first.
|
// new value, we need to evaluate the new state first.
|
||||||
// By setting skip_to_state_evaluation to false, we
|
// By setting skip_to_state_evaluation to true, the next
|
||||||
// are effectively jumping to the beginning of this if.
|
// iteration skips parsing a value and evaluates the
|
||||||
|
// enclosing state directly.
|
||||||
JSON_ASSERT(!states.empty());
|
JSON_ASSERT(!states.empty());
|
||||||
states.pop_back();
|
states.pop_back();
|
||||||
skip_to_state_evaluation = true;
|
skip_to_state_evaluation = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
if (!report_error(sax, parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), nullptr), allow_recovery))
|
||||||
m_lexer.get_token_string(),
|
{
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), nullptr));
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// recover
|
||||||
|
if (last_token == token_type::end_of_input)
|
||||||
|
{
|
||||||
|
// the input ends inside the object
|
||||||
|
return close_containers(sax, states);
|
||||||
|
}
|
||||||
|
if (last_token == token_type::end_array)
|
||||||
|
{
|
||||||
|
// a wrong closing bracket closes the innermost container
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
states.pop_back();
|
||||||
|
skip_to_state_evaluation = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!continue_after(recover_member(sax, allow_recovery), skip_to_state_evaluation))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief continue sax_parse_internal() after a recovery
|
||||||
|
@return whether to continue parsing
|
||||||
|
*/
|
||||||
|
bool continue_after(const next_step step, bool& skip_to_state_evaluation)
|
||||||
|
{
|
||||||
|
if (step == next_step::evaluate_state)
|
||||||
|
{
|
||||||
|
// the state evaluation reads the token again
|
||||||
|
m_lexer.unget_token();
|
||||||
|
skip_to_state_evaluation = true;
|
||||||
|
}
|
||||||
|
return step != next_step::stop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the parser for parse() and accept() never recovers: stop parsing
|
||||||
|
static std::false_type continue_after(std::false_type /*step*/, bool& /*skip_to_state_evaluation*/) noexcept
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief parse an object key and the name separator (:) after it
|
||||||
|
|
||||||
|
last_token is the token where the key is expected. sax_parse_internal()
|
||||||
|
repeats these steps rather than calling this function, which is used
|
||||||
|
when recovering from an error.
|
||||||
|
|
||||||
|
@return next_step::parse_value if the value follows, with last_token its
|
||||||
|
first token; next_step::evaluate_state if the object's state is
|
||||||
|
to be evaluated after recovering from an error; next_step::stop
|
||||||
|
to stop parsing
|
||||||
|
*/
|
||||||
|
template<typename SAX>
|
||||||
|
next_step parse_key(SAX* sax)
|
||||||
|
{
|
||||||
|
const std::true_type allow_recovery{};
|
||||||
|
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
|
||||||
|
{
|
||||||
|
return key_error(sax, allow_recovery, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
|
||||||
|
{
|
||||||
|
return next_step::stop;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse separator (:)
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
|
||||||
|
{
|
||||||
|
return key_error(sax, allow_recovery, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the value begins with the next token
|
||||||
|
get_token();
|
||||||
|
return next_step::parse_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief report a number that is too large for number_float_t, and recover
|
||||||
|
from the error by passing the value on; the SAX parser gets the
|
||||||
|
number's text as well
|
||||||
|
|
||||||
|
This is a separate function, as reading other numbers is measurably
|
||||||
|
slower if the error is handled where they are read.
|
||||||
|
|
||||||
|
@param[in] sax the SAX parser
|
||||||
|
@param[in] value the value that is not finite
|
||||||
|
@return whether to continue parsing
|
||||||
|
*/
|
||||||
|
template<typename SAX, typename AllowRecovery>
|
||||||
|
bool overflow_error(SAX* sax, const number_float_t value, AllowRecovery allow_recovery)
|
||||||
|
{
|
||||||
|
if (!report_error(sax, out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr), allow_recovery))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return sax->number_float(value, m_lexer.get_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief report a missing key, or a missing name separator (:) after the
|
||||||
|
key; the parser for parse() and accept() never recovers
|
||||||
|
|
||||||
|
@param[in] key_read whether the key was read, so that the name separator
|
||||||
|
is missing
|
||||||
|
@return std::false_type, see report_error()
|
||||||
|
*/
|
||||||
|
template<typename SAX>
|
||||||
|
std::false_type key_error(SAX* sax, std::false_type allow_recovery, const bool key_read)
|
||||||
|
{
|
||||||
|
return report_error(sax, parse_error::create(101, m_lexer.get_position(), key_read
|
||||||
|
? exception_message(token_type::name_separator, "object separator")
|
||||||
|
: exception_message(token_type::value_string, "object key"), nullptr), allow_recovery);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief report a missing key, or a missing name separator (:) after the
|
||||||
|
key, and recover from it
|
||||||
|
|
||||||
|
@param[in] key_read whether the key was read, so that the name separator
|
||||||
|
is missing
|
||||||
|
*/
|
||||||
|
template<typename SAX>
|
||||||
|
next_step key_error(SAX* sax, std::true_type allow_recovery, const bool key_read)
|
||||||
|
{
|
||||||
|
if (!key_read)
|
||||||
|
{
|
||||||
|
if (!report_error(sax, parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr), allow_recovery))
|
||||||
|
{
|
||||||
|
return next_step::stop;
|
||||||
|
}
|
||||||
|
return recover_key(sax);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!report_error(sax, parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr), allow_recovery))
|
||||||
|
{
|
||||||
|
return next_step::stop;
|
||||||
|
}
|
||||||
|
return recover_name_separator(sax);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////
|
||||||
|
// error recovery
|
||||||
|
/////////////////////
|
||||||
|
|
||||||
|
/*
|
||||||
|
The functions below repair an error after the SAX parser's parse_error()
|
||||||
|
returned true (see #3989). Each mistake is repaired by the smallest local
|
||||||
|
edit: a missing ',' or ':' is inserted, a stray token is removed, what can
|
||||||
|
be read of an invalid string or number is kept (see
|
||||||
|
lexer::recover_token()), a missing value becomes null, a wrong closing
|
||||||
|
bracket closes the innermost container, and the end of the input closes
|
||||||
|
all of them. The events stay balanced, and every key() is followed by
|
||||||
|
exactly one value.
|
||||||
|
|
||||||
|
A repair hands a token to the state evaluation, by returning it to the
|
||||||
|
lexer (lexer::unget_token()) so that the state evaluation reads it again,
|
||||||
|
only if it is ',', ']', '}', or the end of the input. The state evaluation
|
||||||
|
hands a token to value or key parsing only if it is none of them, so a
|
||||||
|
token is never handed back and forth. Every other step reads a token or
|
||||||
|
closes a container, so parsing always ends.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief report an error to the SAX parser; the parser for parse() and
|
||||||
|
accept() never recovers
|
||||||
|
|
||||||
|
@return std::false_type rather than false: its value is known where the
|
||||||
|
function is called even if the call is not inlined, so the code
|
||||||
|
for recovering is not generated
|
||||||
|
*/
|
||||||
|
template<typename SAX, typename Exception>
|
||||||
|
std::false_type report_error(SAX* sax, const Exception& ex, std::false_type /*allow_recovery*/)
|
||||||
|
{
|
||||||
|
error_reported = true;
|
||||||
|
static_cast<void>(sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), ex));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief report an error to the SAX parser
|
||||||
|
@return whether to recover from the error
|
||||||
|
*/
|
||||||
|
template<typename SAX, typename Exception>
|
||||||
|
bool report_error(SAX* sax, const Exception& ex, std::true_type /*allow_recovery*/)
|
||||||
|
{
|
||||||
|
const std::size_t position = m_lexer.get_position().chars_read_total;
|
||||||
|
if (error_reported && position == last_error_position && last_token == last_error_token)
|
||||||
|
{
|
||||||
|
// a repair handed on the token of the error it repaired; the
|
||||||
|
// token was reported already, and the SAX parser asked to recover
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_reported = true;
|
||||||
|
last_error_position = position;
|
||||||
|
last_error_token = last_token;
|
||||||
|
|
||||||
|
if (!sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), ex))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the token string of the next error begins here
|
||||||
|
m_lexer.restart_token_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief keep what can be read of the token that the lexer rejected
|
||||||
|
|
||||||
|
The error was reported for the rejected token, so it is not reported again
|
||||||
|
for the token it is repaired to (see lexer::recover_token()).
|
||||||
|
*/
|
||||||
|
token_type recover_token()
|
||||||
|
{
|
||||||
|
last_token = m_lexer.recover_token();
|
||||||
|
last_error_position = m_lexer.get_position().chars_read_total;
|
||||||
|
last_error_token = last_token;
|
||||||
|
return last_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// pass the end events of all open containers
|
||||||
|
template<typename SAX>
|
||||||
|
bool close_containers(SAX* sax, std::vector<bool>& states)
|
||||||
|
{
|
||||||
|
while (!states.empty())
|
||||||
|
{
|
||||||
|
const bool is_array = states.back();
|
||||||
|
states.pop_back();
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(is_array ? !sax->end_array() : !sax->end_object()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief read tokens until one begins a value, skipping everything before
|
||||||
|
the top-level value
|
||||||
|
@return whether a value begins with last_token
|
||||||
|
*/
|
||||||
|
bool skip_to_value()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
switch (get_token())
|
||||||
|
{
|
||||||
|
case token_type::begin_array:
|
||||||
|
case token_type::begin_object:
|
||||||
|
case token_type::literal_false:
|
||||||
|
case token_type::literal_null:
|
||||||
|
case token_type::literal_true:
|
||||||
|
case token_type::value_float:
|
||||||
|
case token_type::value_integer:
|
||||||
|
case token_type::value_string:
|
||||||
|
case token_type::value_unsigned:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case token_type::end_of_input:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case token_type::parse_error:
|
||||||
|
recover_token();
|
||||||
|
if (last_token != token_type::uninitialized)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case token_type::uninitialized:
|
||||||
|
case token_type::end_array:
|
||||||
|
case token_type::end_object:
|
||||||
|
case token_type::name_separator:
|
||||||
|
case token_type::value_separator:
|
||||||
|
case token_type::literal_or_value:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief skip the rest of an object member that cannot be read
|
||||||
|
|
||||||
|
Reads tokens, beginning with last_token, until a ',', '}', or ']' that is
|
||||||
|
not inside a container that begins in the skipped tokens, or the end of
|
||||||
|
the input.
|
||||||
|
*/
|
||||||
|
void skip_member()
|
||||||
|
{
|
||||||
|
std::size_t depth = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
switch (last_token)
|
||||||
|
{
|
||||||
|
case token_type::begin_array:
|
||||||
|
case token_type::begin_object:
|
||||||
|
++depth;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case token_type::end_array:
|
||||||
|
case token_type::end_object:
|
||||||
|
if (depth == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
--depth;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case token_type::value_separator:
|
||||||
|
if (depth == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case token_type::end_of_input:
|
||||||
|
return;
|
||||||
|
|
||||||
|
case token_type::parse_error:
|
||||||
|
recover_token();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case token_type::uninitialized:
|
||||||
|
case token_type::literal_true:
|
||||||
|
case token_type::literal_false:
|
||||||
|
case token_type::literal_null:
|
||||||
|
case token_type::value_string:
|
||||||
|
case token_type::value_unsigned:
|
||||||
|
case token_type::value_integer:
|
||||||
|
case token_type::value_float:
|
||||||
|
case token_type::name_separator:
|
||||||
|
case token_type::literal_or_value:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
get_token();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief pass a value where it is missing
|
||||||
|
|
||||||
|
last_token is ',', ']', '}', or the end of the input, where a value was
|
||||||
|
expected. In an object, the key gets null; in an array, a ',' where a
|
||||||
|
value is missing stands for null (as in JavaScript), while an array that
|
||||||
|
ends there just ends.
|
||||||
|
*/
|
||||||
|
template<typename SAX>
|
||||||
|
bool recover_missing_value(SAX* sax, const std::vector<bool>& states)
|
||||||
|
{
|
||||||
|
JSON_ASSERT(!states.empty());
|
||||||
|
if (!states.back() || last_token == token_type::value_separator)
|
||||||
|
{
|
||||||
|
return sax->null();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// recover from a missing key; last_token is where it was expected
|
||||||
|
template<typename SAX>
|
||||||
|
next_step recover_key(SAX* sax)
|
||||||
|
{
|
||||||
|
switch (last_token)
|
||||||
|
{
|
||||||
|
case token_type::value_separator:
|
||||||
|
case token_type::end_object:
|
||||||
|
case token_type::end_array:
|
||||||
|
case token_type::end_of_input:
|
||||||
|
// no member: the object's state handles the token
|
||||||
|
return next_step::evaluate_state;
|
||||||
|
|
||||||
|
case token_type::parse_error:
|
||||||
|
recover_token();
|
||||||
|
if (last_token == token_type::value_string)
|
||||||
|
{
|
||||||
|
// a key that could be repaired
|
||||||
|
return parse_key(sax);
|
||||||
|
}
|
||||||
|
skip_member();
|
||||||
|
return next_step::evaluate_state;
|
||||||
|
|
||||||
|
case token_type::uninitialized:
|
||||||
|
case token_type::literal_true:
|
||||||
|
case token_type::literal_false:
|
||||||
|
case token_type::literal_null:
|
||||||
|
case token_type::value_string:
|
||||||
|
case token_type::value_unsigned:
|
||||||
|
case token_type::value_integer:
|
||||||
|
case token_type::value_float:
|
||||||
|
case token_type::begin_array:
|
||||||
|
case token_type::begin_object:
|
||||||
|
case token_type::name_separator:
|
||||||
|
case token_type::literal_or_value:
|
||||||
|
default:
|
||||||
|
// a member without a key
|
||||||
|
skip_member();
|
||||||
|
return next_step::evaluate_state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// recover from a missing name separator (:) after the key; last_token
|
||||||
|
/// is where it was expected
|
||||||
|
template<typename SAX>
|
||||||
|
next_step recover_name_separator(SAX* sax)
|
||||||
|
{
|
||||||
|
switch (last_token)
|
||||||
|
{
|
||||||
|
case token_type::value_separator:
|
||||||
|
case token_type::end_object:
|
||||||
|
case token_type::end_array:
|
||||||
|
case token_type::end_of_input:
|
||||||
|
// the value is missing as well
|
||||||
|
return sax->null() ? next_step::evaluate_state : next_step::stop;
|
||||||
|
|
||||||
|
case token_type::uninitialized:
|
||||||
|
case token_type::literal_true:
|
||||||
|
case token_type::literal_false:
|
||||||
|
case token_type::literal_null:
|
||||||
|
case token_type::value_string:
|
||||||
|
case token_type::value_unsigned:
|
||||||
|
case token_type::value_integer:
|
||||||
|
case token_type::value_float:
|
||||||
|
case token_type::begin_array:
|
||||||
|
case token_type::begin_object:
|
||||||
|
case token_type::name_separator:
|
||||||
|
case token_type::parse_error:
|
||||||
|
case token_type::literal_or_value:
|
||||||
|
default:
|
||||||
|
// a missing ':'; the value begins here
|
||||||
|
return next_step::parse_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// recover from a token after an object member that is neither ',' nor
|
||||||
|
/// '}' (nor ']' or the end of the input, which the caller handles)
|
||||||
|
template<typename SAX>
|
||||||
|
next_step recover_member(SAX* sax, std::true_type /*allow_recovery*/)
|
||||||
|
{
|
||||||
|
if (last_token == token_type::parse_error)
|
||||||
|
{
|
||||||
|
recover_token();
|
||||||
|
}
|
||||||
|
if (last_token == token_type::value_string)
|
||||||
|
{
|
||||||
|
// a missing ','; the next key begins here
|
||||||
|
return parse_key(sax);
|
||||||
|
}
|
||||||
|
skip_member();
|
||||||
|
return next_step::evaluate_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the parser for parse() and accept() never recovers (and does not come
|
||||||
|
/// here, as report_error() returned false)
|
||||||
|
template<typename SAX>
|
||||||
|
std::false_type recover_member(SAX* /*sax*/, std::false_type /*allow_recovery*/) const noexcept
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
/// get next token from lexer
|
/// get next token from lexer
|
||||||
token_type get_token()
|
token_type get_token()
|
||||||
{
|
{
|
||||||
@@ -560,6 +1173,12 @@ class parser
|
|||||||
const bool allow_exceptions = true;
|
const bool allow_exceptions = true;
|
||||||
/// whether trailing commas in objects and arrays should be ignored (true) or signaled as errors (false)
|
/// whether trailing commas in objects and arrays should be ignored (true) or signaled as errors (false)
|
||||||
const bool ignore_trailing_commas = false;
|
const bool ignore_trailing_commas = false;
|
||||||
|
/// whether an error was reported to the SAX parser
|
||||||
|
bool error_reported = false;
|
||||||
|
/// the position of the last reported error
|
||||||
|
std::size_t last_error_position = 0;
|
||||||
|
/// the token of the last reported error
|
||||||
|
token_type last_error_token = token_type::uninitialized;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|||||||
@@ -0,0 +1,371 @@
|
|||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | JSON for Modern C++
|
||||||
|
// | | |__ | | | | | | version 3.12.0
|
||||||
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2021 The fast_float authors <https://github.com/fastfloat/fast_float>
|
||||||
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array> // array
|
||||||
|
#include <cstdint> // int64_t, uint64_t
|
||||||
|
|
||||||
|
#include <nlohmann/detail/abi_macros.hpp>
|
||||||
|
|
||||||
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
/// the range of decimal exponents covered by pow5_128()
|
||||||
|
constexpr std::int64_t pow5_128_smallest_power = -342;
|
||||||
|
constexpr std::int64_t pow5_128_largest_power = 308;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief 128-bit approximations of 5^q for q in [-342, 308]
|
||||||
|
|
||||||
|
Entry q (at index 2 * (q + 342)) holds the most significant 128 bits of 5^q,
|
||||||
|
normalized so that the highest bit is set: for q >= 0 the truncated value, for
|
||||||
|
q < 0 the value rounded up. This is the table of fast_float (Daniel Lemire and
|
||||||
|
contributors, used under the MIT license), generated like its
|
||||||
|
script/table_generation.py; unit-class_lexer.cpp recomputes every entry.
|
||||||
|
*/
|
||||||
|
inline const std::array<std::uint64_t, 1302>& pow5_128() noexcept
|
||||||
|
{
|
||||||
|
static const std::array<std::uint64_t, 1302> table =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
0xeef453d6923bd65au, 0x113faa2906a13b3fu, 0x9558b4661b6565f8u, 0x4ac7ca59a424c507u,
|
||||||
|
0xbaaee17fa23ebf76u, 0x5d79bcf00d2df649u, 0xe95a99df8ace6f53u, 0xf4d82c2c107973dcu,
|
||||||
|
0x91d8a02bb6c10594u, 0x79071b9b8a4be869u, 0xb64ec836a47146f9u, 0x9748e2826cdee284u,
|
||||||
|
0xe3e27a444d8d98b7u, 0xfd1b1b2308169b25u, 0x8e6d8c6ab0787f72u, 0xfe30f0f5e50e20f7u,
|
||||||
|
0xb208ef855c969f4fu, 0xbdbd2d335e51a935u, 0xde8b2b66b3bc4723u, 0xad2c788035e61382u,
|
||||||
|
0x8b16fb203055ac76u, 0x4c3bcb5021afcc31u, 0xaddcb9e83c6b1793u, 0xdf4abe242a1bbf3du,
|
||||||
|
0xd953e8624b85dd78u, 0xd71d6dad34a2af0du, 0x87d4713d6f33aa6bu, 0x8672648c40e5ad68u,
|
||||||
|
0xa9c98d8ccb009506u, 0x680efdaf511f18c2u, 0xd43bf0effdc0ba48u, 0x0212bd1b2566def2u,
|
||||||
|
0x84a57695fe98746du, 0x014bb630f7604b57u, 0xa5ced43b7e3e9188u, 0x419ea3bd35385e2du,
|
||||||
|
0xcf42894a5dce35eau, 0x52064cac828675b9u, 0x818995ce7aa0e1b2u, 0x7343efebd1940993u,
|
||||||
|
0xa1ebfb4219491a1fu, 0x1014ebe6c5f90bf8u, 0xca66fa129f9b60a6u, 0xd41a26e077774ef6u,
|
||||||
|
0xfd00b897478238d0u, 0x8920b098955522b4u, 0x9e20735e8cb16382u, 0x55b46e5f5d5535b0u,
|
||||||
|
0xc5a890362fddbc62u, 0xeb2189f734aa831du, 0xf712b443bbd52b7bu, 0xa5e9ec7501d523e4u,
|
||||||
|
0x9a6bb0aa55653b2du, 0x47b233c92125366eu, 0xc1069cd4eabe89f8u, 0x999ec0bb696e840au,
|
||||||
|
0xf148440a256e2c76u, 0xc00670ea43ca250du, 0x96cd2a865764dbcau, 0x380406926a5e5728u,
|
||||||
|
0xbc807527ed3e12bcu, 0xc605083704f5ecf2u, 0xeba09271e88d976bu, 0xf7864a44c633682eu,
|
||||||
|
0x93445b8731587ea3u, 0x7ab3ee6afbe0211du, 0xb8157268fdae9e4cu, 0x5960ea05bad82964u,
|
||||||
|
0xe61acf033d1a45dfu, 0x6fb92487298e33bdu, 0x8fd0c16206306babu, 0xa5d3b6d479f8e056u,
|
||||||
|
0xb3c4f1ba87bc8696u, 0x8f48a4899877186cu, 0xe0b62e2929aba83cu, 0x331acdabfe94de87u,
|
||||||
|
0x8c71dcd9ba0b4925u, 0x9ff0c08b7f1d0b14u, 0xaf8e5410288e1b6fu, 0x07ecf0ae5ee44dd9u,
|
||||||
|
0xdb71e91432b1a24au, 0xc9e82cd9f69d6150u, 0x892731ac9faf056eu, 0xbe311c083a225cd2u,
|
||||||
|
0xab70fe17c79ac6cau, 0x6dbd630a48aaf406u, 0xd64d3d9db981787du, 0x092cbbccdad5b108u,
|
||||||
|
0x85f0468293f0eb4eu, 0x25bbf56008c58ea5u, 0xa76c582338ed2621u, 0xaf2af2b80af6f24eu,
|
||||||
|
0xd1476e2c07286faau, 0x1af5af660db4aee1u, 0x82cca4db847945cau, 0x50d98d9fc890ed4du,
|
||||||
|
0xa37fce126597973cu, 0xe50ff107bab528a0u, 0xcc5fc196fefd7d0cu, 0x1e53ed49a96272c8u,
|
||||||
|
0xff77b1fcbebcdc4fu, 0x25e8e89c13bb0f7au, 0x9faacf3df73609b1u, 0x77b191618c54e9acu,
|
||||||
|
0xc795830d75038c1du, 0xd59df5b9ef6a2417u, 0xf97ae3d0d2446f25u, 0x4b0573286b44ad1du,
|
||||||
|
0x9becce62836ac577u, 0x4ee367f9430aec32u, 0xc2e801fb244576d5u, 0x229c41f793cda73fu,
|
||||||
|
0xf3a20279ed56d48au, 0x6b43527578c1110fu, 0x9845418c345644d6u, 0x830a13896b78aaa9u,
|
||||||
|
0xbe5691ef416bd60cu, 0x23cc986bc656d553u, 0xedec366b11c6cb8fu, 0x2cbfbe86b7ec8aa8u,
|
||||||
|
0x94b3a202eb1c3f39u, 0x7bf7d71432f3d6a9u, 0xb9e08a83a5e34f07u, 0xdaf5ccd93fb0cc53u,
|
||||||
|
0xe858ad248f5c22c9u, 0xd1b3400f8f9cff68u, 0x91376c36d99995beu, 0x23100809b9c21fa1u,
|
||||||
|
0xb58547448ffffb2du, 0xabd40a0c2832a78au, 0xe2e69915b3fff9f9u, 0x16c90c8f323f516cu,
|
||||||
|
0x8dd01fad907ffc3bu, 0xae3da7d97f6792e3u, 0xb1442798f49ffb4au, 0x99cd11cfdf41779cu,
|
||||||
|
0xdd95317f31c7fa1du, 0x40405643d711d583u, 0x8a7d3eef7f1cfc52u, 0x482835ea666b2572u,
|
||||||
|
0xad1c8eab5ee43b66u, 0xda3243650005eecfu, 0xd863b256369d4a40u, 0x90bed43e40076a82u,
|
||||||
|
0x873e4f75e2224e68u, 0x5a7744a6e804a291u, 0xa90de3535aaae202u, 0x711515d0a205cb36u,
|
||||||
|
0xd3515c2831559a83u, 0x0d5a5b44ca873e03u, 0x8412d9991ed58091u, 0xe858790afe9486c2u,
|
||||||
|
0xa5178fff668ae0b6u, 0x626e974dbe39a872u, 0xce5d73ff402d98e3u, 0xfb0a3d212dc8128fu,
|
||||||
|
0x80fa687f881c7f8eu, 0x7ce66634bc9d0b99u, 0xa139029f6a239f72u, 0x1c1fffc1ebc44e80u,
|
||||||
|
0xc987434744ac874eu, 0xa327ffb266b56220u, 0xfbe9141915d7a922u, 0x4bf1ff9f0062baa8u,
|
||||||
|
0x9d71ac8fada6c9b5u, 0x6f773fc3603db4a9u, 0xc4ce17b399107c22u, 0xcb550fb4384d21d3u,
|
||||||
|
0xf6019da07f549b2bu, 0x7e2a53a146606a48u, 0x99c102844f94e0fbu, 0x2eda7444cbfc426du,
|
||||||
|
0xc0314325637a1939u, 0xfa911155fefb5308u, 0xf03d93eebc589f88u, 0x793555ab7eba27cau,
|
||||||
|
0x96267c7535b763b5u, 0x4bc1558b2f3458deu, 0xbbb01b9283253ca2u, 0x9eb1aaedfb016f16u,
|
||||||
|
0xea9c227723ee8bcbu, 0x465e15a979c1cadcu, 0x92a1958a7675175fu, 0x0bfacd89ec191ec9u,
|
||||||
|
0xb749faed14125d36u, 0xcef980ec671f667bu, 0xe51c79a85916f484u, 0x82b7e12780e7401au,
|
||||||
|
0x8f31cc0937ae58d2u, 0xd1b2ecb8b0908810u, 0xb2fe3f0b8599ef07u, 0x861fa7e6dcb4aa15u,
|
||||||
|
0xdfbdcece67006ac9u, 0x67a791e093e1d49au, 0x8bd6a141006042bdu, 0xe0c8bb2c5c6d24e0u,
|
||||||
|
0xaecc49914078536du, 0x58fae9f773886e18u, 0xda7f5bf590966848u, 0xaf39a475506a899eu,
|
||||||
|
0x888f99797a5e012du, 0x6d8406c952429603u, 0xaab37fd7d8f58178u, 0xc8e5087ba6d33b83u,
|
||||||
|
0xd5605fcdcf32e1d6u, 0xfb1e4a9a90880a64u, 0x855c3be0a17fcd26u, 0x5cf2eea09a55067fu,
|
||||||
|
0xa6b34ad8c9dfc06fu, 0xf42faa48c0ea481eu, 0xd0601d8efc57b08bu, 0xf13b94daf124da26u,
|
||||||
|
0x823c12795db6ce57u, 0x76c53d08d6b70858u, 0xa2cb1717b52481edu, 0x54768c4b0c64ca6eu,
|
||||||
|
0xcb7ddcdda26da268u, 0xa9942f5dcf7dfd09u, 0xfe5d54150b090b02u, 0xd3f93b35435d7c4cu,
|
||||||
|
0x9efa548d26e5a6e1u, 0xc47bc5014a1a6dafu, 0xc6b8e9b0709f109au, 0x359ab6419ca1091bu,
|
||||||
|
0xf867241c8cc6d4c0u, 0xc30163d203c94b62u, 0x9b407691d7fc44f8u, 0x79e0de63425dcf1du,
|
||||||
|
0xc21094364dfb5636u, 0x985915fc12f542e4u, 0xf294b943e17a2bc4u, 0x3e6f5b7b17b2939du,
|
||||||
|
0x979cf3ca6cec5b5au, 0xa705992ceecf9c42u, 0xbd8430bd08277231u, 0x50c6ff782a838353u,
|
||||||
|
0xece53cec4a314ebdu, 0xa4f8bf5635246428u, 0x940f4613ae5ed136u, 0x871b7795e136be99u,
|
||||||
|
0xb913179899f68584u, 0x28e2557b59846e3fu, 0xe757dd7ec07426e5u, 0x331aeada2fe589cfu,
|
||||||
|
0x9096ea6f3848984fu, 0x3ff0d2c85def7621u, 0xb4bca50b065abe63u, 0x0fed077a756b53a9u,
|
||||||
|
0xe1ebce4dc7f16dfbu, 0xd3e8495912c62894u, 0x8d3360f09cf6e4bdu, 0x64712dd7abbbd95cu,
|
||||||
|
0xb080392cc4349decu, 0xbd8d794d96aacfb3u, 0xdca04777f541c567u, 0xecf0d7a0fc5583a0u,
|
||||||
|
0x89e42caaf9491b60u, 0xf41686c49db57244u, 0xac5d37d5b79b6239u, 0x311c2875c522ced5u,
|
||||||
|
0xd77485cb25823ac7u, 0x7d633293366b828bu, 0x86a8d39ef77164bcu, 0xae5dff9c02033197u,
|
||||||
|
0xa8530886b54dbdebu, 0xd9f57f830283fdfcu, 0xd267caa862a12d66u, 0xd072df63c324fd7bu,
|
||||||
|
0x8380dea93da4bc60u, 0x4247cb9e59f71e6du, 0xa46116538d0deb78u, 0x52d9be85f074e608u,
|
||||||
|
0xcd795be870516656u, 0x67902e276c921f8bu, 0x806bd9714632dff6u, 0x00ba1cd8a3db53b6u,
|
||||||
|
0xa086cfcd97bf97f3u, 0x80e8a40eccd228a4u, 0xc8a883c0fdaf7df0u, 0x6122cd128006b2cdu,
|
||||||
|
0xfad2a4b13d1b5d6cu, 0x796b805720085f81u, 0x9cc3a6eec6311a63u, 0xcbe3303674053bb0u,
|
||||||
|
0xc3f490aa77bd60fcu, 0xbedbfc4411068a9cu, 0xf4f1b4d515acb93bu, 0xee92fb5515482d44u,
|
||||||
|
0x991711052d8bf3c5u, 0x751bdd152d4d1c4au, 0xbf5cd54678eef0b6u, 0xd262d45a78a0635du,
|
||||||
|
0xef340a98172aace4u, 0x86fb897116c87c34u, 0x9580869f0e7aac0eu, 0xd45d35e6ae3d4da0u,
|
||||||
|
0xbae0a846d2195712u, 0x8974836059cca109u, 0xe998d258869facd7u, 0x2bd1a438703fc94bu,
|
||||||
|
0x91ff83775423cc06u, 0x7b6306a34627ddcfu, 0xb67f6455292cbf08u, 0x1a3bc84c17b1d542u,
|
||||||
|
0xe41f3d6a7377eecau, 0x20caba5f1d9e4a93u, 0x8e938662882af53eu, 0x547eb47b7282ee9cu,
|
||||||
|
0xb23867fb2a35b28du, 0xe99e619a4f23aa43u, 0xdec681f9f4c31f31u, 0x6405fa00e2ec94d4u,
|
||||||
|
0x8b3c113c38f9f37eu, 0xde83bc408dd3dd04u, 0xae0b158b4738705eu, 0x9624ab50b148d445u,
|
||||||
|
0xd98ddaee19068c76u, 0x3badd624dd9b0957u, 0x87f8a8d4cfa417c9u, 0xe54ca5d70a80e5d6u,
|
||||||
|
0xa9f6d30a038d1dbcu, 0x5e9fcf4ccd211f4cu, 0xd47487cc8470652bu, 0x7647c3200069671fu,
|
||||||
|
0x84c8d4dfd2c63f3bu, 0x29ecd9f40041e073u, 0xa5fb0a17c777cf09u, 0xf468107100525890u,
|
||||||
|
0xcf79cc9db955c2ccu, 0x7182148d4066eeb4u, 0x81ac1fe293d599bfu, 0xc6f14cd848405530u,
|
||||||
|
0xa21727db38cb002fu, 0xb8ada00e5a506a7cu, 0xca9cf1d206fdc03bu, 0xa6d90811f0e4851cu,
|
||||||
|
0xfd442e4688bd304au, 0x908f4a166d1da663u, 0x9e4a9cec15763e2eu, 0x9a598e4e043287feu,
|
||||||
|
0xc5dd44271ad3cdbau, 0x40eff1e1853f29fdu, 0xf7549530e188c128u, 0xd12bee59e68ef47cu,
|
||||||
|
0x9a94dd3e8cf578b9u, 0x82bb74f8301958ceu, 0xc13a148e3032d6e7u, 0xe36a52363c1faf01u,
|
||||||
|
0xf18899b1bc3f8ca1u, 0xdc44e6c3cb279ac1u, 0x96f5600f15a7b7e5u, 0x29ab103a5ef8c0b9u,
|
||||||
|
0xbcb2b812db11a5deu, 0x7415d448f6b6f0e7u, 0xebdf661791d60f56u, 0x111b495b3464ad21u,
|
||||||
|
0x936b9fcebb25c995u, 0xcab10dd900beec34u, 0xb84687c269ef3bfbu, 0x3d5d514f40eea742u,
|
||||||
|
0xe65829b3046b0afau, 0x0cb4a5a3112a5112u, 0x8ff71a0fe2c2e6dcu, 0x47f0e785eaba72abu,
|
||||||
|
0xb3f4e093db73a093u, 0x59ed216765690f56u, 0xe0f218b8d25088b8u, 0x306869c13ec3532cu,
|
||||||
|
0x8c974f7383725573u, 0x1e414218c73a13fbu, 0xafbd2350644eeacfu, 0xe5d1929ef90898fau,
|
||||||
|
0xdbac6c247d62a583u, 0xdf45f746b74abf39u, 0x894bc396ce5da772u, 0x6b8bba8c328eb783u,
|
||||||
|
0xab9eb47c81f5114fu, 0x066ea92f3f326564u, 0xd686619ba27255a2u, 0xc80a537b0efefebdu,
|
||||||
|
0x8613fd0145877585u, 0xbd06742ce95f5f36u, 0xa798fc4196e952e7u, 0x2c48113823b73704u,
|
||||||
|
0xd17f3b51fca3a7a0u, 0xf75a15862ca504c5u, 0x82ef85133de648c4u, 0x9a984d73dbe722fbu,
|
||||||
|
0xa3ab66580d5fdaf5u, 0xc13e60d0d2e0ebbau, 0xcc963fee10b7d1b3u, 0x318df905079926a8u,
|
||||||
|
0xffbbcfe994e5c61fu, 0xfdf17746497f7052u, 0x9fd561f1fd0f9bd3u, 0xfeb6ea8bedefa633u,
|
||||||
|
0xc7caba6e7c5382c8u, 0xfe64a52ee96b8fc0u, 0xf9bd690a1b68637bu, 0x3dfdce7aa3c673b0u,
|
||||||
|
0x9c1661a651213e2du, 0x06bea10ca65c084eu, 0xc31bfa0fe5698db8u, 0x486e494fcff30a62u,
|
||||||
|
0xf3e2f893dec3f126u, 0x5a89dba3c3efccfau, 0x986ddb5c6b3a76b7u, 0xf89629465a75e01cu,
|
||||||
|
0xbe89523386091465u, 0xf6bbb397f1135823u, 0xee2ba6c0678b597fu, 0x746aa07ded582e2cu,
|
||||||
|
0x94db483840b717efu, 0xa8c2a44eb4571cdcu, 0xba121a4650e4ddebu, 0x92f34d62616ce413u,
|
||||||
|
0xe896a0d7e51e1566u, 0x77b020baf9c81d17u, 0x915e2486ef32cd60u, 0x0ace1474dc1d122eu,
|
||||||
|
0xb5b5ada8aaff80b8u, 0x0d819992132456bau, 0xe3231912d5bf60e6u, 0x10e1fff697ed6c69u,
|
||||||
|
0x8df5efabc5979c8fu, 0xca8d3ffa1ef463c1u, 0xb1736b96b6fd83b3u, 0xbd308ff8a6b17cb2u,
|
||||||
|
0xddd0467c64bce4a0u, 0xac7cb3f6d05ddbdeu, 0x8aa22c0dbef60ee4u, 0x6bcdf07a423aa96bu,
|
||||||
|
0xad4ab7112eb3929du, 0x86c16c98d2c953c6u, 0xd89d64d57a607744u, 0xe871c7bf077ba8b7u,
|
||||||
|
0x87625f056c7c4a8bu, 0x11471cd764ad4972u, 0xa93af6c6c79b5d2du, 0xd598e40d3dd89bcfu,
|
||||||
|
0xd389b47879823479u, 0x4aff1d108d4ec2c3u, 0x843610cb4bf160cbu, 0xcedf722a585139bau,
|
||||||
|
0xa54394fe1eedb8feu, 0xc2974eb4ee658828u, 0xce947a3da6a9273eu, 0x733d226229feea32u,
|
||||||
|
0x811ccc668829b887u, 0x0806357d5a3f525fu, 0xa163ff802a3426a8u, 0xca07c2dcb0cf26f7u,
|
||||||
|
0xc9bcff6034c13052u, 0xfc89b393dd02f0b5u, 0xfc2c3f3841f17c67u, 0xbbac2078d443ace2u,
|
||||||
|
0x9d9ba7832936edc0u, 0xd54b944b84aa4c0du, 0xc5029163f384a931u, 0x0a9e795e65d4df11u,
|
||||||
|
0xf64335bcf065d37du, 0x4d4617b5ff4a16d5u, 0x99ea0196163fa42eu, 0x504bced1bf8e4e45u,
|
||||||
|
0xc06481fb9bcf8d39u, 0xe45ec2862f71e1d6u, 0xf07da27a82c37088u, 0x5d767327bb4e5a4cu,
|
||||||
|
0x964e858c91ba2655u, 0x3a6a07f8d510f86fu, 0xbbe226efb628afeau, 0x890489f70a55368bu,
|
||||||
|
0xeadab0aba3b2dbe5u, 0x2b45ac74ccea842eu, 0x92c8ae6b464fc96fu, 0x3b0b8bc90012929du,
|
||||||
|
0xb77ada0617e3bbcbu, 0x09ce6ebb40173744u, 0xe55990879ddcaabdu, 0xcc420a6a101d0515u,
|
||||||
|
0x8f57fa54c2a9eab6u, 0x9fa946824a12232du, 0xb32df8e9f3546564u, 0x47939822dc96abf9u,
|
||||||
|
0xdff9772470297ebdu, 0x59787e2b93bc56f7u, 0x8bfbea76c619ef36u, 0x57eb4edb3c55b65au,
|
||||||
|
0xaefae51477a06b03u, 0xede622920b6b23f1u, 0xdab99e59958885c4u, 0xe95fab368e45ecedu,
|
||||||
|
0x88b402f7fd75539bu, 0x11dbcb0218ebb414u, 0xaae103b5fcd2a881u, 0xd652bdc29f26a119u,
|
||||||
|
0xd59944a37c0752a2u, 0x4be76d3346f0495fu, 0x857fcae62d8493a5u, 0x6f70a4400c562ddbu,
|
||||||
|
0xa6dfbd9fb8e5b88eu, 0xcb4ccd500f6bb952u, 0xd097ad07a71f26b2u, 0x7e2000a41346a7a7u,
|
||||||
|
0x825ecc24c873782fu, 0x8ed400668c0c28c8u, 0xa2f67f2dfa90563bu, 0x728900802f0f32fau,
|
||||||
|
0xcbb41ef979346bcau, 0x4f2b40a03ad2ffb9u, 0xfea126b7d78186bcu, 0xe2f610c84987bfa8u,
|
||||||
|
0x9f24b832e6b0f436u, 0x0dd9ca7d2df4d7c9u, 0xc6ede63fa05d3143u, 0x91503d1c79720dbbu,
|
||||||
|
0xf8a95fcf88747d94u, 0x75a44c6397ce912au, 0x9b69dbe1b548ce7cu, 0xc986afbe3ee11abau,
|
||||||
|
0xc24452da229b021bu, 0xfbe85badce996168u, 0xf2d56790ab41c2a2u, 0xfae27299423fb9c3u,
|
||||||
|
0x97c560ba6b0919a5u, 0xdccd879fc967d41au, 0xbdb6b8e905cb600fu, 0x5400e987bbc1c920u,
|
||||||
|
0xed246723473e3813u, 0x290123e9aab23b68u, 0x9436c0760c86e30bu, 0xf9a0b6720aaf6521u,
|
||||||
|
0xb94470938fa89bceu, 0xf808e40e8d5b3e69u, 0xe7958cb87392c2c2u, 0xb60b1d1230b20e04u,
|
||||||
|
0x90bd77f3483bb9b9u, 0xb1c6f22b5e6f48c2u, 0xb4ecd5f01a4aa828u, 0x1e38aeb6360b1af3u,
|
||||||
|
0xe2280b6c20dd5232u, 0x25c6da63c38de1b0u, 0x8d590723948a535fu, 0x579c487e5a38ad0eu,
|
||||||
|
0xb0af48ec79ace837u, 0x2d835a9df0c6d851u, 0xdcdb1b2798182244u, 0xf8e431456cf88e65u,
|
||||||
|
0x8a08f0f8bf0f156bu, 0x1b8e9ecb641b58ffu, 0xac8b2d36eed2dac5u, 0xe272467e3d222f3fu,
|
||||||
|
0xd7adf884aa879177u, 0x5b0ed81dcc6abb0fu, 0x86ccbb52ea94baeau, 0x98e947129fc2b4e9u,
|
||||||
|
0xa87fea27a539e9a5u, 0x3f2398d747b36224u, 0xd29fe4b18e88640eu, 0x8eec7f0d19a03aadu,
|
||||||
|
0x83a3eeeef9153e89u, 0x1953cf68300424acu, 0xa48ceaaab75a8e2bu, 0x5fa8c3423c052dd7u,
|
||||||
|
0xcdb02555653131b6u, 0x3792f412cb06794du, 0x808e17555f3ebf11u, 0xe2bbd88bbee40bd0u,
|
||||||
|
0xa0b19d2ab70e6ed6u, 0x5b6aceaeae9d0ec4u, 0xc8de047564d20a8bu, 0xf245825a5a445275u,
|
||||||
|
0xfb158592be068d2eu, 0xeed6e2f0f0d56712u, 0x9ced737bb6c4183du, 0x55464dd69685606bu,
|
||||||
|
0xc428d05aa4751e4cu, 0xaa97e14c3c26b886u, 0xf53304714d9265dfu, 0xd53dd99f4b3066a8u,
|
||||||
|
0x993fe2c6d07b7fabu, 0xe546a8038efe4029u, 0xbf8fdb78849a5f96u, 0xde98520472bdd033u,
|
||||||
|
0xef73d256a5c0f77cu, 0x963e66858f6d4440u, 0x95a8637627989aadu, 0xdde7001379a44aa8u,
|
||||||
|
0xbb127c53b17ec159u, 0x5560c018580d5d52u, 0xe9d71b689dde71afu, 0xaab8f01e6e10b4a6u,
|
||||||
|
0x9226712162ab070du, 0xcab3961304ca70e8u, 0xb6b00d69bb55c8d1u, 0x3d607b97c5fd0d22u,
|
||||||
|
0xe45c10c42a2b3b05u, 0x8cb89a7db77c506au, 0x8eb98a7a9a5b04e3u, 0x77f3608e92adb242u,
|
||||||
|
0xb267ed1940f1c61cu, 0x55f038b237591ed3u, 0xdf01e85f912e37a3u, 0x6b6c46dec52f6688u,
|
||||||
|
0x8b61313bbabce2c6u, 0x2323ac4b3b3da015u, 0xae397d8aa96c1b77u, 0xabec975e0a0d081au,
|
||||||
|
0xd9c7dced53c72255u, 0x96e7bd358c904a21u, 0x881cea14545c7575u, 0x7e50d64177da2e54u,
|
||||||
|
0xaa242499697392d2u, 0xdde50bd1d5d0b9e9u, 0xd4ad2dbfc3d07787u, 0x955e4ec64b44e864u,
|
||||||
|
0x84ec3c97da624ab4u, 0xbd5af13bef0b113eu, 0xa6274bbdd0fadd61u, 0xecb1ad8aeacdd58eu,
|
||||||
|
0xcfb11ead453994bau, 0x67de18eda5814af2u, 0x81ceb32c4b43fcf4u, 0x80eacf948770ced7u,
|
||||||
|
0xa2425ff75e14fc31u, 0xa1258379a94d028du, 0xcad2f7f5359a3b3eu, 0x096ee45813a04330u,
|
||||||
|
0xfd87b5f28300ca0du, 0x8bca9d6e188853fcu, 0x9e74d1b791e07e48u, 0x775ea264cf55347eu,
|
||||||
|
0xc612062576589ddau, 0x95364afe032a819eu, 0xf79687aed3eec551u, 0x3a83ddbd83f52205u,
|
||||||
|
0x9abe14cd44753b52u, 0xc4926a9672793543u, 0xc16d9a0095928a27u, 0x75b7053c0f178294u,
|
||||||
|
0xf1c90080baf72cb1u, 0x5324c68b12dd6339u, 0x971da05074da7beeu, 0xd3f6fc16ebca5e04u,
|
||||||
|
0xbce5086492111aeau, 0x88f4bb1ca6bcf585u, 0xec1e4a7db69561a5u, 0x2b31e9e3d06c32e6u,
|
||||||
|
0x9392ee8e921d5d07u, 0x3aff322e62439fd0u, 0xb877aa3236a4b449u, 0x09befeb9fad487c3u,
|
||||||
|
0xe69594bec44de15bu, 0x4c2ebe687989a9b4u, 0x901d7cf73ab0acd9u, 0x0f9d37014bf60a11u,
|
||||||
|
0xb424dc35095cd80fu, 0x538484c19ef38c95u, 0xe12e13424bb40e13u, 0x2865a5f206b06fbau,
|
||||||
|
0x8cbccc096f5088cbu, 0xf93f87b7442e45d4u, 0xafebff0bcb24aafeu, 0xf78f69a51539d749u,
|
||||||
|
0xdbe6fecebdedd5beu, 0xb573440e5a884d1cu, 0x89705f4136b4a597u, 0x31680a88f8953031u,
|
||||||
|
0xabcc77118461cefcu, 0xfdc20d2b36ba7c3eu, 0xd6bf94d5e57a42bcu, 0x3d32907604691b4du,
|
||||||
|
0x8637bd05af6c69b5u, 0xa63f9a49c2c1b110u, 0xa7c5ac471b478423u, 0x0fcf80dc33721d54u,
|
||||||
|
0xd1b71758e219652bu, 0xd3c36113404ea4a9u, 0x83126e978d4fdf3bu, 0x645a1cac083126eau,
|
||||||
|
0xa3d70a3d70a3d70au, 0x3d70a3d70a3d70a4u, 0xccccccccccccccccu, 0xcccccccccccccccdu,
|
||||||
|
0x8000000000000000u, 0x0000000000000000u, 0xa000000000000000u, 0x0000000000000000u,
|
||||||
|
0xc800000000000000u, 0x0000000000000000u, 0xfa00000000000000u, 0x0000000000000000u,
|
||||||
|
0x9c40000000000000u, 0x0000000000000000u, 0xc350000000000000u, 0x0000000000000000u,
|
||||||
|
0xf424000000000000u, 0x0000000000000000u, 0x9896800000000000u, 0x0000000000000000u,
|
||||||
|
0xbebc200000000000u, 0x0000000000000000u, 0xee6b280000000000u, 0x0000000000000000u,
|
||||||
|
0x9502f90000000000u, 0x0000000000000000u, 0xba43b74000000000u, 0x0000000000000000u,
|
||||||
|
0xe8d4a51000000000u, 0x0000000000000000u, 0x9184e72a00000000u, 0x0000000000000000u,
|
||||||
|
0xb5e620f480000000u, 0x0000000000000000u, 0xe35fa931a0000000u, 0x0000000000000000u,
|
||||||
|
0x8e1bc9bf04000000u, 0x0000000000000000u, 0xb1a2bc2ec5000000u, 0x0000000000000000u,
|
||||||
|
0xde0b6b3a76400000u, 0x0000000000000000u, 0x8ac7230489e80000u, 0x0000000000000000u,
|
||||||
|
0xad78ebc5ac620000u, 0x0000000000000000u, 0xd8d726b7177a8000u, 0x0000000000000000u,
|
||||||
|
0x878678326eac9000u, 0x0000000000000000u, 0xa968163f0a57b400u, 0x0000000000000000u,
|
||||||
|
0xd3c21bcecceda100u, 0x0000000000000000u, 0x84595161401484a0u, 0x0000000000000000u,
|
||||||
|
0xa56fa5b99019a5c8u, 0x0000000000000000u, 0xcecb8f27f4200f3au, 0x0000000000000000u,
|
||||||
|
0x813f3978f8940984u, 0x4000000000000000u, 0xa18f07d736b90be5u, 0x5000000000000000u,
|
||||||
|
0xc9f2c9cd04674edeu, 0xa400000000000000u, 0xfc6f7c4045812296u, 0x4d00000000000000u,
|
||||||
|
0x9dc5ada82b70b59du, 0xf020000000000000u, 0xc5371912364ce305u, 0x6c28000000000000u,
|
||||||
|
0xf684df56c3e01bc6u, 0xc732000000000000u, 0x9a130b963a6c115cu, 0x3c7f400000000000u,
|
||||||
|
0xc097ce7bc90715b3u, 0x4b9f100000000000u, 0xf0bdc21abb48db20u, 0x1e86d40000000000u,
|
||||||
|
0x96769950b50d88f4u, 0x1314448000000000u, 0xbc143fa4e250eb31u, 0x17d955a000000000u,
|
||||||
|
0xeb194f8e1ae525fdu, 0x5dcfab0800000000u, 0x92efd1b8d0cf37beu, 0x5aa1cae500000000u,
|
||||||
|
0xb7abc627050305adu, 0xf14a3d9e40000000u, 0xe596b7b0c643c719u, 0x6d9ccd05d0000000u,
|
||||||
|
0x8f7e32ce7bea5c6fu, 0xe4820023a2000000u, 0xb35dbf821ae4f38bu, 0xdda2802c8a800000u,
|
||||||
|
0xe0352f62a19e306eu, 0xd50b2037ad200000u, 0x8c213d9da502de45u, 0x4526f422cc340000u,
|
||||||
|
0xaf298d050e4395d6u, 0x9670b12b7f410000u, 0xdaf3f04651d47b4cu, 0x3c0cdd765f114000u,
|
||||||
|
0x88d8762bf324cd0fu, 0xa5880a69fb6ac800u, 0xab0e93b6efee0053u, 0x8eea0d047a457a00u,
|
||||||
|
0xd5d238a4abe98068u, 0x72a4904598d6d880u, 0x85a36366eb71f041u, 0x47a6da2b7f864750u,
|
||||||
|
0xa70c3c40a64e6c51u, 0x999090b65f67d924u, 0xd0cf4b50cfe20765u, 0xfff4b4e3f741cf6du,
|
||||||
|
0x82818f1281ed449fu, 0xbff8f10e7a8921a4u, 0xa321f2d7226895c7u, 0xaff72d52192b6a0du,
|
||||||
|
0xcbea6f8ceb02bb39u, 0x9bf4f8a69f764490u, 0xfee50b7025c36a08u, 0x02f236d04753d5b4u,
|
||||||
|
0x9f4f2726179a2245u, 0x01d762422c946590u, 0xc722f0ef9d80aad6u, 0x424d3ad2b7b97ef5u,
|
||||||
|
0xf8ebad2b84e0d58bu, 0xd2e0898765a7deb2u, 0x9b934c3b330c8577u, 0x63cc55f49f88eb2fu,
|
||||||
|
0xc2781f49ffcfa6d5u, 0x3cbf6b71c76b25fbu, 0xf316271c7fc3908au, 0x8bef464e3945ef7au,
|
||||||
|
0x97edd871cfda3a56u, 0x97758bf0e3cbb5acu, 0xbde94e8e43d0c8ecu, 0x3d52eeed1cbea317u,
|
||||||
|
0xed63a231d4c4fb27u, 0x4ca7aaa863ee4bddu, 0x945e455f24fb1cf8u, 0x8fe8caa93e74ef6au,
|
||||||
|
0xb975d6b6ee39e436u, 0xb3e2fd538e122b44u, 0xe7d34c64a9c85d44u, 0x60dbbca87196b616u,
|
||||||
|
0x90e40fbeea1d3a4au, 0xbc8955e946fe31cdu, 0xb51d13aea4a488ddu, 0x6babab6398bdbe41u,
|
||||||
|
0xe264589a4dcdab14u, 0xc696963c7eed2dd1u, 0x8d7eb76070a08aecu, 0xfc1e1de5cf543ca2u,
|
||||||
|
0xb0de65388cc8ada8u, 0x3b25a55f43294bcbu, 0xdd15fe86affad912u, 0x49ef0eb713f39ebeu,
|
||||||
|
0x8a2dbf142dfcc7abu, 0x6e3569326c784337u, 0xacb92ed9397bf996u, 0x49c2c37f07965404u,
|
||||||
|
0xd7e77a8f87daf7fbu, 0xdc33745ec97be906u, 0x86f0ac99b4e8dafdu, 0x69a028bb3ded71a3u,
|
||||||
|
0xa8acd7c0222311bcu, 0xc40832ea0d68ce0cu, 0xd2d80db02aabd62bu, 0xf50a3fa490c30190u,
|
||||||
|
0x83c7088e1aab65dbu, 0x792667c6da79e0fau, 0xa4b8cab1a1563f52u, 0x577001b891185938u,
|
||||||
|
0xcde6fd5e09abcf26u, 0xed4c0226b55e6f86u, 0x80b05e5ac60b6178u, 0x544f8158315b05b4u,
|
||||||
|
0xa0dc75f1778e39d6u, 0x696361ae3db1c721u, 0xc913936dd571c84cu, 0x03bc3a19cd1e38e9u,
|
||||||
|
0xfb5878494ace3a5fu, 0x04ab48a04065c723u, 0x9d174b2dcec0e47bu, 0x62eb0d64283f9c76u,
|
||||||
|
0xc45d1df942711d9au, 0x3ba5d0bd324f8394u, 0xf5746577930d6500u, 0xca8f44ec7ee36479u,
|
||||||
|
0x9968bf6abbe85f20u, 0x7e998b13cf4e1ecbu, 0xbfc2ef456ae276e8u, 0x9e3fedd8c321a67eu,
|
||||||
|
0xefb3ab16c59b14a2u, 0xc5cfe94ef3ea101eu, 0x95d04aee3b80ece5u, 0xbba1f1d158724a12u,
|
||||||
|
0xbb445da9ca61281fu, 0x2a8a6e45ae8edc97u, 0xea1575143cf97226u, 0xf52d09d71a3293bdu,
|
||||||
|
0x924d692ca61be758u, 0x593c2626705f9c56u, 0xb6e0c377cfa2e12eu, 0x6f8b2fb00c77836cu,
|
||||||
|
0xe498f455c38b997au, 0x0b6dfb9c0f956447u, 0x8edf98b59a373fecu, 0x4724bd4189bd5eacu,
|
||||||
|
0xb2977ee300c50fe7u, 0x58edec91ec2cb657u, 0xdf3d5e9bc0f653e1u, 0x2f2967b66737e3edu,
|
||||||
|
0x8b865b215899f46cu, 0xbd79e0d20082ee74u, 0xae67f1e9aec07187u, 0xecd8590680a3aa11u,
|
||||||
|
0xda01ee641a708de9u, 0xe80e6f4820cc9495u, 0x884134fe908658b2u, 0x3109058d147fdcddu,
|
||||||
|
0xaa51823e34a7eedeu, 0xbd4b46f0599fd415u, 0xd4e5e2cdc1d1ea96u, 0x6c9e18ac7007c91au,
|
||||||
|
0x850fadc09923329eu, 0x03e2cf6bc604ddb0u, 0xa6539930bf6bff45u, 0x84db8346b786151cu,
|
||||||
|
0xcfe87f7cef46ff16u, 0xe612641865679a63u, 0x81f14fae158c5f6eu, 0x4fcb7e8f3f60c07eu,
|
||||||
|
0xa26da3999aef7749u, 0xe3be5e330f38f09du, 0xcb090c8001ab551cu, 0x5cadf5bfd3072cc5u,
|
||||||
|
0xfdcb4fa002162a63u, 0x73d9732fc7c8f7f6u, 0x9e9f11c4014dda7eu, 0x2867e7fddcdd9afau,
|
||||||
|
0xc646d63501a1511du, 0xb281e1fd541501b8u, 0xf7d88bc24209a565u, 0x1f225a7ca91a4226u,
|
||||||
|
0x9ae757596946075fu, 0x3375788de9b06958u, 0xc1a12d2fc3978937u, 0x0052d6b1641c83aeu,
|
||||||
|
0xf209787bb47d6b84u, 0xc0678c5dbd23a49au, 0x9745eb4d50ce6332u, 0xf840b7ba963646e0u,
|
||||||
|
0xbd176620a501fbffu, 0xb650e5a93bc3d898u, 0xec5d3fa8ce427affu, 0xa3e51f138ab4cebeu,
|
||||||
|
0x93ba47c980e98cdfu, 0xc66f336c36b10137u, 0xb8a8d9bbe123f017u, 0xb80b0047445d4184u,
|
||||||
|
0xe6d3102ad96cec1du, 0xa60dc059157491e5u, 0x9043ea1ac7e41392u, 0x87c89837ad68db2fu,
|
||||||
|
0xb454e4a179dd1877u, 0x29babe4598c311fbu, 0xe16a1dc9d8545e94u, 0xf4296dd6fef3d67au,
|
||||||
|
0x8ce2529e2734bb1du, 0x1899e4a65f58660cu, 0xb01ae745b101e9e4u, 0x5ec05dcff72e7f8fu,
|
||||||
|
0xdc21a1171d42645du, 0x76707543f4fa1f73u, 0x899504ae72497ebau, 0x6a06494a791c53a8u,
|
||||||
|
0xabfa45da0edbde69u, 0x0487db9d17636892u, 0xd6f8d7509292d603u, 0x45a9d2845d3c42b6u,
|
||||||
|
0x865b86925b9bc5c2u, 0x0b8a2392ba45a9b2u, 0xa7f26836f282b732u, 0x8e6cac7768d7141eu,
|
||||||
|
0xd1ef0244af2364ffu, 0x3207d795430cd926u, 0x8335616aed761f1fu, 0x7f44e6bd49e807b8u,
|
||||||
|
0xa402b9c5a8d3a6e7u, 0x5f16206c9c6209a6u, 0xcd036837130890a1u, 0x36dba887c37a8c0fu,
|
||||||
|
0x802221226be55a64u, 0xc2494954da2c9789u, 0xa02aa96b06deb0fdu, 0xf2db9baa10b7bd6cu,
|
||||||
|
0xc83553c5c8965d3du, 0x6f92829494e5acc7u, 0xfa42a8b73abbf48cu, 0xcb772339ba1f17f9u,
|
||||||
|
0x9c69a97284b578d7u, 0xff2a760414536efbu, 0xc38413cf25e2d70du, 0xfef5138519684abau,
|
||||||
|
0xf46518c2ef5b8cd1u, 0x7eb258665fc25d69u, 0x98bf2f79d5993802u, 0xef2f773ffbd97a61u,
|
||||||
|
0xbeeefb584aff8603u, 0xaafb550ffacfd8fau, 0xeeaaba2e5dbf6784u, 0x95ba2a53f983cf38u,
|
||||||
|
0x952ab45cfa97a0b2u, 0xdd945a747bf26183u, 0xba756174393d88dfu, 0x94f971119aeef9e4u,
|
||||||
|
0xe912b9d1478ceb17u, 0x7a37cd5601aab85du, 0x91abb422ccb812eeu, 0xac62e055c10ab33au,
|
||||||
|
0xb616a12b7fe617aau, 0x577b986b314d6009u, 0xe39c49765fdf9d94u, 0xed5a7e85fda0b80bu,
|
||||||
|
0x8e41ade9fbebc27du, 0x14588f13be847307u, 0xb1d219647ae6b31cu, 0x596eb2d8ae258fc8u,
|
||||||
|
0xde469fbd99a05fe3u, 0x6fca5f8ed9aef3bbu, 0x8aec23d680043beeu, 0x25de7bb9480d5854u,
|
||||||
|
0xada72ccc20054ae9u, 0xaf561aa79a10ae6au, 0xd910f7ff28069da4u, 0x1b2ba1518094da04u,
|
||||||
|
0x87aa9aff79042286u, 0x90fb44d2f05d0842u, 0xa99541bf57452b28u, 0x353a1607ac744a53u,
|
||||||
|
0xd3fa922f2d1675f2u, 0x42889b8997915ce8u, 0x847c9b5d7c2e09b7u, 0x69956135febada11u,
|
||||||
|
0xa59bc234db398c25u, 0x43fab9837e699095u, 0xcf02b2c21207ef2eu, 0x94f967e45e03f4bbu,
|
||||||
|
0x8161afb94b44f57du, 0x1d1be0eebac278f5u, 0xa1ba1ba79e1632dcu, 0x6462d92a69731732u,
|
||||||
|
0xca28a291859bbf93u, 0x7d7b8f7503cfdcfeu, 0xfcb2cb35e702af78u, 0x5cda735244c3d43eu,
|
||||||
|
0x9defbf01b061adabu, 0x3a0888136afa64a7u, 0xc56baec21c7a1916u, 0x088aaa1845b8fdd0u,
|
||||||
|
0xf6c69a72a3989f5bu, 0x8aad549e57273d45u, 0x9a3c2087a63f6399u, 0x36ac54e2f678864bu,
|
||||||
|
0xc0cb28a98fcf3c7fu, 0x84576a1bb416a7ddu, 0xf0fdf2d3f3c30b9fu, 0x656d44a2a11c51d5u,
|
||||||
|
0x969eb7c47859e743u, 0x9f644ae5a4b1b325u, 0xbc4665b596706114u, 0x873d5d9f0dde1feeu,
|
||||||
|
0xeb57ff22fc0c7959u, 0xa90cb506d155a7eau, 0x9316ff75dd87cbd8u, 0x09a7f12442d588f2u,
|
||||||
|
0xb7dcbf5354e9beceu, 0x0c11ed6d538aeb2fu, 0xe5d3ef282a242e81u, 0x8f1668c8a86da5fau,
|
||||||
|
0x8fa475791a569d10u, 0xf96e017d694487bcu, 0xb38d92d760ec4455u, 0x37c981dcc395a9acu,
|
||||||
|
0xe070f78d3927556au, 0x85bbe253f47b1417u, 0x8c469ab843b89562u, 0x93956d7478ccec8eu,
|
||||||
|
0xaf58416654a6babbu, 0x387ac8d1970027b2u, 0xdb2e51bfe9d0696au, 0x06997b05fcc0319eu,
|
||||||
|
0x88fcf317f22241e2u, 0x441fece3bdf81f03u, 0xab3c2fddeeaad25au, 0xd527e81cad7626c3u,
|
||||||
|
0xd60b3bd56a5586f1u, 0x8a71e223d8d3b074u, 0x85c7056562757456u, 0xf6872d5667844e49u,
|
||||||
|
0xa738c6bebb12d16cu, 0xb428f8ac016561dbu, 0xd106f86e69d785c7u, 0xe13336d701beba52u,
|
||||||
|
0x82a45b450226b39cu, 0xecc0024661173473u, 0xa34d721642b06084u, 0x27f002d7f95d0190u,
|
||||||
|
0xcc20ce9bd35c78a5u, 0x31ec038df7b441f4u, 0xff290242c83396ceu, 0x7e67047175a15271u,
|
||||||
|
0x9f79a169bd203e41u, 0x0f0062c6e984d386u, 0xc75809c42c684dd1u, 0x52c07b78a3e60868u,
|
||||||
|
0xf92e0c3537826145u, 0xa7709a56ccdf8a82u, 0x9bbcc7a142b17ccbu, 0x88a66076400bb691u,
|
||||||
|
0xc2abf989935ddbfeu, 0x6acff893d00ea435u, 0xf356f7ebf83552feu, 0x0583f6b8c4124d43u,
|
||||||
|
0x98165af37b2153deu, 0xc3727a337a8b704au, 0xbe1bf1b059e9a8d6u, 0x744f18c0592e4c5cu,
|
||||||
|
0xeda2ee1c7064130cu, 0x1162def06f79df73u, 0x9485d4d1c63e8be7u, 0x8addcb5645ac2ba8u,
|
||||||
|
0xb9a74a0637ce2ee1u, 0x6d953e2bd7173692u, 0xe8111c87c5c1ba99u, 0xc8fa8db6ccdd0437u,
|
||||||
|
0x910ab1d4db9914a0u, 0x1d9c9892400a22a2u, 0xb54d5e4a127f59c8u, 0x2503beb6d00cab4bu,
|
||||||
|
0xe2a0b5dc971f303au, 0x2e44ae64840fd61du, 0x8da471a9de737e24u, 0x5ceaecfed289e5d2u,
|
||||||
|
0xb10d8e1456105dadu, 0x7425a83e872c5f47u, 0xdd50f1996b947518u, 0xd12f124e28f77719u,
|
||||||
|
0x8a5296ffe33cc92fu, 0x82bd6b70d99aaa6fu, 0xace73cbfdc0bfb7bu, 0x636cc64d1001550bu,
|
||||||
|
0xd8210befd30efa5au, 0x3c47f7e05401aa4eu, 0x8714a775e3e95c78u, 0x65acfaec34810a71u,
|
||||||
|
0xa8d9d1535ce3b396u, 0x7f1839a741a14d0du, 0xd31045a8341ca07cu, 0x1ede48111209a050u,
|
||||||
|
0x83ea2b892091e44du, 0x934aed0aab460432u, 0xa4e4b66b68b65d60u, 0xf81da84d5617853fu,
|
||||||
|
0xce1de40642e3f4b9u, 0x36251260ab9d668eu, 0x80d2ae83e9ce78f3u, 0xc1d72b7c6b426019u,
|
||||||
|
0xa1075a24e4421730u, 0xb24cf65b8612f81fu, 0xc94930ae1d529cfcu, 0xdee033f26797b627u,
|
||||||
|
0xfb9b7cd9a4a7443cu, 0x169840ef017da3b1u, 0x9d412e0806e88aa5u, 0x8e1f289560ee864eu,
|
||||||
|
0xc491798a08a2ad4eu, 0xf1a6f2bab92a27e2u, 0xf5b5d7ec8acb58a2u, 0xae10af696774b1dbu,
|
||||||
|
0x9991a6f3d6bf1765u, 0xacca6da1e0a8ef29u, 0xbff610b0cc6edd3fu, 0x17fd090a58d32af3u,
|
||||||
|
0xeff394dcff8a948eu, 0xddfc4b4cef07f5b0u, 0x95f83d0a1fb69cd9u, 0x4abdaf101564f98eu,
|
||||||
|
0xbb764c4ca7a4440fu, 0x9d6d1ad41abe37f1u, 0xea53df5fd18d5513u, 0x84c86189216dc5edu,
|
||||||
|
0x92746b9be2f8552cu, 0x32fd3cf5b4e49bb4u, 0xb7118682dbb66a77u, 0x3fbc8c33221dc2a1u,
|
||||||
|
0xe4d5e82392a40515u, 0x0fabaf3feaa5334au, 0x8f05b1163ba6832du, 0x29cb4d87f2a7400eu,
|
||||||
|
0xb2c71d5bca9023f8u, 0x743e20e9ef511012u, 0xdf78e4b2bd342cf6u, 0x914da9246b255416u,
|
||||||
|
0x8bab8eefb6409c1au, 0x1ad089b6c2f7548eu, 0xae9672aba3d0c320u, 0xa184ac2473b529b1u,
|
||||||
|
0xda3c0f568cc4f3e8u, 0xc9e5d72d90a2741eu, 0x8865899617fb1871u, 0x7e2fa67c7a658892u,
|
||||||
|
0xaa7eebfb9df9de8du, 0xddbb901b98feeab7u, 0xd51ea6fa85785631u, 0x552a74227f3ea565u,
|
||||||
|
0x8533285c936b35deu, 0xd53a88958f87275fu, 0xa67ff273b8460356u, 0x8a892abaf368f137u,
|
||||||
|
0xd01fef10a657842cu, 0x2d2b7569b0432d85u, 0x8213f56a67f6b29bu, 0x9c3b29620e29fc73u,
|
||||||
|
0xa298f2c501f45f42u, 0x8349f3ba91b47b8fu, 0xcb3f2f7642717713u, 0x241c70a936219a73u,
|
||||||
|
0xfe0efb53d30dd4d7u, 0xed238cd383aa0110u, 0x9ec95d1463e8a506u, 0xf4363804324a40aau,
|
||||||
|
0xc67bb4597ce2ce48u, 0xb143c6053edcd0d5u, 0xf81aa16fdc1b81dau, 0xdd94b7868e94050au,
|
||||||
|
0x9b10a4e5e9913128u, 0xca7cf2b4191c8326u, 0xc1d4ce1f63f57d72u, 0xfd1c2f611f63a3f0u,
|
||||||
|
0xf24a01a73cf2dccfu, 0xbc633b39673c8cecu, 0x976e41088617ca01u, 0xd5be0503e085d813u,
|
||||||
|
0xbd49d14aa79dbc82u, 0x4b2d8644d8a74e18u, 0xec9c459d51852ba2u, 0xddf8e7d60ed1219eu,
|
||||||
|
0x93e1ab8252f33b45u, 0xcabb90e5c942b503u, 0xb8da1662e7b00a17u, 0x3d6a751f3b936243u,
|
||||||
|
0xe7109bfba19c0c9du, 0x0cc512670a783ad4u, 0x906a617d450187e2u, 0x27fb2b80668b24c5u,
|
||||||
|
0xb484f9dc9641e9dau, 0xb1f9f660802dedf6u, 0xe1a63853bbd26451u, 0x5e7873f8a0396973u,
|
||||||
|
0x8d07e33455637eb2u, 0xdb0b487b6423e1e8u, 0xb049dc016abc5e5fu, 0x91ce1a9a3d2cda62u,
|
||||||
|
0xdc5c5301c56b75f7u, 0x7641a140cc7810fbu, 0x89b9b3e11b6329bau, 0xa9e904c87fcb0a9du,
|
||||||
|
0xac2820d9623bf429u, 0x546345fa9fbdcd44u, 0xd732290fbacaf133u, 0xa97c177947ad4095u,
|
||||||
|
0x867f59a9d4bed6c0u, 0x49ed8eabcccc485du, 0xa81f301449ee8c70u, 0x5c68f256bfff5a74u,
|
||||||
|
0xd226fc195c6a2f8cu, 0x73832eec6fff3111u, 0x83585d8fd9c25db7u, 0xc831fd53c5ff7eabu,
|
||||||
|
0xa42e74f3d032f525u, 0xba3e7ca8b77f5e55u, 0xcd3a1230c43fb26fu, 0x28ce1bd2e55f35ebu,
|
||||||
|
0x80444b5e7aa7cf85u, 0x7980d163cf5b81b3u, 0xa0555e361951c366u, 0xd7e105bcc332621fu,
|
||||||
|
0xc86ab5c39fa63440u, 0x8dd9472bf3fefaa7u, 0xfa856334878fc150u, 0xb14f98f6f0feb951u,
|
||||||
|
0x9c935e00d4b9d8d2u, 0x6ed1bf9a569f33d3u, 0xc3b8358109e84f07u, 0x0a862f80ec4700c8u,
|
||||||
|
0xf4a642e14c6262c8u, 0xcd27bb612758c0fau, 0x98e7e9cccfbd7dbdu, 0x8038d51cb897789cu,
|
||||||
|
0xbf21e44003acdd2cu, 0xe0470a63e6bd56c3u, 0xeeea5d5004981478u, 0x1858ccfce06cac74u,
|
||||||
|
0x95527a5202df0ccbu, 0x0f37801e0c43ebc8u, 0xbaa718e68396cffdu, 0xd30560258f54e6bau,
|
||||||
|
0xe950df20247c83fdu, 0x47c6b82ef32a2069u, 0x91d28b7416cdd27eu, 0x4cdc331d57fa5441u,
|
||||||
|
0xb6472e511c81471du, 0xe0133fe4adf8e952u, 0xe3d8f9e563a198e5u, 0x58180fddd97723a6u,
|
||||||
|
0x8e679c2f5e44ff8fu, 0x570f09eaa7ea7648u,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
#include <nlohmann/detail/string_concat.hpp>
|
#include <nlohmann/detail/string_concat.hpp>
|
||||||
#include <nlohmann/detail/string_escape.hpp>
|
#include <nlohmann/detail/string_escape.hpp>
|
||||||
|
#include <nlohmann/detail/string_utils.hpp>
|
||||||
#include <nlohmann/detail/value_t.hpp>
|
#include <nlohmann/detail/value_t.hpp>
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
@@ -116,7 +117,7 @@ class json_pointer
|
|||||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
|
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
|
||||||
json_pointer& operator/=(std::size_t array_idx)
|
json_pointer& operator/=(std::size_t array_idx)
|
||||||
{
|
{
|
||||||
return *this /= std::to_string(array_idx);
|
return *this /= detail::to_string<string_t>(array_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer
|
/// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer
|
||||||
@@ -746,7 +747,13 @@ class json_pointer
|
|||||||
// "-" always fails the range check
|
// "-" always fails the range check
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9")))
|
if (JSON_HEDLEY_UNLIKELY(reference_token.empty()))
|
||||||
|
{
|
||||||
|
// an empty reference token is not an array index; array_index()
|
||||||
|
// would throw out_of_range.404 -- contains() must not throw (see #5395)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !('0' <= reference_token[0] && reference_token[0] <= '9')))
|
||||||
{
|
{
|
||||||
// invalid char
|
// invalid char
|
||||||
return false;
|
return false;
|
||||||
@@ -774,7 +781,7 @@ class json_pointer
|
|||||||
// not throw (see #5395), so such a reference token is treated as "not found"
|
// not throw (see #5395), so such a reference token is treated as "not found"
|
||||||
errno = 0; // strtoull() does not reset errno on success
|
errno = 0; // strtoull() does not reset errno on success
|
||||||
char* p_end = nullptr; // NOLINT(misc-const-correctness)
|
char* p_end = nullptr; // NOLINT(misc-const-correctness)
|
||||||
const unsigned long long magnitude = std::strtoull(reference_token.c_str(), &p_end, 10); // NOLINT(runtime/int)
|
const unsigned long long magnitude = std::strtoull(reference_token.data(), &p_end, 10); // NOLINT(runtime/int)
|
||||||
if (JSON_HEDLEY_UNLIKELY(errno == ERANGE // the value exceeds ULLONG_MAX
|
if (JSON_HEDLEY_UNLIKELY(errno == ERANGE // the value exceeds ULLONG_MAX
|
||||||
|| magnitude >= static_cast<unsigned long long>((std::numeric_limits<typename BasicJsonType::size_type>::max)()))) // NOLINT(runtime/int)
|
|| magnitude >= static_cast<unsigned long long>((std::numeric_limits<typename BasicJsonType::size_type>::max)()))) // NOLINT(runtime/int)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -336,7 +336,7 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
return ej_pair.second == j; \
|
return ej_pair.second == j; \
|
||||||
}); \
|
}); \
|
||||||
if (it != std::end(m)) e = it->first; \
|
if (it != std::end(m)) e = it->first; \
|
||||||
else templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(410,"enum value out of range for " #ENUM_TYPE ": " + j.dump(), &j)); \
|
else templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(410, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", j.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace)), &j)); \
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
|
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
|
||||||
@@ -359,6 +359,7 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
|
|
||||||
// Macros to simplify conversion from/to types
|
// Macros to simplify conversion from/to types
|
||||||
|
|
||||||
|
// NLOHMANN_JSON_EXPAND to NLOHMANN_JSON_DOUBLE_PASTE63 are generated by tools/macro_builder (see its README.md)
|
||||||
#define NLOHMANN_JSON_EXPAND( x ) x
|
#define NLOHMANN_JSON_EXPAND( x ) x
|
||||||
#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME
|
#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME
|
||||||
#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \
|
#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \
|
||||||
@@ -621,6 +622,10 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
// arguments, so dispatching on Type,BaseType,member... directly would run out
|
// arguments, so dispatching on Type,BaseType,member... directly would run out
|
||||||
// one slot early and cap the derived-type macros at 62 members instead of the
|
// one slot early and cap the derived-type macros at 62 members instead of the
|
||||||
// 63 that NLOHMANN_JSON_PASTE supports.
|
// 63 that NLOHMANN_JSON_PASTE supports.
|
||||||
|
//
|
||||||
|
// The slot table below (down to the closing NLOHMANN_JSON_TYPE_BODY_SENTINEL))
|
||||||
|
// is generated by tools/macro_builder (see its README.md; run with the
|
||||||
|
// "type_body" argument).
|
||||||
#define NLOHMANN_JSON_TYPE_BODY(Prefix, ...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \
|
#define NLOHMANN_JSON_TYPE_BODY(Prefix, ...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \
|
||||||
Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, \
|
Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, \
|
||||||
Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, \
|
Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, \
|
||||||
@@ -916,6 +921,6 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
#define JSON_DISABLE_ENUM_SERIALIZATION 0
|
#define JSON_DISABLE_ENUM_SERIALIZATION 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef JSON_USE_GLOBAL_UDLS
|
#ifndef JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
||||||
#define JSON_USE_GLOBAL_UDLS 1
|
#define JSON_DISABLE_TUPLE_REFERENCE_CONVERSION 0
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
#undef JSON_INLINE_VARIABLE
|
#undef JSON_INLINE_VARIABLE
|
||||||
#undef JSON_NO_UNIQUE_ADDRESS
|
#undef JSON_NO_UNIQUE_ADDRESS
|
||||||
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
||||||
#undef JSON_USE_GLOBAL_UDLS
|
#undef JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
||||||
|
|
||||||
#ifndef JSON_TEST_KEEP_MACROS
|
#ifndef JSON_TEST_KEEP_MACROS
|
||||||
#undef JSON_CATCH
|
#undef JSON_CATCH
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array> // array
|
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type
|
#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type
|
||||||
#include <utility> // index_sequence, make_index_sequence, index_sequence_for
|
#include <utility> // index_sequence, make_index_sequence, index_sequence_for
|
||||||
@@ -161,11 +160,5 @@ struct static_const
|
|||||||
constexpr T static_const<T>::value;
|
constexpr T static_const<T>::value;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template<typename T, typename... Args>
|
|
||||||
constexpr std::array<T, sizeof...(Args)> make_array(Args&& ... args)
|
|
||||||
{
|
|
||||||
return std::array<T, sizeof...(Args)> {{static_cast<T>(std::forward<Args>(args))...}};
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|||||||
@@ -636,6 +636,18 @@ template<typename BasicJsonType, typename CompatibleType>
|
|||||||
struct is_compatible_type
|
struct is_compatible_type
|
||||||
: is_compatible_type_impl<BasicJsonType, CompatibleType> {};
|
: is_compatible_type_impl<BasicJsonType, CompatibleType> {};
|
||||||
|
|
||||||
|
// a one-element std::tuple holding a reference to BasicJsonType, as created by
|
||||||
|
// std::forward_as_tuple(j); see JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
||||||
|
template<typename BasicJsonType, typename T>
|
||||||
|
struct is_basic_json_reference_tuple : std::false_type {};
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename T>
|
||||||
|
struct is_basic_json_reference_tuple<BasicJsonType, std::tuple<T>>
|
||||||
|
{
|
||||||
|
static constexpr bool value =
|
||||||
|
std::is_reference<T>::value && std::is_same<uncvref_t<T>, BasicJsonType>::value;
|
||||||
|
};
|
||||||
|
|
||||||
template<typename BasicJsonType, typename CompatibleArrayType>
|
template<typename BasicJsonType, typename CompatibleArrayType>
|
||||||
struct is_compatible_binary_type
|
struct is_compatible_binary_type
|
||||||
{
|
{
|
||||||
@@ -725,7 +737,7 @@ std::is_constructible <decltype(std::declval<Compare>()(std::declval<A>(), std::
|
|||||||
// avoid their instantiation on all compilers, even when the first operand
|
// avoid their instantiation on all compilers, even when the first operand
|
||||||
// is false. The dispatch on is_json_pointer_of can be removed once the
|
// is false. The dispatch on is_json_pointer_of can be removed once the
|
||||||
// deprecated json_pointer comparison operators have been removed.
|
// deprecated json_pointer comparison operators have been removed.
|
||||||
template<typename Compare, typename A, typename B, bool = is_json_pointer_of<A, B>::value>
|
template<typename Compare, typename A, typename B, bool = is_json_pointer_of<uncvref_t<A>, uncvref_t<B>>::value>
|
||||||
struct is_comparable : std::false_type {};
|
struct is_comparable : std::false_type {};
|
||||||
|
|
||||||
template<typename Compare, typename A, typename B>
|
template<typename Compare, typename A, typename B>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -8,9 +8,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <algorithm> // copy
|
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <iterator> // back_inserter
|
|
||||||
#include <memory> // shared_ptr, make_shared
|
#include <memory> // shared_ptr, make_shared
|
||||||
#include <string> // basic_string
|
#include <string> // basic_string
|
||||||
#include <utility> // move
|
#include <utility> // move
|
||||||
@@ -31,6 +29,10 @@ namespace detail
|
|||||||
template<typename CharType> struct output_adapter_protocol
|
template<typename CharType> struct output_adapter_protocol
|
||||||
{
|
{
|
||||||
virtual void write_character(CharType c) = 0;
|
virtual void write_character(CharType c) = 0;
|
||||||
|
/// @param[in] s pointer to the characters to write; binary_writer legitimately
|
||||||
|
/// passes a null pointer together with length 0 for an empty
|
||||||
|
/// string or binary value, so implementations must tolerate that
|
||||||
|
/// @param[in] length number of characters at @a s
|
||||||
virtual void write_characters(const CharType* s, std::size_t length) = 0;
|
virtual void write_characters(const CharType* s, std::size_t length) = 0;
|
||||||
virtual ~output_adapter_protocol() = default;
|
virtual ~output_adapter_protocol() = default;
|
||||||
|
|
||||||
@@ -97,7 +99,6 @@ class output_vector_adapter : public output_adapter_protocol<CharType>
|
|||||||
sink.write_character(c);
|
sink.write_character(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
|
||||||
void write_characters(const CharType* s, std::size_t length) override
|
void write_characters(const CharType* s, std::size_t length) override
|
||||||
{
|
{
|
||||||
sink.write_characters(s, length);
|
sink.write_characters(s, length);
|
||||||
@@ -122,7 +123,6 @@ class output_stream_adapter : public output_adapter_protocol<CharType>
|
|||||||
stream.put(c);
|
stream.put(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
|
||||||
void write_characters(const CharType* s, std::size_t length) override
|
void write_characters(const CharType* s, std::size_t length) override
|
||||||
{
|
{
|
||||||
stream.write(s, static_cast<std::streamsize>(length));
|
stream.write(s, static_cast<std::streamsize>(length));
|
||||||
@@ -147,7 +147,6 @@ class output_string_adapter : public output_adapter_protocol<CharType>
|
|||||||
str.push_back(c);
|
str.push_back(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
|
||||||
void write_characters(const CharType* s, std::size_t length) override
|
void write_characters(const CharType* s, std::size_t length) override
|
||||||
{
|
{
|
||||||
str.append(s, length);
|
str.append(s, length);
|
||||||
|
|||||||
@@ -3,24 +3,23 @@
|
|||||||
// | | |__ | | | | | | version 3.12.0
|
// | | |__ | | | | | | version 3.12.0
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
//
|
//
|
||||||
// SPDX-FileCopyrightText: 2008, 2009 Björn Hoehrmann <bjoern@hoehrmann.de>
|
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <algorithm> // reverse, remove, fill, find, none_of, min
|
#include <algorithm> // remove, fill, find, none_of, min
|
||||||
#include <array> // array
|
#include <array> // array
|
||||||
#include <clocale> // localeconv, lconv
|
#include <clocale> // localeconv, lconv
|
||||||
#include <cmath> // labs, isfinite, isnan, signbit
|
#include <cmath> // isfinite
|
||||||
#include <cstddef> // size_t, ptrdiff_t
|
#include <cstddef> // size_t, ptrdiff_t
|
||||||
#include <cstdint> // uint8_t
|
#include <cstdint> // uint8_t
|
||||||
#include <cstdio> // snprintf
|
#include <cstdio> // snprintf
|
||||||
#include <cstring> // memcpy, memset
|
#include <cstring> // memcpy, memset
|
||||||
|
#include <iterator> // next
|
||||||
#include <limits> // numeric_limits
|
#include <limits> // numeric_limits
|
||||||
#include <string> // string, char_traits
|
#include <string> // string, char_traits
|
||||||
#include <type_traits> // is_same
|
#include <type_traits> // is_same
|
||||||
#include <utility> // move
|
|
||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
|
|
||||||
#include <nlohmann/detail/conversions/to_chars.hpp>
|
#include <nlohmann/detail/conversions/to_chars.hpp>
|
||||||
@@ -28,7 +27,6 @@
|
|||||||
#include <nlohmann/detail/input/string_scan.hpp>
|
#include <nlohmann/detail/input/string_scan.hpp>
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
#include <nlohmann/detail/meta/cpp_future.hpp>
|
||||||
#include <nlohmann/detail/output/binary_writer.hpp>
|
|
||||||
#include <nlohmann/detail/output/output_adapters.hpp>
|
#include <nlohmann/detail/output/output_adapters.hpp>
|
||||||
#include <nlohmann/detail/recursion_depth_limit.hpp>
|
#include <nlohmann/detail/recursion_depth_limit.hpp>
|
||||||
#include <nlohmann/detail/string_concat.hpp>
|
#include <nlohmann/detail/string_concat.hpp>
|
||||||
@@ -83,7 +81,6 @@ class serializer
|
|||||||
const std::size_t indent_step_ = 0,
|
const std::size_t indent_step_ = 0,
|
||||||
error_handler_t error_handler_ = error_handler_t::strict)
|
error_handler_t error_handler_ = error_handler_t::strict)
|
||||||
: o(&s)
|
: o(&s)
|
||||||
, locale(std::localeconv())
|
|
||||||
, indent_char(ichar)
|
, indent_char(ichar)
|
||||||
, pretty_print(pretty_print_)
|
, pretty_print(pretty_print_)
|
||||||
, ensure_ascii(ensure_ascii_)
|
, ensure_ascii(ensure_ascii_)
|
||||||
@@ -106,9 +103,10 @@ class serializer
|
|||||||
additional parameter. Arrays and objects are serialized without recursion,
|
additional parameter. Arrays and objects are serialized without recursion,
|
||||||
however deeply they are nested.
|
however deeply they are nested.
|
||||||
|
|
||||||
- strings and object keys are escaped using `escape_string()`
|
- strings and object keys are escaped using @ref dump_escaped
|
||||||
- integer numbers are converted implicitly via `operator<<`
|
- integer numbers are converted using a digit-pair lookup table (@ref dump_integer)
|
||||||
- floating-point numbers are converted to a string using `"%g"` format
|
- floating-point numbers are converted to a string using @ref dump_float, which
|
||||||
|
uses `to_chars` for IEEE-754 types and `snprintf` otherwise
|
||||||
- binary values are serialized as objects containing the subtype and the
|
- binary values are serialized as objects containing the subtype and the
|
||||||
byte array
|
byte array
|
||||||
|
|
||||||
@@ -283,127 +281,16 @@ class serializer
|
|||||||
}
|
}
|
||||||
|
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
{
|
|
||||||
put_char('"');
|
|
||||||
dump_escaped(*val.m_data.m_value.string);
|
|
||||||
put_char('"');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::binary:
|
case value_t::binary:
|
||||||
{
|
|
||||||
if (pretty_print)
|
|
||||||
{
|
|
||||||
put_literal("{\n");
|
|
||||||
|
|
||||||
// variable to hold indentation for recursive calls
|
|
||||||
const auto new_indent = next_indent(current_indent, indent_step);
|
|
||||||
|
|
||||||
put_indent(new_indent);
|
|
||||||
|
|
||||||
put_literal("\"bytes\": [");
|
|
||||||
|
|
||||||
if (!val.m_data.m_value.binary->empty())
|
|
||||||
{
|
|
||||||
for (auto i = val.m_data.m_value.binary->cbegin();
|
|
||||||
i != val.m_data.m_value.binary->cend() - 1; ++i)
|
|
||||||
{
|
|
||||||
dump_byte(*i);
|
|
||||||
put_literal(", ");
|
|
||||||
}
|
|
||||||
dump_byte(val.m_data.m_value.binary->back());
|
|
||||||
}
|
|
||||||
|
|
||||||
put_literal("],\n");
|
|
||||||
put_indent(new_indent);
|
|
||||||
|
|
||||||
put_literal("\"subtype\": ");
|
|
||||||
if (val.m_data.m_value.binary->has_subtype())
|
|
||||||
{
|
|
||||||
dump_integer(val.m_data.m_value.binary->subtype());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
put_literal("null");
|
|
||||||
}
|
|
||||||
put_char('\n');
|
|
||||||
put_indent(current_indent);
|
|
||||||
put_char('}');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
put_literal("{\"bytes\":[");
|
|
||||||
|
|
||||||
if (!val.m_data.m_value.binary->empty())
|
|
||||||
{
|
|
||||||
for (auto i = val.m_data.m_value.binary->cbegin();
|
|
||||||
i != val.m_data.m_value.binary->cend() - 1; ++i)
|
|
||||||
{
|
|
||||||
dump_byte(*i);
|
|
||||||
put_char(',');
|
|
||||||
}
|
|
||||||
dump_byte(val.m_data.m_value.binary->back());
|
|
||||||
}
|
|
||||||
|
|
||||||
put_literal("],\"subtype\":");
|
|
||||||
if (val.m_data.m_value.binary->has_subtype())
|
|
||||||
{
|
|
||||||
dump_integer(val.m_data.m_value.binary->subtype());
|
|
||||||
put_char('}');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
put_literal("null}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::boolean:
|
case value_t::boolean:
|
||||||
{
|
|
||||||
if (val.m_data.m_value.boolean)
|
|
||||||
{
|
|
||||||
put_literal("true");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
put_literal("false");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::number_integer:
|
case value_t::number_integer:
|
||||||
{
|
|
||||||
dump_integer(val.m_data.m_value.number_integer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::number_unsigned:
|
case value_t::number_unsigned:
|
||||||
{
|
|
||||||
dump_integer(val.m_data.m_value.number_unsigned);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::number_float:
|
case value_t::number_float:
|
||||||
{
|
|
||||||
dump_float(val.m_data.m_value.number_float);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::discarded:
|
case value_t::discarded:
|
||||||
{
|
|
||||||
put_literal("<discarded>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::null:
|
case value_t::null:
|
||||||
{
|
default:
|
||||||
put_literal("null");
|
dump_scalar(val, current_indent);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
default: // LCOV_EXCL_LINE
|
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -560,9 +447,9 @@ class serializer
|
|||||||
@brief serialize the value @a val, but not the elements of a container
|
@brief serialize the value @a val, but not the elements of a container
|
||||||
|
|
||||||
An object or array with elements is opened and pushed onto @a stack for
|
An object or array with elements is opened and pushed onto @a stack for
|
||||||
@ref dump_internal to walk; everything else - including a binary value,
|
@ref dump_iteratively to walk; everything else - including a binary value,
|
||||||
which looks like an object but has no elements to descend into - is written
|
which looks like an object but has no elements to descend into - is written
|
||||||
out here in full.
|
out in full by @ref dump_scalar.
|
||||||
*/
|
*/
|
||||||
void dump_value(const BasicJsonType& val,
|
void dump_value(const BasicJsonType& val,
|
||||||
const std::size_t current_indent,
|
const std::size_t current_indent,
|
||||||
@@ -620,6 +507,35 @@ class serializer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case value_t::string:
|
||||||
|
case value_t::binary:
|
||||||
|
case value_t::boolean:
|
||||||
|
case value_t::number_integer:
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
case value_t::number_float:
|
||||||
|
case value_t::discarded:
|
||||||
|
case value_t::null:
|
||||||
|
default:
|
||||||
|
dump_scalar(val, current_indent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief serialize the value @a val, which is neither an object nor an array
|
||||||
|
|
||||||
|
Shared by @ref dump_internal and @ref dump_value, so that a value is written
|
||||||
|
the same way however deeply it is nested. A binary value is written out here
|
||||||
|
in full: it looks like an object, but has no elements to descend into.
|
||||||
|
|
||||||
|
@param[in] val value to serialize; not an object or array
|
||||||
|
@param[in] current_indent the indentation of @a val, used for a
|
||||||
|
pretty-printed binary value
|
||||||
|
*/
|
||||||
|
void dump_scalar(const BasicJsonType& val, const std::size_t current_indent)
|
||||||
|
{
|
||||||
|
switch (val.m_data.m_type)
|
||||||
|
{
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
{
|
{
|
||||||
put_char('"');
|
put_char('"');
|
||||||
@@ -740,6 +656,8 @@ class serializer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case value_t::object: // LCOV_EXCL_LINE
|
||||||
|
case value_t::array: // LCOV_EXCL_LINE
|
||||||
default: // LCOV_EXCL_LINE
|
default: // LCOV_EXCL_LINE
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
@@ -768,7 +686,7 @@ class serializer
|
|||||||
Escape a string by replacing certain special characters by a sequence of an
|
Escape a string by replacing certain special characters by a sequence of an
|
||||||
escape character (backslash) and another character and other control
|
escape character (backslash) and another character and other control
|
||||||
characters by a sequence of "\u" followed by a four-digit hex
|
characters by a sequence of "\u" followed by a four-digit hex
|
||||||
representation. The escaped string is written to output stream @a o.
|
representation. The escaped string is appended to @ref write_buffer.
|
||||||
|
|
||||||
@param[in] s the string to escape
|
@param[in] s the string to escape
|
||||||
|
|
||||||
@@ -962,7 +880,7 @@ class serializer
|
|||||||
{
|
{
|
||||||
case error_handler_t::strict:
|
case error_handler_t::strict:
|
||||||
{
|
{
|
||||||
JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex_bytes(byte | 0)), nullptr));
|
JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", detail::hex_byte(byte)), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
case error_handler_t::ignore:
|
case error_handler_t::ignore:
|
||||||
@@ -995,9 +913,9 @@ class serializer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xEF');
|
string_buffer[bytes++] = '\xEF';
|
||||||
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBF');
|
string_buffer[bytes++] = '\xBF';
|
||||||
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBD');
|
string_buffer[bytes++] = '\xBD';
|
||||||
}
|
}
|
||||||
|
|
||||||
// write buffer and reset index; there must be 13 bytes
|
// write buffer and reset index; there must be 13 bytes
|
||||||
@@ -1054,7 +972,7 @@ class serializer
|
|||||||
{
|
{
|
||||||
case error_handler_t::strict:
|
case error_handler_t::strict:
|
||||||
{
|
{
|
||||||
JSON_THROW(type_error::create(316, concat("incomplete UTF-8 string; last byte: 0x", hex_bytes(static_cast<std::uint8_t>(s[s.size() - 1] | 0))), nullptr));
|
JSON_THROW(type_error::create(316, concat("incomplete UTF-8 string; last byte: 0x", detail::hex_byte(static_cast<std::uint8_t>(s[s.size() - 1]))), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
case error_handler_t::ignore:
|
case error_handler_t::ignore:
|
||||||
@@ -1275,20 +1193,6 @@ class serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
* @brief convert a byte to a uppercase hex representation
|
|
||||||
* @param[in] byte byte to represent
|
|
||||||
* @return representation ("00".."FF")
|
|
||||||
*/
|
|
||||||
static std::string hex_bytes(std::uint8_t byte)
|
|
||||||
{
|
|
||||||
std::string result = "FF";
|
|
||||||
constexpr const char* nibble_to_hex = "0123456789ABCDEF";
|
|
||||||
result[0] = nibble_to_hex[byte / 16];
|
|
||||||
result[1] = nibble_to_hex[byte % 16];
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief write a lowercase "\uXXXX" escape sequence into @a string_buffer
|
* @brief write a lowercase "\uXXXX" escape sequence into @a string_buffer
|
||||||
*
|
*
|
||||||
@@ -1402,7 +1306,7 @@ class serializer
|
|||||||
/*!
|
/*!
|
||||||
@brief dump an integer
|
@brief dump an integer
|
||||||
|
|
||||||
Dump a given integer to output stream @a o. Works internally with
|
Dump a given integer, appending it to @ref write_buffer. Works internally with
|
||||||
@a number_buffer.
|
@a number_buffer.
|
||||||
|
|
||||||
@param[in] x integer number (signed or unsigned) to dump
|
@param[in] x integer number (signed or unsigned) to dump
|
||||||
@@ -1439,7 +1343,7 @@ class serializer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// use a pointer to fill the buffer
|
// use a pointer to fill the buffer
|
||||||
auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto)
|
||||||
|
|
||||||
number_unsigned_t abs_value;
|
number_unsigned_t abs_value;
|
||||||
|
|
||||||
@@ -1493,7 +1397,7 @@ class serializer
|
|||||||
/*!
|
/*!
|
||||||
@brief dump a floating-point number
|
@brief dump a floating-point number
|
||||||
|
|
||||||
Dump a given floating-point number to output stream @a o. Works internally
|
Dump a given floating-point number, appending it to @ref write_buffer. Works internally
|
||||||
with @a number_buffer.
|
with @a number_buffer.
|
||||||
|
|
||||||
@param[in] x floating-point number to dump
|
@param[in] x floating-point number to dump
|
||||||
@@ -1554,21 +1458,28 @@ 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 thousands separator and decimal point now,
|
||||||
|
// matching what snprintf_float() just used (see lexer::get_decimal_point())
|
||||||
|
const auto* loc = std::localeconv();
|
||||||
|
JSON_ASSERT(loc != nullptr);
|
||||||
|
const char thousands_sep = (loc->thousands_sep == nullptr) ? '\0' : *loc->thousands_sep;
|
||||||
|
const char decimal_point = (loc->decimal_point == nullptr) ? '\0' : *loc->decimal_point;
|
||||||
|
|
||||||
// erase thousands separators
|
// erase thousands separators
|
||||||
if (locale.thousands_sep != '\0')
|
if (thousands_sep != '\0')
|
||||||
{
|
{
|
||||||
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081
|
// 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, locale.thousands_sep);
|
const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep);
|
||||||
std::fill(end, number_buffer.end(), '\0');
|
std::fill(end, number_buffer.end(), '\0');
|
||||||
JSON_ASSERT((end - number_buffer.begin()) <= len);
|
JSON_ASSERT((end - number_buffer.begin()) <= len);
|
||||||
len = (end - number_buffer.begin());
|
len = (end - number_buffer.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert decimal point to '.'
|
// convert decimal point to '.'
|
||||||
if (locale.decimal_point != '\0' && locale.decimal_point != '.')
|
if (decimal_point != '\0' && decimal_point != '.')
|
||||||
{
|
{
|
||||||
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081
|
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081
|
||||||
const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), locale.decimal_point);
|
const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
|
||||||
if (dec_pos != number_buffer.end())
|
if (dec_pos != number_buffer.end())
|
||||||
{
|
{
|
||||||
*dec_pos = '.';
|
*dec_pos = '.';
|
||||||
@@ -1613,34 +1524,17 @@ class serializer
|
|||||||
*/
|
*/
|
||||||
number_unsigned_t remove_sign(number_integer_t x) noexcept
|
number_unsigned_t remove_sign(number_integer_t x) noexcept
|
||||||
{
|
{
|
||||||
JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)()); // NOLINT(misc-redundant-expression)
|
JSON_ASSERT(x < 0);
|
||||||
return static_cast<number_unsigned_t>(-(x + 1)) + 1;
|
return static_cast<number_unsigned_t>(-(x + 1)) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// the locale's thousand separator and decimal point characters
|
|
||||||
struct locale_chars
|
|
||||||
{
|
|
||||||
explicit locale_chars(const std::lconv* loc) noexcept
|
|
||||||
: thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->thousands_sep)))
|
|
||||||
, decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point)))
|
|
||||||
{}
|
|
||||||
|
|
||||||
const char thousands_sep;
|
|
||||||
const char decimal_point;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// the output of the serializer (non-owning; the adapter lives at the call site)
|
/// the output of the serializer (non-owning; the adapter lives at the call site)
|
||||||
output_adapter_protocol<char>* o = nullptr;
|
output_adapter_protocol<char>* o = nullptr;
|
||||||
|
|
||||||
/// a (hopefully) large enough character buffer
|
/// a (hopefully) large enough character buffer
|
||||||
std::array<char, 64> number_buffer{{}};
|
std::array<char, 64> number_buffer{{}};
|
||||||
|
|
||||||
/// computed once from std::localeconv() at construction; @ref
|
|
||||||
/// locale_chars keeps std::localeconv()'s pointer from having to be held
|
|
||||||
/// past the constructor, while still letting these stay const
|
|
||||||
const locale_chars locale;
|
|
||||||
|
|
||||||
/// string buffer
|
/// string buffer
|
||||||
std::array<char, 512> string_buffer{{}};
|
std::array<char, 512> string_buffer{{}};
|
||||||
|
|
||||||
|
|||||||
@@ -19,11 +19,17 @@ namespace detail
|
|||||||
/*!
|
/*!
|
||||||
@brief the number of nesting levels an operation recurses into
|
@brief the number of nesting levels an operation recurses into
|
||||||
|
|
||||||
Operations that walk a value (serializing, hashing, merging, ...) recurse once
|
Operations that walk a value (copying, comparing, serializing, hashing, merging,
|
||||||
|
...) recurse once
|
||||||
per nesting level, which is fastest, but a value nested deeply enough would
|
per nesting level, which is fastest, but a value nested deeply enough would
|
||||||
exhaust the call stack. So they recurse only this many levels deep and finish
|
exhaust the call stack. So they recurse only this many levels deep and finish
|
||||||
whatever lies below with an explicit stack. All of them share this limit.
|
whatever lies below with an explicit stack. All of them share this limit.
|
||||||
|
|
||||||
|
Most of them pass the depth down as an argument. The copy constructor and the
|
||||||
|
comparison operators cannot, as their signatures are fixed, so they count it
|
||||||
|
in basic_json::nesting_depth() instead, a byte per thread; the limit must
|
||||||
|
therefore stay below 255.
|
||||||
|
|
||||||
@sa https://github.com/nlohmann/json/issues/5387
|
@sa https://github.com/nlohmann/json/issues/5387
|
||||||
*/
|
*/
|
||||||
constexpr std::size_t recursion_depth_limit() noexcept
|
constexpr std::size_t recursion_depth_limit() noexcept
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// | | |__ | | | | | | version 3.12.0
|
// | | |__ | | | | | | version 3.12.0
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
//
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2008, 2009 Björn Hoehrmann <bjoern@hoehrmann.de>
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
@@ -12,6 +13,7 @@
|
|||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstdint> // uint8_t, uint32_t
|
#include <cstdint> // uint8_t, uint32_t
|
||||||
#include <string> // string, to_string
|
#include <string> // string, to_string
|
||||||
|
#include <utility> // move
|
||||||
|
|
||||||
#include <nlohmann/detail/abi_macros.hpp>
|
#include <nlohmann/detail/abi_macros.hpp>
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
@@ -36,6 +38,71 @@ StringType to_string(std::size_t value)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @return a byte as two uppercase hexadecimal digits
|
||||||
|
inline std::string hex_byte(const std::uint8_t byte)
|
||||||
|
{
|
||||||
|
std::string result = "00";
|
||||||
|
constexpr const char* nibble_to_hex = "0123456789ABCDEF";
|
||||||
|
result[0] = nibble_to_hex[byte / 16];
|
||||||
|
result[1] = nibble_to_hex[byte % 16];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////
|
||||||
|
// UTF-8 encoding //
|
||||||
|
///////////////////
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief encode a Unicode code point as UTF-8
|
||||||
|
|
||||||
|
Used to turn a decoded code point back into bytes: by the wide-string input
|
||||||
|
adapters in input_adapters.hpp (one code point per UTF-32 unit, per UTF-16
|
||||||
|
unit outside the surrogate range, and per valid UTF-16 surrogate pair), and
|
||||||
|
by the lexer's `\uXXXX`/`\uXXXX\uYYYY` handling in lexer.hpp. Passing a
|
||||||
|
code point above U+10FFFF, or one in the surrogate range U+D800..U+DFFF, is
|
||||||
|
undefined behavior; callers are expected to have rejected those already
|
||||||
|
(the wide-string adapters pass malformed units through unencoded instead of
|
||||||
|
calling this function, and the lexer rejects unpaired surrogates before
|
||||||
|
reaching it).
|
||||||
|
|
||||||
|
@tparam Out a callable invoked with one byte (as std::uint32_t, 0x00..0xFF)
|
||||||
|
at a time, most significant byte first
|
||||||
|
@param[in] cp the code point to encode (at most U+10FFFF)
|
||||||
|
@param[in] out called once for each byte of the UTF-8 encoding of @a cp
|
||||||
|
*/
|
||||||
|
template<typename Out>
|
||||||
|
void encode_utf8(std::uint32_t cp, Out&& out)
|
||||||
|
{
|
||||||
|
JSON_ASSERT(cp <= 0x10FFFF);
|
||||||
|
|
||||||
|
if (cp < 0x80)
|
||||||
|
{
|
||||||
|
// 1-byte characters: 0xxxxxxx (ASCII)
|
||||||
|
out(cp);
|
||||||
|
}
|
||||||
|
else if (cp <= 0x7FF)
|
||||||
|
{
|
||||||
|
// 2-byte characters: 110xxxxx 10xxxxxx
|
||||||
|
out(0xC0u | (cp >> 6u));
|
||||||
|
out(0x80u | (cp & 0x3Fu));
|
||||||
|
}
|
||||||
|
else if (cp <= 0xFFFF)
|
||||||
|
{
|
||||||
|
// 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx
|
||||||
|
out(0xE0u | (cp >> 12u));
|
||||||
|
out(0x80u | ((cp >> 6u) & 0x3Fu));
|
||||||
|
out(0x80u | (cp & 0x3Fu));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||||
|
out(0xF0u | (cp >> 18u));
|
||||||
|
out(0x80u | ((cp >> 12u) & 0x3Fu));
|
||||||
|
out(0x80u | ((cp >> 6u) & 0x3Fu));
|
||||||
|
out(0x80u | (cp & 0x3Fu));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////
|
///////////////////
|
||||||
// UTF-8 decoding //
|
// UTF-8 decoding //
|
||||||
///////////////////
|
///////////////////
|
||||||
@@ -51,11 +118,23 @@ This is a single-byte step of a "shift-based" UTF-8 decoder originally
|
|||||||
written by Björn Hoehrmann. See
|
written by Björn Hoehrmann. See
|
||||||
http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
|
http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
|
||||||
|
|
||||||
This decoder is the single source of truth for UTF-8 validation in this
|
The library checks UTF-8 well-formedness (RFC 3629, section 4) in four
|
||||||
library: it is used both by the serializer (to escape and, in strict mode,
|
places, which differ in speed, diagnostics, and how they read the input:
|
||||||
reject ill-formed UTF-8 when dumping a string) and by the binary readers
|
|
||||||
(to reject ill-formed UTF-8 in CBOR/MessagePack/BSON/UBJSON text strings at
|
- decode() and @ref is_valid_utf8 below: the serializer (to escape and, in
|
||||||
decode time; see @ref is_valid_utf8 below).
|
strict mode, reject ill-formed UTF-8 when dumping a string) and the CBOR,
|
||||||
|
MessagePack, BSON, UBJSON and BJData readers (to reject ill-formed UTF-8 in
|
||||||
|
text strings at decode time).
|
||||||
|
- the per-lead-byte switch in lexer::scan_string(): JSON text, with a
|
||||||
|
diagnostic for each kind of error.
|
||||||
|
- validate_one_utf8() and valid_utf8_prefix() in string_scan.hpp: the lexer's
|
||||||
|
bulk string scan, the bulk path of the BON8 reader, and the BON8 writer.
|
||||||
|
They must accept exactly what the lexer's switch accepts.
|
||||||
|
- the byte path of binary_reader::get_bon8_string(): BON8 input without bulk
|
||||||
|
access, and the bytes the bulk path leaves to it.
|
||||||
|
|
||||||
|
All four must accept the same set of sequences, so a change to one needs a
|
||||||
|
matching change to the others.
|
||||||
|
|
||||||
@param[in,out] state the current decoder state
|
@param[in,out] state the current decoder state
|
||||||
@param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT)
|
@param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT)
|
||||||
@@ -133,5 +212,78 @@ inline bool is_valid_utf8(const StringType& s, const std::size_t first = 0) noex
|
|||||||
return state == UTF8_ACCEPT;
|
return state == UTF8_ACCEPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief append U+FFFD REPLACEMENT CHARACTER, encoded in UTF-8
|
||||||
|
@param[in,out] s the string to append to
|
||||||
|
*/
|
||||||
|
template<typename StringType>
|
||||||
|
inline void append_replacement_character(StringType& s)
|
||||||
|
{
|
||||||
|
s.push_back(static_cast<typename StringType::value_type>(0xEFu));
|
||||||
|
s.push_back(static_cast<typename StringType::value_type>(0xBFu));
|
||||||
|
s.push_back(static_cast<typename StringType::value_type>(0xBDu));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief replace ill-formed UTF-8 with U+FFFD REPLACEMENT CHARACTER
|
||||||
|
|
||||||
|
Each maximal subpart of an ill-formed sequence becomes one U+FFFD, as the
|
||||||
|
Unicode Standard recommends (Section 3.9, "U+FFFD Substitution of Maximal
|
||||||
|
Subparts"), and as the parser for JSON text does when it recovers from errors.
|
||||||
|
|
||||||
|
@param[in,out] s the string to repair
|
||||||
|
@param[in] first index of the first byte to repair; the bytes before it are
|
||||||
|
assumed to be valid UTF-8 that ends on a code point boundary
|
||||||
|
*/
|
||||||
|
template<typename StringType>
|
||||||
|
inline void replace_invalid_utf8(StringType& s, const std::size_t first = 0)
|
||||||
|
{
|
||||||
|
StringType result = s;
|
||||||
|
result.resize(first);
|
||||||
|
|
||||||
|
std::uint8_t state = UTF8_ACCEPT;
|
||||||
|
std::uint32_t codepoint = 0;
|
||||||
|
// the first byte of the sequence being decoded
|
||||||
|
std::size_t sequence_start = first;
|
||||||
|
|
||||||
|
std::size_t i = first;
|
||||||
|
while (i < s.size())
|
||||||
|
{
|
||||||
|
switch (decode(state, codepoint, static_cast<std::uint8_t>(s[i])))
|
||||||
|
{
|
||||||
|
case UTF8_ACCEPT:
|
||||||
|
for (++i; sequence_start < i; ++sequence_start)
|
||||||
|
{
|
||||||
|
result.push_back(s[sequence_start]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UTF8_REJECT:
|
||||||
|
append_replacement_character(result);
|
||||||
|
// the byte that made the sequence ill-formed begins the next
|
||||||
|
// one, unless it began this one
|
||||||
|
if (i == sequence_start)
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
state = UTF8_ACCEPT;
|
||||||
|
sequence_start = i;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // in the middle of a sequence
|
||||||
|
++i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// a sequence that the string ends in the middle of
|
||||||
|
if (state != UTF8_ACCEPT)
|
||||||
|
{
|
||||||
|
append_replacement_character(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
s = std::move(result);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|||||||
+840
-353
File diff suppressed because it is too large
Load Diff
@@ -11,8 +11,7 @@
|
|||||||
|
|
||||||
#include <cstdint> // int64_t, uint64_t
|
#include <cstdint> // int64_t, uint64_t
|
||||||
#include <map> // map
|
#include <map> // map
|
||||||
#include <memory> // allocator
|
#include <string> // allocator, string
|
||||||
#include <string> // string
|
|
||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
|
|
||||||
#include <nlohmann/detail/abi_macros.hpp>
|
#include <nlohmann/detail/abi_macros.hpp>
|
||||||
@@ -32,7 +31,7 @@ This serializer ignores the template arguments and uses ADL
|
|||||||
for serialization.
|
for serialization.
|
||||||
*/
|
*/
|
||||||
template<typename T = void, typename SFINAE = void>
|
template<typename T = void, typename SFINAE = void>
|
||||||
struct adl_serializer;
|
struct adl_serializer; // IWYU pragma: keep
|
||||||
|
|
||||||
/// a class to store JSON values
|
/// a class to store JSON values
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/
|
/// @sa https://json.nlohmann.me/api/basic_json/
|
||||||
@@ -48,12 +47,12 @@ template<template<typename U, typename V, typename... Args> class ObjectType =
|
|||||||
adl_serializer,
|
adl_serializer,
|
||||||
class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
|
class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
|
||||||
class CustomBaseClass = void>
|
class CustomBaseClass = void>
|
||||||
class basic_json;
|
class basic_json; // IWYU pragma: keep
|
||||||
|
|
||||||
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/
|
/// @sa https://json.nlohmann.me/api/json_pointer/
|
||||||
template<typename RefStringType>
|
template<typename RefStringType>
|
||||||
class json_pointer;
|
class json_pointer; // IWYU pragma: keep
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief default specialization
|
@brief default specialization
|
||||||
@@ -64,7 +63,7 @@ using json = basic_json<>;
|
|||||||
/// @brief a minimal map-like container that preserves insertion order
|
/// @brief a minimal map-like container that preserves insertion order
|
||||||
/// @sa https://json.nlohmann.me/api/ordered_map/
|
/// @sa https://json.nlohmann.me/api/ordered_map/
|
||||||
template<class Key, class T, class IgnoredLess, class Allocator>
|
template<class Key, class T, class IgnoredLess, class Allocator>
|
||||||
struct ordered_map;
|
struct ordered_map; // IWYU pragma: keep
|
||||||
|
|
||||||
/// @brief specialization that maintains the insertion order of object keys
|
/// @brief specialization that maintains the insertion order of object keys
|
||||||
/// @sa https://json.nlohmann.me/api/ordered_json/
|
/// @sa https://json.nlohmann.me/api/ordered_json/
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | JSON for Modern C++
|
||||||
|
// | | |__ | | | | | | version 3.12.0
|
||||||
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#ifndef INCLUDE_NLOHMANN_JSON_LITERALS_HPP_
|
||||||
|
#define INCLUDE_NLOHMANN_JSON_LITERALS_HPP_
|
||||||
|
|
||||||
|
#include <cstddef> // size_t
|
||||||
|
#include <string> // string
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(misc-header-include-cycle): json.hpp includes this header at its end
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
// This header is included at the end of <nlohmann/json.hpp> unless
|
||||||
|
// JSON_NO_AUTOMATIC_UDLS is defined, and can be included on its own after that.
|
||||||
|
// Either way, the library's internal macros are no longer defined here (and the
|
||||||
|
// amalgamation inlines macro_scope.hpp only once), so only standard and public
|
||||||
|
// macros may be used below.
|
||||||
|
|
||||||
|
// declares the literal operator for the given suffix; GCC 4.8 requires a space
|
||||||
|
// between "" and the suffix, which newer compilers deprecate (CWG 2521)
|
||||||
|
#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)
|
||||||
|
#define NLOHMANN_JSON_LITERAL_OPERATOR(suffix) operator""##suffix
|
||||||
|
#else
|
||||||
|
#define NLOHMANN_JSON_LITERAL_OPERATOR(suffix) operator"" suffix
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
inline namespace literals
|
||||||
|
{
|
||||||
|
inline namespace json_literals
|
||||||
|
{
|
||||||
|
|
||||||
|
/// @brief user-defined string literal for JSON values
|
||||||
|
/// @sa https://json.nlohmann.me/api/operator_literal_json/
|
||||||
|
inline nlohmann::json NLOHMANN_JSON_LITERAL_OPERATOR(_json)(const char* s, std::size_t n)
|
||||||
|
{
|
||||||
|
return nlohmann::json::parse(s, s + n);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__cpp_char8_t)
|
||||||
|
inline nlohmann::json operator""_json(const char8_t* s, std::size_t n)
|
||||||
|
{
|
||||||
|
return nlohmann::json::parse(reinterpret_cast<const char*>(s),
|
||||||
|
reinterpret_cast<const char*>(s) + n);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// @brief user-defined string literal for JSON pointer
|
||||||
|
/// @sa https://json.nlohmann.me/api/operator_literal_json_pointer/
|
||||||
|
inline nlohmann::json::json_pointer NLOHMANN_JSON_LITERAL_OPERATOR(_json_pointer)(const char* s, std::size_t n)
|
||||||
|
{
|
||||||
|
return nlohmann::json::json_pointer(std::string(s, n));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__cpp_char8_t)
|
||||||
|
inline nlohmann::json::json_pointer operator""_json_pointer(const char8_t* s, std::size_t n)
|
||||||
|
{
|
||||||
|
return nlohmann::json::json_pointer(std::string(reinterpret_cast<const char*>(s), n));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace json_literals
|
||||||
|
} // namespace literals
|
||||||
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#if !defined(JSON_USE_GLOBAL_UDLS) || JSON_USE_GLOBAL_UDLS
|
||||||
|
using nlohmann::literals::json_literals::NLOHMANN_JSON_LITERAL_OPERATOR(_json); // NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
||||||
|
using nlohmann::literals::json_literals::NLOHMANN_JSON_LITERAL_OPERATOR(_json_pointer); //NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#undef NLOHMANN_JSON_LITERAL_OPERATOR
|
||||||
|
|
||||||
|
#endif // INCLUDE_NLOHMANN_JSON_LITERALS_HPP_
|
||||||
@@ -8,15 +8,18 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm> // max, min
|
||||||
#include <functional> // equal_to, less
|
#include <functional> // equal_to, less
|
||||||
#include <initializer_list> // initializer_list
|
#include <initializer_list> // initializer_list
|
||||||
#include <iterator> // input_iterator_tag, iterator_traits
|
#include <iterator> // input_iterator_tag, iterator_traits
|
||||||
#include <memory> // allocator
|
#include <new> // for operator new (placement new)
|
||||||
#include <stdexcept> // for out_of_range
|
#include <stdexcept> // for out_of_range
|
||||||
#include <type_traits> // enable_if, is_convertible
|
#include <tuple> // forward_as_tuple
|
||||||
#include <utility> // pair
|
#include <type_traits> // enable_if, integral_constant, is_convertible, is_nothrow_move_constructible
|
||||||
#include <vector> // vector
|
#include <utility> // forward, move, pair, piecewise_construct
|
||||||
|
#include <vector> // vector, allocator
|
||||||
|
|
||||||
|
#include <nlohmann/detail/abi_macros.hpp>
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||||
|
|
||||||
@@ -79,7 +82,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
|
|||||||
return {it, false};
|
return {it, false};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Container::emplace_back(key, std::forward<T>(t));
|
append(key, std::forward<T>(t));
|
||||||
return {std::prev(this->end()), true};
|
return {std::prev(this->end()), true};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +97,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
|
|||||||
return {it, false};
|
return {it, false};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Container::emplace_back(std::forward<KeyType>(key), std::forward<T>(t));
|
append(std::forward<KeyType>(key), std::forward<T>(t));
|
||||||
return {std::prev(this->end()), true};
|
return {std::prev(this->end()), true};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,7 +371,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
|
|||||||
return {it, false};
|
return {it, false};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Container::push_back(value);
|
append(value);
|
||||||
return {--this->end(), true};
|
return {--this->end(), true};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,6 +389,64 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/*!
|
||||||
|
@brief add an element whose key is not yet contained at the end
|
||||||
|
|
||||||
|
A std::vector copies all elements when it grows, because their const keys
|
||||||
|
make them not nothrow move constructible. For ordered_json, this is a deep
|
||||||
|
copy of every value. Where the strong exception guarantee can be kept, grow
|
||||||
|
the storage here instead, copying only the keys and moving the values.
|
||||||
|
*/
|
||||||
|
template<typename... Args>
|
||||||
|
void append(Args&& ... args)
|
||||||
|
{
|
||||||
|
// evaluated here rather than at class scope, because T is still
|
||||||
|
// incomplete when basic_json instantiates its object_t
|
||||||
|
using move_values = std::integral_constant<bool, detail::conjunction<
|
||||||
|
detail::negation<std::is_nothrow_move_constructible<value_type>>,
|
||||||
|
std::is_copy_constructible<key_type>,
|
||||||
|
detail::is_default_constructible<mapped_type>,
|
||||||
|
std::is_nothrow_move_assignable<mapped_type>>::value>;
|
||||||
|
append_impl(move_values{}, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
void append_impl(std::true_type /*unused*/, Args&& ... args)
|
||||||
|
{
|
||||||
|
if (this->size() < this->capacity())
|
||||||
|
{
|
||||||
|
Container::emplace_back(std::forward<Args>(args)...);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. May throw, but only changes tmp: copy the keys, value-initialize
|
||||||
|
// the values, and add the new element. The arguments may refer to
|
||||||
|
// elements of this container, so they are used before any value is
|
||||||
|
// moved out of it.
|
||||||
|
Container tmp(this->get_allocator()); // equal allocators, so swap() is valid
|
||||||
|
tmp.reserve((std::min)(this->max_size(), (std::max)(size_type{1}, 2 * this->size())));
|
||||||
|
for (const auto& element : *this)
|
||||||
|
{
|
||||||
|
tmp.emplace_back(std::piecewise_construct, std::forward_as_tuple(element.first), std::forward_as_tuple());
|
||||||
|
}
|
||||||
|
tmp.emplace_back(std::forward<Args>(args)...);
|
||||||
|
|
||||||
|
// 2. Cannot throw: move the values over and adopt the new storage.
|
||||||
|
auto it = tmp.begin();
|
||||||
|
for (auto& element : *this)
|
||||||
|
{
|
||||||
|
it->second = std::move(element.second);
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
Container::swap(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
void append_impl(std::false_type /*unused*/, Args&& ... args)
|
||||||
|
{
|
||||||
|
Container::emplace_back(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
JSON_NO_UNIQUE_ADDRESS key_compare m_compare = key_compare();
|
JSON_NO_UNIQUE_ADDRESS key_compare m_compare = key_compare();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ nlohmann_json_multiple_headers = declare_dependency(
|
|||||||
if not meson.is_subproject()
|
if not meson.is_subproject()
|
||||||
install_headers('single_include/nlohmann/json.hpp', subdir: 'nlohmann')
|
install_headers('single_include/nlohmann/json.hpp', subdir: 'nlohmann')
|
||||||
install_headers('single_include/nlohmann/json_fwd.hpp', subdir: 'nlohmann')
|
install_headers('single_include/nlohmann/json_fwd.hpp', subdir: 'nlohmann')
|
||||||
|
install_headers('single_include/nlohmann/json_literals.hpp', subdir: 'nlohmann')
|
||||||
|
|
||||||
pkgc = import('pkgconfig')
|
pkgc = import('pkgconfig')
|
||||||
pkgc.generate(name: 'nlohmann_json',
|
pkgc.generate(name: 'nlohmann_json',
|
||||||
|
|||||||
+5538
-2206
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user