mirror of
https://github.com/nlohmann/json.git
synced 2026-10-01 20:20:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffc3691598 | ||
|
|
ec38da53eb | ||
|
|
30d5107b8d | ||
|
|
bdaa8381d7 | ||
|
|
85fec73da7 |
+17
@@ -0,0 +1,17 @@
|
||||
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,10 +24,6 @@ updates:
|
||||
interval: daily
|
||||
cooldown:
|
||||
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
|
||||
directory: /tools/generate_natvis
|
||||
|
||||
@@ -30,6 +30,14 @@ environment:
|
||||
CMAKE_OPTIONS: ""
|
||||
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
|
||||
configuration: Release
|
||||
platform: x86
|
||||
@@ -66,6 +74,9 @@ install:
|
||||
- if "%platform%"=="x86" set GENERATOR_PLATFORM=Win32
|
||||
|
||||
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%"
|
||||
|
||||
build_script:
|
||||
|
||||
@@ -35,7 +35,6 @@ jobs:
|
||||
MAIN_DIR: ${{ github.workspace }}/main
|
||||
INCLUDE_DIR: ${{ github.workspace }}/main/single_include/nlohmann
|
||||
TOOL_DIR: ${{ github.workspace }}/tools/tools/amalgamate
|
||||
NATVIS_TOOL_DIR: ${{ github.workspace }}/tools/tools/generate_natvis
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
@@ -62,55 +61,12 @@ jobs:
|
||||
python3 -mvenv venv
|
||||
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
|
||||
run: |
|
||||
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_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/
|
||||
cmake -P cmake/scripts/gen_bazel_build_file.cmake
|
||||
@@ -131,19 +87,6 @@ jobs:
|
||||
${{ 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)
|
||||
|
||||
- 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
|
||||
id: diff
|
||||
run: |
|
||||
|
||||
@@ -71,3 +71,38 @@ jobs:
|
||||
run: cmake --build build --parallel 10
|
||||
- name: Test
|
||||
run: cd build ; ctest -j 10 --output-on-failure
|
||||
|
||||
swiftpm:
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Check that Package.swift resolves without a deprecation warning
|
||||
run: swift package dump-package
|
||||
- name: Build the SwiftPM documentation example against this checkout
|
||||
run: |
|
||||
mkdir -p /tmp/json-swiftpm-consumer/Sources/MyLibrary
|
||||
cp docs/mkdocs/docs/integration/swift/example.cpp /tmp/json-swiftpm-consumer/Sources/MyLibrary/example.cpp
|
||||
cat > /tmp/json-swiftpm-consumer/Package.swift << EOF
|
||||
// swift-tools-version: 5.9
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "MyPackage",
|
||||
dependencies: [
|
||||
.package(path: "${{ github.workspace }}")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "MyLibrary",
|
||||
dependencies: [
|
||||
.product(name: "json", package: "json")
|
||||
],
|
||||
publicHeadersPath: "."
|
||||
)
|
||||
]
|
||||
)
|
||||
EOF
|
||||
cd /tmp/json-swiftpm-consumer
|
||||
swift build
|
||||
|
||||
@@ -7,16 +7,6 @@ on:
|
||||
- develop
|
||||
paths:
|
||||
- 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:
|
||||
|
||||
# we don't want to have concurrent jobs, and we don't want to cancel running jobs to avoid broken publications
|
||||
@@ -33,7 +23,7 @@ jobs:
|
||||
contents: write
|
||||
|
||||
if: github.repository == 'nlohmann/json'
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
||||
@@ -41,8 +31,6 @@ jobs:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install virtual environment
|
||||
run: make install_venv -C docs/mkdocs
|
||||
|
||||
@@ -52,13 +52,6 @@ jobs:
|
||||
run: cmake -S . -B build -DJSON_CI=On
|
||||
- name: Build
|
||||
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:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -104,13 +97,13 @@ jobs:
|
||||
|
||||
ci_cmake_options:
|
||||
runs-on: ubuntu-latest
|
||||
container: ubuntu:24.04
|
||||
container: ubuntu:focal
|
||||
strategy:
|
||||
matrix:
|
||||
target: [ci_cmake_flags, ci_test_diagnostics, ci_test_diagnostic_positions, ci_test_noexceptions, ci_test_noimplicitconversions, ci_test_legacycomparison, ci_test_noglobaludls, ci_test_disableenumserialization, ci_test_disabletuplereferenceconversion, ci_test_skiplibraryversioncheck, ci_test_simdutf, ci_test_strict_nul_handling, ci_test_no_thread_local]
|
||||
target: [ci_cmake_flags, ci_test_diagnostics, ci_test_diagnostic_positions, ci_test_noexceptions, ci_test_noimplicitconversions, ci_test_legacycomparison, ci_test_noglobaludls, ci_test_disableenumserialization, ci_test_skiplibraryversioncheck, ci_test_simdutf, ci_test_strict_nul_handling, ci_test_no_thread_local]
|
||||
steps:
|
||||
- name: Install build-essential
|
||||
run: apt-get update ; apt-get install -y build-essential unzip wget git
|
||||
run: apt-get update ; apt-get install -y build-essential unzip wget git libssl-dev
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
@@ -353,8 +346,6 @@ jobs:
|
||||
container: intel/oneapi-hpckit: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: Run CMake
|
||||
@@ -367,8 +358,6 @@ jobs:
|
||||
container: nvcr.io/nvidia/nvhpc:25.5-devel-cuda12.9-ubuntu22.04
|
||||
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: Run CMake
|
||||
@@ -414,21 +403,3 @@ jobs:
|
||||
run: cmake -S . -B build -DJSON_CI=On
|
||||
- name: Build
|
||||
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,8 +87,6 @@ jobs:
|
||||
|
||||
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: Set extra CXX_FLAGS for latest std_version
|
||||
@@ -125,8 +123,6 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Run CMake (Release)
|
||||
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'
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"_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"
|
||||
]
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: json
|
||||
Upstream-Contact: Niels Lohmann <mail@nlohmann.me>
|
||||
Source: https://github.com/nlohmann/json
|
||||
|
||||
Files: *
|
||||
Copyright: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||
License: MIT
|
||||
|
||||
Files: include/nlohmann/thirdparty/hedley.hpp
|
||||
Copyright: 2016-2021 Evan Nemerson <evan@nemerson.com>
|
||||
License: CC0
|
||||
|
||||
Files: include/nlohmann/detail/meta/cpp_future.hpp
|
||||
Copyright: 2013-2026 Niels Lohmann <https://nlohmann.me> and 2018 The Abseil Authors
|
||||
License: MIT AND Apache-2.0
|
||||
|
||||
Files: tests/thirdparty/doctest/*
|
||||
Copyright: 2016-2023 Viktor Kirilov
|
||||
License: MIT
|
||||
|
||||
Files: tests/thirdparty/fifo_map/*
|
||||
Copyright: 2015-2017 Niels Lohmann
|
||||
License: MIT
|
||||
|
||||
Files: tests/thirdparty/imapdl/*
|
||||
Copyright: 2017 Georg Sauthoff <mail@gms.tf>
|
||||
License: GPL-3.0-only
|
||||
|
||||
Files: tools/amalgamate/*
|
||||
Copyright: 2012 Erik Edlund <erik.edlund@32767.se>
|
||||
License: BSD-3-Clause
|
||||
|
||||
Files: tools/gdb_pretty_printer/*
|
||||
Copyright: 2020 Hannes Domani <https://github.com/ssbssa>
|
||||
License: MIT
|
||||
+1
-3
@@ -21,7 +21,6 @@ cc_library(
|
||||
"include/nlohmann/adl_serializer.hpp",
|
||||
"include/nlohmann/byte_container_with_subtype.hpp",
|
||||
"include/nlohmann/detail/abi_macros.hpp",
|
||||
"include/nlohmann/detail/bit_ops.hpp",
|
||||
"include/nlohmann/detail/conversions/from_json.hpp",
|
||||
"include/nlohmann/detail/conversions/to_chars.hpp",
|
||||
"include/nlohmann/detail/conversions/to_json.hpp",
|
||||
@@ -34,7 +33,6 @@ cc_library(
|
||||
"include/nlohmann/detail/input/number_parse.hpp",
|
||||
"include/nlohmann/detail/input/parser.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/iterators/internal_iterator.hpp",
|
||||
"include/nlohmann/detail/iterators/iter_impl.hpp",
|
||||
@@ -67,7 +65,6 @@ cc_library(
|
||||
"include/nlohmann/detail/value_t.hpp",
|
||||
"include/nlohmann/json.hpp",
|
||||
"include/nlohmann/json_fwd.hpp",
|
||||
"include/nlohmann/json_literals.hpp",
|
||||
"include/nlohmann/ordered_map.hpp",
|
||||
"include/nlohmann/thirdparty/hedley/hedley.hpp",
|
||||
"include/nlohmann/thirdparty/hedley/hedley_undef.hpp",
|
||||
@@ -80,6 +77,7 @@ cc_library(
|
||||
name = "singleheader-json",
|
||||
hdrs = [
|
||||
"single_include/nlohmann/json.hpp",
|
||||
"single_include/nlohmann/json_fwd.hpp",
|
||||
],
|
||||
includes = ["single_include"],
|
||||
visibility = ["//visibility:public"],
|
||||
|
||||
+1
-1
@@ -10,5 +10,5 @@ title: "JSON for Modern C++"
|
||||
version: 3.12.0
|
||||
date-released: 2025-04-07
|
||||
license: MIT
|
||||
repository-code: "https://github.com/nlohmann"
|
||||
repository-code: "https://github.com/nlohmann/json"
|
||||
url: https://json.nlohmann.me
|
||||
|
||||
+4
-7
@@ -42,8 +42,11 @@ endif()
|
||||
## OPTIONS
|
||||
##
|
||||
|
||||
# Build the tests by default only for the main project and only if the tests
|
||||
# directory exists (the release archive json.tar.xz does not contain it).
|
||||
# VERSION_GREATER_EQUAL is not available in older CMake (< 3.7)
|
||||
if(${MAIN_PROJECT} AND (${CMAKE_VERSION} VERSION_EQUAL 3.13 OR ${CMAKE_VERSION} VERSION_GREATER 3.13))
|
||||
if(${MAIN_PROJECT} AND (${CMAKE_VERSION} VERSION_EQUAL 3.13 OR ${CMAKE_VERSION} VERSION_GREATER 3.13)
|
||||
AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/CMakeLists.txt")
|
||||
set(JSON_BuildTests_INIT ON)
|
||||
else()
|
||||
set(JSON_BuildTests_INIT OFF)
|
||||
@@ -55,7 +58,6 @@ option(JSON_Diagnostic_Positions "Enable diagnostic positions." OFF)
|
||||
option(JSON_GlobalUDLs "Place user-defined string literals in the global namespace." ON)
|
||||
option(JSON_ImplicitConversions "Enable implicit conversions." ON)
|
||||
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_Install "Install CMake targets during install step." ${MAIN_PROJECT})
|
||||
option(JSON_MultipleHeaders "Use non-amalgamated version of the library." ON)
|
||||
@@ -102,10 +104,6 @@ if (JSON_DisableEnumSerialization)
|
||||
message(STATUS "Enum integer serialization is disabled (JSON_DISABLE_ENUM_SERIALIZATION=1)")
|
||||
endif()
|
||||
|
||||
if (JSON_DisableTupleReferenceConversion)
|
||||
message(STATUS "Tuple reference conversion is disabled (JSON_DISABLE_TUPLE_REFERENCE_CONVERSION=1)")
|
||||
endif()
|
||||
|
||||
if (JSON_LegacyDiscardedValueComparison)
|
||||
message(STATUS "Legacy discarded value comparison enabled (JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1)")
|
||||
endif()
|
||||
@@ -148,7 +146,6 @@ target_compile_definitions(
|
||||
$<$<NOT:$<BOOL:${JSON_GlobalUDLs}>>:JSON_USE_GLOBAL_UDLS=0>
|
||||
$<$<NOT:$<BOOL:${JSON_ImplicitConversions}>>:JSON_USE_IMPLICIT_CONVERSIONS=0>
|
||||
$<$<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_Diagnostic_Positions}>:JSON_DIAGNOSTIC_POSITIONS=1>
|
||||
$<$<BOOL:${JSON_LegacyDiscardedValueComparison}>:JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1>
|
||||
|
||||
@@ -33,6 +33,17 @@ Further documentation:
|
||||
> [!IMPORTANT]
|
||||
> 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`
|
||||
|
||||
Configuration for the pipelines at [AppVeyor](https://ci.appveyor.com/project/nlohmann/json).
|
||||
@@ -196,19 +207,19 @@ Further documentation:
|
||||
|
||||
## REUSE
|
||||
|
||||
### `.reuse/dep5`
|
||||
### `REUSE.toml`
|
||||
|
||||
The file defines the licenses of certain third-party components in the repository. The root `Makefile` contains a target `reuse` that checks for compliance.
|
||||
|
||||
Further documentation:
|
||||
|
||||
- [DEP5](https://reuse.software/spec-3.2/#dep5-deprecated)
|
||||
- [REUSE.toml](https://reuse.software/spec-3.3/#reusetoml)
|
||||
- [reuse command-line tool](https://pypi.org/project/reuse/)
|
||||
- [documentation of linting](https://reuse.readthedocs.io/en/stable/man/reuse-lint.html)
|
||||
- [REUSE](http://reuse.software)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The filename `.reuse/dep5` is predetermined by REUSE. Alternatively, a `REUSE.toml` file can be used.
|
||||
> The filename `REUSE.toml` is predetermined by REUSE. Alternatively, a `.reuse/dep5` file (deprecated) can be used.
|
||||
|
||||
### `.reuse/templates`
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.PHONY: pretty clean ChangeLog.md release update_hedley update_hedley_undef BUILD.bazel natvis macro_builder_check
|
||||
.PHONY: pretty clean ChangeLog.md release update_hedley update_hedley_undef BUILD.bazel
|
||||
|
||||
##########################################################################
|
||||
# configuration
|
||||
@@ -7,6 +7,9 @@
|
||||
# find GNU sed to use `-i` parameter
|
||||
SED:=$(shell command -v gsed || which sed)
|
||||
|
||||
# find GNU tar to use `--sort` and `--pax-option` parameters
|
||||
TAR:=$(shell command -v gtar || which tar)
|
||||
|
||||
|
||||
##########################################################################
|
||||
# source files
|
||||
@@ -21,11 +24,6 @@ TESTS_SRCS=$(shell find tests -type f \( -name '*.hpp' -o -name '*.cpp' -o -name
|
||||
# the single headers (amalgamated from the source files)
|
||||
AMALGAMATED_FILE=single_include/nlohmann/json.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
|
||||
|
||||
|
||||
##########################################################################
|
||||
@@ -34,14 +32,18 @@ MACRO_SCOPE_HPP=include/nlohmann/detail/macro_scope.hpp
|
||||
|
||||
# main target
|
||||
all:
|
||||
@echo "amalgamate - amalgamate files single_include/nlohmann/json{,_fwd,_literals}.hpp from the include/nlohmann sources"
|
||||
@echo "amalgamate - amalgamate files single_include/nlohmann/json{,_fwd}.hpp from the include/nlohmann sources"
|
||||
@echo "BUILD.bazel - regenerate the Bazel BUILD file from the include/nlohmann sources"
|
||||
@echo "ChangeLog.md - generate ChangeLog file"
|
||||
@echo "check-amalgamation - check whether sources have been amalgamated and BUILD.bazel is up to date"
|
||||
@echo "clean - remove built files"
|
||||
@echo "fuzzing - see tests/fuzzing.md for how to build and run the fuzzers"
|
||||
@echo "macro_builder_check - check that macro_scope.hpp matches tools/macro_builder's output"
|
||||
@echo "natvis - regenerate nlohmann_json.natvis from the current ABI tags and version"
|
||||
@echo "doctest - compile example files and check their output"
|
||||
@echo "fuzz_testing - prepare fuzz testing of the JSON parser"
|
||||
@echo "fuzz_testing_bon8 - prepare fuzz testing of the BON8 parser"
|
||||
@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 "run_benchmarks - build and run benchmarks"
|
||||
@echo "update_hedley - download Hedley and regenerate hedley.hpp / hedley_undef.hpp"
|
||||
@@ -60,6 +62,74 @@ run_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
|
||||
##########################################################################
|
||||
@@ -87,10 +157,14 @@ install_astyle:
|
||||
|
||||
# call the Artistic Style pretty printer on all source files
|
||||
pretty: install_astyle
|
||||
$(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
|
||||
$(ASTYLE) --project=tools/astyle/.astylerc $(SRCS) $(TESTS_SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) docs/mkdocs/docs/examples/*.cpp
|
||||
|
||||
# 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
|
||||
amalgamate: $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE)
|
||||
amalgamate: $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE)
|
||||
$(MAKE) pretty
|
||||
|
||||
# call the amalgamation tool for json.hpp
|
||||
@@ -101,50 +175,20 @@ $(AMALGAMATED_FILE): $(SRCS)
|
||||
$(AMALGAMATED_FWD_FILE): $(SRCS)
|
||||
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
|
||||
# Note: this target is called by Travis
|
||||
check-amalgamation:
|
||||
@mv $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~
|
||||
@mv $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_FWD_FILE)~
|
||||
@mv $(AMALGAMATED_LITERALS_FILE) $(AMALGAMATED_LITERALS_FILE)~
|
||||
@$(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_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_FWD_FILE)~ $(AMALGAMATED_FWD_FILE)
|
||||
@mv $(AMALGAMATED_LITERALS_FILE)~ $(AMALGAMATED_LITERALS_FILE)
|
||||
@mv BUILD.bazel BUILD.bazel~
|
||||
@$(MAKE) BUILD.bazel
|
||||
@diff BUILD.bazel BUILD.bazel~ || (echo "===================================================================\n BUILD.bazel is out of date! Please run 'make BUILD.bazel'.\n===================================================================" ; mv BUILD.bazel~ BUILD.bazel ; false)
|
||||
@mv BUILD.bazel~ BUILD.bazel
|
||||
@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
|
||||
BUILD.bazel:
|
||||
@@ -174,14 +218,14 @@ ChangeLog.md:
|
||||
# archive is created according to the advices of <https://reproducible-builds.org/docs/archives/>.
|
||||
json.tar.xz:
|
||||
mkdir json
|
||||
rsync -R $(shell find LICENSE.MIT nlohmann_json.natvis CMakeLists.txt cmake/*.in include single_include -type f) json
|
||||
gtar --sort=name --mtime="@$(shell git log -1 --pretty=%ct)" --owner=0 --group=0 --numeric-owner --pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime --create --file - json | xz --compress -9e --threads=2 - > json.tar.xz
|
||||
rsync -R $(shell find LICENSE.MIT nlohmann_json.natvis CMakeLists.txt cmake/*.in include single_include src/modules -type f) json
|
||||
$(TAR) --sort=name --mtime="@$(shell git log -1 --pretty=%ct)" --owner=0 --group=0 --numeric-owner --pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime --create --file - json | xz --compress -9e --threads=2 - > json.tar.xz
|
||||
rm -fr json
|
||||
|
||||
# We use `-X` to make the resulting ZIP file reproducible, see
|
||||
# <https://content.pivotal.io/blog/barriers-to-deterministic-reproducible-zip-files>.
|
||||
include.zip: BUILD.bazel
|
||||
zip -9 --recurse-paths -X include.zip $(SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE) BUILD.bazel MODULE.bazel meson.build LICENSE.MIT
|
||||
zip -9 --recurse-paths -X include.zip $(SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) BUILD.bazel MODULE.bazel meson.build LICENSE.MIT
|
||||
|
||||
# Create the files for a release and add signatures and hashes.
|
||||
release: include.zip json.tar.xz
|
||||
@@ -190,13 +234,11 @@ release: include.zip json.tar.xz
|
||||
gpg --armor --detach-sig include.zip
|
||||
gpg --armor --detach-sig $(AMALGAMATED_FILE)
|
||||
gpg --armor --detach-sig $(AMALGAMATED_FWD_FILE)
|
||||
gpg --armor --detach-sig $(AMALGAMATED_LITERALS_FILE)
|
||||
gpg --armor --detach-sig json.tar.xz
|
||||
cp $(AMALGAMATED_FILE) release_files
|
||||
cp $(AMALGAMATED_FWD_FILE) release_files
|
||||
cp $(AMALGAMATED_LITERALS_FILE) release_files
|
||||
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
|
||||
mv $(AMALGAMATED_FILE).asc $(AMALGAMATED_FWD_FILE).asc json.tar.xz json.tar.xz.asc include.zip include.zip.asc release_files
|
||||
cd release_files ; shasum -a 256 json.hpp include.zip json.tar.xz > hashes.txt
|
||||
|
||||
|
||||
##########################################################################
|
||||
@@ -206,6 +248,7 @@ release: include.zip json.tar.xz
|
||||
# clean up
|
||||
clean:
|
||||
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
|
||||
$(MAKE) clean -Cdocs
|
||||
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import PackageDescription
|
||||
let package = Package(
|
||||
name: "nlohmann-json",
|
||||
platforms: [
|
||||
.iOS(.v12), .macOS(.v10_13), .tvOS(.v12), .watchOS(.v4), .visionOS(.v1)
|
||||
.iOS(.v12), .macOS(.v10_13), .tvOS(.v12), .watchOS(.v9), .visionOS(.v1)
|
||||
],
|
||||
products: [
|
||||
.library(name: "json", targets: ["json"])
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
[](https://coveralls.io/github/nlohmann/json?branch=develop)
|
||||
[](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://cirrus-ci.com/github/nlohmann/json)
|
||||
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:json)
|
||||
[](https://wandbox.org/permlink/1mp10JbaANo6FUc7)
|
||||
[](https://json.nlohmann.me)
|
||||
@@ -495,7 +496,7 @@ bool key(string_t& val);
|
||||
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. For `parse_error`, returning `true` [recovers from the error](https://json.nlohmann.me/features/parsing/error_recovery/): the parser repairs the input and continues.
|
||||
The return value of each function determines whether parsing should proceed.
|
||||
|
||||
To implement your own SAX handler, proceed as follows:
|
||||
|
||||
@@ -503,7 +504,7 @@ To implement your own SAX handler, proceed as follows:
|
||||
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.
|
||||
|
||||
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).
|
||||
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).
|
||||
|
||||
### STL-like access
|
||||
|
||||
@@ -1203,15 +1204,18 @@ language bindings, format converters, and the like. See the curated [Ecosystem](
|
||||
|
||||
Though it's 2026 already, the support for C++11 is still a bit sparse. Currently, the following compilers are known to work:
|
||||
|
||||
- GCC 4.8 - 14.2 (and possibly later)
|
||||
- Clang 3.4 - 21.0 (and possibly later)
|
||||
- Apple Clang 9.1 - 16.0 (and possibly later)
|
||||
- Intel C++ Compiler 17.0.2 (and possibly later)
|
||||
- Nvidia CUDA Compiler 11.0.221 (and possibly later)
|
||||
- Microsoft Visual C++ 2015 / Build Tools 14.0.25123.0 (and possibly later)
|
||||
- Microsoft Visual C++ 2017 / Build Tools 15.5.180.51428 (and possibly later)
|
||||
- Microsoft Visual C++ 2019 / Build Tools 16.3.1+1def00d3d (and possibly later)
|
||||
- Microsoft Visual C++ 2022 / Build Tools 19.30.30709.0 (and possibly later)
|
||||
- GCC 4.8 - 16.2 (and possibly later)
|
||||
- Clang 3.4 - 22.1 (and possibly later)
|
||||
- Apple Clang 15.0 - 21.0 (and possibly later)
|
||||
- Intel C++ Compiler Classic (icpc) 2021.10
|
||||
- Intel oneAPI DPC++/C++ Compiler (icpx) 2025.3 (and possibly later)
|
||||
- NVIDIA CUDA Compiler (nvcc) 11.8 - 12.6 (and possibly later)
|
||||
- NVIDIA HPC SDK C++ Compiler (nvc++) 25.5 (and possibly later)
|
||||
- Microsoft Visual C++ 2015 / MSVC 19.0 (and possibly later)
|
||||
- Microsoft Visual C++ 2017 / MSVC 19.16 (and possibly later)
|
||||
- Microsoft Visual C++ 2019 / MSVC 19.29 (and possibly later)
|
||||
- Microsoft Visual C++ 2022 / MSVC 19.44 (and possibly later)
|
||||
- Microsoft Visual C++ 2026 / MSVC 19.51 (and possibly later)
|
||||
|
||||
I would be happy to learn about other compilers/versions.
|
||||
|
||||
@@ -1394,7 +1398,6 @@ 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 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 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">
|
||||
|
||||
@@ -1402,7 +1405,7 @@ The library is compliant to version 3.3 of the [**REUSE specification**](https:/
|
||||
|
||||
- Every source file contains an SPDX copyright header.
|
||||
- The full text of all licenses used in the repository can be found in the `LICENSES` folder.
|
||||
- File `.reuse/dep5` contains an overview of all files' copyrights and licenses.
|
||||
- File `REUSE.toml` contains an overview of all files' copyrights and licenses.
|
||||
- Run `pipx run reuse lint` to verify the project's REUSE compliance and `pipx run reuse spdx` to generate a SPDX SBOM.
|
||||
|
||||
## Contact
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
version = 1
|
||||
SPDX-PackageName = "json"
|
||||
SPDX-PackageSupplier = "Niels Lohmann <mail@nlohmann.me>"
|
||||
SPDX-PackageDownloadLocation = "https://github.com/nlohmann/json"
|
||||
|
||||
[[annotations]]
|
||||
path = "**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2013-2026 Niels Lohmann <https://nlohmann.me>"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "include/nlohmann/thirdparty/hedley.hpp"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2016-2021 Evan Nemerson <evan@nemerson.com>"
|
||||
SPDX-License-Identifier = "CC0"
|
||||
|
||||
[[annotations]]
|
||||
path = "include/nlohmann/detail/meta/cpp_future.hpp"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2013-2026 Niels Lohmann <https://nlohmann.me> and 2018 The Abseil Authors"
|
||||
SPDX-License-Identifier = "MIT AND Apache-2.0"
|
||||
|
||||
[[annotations]]
|
||||
path = "tests/thirdparty/doctest/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2016-2023 Viktor Kirilov"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "tests/thirdparty/fifo_map/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2015-2017 Niels Lohmann"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "tests/thirdparty/Fuzzer/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2003-2022 LLVM Project."
|
||||
SPDX-License-Identifier = "Apache-2.0"
|
||||
|
||||
[[annotations]]
|
||||
path = "tests/thirdparty/imapdl/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2017 Georg Sauthoff <mail@gms.tf>"
|
||||
SPDX-License-Identifier = "GPL-3.0-only"
|
||||
|
||||
[[annotations]]
|
||||
path = "tools/amalgamate/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2012 Erik Edlund <erik.edlund@32767.se>"
|
||||
SPDX-License-Identifier = "BSD-3-Clause"
|
||||
|
||||
[[annotations]]
|
||||
path = "tools/gdb_pretty_printer/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2020 Hannes Domani <https://github.com/ssbssa>"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
+95
-137
@@ -40,14 +40,6 @@ execute_process(COMMAND ${IWYU_TOOL} --version OUTPUT_VARIABLE 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})")
|
||||
|
||||
# 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)
|
||||
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}")
|
||||
@@ -63,6 +55,12 @@ execute_process(COMMAND ${NINJA_TOOL} --version OUTPUT_VARIABLE NINJA_TOOL_VERSI
|
||||
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" NINJA_TOOL_VERSION "${NINJA_TOOL_VERSION}")
|
||||
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)
|
||||
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}")
|
||||
@@ -130,16 +128,16 @@ foreach(CXX_STANDARD 11 14 17 20 23 26)
|
||||
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}
|
||||
COMMAND CXX=${CLANG_TOOL} CXXFLAGS="${CLANG_CXXFLAGS};-stdlib=libc++" ${CMAKE_COMMAND}
|
||||
COMMAND CXX=${CLANG_TOOL} CXXFLAGS="${CLANG_CXXFLAGS}" ${CMAKE_COMMAND}
|
||||
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
||||
-DJSON_BuildTests=ON -DJSON_FastTests=ON
|
||||
-DJSON_TestStandards=${CXX_STANDARD}
|
||||
-DCMAKE_CXX_FLAGS="-stdlib=libc++"
|
||||
-DCMAKE_EXE_LINKER_FLAGS="-lc++abi"
|
||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_clang_libcxx_cxx${CXX_STANDARD}
|
||||
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_clang_libcxx_cxx${CXX_STANDARD}
|
||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_clang_libcxx_cxx${CXX_STANDARD} && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
|
||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_clang_cxx${CXX_STANDARD}
|
||||
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_clang_cxx${CXX_STANDARD}
|
||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_clang_cxx${CXX_STANDARD} && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
|
||||
COMMENT "Compile and test with Clang for C++${CXX_STANDARD} (libc++)"
|
||||
)
|
||||
endforeach()
|
||||
@@ -278,20 +276,6 @@ add_custom_target(ci_test_disableenumserialization
|
||||
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.
|
||||
###############################################################################
|
||||
@@ -378,29 +362,20 @@ add_custom_target(ci_test_clang_sanitizer
|
||||
# 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
|
||||
${PROJECT_SOURCE_DIR}/docs/mkdocs/docs/examples/*.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/nlohmann/*.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/*.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(tool_dir ${PROJECT_SOURCE_DIR}/tools/amalgamate)
|
||||
add_custom_target(ci_test_amalgamation
|
||||
COMMAND rm -fr ${include_dir}/json.hpp~ ${include_dir}/json_fwd.hpp~ ${include_dir}/json_literals.hpp~
|
||||
COMMAND rm -fr ${include_dir}/json.hpp~ ${include_dir}/json_fwd.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_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 venv_astyle/bin/pip3 --quiet install -r ${CMAKE_SOURCE_DIR}/tools/astyle/requirements.txt
|
||||
@@ -408,20 +383,16 @@ 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_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 ${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_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 for FILE in `find . -name '*.orig'`\; do false \; done
|
||||
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
COMMENT "Check amalgamation, formatting, and BUILD.bazel"
|
||||
COMMENT "Check amalgamation and indentation"
|
||||
)
|
||||
|
||||
###############################################################################
|
||||
@@ -456,14 +427,14 @@ add_custom_target(ci_test_valgrind
|
||||
# Check code with Clang Static Analyzer.
|
||||
###############################################################################
|
||||
|
||||
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")
|
||||
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")
|
||||
|
||||
add_custom_target(ci_clang_analyze
|
||||
COMMAND CXX=${CLANG_TOOL} ${CMAKE_COMMAND}
|
||||
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
||||
-DJSON_BuildTests=ON
|
||||
-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} --use-analyzer=${CLANG_TOOL} -analyze-headers --status-bugs -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} -analyze-headers -o ${PROJECT_BINARY_DIR}/report ninja
|
||||
COMMENT "Check code with Clang Analyzer"
|
||||
)
|
||||
|
||||
@@ -479,7 +450,7 @@ add_custom_target(ci_cppcheck
|
||||
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
|
||||
--error-exitcode=1 --relative-paths=${PROJECT_SOURCE_DIR} -j ${N} --include=default_defines.hpp
|
||||
--cppcheck-build-dir=cppcheck
|
||||
--cppcheck-build-dir=cppcheck --check-level=exhaustive
|
||||
-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
|
||||
COMMENT "Check code with Cppcheck"
|
||||
@@ -497,6 +468,34 @@ add_custom_target(ci_cpplint
|
||||
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.
|
||||
###############################################################################
|
||||
@@ -511,16 +510,29 @@ add_custom_target(ci_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.
|
||||
###############################################################################
|
||||
|
||||
# .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
|
||||
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} run --project-root ${PROJECT_SOURCE_DIR} -- make
|
||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_infer && ${INFER_TOOL} run -- make
|
||||
COMMENT "Check code with Infer"
|
||||
)
|
||||
|
||||
@@ -576,13 +588,7 @@ add_custom_target(ci_reproducible_tests
|
||||
# be compiled individually.
|
||||
###############################################################################
|
||||
|
||||
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)
|
||||
set(iwyu_path_and_options ${IWYU_TOOL} -Xiwyu --max_line_length=300)
|
||||
|
||||
foreach(SRC_FILE ${SRC_FILES})
|
||||
# get relative path of the header file
|
||||
@@ -597,34 +603,14 @@ foreach(SRC_FILE ${SRC_FILES})
|
||||
target_include_directories(single_${RELATIVE_SRC_FILE} PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||
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}")
|
||||
# remember binary for ci_single_binaries
|
||||
# remember binary for ci_single_binaries target
|
||||
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()
|
||||
|
||||
if(IWYU_TOOL_PY)
|
||||
add_custom_target(ci_single_binaries
|
||||
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()
|
||||
add_custom_target(ci_single_binaries
|
||||
DEPENDS ${single_binaries}
|
||||
COMMENT "Check if headers are self-contained"
|
||||
)
|
||||
|
||||
###############################################################################
|
||||
# Benchmarks
|
||||
@@ -646,41 +632,21 @@ add_custom_target(ci_benchmarks
|
||||
# we test the project with different CMake versions:
|
||||
# - CMake 3.5 (the earliest supported)
|
||||
# - CMake 3.31.6 (the latest 3.x release)
|
||||
# - CMake 4.0.0 (the first 4.x release)
|
||||
# - the CMake version running this build (usually the latest release)
|
||||
# - CMake 4.0.0 (the latest release)
|
||||
|
||||
function(ci_get_cmake version var)
|
||||
set(${var} ${PROJECT_BINARY_DIR}/cmake-${version}/bin/cmake)
|
||||
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(x86_64|amd64)$")
|
||||
# Kitware publishes a prebuilt Linux x86_64 archive for every release; unpacking it is far
|
||||
# cheaper than compiling all of CMake (including its own test helpers) from source.
|
||||
add_custom_command(
|
||||
OUTPUT ${${var}}
|
||||
COMMAND wget -nc https://github.com/Kitware/CMake/releases/download/v${version}/cmake-${version}-linux-x86_64.tar.gz
|
||||
COMMAND wget -nc https://github.com/Kitware/CMake/releases/download/v${version}/cmake-${version}-SHA-256.txt
|
||||
# verify the archive against Kitware's published SHA-256 sums before unpacking it
|
||||
COMMAND sh -c "grep ' cmake-${version}-linux-x86_64[.]tar[.]gz$' cmake-${version}-SHA-256.txt | sha256sum -c -"
|
||||
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()
|
||||
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 CMake ${version}"
|
||||
)
|
||||
set(${var} ${${var}} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
@@ -690,24 +656,30 @@ ci_get_cmake(4.0.0 CMAKE_4_0_0_BINARY)
|
||||
|
||||
# the tests require CMake 3.13 or later, so they are excluded for CMake 3.5.0
|
||||
set(JSON_CMAKE_FLAGS_3_5_0 JSON_Diagnostics JSON_Diagnostic_Positions JSON_GlobalUDLs JSON_ImplicitConversions JSON_DisableEnumSerialization
|
||||
JSON_LegacyDiscardedValueComparison JSON_Install JSON_MultipleHeaders JSON_SystemInclude JSON_Valgrind
|
||||
JSON_StrictNulHandling)
|
||||
set(JSON_CMAKE_FLAGS_3_31_6 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
|
||||
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})
|
||||
set(JSON_CMAKE_FLAGS_4_0_0 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
|
||||
|
||||
function(ci_add_cmake_flags_targets flag min_version)
|
||||
string(TOLOWER "ci_cmake_flag_${flag}" flag_target)
|
||||
string(REPLACE . _ min_version_var ${min_version})
|
||||
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}
|
||||
COMMENT "Check CMake flag ${flag} (CMake ${min_version})"
|
||||
COMMENT "Check CMake flag ${JSON_CMAKE_FLAG} (CMake ${min_version})"
|
||||
COMMAND mkdir -pv ${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
|
||||
DEPENDS ${cmake_binary}
|
||||
)
|
||||
list(APPEND JSON_CMAKE_FLAG_TARGETS ${flag_target}_${min_version_var})
|
||||
list(APPEND JSON_CMAKE_FLAG_BUILD_DIRS ${PROJECT_BINARY_DIR}/build_${flag_target}_${min_version_var})
|
||||
list(APPEND JSON_CMAKE_FLAG_TARGETS ${JSON_CMAKE_FLAG_TARGET} ${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})
|
||||
set(JSON_CMAKE_FLAG_TARGETS ${JSON_CMAKE_FLAG_TARGETS} PARENT_SCOPE)
|
||||
set(JSON_CMAKE_FLAG_BUILD_DIRS ${JSON_CMAKE_FLAG_BUILD_DIRS} PARENT_SCOPE)
|
||||
endfunction()
|
||||
@@ -724,20 +696,6 @@ foreach(JSON_CMAKE_FLAG ${JSON_CMAKE_FLAGS_4_0_0})
|
||||
ci_add_cmake_flags_targets(${JSON_CMAKE_FLAG} 4.0.0)
|
||||
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
|
||||
DEPENDS ${JSON_CMAKE_FLAG_TARGETS}
|
||||
COMMENT "Check CMake flags"
|
||||
@@ -896,6 +854,6 @@ add_custom_target(ci_test_build_documentation
|
||||
###############################################################################
|
||||
|
||||
add_custom_target(ci_clean
|
||||
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}
|
||||
COMMAND rm -fr ${PROJECT_BINARY_DIR}/build_* cmake-3.5.0-Darwin64 ${JSON_CMAKE_FLAG_BUILD_DIRS} ${single_binaries}
|
||||
COMMENT "Clean generated directories"
|
||||
)
|
||||
|
||||
@@ -48,6 +48,7 @@ cc_library(
|
||||
name = "singleheader-json",
|
||||
hdrs = [
|
||||
"single_include/nlohmann/json.hpp",
|
||||
"single_include/nlohmann/json_fwd.hpp",
|
||||
],
|
||||
includes = ["single_include"],
|
||||
visibility = ["//visibility:public"],
|
||||
|
||||
@@ -64,7 +64,7 @@ if(MODE STREQUAL "undef")
|
||||
# recipe is self-contained and its output is byte-stable across reruns.
|
||||
# The embedded SPDX tags below are part of the *generated* file's
|
||||
# content, not a REUSE header for this .cmake script itself (which is
|
||||
# already covered by the blanket "Files: *" rule in .reuse/dep5) -- keep
|
||||
# already covered by the blanket path = "**" rule in REUSE.toml) -- keep
|
||||
# them wrapped in REUSE-IgnoreStart/End so `reuse lint` does not try to
|
||||
# parse "MIT\n")" as this file's own SPDX-License-Identifier value.
|
||||
# REUSE-IgnoreStart
|
||||
|
||||
+5
-16
@@ -11,31 +11,20 @@ EXAMPLES = $(wildcard mkdocs/docs/examples/*.cpp)
|
||||
|
||||
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
|
||||
%.output: %.cpp
|
||||
@echo "standard $(call cxx_standard,$(<:.cpp=))"
|
||||
@echo "standard $(call cxx_standard $(<:.cpp=))"
|
||||
$(MAKE) $(<:.cpp=) \
|
||||
CPPFLAGS="$(EXAMPLE_CPPFLAGS)" \
|
||||
CXXFLAGS="-std=$(call cxx_standard,$(<:.cpp=)) $(EXAMPLE_WARNFLAGS)"
|
||||
CPPFLAGS="-I $(SRCDIR) -DJSON_USE_GLOBAL_UDLS=0" \
|
||||
CXXFLAGS="-std=$(call cxx_standard,$(<:.cpp=)) -Wno-deprecated-declarations"
|
||||
./$(<:.cpp=) > $@
|
||||
rm $(<:.cpp=)
|
||||
|
||||
# compare created output with current output of the example files
|
||||
%.test: %.cpp
|
||||
$(MAKE) $(<:.cpp=) \
|
||||
CPPFLAGS="$(EXAMPLE_CPPFLAGS)" \
|
||||
CXXFLAGS="-std=$(call cxx_standard,$(<:.cpp=)) $(EXAMPLE_WARNFLAGS)"
|
||||
CPPFLAGS="-I $(SRCDIR) -DJSON_USE_GLOBAL_UDLS=0" \
|
||||
CXXFLAGS="-std=$(call cxx_standard,$(<:.cpp=)) -Wno-deprecated-declarations"
|
||||
./$(<:.cpp=) > $@
|
||||
diff $@ $(<:.cpp=.output)
|
||||
rm $(<:.cpp=) $@
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<key>dashIndexFilePath</key>
|
||||
<string>index.html</string>
|
||||
<key>DashDocSetFallbackURL</key>
|
||||
<string>https://json.nlohmann.me/</string>
|
||||
<string>https://nlohmann.github.io/json/</string>
|
||||
<key>isJavaScriptEnabled</key>
|
||||
<true/>
|
||||
</dict>
|
||||
|
||||
+22
-12
@@ -52,25 +52,35 @@ install_docset_zeal: JSON_for_Modern_C++.docset
|
||||
mkdir -p $$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
|
||||
.PHONY: list_missing_pages
|
||||
list_missing_pages: docSet.dsidx
|
||||
@comm -23 <(echo '$(DOCSET_PAGE_PATHS)' | tr ' ' '\n') <(echo '$(DOCSET_INDEX_PATHS)' | tr ' ' '\n')
|
||||
@for page in $(MKDOCS_PAGES); do \
|
||||
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
|
||||
.PHONY: list_removed_paths
|
||||
list_removed_paths: docSet.dsidx
|
||||
@comm -13 <(echo '$(DOCSET_PAGE_PATHS)' | tr ' ' '\n') <(echo '$(DOCSET_INDEX_PATHS)' | tr ' ' '\n')
|
||||
@for path in $$(sqlite3 docSet.dsidx "SELECT path FROM searchIndex"); do \
|
||||
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
|
||||
clean:
|
||||
|
||||
@@ -7,11 +7,10 @@ documentation browsers like [Dash](https://kapeli.com/dash), [Velocity](https://
|
||||
The docset can be created with
|
||||
|
||||
```sh
|
||||
make JSON_for_Modern_C++.docset
|
||||
make nlohmann_json.docset
|
||||
```
|
||||
|
||||
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.
|
||||
The generated folder `nlohmann_json.docset` can then be opened in the documentation browser.
|
||||
|
||||
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,7 +210,6 @@ 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_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_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_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');
|
||||
|
||||
@@ -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)
|
||||
is thrown.
|
||||
- In case of other primitive types (number, boolean, string, or binary), `first` must be `begin()` and `last`
|
||||
must be `end()`. In this case, the value is copied. Otherwise,
|
||||
- In case of other primitive types (number, boolean, or string), `first` must be `begin()` and `last` must be
|
||||
`end()`. In this case, the value is copied. Otherwise,
|
||||
[`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
|
||||
`std::map`; that is, a JSON array or object is constructed from the values in the range.
|
||||
@@ -159,8 +159,6 @@ basic_json(basic_json&& other) noexcept;
|
||||
- `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 `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&&)`
|
||||
method
|
||||
|
||||
@@ -244,8 +242,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
|
||||
`[first, last)` is undefined.
|
||||
- Throws [`invalid_iterator.204`](../../home/exceptions.md#jsonexceptioninvalid_iterator204) if iterators `first`
|
||||
and `last` belong to a primitive type (number, boolean, string, or binary), but `first` does not point to the
|
||||
first element anymore. In this case, the range `[first, last)` is undefined. See the example code below.
|
||||
and `last` belong to a primitive type (number, boolean, or string), but `first` does not point to the 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`
|
||||
and `last` belong to a `#!json null` value. In this case, the range `[first, last)` is undefined.
|
||||
8. (none)
|
||||
@@ -425,8 +423,6 @@ basic_json(basic_json&& other) noexcept;
|
||||
4. Since version 3.2.0.
|
||||
5. Since version 1.0.0.
|
||||
6. 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.
|
||||
7. Since version 1.0.0.
|
||||
8. 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
|
||||
have been called with the current value type from [`type()`](type.md):
|
||||
|
||||
| Value type | initial value |
|
||||
|------------|-----------------------------------------|
|
||||
| null | `null` |
|
||||
| boolean | `false` |
|
||||
| string | `""` |
|
||||
| number | `0` |
|
||||
| binary | An empty byte vector with no subtype |
|
||||
| object | `{}` |
|
||||
| array | `[]` |
|
||||
| Value type | initial value |
|
||||
|------------|----------------------|
|
||||
| null | `null` |
|
||||
| boolean | `false` |
|
||||
| string | `""` |
|
||||
| number | `0` |
|
||||
| binary | An empty byte vector |
|
||||
| object | `{}` |
|
||||
| array | `[]` |
|
||||
|
||||
Has the same effect as calling
|
||||
|
||||
@@ -56,4 +56,3 @@ All iterators, pointers, and references related to this container are invalidate
|
||||
|
||||
- Added in version 1.0.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,10 +58,6 @@ 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 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"
|
||||
|
||||
@@ -121,5 +117,3 @@ Logarithmic in the size of the JSON object.
|
||||
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.
|
||||
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,11 +40,7 @@ Logarithmic in the size of the JSON object.
|
||||
|
||||
## Notes
|
||||
|
||||
- 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).
|
||||
This method always returns `0` when executed on a JSON type that is not an object.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -85,5 +81,3 @@ Logarithmic in the size of the JSON object.
|
||||
|
||||
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.
|
||||
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,11 +44,7 @@ Logarithmic in the size of the JSON object.
|
||||
|
||||
## Notes
|
||||
|
||||
- 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).
|
||||
This method always returns `end()` when executed on a JSON type that is not an object.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -89,5 +85,3 @@ Logarithmic in the size of the JSON object.
|
||||
|
||||
1. Added 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,7 +195,5 @@ Strong exception safety: if an exception occurs, the original value stays intact
|
||||
1. Added in version 1.0.0.
|
||||
2. Added in version 1.0.0.
|
||||
3. 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.
|
||||
4. Added in version 1.0.0.
|
||||
5. Added in version 3.0.0.
|
||||
|
||||
@@ -37,12 +37,6 @@ Thereby, `Target` is the current object; that is, the patch is applied to the cu
|
||||
|
||||
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
|
||||
|
||||
??? example
|
||||
@@ -67,5 +61,3 @@ returned by `#!cpp (*this)[key]`); it is read as it was when `merge_patch()` was
|
||||
## Version history
|
||||
|
||||
- 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.
|
||||
|
||||
@@ -51,7 +51,7 @@ range will yield over/underflow when used in a constructor. During deserializati
|
||||
will automatically be stored as [`number_unsigned_t`](number_unsigned_t.md) or [`number_float_t`](number_float_t.md).
|
||||
|
||||
[RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
|
||||
> Note that when such software is used, numbers that are integers and are in the range [-2<sup>53</sup>+1, 2<sup>53</sup>-1] are
|
||||
> 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.
|
||||
|
||||
As this range is a subrange of the exactly supported range [INT64_MIN, INT64_MAX], this class's integer type is
|
||||
|
||||
@@ -52,7 +52,7 @@ when used in a constructor. During deserialization, too large or small integer n
|
||||
as [`number_integer_t`](number_integer_t.md) or [`number_float_t`](number_float_t.md).
|
||||
|
||||
[RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
|
||||
> Note that when such software is used, numbers that are integers and are in the range [-2<sup>53</sup>+1, 2<sup>53</sup>-1] are
|
||||
> 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.
|
||||
|
||||
As this range is a subrange (when considered in conjunction with the `number_integer_t` type) of the exactly supported
|
||||
|
||||
@@ -70,9 +70,6 @@ Strong exception safety: if an exception occurs, the original value stays intact
|
||||
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
|
||||
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:
|
||||
- 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.
|
||||
@@ -260,8 +257,7 @@ Strong exception safety: if an exception occurs, the original value stays intact
|
||||
|
||||
## Version history
|
||||
|
||||
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`.
|
||||
1. Added in version 1.0.0.
|
||||
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.
|
||||
3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as
|
||||
|
||||
@@ -88,12 +88,7 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
||||
## Exceptions
|
||||
|
||||
- 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, 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.
|
||||
empty input like a null `FILE*` or `char*` pointer.
|
||||
|
||||
## Complexity
|
||||
|
||||
@@ -259,8 +254,6 @@ 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.
|
||||
- `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.
|
||||
- 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"
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ classDiagram
|
||||
|
||||
## Notes
|
||||
|
||||
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
|
||||
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
|
||||
or the end of file. This also holds true when reading a byte vector for binary formats.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -100,6 +100,3 @@ the latter case, it is skipped completely, or replaced by `null` if it is the to
|
||||
- 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
|
||||
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,9 +90,7 @@ The SAX event lister must follow the interface of [`json_sax`](../json_sax/index
|
||||
|
||||
## Return value
|
||||
|
||||
`#!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)).
|
||||
return value of the last processed SAX event
|
||||
|
||||
## Exception safety
|
||||
|
||||
@@ -140,7 +138,6 @@ A UTF-8 byte order mark is silently ignored.
|
||||
- Ignoring comments via `ignore_comments` added in version 3.9.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.
|
||||
- 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.
|
||||
- `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`.
|
||||
|
||||
@@ -6,9 +6,7 @@ void swap(reference other) noexcept (
|
||||
std::is_nothrow_move_constructible<value_t>::value &&
|
||||
std::is_nothrow_move_assignable<value_t>::value &&
|
||||
std::is_nothrow_move_constructible<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
|
||||
std::is_nothrow_move_assignable<json_value>::value
|
||||
);
|
||||
|
||||
// (2)
|
||||
@@ -16,9 +14,7 @@ friend void swap(reference left, reference right) noexcept (
|
||||
std::is_nothrow_move_constructible<value_t>::value &&
|
||||
std::is_nothrow_move_assignable<value_t>::value &&
|
||||
std::is_nothrow_move_constructible<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
|
||||
std::is_nothrow_move_assignable<json_value>::value
|
||||
);
|
||||
|
||||
// (3)
|
||||
@@ -41,15 +37,11 @@ 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
|
||||
[`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.
|
||||
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
|
||||
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
|
||||
[`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.
|
||||
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
|
||||
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
|
||||
@@ -172,8 +164,8 @@ Constant.
|
||||
|
||||
## Version history
|
||||
|
||||
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. Exchanges the `json_base_class_t` subobject along with the value since version 3.13.0.
|
||||
1. Since version 1.0.0.
|
||||
2. Since version 1.0.0.
|
||||
3. Since version 1.0.0.
|
||||
4. Since version 1.0.0.
|
||||
5. Since version 1.0.0.
|
||||
|
||||
@@ -43,9 +43,6 @@ 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,
|
||||
string, or binary value exceeds the range of the 32-bit BSON length field; example:
|
||||
`"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
|
||||
|
||||
@@ -81,4 +78,3 @@ pass before anything is written.
|
||||
|
||||
- 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.
|
||||
- `out_of_range.415` is now detected before anything is written, like the other exceptions above, since version 3.13.0.
|
||||
|
||||
@@ -76,6 +76,3 @@ Linear in the size of the JSON value `j`.
|
||||
|
||||
- Added in version 2.0.9.
|
||||
- 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,12 +59,6 @@ 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.
|
||||
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
|
||||
|
||||
??? example
|
||||
@@ -161,5 +155,3 @@ contained in `#!cpp *this` (for example, a subobject returned by `#!cpp (*this)[
|
||||
|
||||
- Added in version 3.0.0.
|
||||
- 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,14 +52,6 @@ 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
|
||||
`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
|
||||
|
||||
`KeyType`
|
||||
@@ -192,9 +184,7 @@ changes to any JSON value.
|
||||
|
||||
## Version history
|
||||
|
||||
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.
|
||||
1. Added in version 1.0.0. Changed parameter `default_value` type from `const ValueType&` to `ValueType&&` in version 3.11.0.
|
||||
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
|
||||
`ptr` through an array unexpectedly threw `out_of_range` instead of returning the resolved element (or
|
||||
|
||||
@@ -7,8 +7,7 @@ struct json_sax;
|
||||
|
||||
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
|
||||
processing the input; for [`parse_error`](parse_error.md), it decides whether to
|
||||
[recover from the error](../../features/parsing/error_recovery.md).
|
||||
processing the input.
|
||||
|
||||
## Template parameters
|
||||
|
||||
|
||||
@@ -21,14 +21,7 @@ A parse error occurred.
|
||||
|
||||
## Return value
|
||||
|
||||
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`.
|
||||
Whether parsing should proceed (**must return `#!cpp false`**).
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -46,22 +39,6 @@ Either way, [`sax_parse`](../basic_json/sax_parse.md) returns `#!cpp false`.
|
||||
--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
|
||||
|
||||
- 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,7 +28,6 @@ header. See also the [macro overview page](../../features/macros.md).
|
||||
- [**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_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_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
|
||||
@@ -53,7 +52,6 @@ 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_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
|
||||
|
||||
## Comparison behavior
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,68 +0,0 @@
|
||||
# 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,8 +16,7 @@ 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.
|
||||
- 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.
|
||||
- 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.
|
||||
- One trailing `'\0'` at the end of a `char` array (e.g., a string literal) is trimmed; see the warning below.
|
||||
|
||||
## Default definition
|
||||
|
||||
@@ -58,15 +57,13 @@ The default value is `0` (disabled — existing behavior is preserved).
|
||||
This macro must be defined **before** including `<nlohmann/json.hpp>`. Defining it after the include has no
|
||||
effect.
|
||||
|
||||
Enabling it also changes how an array of a text-literal element type (`char`, `wchar_t`, `char16_t`, `char32_t`,
|
||||
or, since C++20, `char8_t` - including a string literal, e.g. `json::parse("123")` or `json::parse(L"123")`) is
|
||||
read: such an array normally carries a trailing `'\0'` contributed by the compiler, not by the source text. With
|
||||
this macro enabled, that one trailing element is trimmed if present so that parsing a string literal keeps
|
||||
working, for any of these character types; every other element in the array - including any `'\0'` that is not
|
||||
the very last element - is read as real data and rejected like any other unexpected byte. Arrays of any other
|
||||
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.
|
||||
Enabling it also changes how a `char` array (including a string literal, e.g. `json::parse("123")`) is read: such
|
||||
an array normally carries a trailing `'\0'` contributed by the compiler, not by the source text. With this macro
|
||||
enabled, that one trailing byte is trimmed if present so that parsing a string literal keeps working; every other
|
||||
byte in the array - including any `'\0'` that is not the very last element - is read as real data and rejected
|
||||
like any other unexpected byte. Arrays of any other 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"
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ The default value is `1`.
|
||||
#define JSON_USE_GLOBAL_UDLS 1
|
||||
```
|
||||
|
||||
When the macro is not defined, the library behaves as if it were defined to its default value.
|
||||
When the macro is not defined, the library will define it to its default value.
|
||||
|
||||
## Notes
|
||||
|
||||
@@ -32,11 +32,6 @@ When the macro is not defined, the library behaves as if it were defined to its
|
||||
[`JSON_GlobalUDLs`](../../integration/cmake.md#json_globaludls) (`ON` by default) which defines
|
||||
`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
|
||||
|
||||
??? example "Example 1: Default behavior"
|
||||
@@ -97,7 +92,6 @@ When the macro is not defined, the library behaves as if it were defined to its
|
||||
|
||||
- [`operator""_json`](../operator_literal_json.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
|
||||
|
||||
## Version history
|
||||
|
||||
@@ -18,18 +18,9 @@ Deserializes an input stream to a JSON value.
|
||||
|
||||
the stream `i`
|
||||
|
||||
## Exception safety
|
||||
|
||||
Strong guarantee: if an exception is thrown, there are no changes in `j`.
|
||||
|
||||
## Exceptions
|
||||
|
||||
- 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.
|
||||
- Throws [`parse_error.101`](../home/exceptions.md#jsonexceptionparse_error101) in case of an unexpected token.
|
||||
|
||||
## Complexity
|
||||
|
||||
@@ -127,7 +118,3 @@ being read.
|
||||
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
|
||||
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,9 +18,7 @@ using namespace nlohmann;
|
||||
```
|
||||
|
||||
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. 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.
|
||||
[`JSON_USE_GLOBAL_UDLS`](macros/json_use_global_udls.md#notes) for details.
|
||||
|
||||
## Parameters
|
||||
|
||||
@@ -61,8 +59,6 @@ Linear.
|
||||
## See also
|
||||
|
||||
- [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
|
||||
|
||||
|
||||
@@ -17,9 +17,7 @@ using namespace nlohmann::literals::json_literals;
|
||||
using namespace nlohmann;
|
||||
```
|
||||
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. 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.
|
||||
[`JSON_USE_GLOBAL_UDLS`](macros/json_use_global_udls.md#notes) for details.
|
||||
|
||||
## Parameters
|
||||
|
||||
@@ -60,8 +58,6 @@ Linear.
|
||||
## See also
|
||||
|
||||
- [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
|
||||
|
||||
|
||||
@@ -28,11 +28,6 @@ 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
|
||||
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
|
||||
|
||||
- **key_type** - key type (`Key`)
|
||||
@@ -61,11 +56,6 @@ std::equal_to<> // since C++14
|
||||
- **find**
|
||||
- **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
|
||||
|
||||
Because the elements are stored in a `std::vector` in insertion order, there is no index to look a key up by. Every
|
||||
@@ -132,4 +122,3 @@ 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 **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 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 | arm64 | Ubuntu 24.04 | GitHub |
|
||||
| GNU 16.1.0 | arm64 | Linux 6.1.100 | Cirrus CI |
|
||||
| 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 |
|
||||
| nvc++ (NVIDIA HPC SDK) 25.5-0 | x86_64 | Ubuntu 22.04 LTS | GitHub |
|
||||
@@ -132,8 +132,7 @@ 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.
|
||||
- [x] The code checked with [Include What You Use (IWYU)](https://include-what-you-use.org) that all required standard
|
||||
headers are included.
|
||||
- [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] On Windows, the library is compiled with `<Windows.h>` being included to detect and avoid common bugs.
|
||||
- [x] The library is compiled with exceptions disabled to support alternative means of error handling.
|
||||
|
||||
## Stable public API
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<a target="_blank" href="https://wandbox.org/permlink/hUJYo1HWmfTBLMGn"><b>online</b></a>
|
||||
@@ -0,0 +1 @@
|
||||
<a target="_blank" href="https://wandbox.org/permlink/AWbpa8e1xRV3y4MM"><b>online</b></a>
|
||||
@@ -1,43 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
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:
|
||||
|
||||
- strings with more than 18446744073709551615 bytes, i.e., 2<sup>64</sup>-1 bytes (theoretical)
|
||||
- strings with more than 18446744073709551615 bytes, i.e., $2^{64}-1$ bytes (theoretical)
|
||||
|
||||
!!! info "Unused BJData markers"
|
||||
|
||||
|
||||
@@ -83,22 +83,6 @@ 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).
|
||||
|
||||
## `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`
|
||||
|
||||
When defined, headers `<cstdio>`, `<ios>`, `<iosfwd>`, `<istream>`, and `<ostream>` are not included and parse functions
|
||||
|
||||
@@ -40,9 +40,6 @@ Only the following symbols are exported from `nlohmann.json`:
|
||||
- `nlohmann::literals::json_literals::operator""_json`
|
||||
- `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
|
||||
([#3970](https://github.com/nlohmann/json/issues/3970)). They are implementation details, not part of the public API,
|
||||
and should not be used directly:
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
# 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
|
||||
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
|
||||
options. To get as much as possible out of malformed input, a SAX parser can [recover from errors](error_recovery.md).
|
||||
options.
|
||||
|
||||
## See also
|
||||
|
||||
@@ -76,4 +76,3 @@ options. To get as much as possible out of malformed input, a SAX parser can [re
|
||||
- [parser callbacks](parser_callbacks.md) - influence the parsing by a callback function
|
||||
- [SAX interface](sax_interface.md) - implement a custom SAX handler
|
||||
- [parsing and exceptions](parse_exceptions.md) - control error handling
|
||||
- [error recovery](error_recovery.md) - get as much as possible out of malformed input
|
||||
|
||||
@@ -64,8 +64,7 @@ bool parse_error(std::size_t position,
|
||||
const json::exception& ex);
|
||||
```
|
||||
|
||||
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.
|
||||
The return value indicates whether the parsing should continue, so the function should usually return `#!cpp false`.
|
||||
|
||||
??? example
|
||||
|
||||
|
||||
@@ -60,8 +60,7 @@ bool key(string_t& val);
|
||||
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. For `parse_error`, returning
|
||||
`#!cpp true` [recovers from the error](error_recovery.md).
|
||||
The return value of each function determines whether parsing should proceed.
|
||||
|
||||
To implement your own SAX handler, proceed as follows:
|
||||
|
||||
@@ -69,7 +68,7 @@ To implement your own SAX handler, proceed as follows:
|
||||
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.
|
||||
|
||||
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`.
|
||||
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`.
|
||||
|
||||
## 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:
|
||||
|
||||
> 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.
|
||||
> 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.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
are in the range $[-2^{53}+1, 2^{53}-1]$ are interoperable in the
|
||||
sense that implementations will agree exactly on their numeric
|
||||
values.
|
||||
|
||||
@@ -95,9 +95,9 @@ This is the same behavior as the code `#!c double x = 3.141592653589793238462643
|
||||
|
||||
!!! success "Interoperability"
|
||||
|
||||
- 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<sup>53</sup>+1, 2<sup>53</sup>-1].
|
||||
- All integers outside the range [-2<sup>63</sup>, 2<sup>64</sup>-1], as well as floating-point numbers are stored as `double`.
|
||||
- The library is interoperable with respect to the specification, because its supported range $[-2^{63}, 2^{64}-1]$ is
|
||||
larger than the described range $[-2^{53}+1, 2^{53}-1]$.
|
||||
- All integers outside the range $[-2^{63}, 2^{64}-1]$, as well as floating-point numbers are stored as `double`.
|
||||
This also concurs with the specification above.
|
||||
|
||||
### Zeros
|
||||
|
||||
@@ -389,7 +389,6 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
|
||||
| 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)` |
|
||||
| [`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>` |
|
||||
| [`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 |
|
||||
|
||||
@@ -19,5 +19,3 @@ 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 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
|
||||
|
||||
@@ -125,7 +125,7 @@ automatically download a release as a dependency at configure time.
|
||||
|
||||
### `JSON_BuildTests`
|
||||
|
||||
Build the unit tests when [`BUILD_TESTING`](https://cmake.org/cmake/help/latest/command/enable_testing.html) is enabled. This option is `ON` by default if the library's CMake project is the top project. That is, when integrating the library as described above, the test suite is not built unless explicitly switched on with this option.
|
||||
Build the unit tests when [`BUILD_TESTING`](https://cmake.org/cmake/help/latest/command/enable_testing.html) is enabled. This option is `ON` by default if the library's CMake project is the top project and the `tests` directory exists (the release archive `json.tar.xz` does not contain it). That is, when integrating the library as described above, the test suite is not built unless explicitly switched on with this option.
|
||||
|
||||
### `JSON_CI`
|
||||
|
||||
@@ -169,12 +169,6 @@ Enable position diagnostics by defining macro [`JSON_DIAGNOSTIC_POSITIONS`](../a
|
||||
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_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`
|
||||
|
||||
Skip expensive/slow test suites. This option is `OFF` by default. Depends on `JSON_BuildTests`.
|
||||
|
||||
@@ -15,7 +15,4 @@ Clang).
|
||||
|
||||
You can further use file
|
||||
[`single_include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_fwd.hpp)
|
||||
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).
|
||||
for forward declarations.
|
||||
|
||||
@@ -443,6 +443,30 @@ installed by adding the `-DJSON_MultipleHeaders=ON` flag (i.e., `cget install nl
|
||||
- :octicons-file-24: File issues at the [library issue tracker](https://github.com/nlohmann/json/issues)
|
||||
- :octicons-question-24: [Xcode documentation](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app)
|
||||
|
||||
The `json` target's public headers live at `single_include/nlohmann`, so a consumer must write `#include <json.hpp>` rather than the
|
||||
`#include <nlohmann/json.hpp>` form used elsewhere in this documentation. The `json` target also ships only headers, and SwiftPM/Xcode
|
||||
expect every library target to produce an object file to link against; without one, linking a consumer fails with a missing `json.o`
|
||||
([#4650](https://github.com/nlohmann/json/issues/4650)). The workaround is to add at least one `.cpp` file of your own to the target
|
||||
that depends on `json`.
|
||||
|
||||
??? example
|
||||
|
||||
1. Create the following files:
|
||||
|
||||
```swift title="Package.swift"
|
||||
--8<-- "integration/swift/Package.swift"
|
||||
```
|
||||
|
||||
```cpp title="Sources/MyLibrary/example.cpp"
|
||||
--8<-- "integration/swift/example.cpp"
|
||||
```
|
||||
|
||||
2. Build
|
||||
|
||||
```shell
|
||||
swift build
|
||||
```
|
||||
|
||||
## NuGet
|
||||
|
||||
!!! abstract "Summary"
|
||||
@@ -678,11 +702,11 @@ to install the [nlohmann-json](https://ports.macports.org/port/nlohmann-json/) p
|
||||
1. Create the following files:
|
||||
|
||||
```cpp title="example.cpp"
|
||||
--8<-- "integration/macports/example.cpp"
|
||||
--8<-- "integration/homebrew/example.cpp"
|
||||
```
|
||||
|
||||
```cmake title="CMakeLists.txt"
|
||||
--8<-- "integration/macports/CMakeLists.txt"
|
||||
--8<-- "integration/homebrew/CMakeLists.txt"
|
||||
```
|
||||
|
||||
2. Install the package:
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// swift-tools-version: 5.9
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "MyPackage",
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/nlohmann/json.git", from: "3.12.0")
|
||||
],
|
||||
targets: [
|
||||
// the C++ target that uses nlohmann/json
|
||||
.target(
|
||||
name: "MyLibrary",
|
||||
dependencies: [
|
||||
.product(name: "json", package: "json")
|
||||
],
|
||||
// works around missing public headers in MyLibrary; not related to nlohmann/json
|
||||
publicHeadersPath: "."
|
||||
)
|
||||
]
|
||||
)
|
||||
@@ -0,0 +1,8 @@
|
||||
// MyLibrary must contain at least one .cpp file, or SwiftPM/Xcode will
|
||||
// not build a usable "json" library to link against (see nlohmann/json#4650)
|
||||
#include <json.hpp>
|
||||
|
||||
nlohmann::json example()
|
||||
{
|
||||
return nlohmann::json::meta();
|
||||
}
|
||||
@@ -87,7 +87,6 @@ nav:
|
||||
- features/object_order.md
|
||||
- Parsing:
|
||||
- features/parsing/index.md
|
||||
- features/parsing/error_recovery.md
|
||||
- features/parsing/json_lines.md
|
||||
- features/parsing/parse_exceptions.md
|
||||
- features/parsing/parser_callbacks.md
|
||||
@@ -288,14 +287,12 @@ nav:
|
||||
- 'JSON_DIAGNOSTICS': api/macros/json_diagnostics.md
|
||||
- 'JSON_DIAGNOSTIC_POSITIONS': api/macros/json_diagnostic_positions.md
|
||||
- 'JSON_DISABLE_ENUM_SERIALIZATION': api/macros/json_disable_enum_serialization.md
|
||||
- 'JSON_DISABLE_TUPLE_REFERENCE_CONVERSION': api/macros/json_disable_tuple_reference_conversion.md
|
||||
- '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_RANGES': api/macros/json_has_ranges.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_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_NO_IO': api/macros/json_no_io.md
|
||||
- 'JSON_NO_THREAD_LOCAL': api/macros/json_no_thread_local.md
|
||||
@@ -352,6 +349,7 @@ markdown_extensions:
|
||||
- toc:
|
||||
permalink: true
|
||||
- md_in_html
|
||||
- pymdownx.arithmatex
|
||||
- pymdownx.betterem:
|
||||
smart_enable: all
|
||||
- pymdownx.caret
|
||||
@@ -443,3 +441,6 @@ plugins:
|
||||
|
||||
extra_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)")
|
||||
|
||||
# sections in `<!-- NOLINT -->` comments are treated as present
|
||||
nolint_match = re.match(r"<!--\s*NOLINT\s+(.*?)\s*-->", line)
|
||||
if nolint_match:
|
||||
current_section = nolint_match.group(1)
|
||||
if line.startswith("<!-- NOLINT"):
|
||||
current_section = line.strip("<!-- NOLINT")
|
||||
current_section = current_section.strip(" -->")
|
||||
existing_sections.append(current_section)
|
||||
|
||||
# check if sections are correct
|
||||
@@ -97,7 +97,7 @@ def check_structure() -> None:
|
||||
if len(unexpected):
|
||||
report("style/numbering", f"{file}:{lineno} ({current_section})", f'unexpected overloads: {", ".join([f"({x})" for x in unexpected])}')
|
||||
|
||||
current_section = line[3:]
|
||||
current_section = line.strip("## ")
|
||||
existing_sections.append(current_section)
|
||||
|
||||
if current_section in expected_sections:
|
||||
@@ -141,7 +141,7 @@ def check_structure() -> None:
|
||||
# check that non-example admonitions have titles
|
||||
untitled_admonition = re.match(r"^(\?\?\?|!!!) ([^ ]+)$", line)
|
||||
if untitled_admonition and untitled_admonition.group(2) != "example":
|
||||
report("style/admonition_title", f"{file}:{lineno+1} ({current_section})", f'"{untitled_admonition.group(2)}" admonitions should have a title')
|
||||
report("style/admonition_title", f"{file}:{lineno} ({current_section})", f'"{untitled_admonition.group(2)}" admonitions should have a title')
|
||||
|
||||
previous_line = line
|
||||
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.12.0
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <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,13 +167,9 @@ template<typename BasicJsonType, typename EnumType,
|
||||
enable_if_t<std::is_enum<EnumType>::value, int> = 0>
|
||||
inline void from_json(const BasicJsonType& j, EnumType& e)
|
||||
{
|
||||
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;
|
||||
typename std::underlying_type<EnumType>::type val;
|
||||
get_arithmetic_value(j, val);
|
||||
e = static_cast<EnumType>(static_cast<underlying_type>(val));
|
||||
e = static_cast<EnumType>(val);
|
||||
}
|
||||
#endif // JSON_DISABLE_ENUM_SERIALIZATION
|
||||
|
||||
@@ -568,7 +564,7 @@ inline void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allo
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &p));
|
||||
JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j));
|
||||
}
|
||||
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
|
||||
}
|
||||
@@ -588,7 +584,7 @@ inline void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Has
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &p));
|
||||
JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j));
|
||||
}
|
||||
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
|
||||
template<typename BasicJsonType, typename T,
|
||||
enable_if_t<std::is_constructible<BasicJsonType, T>::value, int> = 0>
|
||||
void to_json(BasicJsonType& j, const std::optional<T>& opt) noexcept(std::is_nothrow_assignable<BasicJsonType&, const T&>::value)
|
||||
void to_json(BasicJsonType& j, const std::optional<T>& opt) noexcept
|
||||
{
|
||||
if (opt.has_value())
|
||||
{
|
||||
@@ -471,13 +471,11 @@ inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<
|
||||
j = { std::get<Idx>(t)... };
|
||||
}
|
||||
|
||||
// A one-element braced list does not reliably wrap its element: with
|
||||
// JSON_BRACE_INIT_COPY_SEMANTICS it copies it, which would serialize
|
||||
// std::tuple<int>{5} as 5 rather than [5], and some compilers (e.g., Apple clang
|
||||
// 15 and 16) copy an element that is itself a basic_json even without it, so
|
||||
// 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.
|
||||
#if JSON_BRACE_INIT_COPY_SEMANTICS
|
||||
// JSON_BRACE_INIT_COPY_SEMANTICS makes a one-element braced list copy its
|
||||
// element instead of wrapping it, which would serialize std::tuple<int>{5} as 5
|
||||
// rather than [5]. Build what the default deduction builds instead: an object
|
||||
// if the element is a [string, value] pair, a one-element array otherwise.
|
||||
template<typename BasicJsonType, typename Tuple>
|
||||
inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<0> /*unused*/)
|
||||
{
|
||||
@@ -495,6 +493,7 @@ inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<
|
||||
j = BasicJsonType::array({std::move(element)});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename BasicJsonType, typename Tuple>
|
||||
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
|
||||
|
||||
#include <algorithm> // min
|
||||
#include <array> // array
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // uint32_t
|
||||
#include <cstring> // strlen
|
||||
#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 <string> // string, char_traits
|
||||
#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <nlohmann/detail/iterators/iterator_traits.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||
#include <nlohmann/detail/string_utils.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
@@ -83,9 +82,8 @@ class file_input_adapter
|
||||
};
|
||||
|
||||
/*!
|
||||
Input adapter for a (caching) istream. Does not skip a UTF Byte Order Mark
|
||||
itself; that is done by the lexer's skip_bom(). Does not support changing
|
||||
the underlying std::streambuf
|
||||
Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at
|
||||
beginning of input. Does not support changing the underlying std::streambuf
|
||||
in mid-input. Maintains underlying std::istream and std::streambuf to support
|
||||
subsequent use of standard std::istream operations to process any input
|
||||
characters following those used in parsing the JSON input. Clears the
|
||||
@@ -108,13 +106,7 @@ class input_stream_adapter
|
||||
// was given back with release_lookahead()
|
||||
commit_lookahead();
|
||||
#endif
|
||||
// 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);
|
||||
}
|
||||
is->clear(is->rdstate() & std::ios::eofbit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,14 +448,32 @@ struct wide_string_input_helper<BaseInputAdapter, 4>
|
||||
// get the current character
|
||||
const auto wc = input.get_character();
|
||||
|
||||
if (wc <= 0x10FFFF)
|
||||
// UTF-32 to UTF-8 encoding
|
||||
if (wc < 0x80)
|
||||
{
|
||||
// UTF-32 to UTF-8 encoding
|
||||
utf8_bytes_filled = 0;
|
||||
encode_utf8(static_cast<std::uint32_t>(wc), [&utf8_bytes, &utf8_bytes_filled](std::uint32_t byte)
|
||||
{
|
||||
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>(wc);
|
||||
utf8_bytes_filled = 1;
|
||||
}
|
||||
else if (wc <= 0x7FF)
|
||||
{
|
||||
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
|
||||
{
|
||||
@@ -500,15 +510,24 @@ struct wide_string_input_helper<BaseInputAdapter, 2>
|
||||
// get the current character
|
||||
const auto wc = input.get_character();
|
||||
|
||||
if (0xD800 > wc || wc >= 0xE000)
|
||||
// UTF-16 to UTF-8 encoding
|
||||
if (wc < 0x80)
|
||||
{
|
||||
// a UTF-16 code unit outside the surrogate range is a valid
|
||||
// code point (at most U+FFFF) on its own
|
||||
utf8_bytes_filled = 0;
|
||||
encode_utf8(static_cast<std::uint32_t>(wc), [&utf8_bytes, &utf8_bytes_filled](std::uint32_t byte)
|
||||
{
|
||||
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>(wc);
|
||||
utf8_bytes_filled = 1;
|
||||
}
|
||||
else if (wc <= 0x7FF)
|
||||
{
|
||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u)));
|
||||
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
|
||||
{
|
||||
@@ -526,11 +545,11 @@ struct wide_string_input_helper<BaseInputAdapter, 2>
|
||||
if (0xDC00 <= wc2 && wc2 <= 0xDFFF)
|
||||
{
|
||||
const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
|
||||
utf8_bytes_filled = 0;
|
||||
encode_utf8(charcode, [&utf8_bytes, &utf8_bytes_filled](std::uint32_t byte)
|
||||
{
|
||||
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>(0xF0u | (charcode >> 18u));
|
||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
|
||||
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_filled = 4;
|
||||
valid_pair = true;
|
||||
}
|
||||
}
|
||||
@@ -792,16 +811,12 @@ inline file_input_adapter input_adapter(std::FILE* file)
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
inline input_stream_adapter input_adapter(std::istream&& stream)
|
||||
{
|
||||
return input_adapter(stream);
|
||||
return input_stream_adapter(stream);
|
||||
}
|
||||
#endif // JSON_NO_IO
|
||||
|
||||
@@ -830,28 +845,16 @@ 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)
|
||||
{
|
||||
#if JSON_STRICT_NUL_HANDLING
|
||||
// A text-literal array from string-literal initialization (e.g.
|
||||
// json::parse("123") or json::parse(L"123")) carries a trailing '\0'
|
||||
// contributed by the compiler, not by the source text; drop exactly that
|
||||
// one byte so it is not mistaken for real trailing data. This covers all
|
||||
// character types that string literals can use: char, wchar_t, char16_t,
|
||||
// char32_t, and (C++20) char8_t. Every other element type (unsigned char,
|
||||
// std::uint8_t, ...) keeps the full extent unconditionally, since a
|
||||
// trailing zero byte there is genuine data (e.g. CBOR/MessagePack). This
|
||||
// intentionally does not strlen()-scan the array (as the pointer overload
|
||||
// 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)
|
||||
// A `char` array from string-literal initialization (e.g. json::parse("123"))
|
||||
// carries a trailing '\0' contributed by the compiler, not by the source
|
||||
// text; drop exactly that one byte so it is not mistaken for real trailing
|
||||
// data. Every other element type (unsigned char, std::uint8_t, ...) keeps
|
||||
// the full extent unconditionally, since a trailing zero byte there is
|
||||
// genuine data (e.g. CBOR/MessagePack). This intentionally does not
|
||||
// strlen()-scan the array (as the pointer overload above does for a
|
||||
// null-delimited string): for a `char` array that is not NUL-terminated
|
||||
// within its bounds, that would read past the end of the array.
|
||||
if (std::is_same<typename std::remove_cv<T>::type, char>::value && N > 0 && array[N - 1] == 0)
|
||||
{
|
||||
return input_adapter(array, array + N - 1);
|
||||
}
|
||||
@@ -859,9 +862,9 @@ auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) /
|
||||
return input_adapter(array, array + N);
|
||||
}
|
||||
|
||||
// This class only handles inputs that construct a contiguous_bytes_input_adapter
|
||||
// (e.g. span_input_adapter). It's required so that expressions like {ptr, len}
|
||||
// can be implicitly cast to the correct adapter.
|
||||
// This class only handles inputs of input_buffer_adapter type.
|
||||
// It's required so that expressions like {ptr, len} can be implicitly cast
|
||||
// to the correct adapter.
|
||||
class span_input_adapter
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
#include <algorithm> // find_if, min
|
||||
#include <cstddef>
|
||||
#include <limits> // numeric_limits
|
||||
#include <string> // string
|
||||
#include <type_traits> // enable_if_t
|
||||
#include <utility> // move, pair
|
||||
@@ -132,9 +131,7 @@ struct json_sax
|
||||
@param[in] position the position in the input where the error occurs
|
||||
@param[in] last_token the last read token
|
||||
@param[in] ex an exception object describing the error
|
||||
@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
|
||||
@return whether parsing should proceed (must return false)
|
||||
*/
|
||||
virtual bool parse_error(std::size_t position,
|
||||
const std::string& last_token,
|
||||
@@ -178,88 +175,6 @@ template<typename ArrayType>
|
||||
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
|
||||
|
||||
@@ -271,12 +186,9 @@ a pointer to the respective array or object for each recursion depth.
|
||||
After successful parsing, the value that is passed by reference to the
|
||||
constructor contains the parsed value.
|
||||
|
||||
@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
|
||||
@tparam BasicJsonType the JSON type
|
||||
*/
|
||||
template<typename BasicJsonType, typename InputAdapterType = string_input_adapter_type>
|
||||
template<typename BasicJsonType, typename InputAdapterType>
|
||||
class json_sax_dom_parser
|
||||
{
|
||||
public:
|
||||
@@ -464,6 +376,76 @@ class json_sax_dom_parser
|
||||
|
||||
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
|
||||
root.
|
||||
@@ -479,7 +461,7 @@ class json_sax_dom_parser
|
||||
root = BasicJsonType(std::forward<Value>(v));
|
||||
|
||||
#if JSON_DIAGNOSTIC_POSITIONS
|
||||
diagnostic_positions::set_from_lexer(root, m_lexer_ref);
|
||||
handle_diagnostic_positions_for_json_value(root);
|
||||
#endif
|
||||
|
||||
return &root;
|
||||
@@ -492,7 +474,7 @@ class json_sax_dom_parser
|
||||
ref_stack.back()->m_data.m_value.array->emplace_back(std::forward<Value>(v));
|
||||
|
||||
#if JSON_DIAGNOSTIC_POSITIONS
|
||||
diagnostic_positions::set_from_lexer(ref_stack.back()->m_data.m_value.array->back(), m_lexer_ref);
|
||||
handle_diagnostic_positions_for_json_value(ref_stack.back()->m_data.m_value.array->back());
|
||||
#endif
|
||||
|
||||
return &(ref_stack.back()->m_data.m_value.array->back());
|
||||
@@ -503,7 +485,7 @@ class json_sax_dom_parser
|
||||
*object_element = BasicJsonType(std::forward<Value>(v));
|
||||
|
||||
#if JSON_DIAGNOSTIC_POSITIONS
|
||||
diagnostic_positions::set_from_lexer(*object_element, m_lexer_ref);
|
||||
handle_diagnostic_positions_for_json_value(*object_element);
|
||||
#endif
|
||||
|
||||
return object_element;
|
||||
@@ -523,7 +505,7 @@ class json_sax_dom_parser
|
||||
lexer_t* m_lexer_ref = nullptr;
|
||||
};
|
||||
|
||||
template<typename BasicJsonType, typename InputAdapterType = string_input_adapter_type>
|
||||
template<typename BasicJsonType, typename InputAdapterType>
|
||||
class json_sax_dom_callback_parser
|
||||
{
|
||||
public:
|
||||
@@ -600,8 +582,8 @@ class json_sax_dom_callback_parser
|
||||
|
||||
bool start_object(std::size_t len)
|
||||
{
|
||||
// check callback for object start; not called inside a discarded container
|
||||
const bool keep = keep_stack.back() && callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);
|
||||
// check callback for object start
|
||||
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);
|
||||
keep_stack.push_back(keep);
|
||||
|
||||
// the key this object will be stored under, read before handle_value()
|
||||
@@ -637,18 +619,6 @@ class json_sax_dom_callback_parser
|
||||
|
||||
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);
|
||||
|
||||
// check callback for the key
|
||||
@@ -692,7 +662,7 @@ class json_sax_dom_callback_parser
|
||||
|
||||
#if JSON_DIAGNOSTIC_POSITIONS
|
||||
// Set start/end positions for discarded object.
|
||||
diagnostic_positions::set_from_lexer(*ref_stack.back(), m_lexer_ref);
|
||||
handle_diagnostic_positions_for_json_value(*ref_stack.back());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -734,7 +704,7 @@ class json_sax_dom_callback_parser
|
||||
|
||||
bool start_array(std::size_t len)
|
||||
{
|
||||
const bool keep = keep_stack.back() && callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);
|
||||
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);
|
||||
keep_stack.push_back(keep);
|
||||
|
||||
// see start_object()
|
||||
@@ -808,7 +778,7 @@ class json_sax_dom_callback_parser
|
||||
|
||||
#if JSON_DIAGNOSTIC_POSITIONS
|
||||
// Set start/end positions for discarded array.
|
||||
diagnostic_positions::set_from_lexer(*ref_stack.back(), m_lexer_ref);
|
||||
handle_diagnostic_positions_for_json_value(*ref_stack.back());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -861,6 +831,72 @@ class json_sax_dom_callback_parser
|
||||
|
||||
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,
|
||||
/// 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
|
||||
@@ -982,7 +1018,7 @@ class json_sax_dom_callback_parser
|
||||
auto value = BasicJsonType(std::forward<Value>(v));
|
||||
|
||||
#if JSON_DIAGNOSTIC_POSITIONS
|
||||
diagnostic_positions::set_from_lexer(value, m_lexer_ref);
|
||||
handle_diagnostic_positions_for_json_value(value);
|
||||
#endif
|
||||
|
||||
// check callback
|
||||
|
||||
@@ -9,9 +9,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <array> // array
|
||||
#include <clocale> // localeconv
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // uint8_t, uint32_t
|
||||
#include <cstdio> // snprintf
|
||||
#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull
|
||||
#include <initializer_list> // initializer_list
|
||||
#include <string> // char_traits, string
|
||||
#include <utility> // move
|
||||
@@ -23,7 +24,6 @@
|
||||
#include <nlohmann/detail/input/string_scan.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||
#include <nlohmann/detail/string_utils.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
@@ -217,6 +217,18 @@ class lexer : public lexer_base<BasicJsonType>
|
||||
~lexer() = default;
|
||||
|
||||
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
|
||||
/////////////////////
|
||||
@@ -439,16 +451,8 @@ class lexer : public lexer_base<BasicJsonType>
|
||||
if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF)
|
||||
{
|
||||
// expect next \uxxxx entry
|
||||
if (JSON_HEDLEY_LIKELY(get() == '\\'))
|
||||
if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u'))
|
||||
{
|
||||
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();
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1))
|
||||
@@ -473,11 +477,7 @@ class lexer : public lexer_base<BasicJsonType>
|
||||
}
|
||||
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";
|
||||
string_error_resume = resume_kind::after_escape;
|
||||
string_error_codepoint = codepoint2;
|
||||
return token_type::parse_error;
|
||||
}
|
||||
}
|
||||
@@ -491,9 +491,7 @@ class lexer : public lexer_base<BasicJsonType>
|
||||
{
|
||||
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";
|
||||
string_error_resume = resume_kind::after_escape;
|
||||
return token_type::parse_error;
|
||||
}
|
||||
}
|
||||
@@ -502,10 +500,32 @@ class lexer : public lexer_base<BasicJsonType>
|
||||
JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF);
|
||||
|
||||
// translate codepoint into bytes
|
||||
encode_utf8(static_cast<std::uint32_t>(codepoint), [this](std::uint32_t byte)
|
||||
if (codepoint < 0x80)
|
||||
{
|
||||
add(static_cast<char_int_type>(byte));
|
||||
});
|
||||
// 1-byte characters: 0xxxxxxx (ASCII)
|
||||
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;
|
||||
}
|
||||
@@ -1016,6 +1036,24 @@ 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
|
||||
|
||||
@@ -1055,7 +1093,7 @@ class lexer : public lexer_base<BasicJsonType>
|
||||
@note The scanner is independent of the current locale: token_buffer
|
||||
always holds `.`. Only the std::strtod fallback of convert_number()
|
||||
depends on the locale, and it looks up the decimal point right
|
||||
before converting (see detail::convert_float_locale_aware()).
|
||||
before converting (see 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.
|
||||
{
|
||||
@@ -1386,6 +1424,59 @@ scan_number_done:
|
||||
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
|
||||
|
||||
@@ -1399,34 +1490,49 @@ scan_number_done:
|
||||
token_buffer (the index of 'e'/'E', or
|
||||
token_buffer.size() when there is no exponent);
|
||||
used to skip Clinger's fast path when it cannot
|
||||
possibly succeed - see detail::mantissa_fits_clinger()
|
||||
possibly succeed - see mantissa_fits_clinger()
|
||||
*/
|
||||
token_type convert_number(token_type number_type, std::size_t mantissa_end)
|
||||
{
|
||||
// accept() only needs to know whether the input is valid, so it sets
|
||||
// discard_number_values (see json.hpp), and an integer token whose
|
||||
// digit count shows that it fits is reported without calling
|
||||
// convert_integer(). A number with up to 18 digits always fits into
|
||||
// both std::uint64_t and std::int64_t (18 nines is about 1e18, below
|
||||
// INT64_MAX, which is about 9.2e18). Longer tokens take the exact path
|
||||
// below, including the fallback to floating point when the value does
|
||||
// not fit.
|
||||
// If the caller does not need the converted value (only whether the
|
||||
// input is syntactically valid; see json_sax_acceptor/accept()), an
|
||||
// unsigned/integer token can be reported without calling
|
||||
// strtoull()/strtoll() at all, *provided* we can already tell from
|
||||
// the digit count alone that the conversion cannot overflow 64 bits.
|
||||
// Such tokens are always finite and are accepted unconditionally by
|
||||
// the parser regardless of their actual value (parser::sax_parse_internal()
|
||||
// never checks finiteness for value_unsigned/value_integer), so the
|
||||
// classification below is all that is needed.
|
||||
//
|
||||
// With a narrower number_unsigned_t/number_integer_t (e.g.
|
||||
// std::uint32_t), the exact path would reclassify some of these tokens
|
||||
// as (finite) floats, while this check reports integers. That does not
|
||||
// change the result of accept(): it always parses through
|
||||
// json_sax_acceptor, whose number callbacks discard their argument and
|
||||
// return true, and the parser rejects neither integers nor finite
|
||||
// floats. value_unsigned/value_integer are left unset here, so a caller
|
||||
// that reads the converted value must not set discard_number_values.
|
||||
// A decimal number with up to 18 digits is always representable in
|
||||
// both std::uint64_t and std::int64_t (18 nines is ~1e18, well below
|
||||
// both UINT64_MAX ~1.8e19 and INT64_MAX ~9.2e18), so strtoull()/strtoll()
|
||||
// could not have set errno to ERANGE for it. Numbers with more digits
|
||||
// (rare in practice) fall through to the exact code below, unchanged,
|
||||
// so their handling -- including reclassification to value_float when
|
||||
// the value overflows 64 bits, and rejection when it is not even
|
||||
// finite as a double -- is bit-for-bit identical to before this
|
||||
// optimization.
|
||||
//
|
||||
// On contiguous input, scan_number_bulk_contiguous() converts integer
|
||||
// tokens itself and does not pass them to this function, unless
|
||||
// JSON_DIAGNOSTIC_POSITIONS is enabled. This check is therefore only
|
||||
// reached for input without bulk access (e.g. streams), with
|
||||
// JSON_DIAGNOSTIC_POSITIONS, or when scan_number_bulk_contiguous()
|
||||
// falls back to scan_number().
|
||||
// Note this reasons about std::uint64_t/std::int64_t, not about
|
||||
// number_unsigned_t/number_integer_t (BasicJsonType's own, possibly
|
||||
// narrower, template parameters -- e.g. std::uint32_t). That is fine
|
||||
// *only* because discard_number_values is exclusively set by
|
||||
// accept() (see json.hpp), and accept() always parses through the
|
||||
// library's own json_sax_acceptor -- never a user-supplied SAX
|
||||
// consumer -- whose number_unsigned()/number_integer()/number_float()
|
||||
// callbacks unconditionally discard their argument and return true.
|
||||
// So for every caller that can reach this branch, neither the token
|
||||
// classification below nor the eventual (possibly narrowed, and on
|
||||
// this fast path left stale/unset) value_unsigned/value_integer is
|
||||
// ever consulted -- an unsigned/integer token is accepted outright,
|
||||
// and even a >18-digit token that this fast path deliberately falls
|
||||
// through for is, once reclassified to value_float, still finite
|
||||
// (and thus accepted) for any digit count that fits in number_unsigned_t
|
||||
// or number_integer_t regardless of that type's width. If this
|
||||
// function is ever taught to run with discard_number_values true for
|
||||
// a caller that *does* read the converted value, this reasoning (and
|
||||
// the fast path below) would need to be revisited.
|
||||
if (discard_number_values)
|
||||
{
|
||||
constexpr std::size_t safe_digit_count = 18;
|
||||
@@ -1457,15 +1563,77 @@ scan_number_done:
|
||||
// (Eisel-Lemire, locale-independent, correctly rounded) when available;
|
||||
// otherwise the exact Clinger fast path (double only); otherwise the
|
||||
// locale-aware strtof/strtod/strtold.
|
||||
if (convert_float_fast(num_begin, num_end, decimal_point_position, mantissa_end, value_float))
|
||||
if (parse_float_from_chars(num_begin, num_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;
|
||||
}
|
||||
|
||||
convert_float_locale_aware(token_buffer, decimal_point_position, value_float);
|
||||
convert_float_locale_aware();
|
||||
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
|
||||
|
||||
@@ -1855,7 +2023,7 @@ scan_number_done:
|
||||
return value_float;
|
||||
}
|
||||
|
||||
/// return current string value
|
||||
/// return current string value (implicitly resets the token; useful only once)
|
||||
string_t& get_string()
|
||||
{
|
||||
// a number token holds '.' regardless of the locale (#4084)
|
||||
@@ -2109,573 +2277,6 @@ 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:
|
||||
/// input adapter
|
||||
InputAdapterType ia;
|
||||
@@ -2716,13 +2317,6 @@ scan_number_done:
|
||||
/// a description of occurred lexer errors
|
||||
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_integer_t value_integer = 0;
|
||||
number_unsigned_t value_unsigned = 0;
|
||||
@@ -2731,11 +2325,11 @@ scan_number_done:
|
||||
/// the position of the decimal point in token_buffer
|
||||
std::size_t decimal_point_position = std::string::npos;
|
||||
|
||||
/// whether the caller only needs the token types and never looks at the
|
||||
/// converted numeric values; set only by accept(), which parses through
|
||||
/// json_sax_acceptor. When set, convert_number() skips converting integer
|
||||
/// tokens whose digit count guarantees that they fit into 64 bits (see
|
||||
/// there)
|
||||
/// whether the caller (e.g. accept()/json_sax_acceptor) only needs the
|
||||
/// token classification and never looks at the converted numeric value;
|
||||
/// when set, scan_number() may skip strtoull()/strtoll() for
|
||||
/// value_unsigned/value_integer tokens whose digit count guarantees they
|
||||
/// fit into 64 bits (see scan_number())
|
||||
const bool discard_number_values = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// | | |__ | | | | | | 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
|
||||
|
||||
@@ -11,16 +10,10 @@
|
||||
|
||||
#include <array> // array
|
||||
#include <cfloat> // FLT_EVAL_METHOD
|
||||
#include <clocale> // localeconv
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // int64_t, uint64_t
|
||||
#include <cstdlib> // strtof, strtod, strtold
|
||||
#include <cstring> // memcpy
|
||||
#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>
|
||||
|
||||
// std::from_chars lives in <charconv>, but being in C++17 mode does not
|
||||
@@ -36,9 +29,8 @@
|
||||
|
||||
// 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
|
||||
// overhead of std::strtoull/std::strtod where possible. They are free functions
|
||||
// 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.
|
||||
// overhead of std::strtoull/std::strtod. They are free functions so the lexer
|
||||
// stays focused on scanning; see lexer::convert_number().
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
@@ -301,415 +293,5 @@ bool parse_float_from_chars(const char* first, const char* last, FloatType& out)
|
||||
#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
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
@@ -54,8 +54,7 @@ using parser_callback_t =
|
||||
/*!
|
||||
@brief syntax analysis
|
||||
|
||||
This class implements an iterative parser that keeps the open containers on
|
||||
an explicit stack and reports what it reads as SAX events.
|
||||
This class implements a recursive descent parser.
|
||||
*/
|
||||
template<typename BasicJsonType, typename InputAdapterType>
|
||||
class parser
|
||||
@@ -99,9 +98,28 @@ class parser
|
||||
if (callback)
|
||||
{
|
||||
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
|
||||
if (!parse_dom(sdp, strict))
|
||||
if (sdp.is_errored())
|
||||
{
|
||||
result = value_t::discarded;
|
||||
return;
|
||||
@@ -117,9 +135,26 @@ class parser
|
||||
else
|
||||
{
|
||||
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
|
||||
if (!parse_dom(sdp, strict))
|
||||
if (sdp.is_errored())
|
||||
{
|
||||
result = value_t::discarded;
|
||||
return;
|
||||
@@ -138,59 +173,26 @@ class parser
|
||||
bool accept(const bool strict = true)
|
||||
{
|
||||
json_sax_acceptor<BasicJsonType> sax_acceptor;
|
||||
return sax_parse_impl<false>(&sax_acceptor, strict);
|
||||
return sax_parse(&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>
|
||||
JSON_HEDLEY_NON_NULL(2)
|
||||
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> {};
|
||||
const bool result = sax_parse_internal<AllowRecovery>(sax);
|
||||
const bool result = sax_parse_internal(sax);
|
||||
|
||||
if (result)
|
||||
{
|
||||
if (strict)
|
||||
{
|
||||
// strict mode: next byte must be EOF; after recovering from an
|
||||
// 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)
|
||||
// strict mode: next byte must be EOF
|
||||
if (get_token() != token_type::end_of_input)
|
||||
{
|
||||
// the value is complete, so there is nothing to recover
|
||||
static_cast<void>(report_error(sax, parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr),
|
||||
std::integral_constant<bool, AllowRecovery> {}));
|
||||
return false;
|
||||
return sax->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
|
||||
@@ -201,63 +203,14 @@ class parser
|
||||
}
|
||||
}
|
||||
|
||||
return result && !error_reported;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
@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>
|
||||
private:
|
||||
template<typename SAX>
|
||||
JSON_HEDLEY_NON_NULL(2)
|
||||
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
|
||||
// true = array; false = object
|
||||
std::vector<bool> states;
|
||||
@@ -288,18 +241,12 @@ class parser
|
||||
break;
|
||||
}
|
||||
|
||||
// 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)
|
||||
// parse key
|
||||
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
|
||||
{
|
||||
if (!continue_after(key_error(sax, allow_recovery, false), skip_to_state_evaluation))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
|
||||
}
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
|
||||
{
|
||||
@@ -309,13 +256,14 @@ class parser
|
||||
// parse separator (:)
|
||||
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
|
||||
{
|
||||
if (!continue_after(key_error(sax, allow_recovery, true), skip_to_state_evaluation))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
|
||||
}
|
||||
|
||||
// remember we are now inside an object
|
||||
states.push_back(false);
|
||||
|
||||
// parse values
|
||||
get_token();
|
||||
continue;
|
||||
@@ -351,11 +299,9 @@ class parser
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))
|
||||
{
|
||||
if (!overflow_error(sax, res, allow_recovery))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr));
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
|
||||
@@ -423,63 +369,23 @@ class parser
|
||||
case token_type::parse_error:
|
||||
{
|
||||
// using "uninitialized" to avoid an "expected" message
|
||||
if (!report_error(sax, parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr), allow_recovery))
|
||||
{
|
||||
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;
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr));
|
||||
}
|
||||
case token_type::end_of_input:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(m_lexer.get_position().chars_read_total == 1))
|
||||
{
|
||||
// there is nothing to recover
|
||||
static_cast<void>(report_error(sax, 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));
|
||||
return false;
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
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));
|
||||
}
|
||||
|
||||
if (!report_error(sax, parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr), allow_recovery))
|
||||
{
|
||||
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;
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
|
||||
}
|
||||
case token_type::uninitialized:
|
||||
case token_type::end_array:
|
||||
@@ -489,35 +395,9 @@ class parser
|
||||
case token_type::literal_or_value:
|
||||
default: // the last token was unexpected
|
||||
{
|
||||
if (!report_error(sax, parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr), allow_recovery))
|
||||
{
|
||||
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;
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -559,39 +439,17 @@ class parser
|
||||
|
||||
// We are done with this array. Before we can parse a
|
||||
// new value, we need to evaluate the new state first.
|
||||
// By setting skip_to_state_evaluation to true, the next
|
||||
// iteration skips parsing a value and evaluates the
|
||||
// enclosing state directly.
|
||||
// By setting skip_to_state_evaluation to false, we
|
||||
// are effectively jumping to the beginning of this if.
|
||||
JSON_ASSERT(!states.empty());
|
||||
states.pop_back();
|
||||
skip_to_state_evaluation = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!report_error(sax, parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), nullptr), allow_recovery))
|
||||
{
|
||||
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;
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), nullptr));
|
||||
}
|
||||
|
||||
// states.back() is false -> object
|
||||
@@ -608,12 +466,11 @@ class parser
|
||||
// parse key
|
||||
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
|
||||
{
|
||||
if (!continue_after(key_error(sax, allow_recovery, false), skip_to_state_evaluation))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
|
||||
{
|
||||
return false;
|
||||
@@ -622,11 +479,9 @@ class parser
|
||||
// parse separator (:)
|
||||
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
|
||||
{
|
||||
if (!continue_after(key_error(sax, allow_recovery, true), skip_to_state_evaluation))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
|
||||
}
|
||||
|
||||
// parse values
|
||||
@@ -645,488 +500,20 @@ class parser
|
||||
|
||||
// We are done with this object. Before we can parse a
|
||||
// new value, we need to evaluate the new state first.
|
||||
// By setting skip_to_state_evaluation to true, the next
|
||||
// iteration skips parsing a value and evaluates the
|
||||
// enclosing state directly.
|
||||
// By setting skip_to_state_evaluation to false, we
|
||||
// are effectively jumping to the beginning of this if.
|
||||
JSON_ASSERT(!states.empty());
|
||||
states.pop_back();
|
||||
skip_to_state_evaluation = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!report_error(sax, parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), nullptr), allow_recovery))
|
||||
{
|
||||
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;
|
||||
}
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@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
|
||||
token_type get_token()
|
||||
{
|
||||
@@ -1173,12 +560,6 @@ class parser
|
||||
const bool allow_exceptions = true;
|
||||
/// whether trailing commas in objects and arrays should be ignored (true) or signaled as errors (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
|
||||
|
||||
@@ -1,371 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | 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,7 +26,6 @@
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#include <nlohmann/detail/string_concat.hpp>
|
||||
#include <nlohmann/detail/string_escape.hpp>
|
||||
#include <nlohmann/detail/string_utils.hpp>
|
||||
#include <nlohmann/detail/value_t.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
@@ -117,7 +116,7 @@ class json_pointer
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
|
||||
json_pointer& operator/=(std::size_t array_idx)
|
||||
{
|
||||
return *this /= detail::to_string<string_t>(array_idx);
|
||||
return *this /= std::to_string(array_idx);
|
||||
}
|
||||
|
||||
/// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer
|
||||
@@ -747,13 +746,7 @@ class json_pointer
|
||||
// "-" always fails the range check
|
||||
return false;
|
||||
}
|
||||
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')))
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9")))
|
||||
{
|
||||
// invalid char
|
||||
return false;
|
||||
@@ -781,7 +774,7 @@ class json_pointer
|
||||
// not throw (see #5395), so such a reference token is treated as "not found"
|
||||
errno = 0; // strtoull() does not reset errno on success
|
||||
char* p_end = nullptr; // NOLINT(misc-const-correctness)
|
||||
const unsigned long long magnitude = std::strtoull(reference_token.data(), &p_end, 10); // NOLINT(runtime/int)
|
||||
const unsigned long long magnitude = std::strtoull(reference_token.c_str(), &p_end, 10); // NOLINT(runtime/int)
|
||||
if (JSON_HEDLEY_UNLIKELY(errno == ERANGE // the value exceeds ULLONG_MAX
|
||||
|| magnitude >= static_cast<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; \
|
||||
}); \
|
||||
if (it != std::end(m)) e = it->first; \
|
||||
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)); \
|
||||
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)); \
|
||||
}
|
||||
|
||||
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
|
||||
@@ -359,7 +359,6 @@ void templated_json_throw(ExceptionType exception)
|
||||
|
||||
// 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_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__, \
|
||||
@@ -622,10 +621,6 @@ void templated_json_throw(ExceptionType exception)
|
||||
// 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
|
||||
// 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__, \
|
||||
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, \
|
||||
@@ -921,6 +916,6 @@ void templated_json_throw(ExceptionType exception)
|
||||
#define JSON_DISABLE_ENUM_SERIALIZATION 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
||||
#define JSON_DISABLE_TUPLE_REFERENCE_CONVERSION 0
|
||||
#ifndef JSON_USE_GLOBAL_UDLS
|
||||
#define JSON_USE_GLOBAL_UDLS 1
|
||||
#endif
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#undef JSON_INLINE_VARIABLE
|
||||
#undef JSON_NO_UNIQUE_ADDRESS
|
||||
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
||||
#undef JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
||||
#undef JSON_USE_GLOBAL_UDLS
|
||||
|
||||
#ifndef JSON_TEST_KEEP_MACROS
|
||||
#undef JSON_CATCH
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array> // array
|
||||
#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 <utility> // index_sequence, make_index_sequence, index_sequence_for
|
||||
@@ -160,5 +161,11 @@ struct static_const
|
||||
constexpr T static_const<T>::value;
|
||||
#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
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
@@ -636,18 +636,6 @@ template<typename BasicJsonType, typename CompatibleType>
|
||||
struct is_compatible_type
|
||||
: 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>
|
||||
struct is_compatible_binary_type
|
||||
{
|
||||
@@ -737,7 +725,7 @@ std::is_constructible <decltype(std::declval<Compare>()(std::declval<A>(), std::
|
||||
// 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
|
||||
// deprecated json_pointer comparison operators have been removed.
|
||||
template<typename Compare, typename A, typename B, bool = is_json_pointer_of<uncvref_t<A>, uncvref_t<B>>::value>
|
||||
template<typename Compare, typename A, typename B, bool = is_json_pointer_of<A, B>::value>
|
||||
struct is_comparable : std::false_type {};
|
||||
|
||||
template<typename Compare, typename A, typename B>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm> // copy
|
||||
#include <cstddef> // size_t
|
||||
#include <iterator> // back_inserter
|
||||
#include <memory> // shared_ptr, make_shared
|
||||
#include <string> // basic_string
|
||||
#include <utility> // move
|
||||
@@ -29,10 +31,6 @@ namespace detail
|
||||
template<typename CharType> struct output_adapter_protocol
|
||||
{
|
||||
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 ~output_adapter_protocol() = default;
|
||||
|
||||
@@ -99,6 +97,7 @@ class output_vector_adapter : public output_adapter_protocol<CharType>
|
||||
sink.write_character(c);
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NON_NULL(2)
|
||||
void write_characters(const CharType* s, std::size_t length) override
|
||||
{
|
||||
sink.write_characters(s, length);
|
||||
@@ -123,6 +122,7 @@ class output_stream_adapter : public output_adapter_protocol<CharType>
|
||||
stream.put(c);
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NON_NULL(2)
|
||||
void write_characters(const CharType* s, std::size_t length) override
|
||||
{
|
||||
stream.write(s, static_cast<std::streamsize>(length));
|
||||
@@ -147,6 +147,7 @@ class output_string_adapter : public output_adapter_protocol<CharType>
|
||||
str.push_back(c);
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NON_NULL(2)
|
||||
void write_characters(const CharType* s, std::size_t length) override
|
||||
{
|
||||
str.append(s, length);
|
||||
|
||||
@@ -3,23 +3,24 @@
|
||||
// | | |__ | | | | | | version 3.12.0
|
||||
// |_____|_____|_____|_|___| 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-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm> // remove, fill, find, none_of, min
|
||||
#include <algorithm> // reverse, remove, fill, find, none_of, min
|
||||
#include <array> // array
|
||||
#include <clocale> // localeconv, lconv
|
||||
#include <cmath> // isfinite
|
||||
#include <cmath> // labs, isfinite, isnan, signbit
|
||||
#include <cstddef> // size_t, ptrdiff_t
|
||||
#include <cstdint> // uint8_t
|
||||
#include <cstdio> // snprintf
|
||||
#include <cstring> // memcpy, memset
|
||||
#include <iterator> // next
|
||||
#include <limits> // numeric_limits
|
||||
#include <string> // string, char_traits
|
||||
#include <type_traits> // is_same
|
||||
#include <utility> // move
|
||||
#include <vector> // vector
|
||||
|
||||
#include <nlohmann/detail/conversions/to_chars.hpp>
|
||||
@@ -27,6 +28,7 @@
|
||||
#include <nlohmann/detail/input/string_scan.hpp>
|
||||
#include <nlohmann/detail/macro_scope.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/recursion_depth_limit.hpp>
|
||||
#include <nlohmann/detail/string_concat.hpp>
|
||||
@@ -81,6 +83,7 @@ class serializer
|
||||
const std::size_t indent_step_ = 0,
|
||||
error_handler_t error_handler_ = error_handler_t::strict)
|
||||
: o(&s)
|
||||
, locale(std::localeconv())
|
||||
, indent_char(ichar)
|
||||
, pretty_print(pretty_print_)
|
||||
, ensure_ascii(ensure_ascii_)
|
||||
@@ -103,10 +106,9 @@ class serializer
|
||||
additional parameter. Arrays and objects are serialized without recursion,
|
||||
however deeply they are nested.
|
||||
|
||||
- strings and object keys are escaped using @ref dump_escaped
|
||||
- integer numbers are converted using a digit-pair lookup table (@ref dump_integer)
|
||||
- floating-point numbers are converted to a string using @ref dump_float, which
|
||||
uses `to_chars` for IEEE-754 types and `snprintf` otherwise
|
||||
- strings and object keys are escaped using `escape_string()`
|
||||
- integer numbers are converted implicitly via `operator<<`
|
||||
- floating-point numbers are converted to a string using `"%g"` format
|
||||
- binary values are serialized as objects containing the subtype and the
|
||||
byte array
|
||||
|
||||
@@ -281,16 +283,127 @@ class serializer
|
||||
}
|
||||
|
||||
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);
|
||||
{
|
||||
put_char('"');
|
||||
dump_escaped(*val.m_data.m_value.string);
|
||||
put_char('"');
|
||||
return;
|
||||
}
|
||||
|
||||
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:
|
||||
{
|
||||
if (val.m_data.m_value.boolean)
|
||||
{
|
||||
put_literal("true");
|
||||
}
|
||||
else
|
||||
{
|
||||
put_literal("false");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::number_integer:
|
||||
{
|
||||
dump_integer(val.m_data.m_value.number_integer);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::number_unsigned:
|
||||
{
|
||||
dump_integer(val.m_data.m_value.number_unsigned);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::number_float:
|
||||
{
|
||||
dump_float(val.m_data.m_value.number_float);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::discarded:
|
||||
{
|
||||
put_literal("<discarded>");
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
{
|
||||
put_literal("null");
|
||||
return;
|
||||
}
|
||||
|
||||
default: // LCOV_EXCL_LINE
|
||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,9 +560,9 @@ class serializer
|
||||
@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
|
||||
@ref dump_iteratively to walk; everything else - including a binary value,
|
||||
@ref dump_internal to walk; everything else - including a binary value,
|
||||
which looks like an object but has no elements to descend into - is written
|
||||
out in full by @ref dump_scalar.
|
||||
out here in full.
|
||||
*/
|
||||
void dump_value(const BasicJsonType& val,
|
||||
const std::size_t current_indent,
|
||||
@@ -507,35 +620,6 @@ class serializer
|
||||
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:
|
||||
{
|
||||
put_char('"');
|
||||
@@ -656,8 +740,6 @@ class serializer
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::object: // LCOV_EXCL_LINE
|
||||
case value_t::array: // LCOV_EXCL_LINE
|
||||
default: // LCOV_EXCL_LINE
|
||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||
}
|
||||
@@ -686,7 +768,7 @@ class serializer
|
||||
Escape a string by replacing certain special characters by a sequence of an
|
||||
escape character (backslash) and another character and other control
|
||||
characters by a sequence of "\u" followed by a four-digit hex
|
||||
representation. The escaped string is appended to @ref write_buffer.
|
||||
representation. The escaped string is written to output stream @a o.
|
||||
|
||||
@param[in] s the string to escape
|
||||
|
||||
@@ -880,7 +962,7 @@ class serializer
|
||||
{
|
||||
case error_handler_t::strict:
|
||||
{
|
||||
JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", detail::hex_byte(byte)), nullptr));
|
||||
JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex_bytes(byte | 0)), nullptr));
|
||||
}
|
||||
|
||||
case error_handler_t::ignore:
|
||||
@@ -913,9 +995,9 @@ class serializer
|
||||
}
|
||||
else
|
||||
{
|
||||
string_buffer[bytes++] = '\xEF';
|
||||
string_buffer[bytes++] = '\xBF';
|
||||
string_buffer[bytes++] = '\xBD';
|
||||
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xEF');
|
||||
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBF');
|
||||
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBD');
|
||||
}
|
||||
|
||||
// write buffer and reset index; there must be 13 bytes
|
||||
@@ -972,7 +1054,7 @@ class serializer
|
||||
{
|
||||
case error_handler_t::strict:
|
||||
{
|
||||
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));
|
||||
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));
|
||||
}
|
||||
|
||||
case error_handler_t::ignore:
|
||||
@@ -1193,6 +1275,20 @@ 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
|
||||
*
|
||||
@@ -1306,7 +1402,7 @@ class serializer
|
||||
/*!
|
||||
@brief dump an integer
|
||||
|
||||
Dump a given integer, appending it to @ref write_buffer. Works internally with
|
||||
Dump a given integer to output stream @a o. Works internally with
|
||||
@a number_buffer.
|
||||
|
||||
@param[in] x integer number (signed or unsigned) to dump
|
||||
@@ -1343,7 +1439,7 @@ class serializer
|
||||
}
|
||||
|
||||
// use a pointer to fill the buffer
|
||||
auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto)
|
||||
auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||
|
||||
number_unsigned_t abs_value;
|
||||
|
||||
@@ -1397,7 +1493,7 @@ class serializer
|
||||
/*!
|
||||
@brief dump a floating-point number
|
||||
|
||||
Dump a given floating-point number, appending it to @ref write_buffer. Works internally
|
||||
Dump a given floating-point number to output stream @a o. Works internally
|
||||
with @a number_buffer.
|
||||
|
||||
@param[in] x floating-point number to dump
|
||||
@@ -1458,28 +1554,21 @@ class serializer
|
||||
// check if the buffer was large enough
|
||||
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
|
||||
if (thousands_sep != '\0')
|
||||
if (locale.thousands_sep != '\0')
|
||||
{
|
||||
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081
|
||||
const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep);
|
||||
const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, locale.thousands_sep);
|
||||
std::fill(end, number_buffer.end(), '\0');
|
||||
JSON_ASSERT((end - number_buffer.begin()) <= len);
|
||||
len = (end - number_buffer.begin());
|
||||
}
|
||||
|
||||
// convert decimal point to '.'
|
||||
if (decimal_point != '\0' && decimal_point != '.')
|
||||
if (locale.decimal_point != '\0' && locale.decimal_point != '.')
|
||||
{
|
||||
// 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(), decimal_point);
|
||||
const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), locale.decimal_point);
|
||||
if (dec_pos != number_buffer.end())
|
||||
{
|
||||
*dec_pos = '.';
|
||||
@@ -1524,17 +1613,34 @@ class serializer
|
||||
*/
|
||||
number_unsigned_t remove_sign(number_integer_t x) noexcept
|
||||
{
|
||||
JSON_ASSERT(x < 0);
|
||||
JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)()); // NOLINT(misc-redundant-expression)
|
||||
return static_cast<number_unsigned_t>(-(x + 1)) + 1;
|
||||
}
|
||||
|
||||
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)
|
||||
output_adapter_protocol<char>* o = nullptr;
|
||||
|
||||
/// a (hopefully) large enough character 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
|
||||
std::array<char, 512> string_buffer{{}};
|
||||
|
||||
|
||||
@@ -19,17 +19,11 @@ namespace detail
|
||||
/*!
|
||||
@brief the number of nesting levels an operation recurses into
|
||||
|
||||
Operations that walk a value (copying, comparing, serializing, hashing, merging,
|
||||
...) recurse once
|
||||
Operations that walk a value (serializing, hashing, merging, ...) recurse once
|
||||
per nesting level, which is fastest, but a value nested deeply enough would
|
||||
exhaust the call stack. So they recurse only this many levels deep and finish
|
||||
whatever lies below with an explicit stack. All of them share this limit.
|
||||
|
||||
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
|
||||
*/
|
||||
constexpr std::size_t recursion_depth_limit() noexcept
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// | | |__ | | | | | | version 3.12.0
|
||||
// |_____|_____|_____|_|___| 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-License-Identifier: MIT
|
||||
|
||||
@@ -13,7 +12,6 @@
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // uint8_t, uint32_t
|
||||
#include <string> // string, to_string
|
||||
#include <utility> // move
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
@@ -38,71 +36,6 @@ StringType to_string(std::size_t value)
|
||||
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 //
|
||||
///////////////////
|
||||
@@ -118,23 +51,11 @@ This is a single-byte step of a "shift-based" UTF-8 decoder originally
|
||||
written by Björn Hoehrmann. See
|
||||
http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
|
||||
|
||||
The library checks UTF-8 well-formedness (RFC 3629, section 4) in four
|
||||
places, which differ in speed, diagnostics, and how they read the input:
|
||||
|
||||
- decode() and @ref is_valid_utf8 below: the serializer (to escape and, in
|
||||
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.
|
||||
This decoder is the single source of truth for UTF-8 validation in this
|
||||
library: it is used both by the serializer (to escape and, in strict mode,
|
||||
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 time; see @ref is_valid_utf8 below).
|
||||
|
||||
@param[in,out] state the current decoder state
|
||||
@param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT)
|
||||
@@ -212,78 +133,5 @@ inline bool is_valid_utf8(const StringType& s, const std::size_t first = 0) noex
|
||||
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
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
+354
-841
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