mirror of
https://github.com/nlohmann/json.git
synced 2026-10-01 04:00:31 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a46d5216cf | ||
|
|
4eb8618d16 | ||
|
|
6be8c2e986 |
+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
|
interval: daily
|
||||||
cooldown:
|
cooldown:
|
||||||
default-days: 7
|
default-days: 7
|
||||||
ignore:
|
|
||||||
# astyle is deliberately pinned (see tools/astyle/requirements.txt): newer versions
|
|
||||||
# reformat the code, and the pinned version defines the formatting CI enforces
|
|
||||||
- dependency-name: astyle
|
|
||||||
|
|
||||||
- package-ecosystem: pip
|
- package-ecosystem: pip
|
||||||
directory: /tools/generate_natvis
|
directory: /tools/generate_natvis
|
||||||
|
|||||||
@@ -30,6 +30,14 @@ environment:
|
|||||||
CMAKE_OPTIONS: ""
|
CMAKE_OPTIONS: ""
|
||||||
GENERATOR: Visual Studio 14 2015
|
GENERATOR: Visual Studio 14 2015
|
||||||
|
|
||||||
|
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||||
|
configuration: Release
|
||||||
|
platform: x86
|
||||||
|
name: with_win_header
|
||||||
|
CXX_FLAGS: "/W4 /WX"
|
||||||
|
CMAKE_OPTIONS: ""
|
||||||
|
GENERATOR: Visual Studio 14 2015
|
||||||
|
|
||||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||||
configuration: Release
|
configuration: Release
|
||||||
platform: x86
|
platform: x86
|
||||||
@@ -66,6 +74,9 @@ install:
|
|||||||
- if "%platform%"=="x86" set GENERATOR_PLATFORM=Win32
|
- if "%platform%"=="x86" set GENERATOR_PLATFORM=Win32
|
||||||
|
|
||||||
before_build:
|
before_build:
|
||||||
|
# for with_win_header build, inject the inclusion of Windows.h to the single-header library
|
||||||
|
- ps: if ($env:name -Eq "with_win_header") { $header_path = "single_include\nlohmann\json.hpp" }
|
||||||
|
- ps: if ($env:name -Eq "with_win_header") { "#include <Windows.h>`n" + (Get-Content $header_path | Out-String) | Set-Content $header_path }
|
||||||
- cmake . -G "%GENERATOR%" -A "%GENERATOR_PLATFORM%" -DCMAKE_CXX_FLAGS="%CXX_FLAGS%" -DCMAKE_IGNORE_PATH="C:/Program Files/Git/usr/bin" -DJSON_BuildTests=On "%CMAKE_OPTIONS%"
|
- cmake . -G "%GENERATOR%" -A "%GENERATOR_PLATFORM%" -DCMAKE_CXX_FLAGS="%CXX_FLAGS%" -DCMAKE_IGNORE_PATH="C:/Program Files/Git/usr/bin" -DJSON_BuildTests=On "%CMAKE_OPTIONS%"
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ jobs:
|
|||||||
MAIN_DIR: ${{ github.workspace }}/main
|
MAIN_DIR: ${{ github.workspace }}/main
|
||||||
INCLUDE_DIR: ${{ github.workspace }}/main/single_include/nlohmann
|
INCLUDE_DIR: ${{ github.workspace }}/main/single_include/nlohmann
|
||||||
TOOL_DIR: ${{ github.workspace }}/tools/tools/amalgamate
|
TOOL_DIR: ${{ github.workspace }}/tools/tools/amalgamate
|
||||||
NATVIS_TOOL_DIR: ${{ github.workspace }}/tools/tools/generate_natvis
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
@@ -62,48 +61,6 @@ jobs:
|
|||||||
python3 -mvenv venv
|
python3 -mvenv venv
|
||||||
venv/bin/pip3 install -r $MAIN_DIR/tools/astyle/requirements.txt
|
venv/bin/pip3 install -r $MAIN_DIR/tools/astyle/requirements.txt
|
||||||
|
|
||||||
- name: Install generate_natvis dependencies
|
|
||||||
run: pip3 install -r $NATVIS_TOOL_DIR/requirements.txt
|
|
||||||
|
|
||||||
- name: Regenerate the tools/macro_builder tables in macro_scope.hpp
|
|
||||||
run: |
|
|
||||||
cd $MAIN_DIR
|
|
||||||
|
|
||||||
# Built from this PR's own tools/macro_builder/main.cpp, not a
|
|
||||||
# develop checkout: unlike amalgamate.py and generate_natvis.py,
|
|
||||||
# this tool has no other source of truth to check against (its
|
|
||||||
# own README documents that it must reproduce macro_scope.hpp
|
|
||||||
# byte for byte), so there is nothing to gain from checking out a
|
|
||||||
# separate copy, and doing so would make this step fail on a PR
|
|
||||||
# that adds support for a new dispatch table until that PR itself
|
|
||||||
# merges to develop, the same way generate_natvis.py's --version
|
|
||||||
# requirement briefly did.
|
|
||||||
TMPDIR=$(mktemp -d ./macro_builder_check.XXXXXX)
|
|
||||||
c++ -std=c++11 tools/macro_builder/main.cpp -o "$TMPDIR/macro_builder"
|
|
||||||
"$TMPDIR/macro_builder" > "$TMPDIR/paste.hpp"
|
|
||||||
"$TMPDIR/macro_builder" type_body > "$TMPDIR/type_body.hpp"
|
|
||||||
|
|
||||||
# Splice the (still unindented) generated blocks back into
|
|
||||||
# macro_scope.hpp; the astyle pass below indents their
|
|
||||||
# continuation lines the same way it does for the rest of
|
|
||||||
# include/, so a correctly regenerated file comes out unchanged.
|
|
||||||
awk -v newfile="$TMPDIR/paste.hpp" '
|
|
||||||
BEGIN { while ((getline line < newfile) > 0) { new = new line "\n" } }
|
|
||||||
/^#define NLOHMANN_JSON_EXPAND\( x \) x$/ { printf "%s", new; skip=1 }
|
|
||||||
skip && /^#define NLOHMANN_JSON_DOUBLE_PASTE63\(/ { skip=0; next }
|
|
||||||
skip { next }
|
|
||||||
{ print }
|
|
||||||
' include/nlohmann/detail/macro_scope.hpp > "$TMPDIR/macro_scope_1.hpp"
|
|
||||||
awk -v newfile="$TMPDIR/type_body.hpp" '
|
|
||||||
BEGIN { while ((getline line < newfile) > 0) { new = new line "\n" } }
|
|
||||||
/^#define NLOHMANN_JSON_TYPE_BODY\(Prefix, \.\.\.\)/ { printf "%s", new; skip=1 }
|
|
||||||
skip && /^[[:space:]]*NLOHMANN_JSON_TYPE_BODY_SENTINEL\)\)$/ { skip=0; next }
|
|
||||||
skip { next }
|
|
||||||
{ print }
|
|
||||||
' "$TMPDIR/macro_scope_1.hpp" > "$TMPDIR/macro_scope_2.hpp"
|
|
||||||
mv "$TMPDIR/macro_scope_2.hpp" include/nlohmann/detail/macro_scope.hpp
|
|
||||||
rm -rf "$TMPDIR"
|
|
||||||
|
|
||||||
- name: Regenerate amalgamation, formatting, and BUILD.bazel
|
- name: Regenerate amalgamation, formatting, and BUILD.bazel
|
||||||
run: |
|
run: |
|
||||||
cd $MAIN_DIR
|
cd $MAIN_DIR
|
||||||
@@ -131,19 +88,6 @@ jobs:
|
|||||||
${{ github.workspace }}/venv/bin/astyle --project=tools/astyle/.astylerc --suffix=none --quiet \
|
${{ github.workspace }}/venv/bin/astyle --project=tools/astyle/.astylerc --suffix=none --quiet \
|
||||||
$(find $SOURCE_DIRS -type f \( -name '*.hpp' -o -name '*.cpp' -o -name '*.cu' \) -not -path 'tests/thirdparty/*' -not -path 'tests/abi/include/nlohmann/*' | sort)
|
$(find $SOURCE_DIRS -type f \( -name '*.hpp' -o -name '*.cpp' -o -name '*.cu' \) -not -path 'tests/thirdparty/*' -not -path 'tests/abi/include/nlohmann/*' | sort)
|
||||||
|
|
||||||
- name: Regenerate nlohmann_json.natvis
|
|
||||||
run: |
|
|
||||||
cd $MAIN_DIR
|
|
||||||
# Pass --version explicitly so this step also works with the tool
|
|
||||||
# copy from develop before this repository's own generate_natvis.py
|
|
||||||
# learns to derive the version itself: the older script requires
|
|
||||||
# --version, and the newer one accepts it as an explicit override.
|
|
||||||
ABI_MACROS=include/nlohmann/detail/abi_macros.hpp
|
|
||||||
VERSION_MAJOR=$(grep -m1 'define NLOHMANN_JSON_VERSION_MAJOR' $ABI_MACROS | grep -o '[0-9]\+')
|
|
||||||
VERSION_MINOR=$(grep -m1 'define NLOHMANN_JSON_VERSION_MINOR' $ABI_MACROS | grep -o '[0-9]\+')
|
|
||||||
VERSION_PATCH=$(grep -m1 'define NLOHMANN_JSON_VERSION_PATCH' $ABI_MACROS | grep -o '[0-9]\+')
|
|
||||||
python3 $NATVIS_TOOL_DIR/generate_natvis.py --version "$VERSION_MAJOR.$VERSION_MINOR.$VERSION_PATCH" $MAIN_DIR
|
|
||||||
|
|
||||||
- name: Build patch and check for differences
|
- name: Build patch and check for differences
|
||||||
id: diff
|
id: diff
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -7,16 +7,6 @@ on:
|
|||||||
- develop
|
- develop
|
||||||
paths:
|
paths:
|
||||||
- docs/mkdocs/**
|
- docs/mkdocs/**
|
||||||
# the site also embeds these files via pymdownx.snippets
|
|
||||||
# (mkdocs.yml sets restrict_base_path: false for this)
|
|
||||||
- .clang-tidy
|
|
||||||
- .github/CODE_OF_CONDUCT.md
|
|
||||||
- .github/CONTRIBUTING.md
|
|
||||||
- .github/SECURITY.md
|
|
||||||
- cmake/clang_flags.cmake
|
|
||||||
- cmake/gcc_flags.cmake
|
|
||||||
- tests/fmt_formatter/project/main.cpp
|
|
||||||
- tools/astyle/.astylerc
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
# we don't want to have concurrent jobs, and we don't want to cancel running jobs to avoid broken publications
|
# we don't want to have concurrent jobs, and we don't want to cancel running jobs to avoid broken publications
|
||||||
@@ -33,7 +23,7 @@ jobs:
|
|||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
if: github.repository == 'nlohmann/json'
|
if: github.repository == 'nlohmann/json'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
||||||
@@ -41,8 +31,6 @@ jobs:
|
|||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Install virtual environment
|
- name: Install virtual environment
|
||||||
run: make install_venv -C docs/mkdocs
|
run: make install_venv -C docs/mkdocs
|
||||||
|
|||||||
@@ -52,13 +52,6 @@ jobs:
|
|||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build --target ci_infer
|
run: cmake --build build --target ci_infer
|
||||||
- name: Archive Infer report
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
||||||
with:
|
|
||||||
name: infer-report
|
|
||||||
path: ${{ github.workspace }}/build/build_infer/infer-out/report.txt
|
|
||||||
if-no-files-found: ignore
|
|
||||||
|
|
||||||
ci_static_analysis_ubuntu:
|
ci_static_analysis_ubuntu:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -104,13 +97,13 @@ jobs:
|
|||||||
|
|
||||||
ci_cmake_options:
|
ci_cmake_options:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: ubuntu:24.04
|
container: ubuntu:focal
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
target: [ci_cmake_flags, ci_test_diagnostics, ci_test_diagnostic_positions, ci_test_noexceptions, ci_test_noimplicitconversions, ci_test_legacycomparison, ci_test_noglobaludls, ci_test_disableenumserialization, ci_test_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:
|
steps:
|
||||||
- name: Install build-essential
|
- 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
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
@@ -353,8 +346,6 @@ jobs:
|
|||||||
container: intel/oneapi-hpckit:latest
|
container: intel/oneapi-hpckit:latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
@@ -367,8 +358,6 @@ jobs:
|
|||||||
container: nvcr.io/nvidia/nvhpc:25.5-devel-cuda12.9-ubuntu22.04
|
container: nvcr.io/nvidia/nvhpc:25.5-devel-cuda12.9-ubuntu22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
@@ -414,21 +403,3 @@ jobs:
|
|||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build --target ${{ matrix.target }}
|
run: cmake --build build --target ${{ matrix.target }}
|
||||||
|
|
||||||
# Native Linux ARM64 runner with the latest GCC. The ubuntu-24.04-arm label is
|
|
||||||
# only available for public repositories.
|
|
||||||
ci_test_arm64:
|
|
||||||
runs-on: ubuntu-24.04-arm
|
|
||||||
container: gcc:latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Get latest CMake and ninja
|
|
||||||
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
|
||||||
- name: Check that the job runs natively on arm64
|
|
||||||
run: uname -m && test "$(uname -m)" = aarch64
|
|
||||||
- name: Run CMake
|
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
|
||||||
- name: Build
|
|
||||||
run: cmake --build build --target ci_test_compiler_default
|
|
||||||
|
|||||||
@@ -87,8 +87,6 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
- name: Set extra CXX_FLAGS for latest std_version
|
- name: Set extra CXX_FLAGS for latest std_version
|
||||||
@@ -125,8 +123,6 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Run CMake (Release)
|
- name: Run CMake (Release)
|
||||||
run: cmake -S . -B build -G "Visual Studio 18 2026" -A ARM64 -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/W4 /WX"
|
run: cmake -S . -B build -G "Visual Studio 18 2026" -A ARM64 -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/W4 /WX"
|
||||||
if: matrix.build_type == 'Release'
|
if: matrix.build_type == 'Release'
|
||||||
|
|||||||
@@ -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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -55,7 +55,6 @@ option(JSON_Diagnostic_Positions "Enable diagnostic positions." OFF)
|
|||||||
option(JSON_GlobalUDLs "Place user-defined string literals in the global namespace." ON)
|
option(JSON_GlobalUDLs "Place user-defined string literals in the global namespace." ON)
|
||||||
option(JSON_ImplicitConversions "Enable implicit conversions." ON)
|
option(JSON_ImplicitConversions "Enable implicit conversions." ON)
|
||||||
option(JSON_DisableEnumSerialization "Disable default integer enum serialization." OFF)
|
option(JSON_DisableEnumSerialization "Disable default integer enum serialization." OFF)
|
||||||
option(JSON_DisableTupleReferenceConversion "Disable conversion from a one-element tuple of a JSON reference." OFF)
|
|
||||||
option(JSON_LegacyDiscardedValueComparison "Enable legacy discarded value comparison." OFF)
|
option(JSON_LegacyDiscardedValueComparison "Enable legacy discarded value comparison." OFF)
|
||||||
option(JSON_Install "Install CMake targets during install step." ${MAIN_PROJECT})
|
option(JSON_Install "Install CMake targets during install step." ${MAIN_PROJECT})
|
||||||
option(JSON_MultipleHeaders "Use non-amalgamated version of the library." ON)
|
option(JSON_MultipleHeaders "Use non-amalgamated version of the library." ON)
|
||||||
@@ -102,10 +101,6 @@ if (JSON_DisableEnumSerialization)
|
|||||||
message(STATUS "Enum integer serialization is disabled (JSON_DISABLE_ENUM_SERIALIZATION=1)")
|
message(STATUS "Enum integer serialization is disabled (JSON_DISABLE_ENUM_SERIALIZATION=1)")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (JSON_DisableTupleReferenceConversion)
|
|
||||||
message(STATUS "Tuple reference conversion is disabled (JSON_DISABLE_TUPLE_REFERENCE_CONVERSION=1)")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (JSON_LegacyDiscardedValueComparison)
|
if (JSON_LegacyDiscardedValueComparison)
|
||||||
message(STATUS "Legacy discarded value comparison enabled (JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1)")
|
message(STATUS "Legacy discarded value comparison enabled (JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1)")
|
||||||
endif()
|
endif()
|
||||||
@@ -148,7 +143,6 @@ target_compile_definitions(
|
|||||||
$<$<NOT:$<BOOL:${JSON_GlobalUDLs}>>:JSON_USE_GLOBAL_UDLS=0>
|
$<$<NOT:$<BOOL:${JSON_GlobalUDLs}>>:JSON_USE_GLOBAL_UDLS=0>
|
||||||
$<$<NOT:$<BOOL:${JSON_ImplicitConversions}>>:JSON_USE_IMPLICIT_CONVERSIONS=0>
|
$<$<NOT:$<BOOL:${JSON_ImplicitConversions}>>:JSON_USE_IMPLICIT_CONVERSIONS=0>
|
||||||
$<$<BOOL:${JSON_DisableEnumSerialization}>:JSON_DISABLE_ENUM_SERIALIZATION=1>
|
$<$<BOOL:${JSON_DisableEnumSerialization}>:JSON_DISABLE_ENUM_SERIALIZATION=1>
|
||||||
$<$<BOOL:${JSON_DisableTupleReferenceConversion}>:JSON_DISABLE_TUPLE_REFERENCE_CONVERSION=1>
|
|
||||||
$<$<BOOL:${JSON_Diagnostics}>:JSON_DIAGNOSTICS=1>
|
$<$<BOOL:${JSON_Diagnostics}>:JSON_DIAGNOSTICS=1>
|
||||||
$<$<BOOL:${JSON_Diagnostic_Positions}>:JSON_DIAGNOSTIC_POSITIONS=1>
|
$<$<BOOL:${JSON_Diagnostic_Positions}>:JSON_DIAGNOSTIC_POSITIONS=1>
|
||||||
$<$<BOOL:${JSON_LegacyDiscardedValueComparison}>:JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1>
|
$<$<BOOL:${JSON_LegacyDiscardedValueComparison}>:JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1>
|
||||||
|
|||||||
@@ -33,6 +33,17 @@ Further documentation:
|
|||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> The folder `.github/workflows` is predetermined by GitHub.
|
> The folder `.github/workflows` is predetermined by GitHub.
|
||||||
|
|
||||||
|
### `.cirrus.yml`
|
||||||
|
|
||||||
|
Configuration file for the pipeline at [Cirrus CI](https://cirrus-ci.com/github/nlohmann/json).
|
||||||
|
|
||||||
|
Further documentation:
|
||||||
|
|
||||||
|
- [Writing tasks](https://cirrus-ci.org/guide/writing-tasks/)
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> The filename `.cirrus.yml` and position (root of the repository) are predetermined by Cirrus CI.
|
||||||
|
|
||||||
### `.github/external_ci/appveyor.yml`
|
### `.github/external_ci/appveyor.yml`
|
||||||
|
|
||||||
Configuration for the pipelines at [AppVeyor](https://ci.appveyor.com/project/nlohmann/json).
|
Configuration for the pipelines at [AppVeyor](https://ci.appveyor.com/project/nlohmann/json).
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.PHONY: pretty clean ChangeLog.md release update_hedley update_hedley_undef BUILD.bazel natvis macro_builder_check
|
.PHONY: pretty clean ChangeLog.md release update_hedley update_hedley_undef BUILD.bazel
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# configuration
|
# configuration
|
||||||
@@ -24,9 +24,6 @@ AMALGAMATED_FWD_FILE=single_include/nlohmann/json_fwd.hpp
|
|||||||
# json_literals.hpp only includes <nlohmann/json.hpp>, so it is copied verbatim
|
# json_literals.hpp only includes <nlohmann/json.hpp>, so it is copied verbatim
|
||||||
AMALGAMATED_LITERALS_FILE=single_include/nlohmann/json_literals.hpp
|
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
|
|
||||||
|
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# documentation of the Makefile's targets
|
# documentation of the Makefile's targets
|
||||||
@@ -39,9 +36,13 @@ all:
|
|||||||
@echo "ChangeLog.md - generate ChangeLog file"
|
@echo "ChangeLog.md - generate ChangeLog file"
|
||||||
@echo "check-amalgamation - check whether sources have been amalgamated and BUILD.bazel is up to date"
|
@echo "check-amalgamation - check whether sources have been amalgamated and BUILD.bazel is up to date"
|
||||||
@echo "clean - remove built files"
|
@echo "clean - remove built files"
|
||||||
@echo "fuzzing - see tests/fuzzing.md for how to build and run the fuzzers"
|
@echo "doctest - compile example files and check their output"
|
||||||
@echo "macro_builder_check - check that macro_scope.hpp matches tools/macro_builder's output"
|
@echo "fuzz_testing - prepare fuzz testing of the JSON parser"
|
||||||
@echo "natvis - regenerate nlohmann_json.natvis from the current ABI tags and version"
|
@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 "pretty - beautify code with Artistic Style"
|
||||||
@echo "run_benchmarks - build and run benchmarks"
|
@echo "run_benchmarks - build and run benchmarks"
|
||||||
@echo "update_hedley - download Hedley and regenerate hedley.hpp / hedley_undef.hpp"
|
@echo "update_hedley - download Hedley and regenerate hedley.hpp / hedley_undef.hpp"
|
||||||
@@ -60,6 +61,74 @@ run_benchmarks:
|
|||||||
cd cmake-build-benchmarks ; ./json_benchmarks
|
cd cmake-build-benchmarks ; ./json_benchmarks
|
||||||
|
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# fuzzing
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
# the overall fuzz testing target
|
||||||
|
fuzz_testing:
|
||||||
|
rm -fr fuzz-testing
|
||||||
|
mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out
|
||||||
|
$(MAKE) parse_afl_fuzzer -C tests CXX=afl-clang++
|
||||||
|
mv tests/parse_afl_fuzzer fuzz-testing/fuzzer
|
||||||
|
find tests/data/json_tests -size -5k -name *json | xargs -I{} cp "{}" fuzz-testing/testcases
|
||||||
|
@echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer"
|
||||||
|
|
||||||
|
fuzz_testing_bon8:
|
||||||
|
rm -fr fuzz-testing
|
||||||
|
mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out
|
||||||
|
$(MAKE) parse_bon8_fuzzer -C tests CXX=afl-clang++
|
||||||
|
mv tests/parse_bon8_fuzzer fuzz-testing/fuzzer
|
||||||
|
find tests/data -size -5k -name *.bon8 | xargs -I{} cp "{}" fuzz-testing/testcases
|
||||||
|
@echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer"
|
||||||
|
|
||||||
|
fuzz_testing_bson:
|
||||||
|
rm -fr fuzz-testing
|
||||||
|
mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out
|
||||||
|
$(MAKE) parse_bson_fuzzer -C tests CXX=afl-clang++
|
||||||
|
mv tests/parse_bson_fuzzer fuzz-testing/fuzzer
|
||||||
|
find tests/data -size -5k -name *.bson | xargs -I{} cp "{}" fuzz-testing/testcases
|
||||||
|
@echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer"
|
||||||
|
|
||||||
|
fuzz_testing_cbor:
|
||||||
|
rm -fr fuzz-testing
|
||||||
|
mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out
|
||||||
|
$(MAKE) parse_cbor_fuzzer -C tests CXX=afl-clang++
|
||||||
|
mv tests/parse_cbor_fuzzer fuzz-testing/fuzzer
|
||||||
|
find tests/data -size -5k -name *.cbor | xargs -I{} cp "{}" fuzz-testing/testcases
|
||||||
|
@echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer"
|
||||||
|
|
||||||
|
fuzz_testing_msgpack:
|
||||||
|
rm -fr fuzz-testing
|
||||||
|
mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out
|
||||||
|
$(MAKE) parse_msgpack_fuzzer -C tests CXX=afl-clang++
|
||||||
|
mv tests/parse_msgpack_fuzzer fuzz-testing/fuzzer
|
||||||
|
find tests/data -size -5k -name *.msgpack | xargs -I{} cp "{}" fuzz-testing/testcases
|
||||||
|
@echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer"
|
||||||
|
|
||||||
|
fuzz_testing_ubjson:
|
||||||
|
rm -fr fuzz-testing
|
||||||
|
mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out
|
||||||
|
$(MAKE) parse_ubjson_fuzzer -C tests CXX=afl-clang++
|
||||||
|
mv tests/parse_ubjson_fuzzer fuzz-testing/fuzzer
|
||||||
|
find tests/data -size -5k -name *.ubjson | xargs -I{} cp "{}" fuzz-testing/testcases
|
||||||
|
@echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer"
|
||||||
|
|
||||||
|
fuzzing-start:
|
||||||
|
afl-fuzz -S fuzzer1 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null &
|
||||||
|
afl-fuzz -S fuzzer2 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null &
|
||||||
|
afl-fuzz -S fuzzer3 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null &
|
||||||
|
afl-fuzz -S fuzzer4 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null &
|
||||||
|
afl-fuzz -S fuzzer5 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null &
|
||||||
|
afl-fuzz -S fuzzer6 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null &
|
||||||
|
afl-fuzz -S fuzzer7 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null &
|
||||||
|
afl-fuzz -M fuzzer0 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer
|
||||||
|
|
||||||
|
fuzzing-stop:
|
||||||
|
-killall fuzzer
|
||||||
|
-killall afl-fuzz
|
||||||
|
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# Static analysis
|
# Static analysis
|
||||||
##########################################################################
|
##########################################################################
|
||||||
@@ -87,7 +156,11 @@ install_astyle:
|
|||||||
|
|
||||||
# call the Artistic Style pretty printer on all source files
|
# call the Artistic Style pretty printer on all source files
|
||||||
pretty: install_astyle
|
pretty: install_astyle
|
||||||
$(ASTYLE) --project=tools/astyle/.astylerc $(SRCS) $(TESTS_SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(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) $(AMALGAMATED_LITERALS_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
|
# create single header files and pretty print
|
||||||
amalgamate: $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE)
|
amalgamate: $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE)
|
||||||
@@ -105,26 +178,8 @@ $(AMALGAMATED_FWD_FILE): $(SRCS)
|
|||||||
$(AMALGAMATED_LITERALS_FILE): include/nlohmann/json_literals.hpp
|
$(AMALGAMATED_LITERALS_FILE): include/nlohmann/json_literals.hpp
|
||||||
cp include/nlohmann/json_literals.hpp $(AMALGAMATED_LITERALS_FILE)
|
cp include/nlohmann/json_literals.hpp $(AMALGAMATED_LITERALS_FILE)
|
||||||
|
|
||||||
# regenerate nlohmann_json.natvis from the ABI tags and version in include/nlohmann/detail/abi_macros.hpp
|
|
||||||
natvis:
|
|
||||||
python3 tools/generate_natvis/generate_natvis.py .
|
|
||||||
|
|
||||||
# regenerate the two tools/macro_builder blocks of $(MACRO_SCOPE_HPP) (see its README.md) and diff against the
|
|
||||||
# checked-in header; phony, because it never writes $(MACRO_SCOPE_HPP) itself
|
|
||||||
macro_builder_check:
|
|
||||||
@set -e; \
|
|
||||||
TMPDIR=$$(mktemp -d ./macro_builder_check.XXXXXX); \
|
|
||||||
trap 'rm -rf "$$TMPDIR"' EXIT; \
|
|
||||||
$(CXX) -std=c++11 tools/macro_builder/main.cpp -o "$$TMPDIR/macro_builder"; \
|
|
||||||
"$$TMPDIR/macro_builder" > "$$TMPDIR/paste.hpp"; \
|
|
||||||
"$$TMPDIR/macro_builder" type_body > "$$TMPDIR/type_body.hpp"; \
|
|
||||||
$(ASTYLE) --project=tools/astyle/.astylerc --suffix=none --quiet "$$TMPDIR/paste.hpp" "$$TMPDIR/type_body.hpp"; \
|
|
||||||
sed -n '/^#define NLOHMANN_JSON_EXPAND( x ) x$$/,/^#define NLOHMANN_JSON_DOUBLE_PASTE63(/p' $(MACRO_SCOPE_HPP) > "$$TMPDIR/paste_actual.hpp"; \
|
|
||||||
sed -n '/^#define NLOHMANN_JSON_TYPE_BODY(Prefix, \.\.\.)/,/^ NLOHMANN_JSON_TYPE_BODY_SENTINEL))$$/p' $(MACRO_SCOPE_HPP) > "$$TMPDIR/type_body_actual.hpp"; \
|
|
||||||
diff "$$TMPDIR/paste.hpp" "$$TMPDIR/paste_actual.hpp" || (echo "===================================================================\n $(MACRO_SCOPE_HPP) (NLOHMANN_JSON_EXPAND..NLOHMANN_JSON_DOUBLE_PASTE63) is out of date!\n Regenerate it, see tools/macro_builder/README.md.\n===================================================================" ; exit 1); \
|
|
||||||
diff "$$TMPDIR/type_body.hpp" "$$TMPDIR/type_body_actual.hpp" || (echo "===================================================================\n $(MACRO_SCOPE_HPP) (NLOHMANN_JSON_TYPE_BODY) is out of date!\n Regenerate it, see tools/macro_builder/README.md.\n===================================================================" ; exit 1)
|
|
||||||
|
|
||||||
# check if file single_include/nlohmann/json.hpp has been amalgamated from the nlohmann sources
|
# check if file single_include/nlohmann/json.hpp has been amalgamated from the nlohmann sources
|
||||||
|
# Note: this target is called by Travis
|
||||||
check-amalgamation:
|
check-amalgamation:
|
||||||
@mv $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~
|
@mv $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~
|
||||||
@mv $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_FWD_FILE)~
|
@mv $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_FWD_FILE)~
|
||||||
@@ -140,11 +195,6 @@ check-amalgamation:
|
|||||||
@$(MAKE) BUILD.bazel
|
@$(MAKE) BUILD.bazel
|
||||||
@diff BUILD.bazel BUILD.bazel~ || (echo "===================================================================\n BUILD.bazel is out of date! Please run 'make BUILD.bazel'.\n===================================================================" ; mv BUILD.bazel~ BUILD.bazel ; false)
|
@diff BUILD.bazel BUILD.bazel~ || (echo "===================================================================\n BUILD.bazel is out of date! Please run 'make BUILD.bazel'.\n===================================================================" ; mv BUILD.bazel~ BUILD.bazel ; false)
|
||||||
@mv BUILD.bazel~ BUILD.bazel
|
@mv BUILD.bazel~ BUILD.bazel
|
||||||
@mv nlohmann_json.natvis nlohmann_json.natvis~
|
|
||||||
@$(MAKE) natvis
|
|
||||||
@diff nlohmann_json.natvis nlohmann_json.natvis~ || (echo "===================================================================\n nlohmann_json.natvis is out of date! Please run 'make natvis'.\n===================================================================" ; mv nlohmann_json.natvis~ nlohmann_json.natvis ; false)
|
|
||||||
@mv nlohmann_json.natvis~ nlohmann_json.natvis
|
|
||||||
@$(MAKE) macro_builder_check
|
|
||||||
|
|
||||||
# generate the Bazel BUILD file; phony, because a removed header would not trigger a rebuild
|
# generate the Bazel BUILD file; phony, because a removed header would not trigger a rebuild
|
||||||
BUILD.bazel:
|
BUILD.bazel:
|
||||||
@@ -196,7 +246,7 @@ release: include.zip json.tar.xz
|
|||||||
cp $(AMALGAMATED_FWD_FILE) release_files
|
cp $(AMALGAMATED_FWD_FILE) release_files
|
||||||
cp $(AMALGAMATED_LITERALS_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
|
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
|
cd release_files ; shasum -a 256 json.hpp include.zip json.tar.xz > hashes.txt
|
||||||
|
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
@@ -206,6 +256,7 @@ release: include.zip json.tar.xz
|
|||||||
# clean up
|
# clean up
|
||||||
clean:
|
clean:
|
||||||
rm -fr fuzz fuzz-testing *.dSYM tests/*.dSYM
|
rm -fr fuzz fuzz-testing *.dSYM tests/*.dSYM
|
||||||
|
rm -fr benchmarks/files/numbers/*.json
|
||||||
rm -fr cmake-build-benchmarks fuzz-testing cmake-build-pvs-studio release_files
|
rm -fr cmake-build-benchmarks fuzz-testing cmake-build-pvs-studio release_files
|
||||||
$(MAKE) clean -Cdocs
|
$(MAKE) clean -Cdocs
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
[](https://coveralls.io/github/nlohmann/json?branch=develop)
|
[](https://coveralls.io/github/nlohmann/json?branch=develop)
|
||||||
[](https://scan.coverity.com/projects/nlohmann-json)
|
[](https://scan.coverity.com/projects/nlohmann-json)
|
||||||
[](https://app.codacy.com/gh/nlohmann/json/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
|
[](https://app.codacy.com/gh/nlohmann/json/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
|
||||||
|
[](https://cirrus-ci.com/github/nlohmann/json)
|
||||||
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:json)
|
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:json)
|
||||||
[](https://wandbox.org/permlink/1mp10JbaANo6FUc7)
|
[](https://wandbox.org/permlink/1mp10JbaANo6FUc7)
|
||||||
[](https://json.nlohmann.me)
|
[](https://json.nlohmann.me)
|
||||||
|
|||||||
+94
-133
@@ -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}")
|
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" IWYU_TOOL_VERSION "${IWYU_TOOL_VERSION}")
|
||||||
message(STATUS "🔖 include-what-you-use ${IWYU_TOOL_VERSION} (${IWYU_TOOL})")
|
message(STATUS "🔖 include-what-you-use ${IWYU_TOOL_VERSION} (${IWYU_TOOL})")
|
||||||
|
|
||||||
# CMake's CXX_INCLUDE_WHAT_YOU_USE launcher runs IWYU during the normal compile step (useful to see
|
|
||||||
# diagnostics inline), but CMake's own __run_co_compile wrapper does not propagate the launched
|
|
||||||
# tool's exit code to the build, so IWYU's own "-Xiwyu --error" cannot fail that step (verified: a
|
|
||||||
# deliberately-unused #include in a header still lets `cmake --build` finish with exit code 0).
|
|
||||||
# iwyu_tool.py, which ships with IWYU, reads compile_commands.json and does return a non-zero exit
|
|
||||||
# code for any analyzed file with findings; ci_single_binaries uses it to actually fail on findings.
|
|
||||||
find_program(IWYU_TOOL_PY NAMES iwyu_tool iwyu_tool.py iwyu-tool)
|
|
||||||
|
|
||||||
find_program(INFER_TOOL NAMES infer)
|
find_program(INFER_TOOL NAMES infer)
|
||||||
execute_process(COMMAND ${INFER_TOOL} --version OUTPUT_VARIABLE INFER_TOOL_VERSION ERROR_VARIABLE INFER_TOOL_VERSION)
|
execute_process(COMMAND ${INFER_TOOL} --version OUTPUT_VARIABLE INFER_TOOL_VERSION ERROR_VARIABLE INFER_TOOL_VERSION)
|
||||||
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" INFER_TOOL_VERSION "${INFER_TOOL_VERSION}")
|
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" INFER_TOOL_VERSION "${INFER_TOOL_VERSION}")
|
||||||
@@ -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}")
|
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" NINJA_TOOL_VERSION "${NINJA_TOOL_VERSION}")
|
||||||
message(STATUS "🔖 Ninja ${NINJA_TOOL_VERSION} (${NINJA_TOOL})")
|
message(STATUS "🔖 Ninja ${NINJA_TOOL_VERSION} (${NINJA_TOOL})")
|
||||||
|
|
||||||
|
find_program(OCLINT_TOOL NAMES oclint-json-compilation-database)
|
||||||
|
find_program(OCLINT_VERSION_TOOL NAMES oclint)
|
||||||
|
execute_process(COMMAND ${OCLINT_VERSION_TOOL} --version OUTPUT_VARIABLE OCLINT_TOOL_VERSION ERROR_VARIABLE OCLINT_TOOL_VERSION)
|
||||||
|
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" OCLINT_TOOL_VERSION "${OCLINT_TOOL_VERSION}")
|
||||||
|
message(STATUS "🔖 OCLint ${OCLINT_TOOL_VERSION} (${OCLINT_TOOL})")
|
||||||
|
|
||||||
find_program(VALGRIND_TOOL NAMES valgrind)
|
find_program(VALGRIND_TOOL NAMES valgrind)
|
||||||
execute_process(COMMAND ${VALGRIND_TOOL} --version OUTPUT_VARIABLE VALGRIND_TOOL_VERSION ERROR_VARIABLE VALGRIND_TOOL_VERSION)
|
execute_process(COMMAND ${VALGRIND_TOOL} --version OUTPUT_VARIABLE VALGRIND_TOOL_VERSION ERROR_VARIABLE VALGRIND_TOOL_VERSION)
|
||||||
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" VALGRIND_TOOL_VERSION "${VALGRIND_TOOL_VERSION}")
|
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" VALGRIND_TOOL_VERSION "${VALGRIND_TOOL_VERSION}")
|
||||||
@@ -130,16 +128,16 @@ foreach(CXX_STANDARD 11 14 17 20 23 26)
|
|||||||
COMMENT "Compile and test with Clang for C++${CXX_STANDARD}"
|
COMMENT "Compile and test with Clang for C++${CXX_STANDARD}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# pass -stdlib=libc++ through CXXFLAGS: an explicit -DCMAKE_CXX_FLAGS would make CMake ignore CXXFLAGS
|
|
||||||
add_custom_target(ci_test_clang_libcxx_cxx${CXX_STANDARD}
|
add_custom_target(ci_test_clang_libcxx_cxx${CXX_STANDARD}
|
||||||
COMMAND CXX=${CLANG_TOOL} CXXFLAGS="${CLANG_CXXFLAGS};-stdlib=libc++" ${CMAKE_COMMAND}
|
COMMAND CXX=${CLANG_TOOL} CXXFLAGS="${CLANG_CXXFLAGS}" ${CMAKE_COMMAND}
|
||||||
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
||||||
-DJSON_BuildTests=ON -DJSON_FastTests=ON
|
-DJSON_BuildTests=ON -DJSON_FastTests=ON
|
||||||
-DJSON_TestStandards=${CXX_STANDARD}
|
-DJSON_TestStandards=${CXX_STANDARD}
|
||||||
|
-DCMAKE_CXX_FLAGS="-stdlib=libc++"
|
||||||
-DCMAKE_EXE_LINKER_FLAGS="-lc++abi"
|
-DCMAKE_EXE_LINKER_FLAGS="-lc++abi"
|
||||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_clang_libcxx_cxx${CXX_STANDARD}
|
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_clang_cxx${CXX_STANDARD}
|
||||||
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_clang_libcxx_cxx${CXX_STANDARD}
|
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_clang_cxx${CXX_STANDARD}
|
||||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_clang_libcxx_cxx${CXX_STANDARD} && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
|
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++)"
|
COMMENT "Compile and test with Clang for C++${CXX_STANDARD} (libc++)"
|
||||||
)
|
)
|
||||||
endforeach()
|
endforeach()
|
||||||
@@ -278,20 +276,6 @@ add_custom_target(ci_test_disableenumserialization
|
|||||||
COMMENT "Compile and test with enum serialization disabled"
|
COMMENT "Compile and test with enum serialization disabled"
|
||||||
)
|
)
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Disable conversion from a one-element tuple of a JSON reference.
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
add_custom_target(ci_test_disabletuplereferenceconversion
|
|
||||||
COMMAND ${CMAKE_COMMAND}
|
|
||||||
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
|
||||||
-DJSON_BuildTests=ON -DJSON_FastTests=ON -DJSON_DisableTupleReferenceConversion=ON
|
|
||||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_disabletuplereferenceconversion
|
|
||||||
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_disabletuplereferenceconversion
|
|
||||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_disabletuplereferenceconversion && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
|
|
||||||
COMMENT "Compile and test with tuple reference conversion disabled"
|
|
||||||
)
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Skip the multiple-inclusion library version check.
|
# Skip the multiple-inclusion library version check.
|
||||||
###############################################################################
|
###############################################################################
|
||||||
@@ -378,20 +362,13 @@ add_custom_target(ci_test_clang_sanitizer
|
|||||||
# Check if header is amalgamated and sources are properly indented.
|
# Check if header is amalgamated and sources are properly indented.
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
# Same file set as .github/workflows/check_amalgamation.yml, so a direct push to develop/master/release/*
|
|
||||||
# (which only this CMake target checks, not the pull_request-only workflow) is held to the same standard.
|
|
||||||
file(GLOB_RECURSE INDENT_FILES
|
file(GLOB_RECURSE INDENT_FILES
|
||||||
${PROJECT_SOURCE_DIR}/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/*.cpp
|
||||||
${PROJECT_SOURCE_DIR}/docs/mkdocs/docs/examples/*.cu
|
|
||||||
${PROJECT_SOURCE_DIR}/include/*.hpp
|
|
||||||
${PROJECT_SOURCE_DIR}/include/*.cpp
|
|
||||||
${PROJECT_SOURCE_DIR}/include/*.cu
|
|
||||||
${PROJECT_SOURCE_DIR}/tests/*.hpp
|
|
||||||
${PROJECT_SOURCE_DIR}/tests/*.cpp
|
|
||||||
${PROJECT_SOURCE_DIR}/tests/*.cu
|
|
||||||
)
|
)
|
||||||
list(FILTER INDENT_FILES EXCLUDE REGEX "/tests/thirdparty/|/tests/abi/include/nlohmann/")
|
|
||||||
|
|
||||||
set(include_dir ${PROJECT_SOURCE_DIR}/single_include/nlohmann)
|
set(include_dir ${PROJECT_SOURCE_DIR}/single_include/nlohmann)
|
||||||
set(tool_dir ${PROJECT_SOURCE_DIR}/tools/amalgamate)
|
set(tool_dir ${PROJECT_SOURCE_DIR}/tools/amalgamate)
|
||||||
@@ -400,7 +377,6 @@ add_custom_target(ci_test_amalgamation
|
|||||||
COMMAND cp ${include_dir}/json.hpp ${include_dir}/json.hpp~
|
COMMAND cp ${include_dir}/json.hpp ${include_dir}/json.hpp~
|
||||||
COMMAND cp ${include_dir}/json_fwd.hpp ${include_dir}/json_fwd.hpp~
|
COMMAND cp ${include_dir}/json_fwd.hpp ${include_dir}/json_fwd.hpp~
|
||||||
COMMAND cp ${include_dir}/json_literals.hpp ${include_dir}/json_literals.hpp~
|
COMMAND cp ${include_dir}/json_literals.hpp ${include_dir}/json_literals.hpp~
|
||||||
COMMAND cp ${PROJECT_SOURCE_DIR}/BUILD.bazel ${PROJECT_SOURCE_DIR}/BUILD.bazel~
|
|
||||||
|
|
||||||
COMMAND ${Python3_EXECUTABLE} -mvenv venv_astyle
|
COMMAND ${Python3_EXECUTABLE} -mvenv venv_astyle
|
||||||
COMMAND venv_astyle/bin/pip3 --quiet install -r ${CMAKE_SOURCE_DIR}/tools/astyle/requirements.txt
|
COMMAND venv_astyle/bin/pip3 --quiet install -r ${CMAKE_SOURCE_DIR}/tools/astyle/requirements.txt
|
||||||
@@ -410,18 +386,16 @@ add_custom_target(ci_test_amalgamation
|
|||||||
COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json_fwd.json -s .
|
COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json_fwd.json -s .
|
||||||
COMMAND cp ${PROJECT_SOURCE_DIR}/include/nlohmann/json_literals.hpp ${include_dir}/json_literals.hpp
|
COMMAND cp ${PROJECT_SOURCE_DIR}/include/nlohmann/json_literals.hpp ${include_dir}/json_literals.hpp
|
||||||
COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=none ${include_dir}/json.hpp ${include_dir}/json_fwd.hpp
|
COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=none ${include_dir}/json.hpp ${include_dir}/json_fwd.hpp
|
||||||
COMMAND ${CMAKE_COMMAND} -P ${PROJECT_SOURCE_DIR}/cmake/scripts/gen_bazel_build_file.cmake
|
|
||||||
|
|
||||||
COMMAND diff ${include_dir}/json.hpp~ ${include_dir}/json.hpp
|
COMMAND diff ${include_dir}/json.hpp~ ${include_dir}/json.hpp
|
||||||
COMMAND diff ${include_dir}/json_fwd.hpp~ ${include_dir}/json_fwd.hpp
|
COMMAND diff ${include_dir}/json_fwd.hpp~ ${include_dir}/json_fwd.hpp
|
||||||
COMMAND diff ${include_dir}/json_literals.hpp~ ${include_dir}/json_literals.hpp
|
COMMAND diff ${include_dir}/json_literals.hpp~ ${include_dir}/json_literals.hpp
|
||||||
COMMAND diff ${PROJECT_SOURCE_DIR}/BUILD.bazel~ ${PROJECT_SOURCE_DIR}/BUILD.bazel
|
|
||||||
|
|
||||||
COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=orig ${INDENT_FILES}
|
COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=orig ${INDENT_FILES}
|
||||||
COMMAND for FILE in `find . -name '*.orig'`\; do false \; done
|
COMMAND for FILE in `find . -name '*.orig'`\; do false \; done
|
||||||
|
|
||||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
COMMENT "Check amalgamation, formatting, and BUILD.bazel"
|
COMMENT "Check amalgamation and indentation"
|
||||||
)
|
)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
@@ -456,14 +430,14 @@ add_custom_target(ci_test_valgrind
|
|||||||
# Check code with Clang Static Analyzer.
|
# 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
|
add_custom_target(ci_clang_analyze
|
||||||
COMMAND CXX=${CLANG_TOOL} ${CMAKE_COMMAND}
|
COMMAND CXX=${CLANG_TOOL} ${CMAKE_COMMAND}
|
||||||
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
||||||
-DJSON_BuildTests=ON
|
-DJSON_BuildTests=ON
|
||||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_clang_analyze
|
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_clang_analyze
|
||||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_clang_analyze && ${SCAN_BUILD_TOOL} -enable-checker ${CLANG_ANALYZER_CHECKS} --use-c++=${CLANG_TOOL} --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"
|
COMMENT "Check code with Clang Analyzer"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -479,7 +453,7 @@ add_custom_target(ci_cppcheck
|
|||||||
COMMAND venv_cppcheck/bin/cppcheck --enable=warning --check-level=exhaustive --inline-suppr --inconclusive --force
|
COMMAND venv_cppcheck/bin/cppcheck --enable=warning --check-level=exhaustive --inline-suppr --inconclusive --force
|
||||||
--std=c++11 ${PROJECT_SOURCE_DIR}/include/nlohmann/json.hpp -I ${CMAKE_SOURCE_DIR}/include
|
--std=c++11 ${PROJECT_SOURCE_DIR}/include/nlohmann/json.hpp -I ${CMAKE_SOURCE_DIR}/include
|
||||||
--error-exitcode=1 --relative-paths=${PROJECT_SOURCE_DIR} -j ${N} --include=default_defines.hpp
|
--error-exitcode=1 --relative-paths=${PROJECT_SOURCE_DIR} -j ${N} --include=default_defines.hpp
|
||||||
--cppcheck-build-dir=cppcheck
|
--cppcheck-build-dir=cppcheck --check-level=exhaustive
|
||||||
-UJSON_CATCH_USER -UJSON_TRY_USER -UJSON_ASSERT -UJSON_INTERNAL_CATCH -UJSON_THROW
|
-UJSON_CATCH_USER -UJSON_TRY_USER -UJSON_ASSERT -UJSON_INTERNAL_CATCH -UJSON_THROW
|
||||||
-DJSON_HAS_CPP_11 -UJSON_HAS_CPP_14 -UJSON_HAS_CPP_17 -UJSON_HAS_CPP_20 -UJSON_HAS_THREE_WAY_COMPARISON
|
-DJSON_HAS_CPP_11 -UJSON_HAS_CPP_14 -UJSON_HAS_CPP_17 -UJSON_HAS_CPP_20 -UJSON_HAS_THREE_WAY_COMPARISON
|
||||||
COMMENT "Check code with Cppcheck"
|
COMMENT "Check code with Cppcheck"
|
||||||
@@ -497,6 +471,34 @@ add_custom_target(ci_cpplint
|
|||||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Check code with OCLint.
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
file(COPY ${PROJECT_SOURCE_DIR}/single_include/nlohmann/json.hpp DESTINATION ${PROJECT_BINARY_DIR}/src_single)
|
||||||
|
file(RENAME ${PROJECT_BINARY_DIR}/src_single/json.hpp ${PROJECT_BINARY_DIR}/src_single/all.cpp)
|
||||||
|
file(APPEND "${PROJECT_BINARY_DIR}/src_single/all.cpp" "\n\nint main()\n{}\n")
|
||||||
|
|
||||||
|
add_executable(single_all ${PROJECT_BINARY_DIR}/src_single/all.cpp)
|
||||||
|
target_compile_features(single_all PRIVATE cxx_std_11)
|
||||||
|
|
||||||
|
add_custom_target(ci_oclint
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
-DCMAKE_BUILD_TYPE=Debug
|
||||||
|
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
||||||
|
-DJSON_BuildTests=OFF -DJSON_CI=ON
|
||||||
|
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_oclint
|
||||||
|
COMMAND ${OCLINT_TOOL} -i ${PROJECT_BINARY_DIR}/build_oclint/src_single/all.cpp -p ${PROJECT_BINARY_DIR}/build_oclint --
|
||||||
|
-report-type html -enable-global-analysis --max-priority-1=0 --max-priority-2=1000 --max-priority-3=2000
|
||||||
|
--disable-rule=MultipleUnaryOperator
|
||||||
|
--disable-rule=DoubleNegative
|
||||||
|
--disable-rule=ShortVariableName
|
||||||
|
--disable-rule=GotoStatement
|
||||||
|
--disable-rule=LongLine
|
||||||
|
-o ${PROJECT_BINARY_DIR}/build_oclint/oclint_report.html
|
||||||
|
COMMENT "Check code with OCLint"
|
||||||
|
)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Check code with Clang-Tidy.
|
# Check code with Clang-Tidy.
|
||||||
###############################################################################
|
###############################################################################
|
||||||
@@ -511,16 +513,29 @@ add_custom_target(ci_clang_tidy
|
|||||||
COMMENT "Check code with Clang-Tidy"
|
COMMENT "Check code with Clang-Tidy"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Check code with PVS-Studio Analyzer <https://www.viva64.com/en/pvs-studio/>.
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
add_custom_target(ci_pvs_studio
|
||||||
|
COMMAND CXX=${CLANG_TOOL} ${CMAKE_COMMAND}
|
||||||
|
-DCMAKE_BUILD_TYPE=Debug
|
||||||
|
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
||||||
|
-DJSON_BuildTests=ON
|
||||||
|
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_pvs_studio
|
||||||
|
COMMAND cd ${PROJECT_BINARY_DIR}/build_pvs_studio && ${PVS_STUDIO_ANALYZER_TOOL} analyze -j 10
|
||||||
|
COMMAND cd ${PROJECT_BINARY_DIR}/build_pvs_studio && ${PLOG_CONVERTER_TOOL} -a'GA:1,2;64:1;CS' -t fullhtml PVS-Studio.log -o pvs
|
||||||
|
COMMENT "Check code with PVS Studio"
|
||||||
|
)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Check code with Infer <https://fbinfer.com> static analyzer.
|
# Check code with Infer <https://fbinfer.com> static analyzer.
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
# .inferconfig (repository root) pins --fail-on-issue and the currently-triaged issue types that
|
|
||||||
# are disabled until they are addressed separately; see #5715 item 4b.
|
|
||||||
add_custom_target(ci_infer
|
add_custom_target(ci_infer
|
||||||
COMMAND mkdir -p ${PROJECT_BINARY_DIR}/build_infer
|
COMMAND mkdir -p ${PROJECT_BINARY_DIR}/build_infer
|
||||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_infer && ${INFER_TOOL} compile -- ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug ${PROJECT_SOURCE_DIR} -DJSON_BuildTests=ON
|
COMMAND cd ${PROJECT_BINARY_DIR}/build_infer && ${INFER_TOOL} compile -- ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug ${PROJECT_SOURCE_DIR} -DJSON_BuildTests=ON
|
||||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_infer && ${INFER_TOOL} run --project-root ${PROJECT_SOURCE_DIR} -- make
|
COMMAND cd ${PROJECT_BINARY_DIR}/build_infer && ${INFER_TOOL} run -- make
|
||||||
COMMENT "Check code with Infer"
|
COMMENT "Check code with Infer"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -576,13 +591,7 @@ add_custom_target(ci_reproducible_tests
|
|||||||
# be compiled individually.
|
# be compiled individually.
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
set(iwyu_options -Xiwyu --error -Xiwyu --max_line_length=300)
|
set(iwyu_path_and_options ${IWYU_TOOL} -Xiwyu --max_line_length=300)
|
||||||
set(iwyu_path_and_options ${IWYU_TOOL} ${iwyu_options})
|
|
||||||
|
|
||||||
# CMake needs to know the exact flags used to compile each src_single/*.cpp below to hand them to
|
|
||||||
# iwyu_tool.py; JSON_CI already implies a from-scratch configure, so enabling this project-wide has
|
|
||||||
# no downside here.
|
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
|
||||||
|
|
||||||
foreach(SRC_FILE ${SRC_FILES})
|
foreach(SRC_FILE ${SRC_FILES})
|
||||||
# get relative path of the header file
|
# get relative path of the header file
|
||||||
@@ -597,34 +606,14 @@ foreach(SRC_FILE ${SRC_FILES})
|
|||||||
target_include_directories(single_${RELATIVE_SRC_FILE} PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
target_include_directories(single_${RELATIVE_SRC_FILE} PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||||
target_compile_features(single_${RELATIVE_SRC_FILE} PRIVATE cxx_std_11)
|
target_compile_features(single_${RELATIVE_SRC_FILE} PRIVATE cxx_std_11)
|
||||||
set_property(TARGET single_${RELATIVE_SRC_FILE} PROPERTY CXX_INCLUDE_WHAT_YOU_USE "${iwyu_path_and_options}")
|
set_property(TARGET single_${RELATIVE_SRC_FILE} PROPERTY CXX_INCLUDE_WHAT_YOU_USE "${iwyu_path_and_options}")
|
||||||
# remember binary for ci_single_binaries
|
# remember binary for ci_single_binaries target
|
||||||
list(APPEND single_binaries single_${RELATIVE_SRC_FILE})
|
list(APPEND single_binaries single_${RELATIVE_SRC_FILE})
|
||||||
# json.hpp pulls together the whole library behind heavily templated, SFINAE-based code, and
|
|
||||||
# IWYU's suggestion for its one truly ambiguous symbol (a container-comparison "swap,
|
|
||||||
# operator!=") is not deterministic between runs (observed <set>, <unordered_map>, and <map>
|
|
||||||
# for the exact same source across otherwise-identical local and containerized builds). Keep
|
|
||||||
# reporting its diagnostics (informational, via CXX_INCLUDE_WHAT_YOU_USE above) but exclude it
|
|
||||||
# from the hard gate below so a fresh IWYU/compiler combination does not fail this target on a
|
|
||||||
# nondeterministic suggestion for a header that already re-exports everything on purpose.
|
|
||||||
if(NOT RELATIVE_SRC_FILE STREQUAL "json")
|
|
||||||
list(APPEND single_binaries_tus src_single/${RELATIVE_SRC_FILE}.cpp)
|
|
||||||
endif()
|
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
if(IWYU_TOOL_PY)
|
add_custom_target(ci_single_binaries
|
||||||
add_custom_target(ci_single_binaries
|
DEPENDS ${single_binaries}
|
||||||
DEPENDS ${single_binaries}
|
COMMENT "Check if headers are self-contained"
|
||||||
COMMAND ${IWYU_TOOL_PY} -p ${PROJECT_BINARY_DIR} ${single_binaries_tus} -- ${iwyu_options}
|
)
|
||||||
COMMENT "Check if headers are self-contained"
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
# iwyu_tool.py (ships with IWYU, e.g. as /usr/bin/iwyu_tool on Debian/Ubuntu) was not found;
|
|
||||||
# fall back to building the self-containment check without enforcing the IWYU findings.
|
|
||||||
add_custom_target(ci_single_binaries
|
|
||||||
DEPENDS ${single_binaries}
|
|
||||||
COMMENT "Check if headers are self-contained"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Benchmarks
|
# Benchmarks
|
||||||
@@ -646,41 +635,21 @@ add_custom_target(ci_benchmarks
|
|||||||
# we test the project with different CMake versions:
|
# we test the project with different CMake versions:
|
||||||
# - CMake 3.5 (the earliest supported)
|
# - CMake 3.5 (the earliest supported)
|
||||||
# - CMake 3.31.6 (the latest 3.x release)
|
# - CMake 3.31.6 (the latest 3.x release)
|
||||||
# - CMake 4.0.0 (the first 4.x release)
|
# - CMake 4.0.0 (the latest release)
|
||||||
# - the CMake version running this build (usually the latest release)
|
|
||||||
|
|
||||||
function(ci_get_cmake version var)
|
function(ci_get_cmake version var)
|
||||||
set(${var} ${PROJECT_BINARY_DIR}/cmake-${version}/bin/cmake)
|
set(${var} ${PROJECT_BINARY_DIR}/cmake-${version}/bin/cmake)
|
||||||
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(x86_64|amd64)$")
|
add_custom_command(
|
||||||
# Kitware publishes a prebuilt Linux x86_64 archive for every release; unpacking it is far
|
OUTPUT ${${var}}
|
||||||
# cheaper than compiling all of CMake (including its own test helpers) from source.
|
COMMAND wget -nc https://github.com/Kitware/CMake/releases/download/v${version}/cmake-${version}.tar.gz
|
||||||
add_custom_command(
|
COMMAND tar xfz cmake-${version}.tar.gz
|
||||||
OUTPUT ${${var}}
|
COMMAND rm cmake-${version}.tar.gz
|
||||||
COMMAND wget -nc https://github.com/Kitware/CMake/releases/download/v${version}/cmake-${version}-linux-x86_64.tar.gz
|
# -DCMAKE_POLICY_VERSION_MINIMUM=3.5 required to compile older CMake versions with CMake 4.0.0
|
||||||
COMMAND wget -nc https://github.com/Kitware/CMake/releases/download/v${version}/cmake-${version}-SHA-256.txt
|
COMMAND cmake -S cmake-${version} -B cmake-${version} -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||||
# verify the archive against Kitware's published SHA-256 sums before unpacking it
|
COMMAND cmake --build cmake-${version} --parallel 10
|
||||||
COMMAND sh -c "grep ' cmake-${version}-linux-x86_64[.]tar[.]gz$' cmake-${version}-SHA-256.txt | sha256sum -c -"
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||||
COMMAND tar xfz cmake-${version}-linux-x86_64.tar.gz
|
COMMENT "Download CMake ${version}"
|
||||||
COMMAND rm cmake-${version}-linux-x86_64.tar.gz cmake-${version}-SHA-256.txt
|
)
|
||||||
COMMAND ${CMAKE_COMMAND} -E rm -rf cmake-${version}
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E rename cmake-${version}-linux-x86_64 cmake-${version}
|
|
||||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
|
||||||
COMMENT "Download prebuilt CMake ${version}"
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
# no prebuilt archive for this platform (e.g. macOS or Linux aarch64): build from source
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT ${${var}}
|
|
||||||
COMMAND wget -nc https://github.com/Kitware/CMake/releases/download/v${version}/cmake-${version}.tar.gz
|
|
||||||
COMMAND tar xfz cmake-${version}.tar.gz
|
|
||||||
COMMAND rm cmake-${version}.tar.gz
|
|
||||||
# -DCMAKE_POLICY_VERSION_MINIMUM=3.5 required to compile older CMake versions with CMake 4.0.0
|
|
||||||
COMMAND cmake -S cmake-${version} -B cmake-${version} -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
|
||||||
COMMAND cmake --build cmake-${version} --parallel 10
|
|
||||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
|
||||||
COMMENT "Download and build CMake ${version} from source"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
set(${var} ${${var}} PARENT_SCOPE)
|
set(${var} ${${var}} PARENT_SCOPE)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
@@ -690,24 +659,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
|
# the tests require CMake 3.13 or later, so they are excluded for CMake 3.5.0
|
||||||
set(JSON_CMAKE_FLAGS_3_5_0 JSON_Diagnostics JSON_Diagnostic_Positions JSON_GlobalUDLs JSON_ImplicitConversions JSON_DisableEnumSerialization
|
set(JSON_CMAKE_FLAGS_3_5_0 JSON_Diagnostics JSON_Diagnostic_Positions JSON_GlobalUDLs JSON_ImplicitConversions JSON_DisableEnumSerialization
|
||||||
JSON_LegacyDiscardedValueComparison JSON_Install JSON_MultipleHeaders JSON_SystemInclude JSON_Valgrind
|
JSON_LegacyDiscardedValueComparison JSON_Install JSON_MultipleHeaders JSON_SystemInclude JSON_Valgrind)
|
||||||
JSON_StrictNulHandling)
|
set(JSON_CMAKE_FLAGS_3_31_6 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_31_6})
|
||||||
set(JSON_CMAKE_FLAGS_3_31_6 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
|
|
||||||
set(JSON_CMAKE_FLAGS_4_0_0 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
|
set(JSON_CMAKE_FLAGS_4_0_0 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
|
||||||
|
|
||||||
function(ci_add_cmake_flags_targets flag min_version)
|
function(ci_add_cmake_flags_targets flag min_version)
|
||||||
string(TOLOWER "ci_cmake_flag_${flag}" flag_target)
|
string(TOLOWER "ci_cmake_flag_${flag}" flag_target)
|
||||||
string(REPLACE . _ min_version_var ${min_version})
|
string(REPLACE . _ min_version_var ${min_version})
|
||||||
set(cmake_binary ${CMAKE_${min_version_var}_BINARY})
|
set(cmake_binary ${CMAKE_${min_version_var}_BINARY})
|
||||||
|
add_custom_target(${flag_target}_${min_version}_2
|
||||||
|
COMMENT "Check CMake flag ${flag} (CMake ${CMAKE_VERSION})"
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
-Werror=dev
|
||||||
|
-D${flag}=ON
|
||||||
|
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_${flag_target}
|
||||||
|
)
|
||||||
add_custom_target(${flag_target}_${min_version_var}
|
add_custom_target(${flag_target}_${min_version_var}
|
||||||
COMMENT "Check CMake flag ${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 mkdir -pv ${PROJECT_BINARY_DIR}/build_${flag_target}_${min_version_var}
|
||||||
COMMAND cd ${PROJECT_BINARY_DIR}/build_${flag_target}_${min_version_var}
|
COMMAND cd ${PROJECT_BINARY_DIR}/build_${flag_target}_${min_version_var}
|
||||||
&& ${cmake_binary} -Werror=dev ${PROJECT_SOURCE_DIR} -D${flag}=ON
|
&& ${cmake_binary} -Werror=dev ${PROJECT_SOURCE_DIR} -D${flag}=ON
|
||||||
DEPENDS ${cmake_binary}
|
DEPENDS ${cmake_binary}
|
||||||
)
|
)
|
||||||
list(APPEND JSON_CMAKE_FLAG_TARGETS ${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}_${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_TARGETS ${JSON_CMAKE_FLAG_TARGETS} PARENT_SCOPE)
|
||||||
set(JSON_CMAKE_FLAG_BUILD_DIRS ${JSON_CMAKE_FLAG_BUILD_DIRS} PARENT_SCOPE)
|
set(JSON_CMAKE_FLAG_BUILD_DIRS ${JSON_CMAKE_FLAG_BUILD_DIRS} PARENT_SCOPE)
|
||||||
endfunction()
|
endfunction()
|
||||||
@@ -724,20 +699,6 @@ foreach(JSON_CMAKE_FLAG ${JSON_CMAKE_FLAGS_4_0_0})
|
|||||||
ci_add_cmake_flags_targets(${JSON_CMAKE_FLAG} 4.0.0)
|
ci_add_cmake_flags_targets(${JSON_CMAKE_FLAG} 4.0.0)
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
# check the same flags with the CMake version running this build
|
|
||||||
foreach(JSON_CMAKE_FLAG ${JSON_CMAKE_FLAGS_4_0_0})
|
|
||||||
string(TOLOWER "ci_cmake_flag_${JSON_CMAKE_FLAG}" flag_target)
|
|
||||||
add_custom_target(${flag_target}
|
|
||||||
COMMENT "Check CMake flag ${JSON_CMAKE_FLAG} (CMake ${CMAKE_VERSION})"
|
|
||||||
COMMAND ${CMAKE_COMMAND}
|
|
||||||
-Werror=dev
|
|
||||||
-D${JSON_CMAKE_FLAG}=ON
|
|
||||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_${flag_target}
|
|
||||||
)
|
|
||||||
list(APPEND JSON_CMAKE_FLAG_TARGETS ${flag_target})
|
|
||||||
list(APPEND JSON_CMAKE_FLAG_BUILD_DIRS ${PROJECT_BINARY_DIR}/build_${flag_target})
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
add_custom_target(ci_cmake_flags
|
add_custom_target(ci_cmake_flags
|
||||||
DEPENDS ${JSON_CMAKE_FLAG_TARGETS}
|
DEPENDS ${JSON_CMAKE_FLAG_TARGETS}
|
||||||
COMMENT "Check CMake flags"
|
COMMENT "Check CMake flags"
|
||||||
@@ -896,6 +857,6 @@ add_custom_target(ci_test_build_documentation
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
add_custom_target(ci_clean
|
add_custom_target(ci_clean
|
||||||
COMMAND rm -fr ${PROJECT_BINARY_DIR}/build_* ${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"
|
COMMENT "Clean generated directories"
|
||||||
)
|
)
|
||||||
|
|||||||
+5
-16
@@ -11,31 +11,20 @@ EXAMPLES = $(wildcard mkdocs/docs/examples/*.cpp)
|
|||||||
|
|
||||||
cxx_standard = $(lastword c++11 $(filter c++%, $(subst ., ,$1)))
|
cxx_standard = $(lastword c++11 $(filter c++%, $(subst ., ,$1)))
|
||||||
|
|
||||||
# common compile flags for the stand-alone example files
|
|
||||||
EXAMPLE_CPPFLAGS = -I $(SRCDIR) -DJSON_USE_GLOBAL_UDLS=0
|
|
||||||
EXAMPLE_WARNFLAGS = -Werror=deprecated-declarations
|
|
||||||
|
|
||||||
# examples that document deprecated API and are allowed to use it
|
|
||||||
DEPRECATED_EXAMPLES = $(addprefix mkdocs/docs/examples/, \
|
|
||||||
json_pointer__operator__equal_stringtype \
|
|
||||||
json_pointer__operator__notequal_stringtype \
|
|
||||||
json_pointer__operator_string_t)
|
|
||||||
$(DEPRECATED_EXAMPLES:=.output) $(DEPRECATED_EXAMPLES:=.test): EXAMPLE_WARNFLAGS = -Wno-deprecated-declarations
|
|
||||||
|
|
||||||
# create output from a stand-alone example file
|
# create output from a stand-alone example file
|
||||||
%.output: %.cpp
|
%.output: %.cpp
|
||||||
@echo "standard $(call cxx_standard,$(<:.cpp=))"
|
@echo "standard $(call cxx_standard $(<:.cpp=))"
|
||||||
$(MAKE) $(<:.cpp=) \
|
$(MAKE) $(<:.cpp=) \
|
||||||
CPPFLAGS="$(EXAMPLE_CPPFLAGS)" \
|
CPPFLAGS="-I $(SRCDIR) -DJSON_USE_GLOBAL_UDLS=0" \
|
||||||
CXXFLAGS="-std=$(call cxx_standard,$(<:.cpp=)) $(EXAMPLE_WARNFLAGS)"
|
CXXFLAGS="-std=$(call cxx_standard,$(<:.cpp=)) -Wno-deprecated-declarations"
|
||||||
./$(<:.cpp=) > $@
|
./$(<:.cpp=) > $@
|
||||||
rm $(<:.cpp=)
|
rm $(<:.cpp=)
|
||||||
|
|
||||||
# compare created output with current output of the example files
|
# compare created output with current output of the example files
|
||||||
%.test: %.cpp
|
%.test: %.cpp
|
||||||
$(MAKE) $(<:.cpp=) \
|
$(MAKE) $(<:.cpp=) \
|
||||||
CPPFLAGS="$(EXAMPLE_CPPFLAGS)" \
|
CPPFLAGS="-I $(SRCDIR) -DJSON_USE_GLOBAL_UDLS=0" \
|
||||||
CXXFLAGS="-std=$(call cxx_standard,$(<:.cpp=)) $(EXAMPLE_WARNFLAGS)"
|
CXXFLAGS="-std=$(call cxx_standard,$(<:.cpp=)) -Wno-deprecated-declarations"
|
||||||
./$(<:.cpp=) > $@
|
./$(<:.cpp=) > $@
|
||||||
diff $@ $(<:.cpp=.output)
|
diff $@ $(<:.cpp=.output)
|
||||||
rm $(<:.cpp=) $@
|
rm $(<:.cpp=) $@
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<key>dashIndexFilePath</key>
|
<key>dashIndexFilePath</key>
|
||||||
<string>index.html</string>
|
<string>index.html</string>
|
||||||
<key>DashDocSetFallbackURL</key>
|
<key>DashDocSetFallbackURL</key>
|
||||||
<string>https://json.nlohmann.me/</string>
|
<string>https://nlohmann.github.io/json/</string>
|
||||||
<key>isJavaScriptEnabled</key>
|
<key>isJavaScriptEnabled</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
|
|||||||
+22
-12
@@ -52,25 +52,35 @@ install_docset_zeal: JSON_for_Modern_C++.docset
|
|||||||
mkdir -p $$docset_root; \
|
mkdir -p $$docset_root; \
|
||||||
cp -r JSON_for_Modern_C++.docset $$docset_root/
|
cp -r JSON_for_Modern_C++.docset $$docset_root/
|
||||||
|
|
||||||
# both targets below compare the docset search index with the mkdocs page
|
|
||||||
# set. They share the same normalization (docs/foo/index.md and
|
|
||||||
# docs/foo.md both become foo/index.html, the URL mkdocs itself would
|
|
||||||
# give the page; the top-level index.md is excluded, as it is not part
|
|
||||||
# of the hand-curated docSet.sql) and use comm(1) on two sorted lists
|
|
||||||
# instead of running a sqlite3 query, or an O(n*m) nested shell loop,
|
|
||||||
# once per page.
|
|
||||||
DOCSET_INDEX_PATHS=$(shell sqlite3 docSet.dsidx "SELECT DISTINCT path FROM searchIndex" | sort)
|
|
||||||
DOCSET_PAGE_PATHS=$(shell echo '$(MKDOCS_PAGES)' | tr ' ' '\n' | grep -v '^index\.md$$' | $(SED) -E 's@/index\.md$$@/index.html@; s@\.md$$@/index.html@' | sort)
|
|
||||||
|
|
||||||
# list mkdocs pages missing from the docset index
|
# list mkdocs pages missing from the docset index
|
||||||
.PHONY: list_missing_pages
|
.PHONY: list_missing_pages
|
||||||
list_missing_pages: docSet.dsidx
|
list_missing_pages: docSet.dsidx
|
||||||
@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
|
# list paths in the docset index without a corresponding mkdocs page
|
||||||
.PHONY: list_removed_paths
|
.PHONY: list_removed_paths
|
||||||
list_removed_paths: docSet.dsidx
|
list_removed_paths: docSet.dsidx
|
||||||
@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
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
|
|||||||
@@ -7,11 +7,10 @@ documentation browsers like [Dash](https://kapeli.com/dash), [Velocity](https://
|
|||||||
The docset can be created with
|
The docset can be created with
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
make 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
|
The generated folder `nlohmann_json.docset` can then be opened in the documentation browser.
|
||||||
`JSON_for_Modern_C++.tgz` archive instead, and `make install_docset_zeal` installs the docset for Zeal directly.
|
|
||||||
|
|
||||||
A recent version is also part of the [Dash user contributions](https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/JSON_for_Modern_C%2B%2B).
|
A recent version is also part of the [Dash user contributions](https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/JSON_for_Modern_C%2B%2B).
|
||||||
|
|
||||||
|
|||||||
@@ -210,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_DIAGNOSTICS', 'Macro', 'api/macros/json_diagnostics/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DIAGNOSTIC_POSITIONS', 'Macro', 'api/macros/json_diagnostic_positions/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DIAGNOSTIC_POSITIONS', 'Macro', 'api/macros/json_diagnostic_positions/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DISABLE_ENUM_SERIALIZATION', 'Macro', 'api/macros/json_disable_enum_serialization/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DISABLE_ENUM_SERIALIZATION', 'Macro', 'api/macros/json_disable_enum_serialization/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DISABLE_TUPLE_REFERENCE_CONVERSION', 'Macro', 'api/macros/json_disable_tuple_reference_conversion/index.html');
|
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_11', 'Macro', 'api/macros/json_has_cpp_11/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_11', 'Macro', 'api/macros/json_has_cpp_11/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_14', 'Macro', 'api/macros/json_has_cpp_11/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_14', 'Macro', 'api/macros/json_has_cpp_11/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_17', 'Macro', 'api/macros/json_has_cpp_11/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_17', 'Macro', 'api/macros/json_has_cpp_11/index.html');
|
||||||
|
|||||||
@@ -159,8 +159,6 @@ basic_json(basic_json&& other) noexcept;
|
|||||||
- `CompatibleType` is not `basic_json` (to avoid hijacking copy/move constructors),
|
- `CompatibleType` is not `basic_json` (to avoid hijacking copy/move constructors),
|
||||||
- `CompatibleType` is not a different `basic_json` type (i.e. with different template arguments)
|
- `CompatibleType` is not a different `basic_json` type (i.e. with different template arguments)
|
||||||
- `CompatibleType` is not a `basic_json` nested type (e.g., `json_pointer`, `iterator`, etc.)
|
- `CompatibleType` is not a `basic_json` nested type (e.g., `json_pointer`, `iterator`, etc.)
|
||||||
- if [`JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`](../macros/json_disable_tuple_reference_conversion.md) is defined
|
|
||||||
to `1`: `CompatibleType` is not a one-element `std::tuple` holding a reference to `basic_json`
|
|
||||||
- `json_serializer<U>` (with `U = uncvref_t<CompatibleType>`) has a `to_json(basic_json_t&, CompatibleType&&)`
|
- `json_serializer<U>` (with `U = uncvref_t<CompatibleType>`) has a `to_json(basic_json_t&, CompatibleType&&)`
|
||||||
method
|
method
|
||||||
|
|
||||||
|
|||||||
@@ -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`.
|
Linear in the lengths of `apply_patch`.
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
`apply_patch` may be `#!cpp *this` itself or refer to a value contained in `#!cpp *this` (for example, a subobject
|
|
||||||
returned by `#!cpp (*this)[key]`); it is read as it was when `merge_patch()` was called, before any modification of
|
|
||||||
`#!cpp *this`.
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
@@ -67,5 +61,3 @@ returned by `#!cpp (*this)[key]`); it is read as it was when `merge_patch()` was
|
|||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 3.0.0.
|
- Added in version 3.0.0.
|
||||||
- Fixed use of freed or relocated memory when `apply_patch` is `#!cpp *this` or refers to a value contained in
|
|
||||||
`#!cpp *this`, in version 3.13.0.
|
|
||||||
|
|||||||
@@ -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).
|
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:
|
[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.
|
> interoperable in the sense that implementations will agree exactly on their numeric values.
|
||||||
|
|
||||||
As this range is a subrange of the exactly supported range [INT64_MIN, INT64_MAX], this class's integer type is
|
As this range is a subrange of the exactly supported range [INT64_MIN, INT64_MAX], this class's integer type is
|
||||||
|
|||||||
@@ -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).
|
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:
|
[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.
|
> interoperable in the sense that implementations will agree exactly on their numeric values.
|
||||||
|
|
||||||
As this range is a subrange (when considered in conjunction with the `number_integer_t` type) of the exactly supported
|
As this range is a subrange (when considered in conjunction with the `number_integer_t` type) of the exactly supported
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ classDiagram
|
|||||||
|
|
||||||
## Notes
|
## 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.
|
or the end of file. This also holds true when reading a byte vector for binary formats.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|||||||
@@ -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.
|
1. O(N*log(size() + N)), where N is the number of elements to insert.
|
||||||
2. O(N*log(size() + N)), where N is the number of elements to insert.
|
2. O(N*log(size() + N)), where N is the number of elements to insert.
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
The argument `j` (or, for overload (2), the range `[first, last)`) may be `#!cpp *this` itself or refer to a value
|
|
||||||
contained in `#!cpp *this` (for example, a subobject returned by `#!cpp (*this)[key]`); it is read as it was when
|
|
||||||
`update()` was called, before any modification of `#!cpp *this`.
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
@@ -161,5 +155,3 @@ contained in `#!cpp *this` (for example, a subobject returned by `#!cpp (*this)[
|
|||||||
|
|
||||||
- Added in version 3.0.0.
|
- Added in version 3.0.0.
|
||||||
- Added `merge_objects` parameter in 3.10.5.
|
- Added `merge_objects` parameter in 3.10.5.
|
||||||
- Fixed use of freed or relocated memory when the argument is `#!cpp *this` or refers to a value contained in
|
|
||||||
`#!cpp *this`, in version 3.13.0.
|
|
||||||
|
|||||||
@@ -53,7 +53,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_BRACE_INIT_COPY_SEMANTICS**](json_brace_init_copy_semantics.md) - opt in to copy/move semantics for single-element brace initialization
|
||||||
- [**JSON_DISABLE_ENUM_SERIALIZATION**](json_disable_enum_serialization.md) - switch off default serialization/deserialization functions for enums
|
- [**JSON_DISABLE_ENUM_SERIALIZATION**](json_disable_enum_serialization.md) - switch off default serialization/deserialization functions for enums
|
||||||
- [**JSON_DISABLE_TUPLE_REFERENCE_CONVERSION**](json_disable_tuple_reference_conversion.md) - switch off conversion from a one-element tuple of a JSON reference
|
|
||||||
- [**JSON_USE_IMPLICIT_CONVERSIONS**](json_use_implicit_conversions.md) - control implicit conversions
|
- [**JSON_USE_IMPLICIT_CONVERSIONS**](json_use_implicit_conversions.md) - control implicit conversions
|
||||||
|
|
||||||
## Comparison behavior
|
## Comparison behavior
|
||||||
|
|||||||
@@ -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.
|
|
||||||
@@ -90,7 +90,7 @@ Note: Some modern features (like C++20 ranges or filesystem support) may be disa
|
|||||||
| GNU 14.2.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
| GNU 14.2.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||||
| GNU 15.1.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
| GNU 15.1.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||||
| GNU 16.1.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
| GNU 16.1.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||||
| GNU 16.1.0 | arm64 | 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 |
|
| icpc (ICC) 2021.10.0 20230609 | x86_64 | Ubuntu 22.04 LTS | GitHub |
|
||||||
| icpx (Intel oneAPI DPC++/C++) 2025.3.2 | x86_64 | Ubuntu 24.04 LTS | GitHub |
|
| icpx (Intel oneAPI DPC++/C++) 2025.3.2 | x86_64 | Ubuntu 24.04 LTS | GitHub |
|
||||||
| nvc++ (NVIDIA HPC SDK) 25.5-0 | x86_64 | Ubuntu 22.04 LTS | GitHub |
|
| nvc++ (NVIDIA HPC SDK) 25.5-0 | x86_64 | Ubuntu 22.04 LTS | GitHub |
|
||||||
@@ -132,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.
|
[libstdc++](https://gcc.gnu.org/onlinedocs/libstdc++/) to detect subtle differences or incompatibilities.
|
||||||
- [x] The code checked with [Include What You Use (IWYU)](https://include-what-you-use.org) that all required standard
|
- [x] The code checked with [Include What You Use (IWYU)](https://include-what-you-use.org) that all required standard
|
||||||
headers are included.
|
headers are included.
|
||||||
- [x] On Windows, the library is compiled with `<Windows.h>` being included to detect and avoid common bugs (see
|
- [x] On Windows, the library is compiled with `<Windows.h>` being included to detect and avoid common bugs.
|
||||||
[`unit-windows_h.cpp`](https://github.com/nlohmann/json/blob/develop/tests/src/unit-windows_h.cpp)).
|
|
||||||
- [x] The library is compiled with exceptions disabled to support alternative means of error handling.
|
- [x] The library is compiled with exceptions disabled to support alternative means of error handling.
|
||||||
|
|
||||||
## Stable public API
|
## Stable public API
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -61,7 +61,7 @@ The library uses the following mapping from JSON values types to BJData types ac
|
|||||||
|
|
||||||
The following values can **not** be converted to a BJData value:
|
The following values can **not** be converted to a BJData value:
|
||||||
|
|
||||||
- strings with more than 18446744073709551615 bytes, i.e., 2<sup>64</sup>-1 bytes (theoretical)
|
- strings with more than 18446744073709551615 bytes, i.e., $2^{64}-1$ bytes (theoretical)
|
||||||
|
|
||||||
!!! info "Unused BJData markers"
|
!!! info "Unused BJData markers"
|
||||||
|
|
||||||
|
|||||||
@@ -83,13 +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).
|
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`
|
## `JSON_NO_AUTOMATIC_UDLS`
|
||||||
|
|
||||||
When defined, `<nlohmann/json.hpp>` does not include `<nlohmann/json_literals.hpp>` with the user-defined string literals
|
When defined, `<nlohmann/json.hpp>` does not include `<nlohmann/json_literals.hpp>` with the user-defined string literals
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ When the default type is used, the maximal unsigned integer number that can be s
|
|||||||
|
|
||||||
[RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
|
[RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
|
||||||
|
|
||||||
> Note that when such software is used, numbers that are integers and are in the range [-2<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.
|
As this range is a subrange of the exactly supported range [`INT64_MIN`, `INT64_MAX`], this class's integer type is interoperable.
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ On number interoperability, the following remarks are made:
|
|||||||
for numeric magnitude and precision than is widely available.
|
for numeric magnitude and precision than is widely available.
|
||||||
|
|
||||||
Note that when such software is used, numbers that are integers and
|
Note that when such software is used, numbers that are integers and
|
||||||
are in the range [-2<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
|
sense that implementations will agree exactly on their numeric
|
||||||
values.
|
values.
|
||||||
|
|
||||||
@@ -95,9 +95,9 @@ This is the same behavior as the code `#!c double x = 3.141592653589793238462643
|
|||||||
|
|
||||||
!!! success "Interoperability"
|
!!! success "Interoperability"
|
||||||
|
|
||||||
- The library is interoperable with respect to the specification, because its supported range [-2<sup>63</sup>, 2<sup>64</sup>-1] is
|
- 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<sup>53</sup>+1, 2<sup>53</sup>-1].
|
larger than the described range $[-2^{53}+1, 2^{53}-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`.
|
- 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.
|
This also concurs with the specification above.
|
||||||
|
|
||||||
### Zeros
|
### Zeros
|
||||||
|
|||||||
@@ -169,12 +169,6 @@ Enable position diagnostics by defining macro [`JSON_DIAGNOSTIC_POSITIONS`](../a
|
|||||||
Disable default `enum` serialization by defining the macro
|
Disable default `enum` serialization by defining the macro
|
||||||
[`JSON_DISABLE_ENUM_SERIALIZATION`](../api/macros/json_disable_enum_serialization.md). This option is `OFF` by default.
|
[`JSON_DISABLE_ENUM_SERIALIZATION`](../api/macros/json_disable_enum_serialization.md). This option is `OFF` by default.
|
||||||
|
|
||||||
### `JSON_DisableTupleReferenceConversion`
|
|
||||||
|
|
||||||
Disable the conversion from a one-element `std::tuple` holding a reference to a JSON value by defining the macro
|
|
||||||
[`JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`](../api/macros/json_disable_tuple_reference_conversion.md). This option is
|
|
||||||
`OFF` by default.
|
|
||||||
|
|
||||||
### `JSON_FastTests`
|
### `JSON_FastTests`
|
||||||
|
|
||||||
Skip expensive/slow test suites. This option is `OFF` by default. Depends on `JSON_BuildTests`.
|
Skip expensive/slow test suites. This option is `OFF` by default. Depends on `JSON_BuildTests`.
|
||||||
|
|||||||
@@ -678,11 +678,11 @@ to install the [nlohmann-json](https://ports.macports.org/port/nlohmann-json/) p
|
|||||||
1. Create the following files:
|
1. Create the following files:
|
||||||
|
|
||||||
```cpp title="example.cpp"
|
```cpp title="example.cpp"
|
||||||
--8<-- "integration/macports/example.cpp"
|
--8<-- "integration/homebrew/example.cpp"
|
||||||
```
|
```
|
||||||
|
|
||||||
```cmake title="CMakeLists.txt"
|
```cmake title="CMakeLists.txt"
|
||||||
--8<-- "integration/macports/CMakeLists.txt"
|
--8<-- "integration/homebrew/CMakeLists.txt"
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Install the package:
|
2. Install the package:
|
||||||
|
|||||||
@@ -287,7 +287,6 @@ nav:
|
|||||||
- 'JSON_DIAGNOSTICS': api/macros/json_diagnostics.md
|
- 'JSON_DIAGNOSTICS': api/macros/json_diagnostics.md
|
||||||
- 'JSON_DIAGNOSTIC_POSITIONS': api/macros/json_diagnostic_positions.md
|
- 'JSON_DIAGNOSTIC_POSITIONS': api/macros/json_diagnostic_positions.md
|
||||||
- 'JSON_DISABLE_ENUM_SERIALIZATION': api/macros/json_disable_enum_serialization.md
|
- 'JSON_DISABLE_ENUM_SERIALIZATION': api/macros/json_disable_enum_serialization.md
|
||||||
- 'JSON_DISABLE_TUPLE_REFERENCE_CONVERSION': api/macros/json_disable_tuple_reference_conversion.md
|
|
||||||
- 'JSON_HAS_CPP_11, JSON_HAS_CPP_14, JSON_HAS_CPP_17, JSON_HAS_CPP_20': api/macros/json_has_cpp_11.md
|
- 'JSON_HAS_CPP_11, JSON_HAS_CPP_14, JSON_HAS_CPP_17, JSON_HAS_CPP_20': api/macros/json_has_cpp_11.md
|
||||||
- 'JSON_HAS_EXPERIMENTAL_FILESYSTEM, JSON_HAS_FILESYSTEM': api/macros/json_has_filesystem.md
|
- 'JSON_HAS_EXPERIMENTAL_FILESYSTEM, JSON_HAS_FILESYSTEM': api/macros/json_has_filesystem.md
|
||||||
- 'JSON_HAS_RANGES': api/macros/json_has_ranges.md
|
- 'JSON_HAS_RANGES': api/macros/json_has_ranges.md
|
||||||
@@ -351,6 +350,7 @@ markdown_extensions:
|
|||||||
- toc:
|
- toc:
|
||||||
permalink: true
|
permalink: true
|
||||||
- md_in_html
|
- md_in_html
|
||||||
|
- pymdownx.arithmatex
|
||||||
- pymdownx.betterem:
|
- pymdownx.betterem:
|
||||||
smart_enable: all
|
smart_enable: all
|
||||||
- pymdownx.caret
|
- pymdownx.caret
|
||||||
@@ -442,3 +442,6 @@ plugins:
|
|||||||
|
|
||||||
extra_css:
|
extra_css:
|
||||||
- css/custom.css
|
- css/custom.css
|
||||||
|
|
||||||
|
extra_javascript:
|
||||||
|
- https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-MML-AM_CHTML
|
||||||
|
|||||||
@@ -79,9 +79,9 @@ def check_structure() -> None:
|
|||||||
report("whitespace/line_length", f"{file}:{lineno+1} ({current_section})", f"line is too long ({len(line)} vs. 160 chars)")
|
report("whitespace/line_length", f"{file}:{lineno+1} ({current_section})", f"line is too long ({len(line)} vs. 160 chars)")
|
||||||
|
|
||||||
# sections in `<!-- NOLINT -->` comments are treated as present
|
# sections in `<!-- NOLINT -->` comments are treated as present
|
||||||
nolint_match = re.match(r"<!--\s*NOLINT\s+(.*?)\s*-->", line)
|
if line.startswith("<!-- NOLINT"):
|
||||||
if nolint_match:
|
current_section = line.strip("<!-- NOLINT")
|
||||||
current_section = nolint_match.group(1)
|
current_section = current_section.strip(" -->")
|
||||||
existing_sections.append(current_section)
|
existing_sections.append(current_section)
|
||||||
|
|
||||||
# check if sections are correct
|
# check if sections are correct
|
||||||
@@ -97,7 +97,7 @@ def check_structure() -> None:
|
|||||||
if len(unexpected):
|
if len(unexpected):
|
||||||
report("style/numbering", f"{file}:{lineno} ({current_section})", f'unexpected overloads: {", ".join([f"({x})" for x in unexpected])}')
|
report("style/numbering", f"{file}:{lineno} ({current_section})", f'unexpected overloads: {", ".join([f"({x})" for x in unexpected])}')
|
||||||
|
|
||||||
current_section = line[3:]
|
current_section = line.strip("## ")
|
||||||
existing_sections.append(current_section)
|
existing_sections.append(current_section)
|
||||||
|
|
||||||
if current_section in expected_sections:
|
if current_section in expected_sections:
|
||||||
@@ -141,7 +141,7 @@ def check_structure() -> None:
|
|||||||
# check that non-example admonitions have titles
|
# check that non-example admonitions have titles
|
||||||
untitled_admonition = re.match(r"^(\?\?\?|!!!) ([^ ]+)$", line)
|
untitled_admonition = re.match(r"^(\?\?\?|!!!) ([^ ]+)$", line)
|
||||||
if untitled_admonition and untitled_admonition.group(2) != "example":
|
if untitled_admonition and untitled_admonition.group(2) != "example":
|
||||||
report("style/admonition_title", f"{file}:{lineno+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
|
previous_line = line
|
||||||
|
|
||||||
|
|||||||
@@ -471,13 +471,11 @@ inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<
|
|||||||
j = { std::get<Idx>(t)... };
|
j = { std::get<Idx>(t)... };
|
||||||
}
|
}
|
||||||
|
|
||||||
// A one-element braced list does not reliably wrap its element: with
|
#if JSON_BRACE_INIT_COPY_SEMANTICS
|
||||||
// JSON_BRACE_INIT_COPY_SEMANTICS it copies it, which would serialize
|
// JSON_BRACE_INIT_COPY_SEMANTICS makes a one-element braced list copy its
|
||||||
// std::tuple<int>{5} as 5 rather than [5], and some compilers (e.g., Apple clang
|
// element instead of wrapping it, which would serialize std::tuple<int>{5} as 5
|
||||||
// 15 and 16) copy an element that is itself a basic_json even without it, so
|
// rather than [5]. Build what the default deduction builds instead: an object
|
||||||
// std::tuple<json>{true} became true rather than [true]. Build what the default
|
// if the element is a [string, value] pair, a one-element array otherwise.
|
||||||
// deduction builds instead: an object if the element is a [string, value] pair,
|
|
||||||
// a one-element array otherwise.
|
|
||||||
template<typename BasicJsonType, typename Tuple>
|
template<typename BasicJsonType, typename Tuple>
|
||||||
inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<0> /*unused*/)
|
inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<0> /*unused*/)
|
||||||
{
|
{
|
||||||
@@ -495,6 +493,7 @@ inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<
|
|||||||
j = BasicJsonType::array({std::move(element)});
|
j = BasicJsonType::array({std::move(element)});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
template<typename BasicJsonType, typename Tuple>
|
template<typename BasicJsonType, typename Tuple>
|
||||||
inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& /*unused*/, index_sequence<> /*unused*/)
|
inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& /*unused*/, index_sequence<> /*unused*/)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm> // generate_n
|
||||||
#include <array> // array
|
#include <array> // array
|
||||||
#include <cmath> // ldexp
|
#include <cmath> // ldexp
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
@@ -122,16 +123,16 @@ class binary_reader
|
|||||||
~binary_reader() = default;
|
~binary_reader() = default;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief parse in the format the constructor was given
|
@param[in] format the binary format to parse
|
||||||
|
|
||||||
@param[in] sax_ a SAX event processor
|
@param[in] sax_ a SAX event processor
|
||||||
@param[in] strict whether to expect the input to be consumed completed
|
@param[in] strict whether to expect the input to be consumed completed
|
||||||
@param[in] tag_handler how to treat CBOR tags
|
@param[in] tag_handler how to treat CBOR tags
|
||||||
|
|
||||||
@return whether parsing was successful
|
@return whether parsing was successful
|
||||||
*/
|
*/
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
JSON_HEDLEY_NON_NULL(3)
|
||||||
bool sax_parse(json_sax_t* sax_,
|
bool sax_parse(const input_format_t format,
|
||||||
|
json_sax_t* sax_,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
||||||
{
|
{
|
||||||
@@ -140,14 +141,14 @@ class binary_reader
|
|||||||
bon8_pushback_size = 0;
|
bon8_pushback_size = 0;
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
switch (input_format)
|
switch (format)
|
||||||
{
|
{
|
||||||
case input_format_t::bson:
|
case input_format_t::bson:
|
||||||
result = parse_bson_internal();
|
result = parse_bson_internal();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case input_format_t::cbor:
|
case input_format_t::cbor:
|
||||||
result = parse_cbor_internal(tag_handler);
|
result = parse_cbor_internal(true, tag_handler);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case input_format_t::msgpack:
|
case input_format_t::msgpack:
|
||||||
@@ -270,22 +271,6 @@ class binary_reader
|
|||||||
return enter_container(/*is_object*/true, len, type_marker);
|
return enter_container(/*is_object*/true, len, type_marker);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief close the innermost open array or object
|
|
||||||
|
|
||||||
Pops the container opened by the matching @ref enter_container call and
|
|
||||||
emits the SAX end event. Every format-specific driver otherwise repeated
|
|
||||||
the same pop-then-dispatch sequence at its own close site.
|
|
||||||
|
|
||||||
@return whether the SAX parser accepted the end event
|
|
||||||
*/
|
|
||||||
bool leave_container()
|
|
||||||
{
|
|
||||||
const bool is_object = container_stack.back().is_object;
|
|
||||||
container_stack.pop_back();
|
|
||||||
return is_object ? sax->end_object() : sax->end_array();
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////
|
//////////
|
||||||
// BSON //
|
// BSON //
|
||||||
//////////
|
//////////
|
||||||
@@ -370,8 +355,8 @@ class binary_reader
|
|||||||
if (element_type == 0) // end of the innermost document
|
if (element_type == 0) // end of the innermost document
|
||||||
{
|
{
|
||||||
// a copy, not a reference: it must stay valid across the
|
// a copy, not a reference: it must stay valid across the
|
||||||
// pop_back() inside leave_container() below, which destroys
|
// pop_back() below, which destroys the container_stack
|
||||||
// the container_stack element it would otherwise alias
|
// element it would otherwise alias
|
||||||
const container_frame top = container_stack.back();
|
const container_frame top = container_stack.back();
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!check_bson_document_size(top.start_position, top.declared_size)))
|
if (JSON_HEDLEY_UNLIKELY(!check_bson_document_size(top.start_position, top.declared_size)))
|
||||||
@@ -379,7 +364,8 @@ class binary_reader
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!leave_container()))
|
container_stack.pop_back();
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(top.is_object ? !sax->end_object() : !sax->end_array()))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -874,13 +860,29 @@ class binary_reader
|
|||||||
return enter_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));
|
return enter_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));
|
||||||
|
|
||||||
case 0x98: // array (one-byte uint8_t for n follows)
|
case 0x98: // array (one-byte uint8_t for n follows)
|
||||||
|
{
|
||||||
|
std::uint8_t len{};
|
||||||
|
return get_number(input_format_t::cbor, len) && enter_array(static_cast<std::size_t>(len));
|
||||||
|
}
|
||||||
|
|
||||||
case 0x99: // array (two-byte uint16_t for n follow)
|
case 0x99: // array (two-byte uint16_t for n follow)
|
||||||
|
{
|
||||||
|
std::uint16_t len{};
|
||||||
|
return get_number(input_format_t::cbor, len) && enter_array(static_cast<std::size_t>(len));
|
||||||
|
}
|
||||||
|
|
||||||
case 0x9A: // array (four-byte uint32_t for n follow)
|
case 0x9A: // array (four-byte uint32_t for n follow)
|
||||||
|
{
|
||||||
|
std::uint32_t len{};
|
||||||
|
std::size_t size{};
|
||||||
|
return get_number(input_format_t::cbor, len) && get_cbor_container_size(len, size, "array") && enter_array(size);
|
||||||
|
}
|
||||||
|
|
||||||
case 0x9B: // array (eight-byte uint64_t for n follow)
|
case 0x9B: // array (eight-byte uint64_t for n follow)
|
||||||
{
|
{
|
||||||
std::uint64_t len{};
|
std::uint64_t len{};
|
||||||
std::size_t size{};
|
std::size_t size{};
|
||||||
return get_cbor_argument(len) && get_cbor_container_size(len, size, "array") && enter_array(size);
|
return get_number(input_format_t::cbor, len) && get_cbor_container_size(len, size, "array") && enter_array(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x9F: // array (indefinite length)
|
case 0x9F: // array (indefinite length)
|
||||||
@@ -914,19 +916,35 @@ class binary_reader
|
|||||||
return enter_object(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));
|
return enter_object(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));
|
||||||
|
|
||||||
case 0xB8: // map (one-byte uint8_t for n follows)
|
case 0xB8: // map (one-byte uint8_t for n follows)
|
||||||
|
{
|
||||||
|
std::uint8_t len{};
|
||||||
|
return get_number(input_format_t::cbor, len) && enter_object(static_cast<std::size_t>(len));
|
||||||
|
}
|
||||||
|
|
||||||
case 0xB9: // map (two-byte uint16_t for n follow)
|
case 0xB9: // map (two-byte uint16_t for n follow)
|
||||||
|
{
|
||||||
|
std::uint16_t len{};
|
||||||
|
return get_number(input_format_t::cbor, len) && enter_object(static_cast<std::size_t>(len));
|
||||||
|
}
|
||||||
|
|
||||||
case 0xBA: // map (four-byte uint32_t for n follow)
|
case 0xBA: // map (four-byte uint32_t for n follow)
|
||||||
|
{
|
||||||
|
std::uint32_t len{};
|
||||||
|
std::size_t size{};
|
||||||
|
return get_number(input_format_t::cbor, len) && get_cbor_container_size(len, size, "map") && enter_object(size);
|
||||||
|
}
|
||||||
|
|
||||||
case 0xBB: // map (eight-byte uint64_t for n follow)
|
case 0xBB: // map (eight-byte uint64_t for n follow)
|
||||||
{
|
{
|
||||||
std::uint64_t len{};
|
std::uint64_t len{};
|
||||||
std::size_t size{};
|
std::size_t size{};
|
||||||
return get_cbor_argument(len) && get_cbor_container_size(len, size, "map") && enter_object(size);
|
return get_number(input_format_t::cbor, len) && get_cbor_container_size(len, size, "map") && enter_object(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xBF: // map (indefinite length)
|
case 0xBF: // map (indefinite length)
|
||||||
return enter_object(detail::unknown_size());
|
return enter_object(detail::unknown_size());
|
||||||
|
|
||||||
case 0xC0: // tagged item (tag value 0-23, in the head itself)
|
case 0xC0: // tagged item
|
||||||
case 0xC1:
|
case 0xC1:
|
||||||
case 0xC2:
|
case 0xC2:
|
||||||
case 0xC3:
|
case 0xC3:
|
||||||
@@ -950,22 +968,6 @@ class binary_reader
|
|||||||
case 0xD5:
|
case 0xD5:
|
||||||
case 0xD6:
|
case 0xD6:
|
||||||
case 0xD7:
|
case 0xD7:
|
||||||
{
|
|
||||||
if (tag_handler == cbor_tag_handler_t::error)
|
|
||||||
{
|
|
||||||
auto last_token = get_token_string();
|
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
|
||||||
exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore and store: the tag value is already in the head, so
|
|
||||||
// there is nothing left to read here; the tagged value that
|
|
||||||
// follows is read by the loop in parse_cbor_internal() rather
|
|
||||||
// than by recursing here
|
|
||||||
tag_pending = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0xD8: // tagged item (1 byte follows)
|
case 0xD8: // tagged item (1 byte follows)
|
||||||
case 0xD9: // tagged item (2 bytes follow)
|
case 0xD9: // tagged item (2 bytes follow)
|
||||||
case 0xDA: // tagged item (4 bytes follow)
|
case 0xDA: // tagged item (4 bytes follow)
|
||||||
@@ -982,11 +984,47 @@ class binary_reader
|
|||||||
|
|
||||||
case cbor_tag_handler_t::ignore:
|
case cbor_tag_handler_t::ignore:
|
||||||
{
|
{
|
||||||
// ignore the tag's binary subtype argument
|
// ignore binary subtype
|
||||||
std::uint64_t subtype_to_ignore{};
|
switch (current)
|
||||||
if (!get_cbor_argument(subtype_to_ignore))
|
|
||||||
{
|
{
|
||||||
return false;
|
case 0xD8:
|
||||||
|
{
|
||||||
|
std::uint8_t subtype_to_ignore{};
|
||||||
|
if (!get_number(input_format_t::cbor, subtype_to_ignore))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xD9:
|
||||||
|
{
|
||||||
|
std::uint16_t subtype_to_ignore{};
|
||||||
|
if (!get_number(input_format_t::cbor, subtype_to_ignore))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xDA:
|
||||||
|
{
|
||||||
|
std::uint32_t subtype_to_ignore{};
|
||||||
|
if (!get_number(input_format_t::cbor, subtype_to_ignore))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xDB:
|
||||||
|
{
|
||||||
|
std::uint64_t subtype_to_ignore{};
|
||||||
|
if (!get_number(input_format_t::cbor, subtype_to_ignore))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
// the tagged value follows; it is read by the loop in
|
// the tagged value follows; it is read by the loop in
|
||||||
// parse_cbor_internal() rather than by recursing here
|
// parse_cbor_internal() rather than by recursing here
|
||||||
@@ -996,15 +1034,57 @@ class binary_reader
|
|||||||
|
|
||||||
case cbor_tag_handler_t::store:
|
case cbor_tag_handler_t::store:
|
||||||
{
|
{
|
||||||
// use binary subtype and store in a binary container
|
|
||||||
std::uint64_t subtype{};
|
|
||||||
if (!get_cbor_argument(subtype))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
binary_t b;
|
binary_t b;
|
||||||
b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
|
// use binary subtype and store in a binary container
|
||||||
|
switch (current)
|
||||||
|
{
|
||||||
|
case 0xD8:
|
||||||
|
{
|
||||||
|
std::uint8_t subtype{};
|
||||||
|
if (!get_number(input_format_t::cbor, subtype))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xD9:
|
||||||
|
{
|
||||||
|
std::uint16_t subtype{};
|
||||||
|
if (!get_number(input_format_t::cbor, subtype))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xDA:
|
||||||
|
{
|
||||||
|
std::uint32_t subtype{};
|
||||||
|
if (!get_number(input_format_t::cbor, subtype))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xDB:
|
||||||
|
{
|
||||||
|
std::uint64_t subtype{};
|
||||||
|
if (!get_number(input_format_t::cbor, subtype))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
// as above, the tagged value is read by the caller
|
||||||
|
tag_pending = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
get();
|
get();
|
||||||
// a byte string (the heads accepted by get_cbor_binary) keeps the tag as subtype
|
// a byte string (the heads accepted by get_cbor_binary) keeps the tag as subtype
|
||||||
if ((current >= 0x40 && current <= 0x5B) || current == 0x5F)
|
if ((current >= 0x40 && current <= 0x5B) || current == 0x5F)
|
||||||
@@ -1035,7 +1115,52 @@ class binary_reader
|
|||||||
return sax->null();
|
return sax->null();
|
||||||
|
|
||||||
case 0xF9: // Half-Precision Float (two-byte IEEE 754)
|
case 0xF9: // Half-Precision Float (two-byte IEEE 754)
|
||||||
return get_half_float(input_format_t::cbor, false);
|
{
|
||||||
|
const auto byte1_raw = get();
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number")))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto byte2_raw = get();
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number")))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto byte1 = static_cast<unsigned char>(byte1_raw);
|
||||||
|
const auto byte2 = static_cast<unsigned char>(byte2_raw);
|
||||||
|
|
||||||
|
// Code from RFC 8949, Appendix D, Figure 3:
|
||||||
|
// As half-precision floating-point numbers were only added
|
||||||
|
// to IEEE 754 in 2008, today's programming platforms often
|
||||||
|
// still only have limited support for them. It is very
|
||||||
|
// easy to include at least decoding support for them even
|
||||||
|
// without such support. An example of a small decoder for
|
||||||
|
// half-precision floating-point numbers in the C language
|
||||||
|
// is shown in Fig. 3.
|
||||||
|
const auto half = static_cast<unsigned int>((byte1 << 8u) + byte2);
|
||||||
|
const double val = [&half]
|
||||||
|
{
|
||||||
|
const int exp = (half >> 10u) & 0x1Fu;
|
||||||
|
const unsigned int mant = half & 0x3FFu;
|
||||||
|
JSON_ASSERT(exp <= 31);
|
||||||
|
JSON_ASSERT(mant <= 1023);
|
||||||
|
switch (exp)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return std::ldexp(mant, -24);
|
||||||
|
case 31:
|
||||||
|
return (mant == 0)
|
||||||
|
? std::numeric_limits<double>::infinity()
|
||||||
|
: std::numeric_limits<double>::quiet_NaN();
|
||||||
|
default:
|
||||||
|
return std::ldexp(mant + 1024, exp - 25);
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
return sax->number_float((half & 0x8000u) != 0
|
||||||
|
? static_cast<number_float_t>(-val)
|
||||||
|
: static_cast<number_float_t>(val), "");
|
||||||
|
}
|
||||||
|
|
||||||
case 0xFA: // Single-Precision Float (four-byte IEEE 754)
|
case 0xFA: // Single-Precision Float (four-byte IEEE 754)
|
||||||
{
|
{
|
||||||
@@ -1414,73 +1539,6 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief read a CBOR argument (additional information 24-27) of the width
|
|
||||||
@ref current announces
|
|
||||||
|
|
||||||
The lower 5 bits of @a current (0x18-0x1B) select a 1/2/4/8-byte
|
|
||||||
big-endian unsigned integer that follows the head byte; this is shared by
|
|
||||||
every major type that uses this encoding (unsigned/negative integers,
|
|
||||||
strings, arrays, maps, tags). Reading always goes through @ref get_number,
|
|
||||||
so EOF is reported the same way as before this helper existed.
|
|
||||||
|
|
||||||
@param[out] value the decoded argument
|
|
||||||
@return whether reading succeeded
|
|
||||||
*/
|
|
||||||
bool get_cbor_argument(std::uint64_t& value)
|
|
||||||
{
|
|
||||||
switch (current & 0x1F)
|
|
||||||
{
|
|
||||||
case 0x18: // 1 byte
|
|
||||||
{
|
|
||||||
std::uint8_t n{};
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::cbor, n)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
value = n;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0x19: // 2 bytes
|
|
||||||
{
|
|
||||||
std::uint16_t n{};
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::cbor, n)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
value = n;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0x1A: // 4 bytes
|
|
||||||
{
|
|
||||||
std::uint32_t n{};
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::cbor, n)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
value = n;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0x1B: // 8 bytes
|
|
||||||
{
|
|
||||||
std::uint64_t n{};
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::cbor, n)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
value = n;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: // LCOV_EXCL_LINE
|
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
|
||||||
return false; // LCOV_EXCL_LINE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief narrow a definite CBOR array/map length to std::size_t
|
@brief narrow a definite CBOR array/map length to std::size_t
|
||||||
|
|
||||||
@@ -1513,15 +1571,19 @@ class binary_reader
|
|||||||
enclosing container after each element, so that the nesting depth of the
|
enclosing container after each element, so that the nesting depth of the
|
||||||
input costs heap rather than native stack (see #5104).
|
input costs heap rather than native stack (see #5104).
|
||||||
|
|
||||||
|
@param[in] get_char whether a new character should be retrieved from the
|
||||||
|
input (true) or whether the last read character
|
||||||
|
@a current should be considered instead
|
||||||
@param[in] tag_handler how CBOR tags should be treated
|
@param[in] tag_handler how CBOR tags should be treated
|
||||||
|
|
||||||
@return whether reading the value succeeded
|
@return whether reading the value succeeded
|
||||||
*/
|
*/
|
||||||
bool parse_cbor_internal(const cbor_tag_handler_t tag_handler)
|
bool parse_cbor_internal(const bool get_char,
|
||||||
|
const cbor_tag_handler_t tag_handler)
|
||||||
{
|
{
|
||||||
// whether the next value starts at a fresh byte or at the one already
|
// whether the next value starts at a fresh byte or at the one already
|
||||||
// read into `current`
|
// read into `current`
|
||||||
bool fetch = true;
|
bool fetch = get_char;
|
||||||
|
|
||||||
// the key currently being read; hoisted out of the loop so that its
|
// the key currently being read; hoisted out of the loop so that its
|
||||||
// capacity is reused across elements and across nesting levels
|
// capacity is reused across elements and across nesting levels
|
||||||
@@ -1564,7 +1626,8 @@ class binary_reader
|
|||||||
|
|
||||||
if (at_end)
|
if (at_end)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!leave_container()))
|
container_stack.pop_back();
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(top.is_object ? !sax->end_object() : !sax->end_array()))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1613,6 +1676,9 @@ class binary_reader
|
|||||||
// MsgPack //
|
// MsgPack //
|
||||||
/////////////
|
/////////////
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@return whether a valid MessagePack value was passed to the SAX parser
|
||||||
|
*/
|
||||||
/*!
|
/*!
|
||||||
@brief read one MessagePack value
|
@brief read one MessagePack value
|
||||||
|
|
||||||
@@ -2311,7 +2377,8 @@ class binary_reader
|
|||||||
|
|
||||||
if (container_stack.back().remaining == 0)
|
if (container_stack.back().remaining == 0)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!leave_container()))
|
container_stack.pop_back();
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(is_object ? !sax->end_object() : !sax->end_array()))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2356,16 +2423,20 @@ class binary_reader
|
|||||||
////////////
|
////////////
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@param[in] get_char whether a new character should be retrieved from the
|
||||||
|
input (true, default) or whether the last read
|
||||||
|
character should be considered instead
|
||||||
|
|
||||||
@return whether a valid UBJSON value was passed to the SAX parser
|
@return whether a valid UBJSON value was passed to the SAX parser
|
||||||
*/
|
*/
|
||||||
bool parse_ubjson_internal()
|
bool parse_ubjson_internal(const bool get_char = true)
|
||||||
{
|
{
|
||||||
// the key currently being read; hoisted out of the loop so that its
|
// the key currently being read; hoisted out of the loop so that its
|
||||||
// capacity is reused across elements and across nesting levels
|
// capacity is reused across elements and across nesting levels
|
||||||
string_t key;
|
string_t key;
|
||||||
|
|
||||||
// the type marker of the value to read next
|
// the type marker of the value to read next
|
||||||
char_int_type prefix = get_ignore_noop();
|
char_int_type prefix = get_char ? get_ignore_noop() : current;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
@@ -2440,7 +2511,8 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!leave_container()))
|
container_stack.pop_back();
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(top.is_object ? !sax->end_object() : !sax->end_array()))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2648,42 +2720,6 @@ class binary_reader
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief read a UBJSON/BJData optimized-container count of a signed marker
|
|
||||||
type ('i', 'I', 'l', 'L') and narrow it to std::size_t
|
|
||||||
|
|
||||||
Every signed count marker rejects a negative value the same way (error
|
|
||||||
113); the value_in_range_of check additionally needed for 'L' is only
|
|
||||||
ever live when @a SignedType is std::int64_t on a target where
|
|
||||||
std::size_t is narrower (e.g. 32-bit), since 'i'/'I'/'l' can never exceed
|
|
||||||
std::size_t there.
|
|
||||||
|
|
||||||
@tparam SignedType std::int8_t, std::int16_t, std::int32_t or std::int64_t
|
|
||||||
@param[out] result the count narrowed to std::size_t
|
|
||||||
@return whether reading and validating succeeded
|
|
||||||
*/
|
|
||||||
template<typename SignedType>
|
|
||||||
bool get_ubjson_signed_count(std::size_t& result)
|
|
||||||
{
|
|
||||||
SignedType number{};
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(number < 0))
|
|
||||||
{
|
|
||||||
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
|
|
||||||
exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
|
|
||||||
}
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(number)))
|
|
||||||
{
|
|
||||||
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
|
|
||||||
exception_message(input_format, "integer value overflow", "size"), nullptr));
|
|
||||||
}
|
|
||||||
result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[out] result determined size
|
@param[out] result determined size
|
||||||
@param[in,out] is_ndarray for input, `true` means already inside an ndarray vector
|
@param[in,out] is_ndarray for input, `true` means already inside an ndarray vector
|
||||||
@@ -2716,16 +2752,73 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'i':
|
case 'i':
|
||||||
return get_ubjson_signed_count<std::int8_t>(result);
|
{
|
||||||
|
std::int8_t number{};
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (number < 0)
|
||||||
|
{
|
||||||
|
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
|
||||||
|
exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
|
||||||
|
}
|
||||||
|
result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
case 'I':
|
case 'I':
|
||||||
return get_ubjson_signed_count<std::int16_t>(result);
|
{
|
||||||
|
std::int16_t number{};
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (number < 0)
|
||||||
|
{
|
||||||
|
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
|
||||||
|
exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
|
||||||
|
}
|
||||||
|
result = static_cast<std::size_t>(number);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
return get_ubjson_signed_count<std::int32_t>(result);
|
{
|
||||||
|
std::int32_t number{};
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (number < 0)
|
||||||
|
{
|
||||||
|
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
|
||||||
|
exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
|
||||||
|
}
|
||||||
|
result = static_cast<std::size_t>(number);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
case 'L':
|
case 'L':
|
||||||
return get_ubjson_signed_count<std::int64_t>(result);
|
{
|
||||||
|
std::int64_t number{};
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (number < 0)
|
||||||
|
{
|
||||||
|
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
|
||||||
|
exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
|
||||||
|
}
|
||||||
|
if (!value_in_range_of<std::size_t>(number))
|
||||||
|
{
|
||||||
|
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
|
||||||
|
exception_message(input_format, "integer value overflow", "size"), nullptr));
|
||||||
|
}
|
||||||
|
result = static_cast<std::size_t>(number);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
case 'u':
|
case 'u':
|
||||||
{
|
{
|
||||||
@@ -2816,23 +2909,16 @@ class binary_reader
|
|||||||
result = 1;
|
result = 1;
|
||||||
for (auto i : dim)
|
for (auto i : dim)
|
||||||
{
|
{
|
||||||
// Pre-multiplication overflow check: since the loop above
|
// Pre-multiplication overflow check: if i > 0 and result > SIZE_MAX/i, then result*i would overflow.
|
||||||
// already rejected any zero dimension, i is always > 0
|
// This check must happen before multiplication since overflow detection after the fact is unreliable
|
||||||
// here, so result > SIZE_MAX/i means result*i would
|
// as modular arithmetic can produce any value, not just 0 or SIZE_MAX.
|
||||||
// overflow. This check must happen before multiplication
|
if (JSON_HEDLEY_UNLIKELY(i > 0 && result > (std::numeric_limits<std::size_t>::max)() / i))
|
||||||
// since overflow detection after the fact is unreliable,
|
|
||||||
// as modular arithmetic can produce any value, not just 0
|
|
||||||
// or SIZE_MAX.
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(result > (std::numeric_limits<std::size_t>::max)() / i))
|
|
||||||
{
|
{
|
||||||
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr));
|
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr));
|
||||||
}
|
}
|
||||||
result *= i;
|
result *= i;
|
||||||
// the pre-check above already rules out result becoming 0
|
// Additional post-multiplication check to catch any edge cases the pre-check might miss
|
||||||
// by overflow; the only value it cannot rule out is an
|
if (result == 0 || result == npos)
|
||||||
// exact match with npos, the sentinel reserved for an
|
|
||||||
// unknown-size container (see get_ubjson_size_type())
|
|
||||||
if (result == npos)
|
|
||||||
{
|
{
|
||||||
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr));
|
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr));
|
||||||
}
|
}
|
||||||
@@ -2893,7 +2979,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
result.second = get(); // must not ignore 'N', because 'N' maybe the type
|
result.second = get(); // must not ignore 'N', because 'N' maybe the type
|
||||||
if (input_format == input_format_t::bjdata
|
if (input_format == input_format_t::bjdata
|
||||||
&& JSON_HEDLEY_UNLIKELY(is_bjd_excluded_optimized_type(result.second)))
|
&& JSON_HEDLEY_UNLIKELY(std::binary_search(bjd_optimized_type_markers.begin(), bjd_optimized_type_markers.end(), result.second)))
|
||||||
{
|
{
|
||||||
auto last_token = get_token_string();
|
auto last_token = get_token_string();
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
@@ -3037,7 +3123,50 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return get_half_float(input_format, true);
|
const auto byte1_raw = get();
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number")))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto byte2_raw = get();
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number")))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto byte1 = static_cast<unsigned char>(byte1_raw);
|
||||||
|
const auto byte2 = static_cast<unsigned char>(byte2_raw);
|
||||||
|
|
||||||
|
// Code from RFC 8949, Appendix D, Figure 3:
|
||||||
|
// As half-precision floating-point numbers were only added
|
||||||
|
// to IEEE 754 in 2008, today's programming platforms often
|
||||||
|
// still only have limited support for them. It is very
|
||||||
|
// easy to include at least decoding support for them even
|
||||||
|
// without such support. An example of a small decoder for
|
||||||
|
// half-precision floating-point numbers in the C language
|
||||||
|
// is shown in Fig. 3.
|
||||||
|
const auto half = static_cast<unsigned int>((byte2 << 8u) + byte1);
|
||||||
|
const double val = [&half]
|
||||||
|
{
|
||||||
|
const int exp = (half >> 10u) & 0x1Fu;
|
||||||
|
const unsigned int mant = half & 0x3FFu;
|
||||||
|
JSON_ASSERT(exp <= 31);
|
||||||
|
JSON_ASSERT(mant <= 1023);
|
||||||
|
switch (exp)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return std::ldexp(mant, -24);
|
||||||
|
case 31:
|
||||||
|
return (mant == 0)
|
||||||
|
? std::numeric_limits<double>::infinity()
|
||||||
|
: std::numeric_limits<double>::quiet_NaN();
|
||||||
|
default:
|
||||||
|
return std::ldexp(mant + 1024, exp - 25);
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
return sax->number_float((half & 0x8000u) != 0
|
||||||
|
? static_cast<number_float_t>(-val)
|
||||||
|
: static_cast<number_float_t>(val), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'd':
|
case 'd':
|
||||||
@@ -3110,16 +3239,19 @@ class binary_reader
|
|||||||
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
||||||
{
|
{
|
||||||
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
||||||
const char* type_name = bjd_type_name(size_and_type.second);
|
auto it = std::lower_bound(bjd_types_map.begin(), bjd_types_map.end(), size_and_type.second, [](const bjd_type & p, char_int_type t)
|
||||||
|
{
|
||||||
|
return p.first < t;
|
||||||
|
});
|
||||||
string_t key = "_ArrayType_";
|
string_t key = "_ArrayType_";
|
||||||
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
|
if (JSON_HEDLEY_UNLIKELY(it == bjd_types_map.end() || it->first != size_and_type.second))
|
||||||
{
|
{
|
||||||
auto last_token = get_token_string();
|
auto last_token = get_token_string();
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
exception_message(input_format, "invalid byte: 0x" + last_token, "type"), nullptr));
|
exception_message(input_format, "invalid byte: 0x" + last_token, "type"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
string_t type = type_name; // sax->string() takes a reference
|
string_t type = it->second; // sax->string() takes a reference
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->string(type)))
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->string(type)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -3411,7 +3543,8 @@ class binary_reader
|
|||||||
|
|
||||||
if (at_end)
|
if (at_end)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!leave_container()))
|
container_stack.pop_back();
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(top.is_object ? !sax->end_object() : !sax->end_array()))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -3950,68 +4083,6 @@ class binary_reader
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief read and decode an IEEE 754 half-precision (16-bit) float
|
|
||||||
|
|
||||||
Used by CBOR (big endian) and BJData (little endian); the two formats
|
|
||||||
only differ in the byte order of the two bytes that make up the half.
|
|
||||||
|
|
||||||
@param[in] format the current format (for diagnostics)
|
|
||||||
@param[in] little_endian whether the two bytes are little endian (BJData)
|
|
||||||
or big endian (CBOR)
|
|
||||||
|
|
||||||
@return whether reading and decoding succeeded
|
|
||||||
*/
|
|
||||||
bool get_half_float(const input_format_t format, const bool little_endian)
|
|
||||||
{
|
|
||||||
const auto byte1_raw = get();
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number")))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto byte2_raw = get();
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number")))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto byte1 = static_cast<unsigned char>(byte1_raw);
|
|
||||||
const auto byte2 = static_cast<unsigned char>(byte2_raw);
|
|
||||||
|
|
||||||
// Code from RFC 8949, Appendix D, Figure 3:
|
|
||||||
// As half-precision floating-point numbers were only added
|
|
||||||
// to IEEE 754 in 2008, today's programming platforms often
|
|
||||||
// still only have limited support for them. It is very
|
|
||||||
// easy to include at least decoding support for them even
|
|
||||||
// without such support. An example of a small decoder for
|
|
||||||
// half-precision floating-point numbers in the C language
|
|
||||||
// is shown in Fig. 3.
|
|
||||||
const auto half = little_endian
|
|
||||||
? static_cast<unsigned int>((byte2 << 8u) + byte1)
|
|
||||||
: static_cast<unsigned int>((byte1 << 8u) + byte2);
|
|
||||||
const double val = [&half]
|
|
||||||
{
|
|
||||||
const int exp = (half >> 10u) & 0x1Fu;
|
|
||||||
const unsigned int mant = half & 0x3FFu;
|
|
||||||
JSON_ASSERT(exp <= 31);
|
|
||||||
JSON_ASSERT(mant <= 1023);
|
|
||||||
switch (exp)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
return std::ldexp(mant, -24);
|
|
||||||
case 31:
|
|
||||||
return (mant == 0)
|
|
||||||
? std::numeric_limits<double>::infinity()
|
|
||||||
: std::numeric_limits<double>::quiet_NaN();
|
|
||||||
default:
|
|
||||||
return std::ldexp(mant + 1024, exp - 25);
|
|
||||||
}
|
|
||||||
}();
|
|
||||||
return sax->number_float((half & 0x8000u) != 0
|
|
||||||
? static_cast<number_float_t>(-val)
|
|
||||||
: static_cast<number_float_t>(val), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief create a string by reading characters from the input
|
@brief create a string by reading characters from the input
|
||||||
|
|
||||||
@@ -4237,61 +4308,38 @@ class binary_reader
|
|||||||
/// BON8: number of bytes in @ref bon8_pushback
|
/// BON8: number of bytes in @ref bon8_pushback
|
||||||
std::size_t bon8_pushback_size = 0;
|
std::size_t bon8_pushback_size = 0;
|
||||||
|
|
||||||
|
// excluded markers in bjdata optimized type
|
||||||
|
#define JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_ \
|
||||||
|
make_array<char_int_type>('F', 'H', 'N', 'S', 'T', 'Z', '[', '{')
|
||||||
|
|
||||||
|
#define JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_ \
|
||||||
|
make_array<bjd_type>( \
|
||||||
|
bjd_type{'B', "byte"}, \
|
||||||
|
bjd_type{'C', "char"}, \
|
||||||
|
bjd_type{'D', "double"}, \
|
||||||
|
bjd_type{'I', "int16"}, \
|
||||||
|
bjd_type{'L', "int64"}, \
|
||||||
|
bjd_type{'M', "uint64"}, \
|
||||||
|
bjd_type{'U', "uint8"}, \
|
||||||
|
bjd_type{'d', "single"}, \
|
||||||
|
bjd_type{'i', "int8"}, \
|
||||||
|
bjd_type{'l', "int32"}, \
|
||||||
|
bjd_type{'m', "uint32"}, \
|
||||||
|
bjd_type{'u', "uint16"})
|
||||||
|
|
||||||
JSON_PRIVATE_UNLESS_TESTED:
|
JSON_PRIVATE_UNLESS_TESTED:
|
||||||
/*!
|
// lookup tables
|
||||||
@brief whether @a marker is excluded from BJData's optimized ND-array types
|
// NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)
|
||||||
@return whether @a marker is one of 'F', 'H', 'N', 'S', 'T', 'Z', '[', '{'
|
const decltype(JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_) bjd_optimized_type_markers =
|
||||||
|
JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_;
|
||||||
|
|
||||||
Mirrors binary_writer's @ref binary_writer::is_bjdata_excluded_type_marker
|
using bjd_type = std::pair<char_int_type, string_t>;
|
||||||
"is_bjdata_excluded_type_marker()`, which encodes the same list the other
|
// NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)
|
||||||
way; keep the two in sync.
|
const decltype(JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_) bjd_types_map =
|
||||||
*/
|
JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_;
|
||||||
static constexpr bool is_bjd_excluded_optimized_type(const char_int_type marker) noexcept
|
|
||||||
{
|
|
||||||
return marker == '[' || marker == '{' || marker == 'S' || marker == 'H'
|
|
||||||
|| marker == 'T' || marker == 'F' || marker == 'N' || marker == 'Z';
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
#undef JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_
|
||||||
@brief look up the ND-array element type name for a BJData dtype marker
|
#undef JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_
|
||||||
@return the type name ("uint8", "int8", ...), or nullptr if @a marker does
|
|
||||||
not name a known dtype
|
|
||||||
|
|
||||||
A C++11 `constexpr` function cannot contain a `switch`, so this is a
|
|
||||||
plain (non-constexpr) switch instead.
|
|
||||||
*/
|
|
||||||
static const char* bjd_type_name(const char_int_type marker)
|
|
||||||
{
|
|
||||||
switch (marker)
|
|
||||||
{
|
|
||||||
case 'B':
|
|
||||||
return "byte";
|
|
||||||
case 'C':
|
|
||||||
return "char";
|
|
||||||
case 'D':
|
|
||||||
return "double";
|
|
||||||
case 'I':
|
|
||||||
return "int16";
|
|
||||||
case 'L':
|
|
||||||
return "int64";
|
|
||||||
case 'M':
|
|
||||||
return "uint64";
|
|
||||||
case 'U':
|
|
||||||
return "uint8";
|
|
||||||
case 'd':
|
|
||||||
return "single";
|
|
||||||
case 'i':
|
|
||||||
return "int8";
|
|
||||||
case 'l':
|
|
||||||
return "int32";
|
|
||||||
case 'm':
|
|
||||||
return "uint32";
|
|
||||||
case 'u':
|
|
||||||
return "uint16";
|
|
||||||
default:
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef JSON_HAS_CPP_17
|
#ifndef JSON_HAS_CPP_17
|
||||||
|
|||||||
@@ -359,7 +359,6 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
|
|
||||||
// Macros to simplify conversion from/to types
|
// Macros to simplify conversion from/to types
|
||||||
|
|
||||||
// NLOHMANN_JSON_EXPAND to NLOHMANN_JSON_DOUBLE_PASTE63 are generated by tools/macro_builder (see its README.md)
|
|
||||||
#define NLOHMANN_JSON_EXPAND( x ) x
|
#define NLOHMANN_JSON_EXPAND( x ) x
|
||||||
#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME
|
#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME
|
||||||
#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \
|
#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \
|
||||||
@@ -622,10 +621,6 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
// arguments, so dispatching on Type,BaseType,member... directly would run out
|
// arguments, so dispatching on Type,BaseType,member... directly would run out
|
||||||
// one slot early and cap the derived-type macros at 62 members instead of the
|
// one slot early and cap the derived-type macros at 62 members instead of the
|
||||||
// 63 that NLOHMANN_JSON_PASTE supports.
|
// 63 that NLOHMANN_JSON_PASTE supports.
|
||||||
//
|
|
||||||
// The slot table below (down to the closing NLOHMANN_JSON_TYPE_BODY_SENTINEL))
|
|
||||||
// is generated by tools/macro_builder (see its README.md; run with the
|
|
||||||
// "type_body" argument).
|
|
||||||
#define NLOHMANN_JSON_TYPE_BODY(Prefix, ...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \
|
#define NLOHMANN_JSON_TYPE_BODY(Prefix, ...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \
|
||||||
Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, \
|
Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, \
|
||||||
Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, \
|
Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, Prefix ## MEMBERS, \
|
||||||
@@ -920,7 +915,3 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
#ifndef JSON_DISABLE_ENUM_SERIALIZATION
|
#ifndef JSON_DISABLE_ENUM_SERIALIZATION
|
||||||
#define JSON_DISABLE_ENUM_SERIALIZATION 0
|
#define JSON_DISABLE_ENUM_SERIALIZATION 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
|
||||||
#define JSON_DISABLE_TUPLE_REFERENCE_CONVERSION 0
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
#undef JSON_INLINE_VARIABLE
|
#undef JSON_INLINE_VARIABLE
|
||||||
#undef JSON_NO_UNIQUE_ADDRESS
|
#undef JSON_NO_UNIQUE_ADDRESS
|
||||||
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
||||||
#undef JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
|
||||||
|
|
||||||
#ifndef JSON_TEST_KEEP_MACROS
|
#ifndef JSON_TEST_KEEP_MACROS
|
||||||
#undef JSON_CATCH
|
#undef JSON_CATCH
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array> // array
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type
|
#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type
|
||||||
#include <utility> // index_sequence, make_index_sequence, index_sequence_for
|
#include <utility> // index_sequence, make_index_sequence, index_sequence_for
|
||||||
@@ -160,5 +161,11 @@ struct static_const
|
|||||||
constexpr T static_const<T>::value;
|
constexpr T static_const<T>::value;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
constexpr std::array<T, sizeof...(Args)> make_array(Args&& ... args)
|
||||||
|
{
|
||||||
|
return std::array<T, sizeof...(Args)> {{static_cast<T>(std::forward<Args>(args))...}};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|||||||
@@ -189,6 +189,37 @@ struct actual_object_comparator
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
using actual_object_comparator_t = typename actual_object_comparator<BasicJsonType>::type;
|
using actual_object_comparator_t = typename actual_object_comparator<BasicJsonType>::type;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using detect_key_comp = decltype(std::declval<const T&>().key_comp());
|
||||||
|
|
||||||
|
// whether ObjectType can be constructed from a pair of Iterator together with
|
||||||
|
// a copy of its own comparator, the way std::map can: it needs a nested
|
||||||
|
// key_compare, a const key_comp() convertible to it, and a matching
|
||||||
|
// (Iterator, Iterator, const key_compare&) constructor.
|
||||||
|
//
|
||||||
|
// used to preserve a stateful comparator when a copy is built from a range
|
||||||
|
// past the iterative deep copy's nesting bound (see copy_object_level); an
|
||||||
|
// object type that does not satisfy this, such as nlohmann::ordered_map
|
||||||
|
// (which has key_compare for its std::map-like interface, but no key_comp()),
|
||||||
|
// keeps default-constructing its comparator, just as it always has
|
||||||
|
template<typename ObjectType, typename Iterator, typename = void>
|
||||||
|
struct is_comparator_constructible_object_type_impl : std::false_type {};
|
||||||
|
|
||||||
|
template<typename ObjectType, typename Iterator>
|
||||||
|
struct is_comparator_constructible_object_type_impl <
|
||||||
|
ObjectType, Iterator, enable_if_t<is_detected<detect_key_compare, ObjectType>::value >>
|
||||||
|
{
|
||||||
|
using key_compare = typename ObjectType::key_compare;
|
||||||
|
|
||||||
|
static constexpr bool value =
|
||||||
|
is_detected_convertible<key_compare, detect_key_comp, ObjectType>::value &&
|
||||||
|
std::is_constructible<ObjectType, Iterator, Iterator, const key_compare&>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ObjectType, typename Iterator>
|
||||||
|
struct is_comparator_constructible_object_type
|
||||||
|
: is_comparator_constructible_object_type_impl<ObjectType, Iterator> {};
|
||||||
|
|
||||||
/////////////////
|
/////////////////
|
||||||
// char_traits //
|
// char_traits //
|
||||||
/////////////////
|
/////////////////
|
||||||
@@ -636,18 +667,6 @@ template<typename BasicJsonType, typename CompatibleType>
|
|||||||
struct is_compatible_type
|
struct is_compatible_type
|
||||||
: is_compatible_type_impl<BasicJsonType, CompatibleType> {};
|
: is_compatible_type_impl<BasicJsonType, CompatibleType> {};
|
||||||
|
|
||||||
// a one-element std::tuple holding a reference to BasicJsonType, as created by
|
|
||||||
// std::forward_as_tuple(j); see JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
|
||||||
template<typename BasicJsonType, typename T>
|
|
||||||
struct is_basic_json_reference_tuple : std::false_type {};
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename T>
|
|
||||||
struct is_basic_json_reference_tuple<BasicJsonType, std::tuple<T>>
|
|
||||||
{
|
|
||||||
static constexpr bool value =
|
|
||||||
std::is_reference<T>::value && std::is_same<uncvref_t<T>, BasicJsonType>::value;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename CompatibleArrayType>
|
template<typename BasicJsonType, typename CompatibleArrayType>
|
||||||
struct is_compatible_binary_type
|
struct is_compatible_binary_type
|
||||||
{
|
{
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+128
-342
@@ -29,9 +29,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <algorithm> // all_of, find, for_each, none_of
|
#include <algorithm> // all_of, find, for_each, none_of
|
||||||
#include <cmath> // isnan
|
|
||||||
#include <cstddef> // nullptr_t, ptrdiff_t, size_t
|
#include <cstddef> // nullptr_t, ptrdiff_t, size_t
|
||||||
#include <cstdint> // uint8_t
|
|
||||||
#include <functional> // hash, less
|
#include <functional> // hash, less
|
||||||
#include <initializer_list> // initializer_list
|
#include <initializer_list> // initializer_list
|
||||||
#ifndef JSON_NO_IO
|
#ifndef JSON_NO_IO
|
||||||
@@ -40,28 +38,19 @@
|
|||||||
#include <iterator> // make_move_iterator, random_access_iterator_tag
|
#include <iterator> // make_move_iterator, random_access_iterator_tag
|
||||||
#include <limits> // numeric_limits
|
#include <limits> // numeric_limits
|
||||||
#include <memory> // unique_ptr
|
#include <memory> // unique_ptr
|
||||||
#include <set> // swap, operator!=
|
|
||||||
#include <stdexcept> // length_error
|
#include <stdexcept> // length_error
|
||||||
#include <string> // string, stoi, to_string
|
#include <string> // string, stoi, to_string
|
||||||
#include <type_traits> // enable_if_t, is_same, is_scalar, ...
|
|
||||||
#include <unordered_map> // swap (for the from_json(..., std::unordered_map&) overload)
|
|
||||||
#include <utility> // declval, forward, move, pair, swap
|
#include <utility> // declval, forward, move, pair, swap
|
||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
|
|
||||||
// keep: json.hpp's own basic_json<> default template arguments need the complete definition of
|
#include <nlohmann/adl_serializer.hpp>
|
||||||
// each of these, not only the forward declarations from json_fwd.hpp, so IWYU's suggestion to
|
|
||||||
// drop them (nothing in this file otherwise names the type) would break every downstream
|
|
||||||
// translation unit that relies on basic_json<>'s defaults actually being usable.
|
|
||||||
#include <nlohmann/adl_serializer.hpp> // IWYU pragma: keep
|
|
||||||
#include <nlohmann/byte_container_with_subtype.hpp>
|
#include <nlohmann/byte_container_with_subtype.hpp>
|
||||||
#include <nlohmann/detail/abi_macros.hpp>
|
#include <nlohmann/detail/conversions/from_json.hpp>
|
||||||
#include <nlohmann/detail/conversions/from_json.hpp> // IWYU pragma: keep
|
#include <nlohmann/detail/conversions/to_json.hpp>
|
||||||
#include <nlohmann/detail/conversions/to_json.hpp> // IWYU pragma: keep
|
|
||||||
#include <nlohmann/detail/exceptions.hpp>
|
#include <nlohmann/detail/exceptions.hpp>
|
||||||
#include <nlohmann/detail/hash.hpp>
|
#include <nlohmann/detail/hash.hpp>
|
||||||
#include <nlohmann/detail/input/binary_reader.hpp>
|
#include <nlohmann/detail/input/binary_reader.hpp>
|
||||||
#include <nlohmann/detail/input/input_adapters.hpp>
|
#include <nlohmann/detail/input/input_adapters.hpp>
|
||||||
#include <nlohmann/detail/input/json_sax.hpp>
|
|
||||||
#include <nlohmann/detail/input/lexer.hpp>
|
#include <nlohmann/detail/input/lexer.hpp>
|
||||||
#include <nlohmann/detail/input/parser.hpp>
|
#include <nlohmann/detail/input/parser.hpp>
|
||||||
#include <nlohmann/detail/iterators/internal_iterator.hpp>
|
#include <nlohmann/detail/iterators/internal_iterator.hpp>
|
||||||
@@ -73,7 +62,6 @@
|
|||||||
#include <nlohmann/detail/json_pointer.hpp>
|
#include <nlohmann/detail/json_pointer.hpp>
|
||||||
#include <nlohmann/detail/json_ref.hpp>
|
#include <nlohmann/detail/json_ref.hpp>
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
#include <nlohmann/detail/meta/detected.hpp>
|
|
||||||
#include <nlohmann/detail/string_concat.hpp>
|
#include <nlohmann/detail/string_concat.hpp>
|
||||||
#include <nlohmann/detail/string_escape.hpp>
|
#include <nlohmann/detail/string_escape.hpp>
|
||||||
#include <nlohmann/detail/string_utils.hpp>
|
#include <nlohmann/detail/string_utils.hpp>
|
||||||
@@ -85,8 +73,7 @@
|
|||||||
#include <nlohmann/detail/recursion_depth_limit.hpp>
|
#include <nlohmann/detail/recursion_depth_limit.hpp>
|
||||||
#include <nlohmann/detail/value_t.hpp>
|
#include <nlohmann/detail/value_t.hpp>
|
||||||
#include <nlohmann/json_fwd.hpp>
|
#include <nlohmann/json_fwd.hpp>
|
||||||
#include <nlohmann/ordered_map.hpp> // IWYU pragma: keep
|
#include <nlohmann/ordered_map.hpp>
|
||||||
#include <nlohmann/thirdparty/hedley/hedley.hpp>
|
|
||||||
|
|
||||||
#if defined(JSON_HAS_CPP_17)
|
#if defined(JSON_HAS_CPP_17)
|
||||||
#if JSON_HAS_STATIC_RTTI
|
#if JSON_HAS_STATIC_RTTI
|
||||||
@@ -919,11 +906,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
/*!
|
/*!
|
||||||
@brief how many levels the operation going on in this thread has descended into
|
@brief how many levels the operation going on in this thread has descended into
|
||||||
|
|
||||||
Copying a value, converting one from another specialization, and comparing
|
Copying a value and comparing two values share this count. The library never
|
||||||
two values share this count. The library never nests one of them inside
|
nests one inside the other - copying a value does not compare one, and
|
||||||
another - none of them does either of the other two on the way - and where
|
comparing two values does not copy them - and where user code nests them
|
||||||
user code nests them anyway, sharing the count only ends a descent sooner
|
anyway, sharing the count only ends a descent sooner than it had to, which
|
||||||
than it had to, which costs a little speed and is never wrong.
|
costs a little speed and is never wrong.
|
||||||
|
|
||||||
A byte is enough: the count never exceeds the limit by more than the single
|
A byte is enough: the count never exceeds the limit by more than the single
|
||||||
level that notices the limit has been reached.
|
level that notices the limit has been reached.
|
||||||
@@ -1140,6 +1127,27 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief create the object type from a range, preserving @a src_object's
|
||||||
|
/// comparator when the object type supports it
|
||||||
|
/// Enabled for object types that provide a key_comp() and a matching
|
||||||
|
/// range-plus-comparator constructor, such as std::map. Other object
|
||||||
|
/// types, such as nlohmann::ordered_map, fall back to the plain range
|
||||||
|
/// constructor and default-construct their comparator, just as they
|
||||||
|
/// always have (@ref detail::is_comparator_constructible_object_type).
|
||||||
|
template<typename Iterator, detail::enable_if_t<
|
||||||
|
detail::is_comparator_constructible_object_type<object_t, Iterator>::value, int> = 0>
|
||||||
|
static object_t* create_object_with_comparator(const object_t& src_object, Iterator first, Iterator last)
|
||||||
|
{
|
||||||
|
return create<object_t>(first, last, src_object.key_comp());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Iterator, detail::enable_if_t<
|
||||||
|
detail::negation<detail::is_comparator_constructible_object_type<object_t, Iterator>>::value, int> = 0>
|
||||||
|
static object_t* create_object_with_comparator(const object_t& /*src_object*/, Iterator first, Iterator last)
|
||||||
|
{
|
||||||
|
return create<object_t>(first, last);
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief create the copy of the object @a src in @a dst
|
/// @brief create the copy of the object @a src in @a dst
|
||||||
/// @note structured values are appended to @a worklist instead
|
/// @note structured values are appended to @a worklist instead
|
||||||
static void copy_object_level(const basic_json& src, basic_json& dst,
|
static void copy_object_level(const basic_json& src, basic_json& dst,
|
||||||
@@ -1157,7 +1165,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
scratch.emplace_back(element.first, basic_json());
|
scratch.emplace_back(element.first, basic_json());
|
||||||
}
|
}
|
||||||
|
|
||||||
dst.m_data.m_value.object = create<object_t>(std::make_move_iterator(scratch.begin()),
|
dst.m_data.m_value.object = create_object_with_comparator(src_object,
|
||||||
|
std::make_move_iterator(scratch.begin()),
|
||||||
std::make_move_iterator(scratch.end()));
|
std::make_move_iterator(scratch.end()));
|
||||||
// only now that the object exists may dst stop being a null value
|
// only now that the object exists may dst stop being a null value
|
||||||
dst.m_data.m_type = value_t::object;
|
dst.m_data.m_type = value_t::object;
|
||||||
@@ -1280,222 +1289,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
copy_iteratively(src);
|
copy_iteratively(src);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief convert the value @a val of another specialization into this null
|
|
||||||
value; @a val must be neither an object nor an array
|
|
||||||
|
|
||||||
Converting such a value never descends, so both ways of converting an
|
|
||||||
object or an array (@ref convert_structured) leave their elements of this
|
|
||||||
kind to the converting constructor, which leaves them to this.
|
|
||||||
*/
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
void convert_leaf(const BasicJsonType& val)
|
|
||||||
{
|
|
||||||
using other_boolean_t = typename BasicJsonType::boolean_t;
|
|
||||||
using other_number_float_t = typename BasicJsonType::number_float_t;
|
|
||||||
using other_number_integer_t = typename BasicJsonType::number_integer_t;
|
|
||||||
using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
|
||||||
using other_string_t = typename BasicJsonType::string_t;
|
|
||||||
using other_binary_t = typename BasicJsonType::binary_t;
|
|
||||||
|
|
||||||
switch (val.type())
|
|
||||||
{
|
|
||||||
case value_t::boolean:
|
|
||||||
JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());
|
|
||||||
break;
|
|
||||||
case value_t::number_float:
|
|
||||||
JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());
|
|
||||||
break;
|
|
||||||
case value_t::number_integer:
|
|
||||||
JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());
|
|
||||||
break;
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());
|
|
||||||
break;
|
|
||||||
case value_t::string:
|
|
||||||
JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());
|
|
||||||
break;
|
|
||||||
case value_t::binary:
|
|
||||||
JSONSerializer<other_binary_t>::to_json(*this, val.template get_ref<const other_binary_t&>());
|
|
||||||
break;
|
|
||||||
case value_t::null:
|
|
||||||
// m_data.m_type is already value_t::null
|
|
||||||
break;
|
|
||||||
case value_t::discarded:
|
|
||||||
m_data.m_type = value_t::discarded;
|
|
||||||
break;
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// scratch space for the converted elements of the arrays that
|
|
||||||
/// @ref convert_iteratively has yet to create
|
|
||||||
using convert_scratch_t = std::vector<basic_json, AllocatorType<basic_json>>;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief create the object or array @a val converted into this null value
|
|
||||||
|
|
||||||
Its converted elements are the last `val.size()` entries of @a elements (an
|
|
||||||
array) or of @a members (an object); they are moved into the container in
|
|
||||||
one go and then removed.
|
|
||||||
*/
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
void convert_level(const BasicJsonType& val, convert_scratch_t& elements, copy_scratch_t& members)
|
|
||||||
{
|
|
||||||
if (val.is_object())
|
|
||||||
{
|
|
||||||
const auto first = members.end() - static_cast<typename copy_scratch_t::difference_type>(val.size());
|
|
||||||
m_data.m_value.object = create<object_t>(std::make_move_iterator(first),
|
|
||||||
std::make_move_iterator(members.end()));
|
|
||||||
// only now that the object exists may this stop being a null value
|
|
||||||
m_data.m_type = value_t::object;
|
|
||||||
members.erase(first, members.end());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto first = elements.end() - static_cast<typename convert_scratch_t::difference_type>(val.size());
|
|
||||||
m_data.m_value.array = create<array_t>(std::make_move_iterator(first),
|
|
||||||
std::make_move_iterator(elements.end()));
|
|
||||||
// only now that the array exists may this stop being a null value
|
|
||||||
m_data.m_type = value_t::array;
|
|
||||||
elements.erase(first, elements.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
set_parents();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief convert the object or array @a val of another specialization into
|
|
||||||
this null value without recursing
|
|
||||||
|
|
||||||
The containers whose conversion has begun are kept on an explicit stack
|
|
||||||
rather than on the call stack. Unlike @ref copy_iteratively, this builds
|
|
||||||
every container from the bottom up: all its elements are converted first,
|
|
||||||
and the container is then created from them in one go, the way the range
|
|
||||||
constructor that converts the levels above the bound does. The two object
|
|
||||||
types need not enumerate their members in the same order, so the members
|
|
||||||
could not be paired up by position anyway, and building from a range keeps
|
|
||||||
what the range constructor does with keys that become equal on conversion.
|
|
||||||
|
|
||||||
Every value is complete before it is handed on, and a container gets its
|
|
||||||
type only once it exists, so whatever throws, every value left behind can
|
|
||||||
be destroyed.
|
|
||||||
*/
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
void convert_iteratively(const BasicJsonType& val)
|
|
||||||
{
|
|
||||||
using other_const_iterator = typename BasicJsonType::const_iterator;
|
|
||||||
|
|
||||||
// the containers whose conversion has begun, innermost last, each with
|
|
||||||
// its element to convert next
|
|
||||||
std::vector<std::pair<const BasicJsonType*, other_const_iterator>> pending;
|
|
||||||
|
|
||||||
// the converted elements of the pending arrays and the converted
|
|
||||||
// members of the pending objects, those of the innermost one last
|
|
||||||
convert_scratch_t elements;
|
|
||||||
copy_scratch_t members;
|
|
||||||
|
|
||||||
pending.emplace_back(&val, val.cbegin());
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
const BasicJsonType& container = *pending.back().first;
|
|
||||||
// a copy, as descending below can reallocate pending; the
|
|
||||||
// iterator kept in pending is only advanced through pending.back()
|
|
||||||
const other_const_iterator next = pending.back().second;
|
|
||||||
|
|
||||||
if (next != container.cend())
|
|
||||||
{
|
|
||||||
if (next->is_structured())
|
|
||||||
{
|
|
||||||
// convert its elements first; next stays where it is until
|
|
||||||
// the converted container is handed back to this one
|
|
||||||
pending.emplace_back(&*next, next->cbegin());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// the converting constructor does not descend into this value
|
|
||||||
if (container.is_object())
|
|
||||||
{
|
|
||||||
members.emplace_back(next.key(), *next);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
elements.emplace_back(*next);
|
|
||||||
}
|
|
||||||
++pending.back().second;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// all elements of the container are converted: create it
|
|
||||||
pending.pop_back();
|
|
||||||
|
|
||||||
if (pending.empty())
|
|
||||||
{
|
|
||||||
convert_level(container, elements, members);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
basic_json converted;
|
|
||||||
converted.convert_level(container, elements, members);
|
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
|
||||||
converted.start_position = container.start_pos();
|
|
||||||
converted.end_position = container.end_pos();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// hand it to the container it is an element of
|
|
||||||
if (pending.back().first->is_object())
|
|
||||||
{
|
|
||||||
members.emplace_back(pending.back().second.key(), std::move(converted));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
elements.push_back(std::move(converted));
|
|
||||||
}
|
|
||||||
++pending.back().second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief convert the object or array @a val of another specialization into
|
|
||||||
this null value
|
|
||||||
|
|
||||||
Converting a container converts its elements, so a value nested deeply
|
|
||||||
enough used to exhaust the call stack. The descent is bounded here as in
|
|
||||||
@ref copy_structured: the first `detail::recursion_depth_limit()` levels
|
|
||||||
are converted by the containers' range constructors, just as they always were,
|
|
||||||
and anything below that is converted without the call stack by
|
|
||||||
@ref convert_iteratively.
|
|
||||||
|
|
||||||
@sa https://github.com/nlohmann/json/issues/5650
|
|
||||||
*/
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
void convert_structured(const BasicJsonType& val)
|
|
||||||
{
|
|
||||||
const nesting_depth_guard guard;
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_LIKELY(guard.okay()))
|
|
||||||
{
|
|
||||||
// every element comes back to the converting constructor
|
|
||||||
if (val.is_object())
|
|
||||||
{
|
|
||||||
using other_object_t = typename BasicJsonType::object_t;
|
|
||||||
JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
using other_array_t = typename BasicJsonType::array_t;
|
|
||||||
JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
convert_iteratively(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// the result of comparing two values, including values that cannot be
|
/// the result of comparing two values, including values that cannot be
|
||||||
/// ordered at all, such as a discarded value or a NaN
|
/// ordered at all, such as a discarded value or a NaN
|
||||||
@@ -1825,12 +1618,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
template < typename CompatibleType,
|
template < typename CompatibleType,
|
||||||
typename U = detail::uncvref_t<CompatibleType>,
|
typename U = detail::uncvref_t<CompatibleType>,
|
||||||
detail::enable_if_t <
|
detail::enable_if_t <
|
||||||
!detail::is_basic_json<U>::value && detail::is_compatible_type<basic_json_t, U>::value
|
!detail::is_basic_json<U>::value && detail::is_compatible_type<basic_json_t, U>::value, int > = 0 >
|
||||||
#if JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
|
||||||
// see https://github.com/nlohmann/json/issues/2226
|
|
||||||
&& !detail::is_basic_json_reference_tuple<basic_json_t, U>::value
|
|
||||||
#endif
|
|
||||||
, int > = 0 >
|
|
||||||
basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape)
|
basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape)
|
||||||
JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),
|
JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),
|
||||||
std::forward<CompatibleType>(val))))
|
std::forward<CompatibleType>(val))))
|
||||||
@@ -1851,13 +1639,49 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
end_position(val.end_pos())
|
end_position(val.end_pos())
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (val.is_structured())
|
using other_boolean_t = typename BasicJsonType::boolean_t;
|
||||||
|
using other_number_float_t = typename BasicJsonType::number_float_t;
|
||||||
|
using other_number_integer_t = typename BasicJsonType::number_integer_t;
|
||||||
|
using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||||
|
using other_string_t = typename BasicJsonType::string_t;
|
||||||
|
using other_object_t = typename BasicJsonType::object_t;
|
||||||
|
using other_array_t = typename BasicJsonType::array_t;
|
||||||
|
using other_binary_t = typename BasicJsonType::binary_t;
|
||||||
|
|
||||||
|
switch (val.type())
|
||||||
{
|
{
|
||||||
convert_structured(val);
|
case value_t::boolean:
|
||||||
}
|
JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());
|
||||||
else
|
break;
|
||||||
{
|
case value_t::number_float:
|
||||||
convert_leaf(val);
|
JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());
|
||||||
|
break;
|
||||||
|
case value_t::number_integer:
|
||||||
|
JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());
|
||||||
|
break;
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());
|
||||||
|
break;
|
||||||
|
case value_t::string:
|
||||||
|
JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());
|
||||||
|
break;
|
||||||
|
case value_t::object:
|
||||||
|
JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
|
||||||
|
break;
|
||||||
|
case value_t::array:
|
||||||
|
JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
|
||||||
|
break;
|
||||||
|
case value_t::binary:
|
||||||
|
JSONSerializer<other_binary_t>::to_json(*this, val.template get_ref<const other_binary_t&>());
|
||||||
|
break;
|
||||||
|
case value_t::null:
|
||||||
|
*this = nullptr;
|
||||||
|
break;
|
||||||
|
case value_t::discarded:
|
||||||
|
m_data.m_type = value_t::discarded;
|
||||||
|
break;
|
||||||
|
default: // LCOV_EXCL_LINE
|
||||||
|
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
JSON_ASSERT(m_data.m_type == val.type());
|
JSON_ASSERT(m_data.m_type == val.type());
|
||||||
|
|
||||||
@@ -4415,26 +4239,25 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
/// @sa https://json.nlohmann.me/api/basic_json/update/
|
/// @sa https://json.nlohmann.me/api/basic_json/update/
|
||||||
void update(const_reference j, bool merge_objects = false)
|
void update(const_reference j, bool merge_objects = false)
|
||||||
{
|
{
|
||||||
prepare_update();
|
update(j.begin(), j.end(), merge_objects);
|
||||||
|
|
||||||
// passed value must be an object (checked here so a type_error names
|
|
||||||
// j, not the copy made below)
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
|
|
||||||
{
|
|
||||||
JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", j.type_name()), &j));
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy first: j may be *this or one of its descendants, and is
|
|
||||||
// iterated (and moved from) below to update *this
|
|
||||||
basic_json source = j;
|
|
||||||
update_from(source, merge_objects);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief updates a JSON object from another object, overwriting existing keys
|
/// @brief updates a JSON object from another object, overwriting existing keys
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/update/
|
/// @sa https://json.nlohmann.me/api/basic_json/update/
|
||||||
void update(const_iterator first, const_iterator last, bool merge_objects = false) // NOLINT(performance-unnecessary-value-param)
|
void update(const_iterator first, const_iterator last, bool merge_objects = false) // NOLINT(performance-unnecessary-value-param)
|
||||||
{
|
{
|
||||||
prepare_update();
|
// implicitly convert a null value to an empty object
|
||||||
|
if (is_null())
|
||||||
|
{
|
||||||
|
m_data.m_type = value_t::object;
|
||||||
|
m_data.m_value.object = create<object_t>();
|
||||||
|
assert_invariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!is_object()))
|
||||||
|
{
|
||||||
|
JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", type_name()), this));
|
||||||
|
}
|
||||||
|
|
||||||
// check if range iterators belong to the same JSON object
|
// check if range iterators belong to the same JSON object
|
||||||
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
|
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
|
||||||
@@ -4448,11 +4271,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", first.m_object->type_name()), first.m_object));
|
JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", first.m_object->type_name()), first.m_object));
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy first: the range may belong to *this or one of its
|
update_members(first, last, merge_objects, 0);
|
||||||
// descendants, and is iterated (and moved from) below to update
|
|
||||||
// *this
|
|
||||||
basic_json source(first, last);
|
|
||||||
update_from(source, merge_objects);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -4460,42 +4279,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
/// merge_patch_iteratively is merging into, and the members still to merge
|
/// merge_patch_iteratively is merging into, and the members still to merge
|
||||||
struct merge_frame
|
struct merge_frame
|
||||||
{
|
{
|
||||||
merge_frame(basic_json* target_, iterator position_, iterator last_) noexcept
|
merge_frame(basic_json* target_, const_iterator position_, const_iterator last_) noexcept
|
||||||
: target(target_), position(std::move(position_)), last(std::move(last_))
|
: target(target_), position(std::move(position_)), last(std::move(last_))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
basic_json* target;
|
basic_json* target;
|
||||||
iterator position;
|
const_iterator position;
|
||||||
iterator last;
|
const_iterator last;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @brief converts a null value to an empty object and checks that this
|
|
||||||
/// value is an object; called first by both @ref update overloads
|
|
||||||
void prepare_update()
|
|
||||||
{
|
|
||||||
// implicitly convert a null value to an empty object; create the
|
|
||||||
// object before setting the type, so a throwing allocation leaves
|
|
||||||
// this value null
|
|
||||||
if (is_null())
|
|
||||||
{
|
|
||||||
m_data.m_value.object = create<object_t>();
|
|
||||||
m_data.m_type = value_t::object;
|
|
||||||
assert_invariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!is_object()))
|
|
||||||
{
|
|
||||||
JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", type_name()), this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief starts the @ref update_members loop over an already-copied @a
|
|
||||||
/// source; called by both @ref update overloads
|
|
||||||
void update_from(basic_json& source, const bool merge_objects)
|
|
||||||
{
|
|
||||||
update_members(source.begin(), source.end(), merge_objects, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief the members loop of @ref update, for this object and range
|
@brief the members loop of @ref update, for this object and range
|
||||||
|
|
||||||
@@ -4508,7 +4300,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
@param[in] depth nesting level of this object, counted from the object
|
@param[in] depth nesting level of this object, counted from the object
|
||||||
@ref update was called on
|
@ref update was called on
|
||||||
*/
|
*/
|
||||||
void update_members(const iterator& first, const iterator& last, const bool merge_objects, const std::size_t depth)
|
void update_members(const const_iterator& first, const const_iterator& last, const bool merge_objects, const std::size_t depth)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(depth >= detail::recursion_depth_limit()))
|
if (JSON_HEDLEY_UNLIKELY(depth >= detail::recursion_depth_limit()))
|
||||||
{
|
{
|
||||||
@@ -4526,13 +4318,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// are overwritten as usual" behavior (see #5402).
|
// are overwritten as usual" behavior (see #5402).
|
||||||
if (it2 != m_data.m_value.object->end() && it2->second.is_object())
|
if (it2 != m_data.m_value.object->end() && it2->second.is_object())
|
||||||
{
|
{
|
||||||
it2->second.update_members(it.value().begin(), it.value().end(), true, depth + 1);
|
it2->second.update_members(it.value().cbegin(), it.value().cend(), true, depth + 1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// set_parent() also repairs the other members, which ordered_json
|
// set_parent() also repairs the other members, which ordered_json
|
||||||
// relocates when adding a key makes its vector grow
|
// relocates when adding a key makes its vector grow
|
||||||
set_parent(m_data.m_value.object->operator[](it.key()) = std::move(it.value()));
|
set_parent(m_data.m_value.object->operator[](it.key()) = it.value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4546,7 +4338,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
version. Only reached for values nested deeper than @ref
|
version. Only reached for values nested deeper than @ref
|
||||||
detail::recursion_depth_limit.
|
detail::recursion_depth_limit.
|
||||||
*/
|
*/
|
||||||
void update_members_iteratively(iterator first, iterator last)
|
void update_members_iteratively(const_iterator first, const_iterator last)
|
||||||
{
|
{
|
||||||
std::vector<merge_frame> stack;
|
std::vector<merge_frame> stack;
|
||||||
|
|
||||||
@@ -4573,18 +4365,18 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
const auto it2 = target->m_data.m_value.object->find(first.key());
|
const auto it2 = target->m_data.m_value.object->find(first.key());
|
||||||
if (it2 != target->m_data.m_value.object->end() && it2->second.is_object())
|
if (it2 != target->m_data.m_value.object->end() && it2->second.is_object())
|
||||||
{
|
{
|
||||||
basic_json& source = first.value();
|
const basic_json& source = first.value();
|
||||||
++first;
|
++first;
|
||||||
stack.emplace_back(target, first, last);
|
stack.emplace_back(target, first, last);
|
||||||
target = &it2->second;
|
target = &it2->second;
|
||||||
first = source.begin();
|
first = source.cbegin();
|
||||||
last = source.end();
|
last = source.cend();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// set_parent() also repairs the other members, which ordered_json
|
// set_parent() also repairs the other members, which ordered_json
|
||||||
// relocates when adding a key makes its vector grow
|
// relocates when adding a key makes its vector grow
|
||||||
target->set_parent(target->m_data.m_value.object->operator[](first.key()) = std::move(first.value()));
|
target->set_parent(target->m_data.m_value.object->operator[](first.key()) = first.value());
|
||||||
++first;
|
++first;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5264,7 +5056,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||||
return format == input_format_t::json
|
return format == input_format_t::json
|
||||||
? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)
|
? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)
|
||||||
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(sax, strict);
|
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief generate SAX events (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
/// @brief generate SAX events (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||||
@@ -5281,7 +5073,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||||
return format == input_format_t::json
|
return format == input_format_t::json
|
||||||
? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)
|
? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)
|
||||||
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(sax, strict);
|
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief generate SAX events
|
/// @brief generate SAX events
|
||||||
@@ -5303,7 +5095,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||||
? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)
|
? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)
|
||||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||||
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(sax, strict);
|
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
|
||||||
}
|
}
|
||||||
#ifndef JSON_NO_IO
|
#ifndef JSON_NO_IO
|
||||||
/// @brief deserialize from stream
|
/// @brief deserialize from stream
|
||||||
@@ -5601,7 +5393,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(&sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5621,7 +5413,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(&sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5650,7 +5442,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
auto ia = i.get();
|
auto ia = i.get();
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(&sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5668,7 +5460,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5687,7 +5479,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5714,7 +5506,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
auto ia = i.get();
|
auto ia = i.get();
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5732,7 +5524,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5751,7 +5543,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5778,7 +5570,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
auto ia = i.get();
|
auto ia = i.get();
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5796,7 +5588,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5815,7 +5607,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5833,7 +5625,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bon8).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bon8).sax_parse(input_format_t::bon8, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5852,7 +5644,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bon8).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bon8).sax_parse(input_format_t::bon8, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5870,7 +5662,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5889,7 +5681,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5916,7 +5708,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
auto ia = i.get();
|
auto ia = i.get();
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -6593,10 +6385,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
/// @sa https://json.nlohmann.me/api/basic_json/merge_patch/
|
/// @sa https://json.nlohmann.me/api/basic_json/merge_patch/
|
||||||
void merge_patch(const basic_json& apply_patch)
|
void merge_patch(const basic_json& apply_patch)
|
||||||
{
|
{
|
||||||
// copy first: apply_patch may be *this or one of its descendants,
|
apply_merge_patch(apply_patch, 0);
|
||||||
// and is iterated (and moved from) below to patch *this
|
|
||||||
basic_json patch = apply_patch;
|
|
||||||
apply_merge_patch(patch, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -6609,7 +6398,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
detail::recursion_depth_limit levels have been entered, @ref
|
detail::recursion_depth_limit levels have been entered, @ref
|
||||||
merge_patch_iteratively applies what is left without the call stack.
|
merge_patch_iteratively applies what is left without the call stack.
|
||||||
*/
|
*/
|
||||||
void apply_merge_patch(basic_json& apply_patch, const std::size_t depth)
|
void apply_merge_patch(const basic_json& apply_patch, const std::size_t depth)
|
||||||
{
|
{
|
||||||
if (apply_patch.is_object())
|
if (apply_patch.is_object())
|
||||||
{
|
{
|
||||||
@@ -6637,7 +6426,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
*this = std::move(apply_patch);
|
*this = apply_patch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6650,12 +6439,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
recursive version. Only reached for patches nested deeper than @ref
|
recursive version. Only reached for patches nested deeper than @ref
|
||||||
detail::recursion_depth_limit.
|
detail::recursion_depth_limit.
|
||||||
*/
|
*/
|
||||||
void merge_patch_iteratively(basic_json& apply_patch)
|
void merge_patch_iteratively(const basic_json& apply_patch)
|
||||||
{
|
{
|
||||||
std::vector<merge_frame> stack;
|
std::vector<merge_frame> stack;
|
||||||
|
|
||||||
// patch `target` with `patch`, or start patching it member by member
|
// patch `target` with `patch`, or start patching it member by member
|
||||||
const auto apply = [&stack](basic_json & target, basic_json & patch)
|
const auto apply = [&stack](basic_json & target, const basic_json & patch)
|
||||||
{
|
{
|
||||||
if (patch.is_object())
|
if (patch.is_object())
|
||||||
{
|
{
|
||||||
@@ -6663,11 +6452,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
{
|
{
|
||||||
target = basic_json::object();
|
target = basic_json::object();
|
||||||
}
|
}
|
||||||
stack.emplace_back(&target, patch.begin(), patch.end());
|
stack.emplace_back(&target, patch.cbegin(), patch.cend());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
target = std::move(patch);
|
target = patch;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -6683,7 +6472,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const iterator member = frame.position;
|
const const_iterator member = frame.position;
|
||||||
++stack.back().position;
|
++stack.back().position;
|
||||||
if (member.value().is_null())
|
if (member.value().is_null())
|
||||||
{
|
{
|
||||||
@@ -6850,10 +6639,7 @@ struct formatter<nlohmann::NLOHMANN_BASIC_JSON_TPL, char> // NOLINT(cert-dcl58-c
|
|||||||
|
|
||||||
} // namespace std
|
} // namespace std
|
||||||
|
|
||||||
// keep: undoes the macros defined via detail/macro_scope.hpp at the top of this file; removing it
|
#include <nlohmann/detail/macro_unscope.hpp>
|
||||||
// (nothing in this file *uses* a symbol from it) would leak JSON_* macros into every translation
|
|
||||||
// unit that includes this header.
|
|
||||||
#include <nlohmann/detail/macro_unscope.hpp> // IWYU pragma: keep
|
|
||||||
|
|
||||||
// End of GCC diagnostic pragmas for C++ modules support
|
// End of GCC diagnostic pragmas for C++ modules support
|
||||||
#if defined(__GNUC__) && !defined(__clang__) && __cplusplus >= 202002L
|
#if defined(__GNUC__) && !defined(__clang__) && __cplusplus >= 202002L
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
|
|
||||||
#include <cstdint> // int64_t, uint64_t
|
#include <cstdint> // int64_t, uint64_t
|
||||||
#include <map> // map
|
#include <map> // map
|
||||||
#include <string> // allocator, string
|
#include <memory> // allocator
|
||||||
|
#include <string> // string
|
||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
|
|
||||||
#include <nlohmann/detail/abi_macros.hpp>
|
#include <nlohmann/detail/abi_macros.hpp>
|
||||||
@@ -31,7 +32,7 @@ This serializer ignores the template arguments and uses ADL
|
|||||||
for serialization.
|
for serialization.
|
||||||
*/
|
*/
|
||||||
template<typename T = void, typename SFINAE = void>
|
template<typename T = void, typename SFINAE = void>
|
||||||
struct adl_serializer; // IWYU pragma: keep
|
struct adl_serializer;
|
||||||
|
|
||||||
/// a class to store JSON values
|
/// a class to store JSON values
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/
|
/// @sa https://json.nlohmann.me/api/basic_json/
|
||||||
@@ -47,12 +48,12 @@ template<template<typename U, typename V, typename... Args> class ObjectType =
|
|||||||
adl_serializer,
|
adl_serializer,
|
||||||
class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
|
class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
|
||||||
class CustomBaseClass = void>
|
class CustomBaseClass = void>
|
||||||
class basic_json; // IWYU pragma: keep
|
class basic_json;
|
||||||
|
|
||||||
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/
|
/// @sa https://json.nlohmann.me/api/json_pointer/
|
||||||
template<typename RefStringType>
|
template<typename RefStringType>
|
||||||
class json_pointer; // IWYU pragma: keep
|
class json_pointer;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief default specialization
|
@brief default specialization
|
||||||
@@ -63,7 +64,7 @@ using json = basic_json<>;
|
|||||||
/// @brief a minimal map-like container that preserves insertion order
|
/// @brief a minimal map-like container that preserves insertion order
|
||||||
/// @sa https://json.nlohmann.me/api/ordered_map/
|
/// @sa https://json.nlohmann.me/api/ordered_map/
|
||||||
template<class Key, class T, class IgnoredLess, class Allocator>
|
template<class Key, class T, class IgnoredLess, class Allocator>
|
||||||
struct ordered_map; // IWYU pragma: keep
|
struct ordered_map;
|
||||||
|
|
||||||
/// @brief specialization that maintains the insertion order of object keys
|
/// @brief specialization that maintains the insertion order of object keys
|
||||||
/// @sa https://json.nlohmann.me/api/ordered_json/
|
/// @sa https://json.nlohmann.me/api/ordered_json/
|
||||||
|
|||||||
@@ -12,14 +12,13 @@
|
|||||||
#include <functional> // equal_to, less
|
#include <functional> // equal_to, less
|
||||||
#include <initializer_list> // initializer_list
|
#include <initializer_list> // initializer_list
|
||||||
#include <iterator> // input_iterator_tag, iterator_traits
|
#include <iterator> // input_iterator_tag, iterator_traits
|
||||||
#include <new> // for operator new (placement new)
|
#include <memory> // allocator
|
||||||
#include <stdexcept> // for out_of_range
|
#include <stdexcept> // for out_of_range
|
||||||
#include <tuple> // forward_as_tuple
|
#include <tuple> // forward_as_tuple
|
||||||
#include <type_traits> // enable_if, integral_constant, is_convertible, is_nothrow_move_constructible
|
#include <type_traits> // enable_if, integral_constant, is_convertible, is_nothrow_move_constructible
|
||||||
#include <utility> // forward, move, pair, piecewise_construct
|
#include <utility> // forward, move, pair, piecewise_construct
|
||||||
#include <vector> // vector, allocator
|
#include <vector> // vector
|
||||||
|
|
||||||
#include <nlohmann/detail/abi_macros.hpp>
|
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||||
|
|
||||||
|
|||||||
+1061
-1065
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,8 @@
|
|||||||
|
|
||||||
#include <cstdint> // int64_t, uint64_t
|
#include <cstdint> // int64_t, uint64_t
|
||||||
#include <map> // map
|
#include <map> // map
|
||||||
#include <string> // allocator, string
|
#include <memory> // allocator
|
||||||
|
#include <string> // string
|
||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
|
|
||||||
// #include <nlohmann/detail/abi_macros.hpp>
|
// #include <nlohmann/detail/abi_macros.hpp>
|
||||||
@@ -176,7 +177,7 @@ This serializer ignores the template arguments and uses ADL
|
|||||||
for serialization.
|
for serialization.
|
||||||
*/
|
*/
|
||||||
template<typename T = void, typename SFINAE = void>
|
template<typename T = void, typename SFINAE = void>
|
||||||
struct adl_serializer; // IWYU pragma: keep
|
struct adl_serializer;
|
||||||
|
|
||||||
/// a class to store JSON values
|
/// a class to store JSON values
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/
|
/// @sa https://json.nlohmann.me/api/basic_json/
|
||||||
@@ -192,12 +193,12 @@ template<template<typename U, typename V, typename... Args> class ObjectType =
|
|||||||
adl_serializer,
|
adl_serializer,
|
||||||
class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
|
class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
|
||||||
class CustomBaseClass = void>
|
class CustomBaseClass = void>
|
||||||
class basic_json; // IWYU pragma: keep
|
class basic_json;
|
||||||
|
|
||||||
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/
|
/// @sa https://json.nlohmann.me/api/json_pointer/
|
||||||
template<typename RefStringType>
|
template<typename RefStringType>
|
||||||
class json_pointer; // IWYU pragma: keep
|
class json_pointer;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief default specialization
|
@brief default specialization
|
||||||
@@ -208,7 +209,7 @@ using json = basic_json<>;
|
|||||||
/// @brief a minimal map-like container that preserves insertion order
|
/// @brief a minimal map-like container that preserves insertion order
|
||||||
/// @sa https://json.nlohmann.me/api/ordered_map/
|
/// @sa https://json.nlohmann.me/api/ordered_map/
|
||||||
template<class Key, class T, class IgnoredLess, class Allocator>
|
template<class Key, class T, class IgnoredLess, class Allocator>
|
||||||
struct ordered_map; // IWYU pragma: keep
|
struct ordered_map;
|
||||||
|
|
||||||
/// @brief specialization that maintains the insertion order of object keys
|
/// @brief specialization that maintains the insertion order of object keys
|
||||||
/// @sa https://json.nlohmann.me/api/ordered_json/
|
/// @sa https://json.nlohmann.me/api/ordered_json/
|
||||||
|
|||||||
@@ -479,77 +479,6 @@ TEST_CASE("deep copy uses the provided allocator")
|
|||||||
CHECK(copy == j);
|
CHECK(copy == j);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
// the number of constructions countdown_allocator lets happen, including the
|
|
||||||
// one that fails; 0 means none ever fails
|
|
||||||
std::size_t constructions_until_failure = 0;
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
struct countdown_allocator : std::allocator<T>
|
|
||||||
{
|
|
||||||
using std::allocator<T>::allocator;
|
|
||||||
|
|
||||||
template<class U, class... Args>
|
|
||||||
void construct(U* p, Args&& ... args)
|
|
||||||
{
|
|
||||||
if (constructions_until_failure != 0 && --constructions_until_failure == 0)
|
|
||||||
{
|
|
||||||
throw std::bad_alloc();
|
|
||||||
}
|
|
||||||
|
|
||||||
::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class U>
|
|
||||||
struct rebind
|
|
||||||
{
|
|
||||||
using other = countdown_allocator<U>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_CASE("converting a deeply nested value from another specialization fails cleanly (#5650)")
|
|
||||||
{
|
|
||||||
using countdown_json = nlohmann::basic_json<std::map,
|
|
||||||
std::vector,
|
|
||||||
std::string,
|
|
||||||
bool,
|
|
||||||
std::int64_t,
|
|
||||||
std::uint64_t,
|
|
||||||
double,
|
|
||||||
countdown_allocator>;
|
|
||||||
|
|
||||||
// deeper than the 128 levels the converting constructor descends into, so
|
|
||||||
// that failures land on both sides of the bound - or, built with
|
|
||||||
// JSON_NO_THREAD_LOCAL, all in the iterative conversion
|
|
||||||
json j = {1, "two", {{"three", 3}}};
|
|
||||||
for (std::size_t i = 0; i < 150; ++i)
|
|
||||||
{
|
|
||||||
j = json{{"a", json::array({j, "sibling"})}};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fail every construction in turn. Each failure has to reach the caller,
|
|
||||||
// and everything built until then has to be destroyed cleanly.
|
|
||||||
std::size_t failures = 0;
|
|
||||||
for (std::size_t n = 1;; ++n)
|
|
||||||
{
|
|
||||||
constructions_until_failure = n;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const countdown_json converted = j;
|
|
||||||
constructions_until_failure = 0;
|
|
||||||
CHECK(converted.dump() == j.dump());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
catch (const std::bad_alloc&)
|
|
||||||
{
|
|
||||||
++failures;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CHECK(failures > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
template<class T>
|
template<class T>
|
||||||
|
|||||||
@@ -210,42 +210,15 @@ TEST_CASE_TEMPLATE_INVOKE(value_in_range_of_test, \
|
|||||||
|
|
||||||
TEST_CASE("BJData")
|
TEST_CASE("BJData")
|
||||||
{
|
{
|
||||||
SECTION("binary_reader BJData lookup tables")
|
SECTION("binary_reader BJData LUT arrays are sorted")
|
||||||
{
|
{
|
||||||
std::vector<std::uint8_t> const data;
|
std::vector<std::uint8_t> const data;
|
||||||
auto ia = nlohmann::detail::input_adapter(data);
|
auto ia = nlohmann::detail::input_adapter(data);
|
||||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||||
nlohmann::detail::binary_reader<json, decltype(ia)> const br{std::move(ia), json::input_format_t::bjdata};
|
nlohmann::detail::binary_reader<json, decltype(ia)> const br{std::move(ia), json::input_format_t::bjdata};
|
||||||
|
|
||||||
// the excluded optimized-type markers must match binary_writer's
|
CHECK(std::is_sorted(br.bjd_optimized_type_markers.begin(), br.bjd_optimized_type_markers.end()));
|
||||||
// is_bjdata_excluded_type_marker(), which encodes the same 8 markers
|
CHECK(std::is_sorted(br.bjd_types_map.begin(), br.bjd_types_map.end()));
|
||||||
for (const char marker :
|
|
||||||
{'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'
|
|
||||||
})
|
|
||||||
{
|
|
||||||
CHECK(br.is_bjd_excluded_optimized_type(marker));
|
|
||||||
}
|
|
||||||
for (const char marker :
|
|
||||||
{'U', 'i', 'u', 'I', 'm', 'l', 'M', 'L', 'd', 'D', 'C', 'B', 'x'
|
|
||||||
})
|
|
||||||
{
|
|
||||||
CHECK(!br.is_bjd_excluded_optimized_type(marker));
|
|
||||||
}
|
|
||||||
|
|
||||||
// every dtype marker must round-trip to its ND-array type name
|
|
||||||
const std::vector<std::pair<char, std::string>> types
|
|
||||||
{
|
|
||||||
{'B', "byte"}, {'C', "char"}, {'D', "double"}, {'I', "int16"},
|
|
||||||
{'L', "int64"}, {'M', "uint64"}, {'U', "uint8"}, {'d', "single"},
|
|
||||||
{'i', "int8"}, {'l', "int32"}, {'m', "uint32"}, {'u', "uint16"}
|
|
||||||
};
|
|
||||||
for (const auto& type : types)
|
|
||||||
{
|
|
||||||
const char* name = br.bjd_type_name(type.first);
|
|
||||||
REQUIRE(name != nullptr);
|
|
||||||
CHECK(std::string(name) == type.second);
|
|
||||||
}
|
|
||||||
CHECK(br.bjd_type_name('x') == nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("individual values")
|
SECTION("individual values")
|
||||||
|
|||||||
@@ -827,6 +827,46 @@ Json nest(Json j, const std::size_t depth)
|
|||||||
return j;
|
return j;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// a std::map comparator with state: case-insensitive, unless constructed
|
||||||
|
// case-sensitive. Used to check that copying an object copies the original's
|
||||||
|
// comparator rather than default-constructing a new one (see #5649).
|
||||||
|
struct key_case_less
|
||||||
|
{
|
||||||
|
key_case_less() = default;
|
||||||
|
explicit key_case_less(const bool cs) noexcept : case_sensitive(cs) {}
|
||||||
|
|
||||||
|
bool operator()(const std::string& a, const std::string& b) const
|
||||||
|
{
|
||||||
|
if (case_sensitive)
|
||||||
|
{
|
||||||
|
return a < b;
|
||||||
|
}
|
||||||
|
return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(),
|
||||||
|
[](unsigned char x, unsigned char y)
|
||||||
|
{
|
||||||
|
return std::tolower(x) < std::tolower(y);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool case_sensitive = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Key, class Value, class /*Compare*/, class Allocator>
|
||||||
|
using key_case_map = std::map<Key, Value, key_case_less, Allocator>;
|
||||||
|
using key_case_json = nlohmann::basic_json<key_case_map>;
|
||||||
|
|
||||||
|
// the innermost value of a chain of single-element arrays
|
||||||
|
template<typename Json>
|
||||||
|
const Json& innermost(const Json& j)
|
||||||
|
{
|
||||||
|
const Json* p = &j;
|
||||||
|
while (p->is_array())
|
||||||
|
{
|
||||||
|
p = &(*p)[0];
|
||||||
|
}
|
||||||
|
return *p;
|
||||||
|
}
|
||||||
|
|
||||||
// orders keys case-insensitively, so "key" and "KEY" compare equivalent
|
// orders keys case-insensitively, so "key" and "KEY" compare equivalent
|
||||||
// (neither less than the other) although they are not equal
|
// (neither less than the other) although they are not equal
|
||||||
struct case_insensitive_less
|
struct case_insensitive_less
|
||||||
@@ -891,6 +931,47 @@ TEST_CASE("equality of objects whose entries have no fixed order")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("copying an object preserves its comparator's state")
|
||||||
|
{
|
||||||
|
// Past the iterative deep copy's nesting bound, an object copy used to be
|
||||||
|
// built with a default-constructed comparator instead of a copy of the
|
||||||
|
// original's. For an object type whose comparator carries state - here, a
|
||||||
|
// std::map that compares keys case-sensitively only when created that way
|
||||||
|
// - this reordered the copy's keys and could even drop entries that the
|
||||||
|
// original's comparator kept distinct (see #5649).
|
||||||
|
key_case_json object = key_case_json::object_t(key_case_less(true)); // case-sensitive
|
||||||
|
object["b"] = 1;
|
||||||
|
object["B"] = 2;
|
||||||
|
object["a"] = 3;
|
||||||
|
REQUIRE(object.dump() == R"({"B":2,"a":3,"b":1})");
|
||||||
|
|
||||||
|
for (const std::size_t depth : std::vector<std::size_t> {0, 127, 128, 200})
|
||||||
|
{
|
||||||
|
CAPTURE(depth);
|
||||||
|
|
||||||
|
key_case_json original = object;
|
||||||
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
|
{
|
||||||
|
original = key_case_json::array({std::move(original)});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const key_case_json copy = original; // NOLINT(performance-unnecessary-copy-initialization)
|
||||||
|
CHECK(innermost(copy).size() == 3);
|
||||||
|
CHECK(innermost(copy).dump() == R"({"B":2,"a":3,"b":1})");
|
||||||
|
CHECK(copy == original);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
key_case_json copy = key_case_json::array();
|
||||||
|
copy = original;
|
||||||
|
CHECK(innermost(copy).size() == 3);
|
||||||
|
CHECK(innermost(copy).dump() == R"({"B":2,"a":3,"b":1})");
|
||||||
|
CHECK(copy == original);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("equality of an object whose comparator treats different keys as equivalent")
|
TEST_CASE("equality of an object whose comparator treats different keys as equivalent")
|
||||||
{
|
{
|
||||||
// https://github.com/nlohmann/json/issues/5655: past the nesting bound,
|
// https://github.com/nlohmann/json/issues/5655: past the nesting bound,
|
||||||
|
|||||||
@@ -141,58 +141,6 @@ TEST_CASE("Better diagnostics with positions")
|
|||||||
check_objects(300);
|
check_objects(300);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("converting keeps the positions of nested values (#5650)")
|
|
||||||
{
|
|
||||||
// Values nested deeper than the converting constructor's descent bound
|
|
||||||
// are converted without the call stack, on a path that has to carry the
|
|
||||||
// positions of every value over itself. Objects and arrays take turns,
|
|
||||||
// and the innermost value is null, which used to lose its positions.
|
|
||||||
const auto check_conversion = [](std::size_t depth)
|
|
||||||
{
|
|
||||||
CAPTURE(depth)
|
|
||||||
|
|
||||||
std::string text;
|
|
||||||
std::string closing;
|
|
||||||
for (std::size_t i = 0; i < depth; ++i)
|
|
||||||
{
|
|
||||||
text += (i % 2 == 0) ? "[12, " : R"({"b":1, "a":)";
|
|
||||||
closing += (i % 2 == 0) ? ']' : '}';
|
|
||||||
}
|
|
||||||
text += "null";
|
|
||||||
text.append(closing.rbegin(), closing.rend());
|
|
||||||
|
|
||||||
const json original = json::parse(text);
|
|
||||||
const nlohmann::ordered_json converted = original;
|
|
||||||
|
|
||||||
const json* o = &original;
|
|
||||||
const nlohmann::ordered_json* c = &converted;
|
|
||||||
for (std::size_t level = 0; level <= depth; ++level)
|
|
||||||
{
|
|
||||||
CAPTURE(level)
|
|
||||||
REQUIRE(c->start_pos() == o->start_pos());
|
|
||||||
REQUIRE(c->end_pos() == o->end_pos());
|
|
||||||
|
|
||||||
if (level < depth)
|
|
||||||
{
|
|
||||||
// the number beside the value nested next
|
|
||||||
const json& o_number = o->is_object() ? o->at("b") : o->at(0);
|
|
||||||
const nlohmann::ordered_json& c_number = c->is_object() ? c->at("b") : c->at(0);
|
|
||||||
REQUIRE(c_number.start_pos() == o_number.start_pos());
|
|
||||||
REQUIRE(c_number.end_pos() == o_number.end_pos());
|
|
||||||
|
|
||||||
o = o->is_object() ? &o->at("a") : &o->at(1);
|
|
||||||
c = c->is_object() ? &c->at("a") : &c->at(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
check_conversion(1);
|
|
||||||
check_conversion(127);
|
|
||||||
check_conversion(128);
|
|
||||||
check_conversion(129);
|
|
||||||
check_conversion(300);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("JSON patch add to primitive parent (#4292)")
|
SECTION("JSON patch add to primitive parent (#4292)")
|
||||||
{
|
{
|
||||||
// the JSON Patch "add" target /foo/bar/baz has a string parent
|
// the JSON Patch "add" target /foo/bar/baz has a string parent
|
||||||
|
|||||||
@@ -236,31 +236,6 @@ TEST_CASE("Regression tests for extended diagnostics")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Regression test for issue #5641 - parent pointers after update()/merge_patch() with an aliasing argument")
|
|
||||||
{
|
|
||||||
// update()'s and merge_patch()'s argument may be *this or one of its
|
|
||||||
// descendants; the values moved out of the (temporary) copy must end
|
|
||||||
// up with their parent pointing at their new location in *this
|
|
||||||
{
|
|
||||||
json j = {{"a", {{"a", 1}, {"b", 2}}}};
|
|
||||||
j.update(j["a"]);
|
|
||||||
CHECK(j == json({{"a", 1}, {"b", 2}}));
|
|
||||||
|
|
||||||
// Must call operator[] on const element, otherwise m_parent gets updated.
|
|
||||||
auto const& constJ = j;
|
|
||||||
CHECK_THROWS_WITH_AS(constJ["a"].at(0), "[json.exception.type_error.304] (/a) cannot use at() with number", json::type_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
json j = {{"a", {{"a", nullptr}, {"b", 2}}}};
|
|
||||||
j.merge_patch(j["a"]);
|
|
||||||
CHECK(j == json({{"b", 2}}));
|
|
||||||
|
|
||||||
auto const& constJ = j;
|
|
||||||
CHECK_THROWS_WITH_AS(constJ["b"].at(0), "[json.exception.type_error.304] (/b) cannot use at() with number", json::type_error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Regression test for issue #3032 - Yet another assertion failure when inserting into arrays with JSON_DIAGNOSTICS set")
|
SECTION("Regression test for issue #3032 - Yet another assertion failure when inserting into arrays with JSON_DIAGNOSTICS set")
|
||||||
{
|
{
|
||||||
// reference operator[](size_type idx)
|
// reference operator[](size_type idx)
|
||||||
@@ -366,36 +341,6 @@ TEST_CASE("Regression tests for extended diagnostics")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Regression test for issue #5650 - converting keeps the parents of nested values")
|
|
||||||
{
|
|
||||||
// A value nested deeper than the converting constructor's descent bound
|
|
||||||
// is converted without the call stack. Every container that path creates
|
|
||||||
// has to have the parents of its children set, or the JSON Pointer in the
|
|
||||||
// diagnostic is cut short. Objects and arrays take turns.
|
|
||||||
const std::size_t pairs = 150;
|
|
||||||
|
|
||||||
json j = "not a number";
|
|
||||||
std::string pointer;
|
|
||||||
for (std::size_t i = 0; i < pairs; ++i)
|
|
||||||
{
|
|
||||||
j = json{{"a", json::array({j})}};
|
|
||||||
pointer += "/a/0";
|
|
||||||
}
|
|
||||||
|
|
||||||
const nlohmann::ordered_json converted = j;
|
|
||||||
|
|
||||||
const nlohmann::ordered_json* inner = &converted;
|
|
||||||
for (std::size_t i = 0; i < pairs; ++i)
|
|
||||||
{
|
|
||||||
inner = &inner->at("a").at(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string const expected = "[json.exception.type_error.302] (" + pointer + ") type must be number, but is string";
|
|
||||||
int i = 0;
|
|
||||||
CHECK_THROWS_WITH_AS(i = inner->get<int>(), expected.c_str(), nlohmann::ordered_json::type_error);
|
|
||||||
CHECK(i == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Regression test for issue #5668 - wrong path for std::map/unordered_map with non-string keys")
|
SECTION("Regression test for issue #5668 - wrong path for std::map/unordered_map with non-string keys")
|
||||||
{
|
{
|
||||||
// a map with non-string keys is read from an array of [key, value] arrays;
|
// a map with non-string keys is read from an array of [key, value] arrays;
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
// __ _____ _____ _____
|
|
||||||
// __| | __| | | | JSON for Modern C++ (supporting code)
|
|
||||||
// | | |__ | | | | | | version 3.12.0
|
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
||||||
//
|
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
#include "doctest_compatibility.h"
|
|
||||||
|
|
||||||
// This file tests the opt-in JSON_DISABLE_TUPLE_REFERENCE_CONVERSION, so it
|
|
||||||
// defines the macro itself rather than relying on a -D flag, and runs in every
|
|
||||||
// build.
|
|
||||||
#ifdef JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
|
||||||
#undef JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define JSON_DISABLE_TUPLE_REFERENCE_CONVERSION 1
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
using nlohmann::json;
|
|
||||||
using nlohmann::ordered_json;
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <tuple>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
// clang before 4 and GCC before 5 cannot create a std::tuple of basic_json
|
|
||||||
// references at all, with or without JSON_DISABLE_TUPLE_REFERENCE_CONVERSION:
|
|
||||||
// the tuple constructors make them instantiate basic_json's conversion operator
|
|
||||||
// for libstdc++'s internal tuple bases, which fails hard
|
|
||||||
#if (defined(__clang__) && __clang_major__ < 4) || (!defined(__clang__) && defined(__GNUC__) && __GNUC__ < 5)
|
|
||||||
#define SKIP_TESTS_FOR_JSON_REFERENCE_TUPLES
|
|
||||||
#endif
|
|
||||||
|
|
||||||
TEST_CASE("JSON_DISABLE_TUPLE_REFERENCE_CONVERSION")
|
|
||||||
{
|
|
||||||
SECTION("json is not constructible from a one-element tuple of a json reference")
|
|
||||||
{
|
|
||||||
CHECK_FALSE(std::is_constructible<json, std::tuple<json&>>::value);
|
|
||||||
CHECK_FALSE(std::is_constructible<json, std::tuple<const json&>>::value);
|
|
||||||
CHECK_FALSE(std::is_constructible < json, std::tuple < json && >>::value);
|
|
||||||
CHECK_FALSE(std::is_constructible<json, const std::tuple<json&>&>::value);
|
|
||||||
CHECK_FALSE(std::is_constructible<ordered_json, std::tuple<ordered_json&>>::value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef SKIP_TESTS_FOR_JSON_REFERENCE_TUPLES
|
|
||||||
SECTION("issue #2226 - tuple<const json&> from tuple<json&> keeps the reference")
|
|
||||||
{
|
|
||||||
json j = true;
|
|
||||||
const std::tuple<const json&> tup(std::forward_as_tuple(j));
|
|
||||||
CHECK(&std::get<0>(tup) == &j);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("tuple<json> from tuple<json&> copies the element")
|
|
||||||
{
|
|
||||||
const json j = {{"key", "value"}};
|
|
||||||
const std::tuple<json> t1(std::forward_as_tuple(j));
|
|
||||||
CHECK(std::get<0>(t1) == j);
|
|
||||||
|
|
||||||
json j2 = "text";
|
|
||||||
const std::tuple<json> t2(std::forward_as_tuple(std::move(j2)));
|
|
||||||
CHECK(std::get<0>(t2) == "text");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SECTION("other tuple conversions are not affected")
|
|
||||||
{
|
|
||||||
const json j = true;
|
|
||||||
|
|
||||||
// one-element tuple holding a json value
|
|
||||||
CHECK(json(std::make_tuple(j)) == json::array({true}));
|
|
||||||
|
|
||||||
// tuples with more than one element, even when holding references
|
|
||||||
int i = 1;
|
|
||||||
#ifndef SKIP_TESTS_FOR_JSON_REFERENCE_TUPLES
|
|
||||||
CHECK(json(std::forward_as_tuple(i, j)) == json::array({1, true}));
|
|
||||||
CHECK(json(std::forward_as_tuple(j, j)) == json::array({true, true}));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// one-element tuples holding references to other types
|
|
||||||
std::string s = "text";
|
|
||||||
CHECK(json(std::forward_as_tuple(s)) == json::array({"text"}));
|
|
||||||
CHECK(json(std::forward_as_tuple(i)) == json::array({1}));
|
|
||||||
|
|
||||||
#ifndef SKIP_TESTS_FOR_JSON_REFERENCE_TUPLES
|
|
||||||
// a reference to a different basic_json specialization
|
|
||||||
ordered_json oj = true;
|
|
||||||
CHECK(json(std::forward_as_tuple(oj)) == json::array({true}));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,6 @@ using nlohmann::json;
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
TEST_CASE("tests on very large JSONs")
|
TEST_CASE("tests on very large JSONs")
|
||||||
{
|
{
|
||||||
@@ -54,24 +53,6 @@ const json* innermost_value(const json& j, std::size_t& depth)
|
|||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The text of a value nested depth levels deep around the number 0. Level i is
|
|
||||||
// an array if pattern[i % pattern.size()] is '[', and otherwise an object with
|
|
||||||
// the single member "a", which every object type enumerates in the same order.
|
|
||||||
std::string nested_text(std::size_t depth, const std::string& pattern)
|
|
||||||
{
|
|
||||||
std::string text;
|
|
||||||
std::string closing;
|
|
||||||
for (std::size_t i = 0; i < depth; ++i)
|
|
||||||
{
|
|
||||||
const bool array = pattern[i % pattern.size()] == '[';
|
|
||||||
text += array ? "[" : "{\"a\":";
|
|
||||||
closing += array ? ']' : '}';
|
|
||||||
}
|
|
||||||
text += '0';
|
|
||||||
text.append(closing.rbegin(), closing.rend());
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST_CASE("tests on deeply nested JSONs")
|
TEST_CASE("tests on deeply nested JSONs")
|
||||||
@@ -243,114 +224,5 @@ TEST_CASE("tests on deeply nested JSONs")
|
|||||||
CHECK(*innermost_value(j, unused) == 0);
|
CHECK(*innermost_value(j, unused) == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("issue #5650 - stack overflow converting between specializations")
|
|
||||||
{
|
|
||||||
const std::vector<std::string> patterns = {"[", "{", "[{"};
|
|
||||||
|
|
||||||
SECTION("json to ordered_json")
|
|
||||||
{
|
|
||||||
for (const auto& pattern : patterns)
|
|
||||||
{
|
|
||||||
CAPTURE(pattern);
|
|
||||||
const std::string text = nested_text(depth, pattern);
|
|
||||||
const json j = json::parse(text);
|
|
||||||
|
|
||||||
const nlohmann::ordered_json converted = j;
|
|
||||||
CHECK(converted.dump() == text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("ordered_json to json")
|
|
||||||
{
|
|
||||||
for (const auto& pattern : patterns)
|
|
||||||
{
|
|
||||||
CAPTURE(pattern);
|
|
||||||
const std::string text = nested_text(depth, pattern);
|
|
||||||
const nlohmann::ordered_json o = nlohmann::ordered_json::parse(text);
|
|
||||||
|
|
||||||
const json converted = o;
|
|
||||||
CHECK(converted.dump() == text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("get<ordered_json>()")
|
|
||||||
{
|
|
||||||
for (const auto& pattern : patterns)
|
|
||||||
{
|
|
||||||
CAPTURE(pattern);
|
|
||||||
const std::string text = nested_text(depth, pattern);
|
|
||||||
const json j = json::parse(text);
|
|
||||||
|
|
||||||
CHECK(j.get<nlohmann::ordered_json>().dump() == text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("depths around the bound of the recursive descent")
|
|
||||||
{
|
|
||||||
for (std::size_t d = 1; d <= 300; ++d)
|
|
||||||
{
|
|
||||||
CAPTURE(d);
|
|
||||||
for (const auto& pattern : patterns)
|
|
||||||
{
|
|
||||||
CAPTURE(pattern);
|
|
||||||
const std::string text = nested_text(d, pattern);
|
|
||||||
const json j = json::parse(text);
|
|
||||||
|
|
||||||
const nlohmann::ordered_json converted = j;
|
|
||||||
CHECK(converted.dump() == text);
|
|
||||||
const json back = converted;
|
|
||||||
CHECK(back.dump() == text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("values below the bound are converted as values above it")
|
|
||||||
{
|
|
||||||
// Bury a value below the bound, where it is converted without the
|
|
||||||
// call stack, and compare it with the same value converted on its
|
|
||||||
// own by the containers' range constructors. Its objects have
|
|
||||||
// members that the two object types enumerate in different orders.
|
|
||||||
const auto bury = [](nlohmann::ordered_json value)
|
|
||||||
{
|
|
||||||
for (std::size_t i = 0; i < 200; ++i)
|
|
||||||
{
|
|
||||||
value = nlohmann::ordered_json::array({std::move(value)});
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
const auto dig = [](const json & value)
|
|
||||||
{
|
|
||||||
const json* current = &value;
|
|
||||||
for (std::size_t i = 0; i < 200; ++i)
|
|
||||||
{
|
|
||||||
current = ¤t->at(0);
|
|
||||||
}
|
|
||||||
return current;
|
|
||||||
};
|
|
||||||
|
|
||||||
nlohmann::ordered_json value = nlohmann::ordered_json::object();
|
|
||||||
value["z"] = {1, -2, 3U, 4.5, true, nullptr, "six", nlohmann::ordered_json::binary({7, 8}, 9),
|
|
||||||
nlohmann::ordered_json::binary({10}), nlohmann::ordered_json::array(), nlohmann::ordered_json::object()
|
|
||||||
};
|
|
||||||
value["y"] = {{"x", {{"w", 1}, {"v", 2}}}, {"u", {3, {{"t", 4}, {"s", 5}}}}};
|
|
||||||
value["r"] = nlohmann::ordered_json::array({nlohmann::ordered_json(nlohmann::ordered_json::value_t::discarded)});
|
|
||||||
|
|
||||||
const json converted_above = value;
|
|
||||||
const json buried = bury(value);
|
|
||||||
const json& converted_below = *dig(buried);
|
|
||||||
|
|
||||||
CHECK(converted_below.dump() == converted_above.dump());
|
|
||||||
CHECK(converted_below.at("z").at(7).get_binary().subtype() == 9);
|
|
||||||
CHECK_FALSE(converted_below.at("z").at(8).get_binary().has_subtype());
|
|
||||||
CHECK(converted_below.at("r").at(0).is_discarded());
|
|
||||||
|
|
||||||
// a discarded value is never equal to anything, so compare the rest
|
|
||||||
value.erase("r");
|
|
||||||
const json without_discarded_above = value;
|
|
||||||
const json without_discarded_buried = bury(value);
|
|
||||||
CHECK(*dig(without_discarded_buried) == without_discarded_above);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -374,52 +374,3 @@ TEST_CASE("JSON Merge Patch and update on ordered_json")
|
|||||||
CHECK(target == ordered_json::parse(R"({"a": 1, "e": {"y": 5}, "g": 6})"));
|
CHECK(target == ordered_json::parse(R"({"a": 1, "e": {"y": 5}, "g": 6})"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("merge_patch() with an argument that aliases *this (#5641)")
|
|
||||||
{
|
|
||||||
SECTION("j.merge_patch(j): erasing a member destroys the node the loop's iterator points to")
|
|
||||||
{
|
|
||||||
// reproduces issue #5641, case 1
|
|
||||||
json j = {{"a", nullptr}, {"b", 1}};
|
|
||||||
j.merge_patch(j);
|
|
||||||
CHECK(j == json({{"b", 1}}));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("j.merge_patch(j[\"a\"]): removing \"a\" destroys the patch while it is iterated")
|
|
||||||
{
|
|
||||||
// reproduces issue #5641, case 2
|
|
||||||
json j = {{"a", {{"a", nullptr}, {"b", 2}}}};
|
|
||||||
j.merge_patch(j["a"]);
|
|
||||||
CHECK(j == json({{"b", 2}}));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("a patch nested past the iterative descent bound aliases *this")
|
|
||||||
{
|
|
||||||
// every depth on either side of where the iterative version takes
|
|
||||||
// over (detail::recursion_depth_limit(), 128); patching *this with
|
|
||||||
// itself is idempotent, aliased or not
|
|
||||||
for (const std::size_t depth :
|
|
||||||
{
|
|
||||||
std::size_t{0}, std::size_t{127}, std::size_t{128}, std::size_t{300}
|
|
||||||
})
|
|
||||||
{
|
|
||||||
CAPTURE(depth);
|
|
||||||
json j = json::parse(nested_objects(depth, 0));
|
|
||||||
const json expected = j;
|
|
||||||
j.merge_patch(j);
|
|
||||||
CHECK(j == expected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("ordered_json")
|
|
||||||
{
|
|
||||||
using nlohmann::ordered_json;
|
|
||||||
|
|
||||||
SECTION("merge_patch with a member of *this")
|
|
||||||
{
|
|
||||||
ordered_json j = {{"a", {{"a", nullptr}, {"b", 2}}}};
|
|
||||||
j.merge_patch(j["a"]);
|
|
||||||
CHECK(j == ordered_json({{"b", 2}}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1116,72 +1116,3 @@ TEST_CASE("update() on deeply nested values")
|
|||||||
CHECK(p->at("y") == 2);
|
CHECK(p->at("y") == 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("update() with an argument that aliases *this (#5641)")
|
|
||||||
{
|
|
||||||
SECTION("the target is checked before the argument, as before the copy")
|
|
||||||
{
|
|
||||||
json j = 1;
|
|
||||||
CHECK_THROWS_WITH_AS(j.update(json::array()), "[json.exception.type_error.312] cannot use update() with number", json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(j.update(j.cbegin(), j.cend()), "[json.exception.type_error.312] cannot use update() with number", json::type_error&);
|
|
||||||
|
|
||||||
json k;
|
|
||||||
CHECK_THROWS_WITH_AS(k.update(json::array()), "[json.exception.type_error.312] cannot use update() with array", json::type_error&);
|
|
||||||
CHECK(k == json::object());
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("const reference")
|
|
||||||
{
|
|
||||||
SECTION("j.update(j[\"a\"]): assigning into the argument's parent destroys it mid-iteration")
|
|
||||||
{
|
|
||||||
// reproduces issue #5641, case 3
|
|
||||||
json j = {{"a", {{"a", 1}, {"b", 2}}}};
|
|
||||||
j.update(j["a"]);
|
|
||||||
CHECK(j == json({{"a", 1}, {"b", 2}}));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("merge_objects with an argument that is a member of *this")
|
|
||||||
{
|
|
||||||
json j = {{"defaults", {{"opts", {{"a", 1}}}}}, {"opts", {{"b", 2}}}};
|
|
||||||
j.update(j["defaults"], true);
|
|
||||||
CHECK(j == json({{"defaults", {{"opts", {{"a", 1}}}}}, {"opts", {{"a", 1}, {"b", 2}}}}));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("ordered_json: inserting a new key relocates the vector behind the argument")
|
|
||||||
{
|
|
||||||
// reproduces issue #5641, case 4
|
|
||||||
using nlohmann::ordered_json;
|
|
||||||
ordered_json j = {{"a", {{"x", 1}, {"y", 2}, {"z", 3}}}};
|
|
||||||
j.update(j["a"]);
|
|
||||||
CHECK(j == ordered_json({{"a", {{"x", 1}, {"y", 2}, {"z", 3}}}, {"x", 1}, {"y", 2}, {"z", 3}}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("iterator range")
|
|
||||||
{
|
|
||||||
SECTION("range that is a member of *this")
|
|
||||||
{
|
|
||||||
json j = {{"a", {{"a", 1}, {"b", 2}}}};
|
|
||||||
j.update(j["a"].begin(), j["a"].end());
|
|
||||||
CHECK(j == json({{"a", 1}, {"b", 2}}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("nested past the iterative descent bound aliases *this")
|
|
||||||
{
|
|
||||||
// every depth on either side of where the iterative version takes
|
|
||||||
// over (detail::recursion_depth_limit(), 128); merging *this into
|
|
||||||
// itself is idempotent, aliased or not
|
|
||||||
for (const std::size_t depth :
|
|
||||||
{
|
|
||||||
std::size_t{0}, std::size_t{127}, std::size_t{128}, std::size_t{300}
|
|
||||||
})
|
|
||||||
{
|
|
||||||
CAPTURE(depth);
|
|
||||||
json j = json::parse(nested_objects(depth, 0));
|
|
||||||
const json expected = j;
|
|
||||||
j.update(j, true);
|
|
||||||
CHECK(j == expected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -18,19 +18,6 @@
|
|||||||
// for some reason including this after the json header leads to linker errors with VS 2017...
|
// for some reason including this after the json header leads to linker errors with VS 2017...
|
||||||
#include <locale>
|
#include <locale>
|
||||||
|
|
||||||
// skip tests if JSON_DISABLE_TUPLE_REFERENCE_CONVERSION=1 (#2226)
|
|
||||||
#if defined(JSON_DISABLE_TUPLE_REFERENCE_CONVERSION) && (JSON_DISABLE_TUPLE_REFERENCE_CONVERSION == 1)
|
|
||||||
#define SKIP_TESTS_FOR_TUPLE_REFERENCE_CONVERSION
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// clang before 4 and GCC before 5 cannot create a std::tuple of basic_json
|
|
||||||
// references at all, with or without JSON_DISABLE_TUPLE_REFERENCE_CONVERSION:
|
|
||||||
// the tuple constructors make them instantiate basic_json's conversion operator
|
|
||||||
// for libstdc++'s internal tuple bases, which fails hard
|
|
||||||
#if (defined(__clang__) && __clang_major__ < 4) || (!defined(__clang__) && defined(__GNUC__) && __GNUC__ < 5)
|
|
||||||
#define SKIP_TESTS_FOR_JSON_REFERENCE_TUPLES
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define JSON_TESTS_PRIVATE
|
#define JSON_TESTS_PRIVATE
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
@@ -41,7 +28,6 @@ using ordered_json = nlohmann::ordered_json;
|
|||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <tuple>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@@ -556,20 +542,6 @@ TEST_CASE("regression tests 2")
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef SKIP_TESTS_FOR_TUPLE_REFERENCE_CONVERSION
|
|
||||||
SECTION("issue #2226 - std::tuple dangling reference - implicit conversion")
|
|
||||||
{
|
|
||||||
// by default, a one-element tuple holding a json reference converts to
|
|
||||||
// a one-element array; JSON_DISABLE_TUPLE_REFERENCE_CONVERSION removes
|
|
||||||
// this conversion (see unit-disable-tuple-reference-conversion.cpp)
|
|
||||||
const json j = true;
|
|
||||||
CHECK(std::is_constructible<json, std::tuple<const json&>>::value);
|
|
||||||
#ifndef SKIP_TESTS_FOR_JSON_REFERENCE_TUPLES
|
|
||||||
CHECK(json(std::forward_as_tuple(j)) == json::array({true}));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SECTION("PR #2181 - regression bug with lvalue")
|
SECTION("PR #2181 - regression bug with lvalue")
|
||||||
{
|
{
|
||||||
// see https://github.com/nlohmann/json/pull/2181#issuecomment-653326060
|
// see https://github.com/nlohmann/json/pull/2181#issuecomment-653326060
|
||||||
|
|||||||
@@ -3033,224 +3033,3 @@ TEST_CASE("UBJSON optimized array of unsigned integers beyond int64")
|
|||||||
CHECK(json::to_ubjson(j, true, true) == expected);
|
CHECK(json::to_ubjson(j, true, true) == expected);
|
||||||
CHECK(json::from_ubjson(expected) == j);
|
CHECK(json::from_ubjson(expected) == j);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
// the bytes that follow the marker of an integer: the value in the width of
|
|
||||||
// the marker (big endian for UBJSON, little endian for BJData), or, for a
|
|
||||||
// high-precision number, the length and the decimal digits
|
|
||||||
std::vector<std::uint8_t> integer_payload(const char marker, const json& value, const bool little_endian)
|
|
||||||
{
|
|
||||||
std::size_t width = 0;
|
|
||||||
switch (marker)
|
|
||||||
{
|
|
||||||
case 'i':
|
|
||||||
case 'U':
|
|
||||||
width = 1;
|
|
||||||
break;
|
|
||||||
case 'I':
|
|
||||||
case 'u':
|
|
||||||
width = 2;
|
|
||||||
break;
|
|
||||||
case 'l':
|
|
||||||
case 'm':
|
|
||||||
width = 4;
|
|
||||||
break;
|
|
||||||
case 'L':
|
|
||||||
case 'M':
|
|
||||||
width = 8;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
const std::string digits = value.dump();
|
|
||||||
std::vector<std::uint8_t> result = {'i', static_cast<std::uint8_t>(digits.size())};
|
|
||||||
for (const char c : digits)
|
|
||||||
{
|
|
||||||
result.push_back(static_cast<std::uint8_t>(c));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::uint64_t bits = value.is_number_unsigned()
|
|
||||||
? value.get<std::uint64_t>()
|
|
||||||
: static_cast<std::uint64_t>(value.get<std::int64_t>());
|
|
||||||
std::vector<std::uint8_t> result(width);
|
|
||||||
for (std::size_t i = 0; i < width; ++i)
|
|
||||||
{
|
|
||||||
result[little_endian ? i : width - 1 - i] = static_cast<std::uint8_t>(bits >> (8 * i));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
json i64(const std::int64_t v)
|
|
||||||
{
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
json u64(const std::uint64_t v)
|
|
||||||
{
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_CASE("UBJSON and BJData integer markers at every range edge")
|
|
||||||
{
|
|
||||||
// An optimized container announces the marker of its values after `$` and
|
|
||||||
// then writes every value without a marker, so the marker the writer
|
|
||||||
// announces and the width it writes must match for every value. This
|
|
||||||
// checks both for the values around each edge of the integer types, as
|
|
||||||
// scalars and as the values of optimized arrays and objects.
|
|
||||||
struct integer_case
|
|
||||||
{
|
|
||||||
json value;
|
|
||||||
char ubjson; // expected UBJSON marker
|
|
||||||
char bjdata; // expected BJData marker
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::int64_t int64_min = (std::numeric_limits<std::int64_t>::min)();
|
|
||||||
const std::int64_t int64_max = (std::numeric_limits<std::int64_t>::max)();
|
|
||||||
const std::uint64_t uint64_max = (std::numeric_limits<std::uint64_t>::max)();
|
|
||||||
|
|
||||||
const std::vector<integer_case> cases =
|
|
||||||
{
|
|
||||||
// int8
|
|
||||||
{i64(-129), 'I', 'I'},
|
|
||||||
{i64(-128), 'i', 'i'},
|
|
||||||
{i64(-127), 'i', 'i'},
|
|
||||||
{i64(-1), 'i', 'i'},
|
|
||||||
{i64(0), 'i', 'i'},
|
|
||||||
{u64(0), 'i', 'i'},
|
|
||||||
{i64(126), 'i', 'i'},
|
|
||||||
{i64(127), 'i', 'i'},
|
|
||||||
{u64(127), 'i', 'i'},
|
|
||||||
{i64(128), 'U', 'U'},
|
|
||||||
{u64(128), 'U', 'U'},
|
|
||||||
// uint8
|
|
||||||
{i64(254), 'U', 'U'},
|
|
||||||
{i64(255), 'U', 'U'},
|
|
||||||
{u64(255), 'U', 'U'},
|
|
||||||
{i64(256), 'I', 'I'},
|
|
||||||
{u64(256), 'I', 'I'},
|
|
||||||
// int16
|
|
||||||
{i64(-32769), 'l', 'l'},
|
|
||||||
{i64(-32768), 'I', 'I'},
|
|
||||||
{i64(-32767), 'I', 'I'},
|
|
||||||
{i64(32766), 'I', 'I'},
|
|
||||||
{i64(32767), 'I', 'I'},
|
|
||||||
{u64(32767), 'I', 'I'},
|
|
||||||
{i64(32768), 'l', 'u'},
|
|
||||||
{u64(32768), 'l', 'u'},
|
|
||||||
// uint16 (BJData only)
|
|
||||||
{i64(65534), 'l', 'u'},
|
|
||||||
{i64(65535), 'l', 'u'},
|
|
||||||
{u64(65535), 'l', 'u'},
|
|
||||||
{i64(65536), 'l', 'l'},
|
|
||||||
{u64(65536), 'l', 'l'},
|
|
||||||
// int32
|
|
||||||
{i64(-2147483649LL), 'L', 'L'},
|
|
||||||
{i64(-2147483648LL), 'l', 'l'},
|
|
||||||
{i64(-2147483647LL), 'l', 'l'},
|
|
||||||
{i64(2147483646LL), 'l', 'l'},
|
|
||||||
{i64(2147483647LL), 'l', 'l'},
|
|
||||||
{u64(2147483647ULL), 'l', 'l'},
|
|
||||||
{i64(2147483648LL), 'L', 'm'},
|
|
||||||
{u64(2147483648ULL), 'L', 'm'},
|
|
||||||
// uint32 (BJData only)
|
|
||||||
{i64(4294967294LL), 'L', 'm'},
|
|
||||||
{i64(4294967295LL), 'L', 'm'},
|
|
||||||
{u64(4294967295ULL), 'L', 'm'},
|
|
||||||
{i64(4294967296LL), 'L', 'L'},
|
|
||||||
{u64(4294967296ULL), 'L', 'L'},
|
|
||||||
// int64
|
|
||||||
{i64(int64_min), 'L', 'L'},
|
|
||||||
{i64(int64_min + 1), 'L', 'L'},
|
|
||||||
{i64(int64_max - 1), 'L', 'L'},
|
|
||||||
{i64(int64_max), 'L', 'L'},
|
|
||||||
{u64(static_cast<std::uint64_t>(int64_max)), 'L', 'L'},
|
|
||||||
// uint64 (BJData only; UBJSON writes a high-precision number)
|
|
||||||
{u64(static_cast<std::uint64_t>(int64_max) + 1), 'H', 'M'},
|
|
||||||
{u64(uint64_max - 1), 'H', 'M'},
|
|
||||||
{u64(uint64_max), 'H', 'M'},
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const auto& c : cases)
|
|
||||||
{
|
|
||||||
for (const bool bjdata :
|
|
||||||
{
|
|
||||||
false, true
|
|
||||||
})
|
|
||||||
{
|
|
||||||
const char marker = bjdata ? c.bjdata : c.ubjson;
|
|
||||||
const std::vector<std::uint8_t> payload = integer_payload(marker, c.value, bjdata);
|
|
||||||
const auto to_binary = [bjdata](const json & j, const bool use_size, const bool use_type)
|
|
||||||
{
|
|
||||||
return bjdata ? json::to_bjdata(j, use_size, use_type) : json::to_ubjson(j, use_size, use_type);
|
|
||||||
};
|
|
||||||
const auto from_binary = [bjdata](const std::vector<std::uint8_t>& v)
|
|
||||||
{
|
|
||||||
return bjdata ? json::from_bjdata(v) : json::from_ubjson(v);
|
|
||||||
};
|
|
||||||
INFO("value = " << c.value.dump() << (c.value.is_number_unsigned() ? " (unsigned)" : "") << ", format = " << (bjdata ? "BJData" : "UBJSON"));
|
|
||||||
|
|
||||||
// scalar
|
|
||||||
std::vector<std::uint8_t> expected = {static_cast<std::uint8_t>(marker)};
|
|
||||||
expected.insert(expected.end(), payload.begin(), payload.end());
|
|
||||||
for (const bool use_size :
|
|
||||||
{
|
|
||||||
false, true
|
|
||||||
})
|
|
||||||
{
|
|
||||||
CHECK(to_binary(c.value, use_size, false) == expected);
|
|
||||||
}
|
|
||||||
CHECK(from_binary(expected) == c.value);
|
|
||||||
|
|
||||||
const json arr = {c.value, c.value, c.value};
|
|
||||||
|
|
||||||
// array without count or type: every value has its marker
|
|
||||||
expected = {'['};
|
|
||||||
for (int i = 0; i < 3; ++i)
|
|
||||||
{
|
|
||||||
expected.push_back(static_cast<std::uint8_t>(marker));
|
|
||||||
expected.insert(expected.end(), payload.begin(), payload.end());
|
|
||||||
}
|
|
||||||
expected.push_back(']');
|
|
||||||
CHECK(to_binary(arr, false, false) == expected);
|
|
||||||
CHECK(from_binary(expected) == arr);
|
|
||||||
|
|
||||||
// array with count: every value has its marker
|
|
||||||
expected = {'[', '#', 'i', 3};
|
|
||||||
for (int i = 0; i < 3; ++i)
|
|
||||||
{
|
|
||||||
expected.push_back(static_cast<std::uint8_t>(marker));
|
|
||||||
expected.insert(expected.end(), payload.begin(), payload.end());
|
|
||||||
}
|
|
||||||
CHECK(to_binary(arr, true, false) == expected);
|
|
||||||
CHECK(from_binary(expected) == arr);
|
|
||||||
|
|
||||||
// array with type and count: the marker once, then the payloads
|
|
||||||
expected = {'[', '$', static_cast<std::uint8_t>(marker), '#', 'i', 3};
|
|
||||||
for (int i = 0; i < 3; ++i)
|
|
||||||
{
|
|
||||||
expected.insert(expected.end(), payload.begin(), payload.end());
|
|
||||||
}
|
|
||||||
CHECK(to_binary(arr, true, true) == expected);
|
|
||||||
CHECK(from_binary(expected) == arr);
|
|
||||||
|
|
||||||
// object with type and count: the marker once, then key and payload
|
|
||||||
const json obj = {{"a", c.value}, {"b", c.value}};
|
|
||||||
expected = {'{', '$', static_cast<std::uint8_t>(marker), '#', 'i', 2};
|
|
||||||
for (const char key :
|
|
||||||
{'a', 'b'
|
|
||||||
})
|
|
||||||
{
|
|
||||||
expected.push_back('i');
|
|
||||||
expected.push_back(1);
|
|
||||||
expected.push_back(static_cast<std::uint8_t>(key));
|
|
||||||
expected.insert(expected.end(), payload.begin(), payload.end());
|
|
||||||
}
|
|
||||||
CHECK(to_binary(obj, true, true) == expected);
|
|
||||||
CHECK(from_binary(expected) == obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,22 +7,4 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||||
|
|
||||||
// libc++ annotates std::mutex for Clang's thread safety analysis, so -Weverything
|
|
||||||
// reports -Wthread-safety-negative for the locks in doctest's reporters, which are
|
|
||||||
// only compiled in this file. __has_warning keeps older Clang versions from
|
|
||||||
// reporting an unknown warning group.
|
|
||||||
#if defined(__clang__) && defined(__has_warning)
|
|
||||||
#if __has_warning("-Wthread-safety-negative")
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wthread-safety-negative"
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "doctest_compatibility.h"
|
#include "doctest_compatibility.h"
|
||||||
|
|
||||||
#if defined(__clang__) && defined(__has_warning)
|
|
||||||
#if __has_warning("-Wthread-safety-negative")
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|||||||
+11
-15
@@ -1,8 +1,8 @@
|
|||||||
# amalgamate.py - Amalgamate C source and header files
|
# amalgamate.py - Amalgamate C source and header files
|
||||||
|
|
||||||
Origin: https://github.com/edlund/amalgamate (formerly hosted at
|
Origin: https://bitbucket.org/erikedlund/amalgamate
|
||||||
https://bitbucket.org/erikedlund/amalgamate, which no longer exists; see
|
|
||||||
`CHANGES.md` for the upstream commit this copy is based on)
|
Mirror: https://github.com/edlund/amalgamate
|
||||||
|
|
||||||
`amalgamate.py` aims to make it easy to use SQLite-style C source and header
|
`amalgamate.py` aims to make it easy to use SQLite-style C source and header
|
||||||
amalgamation in projects.
|
amalgamation in projects.
|
||||||
@@ -41,22 +41,21 @@ results.
|
|||||||
|
|
||||||
## Installing amalgamate.py
|
## Installing amalgamate.py
|
||||||
|
|
||||||
Python 3 is required.
|
Python v.2.7.0 or higher is required.
|
||||||
|
|
||||||
In this repository, `amalgamate.py` is not installed separately; it is run in
|
`amalgamate.py` can be tested and installed using the following commands:
|
||||||
place through `make amalgamate`, which calls it once for `json.hpp` and once
|
|
||||||
for `json_fwd.hpp` (see the root `Makefile`).
|
./test.sh && sudo -k cp ./amalgamate.py /usr/local/bin/
|
||||||
|
|
||||||
## Using amalgamate.py
|
## Using amalgamate.py
|
||||||
|
|
||||||
amalgamate.py -c path/to/config.json -s path/to/source/dir \
|
amalgamate.py [-v] -c path/to/config.json -s path/to/source/dir \
|
||||||
[-p path/to/prologue.(c|h)] [--verbose=yes|no]
|
[-p path/to/prologue.(c|h)]
|
||||||
|
|
||||||
* The `-c, --config` option should specify the path to a JSON config file which
|
* The `-c, --config` option should specify the path to a JSON config file which
|
||||||
lists the source files, include paths and where to write the resulting
|
lists the source files, include paths and where to write the resulting
|
||||||
amalgamation. `config_json.json` and `config_json_fwd.json` in this
|
amalgamation. Have a look at `test/source.c.json` and `test/include.h.json`
|
||||||
directory are the configs used for `json.hpp` and `json_fwd.hpp`; each
|
to see two examples.
|
||||||
sets `target`, `sources` and `include_paths`.
|
|
||||||
|
|
||||||
The optional `external` list names include paths that are kept as `#include`
|
The optional `external` list names include paths that are kept as `#include`
|
||||||
directives instead of being inlined, e.g. `["nlohmann/json.hpp"]` for a header
|
directives instead of being inlined, e.g. `["nlohmann/json.hpp"]` for a header
|
||||||
@@ -69,6 +68,3 @@ for `json_fwd.hpp` (see the root `Makefile`).
|
|||||||
* The `-p, --prologue` option should specify the path to a file which will be
|
* The `-p, --prologue` option should specify the path to a file which will be
|
||||||
added to the beginning of the amalgamation. It is optional.
|
added to the beginning of the amalgamation. It is optional.
|
||||||
|
|
||||||
* The `-v, --verbose` option takes `yes` or `no` (for example
|
|
||||||
`--verbose=yes`, as used by the Makefile). It is optional.
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1 @@
|
|||||||
# astyle is deliberately pinned: newer versions reformat the code, and this version
|
|
||||||
# defines the formatting that `make amalgamate` produces and check_amalgamation.yml enforces.
|
|
||||||
# Dependabot is told to ignore it in .github/dependabot.yml.
|
|
||||||
astyle==3.4.13
|
astyle==3.4.13
|
||||||
|
|||||||
@@ -2,26 +2,8 @@
|
|||||||
|
|
||||||
Generate the Natvis debugger visualization file for all supported namespace combinations.
|
Generate the Natvis debugger visualization file for all supported namespace combinations.
|
||||||
|
|
||||||
The ABI tag list and the library version are parsed from
|
|
||||||
`include/nlohmann/detail/abi_macros.hpp`, so this script must be re-run (via
|
|
||||||
`make natvis`) whenever an `NLOHMANN_JSON_ABI_TAG_*` macro is added to that
|
|
||||||
file or the library version is bumped — otherwise the committed
|
|
||||||
`nlohmann_json.natvis` drifts from the header it visualizes, and
|
|
||||||
`make check-amalgamation` fails.
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
make natvis
|
./generate_natvis.py --version X.Y.Z output_directory/
|
||||||
```
|
```
|
||||||
|
|
||||||
or, equivalently:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
./generate_natvis.py [--version X.Y.Z] [repository_root/]
|
|
||||||
```
|
|
||||||
|
|
||||||
`--version` and the output/repository-root directory both default to values
|
|
||||||
derived from this script's own location, so they only need to be given
|
|
||||||
explicitly when generating a Natvis file for a different checkout or a
|
|
||||||
version other than the one in `abi_macros.hpp`.
|
|
||||||
|
|||||||
@@ -7,75 +7,21 @@ import os
|
|||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
# Directory of the repository, assuming this script stays at
|
|
||||||
# tools/generate_natvis/generate_natvis.py. Used only as the default value
|
|
||||||
# for the "output" argument below.
|
|
||||||
REPO_ROOT = os.path.normpath(os.path.join(sys.path[0], '..', '..'))
|
|
||||||
|
|
||||||
|
|
||||||
def semver(v):
|
def semver(v):
|
||||||
if not re.fullmatch(r'\d+\.\d+\.\d+', v):
|
if not re.fullmatch(r'\d+\.\d+\.\d+', v):
|
||||||
raise ValueError
|
raise ValueError
|
||||||
return v
|
return v
|
||||||
|
|
||||||
|
|
||||||
def abi_info(repo_root):
|
|
||||||
"""Derive the ABI tag list (in NLOHMANN_JSON_ABI_TAGS_CONCAT order) and the
|
|
||||||
library version from <repo_root>/include/nlohmann/detail/abi_macros.hpp,
|
|
||||||
so this script cannot drift from the header it visualizes."""
|
|
||||||
abi_macros_hpp = os.path.join(repo_root, 'include', 'nlohmann', 'detail', 'abi_macros.hpp')
|
|
||||||
with open(abi_macros_hpp) as f:
|
|
||||||
content = f.read()
|
|
||||||
|
|
||||||
# find the NLOHMANN_JSON_ABI_TAGS_CONCAT(...) invocation that lists the
|
|
||||||
# NLOHMANN_JSON_ABI_TAG_* identifiers in order (not its own #define, which
|
|
||||||
# only names its formal parameters a, b, c, ...)
|
|
||||||
tag_idents = None
|
|
||||||
for args in re.findall(r'NLOHMANN_JSON_ABI_TAGS_CONCAT\(\s*(.*?)\)', content, re.S):
|
|
||||||
idents = re.findall(r'NLOHMANN_JSON_ABI_TAG_\w+', args)
|
|
||||||
if idents:
|
|
||||||
tag_idents = idents
|
|
||||||
break
|
|
||||||
if not tag_idents:
|
|
||||||
raise ValueError(f'could not find NLOHMANN_JSON_ABI_TAGS_CONCAT(...) in {abi_macros_hpp}')
|
|
||||||
|
|
||||||
abi_tags = []
|
|
||||||
for ident in tag_idents:
|
|
||||||
# each tag is #define'd to its suffix (e.g. _diag) when the matching
|
|
||||||
# JSON_* option is enabled, and to nothing in the #else branch; only
|
|
||||||
# the non-empty definition matches here
|
|
||||||
match = re.search(r'#define\s+' + re.escape(ident) + r'\s+(_\w+)\s*\n', content)
|
|
||||||
if not match:
|
|
||||||
raise ValueError(f'could not find a non-empty #define for {ident} in {abi_macros_hpp}')
|
|
||||||
abi_tags.append(match.group(1))
|
|
||||||
|
|
||||||
version = {}
|
|
||||||
for part in ('MAJOR', 'MINOR', 'PATCH'):
|
|
||||||
match = re.search(r'#define\s+NLOHMANN_JSON_VERSION_' + part + r'\s+(\d+)', content)
|
|
||||||
if not match:
|
|
||||||
raise ValueError(f'could not find NLOHMANN_JSON_VERSION_{part} in {abi_macros_hpp}')
|
|
||||||
version[part] = match.group(1)
|
|
||||||
|
|
||||||
return abi_tags, '{MAJOR}.{MINOR}.{PATCH}'.format(**version)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('--version', type=semver,
|
parser.add_argument('--version', required=True, type=semver, help='Library version number')
|
||||||
help='Library version number (default: parsed from '
|
parser.add_argument('output', help='Output directory for nlohmann_json.natvis')
|
||||||
'include/nlohmann/detail/abi_macros.hpp below "output")')
|
|
||||||
parser.add_argument('output', nargs='?', default=REPO_ROOT,
|
|
||||||
help='Repository root: where include/nlohmann/detail/abi_macros.hpp is '
|
|
||||||
'read from and where nlohmann_json.natvis is written '
|
|
||||||
'(default: the repository root this script lives in)')
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
derived_tags, derived_version = abi_info(args.output)
|
|
||||||
|
|
||||||
namespaces = ['nlohmann']
|
namespaces = ['nlohmann']
|
||||||
abi_prefix = 'json_abi'
|
abi_prefix = 'json_abi'
|
||||||
abi_tags = derived_tags
|
abi_tags = ['_diag', '_ldvcmp', '_dp', '_bics', '_psp', '_snul']
|
||||||
version = '_v' + (args.version or derived_version).replace('.', '_')
|
version = '_v' + args.version.replace('.', '_')
|
||||||
inline_namespaces = []
|
inline_namespaces = []
|
||||||
|
|
||||||
# generate all combinations of inline namespace names
|
# generate all combinations of inline namespace names
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
# macro_builder
|
|
||||||
|
|
||||||
Generates the argument-counting macros behind the `NLOHMANN_DEFINE_TYPE_*` and `NLOHMANN_DEFINE_DERIVED_TYPE_*`
|
|
||||||
macros in [`include/nlohmann/detail/macro_scope.hpp`](../../include/nlohmann/detail/macro_scope.hpp):
|
|
||||||
|
|
||||||
- `NLOHMANN_JSON_EXPAND`
|
|
||||||
- `NLOHMANN_JSON_GET_MACRO`, which selects a macro by the number of its arguments (64 slots)
|
|
||||||
- `NLOHMANN_JSON_PASTE`, which calls a function-like macro for each member, and its helpers `NLOHMANN_JSON_PASTE2`
|
|
||||||
to `NLOHMANN_JSON_PASTE64`
|
|
||||||
- `NLOHMANN_JSON_DOUBLE_PASTE`, which the `*_WITH_NAMES` macros use to call a function-like macro for each
|
|
||||||
(JSON name, member) pair, and its helpers `NLOHMANN_JSON_DOUBLE_PASTE3` to `NLOHMANN_JSON_DOUBLE_PASTE63`
|
|
||||||
- the slot table of `NLOHMANN_JSON_TYPE_BODY`, which dispatches `NLOHMANN_DEFINE_TYPE_*(Type)` (no further
|
|
||||||
arguments) to the zero-member implementation and every other argument count to the one-or-more-member
|
|
||||||
implementation
|
|
||||||
|
|
||||||
The number of slots (`max_args` in [`main.cpp`](main.cpp)) sets the member limit of these macros.
|
|
||||||
`NLOHMANN_JSON_PASTE` and `NLOHMANN_JSON_TYPE_BODY` take the function/prefix as their first argument, so 64 slots
|
|
||||||
allow 63 members; `NLOHMANN_JSON_DOUBLE_PASTE` additionally consumes its members two at a time (name, member), so
|
|
||||||
it only defines the odd helpers up to `NLOHMANN_JSON_DOUBLE_PASTE63`.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
From the project root:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
c++ -std=c++11 tools/macro_builder/main.cpp -o macro_builder
|
|
||||||
./macro_builder
|
|
||||||
./macro_builder type_body
|
|
||||||
```
|
|
||||||
|
|
||||||
1. Run `./macro_builder` (no arguments). In `include/nlohmann/detail/macro_scope.hpp`, replace the lines from
|
|
||||||
`#define NLOHMANN_JSON_EXPAND( x ) x` to the `#define NLOHMANN_JSON_DOUBLE_PASTE63(...)` line with the output,
|
|
||||||
without its trailing empty line.
|
|
||||||
2. Run `./macro_builder type_body`. Replace the lines from `#define NLOHMANN_JSON_TYPE_BODY(Prefix, ...)` to the
|
|
||||||
`NLOHMANN_JSON_TYPE_BODY_SENTINEL))` line with the output.
|
|
||||||
3. Run `make amalgamate`. It updates `single_include/nlohmann/json.hpp` and runs `make pretty`, which indents the
|
|
||||||
continuation lines that the tool writes unindented.
|
|
||||||
|
|
||||||
With an unchanged `main.cpp`, these steps reproduce both blocks of `macro_scope.hpp` byte for byte. `make
|
|
||||||
macro_builder_check` (also run by CI, see `.github/workflows/check_amalgamation.yml`) automates this: it builds
|
|
||||||
`main.cpp`, regenerates both blocks, and fails on a diff against the checked-in header.
|
|
||||||
|
|
||||||
## Maintained by hand
|
|
||||||
|
|
||||||
The tool does not generate everything that depends on the number of slots. When changing `max_args`, also update:
|
|
||||||
|
|
||||||
- the documented limit of 63 members in `docs/mkdocs/docs` and the tests at that limit in
|
|
||||||
`tests/src/unit-udt_macro.cpp`
|
|
||||||
|
|
||||||
All three tables pass one macro name per slot to `NLOHMANN_JSON_GET_MACRO`, so they need exactly as many entries as
|
|
||||||
it has slots.
|
|
||||||
@@ -1,14 +1,10 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
// Builds NLOHMANN_JSON_EXPAND, NLOHMANN_JSON_GET_MACRO, and the
|
void build_code(int max_args)
|
||||||
// NLOHMANN_JSON_PASTE / NLOHMANN_JSON_PASTE2..PASTE<max_args> dispatch table
|
|
||||||
// and recursive definitions.
|
|
||||||
string build_paste_code(int max_args)
|
|
||||||
{
|
{
|
||||||
stringstream ss;
|
stringstream ss;
|
||||||
ss << "#define NLOHMANN_JSON_EXPAND( x ) x" << endl;
|
ss << "#define NLOHMANN_JSON_EXPAND( x ) x" << endl;
|
||||||
@@ -34,75 +30,14 @@ string build_paste_code(int max_args)
|
|||||||
ss << "v" << i-1 << ")" << endl;
|
ss << "v" << i-1 << ")" << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ss.str();
|
cout << ss.str() << endl;
|
||||||
}
|
|
||||||
|
|
||||||
// Builds the NLOHMANN_JSON_DOUBLE_PASTE dispatch table and recursive
|
|
||||||
// definitions used by the *_WITH_NAMES macros. Its GET_MACRO dispatch reuses
|
|
||||||
// the same max_args slots as NLOHMANN_JSON_PASTE, but DOUBLE_PASTE consumes
|
|
||||||
// its arguments two at a time (name, member), so an even slot count falls
|
|
||||||
// back to the next lower odd NLOHMANN_JSON_DOUBLE_PASTE<N>.
|
|
||||||
string build_double_paste_code(int max_args)
|
|
||||||
{
|
|
||||||
stringstream ss;
|
|
||||||
ss << "#define NLOHMANN_JSON_DOUBLE_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \\" << endl;
|
|
||||||
for (int i = max_args ; i > 1 ; i--)
|
|
||||||
{
|
|
||||||
int k = (i % 2 == 1) ? i : i - 1;
|
|
||||||
ss << "NLOHMANN_JSON_DOUBLE_PASTE" << k << ", \\" << endl;
|
|
||||||
}
|
|
||||||
ss << "NLOHMANN_JSON_DOUBLE_PASTE1)(__VA_ARGS__))" << endl;
|
|
||||||
|
|
||||||
ss << "#define NLOHMANN_JSON_DOUBLE_PASTE3(func, v1, v2) func(v1, v2)" << endl;
|
|
||||||
for (int k = 5 ; k <= max_args - 1 ; k += 2)
|
|
||||||
{
|
|
||||||
ss << "#define NLOHMANN_JSON_DOUBLE_PASTE" << k << "(func, ";
|
|
||||||
for (int j = 1 ; j < k - 1 ; j++)
|
|
||||||
ss << "v" << j << ", ";
|
|
||||||
ss << "v" << k - 1 << ") NLOHMANN_JSON_DOUBLE_PASTE3(func, v1, v2) NLOHMANN_JSON_DOUBLE_PASTE" << k - 2 << "(func, ";
|
|
||||||
for (int j = 3 ; j < k - 1 ; j++)
|
|
||||||
ss << "v" << j << ", ";
|
|
||||||
ss << "v" << k - 1 << ")" << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Builds the NLOHMANN_JSON_TYPE_BODY dispatch table: max_args - 1 slots
|
|
||||||
// selecting the *_MEMBERS implementation and a final slot selecting
|
|
||||||
// *_EMPTY, so NLOHMANN_DEFINE_TYPE_*(Type) with no further arguments still
|
|
||||||
// resolves (issue #4041).
|
|
||||||
string build_type_body_table(int max_args)
|
|
||||||
{
|
|
||||||
stringstream ss;
|
|
||||||
ss << "#define NLOHMANN_JSON_TYPE_BODY(Prefix, ...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \\" << endl;
|
|
||||||
const int per_line = 8;
|
|
||||||
for (int i = 1 ; i <= max_args ; i++)
|
|
||||||
{
|
|
||||||
ss << (i == max_args ? "Prefix ## EMPTY" : "Prefix ## MEMBERS") << ", ";
|
|
||||||
if (i % per_line == 0)
|
|
||||||
ss << "\\" << endl;
|
|
||||||
}
|
|
||||||
ss << "NLOHMANN_JSON_TYPE_BODY_SENTINEL))" << endl;
|
|
||||||
|
|
||||||
return ss.str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
int max_args = 64;
|
int max_args = 64;
|
||||||
|
build_code(max_args);
|
||||||
// With "type_body", print only the NLOHMANN_JSON_TYPE_BODY dispatch
|
|
||||||
// table (a separate insertion point in macro_scope.hpp); otherwise
|
|
||||||
// print the EXPAND/GET_MACRO/PASTE/DOUBLE_PASTE block that precedes it.
|
|
||||||
if (argc > 1 && string(argv[1]) == "type_body")
|
|
||||||
{
|
|
||||||
cout << build_type_body_table(max_args);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cout << build_paste_code(max_args) << build_double_paste_code(max_args);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import socket
|
|
||||||
import ssl
|
|
||||||
import sys
|
import sys
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
@@ -36,7 +34,7 @@ JSON_VERSION_RE = re.compile(r'\s*#\s*define\s+NLOHMANN_JSON_VERSION_MAJOR\s+')
|
|||||||
|
|
||||||
class ExitHandler(logging.StreamHandler):
|
class ExitHandler(logging.StreamHandler):
|
||||||
def __init__(self, level):
|
def __init__(self, level):
|
||||||
"""Exit the process on log records at or above level."""
|
"""."""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.level = level
|
self.level = level
|
||||||
|
|
||||||
@@ -56,7 +54,7 @@ def is_project_root(test_dir='.'):
|
|||||||
|
|
||||||
class DirectoryEventBucket:
|
class DirectoryEventBucket:
|
||||||
def __init__(self, callback, delay=1.2, threshold=0.8):
|
def __init__(self, callback, delay=1.2, threshold=0.8):
|
||||||
"""Batch directory events and pass their common path to callback."""
|
"""."""
|
||||||
self.delay = delay
|
self.delay = delay
|
||||||
self.threshold = timedelta(seconds=threshold)
|
self.threshold = timedelta(seconds=threshold)
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
@@ -101,7 +99,7 @@ class WorkTree:
|
|||||||
make_command = 'make'
|
make_command = 'make'
|
||||||
|
|
||||||
def __init__(self, root_dir, tree_dir):
|
def __init__(self, root_dir, tree_dir):
|
||||||
"""Track the working tree at tree_dir and its amalgamated header."""
|
"""."""
|
||||||
self.root_dir = root_dir
|
self.root_dir = root_dir
|
||||||
self.tree_dir = tree_dir
|
self.tree_dir = tree_dir
|
||||||
self.rel_dir = os.path.relpath(tree_dir, root_dir)
|
self.rel_dir = os.path.relpath(tree_dir, root_dir)
|
||||||
@@ -116,11 +114,11 @@ class WorkTree:
|
|||||||
self.build_time = t.strftime(DATETIME_FORMAT)
|
self.build_time = t.strftime(DATETIME_FORMAT)
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
"""Hash by working tree directory."""
|
"""."""
|
||||||
return hash((self.tree_dir))
|
return hash((self.tree_dir))
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
"""Compare by working tree directory."""
|
"""."""
|
||||||
if not isinstance(other, type(self)):
|
if not isinstance(other, type(self)):
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
return self.tree_dir == other.tree_dir
|
return self.tree_dir == other.tree_dir
|
||||||
@@ -152,7 +150,7 @@ class WorkTree:
|
|||||||
|
|
||||||
class WorkTrees(FileSystemEventHandler):
|
class WorkTrees(FileSystemEventHandler):
|
||||||
def __init__(self, root_dir):
|
def __init__(self, root_dir):
|
||||||
"""Find the working trees below root_dir and watch it for changes."""
|
"""."""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.root_dir = root_dir
|
self.root_dir = root_dir
|
||||||
self.trees = set([])
|
self.trees = set([])
|
||||||
@@ -252,11 +250,11 @@ class WorkTrees(FileSystemEventHandler):
|
|||||||
self.observer.stop()
|
self.observer.stop()
|
||||||
self.observer.join()
|
self.observer.join()
|
||||||
|
|
||||||
class HeaderRequestHandler(SimpleHTTPRequestHandler):
|
class HeaderRequestHandler(SimpleHTTPRequestHandler): # lgtm[py/missing-call-to-init]
|
||||||
cors_origins = DEFAULT_CORS_ORIGINS
|
cors_origins = DEFAULT_CORS_ORIGINS
|
||||||
|
|
||||||
def __init__(self, request, client_address, server):
|
def __init__(self, request, client_address, server):
|
||||||
"""Handle a request for a header below the working trees' root directory."""
|
"""."""
|
||||||
self.worktrees = server.worktrees
|
self.worktrees = server.worktrees
|
||||||
self.worktree = None
|
self.worktree = None
|
||||||
try:
|
try:
|
||||||
@@ -338,7 +336,7 @@ class HeaderRequestHandler(SimpleHTTPRequestHandler):
|
|||||||
|
|
||||||
class DualStackServer(ThreadingHTTPServer):
|
class DualStackServer(ThreadingHTTPServer):
|
||||||
def __init__(self, addr, worktrees):
|
def __init__(self, addr, worktrees):
|
||||||
"""Serve the headers of worktrees on addr."""
|
"""."""
|
||||||
self.worktrees = worktrees
|
self.worktrees = worktrees
|
||||||
super().__init__(addr, HeaderRequestHandler)
|
super().__init__(addr, HeaderRequestHandler)
|
||||||
|
|
||||||
@@ -351,6 +349,8 @@ class DualStackServer(ThreadingHTTPServer):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import argparse
|
import argparse
|
||||||
|
import ssl
|
||||||
|
import socket
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
# exit code
|
# exit code
|
||||||
|
|||||||
Reference in New Issue
Block a user