Compare commits

..
Author SHA1 Message Date
Niels Lohmann ffc3691598 Migrate REUSE metadata from deprecated .reuse/dep5 to REUSE.toml
reuse==6.2.0 (pinned in cmake/requirements/requirements-reuse.txt) warns
"'.reuse/dep5' is deprecated. You are recommended to instead use
REUSE.toml. Use `reuse convert-dep5` to convert." on every
ci_reuse_compliance run. A later reuse release that drops DEP5 support
would break that CI job outright.

Generate REUSE.toml from the 9 Files: stanzas with `reuse convert-dep5`
and remove .reuse/dep5. `reuse lint` still reports full compliance with
spec 3.3 (1268/1268 files with copyright and license information,
unchanged), and `reuse spdx` produces the same per-file copyright and
license entries as before the conversion.

Update the three places that referenced the old file: FILES.md now
documents REUSE.toml with a spec 3.3 link, README.md's REUSE section
names REUSE.toml, and the comment in gen_hedley_undef_check.cmake now
refers to REUSE.toml's blanket path = "**" rule.

Overlaps #4663, which adds an entry to .reuse/dep5; that PR will need to
move its entry into REUSE.toml when rebased on this.

#5716 item 4

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-30 18:13:51 +02:00
Niels Lohmann ec38da53eb Document SwiftPM's #include <json.hpp> form and add CI coverage
Package.swift declares the json target with path: "single_include/nlohmann",
so SwiftPM consumers must write #include <json.hpp>; the <nlohmann/json.hpp>
form used throughout the rest of this documentation does not resolve. The
SwiftPM section of package_managers.md only had the summary box and did not
mention this, nor the #4650 linker problem: because the json target ships
only headers, SwiftPM/Xcode look for a json.o that is never built, so a
consumer needs at least one .cpp file of its own.

Add a usage example (docs/mkdocs/docs/integration/swift/) that depends on
the json product, includes <json.hpp>, and works around the linker problem
with its own example.cpp; verified it builds with `swift build` against a
local checkout. Also raise the deployment target from the deprecated
.watchOS(.v4) to .watchOS(.v9): Swift 6.4 warns "'v4' is deprecated: watchOS
9.0 is the oldest supported version" on every `swift package` invocation,
and `swift package dump-package` is now clean. This is the one behavior
change in this commit: it raises the minimum watchOS version the package
declares.

Add a macOS CI job that runs `swift package dump-package` (so a future
deprecation warning fails CI) and builds the new documentation example
against the checked-out repository with `swift build`, so a change to
Package.swift or the example is caught before release. No workflow
previously referenced Swift or Package.swift.

Adding an in-repository placeholder .cpp to the library itself, so the
issue does not occur for consumers at all, was proposed and declined in
#4650; the documented workaround is a consumer-side .cpp file instead.

#5716 item 3

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-30 18:10:49 +02:00
Niels Lohmann 30d5107b8d Add json_fwd.hpp to the Bazel singleheader-json target
The generator for BUILD.bazel only listed single_include/nlohmann/json.hpp
in the singleheader-json cc_library's hdrs. Bazel's sandboxing only exposes
declared headers, so a consumer of :singleheader-json that includes
<nlohmann/json_fwd.hpp> failed with "file not found" (verified with Bazel
9.2.0). Meson and include.zip already ship json_fwd.hpp; the Bazel target
was missing it.

Add single_include/nlohmann/json_fwd.hpp to the hdrs list and regenerate
BUILD.bazel. Verified that `bazel build //:json //:singleheader-json`
succeeds and that a consumer cc_binary depending on :singleheader-json can
now include <nlohmann/json_fwd.hpp>.

Overlaps #5621, which adds json_view.hpp to the same hdrs list in
gen_bazel_build_file.cmake; that PR will need a rebase.

#5716 item 2

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-30 17:59:47 +02:00
Niels Lohmann bdaa8381d7 Update the supported compiler list and the CITATION.cff repository link
The README's compiler list still named GCC 14.2, Clang 21.0, Apple
Clang 16.0, Intel C++ 17.0.2, nvcc 11.0.221, and MSVC up to 2022,
which no longer matches the CI matrix. List what the workflows test
now, with versions taken from the compiler identification in the CI
logs of the latest develop runs: GCC 4.8 - 16.2, Clang 3.4 - 22.1,
Apple Clang 15.0 - 21.0 (Xcode 15.0.1 up to macos-latest), icpc
2021.10, icpx 2025.3, nvcc 11.8 - 12.6, nvc++ 25.5, and MSVC 2015 -
2026. Apple Clang now starts at 15.0 because CI no longer runs older
Xcode versions; icpc 2021.10 is the last release of that compiler.

CITATION.cff pointed repository-code at the user profile instead of
the repository.

Documentation and metadata only; the library is unchanged.

Part of #5716

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-30 09:44:53 +02:00
Niels Lohmann 85fec73da7 Make the json.tar.xz release archive configure standalone
The json.tar.xz rule packed neither src/modules nor tests, but the
top-level CMakeLists.txt needs src/modules for
NLOHMANN_JSON_BUILD_MODULES=ON and defaulted JSON_BuildTests to ON for
a main project. A standalone configure of the extracted archive failed
at add_subdirectory(tests), and enabling the module failed at
add_subdirectory(src/modules).

Add src/modules to the archive, and default JSON_BuildTests to ON only
if tests/CMakeLists.txt exists, so a repository checkout still builds
the tests by default while the archive configures out of the box. Use
GNU tar via a TAR variable (gtar if present, else tar), the same way
SED is found, instead of hard-coding gtar. The library, its public API,
and its ABI are unchanged.

Part of #5716

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-30 09:40:26 +02:00
25 changed files with 292 additions and 503 deletions
+35
View File
@@ -71,3 +71,38 @@ jobs:
run: cmake --build build --parallel 10
- name: Test
run: cd build ; ctest -j 10 --output-on-failure
swiftpm:
runs-on: macos-15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Check that Package.swift resolves without a deprecation warning
run: swift package dump-package
- name: Build the SwiftPM documentation example against this checkout
run: |
mkdir -p /tmp/json-swiftpm-consumer/Sources/MyLibrary
cp docs/mkdocs/docs/integration/swift/example.cpp /tmp/json-swiftpm-consumer/Sources/MyLibrary/example.cpp
cat > /tmp/json-swiftpm-consumer/Package.swift << EOF
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "MyPackage",
dependencies: [
.package(path: "${{ github.workspace }}")
],
targets: [
.target(
name: "MyLibrary",
dependencies: [
.product(name: "json", package: "json")
],
publicHeadersPath: "."
)
]
)
EOF
cd /tmp/json-swiftpm-consumer
swift build
-40
View File
@@ -1,40 +0,0 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: json
Upstream-Contact: Niels Lohmann <mail@nlohmann.me>
Source: https://github.com/nlohmann/json
Files: *
Copyright: 2013-2026 Niels Lohmann <https://nlohmann.me>
License: MIT
Files: include/nlohmann/thirdparty/hedley.hpp
Copyright: 2016-2021 Evan Nemerson <evan@nemerson.com>
License: CC0
Files: include/nlohmann/detail/meta/cpp_future.hpp
Copyright: 2013-2026 Niels Lohmann <https://nlohmann.me> and 2018 The Abseil Authors
License: MIT AND Apache-2.0
Files: tests/thirdparty/doctest/*
Copyright: 2016-2023 Viktor Kirilov
License: MIT
Files: tests/thirdparty/fifo_map/*
Copyright: 2015-2017 Niels Lohmann
License: MIT
Files: tests/thirdparty/Fuzzer/*
Copyright: 2003-2022 LLVM Project.
License: Apache-2.0
Files: tests/thirdparty/imapdl/*
Copyright: 2017 Georg Sauthoff <mail@gms.tf>
License: GPL-3.0-only
Files: tools/amalgamate/*
Copyright: 2012 Erik Edlund <erik.edlund@32767.se>
License: BSD-3-Clause
Files: tools/gdb_pretty_printer/*
Copyright: 2020 Hannes Domani <https://github.com/ssbssa>
License: MIT
+1
View File
@@ -77,6 +77,7 @@ cc_library(
name = "singleheader-json",
hdrs = [
"single_include/nlohmann/json.hpp",
"single_include/nlohmann/json_fwd.hpp",
],
includes = ["single_include"],
visibility = ["//visibility:public"],
+1 -1
View File
@@ -10,5 +10,5 @@ title: "JSON for Modern C++"
version: 3.12.0
date-released: 2025-04-07
license: MIT
repository-code: "https://github.com/nlohmann"
repository-code: "https://github.com/nlohmann/json"
url: https://json.nlohmann.me
+4 -1
View File
@@ -42,8 +42,11 @@ endif()
## OPTIONS
##
# Build the tests by default only for the main project and only if the tests
# directory exists (the release archive json.tar.xz does not contain it).
# VERSION_GREATER_EQUAL is not available in older CMake (< 3.7)
if(${MAIN_PROJECT} AND (${CMAKE_VERSION} VERSION_EQUAL 3.13 OR ${CMAKE_VERSION} VERSION_GREATER 3.13))
if(${MAIN_PROJECT} AND (${CMAKE_VERSION} VERSION_EQUAL 3.13 OR ${CMAKE_VERSION} VERSION_GREATER 3.13)
AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/CMakeLists.txt")
set(JSON_BuildTests_INIT ON)
else()
set(JSON_BuildTests_INIT OFF)
+3 -3
View File
@@ -207,19 +207,19 @@ Further documentation:
## REUSE
### `.reuse/dep5`
### `REUSE.toml`
The file defines the licenses of certain third-party components in the repository. The root `Makefile` contains a target `reuse` that checks for compliance.
Further documentation:
- [DEP5](https://reuse.software/spec-3.2/#dep5-deprecated)
- [REUSE.toml](https://reuse.software/spec-3.3/#reusetoml)
- [reuse command-line tool](https://pypi.org/project/reuse/)
- [documentation of linting](https://reuse.readthedocs.io/en/stable/man/reuse-lint.html)
- [REUSE](http://reuse.software)
> [!IMPORTANT]
> The filename `.reuse/dep5` is predetermined by REUSE. Alternatively, a `REUSE.toml` file can be used.
> The filename `REUSE.toml` is predetermined by REUSE. Alternatively, a `.reuse/dep5` file (deprecated) can be used.
### `.reuse/templates`
+5 -2
View File
@@ -7,6 +7,9 @@
# find GNU sed to use `-i` parameter
SED:=$(shell command -v gsed || which sed)
# find GNU tar to use `--sort` and `--pax-option` parameters
TAR:=$(shell command -v gtar || which tar)
##########################################################################
# source files
@@ -215,8 +218,8 @@ ChangeLog.md:
# archive is created according to the advices of <https://reproducible-builds.org/docs/archives/>.
json.tar.xz:
mkdir json
rsync -R $(shell find LICENSE.MIT nlohmann_json.natvis CMakeLists.txt cmake/*.in include single_include -type f) json
gtar --sort=name --mtime="@$(shell git log -1 --pretty=%ct)" --owner=0 --group=0 --numeric-owner --pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime --create --file - json | xz --compress -9e --threads=2 - > json.tar.xz
rsync -R $(shell find LICENSE.MIT nlohmann_json.natvis CMakeLists.txt cmake/*.in include single_include src/modules -type f) json
$(TAR) --sort=name --mtime="@$(shell git log -1 --pretty=%ct)" --owner=0 --group=0 --numeric-owner --pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime --create --file - json | xz --compress -9e --threads=2 - > json.tar.xz
rm -fr json
# We use `-X` to make the resulting ZIP file reproducible, see
+1 -1
View File
@@ -6,7 +6,7 @@ import PackageDescription
let package = Package(
name: "nlohmann-json",
platforms: [
.iOS(.v12), .macOS(.v10_13), .tvOS(.v12), .watchOS(.v4), .visionOS(.v1)
.iOS(.v12), .macOS(.v10_13), .tvOS(.v12), .watchOS(.v9), .visionOS(.v1)
],
products: [
.library(name: "json", targets: ["json"])
+13 -10
View File
@@ -1204,15 +1204,18 @@ language bindings, format converters, and the like. See the curated [Ecosystem](
Though it's 2026 already, the support for C++11 is still a bit sparse. Currently, the following compilers are known to work:
- GCC 4.8 - 14.2 (and possibly later)
- Clang 3.4 - 21.0 (and possibly later)
- Apple Clang 9.1 - 16.0 (and possibly later)
- Intel C++ Compiler 17.0.2 (and possibly later)
- Nvidia CUDA Compiler 11.0.221 (and possibly later)
- Microsoft Visual C++ 2015 / Build Tools 14.0.25123.0 (and possibly later)
- Microsoft Visual C++ 2017 / Build Tools 15.5.180.51428 (and possibly later)
- Microsoft Visual C++ 2019 / Build Tools 16.3.1+1def00d3d (and possibly later)
- Microsoft Visual C++ 2022 / Build Tools 19.30.30709.0 (and possibly later)
- GCC 4.8 - 16.2 (and possibly later)
- Clang 3.4 - 22.1 (and possibly later)
- Apple Clang 15.0 - 21.0 (and possibly later)
- Intel C++ Compiler Classic (icpc) 2021.10
- Intel oneAPI DPC++/C++ Compiler (icpx) 2025.3 (and possibly later)
- NVIDIA CUDA Compiler (nvcc) 11.8 - 12.6 (and possibly later)
- NVIDIA HPC SDK C++ Compiler (nvc++) 25.5 (and possibly later)
- Microsoft Visual C++ 2015 / MSVC 19.0 (and possibly later)
- Microsoft Visual C++ 2017 / MSVC 19.16 (and possibly later)
- Microsoft Visual C++ 2019 / MSVC 19.29 (and possibly later)
- Microsoft Visual C++ 2022 / MSVC 19.44 (and possibly later)
- Microsoft Visual C++ 2026 / MSVC 19.51 (and possibly later)
I would be happy to learn about other compilers/versions.
@@ -1402,7 +1405,7 @@ The library is compliant to version 3.3 of the [**REUSE specification**](https:/
- Every source file contains an SPDX copyright header.
- The full text of all licenses used in the repository can be found in the `LICENSES` folder.
- File `.reuse/dep5` contains an overview of all files' copyrights and licenses.
- File `REUSE.toml` contains an overview of all files' copyrights and licenses.
- Run `pipx run reuse lint` to verify the project's REUSE compliance and `pipx run reuse spdx` to generate a SPDX SBOM.
## Contact
+58
View File
@@ -0,0 +1,58 @@
version = 1
SPDX-PackageName = "json"
SPDX-PackageSupplier = "Niels Lohmann <mail@nlohmann.me>"
SPDX-PackageDownloadLocation = "https://github.com/nlohmann/json"
[[annotations]]
path = "**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2013-2026 Niels Lohmann <https://nlohmann.me>"
SPDX-License-Identifier = "MIT"
[[annotations]]
path = "include/nlohmann/thirdparty/hedley.hpp"
precedence = "aggregate"
SPDX-FileCopyrightText = "2016-2021 Evan Nemerson <evan@nemerson.com>"
SPDX-License-Identifier = "CC0"
[[annotations]]
path = "include/nlohmann/detail/meta/cpp_future.hpp"
precedence = "aggregate"
SPDX-FileCopyrightText = "2013-2026 Niels Lohmann <https://nlohmann.me> and 2018 The Abseil Authors"
SPDX-License-Identifier = "MIT AND Apache-2.0"
[[annotations]]
path = "tests/thirdparty/doctest/**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2016-2023 Viktor Kirilov"
SPDX-License-Identifier = "MIT"
[[annotations]]
path = "tests/thirdparty/fifo_map/**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2015-2017 Niels Lohmann"
SPDX-License-Identifier = "MIT"
[[annotations]]
path = "tests/thirdparty/Fuzzer/**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2003-2022 LLVM Project."
SPDX-License-Identifier = "Apache-2.0"
[[annotations]]
path = "tests/thirdparty/imapdl/**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2017 Georg Sauthoff <mail@gms.tf>"
SPDX-License-Identifier = "GPL-3.0-only"
[[annotations]]
path = "tools/amalgamate/**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2012 Erik Edlund <erik.edlund@32767.se>"
SPDX-License-Identifier = "BSD-3-Clause"
[[annotations]]
path = "tools/gdb_pretty_printer/**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2020 Hannes Domani <https://github.com/ssbssa>"
SPDX-License-Identifier = "MIT"
+1
View File
@@ -48,6 +48,7 @@ cc_library(
name = "singleheader-json",
hdrs = [
"single_include/nlohmann/json.hpp",
"single_include/nlohmann/json_fwd.hpp",
],
includes = ["single_include"],
visibility = ["//visibility:public"],
+1 -1
View File
@@ -64,7 +64,7 @@ if(MODE STREQUAL "undef")
# recipe is self-contained and its output is byte-stable across reruns.
# The embedded SPDX tags below are part of the *generated* file's
# content, not a REUSE header for this .cmake script itself (which is
# already covered by the blanket "Files: *" rule in .reuse/dep5) -- keep
# already covered by the blanket path = "**" rule in REUSE.toml) -- keep
# them wrapped in REUSE-IgnoreStart/End so `reuse lint` does not try to
# parse "MIT\n")" as this file's own SPDX-License-Identifier value.
# REUSE-IgnoreStart
@@ -55,10 +55,6 @@ This implementation does exactly follow this approach, as it uses double precisi
smaller than `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` will be stored as NaN internally
and be serialized to `null`.
During deserialization (from JSON text or any of the binary formats), a finite number that does not fit into
`number_float_t` is rejected with [`out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406), for
example a double-precision number in a binary format when `number_float_t` is `#!cpp float`.
#### Storage
Floating-point number values are stored directly inside a `basic_json` type.
@@ -47,9 +47,8 @@ With the default values for `NumberIntegerType` (`std::int64_t`), the default va
When the default type is used, the maximal integer number that can be stored is `9223372036854775807` (INT64_MAX) and
the minimal integer number that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers that are out of
range will yield over/underflow when used in a constructor. During deserialization (from JSON text or any of the binary
formats), too large or small integer numbers will automatically be stored as [`number_unsigned_t`](number_unsigned_t.md)
or [`number_float_t`](number_float_t.md).
range will yield over/underflow when used in a constructor. During deserialization, too large or small integer numbers
will automatically be stored as [`number_unsigned_t`](number_unsigned_t.md) or [`number_float_t`](number_float_t.md).
[RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
> Note that when such software is used, numbers that are integers and are in the range $[-2^{53}+1, 2^{53}-1]$ are
@@ -48,9 +48,8 @@ With the default values for `NumberUnsignedType` (`std::uint64_t`), the default
When the default type is used, the maximal integer number that can be stored is `18446744073709551615` (UINT64_MAX) and
the minimal integer number that can be stored is `0`. Integer numbers that are out of range will yield over/underflow
when used in a constructor. During deserialization (from JSON text or any of the binary formats), too large or small
integer numbers will automatically be stored as [`number_integer_t`](number_integer_t.md) or
[`number_float_t`](number_float_t.md).
when used in a constructor. During deserialization, too large or small integer numbers will automatically be stored
as [`number_integer_t`](number_integer_t.md) or [`number_float_t`](number_float_t.md).
[RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
> Note that when such software is used, numbers that are integers and are in the range $[-2^{53}+1, 2^{53}-1]$ are
@@ -168,9 +168,9 @@ The library maps CBOR types to JSON value types as follows:
!!! warning "Negative integer overflow"
CBOR negative integers (major type 1) are decoded as `-1 - n`. If the encoded magnitude `n` is too large for the
result to fit into `number_integer_t` (`std::int64_t` by default), the result is stored as `number_float_t`, like
a too small integer in JSON text. For example, `-18446744073709551616` (`0x3B` followed by eight `0xFF` bytes) is
stored as `-1.8446744073709552e+19`.
result to fit into `number_integer_t` (`std::int64_t` by default), parsing fails with a
[`parse_error.112`](../../home/exceptions.md#jsonexceptionparse_error112) exception rather than overflowing
silently.
!!! warning "Object keys"
+5 -7
View File
@@ -331,6 +331,9 @@ An unexpected byte was read in a [binary format](../features/binary_formats/inde
[json.exception.parse_error.112] parse error at byte 15: syntax error while parsing BSON binary: byte array length cannot be negative, is -1
```
```
[json.exception.parse_error.112] parse error at byte 9: syntax error while parsing CBOR value: negative integer overflow
```
```
[json.exception.parse_error.112] parse error at byte 5: syntax error while parsing BSON document: document size 6 does not match the number of bytes read (5)
```
@@ -851,18 +854,13 @@ The JSON Patch operations 'remove' and 'add' cannot be applied to the root eleme
### json.exception.out_of_range.406
A parsed number could not be stored without changing it to NaN or INF. For the binary formats, this happens when a
finite floating-point number does not fit into [`number_float_t`](../api/basic_json/number_float_t.md), for example a
double-precision number when `number_float_t` is `#!cpp float`.
A parsed number could not be stored as without changing it to NaN or INF.
!!! failure "Example messages"
!!! failure "Example message"
```
number overflow parsing '10E1000'
```
```
[json.exception.out_of_range.406] syntax error while parsing CBOR value: number overflow
```
### json.exception.out_of_range.407
+1 -1
View File
@@ -125,7 +125,7 @@ automatically download a release as a dependency at configure time.
### `JSON_BuildTests`
Build the unit tests when [`BUILD_TESTING`](https://cmake.org/cmake/help/latest/command/enable_testing.html) is enabled. This option is `ON` by default if the library's CMake project is the top project. That is, when integrating the library as described above, the test suite is not built unless explicitly switched on with this option.
Build the unit tests when [`BUILD_TESTING`](https://cmake.org/cmake/help/latest/command/enable_testing.html) is enabled. This option is `ON` by default if the library's CMake project is the top project and the `tests` directory exists (the release archive `json.tar.xz` does not contain it). That is, when integrating the library as described above, the test suite is not built unless explicitly switched on with this option.
### `JSON_CI`
@@ -443,6 +443,30 @@ installed by adding the `-DJSON_MultipleHeaders=ON` flag (i.e., `cget install nl
- :octicons-file-24: File issues at the [library issue tracker](https://github.com/nlohmann/json/issues)
- :octicons-question-24: [Xcode documentation](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app)
The `json` target's public headers live at `single_include/nlohmann`, so a consumer must write `#include <json.hpp>` rather than the
`#include <nlohmann/json.hpp>` form used elsewhere in this documentation. The `json` target also ships only headers, and SwiftPM/Xcode
expect every library target to produce an object file to link against; without one, linking a consumer fails with a missing `json.o`
([#4650](https://github.com/nlohmann/json/issues/4650)). The workaround is to add at least one `.cpp` file of your own to the target
that depends on `json`.
??? example
1. Create the following files:
```swift title="Package.swift"
--8<-- "integration/swift/Package.swift"
```
```cpp title="Sources/MyLibrary/example.cpp"
--8<-- "integration/swift/example.cpp"
```
2. Build
```shell
swift build
```
## NuGet
!!! abstract "Summary"
@@ -0,0 +1,20 @@
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "MyPackage",
dependencies: [
.package(url: "https://github.com/nlohmann/json.git", from: "3.12.0")
],
targets: [
// the C++ target that uses nlohmann/json
.target(
name: "MyLibrary",
dependencies: [
.product(name: "json", package: "json")
],
// works around missing public headers in MyLibrary; not related to nlohmann/json
publicHeadersPath: "."
)
]
)
@@ -0,0 +1,8 @@
// MyLibrary must contain at least one .cpp file, or SwiftPM/Xcode will
// not build a usable "json" library to link against (see nlohmann/json#4650)
#include <json.hpp>
nlohmann::json example()
{
return nlohmann::json::meta();
}
+45 -133
View File
@@ -559,7 +559,7 @@ class binary_reader
case 0x01: // double
{
double number{};
return get_number<double, true>(input_format_t::bson, number) && emit_float(input_format_t::bson, number);
return get_number<double, true>(input_format_t::bson, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
case 0x02: // string
@@ -600,19 +600,19 @@ class binary_reader
case 0x10: // int32
{
std::int32_t value{};
return get_number<std::int32_t, true>(input_format_t::bson, value) && emit_signed(input_format_t::bson, value);
return get_number<std::int32_t, true>(input_format_t::bson, value) && sax->number_integer(value);
}
case 0x12: // int64
{
std::int64_t value{};
return get_number<std::int64_t, true>(input_format_t::bson, value) && emit_signed(input_format_t::bson, value);
return get_number<std::int64_t, true>(input_format_t::bson, value) && sax->number_integer(value);
}
case 0x11: // uint64
{
std::uint64_t value{};
return get_number<std::uint64_t, true>(input_format_t::bson, value) && emit_unsigned(input_format_t::bson, value);
return get_number<std::uint64_t, true>(input_format_t::bson, value) && sax->number_unsigned(value);
}
default: // anything else is not supported (yet)
@@ -638,19 +638,14 @@ class binary_reader
{
return false;
}
// the value is -1 - number, which fits into number_integer_t
// whenever number does
if (JSON_HEDLEY_LIKELY(value_in_range_of<number_integer_t>(number)))
const auto max_val = static_cast<NumberType>((std::numeric_limits<number_integer_t>::max)());
if (number > max_val)
{
return sax->number_integer(static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(number));
return sax->parse_error(chars_read, get_token_string(),
parse_error::create(112, chars_read,
exception_message(input_format_t::cbor, "negative integer overflow", "value"), nullptr));
}
// like the lexer does for JSON text, store a value too small for
// number_integer_t as number_float_t; compute it as long double so
// that emit_float sees a finite value and can detect an overflow of
// number_float_t
return emit_float(input_format_t::cbor, static_cast<long double>(-1) - static_cast<long double>(number));
return sax->number_integer(static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(number));
}
/*!
@@ -707,25 +702,25 @@ class binary_reader
case 0x18: // Unsigned integer (one-byte uint8_t follows)
{
std::uint8_t number{};
return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
}
case 0x19: // Unsigned integer (two-byte uint16_t follows)
{
std::uint16_t number{};
return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
}
case 0x1A: // Unsigned integer (four-byte uint32_t follows)
{
std::uint32_t number{};
return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
}
case 0x1B: // Unsigned integer (eight-byte uint64_t follows)
{
std::uint64_t number{};
return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
}
// Negative integer -1-0x00..-1-0x17 (-1..-24)
@@ -1170,13 +1165,13 @@ class binary_reader
case 0xFA: // Single-Precision Float (four-byte IEEE 754)
{
float number{};
return get_number(input_format_t::cbor, number) && emit_float(input_format_t::cbor, number);
return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
{
double number{};
return get_number(input_format_t::cbor, number) && emit_float(input_format_t::cbor, number);
return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
default: // anything else (0xFF is handled inside the other types)
@@ -1940,61 +1935,61 @@ class binary_reader
case 0xCA: // float 32
{
float number{};
return get_number(input_format_t::msgpack, number) && emit_float(input_format_t::msgpack, number);
return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
case 0xCB: // float 64
{
double number{};
return get_number(input_format_t::msgpack, number) && emit_float(input_format_t::msgpack, number);
return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
case 0xCC: // uint 8
{
std::uint8_t number{};
return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
}
case 0xCD: // uint 16
{
std::uint16_t number{};
return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
}
case 0xCE: // uint 32
{
std::uint32_t number{};
return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
}
case 0xCF: // uint 64
{
std::uint64_t number{};
return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
}
case 0xD0: // int 8
{
std::int8_t number{};
return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
}
case 0xD1: // int 16
{
std::int16_t number{};
return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
}
case 0xD2: // int 32
{
std::int32_t number{};
return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
}
case 0xD3: // int 64
{
std::int64_t number{};
return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
}
case 0xDC: // array 16
@@ -2927,7 +2922,7 @@ class binary_reader
{
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr));
}
if (JSON_HEDLEY_UNLIKELY(!emit_unsigned(input_format, i)))
if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(static_cast<number_unsigned_t>(i))))
{
return false;
}
@@ -3059,37 +3054,37 @@ class binary_reader
break;
}
std::uint8_t number{};
return get_number(input_format, number) && emit_unsigned(input_format, number);
return get_number(input_format, number) && sax->number_unsigned(number);
}
case 'U':
{
std::uint8_t number{};
return get_number(input_format, number) && emit_unsigned(input_format, number);
return get_number(input_format, number) && sax->number_unsigned(number);
}
case 'i':
{
std::int8_t number{};
return get_number(input_format, number) && emit_signed(input_format, number);
return get_number(input_format, number) && sax->number_integer(number);
}
case 'I':
{
std::int16_t number{};
return get_number(input_format, number) && emit_signed(input_format, number);
return get_number(input_format, number) && sax->number_integer(number);
}
case 'l':
{
std::int32_t number{};
return get_number(input_format, number) && emit_signed(input_format, number);
return get_number(input_format, number) && sax->number_integer(number);
}
case 'L':
{
std::int64_t number{};
return get_number(input_format, number) && emit_signed(input_format, number);
return get_number(input_format, number) && sax->number_integer(number);
}
case 'u':
@@ -3099,7 +3094,7 @@ class binary_reader
break;
}
std::uint16_t number{};
return get_number(input_format, number) && emit_unsigned(input_format, number);
return get_number(input_format, number) && sax->number_unsigned(number);
}
case 'm':
@@ -3109,7 +3104,7 @@ class binary_reader
break;
}
std::uint32_t number{};
return get_number(input_format, number) && emit_unsigned(input_format, number);
return get_number(input_format, number) && sax->number_unsigned(number);
}
case 'M':
@@ -3119,7 +3114,7 @@ class binary_reader
break;
}
std::uint64_t number{};
return get_number(input_format, number) && emit_unsigned(input_format, number);
return get_number(input_format, number) && sax->number_unsigned(number);
}
case 'h':
@@ -3177,13 +3172,13 @@ class binary_reader
case 'd':
{
float number{};
return get_number(input_format, number) && emit_float(input_format, number);
return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
case 'D':
{
double number{};
return get_number(input_format, number) && emit_float(input_format, number);
return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
case 'H':
@@ -3650,13 +3645,13 @@ class binary_reader
case 0x8E: // binary32
{
float number{};
return get_number(input_format_t::bon8, number) && emit_float(input_format_t::bon8, number);
return get_number(input_format_t::bon8, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
case 0x8F: // binary64
{
double number{};
return get_number(input_format_t::bon8, number) && emit_float(input_format_t::bon8, number);
return get_number(input_format_t::bon8, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
case 0xF8:
@@ -3722,9 +3717,7 @@ class binary_reader
@brief pass an integer to the SAX parser
Non-negative integers are passed as unsigned, negative integers as signed
numbers, like the other binary formats do. A value that does not fit the
number type is passed as described for @ref emit_unsigned and
@ref emit_signed.
numbers, like the other binary formats do.
@param[in] number the integer
@return whether the SAX parser accepted the value
@@ -3733,9 +3726,9 @@ class binary_reader
{
if (number >= 0)
{
return emit_unsigned(input_format_t::bon8, static_cast<std::uint64_t>(number));
return sax->number_unsigned(static_cast<number_unsigned_t>(number));
}
return emit_signed(input_format_t::bon8, number);
return sax->number_integer(static_cast<number_integer_t>(number));
}
/*!
@@ -3792,7 +3785,8 @@ class binary_reader
value = (value << 8) | static_cast<std::int64_t>(current);
}
return emit_bon8_integer(negative ? -(value + offset) : value + offset);
return negative ? sax->number_integer(static_cast<number_integer_t>(-(value + offset)))
: sax->number_unsigned(static_cast<number_unsigned_t>(value + offset));
}
/*!
@@ -4089,88 +4083,6 @@ class binary_reader
return true;
}
/*!
@brief pass a signed integer read from the input to the SAX parser
Like the lexer does for JSON text, a value that does not fit into
number_integer_t is passed as number_unsigned_t if it is non-negative and
fits there, and as number_float_t otherwise. With the default number
types, every integer the binary formats can encode fits, so this only
matters for narrower custom number types.
@tparam NumberType a signed integer type
@param[in] format the current format (for diagnostics)
@param[in] number the integer
@return whether the SAX parser accepted the value
@throw out_of_range.406 if @a number overflows number_float_t (see
@ref emit_float)
*/
template<typename NumberType>
bool emit_signed(const input_format_t format, const NumberType number)
{
if (JSON_HEDLEY_LIKELY(value_in_range_of<number_integer_t>(number)))
{
return sax->number_integer(static_cast<number_integer_t>(number));
}
if (value_in_range_of<number_unsigned_t>(number))
{
return sax->number_unsigned(static_cast<number_unsigned_t>(number));
}
return emit_float(format, number);
}
/*!
@brief pass an unsigned integer read from the input to the SAX parser
Like the lexer does for JSON text, a value that does not fit into
number_unsigned_t is passed as number_float_t.
@tparam NumberType an unsigned integer type
@param[in] format the current format (for diagnostics)
@param[in] number the integer
@return whether the SAX parser accepted the value
@throw out_of_range.406 if @a number overflows number_float_t (see
@ref emit_float)
*/
template<typename NumberType>
bool emit_unsigned(const input_format_t format, const NumberType number)
{
if (JSON_HEDLEY_LIKELY(value_in_range_of<number_unsigned_t>(number)))
{
return sax->number_unsigned(static_cast<number_unsigned_t>(number));
}
return emit_float(format, number);
}
/*!
@brief pass a floating-point number read from the input to the SAX parser
Like the lexer does for JSON text, a finite value that overflows
number_float_t is rejected instead of silently becoming infinity. Infinity
and NaN in the input are passed on unchanged. Integers only overflow if
number_float_t cannot represent 2^64, e.g., a half-precision type.
@tparam NumberType a floating-point or integer type
@param[in] format the current format (for diagnostics)
@param[in] number the number
@return whether the SAX parser accepted the value
@throw out_of_range.406 if a finite @a number overflows number_float_t
*/
template<typename NumberType>
bool emit_float(const input_format_t format, const NumberType number)
{
const auto result = static_cast<number_float_t>(number);
if (JSON_HEDLEY_UNLIKELY(std::isfinite(number) && !std::isfinite(result)))
{
return sax->parse_error(chars_read, get_token_string(),
out_of_range::create(406, exception_message(format, "number overflow", "value"), nullptr));
}
return sax->number_float(result, "");
}
/*!
@brief create a string by reading characters from the input
+45 -133
View File
@@ -13326,7 +13326,7 @@ class binary_reader
case 0x01: // double
{
double number{};
return get_number<double, true>(input_format_t::bson, number) && emit_float(input_format_t::bson, number);
return get_number<double, true>(input_format_t::bson, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
case 0x02: // string
@@ -13367,19 +13367,19 @@ class binary_reader
case 0x10: // int32
{
std::int32_t value{};
return get_number<std::int32_t, true>(input_format_t::bson, value) && emit_signed(input_format_t::bson, value);
return get_number<std::int32_t, true>(input_format_t::bson, value) && sax->number_integer(value);
}
case 0x12: // int64
{
std::int64_t value{};
return get_number<std::int64_t, true>(input_format_t::bson, value) && emit_signed(input_format_t::bson, value);
return get_number<std::int64_t, true>(input_format_t::bson, value) && sax->number_integer(value);
}
case 0x11: // uint64
{
std::uint64_t value{};
return get_number<std::uint64_t, true>(input_format_t::bson, value) && emit_unsigned(input_format_t::bson, value);
return get_number<std::uint64_t, true>(input_format_t::bson, value) && sax->number_unsigned(value);
}
default: // anything else is not supported (yet)
@@ -13405,19 +13405,14 @@ class binary_reader
{
return false;
}
// the value is -1 - number, which fits into number_integer_t
// whenever number does
if (JSON_HEDLEY_LIKELY(value_in_range_of<number_integer_t>(number)))
const auto max_val = static_cast<NumberType>((std::numeric_limits<number_integer_t>::max)());
if (number > max_val)
{
return sax->number_integer(static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(number));
return sax->parse_error(chars_read, get_token_string(),
parse_error::create(112, chars_read,
exception_message(input_format_t::cbor, "negative integer overflow", "value"), nullptr));
}
// like the lexer does for JSON text, store a value too small for
// number_integer_t as number_float_t; compute it as long double so
// that emit_float sees a finite value and can detect an overflow of
// number_float_t
return emit_float(input_format_t::cbor, static_cast<long double>(-1) - static_cast<long double>(number));
return sax->number_integer(static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(number));
}
/*!
@@ -13474,25 +13469,25 @@ class binary_reader
case 0x18: // Unsigned integer (one-byte uint8_t follows)
{
std::uint8_t number{};
return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
}
case 0x19: // Unsigned integer (two-byte uint16_t follows)
{
std::uint16_t number{};
return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
}
case 0x1A: // Unsigned integer (four-byte uint32_t follows)
{
std::uint32_t number{};
return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
}
case 0x1B: // Unsigned integer (eight-byte uint64_t follows)
{
std::uint64_t number{};
return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
}
// Negative integer -1-0x00..-1-0x17 (-1..-24)
@@ -13937,13 +13932,13 @@ class binary_reader
case 0xFA: // Single-Precision Float (four-byte IEEE 754)
{
float number{};
return get_number(input_format_t::cbor, number) && emit_float(input_format_t::cbor, number);
return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
{
double number{};
return get_number(input_format_t::cbor, number) && emit_float(input_format_t::cbor, number);
return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
default: // anything else (0xFF is handled inside the other types)
@@ -14707,61 +14702,61 @@ class binary_reader
case 0xCA: // float 32
{
float number{};
return get_number(input_format_t::msgpack, number) && emit_float(input_format_t::msgpack, number);
return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
case 0xCB: // float 64
{
double number{};
return get_number(input_format_t::msgpack, number) && emit_float(input_format_t::msgpack, number);
return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
case 0xCC: // uint 8
{
std::uint8_t number{};
return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
}
case 0xCD: // uint 16
{
std::uint16_t number{};
return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
}
case 0xCE: // uint 32
{
std::uint32_t number{};
return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
}
case 0xCF: // uint 64
{
std::uint64_t number{};
return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
}
case 0xD0: // int 8
{
std::int8_t number{};
return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
}
case 0xD1: // int 16
{
std::int16_t number{};
return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
}
case 0xD2: // int 32
{
std::int32_t number{};
return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
}
case 0xD3: // int 64
{
std::int64_t number{};
return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
}
case 0xDC: // array 16
@@ -15694,7 +15689,7 @@ class binary_reader
{
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr));
}
if (JSON_HEDLEY_UNLIKELY(!emit_unsigned(input_format, i)))
if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(static_cast<number_unsigned_t>(i))))
{
return false;
}
@@ -15826,37 +15821,37 @@ class binary_reader
break;
}
std::uint8_t number{};
return get_number(input_format, number) && emit_unsigned(input_format, number);
return get_number(input_format, number) && sax->number_unsigned(number);
}
case 'U':
{
std::uint8_t number{};
return get_number(input_format, number) && emit_unsigned(input_format, number);
return get_number(input_format, number) && sax->number_unsigned(number);
}
case 'i':
{
std::int8_t number{};
return get_number(input_format, number) && emit_signed(input_format, number);
return get_number(input_format, number) && sax->number_integer(number);
}
case 'I':
{
std::int16_t number{};
return get_number(input_format, number) && emit_signed(input_format, number);
return get_number(input_format, number) && sax->number_integer(number);
}
case 'l':
{
std::int32_t number{};
return get_number(input_format, number) && emit_signed(input_format, number);
return get_number(input_format, number) && sax->number_integer(number);
}
case 'L':
{
std::int64_t number{};
return get_number(input_format, number) && emit_signed(input_format, number);
return get_number(input_format, number) && sax->number_integer(number);
}
case 'u':
@@ -15866,7 +15861,7 @@ class binary_reader
break;
}
std::uint16_t number{};
return get_number(input_format, number) && emit_unsigned(input_format, number);
return get_number(input_format, number) && sax->number_unsigned(number);
}
case 'm':
@@ -15876,7 +15871,7 @@ class binary_reader
break;
}
std::uint32_t number{};
return get_number(input_format, number) && emit_unsigned(input_format, number);
return get_number(input_format, number) && sax->number_unsigned(number);
}
case 'M':
@@ -15886,7 +15881,7 @@ class binary_reader
break;
}
std::uint64_t number{};
return get_number(input_format, number) && emit_unsigned(input_format, number);
return get_number(input_format, number) && sax->number_unsigned(number);
}
case 'h':
@@ -15944,13 +15939,13 @@ class binary_reader
case 'd':
{
float number{};
return get_number(input_format, number) && emit_float(input_format, number);
return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
case 'D':
{
double number{};
return get_number(input_format, number) && emit_float(input_format, number);
return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
case 'H':
@@ -16417,13 +16412,13 @@ class binary_reader
case 0x8E: // binary32
{
float number{};
return get_number(input_format_t::bon8, number) && emit_float(input_format_t::bon8, number);
return get_number(input_format_t::bon8, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
case 0x8F: // binary64
{
double number{};
return get_number(input_format_t::bon8, number) && emit_float(input_format_t::bon8, number);
return get_number(input_format_t::bon8, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
case 0xF8:
@@ -16489,9 +16484,7 @@ class binary_reader
@brief pass an integer to the SAX parser
Non-negative integers are passed as unsigned, negative integers as signed
numbers, like the other binary formats do. A value that does not fit the
number type is passed as described for @ref emit_unsigned and
@ref emit_signed.
numbers, like the other binary formats do.
@param[in] number the integer
@return whether the SAX parser accepted the value
@@ -16500,9 +16493,9 @@ class binary_reader
{
if (number >= 0)
{
return emit_unsigned(input_format_t::bon8, static_cast<std::uint64_t>(number));
return sax->number_unsigned(static_cast<number_unsigned_t>(number));
}
return emit_signed(input_format_t::bon8, number);
return sax->number_integer(static_cast<number_integer_t>(number));
}
/*!
@@ -16559,7 +16552,8 @@ class binary_reader
value = (value << 8) | static_cast<std::int64_t>(current);
}
return emit_bon8_integer(negative ? -(value + offset) : value + offset);
return negative ? sax->number_integer(static_cast<number_integer_t>(-(value + offset)))
: sax->number_unsigned(static_cast<number_unsigned_t>(value + offset));
}
/*!
@@ -16856,88 +16850,6 @@ class binary_reader
return true;
}
/*!
@brief pass a signed integer read from the input to the SAX parser
Like the lexer does for JSON text, a value that does not fit into
number_integer_t is passed as number_unsigned_t if it is non-negative and
fits there, and as number_float_t otherwise. With the default number
types, every integer the binary formats can encode fits, so this only
matters for narrower custom number types.
@tparam NumberType a signed integer type
@param[in] format the current format (for diagnostics)
@param[in] number the integer
@return whether the SAX parser accepted the value
@throw out_of_range.406 if @a number overflows number_float_t (see
@ref emit_float)
*/
template<typename NumberType>
bool emit_signed(const input_format_t format, const NumberType number)
{
if (JSON_HEDLEY_LIKELY(value_in_range_of<number_integer_t>(number)))
{
return sax->number_integer(static_cast<number_integer_t>(number));
}
if (value_in_range_of<number_unsigned_t>(number))
{
return sax->number_unsigned(static_cast<number_unsigned_t>(number));
}
return emit_float(format, number);
}
/*!
@brief pass an unsigned integer read from the input to the SAX parser
Like the lexer does for JSON text, a value that does not fit into
number_unsigned_t is passed as number_float_t.
@tparam NumberType an unsigned integer type
@param[in] format the current format (for diagnostics)
@param[in] number the integer
@return whether the SAX parser accepted the value
@throw out_of_range.406 if @a number overflows number_float_t (see
@ref emit_float)
*/
template<typename NumberType>
bool emit_unsigned(const input_format_t format, const NumberType number)
{
if (JSON_HEDLEY_LIKELY(value_in_range_of<number_unsigned_t>(number)))
{
return sax->number_unsigned(static_cast<number_unsigned_t>(number));
}
return emit_float(format, number);
}
/*!
@brief pass a floating-point number read from the input to the SAX parser
Like the lexer does for JSON text, a finite value that overflows
number_float_t is rejected instead of silently becoming infinity. Infinity
and NaN in the input are passed on unchanged. Integers only overflow if
number_float_t cannot represent 2^64, e.g., a half-precision type.
@tparam NumberType a floating-point or integer type
@param[in] format the current format (for diagnostics)
@param[in] number the number
@return whether the SAX parser accepted the value
@throw out_of_range.406 if a finite @a number overflows number_float_t
*/
template<typename NumberType>
bool emit_float(const input_format_t format, const NumberType number)
{
const auto result = static_cast<number_float_t>(number);
if (JSON_HEDLEY_UNLIKELY(std::isfinite(number) && !std::isfinite(result)))
{
return sax->parse_error(chars_read, get_token_string(),
out_of_range::create(406, exception_message(format, "number overflow", "value"), nullptr));
}
return sax->number_float(result, "");
}
/*!
@brief create a string by reading characters from the input
-141
View File
@@ -11,12 +11,7 @@
#include <nlohmann/json.hpp>
using nlohmann::json;
#include <cmath>
#include <fstream>
#include <limits>
#include <map>
#include <string>
#include <vector>
#include "make_test_data_available.hpp"
TEST_CASE("Binary Formats" * doctest::skip())
@@ -229,139 +224,3 @@ TEST_CASE("Binary Formats" * doctest::skip())
CHECK((100.0 * double(ubjson_3_size) / double(json_size)) == Approx(89.450));
}
}
namespace
{
// the binary formats as function pointers for "Binary formats with narrow number types";
// named functions rather than lambdas, because clang 3.5 cannot convert a lambda
// to a function pointer in the braced initializer of the format table
using narrow_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int32_t, std::uint32_t, float>;
using bytes = std::vector<std::uint8_t>;
bytes encode_cbor(const json& j)
{
return json::to_cbor(j);
}
narrow_json decode_cbor(const bytes& v, bool allow_exceptions)
{
return narrow_json::from_cbor(v, true, allow_exceptions);
}
bytes encode_msgpack(const json& j)
{
return json::to_msgpack(j);
}
narrow_json decode_msgpack(const bytes& v, bool allow_exceptions)
{
return narrow_json::from_msgpack(v, true, allow_exceptions);
}
bytes encode_ubjson(const json& j)
{
return json::to_ubjson(j);
}
narrow_json decode_ubjson(const bytes& v, bool allow_exceptions)
{
return narrow_json::from_ubjson(v, true, allow_exceptions);
}
bytes encode_bjdata(const json& j)
{
return json::to_bjdata(j);
}
narrow_json decode_bjdata(const bytes& v, bool allow_exceptions)
{
return narrow_json::from_bjdata(v, true, allow_exceptions);
}
// BSON can only store numbers as object members
bytes encode_bson(const json& j)
{
return json::to_bson(json{{"a", j}});
}
narrow_json decode_bson(const bytes& v, bool allow_exceptions)
{
const auto result = narrow_json::from_bson(v, true, allow_exceptions);
return result.is_discarded() ? result : result.at("a");
}
bytes encode_bon8(const json& j)
{
return json::to_bon8(j);
}
narrow_json decode_bon8(const bytes& v, bool allow_exceptions)
{
return narrow_json::from_bon8(v, true, allow_exceptions);
}
} // namespace
TEST_CASE("Binary formats with narrow number types")
{
// Numbers that do not fit the number types are handled like the lexer
// handles them in JSON text: an integer that fits neither integer type is
// stored as a floating-point number, and a finite floating-point number
// that overflows number_float_t is rejected with out_of_range.406.
struct binary_format
{
const char* name;
bytes (*encode)(const json&);
narrow_json (*decode)(const bytes&, bool);
};
const std::vector<binary_format> formats =
{
{"CBOR", encode_cbor, decode_cbor},
{"MessagePack", encode_msgpack, decode_msgpack},
{"UBJSON", encode_ubjson, decode_ubjson},
{"BJData", encode_bjdata, decode_bjdata},
{"BSON", encode_bson, decode_bson},
{"BON8", encode_bon8, decode_bon8},
};
for (const auto& format : formats)
{
const std::string name = format.name;
INFO("format := ", name);
const auto roundtrip = [&format](const json & j)
{
return format.decode(format.encode(j), true);
};
// integers that fit keep their type
CHECK(roundtrip(json(-5)).is_number_integer());
CHECK(roundtrip(json(-5)).get<std::int32_t>() == -5);
CHECK(roundtrip(json(3000000000u)).is_number_unsigned());
CHECK(roundtrip(json(3000000000u)).get<std::uint32_t>() == 3000000000u);
// integers that fit neither integer type are stored as float
CHECK(roundtrip(json(5000000000u)).is_number_float());
CHECK(roundtrip(json(5000000000u)).get<float>() == 5000000000.0f);
if (name != "BON8") // BON8 cannot encode integers above INT64_MAX
{
CHECK(roundtrip(json(10000000000000000000u)).is_number_float());
CHECK(roundtrip(json(10000000000000000000u)).get<float>() == 10000000000000000000.0f);
}
CHECK(roundtrip(json(-3000000000LL)).is_number_float());
CHECK(roundtrip(json(-3000000000LL)).get<float>() == -3000000000.0f);
CHECK(roundtrip(json(-5000000000LL)).is_number_float());
CHECK(roundtrip(json(-5000000000LL)).get<float>() == -5000000000.0f);
// floating-point numbers that fit
CHECK(roundtrip(json(1.5)).get<float>() == 1.5f);
const auto just_above_max = std::nextafter(static_cast<double>((std::numeric_limits<float>::max)()),
std::numeric_limits<double>::infinity());
CHECK(roundtrip(json(just_above_max)).get<float>() == (std::numeric_limits<float>::max)());
// infinity and NaN are passed on
CHECK(std::isinf(roundtrip(json(std::numeric_limits<double>::infinity())).get<float>()));
CHECK(std::isnan(roundtrip(json(std::numeric_limits<double>::quiet_NaN())).get<float>()));
// finite floating-point numbers that overflow number_float_t are rejected
const std::string message = "[json.exception.out_of_range.406] syntax error while parsing " + name
+ " value: number overflow";
CHECK_THROWS_WITH_AS(roundtrip(json(1e300)), message.c_str(), narrow_json::out_of_range&);
CHECK_THROWS_WITH_AS(roundtrip(json(-1e300)), message.c_str(), narrow_json::out_of_range&);
CHECK(format.decode(format.encode(json(1e300)), false).is_discarded());
}
}
+14 -16
View File
@@ -3187,8 +3187,7 @@ TEST_CASE("Tagged values")
// CBOR encodes negative integers as: result = -1 - n
// For type 0x3B, n is an 8-byte uint64_t. Valid range for n with
// the default int64_t is [0, INT64_MAX], producing results in [INT64_MIN, -1].
// When n > INT64_MAX, the result exceeds int64_t range and is stored
// as a floating-point number, as the lexer does for JSON text.
// When n > INT64_MAX, the result exceeds int64_t range and is rejected.
SECTION("n = 0 is valid (result = -1)")
{
@@ -3209,34 +3208,33 @@ TEST_CASE("Tagged values")
CHECK(result.get<int64_t>() == (std::numeric_limits<int64_t>::min)());
}
SECTION("n = INT64_MAX + 1 is stored as float")
SECTION("n = INT64_MAX + 1 is rejected (overflow)")
{
// n = INT64_MAX + 1 (0x8000000000000000)
// result = -1 - n = -9223372036854775809, which exceeds int64_t range;
// the nearest double is -9223372036854775808.0
// result = -1 - n = -9223372036854775809, which exceeds int64_t range
const std::vector<uint8_t> input = {0x3B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const auto result = json::from_cbor(input);
CHECK(result.is_number_float());
CHECK(result.get<double>() == -9223372036854775808.0);
CHECK(result == json::parse("-9223372036854775809"));
json _;
CHECK_THROWS_WITH_AS(_ = json::from_cbor(input),
"[json.exception.parse_error.112] parse error at byte 9: syntax error while parsing CBOR value: negative integer overflow",
json::parse_error);
}
SECTION("n = UINT64_MAX is stored as float")
SECTION("n = UINT64_MAX is rejected (overflow)")
{
// n = UINT64_MAX (0xFFFFFFFFFFFFFFFF)
// result = -1 - n = -18446744073709551616, which exceeds int64_t range
const std::vector<uint8_t> input = {0x3B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
const auto result = json::from_cbor(input);
CHECK(result.is_number_float());
CHECK(result.get<double>() == -18446744073709551616.0);
CHECK(result == json::parse("-18446744073709551616"));
json _;
CHECK_THROWS_WITH_AS(_ = json::from_cbor(input),
"[json.exception.parse_error.112] parse error at byte 9: syntax error while parsing CBOR value: negative integer overflow",
json::parse_error);
}
SECTION("overflow with allow_exceptions=false is not an error")
SECTION("overflow with allow_exceptions=false returns discarded")
{
const std::vector<uint8_t> input = {0x3B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const auto result = json::from_cbor(input, true, false);
CHECK(result.is_number_float());
CHECK(result.is_discarded());
}
}