mirror of
https://github.com/nlohmann/json.git
synced 2026-07-09 20:15:12 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 282f84cc8e |
@@ -4,8 +4,6 @@ updates:
|
|||||||
directory: /
|
directory: /
|
||||||
schedule:
|
schedule:
|
||||||
interval: daily
|
interval: daily
|
||||||
cooldown:
|
|
||||||
default-days: 7
|
|
||||||
groups:
|
groups:
|
||||||
codeql-action:
|
codeql-action:
|
||||||
patterns:
|
patterns:
|
||||||
@@ -15,33 +13,23 @@ updates:
|
|||||||
directory: /docs/mkdocs
|
directory: /docs/mkdocs
|
||||||
schedule:
|
schedule:
|
||||||
interval: daily
|
interval: daily
|
||||||
cooldown:
|
|
||||||
default-days: 7
|
|
||||||
|
|
||||||
- package-ecosystem: pip
|
- package-ecosystem: pip
|
||||||
directory: /tools/astyle
|
directory: /tools/astyle
|
||||||
schedule:
|
schedule:
|
||||||
interval: daily
|
interval: daily
|
||||||
cooldown:
|
|
||||||
default-days: 7
|
|
||||||
|
|
||||||
- package-ecosystem: pip
|
- package-ecosystem: pip
|
||||||
directory: /tools/generate_natvis
|
directory: /tools/generate_natvis
|
||||||
schedule:
|
schedule:
|
||||||
interval: daily
|
interval: daily
|
||||||
cooldown:
|
|
||||||
default-days: 7
|
|
||||||
|
|
||||||
- package-ecosystem: pip
|
- package-ecosystem: pip
|
||||||
directory: /tools/serve_header
|
directory: /tools/serve_header
|
||||||
schedule:
|
schedule:
|
||||||
interval: daily
|
interval: daily
|
||||||
cooldown:
|
|
||||||
default-days: 7
|
|
||||||
|
|
||||||
- package-ecosystem: pip
|
- package-ecosystem: pip
|
||||||
directory: /cmake/requirements
|
directory: /cmake/requirements
|
||||||
schedule:
|
schedule:
|
||||||
interval: daily
|
interval: daily
|
||||||
cooldown:
|
|
||||||
default-days: 7
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ jobs:
|
|||||||
# SEMGREP_APP_TOKEN is still passed through so registry auth works if a
|
# SEMGREP_APP_TOKEN is still passed through so registry auth works if a
|
||||||
# token is ever added.
|
# token is ever added.
|
||||||
- name: Install Semgrep
|
- name: Install Semgrep
|
||||||
run: python3 -m pip install --user semgrep==1.168.0
|
run: python3 -m pip install --user semgrep
|
||||||
|
|
||||||
# `semgrep scan --sarif` always exits 0 even with findings; continue-on-error
|
# `semgrep scan --sarif` always exits 0 even with findings; continue-on-error
|
||||||
# is a safety net so the SARIF upload still runs if the scan itself errors.
|
# is a safety net so the SARIF upload still runs if the scan itself errors.
|
||||||
|
|||||||
@@ -90,13 +90,11 @@ jobs:
|
|||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
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
|
|
||||||
# specializes std::tuple (newly diagnosed by the VS2026 v145 toolset)
|
|
||||||
run: |
|
run: |
|
||||||
if [ "${{ matrix.std_version }}" = "latest" ]; then
|
if [ "${{ matrix.std_version }}" = "latest" ]; then
|
||||||
echo "flags=/permissive- /std:c++latest /utf-8 /W4 /WX /wd5285" >> $GITHUB_ENV
|
echo "flags=/permissive- /std:c++latest /utf-8 /W4 /WX" >> $GITHUB_ENV
|
||||||
else
|
else
|
||||||
echo "flags=/W4 /WX /wd5285" >> $GITHUB_ENV
|
echo "flags=/W4 /WX" >> $GITHUB_ENV
|
||||||
fi
|
fi
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: Run CMake (Release)
|
- name: Run CMake (Release)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
# -Wno-extra-semi-stmt The library uses assert which triggers this warning.
|
# -Wno-extra-semi-stmt The library uses assert which triggers this warning.
|
||||||
# -Wno-padded We do not care about padding warnings.
|
# -Wno-padded We do not care about padding warnings.
|
||||||
# -Wno-covered-switch-default All switches list all cases and a default case.
|
# -Wno-covered-switch-default All switches list all cases and a default case.
|
||||||
# -Wno-unsafe-buffer-usage Otherwise Doctest would not compile.
|
# -Wno-unsafe-buffer-usage Otherwise library code (strlen) would not compile.
|
||||||
# -Wno-missing-noreturn We found no way to silence this warning otherwise, see PR #4871
|
# -Wno-missing-noreturn We found no way to silence this warning otherwise, see PR #4871
|
||||||
|
|
||||||
set(CLANG_CXXFLAGS
|
set(CLANG_CXXFLAGS
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
# -Wno-aggregate-return The library uses aggregate returns.
|
# -Wno-aggregate-return The library uses aggregate returns.
|
||||||
# -Wno-long-long The library uses the long long type to interface with system functions.
|
# -Wno-long-long The library uses the long long type to interface with system functions.
|
||||||
# -Wno-namespaces The library uses namespaces.
|
# -Wno-namespaces The library uses namespaces.
|
||||||
# -Wno-nrvo Doctest triggers this warning.
|
|
||||||
# -Wno-padded We do not care about padding warnings.
|
# -Wno-padded We do not care about padding warnings.
|
||||||
# -Wno-system-headers We do not care about warnings in system headers.
|
# -Wno-system-headers We do not care about warnings in system headers.
|
||||||
# -Wno-templates The library uses templates.
|
# -Wno-templates The library uses templates.
|
||||||
@@ -232,7 +231,6 @@ set(GCC_CXXFLAGS
|
|||||||
-Wnonnull
|
-Wnonnull
|
||||||
-Wnonnull-compare
|
-Wnonnull-compare
|
||||||
-Wnormalized=nfkc
|
-Wnormalized=nfkc
|
||||||
-Wno-nrvo
|
|
||||||
-Wnull-dereference
|
-Wnull-dereference
|
||||||
-Wodr
|
-Wodr
|
||||||
-Wold-style-cast
|
-Wold-style-cast
|
||||||
|
|||||||
@@ -82,13 +82,7 @@ basic_json(basic_json&& other) noexcept;
|
|||||||
4. This is a constructor for existing `basic_json` types. It does not hijack copy/move constructors, since the parameter
|
4. This is a constructor for existing `basic_json` types. It does not hijack copy/move constructors, since the parameter
|
||||||
has different template arguments than the current ones.
|
has different template arguments than the current ones.
|
||||||
|
|
||||||
The constructor tries to convert the internal `m_value` of the parameter. Each member value (object, array, string,
|
The constructor tries to convert the internal `m_value` of the parameter.
|
||||||
etc.) is serialized via the corresponding `to_json()` overload. For objects and strings, the conversion requires
|
|
||||||
that the *target* `basic_json` type's `object_t::key_type` (or `string_t`) be directly constructible from the
|
|
||||||
*source* type's corresponding member type via `is_constructible`. If this requirement is not met, the conversion
|
|
||||||
does not fail to compile; instead, it silently falls back to the array-conversion path, which represents objects
|
|
||||||
as arrays of `[key, value]` pairs and strings as arrays of character codes. This is a known limitation tracked in
|
|
||||||
[issue #3425](https://github.com/nlohmann/json/issues/3425).
|
|
||||||
|
|
||||||
5. Creates a JSON value of type array or object from the passed initializer list `init`. In case `type_deduction` is
|
5. Creates a JSON value of type array or object from the passed initializer list `init`. In case `type_deduction` is
|
||||||
`#!cpp true` (default), the type of the JSON value to be created is deducted from the initializer list `init`
|
`#!cpp true` (default), the type of the JSON value to be created is deducted from the initializer list `init`
|
||||||
@@ -152,11 +146,6 @@ basic_json(basic_json&& other) noexcept;
|
|||||||
|
|
||||||
- `BasicJsonType` is a `basic_json` type.
|
- `BasicJsonType` is a `basic_json` type.
|
||||||
- `BasicJsonType` has different template arguments than `basic_json_t`.
|
- `BasicJsonType` has different template arguments than `basic_json_t`.
|
||||||
|
|
||||||
**Note:** For cross-`basic_json` conversions to produce correct results, the target `basic_json`'s
|
|
||||||
`object_t::key_type` and `string_t` must be directly constructible from the source `basic_json`'s
|
|
||||||
corresponding types. See the description of overload (4) above for details on what happens when
|
|
||||||
this requirement is not met.
|
|
||||||
|
|
||||||
`U`:
|
`U`:
|
||||||
: `uncvref_t<CompatibleType>`
|
: `uncvref_t<CompatibleType>`
|
||||||
|
|||||||
@@ -93,15 +93,6 @@ alphabetical order as `std::map` with `std::less` is used by default. Please not
|
|||||||
[RFC 8259](https://tools.ietf.org/html/rfc8259), because any order implements the specified "unordered" nature of JSON
|
[RFC 8259](https://tools.ietf.org/html/rfc8259), because any order implements the specified "unordered" nature of JSON
|
||||||
objects.
|
objects.
|
||||||
|
|
||||||
#### Cross-`basic_json` conversion requirements
|
|
||||||
|
|
||||||
When converting an object from one `basic_json` specialization to another via the
|
|
||||||
[converting constructor](basic_json.md#overload-4), the target `object_t`'s `key_type` must be
|
|
||||||
directly constructible from the source `basic_json`'s `string_t` type (or more generally, from the
|
|
||||||
source object's key type). If this requirement is not met, the conversion does not fail; instead,
|
|
||||||
the object is silently converted as an array of key-value pairs, which is incorrect. See
|
|
||||||
[issue #3425](https://github.com/nlohmann/json/issues/3425) for details and an example.
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
|
|||||||
@@ -6,18 +6,9 @@ namespace std {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Return a hash value for a JSON object. The hash function tries to rely on `std::hash` where possible. To satisfy the
|
Return a hash value for a JSON object. The hash function tries to rely on `std::hash` where possible. Furthermore, the
|
||||||
`std::hash` contract, numeric JSON values that compare equal must hash to the same value. This means:
|
type of the JSON value is taken into account to have different hash values for `#!json null`, `#!cpp 0`, `#!cpp 0U`, and
|
||||||
|
`#!cpp false`, etc.
|
||||||
- `json(42)`, `json(42u)`, and `json(42.0)` all hash to the same value
|
|
||||||
- `json(0)`, `json(0u)`, and `json(0.0)` all hash to the same value
|
|
||||||
|
|
||||||
Different types hash differently for non-numeric types (e.g., `#!json null`, `#!cpp false`, and strings all have distinct hashes).
|
|
||||||
|
|
||||||
**Edge case:** For very large integers outside the exact representable range of the floating-point type (beyond ~2^53 for
|
|
||||||
typical `double`), the hash values for integer and floating-point values may differ, even if the floating-point value
|
|
||||||
was obtained by casting the integer (due to precision loss). This is a documented limitation arising from how the
|
|
||||||
comparison operator normalizes numeric types.
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|||||||
@@ -45,15 +45,6 @@ This implementation is interoperable as it does compare strings code unit by cod
|
|||||||
String values are stored as pointers in a `basic_json` type. That is, for any access to string values, a pointer of type
|
String values are stored as pointers in a `basic_json` type. That is, for any access to string values, a pointer of type
|
||||||
`string_t*` must be dereferenced.
|
`string_t*` must be dereferenced.
|
||||||
|
|
||||||
#### Cross-`basic_json` conversion requirements
|
|
||||||
|
|
||||||
When converting a string value from one `basic_json` specialization to another via the
|
|
||||||
[converting constructor](basic_json.md#overload-4), the target `string_t` must be directly
|
|
||||||
constructible from the source `basic_json`'s `string_t` type. If this requirement is not met, the
|
|
||||||
conversion does not fail; instead, the string is silently converted as an array of character codes,
|
|
||||||
which is incorrect. See [issue #3425](https://github.com/nlohmann/json/issues/3425) for details
|
|
||||||
and an example.
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ int main()
|
|||||||
<< "hash(false) = " << std::hash<json> {}(json(false)) << '\n'
|
<< "hash(false) = " << std::hash<json> {}(json(false)) << '\n'
|
||||||
<< "hash(0) = " << std::hash<json> {}(json(0)) << '\n'
|
<< "hash(0) = " << std::hash<json> {}(json(0)) << '\n'
|
||||||
<< "hash(0U) = " << std::hash<json> {}(json(0U)) << '\n'
|
<< "hash(0U) = " << std::hash<json> {}(json(0U)) << '\n'
|
||||||
<< "hash(0.0) = " << std::hash<json> {}(json(0.0)) << '\n'
|
|
||||||
<< "hash(\"\") = " << std::hash<json> {}(json("")) << '\n'
|
<< "hash(\"\") = " << std::hash<json> {}(json("")) << '\n'
|
||||||
<< "hash({}) = " << std::hash<json> {}(json::object()) << '\n'
|
<< "hash({}) = " << std::hash<json> {}(json::object()) << '\n'
|
||||||
<< "hash([]) = " << std::hash<json> {}(json::array()) << '\n'
|
<< "hash([]) = " << std::hash<json> {}(json::array()) << '\n'
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
hash(null) = 2654435769
|
hash(null) = 2654435769
|
||||||
hash(false) = 2654436030
|
hash(false) = 2654436030
|
||||||
hash(0) = 2654436221
|
hash(0) = 2654436095
|
||||||
hash(0U) = 2654436221
|
hash(0U) = 2654436156
|
||||||
hash(0.0) = 2654436221
|
hash("") = 6142509191626859748
|
||||||
hash("") = 11160318156688833227
|
|
||||||
hash({}) = 2654435832
|
hash({}) = 2654435832
|
||||||
hash([]) = 2654435899
|
hash([]) = 2654435899
|
||||||
hash({"hello": "world"}) = 3701319991624763853
|
hash({"hello": "world"}) = 4469488738203676328
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
User-agent: *
|
|
||||||
Allow: /
|
|
||||||
|
|
||||||
Sitemap: https://json.nlohmann.me/sitemap.xml
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
"""Copy each documentation page's Markdown source into the built site."""
|
|
||||||
|
|
||||||
# Creates a `<path>.md` sibling of each HTML output (for example,
|
|
||||||
# `features/comments/` becomes `features/comments.md`) so agents and tools can
|
|
||||||
# fetch the raw Markdown directly instead of parsing rendered HTML.
|
|
||||||
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
|
|
||||||
_pages = []
|
|
||||||
|
|
||||||
|
|
||||||
def on_files(files, config):
|
|
||||||
global _pages
|
|
||||||
_pages = [f for f in files if f.is_documentation_page()]
|
|
||||||
return files
|
|
||||||
|
|
||||||
|
|
||||||
def on_post_build(config):
|
|
||||||
site_dir = config["site_dir"]
|
|
||||||
for file in _pages:
|
|
||||||
url = file.url.rstrip("/")
|
|
||||||
target = os.path.join(site_dir, (url or "index") + ".md")
|
|
||||||
os.makedirs(os.path.dirname(target), exist_ok=True)
|
|
||||||
shutil.copyfile(file.abs_src_path, target)
|
|
||||||
@@ -367,9 +367,6 @@ markdown_extensions:
|
|||||||
auto_append:
|
auto_append:
|
||||||
- ../includes/glossary.md
|
- ../includes/glossary.md
|
||||||
|
|
||||||
hooks:
|
|
||||||
- hooks/copy_markdown_source.py
|
|
||||||
|
|
||||||
plugins:
|
plugins:
|
||||||
- search:
|
- search:
|
||||||
separator: '[\s\-\.]'
|
separator: '[\s\-\.]'
|
||||||
@@ -392,33 +389,6 @@ plugins:
|
|||||||
- https://nlohmann.github.io/json/*
|
- https://nlohmann.github.io/json/*
|
||||||
- mailto:*
|
- mailto:*
|
||||||
- privacy
|
- privacy
|
||||||
- llmstxt:
|
|
||||||
markdown_description: >
|
|
||||||
JSON for Modern C++ is a C++11 header-only library implementing a JSON
|
|
||||||
value type with an STL-like API, JSON Pointer/Patch, CBOR/MessagePack/
|
|
||||||
BSON/UBJSON/BJData binary format support, and a SAX-style parser interface.
|
|
||||||
sections:
|
|
||||||
Home:
|
|
||||||
- index.md
|
|
||||||
- home/*.md
|
|
||||||
Features:
|
|
||||||
- features/*.md
|
|
||||||
- features/binary_formats/*.md
|
|
||||||
- features/element_access/*.md
|
|
||||||
- features/parsing/*.md
|
|
||||||
- features/types/*.md
|
|
||||||
Integration:
|
|
||||||
- integration/*.md
|
|
||||||
API Documentation:
|
|
||||||
- api/*.md
|
|
||||||
- api/basic_json/*.md
|
|
||||||
- api/adl_serializer/*.md
|
|
||||||
- api/byte_container_with_subtype/*.md
|
|
||||||
- api/json_pointer/*.md
|
|
||||||
- api/json_sax/*.md
|
|
||||||
- api/macros/*.md
|
|
||||||
Community:
|
|
||||||
- community/*.md
|
|
||||||
|
|
||||||
extra_css:
|
extra_css:
|
||||||
- css/custom.css
|
- css/custom.css
|
||||||
|
|||||||
@@ -7,6 +7,5 @@ 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"
|
||||||
mkdocs-htmlproofer-plugin==1.5.0 # plugin "htmlproofer"
|
mkdocs-htmlproofer-plugin==1.5.0 # plugin "htmlproofer"
|
||||||
mkdocs-llmstxt==0.5.0 # plugin "llmstxt"
|
|
||||||
|
|
||||||
PyYAML==6.0.3 # linter
|
PyYAML==6.0.3 # linter
|
||||||
|
|||||||
@@ -11,8 +11,6 @@
|
|||||||
#include <cstdint> // uint8_t
|
#include <cstdint> // uint8_t
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <functional> // hash
|
#include <functional> // hash
|
||||||
#include <limits> // numeric_limits
|
|
||||||
#include <cmath> // isfinite
|
|
||||||
|
|
||||||
#include <nlohmann/detail/abi_macros.hpp>
|
#include <nlohmann/detail/abi_macros.hpp>
|
||||||
#include <nlohmann/detail/value_t.hpp>
|
#include <nlohmann/detail/value_t.hpp>
|
||||||
@@ -28,63 +26,12 @@ inline std::size_t combine(std::size_t seed, std::size_t h) noexcept
|
|||||||
return seed;
|
return seed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a number_integer_t value is exactly representable as number_float_t
|
|
||||||
// Returns true if static_cast<number_integer_t>(static_cast<number_float_t>(val)) == val
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
inline bool is_exactly_representable_as_float(typename BasicJsonType::number_integer_t val) noexcept
|
|
||||||
{
|
|
||||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
|
||||||
using number_float_t = typename BasicJsonType::number_float_t;
|
|
||||||
|
|
||||||
// If the float type's mantissa covers the integer type's entire range, all values round-trip
|
|
||||||
constexpr int float_digits = std::numeric_limits<number_float_t>::digits;
|
|
||||||
constexpr int int_digits = std::numeric_limits<number_integer_t>::digits;
|
|
||||||
|
|
||||||
if (float_digits >= int_digits)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For values outside float's exact range, they don't round-trip
|
|
||||||
// The safe way to check: compute the max magnitude that round-trips
|
|
||||||
// Using unsigned arithmetic to avoid UB with negating INT_MIN
|
|
||||||
|
|
||||||
// Max magnitude representable exactly: 2^(digits-1) - 1 for signed, 2^digits - 1 for unsigned range
|
|
||||||
// But we're checking a signed value, so use 2^digits as the threshold
|
|
||||||
constexpr auto max_exact = static_cast<number_integer_t>(1) << (float_digits - 1);
|
|
||||||
|
|
||||||
// Check absolute value against this threshold
|
|
||||||
if (val >= 0)
|
|
||||||
{
|
|
||||||
if (val >= max_exact) return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// For negative values, check via unsigned wrapping arithmetic
|
|
||||||
// -val in unsigned domain; if it wraps, the value is too negative
|
|
||||||
auto unsigned_abs = static_cast<typename BasicJsonType::number_unsigned_t>(-val);
|
|
||||||
if (unsigned_abs >= static_cast<typename BasicJsonType::number_unsigned_t>(max_exact))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// For values within the exact range, verify the round-trip
|
|
||||||
const auto f = static_cast<number_float_t>(val);
|
|
||||||
return std::isfinite(f) && static_cast<number_integer_t>(f) == val;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief hash a JSON value
|
@brief hash a JSON value
|
||||||
|
|
||||||
The hash function tries to rely on std::hash where possible. Furthermore, the
|
The hash function tries to rely on std::hash where possible. Furthermore, the
|
||||||
type of the JSON value is taken into account to have different hash values for
|
type of the JSON value is taken into account to have different hash values for
|
||||||
most types. However, numeric types (number_integer, number_unsigned, number_float)
|
null, 0, 0U, and false, etc.
|
||||||
are hashed to satisfy the std::hash contract: if two json values compare equal,
|
|
||||||
they must have equal hash values. This means json(42), json(42u), and json(42.0)
|
|
||||||
all hash to the same value (since they compare equal). For large integer values
|
|
||||||
outside the exact representable range of the float type, integer values are hashed
|
|
||||||
in their own domain to avoid precision loss.
|
|
||||||
|
|
||||||
@tparam BasicJsonType basic_json specialization
|
@tparam BasicJsonType basic_json specialization
|
||||||
@param j JSON value to hash
|
@param j JSON value to hash
|
||||||
@@ -143,47 +90,20 @@ std::size_t hash(const BasicJsonType& j)
|
|||||||
|
|
||||||
case BasicJsonType::value_t::number_integer:
|
case BasicJsonType::value_t::number_integer:
|
||||||
{
|
{
|
||||||
const auto v = j.template get<number_integer_t>();
|
const auto h = std::hash<number_integer_t> {}(j.template get<number_integer_t>());
|
||||||
// Use a shared numeric type tag so all numeric types that are equal hash the same
|
return combine(type, h);
|
||||||
const auto numeric_type = static_cast<std::size_t>(BasicJsonType::value_t::number_float);
|
|
||||||
|
|
||||||
if (is_exactly_representable_as_float<BasicJsonType>(v))
|
|
||||||
{
|
|
||||||
const auto h = std::hash<number_float_t> {}(static_cast<number_float_t>(v));
|
|
||||||
return combine(numeric_type, h);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto h = std::hash<number_integer_t> {}(v);
|
|
||||||
return combine(numeric_type, h);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case BasicJsonType::value_t::number_unsigned:
|
case BasicJsonType::value_t::number_unsigned:
|
||||||
{
|
{
|
||||||
const auto v = j.template get<number_unsigned_t>();
|
const auto h = std::hash<number_unsigned_t> {}(j.template get<number_unsigned_t>());
|
||||||
// Normalize to signed (matching operator== behavior for U-vs-I comparison)
|
return combine(type, h);
|
||||||
const auto v_as_signed = static_cast<number_integer_t>(v);
|
|
||||||
// Use a shared numeric type tag so all numeric types that are equal hash the same
|
|
||||||
const auto numeric_type = static_cast<std::size_t>(BasicJsonType::value_t::number_float);
|
|
||||||
|
|
||||||
if (is_exactly_representable_as_float<BasicJsonType>(v_as_signed))
|
|
||||||
{
|
|
||||||
const auto h = std::hash<number_float_t> {}(static_cast<number_float_t>(v_as_signed));
|
|
||||||
return combine(numeric_type, h);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto h = std::hash<number_integer_t> {}(v_as_signed);
|
|
||||||
return combine(numeric_type, h);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case BasicJsonType::value_t::number_float:
|
case BasicJsonType::value_t::number_float:
|
||||||
{
|
{
|
||||||
const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>());
|
const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>());
|
||||||
const auto numeric_type = static_cast<std::size_t>(BasicJsonType::value_t::number_float);
|
return combine(type, h);
|
||||||
return combine(numeric_type, h);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case BasicJsonType::value_t::binary:
|
case BasicJsonType::value_t::binary:
|
||||||
|
|||||||
@@ -465,12 +465,18 @@ class serializer
|
|||||||
{
|
{
|
||||||
if (codepoint <= 0xFFFF)
|
if (codepoint <= 0xFFFF)
|
||||||
{
|
{
|
||||||
write_u_escape(bytes, static_cast<std::uint16_t>(codepoint));
|
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||||
|
static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x",
|
||||||
|
static_cast<std::uint16_t>(codepoint)));
|
||||||
|
bytes += 6;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
write_u_escape(bytes, static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)));
|
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||||
write_u_escape(bytes, static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu)));
|
static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
|
||||||
|
static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),
|
||||||
|
static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu))));
|
||||||
|
bytes += 12;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -677,32 +683,6 @@ class serializer
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
* @brief write a lowercase "\uXXXX" escape sequence into @a string_buffer
|
|
||||||
*
|
|
||||||
* Branch-free replacement for `snprintf(buf, 7, "\\u%04x", codeunit)` in the
|
|
||||||
* string escaping hot path. It writes exactly six characters ('\\', 'u' and
|
|
||||||
* four hex digits) at position @a pos of @a string_buffer via a nibble
|
|
||||||
* lookup table, avoiding the format-string parsing and locale machinery of
|
|
||||||
* `snprintf`. Advances @a pos by the number of bytes written (6).
|
|
||||||
*
|
|
||||||
* @param[in] pos position in @a string_buffer to write at; there must
|
|
||||||
* be at least 6 bytes of headroom
|
|
||||||
* @param[in] codeunit 16-bit value to encode
|
|
||||||
*/
|
|
||||||
void write_u_escape(std::size_t& pos, std::uint16_t codeunit) noexcept
|
|
||||||
{
|
|
||||||
JSON_ASSERT(string_buffer.size() - pos >= 6);
|
|
||||||
constexpr const char* nibble_to_hex = "0123456789abcdef";
|
|
||||||
string_buffer[pos + 0] = '\\';
|
|
||||||
string_buffer[pos + 1] = 'u';
|
|
||||||
string_buffer[pos + 2] = nibble_to_hex[(codeunit >> 12u) & 0x0Fu];
|
|
||||||
string_buffer[pos + 3] = nibble_to_hex[(codeunit >> 8u) & 0x0Fu];
|
|
||||||
string_buffer[pos + 4] = nibble_to_hex[(codeunit >> 4u) & 0x0Fu];
|
|
||||||
string_buffer[pos + 5] = nibble_to_hex[codeunit & 0x0Fu];
|
|
||||||
pos += 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
// templates to avoid warnings about useless casts
|
// templates to avoid warnings about useless casts
|
||||||
template <typename NumberType, enable_if_t<std::is_signed<NumberType>::value, int> = 0>
|
template <typename NumberType, enable_if_t<std::is_signed<NumberType>::value, int> = 0>
|
||||||
bool is_negative_number(NumberType x)
|
bool is_negative_number(NumberType x)
|
||||||
|
|||||||
@@ -3712,71 +3712,71 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
||||||
#define INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
#define INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
||||||
|
|
||||||
#include <cstdint> // int64_t, uint64_t
|
#include <cstdint> // int64_t, uint64_t
|
||||||
#include <map> // map
|
#include <map> // map
|
||||||
#include <memory> // allocator
|
#include <memory> // allocator
|
||||||
#include <string> // string
|
#include <string> // string
|
||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
|
|
||||||
// #include <nlohmann/detail/abi_macros.hpp>
|
// #include <nlohmann/detail/abi_macros.hpp>
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief namespace for Niels Lohmann
|
@brief namespace for Niels Lohmann
|
||||||
@see https://github.com/nlohmann
|
@see https://github.com/nlohmann
|
||||||
@since version 1.0.0
|
@since version 1.0.0
|
||||||
*/
|
*/
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief default JSONSerializer template argument
|
@brief default JSONSerializer template argument
|
||||||
|
|
||||||
This serializer ignores the template arguments and uses ADL
|
This serializer ignores the template arguments and uses ADL
|
||||||
([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
|
([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
|
||||||
for serialization.
|
for serialization.
|
||||||
*/
|
*/
|
||||||
template<typename T = void, typename SFINAE = void>
|
template<typename T = void, typename SFINAE = void>
|
||||||
struct adl_serializer;
|
struct adl_serializer;
|
||||||
|
|
||||||
/// a class to store JSON values
|
/// a class to store JSON values
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/
|
/// @sa https://json.nlohmann.me/api/basic_json/
|
||||||
template<template<typename U, typename V, typename... Args> class ObjectType =
|
template<template<typename U, typename V, typename... Args> class ObjectType =
|
||||||
std::map,
|
std::map,
|
||||||
template<typename U, typename... Args> class ArrayType = std::vector,
|
template<typename U, typename... Args> class ArrayType = std::vector,
|
||||||
class StringType = std::string, class BooleanType = bool,
|
class StringType = std::string, class BooleanType = bool,
|
||||||
class NumberIntegerType = std::int64_t,
|
class NumberIntegerType = std::int64_t,
|
||||||
class NumberUnsignedType = std::uint64_t,
|
class NumberUnsignedType = std::uint64_t,
|
||||||
class NumberFloatType = double,
|
class NumberFloatType = double,
|
||||||
template<typename U> class AllocatorType = std::allocator,
|
template<typename U> class AllocatorType = std::allocator,
|
||||||
template<typename T, typename SFINAE = void> class JSONSerializer =
|
template<typename T, typename SFINAE = void> class JSONSerializer =
|
||||||
adl_serializer,
|
adl_serializer,
|
||||||
class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
|
class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
|
||||||
class CustomBaseClass = void>
|
class CustomBaseClass = void>
|
||||||
class basic_json;
|
class basic_json;
|
||||||
|
|
||||||
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/
|
/// @sa https://json.nlohmann.me/api/json_pointer/
|
||||||
template<typename RefStringType>
|
template<typename RefStringType>
|
||||||
class json_pointer;
|
class json_pointer;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief default specialization
|
@brief default specialization
|
||||||
@sa https://json.nlohmann.me/api/json/
|
@sa https://json.nlohmann.me/api/json/
|
||||||
*/
|
*/
|
||||||
using json = basic_json<>;
|
using json = basic_json<>;
|
||||||
|
|
||||||
/// @brief a minimal map-like container that preserves insertion order
|
/// @brief a minimal map-like container that preserves insertion order
|
||||||
/// @sa https://json.nlohmann.me/api/ordered_map/
|
/// @sa https://json.nlohmann.me/api/ordered_map/
|
||||||
template<class Key, class T, class IgnoredLess, class Allocator>
|
template<class Key, class T, class IgnoredLess, class Allocator>
|
||||||
struct ordered_map;
|
struct ordered_map;
|
||||||
|
|
||||||
/// @brief specialization that maintains the insertion order of object keys
|
/// @brief specialization that maintains the insertion order of object keys
|
||||||
/// @sa https://json.nlohmann.me/api/ordered_json/
|
/// @sa https://json.nlohmann.me/api/ordered_json/
|
||||||
using ordered_json = basic_json<nlohmann::ordered_map>;
|
using ordered_json = basic_json<nlohmann::ordered_map>;
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|
||||||
#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
||||||
|
|
||||||
@@ -5749,7 +5749,7 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
|
|
||||||
|
|
||||||
// #include <nlohmann/detail/macro_scope.hpp>
|
// #include <nlohmann/detail/macro_scope.hpp>
|
||||||
// JSON_HAS_CPP_17
|
// JSON_HAS_CPP_17
|
||||||
#ifdef JSON_HAS_CPP_17
|
#ifdef JSON_HAS_CPP_17
|
||||||
#include <optional> // optional
|
#include <optional> // optional
|
||||||
#endif
|
#endif
|
||||||
@@ -6677,8 +6677,6 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
#include <cstdint> // uint8_t
|
#include <cstdint> // uint8_t
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <functional> // hash
|
#include <functional> // hash
|
||||||
#include <limits> // numeric_limits
|
|
||||||
#include <cmath> // isfinite
|
|
||||||
|
|
||||||
// #include <nlohmann/detail/abi_macros.hpp>
|
// #include <nlohmann/detail/abi_macros.hpp>
|
||||||
|
|
||||||
@@ -6696,63 +6694,12 @@ inline std::size_t combine(std::size_t seed, std::size_t h) noexcept
|
|||||||
return seed;
|
return seed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a number_integer_t value is exactly representable as number_float_t
|
|
||||||
// Returns true if static_cast<number_integer_t>(static_cast<number_float_t>(val)) == val
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
inline bool is_exactly_representable_as_float(typename BasicJsonType::number_integer_t val) noexcept
|
|
||||||
{
|
|
||||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
|
||||||
using number_float_t = typename BasicJsonType::number_float_t;
|
|
||||||
|
|
||||||
// If the float type's mantissa covers the integer type's entire range, all values round-trip
|
|
||||||
constexpr int float_digits = std::numeric_limits<number_float_t>::digits;
|
|
||||||
constexpr int int_digits = std::numeric_limits<number_integer_t>::digits;
|
|
||||||
|
|
||||||
if (float_digits >= int_digits)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For values outside float's exact range, they don't round-trip
|
|
||||||
// The safe way to check: compute the max magnitude that round-trips
|
|
||||||
// Using unsigned arithmetic to avoid UB with negating INT_MIN
|
|
||||||
|
|
||||||
// Max magnitude representable exactly: 2^(digits-1) - 1 for signed, 2^digits - 1 for unsigned range
|
|
||||||
// But we're checking a signed value, so use 2^digits as the threshold
|
|
||||||
constexpr auto max_exact = static_cast<number_integer_t>(1) << (float_digits - 1);
|
|
||||||
|
|
||||||
// Check absolute value against this threshold
|
|
||||||
if (val >= 0)
|
|
||||||
{
|
|
||||||
if (val >= max_exact) return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// For negative values, check via unsigned wrapping arithmetic
|
|
||||||
// -val in unsigned domain; if it wraps, the value is too negative
|
|
||||||
auto unsigned_abs = static_cast<typename BasicJsonType::number_unsigned_t>(-val);
|
|
||||||
if (unsigned_abs >= static_cast<typename BasicJsonType::number_unsigned_t>(max_exact))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// For values within the exact range, verify the round-trip
|
|
||||||
const auto f = static_cast<number_float_t>(val);
|
|
||||||
return std::isfinite(f) && static_cast<number_integer_t>(f) == val;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief hash a JSON value
|
@brief hash a JSON value
|
||||||
|
|
||||||
The hash function tries to rely on std::hash where possible. Furthermore, the
|
The hash function tries to rely on std::hash where possible. Furthermore, the
|
||||||
type of the JSON value is taken into account to have different hash values for
|
type of the JSON value is taken into account to have different hash values for
|
||||||
most types. However, numeric types (number_integer, number_unsigned, number_float)
|
null, 0, 0U, and false, etc.
|
||||||
are hashed to satisfy the std::hash contract: if two json values compare equal,
|
|
||||||
they must have equal hash values. This means json(42), json(42u), and json(42.0)
|
|
||||||
all hash to the same value (since they compare equal). For large integer values
|
|
||||||
outside the exact representable range of the float type, integer values are hashed
|
|
||||||
in their own domain to avoid precision loss.
|
|
||||||
|
|
||||||
@tparam BasicJsonType basic_json specialization
|
@tparam BasicJsonType basic_json specialization
|
||||||
@param j JSON value to hash
|
@param j JSON value to hash
|
||||||
@@ -6811,47 +6758,20 @@ std::size_t hash(const BasicJsonType& j)
|
|||||||
|
|
||||||
case BasicJsonType::value_t::number_integer:
|
case BasicJsonType::value_t::number_integer:
|
||||||
{
|
{
|
||||||
const auto v = j.template get<number_integer_t>();
|
const auto h = std::hash<number_integer_t> {}(j.template get<number_integer_t>());
|
||||||
// Use a shared numeric type tag so all numeric types that are equal hash the same
|
return combine(type, h);
|
||||||
const auto numeric_type = static_cast<std::size_t>(BasicJsonType::value_t::number_float);
|
|
||||||
|
|
||||||
if (is_exactly_representable_as_float<BasicJsonType>(v))
|
|
||||||
{
|
|
||||||
const auto h = std::hash<number_float_t> {}(static_cast<number_float_t>(v));
|
|
||||||
return combine(numeric_type, h);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto h = std::hash<number_integer_t> {}(v);
|
|
||||||
return combine(numeric_type, h);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case BasicJsonType::value_t::number_unsigned:
|
case BasicJsonType::value_t::number_unsigned:
|
||||||
{
|
{
|
||||||
const auto v = j.template get<number_unsigned_t>();
|
const auto h = std::hash<number_unsigned_t> {}(j.template get<number_unsigned_t>());
|
||||||
// Normalize to signed (matching operator== behavior for U-vs-I comparison)
|
return combine(type, h);
|
||||||
const auto v_as_signed = static_cast<number_integer_t>(v);
|
|
||||||
// Use a shared numeric type tag so all numeric types that are equal hash the same
|
|
||||||
const auto numeric_type = static_cast<std::size_t>(BasicJsonType::value_t::number_float);
|
|
||||||
|
|
||||||
if (is_exactly_representable_as_float<BasicJsonType>(v_as_signed))
|
|
||||||
{
|
|
||||||
const auto h = std::hash<number_float_t> {}(static_cast<number_float_t>(v_as_signed));
|
|
||||||
return combine(numeric_type, h);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto h = std::hash<number_integer_t> {}(v_as_signed);
|
|
||||||
return combine(numeric_type, h);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case BasicJsonType::value_t::number_float:
|
case BasicJsonType::value_t::number_float:
|
||||||
{
|
{
|
||||||
const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>());
|
const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>());
|
||||||
const auto numeric_type = static_cast<std::size_t>(BasicJsonType::value_t::number_float);
|
return combine(type, h);
|
||||||
return combine(numeric_type, h);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case BasicJsonType::value_t::binary:
|
case BasicJsonType::value_t::binary:
|
||||||
@@ -20042,12 +19962,18 @@ class serializer
|
|||||||
{
|
{
|
||||||
if (codepoint <= 0xFFFF)
|
if (codepoint <= 0xFFFF)
|
||||||
{
|
{
|
||||||
write_u_escape(bytes, static_cast<std::uint16_t>(codepoint));
|
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||||
|
static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x",
|
||||||
|
static_cast<std::uint16_t>(codepoint)));
|
||||||
|
bytes += 6;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
write_u_escape(bytes, static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)));
|
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||||
write_u_escape(bytes, static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu)));
|
static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
|
||||||
|
static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),
|
||||||
|
static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu))));
|
||||||
|
bytes += 12;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -20254,32 +20180,6 @@ class serializer
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
* @brief write a lowercase "\uXXXX" escape sequence into @a string_buffer
|
|
||||||
*
|
|
||||||
* Branch-free replacement for `snprintf(buf, 7, "\\u%04x", codeunit)` in the
|
|
||||||
* string escaping hot path. It writes exactly six characters ('\\', 'u' and
|
|
||||||
* four hex digits) at position @a pos of @a string_buffer via a nibble
|
|
||||||
* lookup table, avoiding the format-string parsing and locale machinery of
|
|
||||||
* `snprintf`. Advances @a pos by the number of bytes written (6).
|
|
||||||
*
|
|
||||||
* @param[in] pos position in @a string_buffer to write at; there must
|
|
||||||
* be at least 6 bytes of headroom
|
|
||||||
* @param[in] codeunit 16-bit value to encode
|
|
||||||
*/
|
|
||||||
void write_u_escape(std::size_t& pos, std::uint16_t codeunit) noexcept
|
|
||||||
{
|
|
||||||
JSON_ASSERT(string_buffer.size() - pos >= 6);
|
|
||||||
constexpr const char* nibble_to_hex = "0123456789abcdef";
|
|
||||||
string_buffer[pos + 0] = '\\';
|
|
||||||
string_buffer[pos + 1] = 'u';
|
|
||||||
string_buffer[pos + 2] = nibble_to_hex[(codeunit >> 12u) & 0x0Fu];
|
|
||||||
string_buffer[pos + 3] = nibble_to_hex[(codeunit >> 8u) & 0x0Fu];
|
|
||||||
string_buffer[pos + 4] = nibble_to_hex[(codeunit >> 4u) & 0x0Fu];
|
|
||||||
string_buffer[pos + 5] = nibble_to_hex[codeunit & 0x0Fu];
|
|
||||||
pos += 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
// templates to avoid warnings about useless casts
|
// templates to avoid warnings about useless casts
|
||||||
template <typename NumberType, enable_if_t<std::is_signed<NumberType>::value, int> = 0>
|
template <typename NumberType, enable_if_t<std::is_signed<NumberType>::value, int> = 0>
|
||||||
bool is_negative_number(NumberType x)
|
bool is_negative_number(NumberType x)
|
||||||
@@ -21091,10 +20991,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true,
|
||||||
const bool ignore_comments = false,
|
const bool ignore_comments = false,
|
||||||
const bool ignore_trailing_commas = false
|
const bool ignore_trailing_commas = false
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter),
|
return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter),
|
||||||
std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas);
|
std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -21792,8 +21692,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
detail::enable_if_t <
|
detail::enable_if_t <
|
||||||
!detail::is_basic_json<U>::value && detail::is_compatible_type<basic_json_t, U>::value, int > = 0 >
|
!detail::is_basic_json<U>::value && detail::is_compatible_type<basic_json_t, U>::value, int > = 0 >
|
||||||
basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape)
|
basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape)
|
||||||
JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),
|
JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),
|
||||||
std::forward<CompatibleType>(val))))
|
std::forward<CompatibleType>(val))))
|
||||||
{
|
{
|
||||||
JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));
|
JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));
|
||||||
set_parents();
|
set_parents();
|
||||||
@@ -22596,7 +22496,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
detail::has_from_json<basic_json_t, ValueType>::value,
|
detail::has_from_json<basic_json_t, ValueType>::value,
|
||||||
int > = 0 >
|
int > = 0 >
|
||||||
ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept(
|
ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept(
|
||||||
JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))
|
JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))
|
||||||
{
|
{
|
||||||
auto ret = ValueType();
|
auto ret = ValueType();
|
||||||
JSONSerializer<ValueType>::from_json(*this, ret);
|
JSONSerializer<ValueType>::from_json(*this, ret);
|
||||||
@@ -22638,7 +22538,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
detail::has_non_default_from_json<basic_json_t, ValueType>::value,
|
detail::has_non_default_from_json<basic_json_t, ValueType>::value,
|
||||||
int > = 0 >
|
int > = 0 >
|
||||||
ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept(
|
ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept(
|
||||||
JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>())))
|
JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>())))
|
||||||
{
|
{
|
||||||
return JSONSerializer<ValueType>::from_json(*this);
|
return JSONSerializer<ValueType>::from_json(*this);
|
||||||
}
|
}
|
||||||
@@ -22788,7 +22688,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
detail::has_from_json<basic_json_t, ValueType>::value,
|
detail::has_from_json<basic_json_t, ValueType>::value,
|
||||||
int > = 0 >
|
int > = 0 >
|
||||||
ValueType & get_to(ValueType& v) const noexcept(noexcept(
|
ValueType & get_to(ValueType& v) const noexcept(noexcept(
|
||||||
JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))
|
JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))
|
||||||
{
|
{
|
||||||
JSONSerializer<ValueType>::from_json(*this, v);
|
JSONSerializer<ValueType>::from_json(*this, v);
|
||||||
return v;
|
return v;
|
||||||
|
|||||||
@@ -232,9 +232,6 @@ TEST_CASE("algorithms")
|
|||||||
// only the first four elements are expected to be sorted, the rest are
|
// only the first four elements are expected to be sorted, the rest are
|
||||||
// unspecified by the standard
|
// unspecified by the standard
|
||||||
const json expected({nullptr, false, true, 3});
|
const json expected({nullptr, false, true, 3});
|
||||||
// std::equal below only bounds-checks the first range; assert the
|
|
||||||
// second range is at least as long to rule out an over-read (CWE-126)
|
|
||||||
CHECK(std::distance(begin(expected), end(expected)) >= 4);
|
|
||||||
CHECK(std::equal(j.begin(), j.begin() + 4, begin(expected)));
|
CHECK(std::equal(j.begin(), j.begin() + 4, begin(expected)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -322,12 +322,14 @@ TEST_CASE("alternative string type")
|
|||||||
|
|
||||||
SECTION("JSON pointer")
|
SECTION("JSON pointer")
|
||||||
{
|
{
|
||||||
// Direct conversion from a json literal to alt_json is not supported due to issue #3425:
|
// conversion from json to alt_json fails to compile (see #3425);
|
||||||
// alt_json's string_t (alt_string) is not directly constructible from std::string, so the
|
// attempted fix(*) produces: [[['b','a','r'],['b','a','z']]] (with each char being an integer)
|
||||||
// cross-basic_json conversion falls back to the array-conversion path, incorrectly representing
|
// (*) disable implicit conversion for json_refs of any basic_json type
|
||||||
// objects as arrays of [key, value] pairs and strings as arrays of character codes.
|
// alt_json j = R"(
|
||||||
// See https://github.com/nlohmann/json/issues/3425 for details.
|
// {
|
||||||
// Workaround: use alt_json::parse() instead of implicit conversion.
|
// "foo": ["bar", "baz"]
|
||||||
|
// }
|
||||||
|
// )"_json;
|
||||||
auto j = alt_json::parse(R"({"foo": ["bar", "baz"]})");
|
auto j = alt_json::parse(R"({"foo": ["bar", "baz"]})");
|
||||||
|
|
||||||
CHECK(j.at(alt_json::json_pointer("/foo/0")) == j["foo"][0]);
|
CHECK(j.at(alt_json::json_pointer("/foo/0")) == j["foo"][0]);
|
||||||
|
|||||||
@@ -168,32 +168,6 @@ TEST_CASE("convenience functions")
|
|||||||
CHECK_THROWS_WITH_AS(check_escaped("\xC2"), "[json.exception.type_error.316] incomplete UTF-8 string; last byte: 0xC2", json::type_error&);
|
CHECK_THROWS_WITH_AS(check_escaped("\xC2"), "[json.exception.type_error.316] incomplete UTF-8 string; last byte: 0xC2", json::type_error&);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("string escape with ensure_ascii")
|
|
||||||
{
|
|
||||||
// control characters are escaped regardless of ensure_ascii
|
|
||||||
check_escaped("\x01", "\\u0001", true);
|
|
||||||
check_escaped("\x1f", "\\u001f", true);
|
|
||||||
|
|
||||||
// non-ASCII code points in the Basic Multilingual Plane are emitted as
|
|
||||||
// a single lowercase \uXXXX escape (exercises every nibble position)
|
|
||||||
check_escaped("\xC2\x80", "\\u0080", true); // U+0080
|
|
||||||
check_escaped("\xC3\xBF", "\\u00ff", true); // U+00FF (ÿ)
|
|
||||||
check_escaped("\xDF\xBF", "\\u07ff", true); // U+07FF
|
|
||||||
check_escaped("\xE4\xBD\xA0", "\\u4f60", true); // U+4F60 (你)
|
|
||||||
check_escaped("\xEA\xAF\x8D", "\\uabcd", true); // U+ABCD
|
|
||||||
check_escaped("\xEF\xBF\xBD", "\\ufffd", true); // U+FFFD (replacement char, all-f nibbles)
|
|
||||||
|
|
||||||
// code points outside the BMP are emitted as a UTF-16 surrogate pair
|
|
||||||
// of two lowercase \uXXXX escapes
|
|
||||||
check_escaped("\xF0\x90\x80\x80", "\\ud800\\udc00", true); // U+10000 (lowest astral)
|
|
||||||
check_escaped("\xF0\x9F\x98\x80", "\\ud83d\\ude00", true); // U+1F600 (😀)
|
|
||||||
check_escaped("\xF4\x8F\xBF\xBF", "\\udbff\\udfff", true); // U+10FFFF (highest code point)
|
|
||||||
|
|
||||||
// with ensure_ascii disabled, non-ASCII input is passed through verbatim
|
|
||||||
check_escaped("\xE4\xBD\xA0", "\xE4\xBD\xA0", false);
|
|
||||||
check_escaped("\xF0\x9F\x98\x80", "\xF0\x9F\x98\x80", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("string concat")
|
SECTION("string concat")
|
||||||
{
|
{
|
||||||
using nlohmann::detail::concat;
|
using nlohmann::detail::concat;
|
||||||
|
|||||||
+6
-22
@@ -35,10 +35,10 @@ TEST_CASE("hash<nlohmann::json>")
|
|||||||
|
|
||||||
// number
|
// number
|
||||||
hashes.insert(std::hash<json> {}(json(0)));
|
hashes.insert(std::hash<json> {}(json(0)));
|
||||||
hashes.insert(std::hash<json> {}(json(static_cast<unsigned>(0)))); // now same hash as json(0)
|
hashes.insert(std::hash<json> {}(json(static_cast<unsigned>(0))));
|
||||||
hashes.insert(std::hash<json> {}(json(0.0))); // now same hash as json(0)
|
|
||||||
|
|
||||||
hashes.insert(std::hash<json> {}(json(-1)));
|
hashes.insert(std::hash<json> {}(json(-1)));
|
||||||
|
hashes.insert(std::hash<json> {}(json(0.0)));
|
||||||
hashes.insert(std::hash<json> {}(json(42.23)));
|
hashes.insert(std::hash<json> {}(json(42.23)));
|
||||||
|
|
||||||
// array
|
// array
|
||||||
@@ -60,16 +60,7 @@ TEST_CASE("hash<nlohmann::json>")
|
|||||||
// discarded
|
// discarded
|
||||||
hashes.insert(std::hash<json> {}(json(json::value_t::discarded)));
|
hashes.insert(std::hash<json> {}(json(json::value_t::discarded)));
|
||||||
|
|
||||||
// Note: json(0), json(0U), and json(0.0) now hash to the same value
|
CHECK(hashes.size() == 21);
|
||||||
// (to satisfy the std::hash contract: equal values must hash equally)
|
|
||||||
// So we expect 19 distinct hashes instead of 21
|
|
||||||
CHECK(hashes.size() == 19);
|
|
||||||
|
|
||||||
// Verify the std::hash contract: equal values must hash equally
|
|
||||||
CHECK(std::hash<json>{}(json(0)) == std::hash<json>{}(json(static_cast<unsigned>(0))));
|
|
||||||
CHECK(std::hash<json>{}(json(0)) == std::hash<json>{}(json(0.0)));
|
|
||||||
CHECK(std::hash<json>{}(json(42)) == std::hash<json>{}(json(42u)));
|
|
||||||
CHECK(std::hash<json>{}(json(42)) == std::hash<json>{}(json(42.0)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("hash<nlohmann::ordered_json>")
|
TEST_CASE("hash<nlohmann::ordered_json>")
|
||||||
@@ -93,10 +84,10 @@ TEST_CASE("hash<nlohmann::ordered_json>")
|
|||||||
|
|
||||||
// number
|
// number
|
||||||
hashes.insert(std::hash<ordered_json> {}(ordered_json(0)));
|
hashes.insert(std::hash<ordered_json> {}(ordered_json(0)));
|
||||||
hashes.insert(std::hash<ordered_json> {}(ordered_json(static_cast<unsigned>(0)))); // now same hash as ordered_json(0)
|
hashes.insert(std::hash<ordered_json> {}(ordered_json(static_cast<unsigned>(0))));
|
||||||
hashes.insert(std::hash<ordered_json> {}(ordered_json(0.0))); // now same hash as ordered_json(0)
|
|
||||||
|
|
||||||
hashes.insert(std::hash<ordered_json> {}(ordered_json(-1)));
|
hashes.insert(std::hash<ordered_json> {}(ordered_json(-1)));
|
||||||
|
hashes.insert(std::hash<ordered_json> {}(ordered_json(0.0)));
|
||||||
hashes.insert(std::hash<ordered_json> {}(ordered_json(42.23)));
|
hashes.insert(std::hash<ordered_json> {}(ordered_json(42.23)));
|
||||||
|
|
||||||
// array
|
// array
|
||||||
@@ -118,12 +109,5 @@ TEST_CASE("hash<nlohmann::ordered_json>")
|
|||||||
// discarded
|
// discarded
|
||||||
hashes.insert(std::hash<ordered_json> {}(ordered_json(ordered_json::value_t::discarded)));
|
hashes.insert(std::hash<ordered_json> {}(ordered_json(ordered_json::value_t::discarded)));
|
||||||
|
|
||||||
// Note: ordered_json(0), ordered_json(0U), and ordered_json(0.0) now hash to the same value
|
CHECK(hashes.size() == 21);
|
||||||
CHECK(hashes.size() == 19);
|
|
||||||
|
|
||||||
// Verify the std::hash contract for ordered_json as well
|
|
||||||
CHECK(std::hash<ordered_json>{}(ordered_json(0)) == std::hash<ordered_json>{}(ordered_json(static_cast<unsigned>(0))));
|
|
||||||
CHECK(std::hash<ordered_json>{}(ordered_json(0)) == std::hash<ordered_json>{}(ordered_json(0.0)));
|
|
||||||
CHECK(std::hash<ordered_json>{}(ordered_json(42)) == std::hash<ordered_json>{}(ordered_json(42u)));
|
|
||||||
CHECK(std::hash<ordered_json>{}(ordered_json(42)) == std::hash<ordered_json>{}(ordered_json(42.0)));
|
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+7115
-5102
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user