mirror of
https://github.com/nlohmann/json.git
synced 2026-07-28 21:34:55 +00:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ce130af4e | ||
|
|
5b5ba1ac35 | ||
|
|
85839d7408 | ||
|
|
6fb162a394 | ||
|
|
0bc01a066c |
@@ -15,14 +15,6 @@ guidance.
|
|||||||
|
|
||||||
For vulnerabilities in third-party dependencies or modules, please report them directly to the respective maintainers.
|
For vulnerabilities in third-party dependencies or modules, please report them directly to the respective maintainers.
|
||||||
|
|
||||||
## Unofficial packages
|
|
||||||
|
|
||||||
This project does not publish an official npm package. The npm package
|
|
||||||
[`nlohmann-json`](https://www.npmjs.com/package/nlohmann-json) (or similarly named packages) is not maintained or
|
|
||||||
endorsed by this project. See the
|
|
||||||
[package managers documentation](https://json.nlohmann.me/integration/package_managers/#npm) for supported
|
|
||||||
integration options.
|
|
||||||
|
|
||||||
## Additional Resources
|
## Additional Resources
|
||||||
|
|
||||||
- Explore security-related topics and contribute to tools and projects through
|
- Explore security-related topics and contribute to tools and projects through
|
||||||
|
|||||||
@@ -39,14 +39,14 @@ jobs:
|
|||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
- name: Checkout pull request
|
- name: Checkout pull request
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
path: main
|
path: main
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Checkout tools
|
- name: Checkout tools
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
path: tools
|
path: tools
|
||||||
ref: develop
|
ref: develop
|
||||||
|
|||||||
@@ -32,20 +32,20 @@ jobs:
|
|||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1
|
uses: github/codeql-action/init@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
||||||
with:
|
with:
|
||||||
languages: c-cpp
|
languages: c-cpp
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1
|
uses: github/codeql-action/autobuild@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1
|
uses: github/codeql-action/analyze@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
- name: 'Checkout Repository'
|
- name: 'Checkout Repository'
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: 'Dependency Review'
|
- name: 'Dependency Review'
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ jobs:
|
|||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
@@ -43,6 +43,6 @@ jobs:
|
|||||||
output: 'flawfinder_results.sarif'
|
output: 'flawfinder_results.sarif'
|
||||||
|
|
||||||
- name: Upload analysis results to GitHub Security tab
|
- name: Upload analysis results to GitHub Security tab
|
||||||
uses: github/codeql-action/upload-sarif@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1
|
uses: github/codeql-action/upload-sarif@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
||||||
with:
|
with:
|
||||||
sarif_file: ${{github.workspace}}/flawfinder_results.sarif
|
sarif_file: ${{github.workspace}}/flawfinder_results.sarif
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
|
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
@@ -45,7 +45,7 @@ jobs:
|
|||||||
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
|
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
@@ -62,7 +62,7 @@ jobs:
|
|||||||
standard: [11, 14, 17, 20, 23, 26]
|
standard: [11, 14, 17, 20, 23, 26]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
|
||||||
- name: Install virtual environment
|
- name: Install virtual environment
|
||||||
run: make install_venv -C docs/mkdocs
|
run: make install_venv -C docs/mkdocs
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ jobs:
|
|||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
- name: "Checkout code"
|
- name: "Checkout code"
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
@@ -76,6 +76,6 @@ jobs:
|
|||||||
|
|
||||||
# Upload the results to GitHub's code scanning dashboard.
|
# Upload the results to GitHub's code scanning dashboard.
|
||||||
- name: "Upload to code-scanning"
|
- name: "Upload to code-scanning"
|
||||||
uses: github/codeql-action/upload-sarif@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1
|
uses: github/codeql-action/upload-sarif@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
||||||
with:
|
with:
|
||||||
sarif_file: results.sarif
|
sarif_file: results.sarif
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
# Checkout project source
|
# Checkout project source
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ jobs:
|
|||||||
|
|
||||||
# Upload SARIF file generated in previous step
|
# Upload SARIF file generated in previous step
|
||||||
- name: Upload SARIF file
|
- name: Upload SARIF file
|
||||||
uses: github/codeql-action/upload-sarif@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1
|
uses: github/codeql-action/upload-sarif@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
||||||
with:
|
with:
|
||||||
sarif_file: semgrep.sarif
|
sarif_file: semgrep.sarif
|
||||||
if: always()
|
if: always()
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
- uses: actions/stale@1e223db275d687790206a7acac4d1a11bd6fe629 # v10.4.0
|
- uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0
|
||||||
with:
|
with:
|
||||||
stale-issue-label: 'state: stale'
|
stale-issue-label: 'state: stale'
|
||||||
stale-pr-label: 'state: stale'
|
stale-pr-label: 'state: stale'
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: gcc:latest
|
container: gcc:latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -43,11 +43,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
wget -q -O - "https://github.com/facebook/infer/releases/download/v1.3.0/infer-linux-x86_64-v1.3.0.tar.xz" | sudo tar -C /opt -xJ
|
wget -q -O - "https://github.com/facebook/infer/releases/download/v1.3.0/infer-linux-x86_64-v1.3.0.tar.xz" | sudo tar -C /opt -xJ
|
||||||
sudo ln -s /opt/infer-linux-x86_64-v1.3.0/bin/infer /usr/local/bin/infer
|
sudo ln -s /opt/infer-linux-x86_64-v1.3.0/bin/infer /usr/local/bin/infer
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -66,11 +66,11 @@ jobs:
|
|||||||
|
|
||||||
- name: Install Valgrind
|
- name: Install Valgrind
|
||||||
run: sudo apt-get update ; sudo apt-get install -y valgrind
|
run: sudo apt-get update ; sudo apt-get install -y valgrind
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -85,11 +85,11 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Install git, clang-tools, iwyu (ci_single_binaries), and unzip
|
- name: Install git, clang-tools, iwyu (ci_single_binaries), and unzip
|
||||||
run: apt-get update ; apt-get install -y git clang-tools iwyu unzip
|
run: apt-get update ; apt-get install -y git clang-tools iwyu unzip
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -104,11 +104,11 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Install build-essential
|
- name: Install build-essential
|
||||||
run: apt-get update ; apt-get install -y build-essential unzip wget git libssl-dev
|
run: apt-get update ; apt-get install -y build-essential unzip wget git libssl-dev
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -122,7 +122,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Install dependencies and de_DE locale
|
- name: Install dependencies and de_DE locale
|
||||||
@@ -163,15 +163,7 @@ jobs:
|
|||||||
export DEBIAN_FRONTEND=noninteractive
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
apt-get update
|
apt-get update
|
||||||
apt-get install -y --no-install-recommends software-properties-common ca-certificates gnupg make git
|
apt-get install -y --no-install-recommends software-properties-common ca-certificates gnupg make git
|
||||||
# add-apt-repository resolves the PPA through the Launchpad API,
|
add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||||
# which intermittently times out or fails the team lookup (the plain
|
|
||||||
# "deb ..." sources below never hit Launchpad and never flake).
|
|
||||||
# Retry with backoff so a transient Launchpad blip does not fail CI.
|
|
||||||
for attempt in 1 2 3 4 5; do
|
|
||||||
add-apt-repository -y ppa:ubuntu-toolchain-r/test && break
|
|
||||||
echo "::warning::add-apt-repository ppa:ubuntu-toolchain-r/test failed (attempt ${attempt}/5); retrying"
|
|
||||||
sleep $((attempt * 10))
|
|
||||||
done
|
|
||||||
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ bionic main"
|
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ bionic main"
|
||||||
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ bionic universe"
|
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ bionic universe"
|
||||||
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial main"
|
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial main"
|
||||||
@@ -180,11 +172,11 @@ jobs:
|
|||||||
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial-updates universe"
|
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial-updates universe"
|
||||||
apt-get update
|
apt-get update
|
||||||
apt-get install -y --no-install-recommends g++-${{ matrix.compiler }}
|
apt-get install -y --no-install-recommends g++-${{ matrix.compiler }}
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: CXX=g++-${{ matrix.compiler }} cmake -S . -B build -DJSON_CI=On
|
run: CXX=g++-${{ matrix.compiler }} cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -198,11 +190,11 @@ jobs:
|
|||||||
compiler: ['7', '8', '9', '10', '11', '12', '13', '14', '15', 'latest']
|
compiler: ['7', '8', '9', '10', '11', '12', '13', '14', '15', 'latest']
|
||||||
container: gcc:${{ matrix.compiler }}
|
container: gcc:${{ matrix.compiler }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -215,11 +207,11 @@ jobs:
|
|||||||
compiler: ['3.4', '3.5', '3.6', '3.7', '3.8', '3.9', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15-bullseye', '16', '17', '18', '19', '20', 'latest']
|
compiler: ['3.4', '3.5', '3.6', '3.7', '3.8', '3.9', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15-bullseye', '16', '17', '18', '19', '20', 'latest']
|
||||||
container: silkeh/clang:${{ matrix.compiler }}
|
container: silkeh/clang:${{ matrix.compiler }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||||
- name: Set env FORCE_STDCPPFS_FLAG for clang 7 / 8 / 9 / 10
|
- name: Set env FORCE_STDCPPFS_FLAG for clang 7 / 8 / 9 / 10
|
||||||
run: echo "JSON_FORCED_GLOBAL_COMPILE_OPTIONS=-DJSON_HAS_FILESYSTEM=0;-DJSON_HAS_EXPERIMENTAL_FILESYSTEM=0" >> "$GITHUB_ENV"
|
run: echo "JSON_FORCED_GLOBAL_COMPILE_OPTIONS=-DJSON_HAS_FILESYSTEM=0;-DJSON_HAS_EXPERIMENTAL_FILESYSTEM=0" >> "$GITHUB_ENV"
|
||||||
if: ${{ matrix.compiler == '7' || matrix.compiler == '8' || matrix.compiler == '9' || matrix.compiler == '10' }}
|
if: ${{ matrix.compiler == '7' || matrix.compiler == '8' || matrix.compiler == '9' || matrix.compiler == '10' }}
|
||||||
@@ -235,11 +227,11 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
standard: [11, 14, 17, 20, 23, 26]
|
standard: [11, 14, 17, 20, 23, 26]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -255,11 +247,11 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Install git and unzip
|
- name: Install git and unzip
|
||||||
run: apt-get update ; apt-get install -y git unzip
|
run: apt-get update ; apt-get install -y git unzip
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build with libc++
|
- name: Build with libc++
|
||||||
@@ -282,11 +274,11 @@ jobs:
|
|||||||
cuda: ['11.8.0', '12.1.1', '12.6.3']
|
cuda: ['11.8.0', '12.1.1', '12.6.3']
|
||||||
container: nvidia/cuda:${{ matrix.cuda }}-devel-ubuntu22.04
|
container: nvidia/cuda:${{ matrix.cuda }}-devel-ubuntu22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -299,14 +291,14 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: ${{ matrix.container }}
|
container: ${{ matrix.container }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
# The module test uses `import std;`, which needs CMake's experimental
|
# The module test uses `import std;`, which needs CMake's experimental
|
||||||
# import-std support. Its opt-in token is CMake-version-specific, so pin
|
# import-std support. Its opt-in token is CMake-version-specific, so pin
|
||||||
# CMake to the version whose token is set in tests/module_cpp20/CMakeLists.txt.
|
# CMake to the version whose token is set in tests/module_cpp20/CMakeLists.txt.
|
||||||
- name: Get pinned CMake and ninja
|
- name: Get pinned CMake and ninja
|
||||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||||
with:
|
with:
|
||||||
cmakeVersion: 4.3.4
|
cmakeVersion: 4.3.4
|
||||||
# Clang: the std library module is provided by libc++ (the image's libstdc++
|
# Clang: the std library module is provided by libc++ (the image's libstdc++
|
||||||
@@ -328,11 +320,11 @@ jobs:
|
|||||||
# Intel's own last officially published image that still includes it.
|
# Intel's own last officially published image that still includes it.
|
||||||
container: intel/oneapi-hpckit:2023.2.1-devel-ubuntu22.04
|
container: intel/oneapi-hpckit:2023.2.1-devel-ubuntu22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -345,9 +337,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: intel/oneapi-hpckit:latest
|
container: intel/oneapi-hpckit:latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -357,9 +349,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DJSON_CI=On
|
run: cmake -S . -B build -DJSON_CI=On
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -375,11 +367,11 @@ jobs:
|
|||||||
|
|
||||||
- name: Install emscripten
|
- name: Install emscripten
|
||||||
uses: mymindstorm/setup-emsdk@4528d102f7230f0e7b276855c01ea1159be0e984 # v16
|
uses: mymindstorm/setup-emsdk@4528d102f7230f0e7b276855c01ea1159be0e984 # v16
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -GNinja
|
run: cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -GNinja
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -396,7 +388,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
architecture: [x64, x86]
|
architecture: [x64, x86]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Set up MinGW
|
- name: Set up MinGW
|
||||||
@@ -49,7 +49,7 @@ jobs:
|
|||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Set extra CXX_FLAGS for latest std_version
|
- name: Set extra CXX_FLAGS for latest std_version
|
||||||
@@ -86,9 +86,9 @@ jobs:
|
|||||||
runs-on: windows-2025
|
runs-on: windows-2025
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||||
- name: Set extra CXX_FLAGS for latest std_version
|
- name: Set extra CXX_FLAGS for latest std_version
|
||||||
# /wd5285 silences C5285 emitted by the bundled third-party doctest.h, which
|
# /wd5285 silences C5285 emitted by the bundled third-party doctest.h, which
|
||||||
# specializes std::tuple (newly diagnosed by the VS2026 v145 toolset)
|
# specializes std::tuple (newly diagnosed by the VS2026 v145 toolset)
|
||||||
@@ -122,7 +122,7 @@ jobs:
|
|||||||
runs-on: windows-11-arm
|
runs-on: windows-11-arm
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Run CMake (Release)
|
- name: Run CMake (Release)
|
||||||
run: cmake -S . -B build -G "Visual Studio 17 2022" -A ARM64 -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/W4 /WX"
|
run: cmake -S . -B build -G "Visual Studio 17 2022" -A ARM64 -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/W4 /WX"
|
||||||
if: matrix.build_type == 'Release'
|
if: matrix.build_type == 'Release'
|
||||||
@@ -143,7 +143,7 @@ jobs:
|
|||||||
version: [11.0.1, 12.0.1, 13.0.1, 14.0.6, 15.0.7, 16.0.6, 18.1.8, 19.1.7, 20.1.8]
|
version: [11.0.1, 12.0.1, 13.0.1, 14.0.6, 15.0.7, 16.0.6, 18.1.8, 19.1.7, 20.1.8]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Install Clang
|
- name: Install Clang
|
||||||
@@ -173,7 +173,7 @@ jobs:
|
|||||||
architecture: [Win32, x64]
|
architecture: [Win32, x64]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
@@ -186,14 +186,14 @@ jobs:
|
|||||||
ci_module_cpp20:
|
ci_module_cpp20:
|
||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
# The module test uses `import std;`, which needs CMake's experimental
|
# The module test uses `import std;`, which needs CMake's experimental
|
||||||
# import-std support. Its opt-in token is CMake-version-specific, so pin
|
# import-std support. Its opt-in token is CMake-version-specific, so pin
|
||||||
# CMake to the version whose token is set in tests/module_cpp20/CMakeLists.txt.
|
# CMake to the version whose token is set in tests/module_cpp20/CMakeLists.txt.
|
||||||
- name: Get pinned CMake and ninja
|
- name: Get pinned CMake and ninja
|
||||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||||
with:
|
with:
|
||||||
cmakeVersion: 4.3.4
|
cmakeVersion: 4.3.4
|
||||||
- name: Run CMake (Debug)
|
- name: Run CMake (Debug)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ cc_library(
|
|||||||
"include/nlohmann/adl_serializer.hpp",
|
"include/nlohmann/adl_serializer.hpp",
|
||||||
"include/nlohmann/byte_container_with_subtype.hpp",
|
"include/nlohmann/byte_container_with_subtype.hpp",
|
||||||
"include/nlohmann/detail/abi_macros.hpp",
|
"include/nlohmann/detail/abi_macros.hpp",
|
||||||
|
"include/nlohmann/detail/conversions/from_chars.hpp",
|
||||||
"include/nlohmann/detail/conversions/from_json.hpp",
|
"include/nlohmann/detail/conversions/from_json.hpp",
|
||||||
"include/nlohmann/detail/conversions/to_chars.hpp",
|
"include/nlohmann/detail/conversions/to_chars.hpp",
|
||||||
"include/nlohmann/detail/conversions/to_json.hpp",
|
"include/nlohmann/detail/conversions/to_json.hpp",
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ You can sponsor this library at [GitHub Sponsors](https://github.com/sponsors/nl
|
|||||||
- [Steve Sperandeo](https://github.com/homer6)
|
- [Steve Sperandeo](https://github.com/homer6)
|
||||||
- [Robert Jefe Lindstädt](https://github.com/eljefedelrodeodeljefe)
|
- [Robert Jefe Lindstädt](https://github.com/eljefedelrodeodeljefe)
|
||||||
- [Steve Wagner](https://github.com/ciroque)
|
- [Steve Wagner](https://github.com/ciroque)
|
||||||
|
- [Lion Yang](https://github.com/LionNatsu)
|
||||||
|
|
||||||
### Further support
|
### Further support
|
||||||
|
|
||||||
|
|||||||
@@ -43,17 +43,6 @@ Strong guarantee: if an exception is thrown, there are no changes to any JSON va
|
|||||||
Throws [`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) if a string stored inside the JSON value
|
Throws [`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) if a string stored inside the JSON value
|
||||||
is not UTF-8 encoded and `error_handler` is set to `strict`
|
is not UTF-8 encoded and `error_handler` is set to `strict`
|
||||||
|
|
||||||
!!! warning "Serializing untrusted input"
|
|
||||||
|
|
||||||
When serializing values that may contain invalid or untrusted UTF-8 (e.g., bytes taken directly from network
|
|
||||||
input), `dump()` throws [`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) in the default
|
|
||||||
`strict` mode. To serialize such data without throwing, pass
|
|
||||||
[`error_handler_t::replace`](error_handler_t.md) (substitutes U+FFFD) or
|
|
||||||
[`error_handler_t::ignore`](error_handler_t.md). Callers that serialize untrusted input on a crash-sensitive path
|
|
||||||
should either choose a non-strict error handler or wrap `dump()` in a `#!cpp try`/`#!cpp catch`.
|
|
||||||
|
|
||||||
See the [FAQ](../../home/faq.md#serializing-untrusted-or-invalid-utf-8) for details.
|
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
Linear.
|
Linear.
|
||||||
|
|||||||
@@ -40,9 +40,6 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
is not an object; example: `"to serialize to BSON, top-level type must be object, but is string"`
|
is not an object; example: `"to serialize to BSON, top-level type must be object, but is string"`
|
||||||
- Throws [`out_of_range.409`](../../home/exceptions.md#jsonexceptionout_of_range409) if a key in the JSON object contains
|
- Throws [`out_of_range.409`](../../home/exceptions.md#jsonexceptionout_of_range409) if a key in the JSON object contains
|
||||||
a null byte (code point U+0000); example: `"BSON key cannot contain code point U+0000 (at byte 2)"`
|
a null byte (code point U+0000); example: `"BSON key cannot contain code point U+0000 (at byte 2)"`
|
||||||
- Throws [`out_of_range.412`](../../home/exceptions.md#jsonexceptionout_of_range412) if the length of a document, array,
|
|
||||||
string, or binary value exceeds the range of the 32-bit BSON length field; example:
|
|
||||||
`"BSON length 2147483661 exceeds maximum of 2147483647"`
|
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
|
|||||||
@@ -898,22 +898,6 @@ A JSON Patch `add` operation cannot be applied because the target location's par
|
|||||||
|
|
||||||
This exception was added in version 3.13.0. Before that, this situation hit an internal assertion (aborting the program in debug builds) or was silently ignored when assertions were disabled.
|
This exception was added in version 3.13.0. Before that, this situation hit an internal assertion (aborting the program in debug builds) or was silently ignored when assertions were disabled.
|
||||||
|
|
||||||
### json.exception.out_of_range.412
|
|
||||||
|
|
||||||
BSON stores the length of documents, arrays, strings, and binary values in a signed 32-bit integer. This exception is thrown when a value is too large to be described by such a length field.
|
|
||||||
|
|
||||||
!!! failure "Example message"
|
|
||||||
|
|
||||||
```
|
|
||||||
BSON length 2147483661 exceeds maximum of 2147483647
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
|
|
||||||
This exception was added in version 3.13.0. Before that, the length was silently truncated, and
|
|
||||||
[`to_bson`](../api/basic_json/to_bson.md) produced documents with negative length prefixes that
|
|
||||||
[`from_bson`](../api/basic_json/from_bson.md) rejected.
|
|
||||||
|
|
||||||
## Further exceptions
|
## Further exceptions
|
||||||
|
|
||||||
This exception is thrown in case of errors that cannot be classified with the
|
This exception is thrown in case of errors that cannot be classified with the
|
||||||
|
|||||||
@@ -194,27 +194,6 @@ The library uses `std::numeric_limits<number_float_t>::digits10` (15 for IEEE `d
|
|||||||
|
|
||||||
See [this section](../features/types/number_handling.md#number-serialization) on the library's number handling for more information.
|
See [this section](../features/types/number_handling.md#number-serialization) on the library's number handling for more information.
|
||||||
|
|
||||||
### Serializing untrusted or invalid UTF-8
|
|
||||||
|
|
||||||
!!! question "Questions"
|
|
||||||
|
|
||||||
- Why does `dump()` throw when I serialize data that came from the network?
|
|
||||||
- Is CVE-2024-34363 a vulnerability in this library?
|
|
||||||
|
|
||||||
Crashes reported against this library that stem from an uncaught
|
|
||||||
[`type_error.316`](exceptions.md#jsonexceptiontype_error316) while serializing unvalidated input (e.g.,
|
|
||||||
CVE-2024-34363) are a usage issue, not a library vulnerability:
|
|
||||||
[`dump()`](../api/basic_json/dump.md) throws in its default `strict` mode because
|
|
||||||
[RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259#section-8.1) requires JSON text to be valid UTF-8.
|
|
||||||
|
|
||||||
The recommended pattern is to pass a non-strict [`error_handler`](../api/basic_json/error_handler_t.md) or to handle the
|
|
||||||
exception:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// replace invalid sequences with U+FFFD instead of throwing
|
|
||||||
const auto s = j.dump(-1, ' ', false, json::error_handler_t::replace);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using JSON values with `std::format` or `fmt`
|
### Using JSON values with `std::format` or `fmt`
|
||||||
|
|
||||||
!!! question
|
!!! question
|
||||||
|
|||||||
@@ -14,5 +14,6 @@ You can sponsor this library at [GitHub Sponsors](https://github.com/sponsors/nl
|
|||||||
- [Steve Sperandeo](https://github.com/homer6)
|
- [Steve Sperandeo](https://github.com/homer6)
|
||||||
- [Robert Jefe Lindstädt](https://github.com/eljefedelrodeodeljefe)
|
- [Robert Jefe Lindstädt](https://github.com/eljefedelrodeodeljefe)
|
||||||
- [Steve Wagner](https://github.com/ciroque)
|
- [Steve Wagner](https://github.com/ciroque)
|
||||||
|
- [Lion Yang](https://github.com/LionNatsu)
|
||||||
|
|
||||||
Thanks everyone!
|
Thanks everyone!
|
||||||
|
|||||||
@@ -930,12 +930,6 @@ If you are using [CocoaPods](https://cocoapods.org), you can use the library by
|
|||||||
to your podfile (see [an example](https://bitbucket.org/benman/nlohmann_json-cocoapod/src/master/)). Please file issues
|
to your podfile (see [an example](https://bitbucket.org/benman/nlohmann_json-cocoapod/src/master/)). Please file issues
|
||||||
[here](https://bitbucket.org/benman/nlohmann_json-cocoapod/issues?status=new&status=open).
|
[here](https://bitbucket.org/benman/nlohmann_json-cocoapod/issues?status=new&status=open).
|
||||||
|
|
||||||
## npm
|
|
||||||
|
|
||||||
This project does not publish an official [npm](https://www.npmjs.com) package. The npm package
|
|
||||||
[`nlohmann-json`](https://www.npmjs.com/package/nlohmann-json) (or similarly named packages) is not maintained or
|
|
||||||
endorsed by this project. Use one of the package managers listed above, or integrate the single header directly.
|
|
||||||
|
|
||||||
## ESP-IDF and PlatformIO
|
## ESP-IDF and PlatformIO
|
||||||
|
|
||||||
There is no official package published to the [ESP-IDF Component Registry](https://components.espressif.com) or the
|
There is no official package published to the [ESP-IDF Component Registry](https://components.espressif.com) or the
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ wheel==0.47.0
|
|||||||
|
|
||||||
mkdocs==1.6.1 # documentation framework
|
mkdocs==1.6.1 # documentation framework
|
||||||
mkdocs-git-revision-date-localized-plugin==1.5.3 # plugin "git-revision-date-localized"
|
mkdocs-git-revision-date-localized-plugin==1.5.3 # plugin "git-revision-date-localized"
|
||||||
mkdocs-material==9.7.7 # theme for mkdocs
|
mkdocs-material==9.7.6 # theme for mkdocs
|
||||||
mkdocs-material-extensions==1.3.1 # extensions
|
mkdocs-material-extensions==1.3.1 # extensions
|
||||||
mkdocs-minify-plugin==0.8.0 # plugin "minify"
|
mkdocs-minify-plugin==0.8.0 # plugin "minify"
|
||||||
mkdocs-redirects==1.2.3 # plugin "redirects"
|
mkdocs-redirects==1.2.3 # plugin "redirects"
|
||||||
|
|||||||
@@ -0,0 +1,636 @@
|
|||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | JSON for Modern C++
|
||||||
|
// | | |__ | | | | | | version 3.12.0
|
||||||
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
|
|
||||||
|
#if JSON_HAS_FROM_CHARS
|
||||||
|
#include <algorithm> // find, find_if
|
||||||
|
#include <charconv> // from_chars, from_chars_result
|
||||||
|
#include <cmath> // isnan
|
||||||
|
#else
|
||||||
|
#include <cctype> // isspace
|
||||||
|
#include <cerrno> // ERANGE
|
||||||
|
#include <clocale> // localeconv, newlocale, freelocale
|
||||||
|
#include <cstdlib> // strto*
|
||||||
|
#include <memory> // unique_ptr
|
||||||
|
#include <type_traits> // enable_if, is_unsigned, remove_pointer
|
||||||
|
#include <utility> // declval
|
||||||
|
|
||||||
|
#ifdef __has_include
|
||||||
|
#if __has_include(<xlocale.h>)
|
||||||
|
#include <xlocale.h> // strto*_l, isspace_l
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#include <limits> // numeric_limits<T>::[has_]infinity, quiet_NaN
|
||||||
|
#include <system_error> // errc
|
||||||
|
|
||||||
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
constexpr char get_locale_independent_decimal_point() noexcept
|
||||||
|
{
|
||||||
|
return '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
#if JSON_HAS_FROM_CHARS
|
||||||
|
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
// Delegate to std::from_chars if supported //
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Number parsing implementation details
|
||||||
|
|
||||||
|
A traits class template that allows users of nlohmann::detail::from_chars to
|
||||||
|
query details of the used implementation.
|
||||||
|
|
||||||
|
@tparam T numeric type to parse, matching the `value` argument of from_chars
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
struct from_chars_traits
|
||||||
|
{
|
||||||
|
/// whether the from_chars in unaffected by the current global C locale
|
||||||
|
static constexpr bool is_locale_independent = true;
|
||||||
|
|
||||||
|
/// getter for the character used as decimal point by from_chars
|
||||||
|
static constexpr char(*get_decimal_point)() = &get_locale_independent_decimal_point;
|
||||||
|
};
|
||||||
|
|
||||||
|
using std::from_chars_result;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Parse integer/floating-point numbers
|
||||||
|
|
||||||
|
Parses the string [first, last) into the numeric type T.
|
||||||
|
|
||||||
|
This implementation merely delegates to `std::from_chars` with one noteworthy
|
||||||
|
difference: Different C++ standard library implementations do not agree on
|
||||||
|
whether `value` should be set in the out-of-range case for floating-point
|
||||||
|
numbers. Presently, libstdc++ leaves `value` unchanged whereas libc++ and MS STL
|
||||||
|
set `value` to +/-0 or +/-inf which allows its users to distinguish between the
|
||||||
|
various cases of over- and underflow. The C++ proposal [P4168][1] details this
|
||||||
|
discrepancy and advocates to standardize the latter behavior.
|
||||||
|
|
||||||
|
Since we need to be able to distinguish absolute underflow (+/-0) from absolute
|
||||||
|
overflow (+/-inf), this implementation "corrects" the out-of-range behavior
|
||||||
|
accordingly by estimating the effective exponent through manual string parsing.
|
||||||
|
|
||||||
|
[1]: https://isocpp.org/files/papers/P4168R0.html
|
||||||
|
|
||||||
|
@tparam T numeric type to parse
|
||||||
|
@param[in] first points to the beginning of the parsed string (inclusive)
|
||||||
|
@param[in] end points to the end of the parsed string (exclusive)
|
||||||
|
@param[out] value references the variable to set the parsed number
|
||||||
|
@return structure consisting of `ptr` and `ec` such that:
|
||||||
|
- If the string starting at `first` matches a number, `ptr` points to
|
||||||
|
the address immediately after the last character that matches.
|
||||||
|
* If the matched number can be represented by `T`, `value` is set and
|
||||||
|
`ec` is value-initialized.
|
||||||
|
* If the matched number cannot be represented by `T`, `ec` is set to
|
||||||
|
`std::errc::result_out_of_range` and depending on the type of `T`:
|
||||||
|
+ `value` is left unchanged for integral `T`,
|
||||||
|
+ `value` is set to +/-0 or +/-inf for floating-point `T`.
|
||||||
|
- If the string starting at `first` doesn't match a number, `ptr`
|
||||||
|
points to `first`, `ec` is `std::errc::invalid_argument`, and `value`
|
||||||
|
is left unchanged.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
JSON_HEDLEY_NON_NULL(1, 2)
|
||||||
|
inline from_chars_result from_chars(const char* first, const char* last, T& value)
|
||||||
|
{
|
||||||
|
// No implementation of std::from_chars will set its value argument to NaN,
|
||||||
|
// so by using NaN as the initial value, we can tell if it changed.
|
||||||
|
T inner_value = std::numeric_limits<T>::quiet_NaN();
|
||||||
|
auto res = std::from_chars(first, last, inner_value);
|
||||||
|
|
||||||
|
if constexpr (std::numeric_limits<T>::has_infinity)
|
||||||
|
{
|
||||||
|
if (res.ec == std::errc::result_out_of_range)
|
||||||
|
{
|
||||||
|
if (std::isnan(inner_value)) // inner_value was not set
|
||||||
|
{
|
||||||
|
// [first, res.ptr) matches a valid floating-point number that's
|
||||||
|
// out-of-range and our std::from_chars did not set `value`, so we
|
||||||
|
// need to distinguish the type of under-/overflow ourselves.
|
||||||
|
const char* mantissa_begin = *first == '-' ? first + 1 : first;
|
||||||
|
const char* mantissa_end = std::find_if(mantissa_begin, res.ptr, [](char c)
|
||||||
|
{
|
||||||
|
return c == 'e' || c == 'E';
|
||||||
|
});
|
||||||
|
const char* decimal_point = std::find(mantissa_begin, mantissa_end, '.');
|
||||||
|
const char* significant_digit = std::find_if(mantissa_begin, mantissa_end, [](char d)
|
||||||
|
{
|
||||||
|
return d >= '1' && d <= '9';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Position of the significant digit relative to the decimal gives
|
||||||
|
// the order of magnitude of the mantissa.
|
||||||
|
auto effective_exponent = static_cast<int>(decimal_point - significant_digit);
|
||||||
|
|
||||||
|
// If the number includes an explicit exponent, add that to the the
|
||||||
|
// total exponent.
|
||||||
|
if (mantissa_end != res.ptr)
|
||||||
|
{
|
||||||
|
const char* exponent_begin = *(mantissa_end + 1) == '+'
|
||||||
|
? mantissa_end + 2 // skip the exponent's + sign
|
||||||
|
: mantissa_end + 1;
|
||||||
|
int exponent = 0;
|
||||||
|
auto exp_res = std::from_chars(exponent_begin, res.ptr, exponent);
|
||||||
|
JSON_ASSERT(exp_res.ptr == res.ptr);
|
||||||
|
|
||||||
|
if (exp_res.ec == std::errc::result_out_of_range)
|
||||||
|
{
|
||||||
|
// NOTE: Parentheses around function names mitigate min/max macro collision on Windows
|
||||||
|
effective_exponent = *exponent_begin == '-'
|
||||||
|
? (std::numeric_limits<int>::min)()
|
||||||
|
: (std::numeric_limits<int>::max)();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JSON_ASSERT(exp_res.ec == std::errc{});
|
||||||
|
effective_exponent += exponent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set `value` to the sign-correct non-finite value based on the
|
||||||
|
// effective exponent.
|
||||||
|
value = (*first == '-' ? -1 : 1) * (effective_exponent < 0
|
||||||
|
? T{}
|
||||||
|
: std::numeric_limits<T>::infinity());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = inner_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.ec == std::errc{})
|
||||||
|
{
|
||||||
|
value = inner_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
// Fallback implementation using strto*[_l] //
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Extended locale functions on Windows
|
||||||
|
|
||||||
|
A trait object which provides wrappers for isspace and the strto* family of
|
||||||
|
functions, based on the platform's support for extended locale APIs.
|
||||||
|
|
||||||
|
This is the Windows implementation. The primary class template definition
|
||||||
|
falls back to the standard locale-dependent functions, which is used on
|
||||||
|
MinGW, whose C runtime only provides an incomplete subset of the
|
||||||
|
`_<funcname>_l` family (e.g., missing `_strtof_l`/`_strtold_l` depending on
|
||||||
|
the toolchain version). A specialization for genuine MSVC (including
|
||||||
|
clang-cl, which mimics the MSVC ABI and CRT) is used instead, as the full
|
||||||
|
`_<funcname>_l` family has reliably been available there since at least
|
||||||
|
Visual Studio 2005.
|
||||||
|
*/
|
||||||
|
template <typename = const char*, typename = float>
|
||||||
|
struct extended_locale_traits
|
||||||
|
{
|
||||||
|
static constexpr bool is_locale_independent = false;
|
||||||
|
|
||||||
|
JSON_HEDLEY_PURE
|
||||||
|
static char get_decimal_point() noexcept
|
||||||
|
{
|
||||||
|
const auto* loc = localeconv();
|
||||||
|
JSON_ASSERT(loc != nullptr);
|
||||||
|
return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isspace(int c) noexcept
|
||||||
|
{
|
||||||
|
return std::isspace(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(runtime/int)
|
||||||
|
static long long strtoll(const char* str, char** endptr) noexcept
|
||||||
|
{
|
||||||
|
return std::strtoll(str, endptr, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(runtime/int)
|
||||||
|
static unsigned long long strtoull(const char* str, char** endptr) noexcept
|
||||||
|
{
|
||||||
|
return std::strtoull(str, endptr, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr float(&strtof)(const char*, char**) = std::strtof;
|
||||||
|
static constexpr double(&strtod)(const char*, char**) = std::strtod;
|
||||||
|
static constexpr long double(&strtold)(const char*, char**) = std::strtold;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Get a pointer to a globally shared instance of the "C" locale on Windows
|
||||||
|
|
||||||
|
This function uses _create_locale/_free_locale to allocate a "C" locale instance
|
||||||
|
which can be passed to extended locale APIs. This instance is created on first
|
||||||
|
use and has static storage duration, such that it can be used for the duration
|
||||||
|
of the program.
|
||||||
|
*/
|
||||||
|
inline _locale_t get_c_locale_t() noexcept
|
||||||
|
{
|
||||||
|
struct LocaleTDeleter
|
||||||
|
{
|
||||||
|
void operator()(_locale_t loc) noexcept
|
||||||
|
{
|
||||||
|
::_free_locale(loc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using LocaleUPtr = std::unique_ptr<std::remove_pointer<_locale_t>::type, LocaleTDeleter>;
|
||||||
|
static const LocaleUPtr c_locale = LocaleUPtr{::_create_locale(LC_ALL, "C"), LocaleTDeleter{}};
|
||||||
|
return c_locale.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct extended_locale_traits<T, float>
|
||||||
|
{
|
||||||
|
static constexpr bool is_locale_independent = true;
|
||||||
|
static constexpr char(&get_decimal_point)() = get_locale_independent_decimal_point;
|
||||||
|
|
||||||
|
static int isspace(int c) noexcept
|
||||||
|
{
|
||||||
|
return _isspace_l(c, get_c_locale_t());
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(runtime/int)
|
||||||
|
static long long strtoll(const char* str, char** endptr) noexcept
|
||||||
|
{
|
||||||
|
return _strtoll_l(str, endptr, 10, get_c_locale_t());
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(runtime/int)
|
||||||
|
static unsigned long long strtoull(const char* str, char** endptr) noexcept
|
||||||
|
{
|
||||||
|
return _strtoull_l(str, endptr, 10, get_c_locale_t());
|
||||||
|
}
|
||||||
|
|
||||||
|
static float strtof(T str, char** str_end)
|
||||||
|
{
|
||||||
|
return _strtof_l(str, str_end, get_c_locale_t());
|
||||||
|
}
|
||||||
|
|
||||||
|
static double strtod(T str, char** str_end)
|
||||||
|
{
|
||||||
|
return _strtod_l(str, str_end, get_c_locale_t());
|
||||||
|
}
|
||||||
|
|
||||||
|
static long double strtold(T str, char** str_end)
|
||||||
|
{
|
||||||
|
return _strtold_l(str, str_end, get_c_locale_t());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Get a pointer to a globally shared instance of the "C" locale on POSIX
|
||||||
|
|
||||||
|
This function uses newlocale/freelocale to allocate a "C" locale instance
|
||||||
|
which can be passed to extended locale APIs. This instance is created on first
|
||||||
|
use and has static storage duration, such that it can be used for the duration
|
||||||
|
of the program.
|
||||||
|
|
||||||
|
newlocale/freelocale themselves are in POSIX and are available independent of
|
||||||
|
the platform's support for extended locale APIs.
|
||||||
|
*/
|
||||||
|
inline locale_t get_c_locale_t() noexcept
|
||||||
|
{
|
||||||
|
struct LocaleTDeleter
|
||||||
|
{
|
||||||
|
void operator()(locale_t loc) noexcept
|
||||||
|
{
|
||||||
|
::freelocale(loc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using LocaleUPtr = std::unique_ptr<std::remove_pointer<locale_t>::type, LocaleTDeleter>;
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wexit-time-destructors"
|
||||||
|
#endif
|
||||||
|
static const LocaleUPtr c_locale = LocaleUPtr {::newlocale(LC_ALL_MASK, "C", nullptr), LocaleTDeleter{}};
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return c_locale.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Extended locale functions on POSIX
|
||||||
|
|
||||||
|
A trait object which provides wrappers for isspace and the strto* family of
|
||||||
|
functions, based on the platform's support for extended locale APIs.
|
||||||
|
|
||||||
|
This is the POSIX implementation where extended locale support might not be
|
||||||
|
available. The primary class template definition falls back to the standard
|
||||||
|
locale-dependent functions. A partial specialization uses SFINAE to detect the
|
||||||
|
availability of `strtof_l` (as a proxy for the whole `<funcname>_l` family) and
|
||||||
|
provides access to locale-independent functions.
|
||||||
|
*/
|
||||||
|
template <typename = const char*, typename = float>
|
||||||
|
struct extended_locale_traits
|
||||||
|
{
|
||||||
|
static constexpr bool is_locale_independent = false;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Query the decimal point used by the current C locale
|
||||||
|
|
||||||
|
Note that calling this function while switching the global C locale from
|
||||||
|
another thread is undefined behavior.
|
||||||
|
*/
|
||||||
|
JSON_HEDLEY_PURE
|
||||||
|
static char get_decimal_point() noexcept
|
||||||
|
{
|
||||||
|
const auto* loc = localeconv();
|
||||||
|
JSON_ASSERT(loc != nullptr);
|
||||||
|
return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isspace(int c) noexcept
|
||||||
|
{
|
||||||
|
return std::isspace(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(runtime/int)
|
||||||
|
static long long strtoll(const char* str, char** endptr) noexcept
|
||||||
|
{
|
||||||
|
return std::strtoll(str, endptr, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(runtime/int)
|
||||||
|
static unsigned long long strtoull(const char* str, char** endptr) noexcept
|
||||||
|
{
|
||||||
|
return std::strtoull(str, endptr, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr float(&strtof)(const char*, char**) = std::strtof;
|
||||||
|
static constexpr double(&strtod)(const char*, char**) = std::strtod;
|
||||||
|
static constexpr long double(&strtold)(const char*, char**) = std::strtold;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct extended_locale_traits<T, decltype(strtof_l(
|
||||||
|
std::declval<T>(),
|
||||||
|
std::declval<char**>(),
|
||||||
|
std::declval<locale_t>()))>
|
||||||
|
{
|
||||||
|
static constexpr bool is_locale_independent = true;
|
||||||
|
static constexpr char(&get_decimal_point)() = get_locale_independent_decimal_point;
|
||||||
|
|
||||||
|
static int isspace(int c) noexcept
|
||||||
|
{
|
||||||
|
return isspace_l(c, get_c_locale_t());
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(runtime/int)
|
||||||
|
static long long strtoll(const char* str, char** endptr) noexcept
|
||||||
|
{
|
||||||
|
return ::strtoll_l(str, endptr, 10, get_c_locale_t());
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(runtime/int)
|
||||||
|
static unsigned long long strtoull(const char* str, char** endptr) noexcept
|
||||||
|
{
|
||||||
|
return ::strtoull_l(str, endptr, 10, get_c_locale_t());
|
||||||
|
}
|
||||||
|
|
||||||
|
static float strtof(T str, char** str_end)
|
||||||
|
{
|
||||||
|
return ::strtof_l(str, str_end, get_c_locale_t());
|
||||||
|
}
|
||||||
|
|
||||||
|
static double strtod(T str, char** str_end)
|
||||||
|
{
|
||||||
|
return ::strtod_l(str, str_end, get_c_locale_t());
|
||||||
|
}
|
||||||
|
|
||||||
|
static long double strtold(T str, char** str_end)
|
||||||
|
{
|
||||||
|
return ::strtold_l(str, str_end, get_c_locale_t());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Number parsing implementation details
|
||||||
|
|
||||||
|
A traits class template that allows users of nlohmann::detail::from_chars to
|
||||||
|
query details of the used implementation.
|
||||||
|
|
||||||
|
Each specialization provides the following static members:
|
||||||
|
- is_locale_independent: whether the from_chars in unaffected by the current
|
||||||
|
global C locale
|
||||||
|
- get_decimal_point: getter for the character used as decimal point by from_chars
|
||||||
|
- strto: dispatches to the C standard library strto* function for type T, or to
|
||||||
|
the corresponding extended locale function, if available.
|
||||||
|
|
||||||
|
@tparam T numeric type to parse, matching the `value` argument of from_chars
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
struct from_chars_traits;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct from_chars_traits<long long> // NOLINT(runtime/int)
|
||||||
|
{
|
||||||
|
static constexpr bool is_locale_independent = extended_locale_traits<>::is_locale_independent;
|
||||||
|
|
||||||
|
static char get_decimal_point()
|
||||||
|
{
|
||||||
|
return extended_locale_traits<>::get_decimal_point();
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(runtime/int)
|
||||||
|
static constexpr long long(*strto)(const char*, char**) = &extended_locale_traits<>::strtoll;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct from_chars_traits<unsigned long long> // NOLINT(runtime/int)
|
||||||
|
{
|
||||||
|
static constexpr bool is_locale_independent = extended_locale_traits<>::is_locale_independent;
|
||||||
|
|
||||||
|
static char get_decimal_point()
|
||||||
|
{
|
||||||
|
return extended_locale_traits<>::get_decimal_point();
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(runtime/int)
|
||||||
|
static constexpr unsigned long long(*strto)(const char*, char**) = &extended_locale_traits<>::strtoull;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct from_chars_traits<float>
|
||||||
|
{
|
||||||
|
static constexpr bool is_locale_independent = extended_locale_traits<>::is_locale_independent;
|
||||||
|
|
||||||
|
static char get_decimal_point()
|
||||||
|
{
|
||||||
|
return extended_locale_traits<>::get_decimal_point();
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr float(*strto)(const char*, char**) = &extended_locale_traits<>::strtof;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct from_chars_traits<double>
|
||||||
|
{
|
||||||
|
static constexpr bool is_locale_independent = extended_locale_traits<>::is_locale_independent;
|
||||||
|
|
||||||
|
static char get_decimal_point()
|
||||||
|
{
|
||||||
|
return extended_locale_traits<>::get_decimal_point();
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr double(*strto)(const char*, char**) = &extended_locale_traits<>::strtod;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct from_chars_traits<long double>
|
||||||
|
{
|
||||||
|
static constexpr bool is_locale_independent = extended_locale_traits<>::is_locale_independent;
|
||||||
|
|
||||||
|
static char get_decimal_point()
|
||||||
|
{
|
||||||
|
return extended_locale_traits<>::get_decimal_point();
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr long double(*strto)(const char*, char**) = &extended_locale_traits<>::strtold;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr typename std::enable_if<std::numeric_limits<T>::has_infinity, bool>::type is_out_of_range_value(T value) noexcept
|
||||||
|
{
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wfloat-equal"
|
||||||
|
#endif
|
||||||
|
return value == T {} || value == std::numeric_limits<T>::infinity() || value == -std::numeric_limits<T>::infinity();
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr typename std::enable_if < !std::numeric_limits<T>::has_infinity, bool >::type is_out_of_range_value(T value) noexcept
|
||||||
|
{
|
||||||
|
// NOTE: Parentheses around function names mitigate min/max macro collision on Windows
|
||||||
|
return value == (std::numeric_limits<T>::max)() || value == (std::numeric_limits<T>::min)();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct from_chars_result
|
||||||
|
{
|
||||||
|
const char* ptr;
|
||||||
|
std::errc ec;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Parse integer/floating-point numbers
|
||||||
|
|
||||||
|
Parses the string [first, last) into the numeric type T.
|
||||||
|
|
||||||
|
This implementation uses the C standard library functions strto*, or their
|
||||||
|
extended locale counterparts strto*_l, to emulate the behavior of
|
||||||
|
`std::from_chars` on platforms where it is not (fully) supported.
|
||||||
|
|
||||||
|
Regarding the under-/overflow behavior, this implementation adopts the behavior
|
||||||
|
proposed by [P4168][1] and implemented by libc++ and MS STL of setting `value`
|
||||||
|
to the corresponding non-finite floating-point value in case of an out-of-range
|
||||||
|
result.
|
||||||
|
|
||||||
|
[1]: https://isocpp.org/files/papers/P4168R0.html
|
||||||
|
|
||||||
|
@pre Unlike std::from_chars, this implementation requires the string to be
|
||||||
|
null-terminated, such that `last` dereferences into a NUL byte.
|
||||||
|
|
||||||
|
@tparam T numeric type to parse
|
||||||
|
@param[in] first points to the beginning of the parsed string (inclusive)
|
||||||
|
@param[in] end points to the end of the parsed string (exclusive)
|
||||||
|
@param[out] value references the variable to set the parsed number
|
||||||
|
@return structure consisting of `ptr` and `ec` such that:
|
||||||
|
- If the string starting at `first` matches a number, `ptr` points to
|
||||||
|
the address immediately after the last character that matches.
|
||||||
|
* If the matched number can be represented by `T`, `value` is set and
|
||||||
|
`ec` is value-initialized.
|
||||||
|
* If the matched number cannot be represented by `T`, `ec` is set to
|
||||||
|
`std::errc::result_out_of_range` and depending on the type of `T`:
|
||||||
|
+ `value` is left unchanged for integral `T`,
|
||||||
|
+ `value` is set to +/-0 or +/-inf for floating-point `T`.
|
||||||
|
- If the string starting at `first` doesn't match a number, `ptr`
|
||||||
|
points to `first`, `ec` is `std::errc::invalid_argument`, and `value`
|
||||||
|
is left unchanged.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
JSON_HEDLEY_NON_NULL(1, 2)
|
||||||
|
inline from_chars_result from_chars(const char* first, const char* last, T& value)
|
||||||
|
{
|
||||||
|
JSON_ASSERT(*last == '\0');
|
||||||
|
|
||||||
|
// Unlike strto*, from_chars does not accept leading whitespace or + signs
|
||||||
|
if (first == last || *first == '+'
|
||||||
|
|| (std::is_unsigned<T>::value && *first == '-')
|
||||||
|
|| extended_locale_traits<>::isspace(*first) != 0)
|
||||||
|
{
|
||||||
|
return {first, std::errc::invalid_argument};
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
char* ptr = nullptr; // NOLINT(misc-const-correctness)
|
||||||
|
T result = from_chars_traits<T>::strto(first, &ptr);
|
||||||
|
|
||||||
|
if (ptr == first)
|
||||||
|
{
|
||||||
|
return {ptr, std::errc::invalid_argument};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upon under-/overflow, strto* returns a marginal value and sets errno.
|
||||||
|
// Note that it is NOT sufficient to just check errno: strto* only clears
|
||||||
|
// errno if the parsed string actually parses into 0/[U]LLONG_MIN/-_MAX
|
||||||
|
// and this result was returned without indicating an under-/overflow.
|
||||||
|
// Otherwise, errno may not be relied upon to indicate the *absence* of
|
||||||
|
// an out-of-range error.
|
||||||
|
if (is_out_of_range_value(result) && errno == ERANGE)
|
||||||
|
{
|
||||||
|
if (std::numeric_limits<T>::has_infinity)
|
||||||
|
{
|
||||||
|
// ONLY for floating-point types, set `value` to the same non-finite
|
||||||
|
// result returned by strto* to allow users to distinguish between
|
||||||
|
// different types of under-/overflow.
|
||||||
|
value = result;
|
||||||
|
}
|
||||||
|
return {ptr, std::errc::result_out_of_range};
|
||||||
|
}
|
||||||
|
|
||||||
|
value = result;
|
||||||
|
return {ptr, std::errc{}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
@@ -163,39 +163,12 @@ class binary_reader
|
|||||||
// BSON //
|
// BSON //
|
||||||
//////////
|
//////////
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Validate a BSON document's declared size against the bytes read.
|
|
||||||
|
|
||||||
A BSON document starts with an int32 that counts its own total length in
|
|
||||||
bytes, including that prefix and the trailing 0x00. The reader is driven
|
|
||||||
by the terminator rather than the declared length, so without this check a
|
|
||||||
nested document could declare a length that disagrees with where its
|
|
||||||
terminator actually falls and quietly hand the bytes in between to the
|
|
||||||
enclosing document. A well-formed document is at least 5 bytes (the prefix
|
|
||||||
plus the terminator); the equality also rejects those impossible sizes,
|
|
||||||
since at least 5 bytes are always consumed.
|
|
||||||
|
|
||||||
@param[in] document_start value of chars_read before the size prefix
|
|
||||||
@param[in] document_size the declared document size
|
|
||||||
@return whether the declared size matches the number of bytes read
|
|
||||||
*/
|
|
||||||
bool check_bson_document_size(const std::size_t document_start, const std::int32_t document_size)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(document_size < 0 || static_cast<std::size_t>(document_size) != chars_read - document_start))
|
|
||||||
{
|
|
||||||
return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
|
|
||||||
exception_message(input_format_t::bson, concat("document size ", std::to_string(document_size), " does not match the number of bytes read (", std::to_string(chars_read - document_start), ")"), "document"), nullptr));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Reads in a BSON-object and passes it to the SAX-parser.
|
@brief Reads in a BSON-object and passes it to the SAX-parser.
|
||||||
@return whether a valid BSON-value was passed to the SAX parser
|
@return whether a valid BSON-value was passed to the SAX parser
|
||||||
*/
|
*/
|
||||||
bool parse_bson_internal()
|
bool parse_bson_internal()
|
||||||
{
|
{
|
||||||
const std::size_t document_start = chars_read;
|
|
||||||
std::int32_t document_size{};
|
std::int32_t document_size{};
|
||||||
get_number<std::int32_t, true>(input_format_t::bson, document_size);
|
get_number<std::int32_t, true>(input_format_t::bson, document_size);
|
||||||
|
|
||||||
@@ -209,11 +182,6 @@ class binary_reader
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!check_bson_document_size(document_start, document_size)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sax->end_object();
|
return sax->end_object();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,7 +397,6 @@ class binary_reader
|
|||||||
*/
|
*/
|
||||||
bool parse_bson_array()
|
bool parse_bson_array()
|
||||||
{
|
{
|
||||||
const std::size_t document_start = chars_read;
|
|
||||||
std::int32_t document_size{};
|
std::int32_t document_size{};
|
||||||
get_number<std::int32_t, true>(input_format_t::bson, document_size);
|
get_number<std::int32_t, true>(input_format_t::bson, document_size);
|
||||||
|
|
||||||
@@ -443,11 +410,6 @@ class binary_reader
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!check_bson_document_size(document_start, document_size)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sax->end_array();
|
return sax->end_array();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1884,29 +1846,6 @@ class binary_reader
|
|||||||
return get_ubjson_value(get_char ? get_ignore_noop() : current);
|
return get_ubjson_value(get_char ? get_ignore_noop() : current);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief reject a negative UBJSON/BJData string length
|
|
||||||
|
|
||||||
String and key lengths are written with signed integer markers (i, I, l,
|
|
||||||
L). A negative value is malformed; without this check get_string() would
|
|
||||||
silently treat it as an empty string and leave the following bytes to be
|
|
||||||
misread as the next value. This mirrors the non-negative check the
|
|
||||||
optimized-container count path already performs in get_ubjson_size_value.
|
|
||||||
|
|
||||||
@param[in] len the string length read from the input
|
|
||||||
@return whether the length is valid (non-negative)
|
|
||||||
*/
|
|
||||||
template<typename NumberType>
|
|
||||||
bool check_ubjson_string_length(const NumberType len)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(len < 0))
|
|
||||||
{
|
|
||||||
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
|
|
||||||
exception_message(input_format, "string length must not be negative", "string"), nullptr));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief reads a UBJSON string
|
@brief reads a UBJSON string
|
||||||
|
|
||||||
@@ -1944,25 +1883,25 @@ class binary_reader
|
|||||||
case 'i':
|
case 'i':
|
||||||
{
|
{
|
||||||
std::int8_t len{};
|
std::int8_t len{};
|
||||||
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
return get_number(input_format, len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'I':
|
case 'I':
|
||||||
{
|
{
|
||||||
std::int16_t len{};
|
std::int16_t len{};
|
||||||
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
return get_number(input_format, len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
{
|
{
|
||||||
std::int32_t len{};
|
std::int32_t len{};
|
||||||
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
return get_number(input_format, len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'L':
|
case 'L':
|
||||||
{
|
{
|
||||||
std::int64_t len{};
|
std::int64_t len{};
|
||||||
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
return get_number(input_format, len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'u':
|
case 'u':
|
||||||
|
|||||||
@@ -395,30 +395,17 @@ struct wide_string_input_helper<BaseInputAdapter, 2>
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// A supplementary code point is a high surrogate (0xD800..0xDBFF)
|
if (JSON_HEDLEY_UNLIKELY(!input.empty()))
|
||||||
// followed by a low surrogate (0xDC00..0xDFFF). A lone low
|
|
||||||
// surrogate, a high surrogate at the end of the input, or a high
|
|
||||||
// surrogate followed by any other unit is malformed UTF-16. In
|
|
||||||
// that case the offending unit is passed through unchanged so the
|
|
||||||
// UTF-8 decoder rejects it, matching how \uXXXX surrogate escapes
|
|
||||||
// are handled in the lexer.
|
|
||||||
bool valid_pair = false;
|
|
||||||
if (wc <= 0xDBFF && JSON_HEDLEY_UNLIKELY(!input.empty()))
|
|
||||||
{
|
{
|
||||||
const auto wc2 = static_cast<unsigned int>(input.get_character());
|
const auto wc2 = static_cast<unsigned int>(input.get_character());
|
||||||
if (0xDC00 <= wc2 && wc2 <= 0xDFFF)
|
const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
|
||||||
{
|
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
|
||||||
const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
|
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
|
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
|
||||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
|
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
|
||||||
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
|
utf8_bytes_filled = 4;
|
||||||
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
|
|
||||||
utf8_bytes_filled = 4;
|
|
||||||
valid_pair = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (!valid_pair)
|
|
||||||
{
|
{
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
|
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
|
||||||
utf8_bytes_filled = 1;
|
utf8_bytes_filled = 1;
|
||||||
|
|||||||
@@ -9,15 +9,15 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array> // array
|
#include <array> // array
|
||||||
#include <clocale> // localeconv
|
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstdio> // snprintf
|
#include <cstdio> // snprintf
|
||||||
#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull
|
|
||||||
#include <initializer_list> // initializer_list
|
#include <initializer_list> // initializer_list
|
||||||
#include <string> // char_traits, string
|
#include <string> // char_traits, string
|
||||||
|
#include <system_error> // errc
|
||||||
#include <utility> // move
|
#include <utility> // move
|
||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
|
|
||||||
|
#include <nlohmann/detail/conversions/from_chars.hpp>
|
||||||
#include <nlohmann/detail/input/input_adapters.hpp>
|
#include <nlohmann/detail/input/input_adapters.hpp>
|
||||||
#include <nlohmann/detail/input/position_t.hpp>
|
#include <nlohmann/detail/input/position_t.hpp>
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
@@ -152,7 +152,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept
|
explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept
|
||||||
: ia(std::move(adapter))
|
: ia(std::move(adapter))
|
||||||
, ignore_comments(ignore_comments_)
|
, ignore_comments(ignore_comments_)
|
||||||
, decimal_point_char(static_cast<char_int_type>(get_decimal_point()))
|
, decimal_point_char(static_cast<char_int_type>(from_chars_traits<number_float_t>::get_decimal_point()))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
// deleted because of pointer members
|
// deleted because of pointer members
|
||||||
@@ -163,19 +163,6 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
~lexer() = default;
|
~lexer() = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/////////////////////
|
|
||||||
// locales
|
|
||||||
/////////////////////
|
|
||||||
|
|
||||||
/// return the locale-dependent decimal point
|
|
||||||
JSON_HEDLEY_PURE
|
|
||||||
static char get_decimal_point() noexcept
|
|
||||||
{
|
|
||||||
const auto* loc = localeconv();
|
|
||||||
JSON_ASSERT(loc != nullptr);
|
|
||||||
return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////
|
/////////////////////
|
||||||
// scan functions
|
// scan functions
|
||||||
/////////////////////
|
/////////////////////
|
||||||
@@ -941,24 +928,6 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
|
||||||
static void strtof(float& f, const char* str, char** endptr) noexcept
|
|
||||||
{
|
|
||||||
f = std::strtof(str, endptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
|
||||||
static void strtof(double& f, const char* str, char** endptr) noexcept
|
|
||||||
{
|
|
||||||
f = std::strtod(str, endptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
|
||||||
static void strtof(long double& f, const char* str, char** endptr) noexcept
|
|
||||||
{
|
|
||||||
f = std::strtold(str, endptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief scan a number literal
|
@brief scan a number literal
|
||||||
|
|
||||||
@@ -1279,19 +1248,18 @@ scan_number_done:
|
|||||||
// we are done scanning a number)
|
// we are done scanning a number)
|
||||||
unget();
|
unget();
|
||||||
|
|
||||||
char* endptr = nullptr; // NOLINT(misc-const-correctness,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
|
||||||
errno = 0;
|
|
||||||
|
|
||||||
// try to parse integers first and fall back to floats
|
// try to parse integers first and fall back to floats
|
||||||
if (number_type == token_type::value_unsigned)
|
if (number_type == token_type::value_unsigned)
|
||||||
{
|
{
|
||||||
const auto x = std::strtoull(token_buffer.data(), &endptr, 10);
|
unsigned long long x{}; // NOLINT(runtime/int)
|
||||||
|
const auto res = ::nlohmann::detail::from_chars(token_buffer.data(), token_buffer.data() + token_buffer.size(), x);
|
||||||
|
|
||||||
// we checked the number format before
|
// we checked the number format before
|
||||||
JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
|
JSON_ASSERT(res.ptr == token_buffer.data() + token_buffer.size());
|
||||||
|
|
||||||
if (errno != ERANGE)
|
if (res.ec != std::errc::result_out_of_range)
|
||||||
{
|
{
|
||||||
|
JSON_ASSERT(res.ec == std::errc{});
|
||||||
value_unsigned = static_cast<number_unsigned_t>(x);
|
value_unsigned = static_cast<number_unsigned_t>(x);
|
||||||
if (value_unsigned == x)
|
if (value_unsigned == x)
|
||||||
{
|
{
|
||||||
@@ -1301,13 +1269,15 @@ scan_number_done:
|
|||||||
}
|
}
|
||||||
else if (number_type == token_type::value_integer)
|
else if (number_type == token_type::value_integer)
|
||||||
{
|
{
|
||||||
const auto x = std::strtoll(token_buffer.data(), &endptr, 10);
|
long long x{}; // NOLINT(runtime/int)
|
||||||
|
const auto res = ::nlohmann::detail::from_chars(token_buffer.data(), token_buffer.data() + token_buffer.size(), x);
|
||||||
|
|
||||||
// we checked the number format before
|
// we checked the number format before
|
||||||
JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
|
JSON_ASSERT(res.ptr == token_buffer.data() + token_buffer.size());
|
||||||
|
|
||||||
if (errno != ERANGE)
|
if (res.ec != std::errc::result_out_of_range)
|
||||||
{
|
{
|
||||||
|
JSON_ASSERT(res.ec == std::errc{});
|
||||||
value_integer = static_cast<number_integer_t>(x);
|
value_integer = static_cast<number_integer_t>(x);
|
||||||
if (value_integer == x)
|
if (value_integer == x)
|
||||||
{
|
{
|
||||||
@@ -1318,10 +1288,11 @@ scan_number_done:
|
|||||||
|
|
||||||
// this code is reached if we parse a floating-point number or if an
|
// this code is reached if we parse a floating-point number or if an
|
||||||
// integer conversion above failed
|
// integer conversion above failed
|
||||||
strtof(value_float, token_buffer.data(), &endptr);
|
const auto res = ::nlohmann::detail::from_chars(token_buffer.data(), token_buffer.data() + token_buffer.size(), value_float);
|
||||||
|
|
||||||
// we checked the number format before
|
// we checked the number format before
|
||||||
JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
|
JSON_ASSERT(res.ptr == token_buffer.data() + token_buffer.size());
|
||||||
|
JSON_ASSERT(res.ec != std::errc::invalid_argument);
|
||||||
|
|
||||||
return token_type::value_float;
|
return token_type::value_float;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,6 +124,20 @@
|
|||||||
#define JSON_HAS_FILESYSTEM 0
|
#define JSON_HAS_FILESYSTEM 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef JSON_HAS_FROM_CHARS
|
||||||
|
#if defined(JSON_HAS_CPP_17) && defined(__cpp_lib_to_chars)
|
||||||
|
// The std::from_chars<float> implementation in libstdc++ 11 gets some of the corner cases wrong;
|
||||||
|
// starting with libstdc++ 12, these are fixed by switching the implementation to fast_float.
|
||||||
|
#if defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE < 12
|
||||||
|
#define JSON_HAS_FROM_CHARS 0
|
||||||
|
#else
|
||||||
|
#define JSON_HAS_FROM_CHARS 1
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define JSON_HAS_FROM_CHARS 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef JSON_HAS_THREE_WAY_COMPARISON
|
#ifndef JSON_HAS_THREE_WAY_COMPARISON
|
||||||
#if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \
|
#if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \
|
||||||
&& defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L
|
&& defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
#undef JSON_HAS_CPP_26
|
#undef JSON_HAS_CPP_26
|
||||||
#undef JSON_HAS_FILESYSTEM
|
#undef JSON_HAS_FILESYSTEM
|
||||||
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
||||||
|
#undef JSON_HAS_FROM_CHARS
|
||||||
#undef JSON_HAS_THREE_WAY_COMPARISON
|
#undef JSON_HAS_THREE_WAY_COMPARISON
|
||||||
#undef JSON_HAS_RANGES
|
#undef JSON_HAS_RANGES
|
||||||
#undef JSON_HAS_STD_FORMAT
|
#undef JSON_HAS_STD_FORMAT
|
||||||
|
|||||||
@@ -979,28 +979,13 @@ class binary_writer
|
|||||||
return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;
|
return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Checks that @a size fits into the 32-bit length field used by BSON
|
|
||||||
@return The size as a signed 32-bit integer
|
|
||||||
@throw out_of_range.412 if @a size exceeds the range of std::int32_t
|
|
||||||
*/
|
|
||||||
static std::int32_t to_bson_length(const std::size_t size)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::int32_t>(size)))
|
|
||||||
{
|
|
||||||
JSON_THROW(out_of_range::create(412, concat("BSON length ", std::to_string(size), " exceeds maximum of ", std::to_string((std::numeric_limits<std::int32_t>::max)())), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
return static_cast<std::int32_t>(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Writes the given @a element_type and @a name to the output adapter
|
@brief Writes the given @a element_type and @a name to the output adapter
|
||||||
*/
|
*/
|
||||||
void write_bson_entry_header(const string_t& name,
|
void write_bson_entry_header(const string_t& name,
|
||||||
const std::uint8_t element_type)
|
const std::uint8_t element_type)
|
||||||
{
|
{
|
||||||
oa->write_character(to_char_type(element_type));
|
oa->write_character(to_char_type(element_type)); // boolean
|
||||||
oa->write_characters(
|
oa->write_characters(
|
||||||
reinterpret_cast<const CharType*>(name.c_str()),
|
reinterpret_cast<const CharType*>(name.c_str()),
|
||||||
name.size() + 1u);
|
name.size() + 1u);
|
||||||
@@ -1042,7 +1027,7 @@ class binary_writer
|
|||||||
{
|
{
|
||||||
write_bson_entry_header(name, 0x02);
|
write_bson_entry_header(name, 0x02);
|
||||||
|
|
||||||
write_number<std::int32_t>(to_bson_length(value.size() + 1ul), true);
|
write_number<std::int32_t>(static_cast<std::int32_t>(value.size() + 1ul), true);
|
||||||
oa->write_characters(
|
oa->write_characters(
|
||||||
reinterpret_cast<const CharType*>(value.c_str()),
|
reinterpret_cast<const CharType*>(value.c_str()),
|
||||||
value.size() + 1);
|
value.size() + 1);
|
||||||
@@ -1085,7 +1070,7 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@return The size of the BSON-encoded unsigned integer @a value
|
@return The size of the BSON-encoded unsigned integer in @a j
|
||||||
*/
|
*/
|
||||||
static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept
|
static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept
|
||||||
{
|
{
|
||||||
@@ -1098,22 +1083,22 @@ class binary_writer
|
|||||||
@brief Writes a BSON element with key @a name and unsigned @a value
|
@brief Writes a BSON element with key @a name and unsigned @a value
|
||||||
*/
|
*/
|
||||||
void write_bson_unsigned(const string_t& name,
|
void write_bson_unsigned(const string_t& name,
|
||||||
const std::uint64_t value)
|
const BasicJsonType& j)
|
||||||
{
|
{
|
||||||
if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
|
if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
|
||||||
{
|
{
|
||||||
write_bson_entry_header(name, 0x10 /* int32 */);
|
write_bson_entry_header(name, 0x10 /* int32 */);
|
||||||
write_number<std::int32_t>(static_cast<std::int32_t>(value), true);
|
write_number<std::int32_t>(static_cast<std::int32_t>(j.m_data.m_value.number_unsigned), true);
|
||||||
}
|
}
|
||||||
else if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
|
else if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
|
||||||
{
|
{
|
||||||
write_bson_entry_header(name, 0x12 /* int64 */);
|
write_bson_entry_header(name, 0x12 /* int64 */);
|
||||||
write_number<std::int64_t>(static_cast<std::int64_t>(value), true);
|
write_number<std::int64_t>(static_cast<std::int64_t>(j.m_data.m_value.number_unsigned), true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
write_bson_entry_header(name, 0x11 /* uint64 */);
|
write_bson_entry_header(name, 0x11 /* uint64 */);
|
||||||
write_number<std::uint64_t>(value, true);
|
write_number<std::uint64_t>(static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1157,7 +1142,7 @@ class binary_writer
|
|||||||
const typename BasicJsonType::array_t& value)
|
const typename BasicJsonType::array_t& value)
|
||||||
{
|
{
|
||||||
write_bson_entry_header(name, 0x04); // array
|
write_bson_entry_header(name, 0x04); // array
|
||||||
write_number<std::int32_t>(to_bson_length(calc_bson_array_size(value)), true);
|
write_number<std::int32_t>(static_cast<std::int32_t>(calc_bson_array_size(value)), true);
|
||||||
|
|
||||||
std::size_t array_index = 0ul;
|
std::size_t array_index = 0ul;
|
||||||
|
|
||||||
@@ -1177,7 +1162,7 @@ class binary_writer
|
|||||||
{
|
{
|
||||||
write_bson_entry_header(name, 0x05);
|
write_bson_entry_header(name, 0x05);
|
||||||
|
|
||||||
write_number<std::int32_t>(to_bson_length(value.size()), true);
|
write_number<std::int32_t>(static_cast<std::int32_t>(value.size()), true);
|
||||||
write_number(value.has_subtype() ? static_cast<std::uint8_t>(value.subtype()) : static_cast<std::uint8_t>(0x00));
|
write_number(value.has_subtype() ? static_cast<std::uint8_t>(value.subtype()) : static_cast<std::uint8_t>(0x00));
|
||||||
|
|
||||||
oa->write_characters(reinterpret_cast<const CharType*>(value.data()), value.size());
|
oa->write_characters(reinterpret_cast<const CharType*>(value.data()), value.size());
|
||||||
@@ -1259,7 +1244,7 @@ class binary_writer
|
|||||||
return write_bson_integer(name, j.m_data.m_value.number_integer);
|
return write_bson_integer(name, j.m_data.m_value.number_integer);
|
||||||
|
|
||||||
case value_t::number_unsigned:
|
case value_t::number_unsigned:
|
||||||
return write_bson_unsigned(name, j.m_data.m_value.number_unsigned);
|
return write_bson_unsigned(name, j);
|
||||||
|
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
return write_bson_string(name, *j.m_data.m_value.string);
|
return write_bson_string(name, *j.m_data.m_value.string);
|
||||||
@@ -1299,7 +1284,7 @@ class binary_writer
|
|||||||
*/
|
*/
|
||||||
void write_bson_object(const typename BasicJsonType::object_t& value)
|
void write_bson_object(const typename BasicJsonType::object_t& value)
|
||||||
{
|
{
|
||||||
write_number<std::int32_t>(to_bson_length(calc_bson_object_size(value)), true);
|
write_number<std::int32_t>(static_cast<std::int32_t>(calc_bson_object_size(value)), true);
|
||||||
|
|
||||||
for (const auto& el : value)
|
for (const auto& el : value)
|
||||||
{
|
{
|
||||||
|
|||||||
+694
-159
File diff suppressed because it is too large
Load Diff
@@ -121,6 +121,10 @@ json_test_set_test_options(test-disabled_exceptions
|
|||||||
# raise timeout of expensive Unicode test
|
# raise timeout of expensive Unicode test
|
||||||
json_test_set_test_options(test-unicode4 TEST_PROPERTIES TIMEOUT 3000)
|
json_test_set_test_options(test-unicode4 TEST_PROPERTIES TIMEOUT 3000)
|
||||||
|
|
||||||
|
# link pthreads to tests that need it
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
json_test_set_test_options(test-regression2 LINK_LIBRARIES Threads::Threads)
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
# add unit tests
|
# add unit tests
|
||||||
#############################################################################
|
#############################################################################
|
||||||
|
|||||||
@@ -2721,19 +2721,6 @@ TEST_CASE("BJData")
|
|||||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing BJData string: expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x31", json::parse_error&);
|
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing BJData string: expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x31", json::parse_error&);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("negative length")
|
|
||||||
{
|
|
||||||
json _;
|
|
||||||
|
|
||||||
std::vector<uint8_t> const vi = {'S', 'i', 0xFF};
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vi), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing BJData string: string length must not be negative", json::parse_error&);
|
|
||||||
CHECK(json::from_bjdata(vi, true, false).is_discarded());
|
|
||||||
|
|
||||||
std::vector<uint8_t> const vl = {'S', 'l', 0xFF, 0xFF, 0xFF, 0xFF};
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vl), "[json.exception.parse_error.113] parse error at byte 6: syntax error while parsing BJData string: string length must not be negative", json::parse_error&);
|
|
||||||
CHECK(json::from_bjdata(vl, true, false).is_discarded());
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("parse bjdata markers in ubjson")
|
SECTION("parse bjdata markers in ubjson")
|
||||||
{
|
{
|
||||||
// create a single-character string for all number types
|
// create a single-character string for all number types
|
||||||
|
|||||||
@@ -11,35 +11,12 @@
|
|||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <vector>
|
|
||||||
#include "make_test_data_available.hpp"
|
#include "make_test_data_available.hpp"
|
||||||
#include "test_utils.hpp"
|
#include "test_utils.hpp"
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
// a binary container that reports a size beyond INT32_MAX without allocating
|
|
||||||
// that much memory, so the BSON length overflow can be tested cheaply
|
|
||||||
class huge_binary_t : public std::vector<std::uint8_t>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using std::vector<std::uint8_t>::vector;
|
|
||||||
|
|
||||||
size_type size() const noexcept // NOLINT(readability-convert-member-functions-to-static)
|
|
||||||
{
|
|
||||||
// one byte more than the BSON length field can represent
|
|
||||||
return static_cast<size_type>((std::numeric_limits<std::int32_t>::max)()) + 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using huge_binary_json = nlohmann::basic_json <
|
|
||||||
std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t,
|
|
||||||
double, std::allocator, nlohmann::adl_serializer, huge_binary_t, void >;
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_CASE("BSON")
|
TEST_CASE("BSON")
|
||||||
{
|
{
|
||||||
SECTION("individual values not supported")
|
SECTION("individual values not supported")
|
||||||
@@ -103,14 +80,6 @@ TEST_CASE("BSON")
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("lengths exceeding INT32_MAX cannot be serialized to BSON")
|
|
||||||
{
|
|
||||||
huge_binary_json j;
|
|
||||||
j["b"] = huge_binary_json::binary(huge_binary_t{});
|
|
||||||
|
|
||||||
CHECK_THROWS_WITH_AS(huge_binary_json::to_bson(j), "[json.exception.out_of_range.412] BSON length 2147483661 exceeds maximum of 2147483647", huge_binary_json::out_of_range&);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("string length must be at least 1")
|
SECTION("string length must be at least 1")
|
||||||
{
|
{
|
||||||
// from https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=11175
|
// from https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=11175
|
||||||
@@ -885,62 +854,6 @@ TEST_CASE("Unsupported BSON input")
|
|||||||
CHECK(!json::sax_parse(bson, &scp, json::input_format_t::bson));
|
CHECK(!json::sax_parse(bson, &scp, json::input_format_t::bson));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("BSON document size mismatch")
|
|
||||||
{
|
|
||||||
json _;
|
|
||||||
|
|
||||||
SECTION("top-level document declaring more bytes than it contains")
|
|
||||||
{
|
|
||||||
// empty object, but the length prefix claims 6 bytes instead of 5
|
|
||||||
std::vector<std::uint8_t> const input = {0x06, 0x00, 0x00, 0x00, 0x00};
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_bson(input), "[json.exception.parse_error.112] parse error at byte 5: syntax error while parsing BSON document: document size 6 does not match the number of bytes read (5)", json::parse_error&);
|
|
||||||
CHECK(json::from_bson(input, true, false).is_discarded());
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("top-level document with a negative size")
|
|
||||||
{
|
|
||||||
std::vector<std::uint8_t> const input = {0xFF, 0xFF, 0xFF, 0xFF, 0x00};
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_bson(input), "[json.exception.parse_error.112] parse error at byte 5: syntax error while parsing BSON document: document size -1 does not match the number of bytes read (5)", json::parse_error&);
|
|
||||||
CHECK(json::from_bson(input, true, false).is_discarded());
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("embedded document whose size disagrees with its terminator")
|
|
||||||
{
|
|
||||||
// the embedded document "d" declares 0x7FFFFFFF bytes but its 0x00
|
|
||||||
// terminator falls right after {"a":null}; the length prefix would
|
|
||||||
// otherwise let the following "h" element be read as a member of the
|
|
||||||
// enclosing document instead of "d"
|
|
||||||
std::vector<std::uint8_t> const input =
|
|
||||||
{
|
|
||||||
0x00, 0x00, 0x00, 0x00, // outer size
|
|
||||||
0x03, 'd', 0x00, // entry: embedded document "d"
|
|
||||||
0xFF, 0xFF, 0xFF, 0x7F, // embedded size 0x7FFFFFFF
|
|
||||||
0x0A, 'a', 0x00, // entry: null "a"
|
|
||||||
0x00, // embedded end marker
|
|
||||||
0x08, 'h', 0x00, 0x01, // entry: bool "h" = true
|
|
||||||
0x00 // outer end marker
|
|
||||||
};
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_bson(input), "[json.exception.parse_error.112] parse error at byte 15: syntax error while parsing BSON document: document size 2147483647 does not match the number of bytes read (8)", json::parse_error&);
|
|
||||||
CHECK(json::from_bson(input, true, false).is_discarded());
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("embedded array whose size disagrees with its terminator")
|
|
||||||
{
|
|
||||||
// array [42] is 12 bytes, but the length prefix claims 13
|
|
||||||
std::vector<std::uint8_t> const input =
|
|
||||||
{
|
|
||||||
0x00, 0x00, 0x00, 0x00, // outer size
|
|
||||||
0x04, 'a', 0x00, // entry: array "a"
|
|
||||||
0x0D, 0x00, 0x00, 0x00, // array size 13 (real is 12)
|
|
||||||
0x10, '0', 0x00, 0x2A, 0x00, 0x00, 0x00, // entry: int32 "0" = 42
|
|
||||||
0x00, // array end marker
|
|
||||||
0x00 // outer end marker
|
|
||||||
};
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_bson(input), "[json.exception.parse_error.112] parse error at byte 19: syntax error while parsing BSON document: document size 13 does not match the number of bytes read (12)", json::parse_error&);
|
|
||||||
CHECK(json::from_bson(input, true, false).is_discarded());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("BSON numerical data")
|
TEST_CASE("BSON numerical data")
|
||||||
{
|
{
|
||||||
SECTION("number")
|
SECTION("number")
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ TEST_CASE("lexer class")
|
|||||||
|
|
||||||
SECTION("numbers")
|
SECTION("numbers")
|
||||||
{
|
{
|
||||||
|
// Number parsing implementation uses std::from_chars where available,
|
||||||
|
// so run this test suite with JSON_HAS_CPP_17 as well.
|
||||||
CHECK((scan_string("0") == json::lexer::token_type::value_unsigned));
|
CHECK((scan_string("0") == json::lexer::token_type::value_unsigned));
|
||||||
CHECK((scan_string("1") == json::lexer::token_type::value_unsigned));
|
CHECK((scan_string("1") == json::lexer::token_type::value_unsigned));
|
||||||
CHECK((scan_string("2") == json::lexer::token_type::value_unsigned));
|
CHECK((scan_string("2") == json::lexer::token_type::value_unsigned));
|
||||||
@@ -65,13 +67,20 @@ TEST_CASE("lexer class")
|
|||||||
CHECK((scan_string("7") == json::lexer::token_type::value_unsigned));
|
CHECK((scan_string("7") == json::lexer::token_type::value_unsigned));
|
||||||
CHECK((scan_string("8") == json::lexer::token_type::value_unsigned));
|
CHECK((scan_string("8") == json::lexer::token_type::value_unsigned));
|
||||||
CHECK((scan_string("9") == json::lexer::token_type::value_unsigned));
|
CHECK((scan_string("9") == json::lexer::token_type::value_unsigned));
|
||||||
|
CHECK((scan_string("18446744073709551615") == json::lexer::token_type::value_unsigned));
|
||||||
|
|
||||||
CHECK((scan_string("-0") == json::lexer::token_type::value_integer));
|
CHECK((scan_string("-0") == json::lexer::token_type::value_integer));
|
||||||
CHECK((scan_string("-1") == json::lexer::token_type::value_integer));
|
CHECK((scan_string("-1") == json::lexer::token_type::value_integer));
|
||||||
|
CHECK((scan_string("-9223372036854775808") == json::lexer::token_type::value_integer));
|
||||||
|
|
||||||
CHECK((scan_string("1.1") == json::lexer::token_type::value_float));
|
CHECK((scan_string("1.1") == json::lexer::token_type::value_float));
|
||||||
CHECK((scan_string("-1.1") == json::lexer::token_type::value_float));
|
CHECK((scan_string("-1.1") == json::lexer::token_type::value_float));
|
||||||
CHECK((scan_string("1E10") == json::lexer::token_type::value_float));
|
CHECK((scan_string("1E10") == json::lexer::token_type::value_float));
|
||||||
|
|
||||||
|
// out-of-range integers/floats are treated as value_float tokens
|
||||||
|
CHECK((scan_string("18446744073709551616") == json::lexer::token_type::value_float));
|
||||||
|
CHECK((scan_string("-9223372036854775809") == json::lexer::token_type::value_float));
|
||||||
|
CHECK((scan_string("1E400") == json::lexer::token_type::value_float));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("whitespace")
|
SECTION("whitespace")
|
||||||
|
|||||||
@@ -0,0 +1,278 @@
|
|||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | 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"
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <limits>
|
||||||
|
#include <string>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
// nlohmann::details::from_chars is a wrapper around std::from_chars when it is
|
||||||
|
// available (__cpp_lib_to_chars is defined), otherwise our fallback
|
||||||
|
// implementation kicks in.
|
||||||
|
//
|
||||||
|
// Due to the incomplete implementation of this C++17 standard library feature
|
||||||
|
// (for example, lack of support for long double even in current libc++),
|
||||||
|
// JSON_HAS_CPP_17 does not guarantee that std::from_chars is available.
|
||||||
|
// However, the reverse holds: If the test is compiled in C++11 mode, the
|
||||||
|
// fallback implementation will be used for sure.
|
||||||
|
// By mentioning the JSON_HAS_CPP_17 macro in this here comment, the test will
|
||||||
|
// be compiled both using our fallback and, at least on some platforms,
|
||||||
|
// the C++17 standard library version, as a way of ensuring that the
|
||||||
|
// expectations formulated in this test align with `std::from_chars`.
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct init_val_t {};
|
||||||
|
constexpr init_val_t init_val{};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void check_result(std::string& str, T& value, std::ptrdiff_t expected_ptr_offset, std::errc expected_ec)
|
||||||
|
{
|
||||||
|
// On platforms where neither `std::from_chars`, nor extended locale support (`strtof_l`) are
|
||||||
|
// available, the `strtof`-based implementation is in fact locale-dependent and might use a
|
||||||
|
// different decimal separator, so we need to adapt the test expectations accordingly.
|
||||||
|
std::replace(str.begin(), str.end(), '.', nlohmann::detail::from_chars_traits<T>::get_decimal_point());
|
||||||
|
|
||||||
|
auto res = nlohmann::detail::from_chars(str.data(), str.data() + str.size(), value);
|
||||||
|
CHECK_MESSAGE(res.ec == expected_ec, "Error code mismatch while parsing: \"", str,
|
||||||
|
"\": Actual: ", make_error_code(res.ec).message(),
|
||||||
|
"; expected: ", make_error_code(expected_ec).message());
|
||||||
|
CHECK_MESSAGE(res.ptr - str.data() == expected_ptr_offset, "Ptr offset mismatch while parsing: \"", str, "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_integral<T>::value, void>::type
|
||||||
|
check(std::string str, T expected_value, std::ptrdiff_t expected_ptr_offset, std::errc expected_ec)
|
||||||
|
{
|
||||||
|
T value = 42;
|
||||||
|
check_result(str, value, expected_ptr_offset, expected_ec);
|
||||||
|
CHECK_MESSAGE(value == expected_value, "while parsing: \"", str, "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_floating_point<T>::value, void>::type
|
||||||
|
check(std::string str, T expected_value, std::ptrdiff_t expected_ptr_offset, std::errc expected_ec)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
T value = 42;
|
||||||
|
check_result(str, value, expected_ptr_offset, expected_ec);
|
||||||
|
CHECK_MESSAGE(value == expected_value, "while parsing: \"", str, "\"");
|
||||||
|
CHECK_MESSAGE(std::signbit(value) == std::signbit(expected_value), "while parsing: \"", str, "\"");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
T value = std::numeric_limits<T>::quiet_NaN();
|
||||||
|
check_result(str, value, expected_ptr_offset, expected_ec);
|
||||||
|
CHECK_MESSAGE(value == expected_value, "while parsing: \"", str, "\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_integral<T>::value, void>::type
|
||||||
|
check(std::string str, init_val_t /*unused*/, std::ptrdiff_t expected_ptr_offset, std::errc expected_ec)
|
||||||
|
{
|
||||||
|
T const init_value = 42;
|
||||||
|
T value = init_value;
|
||||||
|
check_result(str, value, expected_ptr_offset, expected_ec);
|
||||||
|
CHECK_MESSAGE(value == init_value, "while parsing: \"", str, "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_floating_point<T>::value, void>::type
|
||||||
|
check(std::string str, init_val_t /*unused*/, std::ptrdiff_t expected_ptr_offset, std::errc expected_ec)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
T const init_value = 42;
|
||||||
|
T value = init_value;
|
||||||
|
check_result(str, value, expected_ptr_offset, expected_ec);
|
||||||
|
CHECK_MESSAGE(value == init_value, "while parsing: \"", str, "\"");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
T value = std::numeric_limits<T>::quiet_NaN();
|
||||||
|
check_result(str, value, expected_ptr_offset, expected_ec);
|
||||||
|
CHECK_MESSAGE(doctest::IsNaN<T>(value), "while parsing: \"", str, "\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("integral results consistent with std::from_chars")
|
||||||
|
{
|
||||||
|
SECTION("unsigned long long")
|
||||||
|
{
|
||||||
|
check<unsigned long long>("", init_val, 0, std::errc::invalid_argument);
|
||||||
|
check<unsigned long long>("0", 0ULL, 1, std::errc{});
|
||||||
|
check<unsigned long long>(" 123", init_val, 0, std::errc::invalid_argument);
|
||||||
|
check<unsigned long long>("123 ", 123ULL, 3, std::errc{});
|
||||||
|
check<unsigned long long>("+123", init_val, 0, std::errc::invalid_argument);
|
||||||
|
check<unsigned long long>("-123", init_val, 0, std::errc::invalid_argument);
|
||||||
|
check<unsigned long long>("123", 123ULL, 3, std::errc{});
|
||||||
|
check<unsigned long long>("123e10", 123ULL, 3, std::errc{});
|
||||||
|
check<unsigned long long>("18446744073709551615", 18446744073709551615ULL, 20, std::errc{});
|
||||||
|
check<unsigned long long>("18446744073709551616", init_val, 20, std::errc::result_out_of_range);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("long long")
|
||||||
|
{
|
||||||
|
check<long long>("", init_val, 0, std::errc::invalid_argument);
|
||||||
|
check<long long>("0", 0LL, 1, std::errc{});
|
||||||
|
check<long long>(" 123", init_val, 0, std::errc::invalid_argument);
|
||||||
|
check<long long>("123 ", 123LL, 3, std::errc{});
|
||||||
|
check<long long>("+123", init_val, 0, std::errc::invalid_argument);
|
||||||
|
check<long long>("-123", -123LL, 4, std::errc{});
|
||||||
|
check<long long>("123", 123LL, 3, std::errc{});
|
||||||
|
check<long long>("123e10", 123LL, 3, std::errc{});
|
||||||
|
check<long long>("9223372036854775807", 9223372036854775807LL, 19, std::errc{});
|
||||||
|
check<long long>("9223372036854775808", init_val, 19, std::errc::result_out_of_range);
|
||||||
|
check<long long>("-9223372036854775808", -9223372036854775807LL - 1, 20, std::errc{});
|
||||||
|
check<long long>("-9223372036854775809", init_val, 20, std::errc::result_out_of_range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("floating point results consistent with std::from_chars")
|
||||||
|
{
|
||||||
|
INFO("Using decimal point '",
|
||||||
|
std::string{1, nlohmann::detail::from_chars_traits<float>::get_decimal_point()},
|
||||||
|
"' to match our possibly-locale-dependent from_chars fallback implementation");
|
||||||
|
|
||||||
|
SECTION("single precision")
|
||||||
|
{
|
||||||
|
check<float>("", init_val, 0, std::errc::invalid_argument);
|
||||||
|
check<float>(" 123", init_val, 0, std::errc::invalid_argument);
|
||||||
|
check<float>("123 ", 123.0f, 3, std::errc{});
|
||||||
|
check<float>("+123", init_val, 0, std::errc::invalid_argument);
|
||||||
|
check<float>("-123", -123.0f, 4, std::errc{});
|
||||||
|
check<float>("123", 123.0f, 3, std::errc{});
|
||||||
|
check<float>("123e10", 123e10f, 6, std::errc{});
|
||||||
|
check<float>("123e+10", 123e10f, 7, std::errc{});
|
||||||
|
check<float>("123e-10", 123e-10f, 7, std::errc{});
|
||||||
|
check<float>("123.456", 123.456f, 7, std::errc{});
|
||||||
|
check<float>("123;456", 123.0f, 3, std::errc{});
|
||||||
|
check<float>("123.456 ", 123.456f, 7, std::errc{});
|
||||||
|
check<float>("123;456 ", 123.0f, 3, std::errc{});
|
||||||
|
check<float>("123456789.123456789", 123456789.123456789f, 19, std::errc{});
|
||||||
|
check<float>("1e40", std::numeric_limits<float>::infinity(), 4, std::errc::result_out_of_range);
|
||||||
|
check<float>("-1e40", -std::numeric_limits<float>::infinity(), 5, std::errc::result_out_of_range);
|
||||||
|
check<float>("2e308", std::numeric_limits<float>::infinity(), 5, std::errc::result_out_of_range);
|
||||||
|
check<float>("-2e308", -std::numeric_limits<float>::infinity(), 6, std::errc::result_out_of_range);
|
||||||
|
check<float>("123.456e-789", 0.0f, 12, std::errc::result_out_of_range);
|
||||||
|
check<float>("-123.456e-789", -0.0f, 13, std::errc::result_out_of_range);
|
||||||
|
check<float>("1e-45", 1e-45f, 5, std::errc{});
|
||||||
|
check<float>("1e-46", 0.0f, 5, std::errc::result_out_of_range);
|
||||||
|
check<float>("1E-45", 1e-45f, 5, std::errc{});
|
||||||
|
check<float>("1E-46", 0.0f, 5, std::errc::result_out_of_range);
|
||||||
|
check<float>("10e-46", 1e-45f, 6, std::errc{});
|
||||||
|
check<float>("0.1e-45", 0.0f, 7, std::errc::result_out_of_range);
|
||||||
|
check<float>("100000000000000000000000000000000000000", 1e38f, 39, std::errc{});
|
||||||
|
check<float>("100000000000000000000000000000000000000.0", 1e38f, 41, std::errc{});
|
||||||
|
check<float>("1000000000000000000000000000000000000000e-1", 1e38f, 43, std::errc{});
|
||||||
|
check<float>("0.0000000000000000000000000000000000000000000001e84", 1e38f, 51, std::errc{});
|
||||||
|
check<float>("0.000000000000000000000000000000000000000000001", 1e-45f, 47, std::errc{});
|
||||||
|
check<float>("00.000000000000000000000000000000000000000000001", 1e-45f, 48, std::errc{});
|
||||||
|
check<float>("0.0000000000000000000000000000000000000000000001e1", 1e-45f, 50, std::errc{});
|
||||||
|
check<float>("1000000000000000000000000000000000000000e-84", 1e-45f, 44, std::errc{});
|
||||||
|
check<float>("1000000000000000000000000000000000000000", std::numeric_limits<float>::infinity(), 40, std::errc::result_out_of_range);
|
||||||
|
check<float>("1000000000000000000000000000000000000000.0", std::numeric_limits<float>::infinity(), 42, std::errc::result_out_of_range);
|
||||||
|
check<float>("100000000000000000000000000000000000000e1", std::numeric_limits<float>::infinity(), 41, std::errc::result_out_of_range);
|
||||||
|
check<float>("0.0000000000000000000000000000000000000000000001e85", std::numeric_limits<float>::infinity(), 51, std::errc::result_out_of_range);
|
||||||
|
check<float>("1000000000000000000000000000000000000000e-85", 0.0f, 44, std::errc::result_out_of_range);
|
||||||
|
check<float>("0.0000000000000000000000000000000000000000000001", 0.0f, 48, std::errc::result_out_of_range);
|
||||||
|
check<float>("0.000000000000000000000000000000000000000000001e-1", 0.0f, 50, std::errc::result_out_of_range);
|
||||||
|
check<float>("1e-99999999999999999999", 0.0f, 23, std::errc::result_out_of_range);
|
||||||
|
check<float>("1e+99999999999999999999", std::numeric_limits<float>::infinity(), 23, std::errc::result_out_of_range);
|
||||||
|
check<float>("-1e-99999999999999999999", -0.0f, 24, std::errc::result_out_of_range);
|
||||||
|
check<float>("-1e+99999999999999999999", -std::numeric_limits<float>::infinity(), 24, std::errc::result_out_of_range);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("double precision")
|
||||||
|
{
|
||||||
|
check<double>("", init_val, 0, std::errc::invalid_argument);
|
||||||
|
check<double>(" 123", init_val, 0, std::errc::invalid_argument);
|
||||||
|
check<double>("123 ", 123.0, 3, std::errc{});
|
||||||
|
check<double>("+123", init_val, 0, std::errc::invalid_argument);
|
||||||
|
check<double>("-123", -123.0, 4, std::errc{});
|
||||||
|
check<double>("123", 123.0, 3, std::errc{});
|
||||||
|
check<double>("123e10", 123e10, 6, std::errc{});
|
||||||
|
check<double>("123e+10", 123e10, 7, std::errc{});
|
||||||
|
check<double>("123e-10", 123e-10, 7, std::errc{});
|
||||||
|
check<double>("123.456", 123.456, 7, std::errc{});
|
||||||
|
check<double>("123;456", 123.0, 3, std::errc{});
|
||||||
|
check<double>("123.456 ", 123.456, 7, std::errc{});
|
||||||
|
check<double>("123;456 ", 123.0, 3, std::errc{});
|
||||||
|
check<double>("123456789.123456789", 123456789.123456789, 19, std::errc{});
|
||||||
|
check<double>("1e40", 1e40, 4, std::errc{});
|
||||||
|
check<double>("-1e40", -1e40, 5, std::errc{});
|
||||||
|
check<double>("2e308", std::numeric_limits<double>::infinity(), 5, std::errc::result_out_of_range);
|
||||||
|
check<double>("-2e308", -std::numeric_limits<double>::infinity(), 6, std::errc::result_out_of_range);
|
||||||
|
check<double>("2e-324", 0.0, 6, std::errc::result_out_of_range);
|
||||||
|
check<double>("-2e-324", -0.0, 7, std::errc::result_out_of_range);
|
||||||
|
check<double>("123.456e-789", 0.0, 12, std::errc::result_out_of_range);
|
||||||
|
check<double>("-123.456e-789", -0.0, 13, std::errc::result_out_of_range);
|
||||||
|
check<double>("1e-99999999999999999999", 0.0, 23, std::errc::result_out_of_range);
|
||||||
|
check<double>("1e+99999999999999999999", std::numeric_limits<double>::infinity(), 23, std::errc::result_out_of_range);
|
||||||
|
check<double>("-1e-99999999999999999999", -0.0, 24, std::errc::result_out_of_range);
|
||||||
|
check<double>("-1e+99999999999999999999", -std::numeric_limits<double>::infinity(), 24, std::errc::result_out_of_range);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("long double precision")
|
||||||
|
{
|
||||||
|
check<long double>("", init_val, 0, std::errc::invalid_argument);
|
||||||
|
check<long double>(" 123", init_val, 0, std::errc::invalid_argument);
|
||||||
|
check<long double>("123 ", 123.0L, 3, std::errc{});
|
||||||
|
check<long double>("+123", init_val, 0, std::errc::invalid_argument);
|
||||||
|
check<long double>("-123", -123.0L, 4, std::errc{});
|
||||||
|
check<long double>("123", 123.0L, 3, std::errc{});
|
||||||
|
check<long double>("123e10", 123e10L, 6, std::errc{});
|
||||||
|
check<long double>("123e+10", 123e10L, 7, std::errc{});
|
||||||
|
check<long double>("123e-10", 123e-10L, 7, std::errc{});
|
||||||
|
check<long double>("123.456", 123.456L, 7, std::errc{});
|
||||||
|
check<long double>("123;456", 123.0L, 3, std::errc{});
|
||||||
|
check<long double>("123.456 ", 123.456L, 7, std::errc{});
|
||||||
|
check<long double>("123;456 ", 123.0L, 3, std::errc{});
|
||||||
|
check<long double>("123456789.123456789", 123456789.123456789L, 19, std::errc{});
|
||||||
|
check<long double>("1e40", 1e40L, 4, std::errc{});
|
||||||
|
check<long double>("-1e40", -1e40L, 5, std::errc{});
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4127)
|
||||||
|
#endif
|
||||||
|
if (sizeof(long double) > 8)
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
// Right-hand-side is calculated to avoid warning about literal
|
||||||
|
// exceeding range on platforms where this branch is NOT taken.
|
||||||
|
check<long double>("2e308", 1e308L * 2, 5, std::errc{});
|
||||||
|
check<long double>("-2e308", -1e308L * 2, 6, std::errc{});
|
||||||
|
check<long double>("2e-324", 8e-324L / 4, 6, std::errc{});
|
||||||
|
check<long double>("-2e-324", -8e-324L / 4, 7, std::errc{});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
check<long double>("2e308", std::numeric_limits<long double>::infinity(), 5, std::errc::result_out_of_range);
|
||||||
|
check<long double>("-2e308", -std::numeric_limits<long double>::infinity(), 6, std::errc::result_out_of_range);
|
||||||
|
check<long double>("2e-324", 0.0L, 6, std::errc::result_out_of_range);
|
||||||
|
check<long double>("-2e-324", -0.0L, 7, std::errc::result_out_of_range);
|
||||||
|
}
|
||||||
|
check<long double>("1e5000", std::numeric_limits<long double>::infinity(), 6, std::errc::result_out_of_range);
|
||||||
|
check<long double>("-1e5000", -std::numeric_limits<long double>::infinity(), 7, std::errc::result_out_of_range);
|
||||||
|
check<long double>("123.456e-7890", 0.0L, 13, std::errc::result_out_of_range);
|
||||||
|
check<long double>("-123.456e-7890", -0.0L, 14, std::errc::result_out_of_range);
|
||||||
|
check<long double>("1e-99999999999999999999", 0.0L, 23, std::errc::result_out_of_range);
|
||||||
|
check<long double>("1e+99999999999999999999", std::numeric_limits<long double>::infinity(), 23, std::errc::result_out_of_range);
|
||||||
|
check<long double>("-1e-99999999999999999999", -0.0L, 24, std::errc::result_out_of_range);
|
||||||
|
check<long double>("-1e+99999999999999999999", -std::numeric_limits<long double>::infinity(), 24, std::errc::result_out_of_range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -26,8 +26,11 @@ using ordered_json = nlohmann::ordered_json;
|
|||||||
using namespace nlohmann::literals; // NOLINT(google-build-using-namespace)
|
using namespace nlohmann::literals; // NOLINT(google-build-using-namespace)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <clocale>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <thread>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@@ -1239,6 +1242,44 @@ TEST_CASE("regression tests 2")
|
|||||||
CHECK(j == json({2, 4, 6}));
|
CHECK(j == json({2, 4, 6}));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
SECTION("issue #5198 - TOCTOU race between lexer construction and locale changes causes float truncation")
|
||||||
|
{
|
||||||
|
std::atomic<bool> stop_requested{};
|
||||||
|
std::thread switcher([&stop_requested]
|
||||||
|
{
|
||||||
|
bool german = true;
|
||||||
|
const std::string initial_locale = std::setlocale(LC_NUMERIC, nullptr);
|
||||||
|
|
||||||
|
while (!stop_requested)
|
||||||
|
{
|
||||||
|
const bool setlocale_res = std::setlocale(LC_NUMERIC, german ? "de_DE.UTF-8" : "C");
|
||||||
|
WARN_MESSAGE(setlocale_res,
|
||||||
|
"Setting locale failed, probably because de_DE.UTF-8 is not installed. "
|
||||||
|
"This test was skipped as it would be inconclusive.");
|
||||||
|
if (!setlocale_res)
|
||||||
|
{
|
||||||
|
stop_requested = true;
|
||||||
|
}
|
||||||
|
german = !german;
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)std::setlocale(LC_NUMERIC, initial_locale.c_str()); // restore original locale
|
||||||
|
});
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < 10000 && !stop_requested; ++i)
|
||||||
|
{
|
||||||
|
const json j = json::parse("{\"val\": 99.123456789}");
|
||||||
|
const double parsed = j.value("val", 0.0);
|
||||||
|
if (!CHECK(parsed == 99.123456789))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_requested = true;
|
||||||
|
switcher.join();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_TEMPLATE("issue #4798 - nlohmann::json::to_msgpack() encode float NaN as double", T, double, float) // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization)
|
TEST_CASE_TEMPLATE("issue #4798 - nlohmann::json::to_msgpack() encode float NaN as double", T, double, float) // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization)
|
||||||
|
|||||||
@@ -1862,31 +1862,6 @@ TEST_CASE("UBJSON")
|
|||||||
json _;
|
json _;
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON string: expected length type specification (U, i, I, l, L); last byte: 0x31", json::parse_error&);
|
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON string: expected length type specification (U, i, I, l, L); last byte: 0x31", json::parse_error&);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("negative length")
|
|
||||||
{
|
|
||||||
json _;
|
|
||||||
|
|
||||||
std::vector<uint8_t> const vi = {'S', 'i', 0xFF};
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vi), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing UBJSON string: string length must not be negative", json::parse_error&);
|
|
||||||
CHECK(json::from_ubjson(vi, true, false).is_discarded());
|
|
||||||
|
|
||||||
std::vector<uint8_t> const vI = {'S', 'I', 0xFF, 0xFF};
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vI), "[json.exception.parse_error.113] parse error at byte 4: syntax error while parsing UBJSON string: string length must not be negative", json::parse_error&);
|
|
||||||
CHECK(json::from_ubjson(vI, true, false).is_discarded());
|
|
||||||
|
|
||||||
std::vector<uint8_t> const vl = {'S', 'l', 0xFF, 0xFF, 0xFF, 0xFF};
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vl), "[json.exception.parse_error.113] parse error at byte 6: syntax error while parsing UBJSON string: string length must not be negative", json::parse_error&);
|
|
||||||
CHECK(json::from_ubjson(vl, true, false).is_discarded());
|
|
||||||
|
|
||||||
std::vector<uint8_t> const vL = {'S', 'L', 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vL), "[json.exception.parse_error.113] parse error at byte 10: syntax error while parsing UBJSON string: string length must not be negative", json::parse_error&);
|
|
||||||
CHECK(json::from_ubjson(vL, true, false).is_discarded());
|
|
||||||
|
|
||||||
// a length of zero remains valid and yields an empty string
|
|
||||||
std::vector<uint8_t> const v0 = {'S', 'i', 0};
|
|
||||||
CHECK(json::from_ubjson(v0) == json(""));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("array")
|
SECTION("array")
|
||||||
|
|||||||
@@ -53,27 +53,6 @@ TEST_CASE("wide strings")
|
|||||||
std::wstring const w = L"\"\xDBFF";
|
std::wstring const w = L"\"\xDBFF";
|
||||||
json _;
|
json _;
|
||||||
CHECK_THROWS_AS(_ = json::parse(w), json::parse_error&);
|
CHECK_THROWS_AS(_ = json::parse(w), json::parse_error&);
|
||||||
|
|
||||||
// the exact message depends on the width of wchar_t: a 16-bit
|
|
||||||
// wchar_t passes the lone surrogate to the UTF-8 decoder unchanged
|
|
||||||
// (rejected as a single ill-formed byte at column 2), while a
|
|
||||||
// 32-bit wchar_t first encodes it as an ill-formed three-byte
|
|
||||||
// sequence (rejected one byte later, at column 3)
|
|
||||||
const char* const error_low_surrogate = sizeof(wchar_t) == 2
|
|
||||||
? "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"<U+0000>'"
|
|
||||||
: "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"\xED\xB0'";
|
|
||||||
const char* const error_high_surrogate = sizeof(wchar_t) == 2
|
|
||||||
? "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"<U+0000>'"
|
|
||||||
: "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"\xED\xA0'";
|
|
||||||
|
|
||||||
// a lone low surrogate cannot start a pair
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::parse(std::wstring{L'"', static_cast<wchar_t>(0xDC00), L'"'}), error_low_surrogate, json::parse_error&);
|
|
||||||
// a high surrogate followed by a non-low-surrogate unit is invalid
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::parse(std::wstring{L'"', static_cast<wchar_t>(0xD800), L'a', L'"'}), error_high_surrogate, json::parse_error&);
|
|
||||||
// a lone low surrogate must not swallow the following unit: pairing
|
|
||||||
// it with any second unit would produce valid UTF-8, so the error
|
|
||||||
// has to report an ill-formed byte at the surrogate's own position
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::parse(std::wstring{L'"', static_cast<wchar_t>(0xDC00), L'a', L'"'}), error_low_surrogate, json::parse_error&);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,22 +68,11 @@ TEST_CASE("wide strings")
|
|||||||
|
|
||||||
SECTION("invalid std::u16string")
|
SECTION("invalid std::u16string")
|
||||||
{
|
{
|
||||||
if (u16string_is_utf16())
|
if (wstring_is_utf16())
|
||||||
{
|
{
|
||||||
std::u16string const w = u"\"\xDBFF";
|
std::u16string const w = u"\"\xDBFF";
|
||||||
json _;
|
json _;
|
||||||
CHECK_THROWS_AS(_ = json::parse(w), json::parse_error&);
|
CHECK_THROWS_AS(_ = json::parse(w), json::parse_error&);
|
||||||
|
|
||||||
// a lone low surrogate cannot start a pair
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::parse(std::u16string{u'"', 0xDC00, u'"'}), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"<U+0000>'", json::parse_error&);
|
|
||||||
// a high surrogate followed by a non-low-surrogate unit is invalid
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::parse(std::u16string{u'"', 0xD800, u'a', u'"'}), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"<U+0000>'", json::parse_error&);
|
|
||||||
// a lone low surrogate must not swallow the following unit: pairing
|
|
||||||
// it with any second unit would produce valid UTF-8, so the error
|
|
||||||
// has to report an ill-formed byte at the surrogate's own position
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::parse(std::u16string{u'"', 0xDC00, u'a', u'"'}), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"<U+0000>'", json::parse_error&);
|
|
||||||
// a valid surrogate pair is still decoded (U+1F600)
|
|
||||||
CHECK(json::parse(std::u16string{u'"', 0xD83D, 0xDE00, u'"'}).get<std::string>() == "\xF0\x9F\x98\x80");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user