diff --git a/.drone.yml b/.drone.yml
new file mode 100644
index 000000000..3cdefe4b9
--- /dev/null
+++ b/.drone.yml
@@ -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
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index 42e9098b5..bf9549fcf 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -24,3 +24,20 @@ jobs:
run: cmake --build build --parallel 10
- name: test
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
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index c9f4e3b9b..fad51b632 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -98,7 +98,7 @@ jobs:
runs-on: windows-latest
strategy:
matrix:
- version: [10, 11]
+ version: [11, 12]
steps:
- uses: actions/checkout@v2
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 163657559..3c629ee6a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -12,7 +12,7 @@ project(nlohmann_json VERSION 3.9.1 LANGUAGES CXX)
##
set(MAIN_PROJECT OFF)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
- set(MAIN_PROJECT ON)
+ set(MAIN_PROJECT ON)
endif()
##
@@ -113,8 +113,8 @@ endif()
# Install a pkg-config file, so other tools can find this.
CONFIGURE_FILE(
- "${CMAKE_CURRENT_SOURCE_DIR}/cmake/pkg-config.pc.in"
- "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc"
+ "${CMAKE_CURRENT_SOURCE_DIR}/cmake/pkg-config.pc.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc"
)
##
@@ -160,7 +160,7 @@ if(JSON_Install)
FILES ${NLOHMANN_NATVIS_FILE}
DESTINATION .
)
-endif()
+ endif()
export(
TARGETS ${NLOHMANN_JSON_TARGET_NAME}
NAMESPACE ${PROJECT_NAME}::
diff --git a/README.md b/README.md
index 3309190e0..eabf1302a 100644
--- a/README.md
+++ b/README.md
@@ -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.
-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.
@@ -870,7 +870,7 @@ assert(p == p2);
To make this work with one of your types, you only need to provide two functions:
```cpp
-using nlohmann::json;
+using json = nlohmann::json;
namespace ns {
void to_json(json& j, const person& p) {
@@ -1126,7 +1126,7 @@ Other Important points:
### 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
// 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.
-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 |
|-------------------------------------------------------------------|--------------------|----------------|
@@ -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 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.1.0 | Ubuntu (aarch64) | Drone CI |
| 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.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
-
+
-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 © 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 © 2008-2009 [Björn Hoehrmann](https://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 © 2008-2009 [Björn Hoehrmann](https://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 © 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 © 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/).
@@ -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
- [**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.
-- [**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
- [**Valgrind**](https://valgrind.org) to check for correct memory management
- [**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.
-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.
diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt
index ee4db1912..1243f54ea 100644
--- a/benchmarks/CMakeLists.txt
+++ b/benchmarks/CMakeLists.txt
@@ -11,6 +11,7 @@ include(FetchContent)
FetchContent_Declare(
benchmark
GIT_REPOSITORY https://github.com/google/benchmark.git
+ GIT_TAG origin/main
GIT_SHALLOW TRUE
)
diff --git a/cmake/ci.cmake b/cmake/ci.cmake
index 5e7deba72..eaa2c6793 100644
--- a/cmake/ci.cmake
+++ b/cmake/ci.cmake
@@ -467,7 +467,7 @@ add_custom_target(ci_test_diagnostics
###############################################################################
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"
-DJSON_BuildTests=ON -DJSON_MultipleHeaders=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 ${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 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
- 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"
)
diff --git a/doc/mkdocs/docs/features/arbitrary_types.md b/doc/mkdocs/docs/features/arbitrary_types.md
index 23913bba2..423419130 100644
--- a/doc/mkdocs/docs/features/arbitrary_types.md
+++ b/doc/mkdocs/docs/features/arbitrary_types.md
@@ -54,7 +54,7 @@ assert(p == p2);
To make this work with one of your types, you only need to provide two functions:
```cpp
-using nlohmann::json;
+using json = nlohmann::json;
namespace ns {
void to_json(json& j, const person& p) {
diff --git a/doc/mkdocs/docs/features/macros.md b/doc/mkdocs/docs/features/macros.md
index ee401c5dc..d008393f7 100644
--- a/doc/mkdocs/docs/features/macros.md
+++ b/doc/mkdocs/docs/features/macros.md
@@ -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`.
+## `JSON_NO_IO`
+
+When defined, headers ``, ``, ``, ``, and `` 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`
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.
diff --git a/doc/mkdocs/docs/features/types/number_handling.md b/doc/mkdocs/docs/features/types/number_handling.md
index 4224c155b..aa8df7ad8 100644
--- a/doc/mkdocs/docs/features/types/number_handling.md
+++ b/doc/mkdocs/docs/features/types/number_handling.md
@@ -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`.
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
- Integer numbers are serialized as is; that is, no scientific notation is used.
diff --git a/include/nlohmann/detail/conversions/to_json.hpp b/include/nlohmann/detail/conversions/to_json.hpp
index 9d7f55fc9..08462a4bb 100644
--- a/include/nlohmann/detail/conversions/to_json.hpp
+++ b/include/nlohmann/detail/conversions/to_json.hpp
@@ -22,6 +22,13 @@ namespace detail
// 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 struct external_constructor;
template<>
@@ -30,6 +37,7 @@ struct external_constructor
template
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_value = b;
j.assert_invariant();
@@ -42,6 +50,7 @@ struct external_constructor
template
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_value = s;
j.assert_invariant();
@@ -50,6 +59,7 @@ struct external_constructor
template
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_value = std::move(s);
j.assert_invariant();
@@ -60,6 +70,7 @@ struct external_constructor
int > = 0 >
static void construct(BasicJsonType& j, const CompatibleStringType& str)
{
+ j.m_value.destroy(j.m_type);
j.m_type = value_t::string;
j.m_value.string = j.template create(str);
j.assert_invariant();
@@ -72,6 +83,7 @@ struct external_constructor
template
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_value = typename BasicJsonType::binary_t(b);
j.assert_invariant();
@@ -80,6 +92,7 @@ struct external_constructor
template
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_value = typename BasicJsonType::binary_t(std::move(b));;
j.assert_invariant();
@@ -92,6 +105,7 @@ struct external_constructor
template
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_value = val;
j.assert_invariant();
@@ -104,6 +118,7 @@ struct external_constructor
template
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_value = val;
j.assert_invariant();
@@ -116,6 +131,7 @@ struct external_constructor
template
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_value = val;
j.assert_invariant();
@@ -128,6 +144,7 @@ struct external_constructor
template
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_value = arr;
j.set_parents();
@@ -137,6 +154,7 @@ struct external_constructor
template
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_value = std::move(arr);
j.set_parents();
@@ -150,6 +168,8 @@ struct external_constructor
{
using std::begin;
using std::end;
+
+ j.m_value.destroy(j.m_type);
j.m_type = value_t::array;
j.m_value.array = j.template create(begin(arr), end(arr));
j.set_parents();
@@ -159,6 +179,7 @@ struct external_constructor
template
static void construct(BasicJsonType& j, const std::vector& arr)
{
+ j.m_value.destroy(j.m_type);
j.m_type = value_t::array;
j.m_value = value_t::array;
j.m_value.array->reserve(arr.size());
@@ -174,6 +195,7 @@ struct external_constructor
enable_if_t::value, int> = 0>
static void construct(BasicJsonType& j, const std::valarray& arr)
{
+ j.m_value.destroy(j.m_type);
j.m_type = value_t::array;
j.m_value = value_t::array;
j.m_value.array->resize(arr.size());
@@ -192,6 +214,7 @@ struct external_constructor
template
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_value = obj;
j.set_parents();
@@ -201,6 +224,7 @@ struct external_constructor
template
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_value = std::move(obj);
j.set_parents();
@@ -214,6 +238,7 @@ struct external_constructor
using std::begin;
using std::end;
+ j.m_value.destroy(j.m_type);
j.m_type = value_t::object;
j.m_value.object = j.template create(begin(obj), end(obj));
j.set_parents();
diff --git a/include/nlohmann/detail/input/input_adapters.hpp b/include/nlohmann/detail/input/input_adapters.hpp
index 5d0b59b57..98fb29a99 100644
--- a/include/nlohmann/detail/input/input_adapters.hpp
+++ b/include/nlohmann/detail/input/input_adapters.hpp
@@ -2,9 +2,7 @@
#include // array
#include // size_t
-#include //FILE *
#include // strlen
-#include // istream
#include // begin, end, iterator_traits, random_access_iterator_tag, distance, next
#include // shared_ptr, make_shared, addressof
#include // accumulate
@@ -12,6 +10,11 @@
#include // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
#include // pair, declval
+#ifndef JSON_NO_IO
+ #include //FILE *
+ #include // istream
+#endif // JSON_NO_IO
+
#include
#include
@@ -26,6 +29,7 @@ enum class input_format_t { json, cbor, msgpack, ubjson, bson };
// input adapters //
////////////////////
+#ifndef JSON_NO_IO
/*!
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.
@@ -105,7 +109,7 @@ class input_stream_adapter
{
auto res = sb->sbumpc();
// 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::eof()))
{
is->clear(is->rdstate() | std::ios::eofbit);
}
@@ -117,6 +121,7 @@ class input_stream_adapter
std::istream* is = nullptr;
std::streambuf* sb = nullptr;
};
+#endif // JSON_NO_IO
// General-purpose iterator-based adapter. It might not be as fast as
// theoretically possible for some containers, but it is extremely versatile.
@@ -403,6 +408,7 @@ typename container_input_adapter_factory_impl::container_input_adapter_factory::create(container);
}
+#ifndef JSON_NO_IO
// Special cases with fast paths
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);
}
+#endif // JSON_NO_IO
using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval(), std::declval()));
diff --git a/include/nlohmann/detail/output/output_adapters.hpp b/include/nlohmann/detail/output/output_adapters.hpp
index 25886ad1a..6f2462685 100644
--- a/include/nlohmann/detail/output/output_adapters.hpp
+++ b/include/nlohmann/detail/output/output_adapters.hpp
@@ -2,12 +2,16 @@
#include // copy
#include // size_t
-#include // streamsize
#include // back_inserter
#include // shared_ptr, make_shared
-#include // basic_ostream
#include // basic_string
#include // vector
+
+#ifndef JSON_NO_IO
+ #include // streamsize
+ #include // basic_ostream
+#endif // JSON_NO_IO
+
#include
namespace nlohmann
@@ -56,6 +60,7 @@ class output_vector_adapter : public output_adapter_protocol
std::vector& v;
};
+#ifndef JSON_NO_IO
/// output adapter for output streams
template
class output_stream_adapter : public output_adapter_protocol
@@ -79,6 +84,7 @@ class output_stream_adapter : public output_adapter_protocol
private:
std::basic_ostream& stream;
};
+#endif // JSON_NO_IO
/// output adapter for basic_string
template>
@@ -111,8 +117,10 @@ class output_adapter
output_adapter(std::vector& vec)
: oa(std::make_shared>(vec)) {}
+#ifndef JSON_NO_IO
output_adapter(std::basic_ostream& s)
: oa(std::make_shared>(s)) {}
+#endif // JSON_NO_IO
output_adapter(StringType& s)
: oa(std::make_shared>(s)) {}
diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp
index dfbe54329..9c2182c4a 100644
--- a/include/nlohmann/detail/output/serializer.hpp
+++ b/include/nlohmann/detail/output/serializer.hpp
@@ -499,7 +499,7 @@ class serializer
{
case error_handler_t::strict:
{
- std::string sn(3, '\0');
+ std::string sn(9, '\0');
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
(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()));
@@ -594,7 +594,7 @@ class serializer
{
case error_handler_t::strict:
{
- std::string sn(3, '\0');
+ std::string sn(9, '\0');
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
(std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast(s.back()));
JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn, BasicJsonType()));
diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp
index 7d2715300..6af24555b 100644
--- a/include/nlohmann/json.hpp
+++ b/include/nlohmann/json.hpp
@@ -38,7 +38,9 @@ SOFTWARE.
#include // nullptr_t, ptrdiff_t, size_t
#include // hash, less
#include // initializer_list
-#include // istream, ostream
+#ifndef JSON_NO_IO
+ #include // istream, ostream
+#endif // JSON_NO_IO
#include // random_access_iterator_tag
#include // unique_ptr
#include // accumulate
@@ -1131,53 +1133,55 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
binary = create(std::move(value));
}
- void destroy(value_t t) noexcept
+ void destroy(value_t t)
{
- // flatten the current json_value to a heap-allocated stack
- std::vector stack;
+ if (t == value_t::array || t == value_t::object)
+ {
+ // flatten the current json_value to a heap-allocated stack
+ std::vector stack;
- // move the top-level items to stack
- 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)
+ // move the top-level items to stack
+ if (t == value_t::array)
{
- stack.push_back(std::move(it.second));
+ stack.reserve(array->size());
+ std::move(array->begin(), array->end(), std::back_inserter(stack));
}
- }
-
- 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())
+ else
{
- 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.reserve(object->size());
+ for (auto&& it : *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
+ 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(), 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)
@@ -1304,12 +1308,25 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
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 (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;
#else
static_cast(j);
+ static_cast(old_capacity);
#endif
return j;
}
@@ -5304,8 +5321,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
// add element to array (move semantics)
+ const auto old_capacity = m_value.array->capacity();
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
}
@@ -5340,8 +5358,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
// add element to array
+ const auto old_capacity = m_value.array->capacity();
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)
-#ifdef JSON_HAS_CPP_17
- return set_parent(m_value.array->emplace_back(std::forward(args)...));
-#else
+ const auto old_capacity = m_value.array->capacity();
m_value.array->emplace_back(std::forward(args)...);
- return set_parent(m_value.array->back());
-#endif
+ return set_parent(m_value.array->back(), old_capacity);
}
/*!
@@ -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);
// but the return value of insert is missing in GCC 4.8, so it is written this way instead.
+ set_parents();
return result;
}
@@ -5613,7 +5630,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
// insert to array and return iterator
- return set_parents(insert_iterator(pos, val), static_cast(1));
+ return insert_iterator(pos, val);
}
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
- return set_parents(insert_iterator(pos, cnt, val), static_cast(cnt));
+ return insert_iterator(pos, cnt, val);
}
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
- 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
- return set_parents(insert_iterator(pos, ilist.begin(), ilist.end()), static_cast(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
/// @{
-
+#ifndef JSON_NO_IO
/*!
@brief serialize to stream
@@ -6654,7 +6671,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
{
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)
: detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict);
}
-
+#ifndef JSON_NO_IO
/*!
@brief deserialize from stream
@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);
return i;
}
-
+#endif // JSON_NO_IO
/// @}
///////////////////////////
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index 96c9dd133..aa612f9a1 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -38,7 +38,9 @@ SOFTWARE.
#include // nullptr_t, ptrdiff_t, size_t
#include // hash, less
#include // initializer_list
-#include // istream, ostream
+#ifndef JSON_NO_IO
+ #include // istream, ostream
+#endif // JSON_NO_IO
#include // random_access_iterator_tag
#include // unique_ptr
#include // accumulate
@@ -4505,6 +4507,13 @@ namespace detail
// 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 struct external_constructor;
template<>
@@ -4513,6 +4522,7 @@ struct external_constructor
template
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_value = b;
j.assert_invariant();
@@ -4525,6 +4535,7 @@ struct external_constructor
template
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_value = s;
j.assert_invariant();
@@ -4533,6 +4544,7 @@ struct external_constructor
template
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_value = std::move(s);
j.assert_invariant();
@@ -4543,6 +4555,7 @@ struct external_constructor
int > = 0 >
static void construct(BasicJsonType& j, const CompatibleStringType& str)
{
+ j.m_value.destroy(j.m_type);
j.m_type = value_t::string;
j.m_value.string = j.template create(str);
j.assert_invariant();
@@ -4555,6 +4568,7 @@ struct external_constructor
template
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_value = typename BasicJsonType::binary_t(b);
j.assert_invariant();
@@ -4563,6 +4577,7 @@ struct external_constructor
template
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_value = typename BasicJsonType::binary_t(std::move(b));;
j.assert_invariant();
@@ -4575,6 +4590,7 @@ struct external_constructor
template
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_value = val;
j.assert_invariant();
@@ -4587,6 +4603,7 @@ struct external_constructor
template
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_value = val;
j.assert_invariant();
@@ -4599,6 +4616,7 @@ struct external_constructor
template
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_value = val;
j.assert_invariant();
@@ -4611,6 +4629,7 @@ struct external_constructor
template
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_value = arr;
j.set_parents();
@@ -4620,6 +4639,7 @@ struct external_constructor
template
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_value = std::move(arr);
j.set_parents();
@@ -4633,6 +4653,8 @@ struct external_constructor
{
using std::begin;
using std::end;
+
+ j.m_value.destroy(j.m_type);
j.m_type = value_t::array;
j.m_value.array = j.template create(begin(arr), end(arr));
j.set_parents();
@@ -4642,6 +4664,7 @@ struct external_constructor
template
static void construct(BasicJsonType& j, const std::vector& arr)
{
+ j.m_value.destroy(j.m_type);
j.m_type = value_t::array;
j.m_value = value_t::array;
j.m_value.array->reserve(arr.size());
@@ -4657,6 +4680,7 @@ struct external_constructor
enable_if_t::value, int> = 0>
static void construct(BasicJsonType& j, const std::valarray& arr)
{
+ j.m_value.destroy(j.m_type);
j.m_type = value_t::array;
j.m_value = value_t::array;
j.m_value.array->resize(arr.size());
@@ -4675,6 +4699,7 @@ struct external_constructor
template
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_value = obj;
j.set_parents();
@@ -4684,6 +4709,7 @@ struct external_constructor
template
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_value = std::move(obj);
j.set_parents();
@@ -4697,6 +4723,7 @@ struct external_constructor
using std::begin;
using std::end;
+ j.m_value.destroy(j.m_type);
j.m_type = value_t::object;
j.m_value.object = j.template create(begin(obj), end(obj));
j.set_parents();
@@ -5254,9 +5281,7 @@ std::size_t hash(const BasicJsonType& j)
#include // array
#include // size_t
-#include //FILE *
#include // strlen
-#include // istream
#include // begin, end, iterator_traits, random_access_iterator_tag, distance, next
#include // shared_ptr, make_shared, addressof
#include // accumulate
@@ -5264,6 +5289,11 @@ std::size_t hash(const BasicJsonType& j)
#include // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
#include // pair, declval
+#ifndef JSON_NO_IO
+ #include //FILE *
+ #include // istream
+#endif // JSON_NO_IO
+
// #include
// #include
@@ -5280,6 +5310,7 @@ enum class input_format_t { json, cbor, msgpack, ubjson, bson };
// input adapters //
////////////////////
+#ifndef JSON_NO_IO
/*!
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.
@@ -5359,7 +5390,7 @@ class input_stream_adapter
{
auto res = sb->sbumpc();
// 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::eof()))
{
is->clear(is->rdstate() | std::ios::eofbit);
}
@@ -5371,6 +5402,7 @@ class input_stream_adapter
std::istream* is = nullptr;
std::streambuf* sb = nullptr;
};
+#endif // JSON_NO_IO
// General-purpose iterator-based adapter. It might not be as fast as
// theoretically possible for some containers, but it is extremely versatile.
@@ -5657,6 +5689,7 @@ typename container_input_adapter_factory_impl::container_input_adapter_factory::create(container);
}
+#ifndef JSON_NO_IO
// Special cases with fast paths
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);
}
+#endif // JSON_NO_IO
using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval(), std::declval()));
@@ -13146,12 +13180,16 @@ class json_ref
#include // copy
#include // size_t
-#include // streamsize
#include // back_inserter
#include // shared_ptr, make_shared
-#include // basic_ostream
#include // basic_string
#include // vector
+
+#ifndef JSON_NO_IO
+ #include // streamsize
+ #include // basic_ostream
+#endif // JSON_NO_IO
+
// #include
@@ -13201,6 +13239,7 @@ class output_vector_adapter : public output_adapter_protocol
std::vector& v;
};
+#ifndef JSON_NO_IO
/// output adapter for output streams
template
class output_stream_adapter : public output_adapter_protocol
@@ -13224,6 +13263,7 @@ class output_stream_adapter : public output_adapter_protocol
private:
std::basic_ostream& stream;
};
+#endif // JSON_NO_IO
/// output adapter for basic_string
template>
@@ -13256,8 +13296,10 @@ class output_adapter
output_adapter(std::vector& vec)
: oa(std::make_shared>(vec)) {}
+#ifndef JSON_NO_IO
output_adapter(std::basic_ostream& s)
: oa(std::make_shared>(s)) {}
+#endif // JSON_NO_IO
output_adapter(StringType& s)
: oa(std::make_shared>(s)) {}
@@ -16469,7 +16511,7 @@ class serializer
{
case error_handler_t::strict:
{
- std::string sn(3, '\0');
+ std::string sn(9, '\0');
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
(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()));
@@ -16564,7 +16606,7 @@ class serializer
{
case error_handler_t::strict:
{
- std::string sn(3, '\0');
+ std::string sn(9, '\0');
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
(std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast(s.back()));
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(std::move(value));
}
- void destroy(value_t t) noexcept
+ void destroy(value_t t)
{
- // flatten the current json_value to a heap-allocated stack
- std::vector stack;
+ if (t == value_t::array || t == value_t::object)
+ {
+ // flatten the current json_value to a heap-allocated stack
+ std::vector stack;
- // move the top-level items to stack
- 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)
+ // move the top-level items to stack
+ if (t == value_t::array)
{
- stack.push_back(std::move(it.second));
+ stack.reserve(array->size());
+ std::move(array->begin(), array->end(), std::back_inserter(stack));
}
- }
-
- 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())
+ else
{
- 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.reserve(object->size());
+ for (auto&& it : *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
+ 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(), 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)
@@ -18351,12 +18395,25 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
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 (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;
#else
static_cast(j);
+ static_cast(old_capacity);
#endif
return j;
}
@@ -22351,8 +22408,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
// add element to array (move semantics)
+ const auto old_capacity = m_value.array->capacity();
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
}
@@ -22387,8 +22445,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
// add element to array
+ const auto old_capacity = m_value.array->capacity();
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)
-#ifdef JSON_HAS_CPP_17
- return set_parent(m_value.array->emplace_back(std::forward(args)...));
-#else
+ const auto old_capacity = m_value.array->capacity();
m_value.array->emplace_back(std::forward(args)...);
- return set_parent(m_value.array->back());
-#endif
+ return set_parent(m_value.array->back(), old_capacity);
}
/*!
@@ -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);
// but the return value of insert is missing in GCC 4.8, so it is written this way instead.
+ set_parents();
return result;
}
@@ -22660,7 +22717,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
// insert to array and return iterator
- return set_parents(insert_iterator(pos, val), static_cast(1));
+ return insert_iterator(pos, val);
}
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
- return set_parents(insert_iterator(pos, cnt, val), static_cast(cnt));
+ return insert_iterator(pos, cnt, val);
}
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
- 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
- return set_parents(insert_iterator(pos, ilist.begin(), ilist.end()), static_cast(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
/// @{
-
+#ifndef JSON_NO_IO
/*!
@brief serialize to stream
@@ -23701,7 +23758,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
{
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)
: detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict);
}
-
+#ifndef JSON_NO_IO
/*!
@brief deserialize from stream
@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);
return i;
}
-
+#endif // JSON_NO_IO
/// @}
///////////////////////////
diff --git a/test/src/unit-diagnostics.cpp b/test/src/unit-diagnostics.cpp
index 21ced33b1..ebbe64f38 100644
--- a/test/src/unit-diagnostics.cpp
+++ b/test/src/unit-diagnostics.cpp
@@ -109,4 +109,69 @@ TEST_CASE("Better diagnostics")
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);
}
+
+ 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;
+ }
+ }
}
diff --git a/test/src/unit-regression2.cpp b/test/src/unit-regression2.cpp
index 4d48e4765..ef9d7ee54 100644
--- a/test/src/unit-regression2.cpp
+++ b/test/src/unit-regression2.cpp
@@ -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);
+ }
+ }
}