mirror of
https://github.com/nlohmann/json.git
synced 2026-10-10 16:37:14 +00:00
Compare commits
36
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e83e547b71 | ||
|
|
a8bbc365d5 | ||
|
|
07ec1a4e31 | ||
|
|
e26967139e | ||
|
|
571b655357 | ||
|
|
3caf0e7cdb | ||
|
|
b5c8e57476 | ||
|
|
48a69ef01d | ||
|
|
efcbab2cf3 | ||
|
|
74aefc3486 | ||
|
|
84ee12a67c | ||
|
|
2913e96433 | ||
|
|
44e8597701 | ||
|
|
374dfe4f0f | ||
|
|
d540750f21 | ||
|
|
581efc817d | ||
|
|
3168d591e8 | ||
|
|
35b28d4918 | ||
|
|
86feea50ab | ||
|
|
e1a85099ef | ||
|
|
653edd738a | ||
|
|
0e729be261 | ||
|
|
69a0c1b82c | ||
|
|
d33068da73 | ||
|
|
4c43e03d40 | ||
|
|
d22193a8b6 | ||
|
|
22b82b487b | ||
|
|
d8dfc0d0f9 | ||
|
|
43c75bef51 | ||
|
|
3d7f554927 | ||
|
|
a269794db7 | ||
|
|
cb3c0177ed | ||
|
|
39d34f30ba | ||
|
|
88ddacb84b | ||
|
|
bfa4886f0f | ||
|
|
68d61b6aef |
No files matched your search
@@ -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
|
||||
|
||||
|
||||
@@ -166,6 +166,13 @@ jobs:
|
||||
# link, but the binaries clang 11.0.1 and clang 18.1.8 then produce crash
|
||||
# before doctest prints its first line - 39 of 102 tests on clang 18.
|
||||
# Keep the objects small by splitting the test files instead.
|
||||
# Objects with more than 65535 sections fail to link with "relocation
|
||||
# truncated to fit: IMAGE_REL_AMD64_REL32 against `.rdata'": the MinGW
|
||||
# linker stores the section an associative COMDAT section belongs to in
|
||||
# 16 bits (x_associated in binutils' include/coff/internal.h), so it
|
||||
# discards the jump tables of inline functions together with the wrong
|
||||
# function. Each basic_json specialization a test file instantiates adds
|
||||
# many sections, so test a second one in a file of its own.
|
||||
- name: Run CMake
|
||||
run: cmake -S . -B build ^
|
||||
-DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang++.exe" ^
|
||||
|
||||
@@ -25,6 +25,7 @@ cc_library(
|
||||
"include/nlohmann/detail/conversions/from_json.hpp",
|
||||
"include/nlohmann/detail/conversions/to_chars.hpp",
|
||||
"include/nlohmann/detail/conversions/to_json.hpp",
|
||||
"include/nlohmann/detail/conversions/zmij.hpp",
|
||||
"include/nlohmann/detail/exceptions.hpp",
|
||||
"include/nlohmann/detail/hash.hpp",
|
||||
"include/nlohmann/detail/input/binary_reader.hpp",
|
||||
|
||||
@@ -1394,9 +1394,10 @@ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR I
|
||||
|
||||
- The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2008-2009 [Björn Hoehrmann](https://bjoern.hoehrmann.de/) <bjoern@hoehrmann.de>
|
||||
- The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2009 [Florian Loitsch](https://florian.loitsch.com/)
|
||||
- The class contains a port of the shortest double-to-decimal conversion of [Żmij](https://github.com/vitaut/zmij) by Victor Zverovich, including the conversion of the digits to text by Xiang JunBo and the SIMD instruction sequence of Dougall Johnson, which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2025 [Victor Zverovich](https://github.com/vitaut)
|
||||
- The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||
- The class contains parts of [Google Abseil](https://github.com/abseil/abseil-cpp) which is licensed under the [Apache 2.0 License](https://opensource.org/licenses/Apache-2.0).
|
||||
- The class contains an adapted version of the Eisel-Lemire algorithm and its table of powers of five from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
||||
- The class contains an adapted version of the Eisel-Lemire algorithm, its table of powers of five, and its digit comparison for long numbers from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
||||
|
||||
<img align="right" src="https://git.fsfe.org/reuse/reuse-ci/raw/branch/master/reuse-horizontal.png" alt="REUSE Software">
|
||||
|
||||
|
||||
+11
-55
@@ -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
|
||||
|
||||
@@ -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).
|
||||
|
||||
|
||||
@@ -1,289 +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::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 ('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::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::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_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::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::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_integer', 'Method', 'api/json_sax/number_integer/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::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 ('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 ('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');
|
||||
Executable
+528
@@ -0,0 +1,528 @@
|
||||
#!/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)
|
||||
req = urllib.request.Request(u.geturl(), headers={'User-Agent': USER_AGENT})
|
||||
with urllib.request.urlopen(req, timeout=20) as r:
|
||||
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()).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:
|
||||
return yaml.load(f, Loader=Loader)
|
||||
|
||||
|
||||
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())
|
||||
@@ -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).
|
||||
|
||||
@@ -64,18 +64,7 @@ values of that type directly to a `basic_json` instance, and they will automatic
|
||||
rather than arrays:
|
||||
|
||||
```cpp
|
||||
using custom_json = nlohmann::basic_json<
|
||||
nlohmann::ordered_map, // ObjectType
|
||||
std::vector, // ArrayType
|
||||
std::string, // StringType
|
||||
bool, // BooleanType
|
||||
std::int64_t, // NumberIntegerType
|
||||
std::uint64_t, // NumberUnsignedType
|
||||
double, // NumberFloatType
|
||||
std::allocator, // AllocatorType
|
||||
nlohmann::adl_serializer,
|
||||
std::vector<std::byte> // Custom BinaryType
|
||||
>;
|
||||
using custom_json = nlohmann::ordered_json::with_binary_t<std::vector<std::byte>>;
|
||||
|
||||
std::vector<std::byte> data{std::byte{1}, std::byte{2}, std::byte{3}};
|
||||
custom_json j = data; // Creates a binary value, not an array
|
||||
|
||||
@@ -62,6 +62,9 @@ Linear.
|
||||
|
||||
## Notes
|
||||
|
||||
Floating-point numbers are written with the fewest digits that read back as the same value (for `#!cpp double`; see
|
||||
[number handling](../../features/types/number_handling.md#number-serialization)).
|
||||
|
||||
Binary values are serialized as an object containing two keys:
|
||||
|
||||
- "bytes": an array of bytes as integers
|
||||
@@ -97,3 +100,5 @@ Binary values are serialized as an object containing two keys:
|
||||
- Error handlers added in version 3.4.0.
|
||||
- Serialization of binary values added in version 3.8.0.
|
||||
- Error handler `keep` added in version 3.13.0.
|
||||
- Doubles are written with the shortest digits (Żmij instead of Grisu2) since version 3.13.0; about 0.1% of doubles are
|
||||
written differently, most of them with fewer digits.
|
||||
@@ -23,9 +23,10 @@ type to use.
|
||||
## Template parameters
|
||||
|
||||
`NumberFloatType`
|
||||
: the type to store floating-point numbers. Parsing and serialization are implemented in terms of
|
||||
`#!cpp std::strtof`/`#!cpp std::strtod`/`#!cpp std::strtold` and `#!cpp std::snprintf`, so the type must be
|
||||
`#!cpp float`, `#!cpp double`, or `#!cpp long double`. The
|
||||
: the type to store floating-point numbers. The type must be `#!cpp float`, `#!cpp double`, or
|
||||
`#!cpp long double`. The parser converts `#!cpp float`, `#!cpp double`, and a `#!cpp long double` that is IEEE 754
|
||||
binary64 itself. It converts other `#!cpp long double` formats with `#!cpp std::from_chars` where available, or
|
||||
with `#!cpp std::strtold` otherwise. Serialization falls back to `#!cpp std::snprintf`. The
|
||||
[binary formats](../../features/binary_formats/index.md) additionally require `#!cpp float` or `#!cpp double`,
|
||||
because they have no encoding for `#!cpp long double`. See
|
||||
[Template Parameter Requirements](../../features/types/template_parameters.md#numberfloattype).
|
||||
|
||||
@@ -26,7 +26,8 @@ To store objects in C++, a type is defined by the template parameters described
|
||||
|
||||
`StringType`
|
||||
: the type of the keys or names (e.g., `std::string`). The comparison function `std::less<StringType>` is used to
|
||||
order elements inside the container.
|
||||
order elements inside the container. `object_t::key_type` must be implicitly convertible to `string_t` (required by the
|
||||
binary formats).
|
||||
|
||||
`AllocatorType`
|
||||
: the allocator to use for objects (e.g., `std::allocator`)
|
||||
|
||||
@@ -67,7 +67,7 @@ By default, implicit conversions are enabled.
|
||||
`JSON_USE_IMPLICIT_CONVERSIONS` is defined to `0`:
|
||||
|
||||
```cpp
|
||||
using wjson = nlohmann::basic_json<std::map, std::vector, std::wstring>;
|
||||
using wjson = nlohmann::json::with_string_t<std::wstring>;
|
||||
|
||||
void load(const nlohmann::json& j);
|
||||
|
||||
|
||||
@@ -15,19 +15,7 @@ class base_class_with_hidden_members
|
||||
}
|
||||
};
|
||||
|
||||
using json = nlohmann::basic_json <
|
||||
std::map,
|
||||
std::vector,
|
||||
std::string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
std::allocator,
|
||||
nlohmann::adl_serializer,
|
||||
std::vector<std::uint8_t>,
|
||||
base_class_with_hidden_members
|
||||
>;
|
||||
using json = nlohmann::json::with_base_class_t<base_class_with_hidden_members>;
|
||||
|
||||
int main()
|
||||
{
|
||||
|
||||
@@ -19,25 +19,9 @@ int main()
|
||||
<< j.contains("/array/1"_json_pointer) << '\n'
|
||||
<< j.contains("/array/-"_json_pointer) << '\n'
|
||||
<< j.contains("/array/4"_json_pointer) << '\n'
|
||||
<< j.contains("/baz"_json_pointer) << std::endl;
|
||||
|
||||
try
|
||||
{
|
||||
// try to use an array index with leading '0'
|
||||
j.contains("/array/01"_json_pointer);
|
||||
}
|
||||
catch (const json::parse_error& e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// try to use an array index that is not a number
|
||||
j.contains("/array/one"_json_pointer);
|
||||
}
|
||||
catch (const json::parse_error& e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
}
|
||||
<< j.contains("/baz"_json_pointer) << '\n'
|
||||
// an array index with a leading '0' is not found
|
||||
<< j.contains("/array/01"_json_pointer) << '\n'
|
||||
// an array index that is not a number is not found
|
||||
<< j.contains("/array/one"_json_pointer) << std::endl;
|
||||
}
|
||||
@@ -5,3 +5,5 @@ true
|
||||
false
|
||||
false
|
||||
false
|
||||
false
|
||||
false
|
||||
@@ -1,11 +1,10 @@
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "custom_array_type.hpp"
|
||||
|
||||
using custom_json = nlohmann::basic_json<std::map, custom_array_type>;
|
||||
using custom_json = nlohmann::json::with_array_t<custom_array_type>;
|
||||
|
||||
int main()
|
||||
{
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "custom_binary_type.hpp"
|
||||
|
||||
using custom_json = nlohmann::basic_json<std::map, std::vector, std::string, bool,
|
||||
std::int64_t, std::uint64_t, double, std::allocator,
|
||||
nlohmann::adl_serializer, custom_binary_type>;
|
||||
using custom_json = nlohmann::json::with_binary_t<custom_binary_type>;
|
||||
|
||||
int main()
|
||||
{
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "custom_object_type.hpp"
|
||||
|
||||
using custom_json = nlohmann::basic_json<custom_object_type, std::vector>;
|
||||
using custom_json = nlohmann::json::with_object_t<custom_object_type>;
|
||||
|
||||
int main()
|
||||
{
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "custom_string_type.hpp"
|
||||
|
||||
using custom_json = nlohmann::basic_json<std::map, std::vector, custom_string_type>;
|
||||
using custom_json = nlohmann::json::with_string_t<custom_string_type>;
|
||||
|
||||
int main()
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@ int main()
|
||||
try
|
||||
{
|
||||
// parsing input with a syntax error
|
||||
json::parse("[1,2,3,]");
|
||||
json j = json::parse("[1,2,3,]");
|
||||
}
|
||||
catch (const json::parse_error& e)
|
||||
{
|
||||
|
||||
@@ -82,12 +82,13 @@ flowchart TD
|
||||
|
||||
- Numbers with a decimal digit or scientific notation are always stored as `#!c double`.
|
||||
- The number types can be changed, see [Template number types](#template-number-types).
|
||||
- Integers are converted by the library's own digit parser. Floating-point numbers are converted with
|
||||
[`std::from_chars`](https://en.cppreference.com/w/cpp/utility/from_chars) if the library is compiled with C++17
|
||||
and the standard library supports it, then with an exact fast path for `#!c double` values with few significant
|
||||
digits, and otherwise with the locale-aware
|
||||
[`std::strtod`](https://en.cppreference.com/w/cpp/string/byte/strtof) (`std::strtof`/`std::strtold` for the
|
||||
other floating-point types). Before version 3.13.0, the conversion was realized by
|
||||
- The library converts integers and floating-point numbers itself, independent of the locale. Floating-point
|
||||
numbers are correctly rounded (to nearest, ties to even). Only a `#!c long double` that is not IEEE 754 binary64
|
||||
(e.g., the 80-bit x87 format) is converted with `#!cpp std::from_chars` where available, or else with
|
||||
[`std::strtold`](https://en.cppreference.com/w/cpp/string/byte/strtof). For that call, the library temporarily
|
||||
replaces the `.` with the decimal point of the current locale (which may be longer than one byte, e.g., in
|
||||
`fa_IR.UTF-8`), so the result does not depend on the locale either. Changing the locale in another thread during
|
||||
parsing is undefined behavior of the C library, though. Before version 3.13.0, the conversion was realized by
|
||||
[`std::strtoull`](https://en.cppreference.com/w/cpp/string/byte/strtoul),
|
||||
[`std::strtoll`](https://en.cppreference.com/w/cpp/string/byte/strtol), and `std::strtod`, respectively.
|
||||
|
||||
@@ -100,10 +101,10 @@ flowchart TD
|
||||
### Number limits
|
||||
|
||||
- Any 64-bit signed or unsigned integer can be stored without loss of precision.
|
||||
- Numbers exceeding the limits of `#!c double` (i.e., numbers that after conversion via
|
||||
[`std::strtod`](https://en.cppreference.com/w/cpp/string/byte/strtof) are not satisfying
|
||||
- Numbers exceeding the limits of `#!c double` (i.e., numbers whose rounded value is not satisfying
|
||||
[`std::isfinite`](https://en.cppreference.com/w/cpp/numeric/math/isfinite) such as `#!c 1E400`) will throw exception
|
||||
[`json.exception.out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406) during parsing.
|
||||
[`json.exception.out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406) during parsing. Numbers too
|
||||
small for `#!c double` (such as `#!c 1E-400`) become zero, with the sign of the number.
|
||||
- Floating-point numbers are rounded to the next number representable as `double`. For instance
|
||||
`#!c 3.141592653589793238462643383279` is stored as [`0x400921fb54442d18`](https://float.exposed/0x400921fb54442d18).
|
||||
This is the same behavior as the code `#!c double x = 3.141592653589793238462643383279;`.
|
||||
@@ -133,9 +134,10 @@ That is, `-0` is stored as a signed integer, but the serialization does not repr
|
||||
### Number serialization
|
||||
|
||||
- Integer numbers are serialized as is; that is, no scientific notation is used.
|
||||
- Floating-point numbers are serialized as specified by the `#!c %g` printf modifier with
|
||||
[`std::numeric_limits<double>::max_digits10`](https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10)
|
||||
significant digits. The rationale is to use the shortest representation while still allowing round-tripping.
|
||||
- Floating-point numbers are serialized with the fewest digits that read back as the same value (the closest such
|
||||
digits if there are several), in the layout of the `#!c %g` printf modifier: `#!c 1.5`, `#!c 100.0`, `#!c 1e+100`.
|
||||
Doubles are converted with the algorithm of [Żmij](https://github.com/vitaut/zmij), floats with Grisu2, which
|
||||
can write more digits than necessary.
|
||||
|
||||
!!! hint "Notes regarding precision of floating-point numbers"
|
||||
|
||||
@@ -351,9 +353,8 @@ The number types can be changed with template parameters.
|
||||
|
||||
A `basic_json` type that uses `#!c long double` as floating-point type.
|
||||
|
||||
```cpp hl_lines="2"
|
||||
using json_ld = nlohmann::basic_json<std::map, std::vector, std::string, bool,
|
||||
std::int64_t, std::uint64_t, long double>;
|
||||
```cpp hl_lines="1"
|
||||
using json_ld = nlohmann::json::with_float_t<long double>;
|
||||
```
|
||||
|
||||
Note values should then be parsed with `json_ld::parse` rather than `json::parse` as the latter would parse
|
||||
|
||||
@@ -8,6 +8,10 @@ these requirements so they do not have to be discovered by trial and error. Each
|
||||
that are known to work for that parameter and the ones that do not, checked against Boost 1.83, Abseil 20250127.0,
|
||||
Folly, EASTL 3.21, `ankerl::unordered_dense`, `phmap`, `gtl`, `robin_hood`, `tsl::ordered_map`, and Qt 6.
|
||||
|
||||
To change a single template parameter and keep the others, use the member alias templates
|
||||
[`with_*_t`](../../api/basic_json/with_t.md); for instance, `nlohmann::json::with_float_t<long double>` is `json` with
|
||||
`#!cpp long double` as [`number_float_t`](../../api/basic_json/number_float_t.md).
|
||||
|
||||
## How to read this page
|
||||
|
||||
Requirements are split into two groups:
|
||||
@@ -26,9 +30,9 @@ Requirements are split into two groups:
|
||||
diagnosed with dedicated error messages, and violating most of them results in a compiler error somewhere inside
|
||||
the library. Four violations are not caught at compile time at all:
|
||||
|
||||
- A [`StringType`](#stringtype) whose `data()` is not null-terminated compiles and can silently misparse
|
||||
floating-point numbers, because the lexer may hand the buffer to `#!cpp std::strtod`, which reads up to the
|
||||
terminating null character.
|
||||
- A [`StringType`](#stringtype) whose `data()` is not null-terminated compiles and silently misparses numbers
|
||||
stored as a `#!cpp long double` that is not IEEE 754 binary64 (e.g., the 80-bit x87 format), because the lexer
|
||||
hands the buffer to `#!cpp std::strtold`.
|
||||
- A stateful [`AllocatorType`](#allocatortype) compiles and silently ignores its state: allocation, deallocation,
|
||||
and [`get_allocator()`](../../api/basic_json/get_allocator.md) each use a different default-constructed instance.
|
||||
- The two [cross-specialization conversions](#cross-specialization-conversions) below. These abort on an assertion
|
||||
@@ -143,7 +147,7 @@ struct unordered_map_object
|
||||
using base_t::base_t;
|
||||
};
|
||||
|
||||
using unordered_json = nlohmann::basic_json<unordered_map_object>;
|
||||
using unordered_json = nlohmann::json::with_object_t<unordered_map_object>;
|
||||
```
|
||||
|
||||
Whether `#!cpp std::unordered_map` can be instantiated at all depends on the standard library: `object_t` is formed
|
||||
@@ -176,7 +180,7 @@ struct flat_hash_object
|
||||
using base_t::base_t;
|
||||
};
|
||||
|
||||
using flat_hash_json = nlohmann::basic_json<flat_hash_object>;
|
||||
using flat_hash_json = nlohmann::json::with_object_t<flat_hash_object>;
|
||||
```
|
||||
|
||||
`absl::node_hash_map` keeps references to the mapped values valid across insertions; `absl::flat_hash_map` does not,
|
||||
@@ -349,16 +353,21 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
|
||||
### Always required
|
||||
|
||||
- A member type `value_type` that is one byte wide and `char`-compatible. The library stores and processes UTF-8
|
||||
encoded `char` data and passes `data()` to functions that take a `#!cpp const char*`, such as `#!cpp std::strtod`.
|
||||
encoded `char` data and passes `data()` to functions that take a `#!cpp const char*`, such as `#!cpp std::strtold`
|
||||
(only used to parse a `#!cpp long double` that is not IEEE 754 binary64, see
|
||||
[`NumberFloatType`](#numberfloattype)).
|
||||
`#!cpp std::wstring`, `#!cpp std::u16string`, and `#!cpp std::u32string` are **not** valid choices; see the FAQ on
|
||||
[wide string handling](../../home/faq.md#wide-string-handling).
|
||||
- Constructors: default, copy, move, from `#!cpp const char*` (which must not be `#!cpp explicit`), from
|
||||
`#!cpp (const char*, size_type)`, and from `#!cpp (size_type, char)`; and copy or move assignment.
|
||||
- Member functions `size()`, `clear()`, `resize(n, c)`, `data()`, `push_back(char)`, and `operator[]`
|
||||
(const and non-const, returning references). `c_str()` and `back()` are **not** required.
|
||||
- `data()` must return a pointer to a contiguous, **null-terminated** buffer -- the parser may hand it to
|
||||
`#!cpp std::strtod`, which reads up to the null character. A type whose `data()` is not null-terminated does not
|
||||
fail to compile; it can silently misparse floating-point numbers.
|
||||
- `data()` must return a pointer to a contiguous, **null-terminated** buffer. `#!cpp float`, `#!cpp double`, and a
|
||||
`#!cpp long double` that is IEEE 754 binary64 are converted by the library itself and do not depend on this. For any
|
||||
other `NumberFloatType` (a `#!cpp long double` of another format), the parser falls back to `#!cpp std::strtold` when
|
||||
`#!cpp std::from_chars` is not available or declines the token, and `std::strtold` reads up to the null character. A type whose `data()`
|
||||
is not null-terminated does not fail to compile; with such a `NumberFloatType` it can silently misparse
|
||||
floating-point numbers.
|
||||
- `append(const char*, size_type)`, used by [`dump`](../../api/basic_json/dump.md), and `append(const StringType&)`,
|
||||
used by the CBOR reader for indefinite-length strings. The library's internal string concatenation additionally has
|
||||
to append a `#!cpp char` and a `#!cpp const char*`; for each it selects between `append(arg)`, `#!cpp operator+=`,
|
||||
@@ -537,16 +546,18 @@ therefore silently changes parse results rather than raising an error. See
|
||||
|
||||
`NumberFloatType` must be one of `#!cpp float`, `#!cpp double`, or `#!cpp long double`:
|
||||
|
||||
- The [parser](../parsing/index.md) converts number literals with `#!cpp std::from_chars` or, as a fallback, with
|
||||
`#!cpp std::strtof`, `#!cpp std::strtod`, or `#!cpp std::strtold`; the library provides overloads for exactly these
|
||||
three types.
|
||||
- The [parser](../parsing/index.md) converts number literals to `#!cpp float`, `#!cpp double`, and a
|
||||
`#!cpp long double` that is IEEE 754 binary64 itself; other `#!cpp long double` formats are converted with
|
||||
`#!cpp std::from_chars` where available, or with `#!cpp std::strtold`. The library provides overloads for exactly
|
||||
these three types.
|
||||
- [`dump`](../../api/basic_json/dump.md) falls back to `#!cpp std::snprintf` with the `%g` and `%Lg` conversion
|
||||
specifiers, for which the library likewise provides only `#!cpp double` and `#!cpp long double` overloads
|
||||
(`#!cpp float` is promoted to `#!cpp double`).
|
||||
|
||||
If `#!cpp std::numeric_limits<NumberFloatType>` describes an IEEE 754 binary32 or binary64 number, `dump` uses the
|
||||
Grisu2 algorithm, which produces the shortest representation that round-trips. Otherwise the `snprintf` fallback with
|
||||
`max_digits10` digits is used.
|
||||
If `#!cpp std::numeric_limits<NumberFloatType>` describes an IEEE 754 binary64 number, `dump` uses the algorithm of
|
||||
Żmij, which produces the shortest representation that round-trips. For IEEE 754 binary32 numbers, it uses Grisu2,
|
||||
which produces a short representation that round-trips. Otherwise the `snprintf` fallback with `max_digits10` digits is
|
||||
used.
|
||||
|
||||
### Required for the binary formats
|
||||
|
||||
@@ -558,7 +569,7 @@ binary32 or binary64 field and have no encoding for `#!cpp long double`.
|
||||
|
||||
| Type | Support |
|
||||
|--------------------------|-----------------------------------------------------------------------------------------------------------------------|
|
||||
| `#!cpp double` (default) | full; short round-trip output through Grisu2 |
|
||||
| `#!cpp double` (default) | full; shortest round-trip output through Żmij |
|
||||
| `#!cpp float` | full; short round-trip output through Grisu2 |
|
||||
| `#!cpp long double` | `dump` and `parse` only; the binary format writers do not compile, as they only handle IEEE 754 binary32 and binary64 |
|
||||
| any other type | not usable |
|
||||
|
||||
@@ -18,6 +18,8 @@ The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is licensed und
|
||||
|
||||
The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2009 [Florian Loitsch](https://florian.loitsch.com/)
|
||||
|
||||
The class contains a port of the shortest double-to-decimal conversion of [Żmij](https://github.com/vitaut/zmij) by Victor Zverovich, including the conversion of the digits to text by Xiang JunBo and the SIMD instruction sequence of Dougall Johnson, which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2025 [Victor Zverovich](https://github.com/vitaut)
|
||||
|
||||
The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||
|
||||
The class contains an adapted version of the Eisel-Lemire algorithm and its table of powers of five from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
||||
The class contains an adapted version of the Eisel-Lemire algorithm, its table of powers of five, and its digit comparison for long numbers from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
||||
@@ -116,7 +116,7 @@ function to use instead.
|
||||
=== "Deprecated"
|
||||
|
||||
```cpp
|
||||
using my_json = nlohmann::basic_json<std::map, std::vector, my_string_type>;
|
||||
using my_json = nlohmann::json::with_string_t<my_string_type>;
|
||||
nlohmann::json_pointer<my_json> ptr("/foo/bar/1");
|
||||
```
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -9,12 +9,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint> // uint64_t
|
||||
#include <cstring> // memcpy
|
||||
#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64)) && (!defined(__SIZEOF_INT128__) || (!defined(__GNUC__) && !defined(__clang__)))
|
||||
#include <intrin0.h> // __umulh, _umul128, _BitScanForward64, _BitScanReverse64
|
||||
#endif
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp> // JSON_HEDLEY_ALWAYS_INLINE, NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
// Portable bit-level helpers for the number and string scanners. They use
|
||||
// compiler builtins where available and plain C++ otherwise, so they need no
|
||||
// platform headers and work regardless of byte order.
|
||||
// compiler builtins or platform-specific intrinsics where available and plain
|
||||
// C++ otherwise, so they work regardless of byte order.
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
@@ -25,6 +29,10 @@ inline int count_leading_zeros(std::uint64_t x) noexcept
|
||||
{
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_clzll(x);
|
||||
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
|
||||
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan*64 takes
|
||||
_BitScanReverse64(&index, x);
|
||||
return 63 - static_cast<int>(index);
|
||||
#else
|
||||
int n = 0;
|
||||
for (int shift = 32; shift != 0; shift >>= 1)
|
||||
@@ -39,6 +47,29 @@ inline int count_leading_zeros(std::uint64_t x) noexcept
|
||||
#endif
|
||||
}
|
||||
|
||||
/// number of trailing zero bits of x (x != 0)
|
||||
inline int count_trailing_zeros(std::uint64_t x) noexcept
|
||||
{
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_ctzll(x);
|
||||
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
|
||||
unsigned long index = 0; // NOLINT(runtime/int): the type _BitScan*64 takes
|
||||
_BitScanForward64(&index, x);
|
||||
return static_cast<int>(index);
|
||||
#else
|
||||
int n = 0;
|
||||
for (int shift = 32; shift != 0; shift >>= 1)
|
||||
{
|
||||
if ((x << (64 - shift)) == 0)
|
||||
{
|
||||
n += shift;
|
||||
x >>= shift;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// the 128-bit product of two 64-bit numbers
|
||||
struct uint128_parts
|
||||
{
|
||||
@@ -52,6 +83,12 @@ inline uint128_parts full_multiplication(std::uint64_t a, std::uint64_t b) noexc
|
||||
__extension__ using uint128 = unsigned __int128;
|
||||
const uint128 r = static_cast<uint128>(a) * b;
|
||||
return {static_cast<std::uint64_t>(r), static_cast<std::uint64_t>(r >> 64u)};
|
||||
#elif defined(_MSC_VER) && defined(_M_X64)
|
||||
std::uint64_t high = 0;
|
||||
const std::uint64_t low = _umul128(a, b, &high);
|
||||
return {low, high};
|
||||
#elif defined(_MSC_VER) && defined(_M_ARM64)
|
||||
return {a * b, __umulh(a, b)};
|
||||
#else
|
||||
const std::uint64_t a_lo = a & 0xFFFFFFFFu;
|
||||
const std::uint64_t a_hi = a >> 32u;
|
||||
@@ -66,15 +103,27 @@ inline uint128_parts full_multiplication(std::uint64_t a, std::uint64_t b) noexc
|
||||
#endif
|
||||
}
|
||||
|
||||
/// eight bytes as a little-endian word (compilers fold this into one load on
|
||||
/// little-endian targets)
|
||||
inline std::uint64_t read_eight_bytes(const char* p) noexcept
|
||||
/// eight bytes as a little-endian word (a single load on little-endian
|
||||
/// targets; always inlined, as GCC otherwise calls it in the number loops)
|
||||
JSON_HEDLEY_ALWAYS_INLINE std::uint64_t read_eight_bytes(const unsigned char* b) noexcept
|
||||
{
|
||||
const auto* b = reinterpret_cast<const unsigned char*>(p); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
#if defined(_MSC_VER) || defined(__x86_64__) || defined(__i386__) || (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||
// the byte order already matches (all MSVC targets are little-endian)
|
||||
std::uint64_t result = 0;
|
||||
std::memcpy(&result, b, sizeof(result));
|
||||
return result;
|
||||
#else
|
||||
return static_cast<std::uint64_t>(b[0]) | (static_cast<std::uint64_t>(b[1]) << 8u)
|
||||
| (static_cast<std::uint64_t>(b[2]) << 16u) | (static_cast<std::uint64_t>(b[3]) << 24u)
|
||||
| (static_cast<std::uint64_t>(b[4]) << 32u) | (static_cast<std::uint64_t>(b[5]) << 40u)
|
||||
| (static_cast<std::uint64_t>(b[6]) << 48u) | (static_cast<std::uint64_t>(b[7]) << 56u);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// eight bytes as a little-endian word
|
||||
JSON_HEDLEY_ALWAYS_INLINE std::uint64_t read_eight_bytes(const char* p) noexcept
|
||||
{
|
||||
return read_eight_bytes(reinterpret_cast<const unsigned char*>(p)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2009 Florian Loitsch <https://florian.loitsch.com/>
|
||||
// SPDX-FileCopyrightText: 2025 Victor Zverovich <https://github.com/vitaut/zmij>
|
||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
@@ -11,11 +12,32 @@
|
||||
|
||||
#include <array> // array
|
||||
#include <cmath> // signbit, isfinite
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // intN_t, uintN_t
|
||||
#include <cstring> // memcpy, memmove
|
||||
#include <limits> // numeric_limits
|
||||
#include <type_traits> // conditional
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <cstdlib> // _byteswap_uint64
|
||||
#endif
|
||||
|
||||
// SSE2 (every x86-64 CPU) and NEON (every 64-bit Arm CPU) convert the 16
|
||||
// digits of a double at once
|
||||
#if defined(__x86_64__) || (defined(_M_X64) && !defined(_M_ARM64EC))
|
||||
#include <emmintrin.h>
|
||||
#define JSON_DTOA_SSE2 1
|
||||
#define JSON_DTOA_NEON 0
|
||||
#elif (defined(__aarch64__) || defined(_M_ARM64)) && !defined(_M_ARM64EC) && !defined(__ARM_BIG_ENDIAN)
|
||||
#include <arm_neon.h>
|
||||
#define JSON_DTOA_SSE2 0
|
||||
#define JSON_DTOA_NEON 1
|
||||
#else
|
||||
#define JSON_DTOA_SSE2 0
|
||||
#define JSON_DTOA_NEON 0
|
||||
#endif
|
||||
|
||||
#include <nlohmann/detail/conversions/zmij.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
@@ -1047,6 +1069,381 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent,
|
||||
return append_exponent(buf, n - 1);
|
||||
}
|
||||
|
||||
/// eight decimal digits (a value below 10^8) as bytes 0..9, the first digit
|
||||
/// in the most significant byte: three steps that divide all lanes at once
|
||||
/// by a multiplication (the conversion of Xiang JunBo, as in Zmij)
|
||||
inline std::uint64_t eight_digit_bytes(std::uint64_t abcdefgh) noexcept
|
||||
{
|
||||
const std::uint64_t abcd_efgh = abcdefgh + (((std::uint64_t{1} << 32u) - 10000u) * ((abcdefgh * (((std::uint64_t{1} << 40u) / 10000u) + 1u)) >> 40u));
|
||||
const std::uint64_t ab_cd_ef_gh = abcd_efgh + (((std::uint64_t{1} << 16u) - 100u) * (((abcd_efgh * (((std::uint64_t{1} << 19u) / 100u) + 1u)) >> 19u) & 0x7F0000007Fu));
|
||||
return ab_cd_ef_gh + (((std::uint64_t{1} << 8u) - 10u) * (((ab_cd_ef_gh * (((std::uint64_t{1} << 10u) / 10u) + 1u)) >> 10u) & 0x000F000F000F000Fu));
|
||||
}
|
||||
|
||||
/// store the bytes of v, the most significant one first (one byte swap and
|
||||
/// one store where the byte order is known: compilers do not reliably merge
|
||||
/// the byte stores once this is inlined)
|
||||
inline void store_msb_first(char* p, std::uint64_t v) noexcept
|
||||
{
|
||||
#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
v = __builtin_bswap64(v);
|
||||
std::memcpy(p, &v, sizeof(v));
|
||||
#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
std::memcpy(p, &v, sizeof(v));
|
||||
#elif defined(_MSC_VER) // (little-endian on all its targets)
|
||||
v = _byteswap_uint64(v);
|
||||
std::memcpy(p, &v, sizeof(v));
|
||||
#else
|
||||
for (unsigned i = 0; i < 8; ++i)
|
||||
{
|
||||
p[i] = static_cast<char>(v >> (56u - (8u * i)));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief digits * 10^exp for a double, in the layout of format_buffer()
|
||||
|
||||
The layout is that of format_buffer() with min_exp -4 and max_exp 15 (the
|
||||
digits10 of double). The digits are converted eight at a time and placed
|
||||
with fixed-size moves instead of per-digit loops and moves of the buffer.
|
||||
|
||||
@param[in] digits the digits (not 0, at most 17 digits; trailing zeros allowed)
|
||||
@param[in] exp the decimal exponent of the last digit
|
||||
@return a pointer past the text; up to 41 bytes at @a first are written
|
||||
(some beyond the returned end)
|
||||
*/
|
||||
JSON_HEDLEY_NON_NULL(1)
|
||||
JSON_HEDLEY_RETURNS_NON_NULL
|
||||
inline char* write_decimal(char* first, std::uint64_t digits, int exp) noexcept
|
||||
{
|
||||
JSON_ASSERT(digits != 0 && digits < 100000000000000000u);
|
||||
const std::uint64_t upper = digits / 100000000u;
|
||||
const std::uint64_t b0 = upper / 100000000u; // (one digit: it is its own byte)
|
||||
const std::uint64_t b1 = eight_digit_bytes(upper % 100000000u);
|
||||
const std::uint64_t b2 = eight_digit_bytes(digits % 100000000u);
|
||||
// leading and trailing zero digits: zero bytes, counted without division
|
||||
int leading = 16;
|
||||
int zeros = 16;
|
||||
if (b0 != 0)
|
||||
{
|
||||
leading = count_leading_zeros(b0) / 8;
|
||||
}
|
||||
else if (b1 != 0)
|
||||
{
|
||||
leading = 8 + (count_leading_zeros(b1) / 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
leading += count_leading_zeros(b2) / 8;
|
||||
}
|
||||
if (b2 != 0)
|
||||
{
|
||||
zeros = count_trailing_zeros(b2) / 8;
|
||||
}
|
||||
else if (b1 != 0)
|
||||
{
|
||||
zeros = 8 + (count_trailing_zeros(b1) / 8);
|
||||
}
|
||||
// (else: 16, b0 is the one digit that is not 0)
|
||||
// the digits as text at text + leading, then '0's, so that fixed-size
|
||||
// moves need not check how many digits there are
|
||||
std::array<char, 64> text; // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init): written before read
|
||||
store_msb_first(text.data(), b0 + 0x3030303030303030u);
|
||||
store_msb_first(text.data() + 8, b1 + 0x3030303030303030u);
|
||||
store_msb_first(text.data() + 16, b2 + 0x3030303030303030u);
|
||||
std::memset(text.data() + 24, '0', 40);
|
||||
const int k = 24 - leading - zeros; // significant digits
|
||||
const int n = k + exp + zeros; // position of the decimal point after the first digit
|
||||
const char* const s0 = text.data() + leading;
|
||||
|
||||
if (-4 < n && n <= 15)
|
||||
{
|
||||
// "0.[000]digits" (n <= 0) is the digits after 1 - n leading '0's
|
||||
// with the point after the first; "digits[000].0" (n >= k) and
|
||||
// "dig.its" put the point after n characters
|
||||
const int pad = n <= 0 ? 1 - n : 0;
|
||||
const char* const s = s0 - pad;
|
||||
const int len = k + pad;
|
||||
const int point = n + pad;
|
||||
std::memcpy(first, s, 16);
|
||||
std::memcpy(first + point + 1, s + point, 24);
|
||||
first[point] = '.';
|
||||
return first + (point >= len ? point + 2 : len + 1);
|
||||
}
|
||||
|
||||
// d.igitse+XX, with at least two exponent digits (as append_exponent())
|
||||
std::memcpy(first, s0, 16);
|
||||
std::memcpy(first + 2, s0 + 1, 16);
|
||||
first[1] = '.';
|
||||
char* const end = first + (k == 1 ? 1 : k + 1);
|
||||
const int e = n - 1;
|
||||
const auto ea = e < 0 ? 0u - static_cast<unsigned>(e) : static_cast<unsigned>(e); // (unsigned: no signed overflow to assume)
|
||||
const bool three = ea >= 100;
|
||||
end[0] = 'e';
|
||||
end[1] = e < 0 ? '-' : '+';
|
||||
end[2] = static_cast<char>('0' + (three ? ea / 100 : (ea / 10) % 10));
|
||||
end[3] = static_cast<char>('0' + (three ? (ea / 10) % 10 : ea % 10));
|
||||
end[4] = static_cast<char>('0' + (ea % 10));
|
||||
return end + (three ? 5 : 4);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief the shortest decimal of a positive double (Zmij), as write_decimal()
|
||||
writes it
|
||||
|
||||
For a normal double, the shorter candidate has 15 or 16 digits: they are
|
||||
converted at once (two halves of eight digits) and followed by the digit
|
||||
after them, if there is one, without the multiplication and division by 10
|
||||
that counting the digits of one number would take. The fixed layouts move
|
||||
the digits after the point by one byte.
|
||||
|
||||
@return a pointer past the text; up to 41 bytes at @a first are written
|
||||
(some beyond the returned end)
|
||||
*/
|
||||
JSON_HEDLEY_NON_NULL(1)
|
||||
JSON_HEDLEY_RETURNS_NON_NULL
|
||||
inline char* write_shortest(char* first, const zmij::shortest_decimal d) noexcept
|
||||
{
|
||||
const std::uint64_t sig = d.integral;
|
||||
if (JSON_HEDLEY_UNLIKELY(sig < 100000000000000u || sig >= 10000000000000000u))
|
||||
{
|
||||
// (subnormals)
|
||||
return d.has_digit ? write_decimal(first, (sig * 10) + d.digit, d.exponent) : write_decimal(first, sig, d.exponent + 1);
|
||||
}
|
||||
const bool sixteen = sig >= 1000000000000000u; // (else 15 digits)
|
||||
const int last = d.has_digit ? d.digit : 0;
|
||||
const std::uint64_t upper = sig / 100000000u;
|
||||
#if JSON_DTOA_SSE2
|
||||
// NOLINTBEGIN(portability-simd-intrinsics)
|
||||
// the two halves in the 64-bit lanes, each as abcd * 2^32 + efgh, then as
|
||||
// bytes (as eight_digit_bytes(), one lane each)
|
||||
const __m128i x = _mm_set_epi64x(static_cast<long long>(sig - (upper * 100000000u)), static_cast<long long>(upper)); // NOLINT(runtime/int)
|
||||
const __m128i abcd = _mm_srli_epi64(_mm_mul_epu32(x, _mm_set1_epi64x(109951163)), 40); // 2^40 / 10000 + 1
|
||||
const __m128i abcd_efgh = _mm_add_epi64(x, _mm_mul_epu32(abcd, _mm_set1_epi64x(4294957296))); // 2^32 - 10000
|
||||
// 32-bit lanes in the order of the text: abcd, efgh of both halves
|
||||
const __m128i fours = _mm_shuffle_epi32(abcd_efgh, _MM_SHUFFLE(2, 3, 0, 1));
|
||||
const __m128i ab = _mm_srli_epi16(_mm_mulhi_epu16(fours, _mm_set1_epi32(5243)), 3);
|
||||
const __m128i ab_cd = _mm_or_si128(_mm_slli_epi32(_mm_sub_epi16(fours, _mm_mullo_epi16(ab, _mm_set1_epi32(100))), 16), ab);
|
||||
// 16-bit lanes ab (< 100) -> bytes a, b: 256 * ab - 2559 * (ab / 10)
|
||||
const __m128i bytes = _mm_sub_epi16(_mm_slli_epi16(ab_cd, 8), _mm_mullo_epi16(_mm_set1_epi16(2559), _mm_mulhi_epu16(ab_cd, _mm_set1_epi16(6554))));
|
||||
// the last digit that is not 0 (sig is not 0)
|
||||
const auto nonzero = static_cast<std::uint64_t>(_mm_movemask_epi8(_mm_cmpgt_epi8(bytes, _mm_setzero_si128())));
|
||||
const int digits = 63 - count_leading_zeros(nonzero) + (sixteen ? 1 : 0); // without trailing zeros
|
||||
const __m128i chars = _mm_add_epi8(bytes, _mm_set1_epi8('0'));
|
||||
// the 16 characters from the first digit
|
||||
const __m128i s = sixteen ? chars : _mm_or_si128(_mm_srli_si128(chars, 1), _mm_slli_si128(_mm_cvtsi32_si128('0' + last), 15));
|
||||
const char s16 = static_cast<char>(sixteen ? '0' + last : '0'); // the 17th
|
||||
const auto store_16 = [&s](char* p) noexcept
|
||||
{
|
||||
std::memcpy(p, &s, 16);
|
||||
};
|
||||
const char first_digit = static_cast<char>(_mm_cvtsi128_si32(s));
|
||||
// NOLINTEND(portability-simd-intrinsics)
|
||||
#elif JSON_DTOA_NEON
|
||||
// as with SSE2: the halves in 32-bit lanes, then abcd, efgh of both
|
||||
const uint32x2_t halves = vcreate_u32(upper | ((sig - (upper * 100000000u)) << 32u));
|
||||
const uint32x2_t abcd = vmovn_u64(vshrq_n_u64(vmull_n_u32(halves, static_cast<std::uint32_t>(((std::uint64_t{1} << 40u) / 10000u) + 1u)), 40));
|
||||
const uint32x2_t efgh = vmls_n_u32(halves, abcd, 10000u);
|
||||
const uint32x4_t fours = vcombine_u32(vzip1_u32(abcd, efgh), vzip2_u32(abcd, efgh));
|
||||
const uint32x4_t ab = vshrq_n_u32(vmulq_n_u32(fours, 5243u), 19);
|
||||
const uint16x8_t ab_cd = vreinterpretq_u16_u32(vorrq_u32(ab, vshlq_n_u32(vmlsq_n_u32(fours, ab, 100u), 16)));
|
||||
const uint16x8_t tens = vshrq_n_u16(vmulq_n_u16(ab_cd, 103u), 10);
|
||||
const uint8x16_t bytes = vreinterpretq_u8_u16(vorrq_u16(tens, vshlq_n_u16(vmlsq_n_u16(ab_cd, tens, 10u), 8)));
|
||||
// the last digit that is not 0 (sig is not 0): a nibble per byte
|
||||
const std::uint64_t nonzero = vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(vtstq_u8(bytes, bytes)), 4)), 0);
|
||||
const int digits = ((63 - count_leading_zeros(nonzero)) / 4) + (sixteen ? 1 : 0); // without trailing zeros
|
||||
const uint8x16_t chars = vaddq_u8(bytes, vdupq_n_u8('0'));
|
||||
// the 16 characters from the first digit
|
||||
const uint8x16_t s = sixteen ? chars : vextq_u8(chars, vdupq_n_u8(static_cast<std::uint8_t>('0' + last)), 1);
|
||||
const char s16 = static_cast<char>(sixteen ? '0' + last : '0'); // the 17th
|
||||
const auto store_16 = [&s](char* p) noexcept
|
||||
{
|
||||
vst1q_u8(reinterpret_cast<std::uint8_t*>(p), s); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
};
|
||||
const auto first_digit = static_cast<char>(vgetq_lane_u8(s, 0));
|
||||
#else
|
||||
const std::uint64_t hi = eight_digit_bytes(upper);
|
||||
const std::uint64_t lo = eight_digit_bytes(sig - (upper * 100000000u));
|
||||
// trailing zero digits: zero bytes (sig is not 0)
|
||||
const int zeros = lo != 0 ? count_trailing_zeros(lo) / 8 : 8 + (count_trailing_zeros(hi) / 8);
|
||||
const int digits = 15 - zeros + (sixteen ? 1 : 0); // without trailing zeros
|
||||
// the 16 characters from the first digit
|
||||
const std::uint64_t s_hi = (sixteen ? hi : (hi << 8u) | (lo >> 56u)) + 0x3030303030303030u;
|
||||
const std::uint64_t s_lo = (sixteen ? lo : (lo << 8u) | static_cast<std::uint64_t>(last)) + 0x3030303030303030u;
|
||||
const char s16 = static_cast<char>(sixteen ? '0' + last : '0'); // the 17th
|
||||
const auto store_16 = [s_hi, s_lo](char* p) noexcept
|
||||
{
|
||||
store_msb_first(p, s_hi);
|
||||
store_msb_first(p + 8, s_lo);
|
||||
};
|
||||
const auto first_digit = static_cast<char>(s_hi >> 56u);
|
||||
#endif
|
||||
const int len = d.has_digit ? 16 + (sixteen ? 1 : 0) : digits; // significant digits
|
||||
const int n = 16 + (sixteen ? 1 : 0) + d.exponent; // digits before the point
|
||||
|
||||
if (JSON_HEDLEY_LIKELY(n >= 1 && n <= 15))
|
||||
{
|
||||
// "dig.its" and "digits[000].0": the digits after the point move by
|
||||
// one byte ('0's follow the digits)
|
||||
#if JSON_DTOA_SSE2
|
||||
// NOLINTBEGIN(portability-simd-intrinsics)
|
||||
// (in the register: reading the digits back from memory right after
|
||||
// storing them waits until the stores are done)
|
||||
const __m128i at = _mm_set1_epi8(static_cast<char>(n));
|
||||
const __m128i index = _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
|
||||
const __m128i before = _mm_cmpgt_epi8(at, index);
|
||||
const __m128i after = _mm_cmpgt_epi8(index, at);
|
||||
const __m128i text = _mm_or_si128(_mm_or_si128(_mm_and_si128(s, before), _mm_and_si128(_mm_slli_si128(s, 1), after)),
|
||||
_mm_andnot_si128(_mm_or_si128(before, after), _mm_set1_epi8('.')));
|
||||
std::memcpy(first, &text, 16);
|
||||
first[16] = static_cast<char>(_mm_extract_epi16(s, 7) >> 8);
|
||||
first[17] = s16;
|
||||
// NOLINTEND(portability-simd-intrinsics)
|
||||
#elif JSON_DTOA_NEON
|
||||
const uint8x16_t index = vcombine_u8(vcreate_u8(0x0706050403020100u), vcreate_u8(0x0F0E0D0C0B0A0908u));
|
||||
const uint8x16_t at = vdupq_n_u8(static_cast<std::uint8_t>(n));
|
||||
const uint8x16_t after_point = vbslq_u8(vcgtq_u8(index, at), vextq_u8(vdupq_n_u8(0), s, 15), vdupq_n_u8('.'));
|
||||
vst1q_u8(reinterpret_cast<std::uint8_t*>(first), vbslq_u8(vcltq_u8(index, at), s, after_point)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
first[16] = static_cast<char>(vgetq_lane_u8(s, 15));
|
||||
first[17] = s16;
|
||||
#else
|
||||
store_16(first);
|
||||
first[16] = s16;
|
||||
std::uint64_t after_point[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-pro-type-member-init,hicpp-member-init): written before read
|
||||
std::memcpy(after_point, first + n, 16);
|
||||
std::memcpy(first + n + 1, after_point, 16);
|
||||
first[n] = '.';
|
||||
#endif
|
||||
return first + (n >= len ? n + 2 : len + 1);
|
||||
}
|
||||
if (n <= 0 && n > -4)
|
||||
{
|
||||
// "0.[000]digits"
|
||||
std::memset(first, '0', 8);
|
||||
first[1] = '.';
|
||||
store_16(first + 2 - n);
|
||||
first[18 - n] = s16;
|
||||
return first + 2 - n + len;
|
||||
}
|
||||
// d.igitse+XX, with at least two exponent digits (as append_exponent())
|
||||
store_16(first + 1);
|
||||
first[17] = s16;
|
||||
first[0] = first_digit;
|
||||
first[1] = '.';
|
||||
char* const end = first + (len == 1 ? 1 : len + 1);
|
||||
const int e = n - 1;
|
||||
const auto ea = e < 0 ? 0u - static_cast<unsigned>(e) : static_cast<unsigned>(e); // (unsigned: no signed overflow to assume)
|
||||
const bool three = ea >= 100;
|
||||
end[0] = 'e';
|
||||
end[1] = e < 0 ? '-' : '+';
|
||||
end[2] = static_cast<char>('0' + (three ? ea / 100 : (ea / 10) % 10));
|
||||
end[3] = static_cast<char>('0' + (three ? (ea / 10) % 10 : ea % 10));
|
||||
end[4] = static_cast<char>('0' + (ea % 10));
|
||||
return end + (three ? 5 : 4);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief whether FloatType is an IEEE 754 binary64 type (a double, or a long double
|
||||
that has the same format, as with MSVC and on Apple's Arm CPUs)
|
||||
|
||||
These are the types the conversion of Zmij (see zmij.hpp) is used for; all
|
||||
others (binary32, or a format the library does not know) use Grisu2.
|
||||
*/
|
||||
template<typename FloatType>
|
||||
constexpr bool has_binary64_format() noexcept
|
||||
{
|
||||
return std::numeric_limits<FloatType>::is_iec559
|
||||
&& std::numeric_limits<FloatType>::digits == 53
|
||||
&& std::numeric_limits<FloatType>::max_exponent == 1024
|
||||
&& sizeof(FloatType) == sizeof(std::uint64_t);
|
||||
}
|
||||
|
||||
template<typename FloatType>
|
||||
struct is_binary64 : std::integral_constant<bool, has_binary64_format<FloatType>()> {};
|
||||
|
||||
/// a positive finite float (other than binary64): Grisu2 and format_buffer()
|
||||
template<typename FloatType>
|
||||
JSON_HEDLEY_NON_NULL(1, 2)
|
||||
JSON_HEDLEY_RETURNS_NON_NULL
|
||||
char* write_positive_grisu2(char* first, const char* last, FloatType value)
|
||||
{
|
||||
JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10);
|
||||
static_cast<void>(last); // (only used in the assertion)
|
||||
|
||||
// Compute v = buffer * 10^decimal_exponent.
|
||||
// The decimal digits are stored in the buffer, which needs to be interpreted
|
||||
// as an unsigned decimal integer.
|
||||
// len is the length of the buffer, i.e., the number of decimal digits.
|
||||
int len = 0;
|
||||
int decimal_exponent = 0;
|
||||
grisu2(first, len, decimal_exponent, value);
|
||||
|
||||
JSON_ASSERT(len <= std::numeric_limits<FloatType>::max_digits10);
|
||||
|
||||
// Format the buffer like printf("%.*g", prec, value)
|
||||
constexpr int kMinExp = -4;
|
||||
// Use digits10 here to increase compatibility with version 2.
|
||||
constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;
|
||||
|
||||
JSON_ASSERT(last - first >= kMaxExp + 2);
|
||||
JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);
|
||||
JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);
|
||||
|
||||
return format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);
|
||||
}
|
||||
|
||||
/// a positive finite binary64 number: the shortest digits (Zmij), laid out by
|
||||
/// write_shortest() (through a local buffer if [first, last) is shorter than
|
||||
/// the 41 bytes it may write)
|
||||
template<typename FloatType>
|
||||
JSON_HEDLEY_NON_NULL(1, 2)
|
||||
JSON_HEDLEY_RETURNS_NON_NULL
|
||||
char* write_positive_zmij(char* first, const char* last, FloatType value)
|
||||
{
|
||||
static_assert(is_binary64<FloatType>::value,
|
||||
"internal error: the conversion of Zmij needs IEEE 754 binary64 numbers");
|
||||
std::uint64_t bits = 0;
|
||||
std::memcpy(&bits, &value, sizeof(bits));
|
||||
const zmij::shortest_decimal d = zmij::to_shortest(bits);
|
||||
if (JSON_HEDLEY_LIKELY(last - first >= 41))
|
||||
{
|
||||
return write_shortest(first, d);
|
||||
}
|
||||
std::array<char, 64> buf; // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init): written before read
|
||||
const auto len = static_cast<std::size_t>(write_shortest(buf.data(), d) - buf.data());
|
||||
JSON_ASSERT(last - first >= static_cast<std::ptrdiff_t>(len));
|
||||
std::memcpy(first, buf.data(), len);
|
||||
return first + len;
|
||||
}
|
||||
|
||||
/// a positive finite binary64 number: Zmij (as a long double has the format of
|
||||
/// a double here, its bits are those of the double of the same value)
|
||||
template<typename FloatType>
|
||||
JSON_HEDLEY_NON_NULL(1, 2)
|
||||
JSON_HEDLEY_RETURNS_NON_NULL
|
||||
char* write_positive(char* first, const char* last, FloatType value, std::true_type /*is_binary64*/)
|
||||
{
|
||||
return write_positive_zmij(first, last, value);
|
||||
}
|
||||
|
||||
/// a positive finite float of any other format: Grisu2
|
||||
template<typename FloatType>
|
||||
JSON_HEDLEY_NON_NULL(1, 2)
|
||||
JSON_HEDLEY_RETURNS_NON_NULL
|
||||
char* write_positive(char* first, const char* last, FloatType value, std::false_type /*is_binary64*/)
|
||||
{
|
||||
return write_positive_grisu2(first, last, value);
|
||||
}
|
||||
|
||||
/// a positive finite float: Zmij for binary64 numbers, Grisu2 otherwise
|
||||
template<typename FloatType>
|
||||
JSON_HEDLEY_NON_NULL(1, 2)
|
||||
JSON_HEDLEY_RETURNS_NON_NULL
|
||||
char* write_positive(char* first, const char* last, FloatType value)
|
||||
{
|
||||
return write_positive(first, last, value, is_binary64<FloatType> {});
|
||||
}
|
||||
|
||||
} // namespace dtoa_impl
|
||||
|
||||
/*!
|
||||
@@ -1064,7 +1461,6 @@ JSON_HEDLEY_NON_NULL(1, 2)
|
||||
JSON_HEDLEY_RETURNS_NON_NULL
|
||||
char* to_chars(char* first, const char* last, FloatType value)
|
||||
{
|
||||
static_cast<void>(last); // maybe unused - fix warning
|
||||
JSON_ASSERT(std::isfinite(value));
|
||||
|
||||
// Use signbit(value) instead of (value < 0) since signbit works for -0.
|
||||
@@ -1090,28 +1486,7 @@ char* to_chars(char* first, const char* last, FloatType value)
|
||||
JSON_HEDLEY_DIAGNOSTIC_POP
|
||||
#endif
|
||||
|
||||
JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10);
|
||||
|
||||
// Compute v = buffer * 10^decimal_exponent.
|
||||
// The decimal digits are stored in the buffer, which needs to be interpreted
|
||||
// as an unsigned decimal integer.
|
||||
// len is the length of the buffer, i.e., the number of decimal digits.
|
||||
int len = 0;
|
||||
int decimal_exponent = 0;
|
||||
dtoa_impl::grisu2(first, len, decimal_exponent, value);
|
||||
|
||||
JSON_ASSERT(len <= std::numeric_limits<FloatType>::max_digits10);
|
||||
|
||||
// Format the buffer like printf("%.*g", prec, value)
|
||||
constexpr int kMinExp = -4;
|
||||
// Use digits10 here to increase compatibility with version 2.
|
||||
constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;
|
||||
|
||||
JSON_ASSERT(last - first >= kMaxExp + 2);
|
||||
JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);
|
||||
JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);
|
||||
|
||||
return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);
|
||||
return dtoa_impl::write_positive(first, last, value);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.12.0
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2025 Victor Zverovich <https://github.com/vitaut/zmij>
|
||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array> // array
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // uint32_t, uint64_t
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
#include <nlohmann/detail/bit_ops.hpp>
|
||||
#include <nlohmann/detail/input/pow5_table.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/*!
|
||||
@brief the shortest decimal representation of a double
|
||||
|
||||
A C++11 port of the conversion of Zmij by Victor Zverovich
|
||||
(https://github.com/vitaut/zmij, MIT license): the shortest decimal in the
|
||||
rounding interval of a double, the closest one if there are several. Zmij
|
||||
credits Xiang JunBo (producing the shorter candidate without a division) and
|
||||
Dougall Johnson (the compressed powers of ten). The powers of ten are taken
|
||||
from the table for number parsing (pow5_table.hpp) where it holds them, and
|
||||
computed from the compressed tables of Zmij beyond it.
|
||||
*/
|
||||
namespace zmij
|
||||
{
|
||||
|
||||
/// the compressed powers of ten of Zmij
|
||||
inline const std::array<std::uint64_t, 28>& pow10_minor() noexcept
|
||||
{
|
||||
static const std::array<std::uint64_t, 28> table =
|
||||
{
|
||||
{
|
||||
0x8000000000000000u, 0xa000000000000000u, 0xc800000000000000u, 0xfa00000000000000u, 0x9c40000000000000u,
|
||||
0xc350000000000000u, 0xf424000000000000u, 0x9896800000000000u, 0xbebc200000000000u, 0xee6b280000000000u,
|
||||
0x9502f90000000000u, 0xba43b74000000000u, 0xe8d4a51000000000u, 0x9184e72a00000000u, 0xb5e620f480000000u,
|
||||
0xe35fa931a0000000u, 0x8e1bc9bf04000000u, 0xb1a2bc2ec5000000u, 0xde0b6b3a76400000u, 0x8ac7230489e80000u,
|
||||
0xad78ebc5ac620000u, 0xd8d726b7177a8000u, 0x878678326eac9000u, 0xa968163f0a57b400u, 0xd3c21bcecceda100u,
|
||||
0x84595161401484a0u, 0xa56fa5b99019a5c8u, 0xcecb8f27f4200f3au
|
||||
}
|
||||
};
|
||||
return table;
|
||||
}
|
||||
|
||||
/// (high, low) pairs
|
||||
inline const std::array<std::uint64_t, 50>& pow10_major() noexcept
|
||||
{
|
||||
static const std::array<std::uint64_t, 50> table =
|
||||
{
|
||||
{
|
||||
0xaddcb9e83c6b1793u, 0xdf4abe242a1bbf3eu, 0xaf8e5410288e1b6fu, 0x07ecf0ae5ee44ddau, 0xb1442798f49ffb4au, 0x99cd11cfdf41779du,
|
||||
0xb2fe3f0b8599ef07u, 0x861fa7e6dcb4aa15u, 0xb4bca50b065abe63u, 0x0fed077a756b53aau, 0xb67f6455292cbf08u, 0x1a3bc84c17b1d543u,
|
||||
0xb84687c269ef3bfbu, 0x3d5d514f40eea742u, 0xba121a4650e4ddebu, 0x92f34d62616ce413u, 0xbbe226efb628afeau, 0x890489f70a55368cu,
|
||||
0xbdb6b8e905cb600fu, 0x5400e987bbc1c921u, 0xbf8fdb78849a5f96u, 0xde98520472bdd034u, 0xc16d9a0095928a27u, 0x75b7053c0f178294u,
|
||||
0xc350000000000000u, 0x0000000000000000u, 0xc5371912364ce305u, 0x6c28000000000000u, 0xc722f0ef9d80aad6u, 0x424d3ad2b7b97ef6u,
|
||||
0xc913936dd571c84cu, 0x03bc3a19cd1e38eau, 0xcb090c8001ab551cu, 0x5cadf5bfd3072cc6u, 0xcd036837130890a1u, 0x36dba887c37a8c10u,
|
||||
0xcf02b2c21207ef2eu, 0x94f967e45e03f4bcu, 0xd106f86e69d785c7u, 0xe13336d701beba52u, 0xd31045a8341ca07cu, 0x1ede48111209a051u,
|
||||
0xd51ea6fa85785631u, 0x552a74227f3ea566u, 0xd732290fbacaf133u, 0xa97c177947ad4096u, 0xd94ad8b1c7380874u, 0x18375281ae7822bdu,
|
||||
0xdb68c2ca82ed2a05u, 0xa67398db9f6820e1u
|
||||
}
|
||||
};
|
||||
return table;
|
||||
}
|
||||
|
||||
/// one bit per power: whether the computed value is one unit too large
|
||||
inline const std::array<std::uint32_t, 21>& pow10_fixups() noexcept
|
||||
{
|
||||
static const std::array<std::uint32_t, 21> table =
|
||||
{
|
||||
{
|
||||
0x8d8fc810u, 0x06100293u, 0x19000000u, 0x00100000u, 0x00000908u, 0x00000000u, 0x04e00300u, 0x3807e0b2u, 0x3d83d793u, 0x0006f5ccu,
|
||||
0x00000000u, 0xffff0000u, 0x8076337du, 0x4ff45ba0u, 0x09405033u, 0x034376d9u, 0x09000000u, 0x4e100501u, 0x076d14dcu, 0xf964f45eu,
|
||||
0x0000003du
|
||||
}
|
||||
};
|
||||
return table;
|
||||
}
|
||||
|
||||
/// the 128-bit significand of 10^k, rounded down, for k in [-307, 341]
|
||||
/// (compute_pow10 of Zmij)
|
||||
inline uint128_parts compute_pow10(int k) noexcept
|
||||
{
|
||||
const auto i = static_cast<unsigned>(k + 307);
|
||||
const std::uint64_t m = pow10_minor()[(i + 24) % 28];
|
||||
const std::size_t j = 2 * static_cast<std::size_t>((i + 24) / 28);
|
||||
const std::uint64_t h_hi = pow10_major()[j];
|
||||
const std::uint64_t h_lo = pow10_major()[j + 1];
|
||||
const std::uint64_t h1 = full_multiplication(h_lo, m).high;
|
||||
const std::uint64_t c0 = h_lo * m;
|
||||
const std::uint64_t c1 = h1 + (h_hi * m);
|
||||
const std::uint64_t c2 = (c1 < h1 ? 1u : 0u) + full_multiplication(h_hi, m).high;
|
||||
uint128_parts r{};
|
||||
if ((c2 >> 63u) != 0)
|
||||
{
|
||||
r.high = c2;
|
||||
r.low = c1;
|
||||
}
|
||||
else
|
||||
{
|
||||
r.high = (c2 << 1u) | (c1 >> 63u);
|
||||
r.low = (c1 << 1u) | (c0 >> 63u);
|
||||
}
|
||||
r.low -= (pow10_fixups()[i >> 5u] >> (i & 31u)) & 1u;
|
||||
return r;
|
||||
}
|
||||
|
||||
/// The 128-bit significand of 10^k, rounded down, for k in [-342, 341].
|
||||
/// Up to 10^308, the table for number parsing holds the same significands
|
||||
/// (those of 5^k), except for k in [-27, -1], where it holds them one unit
|
||||
/// larger (as the Eisel-Lemire algorithm needs them).
|
||||
inline uint128_parts pow10(int k) noexcept
|
||||
{
|
||||
if (k > pow5_128_largest_power)
|
||||
{
|
||||
return compute_pow10(k); // (only for the smallest doubles)
|
||||
}
|
||||
const auto i = 2 * static_cast<std::size_t>(k - pow5_128_smallest_power);
|
||||
uint128_parts r{pow5_128()[i + 1], pow5_128()[i]};
|
||||
const std::uint64_t adjust = static_cast<unsigned>(k + 27) < 27u ? 1u : 0u;
|
||||
r.high -= r.low < adjust ? 1u : 0u;
|
||||
r.low -= adjust;
|
||||
return r;
|
||||
}
|
||||
|
||||
/// (x_hi * 2^64 + x_lo) * y >> 64, as 128 bits
|
||||
inline uint128_parts umul192_hi128(std::uint64_t x_hi, std::uint64_t x_lo, std::uint64_t y) noexcept
|
||||
{
|
||||
const uint128_parts p = full_multiplication(x_hi, y);
|
||||
uint128_parts r{};
|
||||
r.low = p.low + full_multiplication(x_lo, y).high;
|
||||
r.high = p.high + (r.low < p.low ? 1u : 0u);
|
||||
return r;
|
||||
}
|
||||
|
||||
/// (x * y + c) >> 64
|
||||
inline std::uint64_t umul128_add_hi64(std::uint64_t x, std::uint64_t y, std::uint64_t c) noexcept
|
||||
{
|
||||
const uint128_parts p = full_multiplication(x, y);
|
||||
return p.high + (p.low + c < p.low ? 1u : 0u);
|
||||
}
|
||||
|
||||
/// the result of Zmij: the shorter candidate and, if that is outside the
|
||||
/// rounding interval, the digit after it (16 bytes: returned in registers)
|
||||
struct shortest_decimal
|
||||
{
|
||||
std::uint64_t integral; ///< the shorter candidate (15 or 16 digits for normal doubles)
|
||||
int exponent; ///< the decimal exponent of the digit after it
|
||||
unsigned char digit; ///< the digit after it (if has_digit)
|
||||
bool has_digit; ///< whether the shortest decimal is integral * 10 + digit
|
||||
};
|
||||
|
||||
/// The shortest decimal in the rounding interval of a positive finite double
|
||||
/// given by its bits, the closest one if there are several (to_decimal of
|
||||
/// Zmij, which keeps the last digit apart: the 15 or 16 digits before it can be
|
||||
/// converted without a multiplication by 10 first). Always inlined: GCC
|
||||
/// otherwise calls it, and its result goes through memory.
|
||||
JSON_HEDLEY_ALWAYS_INLINE shortest_decimal to_shortest(std::uint64_t bits) noexcept
|
||||
{
|
||||
constexpr int extra_shift = 9;
|
||||
const auto raw_exp = static_cast<int>((bits >> 52u) & 0x7FFu);
|
||||
std::uint64_t bin_sig = bits & ((std::uint64_t{1} << 52u) - 1);
|
||||
// a power of two has a narrower interval below (except the smallest normal)
|
||||
const bool regular = bin_sig != 0 || raw_exp <= 1;
|
||||
const int bin_exp = (raw_exp == 0 ? 1 : raw_exp) - 1075;
|
||||
if (raw_exp != 0)
|
||||
{
|
||||
bin_sig |= std::uint64_t{1} << 52u;
|
||||
}
|
||||
// floor(log10(2^bin_exp)), or floor(log10(3/4 * 2^bin_exp)) for the irregular case
|
||||
const int dec_exp = ((bin_exp * 315653) - (regular ? 0 : 131072)) >> 20;
|
||||
// scaled by 10^(-dec_exp - 1): the integral part is the shorter candidate
|
||||
const int shift = bin_exp + ((-(dec_exp + 1) * 217707) >> 16) + 1 + extra_shift;
|
||||
const uint128_parts p10 = pow10(-dec_exp - 1);
|
||||
const uint128_parts p = umul192_hi128(p10.high, p10.low, bin_sig << static_cast<unsigned>(shift));
|
||||
std::uint64_t integral = p.high >> static_cast<unsigned>(extra_shift);
|
||||
const std::uint64_t fractional = (p.high << static_cast<unsigned>(64 - extra_shift)) | (p.low >> static_cast<unsigned>(extra_shift));
|
||||
std::uint64_t digit = 0;
|
||||
bool round_up = false;
|
||||
bool round_down = false;
|
||||
if (JSON_HEDLEY_LIKELY(regular))
|
||||
{
|
||||
const std::uint64_t half_ulp = (p10.high >> static_cast<unsigned>(extra_shift + 1 - shift)) + (1 - (bin_sig & 1u));
|
||||
round_up = fractional + half_ulp < fractional;
|
||||
round_down = half_ulp > fractional;
|
||||
// the last digit of the longer candidate, rounded to nearest
|
||||
digit = umul128_add_hi64(fractional, 10, (std::uint64_t{1} << 63u) + 6);
|
||||
if (fractional == (std::uint64_t{1} << 62u))
|
||||
{
|
||||
digit = 2; // 2.5 rounds to 2
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::uint64_t half_ulp = p10.high >> static_cast<unsigned>(extra_shift + 1 - shift);
|
||||
round_up = half_ulp > ~std::uint64_t{0} - fractional;
|
||||
round_down = (half_ulp >> 1u) > fractional;
|
||||
digit = umul128_add_hi64(fractional, 10, (std::uint64_t{1} << 63u) - 1);
|
||||
const std::uint64_t lowest = umul128_add_hi64(fractional - (half_ulp >> 1u), 10, ~std::uint64_t{0});
|
||||
digit = digit < lowest ? lowest : digit;
|
||||
}
|
||||
integral += round_up ? 1u : 0u;
|
||||
// if the shorter candidate is outside the rounding interval: one digit more
|
||||
return shortest_decimal{integral, dec_exp, static_cast<unsigned char>(digit), !round_up && !round_down};
|
||||
}
|
||||
|
||||
} // namespace zmij
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -221,6 +221,44 @@ class lexer : public lexer_base<BasicJsonType>
|
||||
// scan functions
|
||||
/////////////////////
|
||||
|
||||
/// contiguous input: try to decode the 4 hex digits following `\\u`
|
||||
/// directly from the input buffer via hex_codepoint(), instead of 4 calls
|
||||
/// to get(). On success, advances the adapter and the position counters
|
||||
/// exactly as those 4 get() calls would (a hex digit is never '\n', so
|
||||
/// only the flat counters move) and leaves @a current holding the last of
|
||||
/// the 4 digits, just as the last such get() would; the codepoint is
|
||||
/// written to @a out. Makes no state change and returns false - for a
|
||||
/// pending unget, fewer than 4 remaining bytes, or any of the 4 bytes not
|
||||
/// being a hex digit - so the caller falls back unchanged to the
|
||||
/// per-character loop, which then reports the same diagnostic (stopping
|
||||
/// at the first invalid digit) as before this optimization.
|
||||
bool get_codepoint_bulk(std::true_type /*bulk*/, int& out)
|
||||
{
|
||||
if (next_unget || ia.bulk_remaining() < 4)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const char_type* const raw = ia.bulk_data();
|
||||
const int codepoint = hex_codepoint(reinterpret_cast<const unsigned char*>(raw));
|
||||
if (codepoint < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ia.bulk_skip(4);
|
||||
// a hex digit is never a newline, so only the flat counters advance
|
||||
position.chars_read_total += 4;
|
||||
position.chars_read_current_line += 4;
|
||||
current = char_traits<char_type>::to_int_type(raw[3]);
|
||||
out = codepoint;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// streaming input: no bulk fast path
|
||||
bool get_codepoint_bulk(std::false_type /*bulk*/, int& /*out*/) const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief get codepoint from 4 hex characters following `\\u`
|
||||
|
||||
@@ -240,6 +278,14 @@ class lexer : public lexer_base<BasicJsonType>
|
||||
{
|
||||
// this function only makes sense after reading `\u`
|
||||
JSON_ASSERT(current == 'u');
|
||||
|
||||
// contiguous input: decode all 4 hex digits directly from the buffer
|
||||
int fast_codepoint = 0;
|
||||
if (get_codepoint_bulk(std::integral_constant<bool, bulk_scan> {}, fast_codepoint))
|
||||
{
|
||||
return fast_codepoint;
|
||||
}
|
||||
|
||||
int codepoint = 0;
|
||||
|
||||
const auto factors = { 12u, 8u, 4u, 0u };
|
||||
@@ -1044,9 +1090,11 @@ class lexer : public lexer_base<BasicJsonType>
|
||||
token_type::parse_error otherwise
|
||||
|
||||
@note The scanner is independent of the current locale: token_buffer
|
||||
always holds `.`. Only the std::strtod fallback of convert_number()
|
||||
depends on the locale, and it looks up the decimal point right
|
||||
before converting (see detail::convert_float_locale_aware()).
|
||||
always holds `.`. The conversion of float and double does not use
|
||||
the locale either. Only the std::strtold fallback of
|
||||
convert_number() for long double formats other than binary64
|
||||
depends on it, and it looks up the decimal point right before
|
||||
converting (see detail::convert_float_locale_aware()).
|
||||
*/
|
||||
token_type scan_number() // lgtm [cpp/use-of-goto] `goto` is used in this function to implement the number-parsing state machine described above. By design, any finite input will eventually reach the "done" state or return token_type::parse_error. In each intermediate state, 1 byte of the input is appended to the token_buffer vector, and only the already initialized variables token_buffer, number_type, and error_message are manipulated.
|
||||
{
|
||||
@@ -1059,7 +1107,7 @@ class lexer : public lexer_base<BasicJsonType>
|
||||
|
||||
// offset just past the last mantissa byte in token_buffer (i.e. the
|
||||
// index of 'e'/'E', or the whole token when there is no exponent).
|
||||
// convert_number() uses it to count significant digits; npos means
|
||||
// convert_number() uses it to split the token; npos means
|
||||
// "not seen an exponent yet" and is resolved at scan_number_done
|
||||
std::size_t mantissa_end = std::string::npos;
|
||||
|
||||
@@ -1389,8 +1437,8 @@ scan_number_done:
|
||||
@param[in] mantissa_end offset just past the last mantissa byte in
|
||||
token_buffer (the index of 'e'/'E', or
|
||||
token_buffer.size() when there is no exponent);
|
||||
used to skip Clinger's fast path when it cannot
|
||||
possibly succeed - see detail::mantissa_fits_clinger()
|
||||
with decimal_point_position, it locates the parts
|
||||
of a float token without scanning it again
|
||||
*/
|
||||
token_type convert_number(token_type number_type, std::size_t mantissa_end)
|
||||
{
|
||||
@@ -1444,10 +1492,11 @@ scan_number_done:
|
||||
}
|
||||
|
||||
// this code is reached if we parse a floating-point number or if an
|
||||
// integer conversion above overflowed. Prefer std::from_chars
|
||||
// (Eisel-Lemire, locale-independent, correctly rounded) when available;
|
||||
// otherwise the exact Clinger fast path (double only); otherwise the
|
||||
// locale-aware strtof/strtod/strtold.
|
||||
// integer conversion above overflowed. float and double (and long
|
||||
// double where it is binary64) are converted by the library itself,
|
||||
// correctly rounded and independent of the locale; other long double
|
||||
// formats use std::from_chars when available, otherwise the
|
||||
// locale-aware strtold.
|
||||
if (convert_float_fast(num_begin, num_end, decimal_point_position, mantissa_end, value_float))
|
||||
{
|
||||
return token_type::value_float;
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -22,6 +22,10 @@ namespace detail
|
||||
constexpr std::int64_t pow5_128_smallest_power = -342;
|
||||
constexpr std::int64_t pow5_128_largest_power = 308;
|
||||
|
||||
// every entry of pow5_128() holds two 64-bit halves of 5^q, one per covered power of 5
|
||||
static_assert((pow5_128_largest_power - pow5_128_smallest_power + 1) * 2 == 1302,
|
||||
"pow5_128_smallest_power/pow5_128_largest_power must match the size of the pow5_128() table");
|
||||
|
||||
/*!
|
||||
@brief 128-bit approximations of 5^q for q in [-342, 308]
|
||||
|
||||
|
||||
@@ -8,10 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array> // array
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // uint64_t
|
||||
#include <cstdint> // uint64_t, uint8_t
|
||||
#include <cstring> // memcpy
|
||||
|
||||
#include <nlohmann/detail/bit_ops.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
|
||||
// Optional SIMD backend for bulk UTF-8 validation. This is an opt-in external
|
||||
@@ -69,18 +71,12 @@ inline std::size_t find_string_special(const unsigned char* data, std::size_t n)
|
||||
std::size_t i = 0;
|
||||
for (; i + 8 <= n; i += 8)
|
||||
{
|
||||
std::uint64_t word = 0;
|
||||
std::memcpy(&word, data + i, sizeof(word));
|
||||
if (swar_string_special(word) != 0)
|
||||
const std::uint64_t special = swar_string_special(read_eight_bytes(data + i));
|
||||
if (special != 0)
|
||||
{
|
||||
// a special byte is in this word; locate it (endian-agnostic)
|
||||
for (std::size_t j = 0; j < 8; ++j)
|
||||
{
|
||||
if (is_string_special(data[i + j]))
|
||||
{
|
||||
return i + j;
|
||||
}
|
||||
}
|
||||
// the lowest flagged byte is the first special one: the borrows of
|
||||
// the subtractions can only flag bytes above a true hit
|
||||
return i + (static_cast<std::size_t>(count_trailing_zeros(special)) / 8);
|
||||
}
|
||||
}
|
||||
for (; i < n; ++i)
|
||||
@@ -114,8 +110,7 @@ inline std::size_t find_ascii_copyable_run(const unsigned char* data, std::size_
|
||||
std::size_t i = 0;
|
||||
for (; i + 8 <= n; i += 8)
|
||||
{
|
||||
std::uint64_t v = 0;
|
||||
std::memcpy(&v, data + i, sizeof(v));
|
||||
const std::uint64_t v = read_eight_bytes(data + i);
|
||||
const std::uint64_t q = v ^ 0x2222222222222222ull; // '"' (0x22)
|
||||
const std::uint64_t b = v ^ 0x5C5C5C5C5C5C5C5Cull; // '\\' (0x5C)
|
||||
const std::uint64_t d = v ^ 0x7F7F7F7F7F7F7F7Full; // DEL (0x7F)
|
||||
@@ -126,7 +121,9 @@ inline std::size_t find_ascii_copyable_run(const unsigned char* data, std::size_
|
||||
| (v & high); // >= 0x80
|
||||
if (stop != 0)
|
||||
{
|
||||
break;
|
||||
// the lowest flagged byte is the first one to stop at (see
|
||||
// find_string_special())
|
||||
return i + (static_cast<std::size_t>(count_trailing_zeros(stop)) / 8);
|
||||
}
|
||||
}
|
||||
for (; i < n; ++i)
|
||||
@@ -253,12 +250,18 @@ inline std::size_t scalar_string_bulk_run(const unsigned char* data, std::size_t
|
||||
{
|
||||
break; // end of buffer, or a quote/escape/control byte
|
||||
}
|
||||
const std::size_t seq = validate_one_utf8(data + pos, n - pos);
|
||||
if (seq == 0)
|
||||
// a run of multi-byte sequences (e.g. CJK text) is validated sequence
|
||||
// by sequence without searching for the next special byte in between
|
||||
do
|
||||
{
|
||||
break; // ill-formed or truncated: let the byte path diagnose it
|
||||
const std::size_t seq = validate_one_utf8(data + pos, n - pos);
|
||||
if (seq == 0)
|
||||
{
|
||||
return pos; // ill-formed or truncated: let the byte path diagnose it
|
||||
}
|
||||
pos += seq;
|
||||
}
|
||||
pos += seq;
|
||||
while (pos < n && data[pos] >= 0x80u);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
@@ -273,8 +276,7 @@ inline std::size_t find_string_delimiter(const unsigned char* data, std::size_t
|
||||
std::size_t i = 0;
|
||||
for (; i + 8 <= n; i += 8)
|
||||
{
|
||||
std::uint64_t v = 0;
|
||||
std::memcpy(&v, data + i, sizeof(v));
|
||||
const std::uint64_t v = read_eight_bytes(data + i);
|
||||
const std::uint64_t q = v ^ 0x2222222222222222ull;
|
||||
const std::uint64_t b = v ^ 0x5C5C5C5C5C5C5C5Cull;
|
||||
const std::uint64_t hit = ((q - ones) & ~q & high)
|
||||
@@ -282,14 +284,8 @@ inline std::size_t find_string_delimiter(const unsigned char* data, std::size_t
|
||||
| ((v - 0x2020202020202020ull) & ~v & high);
|
||||
if (hit != 0)
|
||||
{
|
||||
for (std::size_t j = 0; j < 8; ++j)
|
||||
{
|
||||
const unsigned char c = data[i + j];
|
||||
if (c == '\"' || c == '\\' || c < 0x20u)
|
||||
{
|
||||
return i + j;
|
||||
}
|
||||
}
|
||||
// the lowest flagged byte is the first delimiter (see find_string_special())
|
||||
return i + (static_cast<std::size_t>(count_trailing_zeros(hit)) / 8);
|
||||
}
|
||||
}
|
||||
for (; i < n; ++i)
|
||||
@@ -320,5 +316,50 @@ inline std::size_t string_bulk_run(const unsigned char* data, std::size_t n) noe
|
||||
return scalar_string_bulk_run(data, n);
|
||||
}
|
||||
|
||||
// Decode the 4 hex digits at [data, data+4) - the digits following a `\u`
|
||||
// escape - into a codepoint 0x0000..0xFFFF via one table lookup per byte
|
||||
// (after yyjson's read_hex_u16), or return -1 if any of the 4 bytes is not a
|
||||
// hex digit ('0'..'9', 'A'..'F', 'a'..'f'). The caller must already have
|
||||
// checked that 4 bytes are available; used by lexer::get_codepoint()'s
|
||||
// contiguous fast path. On -1 it falls back to the byte-at-a-time loop, which
|
||||
// stops at the first invalid digit, so the reported error and position are
|
||||
// unaffected by this fast path.
|
||||
inline int hex_codepoint(const unsigned char* data) noexcept
|
||||
{
|
||||
static const std::array<std::uint8_t, 256> hex_digit_table = // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
{
|
||||
{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 00..0F
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 10..1F
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 20..2F
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 30..3F ('0'..'9')
|
||||
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 40..4F ('A'..'F')
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 50..5F
|
||||
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 60..6F ('a'..'f')
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 70..7F
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 80..8F
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 90..9F
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // A0..AF
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // B0..BF
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // C0..CF
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // D0..DF
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // E0..EF
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF // F0..FF
|
||||
}
|
||||
};
|
||||
|
||||
const std::uint8_t d0 = hex_digit_table[data[0]];
|
||||
const std::uint8_t d1 = hex_digit_table[data[1]];
|
||||
const std::uint8_t d2 = hex_digit_table[data[2]];
|
||||
const std::uint8_t d3 = hex_digit_table[data[3]];
|
||||
// every valid digit is <= 0xF; the combined OR only exceeds it if at
|
||||
// least one of the four bytes was not a hex digit (looked up as 0xFF)
|
||||
if ((d0 | d1 | d2 | d3) > 0x0F)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return (d0 << 12) | (d1 << 8) | (d2 << 4) | d3;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -16,8 +16,8 @@
|
||||
#include <iosfwd> // ostream
|
||||
#endif // JSON_NO_IO
|
||||
#include <limits> // max
|
||||
#include <map> // map
|
||||
#include <numeric> // accumulate
|
||||
#include <set> // set
|
||||
#include <string> // string
|
||||
#include <utility> // move
|
||||
#include <vector> // vector
|
||||
@@ -359,33 +359,82 @@ class json_pointer
|
||||
|
||||
private:
|
||||
/*!
|
||||
@brief the reference token sequences that denote arrays
|
||||
@brief the pointer prefixes of a flattened object, and which of them denote arrays
|
||||
|
||||
@ref unflatten collects the pointer prefixes that have a reference token 0
|
||||
among their children; @ref get_and_create creates arrays exactly below
|
||||
those prefixes and objects everywhere else. Deciding this up front keeps
|
||||
the result independent of the order in which the flattened object is
|
||||
iterated, which is unspecified for some object types.
|
||||
|
||||
The prefixes form a tree and are numbered, so each of them is stored only
|
||||
once (as a node) rather than as a copy of all of its reference tokens.
|
||||
*/
|
||||
using array_parents_t = std::set<std::vector<string_t>>;
|
||||
struct prefix_tree
|
||||
{
|
||||
// children[id] maps a reference token to the number of the prefix
|
||||
// extended by that token; number 0 is the empty prefix
|
||||
std::vector<std::map<string_t, std::size_t>> children;
|
||||
// is_array[id] is true iff some flattened key has the reference token
|
||||
// 0 directly below the prefix with number id
|
||||
std::vector<bool> is_array;
|
||||
|
||||
// start with the empty prefix only
|
||||
prefix_tree()
|
||||
: children(1)
|
||||
, is_array(1, false)
|
||||
{}
|
||||
|
||||
// return the number of the prefix with number id extended by
|
||||
// reference_token, adding it if it is new
|
||||
std::size_t add_child(std::size_t id, string_t&& reference_token)
|
||||
{
|
||||
if (reference_token == "0")
|
||||
{
|
||||
is_array[id] = true;
|
||||
}
|
||||
|
||||
// read the number before the emplace_back below, which may
|
||||
// reallocate children and invalidate the iterator
|
||||
const std::size_t next = children.size();
|
||||
const auto inserted = children[id].emplace(std::move(reference_token), next);
|
||||
const std::size_t child = inserted.first->second;
|
||||
if (inserted.second)
|
||||
{
|
||||
children.emplace_back();
|
||||
is_array.push_back(false);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
// return the number of the prefix with number id extended by
|
||||
// reference_token, which must have been added before
|
||||
std::size_t find_child(std::size_t id, const string_t& reference_token) const
|
||||
{
|
||||
const auto it = children[id].find(reference_token);
|
||||
JSON_ASSERT(it != children[id].end());
|
||||
return it->second;
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
@brief create and return a reference to the pointed to value
|
||||
|
||||
Complexity: Linear in the number of reference tokens.
|
||||
Complexity: Linear in the number of reference tokens (times the logarithm
|
||||
of the number of siblings for the prefix lookup).
|
||||
|
||||
@throw parse_error.106 if an array index begins with '0'
|
||||
@throw parse_error.109 if array index is not a number
|
||||
@throw type_error.313 if value cannot be unflattened
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
BasicJsonType& get_and_create(BasicJsonType& j, const array_parents_t& array_parents) const
|
||||
BasicJsonType& get_and_create(BasicJsonType& j, const prefix_tree& tree) const
|
||||
{
|
||||
auto* result = &j;
|
||||
|
||||
// the reference tokens that have been consumed so far; used to look up
|
||||
// whether the value to be created below is an array or an object
|
||||
std::vector<string_t> prefix;
|
||||
// the number of the prefix consumed so far; used to look up whether
|
||||
// the value to be created below is an array or an object
|
||||
std::size_t id = 0;
|
||||
|
||||
// in case no reference tokens exist, return a reference to the JSON value
|
||||
// j which will be overwritten by a primitive value
|
||||
@@ -395,7 +444,7 @@ class json_pointer
|
||||
{
|
||||
case detail::value_t::null:
|
||||
{
|
||||
if (array_parents.find(prefix) != array_parents.end())
|
||||
if (tree.is_array[id])
|
||||
{
|
||||
// some reference token below this position is 0, so the
|
||||
// value is an array
|
||||
@@ -440,7 +489,7 @@ class json_pointer
|
||||
JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", &j));
|
||||
}
|
||||
|
||||
prefix.push_back(reference_token);
|
||||
id = tree.find_child(id, reference_token);
|
||||
}
|
||||
|
||||
return *result;
|
||||
@@ -878,64 +927,131 @@ class json_pointer
|
||||
@param[in,out] result the result object to insert values to
|
||||
|
||||
@note Empty objects or arrays are flattened to `null`.
|
||||
|
||||
The value is walked with an explicit stack rather than the call stack, so
|
||||
arbitrarily deeply nested values can be flattened.
|
||||
|
||||
@sa https://github.com/nlohmann/json/issues/5393
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
static void flatten(const string_t& reference_string,
|
||||
const BasicJsonType& value,
|
||||
BasicJsonType& result)
|
||||
{
|
||||
switch (value.type())
|
||||
using object_const_iterator = typename BasicJsonType::object_t::const_iterator;
|
||||
|
||||
// an array or object being walked: the container, the array index or
|
||||
// object iterator of the next child, and the length of the path of the
|
||||
// container itself
|
||||
struct frame
|
||||
{
|
||||
case detail::value_t::array:
|
||||
{
|
||||
if (value.m_data.m_value.array->empty())
|
||||
{
|
||||
// flatten empty array as null
|
||||
result[reference_string] = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// iterate array and use index as a reference string
|
||||
for (std::size_t i = 0; i < value.m_data.m_value.array->size(); ++i)
|
||||
{
|
||||
flatten(detail::concat<string_t>(reference_string, '/', std::to_string(i)),
|
||||
value.m_data.m_value.array->operator[](i), result);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
frame(const BasicJsonType* container_, object_const_iterator member_, const std::size_t path_length_) noexcept
|
||||
: container(container_), member(std::move(member_)), path_length(path_length_)
|
||||
{}
|
||||
|
||||
case detail::value_t::object:
|
||||
{
|
||||
if (value.m_data.m_value.object->empty())
|
||||
{
|
||||
// flatten empty object as null
|
||||
result[reference_string] = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// iterate object and use keys as reference string
|
||||
for (const auto& element : *value.m_data.m_value.object)
|
||||
{
|
||||
flatten(detail::concat<string_t>(reference_string, '/', detail::escape(element.first)), element.second, result);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
const BasicJsonType* container;
|
||||
std::size_t index = 0;
|
||||
object_const_iterator member;
|
||||
std::size_t path_length;
|
||||
};
|
||||
|
||||
case detail::value_t::null:
|
||||
case detail::value_t::string:
|
||||
case detail::value_t::boolean:
|
||||
case detail::value_t::number_integer:
|
||||
case detail::value_t::number_unsigned:
|
||||
case detail::value_t::number_float:
|
||||
case detail::value_t::binary:
|
||||
case detail::value_t::discarded:
|
||||
default:
|
||||
// The containers being flattened are kept on an explicit stack, and
|
||||
// every child is flattened completely before the next one, so the
|
||||
// entries come out in the same order as with a recursive walk. The
|
||||
// path of the value being flattened is kept in one buffer that grows
|
||||
// and shrinks with the stack, rather than in a new string per level.
|
||||
std::vector<frame> stack;
|
||||
string_t path = reference_string;
|
||||
|
||||
// flatten `v`, whose path is `path`: primitives and empty containers
|
||||
// are added to the result right away; other containers get a frame
|
||||
const auto enter = [&stack, &path, &result](const BasicJsonType & v)
|
||||
{
|
||||
switch (v.type())
|
||||
{
|
||||
// add a primitive value with its reference string
|
||||
result[reference_string] = value;
|
||||
break;
|
||||
case detail::value_t::array:
|
||||
{
|
||||
if (v.m_data.m_value.array->empty())
|
||||
{
|
||||
// flatten empty array as null
|
||||
result[path] = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
stack.emplace_back(&v, object_const_iterator(), path.size());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case detail::value_t::object:
|
||||
{
|
||||
if (v.m_data.m_value.object->empty())
|
||||
{
|
||||
// flatten empty object as null
|
||||
result[path] = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
stack.emplace_back(&v, v.m_data.m_value.object->begin(), path.size());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case detail::value_t::null:
|
||||
case detail::value_t::string:
|
||||
case detail::value_t::boolean:
|
||||
case detail::value_t::number_integer:
|
||||
case detail::value_t::number_unsigned:
|
||||
case detail::value_t::number_float:
|
||||
case detail::value_t::binary:
|
||||
case detail::value_t::discarded:
|
||||
default:
|
||||
{
|
||||
// add a primitive value with its reference string
|
||||
result[path] = v;
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
enter(value);
|
||||
while (!stack.empty())
|
||||
{
|
||||
// the frame is changed through stack.back(): enter() may push a
|
||||
// frame, which would invalidate a reference to it
|
||||
const BasicJsonType* const container = stack.back().container;
|
||||
|
||||
// drop the path of the previous child
|
||||
path.resize(stack.back().path_length);
|
||||
|
||||
if (container->is_array())
|
||||
{
|
||||
const auto& array = *container->m_data.m_value.array;
|
||||
const std::size_t i = stack.back().index;
|
||||
if (i == array.size())
|
||||
{
|
||||
stack.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
// iterate array and use index as a reference string
|
||||
++stack.back().index;
|
||||
detail::concat_into(path, '/', detail::to_string<string_t>(i));
|
||||
enter(array[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
const object_const_iterator it = stack.back().member;
|
||||
if (it == container->m_data.m_value.object->end())
|
||||
{
|
||||
stack.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
// iterate object and use keys as reference string
|
||||
++stack.back().member;
|
||||
detail::concat_into(path, '/', detail::escape(it->first));
|
||||
enter(it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -963,19 +1079,15 @@ class json_pointer
|
||||
|
||||
// collect the pointer prefixes that have a reference token 0 among
|
||||
// their children; the values below them are arrays, all others are
|
||||
// objects (see array_parents_t)
|
||||
array_parents_t array_parents;
|
||||
// objects (see prefix_tree)
|
||||
prefix_tree tree;
|
||||
for (const auto& element : *value.m_data.m_value.object)
|
||||
{
|
||||
json_pointer ptr(element.first);
|
||||
std::vector<string_t> prefix;
|
||||
std::size_t id = 0;
|
||||
for (auto& reference_token : ptr.reference_tokens)
|
||||
{
|
||||
if (reference_token == "0")
|
||||
{
|
||||
array_parents.insert(prefix);
|
||||
}
|
||||
prefix.push_back(std::move(reference_token));
|
||||
id = tree.add_child(id, std::move(reference_token));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -991,7 +1103,7 @@ class json_pointer
|
||||
// that if the JSON pointer is "" (i.e., points to the whole value),
|
||||
// function get_and_create returns a reference to the result itself.
|
||||
// An assignment will then create a primitive value.
|
||||
json_pointer(element.first).get_and_create(result, array_parents) = element.second;
|
||||
json_pointer(element.first).get_and_create(result, tree) = element.second;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#undef JSON_NO_UNIQUE_ADDRESS
|
||||
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
||||
#undef JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
||||
#undef JSON_DTOA_SSE2
|
||||
#undef JSON_DTOA_NEON
|
||||
|
||||
#ifndef JSON_TEST_KEEP_MACROS
|
||||
#undef JSON_CATCH
|
||||
|
||||
@@ -85,6 +85,12 @@ template<typename BasicJsonType, typename CharType, typename OutputSinkType = ou
|
||||
class binary_writer
|
||||
{
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
|
||||
/// an object key as string_t: a reference when object_t::key_type already is
|
||||
/// string_t, otherwise a converted copy that outlives sanitize_utf8_for_write's result
|
||||
using object_key_string_t = typename std::conditional <
|
||||
std::is_same<typename BasicJsonType::object_t::key_type, string_t>::value,
|
||||
const string_t&, string_t >::type;
|
||||
using binary_t = typename BasicJsonType::binary_t;
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
|
||||
@@ -244,16 +250,7 @@ class binary_writer
|
||||
|
||||
case value_t::string:
|
||||
{
|
||||
string_t storage;
|
||||
const string_t& value = sanitize_utf8_for_write(*j.m_data.m_value.string, j, storage);
|
||||
|
||||
// step 1: write control byte and the string length
|
||||
write_cbor_head(0x60, value.size());
|
||||
|
||||
// step 2: write the string
|
||||
oa.write_characters(
|
||||
reinterpret_cast<const CharType*>(value.data()),
|
||||
value.size());
|
||||
write_cbor_string(*j.m_data.m_value.string, j);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -316,23 +313,20 @@ class binary_writer
|
||||
|
||||
case value_t::object:
|
||||
{
|
||||
static_assert(
|
||||
std::is_convertible <
|
||||
typename BasicJsonType::object_t::key_type,
|
||||
string_t >::value,
|
||||
"object_t::key_type must be implicitly convertible to string_t");
|
||||
|
||||
// step 1: write control byte and the object size
|
||||
write_cbor_head(0xA0, j.m_data.m_value.object->size());
|
||||
|
||||
for (const auto& el : *j.m_data.m_value.object)
|
||||
{
|
||||
// el.first is checked here, against the object as
|
||||
// diagnostics context, because write_cbor(el.first)
|
||||
// converts it to a temporary basic_json that would be
|
||||
// used as the context instead; for error_handler_t::keep
|
||||
// and ::replace/::ignore the recursive write_cbor(el.first)
|
||||
// call below handles the key like any other string, so no
|
||||
// separate check is needed here for those
|
||||
if (error_handler == error_handler_t::strict)
|
||||
{
|
||||
check_utf8(el.first, j);
|
||||
}
|
||||
write_cbor(el.first);
|
||||
// el.first is written directly (not via a temporary
|
||||
// basic_json), with the object as diagnostics context
|
||||
write_cbor_string(el.first, j);
|
||||
write_cbor(el.second, depth + 1);
|
||||
}
|
||||
break;
|
||||
@@ -491,39 +485,7 @@ class binary_writer
|
||||
|
||||
case value_t::string:
|
||||
{
|
||||
string_t storage;
|
||||
const string_t& value = sanitize_utf8_for_write(*j.m_data.m_value.string, j, storage);
|
||||
|
||||
// step 1: write control byte and the string length
|
||||
const auto N = to_msgpack_length(value.size(), j);
|
||||
if (N <= 31)
|
||||
{
|
||||
// fixstr
|
||||
write_number(static_cast<std::uint8_t>(0xA0 | N));
|
||||
}
|
||||
else if (N <= (std::numeric_limits<std::uint8_t>::max)())
|
||||
{
|
||||
// str 8
|
||||
oa.write_character(to_char_type(0xD9));
|
||||
write_number(static_cast<std::uint8_t>(N));
|
||||
}
|
||||
else if (N <= (std::numeric_limits<std::uint16_t>::max)())
|
||||
{
|
||||
// str 16
|
||||
oa.write_character(to_char_type(0xDA));
|
||||
write_number(static_cast<std::uint16_t>(N));
|
||||
}
|
||||
else
|
||||
{
|
||||
// str 32
|
||||
oa.write_character(to_char_type(0xDB));
|
||||
write_number(static_cast<std::uint32_t>(N));
|
||||
}
|
||||
|
||||
// step 2: write the string
|
||||
oa.write_characters(
|
||||
reinterpret_cast<const CharType*>(value.data()),
|
||||
value.size());
|
||||
write_msgpack_string(*j.m_data.m_value.string, j);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -629,19 +591,20 @@ class binary_writer
|
||||
|
||||
case value_t::object:
|
||||
{
|
||||
static_assert(
|
||||
std::is_convertible <
|
||||
typename BasicJsonType::object_t::key_type,
|
||||
string_t >::value,
|
||||
"object_t::key_type must be implicitly convertible to string_t");
|
||||
|
||||
// step 1: write control byte and the object size
|
||||
write_msgpack_object_prefix(j.m_data.m_value.object->size(), j);
|
||||
|
||||
for (const auto& el : *j.m_data.m_value.object)
|
||||
{
|
||||
// as in write_cbor, el.first is checked here against the
|
||||
// object as diagnostics context; the recursive call below
|
||||
// handles keep/replace/ignore like any other string
|
||||
if (error_handler == error_handler_t::strict)
|
||||
{
|
||||
check_utf8(el.first, j);
|
||||
}
|
||||
write_msgpack(el.first);
|
||||
// as in write_cbor, el.first is written directly with the
|
||||
// object as diagnostics context
|
||||
write_msgpack_string(el.first, j);
|
||||
write_msgpack(el.second, depth + 1);
|
||||
}
|
||||
break;
|
||||
@@ -819,8 +782,10 @@ class binary_writer
|
||||
|
||||
for (const auto& el : *j.m_data.m_value.object)
|
||||
{
|
||||
// a converted key must outlive the reference returned by sanitize_utf8_for_write
|
||||
const object_key_string_t key_string = el.first;
|
||||
string_t storage;
|
||||
const string_t& key = sanitize_utf8_for_write(el.first, j, storage);
|
||||
const string_t& key = sanitize_utf8_for_write(key_string, j, storage);
|
||||
write_number_with_ubjson_prefix(key.size(), true, use_bjdata);
|
||||
oa.write_characters(
|
||||
reinterpret_cast<const CharType*>(key.data()),
|
||||
@@ -1025,13 +990,9 @@ class binary_writer
|
||||
continue;
|
||||
}
|
||||
|
||||
// el.first is checked here, against the object as diagnostics
|
||||
// context, like the matching check in write_cbor's object case
|
||||
if (error_handler == error_handler_t::strict)
|
||||
{
|
||||
check_utf8(current.object_it->first, *current.value);
|
||||
}
|
||||
write_cbor(current.object_it->first);
|
||||
// the key is written directly (not via a temporary basic_json),
|
||||
// with the object as diagnostics context, as in write_cbor
|
||||
write_cbor_string(current.object_it->first, *current.value);
|
||||
const BasicJsonType* child = &(current.object_it->second);
|
||||
++stack.back().object_it;
|
||||
write_cbor_value_or_push(*child, stack);
|
||||
@@ -1106,11 +1067,9 @@ class binary_writer
|
||||
continue;
|
||||
}
|
||||
|
||||
if (error_handler == error_handler_t::strict)
|
||||
{
|
||||
check_utf8(current.object_it->first, *current.value);
|
||||
}
|
||||
write_msgpack(current.object_it->first);
|
||||
// as in write_cbor_iterative, the key is written directly with
|
||||
// the object as diagnostics context
|
||||
write_msgpack_string(current.object_it->first, *current.value);
|
||||
const BasicJsonType* child = &(current.object_it->second);
|
||||
++stack.back().object_it;
|
||||
write_msgpack_value_or_push(*child, stack);
|
||||
@@ -1366,8 +1325,10 @@ class binary_writer
|
||||
continue;
|
||||
}
|
||||
|
||||
// a converted key must outlive the reference returned by sanitize_utf8_for_write
|
||||
const object_key_string_t key_string = current.object_it->first;
|
||||
string_t storage;
|
||||
const string_t& key = sanitize_utf8_for_write(current.object_it->first, j, storage);
|
||||
const string_t& key = sanitize_utf8_for_write(key_string, j, storage);
|
||||
write_number_with_ubjson_prefix(key.size(), true, use_bjdata);
|
||||
oa.write_characters(
|
||||
reinterpret_cast<const CharType*>(key.data()),
|
||||
@@ -1988,6 +1949,85 @@ class binary_writer
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief write a CBOR text string
|
||||
|
||||
@a value is checked or sanitized according to @ref error_handler, with
|
||||
@a context (the string value itself, or the object a key belongs to) used
|
||||
as diagnostics context; this avoids converting object keys to a temporary
|
||||
basic_json just to write them
|
||||
|
||||
@note When object_t::key_type is not string_t, @a value is a temporary
|
||||
string_t converted from the key, which lives only until the end of
|
||||
the caller's statement. The reference returned by
|
||||
@ref sanitize_utf8_for_write may refer to it, so it must not escape
|
||||
this function.
|
||||
*/
|
||||
void write_cbor_string(const string_t& value, const BasicJsonType& context)
|
||||
{
|
||||
string_t storage;
|
||||
const string_t& sanitized = sanitize_utf8_for_write(value, context, storage);
|
||||
|
||||
// step 1: write control byte and the string length
|
||||
write_cbor_head(0x60, sanitized.size());
|
||||
|
||||
// step 2: write the string
|
||||
oa.write_characters(
|
||||
reinterpret_cast<const CharType*>(sanitized.data()),
|
||||
sanitized.size());
|
||||
}
|
||||
|
||||
/////////////
|
||||
// MsgPack //
|
||||
/////////////
|
||||
|
||||
/*!
|
||||
@brief write a MessagePack str
|
||||
|
||||
@a value is checked or sanitized according to @ref error_handler, with
|
||||
@a context used as diagnostics context, as in @ref write_cbor_string
|
||||
|
||||
@note As in @ref write_cbor_string, @a value may be a temporary string_t
|
||||
converted from a key, so the reference returned by
|
||||
@ref sanitize_utf8_for_write must not escape this function.
|
||||
*/
|
||||
void write_msgpack_string(const string_t& value, const BasicJsonType& context)
|
||||
{
|
||||
string_t storage;
|
||||
const string_t& sanitized = sanitize_utf8_for_write(value, context, storage);
|
||||
|
||||
// step 1: write control byte and the string length
|
||||
const auto N = to_msgpack_length(sanitized.size(), context);
|
||||
if (N <= 31)
|
||||
{
|
||||
// fixstr
|
||||
write_number(static_cast<std::uint8_t>(0xA0 | N));
|
||||
}
|
||||
else if (N <= (std::numeric_limits<std::uint8_t>::max)())
|
||||
{
|
||||
// str 8
|
||||
oa.write_character(to_char_type(0xD9));
|
||||
write_number(static_cast<std::uint8_t>(N));
|
||||
}
|
||||
else if (N <= (std::numeric_limits<std::uint16_t>::max)())
|
||||
{
|
||||
// str 16
|
||||
oa.write_character(to_char_type(0xDA));
|
||||
write_number(static_cast<std::uint16_t>(N));
|
||||
}
|
||||
else
|
||||
{
|
||||
// str 32
|
||||
oa.write_character(to_char_type(0xDB));
|
||||
write_number(static_cast<std::uint32_t>(N));
|
||||
}
|
||||
|
||||
// step 2: write the string
|
||||
oa.write_characters(
|
||||
reinterpret_cast<const CharType*>(sanitized.data()),
|
||||
sanitized.size());
|
||||
}
|
||||
|
||||
////////////
|
||||
// UBJSON //
|
||||
////////////
|
||||
@@ -2730,6 +2770,11 @@ class binary_writer
|
||||
itself in every case but a sanitized `replace`/`ignore` one, so @a
|
||||
storage must outlive the returned reference only then.
|
||||
|
||||
@a s must be an lvalue that outlives the returned reference. An object key
|
||||
whose `key_type` is not @ref string_t must therefore first be converted
|
||||
into a named string_t (see @ref object_key_string_t); the deleted overload
|
||||
below enforces this at compile time.
|
||||
|
||||
@param[in] s the string (value or object key) to write
|
||||
@param[in] context the value @a s belongs to (for diagnostics)
|
||||
@param[out] storage backing storage for a sanitized copy
|
||||
@@ -2759,6 +2804,10 @@ class binary_writer
|
||||
}
|
||||
}
|
||||
|
||||
/// deleted: anything but a string_t would bind a temporary that dies before the returned reference is used
|
||||
template < typename T, enable_if_t < !std::is_same<T, string_t>::value, int > = 0 >
|
||||
const string_t& sanitize_utf8_for_write(const T& /*s*/, const BasicJsonType& /*context*/, string_t& /*storage*/) const = delete; // NOLINT(hicpp-use-equals-delete,modernize-use-equals-delete): a private helper's guard, not part of the interface
|
||||
|
||||
/*!
|
||||
@brief write an integer in the shortest encoding
|
||||
|
||||
|
||||
@@ -1366,8 +1366,9 @@ class serializer
|
||||
/*!
|
||||
@brief dump an integer
|
||||
|
||||
Dump a given integer, appending it to @ref write_buffer. Works internally with
|
||||
@a number_buffer.
|
||||
Dump a given integer, appending it to @ref write_buffer (directly: copying
|
||||
the digits from another buffer right after writing them waits until the
|
||||
stores are done).
|
||||
|
||||
@param[in] x integer number (signed or unsigned) to dump
|
||||
@tparam NumberType either @a number_integer_t or @a number_unsigned_t
|
||||
@@ -1402,33 +1403,57 @@ class serializer
|
||||
return;
|
||||
}
|
||||
|
||||
// use a pointer to fill the buffer
|
||||
auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto)
|
||||
// use a pointer to fill the buffer (room for as much as number_buffer holds)
|
||||
if (JSON_HEDLEY_UNLIKELY(write_buffer_pos + number_buffer.size() > write_buffer.size()))
|
||||
{
|
||||
flush();
|
||||
}
|
||||
auto* buffer_ptr = write_buffer.data() + write_buffer_pos;
|
||||
|
||||
number_unsigned_t abs_value;
|
||||
|
||||
unsigned int n_chars{};
|
||||
// one byte for the minus sign
|
||||
unsigned int n_chars = 0;
|
||||
|
||||
if (is_negative_number(x))
|
||||
{
|
||||
*buffer_ptr = '-';
|
||||
abs_value = remove_sign(static_cast<number_integer_t>(x));
|
||||
|
||||
// account one more byte for the minus sign
|
||||
n_chars = 1 + count_digits(abs_value);
|
||||
n_chars = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
abs_value = static_cast<number_unsigned_t>(x);
|
||||
n_chars = count_digits(abs_value);
|
||||
}
|
||||
|
||||
// up to 16 digits: eight at a time (as the digits of floats), written
|
||||
// without leading zeros
|
||||
if (abs_value < 10000000000000000u)
|
||||
{
|
||||
const std::uint64_t value = abs_value;
|
||||
const std::uint64_t upper = value / 100000000u;
|
||||
const std::uint64_t first = dtoa_impl::eight_digit_bytes(upper != 0 ? upper : value);
|
||||
const auto leading = static_cast<unsigned>(count_leading_zeros(first) / 8); // (first is not 0)
|
||||
char* const p = buffer_ptr + n_chars;
|
||||
dtoa_impl::store_msb_first(p, (first << (8 * leading)) + 0x3030303030303030u);
|
||||
n_chars += 8 - leading;
|
||||
if (upper != 0)
|
||||
{
|
||||
dtoa_impl::store_msb_first(p + 8 - leading, dtoa_impl::eight_digit_bytes(value - (upper * 100000000u)) + 0x3030303030303030u);
|
||||
n_chars += 8;
|
||||
}
|
||||
write_buffer_pos += n_chars;
|
||||
return;
|
||||
}
|
||||
|
||||
n_chars += count_digits(abs_value);
|
||||
|
||||
// spare 1 byte for '\0'
|
||||
JSON_ASSERT(n_chars < number_buffer.size() - 1);
|
||||
|
||||
// jump to the end to generate the string from backward,
|
||||
// so we later avoid reversing the result
|
||||
buffer_ptr += static_cast<typename decltype(number_buffer)::difference_type>(n_chars);
|
||||
buffer_ptr += n_chars;
|
||||
|
||||
// Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu
|
||||
// See: https://www.youtube.com/watch?v=o4-CwDo2zpg
|
||||
@@ -1451,14 +1476,13 @@ class serializer
|
||||
*(--buffer_ptr) = static_cast<char>('0' + abs_value);
|
||||
}
|
||||
|
||||
put_buffer(number_buffer, n_chars);
|
||||
write_buffer_pos += n_chars;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief dump a floating-point number
|
||||
|
||||
Dump a given floating-point number, appending it to @ref write_buffer. Works internally
|
||||
with @a number_buffer.
|
||||
Dump a given floating-point number, appending it to @ref write_buffer.
|
||||
|
||||
@param[in] x floating-point number to dump
|
||||
*/
|
||||
@@ -1485,10 +1509,15 @@ class serializer
|
||||
|
||||
void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)
|
||||
{
|
||||
auto* begin = number_buffer.data();
|
||||
// directly into the write buffer: copying the text from number_buffer
|
||||
// right after to_chars() wrote it waits until its stores are done
|
||||
if (JSON_HEDLEY_UNLIKELY(write_buffer_pos + number_buffer.size() > write_buffer.size()))
|
||||
{
|
||||
flush();
|
||||
}
|
||||
auto* begin = write_buffer.data() + write_buffer_pos;
|
||||
auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);
|
||||
|
||||
put_buffer(number_buffer, static_cast<std::size_t>(end - begin));
|
||||
write_buffer_pos += static_cast<std::size_t>(end - begin);
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NON_NULL(1)
|
||||
|
||||
@@ -108,8 +108,8 @@ void encode_utf8(std::uint32_t cp, const Out& out)
|
||||
///////////////////
|
||||
|
||||
// UTF-8 decoder states used by decode() below
|
||||
static constexpr std::uint8_t UTF8_ACCEPT = 0;
|
||||
static constexpr std::uint8_t UTF8_REJECT = 1;
|
||||
JSON_INLINE_VARIABLE constexpr std::uint8_t UTF8_ACCEPT = 0;
|
||||
JSON_INLINE_VARIABLE constexpr std::uint8_t UTF8_REJECT = 1;
|
||||
|
||||
/*!
|
||||
@brief process a byte of a UTF-8 sequence
|
||||
|
||||
+1877
-616
File diff suppressed because it is too large.
Load diff
@@ -138,7 +138,8 @@ json_test_set_test_options(test-disabled_exceptions
|
||||
# only the #972 regression test needs thirdparty/fifo_map on its include path
|
||||
json_test_set_test_options(test-regression1 LINK_LIBRARIES fifo_map_include)
|
||||
|
||||
# GCC's false -Warray-bounds error with JSON_DIAGNOSTICS only shows up when optimizing (#5742).
|
||||
# Regression test for GCC's false -Warray-bounds error with JSON_DIAGNOSTICS (#5742, fixed in #5585). It only
|
||||
# showed up when optimizing, so build this test with -O3 and the warning as an error.
|
||||
# -O3 makes the optimizer-driven warnings of the ci_test_gcc flag set (-Winline,
|
||||
# -Wsuggest-attribute=...) fire on the library's inline functions; they are not
|
||||
# what this test checks, so turn them off for it.
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace custom_object_key_test
|
||||
{
|
||||
class key
|
||||
{
|
||||
public:
|
||||
key() = default;
|
||||
|
||||
key(const char* value)
|
||||
: m_value(value)
|
||||
{}
|
||||
|
||||
key(std::string value)
|
||||
: m_value(std::move(value))
|
||||
{}
|
||||
|
||||
operator std::string() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
// Required by JSON_DIAGNOSTICS, which reads object keys through data()
|
||||
// when building the path of an exception.
|
||||
const char* data() const noexcept
|
||||
{
|
||||
return m_value.data();
|
||||
}
|
||||
|
||||
friend bool operator<(const key& lhs, const key& rhs)
|
||||
{
|
||||
return lhs.m_value < rhs.m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_value;
|
||||
};
|
||||
|
||||
template<typename Key, typename Value, typename Compare, typename Allocator>
|
||||
class object
|
||||
: public std::map <
|
||||
key,
|
||||
Value,
|
||||
std::less<key>, // NOLINT(modernize-use-transparent-functors)
|
||||
typename std::allocator_traits<Allocator>::template rebind_alloc <
|
||||
std::pair<const key, Value >>>
|
||||
{
|
||||
private:
|
||||
using allocator_type =
|
||||
typename std::allocator_traits<Allocator>::template rebind_alloc <
|
||||
std::pair<const key, Value >>;
|
||||
|
||||
using base_type =
|
||||
std::map<key, Value, std::less<key>, allocator_type>; // NOLINT(modernize-use-transparent-functors)
|
||||
|
||||
public:
|
||||
using base_type::base_type;
|
||||
};
|
||||
|
||||
using json = nlohmann::basic_json<object>;
|
||||
} // namespace custom_object_key_test
|
||||
@@ -0,0 +1,599 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array> // array
|
||||
#include <cstdint> // uint32_t, uint64_t
|
||||
|
||||
// Number tokens that are hard to round correctly, with the IEEE-754 binary64
|
||||
// and binary32 bits of their correctly rounded values (ties to even; infinity
|
||||
// for an overflow, a signed zero for an underflow).
|
||||
//
|
||||
// For doubles and floats around 0, the smallest normal number, 1, 2^24, 2^53,
|
||||
// 0.1, and the largest finite number, and for random ones, the exact midpoint
|
||||
// m to the next number gives: m, m with one unit more and less in the last
|
||||
// digit, m with "01" and "0...01" appended, m with trailing zeros, and m cut
|
||||
// after 17 to 30 digits (rounded down and up, so that the rounding is decided
|
||||
// after the 19th digit), in fixed and exponent notation, 30% of them negative.
|
||||
// Tokens longer than 80 characters are left out, except for four of 700 digits
|
||||
// and more. Zeros, underflow, overflow, huge exponents, and integers beyond 64
|
||||
// bits complete the set. Of the 508 tokens, 134 (as double) and 150 (as
|
||||
// float) need the exact comparison with the midpoint (detail::digit_comparison()).
|
||||
//
|
||||
// The expected bits were computed with exact rational arithmetic in Python
|
||||
// (fractions.Fraction) and cross-checked with Python's float(); strtod_l and
|
||||
// strtof_l of Apple's libc and of glibc agree. Generated by
|
||||
// compact_hard_cases.py 5 (with hard_cases.py), see the pull request that
|
||||
// added this file.
|
||||
|
||||
namespace float_hard_cases
|
||||
{
|
||||
|
||||
struct hard_case
|
||||
{
|
||||
const char* token;
|
||||
std::uint64_t bits64;
|
||||
std::uint32_t bits32;
|
||||
};
|
||||
|
||||
inline const std::array<hard_case, 508>& cases()
|
||||
{
|
||||
static const std::array<hard_case, 508> table =
|
||||
{
|
||||
{
|
||||
{"-2.4703282292062327e-324", 0x8000000000000000u, 0x80000000u},
|
||||
{"24703282292062328e-340", 0x0000000000000001u, 0x00000000u},
|
||||
{"247032822920623272e-341", 0x0000000000000000u, 0x00000000u},
|
||||
{"-0.2470328229206232721e-323", 0x8000000000000001u, 0x80000000u},
|
||||
{"-0.24703282292062327208e-323", 0x8000000000000000u, 0x80000000u},
|
||||
{"-2.4703282292062327209e-324", 0x8000000000000001u, 0x80000000u},
|
||||
{"2.47032822920623272088e-324", 0x0000000000000000u, 0x00000000u},
|
||||
{"247032822920623272089e-344", 0x0000000000000001u, 0x00000000u},
|
||||
{"-247032822920623272088284396434e-353", 0x8000000000000000u, 0x80000000u},
|
||||
{"0.247032822920623272088284396435e-323", 0x0000000000000001u, 0x00000000u},
|
||||
{"-74109846876186981e-340", 0x8000000000000001u, 0x80000000u},
|
||||
{"0.74109846876186982e-323", 0x0000000000000002u, 0x00000000u},
|
||||
{"-0.7410984687618698162e-323", 0x8000000000000001u, 0x80000000u},
|
||||
{"-7.410984687618698163e-324", 0x8000000000000002u, 0x80000000u},
|
||||
{"7.4109846876186981626e-324", 0x0000000000000001u, 0x00000000u},
|
||||
{"-74109846876186981627e-343", 0x8000000000000002u, 0x80000000u},
|
||||
{"-741098468761869816264e-344", 0x8000000000000001u, 0x80000000u},
|
||||
{"0.741098468761869816265e-323", 0x0000000000000002u, 0x00000000u},
|
||||
{"0.741098468761869816264853189302e-323", 0x0000000000000001u, 0x00000000u},
|
||||
{"-7.41098468761869816264853189303e-324", 0x8000000000000002u, 0x80000000u},
|
||||
{"0.22250738585072006e-307", 0x000FFFFFFFFFFFFEu, 0x00000000u},
|
||||
{"2.2250738585072007e-308", 0x000FFFFFFFFFFFFFu, 0x00000000u},
|
||||
{"2.225073858507200641e-308", 0x000FFFFFFFFFFFFEu, 0x00000000u},
|
||||
{"-2225073858507200642e-326", 0x800FFFFFFFFFFFFFu, 0x80000000u},
|
||||
{"22250738585072006419e-327", 0x000FFFFFFFFFFFFEu, 0x00000000u},
|
||||
{"0.2225073858507200642e-307", 0x000FFFFFFFFFFFFFu, 0x00000000u},
|
||||
{"0.222507385850720064199e-307", 0x000FFFFFFFFFFFFEu, 0x00000000u},
|
||||
{"2.225073858507200642e-308", 0x000FFFFFFFFFFFFFu, 0x00000000u},
|
||||
{"-2.22507385850720064199176395546e-308", 0x800FFFFFFFFFFFFEu, 0x80000000u},
|
||||
{"222507385850720064199176395547e-337", 0x000FFFFFFFFFFFFFu, 0x00000000u},
|
||||
{"-2.2250738585072011e-308", 0x800FFFFFFFFFFFFFu, 0x80000000u},
|
||||
{"-22250738585072012e-324", 0x8010000000000000u, 0x80000000u},
|
||||
{"-2225073858507201136e-326", 0x800FFFFFFFFFFFFFu, 0x80000000u},
|
||||
{"0.2225073858507201137e-307", 0x0010000000000000u, 0x00000000u},
|
||||
{"0.2225073858507201136e-307", 0x000FFFFFFFFFFFFFu, 0x00000000u},
|
||||
{"-2.2250738585072011361e-308", 0x8010000000000000u, 0x80000000u},
|
||||
{"2.22507385850720113605e-308", 0x000FFFFFFFFFFFFFu, 0x00000000u},
|
||||
{"222507385850720113606e-328", 0x0010000000000000u, 0x00000000u},
|
||||
{"22250738585072011360574097967e-336", 0x000FFFFFFFFFFFFFu, 0x00000000u},
|
||||
{"0.222507385850720113605740979671e-307", 0x0010000000000000u, 0x00000000u},
|
||||
{"22250738585072016e-324", 0x0010000000000000u, 0x00000000u},
|
||||
{"0.22250738585072017e-307", 0x0010000000000001u, 0x00000000u},
|
||||
{"0.222507385850720163e-307", 0x0010000000000000u, 0x00000000u},
|
||||
{"2.225073858507201631e-308", 0x0010000000000001u, 0x00000000u},
|
||||
{"-2.2250738585072016301e-308", 0x8010000000000000u, 0x80000000u},
|
||||
{"22250738585072016302e-327", 0x0010000000000001u, 0x00000000u},
|
||||
{"-222507385850720163012e-328", 0x8010000000000000u, 0x80000000u},
|
||||
{"0.222507385850720163013e-307", 0x0010000000000001u, 0x00000000u},
|
||||
{"0.222507385850720163012305563795e-307", 0x0010000000000000u, 0x00000000u},
|
||||
{"-2.22507385850720163012305563796e-308", 0x8010000000000001u, 0x80000000u},
|
||||
{"0.17976931348623156E+309", 0x7FEFFFFFFFFFFFFEu, 0x7F800000u},
|
||||
{"1.7976931348623157e308", 0x7FEFFFFFFFFFFFFFu, 0x7F800000u},
|
||||
{"1.797693134862315608e308", 0x7FEFFFFFFFFFFFFEu, 0x7F800000u},
|
||||
{"-1797693134862315609e290", 0xFFEFFFFFFFFFFFFFu, 0xFF800000u},
|
||||
{"-17976931348623156083e289", 0xFFEFFFFFFFFFFFFEu, 0xFF800000u},
|
||||
{"-0.17976931348623156084E+309", 0xFFEFFFFFFFFFFFFFu, 0xFF800000u},
|
||||
{"0.179769313486231560835E+309", 0x7FEFFFFFFFFFFFFEu, 0x7F800000u},
|
||||
{"-1.79769313486231560836e308", 0xFFEFFFFFFFFFFFFFu, 0xFF800000u},
|
||||
{"1.79769313486231560835325876058e308", 0x7FEFFFFFFFFFFFFEu, 0x7F800000u},
|
||||
{"179769313486231560835325876059e279", 0x7FEFFFFFFFFFFFFFu, 0x7F800000u},
|
||||
{"1.7976931348623158e308", 0x7FEFFFFFFFFFFFFFu, 0x7F800000u},
|
||||
{"17976931348623159e292", 0x7FF0000000000000u, 0x7F800000u},
|
||||
{"1797693134862315807e290", 0x7FEFFFFFFFFFFFFFu, 0x7F800000u},
|
||||
{"0.1797693134862315808E+309", 0x7FF0000000000000u, 0x7F800000u},
|
||||
{"0.17976931348623158079E+309", 0x7FEFFFFFFFFFFFFFu, 0x7F800000u},
|
||||
{"-1.797693134862315808e308", 0xFFF0000000000000u, 0xFF800000u},
|
||||
{"1.79769313486231580793e308", 0x7FEFFFFFFFFFFFFFu, 0x7F800000u},
|
||||
{"179769313486231580794e288", 0x7FF0000000000000u, 0x7F800000u},
|
||||
{"179769313486231580793728971405e279", 0x7FEFFFFFFFFFFFFFu, 0x7F800000u},
|
||||
{"-0.179769313486231580793728971406E+309", 0xFFF0000000000000u, 0xFF800000u},
|
||||
{"100000000000000011102230246251565404236316680908203125e-53", 0x3FF0000000000000u, 0x3F800000u},
|
||||
{"-1.00000000000000011102230246251565404236316680908203126", 0xBFF0000000000001u, 0xBF800000u},
|
||||
{"1.00000000000000011102230246251565404236316680908203124e0", 0x3FF0000000000000u, 0x3F800000u},
|
||||
{"10000000000000001110223024625156540423631668090820312501e-55", 0x3FF0000000000001u, 0x3F800000u},
|
||||
{"1.00000000000000011102230246251565404236316680908203125000000000000000000001", 0x3FF0000000000001u, 0x3F800000u},
|
||||
{"10000000000000001e-16", 0x3FF0000000000000u, 0x3F800000u},
|
||||
{"1.0000000000000002", 0x3FF0000000000001u, 0x3F800000u},
|
||||
{"1.000000000000000111", 0x3FF0000000000000u, 0x3F800000u},
|
||||
{"1.000000000000000112e0", 0x3FF0000000000001u, 0x3F800000u},
|
||||
{"1.000000000000000111e0", 0x3FF0000000000000u, 0x3F800000u},
|
||||
{"-10000000000000001111e-19", 0xBFF0000000000001u, 0xBF800000u},
|
||||
{"-100000000000000011102e-20", 0xBFF0000000000000u, 0xBF800000u},
|
||||
{"-1.00000000000000011103", 0xBFF0000000000001u, 0xBF800000u},
|
||||
{"1.00000000000000011102230246251", 0x3FF0000000000000u, 0x3F800000u},
|
||||
{"1.00000000000000011102230246252e0", 0x3FF0000000000001u, 0x3F800000u},
|
||||
{"-0.999999999999999944488848768742172978818416595458984375", 0xBFF0000000000000u, 0xBF800000u},
|
||||
{"-9.99999999999999944488848768742172978818416595458984376e-1", 0xBFF0000000000000u, 0xBF800000u},
|
||||
{"999999999999999944488848768742172978818416595458984374e-54", 0x3FEFFFFFFFFFFFFFu, 0x3F800000u},
|
||||
{"0.99999999999999994448884876874217297881841659545898437501", 0x3FF0000000000000u, 0x3F800000u},
|
||||
{"9.99999999999999944488848768742172978818416595458984375000000000000000000001e-1", 0x3FF0000000000000u, 0x3F800000u},
|
||||
{"-0.99999999999999994", 0xBFEFFFFFFFFFFFFFu, 0xBF800000u},
|
||||
{"9.9999999999999995e-1", 0x3FF0000000000000u, 0x3F800000u},
|
||||
{"9.999999999999999444e-1", 0x3FEFFFFFFFFFFFFFu, 0x3F800000u},
|
||||
{"9999999999999999445e-19", 0x3FF0000000000000u, 0x3F800000u},
|
||||
{"99999999999999994448e-20", 0x3FEFFFFFFFFFFFFFu, 0x3F800000u},
|
||||
{"-0.99999999999999994449", 0xBFF0000000000000u, 0xBF800000u},
|
||||
{"0.999999999999999944488", 0x3FEFFFFFFFFFFFFFu, 0x3F800000u},
|
||||
{"-9.99999999999999944489e-1", 0xBFF0000000000000u, 0xBF800000u},
|
||||
{"9.99999999999999944488848768742e-1", 0x3FEFFFFFFFFFFFFFu, 0x3F800000u},
|
||||
{"999999999999999944488848768743e-30", 0x3FF0000000000000u, 0x3F800000u},
|
||||
{"-9.007199254740993e15", 0xC340000000000000u, 0xDA000000u},
|
||||
{"9007199254740994e0", 0x4340000000000001u, 0x5A000000u},
|
||||
{"9007199254740992", 0x4340000000000000u, 0x5A000000u},
|
||||
{"9.00719925474099301e15", 0x4340000000000001u, 0x5A000000u},
|
||||
{"9007199254740993000000000000000000001e-21", 0x4340000000000001u, 0x5A000000u},
|
||||
{"-9007199254740993.000000000000000000000000000000", 0xC340000000000000u, 0xDA000000u},
|
||||
{"90071992547409915e-1", 0x4340000000000000u, 0x5A000000u},
|
||||
{"-9007199254740991.6", 0xC340000000000000u, 0xDA000000u},
|
||||
{"9.0071992547409914e15", 0x433FFFFFFFFFFFFFu, 0x5A000000u},
|
||||
{"-9007199254740991501e-3", 0xC340000000000000u, 0xDA000000u},
|
||||
{"9007199254740991.5000000000000000000001", 0x4340000000000000u, 0x5A000000u},
|
||||
{"9.0071992547409915000000000000000000000000000000e15", 0x4340000000000000u, 0x5A000000u},
|
||||
{"0.100000000000000012490009027033011079765856266021728515625", 0x3FB999999999999Au, 0x3DCCCCCDu},
|
||||
{"1.00000000000000012490009027033011079765856266021728515626e-1", 0x3FB999999999999Bu, 0x3DCCCCCDu},
|
||||
{"100000000000000012490009027033011079765856266021728515624e-57", 0x3FB999999999999Au, 0x3DCCCCCDu},
|
||||
{"0.10000000000000001249000902703301107976585626602172851562501", 0x3FB999999999999Bu, 0x3DCCCCCDu},
|
||||
{"0.10000000000000001", 0x3FB999999999999Au, 0x3DCCCCCDu},
|
||||
{"1.0000000000000002e-1", 0x3FB999999999999Bu, 0x3DCCCCCDu},
|
||||
{"-1.000000000000000124e-1", 0xBFB999999999999Au, 0xBDCCCCCDu},
|
||||
{"1000000000000000125e-19", 0x3FB999999999999Bu, 0x3DCCCCCDu},
|
||||
{"-10000000000000001249e-20", 0xBFB999999999999Au, 0xBDCCCCCDu},
|
||||
{"0.1000000000000000125", 0x3FB999999999999Bu, 0x3DCCCCCDu},
|
||||
{"0.10000000000000001249", 0x3FB999999999999Au, 0x3DCCCCCDu},
|
||||
{"1.00000000000000012491e-1", 0x3FB999999999999Bu, 0x3DCCCCCDu},
|
||||
{"1.00000000000000012490009027033e-1", 0x3FB999999999999Au, 0x3DCCCCCDu},
|
||||
{"100000000000000012490009027034e-30", 0x3FB999999999999Bu, 0x3DCCCCCDu},
|
||||
{"2.45134755833537796875e14", 0x42EBDE5C4164D83Au, 0x575EF2E2u},
|
||||
{"-245134755833537796876e-6", 0xC2EBDE5C4164D83Au, 0xD75EF2E2u},
|
||||
{"-245134755833537.796874", 0xC2EBDE5C4164D839u, 0xD75EF2E2u},
|
||||
{"2.4513475583353779687501e14", 0x42EBDE5C4164D83Au, 0x575EF2E2u},
|
||||
{"245134755833537796875000000000000000000001e-27", 0x42EBDE5C4164D83Au, 0x575EF2E2u},
|
||||
{"245134755833537.796875000000000000000000000000000000", 0x42EBDE5C4164D83Au, 0x575EF2E2u},
|
||||
{"2.4513475583353779e14", 0x42EBDE5C4164D839u, 0x575EF2E2u},
|
||||
{"2451347558335378e-1", 0x42EBDE5C4164D83Au, 0x575EF2E2u},
|
||||
{"2451347558335377968e-4", 0x42EBDE5C4164D839u, 0x575EF2E2u},
|
||||
{"245134755833537.7969", 0x42EBDE5C4164D83Au, 0x575EF2E2u},
|
||||
{"245134755833537.79687", 0x42EBDE5C4164D839u, 0x575EF2E2u},
|
||||
{"2.4513475583353779688e14", 0x42EBDE5C4164D83Au, 0x575EF2E2u},
|
||||
{"181510327827821147441864013671875e-23", 0x41DB0C11CB91CE38u, 0x4ED8608Eu},
|
||||
{"-1815103278.27821147441864013671876", 0xC1DB0C11CB91CE38u, 0xCED8608Eu},
|
||||
{"1.81510327827821147441864013671874e9", 0x41DB0C11CB91CE37u, 0x4ED8608Eu},
|
||||
{"18151032782782114744186401367187501e-25", 0x41DB0C11CB91CE38u, 0x4ED8608Eu},
|
||||
{"1815103278.27821147441864013671875000000000000000000001", 0x41DB0C11CB91CE38u, 0x4ED8608Eu},
|
||||
{"-1.81510327827821147441864013671875000000000000000000000000000000e9", 0xC1DB0C11CB91CE38u, 0xCED8608Eu},
|
||||
{"18151032782782114e-7", 0x41DB0C11CB91CE37u, 0x4ED8608Eu},
|
||||
{"1815103278.2782115", 0x41DB0C11CB91CE38u, 0x4ED8608Eu},
|
||||
{"1815103278.278211474", 0x41DB0C11CB91CE37u, 0x4ED8608Eu},
|
||||
{"-1.815103278278211475e9", 0xC1DB0C11CB91CE38u, 0xCED8608Eu},
|
||||
{"1.8151032782782114744e9", 0x41DB0C11CB91CE37u, 0x4ED8608Eu},
|
||||
{"-18151032782782114745e-10", 0xC1DB0C11CB91CE38u, 0xCED8608Eu},
|
||||
{"181510327827821147441e-11", 0x41DB0C11CB91CE37u, 0x4ED8608Eu},
|
||||
{"1815103278.27821147442", 0x41DB0C11CB91CE38u, 0x4ED8608Eu},
|
||||
{"1815103278.27821147441864013671", 0x41DB0C11CB91CE37u, 0x4ED8608Eu},
|
||||
{"1.81510327827821147441864013672e9", 0x41DB0C11CB91CE38u, 0x4ED8608Eu},
|
||||
{"3809325632181785344", 0x43CA6EB8BD69FE2Au, 0x5E5375C6u},
|
||||
{"3.809325632181785345e18", 0x43CA6EB8BD69FE2Au, 0x5E5375C6u},
|
||||
{"3809325632181785343e0", 0x43CA6EB8BD69FE29u, 0x5E5375C6u},
|
||||
{"3809325632181785344.01", 0x43CA6EB8BD69FE2Au, 0x5E5375C6u},
|
||||
{"3.809325632181785344000000000000000000001e18", 0x43CA6EB8BD69FE2Au, 0x5E5375C6u},
|
||||
{"3809325632181785344000000000000000000000000000000e-30", 0x43CA6EB8BD69FE2Au, 0x5E5375C6u},
|
||||
{"3809325632181785300", 0x43CA6EB8BD69FE29u, 0x5E5375C6u},
|
||||
{"3.8093256321817854e18", 0x43CA6EB8BD69FE2Au, 0x5E5375C6u},
|
||||
{"4.046966549916366943359375e12", 0x428D7210076CE2F0u, 0x546B9080u},
|
||||
{"4046966549916366943359376e-12", 0x428D7210076CE2F0u, 0x546B9080u},
|
||||
{"4046966549916.366943359374", 0x428D7210076CE2EFu, 0x546B9080u},
|
||||
{"4.04696654991636694335937501e12", 0x428D7210076CE2F0u, 0x546B9080u},
|
||||
{"4046966549916366943359375000000000000000000001e-33", 0x428D7210076CE2F0u, 0x546B9080u},
|
||||
{"-4046966549916.366943359375000000000000000000000000000000", 0xC28D7210076CE2F0u, 0xD46B9080u},
|
||||
{"4.0469665499163669e12", 0x428D7210076CE2EFu, 0x546B9080u},
|
||||
{"4046966549916367e-3", 0x428D7210076CE2F0u, 0x546B9080u},
|
||||
{"4046966549916366943e-6", 0x428D7210076CE2EFu, 0x546B9080u},
|
||||
{"4046966549916.366944", 0x428D7210076CE2F0u, 0x546B9080u},
|
||||
{"4046966549916.3669433", 0x428D7210076CE2EFu, 0x546B9080u},
|
||||
{"4.0469665499163669434e12", 0x428D7210076CE2F0u, 0x546B9080u},
|
||||
{"-4.04696654991636694335e12", 0xC28D7210076CE2EFu, 0xD46B9080u},
|
||||
{"404696654991636694336e-8", 0x428D7210076CE2F0u, 0x546B9080u},
|
||||
{"28093802557000874e154", 0x63529C3B77330BDBu, 0x7F800000u},
|
||||
{"0.28093802557000875E+171", 0x63529C3B77330BDCu, 0x7F800000u},
|
||||
{"0.2809380255700087447E+171", 0x63529C3B77330BDBu, 0x7F800000u},
|
||||
{"2.809380255700087448e170", 0x63529C3B77330BDCu, 0x7F800000u},
|
||||
{"2.8093802557000874472e170", 0x63529C3B77330BDBu, 0x7F800000u},
|
||||
{"28093802557000874473e151", 0x63529C3B77330BDCu, 0x7F800000u},
|
||||
{"280938025570008744728e150", 0x63529C3B77330BDBu, 0x7F800000u},
|
||||
{"-0.280938025570008744729E+171", 0xE3529C3B77330BDCu, 0xFF800000u},
|
||||
{"-0.280938025570008744728403667979E+171", 0xE3529C3B77330BDBu, 0xFF800000u},
|
||||
{"2.8093802557000874472840366798e170", 0x63529C3B77330BDCu, 0x7F800000u},
|
||||
{"0.39523280297734525e-154", 0x1FE0F51BF17FD374u, 0x00000000u},
|
||||
{"-3.9523280297734526e-155", 0x9FE0F51BF17FD375u, 0x80000000u},
|
||||
{"3.952328029773452547e-155", 0x1FE0F51BF17FD374u, 0x00000000u},
|
||||
{"3952328029773452548e-173", 0x1FE0F51BF17FD375u, 0x00000000u},
|
||||
{"-39523280297734525478e-174", 0x9FE0F51BF17FD374u, 0x80000000u},
|
||||
{"0.39523280297734525479e-154", 0x1FE0F51BF17FD375u, 0x00000000u},
|
||||
{"-0.395232802977345254787e-154", 0x9FE0F51BF17FD374u, 0x80000000u},
|
||||
{"-3.95232802977345254788e-155", 0x9FE0F51BF17FD375u, 0x80000000u},
|
||||
{"-3.95232802977345254787245825501e-155", 0x9FE0F51BF17FD374u, 0x80000000u},
|
||||
{"395232802977345254787245825502e-184", 0x1FE0F51BF17FD375u, 0x00000000u},
|
||||
{"-1.0790205420931879e-276", 0x86A3209CA6233255u, 0x80000000u},
|
||||
{"-1079020542093188e-291", 0x86A3209CA6233256u, 0x80000000u},
|
||||
{"-1079020542093187947e-294", 0x86A3209CA6233255u, 0x80000000u},
|
||||
{"0.1079020542093187948e-275", 0x06A3209CA6233256u, 0x00000000u},
|
||||
{"0.1079020542093187947e-275", 0x06A3209CA6233255u, 0x00000000u},
|
||||
{"1.0790205420931879471e-276", 0x06A3209CA6233256u, 0x00000000u},
|
||||
{"1.07902054209318794701e-276", 0x06A3209CA6233255u, 0x00000000u},
|
||||
{"-107902054209318794702e-296", 0x86A3209CA6233256u, 0x80000000u},
|
||||
{"107902054209318794701153285302e-305", 0x06A3209CA6233255u, 0x00000000u},
|
||||
{"-0.107902054209318794701153285303e-275", 0x86A3209CA6233256u, 0x80000000u},
|
||||
{"58530471071351308e-228", 0x1413B446E6A16A3Bu, 0x00000000u},
|
||||
{"-0.58530471071351309e-211", 0x9413B446E6A16A3Cu, 0x80000000u},
|
||||
{"0.5853047107135130893e-211", 0x1413B446E6A16A3Bu, 0x00000000u},
|
||||
{"-5.853047107135130894e-212", 0x9413B446E6A16A3Cu, 0x80000000u},
|
||||
{"5.853047107135130893e-212", 0x1413B446E6A16A3Bu, 0x00000000u},
|
||||
{"58530471071351308931e-231", 0x1413B446E6A16A3Cu, 0x00000000u},
|
||||
{"-585304710713513089304e-232", 0x9413B446E6A16A3Bu, 0x80000000u},
|
||||
{"0.585304710713513089305e-211", 0x1413B446E6A16A3Cu, 0x00000000u},
|
||||
{"-0.585304710713513089304248824438e-211", 0x9413B446E6A16A3Bu, 0x80000000u},
|
||||
{"5.85304710713513089304248824439e-212", 0x1413B446E6A16A3Cu, 0x00000000u},
|
||||
{"0.19334214893983531e-78", 0x2F96ECBF1CFB10F6u, 0x00000000u},
|
||||
{"1.9334214893983532e-79", 0x2F96ECBF1CFB10F7u, 0x00000000u},
|
||||
{"1.933421489398353102e-79", 0x2F96ECBF1CFB10F6u, 0x00000000u},
|
||||
{"1933421489398353103e-97", 0x2F96ECBF1CFB10F7u, 0x00000000u},
|
||||
{"19334214893983531023e-98", 0x2F96ECBF1CFB10F6u, 0x00000000u},
|
||||
{"0.19334214893983531024e-78", 0x2F96ECBF1CFB10F7u, 0x00000000u},
|
||||
{"0.193342148939835310231e-78", 0x2F96ECBF1CFB10F6u, 0x00000000u},
|
||||
{"1.93342148939835310232e-79", 0x2F96ECBF1CFB10F7u, 0x00000000u},
|
||||
{"-1.93342148939835310231359014704e-79", 0xAF96ECBF1CFB10F6u, 0x80000000u},
|
||||
{"193342148939835310231359014705e-108", 0x2F96ECBF1CFB10F7u, 0x00000000u},
|
||||
{"2.9873358928024455e227", 0x6F2938807814E8A2u, 0x7F800000u},
|
||||
{"29873358928024456e211", 0x6F2938807814E8A3u, 0x7F800000u},
|
||||
{"298733589280244551e210", 0x6F2938807814E8A2u, 0x7F800000u},
|
||||
{"0.2987335892802445511E+228", 0x6F2938807814E8A3u, 0x7F800000u},
|
||||
{"0.29873358928024455109E+228", 0x6F2938807814E8A2u, 0x7F800000u},
|
||||
{"2.987335892802445511e227", 0x6F2938807814E8A3u, 0x7F800000u},
|
||||
{"-2.98733589280244551098e227", 0xEF2938807814E8A2u, 0xFF800000u},
|
||||
{"-298733589280244551099e207", 0xEF2938807814E8A3u, 0xFF800000u},
|
||||
{"298733589280244551098081559931e198", 0x6F2938807814E8A2u, 0x7F800000u},
|
||||
{"0.298733589280244551098081559932E+228", 0x6F2938807814E8A3u, 0x7F800000u},
|
||||
{"7.0064923216240853e-46", 0x3690000000000000u, 0x00000000u},
|
||||
{"70064923216240854e-62", 0x3690000000000000u, 0x00000001u},
|
||||
{"7006492321624085354e-64", 0x3690000000000000u, 0x00000000u},
|
||||
{"-0.7006492321624085355e-45", 0xB690000000000000u, 0x80000001u},
|
||||
{"0.70064923216240853546e-45", 0x3690000000000000u, 0x00000000u},
|
||||
{"7.0064923216240853547e-46", 0x3690000000000000u, 0x00000001u},
|
||||
{"7.00649232162408535461e-46", 0x3690000000000000u, 0x00000000u},
|
||||
{"-700649232162408535462e-66", 0xB690000000000000u, 0x80000001u},
|
||||
{"-700649232162408535461864791644e-75", 0xB690000000000000u, 0x80000000u},
|
||||
{"0.700649232162408535461864791645e-45", 0x3690000000000000u, 0x00000001u},
|
||||
{"21019476964872256e-61", 0x36A8000000000000u, 0x00000001u},
|
||||
{"0.21019476964872257e-44", 0x36A8000000000000u, 0x00000002u},
|
||||
{"-0.2101947696487225606e-44", 0xB6A8000000000000u, 0x80000001u},
|
||||
{"-2.101947696487225607e-45", 0xB6A8000000000000u, 0x80000002u},
|
||||
{"2.1019476964872256063e-45", 0x36A8000000000000u, 0x00000001u},
|
||||
{"21019476964872256064e-64", 0x36A8000000000000u, 0x00000002u},
|
||||
{"-210194769648722560638e-65", 0xB6A8000000000000u, 0x80000001u},
|
||||
{"-0.210194769648722560639e-44", 0xB6A8000000000000u, 0x80000002u},
|
||||
{"-0.210194769648722560638559437493e-44", 0xB6A8000000000000u, 0x80000001u},
|
||||
{"2.10194769648722560638559437494e-45", 0x36A8000000000000u, 0x00000002u},
|
||||
{"0.11754941406275178e-37", 0x380FFFFFA0000000u, 0x007FFFFEu},
|
||||
{"1.1754941406275179e-38", 0x380FFFFFA0000000u, 0x007FFFFFu},
|
||||
{"-1.175494140627517859e-38", 0xB80FFFFFA0000000u, 0x807FFFFEu},
|
||||
{"117549414062751786e-55", 0x380FFFFFA0000000u, 0x007FFFFFu},
|
||||
{"-11754941406275178592e-57", 0xB80FFFFFA0000000u, 0x807FFFFEu},
|
||||
{"0.11754941406275178593e-37", 0x380FFFFFA0000000u, 0x007FFFFFu},
|
||||
{"0.117549414062751785924e-37", 0x380FFFFFA0000000u, 0x007FFFFEu},
|
||||
{"1.17549414062751785925e-38", 0x380FFFFFA0000000u, 0x007FFFFFu},
|
||||
{"-1.17549414062751785924617589866e-38", 0xB80FFFFFA0000000u, 0x807FFFFEu},
|
||||
{"117549414062751785924617589867e-67", 0x380FFFFFA0000000u, 0x007FFFFFu},
|
||||
{"1.1754942807573642e-38", 0x380FFFFFDFFFFFFFu, 0x007FFFFFu},
|
||||
{"11754942807573643e-54", 0x380FFFFFE0000000u, 0x00800000u},
|
||||
{"1175494280757364291e-56", 0x380FFFFFE0000000u, 0x007FFFFFu},
|
||||
{"0.1175494280757364292e-37", 0x380FFFFFE0000000u, 0x00800000u},
|
||||
{"0.11754942807573642917e-37", 0x380FFFFFE0000000u, 0x007FFFFFu},
|
||||
{"-1.1754942807573642918e-38", 0xB80FFFFFE0000000u, 0x80800000u},
|
||||
{"-1.17549428075736429172e-38", 0xB80FFFFFE0000000u, 0x807FFFFFu},
|
||||
{"-117549428075736429173e-58", 0xB80FFFFFE0000000u, 0x80800000u},
|
||||
{"117549428075736429172788299103e-67", 0x380FFFFFE0000000u, 0x007FFFFFu},
|
||||
{"0.117549428075736429172788299104e-37", 0x380FFFFFE0000000u, 0x00800000u},
|
||||
{"11754944208872107e-54", 0x3810000010000000u, 0x00800000u},
|
||||
{"-0.11754944208872108e-37", 0xB810000010000000u, 0x80800001u},
|
||||
{"0.1175494420887210724e-37", 0x3810000010000000u, 0x00800000u},
|
||||
{"1.175494420887210725e-38", 0x3810000010000000u, 0x00800001u},
|
||||
{"1.1754944208872107242e-38", 0x3810000010000000u, 0x00800000u},
|
||||
{"-11754944208872107243e-57", 0xB810000010000000u, 0x80800001u},
|
||||
{"-11754944208872107242e-57", 0xB810000010000000u, 0x80800000u},
|
||||
{"0.117549442088721072421e-37", 0x3810000010000000u, 0x00800001u},
|
||||
{"0.11754944208872107242095900834e-37", 0x3810000010000000u, 0x00800000u},
|
||||
{"-1.17549442088721072420959008341e-38", 0xB810000010000000u, 0x80800001u},
|
||||
{"340282336497324057985868971510891282432", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
||||
{"-3.40282336497324057985868971510891282433e38", 0xC7EFFFFFD0000000u, 0xFF7FFFFFu},
|
||||
{"340282336497324057985868971510891282431e0", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
||||
{"340282336497324057985868971510891282432.01", 0x47EFFFFFD0000000u, 0x7F7FFFFFu},
|
||||
{"3.40282336497324057985868971510891282432000000000000000000001e38", 0x47EFFFFFD0000000u, 0x7F7FFFFFu},
|
||||
{"340282336497324057985868971510891282432000000000000000000000000000000e-30", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
||||
{"340282336497324050000000000000000000000", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
||||
{"3.4028233649732406e38", 0x47EFFFFFD0000000u, 0x7F7FFFFFu},
|
||||
{"3.402823364973240579e38", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
||||
{"340282336497324058e21", 0x47EFFFFFD0000000u, 0x7F7FFFFFu},
|
||||
{"34028233649732405798e19", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
||||
{"340282336497324057990000000000000000000", 0x47EFFFFFD0000000u, 0x7F7FFFFFu},
|
||||
{"340282336497324057985000000000000000000", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
||||
{"3.40282336497324057986e38", 0x47EFFFFFD0000000u, 0x7F7FFFFFu},
|
||||
{"3.4028233649732405798586897151e38", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
||||
{"340282336497324057985868971511e9", 0x47EFFFFFD0000000u, 0x7F7FFFFFu},
|
||||
{"3.40282356779733661637539395458142568448e38", 0x47EFFFFFF0000000u, 0x7F800000u},
|
||||
{"-340282356779733661637539395458142568449e0", 0xC7EFFFFFF0000000u, 0xFF800000u},
|
||||
{"340282356779733661637539395458142568447", 0x47EFFFFFF0000000u, 0x7F7FFFFFu},
|
||||
{"3.4028235677973366163753939545814256844801e38", 0x47EFFFFFF0000000u, 0x7F800000u},
|
||||
{"340282356779733661637539395458142568448000000000000000000001e-21", 0x47EFFFFFF0000000u, 0x7F800000u},
|
||||
{"340282356779733661637539395458142568448.000000000000000000000000000000", 0x47EFFFFFF0000000u, 0x7F800000u},
|
||||
{"3.4028235677973366e38", 0x47EFFFFFF0000000u, 0x7F7FFFFFu},
|
||||
{"-34028235677973367e22", 0xC7EFFFFFF0000000u, 0xFF800000u},
|
||||
{"3402823567797336616e20", 0x47EFFFFFF0000000u, 0x7F7FFFFFu},
|
||||
{"340282356779733661700000000000000000000", 0x47EFFFFFF0000000u, 0x7F800000u},
|
||||
{"340282356779733661630000000000000000000", 0x47EFFFFFF0000000u, 0x7F7FFFFFu},
|
||||
{"3.4028235677973366164e38", 0x47EFFFFFF0000000u, 0x7F800000u},
|
||||
{"3.40282356779733661637e38", 0x47EFFFFFF0000000u, 0x7F7FFFFFu},
|
||||
{"340282356779733661638e18", 0x47EFFFFFF0000000u, 0x7F800000u},
|
||||
{"-340282356779733661637539395458e9", 0xC7EFFFFFF0000000u, 0xFF7FFFFFu},
|
||||
{"340282356779733661637539395459000000000", 0x47EFFFFFF0000000u, 0x7F800000u},
|
||||
{"-1000000059604644775390625e-24", 0xBFF0000010000000u, 0xBF800000u},
|
||||
{"-1.000000059604644775390626", 0xBFF0000010000000u, 0xBF800001u},
|
||||
{"-1.000000059604644775390624e0", 0xBFF0000010000000u, 0xBF800000u},
|
||||
{"100000005960464477539062501e-26", 0x3FF0000010000000u, 0x3F800001u},
|
||||
{"1.000000059604644775390625000000000000000000001", 0x3FF0000010000000u, 0x3F800001u},
|
||||
{"1.000000059604644775390625000000000000000000000000000000e0", 0x3FF0000010000000u, 0x3F800000u},
|
||||
{"-10000000596046447e-16", 0xBFF0000010000000u, 0xBF800000u},
|
||||
{"1.0000000596046448", 0x3FF0000010000000u, 0x3F800001u},
|
||||
{"1.000000059604644775", 0x3FF0000010000000u, 0x3F800000u},
|
||||
{"-1.000000059604644776e0", 0xBFF0000010000000u, 0xBF800001u},
|
||||
{"-1.0000000596046447753e0", 0xBFF0000010000000u, 0xBF800000u},
|
||||
{"10000000596046447754e-19", 0x3FF0000010000000u, 0x3F800001u},
|
||||
{"100000005960464477539e-20", 0x3FF0000010000000u, 0x3F800000u},
|
||||
{"-1.0000000596046447754", 0xBFF0000010000000u, 0xBF800001u},
|
||||
{"0.9999999701976776123046875", 0x3FEFFFFFF0000000u, 0x3F800000u},
|
||||
{"9.999999701976776123046876e-1", 0x3FEFFFFFF0000000u, 0x3F800000u},
|
||||
{"9999999701976776123046874e-25", 0x3FEFFFFFF0000000u, 0x3F7FFFFFu},
|
||||
{"0.999999970197677612304687501", 0x3FEFFFFFF0000000u, 0x3F800000u},
|
||||
{"-9.999999701976776123046875000000000000000000001e-1", 0xBFEFFFFFF0000000u, 0xBF800000u},
|
||||
{"-9999999701976776123046875000000000000000000000000000000e-55", 0xBFEFFFFFF0000000u, 0xBF800000u},
|
||||
{"0.99999997019767761", 0x3FEFFFFFF0000000u, 0x3F7FFFFFu},
|
||||
{"-9.9999997019767762e-1", 0xBFEFFFFFF0000000u, 0xBF800000u},
|
||||
{"-9.999999701976776123e-1", 0xBFEFFFFFF0000000u, 0xBF7FFFFFu},
|
||||
{"9999999701976776124e-19", 0x3FEFFFFFF0000000u, 0x3F800000u},
|
||||
{"9999999701976776123e-19", 0x3FEFFFFFF0000000u, 0x3F7FFFFFu},
|
||||
{"0.99999997019767761231", 0x3FEFFFFFF0000000u, 0x3F800000u},
|
||||
{"-0.999999970197677612304", 0xBFEFFFFFF0000000u, 0xBF7FFFFFu},
|
||||
{"9.99999970197677612305e-1", 0x3FEFFFFFF0000000u, 0x3F800000u},
|
||||
{"-1.6777217e7", 0xC170000010000000u, 0xCB800000u},
|
||||
{"16777218e0", 0x4170000020000000u, 0x4B800001u},
|
||||
{"16777216", 0x4170000000000000u, 0x4B800000u},
|
||||
{"-1.677721701e7", 0xC17000001028F5C3u, 0xCB800001u},
|
||||
{"-16777217000000000000000000001e-21", 0xC170000010000000u, 0xCB800001u},
|
||||
{"16777217.000000000000000000000000000000", 0x4170000010000000u, 0x4B800000u},
|
||||
{"167772155e-1", 0x416FFFFFF0000000u, 0x4B800000u},
|
||||
{"16777215.6", 0x416FFFFFF3333333u, 0x4B800000u},
|
||||
{"1.67772154e7", 0x416FFFFFECCCCCCDu, 0x4B7FFFFFu},
|
||||
{"16777215501e-3", 0x416FFFFFF0083127u, 0x4B800000u},
|
||||
{"-16777215.5000000000000000000001", 0xC16FFFFFF0000000u, 0xCB800000u},
|
||||
{"-1.67772155000000000000000000000000000000e7", 0xC16FFFFFF0000000u, 0xCB800000u},
|
||||
{"0.1000000052154064178466796875", 0x3FB99999B0000000u, 0x3DCCCCCEu},
|
||||
{"1.000000052154064178466796876e-1", 0x3FB99999B0000000u, 0x3DCCCCCEu},
|
||||
{"-1000000052154064178466796874e-28", 0xBFB99999B0000000u, 0xBDCCCCCDu},
|
||||
{"0.100000005215406417846679687501", 0x3FB99999B0000000u, 0x3DCCCCCEu},
|
||||
{"1.000000052154064178466796875000000000000000000001e-1", 0x3FB99999B0000000u, 0x3DCCCCCEu},
|
||||
{"-1000000052154064178466796875000000000000000000000000000000e-58", 0xBFB99999B0000000u, 0xBDCCCCCEu},
|
||||
{"-0.10000000521540641", 0xBFB99999AFFFFFFFu, 0xBDCCCCCDu},
|
||||
{"-1.0000000521540642e-1", 0xBFB99999B0000000u, 0xBDCCCCCEu},
|
||||
{"1.000000052154064178e-1", 0x3FB99999B0000000u, 0x3DCCCCCDu},
|
||||
{"-1000000052154064179e-19", 0xBFB99999B0000000u, 0xBDCCCCCEu},
|
||||
{"10000000521540641784e-20", 0x3FB99999B0000000u, 0x3DCCCCCDu},
|
||||
{"0.10000000521540641785", 0x3FB99999B0000000u, 0x3DCCCCCEu},
|
||||
{"0.100000005215406417846", 0x3FB99999B0000000u, 0x3DCCCCCDu},
|
||||
{"1.00000005215406417847e-1", 0x3FB99999B0000000u, 0x3DCCCCCEu},
|
||||
{"5.429001220703125e3", 0x40B5350050000000u, 0x45A9A802u},
|
||||
{"-5429001220703126e-12", 0xC0B5350050000001u, 0xC5A9A803u},
|
||||
{"-5429.001220703124", 0xC0B535004FFFFFFFu, 0xC5A9A802u},
|
||||
{"5.42900122070312501e3", 0x40B5350050000000u, 0x45A9A803u},
|
||||
{"5429001220703125000000000000000000001e-33", 0x40B5350050000000u, 0x45A9A803u},
|
||||
{"-5429.001220703125000000000000000000000000000000", 0xC0B5350050000000u, 0xC5A9A802u},
|
||||
{"503719056e0", 0x41BE062490000000u, 0x4DF03124u},
|
||||
{"503719057", 0x41BE062491000000u, 0x4DF03125u},
|
||||
{"5.03719055e8", 0x41BE06248F000000u, 0x4DF03124u},
|
||||
{"50371905601e-2", 0x41BE062490028F5Cu, 0x4DF03125u},
|
||||
{"503719056.000000000000000000001", 0x41BE062490000000u, 0x4DF03125u},
|
||||
{"5.03719056000000000000000000000000000000e8", 0x41BE062490000000u, 0x4DF03124u},
|
||||
{"-92331620", 0xC196037990000000u, 0xCCB01BCCu},
|
||||
{"9.233163e7", 0x41960379B8000000u, 0x4CB01BCEu},
|
||||
{"9233161e1", 0x4196037968000000u, 0x4CB01BCBu},
|
||||
{"92331620.1", 0x4196037990666666u, 0x4CB01BCDu},
|
||||
{"9.233162000000000000000000001e7", 0x4196037990000000u, 0x4CB01BCDu},
|
||||
{"9233162000000000000000000000000000000e-29", 0x4196037990000000u, 0x4CB01BCCu},
|
||||
{"3.002458625e6", 0x4146E82D50000000u, 0x4A37416Au},
|
||||
{"3002458626e-3", 0x4146E82D5020C49Cu, 0x4A37416Bu},
|
||||
{"3002458.624", 0x4146E82D4FDF3B64u, 0x4A37416Au},
|
||||
{"-3.00245862501e6", 0xC146E82D500053E3u, 0xCA37416Bu},
|
||||
{"3002458625000000000000000000001e-24", 0x4146E82D50000000u, 0x4A37416Bu},
|
||||
{"3002458.625000000000000000000000000000000", 0x4146E82D50000000u, 0x4A37416Au},
|
||||
{"-1095485584696182596504479582065262592e1", 0xC7A07BA830000000u, 0xFD03DD42u},
|
||||
{"10954855846961825965044795820652625930", 0x47A07BA830000000u, 0x7D03DD42u},
|
||||
{"1.095485584696182596504479582065262591e37", 0x47A07BA830000000u, 0x7D03DD41u},
|
||||
{"109548558469618259650447958206526259201e-1", 0x47A07BA830000000u, 0x7D03DD42u},
|
||||
{"-10954855846961825965044795820652625920.00000000000000000001", 0xC7A07BA830000000u, 0xFD03DD42u},
|
||||
{"1.095485584696182596504479582065262592000000000000000000000000000000e37", 0x47A07BA830000000u, 0x7D03DD42u},
|
||||
{"10954855846961825e21", 0x47A07BA830000000u, 0x7D03DD41u},
|
||||
{"10954855846961826000000000000000000000", 0x47A07BA830000000u, 0x7D03DD42u},
|
||||
{"10954855846961825960000000000000000000", 0x47A07BA830000000u, 0x7D03DD41u},
|
||||
{"1.095485584696182597e37", 0x47A07BA830000000u, 0x7D03DD42u},
|
||||
{"1.0954855846961825965e37", 0x47A07BA830000000u, 0x7D03DD41u},
|
||||
{"-10954855846961825966e18", 0xC7A07BA830000000u, 0xFD03DD42u},
|
||||
{"10954855846961825965e18", 0x47A07BA830000000u, 0x7D03DD41u},
|
||||
{"10954855846961825965100000000000000000", 0x47A07BA830000000u, 0x7D03DD42u},
|
||||
{"10954855846961825965044795820600000000", 0x47A07BA830000000u, 0x7D03DD41u},
|
||||
{"-1.09548558469618259650447958207e37", 0xC7A07BA830000000u, 0xFD03DD42u},
|
||||
{"1.6449216019182103706535606608388384863861375606575165875256061553955078126e-21", 0x3B9F125A50000000u, 0x1CF892D3u},
|
||||
{"-16449216019182103706535606608388384863861375606575165875256061553955078124e-94", 0xBB9F125A50000000u, 0x9CF892D2u},
|
||||
{"-0.0000000000000000000016449216019182103", 0xBB9F125A50000000u, 0x9CF892D2u},
|
||||
{"-1.6449216019182104e-21", 0xBB9F125A50000000u, 0x9CF892D3u},
|
||||
{"-1.64492160191821037e-21", 0xBB9F125A50000000u, 0x9CF892D2u},
|
||||
{"-1644921601918210371e-39", 0xBB9F125A50000000u, 0x9CF892D3u},
|
||||
{"-16449216019182103706e-40", 0xBB9F125A50000000u, 0x9CF892D2u},
|
||||
{"0.0000000000000000000016449216019182103707", 0x3B9F125A50000000u, 0x1CF892D3u},
|
||||
{"0.00000000000000000000164492160191821037065", 0x3B9F125A50000000u, 0x1CF892D2u},
|
||||
{"1.64492160191821037066e-21", 0x3B9F125A50000000u, 0x1CF892D3u},
|
||||
{"1.64492160191821037065356066083e-21", 0x3B9F125A50000000u, 0x1CF892D2u},
|
||||
{"164492160191821037065356066084e-50", 0x3B9F125A50000000u, 0x1CF892D3u},
|
||||
{"6.565061509609222412109375e-1", 0x3FE5021930000000u, 0x3F2810CAu},
|
||||
{"6565061509609222412109376e-25", 0x3FE5021930000000u, 0x3F2810CAu},
|
||||
{"0.6565061509609222412109374", 0x3FE5021930000000u, 0x3F2810C9u},
|
||||
{"6.56506150960922241210937501e-1", 0x3FE5021930000000u, 0x3F2810CAu},
|
||||
{"6565061509609222412109375000000000000000000001e-46", 0x3FE5021930000000u, 0x3F2810CAu},
|
||||
{"-0.6565061509609222412109375000000000000000000000000000000", 0xBFE5021930000000u, 0xBF2810CAu},
|
||||
{"6.5650615096092224e-1", 0x3FE5021930000000u, 0x3F2810C9u},
|
||||
{"-65650615096092225e-17", 0xBFE5021930000000u, 0xBF2810CAu},
|
||||
{"6565061509609222412e-19", 0x3FE5021930000000u, 0x3F2810C9u},
|
||||
{"0.6565061509609222413", 0x3FE5021930000000u, 0x3F2810CAu},
|
||||
{"0.65650615096092224121", 0x3FE5021930000000u, 0x3F2810C9u},
|
||||
{"6.5650615096092224122e-1", 0x3FE5021930000000u, 0x3F2810CAu},
|
||||
{"-6.5650615096092224121e-1", 0xBFE5021930000000u, 0xBF2810C9u},
|
||||
{"656506150960922241211e-21", 0x3FE5021930000000u, 0x3F2810CAu},
|
||||
{"18014627239033005156980393746124491372029297053813934326171875e-77", 0x3CA9F63970000000u, 0x254FB1CCu},
|
||||
{"0.00000000000000018014627239033005156980393746124491372029297053813934326171876", 0x3CA9F63970000000u, 0x254FB1CCu},
|
||||
{"-1.8014627239033005156980393746124491372029297053813934326171874e-16", 0xBCA9F63970000000u, 0xA54FB1CBu},
|
||||
{"1801462723903300515698039374612449137202929705381393432617187501e-79", 0x3CA9F63970000000u, 0x254FB1CCu},
|
||||
{"18014627239033005e-32", 0x3CA9F63970000000u, 0x254FB1CBu},
|
||||
{"0.00000000000000018014627239033006", 0x3CA9F63970000000u, 0x254FB1CCu},
|
||||
{"0.0000000000000001801462723903300515", 0x3CA9F63970000000u, 0x254FB1CBu},
|
||||
{"-1.801462723903300516e-16", 0xBCA9F63970000000u, 0xA54FB1CCu},
|
||||
{"-1.8014627239033005156e-16", 0xBCA9F63970000000u, 0xA54FB1CBu},
|
||||
{"18014627239033005157e-35", 0x3CA9F63970000000u, 0x254FB1CCu},
|
||||
{"180146272390330051569e-36", 0x3CA9F63970000000u, 0x254FB1CBu},
|
||||
{"-0.00000000000000018014627239033005157", 0xBCA9F63970000000u, 0xA54FB1CCu},
|
||||
{"0.000000000000000180146272390330051569803937461", 0x3CA9F63970000000u, 0x254FB1CBu},
|
||||
{"1.80146272390330051569803937462e-16", 0x3CA9F63970000000u, 0x254FB1CCu},
|
||||
{"0.05534819327294826507568359375", 0x3FAC569930000000u, 0x3D62B4CAu},
|
||||
{"-5.534819327294826507568359376e-2", 0xBFAC569930000000u, 0xBD62B4CAu},
|
||||
{"5534819327294826507568359374e-29", 0x3FAC569930000000u, 0x3D62B4C9u},
|
||||
{"-0.0553481932729482650756835937501", 0xBFAC569930000000u, 0xBD62B4CAu},
|
||||
{"5.534819327294826507568359375000000000000000000001e-2", 0x3FAC569930000000u, 0x3D62B4CAu},
|
||||
{"5534819327294826507568359375000000000000000000000000000000e-59", 0x3FAC569930000000u, 0x3D62B4CAu},
|
||||
{"0.055348193272948265", 0x3FAC569930000000u, 0x3D62B4C9u},
|
||||
{"5.5348193272948266e-2", 0x3FAC569930000000u, 0x3D62B4CAu},
|
||||
{"5.534819327294826507e-2", 0x3FAC569930000000u, 0x3D62B4C9u},
|
||||
{"5534819327294826508e-20", 0x3FAC569930000000u, 0x3D62B4CAu},
|
||||
{"55348193272948265075e-21", 0x3FAC569930000000u, 0x3D62B4C9u},
|
||||
{"-0.055348193272948265076", 0xBFAC569930000000u, 0xBD62B4CAu},
|
||||
{"0.0553481932729482650756", 0x3FAC569930000000u, 0x3D62B4C9u},
|
||||
{"-5.53481932729482650757e-2", 0xBFAC569930000000u, 0xBD62B4CAu},
|
||||
{"5.179692133247783258005389047985340416e36", 0x478F2C9450000000u, 0x7C7964A2u},
|
||||
{"5179692133247783258005389047985340417e0", 0x478F2C9450000000u, 0x7C7964A3u},
|
||||
{"5179692133247783258005389047985340415", 0x478F2C9450000000u, 0x7C7964A2u},
|
||||
{"-5.17969213324778325800538904798534041601e36", 0xC78F2C9450000000u, 0xFC7964A3u},
|
||||
{"-5179692133247783258005389047985340416000000000000000000001e-21", 0xC78F2C9450000000u, 0xFC7964A3u},
|
||||
{"-5179692133247783258005389047985340416.000000000000000000000000000000", 0xC78F2C9450000000u, 0xFC7964A2u},
|
||||
{"5.1796921332477832e36", 0x478F2C9450000000u, 0x7C7964A2u},
|
||||
{"51796921332477833e20", 0x478F2C9450000000u, 0x7C7964A3u},
|
||||
{"-5179692133247783258e18", 0xC78F2C9450000000u, 0xFC7964A2u},
|
||||
{"5179692133247783259000000000000000000", 0x478F2C9450000000u, 0x7C7964A3u},
|
||||
{"5179692133247783258000000000000000000", 0x478F2C9450000000u, 0x7C7964A2u},
|
||||
{"5.1796921332477832581e36", 0x478F2C9450000000u, 0x7C7964A3u},
|
||||
{"-5.179692133247783258e36", 0xC78F2C9450000000u, 0xFC7964A2u},
|
||||
{"-517969213324778325801e16", 0xC78F2C9450000000u, 0xFC7964A3u},
|
||||
{"517969213324778325800538904798e7", 0x478F2C9450000000u, 0x7C7964A2u},
|
||||
{"5179692133247783258005389047990000000", 0x478F2C9450000000u, 0x7C7964A3u},
|
||||
{
|
||||
"0.22250738585072011360574097967091319759348195463516456480234261097248222220210769455165295239081350"
|
||||
"8791414915891303962110687008643869459464552765720740782062174337998814106326732925355228688137214901"
|
||||
"2981122451451889849057222307285255133155755015914397476397983411801999323962548289017107081850690630"
|
||||
"6666559949382757725720157630626906633326475653000092458883164330377797918696120494973903778297049050"
|
||||
"5108060994073026293712895895000358379996720725430436028407889577179615094551674824347103070260914462"
|
||||
"1572289880258182545180325707018860872113128079512233426288368622321503775666622503982534335974568884"
|
||||
"4239002654981983854879482922068947216898310996983658468140228542433306603398508864458040010349339704"
|
||||
"2756718644338377048603786162277173854562306587467901408672332763671875e-307", 0x0010000000000000u, 0x00000000u
|
||||
},
|
||||
{
|
||||
"2.22507385850720113605740979670913197593481954635164564802342610972482222202107694551652952390813508"
|
||||
"7914149158913039621106870086438694594645527657207407820621743379988141063267329253552286881372149012"
|
||||
"9811224514518898490572223072852551331557550159143974763979834118019993239625482890171070818506906306"
|
||||
"6665599493827577257201576306269066333264756530000924588831643303777979186961204949739037782970490505"
|
||||
"1080609940730262937128958950003583799967207254304360284078895771796150945516748243471030702609144621"
|
||||
"5722898802581825451803257070188608721131280795122334262883686223215037756666225039825343359745688844"
|
||||
"2390026549819838548794829220689472168983109969836584681402285424333066033985088644580400103493397042"
|
||||
"756718644338377048603786162277173854562306587467901408672332763671875000000000000000000001e-308", 0x0010000000000000u, 0x00000000u
|
||||
},
|
||||
{
|
||||
"0.11754942807573642917278829910357665133228589927589904276829631184250030649651730385585324256680905"
|
||||
"8189392089843750000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
"00000000000000000000000000000000000000000000000000000000000000000e-37", 0x380FFFFFE0000000u, 0x00800000u
|
||||
},
|
||||
{
|
||||
"1175494280757364291727882991035766513322858992758990427682963118425003064965173038558532425668090581"
|
||||
"8939208984375000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
"00000000000001e-751", 0x380FFFFFE0000000u, 0x00800000u
|
||||
},
|
||||
{"0", 0x0000000000000000u, 0x00000000u},
|
||||
{"-0", 0x8000000000000000u, 0x80000000u},
|
||||
{"0.0", 0x0000000000000000u, 0x00000000u},
|
||||
{"-0.0", 0x8000000000000000u, 0x80000000u},
|
||||
{"0e999999999999999999999", 0x0000000000000000u, 0x00000000u},
|
||||
{"-0.000e-99999", 0x8000000000000000u, 0x80000000u},
|
||||
{"1e-400", 0x0000000000000000u, 0x00000000u},
|
||||
{"-1e-400", 0x8000000000000000u, 0x80000000u},
|
||||
{"1e400", 0x7FF0000000000000u, 0x7F800000u},
|
||||
{"-1e400", 0xFFF0000000000000u, 0xFF800000u},
|
||||
{"1e-50", 0x358DEE7A4AD4B81Fu, 0x00000000u},
|
||||
{"-1e-50", 0xB58DEE7A4AD4B81Fu, 0x80000000u},
|
||||
{"1e39", 0x48078287F49C4A1Du, 0x7F800000u},
|
||||
{"-1e39", 0xC8078287F49C4A1Du, 0xFF800000u},
|
||||
{"1e99999999999999999999999999", 0x7FF0000000000000u, 0x7F800000u},
|
||||
{"1e-99999999999999999999999999", 0x0000000000000000u, 0x00000000u},
|
||||
{"1e0000000000000000000000000000000000000000308", 0x7FE1CCF385EBC8A0u, 0x7F800000u},
|
||||
{"123456789012345678901234567890e-30", 0x3FBF9ADD3746F65Fu, 0x3DFCD6EAu},
|
||||
{"18446744073709551615", 0x43F0000000000000u, 0x5F800000u},
|
||||
{"18446744073709551616", 0x43F0000000000000u, 0x5F800000u},
|
||||
{"-9223372036854775808", 0xC3E0000000000000u, 0xDF000000u},
|
||||
{"-9223372036854775809", 0xC3E0000000000000u, 0xDF000000u},
|
||||
}
|
||||
};
|
||||
return table;
|
||||
}
|
||||
|
||||
} // namespace float_hard_cases
|
||||
@@ -370,14 +370,7 @@ TEST_CASE("copy of a deeply nested value survives a failing allocation (#5640)")
|
||||
#if !(defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL > 0)
|
||||
SECTION("std::map-backed object_t")
|
||||
{
|
||||
using bad_alloc_json = nlohmann::basic_json<std::map,
|
||||
std::vector,
|
||||
std::string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
nth_alloc_fails_allocator>;
|
||||
using bad_alloc_json = nlohmann::json::with_allocator_t<nth_alloc_fails_allocator>;
|
||||
|
||||
check_deep_copy_survives_failing_allocation<bad_alloc_json>(false);
|
||||
check_deep_copy_survives_failing_allocation<bad_alloc_json>(true);
|
||||
@@ -385,14 +378,7 @@ TEST_CASE("copy of a deeply nested value survives a failing allocation (#5640)")
|
||||
|
||||
SECTION("ordered_map-backed object_t")
|
||||
{
|
||||
using bad_alloc_ordered_json = nlohmann::basic_json<nlohmann::ordered_map,
|
||||
std::vector,
|
||||
std::string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
nth_alloc_fails_allocator>;
|
||||
using bad_alloc_ordered_json = nlohmann::ordered_json::with_allocator_t<nth_alloc_fails_allocator>;
|
||||
|
||||
check_deep_copy_survives_failing_allocation<bad_alloc_ordered_json>(false);
|
||||
check_deep_copy_survives_failing_allocation<bad_alloc_ordered_json>(true);
|
||||
@@ -450,14 +436,7 @@ struct scratch_counting_allocator : std::allocator<T>
|
||||
|
||||
TEST_CASE("deep copy uses the provided allocator")
|
||||
{
|
||||
using counting_json = nlohmann::basic_json<std::map,
|
||||
std::vector,
|
||||
std::string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
scratch_counting_allocator>;
|
||||
using counting_json = nlohmann::json::with_allocator_t<scratch_counting_allocator>;
|
||||
|
||||
// deeper than the 128 levels the copy constructor descends into, so the
|
||||
// innermost objects are copied by the iterative deep copy
|
||||
@@ -516,14 +495,7 @@ TEST_CASE("converting a deeply nested value from another specialization fails cl
|
||||
// the allocator in noexcept constructors, so a failing construction crashes
|
||||
// the program there instead of throwing std::bad_alloc. Nothing to check.
|
||||
#if !(defined(_MSC_VER) && _MSC_VER < 1910 && defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL > 0)
|
||||
using countdown_json = nlohmann::basic_json<std::map,
|
||||
std::vector,
|
||||
std::string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
countdown_allocator>;
|
||||
using countdown_json = nlohmann::json::with_allocator_t<countdown_allocator>;
|
||||
|
||||
// deeper than the 128 levels the converting constructor descends into, so
|
||||
// that failures land on both sides of the bound - or, built with
|
||||
@@ -631,14 +603,7 @@ TEST_CASE("destructor performs no allocation, only deallocation")
|
||||
// Since that stack could itself throw bad_alloc from inside the
|
||||
// noexcept destructor (#5135), destroy() no longer allocates anything:
|
||||
// it only ever frees what is already there.
|
||||
using counting_json = nlohmann::basic_json<std::map,
|
||||
std::vector,
|
||||
std::string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
counting_allocator>;
|
||||
using counting_json = nlohmann::json::with_allocator_t<counting_allocator>;
|
||||
|
||||
SECTION("array")
|
||||
{
|
||||
@@ -683,14 +648,7 @@ TEST_CASE("destructor performs no allocation, only deallocation")
|
||||
TEST_CASE("a failed allocation leaves the value unchanged")
|
||||
{
|
||||
// create JSON type using the throwing allocator
|
||||
using my_json = nlohmann::basic_json<std::map,
|
||||
std::vector,
|
||||
std::string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
my_allocator>;
|
||||
using my_json = nlohmann::json::with_allocator_t<my_allocator>;
|
||||
|
||||
// Each of these creates a string, array, object, or binary value. The
|
||||
// value must be created before the type is changed: otherwise, a failed
|
||||
|
||||
@@ -38,7 +38,7 @@ TEST_CASE("Binary Formats")
|
||||
const auto ubjson_2_size = json::to_ubjson(j, true).size();
|
||||
const auto ubjson_3_size = json::to_ubjson(j, true, true).size();
|
||||
|
||||
CHECK(json_size == 2090303);
|
||||
CHECK(json_size == 2090234);
|
||||
CHECK(bjdata_1_size == 1112030);
|
||||
CHECK(bjdata_2_size == 1224148);
|
||||
CHECK(bjdata_3_size == 1224148);
|
||||
@@ -51,16 +51,16 @@ TEST_CASE("Binary Formats")
|
||||
CHECK(ubjson_3_size == 1169069);
|
||||
|
||||
CHECK((100.0 * double(json_size) / double(json_size)) == Approx(100.0));
|
||||
CHECK((100.0 * double(bjdata_1_size) / double(json_size)) == Approx(53.199));
|
||||
CHECK((100.0 * double(bjdata_2_size) / double(json_size)) == Approx(58.563));
|
||||
CHECK((100.0 * double(bjdata_3_size) / double(json_size)) == Approx(58.563));
|
||||
CHECK((100.0 * double(bon8_size) / double(json_size)) == Approx(50.509));
|
||||
CHECK((100.0 * double(bson_size) / double(json_size)) == Approx(85.849));
|
||||
CHECK((100.0 * double(cbor_size) / double(json_size)) == Approx(50.497));
|
||||
CHECK((100.0 * double(msgpack_size) / double(json_size)) == Approx(50.526));
|
||||
CHECK((100.0 * double(ubjson_1_size) / double(json_size)) == Approx(53.199));
|
||||
CHECK((100.0 * double(ubjson_2_size) / double(json_size)) == Approx(58.563));
|
||||
CHECK((100.0 * double(ubjson_3_size) / double(json_size)) == Approx(55.928));
|
||||
CHECK((100.0 * double(bjdata_1_size) / double(json_size)) == Approx(53.201));
|
||||
CHECK((100.0 * double(bjdata_2_size) / double(json_size)) == Approx(58.565));
|
||||
CHECK((100.0 * double(bjdata_3_size) / double(json_size)) == Approx(58.565));
|
||||
CHECK((100.0 * double(bon8_size) / double(json_size)) == Approx(50.511));
|
||||
CHECK((100.0 * double(bson_size) / double(json_size)) == Approx(85.853));
|
||||
CHECK((100.0 * double(cbor_size) / double(json_size)) == Approx(50.499));
|
||||
CHECK((100.0 * double(msgpack_size) / double(json_size)) == Approx(50.528));
|
||||
CHECK((100.0 * double(ubjson_1_size) / double(json_size)) == Approx(53.201));
|
||||
CHECK((100.0 * double(ubjson_2_size) / double(json_size)) == Approx(58.565));
|
||||
CHECK((100.0 * double(ubjson_3_size) / double(json_size)) == Approx(55.930));
|
||||
}
|
||||
|
||||
SECTION("twitter.json")
|
||||
@@ -237,7 +237,7 @@ namespace
|
||||
// the binary formats as function pointers for "Binary formats with narrow number types";
|
||||
// named functions rather than lambdas, because clang 3.5 cannot convert a lambda
|
||||
// to a function pointer in the braced initializer of the format table
|
||||
using narrow_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int32_t, std::uint32_t, float>;
|
||||
using narrow_json = nlohmann::json::with_integers_t<std::int32_t, std::uint32_t>::with_float_t<float>;
|
||||
using bytes = std::vector<std::uint8_t>;
|
||||
|
||||
bytes encode_cbor(const json& j)
|
||||
|
||||
@@ -12,7 +12,11 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
@@ -55,6 +59,53 @@ std::string dump_and_parse(const std::string& raw, eh error_handler)
|
||||
return json::parse(json(raw).dump(-1, ' ', false, error_handler)).get<std::string>();
|
||||
}
|
||||
|
||||
// an object key type that is not string_t, but converts implicitly to it;
|
||||
// data() is only used when JSON_DIAGNOSTICS is enabled
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-member-function")
|
||||
class converting_key
|
||||
{
|
||||
public:
|
||||
converting_key(const char* s) : m_value(s) {} // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)
|
||||
converting_key(std::string s) : m_value(std::move(s)) {} // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)
|
||||
|
||||
// the conversion yields a temporary string_t
|
||||
operator std::string() const // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
// read by the exception messages when JSON_DIAGNOSTICS is enabled
|
||||
const char* data() const noexcept
|
||||
{
|
||||
return m_value.data();
|
||||
}
|
||||
|
||||
friend bool operator<(const converting_key& lhs, const converting_key& rhs)
|
||||
{
|
||||
return lhs.m_value < rhs.m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_value;
|
||||
};
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_POP
|
||||
|
||||
// ObjectType using converting_key; the Key template argument is ignored
|
||||
template<typename Key, typename Value, typename Compare, typename Allocator>
|
||||
class converting_key_object : public std::map<converting_key, Value, std::less<converting_key>, // NOLINT(modernize-use-transparent-functors)
|
||||
typename std::allocator_traits<Allocator>::template rebind_alloc<std::pair<const converting_key, Value>>>
|
||||
{
|
||||
using base_type = std::map<converting_key, Value, std::less<converting_key>, // NOLINT(modernize-use-transparent-functors)
|
||||
typename std::allocator_traits<Allocator>::template rebind_alloc<std::pair<const converting_key, Value>>>;
|
||||
|
||||
public:
|
||||
using base_type::base_type;
|
||||
using base_type::operator=;
|
||||
};
|
||||
|
||||
using converting_key_json = nlohmann::basic_json<converting_key_object>;
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("UTF-8 error_handler for the binary readers and writers")
|
||||
@@ -370,3 +421,109 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers")
|
||||
CHECK(json::from_bson(bson_bytes)["k"].get<std::string>() == ill_formed_cases()[0].bytes);
|
||||
}
|
||||
}
|
||||
|
||||
// The UBJSON and BJData writers bind the (possibly sanitized) key to a const
|
||||
// string_t&. If key_type is not string_t but converts to it, the converted
|
||||
// temporary must outlive that reference; this was a use-after-scope found by
|
||||
// AddressSanitizer. Keys exceed the small string optimization on purpose.
|
||||
TEST_CASE("UBJSON and BJData writers with an object_t whose key_type is not string_t")
|
||||
{
|
||||
const std::string long_prefix(70, 'k');
|
||||
|
||||
SECTION("well-formed keys, every error_handler")
|
||||
{
|
||||
const std::string key1 = long_prefix + "-first";
|
||||
const std::string key2 = long_prefix + "-second";
|
||||
|
||||
converting_key_json::object_t o;
|
||||
o.emplace(converting_key(key1), 1);
|
||||
o.emplace(converting_key(key2), "value");
|
||||
const converting_key_json v(std::move(o));
|
||||
|
||||
json expected;
|
||||
expected[key1] = 1;
|
||||
expected[key2] = "value";
|
||||
|
||||
const std::array<std::pair<bool, bool>, 3> combos = {{{false, false}, {true, false}, {true, true}}};
|
||||
for (const auto h : all_handlers())
|
||||
{
|
||||
CAPTURE(static_cast<int>(h))
|
||||
for (const auto& combo : combos)
|
||||
{
|
||||
const bool use_count = combo.first;
|
||||
const bool use_type = combo.second;
|
||||
CAPTURE(use_count)
|
||||
CAPTURE(use_type)
|
||||
|
||||
CHECK(json::from_ubjson(converting_key_json::to_ubjson(v, use_count, use_type, h)) == expected);
|
||||
CHECK(json::from_bjdata(converting_key_json::to_bjdata(v, use_count, use_type, json::bjdata_version_t::draft2, h)) == expected);
|
||||
CHECK(json::from_bjdata(converting_key_json::to_bjdata(v, use_count, use_type, json::bjdata_version_t::draft3, h)) == expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed keys")
|
||||
{
|
||||
for (const auto& c : ill_formed_cases())
|
||||
{
|
||||
CAPTURE(c.name)
|
||||
const std::string key = long_prefix + c.bytes;
|
||||
|
||||
converting_key_json::object_t o;
|
||||
o.emplace(converting_key(key), 1);
|
||||
const converting_key_json v(std::move(o));
|
||||
|
||||
CHECK_THROWS_AS(converting_key_json::to_ubjson(v, false, false, eh::strict), converting_key_json::type_error&);
|
||||
CHECK_THROWS_AS(converting_key_json::to_bjdata(v, false, false, json::bjdata_version_t::draft2, eh::strict), converting_key_json::type_error&);
|
||||
|
||||
for (const auto h :
|
||||
{
|
||||
eh::replace, eh::ignore
|
||||
})
|
||||
{
|
||||
CAPTURE(static_cast<int>(h))
|
||||
const std::string expected = dump_and_parse(key, h);
|
||||
|
||||
CHECK(json::from_ubjson(converting_key_json::to_ubjson(v, false, false, h)).begin().key() == expected);
|
||||
CHECK(json::from_bjdata(converting_key_json::to_bjdata(v, false, false, json::bjdata_version_t::draft2, h)).begin().key() == expected);
|
||||
}
|
||||
|
||||
CHECK(json::from_ubjson(converting_key_json::to_ubjson(v, false, false, eh::keep)).begin().key() == key);
|
||||
CHECK(json::from_bjdata(converting_key_json::to_bjdata(v, false, false, json::bjdata_version_t::draft2, eh::keep)).begin().key() == key);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("nested deeper than the recursion limit")
|
||||
{
|
||||
// wrap the previous value, innermost first
|
||||
converting_key_json v = 42;
|
||||
json expected = 42;
|
||||
for (int i = 199; i >= 0; --i)
|
||||
{
|
||||
const std::string key = "level-" + std::to_string(i) + "-" + std::string(64, 'x');
|
||||
|
||||
converting_key_json::object_t o;
|
||||
o.emplace(converting_key(key), std::move(v));
|
||||
v = converting_key_json(std::move(o));
|
||||
|
||||
json e;
|
||||
e[key] = std::move(expected);
|
||||
expected = std::move(e);
|
||||
}
|
||||
|
||||
for (const auto h : all_handlers())
|
||||
{
|
||||
CAPTURE(static_cast<int>(h))
|
||||
for (const bool use_count :
|
||||
{
|
||||
false, true
|
||||
})
|
||||
{
|
||||
CAPTURE(use_count)
|
||||
|
||||
CHECK(json::from_ubjson(converting_key_json::to_ubjson(v, use_count, false, h)) == expected);
|
||||
CHECK(json::from_bjdata(converting_key_json::to_bjdata(v, use_count, false, json::bjdata_version_t::draft2, h)) == expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,9 +46,7 @@ class huge_binary_t : public std::vector<std::uint8_t>
|
||||
}
|
||||
};
|
||||
|
||||
using huge_binary_json = nlohmann::basic_json <
|
||||
std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t,
|
||||
double, std::allocator, nlohmann::adl_serializer, huge_binary_t, void >;
|
||||
using huge_binary_json = nlohmann::json::with_binary_t<huge_binary_t>;
|
||||
|
||||
// a string type that can be made to report a size beyond INT32_MAX without
|
||||
// allocating that much memory, so BSON length overflow can be tested for
|
||||
@@ -96,9 +94,7 @@ class huge_string_t : public std::string
|
||||
bool pretend_huge = false;
|
||||
};
|
||||
|
||||
using huge_string_json = nlohmann::basic_json <
|
||||
std::map, std::vector, huge_string_t, bool, std::int64_t, std::uint64_t,
|
||||
double, std::allocator, nlohmann::adl_serializer, std::vector<std::uint8_t>, void >;
|
||||
using huge_string_json = nlohmann::json::with_string_t<huge_string_t>;
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("BSON")
|
||||
|
||||
+594
-109
@@ -13,15 +13,20 @@
|
||||
using nlohmann::json;
|
||||
|
||||
#include <array> // array
|
||||
#include <cfloat> // FLT_EVAL_METHOD
|
||||
#include <cstdint> // uint32_t, uint64_t
|
||||
#include <cstdio> // snprintf
|
||||
#include <cstdlib> // strtod
|
||||
#include <cstring> // memcpy
|
||||
#include <limits> // numeric_limits
|
||||
#include <map> // map
|
||||
#include <random> // mt19937
|
||||
#include <sstream> // stringstream
|
||||
#include <string> // string
|
||||
#include <utility> // pair
|
||||
#include <vector> // vector
|
||||
|
||||
#include "float_hard_cases.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
// shortcut to scan a string literal
|
||||
@@ -257,7 +262,7 @@ TEST_CASE("lexer number fast path")
|
||||
"123456789012345678901234567890", // huge -> float
|
||||
"0.30000000000000004", "2.2250738585072014e-308", "1e308",
|
||||
// high-precision / wide-exponent values that exercise the
|
||||
// std::from_chars (Eisel-Lemire) path beyond the Clinger subset
|
||||
// Eisel-Lemire path beyond the Clinger subset
|
||||
"1.7976931348623157e308", "1.2345678901234567e-250",
|
||||
"9007199254740993", "5e-324", "1e-320"
|
||||
};
|
||||
@@ -279,20 +284,18 @@ TEST_CASE("lexer number fast path")
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("significant-digit gate for the Clinger fast path")
|
||||
SECTION("significant digits around Clinger's fast path")
|
||||
{
|
||||
// Clinger's fast path needs a significand below 2^53, so it cannot
|
||||
// succeed once the mantissa has 17 or more significant digits (the
|
||||
// significand would be at least 10^16). The lexer skips the attempt
|
||||
// there. That is only allowed to save work: every value must still come
|
||||
// out bit-exactly, and both scanners must agree. In particular the gate
|
||||
// must not fire for tokens whose leading zeros merely look like extra
|
||||
// digits - "0.1234567890123456" has 16 significant digits, not 17.
|
||||
// Clinger's fast path needs a significand of at most 2^53, which
|
||||
// tokens with 17 or more significant digits exceed. The conversion
|
||||
// splits the token at the positions the scanners recorded, so leading
|
||||
// zeros must not count as digits - "0.1234567890123456" has 16
|
||||
// significant digits, not 17 - and both scanners must agree.
|
||||
const std::vector<std::string> numbers =
|
||||
{
|
||||
"1234567890123456", // 16 significant digits
|
||||
"12345678901234567", // 17 -> attempt skipped
|
||||
"123456789012345678", // 18 -> attempt skipped
|
||||
"12345678901234567", // 17
|
||||
"123456789012345678", // 18
|
||||
"0.1234567890123456", // 16: the leading "0" is not significant
|
||||
"0.12345678901234567", // 17
|
||||
"0.00000000000000001", // 1, in a long token
|
||||
@@ -663,46 +666,323 @@ TEST_CASE("lexer string fast path")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("parse_float_fast declines what it cannot convert exactly")
|
||||
TEST_CASE("lexer escape fast path")
|
||||
{
|
||||
// The lexer only hands well-formed numbers to parse_float_fast, so the
|
||||
// malformed ones below can only be passed to it directly. Declining is
|
||||
// always safe: the caller then falls back to a slower, exact conversion.
|
||||
const auto fast = [](const std::string & s, double & out)
|
||||
// json::accept() never throws, so this section stays covered without
|
||||
// exceptions; it pins which of the cases below are valid/invalid and
|
||||
// checks the contiguous and streaming paths agree on that classification.
|
||||
SECTION("accept() parity")
|
||||
{
|
||||
return nlohmann::detail::parse_float_fast(s.data(), s.data() + s.size(), out);
|
||||
const std::vector<std::pair<std::string, bool>> cases =
|
||||
{
|
||||
{"\\u0041", true}, {"\\u00e4", true}, {"\\u00E4", true},
|
||||
{"\\uD83D\\uDE00", true},
|
||||
{"\\u12", false}, {"\\u12G4", false}, {"\\uXYZW", false},
|
||||
{"\\uD800", false}, {"\\uD800A", false}, {"\\uD800\\u0041", false},
|
||||
{"\\uDC00", false}, {"\\u", false}
|
||||
};
|
||||
|
||||
for (const auto& c : cases)
|
||||
{
|
||||
for (const std::size_t offset :
|
||||
{
|
||||
std::size_t{0}, std::size_t{9}
|
||||
})
|
||||
{
|
||||
const std::string doc = "[\"" + std::string(offset, 'a') + c.first + "\"]";
|
||||
CAPTURE(doc)
|
||||
CHECK(json::accept(doc) == c.second);
|
||||
std::stringstream ss(doc);
|
||||
CHECK(json::accept(ss) == c.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(JSON_NOEXCEPTION)
|
||||
// the full outcome of parsing @a doc: the parsed value, or the exact
|
||||
// error message, so a mismatch in either is caught
|
||||
const auto outcome = [](const std::string & doc, bool streaming) -> std::string
|
||||
{
|
||||
try
|
||||
{
|
||||
if (streaming)
|
||||
{
|
||||
std::stringstream ss(doc);
|
||||
const json j = json::parse(ss);
|
||||
return j.dump();
|
||||
}
|
||||
const json j = json::parse(doc);
|
||||
return j.dump();
|
||||
}
|
||||
catch (const json::exception& e)
|
||||
{
|
||||
return {e.what()};
|
||||
}
|
||||
};
|
||||
double out = 0;
|
||||
|
||||
#if defined(FLT_EVAL_METHOD) && FLT_EVAL_METHOD != 0
|
||||
// without true double precision, the fast path declines everything
|
||||
CHECK_FALSE(fast("1.5", out));
|
||||
#else
|
||||
CHECK(fast("1.5", out));
|
||||
CHECK(out == 1.5);
|
||||
CHECK(fast("+2.5e1", out));
|
||||
CHECK(out == 25.0);
|
||||
CHECK(fast("-25E-1", out));
|
||||
CHECK(out == -2.5);
|
||||
CHECK(fast("1e", out));
|
||||
CHECK(out == 1.0);
|
||||
SECTION("contiguous vs streaming parity")
|
||||
{
|
||||
const std::vector<std::string> escapes =
|
||||
{
|
||||
"\\u0041", // "A"
|
||||
"\\u00e4", // "ä" (lowercase hex)
|
||||
"\\u00E4", // "ä" (uppercase hex)
|
||||
"\\uD83D\\uDE00", // valid surrogate pair (an emoji)
|
||||
"\\u12", // truncated: only 2 hex digits before the closing quote
|
||||
"\\u12G4", // invalid hex digit at the 3rd position
|
||||
"\\uXYZW", // all 4 bytes invalid
|
||||
"\\uD800", // lone high surrogate, string ends right after
|
||||
"\\uD800A", // high surrogate not followed by another \u escape
|
||||
"\\uD800\\u0041", // high surrogate followed by \u, but not a low surrogate
|
||||
"\\uDC00", // lone low surrogate
|
||||
"\\u", // '\u' with nothing after (closing quote right away)
|
||||
};
|
||||
|
||||
// once at the start of the string and once past the first 8-byte SWAR
|
||||
// word of the outer string_bulk_run, so the escape is reached both
|
||||
// right after the opening quote and mid-run
|
||||
for (const auto& escape : escapes)
|
||||
{
|
||||
for (const std::size_t offset :
|
||||
{
|
||||
std::size_t{0}, std::size_t{9}
|
||||
})
|
||||
{
|
||||
const std::string doc = "[\"" + std::string(offset, 'a') + escape + "\"]";
|
||||
CAPTURE(doc)
|
||||
CHECK(outcome(doc, false) == outcome(doc, true));
|
||||
}
|
||||
|
||||
// the escape is the last thing before end of input: no closing
|
||||
// quote at all
|
||||
const std::string truncated_doc = "[\"" + escape;
|
||||
CAPTURE(truncated_doc)
|
||||
CHECK(outcome(truncated_doc, false) == outcome(truncated_doc, true));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("truncated \\u escape at every distance from the end of input")
|
||||
{
|
||||
// ia.bulk_remaining() must correctly report fewer than 4 bytes for
|
||||
// every possible count of trailing hex-looking bytes (0, 1, 2, or 3)
|
||||
// before end of input, so the fast path declines and the byte path
|
||||
// alone reports the "must be followed by 4 hex digits" error, at the
|
||||
// same position, in every case
|
||||
for (const std::string& tail :
|
||||
{
|
||||
std::string{}, std::string("1"), std::string("12"), std::string("123")
|
||||
})
|
||||
{
|
||||
const std::string doc = "[\"\\u" + tail;
|
||||
CAPTURE(doc)
|
||||
CHECK(outcome(doc, false) == outcome(doc, true));
|
||||
CHECK(outcome(doc, false).find("must be followed by 4 hex digits") != std::string::npos);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("invalid hex digit at every position of the 4")
|
||||
{
|
||||
// the fast path must decline for *any* invalid byte among the 4, not
|
||||
// just the first, and the byte path must then stop at exactly that
|
||||
// position - same as it always has
|
||||
for (std::size_t bad_pos = 0; bad_pos < 4; ++bad_pos)
|
||||
{
|
||||
std::string digits = "1234";
|
||||
digits[bad_pos] = 'g'; // not a hex digit
|
||||
const std::string doc = "[\"\\u" + digits + "\"]";
|
||||
CAPTURE(doc)
|
||||
CHECK(outcome(doc, false) == outcome(doc, true));
|
||||
CHECK(outcome(doc, false).find("must be followed by 4 hex digits") != std::string::npos);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("random escapes")
|
||||
{
|
||||
// A seeded PRNG builds the 4 bytes following `\u` from a mix of hex
|
||||
// digits and non-hex bytes, at varying distances from the start of
|
||||
// the string, to compare the two scanners on many more shapes than
|
||||
// are practical to enumerate by hand.
|
||||
std::mt19937 gen(7654321); // NOLINT(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed)
|
||||
const std::string hex_alphabet = "0123456789AaBbCcDdEeFf";
|
||||
std::uniform_int_distribution<std::size_t> pick_hex(0, hex_alphabet.size() - 1);
|
||||
std::uniform_int_distribution<int> pick_byte(1, 255); // never NUL
|
||||
std::uniform_int_distribution<int> pick_is_hex(0, 4); // 4-in-5 chance of a hex digit
|
||||
std::uniform_int_distribution<std::size_t> pick_offset(0, 12);
|
||||
|
||||
std::vector<std::string> mismatches;
|
||||
for (int iter = 0; iter < 3000; ++iter)
|
||||
{
|
||||
std::string digits;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
if (pick_is_hex(gen) != 0)
|
||||
{
|
||||
digits += hex_alphabet[pick_hex(gen)];
|
||||
}
|
||||
else
|
||||
{
|
||||
char c = static_cast<char>(pick_byte(gen));
|
||||
if (c == '"' || c == '\\')
|
||||
{
|
||||
// keep the string well-formed apart from the escape
|
||||
// itself, so any mismatch is attributable to the \u
|
||||
// handling and not to an unrelated quote/escape
|
||||
c = 'z';
|
||||
}
|
||||
digits += c;
|
||||
}
|
||||
}
|
||||
const std::string doc = "[\"" + std::string(pick_offset(gen), 'a') + "\\u" + digits + "\"]";
|
||||
if (outcome(doc, false) != outcome(doc, true))
|
||||
{
|
||||
mismatches.push_back(doc);
|
||||
}
|
||||
}
|
||||
CAPTURE(mismatches)
|
||||
CHECK(mismatches.empty());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// not a number
|
||||
CHECK_FALSE(fast("", out));
|
||||
CHECK_FALSE(fast("-", out));
|
||||
CHECK_FALSE(fast(".", out));
|
||||
CHECK_FALSE(fast("1.2.3", out));
|
||||
CHECK_FALSE(fast("1x", out));
|
||||
CHECK_FALSE(fast("1e+", out));
|
||||
CHECK_FALSE(fast("1e1x", out));
|
||||
namespace
|
||||
{
|
||||
// the index of the decimal point (or npos) and of the end of the mantissa of a
|
||||
// number token, which the lexer records while scanning it
|
||||
std::pair<std::size_t, std::size_t> float_token_layout(const std::string& s)
|
||||
{
|
||||
std::size_t dot = std::string::npos;
|
||||
std::size_t mantissa_end = s.size();
|
||||
for (std::size_t i = 0; i < s.size(); ++i)
|
||||
{
|
||||
if (s[i] == '.')
|
||||
{
|
||||
dot = i;
|
||||
}
|
||||
else if (s[i] == 'e' || s[i] == 'E')
|
||||
{
|
||||
mantissa_end = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {dot, mantissa_end};
|
||||
}
|
||||
|
||||
// numbers that are not represented exactly on the fast path
|
||||
CHECK_FALSE(fast("12345678901234567890", out));
|
||||
CHECK_FALSE(fast("1e10000", out));
|
||||
CHECK_FALSE(fast("9007199254740993", out));
|
||||
CHECK_FALSE(fast("1e23", out));
|
||||
CHECK_FALSE(fast("1e-23", out));
|
||||
template<typename FloatType>
|
||||
FloatType parse_native(const std::string& s)
|
||||
{
|
||||
const auto layout = float_token_layout(s);
|
||||
return nlohmann::detail::parse_float_native<FloatType>(s.data(), s.data() + s.size(), layout.first, layout.second);
|
||||
}
|
||||
|
||||
std::uint64_t bits_of(double d)
|
||||
{
|
||||
std::uint64_t b = 0;
|
||||
std::memcpy(&b, &d, sizeof(b));
|
||||
return b;
|
||||
}
|
||||
|
||||
std::uint32_t bits_of(float f)
|
||||
{
|
||||
std::uint32_t b = 0;
|
||||
std::memcpy(&b, &f, sizeof(b));
|
||||
return b;
|
||||
}
|
||||
|
||||
std::uint64_t native_bits64(const std::string& s)
|
||||
{
|
||||
return bits_of(parse_native<double>(s));
|
||||
}
|
||||
|
||||
std::uint32_t native_bits32(const std::string& s)
|
||||
{
|
||||
return bits_of(parse_native<float>(s));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("parse_float_native rounds correctly")
|
||||
{
|
||||
SECTION("double")
|
||||
{
|
||||
CHECK(native_bits64("1.5") == 0x3FF8000000000000u);
|
||||
CHECK(native_bits64("0.1") == 0x3FB999999999999Au);
|
||||
CHECK(native_bits64("-0.0") == 0x8000000000000000u);
|
||||
CHECK(native_bits64("0e999999999999999999999") == 0u);
|
||||
// 2^53 + 1 is exactly between two doubles: ties to even, unless more digits follow
|
||||
CHECK(native_bits64("9007199254740993") == 0x4340000000000000u);
|
||||
CHECK(native_bits64("9007199254740993.0000000000000000001") == 0x4340000000000001u);
|
||||
CHECK(native_bits64("9007199254740992.9999999999999999999") == 0x4340000000000000u);
|
||||
// 1 + 2^-53 exactly (a tie), and one unit in the 55th digit around it
|
||||
CHECK(native_bits64("1.00000000000000011102230246251565404236316680908203125") == 0x3FF0000000000000u);
|
||||
CHECK(native_bits64("1.00000000000000011102230246251565404236316680908203126") == 0x3FF0000000000001u);
|
||||
CHECK(native_bits64("1.00000000000000011102230246251565404236316680908203124") == 0x3FF0000000000000u);
|
||||
// subnormal and overflow boundaries
|
||||
CHECK(native_bits64("2.4703282292062327e-324") == 0u);
|
||||
CHECK(native_bits64("2.4703282292062328e-324") == 1u);
|
||||
CHECK(native_bits64("2.2250738585072011e-308") == 0x000FFFFFFFFFFFFFu);
|
||||
CHECK(native_bits64("2.2250738585072012e-308") == 0x0010000000000000u);
|
||||
CHECK(native_bits64("1.7976931348623157e308") == 0x7FEFFFFFFFFFFFFFu);
|
||||
CHECK(native_bits64("1.7976931348623159e308") == 0x7FF0000000000000u);
|
||||
CHECK(native_bits64("-1e400") == 0xFFF0000000000000u);
|
||||
CHECK(native_bits64("-1e-400") == 0x8000000000000000u);
|
||||
// exponents and zeros far beyond the range cancel out
|
||||
CHECK(native_bits64("0." + std::string(1000, '0') + "1e1001") == 0x3FF0000000000000u);
|
||||
CHECK(native_bits64("1" + std::string(1000, '0') + "e-1000") == 0x3FF0000000000000u);
|
||||
CHECK(native_bits64("1e-99999999999999999999999") == 0u);
|
||||
CHECK(native_bits64("1E+99999999999999999999999") == 0x7FF0000000000000u);
|
||||
// more digits than any midpoint has (769): only whether a nonzero digit follows matters
|
||||
const std::string tie = "1.00000000000000011102230246251565404236316680908203125";
|
||||
CHECK(native_bits64(tie + std::string(800, '0')) == 0x3FF0000000000000u);
|
||||
CHECK(native_bits64(tie + std::string(800, '0') + "1") == 0x3FF0000000000001u);
|
||||
}
|
||||
|
||||
SECTION("float")
|
||||
{
|
||||
CHECK(native_bits32("1.5") == 0x3FC00000u);
|
||||
CHECK(native_bits32("0.1") == 0x3DCCCCCDu);
|
||||
CHECK(native_bits32("-0.0") == 0x80000000u);
|
||||
// 2^24 + 1 is exactly between two floats
|
||||
CHECK(native_bits32("16777217") == 0x4B800000u);
|
||||
CHECK(native_bits32("16777217.000000000000000000001") == 0x4B800001u);
|
||||
CHECK(native_bits32("16777218.999999999999999999999") == 0x4B800001u);
|
||||
CHECK(native_bits32("16777219") == 0x4B800002u);
|
||||
// subnormal and overflow boundaries
|
||||
CHECK(native_bits32("3.4028235677973366e38") == 0x7F7FFFFFu);
|
||||
CHECK(native_bits32("3.4028235677973367e38") == 0x7F800000u);
|
||||
CHECK(native_bits32("7.006492321624085e-46") == 0u);
|
||||
CHECK(native_bits32("7.006492321624086e-46") == 1u);
|
||||
CHECK(native_bits32("1.1754942e-38") == 0x007FFFFFu);
|
||||
CHECK(native_bits32("-1.17549435e-38") == 0x80800000u);
|
||||
CHECK(native_bits32("1e39") == 0x7F800000u);
|
||||
CHECK(native_bits32("-1e-50") == 0x80000000u);
|
||||
// not rounded through double: its double would round to another float
|
||||
CHECK(native_bits32("1.00000005960464477539062500000000001") == 0x3F800001u);
|
||||
CHECK(native_bits32("9007199254740993") == 0x5A000000u);
|
||||
}
|
||||
|
||||
SECTION("the conversion shared with other parsers")
|
||||
{
|
||||
// convert_float() gives the lexer's results, for every type
|
||||
const std::vector<std::string> tokens =
|
||||
{
|
||||
"0", "-0.0", "1.5", "0.1", "1e-400", "-2.5E+3", "123456789012345678901234567890",
|
||||
"9007199254740993.0000000000000000001", "4.9406564584124654e-324"
|
||||
};
|
||||
using float_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, float>;
|
||||
using long_double_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, long double>;
|
||||
for (const auto& t : tokens)
|
||||
{
|
||||
CAPTURE(t)
|
||||
const auto layout = float_token_layout(t);
|
||||
const char* const first = t.data();
|
||||
const char* const last = first + t.size();
|
||||
const auto d = nlohmann::detail::convert_float<double>(first, last, layout.first, layout.second);
|
||||
const auto f = nlohmann::detail::convert_float<float>(first, last, layout.first, layout.second);
|
||||
const auto ld = nlohmann::detail::convert_float<long double>(first, last, layout.first, layout.second);
|
||||
CHECK(bits_of(d) == bits_of(json::parse(t).get<double>()));
|
||||
CHECK(bits_of(f) == bits_of(float_json::parse(t).get<float>()));
|
||||
CHECK(ld == long_double_json::parse(t).get<long double>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
@@ -806,40 +1086,6 @@ std::size_t big_bit_length(const big_uint& a)
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
std::uint64_t bits_of(double d)
|
||||
{
|
||||
std::uint64_t b = 0;
|
||||
std::memcpy(&b, &d, sizeof(b));
|
||||
return b;
|
||||
}
|
||||
|
||||
bool eisel_lemire(const std::string& s, double& out)
|
||||
{
|
||||
return nlohmann::detail::parse_float_eisel_lemire(s.data(), s.data() + s.size(), out);
|
||||
}
|
||||
|
||||
// significant digits of a token, without trailing zeros
|
||||
std::size_t significant_digits(const std::string& s)
|
||||
{
|
||||
std::string digits;
|
||||
for (const char c : s)
|
||||
{
|
||||
if (c == 'e' || c == 'E')
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (c >= '0' && c <= '9' && !(digits.empty() && c == '0'))
|
||||
{
|
||||
digits += c;
|
||||
}
|
||||
}
|
||||
while (!digits.empty() && digits.back() == '0')
|
||||
{
|
||||
digits.pop_back();
|
||||
}
|
||||
return digits.size();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Eisel-Lemire float conversion")
|
||||
@@ -891,8 +1137,39 @@ TEST_CASE("Eisel-Lemire float conversion")
|
||||
|
||||
SECTION("128-bit products and leading zeros")
|
||||
{
|
||||
const auto check_product = [](std::uint64_t a, std::uint64_t b)
|
||||
{
|
||||
const auto product = nlohmann::detail::full_multiplication(a, b);
|
||||
CHECK(big_from(product.high, product.low) == big_mul(big_from(0, a), big_from(0, b)));
|
||||
};
|
||||
|
||||
const std::uint64_t max = (std::numeric_limits<std::uint64_t>::max)();
|
||||
const std::array<std::pair<std::uint64_t, std::uint64_t>, 13> edge_cases =
|
||||
{
|
||||
{
|
||||
{0, 0},
|
||||
{0, 1},
|
||||
{1, 1},
|
||||
{1, max},
|
||||
{0xFFFFFFFFu, 0x100000000u},
|
||||
{0x100000000u, 0x100000000u},
|
||||
{0x100000001u, 0x100000001u},
|
||||
{max, max},
|
||||
{max, 2},
|
||||
{0xFFFFFFFF00000000u, 0x100000001u},
|
||||
{0x100000001u, 0xFFFFFFFF00000000u},
|
||||
{max, 1},
|
||||
{2, max},
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto& test : edge_cases)
|
||||
{
|
||||
check_product(test.first, test.second);
|
||||
}
|
||||
|
||||
// whichever implementation the compiler gets (with or without a
|
||||
// 128-bit integer type or a builtin)
|
||||
// 128-bit integer type or a builtin / intrinsic)
|
||||
std::uint64_t state = 42;
|
||||
for (int i = 0; i < 10000; ++i)
|
||||
{
|
||||
@@ -901,8 +1178,7 @@ TEST_CASE("Eisel-Lemire float conversion")
|
||||
state ^= state << 17u;
|
||||
const std::uint64_t a = state;
|
||||
const std::uint64_t b = (state * 0x9E3779B97F4A7C15u) >> (i % 64);
|
||||
const auto product = nlohmann::detail::full_multiplication(a, b);
|
||||
CHECK(big_from(product.high, product.low) == big_mul(big_from(0, a), big_from(0, b)));
|
||||
check_product(a, b);
|
||||
|
||||
const int k = i % 64;
|
||||
const std::uint64_t x = (std::uint64_t{1} << k) | (a & ((std::uint64_t{1} << k) - 1));
|
||||
@@ -1237,26 +1513,33 @@ TEST_CASE("Eisel-Lemire float conversion")
|
||||
for (const auto& c : known)
|
||||
{
|
||||
CAPTURE(c.first)
|
||||
double out = 0;
|
||||
if (eisel_lemire(c.first, out))
|
||||
{
|
||||
CHECK(bits_of(out) == c.second);
|
||||
}
|
||||
else
|
||||
{
|
||||
// only tokens with more than 19 significant digits are left to
|
||||
// strtod: those whose value lies too close to a tie
|
||||
CHECK(significant_digits(c.first) > 19);
|
||||
}
|
||||
CHECK(native_bits64(c.first) == c.second);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("binary32")
|
||||
{
|
||||
using binary32 = nlohmann::detail::ieee_binary_format<24>;
|
||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(0, 1) == 0x3F800000u);
|
||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(-1, 1) == 0x3DCCCCCDu);
|
||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(-1, 15) == 0x3FC00000u);
|
||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(0, 16777217) == 0x4B800000u); // tie, to even
|
||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(0, 16777219) == 0x4B800002u); // tie, to even
|
||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(-45, 1) == 0x00000001u);
|
||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(-46, 7) == 0x00000000u);
|
||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(-46, 8) == 0x00000001u);
|
||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(-65, 9999999999999999999u) == 0x00000000u);
|
||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(20, 3402823466385288598u) == 0x7F7FFFFFu);
|
||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(20, 3402823669209384635u) == 0x7F800000u);
|
||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(39, 1) == 0x7F800000u);
|
||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(-5, 0) == 0x00000000u);
|
||||
}
|
||||
|
||||
SECTION("round trip")
|
||||
{
|
||||
// every double written by to_chars and read back, also with trailing
|
||||
// digits that make the token longer than 19 digits
|
||||
// every double written by to_chars and read back, and its 17-digit
|
||||
// form with trailing digits that make the token longer than 19 digits
|
||||
std::uint64_t state = 5295;
|
||||
std::size_t declined = 0;
|
||||
for (int i = 0; i < 200000; ++i)
|
||||
{
|
||||
state ^= state << 13u;
|
||||
@@ -1278,30 +1561,51 @@ TEST_CASE("Eisel-Lemire float conversion")
|
||||
const char* end = nlohmann::detail::to_chars(buffer.data(), buffer.data() + buffer.size(), d);
|
||||
const std::string token(buffer.data(), static_cast<std::size_t>(end - buffer.data()));
|
||||
CAPTURE(token)
|
||||
double out = 0;
|
||||
REQUIRE(eisel_lemire(token, out));
|
||||
CHECK(bits_of(out) == b);
|
||||
CHECK(native_bits64(token) == b);
|
||||
|
||||
// insert digits before the exponent: the value moves by far less
|
||||
// than the distance to the rounding boundary, so it must not change
|
||||
std::string longer = token;
|
||||
// insert digits before the exponent of the 17-digit form: that
|
||||
// form lies strictly inside the rounding interval of the double
|
||||
// (the shortest one may lie on its boundary), and the digits move
|
||||
// it by far less than the distance to the boundary, so the value
|
||||
// must not change
|
||||
std::array<char, 64> digits17{};
|
||||
static_cast<void>(std::snprintf(digits17.data(), digits17.size(), "%.17g", d)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||
std::string longer = digits17.data();
|
||||
const std::size_t e = longer.find('e');
|
||||
const std::size_t dot = longer.find('.');
|
||||
const std::string extra = dot == std::string::npos ? ".000000000000000000001" : "000000000000000000001";
|
||||
longer.insert(e == std::string::npos ? longer.size() : e, extra);
|
||||
CAPTURE(longer)
|
||||
if (eisel_lemire(longer, out))
|
||||
CHECK(native_bits64(longer) == b);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("round trip, binary32")
|
||||
{
|
||||
std::uint32_t state = 5295;
|
||||
for (int i = 0; i < 100000; ++i)
|
||||
{
|
||||
state ^= state << 13u;
|
||||
state ^= state >> 17u;
|
||||
state ^= state << 5u;
|
||||
std::uint32_t b = state;
|
||||
if ((b & 0x7F800000u) == 0x7F800000u)
|
||||
{
|
||||
CHECK(bits_of(out) == b);
|
||||
continue; // infinity or NaN
|
||||
}
|
||||
else
|
||||
if (i % 4 == 0)
|
||||
{
|
||||
// w and w + 1 round differently: only when the value is very
|
||||
// close to a rounding boundary
|
||||
++declined;
|
||||
b &= 0x807FFFFFu; // subnormals
|
||||
}
|
||||
float f = 0;
|
||||
std::memcpy(&f, &b, sizeof(f));
|
||||
|
||||
std::array<char, 64> buffer{};
|
||||
const char* end = nlohmann::detail::to_chars(buffer.data(), buffer.data() + buffer.size(), f);
|
||||
const std::string token(buffer.data(), static_cast<std::size_t>(end - buffer.data()));
|
||||
CAPTURE(token)
|
||||
CHECK(native_bits32(token) == b);
|
||||
}
|
||||
CHECK(declined < 1000); // 107 of the 200,000
|
||||
}
|
||||
|
||||
SECTION("used by the lexer")
|
||||
@@ -1315,3 +1619,184 @@ TEST_CASE("Eisel-Lemire float conversion")
|
||||
"[json.exception.out_of_range.406] number overflow parsing '1.7976931348623159e308'", json::out_of_range&);
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
using float_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, float>;
|
||||
|
||||
// the bits of the float that parse() gives for a token, via both scanners;
|
||||
// the value must be the same for both
|
||||
template<typename Json, typename Bits>
|
||||
void check_parse(const std::string& token, Bits expected, Bits infinity)
|
||||
{
|
||||
std::stringstream stream(token);
|
||||
if ((expected & ~(Bits{1} << ((8 * sizeof(Bits)) - 1))) == infinity)
|
||||
{
|
||||
Json _;
|
||||
CHECK_THROWS_WITH_AS(_ = Json::parse(token), ("[json.exception.out_of_range.406] number overflow parsing '" + token + "'").c_str(), typename Json::out_of_range&);
|
||||
CHECK_THROWS_WITH_AS(_ = Json::parse(stream), ("[json.exception.out_of_range.406] number overflow parsing '" + token + "'").c_str(), typename Json::out_of_range&);
|
||||
return;
|
||||
}
|
||||
const Json contiguous = Json::parse(token);
|
||||
const Json streamed = Json::parse(stream);
|
||||
if (contiguous.is_number_float()) // not an integer that fits
|
||||
{
|
||||
CHECK(bits_of(contiguous.template get<typename Json::number_float_t>()) == expected);
|
||||
CHECK(bits_of(streamed.template get<typename Json::number_float_t>()) == expected);
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK(streamed.is_number_integer());
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("float conversion of hard cases")
|
||||
{
|
||||
// see float_hard_cases.hpp
|
||||
for (const auto& c : float_hard_cases::cases())
|
||||
{
|
||||
const std::string token = c.token;
|
||||
CAPTURE(token)
|
||||
CHECK(native_bits64(token) == c.bits64);
|
||||
CHECK(native_bits32(token) == c.bits32);
|
||||
check_parse<json>(token, c.bits64, std::uint64_t{0x7FF0000000000000u});
|
||||
check_parse<float_json>(token, c.bits32, std::uint32_t{0x7F800000u});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("float overflow and underflow in the parser")
|
||||
{
|
||||
SECTION("double")
|
||||
{
|
||||
check_parse<json>("1.7976931348623157e308", std::uint64_t{0x7FEFFFFFFFFFFFFFu}, std::uint64_t{0x7FF0000000000000u});
|
||||
check_parse<json>("1.7976931348623159e308", std::uint64_t{0x7FF0000000000000u}, std::uint64_t{0x7FF0000000000000u});
|
||||
check_parse<json>("-1e309", std::uint64_t{0xFFF0000000000000u}, std::uint64_t{0x7FF0000000000000u});
|
||||
check_parse<json>("1" + std::string(400, '0'), std::uint64_t{0x7FF0000000000000u}, std::uint64_t{0x7FF0000000000000u});
|
||||
check_parse<json>("1e99999999999999999999", std::uint64_t{0x7FF0000000000000u}, std::uint64_t{0x7FF0000000000000u});
|
||||
// an underflow gives a zero with the sign of the token
|
||||
check_parse<json>("1e-400", std::uint64_t{0}, std::uint64_t{0x7FF0000000000000u});
|
||||
check_parse<json>("-1e-400", std::uint64_t{0x8000000000000000u}, std::uint64_t{0x7FF0000000000000u});
|
||||
check_parse<json>("-2.4703282292062327e-324", std::uint64_t{0x8000000000000000u}, std::uint64_t{0x7FF0000000000000u});
|
||||
check_parse<json>("0." + std::string(400, '0') + "1", std::uint64_t{0}, std::uint64_t{0x7FF0000000000000u});
|
||||
}
|
||||
|
||||
SECTION("float")
|
||||
{
|
||||
check_parse<float_json>("3.4028234e38", std::uint32_t{0x7F7FFFFFu}, std::uint32_t{0x7F800000u});
|
||||
check_parse<float_json>("3.4028236e38", std::uint32_t{0x7F800000u}, std::uint32_t{0x7F800000u});
|
||||
check_parse<float_json>("-1e39", std::uint32_t{0xFF800000u}, std::uint32_t{0x7F800000u});
|
||||
check_parse<float_json>("1e-46", std::uint32_t{0}, std::uint32_t{0x7F800000u});
|
||||
check_parse<float_json>("-1e-46", std::uint32_t{0x80000000u}, std::uint32_t{0x7F800000u});
|
||||
check_parse<float_json>("-7.006492321624085e-46", std::uint32_t{0x80000000u}, std::uint32_t{0x7F800000u});
|
||||
check_parse<float_json>("-7.006492321624086e-46", std::uint32_t{0x80000001u}, std::uint32_t{0x7F800000u});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("string scanning kernels")
|
||||
{
|
||||
// the word-at-a-time kernels must stop exactly where a byte-by-byte scan
|
||||
// stops, for any content, length, and alignment
|
||||
const auto reference_special = [](const unsigned char* data, std::size_t n)
|
||||
{
|
||||
std::size_t i = 0;
|
||||
while (i < n && !nlohmann::detail::is_string_special(data[i]))
|
||||
{
|
||||
++i;
|
||||
}
|
||||
return i;
|
||||
};
|
||||
const auto reference_copyable = [](const unsigned char* data, std::size_t n)
|
||||
{
|
||||
std::size_t i = 0;
|
||||
while (i < n && nlohmann::detail::is_ascii_copyable(data[i]))
|
||||
{
|
||||
++i;
|
||||
}
|
||||
return i;
|
||||
};
|
||||
const auto reference_bulk_run = [](const unsigned char* data, std::size_t n)
|
||||
{
|
||||
std::size_t i = 0;
|
||||
while (i < n)
|
||||
{
|
||||
if (data[i] < 0x80u)
|
||||
{
|
||||
if (nlohmann::detail::is_string_special(data[i]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
const std::size_t seq = nlohmann::detail::validate_one_utf8(data + i, n - i);
|
||||
if (seq == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
i += seq;
|
||||
}
|
||||
return i;
|
||||
};
|
||||
|
||||
// pieces: ordinary ASCII, stops, DEL, well-formed sequences of every
|
||||
// length, and ill-formed or truncated ones
|
||||
const std::vector<std::string> pieces =
|
||||
{
|
||||
"a", "Z", " ", "~", "0123456789", "\"", "\\", std::string(1, '\0'), "\n", "\x1F", "\x7F",
|
||||
"\xC3\xA4", "\xE2\x82\xAC", "\xE6\x97\xA5\xE6\x9C\xAC", "\xF0\x9F\x98\x80", "\xED\x9F\xBF",
|
||||
"\x80", "\xC0\x80", "\xC3", "\xE2\x82", "\xED\xA0\x80", "\xF4\x90\x80\x80", "\xFF",
|
||||
};
|
||||
std::uint64_t state = 5295;
|
||||
const auto next = [&state]()
|
||||
{
|
||||
state ^= state << 13u;
|
||||
state ^= state >> 7u;
|
||||
state ^= state << 17u;
|
||||
return state;
|
||||
};
|
||||
// the upper half as a 32-bit value: converts to std::size_t implicitly on
|
||||
// every platform (a cast of std::uint64_t is useless where both are the
|
||||
// same type, and required where std::size_t is 32 bits wide)
|
||||
const auto next_small = [&next]()
|
||||
{
|
||||
return static_cast<std::uint32_t>(next() >> 32u);
|
||||
};
|
||||
for (int round = 0; round < 100000; ++round)
|
||||
{
|
||||
// mostly ordinary text, so that runs span several words
|
||||
std::string text(next_small() % 8u, '.');
|
||||
const std::size_t count = next_small() % 12u;
|
||||
for (std::size_t k = 0; k < count; ++k)
|
||||
{
|
||||
const std::size_t p = (next() % 4 == 0) ? next_small() % pieces.size() : 0;
|
||||
text += pieces[p];
|
||||
text += std::string(next_small() % 10u, 'x');
|
||||
}
|
||||
const auto* data = reinterpret_cast<const unsigned char*>(text.data()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
for (std::size_t offset = 0; offset < 3 && offset <= text.size(); ++offset)
|
||||
{
|
||||
const std::size_t n = text.size() - offset;
|
||||
CAPTURE(text)
|
||||
CAPTURE(offset)
|
||||
CHECK(nlohmann::detail::find_string_special(data + offset, n) == reference_special(data + offset, n));
|
||||
CHECK(nlohmann::detail::find_ascii_copyable_run(data + offset, n) == reference_copyable(data + offset, n));
|
||||
CHECK(nlohmann::detail::scalar_string_bulk_run(data + offset, n) == reference_bulk_run(data + offset, n));
|
||||
}
|
||||
}
|
||||
|
||||
// the trailing-zero count, whichever implementation the compiler gets
|
||||
for (int k = 0; k < 64; ++k)
|
||||
{
|
||||
const std::uint64_t bit = std::uint64_t{1} << k;
|
||||
CHECK(nlohmann::detail::count_trailing_zeros(bit) == k);
|
||||
CHECK(nlohmann::detail::count_trailing_zeros(bit | (bit << 1u) | 0x8000000000000000u) == k);
|
||||
}
|
||||
|
||||
// eight bytes as a little-endian word, at any alignment
|
||||
const std::array<unsigned char, 16> bytes = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF}};
|
||||
CHECK(nlohmann::detail::read_eight_bytes(bytes.data()) == 0x0807060504030201u);
|
||||
CHECK(nlohmann::detail::read_eight_bytes(bytes.data() + 1) == 0x0908070605040302u);
|
||||
CHECK(nlohmann::detail::read_eight_bytes(bytes.data() + 8) == 0xFF0F0E0D0C0B0A09u);
|
||||
CHECK(nlohmann::detail::read_eight_bytes(reinterpret_cast<const char*>(bytes.data()) + 3) == 0x0B0A090807060504u);
|
||||
}
|
||||
@@ -824,7 +824,7 @@ struct unordered_object_t : std::map<Key, Value, directed_less<Key>, Allocator>
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
};
|
||||
using unordered_json = nlohmann::basic_json<unordered_object_t>;
|
||||
using unordered_json = nlohmann::json::with_object_t<unordered_object_t>;
|
||||
|
||||
// the entries "0" to "9", enumerated in ascending or in descending order
|
||||
unordered_json make_unordered_object(const bool descending)
|
||||
@@ -875,7 +875,7 @@ struct key_case_less
|
||||
|
||||
template<class Key, class Value, class /*Compare*/, class Allocator>
|
||||
using key_case_map = std::map<Key, Value, key_case_less, Allocator>;
|
||||
using key_case_json = nlohmann::basic_json<key_case_map>;
|
||||
using key_case_json = nlohmann::json::with_object_t<key_case_map>;
|
||||
|
||||
// the innermost value of a chain of single-element arrays
|
||||
template<typename Json>
|
||||
@@ -905,7 +905,7 @@ struct case_insensitive_less
|
||||
|
||||
template<class Key, class Value, class /*Compare*/, class Allocator>
|
||||
using case_insensitive_map = std::map<Key, Value, case_insensitive_less, Allocator>;
|
||||
using ci_json = nlohmann::basic_json<case_insensitive_map>;
|
||||
using ci_json = nlohmann::json::with_object_t<case_insensitive_map>;
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("equality of objects whose entries have no fixed order")
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace
|
||||
|
||||
// std::deque has no capacity() member function, which the library only needs
|
||||
// to detect a reallocation for JSON_DIAGNOSTICS
|
||||
using deque_json = nlohmann::basic_json<std::map, std::deque>;
|
||||
using deque_json = nlohmann::json::with_array_t<std::deque>;
|
||||
|
||||
// a std::vector whose at() is hidden: the library performs its own bounds
|
||||
// check and must not fall back to the container's checked accessor
|
||||
@@ -39,7 +39,7 @@ class vector_without_at : public std::vector<T, Allocator>
|
||||
void at() = delete;
|
||||
};
|
||||
|
||||
using no_at_json = nlohmann::basic_json<std::map, vector_without_at>;
|
||||
using no_at_json = nlohmann::json::with_array_t<vector_without_at>;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -400,20 +400,7 @@ class base_class_with_hidden_members
|
||||
std::size_t m_size = 42;
|
||||
};
|
||||
|
||||
using json_with_hidden_base_members =
|
||||
nlohmann::basic_json <
|
||||
std::map,
|
||||
std::vector,
|
||||
std::string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
std::allocator,
|
||||
nlohmann::adl_serializer,
|
||||
std::vector<std::uint8_t>,
|
||||
base_class_with_hidden_members
|
||||
>;
|
||||
using json_with_hidden_base_members = nlohmann::json::with_base_class_t<base_class_with_hidden_members>;
|
||||
|
||||
TEST_CASE("JSON Node as_base_class")
|
||||
{
|
||||
@@ -459,19 +446,7 @@ struct const_member_base
|
||||
const int id = 7; // NOLINT(misc-non-private-member-variables-in-classes)
|
||||
};
|
||||
|
||||
using json_with_const_base = nlohmann::basic_json <
|
||||
std::map,
|
||||
std::vector,
|
||||
std::string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
std::allocator,
|
||||
nlohmann::adl_serializer,
|
||||
std::vector<std::uint8_t>,
|
||||
const_member_base
|
||||
>;
|
||||
using json_with_const_base = nlohmann::json::with_base_class_t<const_member_base>;
|
||||
|
||||
// build an array nested @a depth levels deep, with the innermost value 1;
|
||||
// every level is constructed (never assigned), since const_member_base does
|
||||
|
||||
@@ -26,15 +26,11 @@ namespace
|
||||
|
||||
// a BinaryType whose value type is signed: the elements must still be
|
||||
// processed as the numbers 0..255
|
||||
using char_binary_json = nlohmann::basic_json <
|
||||
std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t,
|
||||
double, std::allocator, nlohmann::adl_serializer, std::vector<char>, void >;
|
||||
using char_binary_json = nlohmann::json::with_binary_t<std::vector<char>>;
|
||||
|
||||
#ifdef JSON_HAS_CPP_17
|
||||
// a BinaryType whose value type is not an integer type at all
|
||||
using byte_binary_json = nlohmann::basic_json <
|
||||
std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t,
|
||||
double, std::allocator, nlohmann::adl_serializer, std::vector<std::byte>, void >;
|
||||
using byte_binary_json = nlohmann::json::with_binary_t<std::vector<std::byte>>;
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | 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 <cstddef>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "custom_object_key_type.hpp"
|
||||
|
||||
// These tests instantiate a second basic_json specialization. They live in
|
||||
// their own file rather than in unit-cbor.cpp and unit-msgpack.cpp to keep
|
||||
// those objects below 65535 sections: the MinGW linker stores the section a
|
||||
// COMDAT section is associated with in 16 bits, so it misplaces the jump
|
||||
// tables of larger objects (see the clang job in windows.yml).
|
||||
|
||||
TEST_CASE("CBOR supports custom object key types")
|
||||
{
|
||||
using custom_json = custom_object_key_test::json;
|
||||
using custom_key = custom_object_key_test::key;
|
||||
|
||||
custom_json::object_t object;
|
||||
object.emplace(custom_key{"short"}, 1);
|
||||
object.emplace(
|
||||
custom_key{"a key longer than twenty-three characters"},
|
||||
2);
|
||||
|
||||
const custom_json value(std::move(object));
|
||||
const auto encoded = custom_json::to_cbor(value);
|
||||
|
||||
CHECK(nlohmann::json::from_cbor(encoded) == nlohmann::json
|
||||
{
|
||||
{"short", 1},
|
||||
{"a key longer than twenty-three characters", 2}
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("CBOR supports custom object key types nested deeper than the recursion depth limit")
|
||||
{
|
||||
// below detail::recursion_depth_limit(), keys are written by
|
||||
// write_cbor_iterative instead of write_cbor
|
||||
using custom_json = custom_object_key_test::json;
|
||||
using custom_key = custom_object_key_test::key;
|
||||
|
||||
const std::size_t depth = nlohmann::detail::recursion_depth_limit() + 10;
|
||||
|
||||
custom_json value = 1;
|
||||
nlohmann::json expected = 1;
|
||||
for (std::size_t i = 0; i < depth; ++i)
|
||||
{
|
||||
// alternate short keys with ones long enough to need a length byte
|
||||
const std::string name = (i % 2 == 0) ? "k" + std::to_string(i)
|
||||
: "a key longer than thirty-one characters " + std::to_string(i);
|
||||
|
||||
custom_json::object_t object;
|
||||
object.emplace(custom_key{name}, std::move(value));
|
||||
value = custom_json(std::move(object));
|
||||
|
||||
nlohmann::json::object_t expected_object;
|
||||
expected_object.emplace(name, std::move(expected));
|
||||
expected = nlohmann::json(std::move(expected_object));
|
||||
}
|
||||
|
||||
const auto encoded = custom_json::to_cbor(value);
|
||||
CHECK(encoded == nlohmann::json::to_cbor(expected));
|
||||
CHECK(nlohmann::json::from_cbor(encoded) == expected);
|
||||
}
|
||||
|
||||
TEST_CASE("MessagePack supports custom object key types")
|
||||
{
|
||||
using custom_json = custom_object_key_test::json;
|
||||
using custom_key = custom_object_key_test::key;
|
||||
|
||||
custom_json::object_t object;
|
||||
object.emplace(custom_key{"short"}, 1);
|
||||
object.emplace(
|
||||
custom_key{"a key longer than thirty-one characters"},
|
||||
2);
|
||||
|
||||
const custom_json value(std::move(object));
|
||||
const auto encoded = custom_json::to_msgpack(value);
|
||||
|
||||
CHECK(nlohmann::json::from_msgpack(encoded) == nlohmann::json
|
||||
{
|
||||
{"short", 1},
|
||||
{"a key longer than thirty-one characters", 2}
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("MessagePack supports custom object key types nested deeper than the recursion depth limit")
|
||||
{
|
||||
// below detail::recursion_depth_limit(), keys are written by
|
||||
// write_msgpack_iterative instead of write_msgpack
|
||||
using custom_json = custom_object_key_test::json;
|
||||
using custom_key = custom_object_key_test::key;
|
||||
|
||||
const std::size_t depth = nlohmann::detail::recursion_depth_limit() + 10;
|
||||
|
||||
custom_json value = 1;
|
||||
nlohmann::json expected = 1;
|
||||
for (std::size_t i = 0; i < depth; ++i)
|
||||
{
|
||||
// alternate short keys with ones long enough to need a length byte
|
||||
const std::string name = (i % 2 == 0) ? "k" + std::to_string(i)
|
||||
: "a key longer than thirty-one characters " + std::to_string(i);
|
||||
|
||||
custom_json::object_t object;
|
||||
object.emplace(custom_key{name}, std::move(value));
|
||||
value = custom_json(std::move(object));
|
||||
|
||||
nlohmann::json::object_t expected_object;
|
||||
expected_object.emplace(name, std::move(expected));
|
||||
expected = nlohmann::json(std::move(expected_object));
|
||||
}
|
||||
|
||||
const auto encoded = custom_json::to_msgpack(value);
|
||||
CHECK(encoded == nlohmann::json::to_msgpack(expected));
|
||||
CHECK(nlohmann::json::from_msgpack(encoded) == expected);
|
||||
}
|
||||
@@ -179,7 +179,7 @@ class no_key_compare_map
|
||||
}
|
||||
};
|
||||
|
||||
using no_key_compare_json = nlohmann::basic_json<no_key_compare_map>;
|
||||
using no_key_compare_json = nlohmann::json::with_object_t<no_key_compare_map>;
|
||||
|
||||
// An ObjectType whose erase(iterator) returns void rather than the following
|
||||
// iterator, as for instance Abseil's hash maps do
|
||||
@@ -196,7 +196,7 @@ struct void_erase_map : std::map<Key, T, Compare, Allocator>
|
||||
}
|
||||
};
|
||||
|
||||
using void_erase_json = nlohmann::basic_json<void_erase_map>;
|
||||
using void_erase_json = nlohmann::json::with_object_t<void_erase_map>;
|
||||
|
||||
// wraps an iterator, but only offers the LegacyForwardIterator operations,
|
||||
// like the iterators of std::unordered_map and other hash maps
|
||||
@@ -388,7 +388,7 @@ class forward_only_map
|
||||
}
|
||||
};
|
||||
|
||||
using forward_only_json = nlohmann::basic_json<forward_only_map>;
|
||||
using forward_only_json = nlohmann::json::with_object_t<forward_only_map>;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -157,13 +157,13 @@ TEST_CASE("hash<nlohmann::json>")
|
||||
// the ends of the integer ranges, which equal floats exactly
|
||||
const auto int_min = (std::numeric_limits<json::number_integer_t>::min)();
|
||||
const auto int_max = (std::numeric_limits<json::number_integer_t>::max)();
|
||||
const auto two_63 = json::number_unsigned_t(1) << 63U;
|
||||
const auto two_63 = static_cast<json::number_unsigned_t>(1) << 63U;
|
||||
CHECK(json(int_min) == json(-9223372036854775808.0));
|
||||
CHECK(std::hash<json> {}(json(int_min)) == std::hash<json> {}(json(-9223372036854775808.0)));
|
||||
CHECK(json(two_63) == json(9223372036854775808.0));
|
||||
CHECK(std::hash<json> {}(json(two_63)) == std::hash<json> {}(json(9223372036854775808.0)));
|
||||
CHECK(json(json::number_unsigned_t(int_max)) == json(int_max));
|
||||
CHECK(std::hash<json> {}(json(json::number_unsigned_t(int_max))) == std::hash<json> {}(json(int_max)));
|
||||
CHECK(json(static_cast<json::number_unsigned_t>(int_max)) == json(int_max));
|
||||
CHECK(std::hash<json> {}(json(static_cast<json::number_unsigned_t>(int_max))) == std::hash<json> {}(json(int_max)));
|
||||
}
|
||||
|
||||
TEST_CASE("hash<nlohmann::ordered_json>")
|
||||
|
||||
@@ -939,3 +939,114 @@ TEST_CASE("unescaping keeps a '~' that does not start an escape sequence")
|
||||
nlohmann::detail::unescape(s);
|
||||
CHECK(s == "~/~");
|
||||
}
|
||||
|
||||
TEST_CASE("flatten of structured values")
|
||||
{
|
||||
SECTION("values nested too deeply for the call stack (#5393)")
|
||||
{
|
||||
// flatten() used to recurse once per nesting level
|
||||
const std::size_t depth = 100000;
|
||||
for (const bool objects :
|
||||
{
|
||||
false, true
|
||||
})
|
||||
{
|
||||
CAPTURE(objects)
|
||||
std::string text;
|
||||
std::string path;
|
||||
for (std::size_t i = 0; i < depth; ++i)
|
||||
{
|
||||
text += objects ? "{\"a\":" : "[";
|
||||
path += objects ? "/a" : "/0";
|
||||
}
|
||||
text += "0";
|
||||
text += std::string(depth, objects ? '}' : ']');
|
||||
const auto value = json::parse(text);
|
||||
|
||||
const auto flat = value.flatten();
|
||||
REQUIRE(flat.size() == 1);
|
||||
REQUIRE(flat.begin().key().size() == path.size());
|
||||
CHECK(flat.begin().key() == path);
|
||||
CHECK(flat.begin().value() == 0);
|
||||
|
||||
// unflatten() is linear in the depth, so the value roundtrips
|
||||
CHECK(flat.unflatten() == value);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("unflatten of a deeply nested pointer")
|
||||
{
|
||||
const std::size_t depth = 100000;
|
||||
for (const bool objects :
|
||||
{
|
||||
false, true
|
||||
})
|
||||
{
|
||||
CAPTURE(objects)
|
||||
std::string path;
|
||||
for (std::size_t i = 0; i < depth; ++i)
|
||||
{
|
||||
path += objects ? "/a" : "/0";
|
||||
}
|
||||
|
||||
json flat = json::object();
|
||||
flat[path] = 1;
|
||||
const json value = flat.unflatten();
|
||||
|
||||
// walk down iteratively
|
||||
std::size_t levels = 0;
|
||||
const json* current = &value;
|
||||
while (objects ? current->is_object() : current->is_array())
|
||||
{
|
||||
REQUIRE(current->size() == 1);
|
||||
current = objects ? ¤t->at("a") : ¤t->at(0);
|
||||
++levels;
|
||||
}
|
||||
CHECK(levels == depth);
|
||||
CHECK(*current == 1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("unflatten does not depend on the iteration order")
|
||||
{
|
||||
// the "0" key comes after its sibling in iteration order
|
||||
const nlohmann::ordered_json flat_array = nlohmann::ordered_json::parse(R"({"/a/1": 2, "/a/0": 1})");
|
||||
CHECK(flat_array.unflatten() == nlohmann::ordered_json::parse(R"({"a": [1, 2]})"));
|
||||
|
||||
const nlohmann::ordered_json flat_object = nlohmann::ordered_json::parse(R"({"/b/1": 2})");
|
||||
CHECK(flat_object.unflatten() == nlohmann::ordered_json::parse(R"({"b": {"1": 2}})"));
|
||||
}
|
||||
|
||||
SECTION("objects and arrays interleaved")
|
||||
{
|
||||
const json value =
|
||||
{
|
||||
{"a", {1, {{"b", json::array()}, {"c", json::object()}}, json::array({{{"x~/", {true, nullptr}}}})}},
|
||||
{"a/b", {{"~", 1}}},
|
||||
{"z", "s"}
|
||||
};
|
||||
|
||||
const json expected =
|
||||
{
|
||||
{"/a/0", 1},
|
||||
{"/a/1/b", nullptr},
|
||||
{"/a/1/c", nullptr},
|
||||
{"/a/2/0/x~0~1/0", true},
|
||||
{"/a/2/0/x~0~1/1", nullptr},
|
||||
{"/a~1b/~0", 1},
|
||||
{"/z", "s"}
|
||||
};
|
||||
|
||||
CHECK(value.flatten() == expected);
|
||||
}
|
||||
|
||||
SECTION("order of the entries of an ordered_json")
|
||||
{
|
||||
const auto value = nlohmann::ordered_json::parse(
|
||||
R"({"z":"s","a/b":{"~":1,"k":[]},"a":[1,{"c":{},"b":[]},[{"x~/":[true,null],"w":2}]]})");
|
||||
|
||||
const auto flat = value.flatten();
|
||||
CHECK(flat.dump() ==
|
||||
R"({"/z":"s","/a~1b/~0":1,"/a~1b/k":null,"/a/0":1,"/a/1/c":null,"/a/1/b":null,"/a/2/0/x~0~1/0":true,"/a/2/0/x~0~1/1":null,"/a/2/0/w":2})");
|
||||
}
|
||||
}
|
||||
@@ -426,7 +426,7 @@ TEST_CASE("issue #5392 - binary writers on deeply nested values")
|
||||
{
|
||||
for (std::size_t depth = 120; depth <= 140; ++depth)
|
||||
{
|
||||
CAPTURE(depth);
|
||||
CAPTURE(depth)
|
||||
|
||||
const json array = nested_array(depth, json(7));
|
||||
CHECK(json::from_cbor(json::to_cbor(array)) == array);
|
||||
@@ -464,7 +464,7 @@ TEST_CASE("issue #5392 - binary writers on deeply nested values")
|
||||
nlohmann::detail::recursion_depth_limit() + 1, nlohmann::detail::recursion_depth_limit() + 2
|
||||
})
|
||||
{
|
||||
CAPTURE(depth);
|
||||
CAPTURE(depth)
|
||||
const json array = nested_array(depth, json(0));
|
||||
|
||||
std::vector<std::uint8_t> expected_cbor(depth, 0x81);
|
||||
@@ -505,10 +505,22 @@ TEST_CASE("issue #5392 - binary writers on deeply nested values")
|
||||
const json discarded_leaf(json::value_t::discarded);
|
||||
const json deep_discarded = nested_array(depth, discarded_leaf);
|
||||
|
||||
CHECK_THROWS_WITH_AS(json::to_cbor(deep_discarded), "[json.exception.type_error.321] cannot serialize discarded value to CBOR", json::type_error);
|
||||
CHECK_THROWS_WITH_AS(json::to_msgpack(deep_discarded), "[json.exception.type_error.321] cannot serialize discarded value to MessagePack", json::type_error);
|
||||
CHECK_THROWS_WITH_AS(json::to_ubjson(deep_discarded), "[json.exception.type_error.321] cannot serialize discarded value to UBJSON", json::type_error);
|
||||
CHECK_THROWS_WITH_AS(json::to_bjdata(deep_discarded), "[json.exception.type_error.321] cannot serialize discarded value to BJData", json::type_error);
|
||||
// with diagnostics, the message names the path to the discarded leaf
|
||||
#if JSON_DIAGNOSTICS
|
||||
std::string path;
|
||||
for (std::size_t i = 0; i < depth; ++i)
|
||||
{
|
||||
path += "/0";
|
||||
}
|
||||
const std::string prefix = "[json.exception.type_error.321] (" + path + ") ";
|
||||
#else
|
||||
const std::string prefix = "[json.exception.type_error.321] ";
|
||||
#endif
|
||||
|
||||
CHECK_THROWS_WITH_AS(json::to_cbor(deep_discarded), (prefix + "cannot serialize discarded value to CBOR").c_str(), json::type_error);
|
||||
CHECK_THROWS_WITH_AS(json::to_msgpack(deep_discarded), (prefix + "cannot serialize discarded value to MessagePack").c_str(), json::type_error);
|
||||
CHECK_THROWS_WITH_AS(json::to_ubjson(deep_discarded), (prefix + "cannot serialize discarded value to UBJSON").c_str(), json::type_error);
|
||||
CHECK_THROWS_WITH_AS(json::to_bjdata(deep_discarded), (prefix + "cannot serialize discarded value to BJData").c_str(), json::type_error);
|
||||
}
|
||||
|
||||
SECTION("does not overflow the C++ stack")
|
||||
|
||||
@@ -172,7 +172,7 @@ TEST_CASE("locale-dependent test (LC_NUMERIC=de_DE)")
|
||||
// a floating-point type that is not a float or a double is written
|
||||
// with snprintf, whose locale-specific decimal point and thousands
|
||||
// separator are undone afterwards
|
||||
using long_double_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, long double>;
|
||||
using long_double_json = nlohmann::json::with_float_t<long double>;
|
||||
CHECK(long_double_json(12345.5L).dump() == "12345.5");
|
||||
CHECK(long_double_json(1.0L).dump() == "1.0");
|
||||
CHECK(long_double_json(-0.25L).dump() == "-0.25");
|
||||
@@ -260,10 +260,11 @@ struct LocaleSwitchingSax final: public nlohmann::json_sax<json>
|
||||
|
||||
TEST_CASE("locale changes between lexer construction and number conversion (#5198)")
|
||||
{
|
||||
// The numbers are chosen so that the conversion also takes the strtod
|
||||
// fallback, which honors the locale that is current at conversion time:
|
||||
// too many significant digits for Clinger's fast path, an underflow that
|
||||
// std::from_chars rejects, and a plain value.
|
||||
// float and double are converted without the locale. A long double that
|
||||
// is not binary64 can take the strtold fallback, which honors the locale
|
||||
// that is current at conversion time. The numbers are chosen so that it
|
||||
// does: too many significant digits for Clinger's fast path, an underflow
|
||||
// that std::from_chars rejects, and a plain value.
|
||||
const std::vector<std::string> numbers = {"3.14159265358979323846", "1.5e-400", "12.34", "-0.000123456789012345678"};
|
||||
std::string text = "[";
|
||||
for (const auto& n : numbers)
|
||||
@@ -272,7 +273,7 @@ TEST_CASE("locale changes between lexer construction and number conversion (#519
|
||||
}
|
||||
text += "]";
|
||||
|
||||
using long_double_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, long double>;
|
||||
using long_double_json = nlohmann::json::with_float_t<long double>;
|
||||
|
||||
// reference values, parsed without a locale switch
|
||||
REQUIRE(std::setlocale(LC_NUMERIC, "C") != nullptr);
|
||||
@@ -327,7 +328,8 @@ TEST_CASE("locale changes between lexer construction and number conversion (#519
|
||||
}
|
||||
}
|
||||
|
||||
// a long double goes through std::strtold unless std::from_chars supports it
|
||||
// a long double goes through std::strtold unless it is binary64 or
|
||||
// std::from_chars supports it
|
||||
{
|
||||
bool switched = false;
|
||||
const auto cb = [&](int /*depth*/, long_double_json::parse_event_t event, long_double_json& /*parsed*/) noexcept
|
||||
@@ -353,8 +355,15 @@ TEST_CASE("locale with a multi-byte decimal point")
|
||||
{
|
||||
// Some locales use a decimal point that is not a single character, e.g.
|
||||
// U+066B ARABIC DECIMAL SEPARATOR (two bytes in UTF-8). It cannot be
|
||||
// substituted in place for '.', so the strtod fallback stops early. The
|
||||
// conversion must still terminate rather than retry forever.
|
||||
// substituted in place for '.', so the strtold fallback (only for long
|
||||
// double formats other than binary64) converts a copy of the token with
|
||||
// the whole decimal point instead (#5660). The values must be those of the
|
||||
// "C" locale.
|
||||
using long_double_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, long double>;
|
||||
const char* const long_double_numbers = "[3.14159265358979323846, 1.5e-400, -0.000123456789012345678]";
|
||||
REQUIRE(std::setlocale(LC_NUMERIC, "C") != nullptr);
|
||||
const long_double_json expected_long_double = long_double_json::parse(long_double_numbers);
|
||||
|
||||
const std::array<const char*, 6> names = {{"ar_EG.UTF-8", "ar_SA.UTF-8", "fa_IR.UTF-8", "ps_AF.UTF-8", "ar_EG", "fa_IR"}};
|
||||
bool tested = false;
|
||||
for (const char* name : names)
|
||||
@@ -372,12 +381,20 @@ TEST_CASE("locale with a multi-byte decimal point")
|
||||
tested = true;
|
||||
|
||||
// too many significant digits for Clinger's fast path, and an underflow
|
||||
// that std::from_chars rejects: both reach the strtod fallback
|
||||
// that std::from_chars rejects: double does not depend on the locale
|
||||
json j;
|
||||
CHECK_NOTHROW(j = json::parse("[3.14159265358979323846, 1.5e-400, -0.000123456789012345678]"));
|
||||
CHECK(j.is_array());
|
||||
CHECK(j[0] == 3.14159265358979323846);
|
||||
CHECK(j[1] == 0.0);
|
||||
CHECK(j[2] == -0.000123456789012345678);
|
||||
CHECK(json::accept("3.14159265358979323846"));
|
||||
|
||||
// a long double that reaches the strtold fallback is not truncated
|
||||
long_double_json ld;
|
||||
CHECK_NOTHROW(ld = long_double_json::parse(long_double_numbers));
|
||||
CHECK(ld == expected_long_double);
|
||||
|
||||
// a value the locale-independent paths convert is not affected
|
||||
CHECK(json::parse("12.5") == 12.5);
|
||||
}
|
||||
@@ -432,7 +449,7 @@ TEST_CASE("locale changes during a single dump() (#5709 item 3)")
|
||||
// long double on 64-bit Arm, where it is IEEE-754 double) takes the
|
||||
// locale-independent to_chars() path instead, and this test is a no-op
|
||||
// there.
|
||||
using long_double_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, long double>;
|
||||
using long_double_json = nlohmann::json::with_float_t<long double>;
|
||||
using ld_limits = std::numeric_limits<long_double_json::number_float_t>;
|
||||
const bool is_ieee_single_or_double =
|
||||
(ld_limits::is_iec559 && ld_limits::digits == 24 && ld_limits::max_exponent == 128) ||
|
||||
|
||||
@@ -2201,10 +2201,7 @@ struct huge_array : std::vector<T, A>
|
||||
}
|
||||
};
|
||||
|
||||
using huge_array_json = nlohmann::basic_json <
|
||||
std::map, huge_array, std::string, bool, std::int64_t, std::uint64_t,
|
||||
double, std::allocator, nlohmann::adl_serializer,
|
||||
std::vector<std::uint8_t>, void >;
|
||||
using huge_array_json = nlohmann::json::with_array_t<huge_array>;
|
||||
|
||||
TEST_CASE("MessagePack Size above uint32 for array")
|
||||
{
|
||||
@@ -2249,18 +2246,7 @@ template<typename K, typename V,
|
||||
}
|
||||
};
|
||||
|
||||
using huge_object_json = nlohmann::basic_json <
|
||||
huge_map,
|
||||
std::vector,
|
||||
std::string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
std::allocator,
|
||||
nlohmann::adl_serializer,
|
||||
std::vector<std::uint8_t>,
|
||||
void >;
|
||||
using huge_object_json = nlohmann::json::with_object_t<huge_map>;
|
||||
|
||||
TEST_CASE("MessagePack Size above uint32 for object")
|
||||
{
|
||||
@@ -2295,18 +2281,7 @@ struct huge_string : std::string
|
||||
}
|
||||
};
|
||||
|
||||
using huge_string_json = nlohmann::basic_json <
|
||||
std::map,
|
||||
std::vector,
|
||||
huge_string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
std::allocator,
|
||||
nlohmann::adl_serializer,
|
||||
std::vector<std::uint8_t>,
|
||||
void >;
|
||||
using huge_string_json = nlohmann::json::with_string_t<huge_string>;
|
||||
|
||||
TEST_CASE("MessagePack Size above uint32 for string")
|
||||
{
|
||||
@@ -2329,18 +2304,7 @@ struct huge_binary : std::vector<std::uint8_t>
|
||||
}
|
||||
};
|
||||
|
||||
using huge_binary_json = nlohmann::basic_json <
|
||||
std::map,
|
||||
std::vector,
|
||||
std::string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
std::allocator,
|
||||
nlohmann::adl_serializer,
|
||||
huge_binary,
|
||||
void >;
|
||||
using huge_binary_json = nlohmann::json::with_binary_t<huge_binary>;
|
||||
|
||||
TEST_CASE("MessagePack Size above uint32 for binary")
|
||||
{
|
||||
@@ -2390,14 +2354,10 @@ class beyond_uint32_string_t : public std::string
|
||||
}
|
||||
};
|
||||
|
||||
using beyond_uint32_string_json = nlohmann::basic_json <
|
||||
std::map, std::vector, beyond_uint32_string_t, bool, std::int64_t, std::uint64_t,
|
||||
double, std::allocator, nlohmann::adl_serializer, std::vector<std::uint8_t>, void >;
|
||||
using beyond_uint32_string_json = nlohmann::json::with_string_t<beyond_uint32_string_t>;
|
||||
#endif
|
||||
|
||||
using beyond_uint32_binary_json = nlohmann::basic_json <
|
||||
std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t,
|
||||
double, std::allocator, nlohmann::adl_serializer, beyond_uint32_binary_t, void >;
|
||||
using beyond_uint32_binary_json = nlohmann::json::with_binary_t<beyond_uint32_binary_t>;
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("MessagePack lengths beyond UINT32_MAX cannot be serialized")
|
||||
@@ -2432,8 +2392,8 @@ TEST_CASE("MessagePack numbers use the active union member (see #5644)")
|
||||
// used to read the union member that was not the active one, writing
|
||||
// wrong bytes for some values; std::int64_t/std::uint64_t (the default
|
||||
// types, where both members have the same width) were not affected
|
||||
using int32_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int32_t, std::uint64_t, double>;
|
||||
using int16_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int16_t, std::uint64_t, double>;
|
||||
using int32_json = nlohmann::json::with_integers_t<std::int32_t, std::uint64_t>;
|
||||
using int16_json = nlohmann::json::with_integers_t<std::int16_t, std::uint64_t>;
|
||||
|
||||
SECTION("number_integer_t = std::int32_t")
|
||||
{
|
||||
|
||||
@@ -217,16 +217,7 @@ void int_to_string(alt_string& target, std::size_t value)
|
||||
target = std::to_string(value).c_str();
|
||||
}
|
||||
|
||||
using alt_json = nlohmann::basic_json <
|
||||
std::map,
|
||||
std::vector,
|
||||
alt_string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
std::allocator,
|
||||
nlohmann::adl_serializer >;
|
||||
using alt_json = nlohmann::json::with_string_t<alt_string>;
|
||||
|
||||
bool operator<(const char* op1, const alt_string& op2) noexcept
|
||||
{
|
||||
|
||||
@@ -39,7 +39,7 @@ using nlohmann::json;
|
||||
|
||||
template<class K, class V, class dummy_compare, class A>
|
||||
using my_workaround_fifo_map = nlohmann::fifo_map<K, V, nlohmann::fifo_map_compare<K>, A>;
|
||||
using my_json = nlohmann::basic_json<my_workaround_fifo_map>;
|
||||
using my_json = nlohmann::json::with_object_t<my_workaround_fifo_map>;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// for #977
|
||||
@@ -86,8 +86,7 @@ struct foo_serializer < T, typename std::enable_if < !std::is_same<foo, T>::valu
|
||||
};
|
||||
} // namespace ns
|
||||
|
||||
using foo_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t,
|
||||
std::uint64_t, double, std::allocator, ns::foo_serializer, std::vector<std::uint8_t>>;
|
||||
using foo_json = nlohmann::json::with_json_serializer_t<ns::foo_serializer>;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// for #805
|
||||
@@ -254,7 +253,7 @@ TEST_CASE("regression tests 1")
|
||||
{
|
||||
// create JSON class with nonstandard integer number type
|
||||
using custom_json =
|
||||
nlohmann::basic_json<std::map, std::vector, std::string, bool, int32_t, uint32_t, float>;
|
||||
nlohmann::json::with_integers_t<std::int32_t, std::uint32_t>::with_float_t<float>;
|
||||
custom_json j;
|
||||
j["int_1"] = 1;
|
||||
CHECK(j["int_1"] == 1);
|
||||
@@ -470,18 +469,17 @@ TEST_CASE("regression tests 1")
|
||||
// create JSON class with nonstandard float number type
|
||||
|
||||
// float
|
||||
nlohmann::basic_json<std::map, std::vector, std::string, bool, int32_t, uint32_t, float> const j_float =
|
||||
nlohmann::json::with_integers_t<std::int32_t, std::uint32_t>::with_float_t<float> const j_float =
|
||||
1.23e25f;
|
||||
CHECK(j_float.get<float>() == 1.23e25f);
|
||||
|
||||
// double
|
||||
nlohmann::basic_json<std::map, std::vector, std::string, bool, int64_t, uint64_t, double> const j_double =
|
||||
nlohmann::json const j_double =
|
||||
1.23e35;
|
||||
CHECK(j_double.get<double>() == 1.23e35);
|
||||
|
||||
// long double
|
||||
nlohmann::basic_json<std::map, std::vector, std::string, bool, int64_t, uint64_t, long double>
|
||||
const j_long_double = 1.23e45L;
|
||||
nlohmann::json::with_float_t<long double> const j_long_double = 1.23e45L;
|
||||
CHECK(j_long_double.get<long double>() == 1.23e45L);
|
||||
}
|
||||
|
||||
|
||||
@@ -64,18 +64,7 @@ using ordered_json = nlohmann::ordered_json;
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// for #4804
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
using json_4804 = nlohmann::basic_json<std::map, // ObjectType
|
||||
std::vector, // ArrayType
|
||||
std::string, // StringType
|
||||
bool, // BooleanType
|
||||
std::int64_t, // NumberIntegerType
|
||||
std::uint64_t, // NumberUnsignedType
|
||||
double, // NumberFloatType
|
||||
std::allocator, // AllocatorType
|
||||
nlohmann::adl_serializer, // JSONSerializer
|
||||
std::vector<std::byte>, // BinaryType
|
||||
void // CustomBaseClass
|
||||
>;
|
||||
using json_4804 = nlohmann::json::with_binary_t<std::vector<std::byte>>;
|
||||
#endif
|
||||
|
||||
#ifdef JSON_HAS_CPP_20
|
||||
@@ -107,7 +96,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
|
||||
// for #1021
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
using float_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, float>;
|
||||
using float_json = nlohmann::json::with_float_t<float>;
|
||||
|
||||
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)
|
||||
namespace
|
||||
@@ -155,10 +144,8 @@ struct failing_allocator : std::allocator<T>
|
||||
};
|
||||
};
|
||||
|
||||
using failing_json = nlohmann::basic_json<std::map, std::vector, std::string, bool,
|
||||
std::int64_t, std::uint64_t, double, failing_allocator>;
|
||||
using failing_ordered_json = nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::string, bool,
|
||||
std::int64_t, std::uint64_t, double, failing_allocator>;
|
||||
using failing_json = nlohmann::json::with_allocator_t<failing_allocator>;
|
||||
using failing_ordered_json = nlohmann::ordered_json::with_allocator_t<failing_allocator>;
|
||||
|
||||
// builds `depth` levels of nesting around a scalar, iteratively (never
|
||||
// recursing: each wrap only moves the previous, already-built value, which
|
||||
|
||||
@@ -62,18 +62,7 @@ using ordered_json = nlohmann::ordered_json;
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// for #4804
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
using json_4804 = nlohmann::basic_json<std::map, // ObjectType
|
||||
std::vector, // ArrayType
|
||||
std::string, // StringType
|
||||
bool, // BooleanType
|
||||
std::int64_t, // NumberIntegerType
|
||||
std::uint64_t, // NumberUnsignedType
|
||||
double, // NumberFloatType
|
||||
std::allocator, // AllocatorType
|
||||
nlohmann::adl_serializer, // JSONSerializer
|
||||
std::vector<std::byte>, // BinaryType
|
||||
void // CustomBaseClass
|
||||
>;
|
||||
using json_4804 = nlohmann::json::with_binary_t<std::vector<std::byte>>;
|
||||
#endif
|
||||
|
||||
#ifdef JSON_HAS_CPP_20
|
||||
@@ -930,7 +919,7 @@ TEST_CASE("regression test #5476 - array type without reserve()")
|
||||
{
|
||||
// the capacity reserved for definite-length arrays must not require the
|
||||
// array type to have a reserve() member function
|
||||
using deque_json = nlohmann::basic_json<std::map, std::deque>;
|
||||
using deque_json = nlohmann::json::with_array_t<std::deque>;
|
||||
|
||||
SECTION("std::deque")
|
||||
{
|
||||
|
||||
@@ -367,8 +367,7 @@ TEST_CASE("dump for basic_json with long double number_float_t")
|
||||
// serializer::dump_float(x, std::false_type). That branch must use the
|
||||
// "%.*Lg" format specifier; using "%.*g" with a long double argument is
|
||||
// undefined behavior and corrupts the output.
|
||||
using long_double_json = nlohmann::basic_json<std::map, std::vector, std::string,
|
||||
bool, std::int64_t, std::uint64_t, long double>;
|
||||
using long_double_json = nlohmann::json::with_float_t<long double>;
|
||||
|
||||
SECTION("round-trip dump/parse")
|
||||
{
|
||||
|
||||
+343
-1
@@ -15,6 +15,24 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::detail::dtoa_impl::reinterpret_bits;
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <locale>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#if defined(JSON_HAS_CPP_17)
|
||||
#include <charconv>
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
float make_float(uint32_t sign_bit, uint32_t biased_exponent, uint32_t significand)
|
||||
@@ -450,7 +468,7 @@ TEST_CASE("formatting")
|
||||
check_double( 1.2345e+18, "1.2345e+18" ); // 1.2345e+18 1.2345e+18 1.2345e18
|
||||
check_double( 1.2345e+19, "1.2345e+19" ); // 1.2345e+19 1.2345e+19 1.2345e19
|
||||
check_double( 1.2345e+20, "1.2345e+20" ); // 1.2345e+20 1.2345e+20 1.2345e20
|
||||
check_double( 1.2345e+21, "1.2344999999999999e+21" ); // 1.2345e+21 1.2344999999999999e+21 1.2345e21
|
||||
check_double( 1.2345e+21, "1.2345e+21" ); // 1.2345e+21 1.2344999999999999e+21 1.2345e21
|
||||
check_double( 1.2345e+22, "1.2345e+22" ); // 1.2345e+22 1.2345e+22 1.2345e22
|
||||
}
|
||||
|
||||
@@ -514,3 +532,327 @@ TEST_CASE("formatting")
|
||||
check_integer(1000000000000000000LL, "1000000000000000000");
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// a small unsigned big integer (32-bit limbs, least significant first), to
|
||||
// recompute the powers of ten of the shortest double conversion
|
||||
using big = std::vector<std::uint32_t>;
|
||||
|
||||
void big_mul_small(big& x, std::uint32_t m)
|
||||
{
|
||||
std::uint64_t carry = 0;
|
||||
for (auto& limb : x)
|
||||
{
|
||||
const std::uint64_t v = (static_cast<std::uint64_t>(limb) * m) + carry;
|
||||
limb = static_cast<std::uint32_t>(v);
|
||||
carry = v >> 32u;
|
||||
}
|
||||
if (carry != 0)
|
||||
{
|
||||
x.push_back(static_cast<std::uint32_t>(carry));
|
||||
}
|
||||
}
|
||||
|
||||
void big_div_small(big& x, std::uint32_t d)
|
||||
{
|
||||
std::uint64_t rest = 0;
|
||||
for (std::size_t i = x.size(); i-- > 0;)
|
||||
{
|
||||
const std::uint64_t v = (rest << 32u) | x[i];
|
||||
x[i] = static_cast<std::uint32_t>(v / d);
|
||||
rest = v % d;
|
||||
}
|
||||
while (!x.empty() && x.back() == 0)
|
||||
{
|
||||
x.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t big_bit_length(const big& x)
|
||||
{
|
||||
std::size_t n = 32 * x.size();
|
||||
for (std::uint32_t top = x.back(); (top & 0x80000000u) == 0; top <<= 1u)
|
||||
{
|
||||
--n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
bool big_bit(const big& x, std::size_t i)
|
||||
{
|
||||
return ((x[i / 32] >> (i % 32)) & 1u) != 0;
|
||||
}
|
||||
|
||||
/// the 128 most significant bits of x (floor), shifted left if x has fewer bits
|
||||
std::pair<std::uint64_t, std::uint64_t> big_top128(const big& x)
|
||||
{
|
||||
const std::size_t n = big_bit_length(x);
|
||||
std::uint64_t high = 0;
|
||||
std::uint64_t low = 0;
|
||||
for (std::size_t k = 0; k < 128; ++k)
|
||||
{
|
||||
const bool bit = k < n && big_bit(x, n - 1 - k);
|
||||
if (k < 64)
|
||||
{
|
||||
high = (high << 1u) | (bit ? 1u : 0u);
|
||||
}
|
||||
else
|
||||
{
|
||||
low = (low << 1u) | (bit ? 1u : 0u);
|
||||
}
|
||||
}
|
||||
return {high, low};
|
||||
}
|
||||
|
||||
/// the digits (without trailing zeros) and the decimal exponent of a
|
||||
/// representation "[-]d[.ddd][e[+-]x]"
|
||||
std::pair<std::string, int> digits_and_exponent(const std::string& s)
|
||||
{
|
||||
std::string digits;
|
||||
int point = -1;
|
||||
int exponent = 0;
|
||||
for (std::size_t i = 0; i < s.size(); ++i)
|
||||
{
|
||||
const char c = s[i];
|
||||
if (c >= '0' && c <= '9')
|
||||
{
|
||||
digits += c;
|
||||
}
|
||||
else if (c == '.')
|
||||
{
|
||||
point = static_cast<int>(digits.size());
|
||||
}
|
||||
else if (c == 'e' || c == 'E')
|
||||
{
|
||||
exponent = std::stoi(s.substr(i + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
int e = exponent + (point < 0 ? static_cast<int>(digits.size()) : point) - static_cast<int>(digits.size());
|
||||
const std::size_t first = digits.find_first_not_of('0');
|
||||
digits = first == std::string::npos ? "0" : digits.substr(first);
|
||||
while (digits.size() > 1 && digits.back() == '0')
|
||||
{
|
||||
digits.pop_back();
|
||||
++e;
|
||||
}
|
||||
return {digits, e};
|
||||
}
|
||||
|
||||
/// the correctly rounded double of a decimal text
|
||||
/// (not std::strtod: the C runtimes of some platforms, e.g. MinGW's, round
|
||||
/// some 16 and 17 digit inputs wrongly)
|
||||
double parse_double(const std::string& text)
|
||||
{
|
||||
const nlohmann::json j = nlohmann::json::parse(text, nullptr, false);
|
||||
// (a discarded value: out of range, as strtod's HUGE_VAL)
|
||||
return j.is_discarded() ? std::numeric_limits<double>::infinity() : j.get<double>();
|
||||
}
|
||||
|
||||
/// whether the decimal digits * 10^e reads back as v
|
||||
bool reads_back(const std::string& digits, int e, double v)
|
||||
{
|
||||
const std::string text = digits + "e" + std::to_string(e);
|
||||
// (compared bit for bit: v is positive and finite, and -Wfloat-equal)
|
||||
return reinterpret_bits<std::uint64_t>(parse_double(text)) == reinterpret_bits<std::uint64_t>(v);
|
||||
}
|
||||
|
||||
/// Check the representation of a positive finite double: it reads back as
|
||||
/// the same value, and no representation with fewer digits does.
|
||||
void check_shortest(double v)
|
||||
{
|
||||
std::array<char, 33> buf{};
|
||||
char* end = nlohmann::detail::to_chars(buf.data(), buf.data() + 32, v);
|
||||
const std::string text(buf.data(), end);
|
||||
CAPTURE(text)
|
||||
CHECK(parse_double(text) == v);
|
||||
// the layout is that of format_buffer() for the digits of Zmij
|
||||
const auto sd = nlohmann::detail::zmij::to_shortest(reinterpret_bits<std::uint64_t>(v));
|
||||
const std::uint64_t significand = sd.has_digit ? (sd.integral * 10) + sd.digit : sd.integral;
|
||||
int exponent = sd.has_digit ? sd.exponent : sd.exponent + 1;
|
||||
std::string significand_digits = std::to_string(significand);
|
||||
while (significand_digits.size() > 1 && significand_digits.back() == '0')
|
||||
{
|
||||
significand_digits.pop_back();
|
||||
++exponent;
|
||||
}
|
||||
std::array<char, 64> reference{};
|
||||
std::copy(significand_digits.begin(), significand_digits.end(), reference.begin());
|
||||
const char* const reference_end = nlohmann::detail::dtoa_impl::format_buffer(reference.data(), static_cast<int>(significand_digits.size()), exponent, -4, 15);
|
||||
CHECK(text == std::string(reference.data(), static_cast<std::size_t>(reference_end - reference.data())));
|
||||
// and write_positive() is what to_chars() calls
|
||||
std::array<char, 64> positive{};
|
||||
const char* const positive_end = nlohmann::detail::dtoa_impl::write_positive(positive.data(), positive.data() + positive.size(), v);
|
||||
CHECK(text == std::string(positive.data(), static_cast<std::size_t>(positive_end - positive.data())));
|
||||
const auto de = digits_and_exponent(text);
|
||||
const std::string& digits = de.first;
|
||||
if (digits.size() > 1)
|
||||
{
|
||||
// the decimals of one digit fewer next to the value
|
||||
// (a stream rather than snprintf("%.*e"), whose output GCC cannot bound)
|
||||
std::ostringstream shorter;
|
||||
shorter.imbue(std::locale::classic());
|
||||
shorter << std::scientific << std::setprecision(static_cast<int>(digits.size()) - 2) << v;
|
||||
const auto near = digits_and_exponent(shorter.str());
|
||||
// as an integer with digits.size() - 1 digits
|
||||
std::string m = near.first;
|
||||
int e = near.second;
|
||||
while (m.size() < digits.size() - 1)
|
||||
{
|
||||
m += '0';
|
||||
--e;
|
||||
}
|
||||
const std::uint64_t mid = std::stoull(m);
|
||||
for (const std::uint64_t candidate :
|
||||
{
|
||||
mid - 1, mid, mid + 1
|
||||
})
|
||||
{
|
||||
CAPTURE(candidate)
|
||||
CHECK(!reads_back(std::to_string(candidate), e, v));
|
||||
}
|
||||
}
|
||||
// (icpc with libstdc++ 11 defines __cpp_lib_to_chars, but has no floating-point std::to_chars)
|
||||
#if defined(JSON_HAS_CPP_17) && defined(__cpp_lib_to_chars) && !defined(__INTEL_COMPILER)
|
||||
// the closest of the shortest representations, as std::to_chars finds it
|
||||
std::array<char, 64> std_text{};
|
||||
const auto r = std::to_chars(std_text.data(), std_text.data() + std_text.size(), v, std::chars_format::scientific);
|
||||
CHECK(digits_and_exponent(std::string(std_text.data(), r.ptr)) == de);
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("shortest digits of doubles")
|
||||
{
|
||||
SECTION("powers of ten")
|
||||
{
|
||||
// the 128-bit significands of 10^k, rounded down, recomputed
|
||||
for (int k = -342; k <= 341; ++k)
|
||||
{
|
||||
CAPTURE(k)
|
||||
big x{1};
|
||||
if (k >= 0)
|
||||
{
|
||||
for (int i = 0; i < k; ++i)
|
||||
{
|
||||
big_mul_small(x, 10);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// floor(2^b / 10^-k) for a b that leaves more than 128 bits
|
||||
const int b = 128 + 64 + (4 * -k);
|
||||
x.assign(static_cast<std::size_t>(b / 32) + 1, 0);
|
||||
x.back() = 1u << (b % 32);
|
||||
for (int i = 0; i < -k; ++i)
|
||||
{
|
||||
big_div_small(x, 10);
|
||||
}
|
||||
}
|
||||
const auto expected = big_top128(x);
|
||||
const auto actual = nlohmann::detail::zmij::pow10(k);
|
||||
CHECK(actual.high == expected.first);
|
||||
CHECK(actual.low == expected.second);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("boundary values")
|
||||
{
|
||||
for (const double v :
|
||||
{
|
||||
std::numeric_limits<double>::min(), std::numeric_limits<double>::max(), std::numeric_limits<double>::denorm_min(),
|
||||
std::nextafter(std::numeric_limits<double>::min(), 0.0), 1.0, 2.0, 0.1, 0.3, 1e21, 1e22, 1e23, 5e-324, 9007199254740993.0,
|
||||
1.2345e+21, 2.2250738585072014e-308, 1.7976931348623157e308, 4.9406564584124654e-324, 123456789012345680.0
|
||||
})
|
||||
{
|
||||
check_shortest(v);
|
||||
}
|
||||
// all powers of two (their rounding interval is narrower below)
|
||||
for (int e = -1074; e <= 1023; ++e)
|
||||
{
|
||||
check_shortest(std::ldexp(1.0, e));
|
||||
}
|
||||
// powers of ten and their neighbors
|
||||
for (int e = -323; e <= 308; ++e)
|
||||
{
|
||||
const double p = std::strtod(("1e" + std::to_string(e)).c_str(), nullptr);
|
||||
check_shortest(p);
|
||||
check_shortest(std::nextafter(p, 0.0));
|
||||
check_shortest(std::nextafter(p, std::numeric_limits<double>::infinity()));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("random doubles")
|
||||
{
|
||||
std::mt19937_64 rng(5295); // NOLINT(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed): reproducible
|
||||
for (int i = 0; i < 100000; ++i)
|
||||
{
|
||||
const std::uint64_t bits = rng() & 0x7FFFFFFFFFFFFFFFu;
|
||||
const auto v = reinterpret_bits<double>(bits);
|
||||
if (std::isfinite(v) && bits != 0)
|
||||
{
|
||||
check_shortest(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("choice of the conversion")
|
||||
{
|
||||
using nlohmann::detail::dtoa_impl::is_binary64;
|
||||
|
||||
SECTION("by the format of the type")
|
||||
{
|
||||
// Zmij needs binary64 numbers; everything else uses Grisu2
|
||||
static_assert(!is_binary64<float>::value, "float is not binary64");
|
||||
static_assert(is_binary64<double>::value == (std::numeric_limits<double>::is_iec559 && std::numeric_limits<double>::digits == 53),
|
||||
"double is binary64 where it is IEEE 754 with 53 digits");
|
||||
static_assert(!is_binary64<int>::value, "integers are not binary64");
|
||||
static_assert(is_binary64<long double>::value == (std::numeric_limits<long double>::is_iec559 && std::numeric_limits<long double>::digits == 53 && sizeof(long double) == 8),
|
||||
"long double is binary64 where it has the format of a double");
|
||||
CHECK(!is_binary64<float>::value);
|
||||
CHECK(is_binary64<double>::value);
|
||||
}
|
||||
|
||||
SECTION("float: Grisu2, double: Zmij")
|
||||
{
|
||||
// 5.3165205877497296e+16 is one of the doubles for which Grisu2 does not find the shortest digits
|
||||
constexpr double value = 5.3165205877497296e+16;
|
||||
std::array<char, 64> buf{};
|
||||
const char* const last = buf.data() + buf.size();
|
||||
|
||||
char* end = nlohmann::detail::dtoa_impl::write_positive(buf.data(), last, value);
|
||||
CHECK(std::string(buf.data(), end) == "5.31652058774973e+16");
|
||||
end = nlohmann::detail::dtoa_impl::write_positive_grisu2(buf.data(), last, value);
|
||||
CHECK(std::string(buf.data(), end) == "5.3165205877497296e+16");
|
||||
|
||||
constexpr float f = 1.1754944e-38f;
|
||||
end = nlohmann::detail::dtoa_impl::write_positive(buf.data(), last, f);
|
||||
const std::string dispatched(buf.data(), end);
|
||||
end = nlohmann::detail::dtoa_impl::write_positive_grisu2(buf.data(), last, f);
|
||||
CHECK(dispatched == std::string(buf.data(), end));
|
||||
}
|
||||
|
||||
SECTION("long double with the format of a double: Zmij")
|
||||
{
|
||||
// (on platforms where long double is wider, Grisu2 does not apply either: the snprintf fallback does)
|
||||
// (constant on a given platform)
|
||||
DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_MSVC_SUPPRESS_WARNING(4127)
|
||||
if (std::numeric_limits<long double>::digits == 53 && std::numeric_limits<long double>::is_iec559)
|
||||
{
|
||||
using long_double_json = nlohmann::json::with_float_t<long double>;
|
||||
for (const double d :
|
||||
{
|
||||
5.3165205877497296e+16, 1.0, 0.1, 123456.789, 2.2250738585072014e-308, 1.7976931348623157e+308, -5.3165205877497296e+16
|
||||
})
|
||||
{
|
||||
CAPTURE(d)
|
||||
CHECK(long_double_json(static_cast<long double>(d)).dump() == nlohmann::json(d).dump());
|
||||
}
|
||||
CHECK(long_double_json(5.3165205877497296e+16L).dump() == "5.31652058774973e+16");
|
||||
}
|
||||
DOCTEST_MSVC_SUPPRESS_WARNING_POP
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user