Compare commits

..
Author SHA1 Message Date
Niels Lohmann 4001fe68b9 Fix silent wrong results for custom number types
Two bugs for custom number types that compile on develop (found while
analysing #3578):

- compare_integer_with_float() took the signedness of the integer type
  from std::is_signed, which is false for class types such as
  absl::int128 or boost::multiprecision::cpp_int. Any float below zero
  then compared less than every integer, e.g. json(int128(-5)) <
  json(-2.5) was false. The signedness now comes from
  std::numeric_limits, like the digits used for the range bound.

- With number_integer_t/number_unsigned_t wider than 64 bits (e.g.
  __int128), CBOR, MessagePack, and BSON silently truncated integers
  beyond 64 bits (to_cbor of 2^100 read back as 0), BJData truncated
  unsigned ones with the 'M' marker and could encode truncated ND-array
  elements and dimensions, and BON8 did not compile (std::to_string is
  ambiguous for __int128). The writers now throw out_of_range.407 when
  an integer does not fit the format's range ([-2^64, 2^64-1] for CBOR,
  [-2^63, 2^64-1] for MessagePack, int64/uint64 for BSON, int64 for
  BON8); BJData writes such unsigned values as high-precision numbers,
  as it already did for signed ones and UBJSON does for both, and falls
  back to a plain object for the ND-array. The checks use
  std::numeric_limits digits and compile away when the number types are
  at most 64 bits wide, so the default types pay nothing (the CBOR,
  MessagePack, and BSON writers compile to identical code).

The new unit-custom-number-types.cpp tests the comparison with a small
class-type integer and the writers with __int128 where the standard
library supports it as an integral type.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-10 16:55:26 +02:00
Niels Lohmann f8b47ff6f6 Check the URL scheme before downloading in generate_docset.py (#5803)
Codacy flagged two Bandit findings in the docset generator added in
#5799: B310 (urlopen with an unchecked scheme) and B506 (yaml.load).
download() now rejects anything but http(s) URLs before opening them,
and the yaml.load call is marked, since its Loader derives from
yaml.SafeLoader. The SHA-1 used to name downloaded files is marked as
not used for security.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-10 16:21:53 +02:00
Niels Lohmann 07ec1a4e31 Generate the docset index and pages with Python (#5799)
Replace the hand-maintained docs/docset/docSet.sql and the bash/sed
Makefile recipe with docs/docset/generate_docset.py:

- The search index is generated from the mkdocs.yml nav and each page's
  H1 and declaration. Pages documenting several entities (e.g.
  JSON_HAS_CPP_11..26) get one entry per name; all non-API pages become
  guides. New API pages no longer need a manual entry.
- Page titles are set from the index names.
- The docset is self-contained: the mermaid loader no longer points to
  https://json.nlohmann.me/assets/..., the repository widget no longer
  queries api.github.com, remaining remote images are downloaded, and
  the build fails if any remote resource load remains.
- The CSS that hides the site navigation now uses Material's classes;
  the element selectors lost against them, so the header was shown.

check_structure.py's docset check is replaced by running the generator
in `make style_check`.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-10 12:10:11 +02:00
137 changed files with 1268 additions and 72785 deletions

No files matched your search

+4 -2
View File
@@ -149,8 +149,10 @@ make build -C docs/mkdocs # strict build: fails on broken links, anchor
make check_mermaid -C docs/mkdocs # checks the Mermaid diagrams (requires Node.js)
```
A new API page also needs an entry in [`docs/docset/docSet.sql`](https://github.com/nlohmann/json/blob/develop/docs/docset/docSet.sql),
the search index of the docset; `make build` reports missing entries.
The search index of the docset is generated from [`mkdocs.yml`](https://github.com/nlohmann/json/blob/develop/docs/mkdocs/mkdocs.yml)
and each page's title (H1) and declaration by
[`docs/docset/generate_docset.py`](https://github.com/nlohmann/json/blob/develop/docs/docset/generate_docset.py);
`make build` reports API pages that cannot be classified.
### Amalgamate the source code
-81
View File
@@ -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
-5
View File
@@ -3,7 +3,6 @@
*.gcno
*.gcda
.DS_Store
__pycache__/
/.idea
/cmake-build-*
@@ -46,9 +45,5 @@ 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
+1 -1
View File
@@ -104,7 +104,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.
+11 -55
View File
@@ -1,48 +1,24 @@
SHELL=/usr/bin/env bash
SED ?= $(shell which gsed 2>/dev/null || which sed)
MKDOCS_PAGES=$(shell cd ../mkdocs/docs/ && find * -type f -name '*.md' | sort)
PYTHON=../mkdocs/venv/bin/python3
.PHONY: all
all: JSON_for_Modern_C++.tgz
docSet.dsidx: docSet.sql
# generate index
sqlite3 docSet.dsidx <docSet.sql
# generate the search index (the docset target does this itself, this is
# only handy for inspecting the index)
docSet.dsidx: generate_docset.py
$(PYTHON) generate_docset.py index docSet.dsidx
JSON_for_Modern_C++.docset: Info.plist docSet.dsidx
rm -fr JSON_for_Modern_C++.docset JSON_for_Modern_C++.tgz
mkdir -p JSON_for_Modern_C++.docset/Contents/Resources/Documents/
cp icon*.png JSON_for_Modern_C++.docset
cp Info.plist JSON_for_Modern_C++.docset/Contents
# build and copy documentation
# build the documentation and turn it into a self-contained docset
.PHONY: JSON_for_Modern_C++.docset
JSON_for_Modern_C++.docset:
$(MAKE) install_venv -C ../mkdocs
$(MAKE) build -C ../mkdocs
cp -r ../mkdocs/site/* JSON_for_Modern_C++.docset/Contents/Resources/Documents
# patch CSS to hide navigation items
echo -e "\n\nheader, footer, nav.md-tabs, nav.md-tabs--active, div.md-sidebar--primary, a.md-content__button { display: none; }" >> "$$(ls JSON_for_Modern_C++.docset/Contents/Resources/Documents/assets/stylesheets/main.*.min.css)"
# fix spacing
echo -e "\n\ndiv.md-sidebar div.md-sidebar--secondary, div.md-main__inner { top: 0; margin-top: 0 }" >> "$$(ls JSON_for_Modern_C++.docset/Contents/Resources/Documents/assets/stylesheets/main.*.min.css)"
# remove "JSON for Modern C++" from page titles (fallback)
find JSON_for_Modern_C++.docset/Contents/Resources/Documents -type f -exec $(SED) -i 's| - JSON for Modern C++</title>|</title>|' {} +
# replace page titles with name from index, if available
for page in $(MKDOCS_PAGES); do \
case "$$page" in \
*/index.md) path=$${page/\/index.md/} ;; \
*) path=$${page/.md/} ;; \
esac; \
title=$$(sqlite3 docSet.dsidx "SELECT name FROM searchIndex WHERE path='$$path/index.html'" | tr '\n' ',' | $(SED) -e 's/,/, /g' -e 's/, $$/\n/'); \
if [ "x$$title" != "x" ]; then \
$(SED) -i "s%<title>.*</title>%<title>$$title</title>%" "JSON_for_Modern_C++.docset/Contents/Resources/Documents/$$path/index.html"; \
fi \
done
# clean up
rm JSON_for_Modern_C++.docset/Contents/Resources/Documents/sitemap.*
# copy index
cp docSet.dsidx JSON_for_Modern_C++.docset/Contents/Resources/
$(PYTHON) generate_docset.py docset ../mkdocs/site .
.PHONY: JSON_for_Modern_C++.tgz
JSON_for_Modern_C++.tgz: JSON_for_Modern_C++.docset
tar --exclude='.DS_Store' -cvzf JSON_for_Modern_C++.tgz JSON_for_Modern_C++.docset
$(PYTHON) generate_docset.py tgz .
# install docset for Zeal documentation browser (https://zealdocs.org/)
.PHONY: install_docset_zeal
@@ -52,26 +28,6 @@ install_docset_zeal: JSON_for_Modern_C++.docset
mkdir -p $$docset_root; \
cp -r JSON_for_Modern_C++.docset $$docset_root/
# both targets below compare the docset search index with the mkdocs page
# set. They share the same normalization (docs/foo/index.md and
# docs/foo.md both become foo/index.html, the URL mkdocs itself would
# give the page; the top-level index.md is excluded, as it is not part
# of the hand-curated docSet.sql) and use comm(1) on two sorted lists
# instead of running a sqlite3 query, or an O(n*m) nested shell loop,
# once per page.
DOCSET_INDEX_PATHS=$(shell sqlite3 docSet.dsidx "SELECT DISTINCT path FROM searchIndex" | sort)
DOCSET_PAGE_PATHS=$(shell echo '$(MKDOCS_PAGES)' | tr ' ' '\n' | grep -v '^index\.md$$' | $(SED) -E 's@/index\.md$$@/index.html@; s@\.md$$@/index.html@' | sort)
# list mkdocs pages missing from the docset index
.PHONY: list_missing_pages
list_missing_pages: docSet.dsidx
@comm -23 <(echo '$(DOCSET_PAGE_PATHS)' | tr ' ' '\n') <(echo '$(DOCSET_INDEX_PATHS)' | tr ' ' '\n')
# list paths in the docset index without a corresponding mkdocs page
.PHONY: list_removed_paths
list_removed_paths: docSet.dsidx
@comm -13 <(echo '$(DOCSET_PAGE_PATHS)' | tr ' ' '\n') <(echo '$(DOCSET_INDEX_PATHS)' | tr ' ' '\n')
.PHONY: clean
clean:
rm -f docSet.dsidx
+3 -1
View File
@@ -4,7 +4,8 @@ The folder contains the required files to create a [docset](https://kapeli.com/d
documentation browsers like [Dash](https://kapeli.com/dash), [Velocity](https://velocity.silverlakesoftware.com), or
[Zeal](https://zealdocs.org).
The docset can be created with
The docset (pages and search index) is generated by `generate_docset.py` from the mkdocs site and `mkdocs.yml`. It
can be created with
```sh
make JSON_for_Modern_C++.docset
@@ -12,6 +13,7 @@ make JSON_for_Modern_C++.docset
The generated folder `JSON_for_Modern_C++.docset` can then be opened in the documentation browser. `make all` builds a
`JSON_for_Modern_C++.tgz` archive instead, and `make install_docset_zeal` installs the docset for Zeal directly.
`make docSet.dsidx` builds only the search index.
A recent version is also part of the [Dash user contributions](https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/JSON_for_Modern_C%2B%2B).
-314
View File
@@ -1,314 +0,0 @@
DROP TABLE IF EXISTS searchIndex;
CREATE TABLE searchIndex(id INTEGER PRIMARY KEY, name TEXT, type TEXT, path TEXT);
CREATE UNIQUE INDEX anchor ON searchIndex (name, type, path);
-- API
INSERT INTO searchIndex(name, type, path) VALUES ('adl_serializer', 'Class', 'api/adl_serializer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('adl_serializer::from_json', 'Function', 'api/adl_serializer/from_json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('adl_serializer::to_json', 'Function', 'api/adl_serializer/to_json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype', 'Class', 'api/byte_container_with_subtype/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::byte_container_with_subtype', 'Constructor', 'api/byte_container_with_subtype/byte_container_with_subtype/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::clear_subtype', 'Method', 'api/byte_container_with_subtype/clear_subtype/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::container_type', 'Type', 'api/byte_container_with_subtype/container_type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::has_subtype', 'Method', 'api/byte_container_with_subtype/has_subtype/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::operator!=', 'Operator', 'api/byte_container_with_subtype/operator_ne/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::operator==', 'Operator', 'api/byte_container_with_subtype/operator_eq/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::set_subtype', 'Method', 'api/byte_container_with_subtype/set_subtype/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::subtype', 'Method', 'api/byte_container_with_subtype/subtype/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::subtype_type', 'Type', 'api/byte_container_with_subtype/subtype_type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json', 'Class', 'api/basic_json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('format_as', 'Function', 'api/basic_json/format_as/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::accept', 'Function', 'api/basic_json/accept/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::array', 'Function', 'api/basic_json/array/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::array_t', 'Type', 'api/basic_json/array_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::as_base_class', 'Method', 'api/basic_json/as_base_class/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::at', 'Method', 'api/basic_json/at/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::back', 'Method', 'api/basic_json/back/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::basic_json', 'Constructor', 'api/basic_json/basic_json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::begin', 'Method', 'api/basic_json/begin/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::binary', 'Function', 'api/basic_json/binary/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::binary_t', 'Type', 'api/basic_json/binary_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::bjdata_version_t', 'Enum', 'api/basic_json/bjdata_version_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::boolean_t', 'Type', 'api/basic_json/boolean_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::cbegin', 'Method', 'api/basic_json/cbegin/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::cbor_tag_handler_t', 'Enum', 'api/basic_json/cbor_tag_handler_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::cend', 'Method', 'api/basic_json/cend/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::clear', 'Method', 'api/basic_json/clear/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::contains', 'Method', 'api/basic_json/contains/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::count', 'Method', 'api/basic_json/count/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::crbegin', 'Method', 'api/basic_json/crbegin/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::crend', 'Method', 'api/basic_json/crend/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::default_object_comparator_t', 'Type', 'api/basic_json/default_object_comparator_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::diff', 'Function', 'api/basic_json/diff/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::dump', 'Method', 'api/basic_json/dump/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::emplace', 'Method', 'api/basic_json/emplace/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::emplace_back', 'Method', 'api/basic_json/emplace_back/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::empty', 'Method', 'api/basic_json/empty/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::end', 'Method', 'api/basic_json/end/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::end_pos', 'Method', 'api/basic_json/end_pos/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::erase', 'Method', 'api/basic_json/erase/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::error_handler_t', 'Enum', 'api/basic_json/error_handler_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::exception', 'Class', 'api/basic_json/exception/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::find', 'Method', 'api/basic_json/find/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::flatten', 'Method', 'api/basic_json/flatten/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_bjdata', 'Function', 'api/basic_json/from_bjdata/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_bson', 'Function', 'api/basic_json/from_bson/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_cbor', 'Function', 'api/basic_json/from_cbor/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_msgpack', 'Function', 'api/basic_json/from_msgpack/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_bon8', 'Function', 'api/basic_json/from_bon8/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_ubjson', 'Function', 'api/basic_json/from_ubjson/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::front', 'Method', 'api/basic_json/front/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get', 'Method', 'api/basic_json/get/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_allocator', 'Function', 'api/basic_json/get_allocator/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_binary', 'Method', 'api/basic_json/get_binary/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_ptr', 'Method', 'api/basic_json/get_ptr/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_ref', 'Method', 'api/basic_json/get_ref/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_to', 'Method', 'api/basic_json/get_to/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::initializer_list_t', 'Type', 'api/basic_json/initializer_list_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::input_format_t', 'Enum', 'api/basic_json/input_format_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::insert', 'Method', 'api/basic_json/insert/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::invalid_iterator', 'Class', 'api/basic_json/invalid_iterator/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_array', 'Method', 'api/basic_json/is_array/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_binary', 'Method', 'api/basic_json/is_binary/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_boolean', 'Method', 'api/basic_json/is_boolean/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_discarded', 'Method', 'api/basic_json/is_discarded/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_null', 'Method', 'api/basic_json/is_null/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_number', 'Method', 'api/basic_json/is_number/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_number_float', 'Method', 'api/basic_json/is_number_float/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_number_integer', 'Method', 'api/basic_json/is_number_integer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_number_unsigned', 'Method', 'api/basic_json/is_number_unsigned/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_object', 'Method', 'api/basic_json/is_object/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_primitive', 'Method', 'api/basic_json/is_primitive/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_string', 'Method', 'api/basic_json/is_string/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_structured', 'Method', 'api/basic_json/is_structured/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::items', 'Method', 'api/basic_json/items/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::json_base_class_t', 'Type', 'api/basic_json/json_base_class_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::json_sax_t', 'Type', 'api/basic_json/json_sax_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::json_serializer', 'Class', 'api/basic_json/json_serializer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::max_size', 'Method', 'api/basic_json/max_size/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::merge_patch', 'Method', 'api/basic_json/merge_patch/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::meta', 'Function', 'api/basic_json/meta/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::number_float_t', 'Type', 'api/basic_json/number_float_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::number_integer_t', 'Type', 'api/basic_json/number_integer_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::number_unsigned_t', 'Type', 'api/basic_json/number_unsigned_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::object', 'Function', 'api/basic_json/object/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::object_comparator_t', 'Type', 'api/basic_json/object_comparator_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::object_t', 'Type', 'api/basic_json/object_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator ValueType', 'Operator', 'api/basic_json/operator_ValueType/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator value_t', 'Operator', 'api/basic_json/operator_value_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator[]', 'Operator', 'api/basic_json/operator[]/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator=', 'Operator', 'api/basic_json/operator=/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator+=', 'Operator', 'api/basic_json/operator+=/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator==', 'Operator', 'api/basic_json/operator_eq/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator!=', 'Operator', 'api/basic_json/operator_ne/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator<', 'Operator', 'api/basic_json/operator_lt/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator<=', 'Operator', 'api/basic_json/operator_le/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator>', 'Operator', 'api/basic_json/operator_gt/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator>=', 'Operator', 'api/basic_json/operator_ge/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator<=>', 'Operator', 'api/basic_json/operator_spaceship/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::out_of_range', 'Class', 'api/basic_json/out_of_range/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::other_error', 'Class', 'api/basic_json/other_error/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::parse', 'Function', 'api/basic_json/parse/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::parse_error', 'Class', 'api/basic_json/parse_error/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::parse_event_t', 'Enum', 'api/basic_json/parse_event_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::parser_callback_t', 'Type', 'api/basic_json/parser_callback_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::patch', 'Method', 'api/basic_json/patch/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::patch_inplace', 'Method', 'api/basic_json/patch_inplace/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::push_back', 'Method', 'api/basic_json/push_back/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::rbegin', 'Method', 'api/basic_json/rbegin/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::rend', 'Method', 'api/basic_json/rend/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::sax_parse', 'Function', 'api/basic_json/sax_parse/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::size', 'Method', 'api/basic_json/size/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::start_pos', 'Method', 'api/basic_json/start_pos/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::string_t', 'Type', 'api/basic_json/string_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::swap', 'Method', 'api/basic_json/swap/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::type', 'Method', 'api/basic_json/type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::type_error', 'Class', 'api/basic_json/type_error/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::type_name', 'Method', 'api/basic_json/type_name/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::unflatten', 'Method', 'api/basic_json/unflatten/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::update', 'Method', 'api/basic_json/update/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_bjdata', 'Function', 'api/basic_json/to_bjdata/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_bson', 'Function', 'api/basic_json/to_bson/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_cbor', 'Function', 'api/basic_json/to_cbor/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_msgpack', 'Function', 'api/basic_json/to_msgpack/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_bon8', 'Function', 'api/basic_json/to_bon8/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_string', 'Method', 'api/basic_json/to_string/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_ubjson', 'Function', 'api/basic_json/to_ubjson/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::value', 'Method', 'api/basic_json/value/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::value_t', 'Enum', 'api/basic_json/value_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::with_t', 'Type', 'api/basic_json/with_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::~basic_json', 'Method', 'api/basic_json/~basic_json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json', 'Class', 'api/json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer', 'Class', 'api/json_pointer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::back', 'Method', 'api/json_pointer/back/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::empty', 'Method', 'api/json_pointer/empty/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::front', 'Method', 'api/json_pointer/front/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::json_pointer', 'Constructor', 'api/json_pointer/json_pointer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator==', 'Operator', 'api/json_pointer/operator_eq/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator!=', 'Operator', 'api/json_pointer/operator_ne/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator/', 'Operator', 'api/json_pointer/operator_slash/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator/=', 'Operator', 'api/json_pointer/operator_slasheq/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator string_t', 'Operator', 'api/json_pointer/operator_string_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator<=>', 'Operator', 'api/json_pointer/operator_spaceship/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::parent_pointer', 'Method', 'api/json_pointer/parent_pointer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::pop_back', 'Method', 'api/json_pointer/pop_back/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::pop_front', 'Method', 'api/json_pointer/pop_front/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::push_back', 'Method', 'api/json_pointer/push_back/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::push_front', 'Method', 'api/json_pointer/push_front/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::string_t', 'Type', 'api/json_pointer/string_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::to_string', 'Method', 'api/json_pointer/to_string/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax', 'Class', 'api/json_sax/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::binary', 'Method', 'api/json_sax/binary/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::binary_t', 'Type', 'api/json_sax/binary_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::boolean', 'Method', 'api/json_sax/boolean/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::end_array', 'Method', 'api/json_sax/end_array/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::end_object', 'Method', 'api/json_sax/end_object/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::json_sax', 'Constructor', 'api/json_sax/json_sax/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::key', 'Method', 'api/json_sax/key/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::null', 'Method', 'api/json_sax/null/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_float', 'Method', 'api/json_sax/number_float/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_float_t', 'Type', 'api/json_sax/number_float_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_integer', 'Method', 'api/json_sax/number_integer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_integer_t', 'Type', 'api/json_sax/number_integer_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_unsigned', 'Method', 'api/json_sax/number_unsigned/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_unsigned_t', 'Type', 'api/json_sax/number_unsigned_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::operator=', 'Operator', 'api/json_sax/operator=/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::parse_error', 'Method', 'api/json_sax/parse_error/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::start_array', 'Method', 'api/json_sax/start_array/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::start_object', 'Method', 'api/json_sax/start_object/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::string', 'Method', 'api/json_sax/string/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::string_t', 'Type', 'api/json_sax/string_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::~json_sax', 'Method', 'api/json_sax/~json_sax/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('operator""_json', 'Literal', 'api/operator_literal_json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('operator""_json_pointer', 'Literal', 'api/operator_literal_json_pointer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('operator<<', 'Operator', 'api/operator_ltlt/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('operator>>', 'Operator', 'api/operator_gtgt/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_json', 'Class', 'api/ordered_json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map', 'Class', 'api/ordered_map/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::Container', 'Type', 'api/ordered_map/Container/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::at', 'Method', 'api/ordered_map/at/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::count', 'Method', 'api/ordered_map/count/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::emplace', 'Method', 'api/ordered_map/emplace/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::erase', 'Method', 'api/ordered_map/erase/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::find', 'Method', 'api/ordered_map/find/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::insert', 'Method', 'api/ordered_map/insert/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::key_compare', 'Type', 'api/ordered_map/key_compare/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::operator=', 'Operator', 'api/ordered_map/operator=/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::operator[]', 'Operator', 'api/ordered_map/operator[]/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::ordered_map', 'Constructor', 'api/ordered_map/ordered_map/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::~ordered_map', 'Method', 'api/ordered_map/~ordered_map/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('std::formatter<basic_json>', 'Class', 'api/basic_json/std_formatter/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('std::hash<basic_json>', 'Class', 'api/basic_json/std_hash/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('std::swap<basic_json>', 'Function', 'api/basic_json/std_swap/index.html');
-- Features
INSERT INTO searchIndex(name, type, path) VALUES ('Arbitrary Type Conversions', 'Guide', 'features/arbitrary_types/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats', 'Guide', 'features/binary_formats/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: BJData', 'Guide', 'features/binary_formats/bjdata/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: BSON', 'Guide', 'features/binary_formats/bson/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: CBOR', 'Guide', 'features/binary_formats/cbor/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: MessagePack', 'Guide', 'features/binary_formats/messagepack/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: BON8', 'Guide', 'features/binary_formats/bon8/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: UBJSON', 'Guide', 'features/binary_formats/ubjson/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Values', 'Guide', 'features/binary_values/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Comments', 'Guide', 'features/comments/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Element Access', 'Guide', 'features/element_access/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Element Access: Access with default value: value', 'Guide', 'features/element_access/default_value/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Element Access: Checked access: at', 'Guide', 'features/element_access/checked_access/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Element Access: Unchecked access: operator[]', 'Guide', 'features/element_access/unchecked_access/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Exceptions', 'Guide', 'home/exceptions/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Integration: Migration Guide', 'Guide', 'integration/migration_guide/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Integration: CMake', 'Guide', 'integration/cmake/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Integration: Header only', 'Guide', 'integration/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Integration: Package Managers', 'Guide', 'integration/package_managers/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Integration: Pkg-config', 'Guide', 'integration/pkg-config/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Iterators', 'Guide', 'features/iterators/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON Merge Patch', 'Guide', 'features/merge_patch/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON Patch and Diff', 'Guide', 'features/json_patch/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON Pointer', 'Guide', 'features/json_pointer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('nlohmann Namespace', 'Guide', 'features/namespace/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Types', 'Guide', 'features/types/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Types: Number Handling', 'Guide', 'features/types/number_handling/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Object Order', 'Guide', 'features/object_order/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Performance', 'Guide', 'features/performance/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing', 'Guide', 'features/parsing/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing: JSON Lines', 'Guide', 'features/parsing/json_lines/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing: Parser Callbacks', 'Guide', 'features/parsing/parser_callbacks/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing: Parsing and Exceptions', 'Guide', 'features/parsing/parse_exceptions/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing: SAX Interface', 'Guide', 'features/parsing/sax_interface/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing: Untrusted Input', 'Guide', 'features/parsing/untrusted_input/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Runtime Assertions', 'Guide', 'features/assertions/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Specializing enum conversion', 'Guide', 'features/enum_conversion/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Supported Macros', 'Guide', 'features/macros/index.html');
-- Macros
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_ASSERT', 'Macro', 'api/macros/json_assert/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_BRACE_INIT_COPY_SEMANTICS', 'Macro', 'api/macros/json_brace_init_copy_semantics/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_CATCH_USER', 'Macro', 'api/macros/json_throw_user/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DELETE_DEPRECATED_FUNCTIONS', 'Macro', 'api/macros/json_delete_deprecated_functions/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DIAGNOSTICS', 'Macro', 'api/macros/json_diagnostics/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DIAGNOSTIC_POSITIONS', 'Macro', 'api/macros/json_diagnostic_positions/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DISABLE_ENUM_SERIALIZATION', 'Macro', 'api/macros/json_disable_enum_serialization/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DISABLE_TUPLE_REFERENCE_CONVERSION', 'Macro', 'api/macros/json_disable_tuple_reference_conversion/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_11', 'Macro', 'api/macros/json_has_cpp_11/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_14', 'Macro', 'api/macros/json_has_cpp_11/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_17', 'Macro', 'api/macros/json_has_cpp_11/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_20', 'Macro', 'api/macros/json_has_cpp_11/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_23', 'Macro', 'api/macros/json_has_cpp_11/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_26', 'Macro', 'api/macros/json_has_cpp_11/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_EXPERIMENTAL_FILESYSTEM', 'Macro', 'api/macros/json_has_filesystem/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_FILESYSTEM', 'Macro', 'api/macros/json_has_filesystem/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_RANGES', 'Macro', 'api/macros/json_has_ranges/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_STATIC_RTTI', 'Macro', 'api/macros/json_has_static_rtti/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_STD_FORMAT', 'Macro', 'api/macros/json_has_std_format/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_THREE_WAY_COMPARISON', 'Macro', 'api/macros/json_has_three_way_comparison/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_NOEXCEPTION', 'Macro', 'api/macros/json_noexception/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_NO_AUTOMATIC_UDLS', 'Macro', 'api/macros/json_no_automatic_udls/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_NO_IO', 'Macro', 'api/macros/json_no_io/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_NO_THREAD_LOCAL', 'Macro', 'api/macros/json_no_thread_local/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_PRECISE_STREAM_POSITION', 'Macro', 'api/macros/json_precise_stream_position/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_SKIP_LIBRARY_VERSION_CHECK', 'Macro', 'api/macros/json_skip_library_version_check/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_SKIP_UNSUPPORTED_COMPILER_CHECK', 'Macro', 'api/macros/json_skip_unsupported_compiler_check/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_STRICT_BINARY_UTF8', 'Macro', 'api/macros/json_strict_binary_utf8/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_STRICT_NUL_HANDLING', 'Macro', 'api/macros/json_strict_nul_handling/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_THROW_USER', 'Macro', 'api/macros/json_throw_user/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_TRY_USER', 'Macro', 'api/macros/json_throw_user/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_GLOBAL_UDLS', 'Macro', 'api/macros/json_use_global_udls/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_IMPLICIT_CONVERSIONS', 'Macro', 'api/macros/json_use_implicit_conversions/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON', 'Macro', 'api/macros/json_use_legacy_discarded_value_comparison/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS', 'Macro', 'api/macros/json_use_objects_for_enum_keyed_maps/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_SIMDUTF', 'Macro', 'api/macros/json_use_simdutf/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Macros', 'Macro', 'api/macros/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_type_intrusive/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE', 'Macro', 'api/macros/nlohmann_define_type_intrusive/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT', 'Macro', 'api/macros/nlohmann_define_type_intrusive/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_type_non_intrusive/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE', 'Macro', 'api/macros/nlohmann_define_type_non_intrusive/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT', 'Macro', 'api/macros/nlohmann_define_type_non_intrusive/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_NAMESPACE', 'Macro', 'api/macros/nlohmann_json_namespace/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_NAMESPACE_BEGIN', 'Macro', 'api/macros/nlohmann_json_namespace_begin/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_NAMESPACE_END', 'Macro', 'api/macros/nlohmann_json_namespace_begin/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_NAMESPACE_NO_VERSION', 'Macro', 'api/macros/nlohmann_json_namespace_no_version/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_SERIALIZE_ENUM', 'Macro', 'api/macros/nlohmann_json_serialize_enum/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_SERIALIZE_ENUM_STRICT', 'Macro', 'api/macros/nlohmann_json_serialize_enum_strict/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_VERSION_MAJOR', 'Macro', 'api/macros/nlohmann_json_version_major/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_VERSION_MINOR', 'Macro', 'api/macros/nlohmann_json_version_major/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_VERSION_PATCH', 'Macro', 'api/macros/nlohmann_json_version_major/index.html');
+532
View File
@@ -0,0 +1,532 @@
#!/usr/bin/env python3
"""Generate the Dash docset search index from the mkdocs sources."""
import argparse
import glob
import hashlib
import html
import os
import re
import shutil
import sqlite3
import sys
import tarfile
import urllib.parse
import urllib.request
import yaml
HERE = os.path.dirname(os.path.abspath(__file__))
MKDOCS_YML = os.path.join(HERE, '..', 'mkdocs', 'mkdocs.yml')
PAGES = os.path.join(HERE, '..', 'mkdocs', 'docs')
# api pages whose (name, type) cannot be derived by the heuristics
OVERRIDES = {
}
DOCSET = 'JSON_for_Modern_C++.docset'
TITLE_SUFFIX = ' - JSON for Modern C++</title>'
# CSS rules appended to the stylesheet: hide navigation items and fix spacing
# hide the navigation (the documentation browser has its own); Material's class selectors would win over element
# selectors, hence the classes and !important
CSS_PATCH = (
'\n\n.md-header, .md-footer, .md-tabs, .md-sidebar--primary, .md-content__button { display: none !important; }'
'\n\n.md-sidebar--secondary, .md-main__inner { top: 0 !important; margin-top: 0 !important; }'
)
USER_AGENT = ('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/124.0 Safari/537.36')
CONTENT_TYPE_EXT = {'image/svg+xml': '.svg', 'image/png': '.png', 'image/jpeg': '.jpg',
'image/gif': '.gif', 'image/webp': '.webp'}
# remote loads that are allowed to remain (URL -> reason)
ALLOWED_REMOTE = {
# Material only loads this polyfill if the browser has no ResizeObserver
'https://unpkg.com/resize-observer-polyfill': 'fallback for browsers without ResizeObserver',
}
problems = []
def problem(page, reason) -> None:
"""Record a problem; all problems are reported at the end."""
problems.append(f'generate_docset.py: {page}: {reason}')
class Loader(yaml.SafeLoader):
"""YAML loader that tolerates the custom tags used in mkdocs.yml."""
Loader.add_multi_constructor('', lambda loader, suffix, node: None)
def walk_nav(items, groups=()):
"""Yield (group titles, nav title or None, md path) for all nav leaves."""
for item in items:
if isinstance(item, str):
yield list(groups), None, item
elif isinstance(item, dict):
for title, value in item.items():
if isinstance(value, list):
yield from walk_nav(value, groups + (str(title),))
else:
yield list(groups), str(title), value
def page_path(md_path) -> str:
"""Map a markdown path to the HTML path of the rendered page."""
if md_path.endswith('/index.md'):
return md_path[:-len('index.md')] + 'index.html'
return md_path[:-len('.md')] + '/index.html'
def read_page(md_path) -> str:
"""Read a page (resolving a snippet include), return '' if it does not exist."""
try:
with open(os.path.join(PAGES, md_path), encoding='utf-8') as f:
text = f.read()
m = re.match(r'--8<-- "(.+)"\s*$', text)
if m:
with open(os.path.join(PAGES, m.group(1)), encoding='utf-8') as f:
text = f.read()
return text
except OSError:
return ''
def clean(text) -> str:
"""Strip tags and entities from a heading and normalize whitespace."""
text = text.replace('\\>', '\x00') # escaped '>' is not the end of a tag
text = html.unescape(re.sub(r'</?[a-zA-Z][^>]*>', '', text)).replace('\x00', '>')
return re.sub(r'\s+', ' ', text).strip()
def strip_fences(text) -> str:
"""Remove fenced code blocks (their lines may start with '# ')."""
return re.sub(r'^(```|~~~).*?^\1[^\n]*$', '', text, flags=re.M | re.S)
def get_h1(text):
"""Return the cleaned first H1 of a page or None."""
body = strip_fences(text)
m = re.search(r'^# (.+)$', body, flags=re.M)
if m:
return clean(m.group(1))
m = re.search(r'<h1>(.*?)</h1>', body, flags=re.S)
return clean(m.group(1)) if m else None
def split_top_level(text, seps=','):
"""Split at separators that are not nested in <>, () or []."""
parts, depth, current = [], 0, ''
for c in text:
if c in '<([':
depth += 1
elif c in '>)]':
depth -= 1
if c in seps and depth <= 0:
parts.append(current)
current = ''
else:
current += c
parts.append(current)
return [p.strip() for p in parts if p.strip()]
OPERATOR_SYMBOLS = ('<=>', '<<', '>>', '<=', '>=', '<', '>')
def api_names(h1) -> list:
"""Derive the entry names from the H1 of an api page."""
# hide the angle brackets of operators from the nesting detection
for i, sym in enumerate(OPERATOR_SYMBOLS):
h1 = h1.replace('operator' + sym, f'operator\x01{i}\x01')
names = []
for part in split_top_level(h1):
name = part.replace('\\', '').replace('nlohmann::', '')
name = re.sub(r'\x01(\d)\x01', lambda m: OPERATOR_SYMBOLS[int(m.group(1))], name)
# drop qualifiers like "operator<<(basic_json)", but keep "operator()"
if not name.endswith('operator()'):
name = re.sub(r'(?<=\w|[<>=!+\-*/\[\]])\([^()]*\)$', '', name)
if name not in names:
names.append(name)
return names
def first_cpp_block(text):
"""Return the first ```cpp block following the H1."""
m = re.search(r'^# .*?^```cpp\n(.*?)^```', text, flags=re.M | re.S)
return m.group(1) if m else None
def api_type(name, decl):
"""Determine the Dash entry type from name and declaration."""
parts = name.split('::')
last = parts[-1]
if name.startswith('operator""'):
return 'Literal'
if last.startswith('operator'):
return 'Operator'
if decl is None:
return None
if re.search(r'\benum\b', decl):
return 'Enum'
if re.search(r'^\s*(template\s*<.*>\s*)?(class|struct)\s+\w+\s*(final\b|[:{;<]|$)', decl, flags=re.M):
return 'Class'
if re.search(r'\busing\s+\w+\s*=', decl) or 'typedef' in decl:
return 'Type'
if len(parts) > 1 and last == parts[-2]:
return 'Constructor'
if last.startswith('~'):
return 'Method'
if re.search(r'\bstatic\b', decl) or len(parts) == 1 or parts[0] == 'std':
return 'Function'
return 'Method'
def macro_names(h1) -> list:
"""Split the H1 of a macro page into macro names."""
return [n.strip() for n in re.split(r'[,/]', h1) if n.strip()]
def api_entries(md_path):
"""Return the (name, type) pairs of an api page."""
if md_path in OVERRIDES:
return OVERRIDES[md_path]
if md_path == 'api/macros/index.md':
return [('Macros', 'Macro')]
text = read_page(md_path)
h1 = get_h1(text)
if not h1:
problem(md_path, 'no H1 found')
return []
if md_path.startswith('api/macros/'):
return [(n, 'Macro') for n in macro_names(h1)]
names = api_names(h1)
if not names:
problem(md_path, 'no names found')
return []
decl = first_cpp_block(text)
result = []
for name in names:
kind = api_type(name, decl)
if kind is None:
problem(md_path, f'no type determinable for {name}')
else:
result.append((name, kind))
return result
def guide_entries(nav):
"""Yield (name, type, md path) for all non-api nav pages."""
for groups, title, md_path in walk_nav(nav):
if md_path.startswith('api/') or md_path == 'index.md':
continue
if not md_path.endswith('.md'):
continue
groups = groups[1:] # drop the top-level tab
if title is None:
title = get_h1(read_page(md_path))
if not title:
problem(md_path, 'no title found')
continue
# "Parsing: Parsing Untrusted Input" -> "Parsing: Untrusted Input"
if groups and title.startswith(groups[-1] + ' ') and title[len(groups[-1]) + 1:][:1].isupper():
title = title[len(groups[-1]) + 1:]
if md_path.endswith('/index.md') and groups and title == groups[-1]:
name = ': '.join(groups)
else:
name = ': '.join(groups + [title])
yield name, 'Guide', md_path
def build_entries() -> list:
"""Return the sorted list of (name, type, path) index entries."""
nav = load_mkdocs_yml()['nav']
entries = set()
for name, kind, md_path in guide_entries(nav):
entries.add((name, kind, page_path(md_path)))
api_root = os.path.join(PAGES, 'api')
on_disk = set()
for root, _, files in os.walk(api_root):
for file in files:
if file.endswith('.md'):
rel = os.path.relpath(os.path.join(root, file), PAGES)
on_disk.add(rel.replace(os.sep, '/'))
for _, _, md_path in walk_nav(nav):
if md_path.startswith('api/') and md_path not in on_disk:
problem(md_path, 'listed in nav but missing on disk')
for md_path in sorted(on_disk):
for name, kind in api_entries(md_path):
entries.add((name, kind, page_path(md_path)))
return sorted(entries)
def write_index(entries, out) -> None:
"""Write the entries into a SQLite search index."""
if os.path.exists(out):
os.remove(out)
con = sqlite3.connect(out)
con.execute('CREATE TABLE searchIndex(id INTEGER PRIMARY KEY, name TEXT, type TEXT, path TEXT)')
con.execute('CREATE UNIQUE INDEX anchor ON searchIndex (name, type, path)')
con.executemany('INSERT INTO searchIndex(name, type, path) VALUES (?, ?, ?)', entries)
con.commit()
con.close()
def html_files(root):
"""Yield all HTML files below root."""
for base, _, files in os.walk(root):
for file in files:
if file.endswith('.html'):
yield os.path.join(base, file)
def read(path) -> str:
with open(path, encoding='utf-8') as f:
return f.read()
def write(path, text) -> None:
with open(path, 'w', encoding='utf-8') as f:
f.write(text)
def remove_source_widget(docs) -> None:
"""Drop data-md-component=source so Material does not query api.github.com for the stars and version."""
pattern = re.compile(r'\sdata-md-component=(?:"source"|source\b)')
for path in html_files(docs):
text = read(path)
new = pattern.sub('', text)
if new != text:
write(path, new)
def patch_titles(docs, entries) -> None:
"""Strip the site name from all titles; use the index names where available."""
names = {}
for name, _, path in entries:
names.setdefault(path, []).append(name)
for file in html_files(docs):
rel = os.path.relpath(file, docs).replace(os.sep, '/')
text = read(file).replace(TITLE_SUFFIX, '</title>')
if rel in names:
title = html.escape(', '.join(names[rel]), quote=False)
text = re.sub(r'<title>.*?</title>', lambda _: f'<title>{title}</title>', text, count=1, flags=re.S)
write(file, text)
IMG_RE = re.compile(r'<img\b[^>]*>', re.I)
ATTR_RE = r'''(?:{0})\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+))'''
def attr(tag, name):
"""Return the value of an attribute in a tag or None."""
m = re.search(r'(?<![\w-])' + ATTR_RE.format(name), tag, flags=re.I)
return next(g for g in m.groups() if g is not None) if m else None
def is_remote(url) -> bool:
return re.match(r'(https?:)?//', url.strip(), flags=re.I) is not None
def download(url, docs) -> str:
"""Download url into assets/external and return the path relative to docs."""
u = urllib.parse.urlparse(url if not url.startswith('//') else 'https:' + url)
if u.scheme.lower() not in ('http', 'https'):
raise ValueError(f'not an http(s) URL: {url}')
req = urllib.request.Request(u.geturl(), headers={'User-Agent': USER_AGENT})
# (the scheme is checked above)
with urllib.request.urlopen(req, timeout=20) as r: # nosec B310
data = r.read()
ctype = r.headers.get_content_type()
path = urllib.parse.unquote(u.path).lstrip('/')
ext = os.path.splitext(path)[1]
if u.query or not ext or path.endswith('/'):
digest = hashlib.sha1(url.encode(), usedforsecurity=False).hexdigest()[:12]
path = os.path.join(os.path.dirname(path), digest + CONTENT_TYPE_EXT.get(ctype, ext or '.bin'))
rel = os.path.normpath(os.path.join('assets', 'external', u.hostname, path))
out = os.path.join(docs, rel)
os.makedirs(os.path.dirname(out), exist_ok=True)
with open(out, 'wb') as f:
f.write(data)
return rel.replace(os.sep, '/')
def localize_images(docs) -> None:
"""Download remote images and rewrite their src; drop them on failure."""
cache = {}
for file in html_files(docs):
text = read(file)
def repl(m):
tag = m.group(0)
src = attr(tag, 'src')
if src is None or not is_remote(src):
return tag
if src not in cache:
try:
cache[src] = download(src, docs)
except Exception as e: # noqa: BLE001
print(f'generate_docset.py: warning: cannot download {src}: {e}', file=sys.stderr)
cache[src] = None
if cache[src] is None:
return attr(tag, 'alt') or ''
local = os.path.relpath(os.path.join(docs, cache[src]), os.path.dirname(file))
local = local.replace(os.sep, '/')
return re.sub(ATTR_RE.format('src'), lambda _: f'src="{local}"', tag, count=1, flags=re.I)
new = IMG_RE.sub(repl, text)
if new != text:
write(file, new)
def load_mkdocs_yml() -> dict:
"""Load mkdocs.yml, ignoring tags like !ENV and !!python/name."""
with open(MKDOCS_YML, encoding='utf-8') as f:
# (Loader is a yaml.SafeLoader)
return yaml.load(f, Loader=Loader) # nosec B506
def localize_site_urls(docs, site_url) -> None:
"""Load assets that the theme's JavaScript references by absolute site URL from the docset.
The privacy plugin rewrites the mermaid loader to "<site_url>assets/external/unpkg.com/mermaid@11/...", so the
docset would fetch mermaid from the live site. __md_scope is the site root that Material defines in every page.
"""
pattern = re.compile(r'"' + re.escape(site_url) + r'(assets/[^"]*)"')
for base, _, files in os.walk(docs):
for file in (f for f in files if f.endswith('.js')):
path = os.path.join(base, file)
text = read(path)
new = pattern.sub(r'new URL("\1",__md_scope).href', text)
if new != text:
write(path, new)
def remote_loads(docs, site_url) -> list:
"""Return 'file: url' strings for resources that would be loaded remotely."""
found = []
cdn = re.compile(r'https://(?:unpkg\.com|cdn\.jsdelivr\.net|cdnjs\.cloudflare\.com|'
r'fonts\.googleapis\.com|fonts\.gstatic\.com)/[^\s"\'`)\\]*')
css_url = re.compile(r'url\(\s*["\']?((?:https?:)?//[^)"\']+)', re.I)
css_import = re.compile(r'@import\s+(?:url\(\s*)?["\']?((?:https?:)?//[^)"\'; ]+)', re.I)
for base, _, files in os.walk(docs):
for file in files:
path = os.path.join(base, file)
rel = os.path.relpath(path, docs)
urls = []
if file.endswith('.html'):
text = read(path)
for m in re.finditer(r'<[a-zA-Z][^>]*>', text):
tag = m.group(0)
for name in ('src', 'poster'):
v = attr(tag, name)
if v and is_remote(v):
urls.append(v)
v = attr(tag, 'srcset')
if v:
urls += [c.split()[0] for c in v.split(',') if c.strip() and is_remote(c.strip())]
if re.match(r'<link\b', tag, flags=re.I):
rel_attr = (attr(tag, 'rel') or '').lower()
v = attr(tag, 'href')
if v and is_remote(v) and re.search(r'stylesheet|icon|preload|modulepreload|manifest', rel_attr):
urls.append(v)
urls += css_url.findall(text) + css_import.findall(text)
elif file.endswith('.css'):
text = read(path)
urls += css_url.findall(text) + css_import.findall(text)
elif file.endswith('.js'):
text = read(path)
urls += cdn.findall(text)
urls += re.findall(re.escape(site_url) + r'assets/[^\s"\'`)\\]*', text)
found += [f'{rel}: {u}' for u in urls if u not in ALLOWED_REMOTE]
return found
def make_docset(site, out_dir) -> int:
entries = build_entries()
if problems:
print('\n'.join(problems), file=sys.stderr)
return 1
docset = os.path.join(out_dir, DOCSET)
docs = os.path.join(docset, 'Contents', 'Resources', 'Documents')
if os.path.exists(docset):
shutil.rmtree(docset)
shutil.copytree(site, docs)
for icon in ('icon.png', 'icon@2x.png'):
shutil.copy(os.path.join(HERE, icon), docset)
shutil.copy(os.path.join(HERE, 'Info.plist'), os.path.join(docset, 'Contents'))
write_index(entries, os.path.join(docset, 'Contents', 'Resources', 'docSet.dsidx'))
# patch CSS to hide navigation items and fix spacing
css = glob.glob(os.path.join(docs, 'assets', 'stylesheets', 'main.*.min.css'))
if len(css) != 1:
print(f'generate_docset.py: expected exactly one main.*.min.css, found {len(css)}', file=sys.stderr)
return 1
with open(css[0], 'a', encoding='utf-8') as f:
f.write(CSS_PATCH)
patch_titles(docs, entries)
remove_source_widget(docs)
for sitemap in glob.glob(os.path.join(docs, 'sitemap.*')):
os.remove(sitemap)
# make the docset self-contained
site_url = load_mkdocs_yml()['site_url']
localize_images(docs)
localize_site_urls(docs, site_url)
remote = remote_loads(docs, site_url)
if remote:
print('generate_docset.py: remote resources remain in the docset:', file=sys.stderr)
print('\n'.join(' ' + r for r in remote), file=sys.stderr)
return 1
return 0
def make_tgz(out_dir) -> int:
docset = os.path.join(out_dir, DOCSET)
if not os.path.isdir(docset):
print(f'generate_docset.py: {docset} does not exist', file=sys.stderr)
return 1
with tarfile.open(os.path.join(out_dir, 'JSON_for_Modern_C++.tgz'), 'w:gz') as tar:
tar.add(docset, arcname=DOCSET, filter=lambda i: None if os.path.basename(i.name) == '.DS_Store' else i)
return 0
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
sub = parser.add_subparsers(dest='command', required=True)
sub.add_parser('list', help='print the index entries as TSV')
p = sub.add_parser('index', help='write the SQLite search index')
p.add_argument('out')
p = sub.add_parser('docset', help='build the docset from a built mkdocs site')
p.add_argument('site_dir')
p.add_argument('out_dir')
p = sub.add_parser('tgz', help='pack the docset into a tarball')
p.add_argument('out_dir')
args = parser.parse_args()
if args.command == 'docset':
return make_docset(args.site_dir, args.out_dir)
if args.command == 'tgz':
return make_tgz(args.out_dir)
entries = build_entries()
if problems:
print('\n'.join(problems), file=sys.stderr)
return 1
if args.command == 'list':
for entry in entries:
print('\t'.join(entry))
elif args.command == 'index':
write_index(entries, args.out)
return 0
if __name__ == '__main__':
sys.exit(main())
+1
View File
@@ -17,6 +17,7 @@ build: style_check
style_check:
@cd docs ; ../venv/bin/python3 ../scripts/check_structure.py
@venv/bin/python3 ../docset/generate_docset.py list > /dev/null
# check that all Mermaid diagrams parse (needs Node.js)
# This target is used in the CI (ci_test_documentation_mermaid).
@@ -475,10 +475,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.
@@ -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)).
+3 -1
View File
@@ -123,4 +123,6 @@ Linear in the size of the JSON value `j`.
that is not valid UTF-8 unchanged, as before; `strict` (the default if
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316`.
- Throws `type_error.321` for a discarded value since version 3.13.0; previously, a discarded value nested in an
array or object was silently skipped, producing invalid BJData.
array or object was silently skipped, producing invalid BJData.
- Writes unsigned integers wider than 64 bits as high-precision numbers since version 3.13.0; previously, they were
silently truncated to 64 bits.
+3 -2
View File
@@ -37,8 +37,9 @@ With (2), the bytes written before the exception remain in the output adapter.
## Exceptions
- Throws [out_of_range.407](../../home/exceptions.md#jsonexceptionout_of_range407) if `j` contains an unsigned integer
above 9223372036854775807, which BON8 cannot represent
- Throws [out_of_range.407](../../home/exceptions.md#jsonexceptionout_of_range407) if `j` contains an integer outside
the range of int64 (an unsigned integer above 9223372036854775807, or, with a number type wider than 64 bits, any
integer beyond int64), which BON8 cannot represent
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if `j` contains a string that is not
valid UTF-8
@@ -47,6 +47,10 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
- Throws [`type_error.317`](../../home/exceptions.md#jsonexceptiontype_error317) if the top-level type of the JSON value
is not an object; example: `"to serialize to BSON, top-level type must be object, but is string"`
- Throws [`out_of_range.407`](../../home/exceptions.md#jsonexceptionout_of_range407) if `j` contains a signed integer
outside the range of int64 or an unsigned integer outside the range of uint64, which is only possible with a number
type wider than 64 bits; example:
`"integer number 9223372036854775808 cannot be represented by BSON as it does not fit int64"`
- Throws [`out_of_range.409`](../../home/exceptions.md#jsonexceptionout_of_range409) if a key in the JSON object contains
a null byte (code point U+0000); example: `"BSON key cannot contain code point U+0000 (at byte 2)"`
- Throws [`out_of_range.412`](../../home/exceptions.md#jsonexceptionout_of_range412) if the length of a document, array,
@@ -119,3 +123,5 @@ pass before anything is written.
that is not valid UTF-8 unchanged, as before; `strict` (the default if
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316` before anything
is written.
- Throws `out_of_range.407` for integers that do not fit 64 bits since version 3.13.0; previously, integers of a
number type wider than 64 bits were silently truncated.
@@ -46,6 +46,9 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
## Exceptions
- Throws [`out_of_range.407`](../../home/exceptions.md#jsonexceptionout_of_range407) if `j` contains an integer
outside [-2^64, 2^64-1], which is only possible with a number type wider than 64 bits; example:
`"integer number 18446744073709551616 cannot be represented by CBOR as it does not fit [-2^64, 2^64-1]"`
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is
not valid UTF-8 and `error_handler` is `strict` (the default only if
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled)
@@ -90,3 +93,5 @@ Linear in the size of the JSON value `j`.
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316`.
- Throws `type_error.321` for a discarded value since version 3.13.0; previously, a discarded value nested in an
array or object was silently skipped, producing invalid CBOR.
- Throws `out_of_range.407` for integers that do not fit 64 bits since version 3.13.0; previously, integers of a
number type wider than 64 bits were silently truncated.
@@ -46,6 +46,9 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
## Exceptions
- Throws [`out_of_range.407`](../../home/exceptions.md#jsonexceptionout_of_range407) if `j` contains an integer
outside [-2^63, 2^64-1], which is only possible with a number type wider than 64 bits; example:
`"integer number 18446744073709551616 cannot be represented by MessagePack as it does not fit [-2^63, 2^64-1]"`
- Throws [`out_of_range.412`](../../home/exceptions.md#jsonexceptionout_of_range412) if the length of a string, binary
value, array, or object exceeds 4294967295, the maximum MessagePack can store; example:
`"MessagePack length 4294967296 exceeds maximum of 4294967295"`
@@ -112,3 +115,5 @@ Linear in the size of the JSON value `j`.
`number_unsigned_t`.
- Throws `type_error.321` for a discarded value since version 3.13.0; previously, a discarded value nested in an
array or object was silently skipped, producing invalid MessagePack.
- Throws `out_of_range.407` for integers that do not fit 64 bits since version 3.13.0; previously, integers of a
number type wider than 64 bits were silently truncated.
@@ -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.
-30
View File
@@ -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.
-33
View File
@@ -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.
-30
View File
@@ -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.
+4 -4
View File
@@ -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).
-61
View File
@@ -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.
-53
View File
@@ -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.
-75
View File
@@ -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.
-56
View File
@@ -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,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,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
-10
View File
@@ -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
+3 -3
View File
@@ -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
+2 -2
View File
@@ -88,10 +88,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
+2 -2
View File
@@ -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).
Everything else lives in [`detail/`](https://github.com/nlohmann/json/tree/develop/include/nlohmann/detail) and namespace `nlohmann::detail`, which is not part of the public API. Paths
below are relative to `include/nlohmann`.
@@ -97,7 +97,7 @@ is generated from these files with `make amalgamate` and must not be edited by h
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
+26 -6
View File
@@ -906,14 +906,34 @@ double-precision number when `number_float_t` is `#!cpp float`.
### json.exception.out_of_range.407
This exception previously indicated that the UBJSON and BSON binary formats did not support integer numbers greater than
9223372036854775807 due to limitations in the implemented mapping. However, these limitations have since been resolved,
and this exception no longer occurs.
An integer number cannot be represented by the binary format it is serialized to:
!!! success "Exception cannot occur any more"
- [BON8](../features/binary_formats/bon8.md) only stores integers that fit into int64.
- [CBOR](../features/binary_formats/cbor.md), [MessagePack](../features/binary_formats/msgpack.md), and
[BSON](../features/binary_formats/bson.md) store integers in at most 64 bits. With the default number types, every
integer fits, but a [`number_integer_t`](../api/basic_json/number_integer_t.md) or
[`number_unsigned_t`](../api/basic_json/number_unsigned_t.md) wider than 64 bits (e.g., `__int128`) can hold values
outside the range of the format: [-2^64, 2^64-1] for CBOR, [-2^63, 2^64-1] for MessagePack, and the range of int64
(signed integers) or uint64 (unsigned integers) for BSON.
- Since version 3.9.0, integer numbers beyond int64 are serialized as high-precision UBJSON numbers.
- Since version 3.12.0, integer numbers beyond int64 are serialized as uint64 BSON numbers.
[UBJSON](../features/binary_formats/ubjson.md) and [BJData](../features/binary_formats/bjdata.md) never throw this
exception, because they serialize integers beyond 64 bits as high-precision numbers.
!!! failure "Example messages"
```
integer number 9223372036854775808 cannot be represented by BON8 as it does not fit int64
```
```
integer number 1267650600228229401496703205376 cannot be represented by CBOR as it does not fit [-2^64, 2^64-1]
```
!!! note
Before version 3.13.0, CBOR, MessagePack, and BSON silently truncated integers wider than 64 bits, and BJData
truncated unsigned integers wider than 64 bits. This exception was previously thrown by UBJSON and BSON for
integers greater than 9223372036854775807; since version 3.9.0, such integers are serialized as high-precision
UBJSON numbers, and since version 3.12.0 as uint64 BSON numbers.
### json.exception.out_of_range.408
+1 -2
View File
@@ -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
View File
@@ -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
@@ -124,7 +123,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
@@ -163,7 +161,6 @@ nav:
- 'get_to': api/basic_json/get_to.md
- 'std::formatter&lt;basic_json&gt;': api/basic_json/std_formatter.md
- 'std::hash&lt;basic_json&gt;': 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
@@ -182,7 +179,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
@@ -241,13 +237,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
@@ -274,46 +268,25 @@ 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
- 'operator<<(basic_json), operator<<(json_pointer)': api/operator_ltlt.md
- 'operator>>(basic_json)': api/operator_gtgt.md
- 'operator""_json': api/operator_literal_json.md
- 'operator""_json_pointer': api/operator_literal_json_pointer.md
- 'ordered_json': api/ordered_json.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
-31
View File
@@ -288,36 +288,6 @@ def check_header_links() -> None:
f'link to "{match.group(0)}" does not point to a documentation page')
def check_docset() -> None:
"""Every API page and every macro has an entry in the docset index; no entry points to a missing page."""
entry_re = re.compile(r"VALUES \('((?:[^']|'')*)', '(\w+)', '([^']*)'\);")
names_by_path = {}
with open("../../docset/docSet.sql", encoding="utf-8") as sql:
for name, _, path in entry_re.findall(sql.read()):
names_by_path.setdefault(path, set()).add(name.replace("''", "'"))
def to_path(page):
if os.path.basename(page) == "index.md":
return page[:-len("index.md")] + "index.html"
return page[:-len(".md")] + "/index.html"
pages = sorted(glob.glob("**/*.md", recursive=True))
for path in sorted(set(names_by_path) - {to_path(p) for p in pages}):
report("docset/stale_entry", "../../docset/docSet.sql", f'entry "{path}" has no documentation page')
for page in (p for p in pages if p.startswith("api/")):
names = names_by_path.get(to_path(page))
if not names:
report("docset/missing_entry", page, "page has no entry in docs/docset/docSet.sql")
elif page.startswith("api/macros/") and os.path.basename(page) != "index.md":
with open(page, encoding="utf-8") as content:
text = content.read()
match = re.search(r"^# (.+)$", text, re.MULTILINE) or re.search(r"<h1>(.*?)</h1>", text, re.DOTALL)
title = re.sub(r"<[^>]+>|\s+", " ", match.group(1))
for macro in filter(None, (x.strip() for x in re.split(r"[,/]", title))):
if macro not in names:
report("docset/missing_macro", page, f'macro "{macro}" has no entry in docs/docset/docSet.sql')
if __name__ == "__main__":
print(120 * "-")
check_structure()
@@ -327,7 +297,6 @@ if __name__ == "__main__":
check_heading_levels()
check_image_alt_text()
check_header_links()
check_docset()
print(120 * "-")
if warnings > 0:
@@ -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);
@@ -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;
};
-1
View File
@@ -57,7 +57,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
+135 -15
View File
@@ -207,6 +207,10 @@ class binary_writer
{
if (j.m_data.m_value.number_integer >= 0)
{
if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::uint64_t>(j.m_data.m_value.number_integer)))
{
throw_integer_out_of_range(j, "CBOR", "[-2^64, 2^64-1]");
}
// CBOR does not differentiate between positive signed
// integers and unsigned integers
write_cbor_head(0x00, static_cast<std::uint64_t>(j.m_data.m_value.number_integer));
@@ -214,6 +218,10 @@ class binary_writer
else
{
// a negative integer n is encoded as -1 - n
if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::uint64_t>(-1 - j.m_data.m_value.number_integer)))
{
throw_integer_out_of_range(j, "CBOR", "[-2^64, 2^64-1]");
}
write_cbor_head(0x20, static_cast<std::uint64_t>(-1 - j.m_data.m_value.number_integer));
}
break;
@@ -221,7 +229,11 @@ class binary_writer
case value_t::number_unsigned:
{
write_cbor_head(0x00, j.m_data.m_value.number_unsigned);
if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::uint64_t>(j.m_data.m_value.number_unsigned)))
{
throw_integer_out_of_range(j, "CBOR", "uint64");
}
write_cbor_head(0x00, static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned));
break;
}
@@ -429,12 +441,20 @@ class binary_writer
{
if (j.m_data.m_value.number_integer >= 0)
{
if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::uint64_t>(j.m_data.m_value.number_integer)))
{
throw_integer_out_of_range(j, "MessagePack", "[-2^63, 2^64-1]");
}
// MessagePack does not differentiate between positive
// signed integers and unsigned integers.
write_msgpack_unsigned(static_cast<std::uint64_t>(j.m_data.m_value.number_integer));
}
else
{
if (JSON_HEDLEY_UNLIKELY(!integer_fits_min<std::int64_t>(j.m_data.m_value.number_integer)))
{
throw_integer_out_of_range(j, "MessagePack", "[-2^63, 2^64-1]");
}
if (j.m_data.m_value.number_integer >= -32)
{
// negative fixnum
@@ -473,6 +493,10 @@ class binary_writer
case value_t::number_unsigned:
{
if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::uint64_t>(j.m_data.m_value.number_unsigned)))
{
throw_integer_out_of_range(j, "MessagePack", "uint64");
}
write_msgpack_unsigned(static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned));
break;
}
@@ -834,6 +858,84 @@ class binary_writer
JSON_THROW(type_error::create(321, concat("cannot serialize discarded value to ", format_name), &j));
}
/*!
@brief whether integer @a n does not exceed the maximum of @a TargetType
The binary formats store integers in at most 64 bits, but number_integer_t
and number_unsigned_t may be wider (e.g., __int128). The range of the
integer is taken from std::numeric_limits, so a number type whose range
fits into @a TargetType is never checked: the function is then constant
true, and the default int64_t/uint64_t types pay nothing.
*/
template<typename TargetType, typename NumberType>
static constexpr bool integer_fits_max(const NumberType n) noexcept
{
return integer_fits_max<TargetType>(n, std::integral_constant < bool,
(std::numeric_limits<NumberType>::digits > std::numeric_limits<TargetType>::digits) > ());
}
template<typename TargetType, typename NumberType>
static constexpr bool integer_fits_max(const NumberType /*unused*/, std::false_type /*may_exceed*/) noexcept
{
return true;
}
template<typename TargetType, typename NumberType>
static constexpr bool integer_fits_max(const NumberType n, std::true_type /*may_exceed*/) noexcept
{
// NumberType has more digits than TargetType, so it can hold its maximum
return n <= static_cast<NumberType>((std::numeric_limits<TargetType>::max)());
}
/*!
@brief whether integer @a n is not below the minimum of @a TargetType;
constant true if NumberType cannot hold such a value
*/
template<typename TargetType, typename NumberType>
static constexpr bool integer_fits_min(const NumberType n) noexcept
{
return integer_fits_min<TargetType>(n, std::integral_constant < bool, std::numeric_limits<NumberType>::is_signed &&
(!std::numeric_limits<TargetType>::is_signed || std::numeric_limits<NumberType>::digits > std::numeric_limits<TargetType>::digits) > ());
}
template<typename TargetType, typename NumberType>
static constexpr bool integer_fits_min(const NumberType /*unused*/, std::false_type /*may_fall_below*/) noexcept
{
return true;
}
template<typename TargetType, typename NumberType>
static constexpr bool integer_fits_min(const NumberType n, std::true_type /*may_fall_below*/) noexcept
{
// NumberType is signed and either TargetType is unsigned (minimum 0)
// or NumberType has more digits, so it can hold the minimum
return n >= static_cast<NumberType>((std::numeric_limits<TargetType>::min)());
}
/*!
@brief whether integer @a n lies in the range of @a TargetType
*/
template<typename TargetType, typename NumberType>
static constexpr bool integer_fits(const NumberType n) noexcept
{
return integer_fits_min<TargetType>(n) && integer_fits_max<TargetType>(n);
}
/*!
@brief throws because the integer @a j is too large for @a format_name
@throw out_of_range.407 always
*/
JSON_HEDLEY_NO_RETURN static void throw_integer_out_of_range(const BasicJsonType& j, const char* format_name, const char* range)
{
// dump() rather than std::to_string(), which has no overload for
// integer types wider than 64 bits
const auto number = j.dump();
static_cast<void>(number); // unused when JSON_NOEXCEPTION is defined
static_cast<void>(format_name);
static_cast<void>(range);
JSON_THROW(out_of_range::create(407, concat("integer number ", std::string(number.begin(), number.end()), " cannot be represented by ", format_name, " as it does not fit ", range), &j));
}
void write_msgpack_array_prefix(const std::size_t N, const BasicJsonType& j)
{
const auto n = to_msgpack_length(N, j);
@@ -1590,6 +1692,8 @@ class binary_writer
is neither an object nor an array
@throw out_of_range.415 if @a j is binary with a subtype that does not fit
into a byte, before anything is written
@throw out_of_range.407 if @a j is an integer that does not fit the 64 bits
of a BSON integer, before anything is written
@throw type_error.316 if @a j is a string that is not valid UTF-8, before
anything is written
@throw type_error.321 if @a j is discarded
@@ -1608,10 +1712,18 @@ class binary_writer
return 8ul;
case value_t::number_integer:
return calc_bson_integer_size(j.m_data.m_value.number_integer);
if (JSON_HEDLEY_UNLIKELY(!integer_fits<std::int64_t>(j.m_data.m_value.number_integer)))
{
throw_integer_out_of_range(j, "BSON", "int64");
}
return calc_bson_integer_size(static_cast<std::int64_t>(j.m_data.m_value.number_integer));
case value_t::number_unsigned:
return calc_bson_unsigned_size(j.m_data.m_value.number_unsigned);
if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::uint64_t>(j.m_data.m_value.number_unsigned)))
{
throw_integer_out_of_range(j, "BSON", "uint64");
}
return calc_bson_unsigned_size(static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned));
case value_t::string:
return calc_bson_string_size(*j.m_data.m_value.string, j);
@@ -1649,11 +1761,12 @@ class binary_writer
case value_t::number_float:
return write_bson_double(name, j.m_data.m_value.number_float);
// calc_bson_value_size() checked that integers fit 64 bits
case value_t::number_integer:
return write_bson_integer(name, j.m_data.m_value.number_integer);
return write_bson_integer(name, static_cast<std::int64_t>(j.m_data.m_value.number_integer));
case value_t::number_unsigned:
return write_bson_unsigned(name, j.m_data.m_value.number_unsigned);
return write_bson_unsigned(name, static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned));
case value_t::string:
return write_bson_string(name, *j.m_data.m_value.string);
@@ -2108,7 +2221,7 @@ class binary_writer
{
return 'L';
}
if (use_bjdata && std::is_unsigned<NumberType>::value)
if (use_bjdata && std::is_unsigned<NumberType>::value && integer_fits_max<std::uint64_t>(n))
{
return 'M';
}
@@ -2233,14 +2346,16 @@ class binary_writer
@brief checks whether a JSON number fits into @a TargetType
@param[in] el a JSON number of either the signed or unsigned integer kind
@return whether @a el's value can be represented by @a TargetType without
wrapping, regardless of which of the two kinds it is stored as
wrapping, regardless of which of the two kinds it is stored as;
false for a value that does not even fit the 64-bit type it is
read as (possible for number types wider than 64 bits)
*/
template<typename TargetType>
static bool bjdata_ndarray_value_in_range(const BasicJsonType& el)
{
return el.is_number_unsigned()
? value_in_range_of<TargetType>(el.template get<std::uint64_t>())
: value_in_range_of<TargetType>(el.template get<std::int64_t>());
? integer_fits_max<std::uint64_t>(el.m_data.m_value.number_unsigned) && value_in_range_of<TargetType>(el.template get<std::uint64_t>())
: integer_fits<std::int64_t>(el.m_data.m_value.number_integer) && value_in_range_of<TargetType>(el.template get<std::int64_t>());
}
/*!
@@ -2472,10 +2587,11 @@ class binary_writer
for (const auto& el : dims)
{
// a dimension is read as an unsigned value below, so anything that
// is not a non-negative integer is rejected: a non-integer entry
// would pun unrelated bytes as the dimension, and a negative one
// would wrap into a nonsensical length
if (!el.is_number_integer() || (!el.is_number_unsigned() && el.template get<std::int64_t>() < 0))
// is not a non-negative integer in the range of std::uint64_t is
// rejected: a non-integer entry would pun unrelated bytes as the
// dimension, and a negative or wider one would wrap into a
// nonsensical length
if (!el.is_number_integer() || !bjdata_ndarray_value_in_range<std::uint64_t>(el))
{
return true;
}
@@ -2589,9 +2705,9 @@ class binary_writer
case value_t::number_unsigned:
{
if (j.m_data.m_value.number_unsigned > static_cast<typename BasicJsonType::number_unsigned_t>((std::numeric_limits<std::int64_t>::max)()))
if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::int64_t>(j.m_data.m_value.number_unsigned)))
{
JSON_THROW(out_of_range::create(407, concat("integer number ", std::to_string(j.m_data.m_value.number_unsigned), " cannot be represented by BON8 as it does not fit int64"), &j));
throw_integer_out_of_range(j, "BON8", "int64");
}
write_bon8_integer(static_cast<std::int64_t>(j.m_data.m_value.number_unsigned));
string_open = false;
@@ -2600,6 +2716,10 @@ class binary_writer
case value_t::number_integer:
{
if (JSON_HEDLEY_UNLIKELY(!integer_fits<std::int64_t>(j.m_data.m_value.number_integer)))
{
throw_integer_out_of_range(j, "BON8", "int64");
}
write_bon8_integer(static_cast<std::int64_t>(j.m_data.m_value.number_integer));
string_open = false;
break;
+6 -4
View File
@@ -14,7 +14,6 @@
#include <cstdint> // uint8_t
#include <limits> // numeric_limits
#include <string> // string
#include <type_traits> // is_signed
#include <nlohmann/detail/macro_scope.hpp>
#if JSON_HAS_THREE_WAY_COMPARISON
@@ -147,13 +146,16 @@ FloatType compare_integer_with_float(const IntegerType i, const FloatType f) noe
// values of IntegerType lie in [-bound, bound) when signed and in
// [0, bound) when unsigned; digits excludes the sign bit, so bound is a
// power of two that the float represents exactly
const FloatType bound = std::ldexp(static_cast<FloatType>(1), std::numeric_limits<IntegerType>::digits);
// power of two that the float represents exactly; the signedness comes
// from numeric_limits as well, because std::is_signed is false for class
// types such as 128-bit or multiprecision integers
using limits = std::numeric_limits<IntegerType>;
const FloatType bound = std::ldexp(static_cast<FloatType>(1), limits::digits);
if (f >= bound)
{
return ordered(-1);
}
if (std::is_signed<IntegerType>::value ? (f < -bound) : (f < static_cast<FloatType>(0)))
if (limits::is_signed ? (f < -bound) : (f < static_cast<FloatType>(0)))
{
return ordered(1);
}
+1 -41
View File
@@ -205,34 +205,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>;
////////////////////////////////////////////////////////////////////////////////
@@ -438,19 +429,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
@@ -2518,7 +2505,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 >
@@ -3104,8 +3090,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)
@@ -3149,8 +3133,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>
@@ -3177,7 +3159,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,
@@ -3188,7 +3169,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)
@@ -3253,7 +3233,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>>,
@@ -3562,14 +3541,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
{
@@ -3747,7 +3724,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
@@ -3762,7 +3738,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
@@ -4130,7 +4105,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)
@@ -5647,7 +5621,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,
@@ -5689,7 +5662,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,
@@ -6136,7 +6108,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))
@@ -6152,7 +6123,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,
@@ -6192,7 +6162,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))
@@ -6207,7 +6176,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,
@@ -6246,7 +6214,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))
@@ -6261,7 +6228,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,
@@ -6376,7 +6342,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))
@@ -6391,7 +6356,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,
@@ -6420,7 +6384,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)
@@ -6439,7 +6402,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
@@ -6458,7 +6420,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)
@@ -6477,7 +6438,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
View File
@@ -34,41 +34,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)
@@ -79,7 +68,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)));
@@ -115,7 +103,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)
@@ -129,7 +116,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>
@@ -144,13 +130,11 @@ public:
return {std::prev(this->end()), true};
}
/// @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)
@@ -158,13 +142,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
@@ -172,7 +154,6 @@ public:
return at(std::forward<KeyType>(key));
}
/// @sa https://json.nlohmann.me/api/ordered_map/at/
T& at(const key_type& key)
{
const auto it = find_impl(*this, key);
@@ -183,7 +164,6 @@ public:
return it->second;
}
/// @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)
@@ -196,7 +176,6 @@ public:
return it->second;
}
/// @sa https://json.nlohmann.me/api/ordered_map/at/
const T& at(const key_type& key) const
{
const auto it = find_impl(*this, key);
@@ -207,7 +186,6 @@ public:
return it->second;
}
/// @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)
@@ -220,7 +198,6 @@ public:
return it->second;
}
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
size_type erase(const key_type& key)
{
const auto it = find_impl(*this, key);
@@ -232,7 +209,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)
@@ -246,13 +222,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)
@@ -309,13 +283,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)
@@ -323,13 +295,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)
@@ -337,13 +307,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)
@@ -351,13 +319,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);
@@ -373,7 +339,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)
{
+142 -124
View File
@@ -284,7 +284,6 @@
#include <cstdint> // uint8_t
#include <limits> // numeric_limits
#include <string> // string
#include <type_traits> // is_signed
// #include <nlohmann/detail/macro_scope.hpp>
// __ _____ _____ _____
@@ -3363,13 +3362,16 @@ FloatType compare_integer_with_float(const IntegerType i, const FloatType f) noe
// values of IntegerType lie in [-bound, bound) when signed and in
// [0, bound) when unsigned; digits excludes the sign bit, so bound is a
// power of two that the float represents exactly
const FloatType bound = std::ldexp(static_cast<FloatType>(1), std::numeric_limits<IntegerType>::digits);
// power of two that the float represents exactly; the signedness comes
// from numeric_limits as well, because std::is_signed is false for class
// types such as 128-bit or multiprecision integers
using limits = std::numeric_limits<IntegerType>;
const FloatType bound = std::ldexp(static_cast<FloatType>(1), limits::digits);
if (f >= bound)
{
return ordered(-1);
}
if (std::is_signed<IntegerType>::value ? (f < -bound) : (f < static_cast<FloatType>(0)))
if (limits::is_signed ? (f < -bound) : (f < static_cast<FloatType>(0)))
{
return ordered(1);
}
@@ -7412,9 +7414,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/
@@ -7446,14 +7446,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);
@@ -12465,21 +12463,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;
@@ -12487,7 +12479,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;
@@ -12495,7 +12486,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;
@@ -12503,7 +12493,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;
@@ -12512,7 +12501,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;
@@ -12521,7 +12509,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;
@@ -12530,7 +12517,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;
@@ -12539,7 +12525,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;
@@ -12548,14 +12533,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;
@@ -12564,14 +12547,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;
@@ -12581,23 +12562,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;
};
@@ -20003,7 +19977,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
@@ -21853,6 +21826,10 @@ class binary_writer
{
if (j.m_data.m_value.number_integer >= 0)
{
if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::uint64_t>(j.m_data.m_value.number_integer)))
{
throw_integer_out_of_range(j, "CBOR", "[-2^64, 2^64-1]");
}
// CBOR does not differentiate between positive signed
// integers and unsigned integers
write_cbor_head(0x00, static_cast<std::uint64_t>(j.m_data.m_value.number_integer));
@@ -21860,6 +21837,10 @@ class binary_writer
else
{
// a negative integer n is encoded as -1 - n
if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::uint64_t>(-1 - j.m_data.m_value.number_integer)))
{
throw_integer_out_of_range(j, "CBOR", "[-2^64, 2^64-1]");
}
write_cbor_head(0x20, static_cast<std::uint64_t>(-1 - j.m_data.m_value.number_integer));
}
break;
@@ -21867,7 +21848,11 @@ class binary_writer
case value_t::number_unsigned:
{
write_cbor_head(0x00, j.m_data.m_value.number_unsigned);
if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::uint64_t>(j.m_data.m_value.number_unsigned)))
{
throw_integer_out_of_range(j, "CBOR", "uint64");
}
write_cbor_head(0x00, static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned));
break;
}
@@ -22075,12 +22060,20 @@ class binary_writer
{
if (j.m_data.m_value.number_integer >= 0)
{
if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::uint64_t>(j.m_data.m_value.number_integer)))
{
throw_integer_out_of_range(j, "MessagePack", "[-2^63, 2^64-1]");
}
// MessagePack does not differentiate between positive
// signed integers and unsigned integers.
write_msgpack_unsigned(static_cast<std::uint64_t>(j.m_data.m_value.number_integer));
}
else
{
if (JSON_HEDLEY_UNLIKELY(!integer_fits_min<std::int64_t>(j.m_data.m_value.number_integer)))
{
throw_integer_out_of_range(j, "MessagePack", "[-2^63, 2^64-1]");
}
if (j.m_data.m_value.number_integer >= -32)
{
// negative fixnum
@@ -22119,6 +22112,10 @@ class binary_writer
case value_t::number_unsigned:
{
if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::uint64_t>(j.m_data.m_value.number_unsigned)))
{
throw_integer_out_of_range(j, "MessagePack", "uint64");
}
write_msgpack_unsigned(static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned));
break;
}
@@ -22480,6 +22477,84 @@ class binary_writer
JSON_THROW(type_error::create(321, concat("cannot serialize discarded value to ", format_name), &j));
}
/*!
@brief whether integer @a n does not exceed the maximum of @a TargetType
The binary formats store integers in at most 64 bits, but number_integer_t
and number_unsigned_t may be wider (e.g., __int128). The range of the
integer is taken from std::numeric_limits, so a number type whose range
fits into @a TargetType is never checked: the function is then constant
true, and the default int64_t/uint64_t types pay nothing.
*/
template<typename TargetType, typename NumberType>
static constexpr bool integer_fits_max(const NumberType n) noexcept
{
return integer_fits_max<TargetType>(n, std::integral_constant < bool,
(std::numeric_limits<NumberType>::digits > std::numeric_limits<TargetType>::digits) > ());
}
template<typename TargetType, typename NumberType>
static constexpr bool integer_fits_max(const NumberType /*unused*/, std::false_type /*may_exceed*/) noexcept
{
return true;
}
template<typename TargetType, typename NumberType>
static constexpr bool integer_fits_max(const NumberType n, std::true_type /*may_exceed*/) noexcept
{
// NumberType has more digits than TargetType, so it can hold its maximum
return n <= static_cast<NumberType>((std::numeric_limits<TargetType>::max)());
}
/*!
@brief whether integer @a n is not below the minimum of @a TargetType;
constant true if NumberType cannot hold such a value
*/
template<typename TargetType, typename NumberType>
static constexpr bool integer_fits_min(const NumberType n) noexcept
{
return integer_fits_min<TargetType>(n, std::integral_constant < bool, std::numeric_limits<NumberType>::is_signed &&
(!std::numeric_limits<TargetType>::is_signed || std::numeric_limits<NumberType>::digits > std::numeric_limits<TargetType>::digits) > ());
}
template<typename TargetType, typename NumberType>
static constexpr bool integer_fits_min(const NumberType /*unused*/, std::false_type /*may_fall_below*/) noexcept
{
return true;
}
template<typename TargetType, typename NumberType>
static constexpr bool integer_fits_min(const NumberType n, std::true_type /*may_fall_below*/) noexcept
{
// NumberType is signed and either TargetType is unsigned (minimum 0)
// or NumberType has more digits, so it can hold the minimum
return n >= static_cast<NumberType>((std::numeric_limits<TargetType>::min)());
}
/*!
@brief whether integer @a n lies in the range of @a TargetType
*/
template<typename TargetType, typename NumberType>
static constexpr bool integer_fits(const NumberType n) noexcept
{
return integer_fits_min<TargetType>(n) && integer_fits_max<TargetType>(n);
}
/*!
@brief throws because the integer @a j is too large for @a format_name
@throw out_of_range.407 always
*/
JSON_HEDLEY_NO_RETURN static void throw_integer_out_of_range(const BasicJsonType& j, const char* format_name, const char* range)
{
// dump() rather than std::to_string(), which has no overload for
// integer types wider than 64 bits
const auto number = j.dump();
static_cast<void>(number); // unused when JSON_NOEXCEPTION is defined
static_cast<void>(format_name);
static_cast<void>(range);
JSON_THROW(out_of_range::create(407, concat("integer number ", std::string(number.begin(), number.end()), " cannot be represented by ", format_name, " as it does not fit ", range), &j));
}
void write_msgpack_array_prefix(const std::size_t N, const BasicJsonType& j)
{
const auto n = to_msgpack_length(N, j);
@@ -23236,6 +23311,8 @@ class binary_writer
is neither an object nor an array
@throw out_of_range.415 if @a j is binary with a subtype that does not fit
into a byte, before anything is written
@throw out_of_range.407 if @a j is an integer that does not fit the 64 bits
of a BSON integer, before anything is written
@throw type_error.316 if @a j is a string that is not valid UTF-8, before
anything is written
@throw type_error.321 if @a j is discarded
@@ -23254,10 +23331,18 @@ class binary_writer
return 8ul;
case value_t::number_integer:
return calc_bson_integer_size(j.m_data.m_value.number_integer);
if (JSON_HEDLEY_UNLIKELY(!integer_fits<std::int64_t>(j.m_data.m_value.number_integer)))
{
throw_integer_out_of_range(j, "BSON", "int64");
}
return calc_bson_integer_size(static_cast<std::int64_t>(j.m_data.m_value.number_integer));
case value_t::number_unsigned:
return calc_bson_unsigned_size(j.m_data.m_value.number_unsigned);
if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::uint64_t>(j.m_data.m_value.number_unsigned)))
{
throw_integer_out_of_range(j, "BSON", "uint64");
}
return calc_bson_unsigned_size(static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned));
case value_t::string:
return calc_bson_string_size(*j.m_data.m_value.string, j);
@@ -23295,11 +23380,12 @@ class binary_writer
case value_t::number_float:
return write_bson_double(name, j.m_data.m_value.number_float);
// calc_bson_value_size() checked that integers fit 64 bits
case value_t::number_integer:
return write_bson_integer(name, j.m_data.m_value.number_integer);
return write_bson_integer(name, static_cast<std::int64_t>(j.m_data.m_value.number_integer));
case value_t::number_unsigned:
return write_bson_unsigned(name, j.m_data.m_value.number_unsigned);
return write_bson_unsigned(name, static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned));
case value_t::string:
return write_bson_string(name, *j.m_data.m_value.string);
@@ -23754,7 +23840,7 @@ class binary_writer
{
return 'L';
}
if (use_bjdata && std::is_unsigned<NumberType>::value)
if (use_bjdata && std::is_unsigned<NumberType>::value && integer_fits_max<std::uint64_t>(n))
{
return 'M';
}
@@ -23879,14 +23965,16 @@ class binary_writer
@brief checks whether a JSON number fits into @a TargetType
@param[in] el a JSON number of either the signed or unsigned integer kind
@return whether @a el's value can be represented by @a TargetType without
wrapping, regardless of which of the two kinds it is stored as
wrapping, regardless of which of the two kinds it is stored as;
false for a value that does not even fit the 64-bit type it is
read as (possible for number types wider than 64 bits)
*/
template<typename TargetType>
static bool bjdata_ndarray_value_in_range(const BasicJsonType& el)
{
return el.is_number_unsigned()
? value_in_range_of<TargetType>(el.template get<std::uint64_t>())
: value_in_range_of<TargetType>(el.template get<std::int64_t>());
? integer_fits_max<std::uint64_t>(el.m_data.m_value.number_unsigned) && value_in_range_of<TargetType>(el.template get<std::uint64_t>())
: integer_fits<std::int64_t>(el.m_data.m_value.number_integer) && value_in_range_of<TargetType>(el.template get<std::int64_t>());
}
/*!
@@ -24118,10 +24206,11 @@ class binary_writer
for (const auto& el : dims)
{
// a dimension is read as an unsigned value below, so anything that
// is not a non-negative integer is rejected: a non-integer entry
// would pun unrelated bytes as the dimension, and a negative one
// would wrap into a nonsensical length
if (!el.is_number_integer() || (!el.is_number_unsigned() && el.template get<std::int64_t>() < 0))
// is not a non-negative integer in the range of std::uint64_t is
// rejected: a non-integer entry would pun unrelated bytes as the
// dimension, and a negative or wider one would wrap into a
// nonsensical length
if (!el.is_number_integer() || !bjdata_ndarray_value_in_range<std::uint64_t>(el))
{
return true;
}
@@ -24235,9 +24324,9 @@ class binary_writer
case value_t::number_unsigned:
{
if (j.m_data.m_value.number_unsigned > static_cast<typename BasicJsonType::number_unsigned_t>((std::numeric_limits<std::int64_t>::max)()))
if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::int64_t>(j.m_data.m_value.number_unsigned)))
{
JSON_THROW(out_of_range::create(407, concat("integer number ", std::to_string(j.m_data.m_value.number_unsigned), " cannot be represented by BON8 as it does not fit int64"), &j));
throw_integer_out_of_range(j, "BON8", "int64");
}
write_bon8_integer(static_cast<std::int64_t>(j.m_data.m_value.number_unsigned));
string_open = false;
@@ -24246,6 +24335,10 @@ class binary_writer
case value_t::number_integer:
{
if (JSON_HEDLEY_UNLIKELY(!integer_fits<std::int64_t>(j.m_data.m_value.number_integer)))
{
throw_integer_out_of_range(j, "BON8", "int64");
}
write_bon8_integer(static_cast<std::int64_t>(j.m_data.m_value.number_integer));
string_open = false;
break;
@@ -27596,41 +27689,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)
@@ -27641,7 +27723,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)));
@@ -27677,7 +27758,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)
@@ -27691,7 +27771,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>
@@ -27706,13 +27785,11 @@ public:
return {std::prev(this->end()), true};
}
/// @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)
@@ -27720,13 +27797,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
@@ -27734,7 +27809,6 @@ public:
return at(std::forward<KeyType>(key));
}
/// @sa https://json.nlohmann.me/api/ordered_map/at/
T& at(const key_type& key)
{
const auto it = find_impl(*this, key);
@@ -27745,7 +27819,6 @@ public:
return it->second;
}
/// @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)
@@ -27758,7 +27831,6 @@ public:
return it->second;
}
/// @sa https://json.nlohmann.me/api/ordered_map/at/
const T& at(const key_type& key) const
{
const auto it = find_impl(*this, key);
@@ -27769,7 +27841,6 @@ public:
return it->second;
}
/// @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)
@@ -27782,7 +27853,6 @@ public:
return it->second;
}
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
size_type erase(const key_type& key)
{
const auto it = find_impl(*this, key);
@@ -27794,7 +27864,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)
@@ -27808,13 +27877,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)
@@ -27871,13 +27938,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)
@@ -27885,13 +27950,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)
@@ -27899,13 +27962,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)
@@ -27913,13 +27974,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);
@@ -27935,7 +27994,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)
{
@@ -28139,34 +28197,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>;
////////////////////////////////////////////////////////////////////////////////
@@ -28372,19 +28421,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
@@ -30452,7 +30497,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 >
@@ -31038,8 +31082,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)
@@ -31083,8 +31125,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>
@@ -31111,7 +31151,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,
@@ -31122,7 +31161,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)
@@ -31187,7 +31225,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>>,
@@ -31496,14 +31533,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
{
@@ -31681,7 +31716,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
@@ -31696,7 +31730,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
@@ -32064,7 +32097,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)
@@ -33581,7 +33613,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,
@@ -33623,7 +33654,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,
@@ -34070,7 +34100,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))
@@ -34086,7 +34115,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,
@@ -34126,7 +34154,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))
@@ -34141,7 +34168,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,
@@ -34180,7 +34206,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))
@@ -34195,7 +34220,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,
@@ -34310,7 +34334,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))
@@ -34325,7 +34348,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,
@@ -34354,7 +34376,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)
@@ -34373,7 +34394,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
@@ -34392,7 +34412,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)
@@ -34411,7 +34430,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
+350
View File
@@ -0,0 +1,350 @@
// __ _____ _____ _____
// __| | __| | | | 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 <cstdint>
#include <limits>
#include <map>
#include <string>
#include <type_traits>
#include <vector>
#if JSON_HAS_THREE_WAY_COMPARISON
#include <compare>
#endif
namespace custom_number_types
{
// a signed integer of class type: std::is_signed is only true for arithmetic
// types, so the library has to take the signedness from std::numeric_limits,
// as it does for 128-bit and multiprecision integer classes such as
// absl::int128 or boost::multiprecision::cpp_int
class class_int
{
public:
// trivial, like the 128-bit integer classes: the value is stored in a union
class_int() = default;
// implicit from built-in integers and explicit from floats, like absl::int128
template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
class_int(T v) : value(static_cast<std::int64_t>(v)) {} // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)
template<typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
explicit class_int(T v) : value(static_cast<std::int64_t>(v)) {}
template<typename T, typename std::enable_if<std::is_arithmetic<T>::value, int>::type = 0>
explicit operator T() const
{
return static_cast<T>(value);
}
friend bool operator==(class_int lhs, class_int rhs)
{
return lhs.value == rhs.value;
}
friend bool operator!=(class_int lhs, class_int rhs)
{
return lhs.value != rhs.value;
}
friend bool operator<(class_int lhs, class_int rhs)
{
return lhs.value < rhs.value;
}
#if JSON_HAS_THREE_WAY_COMPARISON
friend std::strong_ordering operator<=>(class_int lhs, class_int rhs) // *NOPAD*
{
return lhs.value <=> rhs.value; // *NOPAD*
}
#endif
private:
std::int64_t value;
};
} // namespace custom_number_types
using custom_number_types::class_int;
namespace std
{
// only the members the library uses
template<>
class numeric_limits<class_int>
{
public:
static constexpr bool is_signed = true;
static constexpr int digits = std::numeric_limits<std::int64_t>::digits;
};
} // namespace std
namespace
{
using class_int_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, class_int, std::uint64_t, double>;
class_int_json make_class_int(std::int64_t v)
{
class_int_json j(class_int_json::value_t::number_integer);
j.get_ref<class_int&>() = class_int(v);
return j;
}
} // namespace
// the serializer cannot print an integer of class type, so doctest must not
// try when an assertion fails
namespace doctest
{
template<>
struct StringMaker<class_int_json>
{
static String convert(const class_int_json& j)
{
return j.type_name();
}
};
} // namespace doctest
// __int128 as number type needs the std::numeric_limits (for the range checks)
// and std::is_integral (for the serializer) specializations, which libc++
// always provides and libstdc++ only outside strict ISO modes; the MSVC
// standard library has none
#if defined(__SIZEOF_INT128__) && (defined(_LIBCPP_VERSION) || defined(__GLIBCXX_TYPE_INT_N_0))
#define JSON_TEST_INT128_NUMBER_TYPES 1
#else
#define JSON_TEST_INT128_NUMBER_TYPES 0
#endif
#if JSON_TEST_INT128_NUMBER_TYPES
namespace
{
// __extension__ keeps -Wpedantic from flagging the non-standard type
__extension__ typedef __int128 int128; // NOLINT(modernize-use-using)
__extension__ typedef unsigned __int128 uint128; // NOLINT(modernize-use-using)
static_assert(std::numeric_limits<int128>::digits == 127 && std::numeric_limits<uint128>::digits == 128 &&
std::is_integral<int128>::value && std::is_integral<uint128>::value,
"__int128 is not fully supported by the standard library");
using wide_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, int128, uint128, double>;
wide_json make_int(int128 v)
{
wide_json j(wide_json::value_t::number_integer);
j.get_ref<int128&>() = v;
return j;
}
wide_json make_uint(uint128 v)
{
wide_json j(wide_json::value_t::number_unsigned);
j.get_ref<uint128&>() = v;
return j;
}
wide_json wrap(const wide_json& value)
{
wide_json o(wide_json::value_t::object);
o["v"] = value;
return o;
}
} // namespace
TEST_CASE("custom number types: integers beyond 64 bits")
{
using out_of_range = wide_json::out_of_range;
const int128 two_64 = static_cast<int128>(1) << 64;
const int128 two_100 = static_cast<int128>(1) << 100;
const int128 max_int64 = (std::numeric_limits<std::int64_t>::max)();
const int128 min_int64 = (std::numeric_limits<std::int64_t>::min)();
const uint128 max_uint64 = (std::numeric_limits<std::uint64_t>::max)();
// before the range checks, these values were silently truncated, e.g.,
// 2^100 was written as 0
const wide_json int_big = make_int(two_100);
const wide_json int_big_negative = make_int(-two_100);
const wide_json uint_big = make_uint(static_cast<uint128>(two_100));
std::vector<std::uint8_t> _;
SECTION("CBOR")
{
CHECK_THROWS_WITH_AS(_ = wide_json::to_cbor(int_big), "[json.exception.out_of_range.407] integer number 1267650600228229401496703205376 cannot be represented by CBOR as it does not fit [-2^64, 2^64-1]", out_of_range&);
CHECK_THROWS_WITH_AS(_ = wide_json::to_cbor(int_big_negative), "[json.exception.out_of_range.407] integer number -1267650600228229401496703205376 cannot be represented by CBOR as it does not fit [-2^64, 2^64-1]", out_of_range&);
CHECK_THROWS_WITH_AS(_ = wide_json::to_cbor(uint_big), "[json.exception.out_of_range.407] integer number 1267650600228229401496703205376 cannot be represented by CBOR as it does not fit uint64", out_of_range&);
CHECK_THROWS_AS(_ = wide_json::to_cbor(make_int(two_64)), out_of_range&);
CHECK_THROWS_AS(_ = wide_json::to_cbor(make_int(-two_64 - 1)), out_of_range&);
CHECK_THROWS_AS(_ = wide_json::to_cbor(make_uint(static_cast<uint128>(max_uint64) + 1)), out_of_range&);
// CBOR's integers cover [-2^64, 2^64-1]
CHECK(wide_json::to_cbor(make_int(two_64 - 1)) == std::vector<std::uint8_t>({0x1B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}));
CHECK(wide_json::to_cbor(make_int(-two_64)) == std::vector<std::uint8_t>({0x3B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}));
CHECK(wide_json::to_cbor(make_uint(max_uint64)) == std::vector<std::uint8_t>({0x1B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}));
}
SECTION("MessagePack")
{
CHECK_THROWS_WITH_AS(_ = wide_json::to_msgpack(int_big), "[json.exception.out_of_range.407] integer number 1267650600228229401496703205376 cannot be represented by MessagePack as it does not fit [-2^63, 2^64-1]", out_of_range&);
CHECK_THROWS_WITH_AS(_ = wide_json::to_msgpack(int_big_negative), "[json.exception.out_of_range.407] integer number -1267650600228229401496703205376 cannot be represented by MessagePack as it does not fit [-2^63, 2^64-1]", out_of_range&);
CHECK_THROWS_WITH_AS(_ = wide_json::to_msgpack(uint_big), "[json.exception.out_of_range.407] integer number 1267650600228229401496703205376 cannot be represented by MessagePack as it does not fit uint64", out_of_range&);
CHECK_THROWS_AS(_ = wide_json::to_msgpack(make_int(two_64)), out_of_range&);
CHECK_THROWS_AS(_ = wide_json::to_msgpack(make_int(min_int64 - 1)), out_of_range&);
// MessagePack's integers cover [-2^63, 2^64-1]
CHECK(wide_json::to_msgpack(make_int(two_64 - 1)) == std::vector<std::uint8_t>({0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}));
CHECK(wide_json::to_msgpack(make_int(min_int64)) == std::vector<std::uint8_t>({0xD3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
CHECK(wide_json::to_msgpack(make_uint(max_uint64)) == std::vector<std::uint8_t>({0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}));
}
SECTION("BSON")
{
CHECK_THROWS_WITH_AS(_ = wide_json::to_bson(wrap(int_big)), "[json.exception.out_of_range.407] integer number 1267650600228229401496703205376 cannot be represented by BSON as it does not fit int64", out_of_range&);
CHECK_THROWS_WITH_AS(_ = wide_json::to_bson(wrap(int_big_negative)), "[json.exception.out_of_range.407] integer number -1267650600228229401496703205376 cannot be represented by BSON as it does not fit int64", out_of_range&);
CHECK_THROWS_WITH_AS(_ = wide_json::to_bson(wrap(uint_big)), "[json.exception.out_of_range.407] integer number 1267650600228229401496703205376 cannot be represented by BSON as it does not fit uint64", out_of_range&);
CHECK_THROWS_AS(_ = wide_json::to_bson(wrap(make_int(max_int64 + 1))), out_of_range&);
CHECK_THROWS_AS(_ = wide_json::to_bson(wrap(make_int(min_int64 - 1))), out_of_range&);
// nested values are checked as well, before anything is written
wide_json nested(wide_json::value_t::object);
nested["a"] = wide_json::array({wrap(int_big)});
std::vector<std::uint8_t> out;
CHECK_THROWS_AS(wide_json::to_bson(nested, out), out_of_range&);
CHECK(out.empty());
CHECK(wide_json::from_bson(wide_json::to_bson(wrap(make_int(max_int64)))) == wrap(make_int(max_int64)));
CHECK(wide_json::from_bson(wide_json::to_bson(wrap(make_int(min_int64)))) == wrap(make_int(min_int64)));
CHECK_NOTHROW(wide_json::to_bson(wrap(make_uint(max_uint64))));
}
SECTION("BON8")
{
CHECK_THROWS_WITH_AS(_ = wide_json::to_bon8(int_big), "[json.exception.out_of_range.407] integer number 1267650600228229401496703205376 cannot be represented by BON8 as it does not fit int64", out_of_range&);
CHECK_THROWS_WITH_AS(_ = wide_json::to_bon8(int_big_negative), "[json.exception.out_of_range.407] integer number -1267650600228229401496703205376 cannot be represented by BON8 as it does not fit int64", out_of_range&);
CHECK_THROWS_WITH_AS(_ = wide_json::to_bon8(uint_big), "[json.exception.out_of_range.407] integer number 1267650600228229401496703205376 cannot be represented by BON8 as it does not fit int64", out_of_range&);
CHECK_THROWS_AS(_ = wide_json::to_bon8(make_int(max_int64 + 1)), out_of_range&);
CHECK_THROWS_AS(_ = wide_json::to_bon8(make_int(min_int64 - 1)), out_of_range&);
CHECK(wide_json::from_bon8(wide_json::to_bon8(make_int(max_int64))) == make_int(max_int64));
CHECK(wide_json::from_bon8(wide_json::to_bon8(make_int(min_int64))) == make_int(min_int64));
}
SECTION("UBJSON and BJData")
{
// integers beyond 64 bits are written exactly as high-precision numbers
const std::string digits = "1267650600228229401496703205376";
std::vector<std::uint8_t> expected = {'H', 'i', static_cast<std::uint8_t>(digits.size())};
expected.insert(expected.end(), digits.begin(), digits.end());
CHECK(wide_json::to_ubjson(int_big) == expected);
CHECK(wide_json::to_ubjson(uint_big) == expected);
CHECK(wide_json::to_bjdata(int_big) == expected);
// BJData's uint64 marker 'M' was used for any unsigned value beyond
// int64, which truncated the ones beyond 64 bits
CHECK(wide_json::to_bjdata(uint_big) == expected);
// the uint64 marker is still used where the value fits
CHECK(wide_json::to_bjdata(make_uint(max_uint64)) == std::vector<std::uint8_t>({'M', 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}));
// the elements of an optimized container are written the same way;
// BJData does not allow 'H' as the type of an optimized container
std::vector<std::uint8_t> expected_ubjson_array = {'[', '$', 'H', '#', 'i', 2};
std::vector<std::uint8_t> expected_bjdata_array = {'[', '#', 'i', 2};
for (int i = 0; i < 2; ++i)
{
expected_ubjson_array.insert(expected_ubjson_array.end(), expected.begin() + 1, expected.end());
expected_bjdata_array.insert(expected_bjdata_array.end(), expected.begin(), expected.end());
}
CHECK(wide_json::to_ubjson(wide_json::array({uint_big, uint_big}), true, true) == expected_ubjson_array);
CHECK(wide_json::to_bjdata(wide_json::array({uint_big, uint_big}), true, true) == expected_bjdata_array);
}
SECTION("BJData ND-array")
{
// an ND-array element beyond 64 bits does not fit any dtype, so the
// annotated object is written as a plain object instead of truncating
// the element into range
wide_json element(wide_json::value_t::object);
element["_ArrayType_"] = "uint8";
element["_ArraySize_"] = wide_json::array({make_uint(2), make_uint(1)});
element["_ArrayData_"] = wide_json::array({make_uint(1), make_uint(static_cast<uint128>(two_64) + 1)});
const auto element_bytes = wide_json::to_bjdata(element, true, true);
REQUIRE(!element_bytes.empty());
CHECK(element_bytes[0] == '{');
wide_json signed_element = element;
signed_element["_ArrayType_"] = "int8";
signed_element["_ArrayData_"] = wide_json::array({make_int(1), make_int(-two_64 + 1)});
const auto signed_element_bytes = wide_json::to_bjdata(signed_element, true, true);
REQUIRE(!signed_element_bytes.empty());
CHECK(signed_element_bytes[0] == '{');
// the same for a dimension beyond 64 bits, which wrapped into a
// dimension matching the size of _ArrayData_ (here: 1)
wide_json dimension = element;
dimension["_ArraySize_"] = wide_json::array({make_uint(2), make_uint(static_cast<uint128>(two_64) + 1)});
dimension["_ArrayData_"] = wide_json::array({make_uint(1), make_uint(2)});
const auto dimension_bytes = wide_json::to_bjdata(dimension, true, true);
REQUIRE(!dimension_bytes.empty());
CHECK(dimension_bytes[0] == '{');
// in range, the ND-array is still written
wide_json fits = element;
fits["_ArrayData_"] = wide_json::array({make_uint(1), make_uint(2)});
const auto fits_bytes = wide_json::to_bjdata(fits, true, true);
REQUIRE(!fits_bytes.empty());
CHECK(fits_bytes[0] == '[');
}
}
#endif
TEST_CASE("custom number types")
{
SECTION("signed integer of class type compared with a float")
{
// std::is_signed is false for a class type, which made the comparison
// treat any float below zero as less than every integer
const class_int_json minus_five = make_class_int(-5);
const class_int_json minus_two = make_class_int(-2);
const class_int_json five = make_class_int(5);
const class_int_json minus_two_and_a_half = -2.5;
const class_int_json two_and_a_half = 2.5;
const class_int_json huge_negative = -1e30;
const class_int_json huge_positive = 1e30;
CHECK(minus_five < minus_two_and_a_half);
CHECK_FALSE(minus_two_and_a_half < minus_five);
CHECK(minus_two_and_a_half > minus_five);
CHECK(minus_five <= minus_two_and_a_half);
CHECK(minus_two_and_a_half >= minus_five);
CHECK(minus_five != minus_two_and_a_half);
CHECK(minus_two_and_a_half < minus_two);
CHECK_FALSE(minus_two < minus_two_and_a_half);
CHECK(two_and_a_half < five);
CHECK(minus_five < two_and_a_half);
// floats beyond the integer's range
CHECK(huge_negative < minus_five);
CHECK_FALSE(minus_five < huge_negative);
CHECK(five < huge_positive);
CHECK_FALSE(huge_positive < five);
// equality
const class_int_json minus_two_float = -2.0;
CHECK(minus_two == minus_two_float);
CHECK(minus_two_float == minus_two);
}
}
Loaded 100 of 137 files, more files were not shown because too many files have changed in this diff. Show more