mirror of
https://github.com/nlohmann/json.git
synced 2026-09-29 11:10:30 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
056d187986 | ||
|
|
90078597c7 | ||
|
|
fa28fff7d6 | ||
|
|
e9c84befa1 | ||
|
|
8a26f2dc8f | ||
|
|
2c108d0b56 |
@@ -67,7 +67,6 @@ jobs:
|
|||||||
|
|
||||||
python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json.json -s .
|
python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json.json -s .
|
||||||
python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json_fwd.json -s .
|
python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json_fwd.json -s .
|
||||||
cp include/nlohmann/json_literals.hpp $INCLUDE_DIR/json_literals.hpp
|
|
||||||
|
|
||||||
# the header list of the Bazel "json" target must match the files in include/
|
# the header list of the Bazel "json" target must match the files in include/
|
||||||
cmake -P cmake/scripts/gen_bazel_build_file.cmake
|
cmake -P cmake/scripts/gen_bazel_build_file.cmake
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ cc_library(
|
|||||||
"include/nlohmann/detail/value_t.hpp",
|
"include/nlohmann/detail/value_t.hpp",
|
||||||
"include/nlohmann/json.hpp",
|
"include/nlohmann/json.hpp",
|
||||||
"include/nlohmann/json_fwd.hpp",
|
"include/nlohmann/json_fwd.hpp",
|
||||||
"include/nlohmann/json_literals.hpp",
|
|
||||||
"include/nlohmann/ordered_map.hpp",
|
"include/nlohmann/ordered_map.hpp",
|
||||||
"include/nlohmann/thirdparty/hedley/hedley.hpp",
|
"include/nlohmann/thirdparty/hedley/hedley.hpp",
|
||||||
"include/nlohmann/thirdparty/hedley/hedley_undef.hpp",
|
"include/nlohmann/thirdparty/hedley/hedley_undef.hpp",
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ TESTS_SRCS=$(shell find tests -type f \( -name '*.hpp' -o -name '*.cpp' -o -name
|
|||||||
# the single headers (amalgamated from the source files)
|
# the single headers (amalgamated from the source files)
|
||||||
AMALGAMATED_FILE=single_include/nlohmann/json.hpp
|
AMALGAMATED_FILE=single_include/nlohmann/json.hpp
|
||||||
AMALGAMATED_FWD_FILE=single_include/nlohmann/json_fwd.hpp
|
AMALGAMATED_FWD_FILE=single_include/nlohmann/json_fwd.hpp
|
||||||
# json_literals.hpp only includes <nlohmann/json.hpp>, so it is copied verbatim
|
|
||||||
AMALGAMATED_LITERALS_FILE=single_include/nlohmann/json_literals.hpp
|
|
||||||
|
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
@@ -31,7 +29,7 @@ AMALGAMATED_LITERALS_FILE=single_include/nlohmann/json_literals.hpp
|
|||||||
|
|
||||||
# main target
|
# main target
|
||||||
all:
|
all:
|
||||||
@echo "amalgamate - amalgamate files single_include/nlohmann/json{,_fwd,_literals}.hpp from the include/nlohmann sources"
|
@echo "amalgamate - amalgamate files single_include/nlohmann/json{,_fwd}.hpp from the include/nlohmann sources"
|
||||||
@echo "BUILD.bazel - regenerate the Bazel BUILD file from the include/nlohmann sources"
|
@echo "BUILD.bazel - regenerate the Bazel BUILD file from the include/nlohmann sources"
|
||||||
@echo "ChangeLog.md - generate ChangeLog file"
|
@echo "ChangeLog.md - generate ChangeLog file"
|
||||||
@echo "check-amalgamation - check whether sources have been amalgamated and BUILD.bazel is up to date"
|
@echo "check-amalgamation - check whether sources have been amalgamated and BUILD.bazel is up to date"
|
||||||
@@ -156,14 +154,14 @@ install_astyle:
|
|||||||
|
|
||||||
# call the Artistic Style pretty printer on all source files
|
# call the Artistic Style pretty printer on all source files
|
||||||
pretty: install_astyle
|
pretty: install_astyle
|
||||||
$(ASTYLE) --project=tools/astyle/.astylerc $(SRCS) $(TESTS_SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE) docs/mkdocs/docs/examples/*.cpp
|
$(ASTYLE) --project=tools/astyle/.astylerc $(SRCS) $(TESTS_SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) docs/mkdocs/docs/examples/*.cpp
|
||||||
|
|
||||||
# call the Clang-Format on all source files
|
# call the Clang-Format on all source files
|
||||||
pretty_format:
|
pretty_format:
|
||||||
for FILE in $(SRCS) $(TESTS_SRCS) $(AMALGAMATED_FILE) docs/mkdocs/docs/examples/*.cpp; do echo $$FILE; clang-format -i $$FILE; done
|
for FILE in $(SRCS) $(TESTS_SRCS) $(AMALGAMATED_FILE) docs/mkdocs/docs/examples/*.cpp; do echo $$FILE; clang-format -i $$FILE; done
|
||||||
|
|
||||||
# create single header files and pretty print
|
# create single header files and pretty print
|
||||||
amalgamate: $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE)
|
amalgamate: $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE)
|
||||||
$(MAKE) pretty
|
$(MAKE) pretty
|
||||||
|
|
||||||
# call the amalgamation tool for json.hpp
|
# call the amalgamation tool for json.hpp
|
||||||
@@ -174,23 +172,16 @@ $(AMALGAMATED_FILE): $(SRCS)
|
|||||||
$(AMALGAMATED_FWD_FILE): $(SRCS)
|
$(AMALGAMATED_FWD_FILE): $(SRCS)
|
||||||
tools/amalgamate/amalgamate.py -c tools/amalgamate/config_json_fwd.json -s . --verbose=yes
|
tools/amalgamate/amalgamate.py -c tools/amalgamate/config_json_fwd.json -s . --verbose=yes
|
||||||
|
|
||||||
# copy json_literals.hpp
|
|
||||||
$(AMALGAMATED_LITERALS_FILE): include/nlohmann/json_literals.hpp
|
|
||||||
cp include/nlohmann/json_literals.hpp $(AMALGAMATED_LITERALS_FILE)
|
|
||||||
|
|
||||||
# check if file single_include/nlohmann/json.hpp has been amalgamated from the nlohmann sources
|
# check if file single_include/nlohmann/json.hpp has been amalgamated from the nlohmann sources
|
||||||
# Note: this target is called by Travis
|
# Note: this target is called by Travis
|
||||||
check-amalgamation:
|
check-amalgamation:
|
||||||
@mv $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~
|
@mv $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~
|
||||||
@mv $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_FWD_FILE)~
|
@mv $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_FWD_FILE)~
|
||||||
@mv $(AMALGAMATED_LITERALS_FILE) $(AMALGAMATED_LITERALS_FILE)~
|
|
||||||
@$(MAKE) amalgamate
|
@$(MAKE) amalgamate
|
||||||
@diff $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv $(AMALGAMATED_FILE)~ $(AMALGAMATED_FILE) ; false)
|
@diff $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv $(AMALGAMATED_FILE)~ $(AMALGAMATED_FILE) ; false)
|
||||||
@diff $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_FWD_FILE)~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv $(AMALGAMATED_FWD_FILE)~ $(AMALGAMATED_FWD_FILE) ; false)
|
@diff $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_FWD_FILE)~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv $(AMALGAMATED_FWD_FILE)~ $(AMALGAMATED_FWD_FILE) ; false)
|
||||||
@diff $(AMALGAMATED_LITERALS_FILE) $(AMALGAMATED_LITERALS_FILE)~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv $(AMALGAMATED_LITERALS_FILE)~ $(AMALGAMATED_LITERALS_FILE) ; false)
|
|
||||||
@mv $(AMALGAMATED_FILE)~ $(AMALGAMATED_FILE)
|
@mv $(AMALGAMATED_FILE)~ $(AMALGAMATED_FILE)
|
||||||
@mv $(AMALGAMATED_FWD_FILE)~ $(AMALGAMATED_FWD_FILE)
|
@mv $(AMALGAMATED_FWD_FILE)~ $(AMALGAMATED_FWD_FILE)
|
||||||
@mv $(AMALGAMATED_LITERALS_FILE)~ $(AMALGAMATED_LITERALS_FILE)
|
|
||||||
@mv BUILD.bazel BUILD.bazel~
|
@mv BUILD.bazel BUILD.bazel~
|
||||||
@$(MAKE) BUILD.bazel
|
@$(MAKE) BUILD.bazel
|
||||||
@diff BUILD.bazel BUILD.bazel~ || (echo "===================================================================\n BUILD.bazel is out of date! Please run 'make BUILD.bazel'.\n===================================================================" ; mv BUILD.bazel~ BUILD.bazel ; false)
|
@diff BUILD.bazel BUILD.bazel~ || (echo "===================================================================\n BUILD.bazel is out of date! Please run 'make BUILD.bazel'.\n===================================================================" ; mv BUILD.bazel~ BUILD.bazel ; false)
|
||||||
@@ -231,7 +222,7 @@ json.tar.xz:
|
|||||||
# We use `-X` to make the resulting ZIP file reproducible, see
|
# We use `-X` to make the resulting ZIP file reproducible, see
|
||||||
# <https://content.pivotal.io/blog/barriers-to-deterministic-reproducible-zip-files>.
|
# <https://content.pivotal.io/blog/barriers-to-deterministic-reproducible-zip-files>.
|
||||||
include.zip: BUILD.bazel
|
include.zip: BUILD.bazel
|
||||||
zip -9 --recurse-paths -X include.zip $(SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE) BUILD.bazel MODULE.bazel meson.build LICENSE.MIT
|
zip -9 --recurse-paths -X include.zip $(SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) BUILD.bazel MODULE.bazel meson.build LICENSE.MIT
|
||||||
|
|
||||||
# Create the files for a release and add signatures and hashes.
|
# Create the files for a release and add signatures and hashes.
|
||||||
release: include.zip json.tar.xz
|
release: include.zip json.tar.xz
|
||||||
@@ -240,12 +231,10 @@ release: include.zip json.tar.xz
|
|||||||
gpg --armor --detach-sig include.zip
|
gpg --armor --detach-sig include.zip
|
||||||
gpg --armor --detach-sig $(AMALGAMATED_FILE)
|
gpg --armor --detach-sig $(AMALGAMATED_FILE)
|
||||||
gpg --armor --detach-sig $(AMALGAMATED_FWD_FILE)
|
gpg --armor --detach-sig $(AMALGAMATED_FWD_FILE)
|
||||||
gpg --armor --detach-sig $(AMALGAMATED_LITERALS_FILE)
|
|
||||||
gpg --armor --detach-sig json.tar.xz
|
gpg --armor --detach-sig json.tar.xz
|
||||||
cp $(AMALGAMATED_FILE) release_files
|
cp $(AMALGAMATED_FILE) release_files
|
||||||
cp $(AMALGAMATED_FWD_FILE) release_files
|
cp $(AMALGAMATED_FWD_FILE) release_files
|
||||||
cp $(AMALGAMATED_LITERALS_FILE) release_files
|
mv $(AMALGAMATED_FILE).asc $(AMALGAMATED_FWD_FILE).asc json.tar.xz json.tar.xz.asc include.zip include.zip.asc release_files
|
||||||
mv $(AMALGAMATED_FILE).asc $(AMALGAMATED_FWD_FILE).asc $(AMALGAMATED_LITERALS_FILE).asc json.tar.xz json.tar.xz.asc include.zip include.zip.asc release_files
|
|
||||||
cd release_files ; shasum -a 256 json.hpp include.zip json.tar.xz > hashes.txt
|
cd release_files ; shasum -a 256 json.hpp include.zip json.tar.xz > hashes.txt
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
-4
@@ -373,10 +373,9 @@ file(GLOB_RECURSE INDENT_FILES
|
|||||||
set(include_dir ${PROJECT_SOURCE_DIR}/single_include/nlohmann)
|
set(include_dir ${PROJECT_SOURCE_DIR}/single_include/nlohmann)
|
||||||
set(tool_dir ${PROJECT_SOURCE_DIR}/tools/amalgamate)
|
set(tool_dir ${PROJECT_SOURCE_DIR}/tools/amalgamate)
|
||||||
add_custom_target(ci_test_amalgamation
|
add_custom_target(ci_test_amalgamation
|
||||||
COMMAND rm -fr ${include_dir}/json.hpp~ ${include_dir}/json_fwd.hpp~ ${include_dir}/json_literals.hpp~
|
COMMAND rm -fr ${include_dir}/json.hpp~ ${include_dir}/json_fwd.hpp~
|
||||||
COMMAND cp ${include_dir}/json.hpp ${include_dir}/json.hpp~
|
COMMAND cp ${include_dir}/json.hpp ${include_dir}/json.hpp~
|
||||||
COMMAND cp ${include_dir}/json_fwd.hpp ${include_dir}/json_fwd.hpp~
|
COMMAND cp ${include_dir}/json_fwd.hpp ${include_dir}/json_fwd.hpp~
|
||||||
COMMAND cp ${include_dir}/json_literals.hpp ${include_dir}/json_literals.hpp~
|
|
||||||
|
|
||||||
COMMAND ${Python3_EXECUTABLE} -mvenv venv_astyle
|
COMMAND ${Python3_EXECUTABLE} -mvenv venv_astyle
|
||||||
COMMAND venv_astyle/bin/pip3 --quiet install -r ${CMAKE_SOURCE_DIR}/tools/astyle/requirements.txt
|
COMMAND venv_astyle/bin/pip3 --quiet install -r ${CMAKE_SOURCE_DIR}/tools/astyle/requirements.txt
|
||||||
@@ -384,12 +383,10 @@ add_custom_target(ci_test_amalgamation
|
|||||||
|
|
||||||
COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json.json -s .
|
COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json.json -s .
|
||||||
COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json_fwd.json -s .
|
COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json_fwd.json -s .
|
||||||
COMMAND cp ${PROJECT_SOURCE_DIR}/include/nlohmann/json_literals.hpp ${include_dir}/json_literals.hpp
|
|
||||||
COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=none ${include_dir}/json.hpp ${include_dir}/json_fwd.hpp
|
COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=none ${include_dir}/json.hpp ${include_dir}/json_fwd.hpp
|
||||||
|
|
||||||
COMMAND diff ${include_dir}/json.hpp~ ${include_dir}/json.hpp
|
COMMAND diff ${include_dir}/json.hpp~ ${include_dir}/json.hpp
|
||||||
COMMAND diff ${include_dir}/json_fwd.hpp~ ${include_dir}/json_fwd.hpp
|
COMMAND diff ${include_dir}/json_fwd.hpp~ ${include_dir}/json_fwd.hpp
|
||||||
COMMAND diff ${include_dir}/json_literals.hpp~ ${include_dir}/json_literals.hpp
|
|
||||||
|
|
||||||
COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=orig ${INDENT_FILES}
|
COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=orig ${INDENT_FILES}
|
||||||
COMMAND for FILE in `find . -name '*.orig'`\; do false \; done
|
COMMAND for FILE in `find . -name '*.orig'`\; do false \; done
|
||||||
|
|||||||
@@ -80,8 +80,8 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
the end of the file was not reached when `strict` was set to true
|
the end of the file was not reached when `strict` was set to true
|
||||||
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if unsupported features from CBOR were
|
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if unsupported features from CBOR were
|
||||||
used in the given input or if the input is not valid CBOR
|
used in the given input or if the input is not valid CBOR
|
||||||
- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a map key is not a string (keys of other
|
- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a string was expected as a map key,
|
||||||
types are not supported, as JSON object keys are always strings) or a string is malformed
|
but not found
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
|
|||||||
@@ -73,8 +73,8 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
the end of the file was not reached when `strict` was set to true
|
the end of the file was not reached when `strict` was set to true
|
||||||
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if unsupported features from
|
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if unsupported features from
|
||||||
MessagePack were used in the given input or if the input is not valid MessagePack
|
MessagePack were used in the given input or if the input is not valid MessagePack
|
||||||
- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a map key is not a string (keys of other
|
- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a string was expected as a map key,
|
||||||
types are not supported, as JSON object keys are always strings) or a string is malformed
|
but not found
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ header. See also the [macro overview page](../../features/macros.md).
|
|||||||
- [**JSON_HAS_RANGES**](json_has_ranges.md) - control `std::ranges` support
|
- [**JSON_HAS_RANGES**](json_has_ranges.md) - control `std::ranges` support
|
||||||
- [**JSON_HAS_STD_FORMAT**](json_has_std_format.md) - control `std::format`/`std::formatter` support
|
- [**JSON_HAS_STD_FORMAT**](json_has_std_format.md) - control `std::format`/`std::formatter` support
|
||||||
- [**JSON_HAS_THREE_WAY_COMPARISON**](json_has_three_way_comparison.md) - control 3-way comparison support
|
- [**JSON_HAS_THREE_WAY_COMPARISON**](json_has_three_way_comparison.md) - control 3-way comparison support
|
||||||
- [**JSON_NO_AUTOMATIC_UDLS**](json_no_automatic_udls.md) - do not include the user-defined string literals (UDLs) automatically
|
|
||||||
- [**JSON_NO_IO**](json_no_io.md) - switch off functions relying on certain C++ I/O headers
|
- [**JSON_NO_IO**](json_no_io.md) - switch off functions relying on certain C++ I/O headers
|
||||||
- [**JSON_NO_THREAD_LOCAL**](json_no_thread_local.md) - switch off the use of `thread_local` storage
|
- [**JSON_NO_THREAD_LOCAL**](json_no_thread_local.md) - switch off the use of `thread_local` storage
|
||||||
- [**JSON_SKIP_UNSUPPORTED_COMPILER_CHECK**](json_skip_unsupported_compiler_check.md) - do not warn about unsupported compilers
|
- [**JSON_SKIP_UNSUPPORTED_COMPILER_CHECK**](json_skip_unsupported_compiler_check.md) - do not warn about unsupported compilers
|
||||||
|
|||||||
@@ -1,68 +0,0 @@
|
|||||||
# JSON_NO_AUTOMATIC_UDLS
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#define JSON_NO_AUTOMATIC_UDLS
|
|
||||||
```
|
|
||||||
|
|
||||||
When defined, `<nlohmann/json.hpp>` does not include `<nlohmann/json_literals.hpp>`, so the user-defined string
|
|
||||||
literals [`operator""_json`](../operator_literal_json.md) and
|
|
||||||
[`operator""_json_pointer`](../operator_literal_json_pointer.md) are not declared. Include
|
|
||||||
`<nlohmann/json_literals.hpp>` in the files that use them.
|
|
||||||
|
|
||||||
The literals are ordinary inline functions whose bodies call the parser, so every translation unit that includes them
|
|
||||||
instantiates the parser — even if it never parses anything itself. Defining `JSON_NO_AUTOMATIC_UDLS` for a whole project
|
|
||||||
avoids this cost in translation units that do not parse (e.g., ones that only define types and conversions or pass
|
|
||||||
`json` values around) and reduces their compile time.
|
|
||||||
|
|
||||||
## Default definition
|
|
||||||
|
|
||||||
By default, `#!cpp JSON_NO_AUTOMATIC_UDLS` is not defined, and `<nlohmann/json.hpp>` includes
|
|
||||||
`<nlohmann/json_literals.hpp>`.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#undef JSON_NO_AUTOMATIC_UDLS
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
!!! info "Header `<nlohmann/json_literals.hpp>`"
|
|
||||||
|
|
||||||
The header includes `<nlohmann/json.hpp>` itself and places the literals according to
|
|
||||||
[`JSON_USE_GLOBAL_UDLS`](json_use_global_udls.md). It is part of the multi-header sources (`include/nlohmann`)
|
|
||||||
and of the single-header sources (`single_include/nlohmann`), next to `json.hpp`.
|
|
||||||
|
|
||||||
!!! info "C++ modules"
|
|
||||||
|
|
||||||
The `nlohmann.json` [module](../../features/modules.md) always exports the literals, regardless of this macro.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
??? example
|
|
||||||
|
|
||||||
The code below includes the library without the literals and adds them in a single translation unit.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// compiled with -DJSON_NO_AUTOMATIC_UDLS for the whole project
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
// this file uses the literals, so it includes them explicitly
|
|
||||||
#include <nlohmann/json_literals.hpp>
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
auto j = R"({"foo": 42})"_json;
|
|
||||||
return j.at("/foo"_json_pointer) == 42 ? 0 : 1;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Without the include of `<nlohmann/json_literals.hpp>`, the code would fail to compile.
|
|
||||||
|
|
||||||
## See also
|
|
||||||
|
|
||||||
- [`operator""_json`](../operator_literal_json.md)
|
|
||||||
- [`operator""_json_pointer`](../operator_literal_json_pointer.md)
|
|
||||||
- [`JSON_USE_GLOBAL_UDLS`](json_use_global_udls.md) - place user-defined string literals (UDLs) into the global namespace
|
|
||||||
|
|
||||||
## Version history
|
|
||||||
|
|
||||||
- Added in version 3.13.0.
|
|
||||||
@@ -15,7 +15,7 @@ The default value is `1`.
|
|||||||
#define JSON_USE_GLOBAL_UDLS 1
|
#define JSON_USE_GLOBAL_UDLS 1
|
||||||
```
|
```
|
||||||
|
|
||||||
When the macro is not defined, the library behaves as if it were defined to its default value.
|
When the macro is not defined, the library will define it to its default value.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
@@ -32,11 +32,6 @@ When the macro is not defined, the library behaves as if it were defined to its
|
|||||||
[`JSON_GlobalUDLs`](../../integration/cmake.md#json_globaludls) (`ON` by default) which defines
|
[`JSON_GlobalUDLs`](../../integration/cmake.md#json_globaludls) (`ON` by default) which defines
|
||||||
`JSON_USE_GLOBAL_UDLS` accordingly.
|
`JSON_USE_GLOBAL_UDLS` accordingly.
|
||||||
|
|
||||||
!!! info "Leaving out the literals"
|
|
||||||
|
|
||||||
If [`JSON_NO_AUTOMATIC_UDLS`](json_no_automatic_udls.md) is defined, the literals are only declared where
|
|
||||||
`<nlohmann/json_literals.hpp>` is included; this macro then applies to that header.
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example "Example 1: Default behavior"
|
??? example "Example 1: Default behavior"
|
||||||
@@ -97,7 +92,6 @@ When the macro is not defined, the library behaves as if it were defined to its
|
|||||||
|
|
||||||
- [`operator""_json`](../operator_literal_json.md)
|
- [`operator""_json`](../operator_literal_json.md)
|
||||||
- [`operator""_json_pointer`](../operator_literal_json_pointer.md)
|
- [`operator""_json_pointer`](../operator_literal_json_pointer.md)
|
||||||
- [`JSON_NO_AUTOMATIC_UDLS`](json_no_automatic_udls.md) - do not include the user-defined string literals automatically
|
|
||||||
- [:simple-cmake: JSON_GlobalUDLs](../../integration/cmake.md#json_globaludls) - CMake option to control the macro
|
- [:simple-cmake: JSON_GlobalUDLs](../../integration/cmake.md#json_globaludls) - CMake option to control the macro
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|||||||
@@ -18,9 +18,7 @@ using namespace nlohmann;
|
|||||||
```
|
```
|
||||||
|
|
||||||
This is suggested to ease migration to the next major version release of the library. See
|
This is suggested to ease migration to the next major version release of the library. See
|
||||||
[`JSON_USE_GLOBAL_UDLS`](macros/json_use_global_udls.md#notes) for details. The operator is declared in header
|
[`JSON_USE_GLOBAL_UDLS`](macros/json_use_global_udls.md#notes) for details.
|
||||||
`<nlohmann/json_literals.hpp>`, which `<nlohmann/json.hpp>` includes unless
|
|
||||||
[`JSON_NO_AUTOMATIC_UDLS`](macros/json_no_automatic_udls.md) is defined.
|
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
@@ -61,8 +59,6 @@ Linear.
|
|||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [Creating JSON values](../features/creating_values.md) - the article on creating JSON values
|
- [Creating JSON values](../features/creating_values.md) - the article on creating JSON values
|
||||||
- [JSON_NO_AUTOMATIC_UDLS](macros/json_no_automatic_udls.md) - do not include the user-defined string literals
|
|
||||||
automatically
|
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,7 @@ using namespace nlohmann::literals::json_literals;
|
|||||||
using namespace nlohmann;
|
using namespace nlohmann;
|
||||||
```
|
```
|
||||||
This is suggested to ease migration to the next major version release of the library. See
|
This is suggested to ease migration to the next major version release of the library. See
|
||||||
[`JSON_USE_GLOBAL_UDLS`](macros/json_use_global_udls.md#notes) for details. The operator is declared in header
|
[`JSON_USE_GLOBAL_UDLS`](macros/json_use_global_udls.md#notes) for details.
|
||||||
`<nlohmann/json_literals.hpp>`, which `<nlohmann/json.hpp>` includes unless
|
|
||||||
[`JSON_NO_AUTOMATIC_UDLS`](macros/json_no_automatic_udls.md) is defined.
|
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
@@ -60,8 +58,6 @@ Linear.
|
|||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [json_pointer](json_pointer/index.md) - type to represent JSON Pointers
|
- [json_pointer](json_pointer/index.md) - type to represent JSON Pointers
|
||||||
- [JSON_NO_AUTOMATIC_UDLS](macros/json_no_automatic_udls.md) - do not include the user-defined string literals
|
|
||||||
automatically
|
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -174,20 +174,7 @@ The library maps CBOR types to JSON value types as follows:
|
|||||||
|
|
||||||
!!! warning "Object keys"
|
!!! warning "Object keys"
|
||||||
|
|
||||||
CBOR allows map keys of any type, whereas JSON only allows strings as keys in object values. Therefore, CBOR maps
|
CBOR allows map keys of any type, whereas JSON only allows strings as keys in object values. Therefore, CBOR maps with keys other than UTF-8 strings are rejected.
|
||||||
with keys other than text strings (major type 3) are rejected with a
|
|
||||||
[`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) exception (or, with `allow_exceptions` set
|
|
||||||
to `false`, a discarded value) naming the type of the key that was found, for instance:
|
|
||||||
|
|
||||||
```
|
|
||||||
[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR object key: only string keys are supported, but found an unsigned integer; last byte: 0x01
|
|
||||||
```
|
|
||||||
|
|
||||||
This applies to the [SAX interface](../parsing/sax_interface.md) as well, as the key is read before it is passed
|
|
||||||
on. This is a deliberate restriction of the library's JSON value model, not an oversight: formats built on CBOR
|
|
||||||
maps with integer keys, such as COSE ([RFC 9052](https://www.rfc-editor.org/rfc/rfc9052.html)) or CWT
|
|
||||||
([RFC 8392](https://www.rfc-editor.org/rfc/rfc8392.html)), cannot be read with this library and need a
|
|
||||||
general-purpose CBOR library instead.
|
|
||||||
|
|
||||||
!!! warning "UTF-8 validation of text strings"
|
!!! warning "UTF-8 validation of text strings"
|
||||||
|
|
||||||
|
|||||||
@@ -138,21 +138,6 @@ The library maps MessagePack types to JSON value types as follows:
|
|||||||
|
|
||||||
Any MessagePack output created by `to_msgpack` can be successfully parsed by `from_msgpack`.
|
Any MessagePack output created by `to_msgpack` can be successfully parsed by `from_msgpack`.
|
||||||
|
|
||||||
!!! warning "Object keys"
|
|
||||||
|
|
||||||
MessagePack allows map keys of any type, whereas JSON only allows strings as keys in object values. Like the
|
|
||||||
JSON-compatible [profile](https://github.com/msgpack/msgpack/blob/master/spec.md#profile) sketched in the
|
|
||||||
MessagePack specification, this library restricts map keys to `str` values. Maps with keys of any other type are
|
|
||||||
rejected with a [`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) exception (or, with
|
|
||||||
`allow_exceptions` set to `false`, a discarded value) naming the type of the key that was found, for instance:
|
|
||||||
|
|
||||||
```
|
|
||||||
[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing MessagePack object key: only string keys are supported, but found nil; last byte: 0xC0
|
|
||||||
```
|
|
||||||
|
|
||||||
This applies to the [SAX interface](../parsing/sax_interface.md) as well, as the key is read before it is passed
|
|
||||||
on. Such input needs a general-purpose MessagePack library instead.
|
|
||||||
|
|
||||||
!!! warning "UTF-8 validation of string values"
|
!!! warning "UTF-8 validation of string values"
|
||||||
|
|
||||||
The MessagePack specification requires `str` values (`fixstr`, `str 8`, `str 16`, `str 32`) to be valid UTF-8.
|
The MessagePack specification requires `str` values (`fixstr`, `str 8`, `str 16`, `str 32`) to be valid UTF-8.
|
||||||
|
|||||||
@@ -83,15 +83,6 @@ When defined, default parse and serialize functions for enums are excluded and h
|
|||||||
|
|
||||||
See [full documentation of `JSON_DISABLE_ENUM_SERIALIZATION`](../api/macros/json_disable_enum_serialization.md).
|
See [full documentation of `JSON_DISABLE_ENUM_SERIALIZATION`](../api/macros/json_disable_enum_serialization.md).
|
||||||
|
|
||||||
## `JSON_NO_AUTOMATIC_UDLS`
|
|
||||||
|
|
||||||
When defined, `<nlohmann/json.hpp>` does not include `<nlohmann/json_literals.hpp>` with the user-defined string literals
|
|
||||||
`operator""_json` and `operator""_json_pointer`. This reduces the compile time of translation units that do not use
|
|
||||||
them, because the literals instantiate the parser in every translation unit that includes them. Include
|
|
||||||
`<nlohmann/json_literals.hpp>` where the literals are needed.
|
|
||||||
|
|
||||||
See [full documentation of `JSON_NO_AUTOMATIC_UDLS`](../api/macros/json_no_automatic_udls.md).
|
|
||||||
|
|
||||||
## `JSON_NO_IO`
|
## `JSON_NO_IO`
|
||||||
|
|
||||||
When defined, headers `<cstdio>`, `<ios>`, `<iosfwd>`, `<istream>`, and `<ostream>` are not included and parse functions
|
When defined, headers `<cstdio>`, `<ios>`, `<iosfwd>`, `<istream>`, and `<ostream>` are not included and parse functions
|
||||||
|
|||||||
@@ -40,9 +40,6 @@ Only the following symbols are exported from `nlohmann.json`:
|
|||||||
- `nlohmann::literals::json_literals::operator""_json`
|
- `nlohmann::literals::json_literals::operator""_json`
|
||||||
- `nlohmann::literals::json_literals::operator""_json_pointer`
|
- `nlohmann::literals::json_literals::operator""_json_pointer`
|
||||||
|
|
||||||
The module always exports the two user-defined string literals, even if
|
|
||||||
[`JSON_NO_AUTOMATIC_UDLS`](../api/macros/json_no_automatic_udls.md) is defined when building it.
|
|
||||||
|
|
||||||
Additionally, the following `nlohmann::detail` symbols are exported, solely to work around an MSVC compilation issue
|
Additionally, the following `nlohmann::detail` symbols are exported, solely to work around an MSVC compilation issue
|
||||||
([#3970](https://github.com/nlohmann/json/issues/3970)). They are implementation details, not part of the public API,
|
([#3970](https://github.com/nlohmann/json/issues/3970)). They are implementation details, not part of the public API,
|
||||||
and should not be used directly:
|
and should not be used directly:
|
||||||
|
|||||||
@@ -343,20 +343,13 @@ A string could not be read from a [binary format](../features/binary_formats/ind
|
|||||||
string was read where one was required (for instance as a map key), the string's length specification is invalid, or
|
string was read where one was required (for instance as a map key), the string's length specification is invalid, or
|
||||||
the string's bytes are not valid UTF-8.
|
the string's bytes are not valid UTF-8.
|
||||||
|
|
||||||
CBOR and MessagePack allow map keys of any type, but JSON object keys are always strings. Maps with keys of any other
|
|
||||||
type (for instance integers or `null`) are therefore not supported; see the notes on
|
|
||||||
[CBOR](../features/binary_formats/cbor.md) and [MessagePack](../features/binary_formats/messagepack.md).
|
|
||||||
|
|
||||||
!!! failure "Example messages"
|
!!! failure "Example messages"
|
||||||
|
|
||||||
```
|
```
|
||||||
[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR object key: only string keys are supported, but found an unsigned integer; last byte: 0x01
|
[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0xFF
|
||||||
```
|
```
|
||||||
```
|
```
|
||||||
[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing MessagePack object key: only string keys are supported, but found nil; last byte: 0xC0
|
[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing MessagePack string: expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0xFF
|
||||||
```
|
|
||||||
```
|
|
||||||
[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x7C
|
|
||||||
```
|
```
|
||||||
```
|
```
|
||||||
[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON char: byte after 'C' must be in range 0x00..0x7F; last byte: 0x82
|
[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON char: byte after 'C' must be in range 0x00..0x7F; last byte: 0x82
|
||||||
|
|||||||
@@ -15,7 +15,4 @@ Clang).
|
|||||||
|
|
||||||
You can further use file
|
You can further use file
|
||||||
[`single_include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_fwd.hpp)
|
[`single_include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_fwd.hpp)
|
||||||
for forward declarations, and file
|
for forward declarations.
|
||||||
[`single_include/nlohmann/json_literals.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_literals.hpp)
|
|
||||||
for the user-defined string literals if you define
|
|
||||||
[`JSON_NO_AUTOMATIC_UDLS`](../api/macros/json_no_automatic_udls.md).
|
|
||||||
|
|||||||
@@ -293,7 +293,6 @@ nav:
|
|||||||
- 'JSON_HAS_STATIC_RTTI': api/macros/json_has_static_rtti.md
|
- 'JSON_HAS_STATIC_RTTI': api/macros/json_has_static_rtti.md
|
||||||
- 'JSON_HAS_STD_FORMAT': api/macros/json_has_std_format.md
|
- 'JSON_HAS_STD_FORMAT': api/macros/json_has_std_format.md
|
||||||
- 'JSON_HAS_THREE_WAY_COMPARISON': api/macros/json_has_three_way_comparison.md
|
- 'JSON_HAS_THREE_WAY_COMPARISON': api/macros/json_has_three_way_comparison.md
|
||||||
- 'JSON_NO_AUTOMATIC_UDLS': api/macros/json_no_automatic_udls.md
|
|
||||||
- 'JSON_NOEXCEPTION': api/macros/json_noexception.md
|
- 'JSON_NOEXCEPTION': api/macros/json_noexception.md
|
||||||
- 'JSON_NO_IO': api/macros/json_no_io.md
|
- 'JSON_NO_IO': api/macros/json_no_io.md
|
||||||
- 'JSON_NO_THREAD_LOCAL': api/macros/json_no_thread_local.md
|
- 'JSON_NO_THREAD_LOCAL': api/macros/json_no_thread_local.md
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ namespace detail
|
|||||||
* j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an
|
* j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an
|
||||||
* allocated value (e.g., a string). See bug issue
|
* allocated value (e.g., a string). See bug issue
|
||||||
* https://github.com/nlohmann/json/issues/2865 for more information.
|
* https://github.com/nlohmann/json/issues/2865 for more information.
|
||||||
|
*
|
||||||
|
* A value that has to be allocated is created before the old one is destroyed:
|
||||||
|
* were it the other way around, an exception while creating the new value would
|
||||||
|
* leave j with the type of the new value, but the pointer to the destroyed old one.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
template<value_t> struct external_constructor;
|
template<value_t> struct external_constructor;
|
||||||
@@ -65,18 +69,20 @@ struct external_constructor<value_t::string>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(s);
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::string;
|
j.m_data.m_type = value_t::string;
|
||||||
j.m_data.m_value = s;
|
j.m_data.m_value = value;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(std::move(s));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::string;
|
j.m_data.m_type = value_t::string;
|
||||||
j.m_data.m_value = std::move(s);
|
j.m_data.m_value = value;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,9 +91,10 @@ struct external_constructor<value_t::string>
|
|||||||
int > = 0 >
|
int > = 0 >
|
||||||
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
||||||
{
|
{
|
||||||
|
auto* created = j.template create<typename BasicJsonType::string_t>(str);
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::string;
|
j.m_data.m_type = value_t::string;
|
||||||
j.m_data.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
|
j.m_data.m_value.string = created;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -98,18 +105,20 @@ struct external_constructor<value_t::binary>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(b);
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::binary;
|
j.m_data.m_type = value_t::binary;
|
||||||
j.m_data.m_value = typename BasicJsonType::binary_t(b);
|
j.m_data.m_value = value;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(std::move(b));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::binary;
|
j.m_data.m_type = value_t::binary;
|
||||||
j.m_data.m_value = typename BasicJsonType::binary_t(std::move(b));
|
j.m_data.m_value = value;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -159,9 +168,10 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(arr);
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = arr;
|
j.m_data.m_value = value;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -169,9 +179,10 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(std::move(arr));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = std::move(arr);
|
j.m_data.m_value = value;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -187,9 +198,10 @@ struct external_constructor<value_t::array>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
|
auto* created = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
j.m_data.m_value.array = created;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -197,15 +209,17 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
||||||
{
|
{
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
typename BasicJsonType::array_t elements;
|
||||||
j.m_data.m_type = value_t::array;
|
elements.reserve(arr.size());
|
||||||
j.m_data.m_value = value_t::array;
|
|
||||||
j.m_data.m_value.array->reserve(arr.size());
|
|
||||||
for (const bool x : arr)
|
for (const bool x : arr)
|
||||||
{
|
{
|
||||||
j.m_data.m_value.array->push_back(x);
|
elements.push_back(x);
|
||||||
j.set_parent(j.m_data.m_value.array->back());
|
|
||||||
}
|
}
|
||||||
|
const typename BasicJsonType::json_value value(std::move(elements));
|
||||||
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
|
j.m_data.m_type = value_t::array;
|
||||||
|
j.m_data.m_value = value;
|
||||||
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,11 +227,12 @@ struct external_constructor<value_t::array>
|
|||||||
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
||||||
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
||||||
{
|
{
|
||||||
|
typename BasicJsonType::array_t elements(arr.size());
|
||||||
|
std::copy(std::begin(arr), std::end(arr), elements.begin());
|
||||||
|
const typename BasicJsonType::json_value value(std::move(elements));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = value_t::array;
|
j.m_data.m_value = value;
|
||||||
j.m_data.m_value.array->resize(arr.size());
|
|
||||||
std::copy(std::begin(arr), std::end(arr), j.m_data.m_value.array->begin());
|
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -229,14 +244,16 @@ struct external_constructor<value_t::array>
|
|||||||
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
|
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
|
||||||
static void construct(BasicJsonType& j, CompatibleArrayType && arr)
|
static void construct(BasicJsonType& j, CompatibleArrayType && arr)
|
||||||
{
|
{
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
typename BasicJsonType::array_t elements;
|
||||||
j.m_data.m_type = value_t::array;
|
|
||||||
j.m_data.m_value = value_t::array;
|
|
||||||
for (auto&& x : std::forward<CompatibleArrayType>(arr))
|
for (auto&& x : std::forward<CompatibleArrayType>(arr))
|
||||||
{
|
{
|
||||||
j.m_data.m_value.array->push_back(x);
|
elements.push_back(x);
|
||||||
j.set_parent(j.m_data.m_value.array->back());
|
|
||||||
}
|
}
|
||||||
|
const typename BasicJsonType::json_value value(std::move(elements));
|
||||||
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
|
j.m_data.m_type = value_t::array;
|
||||||
|
j.m_data.m_value = value;
|
||||||
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -248,9 +265,10 @@ struct external_constructor<value_t::object>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(obj);
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::object;
|
j.m_data.m_type = value_t::object;
|
||||||
j.m_data.m_value = obj;
|
j.m_data.m_value = value;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -258,9 +276,10 @@ struct external_constructor<value_t::object>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(std::move(obj));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::object;
|
j.m_data.m_type = value_t::object;
|
||||||
j.m_data.m_value = std::move(obj);
|
j.m_data.m_value = value;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -272,9 +291,10 @@ struct external_constructor<value_t::object>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
|
auto* created = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::object;
|
j.m_data.m_type = value_t::object;
|
||||||
j.m_data.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
j.m_data.m_value.object = created;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1324,80 +1324,6 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief reads a CBOR object key
|
|
||||||
|
|
||||||
RFC 8949 allows any data item as a map key, but only strings have a
|
|
||||||
counterpart in JSON. A key of any other type is rejected with a message
|
|
||||||
naming that type, rather than the one @ref get_cbor_string gives for a
|
|
||||||
malformed string.
|
|
||||||
|
|
||||||
@param[out] result created key
|
|
||||||
|
|
||||||
@return whether key creation completed
|
|
||||||
*/
|
|
||||||
bool get_cbor_object_key(string_t& result)
|
|
||||||
{
|
|
||||||
// EOF and major type 3 (text string) are left to get_cbor_string
|
|
||||||
if (current == char_traits<char_type>::eof() || (static_cast<unsigned int>(current) & 0xE0u) == 0x60u)
|
|
||||||
{
|
|
||||||
return get_cbor_string(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* found = nullptr;
|
|
||||||
switch (static_cast<unsigned int>(current) >> 5u)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
found = "an unsigned integer";
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
found = "a negative integer";
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
found = "a byte string";
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
found = "an array";
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
found = "a map";
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
found = "a tag";
|
|
||||||
break;
|
|
||||||
default: // major type 7
|
|
||||||
switch (current)
|
|
||||||
{
|
|
||||||
case 0xF4:
|
|
||||||
case 0xF5:
|
|
||||||
found = "a boolean";
|
|
||||||
break;
|
|
||||||
case 0xF6:
|
|
||||||
found = "null";
|
|
||||||
break;
|
|
||||||
case 0xF7:
|
|
||||||
found = "undefined";
|
|
||||||
break;
|
|
||||||
case 0xF9:
|
|
||||||
case 0xFA:
|
|
||||||
case 0xFB:
|
|
||||||
found = "a floating-point number";
|
|
||||||
break;
|
|
||||||
case 0xFF:
|
|
||||||
found = "a break stop code";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
found = "a simple value";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto last_token = get_token_string();
|
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
|
||||||
exception_message(input_format_t::cbor, concat("only string keys are supported, but found ", found, "; last byte: 0x", last_token), "object key"), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief reads a definite-length CBOR byte array
|
@brief reads a definite-length CBOR byte array
|
||||||
|
|
||||||
@@ -1642,7 +1568,7 @@ class binary_reader
|
|||||||
if (top.is_object)
|
if (top.is_object)
|
||||||
{
|
{
|
||||||
key.clear();
|
key.clear();
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_object_key(key) || !sax->key(key)))
|
if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2143,98 +2069,6 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief reads a MessagePack object key
|
|
||||||
|
|
||||||
The MessagePack specification allows any type as a map key, but only
|
|
||||||
strings have a counterpart in JSON. A key of any other type is rejected
|
|
||||||
with a message naming that type, rather than the one @ref
|
|
||||||
get_msgpack_string gives for a malformed string.
|
|
||||||
|
|
||||||
@param[out] result created key
|
|
||||||
|
|
||||||
@return whether key creation completed
|
|
||||||
*/
|
|
||||||
bool get_msgpack_object_key(string_t& result)
|
|
||||||
{
|
|
||||||
const char* found = nullptr;
|
|
||||||
switch (current)
|
|
||||||
{
|
|
||||||
case 0xC0:
|
|
||||||
found = "nil";
|
|
||||||
break;
|
|
||||||
case 0xC2:
|
|
||||||
case 0xC3:
|
|
||||||
found = "a boolean";
|
|
||||||
break;
|
|
||||||
case 0xCA:
|
|
||||||
case 0xCB:
|
|
||||||
found = "a float";
|
|
||||||
break;
|
|
||||||
case 0xC4:
|
|
||||||
case 0xC5:
|
|
||||||
case 0xC6:
|
|
||||||
found = "a bin";
|
|
||||||
break;
|
|
||||||
case 0xC7:
|
|
||||||
case 0xC8:
|
|
||||||
case 0xC9:
|
|
||||||
case 0xD4:
|
|
||||||
case 0xD5:
|
|
||||||
case 0xD6:
|
|
||||||
case 0xD7:
|
|
||||||
case 0xD8:
|
|
||||||
found = "an ext";
|
|
||||||
break;
|
|
||||||
case 0xCC:
|
|
||||||
case 0xCD:
|
|
||||||
case 0xCE:
|
|
||||||
case 0xCF:
|
|
||||||
case 0xD0:
|
|
||||||
case 0xD1:
|
|
||||||
case 0xD2:
|
|
||||||
case 0xD3:
|
|
||||||
found = "an integer";
|
|
||||||
break;
|
|
||||||
case 0xDC:
|
|
||||||
case 0xDD:
|
|
||||||
found = "an array";
|
|
||||||
break;
|
|
||||||
case 0xDE:
|
|
||||||
case 0xDF:
|
|
||||||
found = "a map";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// fixint, fixmap, and fixarray; strings, EOF, and the unused
|
|
||||||
// byte 0xC1 are left to get_msgpack_string
|
|
||||||
if (current == char_traits<char_type>::eof())
|
|
||||||
{
|
|
||||||
return get_msgpack_string(result);
|
|
||||||
}
|
|
||||||
if (current <= 0x7F || current >= 0xE0)
|
|
||||||
{
|
|
||||||
found = "an integer";
|
|
||||||
}
|
|
||||||
else if (current <= 0x8F)
|
|
||||||
{
|
|
||||||
found = "a map";
|
|
||||||
}
|
|
||||||
else if (current <= 0x9F)
|
|
||||||
{
|
|
||||||
found = "an array";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return get_msgpack_string(result);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto last_token = get_token_string();
|
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
|
||||||
exception_message(input_format_t::msgpack, concat("only string keys are supported, but found ", found, "; last byte: 0x", last_token), "object key"), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief reads a MessagePack byte array
|
@brief reads a MessagePack byte array
|
||||||
|
|
||||||
@@ -2397,7 +2231,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
get();
|
get();
|
||||||
key.clear();
|
key.clear();
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_msgpack_object_key(key) || !sax->key(key)))
|
if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,6 +206,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false, bool discard_number_values_ = false) noexcept
|
explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false, bool discard_number_values_ = false) noexcept
|
||||||
: ia(std::move(adapter))
|
: ia(std::move(adapter))
|
||||||
, ignore_comments(ignore_comments_)
|
, ignore_comments(ignore_comments_)
|
||||||
|
, decimal_point_char(static_cast<char_int_type>(get_decimal_point()))
|
||||||
, discard_number_values(discard_number_values_)
|
, discard_number_values(discard_number_values_)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@@ -221,7 +222,8 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
// locales
|
// locales
|
||||||
/////////////////////
|
/////////////////////
|
||||||
|
|
||||||
/// return the decimal point of the current locale
|
/// return the locale-dependent decimal point
|
||||||
|
JSON_HEDLEY_PURE
|
||||||
static char get_decimal_point() noexcept
|
static char get_decimal_point() noexcept
|
||||||
{
|
{
|
||||||
const auto* loc = localeconv();
|
const auto* loc = localeconv();
|
||||||
@@ -1090,10 +1092,9 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
token_type::value_float if number could be successfully scanned,
|
token_type::value_float if number could be successfully scanned,
|
||||||
token_type::parse_error otherwise
|
token_type::parse_error otherwise
|
||||||
|
|
||||||
@note The scanner is independent of the current locale: token_buffer
|
@note The scanner is independent of the current locale. Internally, the
|
||||||
always holds `.`. Only the std::strtod fallback of convert_number()
|
locale's decimal point is used instead of `.` to work with the
|
||||||
depends on the locale, and it looks up the decimal point right
|
locale-dependent converters.
|
||||||
before converting (see 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.
|
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.
|
||||||
{
|
{
|
||||||
@@ -1182,7 +1183,7 @@ scan_number_zero:
|
|||||||
{
|
{
|
||||||
case '.':
|
case '.':
|
||||||
{
|
{
|
||||||
add(current);
|
add(decimal_point_char);
|
||||||
decimal_point_position = token_buffer.size() - 1;
|
decimal_point_position = token_buffer.size() - 1;
|
||||||
goto scan_number_decimal1;
|
goto scan_number_decimal1;
|
||||||
}
|
}
|
||||||
@@ -1219,7 +1220,7 @@ scan_number_any1:
|
|||||||
|
|
||||||
case '.':
|
case '.':
|
||||||
{
|
{
|
||||||
add(current);
|
add(decimal_point_char);
|
||||||
decimal_point_position = token_buffer.size() - 1;
|
decimal_point_position = token_buffer.size() - 1;
|
||||||
goto scan_number_decimal1;
|
goto scan_number_decimal1;
|
||||||
}
|
}
|
||||||
@@ -1461,9 +1462,9 @@ scan_number_done:
|
|||||||
|
|
||||||
// Only a number below 1 can carry further insignificant zeros, and only
|
// Only a number below 1 can carry further insignificant zeros, and only
|
||||||
// while the count stays at the limit does removing them change the
|
// while the count stays at the limit does removing them change the
|
||||||
// answer - so this loop is skipped for all but a few tokens. The
|
// answer - so this loop is skipped for all but a few tokens. Note
|
||||||
// fraction is located through decimal_point_position rather than by
|
// token_buffer holds the locale's decimal point, so the fraction is
|
||||||
// searching '.'.
|
// located through decimal_point_position rather than by searching '.'.
|
||||||
if (lead_zero != 0)
|
if (lead_zero != 0)
|
||||||
{
|
{
|
||||||
JSON_ASSERT(has_dot != 0); // an integer "0" cannot reach the limit
|
JSON_ASSERT(has_dot != 0); // an integer "0" cannot reach the limit
|
||||||
@@ -1481,8 +1482,8 @@ scan_number_done:
|
|||||||
@brief convert the number text in token_buffer to its value and token type
|
@brief convert the number text in token_buffer to its value and token type
|
||||||
|
|
||||||
The digit sequence in token_buffer has already been validated (by the
|
The digit sequence in token_buffer has already been validated (by the
|
||||||
scan_number() state machine or by the contiguous fast path) and holds '.'
|
scan_number() state machine or by the contiguous fast path) and holds the
|
||||||
as decimal point, independent of the locale. Integers are parsed first and fall
|
locale decimal point in place of '.'. Integers are parsed first and fall
|
||||||
back to floating point on overflow. This is shared so both scanners produce
|
back to floating point on overflow. This is shared so both scanners produce
|
||||||
identical results.
|
identical results.
|
||||||
|
|
||||||
@@ -1562,7 +1563,7 @@ scan_number_done:
|
|||||||
// integer conversion above overflowed. Prefer std::from_chars
|
// integer conversion above overflowed. Prefer std::from_chars
|
||||||
// (Eisel-Lemire, locale-independent, correctly rounded) when available;
|
// (Eisel-Lemire, locale-independent, correctly rounded) when available;
|
||||||
// otherwise the exact Clinger fast path (double only); otherwise the
|
// otherwise the exact Clinger fast path (double only); otherwise the
|
||||||
// locale-aware strtof/strtod/strtold.
|
// locale-aware strtof/strtod.
|
||||||
if (parse_float_from_chars(num_begin, num_end, value_float))
|
if (parse_float_from_chars(num_begin, num_end, value_float))
|
||||||
{
|
{
|
||||||
return token_type::value_float;
|
return token_type::value_float;
|
||||||
@@ -1571,75 +1572,26 @@ scan_number_done:
|
|||||||
// extra pass over the token's bytes, which otherwise shows up on
|
// extra pass over the token's bytes, which otherwise shows up on
|
||||||
// high-precision inputs such as canada.json
|
// high-precision inputs such as canada.json
|
||||||
if (mantissa_fits_clinger(mantissa_end)
|
if (mantissa_fits_clinger(mantissa_end)
|
||||||
&& parse_float_fast(num_begin, num_end, value_float))
|
&& parse_float_fast(num_begin, num_end, decimal_point_char, value_float))
|
||||||
{
|
{
|
||||||
return token_type::value_float;
|
return token_type::value_float;
|
||||||
}
|
}
|
||||||
|
|
||||||
convert_float_locale_aware();
|
char* endptr = nullptr; // NOLINT(misc-const-correctness,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||||
|
strtof(value_float, token_buffer.data(), &endptr);
|
||||||
|
|
||||||
|
// we checked the number format before
|
||||||
|
JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
|
||||||
|
|
||||||
return token_type::value_float;
|
return token_type::value_float;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief convert the float in token_buffer with strtof/strtod/strtold
|
|
||||||
|
|
||||||
These functions expect the decimal point of the *current* locale, so it is
|
|
||||||
looked up right before the conversion instead of once when the lexer is
|
|
||||||
constructed: a locale change in between (by a parser callback, a SAX
|
|
||||||
handler, or another thread) must not truncate the value (#5198). The
|
|
||||||
token has been validated before, so if the conversion stops early and the
|
|
||||||
decimal point changed in the meantime, the locale changed between the
|
|
||||||
lookup and the call, and the conversion is repeated with the new decimal
|
|
||||||
point. If the decimal point did not change, a retry cannot succeed: the
|
|
||||||
locale's decimal point is not a single character (e.g., the two-byte
|
|
||||||
U+066B of ar_EG.UTF-8 or fa_IR.UTF-8) and cannot be substituted in place.
|
|
||||||
The value strtod parsed up to that point is kept, as before this change.
|
|
||||||
|
|
||||||
Note that changing the locale in another thread *while* strtod runs is
|
|
||||||
undefined behavior of the C library, which this function cannot prevent.
|
|
||||||
*/
|
|
||||||
void convert_float_locale_aware()
|
|
||||||
{
|
|
||||||
const bool has_dot = decimal_point_position != std::string::npos;
|
|
||||||
char decimal_point = get_decimal_point();
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
const bool substitute = has_dot && decimal_point != '.';
|
|
||||||
if (substitute)
|
|
||||||
{
|
|
||||||
token_buffer[decimal_point_position] = static_cast<typename string_t::value_type>(decimal_point);
|
|
||||||
}
|
|
||||||
|
|
||||||
char* endptr = nullptr; // NOLINT(misc-const-correctness,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
|
||||||
strtof(value_float, token_buffer.data(), &endptr);
|
|
||||||
|
|
||||||
if (substitute)
|
|
||||||
{
|
|
||||||
// get_string() hands the token to the SAX interface with '.'
|
|
||||||
token_buffer[decimal_point_position] = '.';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_LIKELY(endptr == token_buffer.data() + token_buffer.size()))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// retry only if the locale changed; otherwise, this would loop forever
|
|
||||||
const char current_decimal_point = get_decimal_point();
|
|
||||||
if (current_decimal_point == decimal_point)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
decimal_point = current_decimal_point;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief contiguous fast path for scanning a number
|
@brief contiguous fast path for scanning a number
|
||||||
|
|
||||||
Parses the whole number token straight from the input buffer, avoiding the
|
Parses the whole number token straight from the input buffer, avoiding the
|
||||||
per-character get()/add() of scan_number(). On success it fills token_buffer
|
per-character get()/add() of scan_number(). On success it fills token_buffer
|
||||||
(as scan_number() does) and
|
(with the locale decimal point substituted, as scan_number() does) and
|
||||||
returns the token type. On anything it does not fully recognize as a
|
returns the token type. On anything it does not fully recognize as a
|
||||||
well-formed number it makes no state change and returns
|
well-formed number it makes no state change and returns
|
||||||
token_type::uninitialized, so the caller falls back to scan_number(), which
|
token_type::uninitialized, so the caller falls back to scan_number(), which
|
||||||
@@ -1755,11 +1707,16 @@ scan_number_done:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// materialize the token exactly as scan_number() would. reset() already
|
// materialize the token exactly as scan_number() would, substituting the
|
||||||
// cleared token_buffer, so append() fills it (assign() is avoided
|
// locale decimal point so convert_number()'s strtof fallback stays valid.
|
||||||
// because custom string_t types need not provide it)
|
// reset() already cleared token_buffer, so append() fills it (assign() is
|
||||||
|
// avoided because custom string_t types need not provide it)
|
||||||
token_buffer.append(reinterpret_cast<const typename string_t::value_type*>(data), len);
|
token_buffer.append(reinterpret_cast<const typename string_t::value_type*>(data), len);
|
||||||
decimal_point_position = dot_index;
|
if (dot_index != std::string::npos)
|
||||||
|
{
|
||||||
|
token_buffer[dot_index] = static_cast<typename string_t::value_type>(decimal_point_char);
|
||||||
|
decimal_point_position = dot_index;
|
||||||
|
}
|
||||||
|
|
||||||
ia.bulk_skip(len - 1);
|
ia.bulk_skip(len - 1);
|
||||||
position.chars_read_total += (len - 1);
|
position.chars_read_total += (len - 1);
|
||||||
@@ -2026,7 +1983,11 @@ scan_number_done:
|
|||||||
/// return current string value (implicitly resets the token; useful only once)
|
/// return current string value (implicitly resets the token; useful only once)
|
||||||
string_t& get_string()
|
string_t& get_string()
|
||||||
{
|
{
|
||||||
// a number token holds '.' regardless of the locale (#4084)
|
// translate decimal points from locale back to '.' (#4084)
|
||||||
|
if (decimal_point_char != '.' && decimal_point_position != std::string::npos)
|
||||||
|
{
|
||||||
|
token_buffer[decimal_point_position] = '.';
|
||||||
|
}
|
||||||
return token_buffer;
|
return token_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2322,7 +2283,9 @@ scan_number_done:
|
|||||||
number_unsigned_t value_unsigned = 0;
|
number_unsigned_t value_unsigned = 0;
|
||||||
number_float_t value_float = 0;
|
number_float_t value_float = 0;
|
||||||
|
|
||||||
/// the position of the decimal point in token_buffer
|
/// the decimal point
|
||||||
|
const char_int_type decimal_point_char = '.';
|
||||||
|
/// the position of the decimal point in the input
|
||||||
std::size_t decimal_point_position = std::string::npos;
|
std::size_t decimal_point_position = std::string::npos;
|
||||||
|
|
||||||
/// whether the caller (e.g. accept()/json_sax_acceptor) only needs the
|
/// whether the caller (e.g. accept()/json_sax_acceptor) only needs the
|
||||||
|
|||||||
@@ -118,12 +118,14 @@ std::strtod. The parser only activates for number_float_t == double; float and
|
|||||||
long double keep the std::strtof/std::strtold paths (see the templated overload
|
long double keep the std::strtof/std::strtold paths (see the templated overload
|
||||||
below).
|
below).
|
||||||
|
|
||||||
@param[in] first pointer to the first character of the number
|
@param[in] first pointer to the first character of the number
|
||||||
@param[in] last pointer past the last character
|
@param[in] last pointer past the last character
|
||||||
@param[out] out the parsed value on success
|
@param[in] decimal_point the (locale-dependent) decimal point character
|
||||||
|
@param[out] out the parsed value on success
|
||||||
@return true if the value was parsed exactly; false to fall back to strtod
|
@return true if the value was parsed exactly; false to fall back to strtod
|
||||||
*/
|
*/
|
||||||
inline bool parse_float_fast(const char* first, const char* last, double& out) noexcept
|
template<typename DecimalPointType>
|
||||||
|
bool parse_float_fast(const char* first, const char* last, DecimalPointType decimal_point, double& out) noexcept
|
||||||
{
|
{
|
||||||
#if defined(FLT_EVAL_METHOD) && FLT_EVAL_METHOD != 0
|
#if defined(FLT_EVAL_METHOD) && FLT_EVAL_METHOD != 0
|
||||||
// Clinger's fast path is only exact when double operations are evaluated in
|
// Clinger's fast path is only exact when double operations are evaluated in
|
||||||
@@ -134,6 +136,7 @@ inline bool parse_float_fast(const char* first, const char* last, double& out) n
|
|||||||
// std::from_chars / std::strtod path.
|
// std::from_chars / std::strtod path.
|
||||||
static_cast<void>(first);
|
static_cast<void>(first);
|
||||||
static_cast<void>(last);
|
static_cast<void>(last);
|
||||||
|
static_cast<void>(decimal_point);
|
||||||
static_cast<void>(out);
|
static_cast<void>(out);
|
||||||
return false;
|
return false;
|
||||||
#else
|
#else
|
||||||
@@ -172,7 +175,7 @@ inline bool parse_float_fast(const char* first, const char* last, double& out) n
|
|||||||
++num_digits;
|
++num_digits;
|
||||||
fractional_digits += static_cast<int>(seen_dot);
|
fractional_digits += static_cast<int>(seen_dot);
|
||||||
}
|
}
|
||||||
else if (c == '.')
|
else if (static_cast<DecimalPointType>(c) == decimal_point)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(seen_dot))
|
if (JSON_HEDLEY_UNLIKELY(seen_dot))
|
||||||
{
|
{
|
||||||
@@ -257,8 +260,8 @@ inline bool parse_float_fast(const char* first, const char* last, double& out) n
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// fast float path is only exact for `double`; decline for float/long double
|
/// fast float path is only exact for `double`; decline for float/long double
|
||||||
template<typename FloatType>
|
template<typename DecimalPointType, typename FloatType>
|
||||||
bool parse_float_fast(const char* /*first*/, const char* /*last*/, FloatType& /*out*/) noexcept
|
bool parse_float_fast(const char* /*first*/, const char* /*last*/, DecimalPointType /*decimal_point*/, FloatType& /*out*/) noexcept
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -270,7 +273,9 @@ std::from_chars is locale-independent, correctly rounded, and - via the
|
|||||||
Eisel-Lemire algorithm in modern standard libraries - much faster than strtod
|
Eisel-Lemire algorithm in modern standard libraries - much faster than strtod
|
||||||
over the whole value range (not just the Clinger subset). It is used only when
|
over the whole value range (not just the Clinger subset). It is used only when
|
||||||
__cpp_lib_to_chars indicates full floating-point support and only when it
|
__cpp_lib_to_chars indicates full floating-point support and only when it
|
||||||
consumes the entire token ([first, last)). An under-/overflow (result_out_of_range) also declines, so
|
consumes the entire token ([first, last)); a partial parse means the buffer
|
||||||
|
uses a non-'.' locale decimal point, in which case the caller falls back to the
|
||||||
|
locale-aware path. An under-/overflow (result_out_of_range) also declines, so
|
||||||
the caller's strtod fallback supplies the well-defined ±inf/0 result the parser
|
the caller's strtod fallback supplies the well-defined ±inf/0 result the parser
|
||||||
expects (side-stepping the P4168 divergence between implementations).
|
expects (side-stepping the P4168 divergence between implementations).
|
||||||
|
|
||||||
|
|||||||
@@ -915,3 +915,7 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
#ifndef JSON_DISABLE_ENUM_SERIALIZATION
|
#ifndef JSON_DISABLE_ENUM_SERIALIZATION
|
||||||
#define JSON_DISABLE_ENUM_SERIALIZATION 0
|
#define JSON_DISABLE_ENUM_SERIALIZATION 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef JSON_USE_GLOBAL_UDLS
|
||||||
|
#define JSON_USE_GLOBAL_UDLS 1
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#undef JSON_INLINE_VARIABLE
|
#undef JSON_INLINE_VARIABLE
|
||||||
#undef JSON_NO_UNIQUE_ADDRESS
|
#undef JSON_NO_UNIQUE_ADDRESS
|
||||||
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
||||||
|
#undef JSON_USE_GLOBAL_UDLS
|
||||||
|
|
||||||
#ifndef JSON_TEST_KEEP_MACROS
|
#ifndef JSON_TEST_KEEP_MACROS
|
||||||
#undef JSON_CATCH
|
#undef JSON_CATCH
|
||||||
|
|||||||
+75
-23
@@ -1702,8 +1702,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
if (is_an_object)
|
if (is_an_object)
|
||||||
{
|
{
|
||||||
// the initializer list is a list of pairs -> create an object
|
// the initializer list is a list of pairs -> create an object
|
||||||
m_data.m_type = value_t::object;
|
|
||||||
m_data.m_value = value_t::object;
|
m_data.m_value = value_t::object;
|
||||||
|
m_data.m_type = value_t::object;
|
||||||
|
|
||||||
for (auto& element_ref : init)
|
for (auto& element_ref : init)
|
||||||
{
|
{
|
||||||
@@ -1725,8 +1725,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// the initializer list describes an array -> create an array
|
// the initializer list describes an array -> create an array
|
||||||
m_data.m_type = value_t::array;
|
|
||||||
m_data.m_value.array = create<array_t>(init.begin(), init.end());
|
m_data.m_value.array = create<array_t>(init.begin(), init.end());
|
||||||
|
m_data.m_type = value_t::array;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_parents();
|
set_parents();
|
||||||
@@ -1739,8 +1739,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(const typename binary_t::container_type& init)
|
static basic_json binary(const typename binary_t::container_type& init)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_type = value_t::binary;
|
|
||||||
res.m_data.m_value = init;
|
res.m_data.m_value = init;
|
||||||
|
res.m_data.m_type = value_t::binary;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1750,8 +1750,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
|
static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_type = value_t::binary;
|
|
||||||
res.m_data.m_value = binary_t(init, subtype);
|
res.m_data.m_value = binary_t(init, subtype);
|
||||||
|
res.m_data.m_type = value_t::binary;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1761,8 +1761,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(typename binary_t::container_type&& init)
|
static basic_json binary(typename binary_t::container_type&& init)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_type = value_t::binary;
|
|
||||||
res.m_data.m_value = std::move(init);
|
res.m_data.m_value = std::move(init);
|
||||||
|
res.m_data.m_type = value_t::binary;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1772,8 +1772,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
|
static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_type = value_t::binary;
|
|
||||||
res.m_data.m_value = binary_t(std::move(init), subtype);
|
res.m_data.m_value = binary_t(std::move(init), subtype);
|
||||||
|
res.m_data.m_type = value_t::binary;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2819,8 +2819,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// implicitly convert a null value to an empty array
|
// implicitly convert a null value to an empty array
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_type = value_t::array;
|
|
||||||
m_data.m_value.array = create<array_t>();
|
m_data.m_value.array = create<array_t>();
|
||||||
|
m_data.m_type = value_t::array;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2879,8 +2879,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// implicitly convert a null value to an empty object
|
// implicitly convert a null value to an empty object
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_type = value_t::object;
|
|
||||||
m_data.m_value.object = create<object_t>();
|
m_data.m_value.object = create<object_t>();
|
||||||
|
m_data.m_type = value_t::object;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2932,8 +2932,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// implicitly convert a null value to an empty object
|
// implicitly convert a null value to an empty object
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_type = value_t::object;
|
|
||||||
m_data.m_value.object = create<object_t>();
|
m_data.m_value.object = create<object_t>();
|
||||||
|
m_data.m_type = value_t::object;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3868,8 +3868,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// transform a null object into an array
|
// transform a null object into an array
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_type = value_t::array;
|
|
||||||
m_data.m_value = value_t::array;
|
m_data.m_value = value_t::array;
|
||||||
|
m_data.m_type = value_t::array;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3901,8 +3901,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// transform a null object into an array
|
// transform a null object into an array
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_type = value_t::array;
|
|
||||||
m_data.m_value = value_t::array;
|
m_data.m_value = value_t::array;
|
||||||
|
m_data.m_type = value_t::array;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3933,8 +3933,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// transform a null object into an object
|
// transform a null object into an object
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_type = value_t::object;
|
|
||||||
m_data.m_value = value_t::object;
|
m_data.m_value = value_t::object;
|
||||||
|
m_data.m_type = value_t::object;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3989,8 +3989,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// transform a null object into an array
|
// transform a null object into an array
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_type = value_t::array;
|
|
||||||
m_data.m_value = value_t::array;
|
m_data.m_value = value_t::array;
|
||||||
|
m_data.m_type = value_t::array;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4014,8 +4014,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// transform a null object into an object
|
// transform a null object into an object
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_type = value_t::object;
|
|
||||||
m_data.m_value = value_t::object;
|
m_data.m_value = value_t::object;
|
||||||
|
m_data.m_type = value_t::object;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4196,8 +4196,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// implicitly convert a null value to an empty object
|
// implicitly convert a null value to an empty object
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_type = value_t::object;
|
|
||||||
m_data.m_value.object = create<object_t>();
|
m_data.m_value.object = create<object_t>();
|
||||||
|
m_data.m_type = value_t::object;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6423,6 +6423,55 @@ std::string format_as(const NLOHMANN_BASIC_JSON_TPL& j)
|
|||||||
return j.dump();
|
return j.dump();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline namespace literals
|
||||||
|
{
|
||||||
|
inline namespace json_literals
|
||||||
|
{
|
||||||
|
|
||||||
|
/// @brief user-defined string literal for JSON values
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/
|
||||||
|
JSON_HEDLEY_NON_NULL(1)
|
||||||
|
#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0)
|
||||||
|
inline nlohmann::json operator""_json(const char* s, std::size_t n)
|
||||||
|
#else
|
||||||
|
// GCC 4.8 requires a space between "" and suffix
|
||||||
|
inline nlohmann::json operator"" _json(const char* s, std::size_t n)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
return nlohmann::json::parse(s, s + n);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__cpp_char8_t)
|
||||||
|
JSON_HEDLEY_NON_NULL(1)
|
||||||
|
inline nlohmann::json operator""_json(const char8_t* s, std::size_t n)
|
||||||
|
{
|
||||||
|
return nlohmann::json::parse(reinterpret_cast<const char*>(s),
|
||||||
|
reinterpret_cast<const char*>(s) + n);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// @brief user-defined string literal for JSON pointer
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/
|
||||||
|
JSON_HEDLEY_NON_NULL(1)
|
||||||
|
#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0)
|
||||||
|
inline nlohmann::json::json_pointer operator""_json_pointer(const char* s, std::size_t n)
|
||||||
|
#else
|
||||||
|
// GCC 4.8 requires a space between "" and suffix
|
||||||
|
inline nlohmann::json::json_pointer operator"" _json_pointer(const char* s, std::size_t n)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
return nlohmann::json::json_pointer(std::string(s, n));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__cpp_char8_t)
|
||||||
|
inline nlohmann::json::json_pointer operator""_json_pointer(const char8_t* s, std::size_t n)
|
||||||
|
{
|
||||||
|
return nlohmann::json::json_pointer(std::string(reinterpret_cast<const char*>(s), n));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace json_literals
|
||||||
|
} // namespace literals
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|
||||||
///////////////////////
|
///////////////////////
|
||||||
@@ -6557,6 +6606,17 @@ struct formatter<nlohmann::NLOHMANN_BASIC_JSON_TPL, char> // NOLINT(cert-dcl58-c
|
|||||||
|
|
||||||
} // namespace std
|
} // namespace std
|
||||||
|
|
||||||
|
#if JSON_USE_GLOBAL_UDLS
|
||||||
|
#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0)
|
||||||
|
using nlohmann::literals::json_literals::operator""_json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
||||||
|
using nlohmann::literals::json_literals::operator""_json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
||||||
|
#else
|
||||||
|
// GCC 4.8 requires a space between "" and suffix
|
||||||
|
using nlohmann::literals::json_literals::operator"" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
||||||
|
using nlohmann::literals::json_literals::operator"" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <nlohmann/detail/macro_unscope.hpp>
|
#include <nlohmann/detail/macro_unscope.hpp>
|
||||||
|
|
||||||
// End of GCC diagnostic pragmas for C++ modules support
|
// End of GCC diagnostic pragmas for C++ modules support
|
||||||
@@ -6564,12 +6624,4 @@ struct formatter<nlohmann::NLOHMANN_BASIC_JSON_TPL, char> // NOLINT(cert-dcl58-c
|
|||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// The user-defined string literals are in a separate header, because their
|
|
||||||
// bodies instantiate the parser in every translation unit that includes them.
|
|
||||||
// Define JSON_NO_AUTOMATIC_UDLS to include <nlohmann/json_literals.hpp> only
|
|
||||||
// where needed.
|
|
||||||
#ifndef JSON_NO_AUTOMATIC_UDLS
|
|
||||||
#include <nlohmann/json_literals.hpp>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // INCLUDE_NLOHMANN_JSON_HPP_
|
#endif // INCLUDE_NLOHMANN_JSON_HPP_
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
// __ _____ _____ _____
|
|
||||||
// __| | __| | | | JSON for Modern C++
|
|
||||||
// | | |__ | | | | | | version 3.12.0
|
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
||||||
//
|
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
#ifndef INCLUDE_NLOHMANN_JSON_LITERALS_HPP_
|
|
||||||
#define INCLUDE_NLOHMANN_JSON_LITERALS_HPP_
|
|
||||||
|
|
||||||
#include <cstddef> // size_t
|
|
||||||
#include <string> // string
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
// This header is included at the end of <nlohmann/json.hpp> unless
|
|
||||||
// JSON_NO_AUTOMATIC_UDLS is defined, and can be included on its own after that.
|
|
||||||
// Either way, the library's internal macros are no longer defined here (and the
|
|
||||||
// amalgamation inlines macro_scope.hpp only once), so only standard and public
|
|
||||||
// macros may be used below.
|
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
|
||||||
inline namespace literals
|
|
||||||
{
|
|
||||||
inline namespace json_literals
|
|
||||||
{
|
|
||||||
|
|
||||||
/// @brief user-defined string literal for JSON values
|
|
||||||
/// @sa https://json.nlohmann.me/api/operator_literal_json/
|
|
||||||
#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)
|
|
||||||
inline nlohmann::json operator""_json(const char* s, std::size_t n)
|
|
||||||
#else
|
|
||||||
// GCC 4.8 requires a space between "" and suffix
|
|
||||||
inline nlohmann::json operator"" _json(const char* s, std::size_t n)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
return nlohmann::json::parse(s, s + n);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__cpp_char8_t)
|
|
||||||
inline nlohmann::json operator""_json(const char8_t* s, std::size_t n)
|
|
||||||
{
|
|
||||||
return nlohmann::json::parse(reinterpret_cast<const char*>(s),
|
|
||||||
reinterpret_cast<const char*>(s) + n);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// @brief user-defined string literal for JSON pointer
|
|
||||||
/// @sa https://json.nlohmann.me/api/operator_literal_json_pointer/
|
|
||||||
#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)
|
|
||||||
inline nlohmann::json::json_pointer operator""_json_pointer(const char* s, std::size_t n)
|
|
||||||
#else
|
|
||||||
// GCC 4.8 requires a space between "" and suffix
|
|
||||||
inline nlohmann::json::json_pointer operator"" _json_pointer(const char* s, std::size_t n)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
return nlohmann::json::json_pointer(std::string(s, n));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__cpp_char8_t)
|
|
||||||
inline nlohmann::json::json_pointer operator""_json_pointer(const char8_t* s, std::size_t n)
|
|
||||||
{
|
|
||||||
return nlohmann::json::json_pointer(std::string(reinterpret_cast<const char*>(s), n));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace json_literals
|
|
||||||
} // namespace literals
|
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
|
||||||
|
|
||||||
#if !defined(JSON_USE_GLOBAL_UDLS) || JSON_USE_GLOBAL_UDLS
|
|
||||||
#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)
|
|
||||||
using nlohmann::literals::json_literals::operator""_json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
|
||||||
using nlohmann::literals::json_literals::operator""_json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
|
||||||
#else
|
|
||||||
// GCC 4.8 requires a space between "" and suffix
|
|
||||||
using nlohmann::literals::json_literals::operator"" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
|
||||||
using nlohmann::literals::json_literals::operator"" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // INCLUDE_NLOHMANN_JSON_LITERALS_HPP_
|
|
||||||
@@ -15,7 +15,6 @@ nlohmann_json_multiple_headers = declare_dependency(
|
|||||||
if not meson.is_subproject()
|
if not meson.is_subproject()
|
||||||
install_headers('single_include/nlohmann/json.hpp', subdir: 'nlohmann')
|
install_headers('single_include/nlohmann/json.hpp', subdir: 'nlohmann')
|
||||||
install_headers('single_include/nlohmann/json_fwd.hpp', subdir: 'nlohmann')
|
install_headers('single_include/nlohmann/json_fwd.hpp', subdir: 'nlohmann')
|
||||||
install_headers('single_include/nlohmann/json_literals.hpp', subdir: 'nlohmann')
|
|
||||||
|
|
||||||
pkgc = import('pkgconfig')
|
pkgc = import('pkgconfig')
|
||||||
pkgc.generate(name: 'nlohmann_json',
|
pkgc.generate(name: 'nlohmann_json',
|
||||||
|
|||||||
+179
-385
@@ -3327,6 +3327,10 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
#define JSON_DISABLE_ENUM_SERIALIZATION 0
|
#define JSON_DISABLE_ENUM_SERIALIZATION 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef JSON_USE_GLOBAL_UDLS
|
||||||
|
#define JSON_USE_GLOBAL_UDLS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
#if JSON_HAS_THREE_WAY_COMPARISON
|
#if JSON_HAS_THREE_WAY_COMPARISON
|
||||||
#include <compare> // partial_ordering
|
#include <compare> // partial_ordering
|
||||||
#endif
|
#endif
|
||||||
@@ -6567,6 +6571,10 @@ namespace detail
|
|||||||
* j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an
|
* j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an
|
||||||
* allocated value (e.g., a string). See bug issue
|
* allocated value (e.g., a string). See bug issue
|
||||||
* https://github.com/nlohmann/json/issues/2865 for more information.
|
* https://github.com/nlohmann/json/issues/2865 for more information.
|
||||||
|
*
|
||||||
|
* A value that has to be allocated is created before the old one is destroyed:
|
||||||
|
* were it the other way around, an exception while creating the new value would
|
||||||
|
* leave j with the type of the new value, but the pointer to the destroyed old one.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
template<value_t> struct external_constructor;
|
template<value_t> struct external_constructor;
|
||||||
@@ -6590,18 +6598,20 @@ struct external_constructor<value_t::string>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(s);
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::string;
|
j.m_data.m_type = value_t::string;
|
||||||
j.m_data.m_value = s;
|
j.m_data.m_value = value;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(std::move(s));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::string;
|
j.m_data.m_type = value_t::string;
|
||||||
j.m_data.m_value = std::move(s);
|
j.m_data.m_value = value;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6610,9 +6620,10 @@ struct external_constructor<value_t::string>
|
|||||||
int > = 0 >
|
int > = 0 >
|
||||||
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
||||||
{
|
{
|
||||||
|
auto* created = j.template create<typename BasicJsonType::string_t>(str);
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::string;
|
j.m_data.m_type = value_t::string;
|
||||||
j.m_data.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
|
j.m_data.m_value.string = created;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -6623,18 +6634,20 @@ struct external_constructor<value_t::binary>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(b);
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::binary;
|
j.m_data.m_type = value_t::binary;
|
||||||
j.m_data.m_value = typename BasicJsonType::binary_t(b);
|
j.m_data.m_value = value;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(std::move(b));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::binary;
|
j.m_data.m_type = value_t::binary;
|
||||||
j.m_data.m_value = typename BasicJsonType::binary_t(std::move(b));
|
j.m_data.m_value = value;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -6684,9 +6697,10 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(arr);
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = arr;
|
j.m_data.m_value = value;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -6694,9 +6708,10 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(std::move(arr));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = std::move(arr);
|
j.m_data.m_value = value;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -6712,9 +6727,10 @@ struct external_constructor<value_t::array>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
|
auto* created = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
j.m_data.m_value.array = created;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -6722,15 +6738,17 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
||||||
{
|
{
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
typename BasicJsonType::array_t elements;
|
||||||
j.m_data.m_type = value_t::array;
|
elements.reserve(arr.size());
|
||||||
j.m_data.m_value = value_t::array;
|
|
||||||
j.m_data.m_value.array->reserve(arr.size());
|
|
||||||
for (const bool x : arr)
|
for (const bool x : arr)
|
||||||
{
|
{
|
||||||
j.m_data.m_value.array->push_back(x);
|
elements.push_back(x);
|
||||||
j.set_parent(j.m_data.m_value.array->back());
|
|
||||||
}
|
}
|
||||||
|
const typename BasicJsonType::json_value value(std::move(elements));
|
||||||
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
|
j.m_data.m_type = value_t::array;
|
||||||
|
j.m_data.m_value = value;
|
||||||
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6738,11 +6756,12 @@ struct external_constructor<value_t::array>
|
|||||||
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
||||||
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
||||||
{
|
{
|
||||||
|
typename BasicJsonType::array_t elements(arr.size());
|
||||||
|
std::copy(std::begin(arr), std::end(arr), elements.begin());
|
||||||
|
const typename BasicJsonType::json_value value(std::move(elements));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = value_t::array;
|
j.m_data.m_value = value;
|
||||||
j.m_data.m_value.array->resize(arr.size());
|
|
||||||
std::copy(std::begin(arr), std::end(arr), j.m_data.m_value.array->begin());
|
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -6754,14 +6773,16 @@ struct external_constructor<value_t::array>
|
|||||||
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
|
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
|
||||||
static void construct(BasicJsonType& j, CompatibleArrayType && arr)
|
static void construct(BasicJsonType& j, CompatibleArrayType && arr)
|
||||||
{
|
{
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
typename BasicJsonType::array_t elements;
|
||||||
j.m_data.m_type = value_t::array;
|
|
||||||
j.m_data.m_value = value_t::array;
|
|
||||||
for (auto&& x : std::forward<CompatibleArrayType>(arr))
|
for (auto&& x : std::forward<CompatibleArrayType>(arr))
|
||||||
{
|
{
|
||||||
j.m_data.m_value.array->push_back(x);
|
elements.push_back(x);
|
||||||
j.set_parent(j.m_data.m_value.array->back());
|
|
||||||
}
|
}
|
||||||
|
const typename BasicJsonType::json_value value(std::move(elements));
|
||||||
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
|
j.m_data.m_type = value_t::array;
|
||||||
|
j.m_data.m_value = value;
|
||||||
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -6773,9 +6794,10 @@ struct external_constructor<value_t::object>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(obj);
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::object;
|
j.m_data.m_type = value_t::object;
|
||||||
j.m_data.m_value = obj;
|
j.m_data.m_value = value;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -6783,9 +6805,10 @@ struct external_constructor<value_t::object>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(std::move(obj));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::object;
|
j.m_data.m_type = value_t::object;
|
||||||
j.m_data.m_value = std::move(obj);
|
j.m_data.m_value = value;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -6797,9 +6820,10 @@ struct external_constructor<value_t::object>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
|
auto* created = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::object;
|
j.m_data.m_type = value_t::object;
|
||||||
j.m_data.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
j.m_data.m_value.object = created;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -8601,12 +8625,14 @@ std::strtod. The parser only activates for number_float_t == double; float and
|
|||||||
long double keep the std::strtof/std::strtold paths (see the templated overload
|
long double keep the std::strtof/std::strtold paths (see the templated overload
|
||||||
below).
|
below).
|
||||||
|
|
||||||
@param[in] first pointer to the first character of the number
|
@param[in] first pointer to the first character of the number
|
||||||
@param[in] last pointer past the last character
|
@param[in] last pointer past the last character
|
||||||
@param[out] out the parsed value on success
|
@param[in] decimal_point the (locale-dependent) decimal point character
|
||||||
|
@param[out] out the parsed value on success
|
||||||
@return true if the value was parsed exactly; false to fall back to strtod
|
@return true if the value was parsed exactly; false to fall back to strtod
|
||||||
*/
|
*/
|
||||||
inline bool parse_float_fast(const char* first, const char* last, double& out) noexcept
|
template<typename DecimalPointType>
|
||||||
|
bool parse_float_fast(const char* first, const char* last, DecimalPointType decimal_point, double& out) noexcept
|
||||||
{
|
{
|
||||||
#if defined(FLT_EVAL_METHOD) && FLT_EVAL_METHOD != 0
|
#if defined(FLT_EVAL_METHOD) && FLT_EVAL_METHOD != 0
|
||||||
// Clinger's fast path is only exact when double operations are evaluated in
|
// Clinger's fast path is only exact when double operations are evaluated in
|
||||||
@@ -8617,6 +8643,7 @@ inline bool parse_float_fast(const char* first, const char* last, double& out) n
|
|||||||
// std::from_chars / std::strtod path.
|
// std::from_chars / std::strtod path.
|
||||||
static_cast<void>(first);
|
static_cast<void>(first);
|
||||||
static_cast<void>(last);
|
static_cast<void>(last);
|
||||||
|
static_cast<void>(decimal_point);
|
||||||
static_cast<void>(out);
|
static_cast<void>(out);
|
||||||
return false;
|
return false;
|
||||||
#else
|
#else
|
||||||
@@ -8655,7 +8682,7 @@ inline bool parse_float_fast(const char* first, const char* last, double& out) n
|
|||||||
++num_digits;
|
++num_digits;
|
||||||
fractional_digits += static_cast<int>(seen_dot);
|
fractional_digits += static_cast<int>(seen_dot);
|
||||||
}
|
}
|
||||||
else if (c == '.')
|
else if (static_cast<DecimalPointType>(c) == decimal_point)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(seen_dot))
|
if (JSON_HEDLEY_UNLIKELY(seen_dot))
|
||||||
{
|
{
|
||||||
@@ -8740,8 +8767,8 @@ inline bool parse_float_fast(const char* first, const char* last, double& out) n
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// fast float path is only exact for `double`; decline for float/long double
|
/// fast float path is only exact for `double`; decline for float/long double
|
||||||
template<typename FloatType>
|
template<typename DecimalPointType, typename FloatType>
|
||||||
bool parse_float_fast(const char* /*first*/, const char* /*last*/, FloatType& /*out*/) noexcept
|
bool parse_float_fast(const char* /*first*/, const char* /*last*/, DecimalPointType /*decimal_point*/, FloatType& /*out*/) noexcept
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -8753,7 +8780,9 @@ std::from_chars is locale-independent, correctly rounded, and - via the
|
|||||||
Eisel-Lemire algorithm in modern standard libraries - much faster than strtod
|
Eisel-Lemire algorithm in modern standard libraries - much faster than strtod
|
||||||
over the whole value range (not just the Clinger subset). It is used only when
|
over the whole value range (not just the Clinger subset). It is used only when
|
||||||
__cpp_lib_to_chars indicates full floating-point support and only when it
|
__cpp_lib_to_chars indicates full floating-point support and only when it
|
||||||
consumes the entire token ([first, last)). An under-/overflow (result_out_of_range) also declines, so
|
consumes the entire token ([first, last)); a partial parse means the buffer
|
||||||
|
uses a non-'.' locale decimal point, in which case the caller falls back to the
|
||||||
|
locale-aware path. An under-/overflow (result_out_of_range) also declines, so
|
||||||
the caller's strtod fallback supplies the well-defined ±inf/0 result the parser
|
the caller's strtod fallback supplies the well-defined ±inf/0 result the parser
|
||||||
expects (side-stepping the P4168 divergence between implementations).
|
expects (side-stepping the P4168 divergence between implementations).
|
||||||
|
|
||||||
@@ -9294,6 +9323,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false, bool discard_number_values_ = false) noexcept
|
explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false, bool discard_number_values_ = false) noexcept
|
||||||
: ia(std::move(adapter))
|
: ia(std::move(adapter))
|
||||||
, ignore_comments(ignore_comments_)
|
, ignore_comments(ignore_comments_)
|
||||||
|
, decimal_point_char(static_cast<char_int_type>(get_decimal_point()))
|
||||||
, discard_number_values(discard_number_values_)
|
, discard_number_values(discard_number_values_)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@@ -9309,7 +9339,8 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
// locales
|
// locales
|
||||||
/////////////////////
|
/////////////////////
|
||||||
|
|
||||||
/// return the decimal point of the current locale
|
/// return the locale-dependent decimal point
|
||||||
|
JSON_HEDLEY_PURE
|
||||||
static char get_decimal_point() noexcept
|
static char get_decimal_point() noexcept
|
||||||
{
|
{
|
||||||
const auto* loc = localeconv();
|
const auto* loc = localeconv();
|
||||||
@@ -10178,10 +10209,9 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
token_type::value_float if number could be successfully scanned,
|
token_type::value_float if number could be successfully scanned,
|
||||||
token_type::parse_error otherwise
|
token_type::parse_error otherwise
|
||||||
|
|
||||||
@note The scanner is independent of the current locale: token_buffer
|
@note The scanner is independent of the current locale. Internally, the
|
||||||
always holds `.`. Only the std::strtod fallback of convert_number()
|
locale's decimal point is used instead of `.` to work with the
|
||||||
depends on the locale, and it looks up the decimal point right
|
locale-dependent converters.
|
||||||
before converting (see 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.
|
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.
|
||||||
{
|
{
|
||||||
@@ -10270,7 +10300,7 @@ scan_number_zero:
|
|||||||
{
|
{
|
||||||
case '.':
|
case '.':
|
||||||
{
|
{
|
||||||
add(current);
|
add(decimal_point_char);
|
||||||
decimal_point_position = token_buffer.size() - 1;
|
decimal_point_position = token_buffer.size() - 1;
|
||||||
goto scan_number_decimal1;
|
goto scan_number_decimal1;
|
||||||
}
|
}
|
||||||
@@ -10307,7 +10337,7 @@ scan_number_any1:
|
|||||||
|
|
||||||
case '.':
|
case '.':
|
||||||
{
|
{
|
||||||
add(current);
|
add(decimal_point_char);
|
||||||
decimal_point_position = token_buffer.size() - 1;
|
decimal_point_position = token_buffer.size() - 1;
|
||||||
goto scan_number_decimal1;
|
goto scan_number_decimal1;
|
||||||
}
|
}
|
||||||
@@ -10549,9 +10579,9 @@ scan_number_done:
|
|||||||
|
|
||||||
// Only a number below 1 can carry further insignificant zeros, and only
|
// Only a number below 1 can carry further insignificant zeros, and only
|
||||||
// while the count stays at the limit does removing them change the
|
// while the count stays at the limit does removing them change the
|
||||||
// answer - so this loop is skipped for all but a few tokens. The
|
// answer - so this loop is skipped for all but a few tokens. Note
|
||||||
// fraction is located through decimal_point_position rather than by
|
// token_buffer holds the locale's decimal point, so the fraction is
|
||||||
// searching '.'.
|
// located through decimal_point_position rather than by searching '.'.
|
||||||
if (lead_zero != 0)
|
if (lead_zero != 0)
|
||||||
{
|
{
|
||||||
JSON_ASSERT(has_dot != 0); // an integer "0" cannot reach the limit
|
JSON_ASSERT(has_dot != 0); // an integer "0" cannot reach the limit
|
||||||
@@ -10569,8 +10599,8 @@ scan_number_done:
|
|||||||
@brief convert the number text in token_buffer to its value and token type
|
@brief convert the number text in token_buffer to its value and token type
|
||||||
|
|
||||||
The digit sequence in token_buffer has already been validated (by the
|
The digit sequence in token_buffer has already been validated (by the
|
||||||
scan_number() state machine or by the contiguous fast path) and holds '.'
|
scan_number() state machine or by the contiguous fast path) and holds the
|
||||||
as decimal point, independent of the locale. Integers are parsed first and fall
|
locale decimal point in place of '.'. Integers are parsed first and fall
|
||||||
back to floating point on overflow. This is shared so both scanners produce
|
back to floating point on overflow. This is shared so both scanners produce
|
||||||
identical results.
|
identical results.
|
||||||
|
|
||||||
@@ -10650,7 +10680,7 @@ scan_number_done:
|
|||||||
// integer conversion above overflowed. Prefer std::from_chars
|
// integer conversion above overflowed. Prefer std::from_chars
|
||||||
// (Eisel-Lemire, locale-independent, correctly rounded) when available;
|
// (Eisel-Lemire, locale-independent, correctly rounded) when available;
|
||||||
// otherwise the exact Clinger fast path (double only); otherwise the
|
// otherwise the exact Clinger fast path (double only); otherwise the
|
||||||
// locale-aware strtof/strtod/strtold.
|
// locale-aware strtof/strtod.
|
||||||
if (parse_float_from_chars(num_begin, num_end, value_float))
|
if (parse_float_from_chars(num_begin, num_end, value_float))
|
||||||
{
|
{
|
||||||
return token_type::value_float;
|
return token_type::value_float;
|
||||||
@@ -10659,75 +10689,26 @@ scan_number_done:
|
|||||||
// extra pass over the token's bytes, which otherwise shows up on
|
// extra pass over the token's bytes, which otherwise shows up on
|
||||||
// high-precision inputs such as canada.json
|
// high-precision inputs such as canada.json
|
||||||
if (mantissa_fits_clinger(mantissa_end)
|
if (mantissa_fits_clinger(mantissa_end)
|
||||||
&& parse_float_fast(num_begin, num_end, value_float))
|
&& parse_float_fast(num_begin, num_end, decimal_point_char, value_float))
|
||||||
{
|
{
|
||||||
return token_type::value_float;
|
return token_type::value_float;
|
||||||
}
|
}
|
||||||
|
|
||||||
convert_float_locale_aware();
|
char* endptr = nullptr; // NOLINT(misc-const-correctness,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||||
|
strtof(value_float, token_buffer.data(), &endptr);
|
||||||
|
|
||||||
|
// we checked the number format before
|
||||||
|
JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
|
||||||
|
|
||||||
return token_type::value_float;
|
return token_type::value_float;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief convert the float in token_buffer with strtof/strtod/strtold
|
|
||||||
|
|
||||||
These functions expect the decimal point of the *current* locale, so it is
|
|
||||||
looked up right before the conversion instead of once when the lexer is
|
|
||||||
constructed: a locale change in between (by a parser callback, a SAX
|
|
||||||
handler, or another thread) must not truncate the value (#5198). The
|
|
||||||
token has been validated before, so if the conversion stops early and the
|
|
||||||
decimal point changed in the meantime, the locale changed between the
|
|
||||||
lookup and the call, and the conversion is repeated with the new decimal
|
|
||||||
point. If the decimal point did not change, a retry cannot succeed: the
|
|
||||||
locale's decimal point is not a single character (e.g., the two-byte
|
|
||||||
U+066B of ar_EG.UTF-8 or fa_IR.UTF-8) and cannot be substituted in place.
|
|
||||||
The value strtod parsed up to that point is kept, as before this change.
|
|
||||||
|
|
||||||
Note that changing the locale in another thread *while* strtod runs is
|
|
||||||
undefined behavior of the C library, which this function cannot prevent.
|
|
||||||
*/
|
|
||||||
void convert_float_locale_aware()
|
|
||||||
{
|
|
||||||
const bool has_dot = decimal_point_position != std::string::npos;
|
|
||||||
char decimal_point = get_decimal_point();
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
const bool substitute = has_dot && decimal_point != '.';
|
|
||||||
if (substitute)
|
|
||||||
{
|
|
||||||
token_buffer[decimal_point_position] = static_cast<typename string_t::value_type>(decimal_point);
|
|
||||||
}
|
|
||||||
|
|
||||||
char* endptr = nullptr; // NOLINT(misc-const-correctness,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
|
||||||
strtof(value_float, token_buffer.data(), &endptr);
|
|
||||||
|
|
||||||
if (substitute)
|
|
||||||
{
|
|
||||||
// get_string() hands the token to the SAX interface with '.'
|
|
||||||
token_buffer[decimal_point_position] = '.';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_LIKELY(endptr == token_buffer.data() + token_buffer.size()))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// retry only if the locale changed; otherwise, this would loop forever
|
|
||||||
const char current_decimal_point = get_decimal_point();
|
|
||||||
if (current_decimal_point == decimal_point)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
decimal_point = current_decimal_point;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief contiguous fast path for scanning a number
|
@brief contiguous fast path for scanning a number
|
||||||
|
|
||||||
Parses the whole number token straight from the input buffer, avoiding the
|
Parses the whole number token straight from the input buffer, avoiding the
|
||||||
per-character get()/add() of scan_number(). On success it fills token_buffer
|
per-character get()/add() of scan_number(). On success it fills token_buffer
|
||||||
(as scan_number() does) and
|
(with the locale decimal point substituted, as scan_number() does) and
|
||||||
returns the token type. On anything it does not fully recognize as a
|
returns the token type. On anything it does not fully recognize as a
|
||||||
well-formed number it makes no state change and returns
|
well-formed number it makes no state change and returns
|
||||||
token_type::uninitialized, so the caller falls back to scan_number(), which
|
token_type::uninitialized, so the caller falls back to scan_number(), which
|
||||||
@@ -10843,11 +10824,16 @@ scan_number_done:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// materialize the token exactly as scan_number() would. reset() already
|
// materialize the token exactly as scan_number() would, substituting the
|
||||||
// cleared token_buffer, so append() fills it (assign() is avoided
|
// locale decimal point so convert_number()'s strtof fallback stays valid.
|
||||||
// because custom string_t types need not provide it)
|
// reset() already cleared token_buffer, so append() fills it (assign() is
|
||||||
|
// avoided because custom string_t types need not provide it)
|
||||||
token_buffer.append(reinterpret_cast<const typename string_t::value_type*>(data), len);
|
token_buffer.append(reinterpret_cast<const typename string_t::value_type*>(data), len);
|
||||||
decimal_point_position = dot_index;
|
if (dot_index != std::string::npos)
|
||||||
|
{
|
||||||
|
token_buffer[dot_index] = static_cast<typename string_t::value_type>(decimal_point_char);
|
||||||
|
decimal_point_position = dot_index;
|
||||||
|
}
|
||||||
|
|
||||||
ia.bulk_skip(len - 1);
|
ia.bulk_skip(len - 1);
|
||||||
position.chars_read_total += (len - 1);
|
position.chars_read_total += (len - 1);
|
||||||
@@ -11114,7 +11100,11 @@ scan_number_done:
|
|||||||
/// return current string value (implicitly resets the token; useful only once)
|
/// return current string value (implicitly resets the token; useful only once)
|
||||||
string_t& get_string()
|
string_t& get_string()
|
||||||
{
|
{
|
||||||
// a number token holds '.' regardless of the locale (#4084)
|
// translate decimal points from locale back to '.' (#4084)
|
||||||
|
if (decimal_point_char != '.' && decimal_point_position != std::string::npos)
|
||||||
|
{
|
||||||
|
token_buffer[decimal_point_position] = '.';
|
||||||
|
}
|
||||||
return token_buffer;
|
return token_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11410,7 +11400,9 @@ scan_number_done:
|
|||||||
number_unsigned_t value_unsigned = 0;
|
number_unsigned_t value_unsigned = 0;
|
||||||
number_float_t value_float = 0;
|
number_float_t value_float = 0;
|
||||||
|
|
||||||
/// the position of the decimal point in token_buffer
|
/// the decimal point
|
||||||
|
const char_int_type decimal_point_char = '.';
|
||||||
|
/// the position of the decimal point in the input
|
||||||
std::size_t decimal_point_position = std::string::npos;
|
std::size_t decimal_point_position = std::string::npos;
|
||||||
|
|
||||||
/// whether the caller (e.g. accept()/json_sax_acceptor) only needs the
|
/// whether the caller (e.g. accept()/json_sax_acceptor) only needs the
|
||||||
@@ -14087,80 +14079,6 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief reads a CBOR object key
|
|
||||||
|
|
||||||
RFC 8949 allows any data item as a map key, but only strings have a
|
|
||||||
counterpart in JSON. A key of any other type is rejected with a message
|
|
||||||
naming that type, rather than the one @ref get_cbor_string gives for a
|
|
||||||
malformed string.
|
|
||||||
|
|
||||||
@param[out] result created key
|
|
||||||
|
|
||||||
@return whether key creation completed
|
|
||||||
*/
|
|
||||||
bool get_cbor_object_key(string_t& result)
|
|
||||||
{
|
|
||||||
// EOF and major type 3 (text string) are left to get_cbor_string
|
|
||||||
if (current == char_traits<char_type>::eof() || (static_cast<unsigned int>(current) & 0xE0u) == 0x60u)
|
|
||||||
{
|
|
||||||
return get_cbor_string(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* found = nullptr;
|
|
||||||
switch (static_cast<unsigned int>(current) >> 5u)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
found = "an unsigned integer";
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
found = "a negative integer";
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
found = "a byte string";
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
found = "an array";
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
found = "a map";
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
found = "a tag";
|
|
||||||
break;
|
|
||||||
default: // major type 7
|
|
||||||
switch (current)
|
|
||||||
{
|
|
||||||
case 0xF4:
|
|
||||||
case 0xF5:
|
|
||||||
found = "a boolean";
|
|
||||||
break;
|
|
||||||
case 0xF6:
|
|
||||||
found = "null";
|
|
||||||
break;
|
|
||||||
case 0xF7:
|
|
||||||
found = "undefined";
|
|
||||||
break;
|
|
||||||
case 0xF9:
|
|
||||||
case 0xFA:
|
|
||||||
case 0xFB:
|
|
||||||
found = "a floating-point number";
|
|
||||||
break;
|
|
||||||
case 0xFF:
|
|
||||||
found = "a break stop code";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
found = "a simple value";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto last_token = get_token_string();
|
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
|
||||||
exception_message(input_format_t::cbor, concat("only string keys are supported, but found ", found, "; last byte: 0x", last_token), "object key"), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief reads a definite-length CBOR byte array
|
@brief reads a definite-length CBOR byte array
|
||||||
|
|
||||||
@@ -14405,7 +14323,7 @@ class binary_reader
|
|||||||
if (top.is_object)
|
if (top.is_object)
|
||||||
{
|
{
|
||||||
key.clear();
|
key.clear();
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_object_key(key) || !sax->key(key)))
|
if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -14906,98 +14824,6 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief reads a MessagePack object key
|
|
||||||
|
|
||||||
The MessagePack specification allows any type as a map key, but only
|
|
||||||
strings have a counterpart in JSON. A key of any other type is rejected
|
|
||||||
with a message naming that type, rather than the one @ref
|
|
||||||
get_msgpack_string gives for a malformed string.
|
|
||||||
|
|
||||||
@param[out] result created key
|
|
||||||
|
|
||||||
@return whether key creation completed
|
|
||||||
*/
|
|
||||||
bool get_msgpack_object_key(string_t& result)
|
|
||||||
{
|
|
||||||
const char* found = nullptr;
|
|
||||||
switch (current)
|
|
||||||
{
|
|
||||||
case 0xC0:
|
|
||||||
found = "nil";
|
|
||||||
break;
|
|
||||||
case 0xC2:
|
|
||||||
case 0xC3:
|
|
||||||
found = "a boolean";
|
|
||||||
break;
|
|
||||||
case 0xCA:
|
|
||||||
case 0xCB:
|
|
||||||
found = "a float";
|
|
||||||
break;
|
|
||||||
case 0xC4:
|
|
||||||
case 0xC5:
|
|
||||||
case 0xC6:
|
|
||||||
found = "a bin";
|
|
||||||
break;
|
|
||||||
case 0xC7:
|
|
||||||
case 0xC8:
|
|
||||||
case 0xC9:
|
|
||||||
case 0xD4:
|
|
||||||
case 0xD5:
|
|
||||||
case 0xD6:
|
|
||||||
case 0xD7:
|
|
||||||
case 0xD8:
|
|
||||||
found = "an ext";
|
|
||||||
break;
|
|
||||||
case 0xCC:
|
|
||||||
case 0xCD:
|
|
||||||
case 0xCE:
|
|
||||||
case 0xCF:
|
|
||||||
case 0xD0:
|
|
||||||
case 0xD1:
|
|
||||||
case 0xD2:
|
|
||||||
case 0xD3:
|
|
||||||
found = "an integer";
|
|
||||||
break;
|
|
||||||
case 0xDC:
|
|
||||||
case 0xDD:
|
|
||||||
found = "an array";
|
|
||||||
break;
|
|
||||||
case 0xDE:
|
|
||||||
case 0xDF:
|
|
||||||
found = "a map";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// fixint, fixmap, and fixarray; strings, EOF, and the unused
|
|
||||||
// byte 0xC1 are left to get_msgpack_string
|
|
||||||
if (current == char_traits<char_type>::eof())
|
|
||||||
{
|
|
||||||
return get_msgpack_string(result);
|
|
||||||
}
|
|
||||||
if (current <= 0x7F || current >= 0xE0)
|
|
||||||
{
|
|
||||||
found = "an integer";
|
|
||||||
}
|
|
||||||
else if (current <= 0x8F)
|
|
||||||
{
|
|
||||||
found = "a map";
|
|
||||||
}
|
|
||||||
else if (current <= 0x9F)
|
|
||||||
{
|
|
||||||
found = "an array";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return get_msgpack_string(result);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto last_token = get_token_string();
|
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
|
||||||
exception_message(input_format_t::msgpack, concat("only string keys are supported, but found ", found, "; last byte: 0x", last_token), "object key"), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief reads a MessagePack byte array
|
@brief reads a MessagePack byte array
|
||||||
|
|
||||||
@@ -15160,7 +14986,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
get();
|
get();
|
||||||
key.clear();
|
key.clear();
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_msgpack_object_key(key) || !sax->key(key)))
|
if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -27779,8 +27605,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
if (is_an_object)
|
if (is_an_object)
|
||||||
{
|
{
|
||||||
// the initializer list is a list of pairs -> create an object
|
// the initializer list is a list of pairs -> create an object
|
||||||
m_data.m_type = value_t::object;
|
|
||||||
m_data.m_value = value_t::object;
|
m_data.m_value = value_t::object;
|
||||||
|
m_data.m_type = value_t::object;
|
||||||
|
|
||||||
for (auto& element_ref : init)
|
for (auto& element_ref : init)
|
||||||
{
|
{
|
||||||
@@ -27802,8 +27628,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// the initializer list describes an array -> create an array
|
// the initializer list describes an array -> create an array
|
||||||
m_data.m_type = value_t::array;
|
|
||||||
m_data.m_value.array = create<array_t>(init.begin(), init.end());
|
m_data.m_value.array = create<array_t>(init.begin(), init.end());
|
||||||
|
m_data.m_type = value_t::array;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_parents();
|
set_parents();
|
||||||
@@ -27816,8 +27642,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(const typename binary_t::container_type& init)
|
static basic_json binary(const typename binary_t::container_type& init)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_type = value_t::binary;
|
|
||||||
res.m_data.m_value = init;
|
res.m_data.m_value = init;
|
||||||
|
res.m_data.m_type = value_t::binary;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27827,8 +27653,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
|
static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_type = value_t::binary;
|
|
||||||
res.m_data.m_value = binary_t(init, subtype);
|
res.m_data.m_value = binary_t(init, subtype);
|
||||||
|
res.m_data.m_type = value_t::binary;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27838,8 +27664,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(typename binary_t::container_type&& init)
|
static basic_json binary(typename binary_t::container_type&& init)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_type = value_t::binary;
|
|
||||||
res.m_data.m_value = std::move(init);
|
res.m_data.m_value = std::move(init);
|
||||||
|
res.m_data.m_type = value_t::binary;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27849,8 +27675,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
|
static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_type = value_t::binary;
|
|
||||||
res.m_data.m_value = binary_t(std::move(init), subtype);
|
res.m_data.m_value = binary_t(std::move(init), subtype);
|
||||||
|
res.m_data.m_type = value_t::binary;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28896,8 +28722,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// implicitly convert a null value to an empty array
|
// implicitly convert a null value to an empty array
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_type = value_t::array;
|
|
||||||
m_data.m_value.array = create<array_t>();
|
m_data.m_value.array = create<array_t>();
|
||||||
|
m_data.m_type = value_t::array;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28956,8 +28782,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// implicitly convert a null value to an empty object
|
// implicitly convert a null value to an empty object
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_type = value_t::object;
|
|
||||||
m_data.m_value.object = create<object_t>();
|
m_data.m_value.object = create<object_t>();
|
||||||
|
m_data.m_type = value_t::object;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29009,8 +28835,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// implicitly convert a null value to an empty object
|
// implicitly convert a null value to an empty object
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_type = value_t::object;
|
|
||||||
m_data.m_value.object = create<object_t>();
|
m_data.m_value.object = create<object_t>();
|
||||||
|
m_data.m_type = value_t::object;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29945,8 +29771,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// transform a null object into an array
|
// transform a null object into an array
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_type = value_t::array;
|
|
||||||
m_data.m_value = value_t::array;
|
m_data.m_value = value_t::array;
|
||||||
|
m_data.m_type = value_t::array;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29978,8 +29804,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// transform a null object into an array
|
// transform a null object into an array
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_type = value_t::array;
|
|
||||||
m_data.m_value = value_t::array;
|
m_data.m_value = value_t::array;
|
||||||
|
m_data.m_type = value_t::array;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30010,8 +29836,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// transform a null object into an object
|
// transform a null object into an object
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_type = value_t::object;
|
|
||||||
m_data.m_value = value_t::object;
|
m_data.m_value = value_t::object;
|
||||||
|
m_data.m_type = value_t::object;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30066,8 +29892,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// transform a null object into an array
|
// transform a null object into an array
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_type = value_t::array;
|
|
||||||
m_data.m_value = value_t::array;
|
m_data.m_value = value_t::array;
|
||||||
|
m_data.m_type = value_t::array;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30091,8 +29917,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// transform a null object into an object
|
// transform a null object into an object
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_type = value_t::object;
|
|
||||||
m_data.m_value = value_t::object;
|
m_data.m_value = value_t::object;
|
||||||
|
m_data.m_type = value_t::object;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30273,8 +30099,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// implicitly convert a null value to an empty object
|
// implicitly convert a null value to an empty object
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_type = value_t::object;
|
|
||||||
m_data.m_value.object = create<object_t>();
|
m_data.m_value.object = create<object_t>();
|
||||||
|
m_data.m_type = value_t::object;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32500,6 +32326,55 @@ std::string format_as(const NLOHMANN_BASIC_JSON_TPL& j)
|
|||||||
return j.dump();
|
return j.dump();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline namespace literals
|
||||||
|
{
|
||||||
|
inline namespace json_literals
|
||||||
|
{
|
||||||
|
|
||||||
|
/// @brief user-defined string literal for JSON values
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/
|
||||||
|
JSON_HEDLEY_NON_NULL(1)
|
||||||
|
#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0)
|
||||||
|
inline nlohmann::json operator""_json(const char* s, std::size_t n)
|
||||||
|
#else
|
||||||
|
// GCC 4.8 requires a space between "" and suffix
|
||||||
|
inline nlohmann::json operator"" _json(const char* s, std::size_t n)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
return nlohmann::json::parse(s, s + n);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__cpp_char8_t)
|
||||||
|
JSON_HEDLEY_NON_NULL(1)
|
||||||
|
inline nlohmann::json operator""_json(const char8_t* s, std::size_t n)
|
||||||
|
{
|
||||||
|
return nlohmann::json::parse(reinterpret_cast<const char*>(s),
|
||||||
|
reinterpret_cast<const char*>(s) + n);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// @brief user-defined string literal for JSON pointer
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/
|
||||||
|
JSON_HEDLEY_NON_NULL(1)
|
||||||
|
#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0)
|
||||||
|
inline nlohmann::json::json_pointer operator""_json_pointer(const char* s, std::size_t n)
|
||||||
|
#else
|
||||||
|
// GCC 4.8 requires a space between "" and suffix
|
||||||
|
inline nlohmann::json::json_pointer operator"" _json_pointer(const char* s, std::size_t n)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
return nlohmann::json::json_pointer(std::string(s, n));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__cpp_char8_t)
|
||||||
|
inline nlohmann::json::json_pointer operator""_json_pointer(const char8_t* s, std::size_t n)
|
||||||
|
{
|
||||||
|
return nlohmann::json::json_pointer(std::string(reinterpret_cast<const char*>(s), n));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace json_literals
|
||||||
|
} // namespace literals
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|
||||||
///////////////////////
|
///////////////////////
|
||||||
@@ -32634,6 +32509,17 @@ struct formatter<nlohmann::NLOHMANN_BASIC_JSON_TPL, char> // NOLINT(cert-dcl58-c
|
|||||||
|
|
||||||
} // namespace std
|
} // namespace std
|
||||||
|
|
||||||
|
#if JSON_USE_GLOBAL_UDLS
|
||||||
|
#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0)
|
||||||
|
using nlohmann::literals::json_literals::operator""_json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
||||||
|
using nlohmann::literals::json_literals::operator""_json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
||||||
|
#else
|
||||||
|
// GCC 4.8 requires a space between "" and suffix
|
||||||
|
using nlohmann::literals::json_literals::operator"" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
||||||
|
using nlohmann::literals::json_literals::operator"" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
// #include <nlohmann/detail/macro_unscope.hpp>
|
// #include <nlohmann/detail/macro_unscope.hpp>
|
||||||
// __ _____ _____ _____
|
// __ _____ _____ _____
|
||||||
// __| | __| | | | JSON for Modern C++
|
// __| | __| | | | JSON for Modern C++
|
||||||
@@ -32662,6 +32548,7 @@ struct formatter<nlohmann::NLOHMANN_BASIC_JSON_TPL, char> // NOLINT(cert-dcl58-c
|
|||||||
#undef JSON_INLINE_VARIABLE
|
#undef JSON_INLINE_VARIABLE
|
||||||
#undef JSON_NO_UNIQUE_ADDRESS
|
#undef JSON_NO_UNIQUE_ADDRESS
|
||||||
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
||||||
|
#undef JSON_USE_GLOBAL_UDLS
|
||||||
|
|
||||||
#ifndef JSON_TEST_KEEP_MACROS
|
#ifndef JSON_TEST_KEEP_MACROS
|
||||||
#undef JSON_CATCH
|
#undef JSON_CATCH
|
||||||
@@ -32854,97 +32741,4 @@ struct formatter<nlohmann::NLOHMANN_BASIC_JSON_TPL, char> // NOLINT(cert-dcl58-c
|
|||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// The user-defined string literals are in a separate header, because their
|
|
||||||
// bodies instantiate the parser in every translation unit that includes them.
|
|
||||||
// Define JSON_NO_AUTOMATIC_UDLS to include <nlohmann/json_literals.hpp> only
|
|
||||||
// where needed.
|
|
||||||
#ifndef JSON_NO_AUTOMATIC_UDLS
|
|
||||||
// #include <nlohmann/json_literals.hpp>
|
|
||||||
// __ _____ _____ _____
|
|
||||||
// __| | __| | | | JSON for Modern C++
|
|
||||||
// | | |__ | | | | | | version 3.12.0
|
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
||||||
//
|
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
#ifndef INCLUDE_NLOHMANN_JSON_LITERALS_HPP_
|
|
||||||
#define INCLUDE_NLOHMANN_JSON_LITERALS_HPP_
|
|
||||||
|
|
||||||
#include <cstddef> // size_t
|
|
||||||
#include <string> // string
|
|
||||||
|
|
||||||
// #include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
// This header is included at the end of <nlohmann/json.hpp> unless
|
|
||||||
// JSON_NO_AUTOMATIC_UDLS is defined, and can be included on its own after that.
|
|
||||||
// Either way, the library's internal macros are no longer defined here (and the
|
|
||||||
// amalgamation inlines macro_scope.hpp only once), so only standard and public
|
|
||||||
// macros may be used below.
|
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
|
||||||
inline namespace literals
|
|
||||||
{
|
|
||||||
inline namespace json_literals
|
|
||||||
{
|
|
||||||
|
|
||||||
/// @brief user-defined string literal for JSON values
|
|
||||||
/// @sa https://json.nlohmann.me/api/operator_literal_json/
|
|
||||||
#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)
|
|
||||||
inline nlohmann::json operator""_json(const char* s, std::size_t n)
|
|
||||||
#else
|
|
||||||
// GCC 4.8 requires a space between "" and suffix
|
|
||||||
inline nlohmann::json operator"" _json(const char* s, std::size_t n)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
return nlohmann::json::parse(s, s + n);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__cpp_char8_t)
|
|
||||||
inline nlohmann::json operator""_json(const char8_t* s, std::size_t n)
|
|
||||||
{
|
|
||||||
return nlohmann::json::parse(reinterpret_cast<const char*>(s),
|
|
||||||
reinterpret_cast<const char*>(s) + n);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// @brief user-defined string literal for JSON pointer
|
|
||||||
/// @sa https://json.nlohmann.me/api/operator_literal_json_pointer/
|
|
||||||
#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)
|
|
||||||
inline nlohmann::json::json_pointer operator""_json_pointer(const char* s, std::size_t n)
|
|
||||||
#else
|
|
||||||
// GCC 4.8 requires a space between "" and suffix
|
|
||||||
inline nlohmann::json::json_pointer operator"" _json_pointer(const char* s, std::size_t n)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
return nlohmann::json::json_pointer(std::string(s, n));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__cpp_char8_t)
|
|
||||||
inline nlohmann::json::json_pointer operator""_json_pointer(const char8_t* s, std::size_t n)
|
|
||||||
{
|
|
||||||
return nlohmann::json::json_pointer(std::string(reinterpret_cast<const char*>(s), n));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace json_literals
|
|
||||||
} // namespace literals
|
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
|
||||||
|
|
||||||
#if !defined(JSON_USE_GLOBAL_UDLS) || JSON_USE_GLOBAL_UDLS
|
|
||||||
#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)
|
|
||||||
using nlohmann::literals::json_literals::operator""_json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
|
||||||
using nlohmann::literals::json_literals::operator""_json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
|
||||||
#else
|
|
||||||
// GCC 4.8 requires a space between "" and suffix
|
|
||||||
using nlohmann::literals::json_literals::operator"" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
|
||||||
using nlohmann::literals::json_literals::operator"" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // INCLUDE_NLOHMANN_JSON_LITERALS_HPP_
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // INCLUDE_NLOHMANN_JSON_HPP_
|
#endif // INCLUDE_NLOHMANN_JSON_HPP_
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
// __ _____ _____ _____
|
|
||||||
// __| | __| | | | JSON for Modern C++
|
|
||||||
// | | |__ | | | | | | version 3.12.0
|
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
||||||
//
|
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
#ifndef INCLUDE_NLOHMANN_JSON_LITERALS_HPP_
|
|
||||||
#define INCLUDE_NLOHMANN_JSON_LITERALS_HPP_
|
|
||||||
|
|
||||||
#include <cstddef> // size_t
|
|
||||||
#include <string> // string
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
// This header is included at the end of <nlohmann/json.hpp> unless
|
|
||||||
// JSON_NO_AUTOMATIC_UDLS is defined, and can be included on its own after that.
|
|
||||||
// Either way, the library's internal macros are no longer defined here (and the
|
|
||||||
// amalgamation inlines macro_scope.hpp only once), so only standard and public
|
|
||||||
// macros may be used below.
|
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
|
||||||
inline namespace literals
|
|
||||||
{
|
|
||||||
inline namespace json_literals
|
|
||||||
{
|
|
||||||
|
|
||||||
/// @brief user-defined string literal for JSON values
|
|
||||||
/// @sa https://json.nlohmann.me/api/operator_literal_json/
|
|
||||||
#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)
|
|
||||||
inline nlohmann::json operator""_json(const char* s, std::size_t n)
|
|
||||||
#else
|
|
||||||
// GCC 4.8 requires a space between "" and suffix
|
|
||||||
inline nlohmann::json operator"" _json(const char* s, std::size_t n)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
return nlohmann::json::parse(s, s + n);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__cpp_char8_t)
|
|
||||||
inline nlohmann::json operator""_json(const char8_t* s, std::size_t n)
|
|
||||||
{
|
|
||||||
return nlohmann::json::parse(reinterpret_cast<const char*>(s),
|
|
||||||
reinterpret_cast<const char*>(s) + n);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// @brief user-defined string literal for JSON pointer
|
|
||||||
/// @sa https://json.nlohmann.me/api/operator_literal_json_pointer/
|
|
||||||
#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)
|
|
||||||
inline nlohmann::json::json_pointer operator""_json_pointer(const char* s, std::size_t n)
|
|
||||||
#else
|
|
||||||
// GCC 4.8 requires a space between "" and suffix
|
|
||||||
inline nlohmann::json::json_pointer operator"" _json_pointer(const char* s, std::size_t n)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
return nlohmann::json::json_pointer(std::string(s, n));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__cpp_char8_t)
|
|
||||||
inline nlohmann::json::json_pointer operator""_json_pointer(const char8_t* s, std::size_t n)
|
|
||||||
{
|
|
||||||
return nlohmann::json::json_pointer(std::string(reinterpret_cast<const char*>(s), n));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace json_literals
|
|
||||||
} // namespace literals
|
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
|
||||||
|
|
||||||
#if !defined(JSON_USE_GLOBAL_UDLS) || JSON_USE_GLOBAL_UDLS
|
|
||||||
#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)
|
|
||||||
using nlohmann::literals::json_literals::operator""_json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
|
||||||
using nlohmann::literals::json_literals::operator""_json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
|
||||||
#else
|
|
||||||
// GCC 4.8 requires a space between "" and suffix
|
|
||||||
using nlohmann::literals::json_literals::operator"" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
|
||||||
using nlohmann::literals::json_literals::operator"" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers)
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // INCLUDE_NLOHMANN_JSON_LITERALS_HPP_
|
|
||||||
@@ -18,7 +18,6 @@ module;
|
|||||||
// See: https://github.com/nlohmann/json/issues/5103
|
// See: https://github.com/nlohmann/json/issues/5103
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <nlohmann/json_literals.hpp>
|
|
||||||
|
|
||||||
export module nlohmann.json;
|
export module nlohmann.json;
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,11 @@
|
|||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
|
#include <valarray>
|
||||||
|
#if JSON_HAS_RANGES
|
||||||
|
#include <ranges>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
// special test case to check if memory is leaked if constructor throws
|
// special test case to check if memory is leaked if constructor throws
|
||||||
@@ -394,3 +399,173 @@ TEST_CASE("bad my_allocator::construct")
|
|||||||
j["test"].push_back("should not leak");
|
j["test"].push_back("should not leak");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the no-exceptions CI job skips every CHECK_THROWS_AS, which would leave
|
||||||
|
// next_construct_fails set for the next allocation outside a check
|
||||||
|
#if !defined(JSON_NOEXCEPTION)
|
||||||
|
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>;
|
||||||
|
|
||||||
|
// Each of these creates a string, array, object, or binary value. The
|
||||||
|
// value must be created before the type is changed: otherwise, a failed
|
||||||
|
// creation left a value of the new type without anything behind it (an
|
||||||
|
// assertion in its destructor, a null pointer everywhere else) or, when
|
||||||
|
// an old value was destroyed first, with a pointer to that destroyed one.
|
||||||
|
|
||||||
|
SECTION("creating a binary value")
|
||||||
|
{
|
||||||
|
const std::vector<std::uint8_t> bytes = {1, 2, 3};
|
||||||
|
my_json _;
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(_ = my_json::binary(bytes), std::bad_alloc&);
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(_ = my_json::binary(bytes, 42), std::bad_alloc&);
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(_ = my_json::binary(std::vector<std::uint8_t>(bytes)), std::bad_alloc&);
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(_ = my_json::binary(std::vector<std::uint8_t>(bytes), 42), std::bad_alloc&);
|
||||||
|
next_construct_fails = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("turning a null value into an array or object")
|
||||||
|
{
|
||||||
|
my_json j;
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(j[0], std::bad_alloc&);
|
||||||
|
CHECK(j.is_null());
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(j["key"], std::bad_alloc&);
|
||||||
|
CHECK(j.is_null());
|
||||||
|
|
||||||
|
#ifdef JSON_HAS_CPP_17
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(j[std::string_view("key")], std::bad_alloc&);
|
||||||
|
CHECK(j.is_null());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(j.push_back(my_json(1)), std::bad_alloc&);
|
||||||
|
CHECK(j.is_null());
|
||||||
|
|
||||||
|
const my_json one = 1;
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(j.push_back(one), std::bad_alloc&);
|
||||||
|
CHECK(j.is_null());
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(j.push_back(my_json::object_t::value_type("key", 1)), std::bad_alloc&);
|
||||||
|
CHECK(j.is_null());
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(j.emplace_back(1), std::bad_alloc&);
|
||||||
|
CHECK(j.is_null());
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(j.emplace("key", 1), std::bad_alloc&);
|
||||||
|
CHECK(j.is_null());
|
||||||
|
|
||||||
|
const my_json object = {{"key", 1}};
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(j.update(object), std::bad_alloc&);
|
||||||
|
CHECK(j.is_null());
|
||||||
|
|
||||||
|
next_construct_fails = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("converting into an existing value")
|
||||||
|
{
|
||||||
|
// to_json replaces the value it is given; the old one must survive a
|
||||||
|
// failed creation of the new one
|
||||||
|
my_json j = "old";
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, std::string("new")), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, std::vector<int> {1, 2}), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
|
||||||
|
// with iterator debugging, the default constructor of VS 2015's
|
||||||
|
// std::vector is noexcept but constructs a proxy with the allocator,
|
||||||
|
// which terminates when this allocator throws
|
||||||
|
#if !(defined(_MSC_VER) && _MSC_VER < 1910 && defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL > 0)
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, std::vector<bool> {true, false}), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, std::map<std::string, int> {{"a", 1}}), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, my_json::binary_t({1, 2})), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
|
||||||
|
// the overloads for lvalues of the value types, for the value types
|
||||||
|
// themselves, and for the remaining compatible types
|
||||||
|
const std::string string = "new";
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, string), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, "new"), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
|
||||||
|
// to_json only moves a binary value that it converted from another
|
||||||
|
// container type, which my_json's std::vector<std::uint8_t> is not
|
||||||
|
using binary_constructor = nlohmann::detail::external_constructor<nlohmann::detail::value_t::binary>;
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(binary_constructor::construct(j, my_json::binary_t({1, 2})), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
|
||||||
|
my_json::array_t array = {1, 2};
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, array), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, std::move(array)), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
|
||||||
|
my_json::object_t object = {{"a", 1}};
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, object), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, std::move(object)), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, std::valarray<int> {1, 2}), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
|
||||||
|
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||||
|
const std::vector<int> numbers = {1, 2};
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, numbers | std::views::filter([](int /*unused*/)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
})), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
next_construct_fails = false;
|
||||||
|
nlohmann::to_json(j, std::vector<int> {1, 2});
|
||||||
|
CHECK(j == my_json({1, 2}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
+2
-43
@@ -1830,51 +1830,10 @@ TEST_CASE("CBOR")
|
|||||||
SECTION("invalid string in map")
|
SECTION("invalid string in map")
|
||||||
{
|
{
|
||||||
json _;
|
json _;
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0xa1, 0xff, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR object key: only string keys are supported, but found a break stop code; last byte: 0xFF", json::parse_error&);
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0xa1, 0xff, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0xFF", json::parse_error&);
|
||||||
CHECK(json::from_cbor(std::vector<uint8_t>({0xa1, 0xff, 0x01}), true, false).is_discarded());
|
CHECK(json::from_cbor(std::vector<uint8_t>({0xa1, 0xff, 0x01}), true, false).is_discarded());
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("non-string key (see #2766 and #3381)")
|
|
||||||
{
|
|
||||||
// only text strings map to JSON object keys; any other key is
|
|
||||||
// rejected with a message naming its type
|
|
||||||
const std::vector<std::pair<std::vector<std::uint8_t>, std::string>> cases =
|
|
||||||
{
|
|
||||||
{{0xA1, 0x01, 0x01}, "an unsigned integer; last byte: 0x01"},
|
|
||||||
{{0xA1, 0x20, 0x01}, "a negative integer; last byte: 0x20"},
|
|
||||||
{{0xA1, 0x41, 0x61, 0x01}, "a byte string; last byte: 0x41"},
|
|
||||||
{{0xA1, 0x80, 0x01}, "an array; last byte: 0x80"},
|
|
||||||
{{0xA1, 0xA0, 0x01}, "a map; last byte: 0xA0"},
|
|
||||||
{{0xA1, 0xC0, 0x61, 0x61, 0x01}, "a tag; last byte: 0xC0"},
|
|
||||||
{{0xA1, 0xF4, 0x01}, "a boolean; last byte: 0xF4"},
|
|
||||||
{{0xA1, 0xF5, 0x01}, "a boolean; last byte: 0xF5"},
|
|
||||||
{{0xA1, 0xF6, 0x01}, "null; last byte: 0xF6"},
|
|
||||||
{{0xA1, 0xF7, 0x01}, "undefined; last byte: 0xF7"},
|
|
||||||
{{0xA1, 0xF9, 0x3C, 0x00, 0x01}, "a floating-point number; last byte: 0xF9"},
|
|
||||||
{{0xA1, 0xFA, 0x3F, 0x80, 0x00, 0x00, 0x01}, "a floating-point number; last byte: 0xFA"},
|
|
||||||
{{0xA1, 0xFB, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, "a floating-point number; last byte: 0xFB"},
|
|
||||||
{{0xA1, 0xE0, 0x01}, "a simple value; last byte: 0xE0"},
|
|
||||||
{{0xA1, 0xF8, 0x20, 0x01}, "a simple value; last byte: 0xF8"},
|
|
||||||
// indefinite-length map
|
|
||||||
{{0xBF, 0x01, 0x01, 0xFF}, "an unsigned integer; last byte: 0x01"},
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const auto& c : cases)
|
|
||||||
{
|
|
||||||
CAPTURE(c.first)
|
|
||||||
const std::string expected = "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR object key: only string keys are supported, but found " + c.second;
|
|
||||||
json _;
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(c.first), expected.c_str(), json::parse_error&);
|
|
||||||
CHECK(json::from_cbor(c.first, true, false).is_discarded());
|
|
||||||
}
|
|
||||||
|
|
||||||
// a key of major type 3 with a reserved length is still reported as
|
|
||||||
// a malformed string, and a missing key as the end of input
|
|
||||||
json _;
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0xA1})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0xA1, 0x7C, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x7C", json::parse_error&);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("invalid UTF-8 in string (see #5529)")
|
SECTION("invalid UTF-8 in string (see #5529)")
|
||||||
{
|
{
|
||||||
// a two-character text string (major type 3) whose bytes are not
|
// a two-character text string (major type 3) whose bytes are not
|
||||||
@@ -2325,7 +2284,7 @@ TEST_CASE("CBOR indefinite-length strings do not recurse per chunk")
|
|||||||
SECTION("a break marker outside an indefinite-length string is not a string")
|
SECTION("a break marker outside an indefinite-length string is not a string")
|
||||||
{
|
{
|
||||||
// 0xFF only closes a string that was opened; on its own it is not one
|
// 0xFF only closes a string that was opened; on its own it is not one
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0xA1, 0xFF, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR object key: only string keys are supported, but found a break stop code; last byte: 0xFF", json::parse_error&);
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0xA1, 0xFF, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0xFF", json::parse_error&);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -666,7 +666,7 @@ TEST_CASE("parse_float_fast declines what it cannot convert exactly")
|
|||||||
// always safe: the caller then falls back to a slower, exact conversion.
|
// always safe: the caller then falls back to a slower, exact conversion.
|
||||||
const auto fast = [](const std::string & s, double & out)
|
const auto fast = [](const std::string & s, double & out)
|
||||||
{
|
{
|
||||||
return nlohmann::detail::parse_float_fast(s.data(), s.data() + s.size(), out);
|
return nlohmann::detail::parse_float_fast(s.data(), s.data() + s.size(), '.', out);
|
||||||
};
|
};
|
||||||
double out = 0;
|
double out = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -12,12 +12,7 @@
|
|||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <clocale>
|
#include <clocale>
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
struct ParserImpl final: public nlohmann::json_sax<json>
|
struct ParserImpl final: public nlohmann::json_sax<json>
|
||||||
{
|
{
|
||||||
@@ -180,208 +175,3 @@ TEST_CASE("locale-dependent test (LC_NUMERIC=de_DE)")
|
|||||||
MESSAGE("locale de_DE is not usable");
|
MESSAGE("locale de_DE is not usable");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
// records the numbers of a flat array and switches LC_NUMERIC to the given
|
|
||||||
// locale once the array opens - after the lexer was constructed, but before
|
|
||||||
// any number in the array is lexed
|
|
||||||
struct LocaleSwitchingSax final: public nlohmann::json_sax<json>
|
|
||||||
{
|
|
||||||
explicit LocaleSwitchingSax(const char* switch_to)
|
|
||||||
: locale_after_open(switch_to)
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool null() override
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool boolean(bool /*val*/) override
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool number_integer(json::number_integer_t /*val*/) override
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool number_unsigned(json::number_unsigned_t /*val*/) override
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool number_float(json::number_float_t val, const json::string_t& s) override
|
|
||||||
{
|
|
||||||
values.push_back(val);
|
|
||||||
strings.push_back(s);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool string(json::string_t& /*val*/) override
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool binary(json::binary_t& /*val*/) override
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool start_object(std::size_t /*val*/) override
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool key(json::string_t& /*val*/) override
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool end_object() override
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool start_array(std::size_t /*val*/) override
|
|
||||||
{
|
|
||||||
switched = std::setlocale(LC_NUMERIC, locale_after_open.c_str()) != nullptr;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool end_array() override
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool parse_error(std::size_t /*val*/, const std::string& /*val*/, const nlohmann::detail::exception& /*val*/) override
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string locale_after_open;
|
|
||||||
bool switched = false;
|
|
||||||
std::vector<json::number_float_t> values {}; // NOLINT(readability-redundant-member-init)
|
|
||||||
std::vector<json::string_t> strings {}; // NOLINT(readability-redundant-member-init)
|
|
||||||
};
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
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.
|
|
||||||
const std::vector<std::string> numbers = {"3.14159265358979323846", "1.5e-400", "12.34", "-0.000123456789012345678"};
|
|
||||||
std::string text = "[";
|
|
||||||
for (const auto& n : numbers)
|
|
||||||
{
|
|
||||||
text += (text.size() == 1 ? "" : ",") + n;
|
|
||||||
}
|
|
||||||
text += "]";
|
|
||||||
|
|
||||||
using long_double_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, long double>;
|
|
||||||
|
|
||||||
// reference values, parsed without a locale switch
|
|
||||||
REQUIRE(std::setlocale(LC_NUMERIC, "C") != nullptr);
|
|
||||||
const json expected = json::parse(text);
|
|
||||||
const long_double_json expected_ld = long_double_json::parse(text);
|
|
||||||
|
|
||||||
const std::array<std::pair<const char*, const char*>, 2> transitions =
|
|
||||||
{
|
|
||||||
{
|
|
||||||
{"C", "de_DE"},
|
|
||||||
{"de_DE", "C"}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const auto& transition : transitions)
|
|
||||||
{
|
|
||||||
CAPTURE(transition.first);
|
|
||||||
CAPTURE(transition.second);
|
|
||||||
|
|
||||||
if (std::setlocale(LC_NUMERIC, transition.first) == nullptr)
|
|
||||||
{
|
|
||||||
MESSAGE("locale is not usable");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SAX parsing
|
|
||||||
{
|
|
||||||
LocaleSwitchingSax sax(transition.second);
|
|
||||||
CHECK(json::sax_parse(text, &sax));
|
|
||||||
if (sax.switched)
|
|
||||||
{
|
|
||||||
CHECK(sax.values == expected.get<std::vector<json::number_float_t>>());
|
|
||||||
CHECK(sax.strings == numbers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DOM parsing with a callback
|
|
||||||
{
|
|
||||||
bool switched = false;
|
|
||||||
const auto cb = [&](int /*depth*/, json::parse_event_t event, json& /*parsed*/) noexcept
|
|
||||||
{
|
|
||||||
if (event == json::parse_event_t::array_start)
|
|
||||||
{
|
|
||||||
switched = std::setlocale(LC_NUMERIC, transition.second) != nullptr;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
const json j = json::parse(text, cb);
|
|
||||||
if (switched)
|
|
||||||
{
|
|
||||||
CHECK(j == expected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// a long double goes through std::strtold unless 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
|
|
||||||
{
|
|
||||||
if (event == long_double_json::parse_event_t::array_start)
|
|
||||||
{
|
|
||||||
switched = std::setlocale(LC_NUMERIC, transition.second) != nullptr;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
const long_double_json j = long_double_json::parse(text, cb);
|
|
||||||
if (switched)
|
|
||||||
{
|
|
||||||
CHECK(j == expected_ld);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CHECK(std::setlocale(LC_NUMERIC, "C") != nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (std::setlocale(LC_NUMERIC, name) == nullptr)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const std::string decimal_point = std::localeconv()->decimal_point;
|
|
||||||
if (decimal_point.size() < 2)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
CAPTURE(name);
|
|
||||||
tested = true;
|
|
||||||
|
|
||||||
// too many significant digits for Clinger's fast path, and an underflow
|
|
||||||
// that std::from_chars rejects: both reach the strtod fallback
|
|
||||||
json j;
|
|
||||||
CHECK_NOTHROW(j = json::parse("[3.14159265358979323846, 1.5e-400, -0.000123456789012345678]"));
|
|
||||||
CHECK(j.is_array());
|
|
||||||
CHECK(json::accept("3.14159265358979323846"));
|
|
||||||
|
|
||||||
// a value the locale-independent paths convert is not affected
|
|
||||||
CHECK(json::parse("12.5") == 12.5);
|
|
||||||
}
|
|
||||||
if (!tested)
|
|
||||||
{
|
|
||||||
MESSAGE("no locale with a multi-byte decimal point is usable");
|
|
||||||
}
|
|
||||||
|
|
||||||
CHECK(std::setlocale(LC_NUMERIC, "C") != nullptr);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1551,69 +1551,10 @@ TEST_CASE("MessagePack")
|
|||||||
SECTION("invalid string in map")
|
SECTION("invalid string in map")
|
||||||
{
|
{
|
||||||
json _;
|
json _;
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector<uint8_t>({0x81, 0xff, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing MessagePack object key: only string keys are supported, but found an integer; last byte: 0xFF", json::parse_error&);
|
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector<uint8_t>({0x81, 0xff, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing MessagePack string: expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0xFF", json::parse_error&);
|
||||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0x81, 0xff, 0x01}), true, false).is_discarded());
|
CHECK(json::from_msgpack(std::vector<uint8_t>({0x81, 0xff, 0x01}), true, false).is_discarded());
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("non-string key (see #3381)")
|
|
||||||
{
|
|
||||||
// only strings map to JSON object keys; any other key is rejected
|
|
||||||
// with a message naming its type
|
|
||||||
const std::vector<std::pair<std::vector<std::uint8_t>, std::string>> cases =
|
|
||||||
{
|
|
||||||
{{0x81, 0xC0, 0x01}, "nil; last byte: 0xC0"},
|
|
||||||
{{0x81, 0xC2, 0x01}, "a boolean; last byte: 0xC2"},
|
|
||||||
{{0x81, 0xC3, 0x01}, "a boolean; last byte: 0xC3"},
|
|
||||||
{{0x81, 0xCA, 0x3F, 0x80, 0x00, 0x00, 0x01}, "a float; last byte: 0xCA"},
|
|
||||||
{{0x81, 0xCB, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, "a float; last byte: 0xCB"},
|
|
||||||
{{0x81, 0xC4, 0x00, 0x01}, "a bin; last byte: 0xC4"},
|
|
||||||
{{0x81, 0xC5, 0x00, 0x00, 0x01}, "a bin; last byte: 0xC5"},
|
|
||||||
{{0x81, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x01}, "a bin; last byte: 0xC6"},
|
|
||||||
{{0x81, 0xC7, 0x00, 0x01, 0x01}, "an ext; last byte: 0xC7"},
|
|
||||||
{{0x81, 0xC8, 0x00, 0x00, 0x01, 0x01}, "an ext; last byte: 0xC8"},
|
|
||||||
{{0x81, 0xC9, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01}, "an ext; last byte: 0xC9"},
|
|
||||||
{{0x81, 0xD4, 0x01, 0x00, 0x01}, "an ext; last byte: 0xD4"},
|
|
||||||
{{0x81, 0xD5, 0x01, 0x00, 0x00, 0x01}, "an ext; last byte: 0xD5"},
|
|
||||||
{{0x81, 0xD6, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01}, "an ext; last byte: 0xD6"},
|
|
||||||
{{0x81, 0xD7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, "an ext; last byte: 0xD7"},
|
|
||||||
{{0x81, 0xD8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, "an ext; last byte: 0xD8"},
|
|
||||||
{{0x81, 0xCC, 0x01, 0x01}, "an integer; last byte: 0xCC"},
|
|
||||||
{{0x81, 0xCD, 0x00, 0x01, 0x01}, "an integer; last byte: 0xCD"},
|
|
||||||
{{0x81, 0xCE, 0x00, 0x00, 0x00, 0x01, 0x01}, "an integer; last byte: 0xCE"},
|
|
||||||
{{0x81, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01}, "an integer; last byte: 0xCF"},
|
|
||||||
{{0x81, 0xD0, 0x01, 0x01}, "an integer; last byte: 0xD0"},
|
|
||||||
{{0x81, 0xD1, 0x00, 0x01, 0x01}, "an integer; last byte: 0xD1"},
|
|
||||||
{{0x81, 0xD2, 0x00, 0x00, 0x00, 0x01, 0x01}, "an integer; last byte: 0xD2"},
|
|
||||||
{{0x81, 0xD3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01}, "an integer; last byte: 0xD3"},
|
|
||||||
{{0x81, 0x00, 0x01}, "an integer; last byte: 0x00"},
|
|
||||||
{{0x81, 0x7F, 0x01}, "an integer; last byte: 0x7F"},
|
|
||||||
{{0x81, 0xE0, 0x01}, "an integer; last byte: 0xE0"},
|
|
||||||
{{0x81, 0x80, 0x01}, "a map; last byte: 0x80"},
|
|
||||||
{{0x81, 0x8F, 0x01}, "a map; last byte: 0x8F"},
|
|
||||||
{{0x81, 0xDE, 0x00, 0x00, 0x01}, "a map; last byte: 0xDE"},
|
|
||||||
{{0x81, 0xDF, 0x00, 0x00, 0x00, 0x00, 0x01}, "a map; last byte: 0xDF"},
|
|
||||||
{{0x81, 0x90, 0x01}, "an array; last byte: 0x90"},
|
|
||||||
{{0x81, 0x9F, 0x01}, "an array; last byte: 0x9F"},
|
|
||||||
{{0x81, 0xDC, 0x00, 0x00, 0x01}, "an array; last byte: 0xDC"},
|
|
||||||
{{0x81, 0xDD, 0x00, 0x00, 0x00, 0x00, 0x01}, "an array; last byte: 0xDD"},
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const auto& c : cases)
|
|
||||||
{
|
|
||||||
CAPTURE(c.first)
|
|
||||||
const std::string expected = "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing MessagePack object key: only string keys are supported, but found " + c.second;
|
|
||||||
json _;
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(c.first), expected.c_str(), json::parse_error&);
|
|
||||||
CHECK(json::from_msgpack(c.first, true, false).is_discarded());
|
|
||||||
}
|
|
||||||
|
|
||||||
json _;
|
|
||||||
// the unused byte 0xC1 is still reported as a malformed string
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector<uint8_t>({0x81, 0xC1, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing MessagePack string: expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0xC1", json::parse_error&);
|
|
||||||
// a missing key is still reported as the end of input
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector<uint8_t>({0x81})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack string: unexpected end of input", json::parse_error&);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("invalid UTF-8 in string (see #5529)")
|
SECTION("invalid UTF-8 in string (see #5529)")
|
||||||
{
|
{
|
||||||
// a fixstr of length 2 (0xA0 | 2) whose bytes are not valid UTF-8
|
// a fixstr of length 2 (0xA0 | 2) whose bytes are not valid UTF-8
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
// __ _____ _____ _____
|
|
||||||
// __| | __| | | | 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
|
|
||||||
|
|
||||||
// This translation unit checks JSON_NO_AUTOMATIC_UDLS, which keeps
|
|
||||||
// <nlohmann/json.hpp> from including <nlohmann/json_literals.hpp> and thereby
|
|
||||||
// leaves out the user-defined string literals operator""_json and
|
|
||||||
// operator""_json_pointer (see #5294), and that including
|
|
||||||
// <nlohmann/json_literals.hpp> afterwards brings them back.
|
|
||||||
#define JSON_NO_AUTOMATIC_UDLS 1
|
|
||||||
|
|
||||||
#include "doctest_compatibility.h"
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
// An argument type whose associated namespace is the library namespace, so
|
|
||||||
// argument-dependent lookup of a literal operator called by its function name
|
|
||||||
// also searches the inline namespaces nlohmann::literals::json_literals.
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
|
||||||
struct no_automatic_udls_probe
|
|
||||||
{
|
|
||||||
operator const char* () const // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
// The calls below use a dependent argument, so a literal operator that is not
|
|
||||||
// declared at all is a substitution failure rather than a hard error: lookup is
|
|
||||||
// deferred to the point of instantiation, where it considers the declarations
|
|
||||||
// visible from here (the global using-declarations of JSON_USE_GLOBAL_UDLS) plus
|
|
||||||
// argument-dependent lookup (the literals in the library namespace).
|
|
||||||
template<typename T>
|
|
||||||
using json_udl_t = decltype(operator""_json(std::declval<T>(), std::size_t()));
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
using json_pointer_udl_t = decltype(operator""_json_pointer(std::declval<T>(), std::size_t()));
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
using has_json_udl = nlohmann::detail::is_detected<json_udl_t, T>;
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
using has_json_pointer_udl = nlohmann::detail::is_detected<json_pointer_udl_t, T>;
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_CASE("JSON_NO_AUTOMATIC_UDLS")
|
|
||||||
{
|
|
||||||
SECTION("literals are not declared")
|
|
||||||
{
|
|
||||||
// global namespace (JSON_USE_GLOBAL_UDLS defaults to 1)
|
|
||||||
CHECK_FALSE(has_json_udl<const char*>::value);
|
|
||||||
CHECK_FALSE(has_json_pointer_udl<const char*>::value);
|
|
||||||
|
|
||||||
// nlohmann::literals::json_literals
|
|
||||||
CHECK_FALSE(has_json_udl<nlohmann::no_automatic_udls_probe>::value);
|
|
||||||
CHECK_FALSE(has_json_pointer_udl<nlohmann::no_automatic_udls_probe>::value);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("the rest of the library keeps working")
|
|
||||||
{
|
|
||||||
const json j = json::parse(R"({"foo": {"bar": 42}})");
|
|
||||||
CHECK(j.dump() == R"({"foo":{"bar":42}})");
|
|
||||||
|
|
||||||
const json::json_pointer ptr("/foo/bar");
|
|
||||||
CHECK(j.at(ptr) == 42);
|
|
||||||
CHECK(j.contains(ptr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// the literals can still be added where they are needed
|
|
||||||
#include <nlohmann/json_literals.hpp>
|
|
||||||
|
|
||||||
TEST_CASE("JSON_NO_AUTOMATIC_UDLS with <nlohmann/json_literals.hpp>")
|
|
||||||
{
|
|
||||||
SECTION("global namespace")
|
|
||||||
{
|
|
||||||
CHECK("[1,2]"_json == json({1, 2}));
|
|
||||||
CHECK("/a/0"_json_pointer == json::json_pointer("/a/0"));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("nlohmann::literals::json_literals")
|
|
||||||
{
|
|
||||||
using namespace nlohmann::literals::json_literals; // NOLINT(google-build-using-namespace)
|
|
||||||
CHECK(R"({"a":[42]})"_json.at("/a/0"_json_pointer) == 42);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1018,7 +1018,7 @@ TEST_CASE("regression tests 1")
|
|||||||
};
|
};
|
||||||
|
|
||||||
json _;
|
json _;
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR object key: only string keys are supported, but found an array; last byte: 0x98", json::parse_error&);
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x98", json::parse_error&);
|
||||||
|
|
||||||
// related test case: nonempty UTF-8 string (indefinite length)
|
// related test case: nonempty UTF-8 string (indefinite length)
|
||||||
std::vector<uint8_t> const vec1 {0x7f, 0x61, 0x61};
|
std::vector<uint8_t> const vec1 {0x7f, 0x61, 0x61};
|
||||||
@@ -1065,7 +1065,7 @@ TEST_CASE("regression tests 1")
|
|||||||
};
|
};
|
||||||
|
|
||||||
json _;
|
json _;
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec1), "[json.exception.parse_error.113] parse error at byte 13: syntax error while parsing CBOR object key: only string keys are supported, but found a map; last byte: 0xB4", json::parse_error&);
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec1), "[json.exception.parse_error.113] parse error at byte 13: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0xB4", json::parse_error&);
|
||||||
|
|
||||||
// related test case: double-precision
|
// related test case: double-precision
|
||||||
std::vector<uint8_t> const vec2
|
std::vector<uint8_t> const vec2
|
||||||
@@ -1077,7 +1077,7 @@ TEST_CASE("regression tests 1")
|
|||||||
0x96, 0x96, 0xb4, 0xb4, 0xfa, 0x94, 0x94, 0x61,
|
0x96, 0x96, 0xb4, 0xb4, 0xfa, 0x94, 0x94, 0x61,
|
||||||
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0xfb
|
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0xfb
|
||||||
};
|
};
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec2), "[json.exception.parse_error.113] parse error at byte 13: syntax error while parsing CBOR object key: only string keys are supported, but found a map; last byte: 0xB4", json::parse_error&);
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec2), "[json.exception.parse_error.113] parse error at byte 13: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0xB4", json::parse_error&);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("issue #452 - Heap-buffer-overflow (OSS-Fuzz issue 585)")
|
SECTION("issue #452 - Heap-buffer-overflow (OSS-Fuzz issue 585)")
|
||||||
|
|||||||
Reference in New Issue
Block a user