Merge branch 'develop' of https://github.com/nlohmann/json into string_view

This commit is contained in:
Niels Lohmann 2021-07-16 08:45:53 +02:00
commit 8ba8c43707
No known key found for this signature in database
GPG Key ID: 7F3CEA63AE251B69
18 changed files with 406 additions and 141 deletions

22
.drone.yml Normal file
View File

@ -0,0 +1,22 @@
kind: pipeline
name: test-on-arm64
platform:
arch: arm64
steps:
- name: build
image: gcc
commands:
- wget https://github.com/Kitware/CMake/releases/download/v3.20.2/cmake-3.20.2.tar.gz
- tar xfz cmake-3.20.2.tar.gz
- cd cmake-3.20.2
- ./configure
- make cmake ctest -j10
- cd ..
- mkdir build
- cd build
- ../cmake-3.20.2/bin/cmake .. -DJSON_FastTests=ON
- make -j10
- cd test
- ../../cmake-3.20.2/bin/ctest -j10

View File

@ -24,3 +24,20 @@ jobs:
run: cmake --build build --parallel 10 run: cmake --build build --parallel 10
- name: test - name: test
run: cd build ; ctest -j 10 --output-on-failure run: cd build ; ctest -j 10 --output-on-failure
xcode_standards:
runs-on: macos-10.15
strategy:
matrix:
standard: [11, 14, 17, 20]
env:
DEVELOPER_DIR: /Applications/Xcode_12.4.app/Contents/Developer
steps:
- uses: actions/checkout@v2
- name: cmake
run: cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug -DJSON_BuildTests=On -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DCMAKE_CXX_STANDARD_REQUIRED=ON
- name: build
run: cmake --build build --parallel 10
- name: test
run: cd build ; ctest -j 10 --output-on-failure

View File

@ -98,7 +98,7 @@ jobs:
runs-on: windows-latest runs-on: windows-latest
strategy: strategy:
matrix: matrix:
version: [10, 11] version: [11, 12]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2

View File

@ -12,7 +12,7 @@ project(nlohmann_json VERSION 3.9.1 LANGUAGES CXX)
## ##
set(MAIN_PROJECT OFF) set(MAIN_PROJECT OFF)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(MAIN_PROJECT ON) set(MAIN_PROJECT ON)
endif() endif()
## ##
@ -113,8 +113,8 @@ endif()
# Install a pkg-config file, so other tools can find this. # Install a pkg-config file, so other tools can find this.
CONFIGURE_FILE( CONFIGURE_FILE(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/pkg-config.pc.in" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/pkg-config.pc.in"
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc" "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc"
) )
## ##
@ -160,7 +160,7 @@ if(JSON_Install)
FILES ${NLOHMANN_NATVIS_FILE} FILES ${NLOHMANN_NATVIS_FILE}
DESTINATION . DESTINATION .
) )
endif() endif()
export( export(
TARGETS ${NLOHMANN_JSON_TARGET_NAME} TARGETS ${NLOHMANN_JSON_TARGET_NAME}
NAMESPACE ${PROJECT_NAME}:: NAMESPACE ${PROJECT_NAME}::

View File

@ -206,7 +206,7 @@ If you are using the [Meson Build System](https://mesonbuild.com), add this sour
The provided meson.build can also be used as an alternative to cmake for installing `nlohmann_json` system-wide in which case a pkg-config file is installed. To use it, simply have your build system require the `nlohmann_json` pkg-config dependency. In Meson, it is preferred to use the [`dependency()`](https://mesonbuild.com/Reference-manual.html#dependency) object with a subproject fallback, rather than using the subproject directly. The provided meson.build can also be used as an alternative to cmake for installing `nlohmann_json` system-wide in which case a pkg-config file is installed. To use it, simply have your build system require the `nlohmann_json` pkg-config dependency. In Meson, it is preferred to use the [`dependency()`](https://mesonbuild.com/Reference-manual.html#dependency) object with a subproject fallback, rather than using the subproject directly.
If you are using [Conan](https://www.conan.io/) to manage your dependencies, merely add `nlohmann_json/x.y.z` to your `conanfile`'s requires, where `x.y.z` is the release version you want to use. Please file issues [here](https://github.com/conan-io/conan-center-index/issues) if you experience problems with the packages. If you are using [Conan](https://www.conan.io/) to manage your dependencies, merely add [`nlohmann_json/x.y.z`](https://conan.io/center/nlohmann_json) to your `conanfile`'s requires, where `x.y.z` is the release version you want to use. Please file issues [here](https://github.com/conan-io/conan-center-index/issues) if you experience problems with the packages.
If you are using [Spack](https://www.spack.io/) to manage your dependencies, you can use the [`nlohmann-json` package](https://spack.readthedocs.io/en/latest/package_list.html#nlohmann-json). Please see the [spack project](https://github.com/spack/spack) for any issues regarding the packaging. If you are using [Spack](https://www.spack.io/) to manage your dependencies, you can use the [`nlohmann-json` package](https://spack.readthedocs.io/en/latest/package_list.html#nlohmann-json). Please see the [spack project](https://github.com/spack/spack) for any issues regarding the packaging.
@ -870,7 +870,7 @@ assert(p == p2);
To make this work with one of your types, you only need to provide two functions: To make this work with one of your types, you only need to provide two functions:
```cpp ```cpp
using nlohmann::json; using json = nlohmann::json;
namespace ns { namespace ns {
void to_json(json& j, const person& p) { void to_json(json& j, const person& p) {
@ -1126,7 +1126,7 @@ Other Important points:
### Binary formats (BSON, CBOR, MessagePack, and UBJSON) ### Binary formats (BSON, CBOR, MessagePack, and UBJSON)
Though JSON is a ubiquitous data format, it is not a very compact format suitable for data exchange, for instance over a network. Hence, the library supports [BSON](http://bsonspec.org) (Binary JSON), [CBOR](https://cbor.io) (Concise Binary Object Representation), [MessagePack](https://msgpack.org), and [UBJSON](http://ubjson.org) (Universal Binary JSON Specification) to efficiently encode JSON values to byte vectors and to decode such vectors. Though JSON is a ubiquitous data format, it is not a very compact format suitable for data exchange, for instance over a network. Hence, the library supports [BSON](https://bsonspec.org) (Binary JSON), [CBOR](https://cbor.io) (Concise Binary Object Representation), [MessagePack](https://msgpack.org), and [UBJSON](https://ubjson.org) (Universal Binary JSON Specification) to efficiently encode JSON values to byte vectors and to decode such vectors.
```cpp ```cpp
// create a JSON value // create a JSON value
@ -1227,7 +1227,7 @@ Please note:
- Unsupported versions of GCC and Clang are rejected by `#error` directives. This can be switched off by defining `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK`. Note that you can expect no support in this case. - Unsupported versions of GCC and Clang are rejected by `#error` directives. This can be switched off by defining `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK`. Note that you can expect no support in this case.
The following compilers are currently used in continuous integration at [Travis](https://travis-ci.org/nlohmann/json), [AppVeyor](https://ci.appveyor.com/project/nlohmann/json), and [GitHub Actions](https://github.com/nlohmann/json/actions): The following compilers are currently used in continuous integration at [Travis](https://travis-ci.org/nlohmann/json), [AppVeyor](https://ci.appveyor.com/project/nlohmann/json), [Drone CI](https://cloud.drone.io/nlohmann/json), and [GitHub Actions](https://github.com/nlohmann/json/actions):
| Compiler | Operating System | CI Provider | | Compiler | Operating System | CI Provider |
|-------------------------------------------------------------------|--------------------|----------------| |-------------------------------------------------------------------|--------------------|----------------|
@ -1256,6 +1256,7 @@ The following compilers are currently used in continuous integration at [Travis]
| GCC 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04) | Ubuntu 20.04.2 LTS | GitHub Actions | | GCC 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04) | Ubuntu 20.04.2 LTS | GitHub Actions |
| GCC 10.2.0 (Ubuntu 10.2.0-5ubuntu1~20.04) | Ubuntu 20.04.2 LTS | GitHub Actions | | GCC 10.2.0 (Ubuntu 10.2.0-5ubuntu1~20.04) | Ubuntu 20.04.2 LTS | GitHub Actions |
| GCC 11.0.1 20210321 (experimental) | Ubuntu 20.04.2 LTS | GitHub Actions | | GCC 11.0.1 20210321 (experimental) | Ubuntu 20.04.2 LTS | GitHub Actions |
| GCC 11.1.0 | Ubuntu (aarch64) | Drone CI |
| Clang 3.5.2 (3.5.2-3ubuntu1) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 3.5.2 (3.5.2-3ubuntu1) | Ubuntu 20.04.2 LTS | GitHub Actions |
| Clang 3.6.2 (3.6.2-3ubuntu2) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 3.6.2 (3.6.2-3ubuntu2) | Ubuntu 20.04.2 LTS | GitHub Actions |
| Clang 3.7.1 (3.7.1-2ubuntu2) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 3.7.1 (3.7.1-2ubuntu2) | Ubuntu 20.04.2 LTS | GitHub Actions |
@ -1281,9 +1282,9 @@ The following compilers are currently used in continuous integration at [Travis]
## License ## License
<img align="right" src="http://opensource.org/trademarks/opensource/OSI-Approved-License-100x137.png"> <img align="right" src="https://opensource.org/trademarks/opensource/OSI-Approved-License-100x137.png">
The class is licensed under the [MIT License](http://opensource.org/licenses/MIT): The class is licensed under the [MIT License](https://opensource.org/licenses/MIT):
Copyright &copy; 2013-2021 [Niels Lohmann](https://nlohmann.me) Copyright &copy; 2013-2021 [Niels Lohmann](https://nlohmann.me)
@ -1295,9 +1296,9 @@ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR I
* * * * * *
The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is licensed under the [MIT License](http://opensource.org/licenses/MIT) (see above). Copyright &copy; 2008-2009 [Björn Hoehrmann](https://bjoern.hoehrmann.de/) <bjoern@hoehrmann.de> The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright &copy; 2008-2009 [Björn Hoehrmann](https://bjoern.hoehrmann.de/) <bjoern@hoehrmann.de>
The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](http://opensource.org/licenses/MIT) (see above). Copyright &copy; 2009 [Florian Loitsch](https://florian.loitsch.com/) The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright &copy; 2009 [Florian Loitsch](https://florian.loitsch.com/)
The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/). The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/).
@ -1573,7 +1574,7 @@ The library itself consists of a single header file licensed under the MIT licen
- [**libFuzzer**](https://llvm.org/docs/LibFuzzer.html) to implement fuzz testing for OSS-Fuzz - [**libFuzzer**](https://llvm.org/docs/LibFuzzer.html) to implement fuzz testing for OSS-Fuzz
- [**OSS-Fuzz**](https://github.com/google/oss-fuzz) for continuous fuzz testing of the library ([project repository](https://github.com/google/oss-fuzz/tree/master/projects/json)) - [**OSS-Fuzz**](https://github.com/google/oss-fuzz) for continuous fuzz testing of the library ([project repository](https://github.com/google/oss-fuzz/tree/master/projects/json))
- [**Probot**](https://probot.github.io) for automating maintainer tasks such as closing stale issues, requesting missing information, or detecting toxic comments. - [**Probot**](https://probot.github.io) for automating maintainer tasks such as closing stale issues, requesting missing information, or detecting toxic comments.
- [**send_to_wandbox**](https://github.com/nlohmann/json/blob/develop/doc/scripts/send_to_wandbox.py) to send code examples to [Wandbox](http://melpon.org/wandbox) - [**send_to_wandbox**](https://github.com/nlohmann/json/blob/develop/doc/scripts/send_to_wandbox.py) to send code examples to [Wandbox](https://wandbox.org)
- [**Travis**](https://travis-ci.org) for [continuous integration](https://travis-ci.org/nlohmann/json) on Linux and macOS - [**Travis**](https://travis-ci.org) for [continuous integration](https://travis-ci.org/nlohmann/json) on Linux and macOS
- [**Valgrind**](https://valgrind.org) to check for correct memory management - [**Valgrind**](https://valgrind.org) to check for correct memory management
- [**Wandbox**](https://wandbox.org) for [online examples](https://wandbox.org/permlink/3lCHrFUZANONKv7a) - [**Wandbox**](https://wandbox.org) for [online examples](https://wandbox.org/permlink/3lCHrFUZANONKv7a)
@ -1649,7 +1650,7 @@ $ ctest --output-on-failure
Note that during the `ctest` stage, several JSON test files are downloaded from an [external repository](https://github.com/nlohmann/json_test_data). If policies forbid downloading artifacts during testing, you can download the files yourself and pass the directory with the test files via `-DJSON_TestDataDirectory=path` to CMake. Then, no Internet connectivity is required. See [issue #2189](https://github.com/nlohmann/json/issues/2189) for more information. Note that during the `ctest` stage, several JSON test files are downloaded from an [external repository](https://github.com/nlohmann/json_test_data). If policies forbid downloading artifacts during testing, you can download the files yourself and pass the directory with the test files via `-DJSON_TestDataDirectory=path` to CMake. Then, no Internet connectivity is required. See [issue #2189](https://github.com/nlohmann/json/issues/2189) for more information.
In case you have downloaded the library rather than checked out the code via Git, test `cmake_fetch_content_configure`. Please execute `ctest -LE git_required` to skip these tests. See [issue #2189](https://github.com/nlohmann/json/issues/2189) for more information. In case you have downloaded the library rather than checked out the code via Git, test `cmake_fetch_content_configure` will fail. Please execute `ctest -LE git_required` to skip these tests. See [issue #2189](https://github.com/nlohmann/json/issues/2189) for more information.
Some tests change the installed files and hence make the whole process not reproducible. Please execute `ctest -LE not_reproducible` to skip these tests. See [issue #2324](https://github.com/nlohmann/json/issues/2324) for more information. Some tests change the installed files and hence make the whole process not reproducible. Please execute `ctest -LE not_reproducible` to skip these tests. See [issue #2324](https://github.com/nlohmann/json/issues/2324) for more information.

View File

@ -11,6 +11,7 @@ include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
benchmark benchmark
GIT_REPOSITORY https://github.com/google/benchmark.git GIT_REPOSITORY https://github.com/google/benchmark.git
GIT_TAG origin/main
GIT_SHALLOW TRUE GIT_SHALLOW TRUE
) )

View File

@ -467,7 +467,7 @@ add_custom_target(ci_test_diagnostics
############################################################################### ###############################################################################
add_custom_target(ci_test_coverage add_custom_target(ci_test_coverage
COMMAND CXX=${GCC_TOOL} ${CMAKE_COMMAND} COMMAND CXX=g++ ${CMAKE_COMMAND}
-DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_CXX_FLAGS="--coverage;-fprofile-arcs;-ftest-coverage" -DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_CXX_FLAGS="--coverage;-fprofile-arcs;-ftest-coverage"
-DJSON_BuildTests=ON -DJSON_MultipleHeaders=ON -DJSON_BuildTests=ON -DJSON_MultipleHeaders=ON
-DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD_REQUIRED=ON -DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD_REQUIRED=ON
@ -476,7 +476,7 @@ add_custom_target(ci_test_coverage
COMMAND cd ${PROJECT_BINARY_DIR}/build_coverage && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure COMMAND cd ${PROJECT_BINARY_DIR}/build_coverage && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
COMMAND ${LCOV_TOOL} --directory . --capture --output-file json.info --rc lcov_branch_coverage=1 COMMAND ${LCOV_TOOL} --directory . --capture --output-file json.info --rc lcov_branch_coverage=1
COMMAND ${LCOV_TOOL} -e json.info ${SRC_FILES} --output-file json.info.filtered --gcov-tool ${GCOV_TOOL} --rc lcov_branch_coverage=1 COMMAND ${LCOV_TOOL} -e json.info ${SRC_FILES} --output-file json.info.filtered --rc lcov_branch_coverage=1
COMMAND ${CMAKE_SOURCE_DIR}/test/thirdparty/imapdl/filterbr.py json.info.filtered > json.info.filtered.noexcept COMMAND ${CMAKE_SOURCE_DIR}/test/thirdparty/imapdl/filterbr.py json.info.filtered > json.info.filtered.noexcept
COMMAND genhtml --title "JSON for Modern C++" --legend --demangle-cpp --output-directory html --show-details --branch-coverage json.info.filtered.noexcept COMMAND genhtml --title "JSON for Modern C++" --legend --demangle-cpp --output-directory html --show-details --branch-coverage json.info.filtered.noexcept
@ -561,7 +561,7 @@ add_custom_target(ci_clang_analyze
############################################################################### ###############################################################################
add_custom_target(ci_cppcheck add_custom_target(ci_cppcheck
COMMAND ${CPPCHECK_TOOL} --enable=warning --inline-suppr --inconclusive --force --std=c++11 ${PROJECT_SOURCE_DIR}/single_include/nlohmann/json.hpp --error-exitcode=1 COMMAND ${CPPCHECK_TOOL} --enable=warning --suppress=missingReturn --inline-suppr --inconclusive --force --std=c++11 ${PROJECT_SOURCE_DIR}/single_include/nlohmann/json.hpp --error-exitcode=1
COMMENT "Check code with Cppcheck" COMMENT "Check code with Cppcheck"
) )

View File

@ -54,7 +54,7 @@ assert(p == p2);
To make this work with one of your types, you only need to provide two functions: To make this work with one of your types, you only need to provide two functions:
```cpp ```cpp
using nlohmann::json; using json = nlohmann::json;
namespace ns { namespace ns {
void to_json(json& j, const person& p) { void to_json(json& j, const person& p) {

View File

@ -32,6 +32,10 @@ When defining `JSON_NOEXCEPTION`, `#!cpp try` is replaced by `#!cpp if (true)`,
The same effect is achieved by setting the compiler flag `-fno-exceptions`. The same effect is achieved by setting the compiler flag `-fno-exceptions`.
## `JSON_NO_IO`
When defined, headers `<cstdio>`, `<ios>`, `<iosfwd>`, `<istream>`, and `<ostream>` are not included and parse functions relying on these headers are excluded. This is relevant for environment where these I/O functions are disallowed for security reasons (e.g., Intel Software Guard Extensions (SGX)).
## `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK` ## `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK`
When defined, the library will not create a compile error when a known unsupported compiler is detected. This allows to use the library with compilers that do not fully support C++11 and may only work if unsupported features are not used. When defined, the library will not create a compile error when a known unsupported compiler is detected. This allows to use the library with compilers that do not fully support C++11 and may only work if unsupported features are not used.

View File

@ -96,6 +96,21 @@ This is the same behavior as the code `#!c double x = 3.141592653589793238462643
- All integers outside the range $[-2^{63}, 2^{64}-1]$, as well as floating-point numbers are stored as `double`. - All integers outside the range $[-2^{63}, 2^{64}-1]$, as well as floating-point numbers are stored as `double`.
This also concurs with the specification above. This also concurs with the specification above.
### Zeros
The JSON number grammar allows for different ways to express zero, and this library will store zeros differently:
| Literal | Stored value and type | Serialization |
| ------- | --------------------- | ------------- |
| `0` | `#!c std::uint64_t(0)` | `0` |
| `-0` | `#!c std::int64_t(0)` | `0` |
| `0.0` | `#!c double(0.0)` | `0.0` |
| `-0.0` | `#!c double(-0.0)` | `-0.0` |
| `0E0` | `#!c double(0.0)` | `0.0` |
| `-0E0` | `#!c double(-0.0)` | `-0.0` |
That is, `-0` is stored as a signed integer, but the serialization does not reproduce the `-`.
### Number serialization ### Number serialization
- Integer numbers are serialized as is; that is, no scientific notation is used. - Integer numbers are serialized as is; that is, no scientific notation is used.

View File

@ -22,6 +22,13 @@ namespace detail
// constructors // // constructors //
////////////////// //////////////////
/*
* Note all external_constructor<>::construct functions need to call
* j.m_value.destroy(j.m_type) to avoid a memory leak in case j contains an
* allocated value (e.g., a string). See bug issue
* https://github.com/nlohmann/json/issues/2865 for more information.
*/
template<value_t> struct external_constructor; template<value_t> struct external_constructor;
template<> template<>
@ -30,6 +37,7 @@ struct external_constructor<value_t::boolean>
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::boolean; j.m_type = value_t::boolean;
j.m_value = b; j.m_value = b;
j.assert_invariant(); j.assert_invariant();
@ -42,6 +50,7 @@ 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)
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::string; j.m_type = value_t::string;
j.m_value = s; j.m_value = s;
j.assert_invariant(); j.assert_invariant();
@ -50,6 +59,7 @@ struct external_constructor<value_t::string>
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)
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::string; j.m_type = value_t::string;
j.m_value = std::move(s); j.m_value = std::move(s);
j.assert_invariant(); j.assert_invariant();
@ -60,6 +70,7 @@ 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)
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::string; j.m_type = value_t::string;
j.m_value.string = j.template create<typename BasicJsonType::string_t>(str); j.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
j.assert_invariant(); j.assert_invariant();
@ -72,6 +83,7 @@ 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)
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::binary; j.m_type = value_t::binary;
j.m_value = typename BasicJsonType::binary_t(b); j.m_value = typename BasicJsonType::binary_t(b);
j.assert_invariant(); j.assert_invariant();
@ -80,6 +92,7 @@ struct external_constructor<value_t::binary>
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)
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::binary; j.m_type = value_t::binary;
j.m_value = typename BasicJsonType::binary_t(std::move(b));; j.m_value = typename BasicJsonType::binary_t(std::move(b));;
j.assert_invariant(); j.assert_invariant();
@ -92,6 +105,7 @@ struct external_constructor<value_t::number_float>
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::number_float; j.m_type = value_t::number_float;
j.m_value = val; j.m_value = val;
j.assert_invariant(); j.assert_invariant();
@ -104,6 +118,7 @@ struct external_constructor<value_t::number_unsigned>
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::number_unsigned; j.m_type = value_t::number_unsigned;
j.m_value = val; j.m_value = val;
j.assert_invariant(); j.assert_invariant();
@ -116,6 +131,7 @@ struct external_constructor<value_t::number_integer>
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::number_integer; j.m_type = value_t::number_integer;
j.m_value = val; j.m_value = val;
j.assert_invariant(); j.assert_invariant();
@ -128,6 +144,7 @@ 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)
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::array; j.m_type = value_t::array;
j.m_value = arr; j.m_value = arr;
j.set_parents(); j.set_parents();
@ -137,6 +154,7 @@ 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)
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::array; j.m_type = value_t::array;
j.m_value = std::move(arr); j.m_value = std::move(arr);
j.set_parents(); j.set_parents();
@ -150,6 +168,8 @@ struct external_constructor<value_t::array>
{ {
using std::begin; using std::begin;
using std::end; using std::end;
j.m_value.destroy(j.m_type);
j.m_type = value_t::array; j.m_type = value_t::array;
j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr)); j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
j.set_parents(); j.set_parents();
@ -159,6 +179,7 @@ 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_value.destroy(j.m_type);
j.m_type = value_t::array; j.m_type = value_t::array;
j.m_value = value_t::array; j.m_value = value_t::array;
j.m_value.array->reserve(arr.size()); j.m_value.array->reserve(arr.size());
@ -174,6 +195,7 @@ 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)
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::array; j.m_type = value_t::array;
j.m_value = value_t::array; j.m_value = value_t::array;
j.m_value.array->resize(arr.size()); j.m_value.array->resize(arr.size());
@ -192,6 +214,7 @@ 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)
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::object; j.m_type = value_t::object;
j.m_value = obj; j.m_value = obj;
j.set_parents(); j.set_parents();
@ -201,6 +224,7 @@ 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)
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::object; j.m_type = value_t::object;
j.m_value = std::move(obj); j.m_value = std::move(obj);
j.set_parents(); j.set_parents();
@ -214,6 +238,7 @@ struct external_constructor<value_t::object>
using std::begin; using std::begin;
using std::end; using std::end;
j.m_value.destroy(j.m_type);
j.m_type = value_t::object; j.m_type = value_t::object;
j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj)); j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
j.set_parents(); j.set_parents();

View File

@ -2,9 +2,7 @@
#include <array> // array #include <array> // array
#include <cstddef> // size_t #include <cstddef> // size_t
#include <cstdio> //FILE *
#include <cstring> // strlen #include <cstring> // strlen
#include <istream> // istream
#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next #include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next
#include <memory> // shared_ptr, make_shared, addressof #include <memory> // shared_ptr, make_shared, addressof
#include <numeric> // accumulate #include <numeric> // accumulate
@ -12,6 +10,11 @@
#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer #include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
#include <utility> // pair, declval #include <utility> // pair, declval
#ifndef JSON_NO_IO
#include <cstdio> //FILE *
#include <istream> // istream
#endif // JSON_NO_IO
#include <nlohmann/detail/iterators/iterator_traits.hpp> #include <nlohmann/detail/iterators/iterator_traits.hpp>
#include <nlohmann/detail/macro_scope.hpp> #include <nlohmann/detail/macro_scope.hpp>
@ -26,6 +29,7 @@ enum class input_format_t { json, cbor, msgpack, ubjson, bson };
// input adapters // // input adapters //
//////////////////// ////////////////////
#ifndef JSON_NO_IO
/*! /*!
Input adapter for stdio file access. This adapter read only 1 byte and do not use any Input adapter for stdio file access. This adapter read only 1 byte and do not use any
buffer. This adapter is a very low level adapter. buffer. This adapter is a very low level adapter.
@ -105,7 +109,7 @@ class input_stream_adapter
{ {
auto res = sb->sbumpc(); auto res = sb->sbumpc();
// set eof manually, as we don't use the istream interface. // set eof manually, as we don't use the istream interface.
if (JSON_HEDLEY_UNLIKELY(res == EOF)) if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))
{ {
is->clear(is->rdstate() | std::ios::eofbit); is->clear(is->rdstate() | std::ios::eofbit);
} }
@ -117,6 +121,7 @@ class input_stream_adapter
std::istream* is = nullptr; std::istream* is = nullptr;
std::streambuf* sb = nullptr; std::streambuf* sb = nullptr;
}; };
#endif // JSON_NO_IO
// General-purpose iterator-based adapter. It might not be as fast as // General-purpose iterator-based adapter. It might not be as fast as
// theoretically possible for some containers, but it is extremely versatile. // theoretically possible for some containers, but it is extremely versatile.
@ -403,6 +408,7 @@ typename container_input_adapter_factory_impl::container_input_adapter_factory<C
return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(container); return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(container);
} }
#ifndef JSON_NO_IO
// Special cases with fast paths // Special cases with fast paths
inline file_input_adapter input_adapter(std::FILE* file) inline file_input_adapter input_adapter(std::FILE* file)
{ {
@ -418,6 +424,7 @@ inline input_stream_adapter input_adapter(std::istream&& stream)
{ {
return input_stream_adapter(stream); return input_stream_adapter(stream);
} }
#endif // JSON_NO_IO
using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const char*>(), std::declval<const char*>())); using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const char*>(), std::declval<const char*>()));

View File

@ -2,12 +2,16 @@
#include <algorithm> // copy #include <algorithm> // copy
#include <cstddef> // size_t #include <cstddef> // size_t
#include <ios> // streamsize
#include <iterator> // back_inserter #include <iterator> // back_inserter
#include <memory> // shared_ptr, make_shared #include <memory> // shared_ptr, make_shared
#include <ostream> // basic_ostream
#include <string> // basic_string #include <string> // basic_string
#include <vector> // vector #include <vector> // vector
#ifndef JSON_NO_IO
#include <ios> // streamsize
#include <ostream> // basic_ostream
#endif // JSON_NO_IO
#include <nlohmann/detail/macro_scope.hpp> #include <nlohmann/detail/macro_scope.hpp>
namespace nlohmann namespace nlohmann
@ -56,6 +60,7 @@ class output_vector_adapter : public output_adapter_protocol<CharType>
std::vector<CharType>& v; std::vector<CharType>& v;
}; };
#ifndef JSON_NO_IO
/// output adapter for output streams /// output adapter for output streams
template<typename CharType> template<typename CharType>
class output_stream_adapter : public output_adapter_protocol<CharType> class output_stream_adapter : public output_adapter_protocol<CharType>
@ -79,6 +84,7 @@ class output_stream_adapter : public output_adapter_protocol<CharType>
private: private:
std::basic_ostream<CharType>& stream; std::basic_ostream<CharType>& stream;
}; };
#endif // JSON_NO_IO
/// output adapter for basic_string /// output adapter for basic_string
template<typename CharType, typename StringType = std::basic_string<CharType>> template<typename CharType, typename StringType = std::basic_string<CharType>>
@ -111,8 +117,10 @@ class output_adapter
output_adapter(std::vector<CharType>& vec) output_adapter(std::vector<CharType>& vec)
: oa(std::make_shared<output_vector_adapter<CharType>>(vec)) {} : oa(std::make_shared<output_vector_adapter<CharType>>(vec)) {}
#ifndef JSON_NO_IO
output_adapter(std::basic_ostream<CharType>& s) output_adapter(std::basic_ostream<CharType>& s)
: oa(std::make_shared<output_stream_adapter<CharType>>(s)) {} : oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}
#endif // JSON_NO_IO
output_adapter(StringType& s) output_adapter(StringType& s)
: oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {} : oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {}

View File

@ -499,7 +499,7 @@ class serializer
{ {
case error_handler_t::strict: case error_handler_t::strict:
{ {
std::string sn(3, '\0'); std::string sn(9, '\0');
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
(std::snprintf)(&sn[0], sn.size(), "%.2X", byte); (std::snprintf)(&sn[0], sn.size(), "%.2X", byte);
JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn, BasicJsonType())); JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn, BasicJsonType()));
@ -594,7 +594,7 @@ class serializer
{ {
case error_handler_t::strict: case error_handler_t::strict:
{ {
std::string sn(3, '\0'); std::string sn(9, '\0');
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
(std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast<std::uint8_t>(s.back())); (std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast<std::uint8_t>(s.back()));
JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn, BasicJsonType())); JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn, BasicJsonType()));

View File

@ -38,7 +38,9 @@ SOFTWARE.
#include <cstddef> // nullptr_t, ptrdiff_t, size_t #include <cstddef> // nullptr_t, ptrdiff_t, size_t
#include <functional> // hash, less #include <functional> // hash, less
#include <initializer_list> // initializer_list #include <initializer_list> // initializer_list
#include <iosfwd> // istream, ostream #ifndef JSON_NO_IO
#include <iosfwd> // istream, ostream
#endif // JSON_NO_IO
#include <iterator> // random_access_iterator_tag #include <iterator> // random_access_iterator_tag
#include <memory> // unique_ptr #include <memory> // unique_ptr
#include <numeric> // accumulate #include <numeric> // accumulate
@ -1131,53 +1133,55 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
binary = create<binary_t>(std::move(value)); binary = create<binary_t>(std::move(value));
} }
void destroy(value_t t) noexcept void destroy(value_t t)
{ {
// flatten the current json_value to a heap-allocated stack if (t == value_t::array || t == value_t::object)
std::vector<basic_json> stack; {
// flatten the current json_value to a heap-allocated stack
std::vector<basic_json> stack;
// move the top-level items to stack // move the top-level items to stack
if (t == value_t::array) if (t == value_t::array)
{
stack.reserve(array->size());
std::move(array->begin(), array->end(), std::back_inserter(stack));
}
else if (t == value_t::object)
{
stack.reserve(object->size());
for (auto&& it : *object)
{ {
stack.push_back(std::move(it.second)); stack.reserve(array->size());
std::move(array->begin(), array->end(), std::back_inserter(stack));
} }
} else
while (!stack.empty())
{
// move the last item to local variable to be processed
basic_json current_item(std::move(stack.back()));
stack.pop_back();
// if current_item is array/object, move
// its children to the stack to be processed later
if (current_item.is_array())
{ {
std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(), stack.reserve(object->size());
std::back_inserter(stack)); for (auto&& it : *object)
current_item.m_value.array->clear();
}
else if (current_item.is_object())
{
for (auto&& it : *current_item.m_value.object)
{ {
stack.push_back(std::move(it.second)); stack.push_back(std::move(it.second));
} }
current_item.m_value.object->clear();
} }
// it's now safe that current_item get destructed while (!stack.empty())
// since it doesn't have any children {
// move the last item to local variable to be processed
basic_json current_item(std::move(stack.back()));
stack.pop_back();
// if current_item is array/object, move
// its children to the stack to be processed later
if (current_item.is_array())
{
std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(), std::back_inserter(stack));
current_item.m_value.array->clear();
}
else if (current_item.is_object())
{
for (auto&& it : *current_item.m_value.object)
{
stack.push_back(std::move(it.second));
}
current_item.m_value.object->clear();
}
// it's now safe that current_item get destructed
// since it doesn't have any children
}
} }
switch (t) switch (t)
@ -1304,12 +1308,25 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return it; return it;
} }
reference set_parent(reference j) reference set_parent(reference j, std::size_t old_capacity = std::size_t(-1))
{ {
#if JSON_DIAGNOSTICS #if JSON_DIAGNOSTICS
if (old_capacity != std::size_t(-1))
{
// see https://github.com/nlohmann/json/issues/2838
JSON_ASSERT(type() == value_t::array);
if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity))
{
// capacity has changed: update all parents
set_parents();
return j;
}
}
j.m_parent = this; j.m_parent = this;
#else #else
static_cast<void>(j); static_cast<void>(j);
static_cast<void>(old_capacity);
#endif #endif
return j; return j;
} }
@ -5304,8 +5321,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
// add element to array (move semantics) // add element to array (move semantics)
const auto old_capacity = m_value.array->capacity();
m_value.array->push_back(std::move(val)); m_value.array->push_back(std::move(val));
set_parent(m_value.array->back()); set_parent(m_value.array->back(), old_capacity);
// if val is moved from, basic_json move constructor marks it null so we do not call the destructor // if val is moved from, basic_json move constructor marks it null so we do not call the destructor
} }
@ -5340,8 +5358,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
// add element to array // add element to array
const auto old_capacity = m_value.array->capacity();
m_value.array->push_back(val); m_value.array->push_back(val);
set_parent(m_value.array->back()); set_parent(m_value.array->back(), old_capacity);
} }
/*! /*!
@ -5495,12 +5514,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
// add element to array (perfect forwarding) // add element to array (perfect forwarding)
#ifdef JSON_HAS_CPP_17 const auto old_capacity = m_value.array->capacity();
return set_parent(m_value.array->emplace_back(std::forward<Args>(args)...));
#else
m_value.array->emplace_back(std::forward<Args>(args)...); m_value.array->emplace_back(std::forward<Args>(args)...);
return set_parent(m_value.array->back()); return set_parent(m_value.array->back(), old_capacity);
#endif
} }
/*! /*!
@ -5576,6 +5592,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);
// but the return value of insert is missing in GCC 4.8, so it is written this way instead. // but the return value of insert is missing in GCC 4.8, so it is written this way instead.
set_parents();
return result; return result;
} }
@ -5613,7 +5630,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
// insert to array and return iterator // insert to array and return iterator
return set_parents(insert_iterator(pos, val), static_cast<typename iterator::difference_type>(1)); return insert_iterator(pos, val);
} }
JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this));
@ -5664,7 +5681,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
// insert to array and return iterator // insert to array and return iterator
return set_parents(insert_iterator(pos, cnt, val), static_cast<typename iterator::difference_type>(cnt)); return insert_iterator(pos, cnt, val);
} }
JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this));
@ -5726,7 +5743,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
// insert to array and return iterator // insert to array and return iterator
return set_parents(insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator), std::distance(first, last)); return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator);
} }
/*! /*!
@ -5768,7 +5785,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
// insert to array and return iterator // insert to array and return iterator
return set_parents(insert_iterator(pos, ilist.begin(), ilist.end()), static_cast<typename iterator::difference_type>(ilist.size())); return insert_iterator(pos, ilist.begin(), ilist.end());
} }
/*! /*!
@ -6594,7 +6611,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @name serialization /// @name serialization
/// @{ /// @{
#ifndef JSON_NO_IO
/*! /*!
@brief serialize to stream @brief serialize to stream
@ -6654,7 +6671,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
{ {
return o << j; return o << j;
} }
#endif // JSON_NO_IO
/// @} /// @}
@ -6912,7 +6929,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict); : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict);
} }
#ifndef JSON_NO_IO
/*! /*!
@brief deserialize from stream @brief deserialize from stream
@deprecated This stream operator is deprecated and will be removed in @deprecated This stream operator is deprecated and will be removed in
@ -6957,7 +6974,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
parser(detail::input_adapter(i)).parse(false, j); parser(detail::input_adapter(i)).parse(false, j);
return i; return i;
} }
#endif // JSON_NO_IO
/// @} /// @}
/////////////////////////// ///////////////////////////

View File

@ -38,7 +38,9 @@ SOFTWARE.
#include <cstddef> // nullptr_t, ptrdiff_t, size_t #include <cstddef> // nullptr_t, ptrdiff_t, size_t
#include <functional> // hash, less #include <functional> // hash, less
#include <initializer_list> // initializer_list #include <initializer_list> // initializer_list
#include <iosfwd> // istream, ostream #ifndef JSON_NO_IO
#include <iosfwd> // istream, ostream
#endif // JSON_NO_IO
#include <iterator> // random_access_iterator_tag #include <iterator> // random_access_iterator_tag
#include <memory> // unique_ptr #include <memory> // unique_ptr
#include <numeric> // accumulate #include <numeric> // accumulate
@ -4505,6 +4507,13 @@ namespace detail
// constructors // // constructors //
////////////////// //////////////////
/*
* Note all external_constructor<>::construct functions need to call
* j.m_value.destroy(j.m_type) to avoid a memory leak in case j contains an
* allocated value (e.g., a string). See bug issue
* https://github.com/nlohmann/json/issues/2865 for more information.
*/
template<value_t> struct external_constructor; template<value_t> struct external_constructor;
template<> template<>
@ -4513,6 +4522,7 @@ struct external_constructor<value_t::boolean>
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::boolean; j.m_type = value_t::boolean;
j.m_value = b; j.m_value = b;
j.assert_invariant(); j.assert_invariant();
@ -4525,6 +4535,7 @@ 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)
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::string; j.m_type = value_t::string;
j.m_value = s; j.m_value = s;
j.assert_invariant(); j.assert_invariant();
@ -4533,6 +4544,7 @@ struct external_constructor<value_t::string>
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)
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::string; j.m_type = value_t::string;
j.m_value = std::move(s); j.m_value = std::move(s);
j.assert_invariant(); j.assert_invariant();
@ -4543,6 +4555,7 @@ 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)
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::string; j.m_type = value_t::string;
j.m_value.string = j.template create<typename BasicJsonType::string_t>(str); j.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
j.assert_invariant(); j.assert_invariant();
@ -4555,6 +4568,7 @@ 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)
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::binary; j.m_type = value_t::binary;
j.m_value = typename BasicJsonType::binary_t(b); j.m_value = typename BasicJsonType::binary_t(b);
j.assert_invariant(); j.assert_invariant();
@ -4563,6 +4577,7 @@ struct external_constructor<value_t::binary>
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)
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::binary; j.m_type = value_t::binary;
j.m_value = typename BasicJsonType::binary_t(std::move(b));; j.m_value = typename BasicJsonType::binary_t(std::move(b));;
j.assert_invariant(); j.assert_invariant();
@ -4575,6 +4590,7 @@ struct external_constructor<value_t::number_float>
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::number_float; j.m_type = value_t::number_float;
j.m_value = val; j.m_value = val;
j.assert_invariant(); j.assert_invariant();
@ -4587,6 +4603,7 @@ struct external_constructor<value_t::number_unsigned>
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::number_unsigned; j.m_type = value_t::number_unsigned;
j.m_value = val; j.m_value = val;
j.assert_invariant(); j.assert_invariant();
@ -4599,6 +4616,7 @@ struct external_constructor<value_t::number_integer>
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::number_integer; j.m_type = value_t::number_integer;
j.m_value = val; j.m_value = val;
j.assert_invariant(); j.assert_invariant();
@ -4611,6 +4629,7 @@ 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)
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::array; j.m_type = value_t::array;
j.m_value = arr; j.m_value = arr;
j.set_parents(); j.set_parents();
@ -4620,6 +4639,7 @@ 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)
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::array; j.m_type = value_t::array;
j.m_value = std::move(arr); j.m_value = std::move(arr);
j.set_parents(); j.set_parents();
@ -4633,6 +4653,8 @@ struct external_constructor<value_t::array>
{ {
using std::begin; using std::begin;
using std::end; using std::end;
j.m_value.destroy(j.m_type);
j.m_type = value_t::array; j.m_type = value_t::array;
j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr)); j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
j.set_parents(); j.set_parents();
@ -4642,6 +4664,7 @@ 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_value.destroy(j.m_type);
j.m_type = value_t::array; j.m_type = value_t::array;
j.m_value = value_t::array; j.m_value = value_t::array;
j.m_value.array->reserve(arr.size()); j.m_value.array->reserve(arr.size());
@ -4657,6 +4680,7 @@ 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)
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::array; j.m_type = value_t::array;
j.m_value = value_t::array; j.m_value = value_t::array;
j.m_value.array->resize(arr.size()); j.m_value.array->resize(arr.size());
@ -4675,6 +4699,7 @@ 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)
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::object; j.m_type = value_t::object;
j.m_value = obj; j.m_value = obj;
j.set_parents(); j.set_parents();
@ -4684,6 +4709,7 @@ 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)
{ {
j.m_value.destroy(j.m_type);
j.m_type = value_t::object; j.m_type = value_t::object;
j.m_value = std::move(obj); j.m_value = std::move(obj);
j.set_parents(); j.set_parents();
@ -4697,6 +4723,7 @@ struct external_constructor<value_t::object>
using std::begin; using std::begin;
using std::end; using std::end;
j.m_value.destroy(j.m_type);
j.m_type = value_t::object; j.m_type = value_t::object;
j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj)); j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
j.set_parents(); j.set_parents();
@ -5254,9 +5281,7 @@ std::size_t hash(const BasicJsonType& j)
#include <array> // array #include <array> // array
#include <cstddef> // size_t #include <cstddef> // size_t
#include <cstdio> //FILE *
#include <cstring> // strlen #include <cstring> // strlen
#include <istream> // istream
#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next #include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next
#include <memory> // shared_ptr, make_shared, addressof #include <memory> // shared_ptr, make_shared, addressof
#include <numeric> // accumulate #include <numeric> // accumulate
@ -5264,6 +5289,11 @@ std::size_t hash(const BasicJsonType& j)
#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer #include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
#include <utility> // pair, declval #include <utility> // pair, declval
#ifndef JSON_NO_IO
#include <cstdio> //FILE *
#include <istream> // istream
#endif // JSON_NO_IO
// #include <nlohmann/detail/iterators/iterator_traits.hpp> // #include <nlohmann/detail/iterators/iterator_traits.hpp>
// #include <nlohmann/detail/macro_scope.hpp> // #include <nlohmann/detail/macro_scope.hpp>
@ -5280,6 +5310,7 @@ enum class input_format_t { json, cbor, msgpack, ubjson, bson };
// input adapters // // input adapters //
//////////////////// ////////////////////
#ifndef JSON_NO_IO
/*! /*!
Input adapter for stdio file access. This adapter read only 1 byte and do not use any Input adapter for stdio file access. This adapter read only 1 byte and do not use any
buffer. This adapter is a very low level adapter. buffer. This adapter is a very low level adapter.
@ -5359,7 +5390,7 @@ class input_stream_adapter
{ {
auto res = sb->sbumpc(); auto res = sb->sbumpc();
// set eof manually, as we don't use the istream interface. // set eof manually, as we don't use the istream interface.
if (JSON_HEDLEY_UNLIKELY(res == EOF)) if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))
{ {
is->clear(is->rdstate() | std::ios::eofbit); is->clear(is->rdstate() | std::ios::eofbit);
} }
@ -5371,6 +5402,7 @@ class input_stream_adapter
std::istream* is = nullptr; std::istream* is = nullptr;
std::streambuf* sb = nullptr; std::streambuf* sb = nullptr;
}; };
#endif // JSON_NO_IO
// General-purpose iterator-based adapter. It might not be as fast as // General-purpose iterator-based adapter. It might not be as fast as
// theoretically possible for some containers, but it is extremely versatile. // theoretically possible for some containers, but it is extremely versatile.
@ -5657,6 +5689,7 @@ typename container_input_adapter_factory_impl::container_input_adapter_factory<C
return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(container); return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(container);
} }
#ifndef JSON_NO_IO
// Special cases with fast paths // Special cases with fast paths
inline file_input_adapter input_adapter(std::FILE* file) inline file_input_adapter input_adapter(std::FILE* file)
{ {
@ -5672,6 +5705,7 @@ inline input_stream_adapter input_adapter(std::istream&& stream)
{ {
return input_stream_adapter(stream); return input_stream_adapter(stream);
} }
#endif // JSON_NO_IO
using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const char*>(), std::declval<const char*>())); using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const char*>(), std::declval<const char*>()));
@ -13146,12 +13180,16 @@ class json_ref
#include <algorithm> // copy #include <algorithm> // copy
#include <cstddef> // size_t #include <cstddef> // size_t
#include <ios> // streamsize
#include <iterator> // back_inserter #include <iterator> // back_inserter
#include <memory> // shared_ptr, make_shared #include <memory> // shared_ptr, make_shared
#include <ostream> // basic_ostream
#include <string> // basic_string #include <string> // basic_string
#include <vector> // vector #include <vector> // vector
#ifndef JSON_NO_IO
#include <ios> // streamsize
#include <ostream> // basic_ostream
#endif // JSON_NO_IO
// #include <nlohmann/detail/macro_scope.hpp> // #include <nlohmann/detail/macro_scope.hpp>
@ -13201,6 +13239,7 @@ class output_vector_adapter : public output_adapter_protocol<CharType>
std::vector<CharType>& v; std::vector<CharType>& v;
}; };
#ifndef JSON_NO_IO
/// output adapter for output streams /// output adapter for output streams
template<typename CharType> template<typename CharType>
class output_stream_adapter : public output_adapter_protocol<CharType> class output_stream_adapter : public output_adapter_protocol<CharType>
@ -13224,6 +13263,7 @@ class output_stream_adapter : public output_adapter_protocol<CharType>
private: private:
std::basic_ostream<CharType>& stream; std::basic_ostream<CharType>& stream;
}; };
#endif // JSON_NO_IO
/// output adapter for basic_string /// output adapter for basic_string
template<typename CharType, typename StringType = std::basic_string<CharType>> template<typename CharType, typename StringType = std::basic_string<CharType>>
@ -13256,8 +13296,10 @@ class output_adapter
output_adapter(std::vector<CharType>& vec) output_adapter(std::vector<CharType>& vec)
: oa(std::make_shared<output_vector_adapter<CharType>>(vec)) {} : oa(std::make_shared<output_vector_adapter<CharType>>(vec)) {}
#ifndef JSON_NO_IO
output_adapter(std::basic_ostream<CharType>& s) output_adapter(std::basic_ostream<CharType>& s)
: oa(std::make_shared<output_stream_adapter<CharType>>(s)) {} : oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}
#endif // JSON_NO_IO
output_adapter(StringType& s) output_adapter(StringType& s)
: oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {} : oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {}
@ -16469,7 +16511,7 @@ class serializer
{ {
case error_handler_t::strict: case error_handler_t::strict:
{ {
std::string sn(3, '\0'); std::string sn(9, '\0');
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
(std::snprintf)(&sn[0], sn.size(), "%.2X", byte); (std::snprintf)(&sn[0], sn.size(), "%.2X", byte);
JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn, BasicJsonType())); JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn, BasicJsonType()));
@ -16564,7 +16606,7 @@ class serializer
{ {
case error_handler_t::strict: case error_handler_t::strict:
{ {
std::string sn(3, '\0'); std::string sn(9, '\0');
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
(std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast<std::uint8_t>(s.back())); (std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast<std::uint8_t>(s.back()));
JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn, BasicJsonType())); JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn, BasicJsonType()));
@ -18178,53 +18220,55 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
binary = create<binary_t>(std::move(value)); binary = create<binary_t>(std::move(value));
} }
void destroy(value_t t) noexcept void destroy(value_t t)
{ {
// flatten the current json_value to a heap-allocated stack if (t == value_t::array || t == value_t::object)
std::vector<basic_json> stack; {
// flatten the current json_value to a heap-allocated stack
std::vector<basic_json> stack;
// move the top-level items to stack // move the top-level items to stack
if (t == value_t::array) if (t == value_t::array)
{
stack.reserve(array->size());
std::move(array->begin(), array->end(), std::back_inserter(stack));
}
else if (t == value_t::object)
{
stack.reserve(object->size());
for (auto&& it : *object)
{ {
stack.push_back(std::move(it.second)); stack.reserve(array->size());
std::move(array->begin(), array->end(), std::back_inserter(stack));
} }
} else
while (!stack.empty())
{
// move the last item to local variable to be processed
basic_json current_item(std::move(stack.back()));
stack.pop_back();
// if current_item is array/object, move
// its children to the stack to be processed later
if (current_item.is_array())
{ {
std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(), stack.reserve(object->size());
std::back_inserter(stack)); for (auto&& it : *object)
current_item.m_value.array->clear();
}
else if (current_item.is_object())
{
for (auto&& it : *current_item.m_value.object)
{ {
stack.push_back(std::move(it.second)); stack.push_back(std::move(it.second));
} }
current_item.m_value.object->clear();
} }
// it's now safe that current_item get destructed while (!stack.empty())
// since it doesn't have any children {
// move the last item to local variable to be processed
basic_json current_item(std::move(stack.back()));
stack.pop_back();
// if current_item is array/object, move
// its children to the stack to be processed later
if (current_item.is_array())
{
std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(), std::back_inserter(stack));
current_item.m_value.array->clear();
}
else if (current_item.is_object())
{
for (auto&& it : *current_item.m_value.object)
{
stack.push_back(std::move(it.second));
}
current_item.m_value.object->clear();
}
// it's now safe that current_item get destructed
// since it doesn't have any children
}
} }
switch (t) switch (t)
@ -18351,12 +18395,25 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return it; return it;
} }
reference set_parent(reference j) reference set_parent(reference j, std::size_t old_capacity = std::size_t(-1))
{ {
#if JSON_DIAGNOSTICS #if JSON_DIAGNOSTICS
if (old_capacity != std::size_t(-1))
{
// see https://github.com/nlohmann/json/issues/2838
JSON_ASSERT(type() == value_t::array);
if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity))
{
// capacity has changed: update all parents
set_parents();
return j;
}
}
j.m_parent = this; j.m_parent = this;
#else #else
static_cast<void>(j); static_cast<void>(j);
static_cast<void>(old_capacity);
#endif #endif
return j; return j;
} }
@ -22351,8 +22408,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
// add element to array (move semantics) // add element to array (move semantics)
const auto old_capacity = m_value.array->capacity();
m_value.array->push_back(std::move(val)); m_value.array->push_back(std::move(val));
set_parent(m_value.array->back()); set_parent(m_value.array->back(), old_capacity);
// if val is moved from, basic_json move constructor marks it null so we do not call the destructor // if val is moved from, basic_json move constructor marks it null so we do not call the destructor
} }
@ -22387,8 +22445,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
// add element to array // add element to array
const auto old_capacity = m_value.array->capacity();
m_value.array->push_back(val); m_value.array->push_back(val);
set_parent(m_value.array->back()); set_parent(m_value.array->back(), old_capacity);
} }
/*! /*!
@ -22542,12 +22601,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
// add element to array (perfect forwarding) // add element to array (perfect forwarding)
#ifdef JSON_HAS_CPP_17 const auto old_capacity = m_value.array->capacity();
return set_parent(m_value.array->emplace_back(std::forward<Args>(args)...));
#else
m_value.array->emplace_back(std::forward<Args>(args)...); m_value.array->emplace_back(std::forward<Args>(args)...);
return set_parent(m_value.array->back()); return set_parent(m_value.array->back(), old_capacity);
#endif
} }
/*! /*!
@ -22623,6 +22679,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);
// but the return value of insert is missing in GCC 4.8, so it is written this way instead. // but the return value of insert is missing in GCC 4.8, so it is written this way instead.
set_parents();
return result; return result;
} }
@ -22660,7 +22717,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
// insert to array and return iterator // insert to array and return iterator
return set_parents(insert_iterator(pos, val), static_cast<typename iterator::difference_type>(1)); return insert_iterator(pos, val);
} }
JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this));
@ -22711,7 +22768,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
// insert to array and return iterator // insert to array and return iterator
return set_parents(insert_iterator(pos, cnt, val), static_cast<typename iterator::difference_type>(cnt)); return insert_iterator(pos, cnt, val);
} }
JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this));
@ -22773,7 +22830,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
// insert to array and return iterator // insert to array and return iterator
return set_parents(insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator), std::distance(first, last)); return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator);
} }
/*! /*!
@ -22815,7 +22872,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
// insert to array and return iterator // insert to array and return iterator
return set_parents(insert_iterator(pos, ilist.begin(), ilist.end()), static_cast<typename iterator::difference_type>(ilist.size())); return insert_iterator(pos, ilist.begin(), ilist.end());
} }
/*! /*!
@ -23641,7 +23698,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @name serialization /// @name serialization
/// @{ /// @{
#ifndef JSON_NO_IO
/*! /*!
@brief serialize to stream @brief serialize to stream
@ -23701,7 +23758,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
{ {
return o << j; return o << j;
} }
#endif // JSON_NO_IO
/// @} /// @}
@ -23959,7 +24016,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict); : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict);
} }
#ifndef JSON_NO_IO
/*! /*!
@brief deserialize from stream @brief deserialize from stream
@deprecated This stream operator is deprecated and will be removed in @deprecated This stream operator is deprecated and will be removed in
@ -24004,7 +24061,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
parser(detail::input_adapter(i)).parse(false, j); parser(detail::input_adapter(i)).parse(false, j);
return i; return i;
} }
#endif // JSON_NO_IO
/// @} /// @}
/////////////////////////// ///////////////////////////

View File

@ -109,4 +109,69 @@ TEST_CASE("Better diagnostics")
j["/foo"] = {1, 2, 3}; j["/foo"] = {1, 2, 3};
CHECK_THROWS_WITH_AS(j.unflatten(), "[json.exception.type_error.315] (/~1foo) values in object must be primitive", json::type_error); CHECK_THROWS_WITH_AS(j.unflatten(), "[json.exception.type_error.315] (/~1foo) values in object must be primitive", json::type_error);
} }
SECTION("Regression test for https://github.com/nlohmann/json/issues/2838")
{
// void push_back(basic_json&& val)
{
json j_arr = json::array();
j_arr.push_back(json::object());
j_arr.push_back(json::object());
j_arr.push_back(json::object());
j_arr.push_back(json::object());
json j_obj = json::object();
j_obj["key"] = j_arr;
}
// void push_back(const basic_json& val)
{
json j_arr = json::array();
auto object = json::object();
j_arr.push_back(object);
j_arr.push_back(object);
j_arr.push_back(object);
j_arr.push_back(object);
json j_obj = json::object();
j_obj["key"] = j_arr;
}
// reference emplace_back(Args&& ... args)
{
json j_arr = json::array();
j_arr.emplace_back(json::object());
j_arr.emplace_back(json::object());
j_arr.emplace_back(json::object());
j_arr.emplace_back(json::object());
json j_obj = json::object();
j_obj["key"] = j_arr;
}
// iterator insert(const_iterator pos, const basic_json& val)
{
json j_arr = json::array();
j_arr.insert(j_arr.begin(), json::object());
j_arr.insert(j_arr.begin(), json::object());
j_arr.insert(j_arr.begin(), json::object());
j_arr.insert(j_arr.begin(), json::object());
json j_obj = json::object();
j_obj["key"] = j_arr;
}
// iterator insert(const_iterator pos, size_type cnt, const basic_json& val)
{
json j_arr = json::array();
j_arr.insert(j_arr.begin(), 2, json::object());
json j_obj = json::object();
j_obj["key"] = j_arr;
}
// iterator insert(const_iterator pos, const_iterator first, const_iterator last)
{
json j_arr = json::array();
json j_objects = {json::object(), json::object()};
j_arr.insert(j_arr.begin(), j_objects.begin(), j_objects.end());
json j_obj = json::object();
j_obj["key"] = j_arr;
}
}
} }

View File

@ -594,4 +594,30 @@ TEST_CASE("regression tests 2")
} }
} }
} }
SECTION("issue #2865 - ASAN detects memory leaks")
{
// the code below is expected to not leak memory
{
nlohmann::json o;
std::string s = "bar";
nlohmann::to_json(o["foo"], s);
nlohmann::json p = o;
// call to_json with a non-null JSON value
nlohmann::to_json(p["foo"], s);
}
{
nlohmann::json o;
std::string s = "bar";
nlohmann::to_json(o["foo"], s);
// call to_json with a non-null JSON value
nlohmann::to_json(o["foo"], s);
}
}
} }