mirror of
https://github.com/nlohmann/json.git
synced 2026-10-21 13:38:16 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c634c24877 | ||
|
|
5e13f834c1 | ||
|
|
e13e976801 | ||
|
|
898e6ee81d | ||
|
|
ef1cdb59ac |
No files matched your search
@@ -1,81 +0,0 @@
|
||||
name: "Check API documentation"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check_api_docs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout pull request
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Install clang
|
||||
# Used only as a subprocess for `clang++ -E -v` system-include-path discovery in
|
||||
# extract_api.py; it does not need to version-match the pinned libclang pip wheel
|
||||
# below, which does the actual AST parsing. Do not "fix" this to be version-matched.
|
||||
run: sudo apt-get update && sudo apt-get install -y clang
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: pip install -r tools/api_checker/requirements.txt
|
||||
|
||||
- name: Extract API and regenerate the committed surface file
|
||||
run: |
|
||||
python3 tools/api_checker/extract_api.py \
|
||||
--header include/nlohmann/json.hpp \
|
||||
--include include \
|
||||
--output /tmp/api_snapshot.json \
|
||||
--surface-output tools/api_checker/api_surface.json
|
||||
|
||||
- name: "Check API documentation (Phase 1: advisory)"
|
||||
# Surfaces missing/broken @sa links without failing the job while the backlog from the
|
||||
# initial AST-based rollout is burned down. See tools/api_checker/POLICY.md and the PR
|
||||
# that introduced this workflow for the two-phase rollout plan.
|
||||
continue-on-error: true
|
||||
run: |
|
||||
python3 tools/api_checker/check_docs.py \
|
||||
--snapshot /tmp/api_snapshot.json
|
||||
|
||||
- name: Check macro documentation (advisory only)
|
||||
# Cross-checks docs/mkdocs/docs/api/macros/ pages against #define sites. Only checks the
|
||||
# documented-macro-still-exists direction; never blocks CI. See POLICY.md.
|
||||
run: python3 tools/api_checker/check_macros.py
|
||||
|
||||
- name: Check for uncommitted API surface changes
|
||||
id: diff
|
||||
run: |
|
||||
mkdir -p ${{ github.workspace }}/patch
|
||||
git diff --patch --no-color -- tools/api_checker/api_surface.json > ${{ github.workspace }}/patch/api_surface.patch
|
||||
if [ -s ${{ github.workspace }}/patch/api_surface.patch ]; then
|
||||
echo "tools/api_checker/api_surface.json is out of date. Diff:"
|
||||
cat ${{ github.workspace }}/patch/api_surface.patch
|
||||
echo "has_diff=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "has_diff=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
# Uploaded so contributors can fix their PR with `git apply api_surface.patch`
|
||||
# instead of installing libclang locally.
|
||||
- name: Upload patch
|
||||
if: steps.diff.outputs.has_diff == 'true'
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: api-surface-patch
|
||||
path: patch/api_surface.patch
|
||||
|
||||
- name: Fail if API surface file is not up to date
|
||||
# Unlike the doc-backlog check above, this is purely mechanical regeneration with no
|
||||
# backlog to phase in -- blocking from the start, matching check_amalgamation.yml's
|
||||
# precedent. Contributors who add/remove/rename public API must regenerate and commit
|
||||
# tools/api_checker/api_surface.json as part of their PR.
|
||||
if: steps.diff.outputs.has_diff == 'true'
|
||||
run: exit 1
|
||||
@@ -3,7 +3,6 @@
|
||||
*.gcno
|
||||
*.gcda
|
||||
.DS_Store
|
||||
__pycache__/
|
||||
|
||||
/.idea
|
||||
/cmake-build-*
|
||||
@@ -45,10 +44,6 @@ venv
|
||||
|
||||
nlohmann_json.spdx
|
||||
|
||||
# api_checker: ephemeral, location/doc-status-sensitive working file (not the committed
|
||||
# release-tracking artifact -- see tools/api_checker/api_surface.json for that)
|
||||
/tools/api_checker/api_snapshot.json
|
||||
|
||||
# Bazel-related
|
||||
MODULE.bazel.lock
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ Thanks everyone!
|
||||
|
||||
:books: If you want to **learn more** about how to use the library, check out the rest of the [**README**](#examples), have a look at [**code examples**](https://github.com/nlohmann/json/tree/develop/docs/mkdocs/docs/examples), or browse through the [**help pages**](https://json.nlohmann.me).
|
||||
|
||||
:construction: If you want to understand the **API** better, check out the [**API Reference**](https://json.nlohmann.me/api/basic_json/) or have a look at the [quick reference](#quick-reference) below. The public API surface is derived mechanically and checked for documentation coverage by the tooling in [`tools/api_checker/`](tools/api_checker/), whose [POLICY.md](tools/api_checker/POLICY.md) defines what counts as public API and what stability is guaranteed.
|
||||
:construction: If you want to understand the **API** better, check out the [**API Reference**](https://json.nlohmann.me/api/basic_json/) or have a look at the [quick reference](#quick-reference) below.
|
||||
|
||||
:bug: If you found a **bug**, please check the [**FAQ**](https://json.nlohmann.me/home/faq/) if it is a known issue or the result of a design decision. Please also have a look at the [**issue list**](https://github.com/nlohmann/json/issues) before you [**create a new issue**](https://github.com/nlohmann/json/issues/new/choose). Please provide as much information as possible to help us understand and reproduce your issue.
|
||||
|
||||
|
||||
@@ -485,10 +485,8 @@ basic_json(basic_json&& other) noexcept;
|
||||
1. Since version 1.0.0.
|
||||
2. Since version 1.0.0.
|
||||
3. Since version 2.1.0.
|
||||
4. Since version 3.2.0. Also initializes the position reported by
|
||||
[`start_pos()`](start_pos.md)/[`end_pos()`](end_pos.md) from `val` when
|
||||
[`JSON_DIAGNOSTIC_POSITIONS`](../macros/json_diagnostic_positions.md) is enabled, since version 3.12.0. Explicit
|
||||
for different string types if `JSON_USE_IMPLICIT_CONVERSIONS` is `0` since version 3.13.0.
|
||||
4. Since version 3.2.0. Explicit for different string types if `JSON_USE_IMPLICIT_CONVERSIONS` is `0` since
|
||||
version 3.13.0.
|
||||
5. Since version 1.0.0.
|
||||
6. Since version 1.0.0.
|
||||
7. Since version 1.0.0. Fixed in version 3.13.0 to also check the iterator range for binary values; before, a range
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
# <small>nlohmann::basic_json::</small>bjdata_version_t
|
||||
|
||||
```cpp
|
||||
enum class bjdata_version_t
|
||||
{
|
||||
draft2,
|
||||
draft3,
|
||||
};
|
||||
```
|
||||
|
||||
This enumeration is used in the [`to_bjdata`](to_bjdata.md) function to choose which draft version of
|
||||
the BJData specification to encode ND-array extensions for:
|
||||
|
||||
draft2
|
||||
: encode using the BJData Draft 2 ND-array format
|
||||
|
||||
draft3
|
||||
: encode using the BJData Draft 3 ND-array format
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example shows how `bjdata_version_t` selects the BJData draft used by `to_bjdata`.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/bjdata_version_t.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
--8<-- "examples/bjdata_version_t.output"
|
||||
```
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.12.0.
|
||||
@@ -1,32 +0,0 @@
|
||||
# <small>nlohmann::basic_json::</small>initializer_list_t
|
||||
|
||||
```cpp
|
||||
using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
|
||||
```
|
||||
|
||||
The type used for the initializer-list [constructor](basic_json.md) (overload 5) and for functions
|
||||
such as [`operator=`](operator=.md) that accept a braced-init-list of JSON values. Each element wraps a
|
||||
`basic_json` value or something convertible to one, deferring the decision of whether the list should be
|
||||
parsed as a JSON array or a JSON object to the constructor itself.
|
||||
|
||||
See the [constructor](basic_json.md) documentation for how `initializer_list_t` values are interpreted.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example shows how an `initializer_list_t` is used to construct a JSON value.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/initializer_list_t.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
--8<-- "examples/initializer_list_t.output"
|
||||
```
|
||||
|
||||
## Version history
|
||||
|
||||
- Since version 1.0.0.
|
||||
@@ -1,31 +0,0 @@
|
||||
# <small>nlohmann::basic_json::</small>json_sax_t
|
||||
|
||||
```cpp
|
||||
using json_sax_t = json_sax<basic_json>;
|
||||
```
|
||||
|
||||
The [`json_sax`](../json_sax/index.md) interface bound to this `basic_json` specialization, i.e. with
|
||||
`BasicJsonType` fixed to `basic_json`. Used as the SAX interface type by [`sax_parse`](sax_parse.md) and
|
||||
other SAX-based parsing functions.
|
||||
|
||||
See [`nlohmann::json_sax`](../json_sax/index.md) for more information.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example shows the type `json_sax_t`.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/json_sax_t.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
--8<-- "examples/json_sax_t.output"
|
||||
```
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.2.0.
|
||||
@@ -38,7 +38,7 @@ Strong guarantee: `source` and `target` are never modified.
|
||||
|
||||
Linear in the total number of values (including nested ones) in `source` and `target`, times the cost of a key-based
|
||||
object operation (looking up a key, or adding one to the patch): logarithmic for [`json`](../json.md), and linear for
|
||||
[`ordered_json`](../ordered_json.md) (see [`ordered_map` complexity](../ordered_map/index.md#complexity)). For
|
||||
[`ordered_json`](../ordered_json.md) (see [`ordered_map` complexity](../ordered_map.md#complexity)). For
|
||||
`ordered_json`, diffing two objects with `n` and `m` members therefore takes O((n+m)²).
|
||||
|
||||
## Notes
|
||||
|
||||
@@ -51,5 +51,3 @@ Linear.
|
||||
## Version history
|
||||
|
||||
- Added in version 1.0.0.
|
||||
- The `noexcept` specification was extended to also depend on
|
||||
[`json_base_class_t`](json_base_class_t.md)'s move-assignment in version 3.11.3.
|
||||
@@ -92,8 +92,3 @@ Linear in the size of the JSON value.
|
||||
- Since version 1.0.0.
|
||||
- Macros `JSON_EXPLICIT`/[`JSON_USE_IMPLICIT_CONVERSIONS`](../macros/json_use_implicit_conversions.md) added
|
||||
in version 3.9.0.
|
||||
- The exclusion of `std::any` from this conversion became conditional on
|
||||
[`JSON_HAS_STATIC_RTTI`](../macros/json_has_static_rtti.md) in version 3.11.3.
|
||||
- `std::optional<T>` excluded from this conversion in version 3.13.0; use
|
||||
[`get<std::optional<T>>()`](get.md)/[`get_to()`](get_to.md) instead (see
|
||||
[Converting values](../../features/conversions.md)).
|
||||
@@ -1,32 +0,0 @@
|
||||
# <small>nlohmann::byte_container_with_subtype::</small>container_type
|
||||
|
||||
```cpp
|
||||
using container_type = BinaryType;
|
||||
```
|
||||
|
||||
The type of the underlying binary container, forwarded from the `BinaryType` template parameter that
|
||||
`byte_container_with_subtype` is instantiated with. `byte_container_with_subtype` publicly inherits from
|
||||
`container_type`.
|
||||
|
||||
See [`basic_json::binary_t`](../basic_json/binary_t.md) for the type typically used to instantiate
|
||||
`BinaryType`.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example shows the type `container_type`.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/byte_container_with_subtype__container_type.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
--8<-- "examples/byte_container_with_subtype__container_type.output"
|
||||
```
|
||||
|
||||
## Version history
|
||||
|
||||
- Since version 3.8.0.
|
||||
@@ -1,28 +0,0 @@
|
||||
# <small>nlohmann::byte_container_with_subtype::</small>subtype_type
|
||||
|
||||
```cpp
|
||||
using subtype_type = std::uint64_t;
|
||||
```
|
||||
|
||||
The type used to store the optional binary subtype tag. See [`subtype`](subtype.md) and
|
||||
[`set_subtype`](set_subtype.md).
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example shows the type `subtype_type`.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/byte_container_with_subtype__subtype_type.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
--8<-- "examples/byte_container_with_subtype__subtype_type.output"
|
||||
```
|
||||
|
||||
## Version history
|
||||
|
||||
- Since version 3.8.0.
|
||||
@@ -1,30 +0,0 @@
|
||||
# <small>nlohmann::json_sax::</small>binary_t
|
||||
|
||||
```cpp
|
||||
using binary_t = typename BasicJsonType::binary_t;
|
||||
```
|
||||
|
||||
The type used by the [`binary`](binary.md) callback for JSON binary values, forwarded from the
|
||||
`BasicJsonType` template parameter.
|
||||
|
||||
See [`basic_json::binary_t`](../basic_json/binary_t.md) for more information.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example shows the type `binary_t` and its relation to `basic_json::binary_t`.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/json_sax__binary_t.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
--8<-- "examples/json_sax__binary_t.output"
|
||||
```
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.8.0.
|
||||
@@ -1,33 +0,0 @@
|
||||
# <small>nlohmann::json_sax::</small>json_sax
|
||||
|
||||
```cpp
|
||||
// (1)
|
||||
json_sax() = default;
|
||||
|
||||
// (2)
|
||||
json_sax(const json_sax&) = default;
|
||||
|
||||
// (3)
|
||||
json_sax(json_sax&&) noexcept = default;
|
||||
```
|
||||
|
||||
1. Default constructor.
|
||||
2. Copy constructor.
|
||||
3. Move constructor.
|
||||
|
||||
`json_sax` is a pure abstract base class with no data members of its own, so all three constructors are
|
||||
defaulted and only exist to make derived SAX consumers explicitly copyable/movable.
|
||||
|
||||
## Exception safety
|
||||
|
||||
No-throw guarantee: none of these constructors throw exceptions.
|
||||
|
||||
## Complexity
|
||||
|
||||
Constant.
|
||||
|
||||
<!-- NOLINT Examples -->
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.2.0.
|
||||
@@ -1,30 +0,0 @@
|
||||
# <small>nlohmann::json_sax::</small>number_float_t
|
||||
|
||||
```cpp
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
```
|
||||
|
||||
The type used by the [`number_float`](number_float.md) callback for JSON floating-point numbers,
|
||||
forwarded from the `BasicJsonType` template parameter.
|
||||
|
||||
See [`basic_json::number_float_t`](../basic_json/number_float_t.md) for more information.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example shows the type `number_float_t` and its relation to `basic_json::number_float_t`.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/json_sax__number_float_t.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
--8<-- "examples/json_sax__number_float_t.output"
|
||||
```
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.2.0.
|
||||
@@ -1,30 +0,0 @@
|
||||
# <small>nlohmann::json_sax::</small>number_integer_t
|
||||
|
||||
```cpp
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
```
|
||||
|
||||
The type used by the [`number_integer`](number_integer.md) callback for JSON integer numbers, forwarded
|
||||
from the `BasicJsonType` template parameter.
|
||||
|
||||
See [`basic_json::number_integer_t`](../basic_json/number_integer_t.md) for more information.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example shows the type `number_integer_t` and its relation to `basic_json::number_integer_t`.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/json_sax__number_integer_t.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
--8<-- "examples/json_sax__number_integer_t.output"
|
||||
```
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.2.0.
|
||||
@@ -1,30 +0,0 @@
|
||||
# <small>nlohmann::json_sax::</small>number_unsigned_t
|
||||
|
||||
```cpp
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
```
|
||||
|
||||
The type used by the [`number_unsigned`](number_unsigned.md) callback for JSON unsigned integer numbers,
|
||||
forwarded from the `BasicJsonType` template parameter.
|
||||
|
||||
See [`basic_json::number_unsigned_t`](../basic_json/number_unsigned_t.md) for more information.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example shows the type `number_unsigned_t` and its relation to `basic_json::number_unsigned_t`.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/json_sax__number_unsigned_t.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
--8<-- "examples/json_sax__number_unsigned_t.output"
|
||||
```
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.2.0.
|
||||
@@ -1,29 +0,0 @@
|
||||
# <small>nlohmann::json_sax::</small>operator=
|
||||
|
||||
```cpp
|
||||
// (1)
|
||||
json_sax& operator=(const json_sax&) = default;
|
||||
|
||||
// (2)
|
||||
json_sax& operator=(json_sax&&) noexcept = default;
|
||||
```
|
||||
|
||||
1. Copy assignment operator.
|
||||
2. Move assignment operator.
|
||||
|
||||
`json_sax` is a pure abstract base class with no data members of its own, so both assignment operators
|
||||
are defaulted and only exist to make derived SAX consumers explicitly copy-/move-assignable.
|
||||
|
||||
## Exception safety
|
||||
|
||||
No-throw guarantee: neither operator throws exceptions.
|
||||
|
||||
## Complexity
|
||||
|
||||
Constant.
|
||||
|
||||
<!-- NOLINT Examples -->
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.2.0.
|
||||
@@ -1,30 +0,0 @@
|
||||
# <small>nlohmann::json_sax::</small>string_t
|
||||
|
||||
```cpp
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
```
|
||||
|
||||
The type used by the [`string`](string.md) and [`key`](key.md) callbacks for JSON strings and object
|
||||
keys, forwarded from the `BasicJsonType` template parameter.
|
||||
|
||||
See [`basic_json::string_t`](../basic_json/string_t.md) for more information.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example shows the type `string_t` and its relation to `basic_json::string_t`.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/json_sax__string_t.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
--8<-- "examples/json_sax__string_t.output"
|
||||
```
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.2.0.
|
||||
@@ -1,22 +0,0 @@
|
||||
# <small>nlohmann::json_sax::</small>~json_sax
|
||||
|
||||
```cpp
|
||||
virtual ~json_sax() = default;
|
||||
```
|
||||
|
||||
Destructor. Virtual to allow proper destruction of derived SAX consumer classes through a
|
||||
pointer/reference to `json_sax`.
|
||||
|
||||
## Exception safety
|
||||
|
||||
No-throw guarantee: this destructor never throws exceptions.
|
||||
|
||||
## Complexity
|
||||
|
||||
Constant.
|
||||
|
||||
<!-- NOLINT Examples -->
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.2.0.
|
||||
@@ -8,16 +8,16 @@ This type preserves the insertion order of object keys.
|
||||
|
||||
## Iterator invalidation
|
||||
|
||||
The type is based on [`ordered_map`](ordered_map/index.md) which in turn uses a `std::vector` to store object elements.
|
||||
The type is based on [`ordered_map`](ordered_map.md) which in turn uses a `std::vector` to store object elements.
|
||||
Therefore, adding object elements can yield a reallocation in which case all iterators (including the
|
||||
[`end()`](basic_json/end.md) iterator) and all references to the elements are invalidated. Also, any iterator or
|
||||
reference after the insertion point will point to the same index, which is now a different value.
|
||||
|
||||
## Complexity
|
||||
|
||||
[`ordered_map`](ordered_map/index.md) has no lookup index: every key-based object operation is a linear scan, so building or
|
||||
[`ordered_map`](ordered_map.md) has no lookup index: every key-based object operation is a linear scan, so building or
|
||||
parsing an object of `n` keys costs O(n²) rather than O(n log n). See
|
||||
[`ordered_map` complexity](ordered_map/index.md#complexity) for the per-operation table and for measured numbers.
|
||||
[`ordered_map` complexity](ordered_map.md#complexity) for the per-operation table and for measured numbers.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -37,7 +37,7 @@ parsing an object of `n` keys costs O(n²) rather than O(n log n). See
|
||||
|
||||
## See also
|
||||
|
||||
- [ordered_map](ordered_map/index.md)
|
||||
- [ordered_map](ordered_map.md)
|
||||
- [Object Order](../features/object_order.md)
|
||||
|
||||
## Version history
|
||||
|
||||
@@ -6,7 +6,7 @@ template<class Key, class T, class IgnoredLess = std::less<Key>,
|
||||
struct ordered_map : std::vector<std::pair<const Key, T>, Allocator>;
|
||||
```
|
||||
|
||||
A minimal map-like container that preserves insertion order for use within [`nlohmann::ordered_json`](../ordered_json.md)
|
||||
A minimal map-like container that preserves insertion order for use within [`nlohmann::ordered_json`](ordered_json.md)
|
||||
(`nlohmann::basic_json<ordered_map>`).
|
||||
|
||||
## Template parameters
|
||||
@@ -30,19 +30,19 @@ case all iterators (including the `end()` iterator) and all references to the el
|
||||
|
||||
When the storage grows, the keys are copied and the mapped values are moved to the new storage. A plain `std::vector`
|
||||
would copy the whole elements instead, because their `#!cpp const` keys make them not nothrow move constructible; for
|
||||
[`ordered_json`](../ordered_json.md), this would be a deep copy of every nested value. The values are only copied if
|
||||
[`ordered_json`](ordered_json.md), this would be a deep copy of every nested value. The values are only copied if
|
||||
`T` is not default constructible or not nothrow move assignable.
|
||||
|
||||
## Member types
|
||||
|
||||
- **key_type** - key type (`Key`)
|
||||
- **mapped_type** - mapped type (`T`)
|
||||
- [**Container**](Container.md) - base container type (`#!cpp std::vector<std::pair<const Key, T>, Allocator>`)
|
||||
- **Container** - base container type (`#!cpp std::vector<std::pair<const Key, T>, Allocator>`)
|
||||
- **iterator**
|
||||
- **const_iterator**
|
||||
- **size_type**
|
||||
- **value_type**
|
||||
- [**key_compare**](key_compare.md) - key comparison function
|
||||
- **key_compare** - key comparison function
|
||||
```cpp
|
||||
std::equal_to<Key> // until C++14
|
||||
|
||||
@@ -51,16 +51,15 @@ std::equal_to<> // since C++14
|
||||
|
||||
## Member functions
|
||||
|
||||
- [(constructor)](ordered_map.md)
|
||||
- [(destructor)](~ordered_map.md)
|
||||
- [**operator=**](operator=.md)
|
||||
- [**emplace**](emplace.md)
|
||||
- [**operator\[\]**](operator[].md)
|
||||
- [**at**](at.md)
|
||||
- [**erase**](erase.md)
|
||||
- [**count**](count.md)
|
||||
- [**find**](find.md)
|
||||
- [**insert**](insert.md)
|
||||
- (constructor)
|
||||
- (destructor)
|
||||
- **emplace**
|
||||
- **operator\[\]**
|
||||
- **at**
|
||||
- **erase**
|
||||
- **count**
|
||||
- **find**
|
||||
- **insert**
|
||||
|
||||
## Exception safety
|
||||
|
||||
@@ -90,7 +89,7 @@ This differs from `#!cpp std::map`, where the same operations are O(log n).
|
||||
!!! warning "Quadratic cost of building large objects"
|
||||
|
||||
Because every insertion scans all elements inserted so far, building an object of `n` distinct keys costs
|
||||
**O(n²)** in total. This applies to filling an [`ordered_json`](../ordered_json.md) object key by key as well as to
|
||||
**O(n²)** in total. This applies to filling an [`ordered_json`](ordered_json.md) object key by key as well as to
|
||||
parsing one, since the parser inserts each key as it is read.
|
||||
|
||||
The cost is negligible for the object sizes typically found in configuration files or API payloads, but it grows
|
||||
@@ -107,7 +106,7 @@ This differs from `#!cpp std::map`, where the same operations are O(log n).
|
||||
If key order matters for objects of that size, consider a container with a lookup index, such as
|
||||
[`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map)
|
||||
([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)), as the object type -- see
|
||||
[object order](../../features/object_order.md).
|
||||
[object order](../features/object_order.md).
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -127,10 +126,10 @@ This differs from `#!cpp std::map`, where the same operations are O(log n).
|
||||
|
||||
## See also
|
||||
|
||||
- [ordered_json](../ordered_json.md)
|
||||
- [ordered_json](ordered_json.md)
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
|
||||
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](ordered_json.md).
|
||||
- Added **key_compare** member in version 3.11.0.
|
||||
- Changed in version 3.13.0: growing the storage moves the mapped values instead of copying them.
|
||||
@@ -1,28 +0,0 @@
|
||||
# <small>nlohmann::ordered_map::</small>Container
|
||||
|
||||
```cpp
|
||||
using Container = std::vector<std::pair<const Key, T>, Allocator>;
|
||||
```
|
||||
|
||||
The base container type that `ordered_map` publicly inherits from. Elements are stored in insertion
|
||||
order as `#!cpp std::pair<const Key, T>` entries in a `std::vector`.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example shows the type `Container`.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/ordered_map__Container.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
--8<-- "examples/ordered_map__Container.output"
|
||||
```
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
|
||||
@@ -1,61 +0,0 @@
|
||||
# <small>nlohmann::ordered_map::</small>at
|
||||
|
||||
```cpp
|
||||
// (1)
|
||||
T& at(const key_type& key);
|
||||
const T& at(const key_type& key) const;
|
||||
|
||||
// (2)
|
||||
template<class KeyType>
|
||||
T& at(KeyType&& key);
|
||||
template<class KeyType>
|
||||
const T& at(KeyType&& key) const;
|
||||
```
|
||||
|
||||
1. Returns a reference to the value mapped to `key`.
|
||||
2. Same as (1), but for any `KeyType` comparable to `key_type` via [`key_compare`](key_compare.md)
|
||||
(heterogeneous lookup, e.g. looking up by a `#!cpp const char*` without constructing a temporary
|
||||
`key_type`). Only participates in overload resolution if `KeyType` is usable as a key type.
|
||||
|
||||
## Template parameters
|
||||
|
||||
`KeyType`
|
||||
: a type comparable to `key_type` via [`key_compare`](key_compare.md)
|
||||
|
||||
## Parameters
|
||||
|
||||
`key` (in)
|
||||
: key of the element to find
|
||||
|
||||
## Return value
|
||||
|
||||
reference to the mapped value of the element with key equal to `key`
|
||||
|
||||
## Exceptions
|
||||
|
||||
Throws `std::out_of_range` if no element with key `key` exists.
|
||||
|
||||
## Complexity
|
||||
|
||||
Linear in the number of elements.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example shows how `at` is used.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/ordered_map__at.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
--8<-- "examples/ordered_map__at.output"
|
||||
```
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.9.1 to implement [`nlohmann::ordered_json`](../ordered_json.md).
|
||||
- Overload (2) added in version 3.11.0.
|
||||
@@ -1,53 +0,0 @@
|
||||
# <small>nlohmann::ordered_map::</small>count
|
||||
|
||||
```cpp
|
||||
// (1)
|
||||
size_type count(const key_type& key) const;
|
||||
|
||||
// (2)
|
||||
template<class KeyType>
|
||||
size_type count(KeyType&& key) const;
|
||||
```
|
||||
|
||||
1. Returns the number of elements with key equal to `key` (0 or 1, since keys are unique).
|
||||
2. Same as (1), but for any `KeyType` comparable to `key_type` via [`key_compare`](key_compare.md)
|
||||
(heterogeneous lookup). Only participates in overload resolution if `KeyType` is usable as a key type.
|
||||
|
||||
## Template parameters
|
||||
|
||||
`KeyType`
|
||||
: a type comparable to `key_type` via [`key_compare`](key_compare.md)
|
||||
|
||||
## Parameters
|
||||
|
||||
`key` (in)
|
||||
: key of the elements to count
|
||||
|
||||
## Return value
|
||||
|
||||
number of elements with key equal to `key` (0 or 1)
|
||||
|
||||
## Complexity
|
||||
|
||||
Linear in the number of elements.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example shows how `count` is used.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/ordered_map__count.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
--8<-- "examples/ordered_map__count.output"
|
||||
```
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.9.1 to implement [`nlohmann::ordered_json`](../ordered_json.md).
|
||||
- Overload (2) added in version 3.11.0.
|
||||
@@ -1,58 +0,0 @@
|
||||
# <small>nlohmann::ordered_map::</small>emplace
|
||||
|
||||
```cpp
|
||||
// (1)
|
||||
std::pair<iterator, bool> emplace(const key_type& key, T&& t);
|
||||
|
||||
// (2)
|
||||
template<class KeyType>
|
||||
std::pair<iterator, bool> emplace(KeyType&& key, T&& t);
|
||||
```
|
||||
|
||||
1. Inserts `#!cpp {key, t}` if no element with an equal key already exists (per [`key_compare`](key_compare.md)),
|
||||
appending it at the end to preserve insertion order. If an equal key already exists, does nothing.
|
||||
2. Same as (1), but for any `KeyType` comparable to `key_type` via [`key_compare`](key_compare.md)
|
||||
(heterogeneous lookup). Only participates in overload resolution if `KeyType` is usable as a key type.
|
||||
|
||||
## Template parameters
|
||||
|
||||
`KeyType`
|
||||
: a type comparable to `key_type` via [`key_compare`](key_compare.md)
|
||||
|
||||
## Parameters
|
||||
|
||||
`key` (in)
|
||||
: key of the element to insert
|
||||
|
||||
`t` (in)
|
||||
: value of the element to insert
|
||||
|
||||
## Return value
|
||||
|
||||
pair of an iterator to the (possibly newly inserted) element, and a `bool` that is `true` if insertion
|
||||
took place and `false` if an element with an equal key already existed
|
||||
|
||||
## Complexity
|
||||
|
||||
Linear in the number of elements.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example shows how `emplace` is used.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/ordered_map__emplace.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
--8<-- "examples/ordered_map__emplace.output"
|
||||
```
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
|
||||
- Overload (2) added in version 3.11.0.
|
||||
@@ -1,75 +0,0 @@
|
||||
# <small>nlohmann::ordered_map::</small>erase
|
||||
|
||||
```cpp
|
||||
// (1)
|
||||
size_type erase(const key_type& key);
|
||||
|
||||
// (2)
|
||||
template<class KeyType>
|
||||
size_type erase(KeyType&& key);
|
||||
|
||||
// (3)
|
||||
iterator erase(iterator pos);
|
||||
|
||||
// (4)
|
||||
iterator erase(iterator first, iterator last);
|
||||
```
|
||||
|
||||
1. Removes the element with key equal to `key`, if any, preserving the relative order of the remaining
|
||||
elements.
|
||||
2. Same as (1), but for any `KeyType` comparable to `key_type` via [`key_compare`](key_compare.md)
|
||||
(heterogeneous lookup). Only participates in overload resolution if `KeyType` is usable as a key type.
|
||||
3. Removes the element at `pos`.
|
||||
4. Removes the elements in range `[first, last)`.
|
||||
|
||||
## Template parameters
|
||||
|
||||
`KeyType`
|
||||
: a type comparable to `key_type` via [`key_compare`](key_compare.md)
|
||||
|
||||
## Parameters
|
||||
|
||||
`key` (in)
|
||||
: key of the element to remove
|
||||
|
||||
`pos` (in)
|
||||
: iterator to the element to remove
|
||||
|
||||
`first` (in)
|
||||
: iterator to the first element to remove
|
||||
|
||||
`last` (in)
|
||||
: iterator one past the last element to remove
|
||||
|
||||
## Return value
|
||||
|
||||
1. number of elements removed (0 or 1)
|
||||
2. number of elements removed (0 or 1)
|
||||
3. iterator following the removed element
|
||||
4. iterator following the last removed element
|
||||
|
||||
## Complexity
|
||||
|
||||
Linear in the number of elements (elements after the removed one(s) are shifted to keep storage
|
||||
contiguous).
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example shows how `erase` is used.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/ordered_map__erase.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
--8<-- "examples/ordered_map__erase.output"
|
||||
```
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
|
||||
- Overload (2) added in version 3.11.0.
|
||||
@@ -1,56 +0,0 @@
|
||||
# <small>nlohmann::ordered_map::</small>find
|
||||
|
||||
```cpp
|
||||
// (1)
|
||||
iterator find(const key_type& key);
|
||||
const_iterator find(const key_type& key) const;
|
||||
|
||||
// (2)
|
||||
template<class KeyType>
|
||||
iterator find(KeyType&& key);
|
||||
template<class KeyType>
|
||||
const_iterator find(KeyType&& key) const;
|
||||
```
|
||||
|
||||
1. Returns an iterator to the element with key equal to `key`, or `end()` if no such element exists.
|
||||
2. Same as (1), but for any `KeyType` comparable to `key_type` via [`key_compare`](key_compare.md)
|
||||
(heterogeneous lookup). Only participates in overload resolution if `KeyType` is usable as a key type.
|
||||
|
||||
## Template parameters
|
||||
|
||||
`KeyType`
|
||||
: a type comparable to `key_type` via [`key_compare`](key_compare.md)
|
||||
|
||||
## Parameters
|
||||
|
||||
`key` (in)
|
||||
: key of the element to find
|
||||
|
||||
## Return value
|
||||
|
||||
iterator to the element with key equal to `key`, or `end()` if not found
|
||||
|
||||
## Complexity
|
||||
|
||||
Linear in the number of elements.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example shows how `find` is used.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/ordered_map__find.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
--8<-- "examples/ordered_map__find.output"
|
||||
```
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.9.1 to implement [`nlohmann::ordered_json`](../ordered_json.md).
|
||||
- Overload (2) added in version 3.11.0.
|
||||
@@ -1,63 +0,0 @@
|
||||
# <small>nlohmann::ordered_map::</small>insert
|
||||
|
||||
```cpp
|
||||
// (1)
|
||||
std::pair<iterator, bool> insert(value_type&& value);
|
||||
std::pair<iterator, bool> insert(const value_type& value);
|
||||
|
||||
// (2)
|
||||
template<typename InputIt>
|
||||
void insert(InputIt first, InputIt last);
|
||||
```
|
||||
|
||||
1. Inserts `value` if no element with an equal key already exists (per [`key_compare`](key_compare.md)),
|
||||
appending it at the end to preserve insertion order. If an equal key already exists, does nothing.
|
||||
2. Inserts the elements from range `[first, last)`, in iteration order, applying the same equal-key rule
|
||||
as (1) to each element.
|
||||
|
||||
## Template parameters
|
||||
|
||||
`InputIt`
|
||||
: an input iterator type
|
||||
|
||||
## Parameters
|
||||
|
||||
`value` (in)
|
||||
: value to insert
|
||||
|
||||
`first` (in)
|
||||
: iterator to the first element to insert
|
||||
|
||||
`last` (in)
|
||||
: iterator one past the last element to insert
|
||||
|
||||
## Return value
|
||||
|
||||
1. pair of an iterator to the (possibly newly inserted) element, and a `bool` that is `true` if insertion
|
||||
took place and `false` if an element with an equal key already existed
|
||||
2. (none)
|
||||
|
||||
## Complexity
|
||||
|
||||
1. Linear in the number of elements.
|
||||
2. Linear in the distance between `first` and `last`, times linear in the number of elements.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example shows how `insert` is used.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/ordered_map__insert.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
--8<-- "examples/ordered_map__insert.output"
|
||||
```
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.9.1 to implement [`nlohmann::ordered_json`](../ordered_json.md).
|
||||
@@ -1,34 +0,0 @@
|
||||
# <small>nlohmann::ordered_map::</small>key_compare
|
||||
|
||||
```cpp
|
||||
using key_compare = std::equal_to<Key>; // until C++14
|
||||
|
||||
using key_compare = std::equal_to<>; // since C++14
|
||||
```
|
||||
|
||||
The comparator used to determine key equality when looking up elements. Unlike `std::map`, `ordered_map`
|
||||
uses linear search with `key_compare` rather than an ordering relation, since element order reflects
|
||||
insertion order rather than key order.
|
||||
|
||||
Since C++14, the transparent `#!cpp std::equal_to<>` is used, which enables heterogeneous lookup (e.g.
|
||||
looking up by a `#!cpp const char*` key without constructing a temporary `Key`).
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example shows how `key_compare` is used.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/ordered_map__key_compare.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
--8<-- "examples/ordered_map__key_compare.output"
|
||||
```
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.11.0.
|
||||
@@ -1,32 +0,0 @@
|
||||
# <small>nlohmann::ordered_map::</small>operator=
|
||||
|
||||
```cpp
|
||||
// (1)
|
||||
ordered_map& operator=(const ordered_map& other);
|
||||
|
||||
// (2)
|
||||
ordered_map& operator=(ordered_map&& other) noexcept(std::is_nothrow_move_assignable<Container>::value);
|
||||
```
|
||||
|
||||
1. Copy assignment operator.
|
||||
2. Move assignment operator.
|
||||
|
||||
## Parameters
|
||||
|
||||
`other` (in)
|
||||
: value to assign from
|
||||
|
||||
## Return value
|
||||
|
||||
`*this`
|
||||
|
||||
## Complexity
|
||||
|
||||
1. Linear in the size of `other`.
|
||||
2. Constant.
|
||||
|
||||
<!-- NOLINT Examples -->
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
|
||||
@@ -1,62 +0,0 @@
|
||||
# <small>nlohmann::ordered_map::</small>operator[]
|
||||
|
||||
```cpp
|
||||
// (1)
|
||||
T& operator[](const key_type& key);
|
||||
const T& operator[](const key_type& key) const;
|
||||
|
||||
// (2)
|
||||
template<class KeyType>
|
||||
T& operator[](KeyType&& key);
|
||||
template<class KeyType>
|
||||
const T& operator[](KeyType&& key) const;
|
||||
```
|
||||
|
||||
1. Returns a reference to the value mapped to `key`, inserting a default-constructed `T` (non-`const`
|
||||
overload only) if no such element exists yet.
|
||||
2. Same as (1), but for any `KeyType` comparable to `key_type` via [`key_compare`](key_compare.md)
|
||||
(heterogeneous lookup). Only participates in overload resolution if `KeyType` is usable as a key type.
|
||||
|
||||
## Template parameters
|
||||
|
||||
`KeyType`
|
||||
: a type comparable to `key_type` via [`key_compare`](key_compare.md)
|
||||
|
||||
## Parameters
|
||||
|
||||
`key` (in)
|
||||
: key of the element to find or insert
|
||||
|
||||
## Return value
|
||||
|
||||
reference to the mapped value of the element with key equal to `key`
|
||||
|
||||
## Exceptions
|
||||
|
||||
The `const` overloads throw `std::out_of_range` if no element with key `key` exists (they delegate to
|
||||
[`at`](at.md)).
|
||||
|
||||
## Complexity
|
||||
|
||||
Linear in the number of elements.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example shows how `operator[]` is used.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/ordered_map__operator_idx.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
--8<-- "examples/ordered_map__operator_idx.output"
|
||||
```
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
|
||||
- Overload (2) added in version 3.11.0.
|
||||
@@ -1,66 +0,0 @@
|
||||
# <small>nlohmann::ordered_map::</small>ordered_map
|
||||
|
||||
```cpp
|
||||
// (1)
|
||||
ordered_map() noexcept(noexcept(Container()));
|
||||
|
||||
// (2)
|
||||
explicit ordered_map(const Allocator& alloc) noexcept(noexcept(Container(alloc)));
|
||||
|
||||
// (3)
|
||||
template <class It>
|
||||
ordered_map(It first, It last, const Allocator& alloc = Allocator());
|
||||
|
||||
// (4)
|
||||
ordered_map(std::initializer_list<value_type> init, const Allocator& alloc = Allocator());
|
||||
|
||||
// (5)
|
||||
ordered_map(const ordered_map&) = default;
|
||||
|
||||
// (6)
|
||||
ordered_map(ordered_map&&) noexcept(std::is_nothrow_move_constructible<Container>::value) = default;
|
||||
```
|
||||
|
||||
1. Default constructor. Creates an empty `ordered_map`.
|
||||
2. Creates an empty `ordered_map` using the given allocator.
|
||||
3. Creates an `ordered_map` from the elements in range `[first, last)`, inserted in iteration order.
|
||||
4. Creates an `ordered_map` from an initializer list of key/value pairs, inserted in list order.
|
||||
5. Copy constructor.
|
||||
6. Move constructor.
|
||||
|
||||
These constructors are declared explicitly (rather than inherited via `#!cpp using Container::Container`)
|
||||
because older compilers (GCC <= 5.5, Xcode <= 9.4) do not handle the inherited constructors correctly.
|
||||
|
||||
## Template parameters
|
||||
|
||||
`It`
|
||||
: an input iterator type
|
||||
|
||||
## Parameters
|
||||
|
||||
`alloc` (in)
|
||||
: allocator to use for the underlying container
|
||||
|
||||
`first` (in)
|
||||
: iterator to the first element to insert
|
||||
|
||||
`last` (in)
|
||||
: iterator one past the last element to insert
|
||||
|
||||
`init` (in)
|
||||
: initializer list of key/value pairs to insert
|
||||
|
||||
## Complexity
|
||||
|
||||
1. Constant.
|
||||
2. Constant.
|
||||
3. Linear in the distance between `first` and `last`.
|
||||
4. Linear in the size of `init`.
|
||||
5. Linear in the size of `other`.
|
||||
6. Constant.
|
||||
|
||||
<!-- NOLINT Examples -->
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
|
||||
@@ -1,17 +0,0 @@
|
||||
# <small>nlohmann::ordered_map::</small>~ordered_map
|
||||
|
||||
```cpp
|
||||
~ordered_map() = default;
|
||||
```
|
||||
|
||||
Destroys the `ordered_map` and frees all allocated memory.
|
||||
|
||||
## Complexity
|
||||
|
||||
Linear in the number of elements.
|
||||
|
||||
<!-- NOLINT Examples -->
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
|
||||
@@ -1,21 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
int main()
|
||||
{
|
||||
// an empty binary value is encoded differently by the two drafts:
|
||||
// draft2 omits the optimized type marker for an empty byte array,
|
||||
// while draft3 always writes it
|
||||
json j = json::binary({});
|
||||
|
||||
// encode using BJData draft2 (the default)
|
||||
auto v_draft2 = json::to_bjdata(j, true, true, json::bjdata_version_t::draft2);
|
||||
|
||||
// encode using BJData draft3
|
||||
auto v_draft3 = json::to_bjdata(j, true, true, json::bjdata_version_t::draft3);
|
||||
|
||||
std::cout << "draft2 size: " << v_draft2.size() << '\n'
|
||||
<< "draft3 size: " << v_draft3.size() << std::endl;
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
draft2 size: 4
|
||||
draft3 size: 6
|
||||
@@ -1,11 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using byte_container_with_subtype = nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>>;
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << std::boolalpha
|
||||
<< std::is_same<byte_container_with_subtype::container_type, std::vector<std::uint8_t>>::value
|
||||
<< std::endl;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
true
|
||||
@@ -1,10 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using byte_container_with_subtype = nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>>;
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << std::boolalpha
|
||||
<< std::is_same<byte_container_with_subtype::subtype_type, std::uint64_t>::value << std::endl;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
true
|
||||
@@ -1,13 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
int main()
|
||||
{
|
||||
// an initializer_list_t is what a braced-init-list of JSON values is deduced as
|
||||
json::initializer_list_t init = {"a", 1, 2.0, false};
|
||||
|
||||
json j(init);
|
||||
std::cout << j.dump() << std::endl;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
["a",1,2.0,false]
|
||||
@@ -1,10 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << std::boolalpha
|
||||
<< std::is_same<json::json_sax_t::binary_t, json::binary_t>::value << std::endl;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
true
|
||||
@@ -1,10 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << std::boolalpha
|
||||
<< std::is_same<json::json_sax_t::number_float_t, json::number_float_t>::value << std::endl;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
true
|
||||
@@ -1,10 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << std::boolalpha
|
||||
<< std::is_same<json::json_sax_t::number_integer_t, json::number_integer_t>::value << std::endl;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
true
|
||||
@@ -1,10 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << std::boolalpha
|
||||
<< std::is_same<json::json_sax_t::number_unsigned_t, json::number_unsigned_t>::value << std::endl;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
true
|
||||
@@ -1,10 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << std::boolalpha
|
||||
<< std::is_same<json::json_sax_t::string_t, json::string_t>::value << std::endl;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
true
|
||||
@@ -1,10 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << std::boolalpha
|
||||
<< std::is_same<json::json_sax_t, nlohmann::json_sax<json>>::value << std::endl;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
true
|
||||
@@ -1,12 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
int main()
|
||||
{
|
||||
using Map = nlohmann::ordered_map<std::string, int>;
|
||||
|
||||
std::cout << std::boolalpha
|
||||
<< "Container is std::vector<std::pair<const Key, T>>: "
|
||||
<< std::is_same<Map::Container, std::vector<std::pair<const std::string, int>>>::value
|
||||
<< std::endl;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
Container is std::vector<std::pair<const Key, T>>: true
|
||||
@@ -1,26 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
int main()
|
||||
{
|
||||
nlohmann::ordered_map<std::string, int> m;
|
||||
m["one"] = 1;
|
||||
m["two"] = 2;
|
||||
|
||||
// access an existing element
|
||||
std::cout << "m.at(\"one\") = " << m.at("one") << std::endl;
|
||||
|
||||
// modify through the reference returned by at()
|
||||
m.at("two") = 22;
|
||||
std::cout << "m.at(\"two\") = " << m.at("two") << std::endl;
|
||||
|
||||
// accessing a missing key throws
|
||||
try
|
||||
{
|
||||
m.at("three");
|
||||
}
|
||||
catch (const std::out_of_range& e)
|
||||
{
|
||||
std::cout << "exception: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
m.at("one") = 1
|
||||
m.at("two") = 22
|
||||
exception: key not found
|
||||
@@ -1,12 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
int main()
|
||||
{
|
||||
nlohmann::ordered_map<std::string, int> m;
|
||||
m["one"] = 1;
|
||||
|
||||
std::cout << std::boolalpha
|
||||
<< "m.count(\"one\") = " << m.count("one") << '\n'
|
||||
<< "m.count(\"two\") = " << m.count("two") << std::endl;
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
m.count("one") = 1
|
||||
m.count("two") = 0
|
||||
@@ -1,15 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
int main()
|
||||
{
|
||||
nlohmann::ordered_map<std::string, std::string> m;
|
||||
|
||||
// emplace a new element
|
||||
auto res1 = m.emplace("one", "eins");
|
||||
std::cout << std::boolalpha << "inserted: " << res1.second << ", value: " << res1.first->second << std::endl;
|
||||
|
||||
// emplace with an already-existing key: no-op, returns the existing element
|
||||
auto res2 = m.emplace("one", "uno");
|
||||
std::cout << std::boolalpha << "inserted: " << res2.second << ", value: " << res2.first->second << std::endl;
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
inserted: true, value: eins
|
||||
inserted: false, value: eins
|
||||
@@ -1,24 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
int main()
|
||||
{
|
||||
nlohmann::ordered_map<std::string, int> m;
|
||||
m["one"] = 1;
|
||||
m["two"] = 2;
|
||||
m["three"] = 3;
|
||||
|
||||
// erase by key
|
||||
std::size_t removed = m.erase("two");
|
||||
std::cout << "removed by key: " << removed << std::endl;
|
||||
|
||||
// erase by iterator
|
||||
m.erase(m.begin());
|
||||
|
||||
std::cout << "remaining: ";
|
||||
for (const auto& element : m)
|
||||
{
|
||||
std::cout << element.first << ' ';
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
removed by key: 1
|
||||
remaining: three
|
||||
@@ -1,19 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
int main()
|
||||
{
|
||||
nlohmann::ordered_map<std::string, int> m;
|
||||
m["one"] = 1;
|
||||
|
||||
auto it = m.find("one");
|
||||
if (it != m.end())
|
||||
{
|
||||
std::cout << "found: " << it->first << " = " << it->second << std::endl;
|
||||
}
|
||||
|
||||
if (m.find("two") == m.end())
|
||||
{
|
||||
std::cout << "\"two\" not found" << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
found: one = 1
|
||||
"two" not found
|
||||
@@ -1,21 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
int main()
|
||||
{
|
||||
nlohmann::ordered_map<std::string, int> m;
|
||||
|
||||
// insert a single value
|
||||
auto res = m.insert({"one", 1});
|
||||
std::cout << std::boolalpha << "inserted: " << res.second << std::endl;
|
||||
|
||||
// insert a range from another container
|
||||
std::vector<std::pair<const std::string, int>> more = {{"two", 2}, {"three", 3}};
|
||||
m.insert(more.begin(), more.end());
|
||||
|
||||
for (const auto& element : m)
|
||||
{
|
||||
std::cout << element.first << ':' << element.second << ' ';
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
inserted: true
|
||||
one:1 two:2 three:3
|
||||
@@ -1,12 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
int main()
|
||||
{
|
||||
using Map = nlohmann::ordered_map<std::string, int>;
|
||||
Map::key_compare compare{};
|
||||
|
||||
std::cout << std::boolalpha
|
||||
<< "compare(\"a\", \"a\") = " << compare("a", "a") << '\n'
|
||||
<< "compare(\"a\", \"b\") = " << compare("a", "b") << std::endl;
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
compare("a", "a") = true
|
||||
compare("a", "b") = false
|
||||
@@ -1,14 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
int main()
|
||||
{
|
||||
nlohmann::ordered_map<std::string, int> m;
|
||||
|
||||
// operator[] inserts a default-constructed value if the key doesn't exist yet
|
||||
m["one"] = 1;
|
||||
std::cout << "m[\"one\"] = " << m["one"] << std::endl;
|
||||
|
||||
// accessing again just returns the existing value
|
||||
std::cout << "m[\"one\"] = " << m["one"] << std::endl;
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
m["one"] = 1
|
||||
m["one"] = 1
|
||||
@@ -51,16 +51,16 @@ If you do want to preserve the **insertion order**, you can use the type [`nlohm
|
||||
--8<-- "examples/ordered_json.output"
|
||||
```
|
||||
|
||||
Alternatively, [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) also preserves the insertion order and, unlike [`ordered_map`](../api/ordered_map/index.md), keeps a lookup index, so it does not have the quadratic cost described below. It is used through a small adapter ([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)).
|
||||
Alternatively, [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) also preserves the insertion order and, unlike [`ordered_map`](../api/ordered_map.md), keeps a lookup index, so it does not have the quadratic cost described below. It is used through a small adapter ([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)).
|
||||
|
||||
If the order does not matter and you only want faster lookup, `boost::unordered_flat_map`, `absl::flat_hash_map`, `absl::node_hash_map`, and several other hash maps work through an adapter that restores the template argument order `basic_json` expects; see [Template Parameter Requirements](types/template_parameters.md#objecttype). Note these are *unordered*, not insertion-ordered.
|
||||
|
||||
[`tsl::ordered_map`](https://github.com/Tessil/ordered-map) cannot be used: its iterators expose the mapped value as `const`, while `basic_json` needs to modify it in place.
|
||||
|
||||
The [`ordered_map`](../api/ordered_map/index.md) behind `nlohmann::ordered_json` is deliberately minimal and has no lookup
|
||||
The [`ordered_map`](../api/ordered_map.md) behind `nlohmann::ordered_json` is deliberately minimal and has no lookup
|
||||
index, so every key access is a linear scan and building an object of `n` keys costs O(n²). This is unnoticeable at
|
||||
typical object sizes but becomes significant for objects with many thousands of keys; see
|
||||
[`ordered_map` complexity](../api/ordered_map/index.md#complexity). The alternatives above keep a lookup index and do not
|
||||
[`ordered_map` complexity](../api/ordered_map.md#complexity). The alternatives above keep a lookup index and do not
|
||||
have this cost.
|
||||
|
||||
### Notes on parsing
|
||||
|
||||
@@ -20,32 +20,39 @@ below apply on every supported standard. This adapter exposes two optimizations
|
||||
|
||||
- it can reconstruct already-consumed input on demand for error messages, instead of copying every character as it is
|
||||
read, and
|
||||
- the lexer can scan ordinary string characters directly out of the buffer, several bytes at a time, rather than one
|
||||
character (and one function call) at a time.
|
||||
- the lexer can scan ordinary string characters, numbers, and indentation directly out of the buffer, several bytes at
|
||||
a time, rather than one character (and one function call) at a time.
|
||||
|
||||
A `#!cpp std::istream` (including `#!cpp std::ifstream`) or `#!cpp FILE*`, by contrast, is read through
|
||||
`input_stream_adapter` or `file_input_adapter`, which read one character (or one block, for binary formats) at a time
|
||||
and expose neither optimization -- the lexer falls back to the same byte-at-a-time path it uses for any
|
||||
non-contiguous, general-purpose iterator range. An iterator pair over non-contiguous but random-access storage (e.g.
|
||||
`#!cpp std::deque<char>::iterator`) gets the first optimization but not the second, since the byte-scanning fast path
|
||||
additionally requires contiguous storage.
|
||||
A `#!cpp std::istream` (including `#!cpp std::ifstream`) gets the same bulk scanning: `input_stream_adapter` scans the
|
||||
characters its stream buffer already holds (the *get area*) and asks the stream buffer for more only when they are used
|
||||
up. Nothing is read ahead, so the stream is left exactly where it would be after reading one character at a time, and
|
||||
[`operator>>`](../api/operator_gtgt.md) can still read several values from one stream. A stream buffer without a get
|
||||
area is read one character at a time. A `#!cpp FILE*` is still read one character at a time, but `file_input_adapter`
|
||||
locks the file once per parse instead of once per character. See
|
||||
[input sources and performance](parsing/index.md#input-sources-and-performance) for the full table. According to the
|
||||
description of [#5828](https://github.com/nlohmann/json/pull/5828), parsing from a stream is within about 10 to 20 % of
|
||||
parsing an equivalent `#!cpp std::string`.
|
||||
|
||||
Practically: if the JSON text is already in memory, or small enough to read into memory, prefer passing a
|
||||
`#!cpp std::string`, a `#!cpp std::vector<char>`, or a pointer range to `parse` over a `#!cpp std::istream`. For a
|
||||
file, that means reading it into a string first and then parsing the string, rather than passing a
|
||||
`#!cpp std::ifstream` directly to `parse` -- the latter never benefits from either optimization:
|
||||
Inputs that are still read one character at a time: iterator ranges that are not contiguous (e.g.
|
||||
`#!cpp std::list<char>` or `#!cpp std::istreambuf_iterator<char>`), wide strings, and user-defined adapters. An
|
||||
iterator pair over non-contiguous but random-access storage (e.g. `#!cpp std::deque<char>::iterator`) gets the first
|
||||
optimization but not the second, since the byte-scanning fast path requires contiguous storage.
|
||||
|
||||
Practically: a file can be passed directly to `parse`, which also avoids the memory for a copy of the file. Reading it
|
||||
into a string first is no longer necessary for speed:
|
||||
|
||||
```cpp
|
||||
// gets the contiguous fast paths
|
||||
// fast: the stream buffer's get area is scanned in bulk
|
||||
std::ifstream f("example.json");
|
||||
std::string contents((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
|
||||
json j = json::parse(contents);
|
||||
|
||||
// does not: input_stream_adapter has no fast path
|
||||
std::ifstream f2("example.json");
|
||||
json j2 = json::parse(f2);
|
||||
json j = json::parse(f);
|
||||
```
|
||||
|
||||
Two macros switch the stream optimizations off without changing any result:
|
||||
[`JSON_DISABLE_STREAM_BULK_SCAN`](../api/macros/json_disable_stream_bulk_scan.md) for stream buffers that do not follow
|
||||
the usual `#!cpp std::streambuf` contract, and
|
||||
[`JSON_DISABLE_UNLOCKED_FILE_IO`](../api/macros/json_disable_unlocked_file_io.md) for programs in which another thread
|
||||
must use the same `#!cpp FILE` object while it is parsed.
|
||||
|
||||
For contiguous input with many non-ASCII characters, [`JSON_USE_SIMDUTF`](../api/macros/json_use_simdutf.md) can
|
||||
additionally speed up UTF-8 validation by using the [simdutf](https://github.com/simdutf/simdutf) library instead of
|
||||
the built-in scalar validator; streaming inputs (files, `#!cpp std::istream`, wide strings, user-defined adapters)
|
||||
@@ -94,10 +101,10 @@ whether it is worth the loss of human readability at all -- depends on the actua
|
||||
The default [`json`](../api/json.md) type stores object keys in a `#!cpp std::map`, giving logarithmic-time lookup,
|
||||
insertion, and erasure, at the cost of sorting keys alphabetically rather than preserving insertion order (see
|
||||
[Object Order](object_order.md)). [`ordered_json`](../api/ordered_json.md) uses
|
||||
[`nlohmann::ordered_map`](../api/ordered_map/index.md) instead, a `#!cpp std::vector`-backed container with no lookup index:
|
||||
[`nlohmann::ordered_map`](../api/ordered_map.md) instead, a `#!cpp std::vector`-backed container with no lookup index:
|
||||
every key-based operation is a **linear scan**, so building an object of `n` distinct keys costs **O(n²)** in total --
|
||||
this applies equally to inserting keys one by one and to parsing an object, since the parser inserts each key as it is
|
||||
read. The [measurements on the `ordered_map` page](../api/ordered_map/index.md#complexity) show this is
|
||||
read. The [measurements on the `ordered_map` page](../api/ordered_map.md#complexity) show this is
|
||||
negligible at typical object sizes (2000 keys: 0.7 ms for `json` vs. 3.6 ms for `ordered_json`, a 5x factor) but grows
|
||||
steeply for large, machine-generated objects (16 000 keys: 3.3 ms vs. 181.6 ms, a 54x factor).
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ Requirements are split into two groups:
|
||||
|
||||
| Template parameter | Default | Notable substitutes |
|
||||
|-------------------------------------------------------------------|-----------------------------------|--------------------------------------------------------------------------------------------------|
|
||||
| [`ObjectType`](#objecttype) | `std::map` | [`nlohmann::ordered_map`](../../api/ordered_map/index.md), Abseil hash maps |
|
||||
| [`ObjectType`](#objecttype) | `std::map` | [`nlohmann::ordered_map`](../../api/ordered_map.md), Abseil hash maps |
|
||||
| [`ArrayType`](#arraytype) | `std::vector` | `#!cpp std::deque` |
|
||||
| [`StringType`](#stringtype) | `std::string` | `std::string`-like types over `char` |
|
||||
| [`BooleanType`](#booleantype) | `bool` | none worth using |
|
||||
@@ -235,7 +235,7 @@ The library does not sort or de-duplicate keys itself; the behavior described in
|
||||
| Container | Notes |
|
||||
|----------------------------------------------------------------------------------|-------------------------------------------------------------------------------|
|
||||
| `#!cpp std::map` (default) | |
|
||||
| [`nlohmann::ordered_map`](../../api/ordered_map/index.md) | used by [`ordered_json`](../../api/ordered_json.md); keeps insertion order |
|
||||
| [`nlohmann::ordered_map`](../../api/ordered_map.md) | used by [`ordered_json`](../../api/ordered_json.md); keeps insertion order |
|
||||
| [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) | keeps insertion order; adapter puts `fifo_map_compare` in the comparator slot |
|
||||
| `boost::container::map`, `boost::container::flat_map` | no adapter needed |
|
||||
| `#!cpp std::unordered_map` | through the adapter above; not with libstdc++ 9, see the note |
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -50,7 +50,7 @@ The public headers are in [`include/nlohmann`](https://github.com/nlohmann/json/
|
||||
- [`adl_serializer.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/adl_serializer.hpp), [`byte_container_with_subtype.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/byte_container_with_subtype.hpp), and [`ordered_map.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/ordered_map.hpp) define
|
||||
[`adl_serializer`](../api/adl_serializer/index.md),
|
||||
[`byte_container_with_subtype`](../api/byte_container_with_subtype/index.md), and
|
||||
[`ordered_map`](../api/ordered_map/index.md).
|
||||
[`ordered_map`](../api/ordered_map.md).
|
||||
- [`json_view.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/json_view.hpp) is a separate,
|
||||
optional header that defines [`basic_json_document`](../api/basic_json_document/index.md) and
|
||||
[`basic_json_view`](../api/basic_json_view/index.md), a flat-index, read-only, non-owning way to look at a parsed
|
||||
@@ -104,7 +104,7 @@ from `json_view.hpp` and `detail/view/`.
|
||||
The library provides two specializations:
|
||||
|
||||
- [`json`](../api/json.md) uses all default template arguments.
|
||||
- [`ordered_json`](../api/ordered_json.md) uses [`ordered_map`](../api/ordered_map/index.md) as `ObjectType` to keep the
|
||||
- [`ordered_json`](../api/ordered_json.md) uses [`ordered_map`](../api/ordered_map.md) as `ObjectType` to keep the
|
||||
insertion order of object keys.
|
||||
|
||||
The requirements on the template arguments are listed in
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
This page summarizes the notable changes of every release and links to the relevant documentation.
|
||||
The **complete release notes** — including all changes, the download files, and their checksums — are
|
||||
published on the [GitHub releases page](https://github.com/nlohmann/json/releases). For a raw,
|
||||
signature-level diff of the public API between releases, see [API Changes](api_changes.md).
|
||||
published on the [GitHub releases page](https://github.com/nlohmann/json/releases).
|
||||
|
||||
!!! info "Unreleased changes"
|
||||
|
||||
|
||||
+1
-28
@@ -52,7 +52,6 @@ nav:
|
||||
- "FAQ": home/faq.md
|
||||
- home/exceptions.md
|
||||
- home/releases.md
|
||||
- home/api_changes.md
|
||||
- home/design_goals.md
|
||||
- home/architecture.md
|
||||
- home/customers.md
|
||||
@@ -125,7 +124,6 @@ nav:
|
||||
- 'begin': api/basic_json/begin.md
|
||||
- 'binary': api/basic_json/binary.md
|
||||
- 'binary_t': api/basic_json/binary_t.md
|
||||
- 'bjdata_version_t': api/basic_json/bjdata_version_t.md
|
||||
- 'boolean_t': api/basic_json/boolean_t.md
|
||||
- 'cbegin': api/basic_json/cbegin.md
|
||||
- 'cbor_tag_handler_t': api/basic_json/cbor_tag_handler_t.md
|
||||
@@ -164,7 +162,6 @@ nav:
|
||||
- 'get_to': api/basic_json/get_to.md
|
||||
- 'std::formatter<basic_json>': api/basic_json/std_formatter.md
|
||||
- 'std::hash<basic_json>': api/basic_json/std_hash.md
|
||||
- 'initializer_list_t': api/basic_json/initializer_list_t.md
|
||||
- 'input_format_t': api/basic_json/input_format_t.md
|
||||
- 'insert': api/basic_json/insert.md
|
||||
- 'invalid_iterator': api/basic_json/invalid_iterator.md
|
||||
@@ -183,7 +180,6 @@ nav:
|
||||
- 'is_structured': api/basic_json/is_structured.md
|
||||
- 'items': api/basic_json/items.md
|
||||
- 'json_base_class_t': api/basic_json/json_base_class_t.md
|
||||
- 'json_sax_t': api/basic_json/json_sax_t.md
|
||||
- 'json_serializer': api/basic_json/json_serializer.md
|
||||
- 'max_size': api/basic_json/max_size.md
|
||||
- 'meta': api/basic_json/meta.md
|
||||
@@ -307,13 +303,11 @@ nav:
|
||||
- 'Overview': api/byte_container_with_subtype/index.md
|
||||
- '(constructor)': api/byte_container_with_subtype/byte_container_with_subtype.md
|
||||
- 'clear_subtype': api/byte_container_with_subtype/clear_subtype.md
|
||||
- 'container_type': api/byte_container_with_subtype/container_type.md
|
||||
- 'has_subtype': api/byte_container_with_subtype/has_subtype.md
|
||||
- 'operator==': api/byte_container_with_subtype/operator_eq.md
|
||||
- 'operator!=': api/byte_container_with_subtype/operator_ne.md
|
||||
- 'set_subtype': api/byte_container_with_subtype/set_subtype.md
|
||||
- 'subtype': api/byte_container_with_subtype/subtype.md
|
||||
- 'subtype_type': api/byte_container_with_subtype/subtype_type.md
|
||||
- adl_serializer:
|
||||
- 'Overview': api/adl_serializer/index.md
|
||||
- 'from_json': api/adl_serializer/from_json.md
|
||||
@@ -343,27 +337,19 @@ nav:
|
||||
- 'to_string': api/json_pointer/to_string.md
|
||||
- json_sax:
|
||||
- 'Overview': api/json_sax/index.md
|
||||
- '(Constructor)': api/json_sax/json_sax.md
|
||||
- '(Destructor)': api/json_sax/~json_sax.md
|
||||
- 'operator=': api/json_sax/operator=.md
|
||||
- 'binary': api/json_sax/binary.md
|
||||
- 'binary_t': api/json_sax/binary_t.md
|
||||
- 'boolean': api/json_sax/boolean.md
|
||||
- 'end_array': api/json_sax/end_array.md
|
||||
- 'end_object': api/json_sax/end_object.md
|
||||
- 'key': api/json_sax/key.md
|
||||
- 'null': api/json_sax/null.md
|
||||
- 'number_float': api/json_sax/number_float.md
|
||||
- 'number_float_t': api/json_sax/number_float_t.md
|
||||
- 'number_integer': api/json_sax/number_integer.md
|
||||
- 'number_integer_t': api/json_sax/number_integer_t.md
|
||||
- 'number_unsigned': api/json_sax/number_unsigned.md
|
||||
- 'number_unsigned_t': api/json_sax/number_unsigned_t.md
|
||||
- 'parse_error': api/json_sax/parse_error.md
|
||||
- 'start_array': api/json_sax/start_array.md
|
||||
- 'start_object': api/json_sax/start_object.md
|
||||
- 'string': api/json_sax/string.md
|
||||
- 'string_t': api/json_sax/string_t.md
|
||||
- 'json_view': api/json_view.md
|
||||
- 'operator<<(basic_json), operator<<(json_pointer)': api/operator_ltlt.md
|
||||
- 'operator>>(basic_json)': api/operator_gtgt.md
|
||||
@@ -374,20 +360,7 @@ nav:
|
||||
- 'ordered_json_editable_document': api/ordered_json_editable_document.md
|
||||
- 'ordered_json_editable_view': api/ordered_json_editable_view.md
|
||||
- 'ordered_json_view': api/ordered_json_view.md
|
||||
- ordered_map:
|
||||
- 'Overview': api/ordered_map/index.md
|
||||
- '(Constructor)': api/ordered_map/ordered_map.md
|
||||
- '(Destructor)': api/ordered_map/~ordered_map.md
|
||||
- 'operator=': api/ordered_map/operator=.md
|
||||
- 'at': api/ordered_map/at.md
|
||||
- 'Container': api/ordered_map/Container.md
|
||||
- 'count': api/ordered_map/count.md
|
||||
- 'emplace': api/ordered_map/emplace.md
|
||||
- 'erase': api/ordered_map/erase.md
|
||||
- 'find': api/ordered_map/find.md
|
||||
- 'insert': api/ordered_map/insert.md
|
||||
- 'key_compare': api/ordered_map/key_compare.md
|
||||
- 'operator[]': api/ordered_map/operator[].md
|
||||
- 'ordered_map': api/ordered_map.md
|
||||
- macros:
|
||||
- 'Overview': api/macros/index.md
|
||||
- 'JSON_ASSERT': api/macros/json_assert.md
|
||||
|
||||
@@ -22,9 +22,7 @@ template<typename BinaryType>
|
||||
class byte_container_with_subtype : public BinaryType
|
||||
{
|
||||
public:
|
||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/container_type/
|
||||
using container_type = BinaryType;
|
||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/subtype_type/
|
||||
using subtype_type = std::uint64_t;
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
|
||||
@@ -56,14 +54,12 @@ class byte_container_with_subtype : public BinaryType
|
||||
, m_has_subtype(true)
|
||||
{}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/operator_eq/
|
||||
bool operator==(const byte_container_with_subtype& rhs) const
|
||||
{
|
||||
return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) ==
|
||||
std::tie(static_cast<const BinaryType&>(rhs), rhs.m_subtype, rhs.m_has_subtype);
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/operator_ne/
|
||||
bool operator!=(const byte_container_with_subtype& rhs) const
|
||||
{
|
||||
return !(rhs == *this);
|
||||
|
||||
@@ -14,11 +14,24 @@
|
||||
#include <intrin0.h> // __umulh, _umul128, _BitScanForward64, _BitScanReverse64
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <cstdlib> // _byteswap_ushort, _byteswap_ulong, _byteswap_uint64
|
||||
#endif
|
||||
#if defined(_MSC_VER) && !defined(__clang__) && (defined(_M_IX86) || defined(_M_ARM))
|
||||
#include <intrin.h> // _BitScanForward, _BitScanReverse
|
||||
#endif
|
||||
|
||||
#include <nlohmann/detail/macro_scope.hpp> // JSON_HEDLEY_ALWAYS_INLINE, NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(__cpp_lib_bit_cast) || defined(__cpp_lib_bitops) || defined(__cpp_lib_byteswap)
|
||||
#include <bit> // bit_cast, byteswap, countl_zero, countr_zero
|
||||
#endif
|
||||
|
||||
// Portable bit-level helpers for the number and string scanners. They use
|
||||
// compiler builtins or platform-specific intrinsics where available and plain
|
||||
// C++ otherwise, so they work regardless of byte order.
|
||||
// compiler builtins or platform-specific intrinsics where available, the
|
||||
// C++20 <bit> functions next, and plain C++ otherwise, so they work regardless
|
||||
// of byte order. (The builtins and intrinsics come first: MSVC's
|
||||
// std::countl_zero checks for LZCNT at run time unless AVX2 is enabled.)
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
@@ -33,6 +46,16 @@ inline int count_leading_zeros(std::uint64_t x) noexcept
|
||||
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan*64 takes
|
||||
_BitScanReverse64(&index, x);
|
||||
return 63 - static_cast<int>(index);
|
||||
#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_ARM))
|
||||
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan* takes
|
||||
if (_BitScanReverse(&index, static_cast<unsigned long>(x >> 32u))) // NOLINT(runtime/int)
|
||||
{
|
||||
return 31 - static_cast<int>(index);
|
||||
}
|
||||
_BitScanReverse(&index, static_cast<unsigned long>(x)); // NOLINT(runtime/int)
|
||||
return 63 - static_cast<int>(index);
|
||||
#elif defined(__cpp_lib_bitops)
|
||||
return std::countl_zero(x);
|
||||
#else
|
||||
int n = 0;
|
||||
for (int shift = 32; shift != 0; shift >>= 1)
|
||||
@@ -56,6 +79,16 @@ inline int count_trailing_zeros(std::uint64_t x) noexcept
|
||||
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan*64 takes
|
||||
_BitScanForward64(&index, x);
|
||||
return static_cast<int>(index);
|
||||
#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_ARM))
|
||||
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan* takes
|
||||
if (_BitScanForward(&index, static_cast<unsigned long>(x))) // NOLINT(runtime/int)
|
||||
{
|
||||
return static_cast<int>(index);
|
||||
}
|
||||
_BitScanForward(&index, static_cast<unsigned long>(x >> 32u)); // NOLINT(runtime/int)
|
||||
return 32 + static_cast<int>(index);
|
||||
#elif defined(__cpp_lib_bitops)
|
||||
return std::countr_zero(x);
|
||||
#else
|
||||
int n = 0;
|
||||
for (int shift = 32; shift != 0; shift >>= 1)
|
||||
@@ -70,6 +103,67 @@ inline int count_trailing_zeros(std::uint64_t x) noexcept
|
||||
#endif
|
||||
}
|
||||
|
||||
/// the object representation of @a from as a @a To (std::bit_cast in C++20)
|
||||
template<typename To, typename From>
|
||||
JSON_HEDLEY_ALWAYS_INLINE To bit_cast(const From& from) noexcept
|
||||
{
|
||||
static_assert(sizeof(To) == sizeof(From), "bit_cast needs types of the same size");
|
||||
#if defined(__cpp_lib_bit_cast)
|
||||
return std::bit_cast<To>(from);
|
||||
#else
|
||||
To to;
|
||||
std::memcpy(&to, &from, sizeof(To));
|
||||
return to;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// x with its bytes in reverse order (std::byteswap in C++23); compilers lower
|
||||
/// it to a single bswap/rev/movbe
|
||||
inline std::uint16_t byteswap(std::uint16_t x) noexcept
|
||||
{
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_bswap16(x);
|
||||
#elif defined(_MSC_VER)
|
||||
return _byteswap_ushort(x);
|
||||
#elif defined(__cpp_lib_byteswap)
|
||||
return std::byteswap(x);
|
||||
#else
|
||||
return static_cast<std::uint16_t>((x >> 8u) | (x << 8u));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// x with its bytes in reverse order (std::byteswap in C++23)
|
||||
inline std::uint32_t byteswap(std::uint32_t x) noexcept
|
||||
{
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_bswap32(x);
|
||||
#elif defined(_MSC_VER)
|
||||
return _byteswap_ulong(x);
|
||||
#elif defined(__cpp_lib_byteswap)
|
||||
return std::byteswap(x);
|
||||
#else
|
||||
return ((x & 0x000000FFu) << 24u) | ((x & 0x0000FF00u) << 8u)
|
||||
| ((x & 0x00FF0000u) >> 8u) | ((x & 0xFF000000u) >> 24u);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// x with its bytes in reverse order (std::byteswap in C++23)
|
||||
inline std::uint64_t byteswap(std::uint64_t x) noexcept
|
||||
{
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_bswap64(x);
|
||||
#elif defined(_MSC_VER)
|
||||
return _byteswap_uint64(x);
|
||||
#elif defined(__cpp_lib_byteswap)
|
||||
return std::byteswap(x);
|
||||
#else
|
||||
x = ((x & 0x00000000FFFFFFFFull) << 32u) | ((x & 0xFFFFFFFF00000000ull) >> 32u);
|
||||
x = ((x & 0x0000FFFF0000FFFFull) << 16u) | ((x & 0xFFFF0000FFFF0000ull) >> 16u);
|
||||
x = ((x & 0x00FF00FF00FF00FFull) << 8u) | ((x & 0xFF00FF00FF00FF00ull) >> 8u);
|
||||
return x;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// the 128-bit product of two 64-bit numbers
|
||||
struct uint128_parts
|
||||
{
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
#include <limits> // numeric_limits
|
||||
#include <type_traits> // conditional
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <cstdlib> // _byteswap_uint64
|
||||
#endif
|
||||
|
||||
// SSE2 (every x86-64 CPU) and NEON (every 64-bit Arm CPU) convert the 16
|
||||
// digits of a double at once
|
||||
@@ -37,6 +34,7 @@
|
||||
#define JSON_DTOA_NEON 0
|
||||
#endif
|
||||
|
||||
#include <nlohmann/detail/bit_ops.hpp>
|
||||
#include <nlohmann/detail/conversions/zmij.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
|
||||
@@ -66,16 +64,6 @@ For a detailed description of the algorithm see:
|
||||
namespace dtoa_impl
|
||||
{
|
||||
|
||||
template<typename Target, typename Source>
|
||||
Target reinterpret_bits(const Source source)
|
||||
{
|
||||
static_assert(sizeof(Target) == sizeof(Source), "size mismatch");
|
||||
|
||||
Target target;
|
||||
std::memcpy(&target, &source, sizeof(Source));
|
||||
return target;
|
||||
}
|
||||
|
||||
struct diyfp // f * 2^e
|
||||
{
|
||||
static constexpr int kPrecision = 64; // = q
|
||||
@@ -230,7 +218,7 @@ boundaries compute_boundaries(FloatType value)
|
||||
|
||||
using bits_type = typename std::conditional<kPrecision == 24, std::uint32_t, std::uint64_t >::type;
|
||||
|
||||
const auto bits = static_cast<std::uint64_t>(reinterpret_bits<bits_type>(value));
|
||||
const auto bits = static_cast<std::uint64_t>(bit_cast<bits_type>(value));
|
||||
const std::uint64_t E = bits >> (kPrecision - 1);
|
||||
const std::uint64_t F = bits & (kHiddenBit - 1);
|
||||
|
||||
@@ -1084,14 +1072,11 @@ inline std::uint64_t eight_digit_bytes(std::uint64_t abcdefgh) noexcept
|
||||
/// the byte stores once this is inlined)
|
||||
inline void store_msb_first(char* p, std::uint64_t v) noexcept
|
||||
{
|
||||
#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
v = __builtin_bswap64(v);
|
||||
#if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || defined(_MSC_VER) // (MSVC: little-endian on all its targets)
|
||||
v = byteswap(v);
|
||||
std::memcpy(p, &v, sizeof(v));
|
||||
#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
std::memcpy(p, &v, sizeof(v));
|
||||
#elif defined(_MSC_VER) // (little-endian on all its targets)
|
||||
v = _byteswap_uint64(v);
|
||||
std::memcpy(p, &v, sizeof(v));
|
||||
#else
|
||||
for (unsigned i = 0; i < 8; ++i)
|
||||
{
|
||||
@@ -1462,8 +1447,7 @@ char* write_positive_zmij(char* first, const char* last, FloatType value)
|
||||
{
|
||||
static_assert(is_binary64<FloatType>::value,
|
||||
"internal error: the conversion of Zmij needs IEEE 754 binary64 numbers");
|
||||
std::uint64_t bits = 0;
|
||||
std::memcpy(&bits, &value, sizeof(bits));
|
||||
const auto bits = bit_cast<std::uint64_t>(value);
|
||||
const zmij::shortest_decimal d = zmij::to_shortest(bits);
|
||||
if (JSON_HEDLEY_LIKELY(last - first >= 41))
|
||||
{
|
||||
|
||||
@@ -34,21 +34,15 @@ input.
|
||||
template<typename BasicJsonType>
|
||||
struct json_sax
|
||||
{
|
||||
/// @sa https://json.nlohmann.me/api/json_sax/number_integer_t/
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
/// @sa https://json.nlohmann.me/api/json_sax/number_unsigned_t/
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
/// @sa https://json.nlohmann.me/api/json_sax/number_float_t/
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
/// @sa https://json.nlohmann.me/api/json_sax/string_t/
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
/// @sa https://json.nlohmann.me/api/json_sax/binary_t/
|
||||
using binary_t = typename BasicJsonType::binary_t;
|
||||
|
||||
/*!
|
||||
@brief a null value was read
|
||||
@return whether parsing should proceed
|
||||
@sa https://json.nlohmann.me/api/json_sax/null/
|
||||
*/
|
||||
virtual bool null() = 0;
|
||||
|
||||
@@ -56,7 +50,6 @@ struct json_sax
|
||||
@brief a boolean value was read
|
||||
@param[in] val boolean value
|
||||
@return whether parsing should proceed
|
||||
@sa https://json.nlohmann.me/api/json_sax/boolean/
|
||||
*/
|
||||
virtual bool boolean(bool val) = 0;
|
||||
|
||||
@@ -64,7 +57,6 @@ struct json_sax
|
||||
@brief an integer number was read
|
||||
@param[in] val integer value
|
||||
@return whether parsing should proceed
|
||||
@sa https://json.nlohmann.me/api/json_sax/number_integer/
|
||||
*/
|
||||
virtual bool number_integer(number_integer_t val) = 0;
|
||||
|
||||
@@ -72,7 +64,6 @@ struct json_sax
|
||||
@brief an unsigned integer number was read
|
||||
@param[in] val unsigned integer value
|
||||
@return whether parsing should proceed
|
||||
@sa https://json.nlohmann.me/api/json_sax/number_unsigned/
|
||||
*/
|
||||
virtual bool number_unsigned(number_unsigned_t val) = 0;
|
||||
|
||||
@@ -81,7 +72,6 @@ struct json_sax
|
||||
@param[in] val floating-point value
|
||||
@param[in] s raw token value
|
||||
@return whether parsing should proceed
|
||||
@sa https://json.nlohmann.me/api/json_sax/number_float/
|
||||
*/
|
||||
virtual bool number_float(number_float_t val, const string_t& s) = 0;
|
||||
|
||||
@@ -90,7 +80,6 @@ struct json_sax
|
||||
@param[in] val string value
|
||||
@return whether parsing should proceed
|
||||
@note It is safe to move the passed string value.
|
||||
@sa https://json.nlohmann.me/api/json_sax/string/
|
||||
*/
|
||||
virtual bool string(string_t& val) = 0;
|
||||
|
||||
@@ -99,7 +88,6 @@ struct json_sax
|
||||
@param[in] val binary value
|
||||
@return whether parsing should proceed
|
||||
@note It is safe to move the passed binary value.
|
||||
@sa https://json.nlohmann.me/api/json_sax/binary/
|
||||
*/
|
||||
virtual bool binary(binary_t& val) = 0;
|
||||
|
||||
@@ -108,7 +96,6 @@ struct json_sax
|
||||
@param[in] elements number of object elements or -1 if unknown
|
||||
@return whether parsing should proceed
|
||||
@note binary formats may report the number of elements
|
||||
@sa https://json.nlohmann.me/api/json_sax/start_object/
|
||||
*/
|
||||
virtual bool start_object(std::size_t elements) = 0;
|
||||
|
||||
@@ -117,14 +104,12 @@ struct json_sax
|
||||
@param[in] val object key
|
||||
@return whether parsing should proceed
|
||||
@note It is safe to move the passed string.
|
||||
@sa https://json.nlohmann.me/api/json_sax/key/
|
||||
*/
|
||||
virtual bool key(string_t& val) = 0;
|
||||
|
||||
/*!
|
||||
@brief the end of an object was read
|
||||
@return whether parsing should proceed
|
||||
@sa https://json.nlohmann.me/api/json_sax/end_object/
|
||||
*/
|
||||
virtual bool end_object() = 0;
|
||||
|
||||
@@ -133,14 +118,12 @@ struct json_sax
|
||||
@param[in] elements number of array elements or -1 if unknown
|
||||
@return whether parsing should proceed
|
||||
@note binary formats may report the number of elements
|
||||
@sa https://json.nlohmann.me/api/json_sax/start_array/
|
||||
*/
|
||||
virtual bool start_array(std::size_t elements) = 0;
|
||||
|
||||
/*!
|
||||
@brief the end of an array was read
|
||||
@return whether parsing should proceed
|
||||
@sa https://json.nlohmann.me/api/json_sax/end_array/
|
||||
*/
|
||||
virtual bool end_array() = 0;
|
||||
|
||||
@@ -150,23 +133,16 @@ struct json_sax
|
||||
@param[in] last_token the last read token
|
||||
@param[in] ex an exception object describing the error
|
||||
@return whether parsing should proceed (must return false)
|
||||
@sa https://json.nlohmann.me/api/json_sax/parse_error/
|
||||
*/
|
||||
virtual bool parse_error(std::size_t position,
|
||||
const std::string& last_token,
|
||||
const detail::exception& ex) = 0;
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/json_sax/json_sax/
|
||||
json_sax() = default;
|
||||
/// @sa https://json.nlohmann.me/api/json_sax/json_sax/
|
||||
json_sax(const json_sax&) = default;
|
||||
/// @sa https://json.nlohmann.me/api/json_sax/json_sax/
|
||||
json_sax(json_sax&&) noexcept = default;
|
||||
/// @sa https://json.nlohmann.me/api/json_sax/operator=/
|
||||
json_sax& operator=(const json_sax&) = default;
|
||||
/// @sa https://json.nlohmann.me/api/json_sax/operator=/
|
||||
json_sax& operator=(json_sax&&) noexcept = default;
|
||||
/// @sa https://json.nlohmann.me/api/json_sax/~json_sax/
|
||||
virtual ~json_sax() = default;
|
||||
};
|
||||
|
||||
|
||||
@@ -1625,9 +1625,12 @@ scan_number_done:
|
||||
const std::size_t avail = rem + 1;
|
||||
|
||||
// validate + classify the number extent (mirrors scan_number()'s grammar)
|
||||
// and accumulate the mantissa digits on the way, so that tokens with up
|
||||
// to 19 digits need no second pass for their value
|
||||
std::size_t i = 0;
|
||||
std::size_t dot_index = std::string::npos;
|
||||
token_type number_type = token_type::value_unsigned;
|
||||
std::uint64_t w = 0;
|
||||
if (data[0] == '-')
|
||||
{
|
||||
number_type = token_type::value_integer;
|
||||
@@ -1637,22 +1640,21 @@ scan_number_done:
|
||||
return token_type::uninitialized;
|
||||
}
|
||||
}
|
||||
const std::size_t integer_begin = i;
|
||||
if (data[i] == '0')
|
||||
{
|
||||
++i;
|
||||
}
|
||||
else if (data[i] >= '1' && data[i] <= '9')
|
||||
{
|
||||
++i;
|
||||
while (i < avail && data[i] >= '0' && data[i] <= '9')
|
||||
{
|
||||
++i;
|
||||
}
|
||||
i = accumulate_digits(data, i, avail, w);
|
||||
}
|
||||
else
|
||||
{
|
||||
return token_type::uninitialized;
|
||||
}
|
||||
std::size_t digit_count = i - integer_begin;
|
||||
std::size_t fraction_digits = 0;
|
||||
if (i < avail && data[i] == '.')
|
||||
{
|
||||
number_type = token_type::value_float;
|
||||
@@ -1662,29 +1664,39 @@ scan_number_done:
|
||||
{
|
||||
return token_type::uninitialized;
|
||||
}
|
||||
while (i < avail && data[i] >= '0' && data[i] <= '9')
|
||||
{
|
||||
++i;
|
||||
}
|
||||
const std::size_t fraction_begin = i;
|
||||
i = accumulate_digits(data, i, avail, w);
|
||||
fraction_digits = i - fraction_begin;
|
||||
digit_count += fraction_digits;
|
||||
}
|
||||
// the mantissa ends here, whether or not an exponent part follows
|
||||
const std::size_t mantissa_end = i;
|
||||
std::int64_t exponent = 0;
|
||||
if (i < avail && (data[i] == 'e' || data[i] == 'E'))
|
||||
{
|
||||
number_type = token_type::value_float;
|
||||
++i;
|
||||
bool negative_exponent = false;
|
||||
if (i < avail && (data[i] == '+' || data[i] == '-'))
|
||||
{
|
||||
negative_exponent = data[i] == '-';
|
||||
++i;
|
||||
}
|
||||
if (i >= avail || !(data[i] >= '0' && data[i] <= '9'))
|
||||
{
|
||||
return token_type::uninitialized;
|
||||
}
|
||||
// saturated far beyond every range, see parse_float_exponent()
|
||||
constexpr std::int64_t saturation = 100000000000000000; // 10^17
|
||||
while (i < avail && data[i] >= '0' && data[i] <= '9')
|
||||
{
|
||||
if (exponent < saturation)
|
||||
{
|
||||
exponent = (exponent * 10) + (data[i] - '0');
|
||||
}
|
||||
++i;
|
||||
}
|
||||
exponent = negative_exponent ? -exponent : exponent;
|
||||
}
|
||||
const std::size_t len = i;
|
||||
if (number_may_continue(len == avail, std::integral_constant<bool, bulk_window> {}))
|
||||
@@ -1705,7 +1717,10 @@ scan_number_done:
|
||||
#if !JSON_DIAGNOSTIC_POSITIONS
|
||||
if (number_type != token_type::value_float)
|
||||
{
|
||||
const token_type integer_result = convert_integer(number_type, data, data + len);
|
||||
// up to 18 digits always fit into std::uint64_t and std::int64_t
|
||||
const token_type integer_result = (digit_count <= 18)
|
||||
? store_integer(number_type, w)
|
||||
: convert_integer(number_type, data, data + len);
|
||||
if (JSON_HEDLEY_LIKELY(integer_result != token_type::uninitialized))
|
||||
{
|
||||
bulk_consume(data + 1, len - 1);
|
||||
@@ -1719,15 +1734,42 @@ scan_number_done:
|
||||
}
|
||||
#endif
|
||||
|
||||
// materialize the token exactly as scan_number() would. reset() already
|
||||
// cleared token_buffer, so append() fills it (assign() is avoided
|
||||
// because custom string_t types need not provide it)
|
||||
token_buffer.append(reinterpret_cast<const typename string_t::value_type*>(data), len);
|
||||
decimal_point_position = dot_index;
|
||||
|
||||
bulk_consume(data + 1, len - 1);
|
||||
read_number_terminator(std::integral_constant<bool, bulk_window> {});
|
||||
|
||||
// a float whose digits all fit into w converts from the accumulated
|
||||
// value; longer ones (and other float formats) take the general path
|
||||
if (number_type == token_type::value_float && digit_count <= 19)
|
||||
{
|
||||
float_significand s;
|
||||
s.w = w;
|
||||
s.exponent = exponent - static_cast<std::int64_t>(fraction_digits);
|
||||
s.negative = data[0] == '-';
|
||||
if (convert_accumulated_float(s, std::integral_constant<bool, has_native_float_format<number_float_t>::value> {}))
|
||||
{
|
||||
// the token text is only copied into token_buffer when
|
||||
// get_string() asks for it; the library's own SAX handlers
|
||||
// ignore it. A window into a stream may be refilled by the
|
||||
// next read, so there the text is copied right away.
|
||||
if (bulk_window)
|
||||
{
|
||||
token_buffer.append(reinterpret_cast<const typename string_t::value_type*>(data), len);
|
||||
}
|
||||
else
|
||||
{
|
||||
pending_number_text = data;
|
||||
pending_number_length = len;
|
||||
}
|
||||
return token_type::value_float;
|
||||
}
|
||||
}
|
||||
|
||||
// materialize the token exactly as scan_number() would. reset() already
|
||||
// cleared token_buffer, so append() fills it (assign() is avoided
|
||||
// because custom string_t types need not provide it)
|
||||
token_buffer.append(reinterpret_cast<const typename string_t::value_type*>(data), len);
|
||||
return convert_number(number_type, mantissa_end);
|
||||
}
|
||||
|
||||
@@ -1756,6 +1798,35 @@ scan_number_done:
|
||||
/// the input is not handed back, so the terminator can stay unread
|
||||
void read_number_terminator(std::false_type /*window*/) const noexcept {}
|
||||
|
||||
/// binary32/binary64: the value of a float token from its accumulated
|
||||
/// significand (see scan_number_bulk_contiguous())
|
||||
bool convert_accumulated_float(const float_significand& s, std::true_type /*native*/) noexcept
|
||||
{
|
||||
value_float = decimal_to_float<number_float_t>(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// other formats (e.g. x87 long double): not instantiated for them, as
|
||||
/// decimal_to_float() only supports binary32/binary64
|
||||
static bool convert_accumulated_float(const float_significand& /*s*/, std::false_type /*native*/) noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// the value of an integer token with at most 18 digits, accumulated in @a w
|
||||
token_type store_integer(token_type number_type, std::uint64_t w) noexcept
|
||||
{
|
||||
if (number_type == token_type::value_unsigned)
|
||||
{
|
||||
value_unsigned = static_cast<number_unsigned_t>(w);
|
||||
// reject values that do not round-trip into a narrower type
|
||||
return (static_cast<std::uint64_t>(value_unsigned) == w) ? token_type::value_unsigned : token_type::uninitialized;
|
||||
}
|
||||
const std::int64_t x = -static_cast<std::int64_t>(w);
|
||||
value_integer = static_cast<number_integer_t>(x);
|
||||
return (static_cast<std::int64_t>(value_integer) == x) ? token_type::value_integer : token_type::uninitialized;
|
||||
}
|
||||
|
||||
/// contiguous input: try the number fast path, else the byte-path scanner
|
||||
token_type scan_number_dispatch(std::true_type /*bulk*/)
|
||||
{
|
||||
@@ -1798,6 +1869,7 @@ scan_number_done:
|
||||
void reset() noexcept
|
||||
{
|
||||
token_buffer.clear();
|
||||
pending_number_text = nullptr;
|
||||
decimal_point_position = std::string::npos;
|
||||
|
||||
#if JSON_DIAGNOSTIC_POSITIONS
|
||||
@@ -2057,10 +2129,30 @@ scan_number_done:
|
||||
/// return current string value
|
||||
string_t& get_string()
|
||||
{
|
||||
if (pending_number_text != nullptr)
|
||||
{
|
||||
token_buffer.append(reinterpret_cast<const typename string_t::value_type*>(pending_number_text), pending_number_length);
|
||||
pending_number_text = nullptr;
|
||||
}
|
||||
// a number token holds '.' regardless of the locale (#4084)
|
||||
return token_buffer;
|
||||
}
|
||||
|
||||
/// the value of a string token (key or string), which never has pending
|
||||
/// number text, so get_string()'s check is not needed
|
||||
string_t& get_string_token() noexcept
|
||||
{
|
||||
JSON_ASSERT(pending_number_text == nullptr);
|
||||
return token_buffer;
|
||||
}
|
||||
|
||||
/// the text of a float token for a SAX handler that ignores it (the
|
||||
/// library's own handlers): empty, without copying the token
|
||||
const string_t& get_ignored_number_text() const noexcept
|
||||
{
|
||||
return token_buffer;
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// diagnostics
|
||||
/////////////////////
|
||||
@@ -2456,6 +2548,11 @@ scan_number_done:
|
||||
/// whether the next get() call should just return current
|
||||
bool next_unget = false;
|
||||
|
||||
/// the text of the current float token in the input, not yet copied into
|
||||
/// token_buffer (see get_string()), or nullptr
|
||||
const char* pending_number_text = nullptr;
|
||||
std::size_t pending_number_length = 0;
|
||||
|
||||
/// the start position of the current token
|
||||
position_t position {};
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // int64_t, uint64_t
|
||||
#include <cstdlib> // strtof, strtod, strtold
|
||||
#include <cstring> // memcpy
|
||||
#include <limits> // numeric_limits
|
||||
#include <string> // string
|
||||
#include <type_traits> // conditional, integral_constant, is_signed, true_type, false_type
|
||||
@@ -275,13 +274,77 @@ JSON_HEDLEY_ALWAYS_INLINE std::uint32_t parse_eight_digits(std::uint64_t v) noex
|
||||
return static_cast<std::uint32_t>(((v & 0x0000FFFF0000FFFFu) * 42949672960001u) >> 32u);
|
||||
}
|
||||
|
||||
/// whether [first, last) contains a digit other than '0'
|
||||
inline bool has_nonzero_digit(const char* first, const char* last) noexcept
|
||||
/// whether the eight bytes in @a v (see read_eight_bytes()) are all ASCII
|
||||
/// digits (after fast_float)
|
||||
JSON_HEDLEY_ALWAYS_INLINE bool is_eight_digits(std::uint64_t v) noexcept
|
||||
{
|
||||
return std::any_of(first, last, [](char c) noexcept
|
||||
return ((v & 0xF0F0F0F0F0F0F0F0u) | (((v + 0x0606060606060606u) & 0xF0F0F0F0F0F0F0F0u) >> 4u)) == 0x3333333333333333u;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief skip the ASCII digits starting at data[i] and accumulate their value
|
||||
|
||||
Eight digits are taken at a time while at least eight bytes remain. @a w wraps
|
||||
around after 19 digits; the caller only uses it for shorter digit runs.
|
||||
|
||||
@return the index of the first byte at or after @a i that is not a digit
|
||||
*/
|
||||
JSON_HEDLEY_ALWAYS_INLINE std::size_t accumulate_digits(const char* data, std::size_t i, std::size_t avail, std::uint64_t& w) noexcept
|
||||
{
|
||||
while (avail - i >= 8)
|
||||
{
|
||||
return c != '0';
|
||||
});
|
||||
const std::uint64_t v = read_eight_bytes(data + i);
|
||||
if (!is_eight_digits(v))
|
||||
{
|
||||
break;
|
||||
}
|
||||
w = (w * 100000000u) + parse_eight_digits(v);
|
||||
i += 8;
|
||||
}
|
||||
while (i < avail && data[i] >= '0' && data[i] <= '9')
|
||||
{
|
||||
w = (w * 10u) + static_cast<std::uint64_t>(data[i] - '0');
|
||||
++i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/// whether [first, last) contains a digit other than '0'; a plain loop, as
|
||||
/// GCC calls std::any_of out of line even for the usual empty range
|
||||
JSON_HEDLEY_ALWAYS_INLINE bool has_nonzero_digit(const char* first, const char* last) noexcept
|
||||
{
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
if (*first != '0')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief accumulate the validated digits [p, last) into @a w, as long as
|
||||
@a remaining digits still fit into it
|
||||
|
||||
Eight digits are taken at a time while at least eight remain, and @a remaining
|
||||
is decreased by the number of digits taken.
|
||||
|
||||
@return a pointer past the last digit taken
|
||||
*/
|
||||
JSON_HEDLEY_ALWAYS_INLINE const char* accumulate_significant_digits(const char* p, const char* last, int& remaining, std::uint64_t& w) noexcept
|
||||
{
|
||||
while (remaining >= 8 && last - p >= 8)
|
||||
{
|
||||
w = (w * 100000000u) + parse_eight_digits(read_eight_bytes(p));
|
||||
p += 8;
|
||||
remaining -= 8;
|
||||
}
|
||||
for (; remaining > 0 && p != last; ++p, --remaining)
|
||||
{
|
||||
w = (w * 10u) + static_cast<std::uint64_t>(*p - '0');
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/// the value of the validated exponent digits [+-]?[0-9]+ in [first, last),
|
||||
@@ -327,7 +390,7 @@ truncated is set.
|
||||
std::string::npos if there is none
|
||||
@param[in] mantissa_end index of the 'e'/'E', or the token length
|
||||
*/
|
||||
inline float_significand parse_float_significand(const char* first, const char* last,
|
||||
JSON_HEDLEY_ALWAYS_INLINE float_significand parse_float_significand(const char* first, const char* last,
|
||||
std::size_t decimal_point_position, std::size_t mantissa_end) noexcept
|
||||
{
|
||||
float_significand s;
|
||||
@@ -342,16 +405,7 @@ inline float_significand parse_float_significand(const char* first, const char*
|
||||
int remaining = 19; // digits that still fit into w
|
||||
if (*p != '0') // the integer part is "0" or [1-9][0-9]*
|
||||
{
|
||||
while (remaining >= 8 && integer_last - p >= 8)
|
||||
{
|
||||
w = (w * 100000000u) + parse_eight_digits(read_eight_bytes(p));
|
||||
p += 8;
|
||||
remaining -= 8;
|
||||
}
|
||||
for (; remaining > 0 && p != integer_last; ++p, --remaining)
|
||||
{
|
||||
w = (w * 10u) + static_cast<std::uint64_t>(*p - '0');
|
||||
}
|
||||
p = accumulate_significant_digits(p, integer_last, remaining, w);
|
||||
s.exponent = integer_last - p;
|
||||
s.truncated = has_nonzero_digit(p, integer_last);
|
||||
}
|
||||
@@ -370,16 +424,7 @@ inline float_significand parse_float_significand(const char* first, const char*
|
||||
s.exponent -= p - zeros;
|
||||
}
|
||||
const char* const digits = p;
|
||||
while (remaining >= 8 && mantissa_last - p >= 8)
|
||||
{
|
||||
w = (w * 100000000u) + parse_eight_digits(read_eight_bytes(p));
|
||||
p += 8;
|
||||
remaining -= 8;
|
||||
}
|
||||
for (; remaining > 0 && p != mantissa_last; ++p, --remaining)
|
||||
{
|
||||
w = (w * 10u) + static_cast<std::uint64_t>(*p - '0');
|
||||
}
|
||||
p = accumulate_significant_digits(p, mantissa_last, remaining, w);
|
||||
s.exponent -= p - digits;
|
||||
s.truncated = s.truncated || has_nonzero_digit(p, mantissa_last);
|
||||
}
|
||||
@@ -712,15 +757,14 @@ std::uint64_t digit_comparison(const char* first, const char* last, std::uint64_
|
||||
inline void float_from_bits(std::uint64_t bits, double& value) noexcept
|
||||
{
|
||||
static_assert(sizeof(double) == sizeof(std::uint64_t), "double must have 64 bits");
|
||||
std::memcpy(&value, &bits, sizeof(value));
|
||||
value = bit_cast<double>(bits);
|
||||
}
|
||||
|
||||
/// the float with the IEEE-754 bits @a bits (the lower 32)
|
||||
inline void float_from_bits(std::uint64_t bits, float& value) noexcept
|
||||
{
|
||||
static_assert(sizeof(float) == sizeof(std::uint32_t), "float must have 32 bits");
|
||||
const auto bits32 = static_cast<std::uint32_t>(bits);
|
||||
std::memcpy(&value, &bits32, sizeof(bits32));
|
||||
value = bit_cast<float>(static_cast<std::uint32_t>(bits));
|
||||
}
|
||||
|
||||
/// the powers of ten that are exact in binary64 (up to 10^22)
|
||||
|
||||
@@ -51,6 +51,20 @@ template<typename BasicJsonType>
|
||||
using parser_callback_t =
|
||||
std::function<bool(int /*depth*/, parse_event_t /*event*/, BasicJsonType& /*parsed*/)>;
|
||||
|
||||
/// whether a SAX handler ignores the text of float tokens (the library's own
|
||||
/// handlers do), so that the parser need not materialize it
|
||||
template<typename SAX>
|
||||
struct sax_ignores_number_text : std::false_type {};
|
||||
|
||||
template<typename BasicJsonType, typename InputAdapterType>
|
||||
struct sax_ignores_number_text<json_sax_dom_parser<BasicJsonType, InputAdapterType>> : std::true_type {};
|
||||
|
||||
template<typename BasicJsonType, typename InputAdapterType>
|
||||
struct sax_ignores_number_text<json_sax_dom_callback_parser<BasicJsonType, InputAdapterType>> : std::true_type {};
|
||||
|
||||
template<typename BasicJsonType>
|
||||
struct sax_ignores_number_text<json_sax_acceptor<BasicJsonType>> : std::true_type {};
|
||||
|
||||
/*!
|
||||
@brief syntax analysis
|
||||
|
||||
@@ -171,6 +185,23 @@ class parser
|
||||
}
|
||||
|
||||
private:
|
||||
/// the text of the current float token for @a sax (see sax_ignores_number_text)
|
||||
template<typename SAX>
|
||||
const string_t& number_float_text(SAX* /*sax*/)
|
||||
{
|
||||
return number_float_text(std::integral_constant<bool, sax_ignores_number_text<SAX>::value> {});
|
||||
}
|
||||
|
||||
const string_t& number_float_text(std::true_type /*ignored*/) const noexcept
|
||||
{
|
||||
return m_lexer.get_ignored_number_text();
|
||||
}
|
||||
|
||||
string_t& number_float_text(std::false_type /*ignored*/)
|
||||
{
|
||||
return m_lexer.get_string();
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief run a DOM SAX parser to completion and position the lexer
|
||||
|
||||
@@ -247,7 +278,7 @@ class parser
|
||||
{
|
||||
return syntax_error(*sax, exception_message(token_type::value_string, "object key"));
|
||||
}
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string_token())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -301,7 +332,7 @@ class parser
|
||||
out_of_range::create(exception_id::number_overflow, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr));
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, number_float_text(sax))))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -347,7 +378,7 @@ class parser
|
||||
|
||||
case token_type::value_string:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string())))
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string_token())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -456,7 +487,7 @@ class parser
|
||||
return syntax_error(*sax, exception_message(token_type::value_string, "object key"));
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string_token())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -64,7 +64,6 @@ class json_pointer
|
||||
|
||||
public:
|
||||
// for backwards compatibility accept BasicJsonType
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/string_t/
|
||||
using string_t = typename string_t_helper<RefStringType>::type;
|
||||
|
||||
/// @brief create JSON pointer
|
||||
|
||||
@@ -10,17 +10,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef> // size_t
|
||||
#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type
|
||||
#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_arithmetic, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type
|
||||
#include <utility> // index_sequence, make_index_sequence, index_sequence_for
|
||||
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
|
||||
#ifdef JSON_HAS_CPP_17
|
||||
#include <algorithm> // clamp
|
||||
#endif
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
||||
#ifdef __cpp_lib_remove_cvref
|
||||
template<typename T>
|
||||
using uncvref_t = std::remove_cvref_t<T>;
|
||||
#else
|
||||
template<typename T>
|
||||
using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
||||
#endif
|
||||
|
||||
#ifdef JSON_HAS_CPP_14
|
||||
|
||||
@@ -160,5 +169,18 @@ struct static_const
|
||||
constexpr T static_const<T>::value;
|
||||
#endif
|
||||
|
||||
/// @a v bounded to [@a lo, @a hi] (requires !(hi < lo)); like std::clamp,
|
||||
/// but by value, so that static constexpr members can be passed without an
|
||||
/// out-of-line definition before C++17
|
||||
template<typename T, enable_if_t<std::is_arithmetic<T>::value, int> = 0>
|
||||
constexpr T clamp(T v, T lo, T hi) noexcept
|
||||
{
|
||||
#ifdef __cpp_lib_clamp
|
||||
return std::clamp(v, lo, hi);
|
||||
#else
|
||||
return v < lo ? lo : (hi < v ? hi : v);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -313,6 +313,14 @@ struct char_traits<std::byte> : std::char_traits<char>
|
||||
// is_ functions //
|
||||
///////////////////
|
||||
|
||||
#ifdef __cpp_lib_logical_traits
|
||||
|
||||
using std::conjunction;
|
||||
using std::disjunction;
|
||||
using std::negation;
|
||||
|
||||
#else
|
||||
|
||||
// https://en.cppreference.com/w/cpp/types/conjunction
|
||||
template<class...> struct conjunction : std::true_type { };
|
||||
template<class B> struct conjunction<B> : B { };
|
||||
@@ -330,6 +338,8 @@ struct disjunction<B, Bn...>
|
||||
// https://en.cppreference.com/w/cpp/types/negation
|
||||
template<class B> struct negation : std::integral_constant < bool, !B::value > { };
|
||||
|
||||
#endif
|
||||
|
||||
// Reimplementation of is_constructible and is_default_constructible, due to them being broken for
|
||||
// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367).
|
||||
// This causes compile errors in e.g., Clang 3.5 or GCC 4.9.
|
||||
@@ -1060,8 +1070,12 @@ constexpr bool value_in_range_of(T val)
|
||||
return value_in_range_of_impl1<OfType, T>::test(val);
|
||||
}
|
||||
|
||||
template<bool Value>
|
||||
using bool_constant = std::integral_constant<bool, Value>;
|
||||
#ifdef __cpp_lib_bool_constant
|
||||
using std::bool_constant;
|
||||
#else
|
||||
template<bool Value>
|
||||
using bool_constant = std::integral_constant<bool, Value>;
|
||||
#endif
|
||||
|
||||
// Stands in for number_unsigned_t in overload sets if it is the same type as
|
||||
// number_integer_t, so that the unsigned overloads do not collide with the
|
||||
|
||||
@@ -19,10 +19,6 @@
|
||||
#include <utility> // move
|
||||
#include <vector> // vector
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <cstdlib> // _byteswap_ushort, _byteswap_ulong, _byteswap_uint64
|
||||
#endif
|
||||
|
||||
#include <nlohmann/detail/bit_ops.hpp>
|
||||
#include <nlohmann/detail/input/binary_reader.hpp>
|
||||
#include <nlohmann/detail/input/string_scan.hpp>
|
||||
@@ -2073,7 +2069,7 @@ class binary_writer
|
||||
{
|
||||
out[0] = to_char_type(first_byte);
|
||||
const unsigned unused_bits = 64u - ((8u << size_class) >> 1u);
|
||||
const std::uint64_t tail = is_little_endian ? byte_swap(value) >> unused_bits : value << unused_bits;
|
||||
const std::uint64_t tail = is_little_endian ? byteswap(value) >> unused_bits : value << unused_bits;
|
||||
std::memcpy(out + 1, &tail, sizeof(tail));
|
||||
return ((std::size_t{1} << size_class) >> 1u) + 1;
|
||||
}
|
||||
@@ -3517,53 +3513,14 @@ class binary_writer
|
||||
// Utility functions //
|
||||
///////////////////////
|
||||
|
||||
// single-instruction byte swaps (compilers lower these to bswap/rev/movbe);
|
||||
// used to emit big-endian numbers without a per-byte std::reverse loop
|
||||
static std::uint16_t byte_swap(std::uint16_t x) noexcept
|
||||
{
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_bswap16(x);
|
||||
#elif defined(_MSC_VER)
|
||||
return _byteswap_ushort(x);
|
||||
#else
|
||||
return static_cast<std::uint16_t>((x >> 8) | (x << 8));
|
||||
#endif
|
||||
}
|
||||
|
||||
static std::uint32_t byte_swap(std::uint32_t x) noexcept
|
||||
{
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_bswap32(x);
|
||||
#elif defined(_MSC_VER)
|
||||
return _byteswap_ulong(x);
|
||||
#else
|
||||
return ((x & 0x000000FFu) << 24) | ((x & 0x0000FF00u) << 8)
|
||||
| ((x & 0x00FF0000u) >> 8) | ((x & 0xFF000000u) >> 24);
|
||||
#endif
|
||||
}
|
||||
|
||||
static std::uint64_t byte_swap(std::uint64_t x) noexcept
|
||||
{
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_bswap64(x);
|
||||
#elif defined(_MSC_VER)
|
||||
return _byteswap_uint64(x);
|
||||
#else
|
||||
x = ((x & 0x00000000FFFFFFFFull) << 32) | ((x & 0xFFFFFFFF00000000ull) >> 32);
|
||||
x = ((x & 0x0000FFFF0000FFFFull) << 16) | ((x & 0xFFFF0000FFFF0000ull) >> 16);
|
||||
x = ((x & 0x00FF00FF00FF00FFull) << 8) | ((x & 0xFF00FF00FF00FF00ull) >> 8);
|
||||
return x;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reverse the bytes of a buffer by byte-swapping it as UIntType
|
||||
|
||||
Loading the buffer into an unsigned integer of the same width and swapping
|
||||
that is what lets the compiler emit a single bswap/rev/movbe; reversing the
|
||||
buffer element by element does not reliably get there (clang keeps a scalar
|
||||
shuffle). The two memcpy calls are the only portable way to reinterpret the
|
||||
bytes and are folded away by every optimizer.
|
||||
shuffle). (The two memcpy calls, rather than bit_cast, keep GCC from
|
||||
inlining differently in C++20, which made writing slower.)
|
||||
*/
|
||||
template<typename UIntType, std::size_t N>
|
||||
static void byte_swap_buffer(std::array<CharType, N>& a) noexcept
|
||||
@@ -3571,11 +3528,11 @@ class binary_writer
|
||||
static_assert(sizeof(UIntType) == N, "swap width must match the buffer size");
|
||||
UIntType v{};
|
||||
std::memcpy(&v, a.data(), sizeof(v));
|
||||
v = byte_swap(v);
|
||||
v = byteswap(v);
|
||||
std::memcpy(a.data(), &v, sizeof(v));
|
||||
}
|
||||
|
||||
// reverse the bytes of a fixed-size buffer; a single byte_swap() for the
|
||||
// reverse the bytes of a fixed-size buffer; a single byteswap() for the
|
||||
// common 2/4/8-byte number payloads, std::reverse for any other size
|
||||
static void reverse_bytes(std::array<CharType, 2>& a) noexcept
|
||||
{
|
||||
@@ -3700,9 +3657,7 @@ class binary_writer
|
||||
#endif
|
||||
|
||||
static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t");
|
||||
CharType result;
|
||||
std::memcpy(&result, &x, sizeof(x));
|
||||
return result;
|
||||
return bit_cast<CharType>(x);
|
||||
}
|
||||
|
||||
template<typename C = CharType,
|
||||
|
||||
@@ -105,17 +105,13 @@ class output_buffer
|
||||
}
|
||||
|
||||
private:
|
||||
/// the size of a growth step (a function: std::min() takes a reference,
|
||||
/// which a static constexpr member does not have before C++17)
|
||||
static constexpr std::size_t step() noexcept
|
||||
{
|
||||
return 0x10000;
|
||||
}
|
||||
/// the size of a growth step
|
||||
static constexpr std::size_t step = 0x10000;
|
||||
|
||||
static StringType& sized(StringType& out, std::size_t estimate)
|
||||
{
|
||||
out.reserve(estimate);
|
||||
out.resize((std::min)((std::max)(estimate, static_cast<std::size_t>(64)), step()));
|
||||
out.resize(detail::clamp(estimate, std::size_t{64}, step));
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -124,7 +120,7 @@ class output_buffer
|
||||
const auto used = static_cast<std::size_t>(m_pos - m_out.data());
|
||||
// (a step does not go beyond the reserved estimate, so that a good
|
||||
// estimate is never copied to a larger allocation)
|
||||
const std::size_t size = (std::max)((std::min)(m_out.size() + step(), m_out.capacity()), used + n + 256);
|
||||
const std::size_t size = (std::max)((std::min)(m_out.size() + step, m_out.capacity()), used + n + 256);
|
||||
if (size > m_out.capacity())
|
||||
{
|
||||
m_out.reserve((std::max)(m_out.capacity() * 2, size));
|
||||
@@ -794,8 +790,7 @@ class view_serializer
|
||||
static char* write_double_value_at(char* w, double x)
|
||||
{
|
||||
// (from the bits: without the checks of to_chars())
|
||||
std::uint64_t bits = 0;
|
||||
std::memcpy(&bits, &x, sizeof(bits));
|
||||
auto bits = bit_cast<std::uint64_t>(x);
|
||||
if (NLOHMANN_VIEW_UNLIKELY((bits & 0x7FF0000000000000u) == 0x7FF0000000000000u))
|
||||
{
|
||||
return write_text_at(w, "null", 4);
|
||||
|
||||
@@ -212,34 +212,25 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
using serializer = ::nlohmann::detail::serializer<basic_json>;
|
||||
|
||||
public:
|
||||
/// @brief the type of the JSON value
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/value_t/
|
||||
using value_t = detail::value_t;
|
||||
/// JSON Pointer, see @ref nlohmann::json_pointer
|
||||
using json_pointer = ::nlohmann::json_pointer<StringType>;
|
||||
template<typename T, typename SFINAE>
|
||||
using json_serializer = JSONSerializer<T, SFINAE>;
|
||||
/// how to treat decoding errors
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/error_handler_t/
|
||||
using error_handler_t = detail::error_handler_t;
|
||||
/// how to treat CBOR tags
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/cbor_tag_handler_t/
|
||||
using cbor_tag_handler_t = detail::cbor_tag_handler_t;
|
||||
/// how to encode BJData
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/bjdata_version_t/
|
||||
using bjdata_version_t = detail::bjdata_version_t;
|
||||
/// base class used to inject custom functionality into each instance of basic_json
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/json_base_class_t/
|
||||
using json_base_class_t = ::nlohmann::detail::json_base_class<CustomBaseClass>;
|
||||
/// helper type for initializer lists of basic_json values
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/initializer_list_t/
|
||||
using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
|
||||
|
||||
/// @brief the type of the SAX interface used to parse and serialize the JSON value
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/input_format_t/
|
||||
using input_format_t = detail::input_format_t;
|
||||
/// SAX interface type, see @ref nlohmann::json_sax
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/json_sax_t/
|
||||
using json_sax_t = json_sax<basic_json>;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -445,19 +436,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// the template arguments passed to class @ref basic_json.
|
||||
/// @{
|
||||
|
||||
#if defined(JSON_HAS_CPP_14)
|
||||
/// @brief default object key comparator type
|
||||
/// The actual object key comparator type (@ref object_comparator_t) may be
|
||||
/// different.
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/default_object_comparator_t/
|
||||
#if defined(JSON_HAS_CPP_14)
|
||||
// use of transparent comparator avoids unnecessary repeated construction of temporaries
|
||||
// in functions involving lookup by key with types other than object_t::key_type (aka. StringType)
|
||||
using default_object_comparator_t = std::less<>;
|
||||
#else
|
||||
/// @brief default object key comparator type
|
||||
/// The actual object key comparator type (@ref object_comparator_t) may be
|
||||
/// different.
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/default_object_comparator_t/
|
||||
using default_object_comparator_t = std::less<StringType>;
|
||||
#endif
|
||||
|
||||
@@ -2677,7 +2664,6 @@ public:
|
||||
// other constructors and destructor //
|
||||
///////////////////////////////////////
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
|
||||
template<typename JsonRef,
|
||||
detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>,
|
||||
std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 >
|
||||
@@ -3320,8 +3306,6 @@ public:
|
||||
@throw what @ref json_serializer<ValueType> `from_json()` method throws if conversion is required
|
||||
|
||||
@since version 2.1.0
|
||||
|
||||
@sa https://json.nlohmann.me/api/basic_json/get/
|
||||
*/
|
||||
template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>>
|
||||
#if defined(JSON_HAS_CPP_14)
|
||||
@@ -3365,8 +3349,6 @@ public:
|
||||
@sa see @ref get_ptr() for explicit pointer-member access
|
||||
|
||||
@since version 1.0.0
|
||||
|
||||
@sa https://json.nlohmann.me/api/basic_json/get/
|
||||
*/
|
||||
template<typename PointerType, typename std::enable_if<
|
||||
std::is_pointer<PointerType>::value, int>::type = 0>
|
||||
@@ -3393,7 +3375,6 @@ public:
|
||||
|
||||
// specialization to allow calling get_to with a basic_json value
|
||||
// see https://github.com/nlohmann/json/issues/2175
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/get_to/
|
||||
template<typename ValueType,
|
||||
detail::enable_if_t <
|
||||
detail::is_basic_json<ValueType>::value,
|
||||
@@ -3404,7 +3385,6 @@ public:
|
||||
return v;
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/get_to/
|
||||
template <
|
||||
typename T, std::size_t N,
|
||||
typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
@@ -3469,7 +3449,6 @@ public:
|
||||
|
||||
@since version 1.0.0
|
||||
*/
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_ValueType/
|
||||
template < typename ValueType, typename std::enable_if <
|
||||
detail::conjunction <
|
||||
detail::negation<std::is_pointer<ValueType>>,
|
||||
@@ -3805,14 +3784,12 @@ public:
|
||||
|
||||
// these two functions resolve a (const) char * ambiguity affecting Clang and MSVC
|
||||
// (they seemingly cannot be constrained to resolve the ambiguity)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator[]/
|
||||
template<typename T>
|
||||
reference operator[](T* key)
|
||||
{
|
||||
return operator[](typename object_t::key_type(key));
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator[]/
|
||||
template<typename T>
|
||||
const_reference operator[](T* key) const
|
||||
{
|
||||
@@ -3991,7 +3968,6 @@ public:
|
||||
return found != nullptr ? found->template get<ReturnType>() : std::forward<ValueType>(default_value);
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/value/
|
||||
template < class ValueType, class BasicJsonType, detail::enable_if_t <
|
||||
detail::is_basic_json<BasicJsonType>::value
|
||||
&& detail::is_getable<basic_json_t, ValueType>::value
|
||||
@@ -4006,7 +3982,6 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/value/
|
||||
template < class ValueType, class BasicJsonType, class ReturnType = typename value_return_type<ValueType>::type,
|
||||
detail::enable_if_t <
|
||||
detail::is_basic_json<BasicJsonType>::value
|
||||
@@ -4374,7 +4349,6 @@ public:
|
||||
return ptr.contains(this);
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/contains/
|
||||
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
|
||||
@@ -6073,7 +6047,6 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/parse/
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len))
|
||||
static basic_json parse(detail::span_input_adapter&& i,
|
||||
@@ -6115,7 +6088,6 @@ public:
|
||||
return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true);
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/accept/
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len))
|
||||
static bool accept(detail::span_input_adapter&& i,
|
||||
@@ -6540,7 +6512,6 @@ public:
|
||||
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::cbor, strict, allow_exceptions, error_handler, tag_handler);
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
|
||||
template<typename T>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
|
||||
@@ -6556,7 +6527,6 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
|
||||
static basic_json from_cbor(detail::span_input_adapter&& i,
|
||||
@@ -6596,7 +6566,6 @@ public:
|
||||
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::msgpack, strict, allow_exceptions, error_handler);
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
|
||||
template<typename T>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
|
||||
@@ -6611,7 +6580,6 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
|
||||
static basic_json from_msgpack(detail::span_input_adapter&& i,
|
||||
@@ -6650,7 +6618,6 @@ public:
|
||||
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::ubjson, strict, allow_exceptions, error_handler);
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
|
||||
template<typename T>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
|
||||
@@ -6665,7 +6632,6 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
|
||||
static basic_json from_ubjson(detail::span_input_adapter&& i,
|
||||
@@ -6780,7 +6746,6 @@ public:
|
||||
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::bson, strict, allow_exceptions, error_handler);
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
|
||||
template<typename T>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
|
||||
@@ -6795,7 +6760,6 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
|
||||
static basic_json from_bson(detail::span_input_adapter&& i,
|
||||
@@ -6824,7 +6788,6 @@ public:
|
||||
return ptr.get_unchecked(this);
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
|
||||
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
|
||||
reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr)
|
||||
@@ -6843,7 +6806,6 @@ public:
|
||||
return ptr.get_unchecked(this);
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
|
||||
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
|
||||
const_reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
|
||||
@@ -6862,7 +6824,6 @@ public:
|
||||
return ptr.get_checked(this);
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/at/
|
||||
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
|
||||
reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr)
|
||||
@@ -6881,7 +6842,6 @@ public:
|
||||
return ptr.get_checked(this);
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/at/
|
||||
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
|
||||
const_reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
|
||||
|
||||
@@ -35,41 +35,30 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
|
||||
{
|
||||
using key_type = Key;
|
||||
using mapped_type = T;
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/Container/
|
||||
using Container = std::vector<std::pair<const Key, T>, Allocator>;
|
||||
using iterator = typename Container::iterator;
|
||||
using const_iterator = typename Container::const_iterator;
|
||||
using size_type = typename Container::size_type;
|
||||
using value_type = typename Container::value_type;
|
||||
#ifdef JSON_HAS_CPP_14
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/key_compare/
|
||||
using key_compare = std::equal_to<>;
|
||||
#else
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/key_compare/
|
||||
using key_compare = std::equal_to<Key>;
|
||||
#endif
|
||||
|
||||
// Explicit constructors instead of `using Container::Container`
|
||||
// otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4)
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
|
||||
ordered_map() noexcept(noexcept(Container())) : Container{} {}
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
|
||||
explicit ordered_map(const Allocator& alloc) noexcept(noexcept(Container(alloc))) : Container{alloc} {}
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
|
||||
template <class It>
|
||||
ordered_map(It first, It last, const Allocator& alloc = Allocator())
|
||||
: Container{first, last, alloc} {}
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
|
||||
ordered_map(std::initializer_list<value_type> init, const Allocator& alloc = Allocator() )
|
||||
: Container{init, alloc} {}
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
|
||||
ordered_map(const ordered_map&) = default;
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
|
||||
ordered_map(ordered_map&&) noexcept(std::is_nothrow_move_constructible<Container>::value) = default;
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/~ordered_map/
|
||||
~ordered_map() = default;
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/operator=/
|
||||
ordered_map& operator=(const ordered_map& other)
|
||||
{
|
||||
if (this != &other)
|
||||
@@ -80,7 +69,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/operator=/
|
||||
ordered_map& operator=(ordered_map&& other) noexcept(std::is_nothrow_move_assignable<Container>::value)
|
||||
{
|
||||
Container::operator=(std::move(static_cast<Container&>(other)));
|
||||
@@ -138,7 +126,6 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/emplace/
|
||||
template<class V, detail::enable_if_t<
|
||||
detail::is_constructible<T, V>::value, int> = 0>
|
||||
std::pair<iterator, bool> emplace(const key_type& key, V && t)
|
||||
@@ -152,7 +139,6 @@ public:
|
||||
return {std::prev(this->end()), true};
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/emplace/
|
||||
template<class KeyType, class V, detail::enable_if_t<
|
||||
detail::conjunction<detail::is_usable_as_key_type<key_compare, key_type, KeyType>,
|
||||
detail::is_constructible<T, V>>::value, int> = 0>
|
||||
@@ -178,13 +164,11 @@ public:
|
||||
append(key, std::forward<V>(t));
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/operator[]/
|
||||
T& operator[](const key_type& key)
|
||||
{
|
||||
return emplace(key, T{}).first->second;
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/operator[]/
|
||||
template<class KeyType, detail::enable_if_t<
|
||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||
T & operator[](KeyType && key)
|
||||
@@ -192,13 +176,11 @@ public:
|
||||
return emplace(std::forward<KeyType>(key), T{}).first->second;
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/operator[]/
|
||||
const T& operator[](const key_type& key) const
|
||||
{
|
||||
return at(key);
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/operator[]/
|
||||
template<class KeyType, detail::enable_if_t<
|
||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||
const T & operator[](KeyType && key) const
|
||||
@@ -206,13 +188,11 @@ public:
|
||||
return at(std::forward<KeyType>(key));
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/at/
|
||||
T& at(const key_type& key)
|
||||
{
|
||||
return at_impl(*this, key);
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/at/
|
||||
template<class KeyType, detail::enable_if_t<
|
||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||
T & at(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||
@@ -220,13 +200,11 @@ public:
|
||||
return at_impl(*this, key);
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/at/
|
||||
const T& at(const key_type& key) const
|
||||
{
|
||||
return at_impl(*this, key);
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/at/
|
||||
template<class KeyType, detail::enable_if_t<
|
||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||
const T & at(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||
@@ -234,7 +212,6 @@ public:
|
||||
return at_impl(*this, key);
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
|
||||
size_type erase(const key_type& key)
|
||||
{
|
||||
const auto it = find_impl(*this, key);
|
||||
@@ -246,7 +223,6 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
|
||||
template<class KeyType, detail::enable_if_t<
|
||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||
size_type erase(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||
@@ -260,13 +236,11 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
|
||||
iterator erase(iterator pos)
|
||||
{
|
||||
return erase(pos, std::next(pos));
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
|
||||
iterator erase(iterator first, iterator last)
|
||||
{
|
||||
if (first == last)
|
||||
@@ -331,13 +305,11 @@ public:
|
||||
return Container::begin() + offset;
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/count/
|
||||
size_type count(const key_type& key) const
|
||||
{
|
||||
return find_impl(*this, key) != this->end() ? 1 : 0;
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/count/
|
||||
template<class KeyType, detail::enable_if_t<
|
||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||
size_type count(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||
@@ -345,13 +317,11 @@ public:
|
||||
return find_impl(*this, key) != this->end() ? 1 : 0;
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/find/
|
||||
iterator find(const key_type& key)
|
||||
{
|
||||
return find_impl(*this, key);
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/find/
|
||||
template<class KeyType, detail::enable_if_t<
|
||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||
iterator find(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||
@@ -359,13 +329,11 @@ public:
|
||||
return find_impl(*this, key);
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/find/
|
||||
const_iterator find(const key_type& key) const
|
||||
{
|
||||
return find_impl(*this, key);
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/find/
|
||||
template<class KeyType, detail::enable_if_t<
|
||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||
const_iterator find(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||
@@ -373,13 +341,11 @@ public:
|
||||
return find_impl(*this, key);
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/insert/
|
||||
std::pair<iterator, bool> insert( value_type&& value )
|
||||
{
|
||||
return emplace(value.first, std::move(value.second));
|
||||
}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/insert/
|
||||
std::pair<iterator, bool> insert( const value_type& value )
|
||||
{
|
||||
const auto it = find_impl(*this, value.first);
|
||||
@@ -395,7 +361,6 @@ public:
|
||||
using require_input_iter = typename std::enable_if<std::is_convertible<typename std::iterator_traits<InputIt>::iterator_category,
|
||||
std::input_iterator_tag>::value>::type;
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/insert/
|
||||
template<typename InputIt, typename = require_input_iter<InputIt>>
|
||||
void insert(InputIt first, InputIt last)
|
||||
{
|
||||
|
||||
Generated
+370
-232
File diff suppressed because it is too large.
Load diff
Generated
+5
-10
@@ -6166,17 +6166,13 @@ class output_buffer
|
||||
}
|
||||
|
||||
private:
|
||||
/// the size of a growth step (a function: std::min() takes a reference,
|
||||
/// which a static constexpr member does not have before C++17)
|
||||
static constexpr std::size_t step() noexcept
|
||||
{
|
||||
return 0x10000;
|
||||
}
|
||||
/// the size of a growth step
|
||||
static constexpr std::size_t step = 0x10000;
|
||||
|
||||
static StringType& sized(StringType& out, std::size_t estimate)
|
||||
{
|
||||
out.reserve(estimate);
|
||||
out.resize((std::min)((std::max)(estimate, static_cast<std::size_t>(64)), step()));
|
||||
out.resize(detail::clamp(estimate, std::size_t{64}, step));
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -6185,7 +6181,7 @@ class output_buffer
|
||||
const auto used = static_cast<std::size_t>(m_pos - m_out.data());
|
||||
// (a step does not go beyond the reserved estimate, so that a good
|
||||
// estimate is never copied to a larger allocation)
|
||||
const std::size_t size = (std::max)((std::min)(m_out.size() + step(), m_out.capacity()), used + n + 256);
|
||||
const std::size_t size = (std::max)((std::min)(m_out.size() + step, m_out.capacity()), used + n + 256);
|
||||
if (size > m_out.capacity())
|
||||
{
|
||||
m_out.reserve((std::max)(m_out.capacity() * 2, size));
|
||||
@@ -6855,8 +6851,7 @@ class view_serializer
|
||||
static char* write_double_value_at(char* w, double x)
|
||||
{
|
||||
// (from the bits: without the checks of to_chars())
|
||||
std::uint64_t bits = 0;
|
||||
std::memcpy(&bits, &x, sizeof(bits));
|
||||
auto bits = bit_cast<std::uint64_t>(x);
|
||||
if (NLOHMANN_VIEW_UNLIKELY((bits & 0x7FF0000000000000u) == 0x7FF0000000000000u))
|
||||
{
|
||||
return write_text_at(w, "null", 4);
|
||||
|
||||
@@ -22,6 +22,15 @@ Micro-benchmarks for parsing, serialization and the binary formats, written with
|
||||
| `ViewAccept` | validate with `json_document::accept`; compare with `Accept` |
|
||||
| `ViewMaterialize` | convert a parsed `json_document` into a `json` value |
|
||||
| `ViewDump` | serialize a parsed `json_document`; compare with `Dump` |
|
||||
| `SaxParse` | parse with a SAX handler that only counts events, so no values are built |
|
||||
| `Copy`, `CopyAssign`, `CopyRing` | copy a parsed document and destroy a copy, both timed: a temporary copy, an assignment over an earlier copy, and replacing the oldest of eight live copies |
|
||||
| `CopyOnly`, `DestroyOnly` | only the copy or only the destruction, the other half untimed; a fast destructor can leave work to the next allocations, so these tell the halves apart but do not compare versions |
|
||||
| `Equal`, `Iterate` | compare a parsed document with a copy, or visit every value with range-based `for` loops |
|
||||
| `Flatten`, `FlattenOrdered`, `Unflatten` | `flatten()` for `json` and `ordered_json`, and `unflatten()` |
|
||||
| `Diff`, `Patch` | `diff()` against the document with every eighth scalar changed, and applying that patch |
|
||||
| `JsonPointerParse`, `JsonPointerToString`, `JsonPointerAt`, `JsonPointerContains` | the JSON pointers of every scalar in a document: parse them from strings, turn them back into strings, look them up with `at()` and `contains()` |
|
||||
| `GetVectorInt`, `GetVectorDouble`, `GetMap` | `get<std::vector<std::int64_t>>()`, `get<std::vector<double>>()` and `get<std::map<std::string, json>>()` on large arrays and a large flat object |
|
||||
| `ToJsonStrings` | create a `json` array from a `std::vector` of 100,000 strings, copied or moved, and destroy it again |
|
||||
|
||||
The input files are those of [nativejson-benchmark](https://github.com/miloyip/nativejson-benchmark) (`canada`,
|
||||
`citm_catalog`, `twitter`), a large `jeopardy` file, and number-heavy files (`floats`, `signed_ints`, ...).
|
||||
|
||||
@@ -9,12 +9,15 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <deque>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <numeric>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <test_data.hpp>
|
||||
|
||||
@@ -710,6 +713,584 @@ static void FromCborChunkedString(benchmark::State& state, const std::size_t chu
|
||||
|
||||
BENCHMARK_CAPTURE(FromCborChunkedString, 10000 chunks, 10000);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// SAX-parse JSON from string
|
||||
//
|
||||
// sax_parse() with a handler that does next to nothing runs the lexer and
|
||||
// parser without building values, so the gap to ParseString above is the cost
|
||||
// of building the DOM.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static std::string read_file(const char* filename)
|
||||
{
|
||||
std::ifstream f(filename);
|
||||
return std::string((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
static json load_json(const char* filename)
|
||||
{
|
||||
return json::parse(read_file(filename));
|
||||
}
|
||||
|
||||
/// SAX handler that only counts the events, so that the handler costs next to nothing
|
||||
class counting_sax : public nlohmann::json_sax<json>
|
||||
{
|
||||
public:
|
||||
std::size_t events = 0;
|
||||
|
||||
bool null() override
|
||||
{
|
||||
return count();
|
||||
}
|
||||
bool boolean(bool /*val*/) override
|
||||
{
|
||||
return count();
|
||||
}
|
||||
bool number_integer(number_integer_t /*val*/) override
|
||||
{
|
||||
return count();
|
||||
}
|
||||
bool number_unsigned(number_unsigned_t /*val*/) override
|
||||
{
|
||||
return count();
|
||||
}
|
||||
bool number_float(number_float_t /*val*/, const string_t& /*s*/) override
|
||||
{
|
||||
return count();
|
||||
}
|
||||
bool string(string_t& /*val*/) override
|
||||
{
|
||||
return count();
|
||||
}
|
||||
bool binary(binary_t& /*val*/) override
|
||||
{
|
||||
return count();
|
||||
}
|
||||
bool start_object(std::size_t /*elements*/) override
|
||||
{
|
||||
return count();
|
||||
}
|
||||
bool key(string_t& /*val*/) override
|
||||
{
|
||||
return count();
|
||||
}
|
||||
bool end_object() override
|
||||
{
|
||||
return count();
|
||||
}
|
||||
bool start_array(std::size_t /*elements*/) override
|
||||
{
|
||||
return count();
|
||||
}
|
||||
bool end_array() override
|
||||
{
|
||||
return count();
|
||||
}
|
||||
bool parse_error(std::size_t /*position*/, const std::string& /*last_token*/, const json::exception& /*ex*/) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
bool count()
|
||||
{
|
||||
++events;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static void SaxParse(benchmark::State& state, const char* filename)
|
||||
{
|
||||
const std::string str = read_file(filename);
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
counting_sax sax;
|
||||
bool result = json::sax_parse(str, &sax);
|
||||
benchmark::DoNotOptimize(result);
|
||||
benchmark::DoNotOptimize(sax.events);
|
||||
}
|
||||
|
||||
state.SetBytesProcessed(state.iterations() * str.size());
|
||||
}
|
||||
BENCHMARK_CAPTURE(SaxParse, canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json");
|
||||
BENCHMARK_CAPTURE(SaxParse, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json");
|
||||
BENCHMARK_CAPTURE(SaxParse, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// work with values: copy, destroy, compare, iterate
|
||||
//
|
||||
// The parsed test files stand in for a document an application keeps around.
|
||||
// Each benchmark times one whole-document operation; setup and cleanup run
|
||||
// outside the timed section.
|
||||
//
|
||||
// A copy is always destroyed again, and how a document is freed shapes the
|
||||
// heap the next copy allocates from: a destructor that frees quickly can leave
|
||||
// work to the allocator's next calls. Copy, CopyAssign and CopyRing therefore
|
||||
// time the copy together with the destruction of a copy; CopyOnly and
|
||||
// DestroyOnly time one half each and are meant for telling the halves apart,
|
||||
// not for comparing versions.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// copy a document and destroy the copy again, as a temporary copy is used
|
||||
static void Copy(benchmark::State& state, const char* filename)
|
||||
{
|
||||
const json j = load_json(filename);
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
json copy = j;
|
||||
benchmark::DoNotOptimize(copy);
|
||||
}
|
||||
}
|
||||
BENCHMARK_CAPTURE(Copy, canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json");
|
||||
BENCHMARK_CAPTURE(Copy, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json");
|
||||
BENCHMARK_CAPTURE(Copy, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json");
|
||||
|
||||
/// copy-assign a document over an earlier copy of it, which is freed in the process
|
||||
static void CopyAssign(benchmark::State& state, const char* filename)
|
||||
{
|
||||
const json j = load_json(filename);
|
||||
json target = j;
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
target = j;
|
||||
benchmark::DoNotOptimize(target);
|
||||
}
|
||||
}
|
||||
BENCHMARK_CAPTURE(CopyAssign, canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json");
|
||||
BENCHMARK_CAPTURE(CopyAssign, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json");
|
||||
BENCHMARK_CAPTURE(CopyAssign, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json");
|
||||
|
||||
/// keep eight copies alive and replace the oldest one, as a cache or an undo
|
||||
/// history does, so that the copy is not made from memory just freed by an
|
||||
/// identical document
|
||||
static void CopyRing(benchmark::State& state, const char* filename)
|
||||
{
|
||||
const json j = load_json(filename);
|
||||
std::vector<json> ring(8, j);
|
||||
std::size_t oldest = 0;
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
ring[oldest] = j;
|
||||
benchmark::DoNotOptimize(ring[oldest]);
|
||||
oldest = (oldest + 1) % ring.size();
|
||||
}
|
||||
}
|
||||
BENCHMARK_CAPTURE(CopyRing, canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json");
|
||||
BENCHMARK_CAPTURE(CopyRing, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json");
|
||||
BENCHMARK_CAPTURE(CopyRing, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json");
|
||||
|
||||
/// only the copy; the copy is destroyed with the timer paused (diagnostic)
|
||||
static void CopyOnly(benchmark::State& state, const char* filename)
|
||||
{
|
||||
const json j = load_json(filename);
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
auto* copy = new json(j);
|
||||
|
||||
state.PauseTiming();
|
||||
delete copy;
|
||||
state.ResumeTiming();
|
||||
}
|
||||
}
|
||||
BENCHMARK_CAPTURE(CopyOnly, canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json");
|
||||
BENCHMARK_CAPTURE(CopyOnly, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json");
|
||||
BENCHMARK_CAPTURE(CopyOnly, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json");
|
||||
|
||||
/// only the destruction; the copy is made with the timer paused (diagnostic)
|
||||
static void DestroyOnly(benchmark::State& state, const char* filename)
|
||||
{
|
||||
const json j = load_json(filename);
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
state.PauseTiming();
|
||||
auto* copy = new json(j);
|
||||
state.ResumeTiming();
|
||||
|
||||
delete copy;
|
||||
}
|
||||
}
|
||||
BENCHMARK_CAPTURE(DestroyOnly, canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json");
|
||||
BENCHMARK_CAPTURE(DestroyOnly, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json");
|
||||
BENCHMARK_CAPTURE(DestroyOnly, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json");
|
||||
|
||||
static void Equal(benchmark::State& state, const char* filename)
|
||||
{
|
||||
// two separate values, so that the comparison cannot stop at identical addresses
|
||||
const json j = load_json(filename);
|
||||
const json copy = j;
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
bool result = (j == copy);
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
BENCHMARK_CAPTURE(Equal, canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json");
|
||||
BENCHMARK_CAPTURE(Equal, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json");
|
||||
BENCHMARK_CAPTURE(Equal, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json");
|
||||
|
||||
/// visit every value with range-based for loops, as client code walks a document
|
||||
static std::size_t count_values(const json& j)
|
||||
{
|
||||
std::size_t result = 1;
|
||||
if (j.is_structured())
|
||||
{
|
||||
for (const auto& element : j)
|
||||
{
|
||||
result += count_values(element);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void Iterate(benchmark::State& state, const char* filename)
|
||||
{
|
||||
const json j = load_json(filename);
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
std::size_t result = count_values(j);
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * static_cast<std::int64_t>(count_values(j)));
|
||||
}
|
||||
BENCHMARK_CAPTURE(Iterate, canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json");
|
||||
BENCHMARK_CAPTURE(Iterate, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json");
|
||||
BENCHMARK_CAPTURE(Iterate, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// flatten and unflatten
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void Flatten(benchmark::State& state, const char* filename)
|
||||
{
|
||||
const json j = load_json(filename);
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
json flat = j.flatten();
|
||||
benchmark::DoNotOptimize(flat);
|
||||
}
|
||||
}
|
||||
BENCHMARK_CAPTURE(Flatten, canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json");
|
||||
BENCHMARK_CAPTURE(Flatten, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json");
|
||||
BENCHMARK_CAPTURE(Flatten, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json");
|
||||
|
||||
// ordered_json objects look keys up linearly, which flatten() used to do for
|
||||
// every value; canada (330,000 values in one flat object) is left out, as it
|
||||
// would take minutes with such versions
|
||||
static void FlattenOrdered(benchmark::State& state, const char* filename)
|
||||
{
|
||||
const nlohmann::ordered_json j = nlohmann::ordered_json::parse(read_file(filename));
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
nlohmann::ordered_json flat = j.flatten();
|
||||
benchmark::DoNotOptimize(flat);
|
||||
}
|
||||
}
|
||||
BENCHMARK_CAPTURE(FlattenOrdered, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json");
|
||||
BENCHMARK_CAPTURE(FlattenOrdered, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json");
|
||||
|
||||
static void Unflatten(benchmark::State& state, const char* filename)
|
||||
{
|
||||
const json flat = load_json(filename).flatten();
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
json j = flat.unflatten();
|
||||
benchmark::DoNotOptimize(j);
|
||||
}
|
||||
}
|
||||
BENCHMARK_CAPTURE(Unflatten, canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json");
|
||||
BENCHMARK_CAPTURE(Unflatten, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json");
|
||||
BENCHMARK_CAPTURE(Unflatten, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// diff and patch
|
||||
//
|
||||
// The target is the document with every eighth scalar changed, so the patch
|
||||
// has one "replace" operation per changed value and the structure stays the
|
||||
// same.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void change_every_eighth_scalar(json& j, std::size_t& counter)
|
||||
{
|
||||
if (j.is_structured())
|
||||
{
|
||||
for (auto& element : j)
|
||||
{
|
||||
change_every_eighth_scalar(element, counter);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (++counter % 8 != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (j.type())
|
||||
{
|
||||
case json::value_t::number_integer:
|
||||
j = j.get<json::number_integer_t>() + 1;
|
||||
break;
|
||||
case json::value_t::number_unsigned:
|
||||
j = j.get<json::number_unsigned_t>() + 1;
|
||||
break;
|
||||
case json::value_t::number_float:
|
||||
j = j.get<json::number_float_t>() + 0.5;
|
||||
break;
|
||||
case json::value_t::string:
|
||||
j = j.get<std::string>() + "!";
|
||||
break;
|
||||
case json::value_t::boolean:
|
||||
j = !j.get<bool>();
|
||||
break;
|
||||
case json::value_t::null:
|
||||
case json::value_t::object:
|
||||
case json::value_t::array:
|
||||
case json::value_t::binary:
|
||||
case json::value_t::discarded:
|
||||
default:
|
||||
j = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static json changed_copy(const json& j)
|
||||
{
|
||||
json result = j;
|
||||
std::size_t counter = 0;
|
||||
change_every_eighth_scalar(result, counter);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void Diff(benchmark::State& state, const char* filename)
|
||||
{
|
||||
const json source = load_json(filename);
|
||||
const json target = changed_copy(source);
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
json patch = json::diff(source, target);
|
||||
benchmark::DoNotOptimize(patch);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * static_cast<std::int64_t>(json::diff(source, target).size()));
|
||||
}
|
||||
BENCHMARK_CAPTURE(Diff, canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json");
|
||||
BENCHMARK_CAPTURE(Diff, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json");
|
||||
BENCHMARK_CAPTURE(Diff, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json");
|
||||
|
||||
static void Patch(benchmark::State& state, const char* filename)
|
||||
{
|
||||
const json source = load_json(filename);
|
||||
const json patch = json::diff(source, changed_copy(source));
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
json result = source.patch(patch);
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * static_cast<std::int64_t>(patch.size()));
|
||||
}
|
||||
BENCHMARK_CAPTURE(Patch, canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json");
|
||||
BENCHMARK_CAPTURE(Patch, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json");
|
||||
BENCHMARK_CAPTURE(Patch, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// JSON Pointer
|
||||
//
|
||||
// The pointers are those of every scalar in the document, i.e., the keys of
|
||||
// its flattened form.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static std::vector<std::string> pointer_strings(const json& j)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
const json flat = j.flatten();
|
||||
result.reserve(flat.size());
|
||||
for (auto it = flat.begin(); it != flat.end(); ++it)
|
||||
{
|
||||
result.push_back(it.key());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::vector<json::json_pointer> pointers(const json& j)
|
||||
{
|
||||
const std::vector<std::string> strings = pointer_strings(j);
|
||||
std::vector<json::json_pointer> result;
|
||||
result.reserve(strings.size());
|
||||
for (const auto& s : strings)
|
||||
{
|
||||
result.emplace_back(s);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void JsonPointerParse(benchmark::State& state, const char* filename)
|
||||
{
|
||||
const std::vector<std::string> strings = pointer_strings(load_json(filename));
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
for (const auto& s : strings)
|
||||
{
|
||||
json::json_pointer ptr(s);
|
||||
benchmark::DoNotOptimize(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * static_cast<std::int64_t>(strings.size()));
|
||||
}
|
||||
BENCHMARK_CAPTURE(JsonPointerParse, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json");
|
||||
BENCHMARK_CAPTURE(JsonPointerParse, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json");
|
||||
|
||||
static void JsonPointerToString(benchmark::State& state, const char* filename)
|
||||
{
|
||||
const std::vector<json::json_pointer> ptrs = pointers(load_json(filename));
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
for (const auto& ptr : ptrs)
|
||||
{
|
||||
std::string s = ptr.to_string();
|
||||
benchmark::DoNotOptimize(s);
|
||||
}
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * static_cast<std::int64_t>(ptrs.size()));
|
||||
}
|
||||
BENCHMARK_CAPTURE(JsonPointerToString, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json");
|
||||
BENCHMARK_CAPTURE(JsonPointerToString, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json");
|
||||
|
||||
static void JsonPointerAt(benchmark::State& state, const char* filename)
|
||||
{
|
||||
const json j = load_json(filename);
|
||||
const std::vector<json::json_pointer> ptrs = pointers(j);
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
for (const auto& ptr : ptrs)
|
||||
{
|
||||
const json& value = j.at(ptr);
|
||||
benchmark::DoNotOptimize(&value);
|
||||
}
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * static_cast<std::int64_t>(ptrs.size()));
|
||||
}
|
||||
BENCHMARK_CAPTURE(JsonPointerAt, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json");
|
||||
BENCHMARK_CAPTURE(JsonPointerAt, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json");
|
||||
|
||||
static void JsonPointerContains(benchmark::State& state, const char* filename)
|
||||
{
|
||||
const json j = load_json(filename);
|
||||
const std::vector<json::json_pointer> ptrs = pointers(j);
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
for (const auto& ptr : ptrs)
|
||||
{
|
||||
bool result = j.contains(ptr);
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * static_cast<std::int64_t>(ptrs.size()));
|
||||
}
|
||||
BENCHMARK_CAPTURE(JsonPointerContains, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json");
|
||||
BENCHMARK_CAPTURE(JsonPointerContains, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// conversions from and to C++ containers
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void GetVectorInt(benchmark::State& state, const char* filename)
|
||||
{
|
||||
const json j = load_json(filename);
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
auto v = j.get<std::vector<std::int64_t>>();
|
||||
benchmark::DoNotOptimize(v);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * static_cast<std::int64_t>(j.size()));
|
||||
}
|
||||
BENCHMARK_CAPTURE(GetVectorInt, signed_ints, TEST_DATA_DIRECTORY "/regression/signed_ints.json");
|
||||
|
||||
static void GetVectorDouble(benchmark::State& state, const char* filename)
|
||||
{
|
||||
const json j = load_json(filename);
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
auto v = j.get<std::vector<double>>();
|
||||
benchmark::DoNotOptimize(v);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * static_cast<std::int64_t>(j.size()));
|
||||
}
|
||||
BENCHMARK_CAPTURE(GetVectorDouble, floats, TEST_DATA_DIRECTORY "/regression/floats.json");
|
||||
|
||||
// a large flat object: the flattened document, one member per scalar
|
||||
static void GetMap(benchmark::State& state, const char* filename)
|
||||
{
|
||||
const json j = load_json(filename).flatten();
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
auto m = j.get<std::map<std::string, json>>();
|
||||
benchmark::DoNotOptimize(m);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * static_cast<std::int64_t>(j.size()));
|
||||
}
|
||||
BENCHMARK_CAPTURE(GetMap, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json");
|
||||
|
||||
/// 100,000 strings, each too long for the small string optimization
|
||||
static std::vector<std::string> make_strings()
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
result.reserve(100000);
|
||||
for (std::size_t i = 0; i < 100000; ++i)
|
||||
{
|
||||
result.push_back("a string that does not fit in place: " + std::to_string(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void ToJsonStrings(benchmark::State& state, const bool move)
|
||||
{
|
||||
const std::vector<std::string> strings = make_strings();
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
state.PauseTiming();
|
||||
std::vector<std::string> input = strings;
|
||||
state.ResumeTiming();
|
||||
|
||||
// destroyed inside the timed section, like the copies above
|
||||
auto* j = move ? new json(std::move(input)) : new json(input);
|
||||
delete j;
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * static_cast<std::int64_t>(strings.size()));
|
||||
}
|
||||
BENCHMARK_CAPTURE(ToJsonStrings, copy, false);
|
||||
BENCHMARK_CAPTURE(ToJsonStrings, move, true);
|
||||
|
||||
// conversion of a JSON array into standard containers
|
||||
template<typename Container>
|
||||
static void GetArray(benchmark::State& state)
|
||||
|
||||
@@ -15,9 +15,12 @@ using nlohmann::json;
|
||||
#include <array> // array
|
||||
#include <cstdint> // uint32_t, uint64_t
|
||||
#include <cstdio> // snprintf
|
||||
#include <cmath> // isfinite
|
||||
#include <cstdlib> // strtod
|
||||
#include <cstring> // memcpy
|
||||
#include <limits> // numeric_limits
|
||||
#include <initializer_list> // initializer_list
|
||||
#include <list> // list
|
||||
#include <map> // map
|
||||
#include <random> // mt19937
|
||||
#include <sstream> // stringstream
|
||||
@@ -332,6 +335,112 @@ TEST_CASE("lexer number fast path")
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(JSON_NOEXCEPTION)
|
||||
// this section reports parse errors, which abort when exceptions are off
|
||||
SECTION("digits accumulated while scanning")
|
||||
{
|
||||
// The fast path accumulates the digits while it validates them, eight
|
||||
// at a time where possible, and converts tokens with at most 19
|
||||
// digits (floats) or 18 digits (integers) from that value. Every digit
|
||||
// count from 1 to 25, in the integer and in the fraction part, with
|
||||
// and without sign and exponent, must give the byte path's result. A
|
||||
// std::list has no contiguous storage, so parsing it takes the byte
|
||||
// path.
|
||||
// type and value, or the error (values beyond the range of double
|
||||
// are rejected with out_of_range.406 on both paths)
|
||||
const auto outcome = [](const std::string & doc, bool byte_path) -> std::string
|
||||
{
|
||||
JSON_TRY
|
||||
{
|
||||
const std::list<char> chars(doc.begin(), doc.end());
|
||||
const json j = byte_path ? json::parse(chars.begin(), chars.end()) : json::parse(doc);
|
||||
return std::string(j[0].type_name()) + '|' + j.dump();
|
||||
}
|
||||
JSON_CATCH (const json::exception & e)
|
||||
{
|
||||
return e.what();
|
||||
}
|
||||
};
|
||||
|
||||
// the concatenation of @a parts
|
||||
const auto join = [](std::initializer_list<std::string> parts)
|
||||
{
|
||||
std::string result;
|
||||
for (const auto& part : parts)
|
||||
{
|
||||
result += part;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
std::vector<std::string> numbers;
|
||||
for (std::size_t digits = 1; digits <= 25; ++digits)
|
||||
{
|
||||
std::string run;
|
||||
for (std::size_t i = 0; i < digits; ++i)
|
||||
{
|
||||
run += static_cast<char>('1' + static_cast<char>((i * 7) % 9));
|
||||
}
|
||||
const std::string nines(digits, '9');
|
||||
for (const char* sign_text :
|
||||
{"", "-"
|
||||
})
|
||||
{
|
||||
const std::string sign = sign_text;
|
||||
numbers.push_back(join({sign, run}));
|
||||
numbers.push_back(join({sign, nines}));
|
||||
numbers.push_back(join({sign, "1", std::string(digits - 1, '0')}));
|
||||
numbers.push_back(join({sign, "0.", run}));
|
||||
numbers.push_back(join({sign, "0.", std::string(digits, '0'), "1"}));
|
||||
numbers.push_back(join({sign, run, ".", run}));
|
||||
numbers.push_back(join({sign, run, "e-", std::to_string(digits)}));
|
||||
numbers.push_back(join({sign, "1.", run, "E+", std::to_string(digits * 13)}));
|
||||
}
|
||||
}
|
||||
// exponents beyond every range, and zeros
|
||||
numbers.insert(numbers.end(), {"1e400", "1e-400", "-1e99999999999999999999", "1e-99999999999999999999",
|
||||
"0e99999999999999999999", "0.0e-5", "-0.000", "123456789e-0000000000000000000000330"
|
||||
});
|
||||
|
||||
for (const auto& n : numbers)
|
||||
{
|
||||
CAPTURE(n)
|
||||
const std::string doc = "[" + n + "]";
|
||||
const std::string a = outcome(doc, false);
|
||||
CHECK(a == outcome(doc, true));
|
||||
const double expected = std::strtod(n.c_str(), nullptr);
|
||||
if (a.compare(0, 6, "number") == 0 && std::isfinite(expected) && n.find_first_of(".eE") != std::string::npos)
|
||||
{
|
||||
CHECK(json::parse(doc)[0].get<double>() == expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("integers that do not fit a narrower type become floats")
|
||||
{
|
||||
// up to 18 digits, the fast path stores the accumulated value without
|
||||
// an overflow check; a narrower number_integer_t/number_unsigned_t
|
||||
// must still see the value rejected and converted as a float
|
||||
using json32 = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int32_t, std::uint32_t, float>;
|
||||
for (const char* n :
|
||||
{"4294967295", "4294967296", "2147483647", "-2147483648", "-2147483649", "99999999999", "-99999999999"
|
||||
})
|
||||
{
|
||||
CAPTURE(n)
|
||||
const std::string doc = std::string("[") + n + "]";
|
||||
const std::list<char> chars(doc.begin(), doc.end());
|
||||
const json32 a = json32::parse(doc);
|
||||
const json32 b = json32::parse(chars.begin(), chars.end());
|
||||
CHECK(a[0].type() == b[0].type());
|
||||
CHECK(a == b);
|
||||
}
|
||||
CHECK(json32::parse("[4294967295]")[0].is_number_unsigned());
|
||||
CHECK(json32::parse("[4294967296]")[0].is_number_float());
|
||||
CHECK(json32::parse("[-2147483648]")[0].is_number_integer());
|
||||
CHECK(json32::parse("[-2147483649]")[0].is_number_float());
|
||||
}
|
||||
|
||||
SECTION("token type classification")
|
||||
{
|
||||
CHECK((scan_string("0") == json::lexer::token_type::value_unsigned));
|
||||
|
||||
@@ -30,7 +30,9 @@ using nlohmann::json;
|
||||
#include <valarray>
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <list>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
@@ -2450,6 +2452,79 @@ std::string parse_error_message_range(IteratorType first, IteratorType last)
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("text of float tokens")
|
||||
{
|
||||
// For contiguous input, the lexer copies the text of a float token into
|
||||
// its buffer only when a SAX handler asks for it (the library's own
|
||||
// handlers do not). A user handler must still see the exact text, for
|
||||
// every input adapter and in every position of a document.
|
||||
const std::string doc = R"([1.5, -0.0, 0.1, 1e5, -2.5E-4, 12345678901234567890.5, 0.12345678901234567890123,
|
||||
{"a": 3.25, "b": [6.02e23, 1]}, "x", true, 7e-1])";
|
||||
const std::vector<std::string> expected_floats =
|
||||
{
|
||||
"number_float(1.5)", "number_float(-0.0)", "number_float(0.1)", "number_float(1e5)", "number_float(-2.5E-4)",
|
||||
"number_float(12345678901234567890.5)", "number_float(0.12345678901234567890123)", "number_float(3.25)",
|
||||
"number_float(6.02e23)", "number_float(7e-1)"
|
||||
};
|
||||
|
||||
const auto floats = [](const std::vector<std::string>& events)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
std::copy_if(events.begin(), events.end(), std::back_inserter(result), [](const std::string & e)
|
||||
{
|
||||
return e.compare(0, 13, "number_float(") == 0;
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
SECTION("contiguous input")
|
||||
{
|
||||
SaxEventLogger l;
|
||||
CHECK(json::sax_parse(doc, &l));
|
||||
CHECK(floats(l.events) == expected_floats);
|
||||
}
|
||||
|
||||
SECTION("byte input")
|
||||
{
|
||||
const std::vector<std::uint8_t> bytes(doc.begin(), doc.end());
|
||||
SaxEventLogger l;
|
||||
CHECK(json::sax_parse(bytes, &l));
|
||||
CHECK(floats(l.events) == expected_floats);
|
||||
}
|
||||
|
||||
SECTION("stream input")
|
||||
{
|
||||
std::istringstream is(doc);
|
||||
SaxEventLogger l;
|
||||
CHECK(json::sax_parse(is, &l));
|
||||
CHECK(floats(l.events) == expected_floats);
|
||||
}
|
||||
|
||||
SECTION("non-contiguous iterators")
|
||||
{
|
||||
const std::list<char> chars(doc.begin(), doc.end());
|
||||
SaxEventLogger l;
|
||||
CHECK(json::sax_parse(chars.begin(), chars.end(), &l));
|
||||
CHECK(floats(l.events) == expected_floats);
|
||||
}
|
||||
|
||||
SECTION("the library's own handlers do not need the text")
|
||||
{
|
||||
using lexer_adapter = nlohmann::detail::iterator_input_adapter<const char*>;
|
||||
CHECK(nlohmann::detail::sax_ignores_number_text<nlohmann::detail::json_sax_dom_parser<json, lexer_adapter>>::value);
|
||||
CHECK(nlohmann::detail::sax_ignores_number_text<nlohmann::detail::json_sax_dom_callback_parser<json, lexer_adapter>>::value);
|
||||
CHECK(nlohmann::detail::sax_ignores_number_text<nlohmann::detail::json_sax_acceptor<json>>::value);
|
||||
CHECK_FALSE(nlohmann::detail::sax_ignores_number_text<SaxEventLogger>::value);
|
||||
|
||||
// and the values are the same as when the text is read
|
||||
const json j = json::parse(doc);
|
||||
CHECK(j[0] == 1.5);
|
||||
CHECK(j[5].get<double>() == 12345678901234567890.5);
|
||||
CHECK(j[7]["b"][0] == 6.02e23);
|
||||
CHECK(json::accept(doc));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("last-read diagnostics are identical across input adapters")
|
||||
{
|
||||
// The lexer reconstructs the "last read" token lazily for seekable adapters
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++ (supporting code)
|
||||
// | | |__ | | | | | | version 3.12.0
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
// The detail helpers that stand in for standard library functions of newer
|
||||
// standards: each uses the standard function where the library has it and a
|
||||
// C++11 implementation otherwise, so this file is also built with
|
||||
// JSON_HAS_CPP_17, JSON_HAS_CPP_20, and JSON_HAS_CPP_23 to test both.
|
||||
|
||||
namespace
|
||||
{
|
||||
struct with_constexpr_member
|
||||
{
|
||||
static constexpr std::size_t lower = 64; // no out-of-line definition
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("detail::clamp")
|
||||
{
|
||||
using nlohmann::detail::clamp;
|
||||
|
||||
SECTION("integers")
|
||||
{
|
||||
CHECK(clamp(5, 1, 10) == 5);
|
||||
CHECK(clamp(0, 1, 10) == 1);
|
||||
CHECK(clamp(11, 1, 10) == 10);
|
||||
CHECK(clamp(1, 1, 10) == 1);
|
||||
CHECK(clamp(10, 1, 10) == 10);
|
||||
CHECK(clamp(7, 7, 7) == 7);
|
||||
CHECK(clamp(-5, -3, 3) == -3);
|
||||
CHECK(clamp(std::size_t{0}, std::size_t{64}, std::size_t{0x10000}) == 64);
|
||||
CHECK(clamp(std::size_t{0x20000}, std::size_t{64}, std::size_t{0x10000}) == 0x10000);
|
||||
}
|
||||
|
||||
SECTION("floating-point numbers")
|
||||
{
|
||||
CHECK(clamp(0.5, 0.0, 1.0) == 0.5);
|
||||
CHECK(clamp(-0.5, 0.0, 1.0) == 0.0);
|
||||
CHECK(clamp(1.5, 0.0, 1.0) == 1.0);
|
||||
CHECK(clamp(-std::numeric_limits<double>::infinity(), 0.0, 1.0) == 0.0);
|
||||
}
|
||||
|
||||
SECTION("by value: a static constexpr member needs no definition")
|
||||
{
|
||||
CHECK(clamp(std::size_t{1}, with_constexpr_member::lower, std::size_t{100}) == 64);
|
||||
}
|
||||
|
||||
SECTION("constexpr and noexcept")
|
||||
{
|
||||
static_assert(clamp(3, 1, 2) == 2, "clamp must be constexpr");
|
||||
static_assert(noexcept(clamp(3, 1, 2)), "clamp must be noexcept");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("detail::bit_cast")
|
||||
{
|
||||
using nlohmann::detail::bit_cast;
|
||||
|
||||
CHECK(bit_cast<std::uint64_t>(1.0) == 0x3FF0000000000000u);
|
||||
CHECK(bit_cast<std::uint64_t>(-2.0) == 0xC000000000000000u);
|
||||
CHECK(bit_cast<std::uint32_t>(1.0f) == 0x3F800000u);
|
||||
CHECK(bit_cast<double>(std::uint64_t{0x4009000000000000u}) == 3.125);
|
||||
CHECK(bit_cast<float>(std::uint32_t{0xC0490000u}) == -3.140625f);
|
||||
CHECK(bit_cast<std::int8_t>(std::uint8_t{0xFF}) == -1);
|
||||
|
||||
// to and from byte arrays
|
||||
const auto bytes = bit_cast<std::array<std::uint8_t, 4>>(std::uint32_t{0x01020304u});
|
||||
CHECK(bit_cast<std::uint32_t>(bytes) == 0x01020304u);
|
||||
|
||||
static_assert(noexcept(bit_cast<std::uint64_t>(1.0)), "bit_cast must be noexcept");
|
||||
}
|
||||
|
||||
TEST_CASE("detail::byteswap")
|
||||
{
|
||||
using nlohmann::detail::byteswap;
|
||||
|
||||
CHECK(byteswap(std::uint16_t{0x0102u}) == 0x0201u);
|
||||
CHECK(byteswap(std::uint16_t{0xFF00u}) == 0x00FFu);
|
||||
CHECK(byteswap(std::uint32_t{0x01020304u}) == 0x04030201u);
|
||||
CHECK(byteswap(std::uint32_t{0xFF000080u}) == 0x800000FFu);
|
||||
CHECK(byteswap(std::uint64_t{0x0102030405060708u}) == 0x0807060504030201u);
|
||||
CHECK(byteswap(std::uint64_t{0xFF00000000000080u}) == 0x80000000000000FFu);
|
||||
|
||||
// an involution
|
||||
for (std::uint64_t x :
|
||||
{
|
||||
std::uint64_t{0}, std::uint64_t{1}, std::uint64_t{0x123456789ABCDEF0u}, (std::numeric_limits<std::uint64_t>::max)()
|
||||
})
|
||||
{
|
||||
CHECK(byteswap(byteswap(x)) == x);
|
||||
CHECK(byteswap(byteswap(static_cast<std::uint32_t>(x))) == static_cast<std::uint32_t>(x));
|
||||
CHECK(byteswap(byteswap(static_cast<std::uint16_t>(x))) == static_cast<std::uint16_t>(x));
|
||||
}
|
||||
|
||||
static_assert(std::is_same < decltype(byteswap(std::uint16_t{})), std::uint16_t >::value, "byteswap keeps the type");
|
||||
static_assert(std::is_same < decltype(byteswap(std::uint32_t{})), std::uint32_t >::value, "byteswap keeps the type");
|
||||
static_assert(std::is_same < decltype(byteswap(std::uint64_t{})), std::uint64_t >::value, "byteswap keeps the type");
|
||||
}
|
||||
|
||||
TEST_CASE("detail::count_leading_zeros and count_trailing_zeros")
|
||||
{
|
||||
using nlohmann::detail::count_leading_zeros;
|
||||
using nlohmann::detail::count_trailing_zeros;
|
||||
|
||||
// a single bit, and that bit with all lower (resp. higher) bits set: both
|
||||
// halves of the 32-bit fallbacks
|
||||
for (int k = 0; k < 64; ++k)
|
||||
{
|
||||
const std::uint64_t bit = std::uint64_t{1} << k;
|
||||
CAPTURE(k)
|
||||
CHECK(count_leading_zeros(bit) == 63 - k);
|
||||
CHECK(count_leading_zeros(bit | (bit - 1)) == 63 - k);
|
||||
CHECK(count_trailing_zeros(bit) == k);
|
||||
CHECK(count_trailing_zeros(bit | ~(bit | (bit - 1))) == k);
|
||||
}
|
||||
}
|
||||
Loaded 100 of 138 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user