Compare commits

..
Author SHA1 Message Date
Niels Lohmann 93fe62fc0b Assert on missing array indices in const operator[] and document the JSON pointer case
The const operator[] overloads are unchecked by design, and a missing key
or index is undefined behavior. The key overload guards this with a
runtime assertion, but the index overload did not, although the element
access documentation says an assertion fires in both cases. The const
JSON pointer overload inherits both through json_pointer::get_unchecked(),
so a pointer to a missing array index read out of bounds even in debug
builds, and its documentation promised out_of_range.404 for any pointer
that cannot be resolved.

Add JSON_ASSERT(idx < size()) to const operator[](size_type), which also
covers the index leg of the const JSON pointer overload. Document the
undefined behavior for the const JSON pointer overload in operator[].md
and in the runtime assertions page. Release builds are unchanged.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-27 23:17:42 +02:00
7 changed files with 144 additions and 313 deletions
+11 -3
View File
@@ -86,6 +86,9 @@ Strong exception safety: if an exception occurs, the original value stays intact
- Throws [`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) if an array index in the passed - Throws [`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) if an array index in the passed
JSON pointer `ptr` exceeds the range of `size_type` (e.g., on 32-bit platforms). JSON pointer `ptr` exceeds the range of `size_type` (e.g., on 32-bit platforms).
For the **const** version, an object key or array index in `ptr` that does not exist is not reported by an
exception, but is undefined behavior (see the notes below). Use [`at`](at.md) for checked access.
## Complexity ## Complexity
1. Constant if `idx` is in the range of the array. Otherwise, linear in `idx - size()`. 1. Constant if `idx` is in the range of the array. Otherwise, linear in `idx - size()`.
@@ -100,9 +103,12 @@ Strong exception safety: if an exception occurs, the original value stays intact
The following cases apply to the **const** overloads; the non-const overloads instead insert the missing element The following cases apply to the **const** overloads; the non-const overloads instead insert the missing element
(see the notes below). (see the notes below).
1. If the element at index `idx` does not exist, the behavior is undefined. 1. If the element at index `idx` does not exist, the behavior is undefined and is **guarded by a
[runtime assertion](../../features/assertions.md)**!
2. If the element with key `key` does not exist, the behavior is undefined and is **guarded by a 2. If the element with key `key` does not exist, the behavior is undefined and is **guarded by a
[runtime assertion](../../features/assertions.md)**! [runtime assertion](../../features/assertions.md)**!
3. If the JSON pointer `ptr` refers to an object key or an array index that does not exist, the behavior is
undefined and is **guarded by a [runtime assertion](../../features/assertions.md)**!
1. The non-const version may add values: If `idx` is beyond the range of the array (i.e., `idx >= size()`), then the 1. The non-const version may add values: If `idx` is beyond the range of the array (i.e., `idx >= size()`), then the
array is silently filled up with `#!json null` values to make `idx` a valid reference to the last stored element. In array is silently filled up with `#!json null` values to make `idx` a valid reference to the last stored element. In
@@ -257,9 +263,11 @@ Strong exception safety: if an exception occurs, the original value stays intact
## Version history ## Version history
1. Added in version 1.0.0. 1. Added in version 1.0.0. A missing index in the const version is guarded by a runtime assertion since version
3.13.0.
2. Added in version 1.0.0. Added overloads for `T* key` in version 1.1.0. Removed overloads for `T* key` (replaced by 3) 2. Added in version 1.0.0. Added overloads for `T* key` in version 1.1.0. Removed overloads for `T* key` (replaced by 3)
in version 3.11.0. in version 3.11.0.
3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as 3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as
already supported by [`at`](at.md), [`value`](value.md), [`find`](find.md), and other lookup functions. already supported by [`at`](at.md), [`value`](value.md), [`find`](find.md), and other lookup functions.
4. Added in version 2.0.0. 4. Added in version 2.0.0. A missing array index in the const version is guarded by a runtime assertion since
version 3.13.0.
+31 -7
View File
@@ -16,14 +16,15 @@ before including the `json.hpp` header.
## Function with runtime assertions ## Function with runtime assertions
### Unchecked object access to a const value ### Unchecked access to a const value
Function [`operator[]`](../api/basic_json/operator%5B%5D.md) implements unchecked access for objects. Whereas a missing Function [`operator[]`](../api/basic_json/operator%5B%5D.md) implements unchecked access for arrays and objects. Whereas
key is added in the case of non-const objects, accessing a const object with a missing key is undefined behavior (think a missing element is added in the case of non-const values, accessing a const value with a missing object key or an
of a dereferenced null pointer) and yields a runtime assertion. invalid array index is undefined behavior (think of a dereferenced null pointer) and yields a runtime assertion. This
also applies to a [JSON pointer](json_pointer.md) that refers to a missing key or an invalid index.
If you are not sure whether an element in an object exists, use checked access with the If you are not sure whether an element exists, use checked access with the [`at` function](../api/basic_json/at.md)
[`at` function](../api/basic_json/at.md) or call the [`contains` function](../api/basic_json/contains.md) before. or call the [`contains` function](../api/basic_json/contains.md) before.
See also the documentation on [element access](element_access/index.md). See also the documentation on [element access](element_access/index.md).
@@ -46,7 +47,30 @@ See also the documentation on [element access](element_access/index.md).
Output: Output:
``` ```
Assertion failed: (m_value.object->find(key) != m_value.object->end()), function operator[], file json.hpp, line 2144. Assertion failed: (it != m_data.m_value.object->end()), function operator[], file json.hpp, line 28795.
```
??? example "Example 2: Invalid array index in a JSON pointer"
The following code will trigger an assertion at runtime:
```cpp
#include <nlohmann/json.hpp>
using json = nlohmann::json;
using namespace nlohmann::literals;
int main()
{
const json j = {{"array", {1, 2, 3}}};
auto v = j["/array/5"_json_pointer];
}
```
Output:
```
Assertion failed: (idx < m_data.m_value.array->size()), function operator[], file json.hpp, line 28758.
``` ```
### Constructing from an uninitialized iterator range ### Constructing from an uninitialized iterator range
+27 -47
View File
@@ -42,10 +42,6 @@ namespace detail
* j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an * j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an
* allocated value (e.g., a string). See bug issue * allocated value (e.g., a string). See bug issue
* https://github.com/nlohmann/json/issues/2865 for more information. * https://github.com/nlohmann/json/issues/2865 for more information.
*
* A value that has to be allocated is created before the old one is destroyed:
* were it the other way around, an exception while creating the new value would
* leave j with the type of the new value, but the pointer to the destroyed old one.
*/ */
template<value_t> struct external_constructor; template<value_t> struct external_constructor;
@@ -69,20 +65,18 @@ struct external_constructor<value_t::string>
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
{ {
const typename BasicJsonType::json_value value(s);
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::string; j.m_data.m_type = value_t::string;
j.m_data.m_value = value; j.m_data.m_value = s;
j.assert_invariant(); j.assert_invariant();
} }
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
{ {
const typename BasicJsonType::json_value value(std::move(s));
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::string; j.m_data.m_type = value_t::string;
j.m_data.m_value = value; j.m_data.m_value = std::move(s);
j.assert_invariant(); j.assert_invariant();
} }
@@ -91,10 +85,9 @@ struct external_constructor<value_t::string>
int > = 0 > int > = 0 >
static void construct(BasicJsonType& j, const CompatibleStringType& str) static void construct(BasicJsonType& j, const CompatibleStringType& str)
{ {
auto* created = j.template create<typename BasicJsonType::string_t>(str);
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::string; j.m_data.m_type = value_t::string;
j.m_data.m_value.string = created; j.m_data.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
j.assert_invariant(); j.assert_invariant();
} }
}; };
@@ -105,20 +98,18 @@ struct external_constructor<value_t::binary>
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
{ {
const typename BasicJsonType::json_value value(b);
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::binary; j.m_data.m_type = value_t::binary;
j.m_data.m_value = value; j.m_data.m_value = typename BasicJsonType::binary_t(b);
j.assert_invariant(); j.assert_invariant();
} }
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
{ {
const typename BasicJsonType::json_value value(std::move(b));
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::binary; j.m_data.m_type = value_t::binary;
j.m_data.m_value = value; j.m_data.m_value = typename BasicJsonType::binary_t(std::move(b));
j.assert_invariant(); j.assert_invariant();
} }
}; };
@@ -168,10 +159,9 @@ struct external_constructor<value_t::array>
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
{ {
const typename BasicJsonType::json_value value(arr);
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::array; j.m_data.m_type = value_t::array;
j.m_data.m_value = value; j.m_data.m_value = arr;
j.set_parents(); j.set_parents();
j.assert_invariant(); j.assert_invariant();
} }
@@ -179,10 +169,9 @@ struct external_constructor<value_t::array>
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
{ {
const typename BasicJsonType::json_value value(std::move(arr));
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::array; j.m_data.m_type = value_t::array;
j.m_data.m_value = value; j.m_data.m_value = std::move(arr);
j.set_parents(); j.set_parents();
j.assert_invariant(); j.assert_invariant();
} }
@@ -198,10 +187,9 @@ struct external_constructor<value_t::array>
using std::begin; using std::begin;
using std::end; using std::end;
auto* created = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::array; j.m_data.m_type = value_t::array;
j.m_data.m_value.array = created; j.m_data.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
j.set_parents(); j.set_parents();
j.assert_invariant(); j.assert_invariant();
} }
@@ -209,17 +197,15 @@ 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)
{ {
typename BasicJsonType::array_t elements;
elements.reserve(arr.size());
for (const bool x : arr)
{
elements.push_back(x);
}
const typename BasicJsonType::json_value value(std::move(elements));
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::array; j.m_data.m_type = value_t::array;
j.m_data.m_value = value; j.m_data.m_value = value_t::array;
j.set_parents(); j.m_data.m_value.array->reserve(arr.size());
for (const bool x : arr)
{
j.m_data.m_value.array->push_back(x);
j.set_parent(j.m_data.m_value.array->back());
}
j.assert_invariant(); j.assert_invariant();
} }
@@ -227,12 +213,11 @@ struct external_constructor<value_t::array>
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0> enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
static void construct(BasicJsonType& j, const std::valarray<T>& arr) static void construct(BasicJsonType& j, const std::valarray<T>& arr)
{ {
typename BasicJsonType::array_t elements(arr.size());
std::copy(std::begin(arr), std::end(arr), elements.begin());
const typename BasicJsonType::json_value value(std::move(elements));
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::array; j.m_data.m_type = value_t::array;
j.m_data.m_value = value; j.m_data.m_value = value_t::array;
j.m_data.m_value.array->resize(arr.size());
std::copy(std::begin(arr), std::end(arr), j.m_data.m_value.array->begin());
j.set_parents(); j.set_parents();
j.assert_invariant(); j.assert_invariant();
} }
@@ -244,16 +229,14 @@ struct external_constructor<value_t::array>
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0> enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
static void construct(BasicJsonType& j, CompatibleArrayType && arr) static void construct(BasicJsonType& j, CompatibleArrayType && arr)
{ {
typename BasicJsonType::array_t elements;
for (auto&& x : std::forward<CompatibleArrayType>(arr))
{
elements.push_back(x);
}
const typename BasicJsonType::json_value value(std::move(elements));
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::array; j.m_data.m_type = value_t::array;
j.m_data.m_value = value; j.m_data.m_value = value_t::array;
j.set_parents(); for (auto&& x : std::forward<CompatibleArrayType>(arr))
{
j.m_data.m_value.array->push_back(x);
j.set_parent(j.m_data.m_value.array->back());
}
j.assert_invariant(); j.assert_invariant();
} }
#endif #endif
@@ -265,10 +248,9 @@ struct external_constructor<value_t::object>
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
{ {
const typename BasicJsonType::json_value value(obj);
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::object; j.m_data.m_type = value_t::object;
j.m_data.m_value = value; j.m_data.m_value = obj;
j.set_parents(); j.set_parents();
j.assert_invariant(); j.assert_invariant();
} }
@@ -276,10 +258,9 @@ struct external_constructor<value_t::object>
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
{ {
const typename BasicJsonType::json_value value(std::move(obj));
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::object; j.m_data.m_type = value_t::object;
j.m_data.m_value = value; j.m_data.m_value = std::move(obj);
j.set_parents(); j.set_parents();
j.assert_invariant(); j.assert_invariant();
} }
@@ -291,10 +272,9 @@ struct external_constructor<value_t::object>
using std::begin; using std::begin;
using std::end; using std::end;
auto* created = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::object; j.m_data.m_type = value_t::object;
j.m_data.m_value.object = created; j.m_data.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
j.set_parents(); j.set_parents();
j.assert_invariant(); j.assert_invariant();
} }
+8 -2
View File
@@ -535,6 +535,10 @@ class json_pointer
@return const reference to the JSON value pointed to by the JSON @return const reference to the JSON value pointed to by the JSON
pointer pointer
@pre Every object key and array index the pointer refers to exists.
Like the const operator[] for keys and indices, a missing one is
undefined behavior, guarded by a runtime assertion.
@throw parse_error.106 if an array index begins with '0' @throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number @throw parse_error.109 if an array index was not a number
@throw out_of_range.402 if the array index '-' is used @throw out_of_range.402 if the array index '-' is used
@@ -549,7 +553,8 @@ class json_pointer
{ {
case detail::value_t::object: case detail::value_t::object:
{ {
// use unchecked object access // use unchecked object access; the const operator[]
// asserts that the key exists
ptr = &ptr->operator[](reference_token); ptr = &ptr->operator[](reference_token);
break; break;
} }
@@ -562,7 +567,8 @@ class json_pointer
JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr)); JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr));
} }
// use unchecked array access // use unchecked array access; the const operator[]
// asserts that the index exists
ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token)); ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
break; break;
} }
+16 -15
View File
@@ -1702,8 +1702,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
if (is_an_object) if (is_an_object)
{ {
// the initializer list is a list of pairs -> create an object // the initializer list is a list of pairs -> create an object
m_data.m_value = value_t::object;
m_data.m_type = value_t::object; m_data.m_type = value_t::object;
m_data.m_value = value_t::object;
for (auto& element_ref : init) for (auto& element_ref : init)
{ {
@@ -1725,8 +1725,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
#endif #endif
// the initializer list describes an array -> create an array // the initializer list describes an array -> create an array
m_data.m_value.array = create<array_t>(init.begin(), init.end());
m_data.m_type = value_t::array; m_data.m_type = value_t::array;
m_data.m_value.array = create<array_t>(init.begin(), init.end());
} }
set_parents(); set_parents();
@@ -1739,8 +1739,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static basic_json binary(const typename binary_t::container_type& init) static basic_json binary(const typename binary_t::container_type& init)
{ {
auto res = basic_json(); auto res = basic_json();
res.m_data.m_value = init;
res.m_data.m_type = value_t::binary; res.m_data.m_type = value_t::binary;
res.m_data.m_value = init;
return res; return res;
} }
@@ -1750,8 +1750,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype) static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
{ {
auto res = basic_json(); auto res = basic_json();
res.m_data.m_value = binary_t(init, subtype);
res.m_data.m_type = value_t::binary; res.m_data.m_type = value_t::binary;
res.m_data.m_value = binary_t(init, subtype);
return res; return res;
} }
@@ -1761,8 +1761,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static basic_json binary(typename binary_t::container_type&& init) static basic_json binary(typename binary_t::container_type&& init)
{ {
auto res = basic_json(); auto res = basic_json();
res.m_data.m_value = std::move(init);
res.m_data.m_type = value_t::binary; res.m_data.m_type = value_t::binary;
res.m_data.m_value = std::move(init);
return res; return res;
} }
@@ -1772,8 +1772,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype) static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
{ {
auto res = basic_json(); auto res = basic_json();
res.m_data.m_value = binary_t(std::move(init), subtype);
res.m_data.m_type = value_t::binary; res.m_data.m_type = value_t::binary;
res.m_data.m_value = binary_t(std::move(init), subtype);
return res; return res;
} }
@@ -2819,8 +2819,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// implicitly convert a null value to an empty array // implicitly convert a null value to an empty array
if (is_null()) if (is_null())
{ {
m_data.m_value.array = create<array_t>();
m_data.m_type = value_t::array; m_data.m_type = value_t::array;
m_data.m_value.array = create<array_t>();
assert_invariant(); assert_invariant();
} }
@@ -2866,6 +2866,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// const operator[] only works for arrays // const operator[] only works for arrays
if (JSON_HEDLEY_LIKELY(is_array())) if (JSON_HEDLEY_LIKELY(is_array()))
{ {
JSON_ASSERT(idx < m_data.m_value.array->size());
return m_data.m_value.array->operator[](idx); return m_data.m_value.array->operator[](idx);
} }
@@ -2879,8 +2880,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// implicitly convert a null value to an empty object // implicitly convert a null value to an empty object
if (is_null()) if (is_null())
{ {
m_data.m_value.object = create<object_t>();
m_data.m_type = value_t::object; m_data.m_type = value_t::object;
m_data.m_value.object = create<object_t>();
assert_invariant(); assert_invariant();
} }
@@ -2932,8 +2933,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// implicitly convert a null value to an empty object // implicitly convert a null value to an empty object
if (is_null()) if (is_null())
{ {
m_data.m_value.object = create<object_t>();
m_data.m_type = value_t::object; m_data.m_type = value_t::object;
m_data.m_value.object = create<object_t>();
assert_invariant(); assert_invariant();
} }
@@ -3868,8 +3869,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// transform a null object into an array // transform a null object into an array
if (is_null()) if (is_null())
{ {
m_data.m_value = value_t::array;
m_data.m_type = value_t::array; m_data.m_type = value_t::array;
m_data.m_value = value_t::array;
assert_invariant(); assert_invariant();
} }
@@ -3901,8 +3902,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// transform a null object into an array // transform a null object into an array
if (is_null()) if (is_null())
{ {
m_data.m_value = value_t::array;
m_data.m_type = value_t::array; m_data.m_type = value_t::array;
m_data.m_value = value_t::array;
assert_invariant(); assert_invariant();
} }
@@ -3933,8 +3934,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// transform a null object into an object // transform a null object into an object
if (is_null()) if (is_null())
{ {
m_data.m_value = value_t::object;
m_data.m_type = value_t::object; m_data.m_type = value_t::object;
m_data.m_value = value_t::object;
assert_invariant(); assert_invariant();
} }
@@ -3989,8 +3990,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// transform a null object into an array // transform a null object into an array
if (is_null()) if (is_null())
{ {
m_data.m_value = value_t::array;
m_data.m_type = value_t::array; m_data.m_type = value_t::array;
m_data.m_value = value_t::array;
assert_invariant(); assert_invariant();
} }
@@ -4014,8 +4015,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// transform a null object into an object // transform a null object into an object
if (is_null()) if (is_null())
{ {
m_data.m_value = value_t::object;
m_data.m_type = value_t::object; m_data.m_type = value_t::object;
m_data.m_value = value_t::object;
assert_invariant(); assert_invariant();
} }
@@ -4196,8 +4197,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// implicitly convert a null value to an empty object // implicitly convert a null value to an empty object
if (is_null()) if (is_null())
{ {
m_data.m_value.object = create<object_t>();
m_data.m_type = value_t::object; m_data.m_type = value_t::object;
m_data.m_value.object = create<object_t>();
assert_invariant(); assert_invariant();
} }
+51 -64
View File
@@ -6571,10 +6571,6 @@ namespace detail
* j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an * j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an
* allocated value (e.g., a string). See bug issue * allocated value (e.g., a string). See bug issue
* https://github.com/nlohmann/json/issues/2865 for more information. * https://github.com/nlohmann/json/issues/2865 for more information.
*
* A value that has to be allocated is created before the old one is destroyed:
* were it the other way around, an exception while creating the new value would
* leave j with the type of the new value, but the pointer to the destroyed old one.
*/ */
template<value_t> struct external_constructor; template<value_t> struct external_constructor;
@@ -6598,20 +6594,18 @@ struct external_constructor<value_t::string>
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
{ {
const typename BasicJsonType::json_value value(s);
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::string; j.m_data.m_type = value_t::string;
j.m_data.m_value = value; j.m_data.m_value = s;
j.assert_invariant(); j.assert_invariant();
} }
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
{ {
const typename BasicJsonType::json_value value(std::move(s));
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::string; j.m_data.m_type = value_t::string;
j.m_data.m_value = value; j.m_data.m_value = std::move(s);
j.assert_invariant(); j.assert_invariant();
} }
@@ -6620,10 +6614,9 @@ struct external_constructor<value_t::string>
int > = 0 > int > = 0 >
static void construct(BasicJsonType& j, const CompatibleStringType& str) static void construct(BasicJsonType& j, const CompatibleStringType& str)
{ {
auto* created = j.template create<typename BasicJsonType::string_t>(str);
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::string; j.m_data.m_type = value_t::string;
j.m_data.m_value.string = created; j.m_data.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
j.assert_invariant(); j.assert_invariant();
} }
}; };
@@ -6634,20 +6627,18 @@ struct external_constructor<value_t::binary>
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
{ {
const typename BasicJsonType::json_value value(b);
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::binary; j.m_data.m_type = value_t::binary;
j.m_data.m_value = value; j.m_data.m_value = typename BasicJsonType::binary_t(b);
j.assert_invariant(); j.assert_invariant();
} }
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
{ {
const typename BasicJsonType::json_value value(std::move(b));
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::binary; j.m_data.m_type = value_t::binary;
j.m_data.m_value = value; j.m_data.m_value = typename BasicJsonType::binary_t(std::move(b));
j.assert_invariant(); j.assert_invariant();
} }
}; };
@@ -6697,10 +6688,9 @@ struct external_constructor<value_t::array>
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
{ {
const typename BasicJsonType::json_value value(arr);
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::array; j.m_data.m_type = value_t::array;
j.m_data.m_value = value; j.m_data.m_value = arr;
j.set_parents(); j.set_parents();
j.assert_invariant(); j.assert_invariant();
} }
@@ -6708,10 +6698,9 @@ struct external_constructor<value_t::array>
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
{ {
const typename BasicJsonType::json_value value(std::move(arr));
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::array; j.m_data.m_type = value_t::array;
j.m_data.m_value = value; j.m_data.m_value = std::move(arr);
j.set_parents(); j.set_parents();
j.assert_invariant(); j.assert_invariant();
} }
@@ -6727,10 +6716,9 @@ struct external_constructor<value_t::array>
using std::begin; using std::begin;
using std::end; using std::end;
auto* created = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::array; j.m_data.m_type = value_t::array;
j.m_data.m_value.array = created; j.m_data.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
j.set_parents(); j.set_parents();
j.assert_invariant(); j.assert_invariant();
} }
@@ -6738,17 +6726,15 @@ 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)
{ {
typename BasicJsonType::array_t elements;
elements.reserve(arr.size());
for (const bool x : arr)
{
elements.push_back(x);
}
const typename BasicJsonType::json_value value(std::move(elements));
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::array; j.m_data.m_type = value_t::array;
j.m_data.m_value = value; j.m_data.m_value = value_t::array;
j.set_parents(); j.m_data.m_value.array->reserve(arr.size());
for (const bool x : arr)
{
j.m_data.m_value.array->push_back(x);
j.set_parent(j.m_data.m_value.array->back());
}
j.assert_invariant(); j.assert_invariant();
} }
@@ -6756,12 +6742,11 @@ struct external_constructor<value_t::array>
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0> enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
static void construct(BasicJsonType& j, const std::valarray<T>& arr) static void construct(BasicJsonType& j, const std::valarray<T>& arr)
{ {
typename BasicJsonType::array_t elements(arr.size());
std::copy(std::begin(arr), std::end(arr), elements.begin());
const typename BasicJsonType::json_value value(std::move(elements));
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::array; j.m_data.m_type = value_t::array;
j.m_data.m_value = value; j.m_data.m_value = value_t::array;
j.m_data.m_value.array->resize(arr.size());
std::copy(std::begin(arr), std::end(arr), j.m_data.m_value.array->begin());
j.set_parents(); j.set_parents();
j.assert_invariant(); j.assert_invariant();
} }
@@ -6773,16 +6758,14 @@ struct external_constructor<value_t::array>
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0> enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
static void construct(BasicJsonType& j, CompatibleArrayType && arr) static void construct(BasicJsonType& j, CompatibleArrayType && arr)
{ {
typename BasicJsonType::array_t elements;
for (auto&& x : std::forward<CompatibleArrayType>(arr))
{
elements.push_back(x);
}
const typename BasicJsonType::json_value value(std::move(elements));
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::array; j.m_data.m_type = value_t::array;
j.m_data.m_value = value; j.m_data.m_value = value_t::array;
j.set_parents(); for (auto&& x : std::forward<CompatibleArrayType>(arr))
{
j.m_data.m_value.array->push_back(x);
j.set_parent(j.m_data.m_value.array->back());
}
j.assert_invariant(); j.assert_invariant();
} }
#endif #endif
@@ -6794,10 +6777,9 @@ struct external_constructor<value_t::object>
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
{ {
const typename BasicJsonType::json_value value(obj);
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::object; j.m_data.m_type = value_t::object;
j.m_data.m_value = value; j.m_data.m_value = obj;
j.set_parents(); j.set_parents();
j.assert_invariant(); j.assert_invariant();
} }
@@ -6805,10 +6787,9 @@ struct external_constructor<value_t::object>
template<typename BasicJsonType> template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
{ {
const typename BasicJsonType::json_value value(std::move(obj));
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::object; j.m_data.m_type = value_t::object;
j.m_data.m_value = value; j.m_data.m_value = std::move(obj);
j.set_parents(); j.set_parents();
j.assert_invariant(); j.assert_invariant();
} }
@@ -6820,10 +6801,9 @@ struct external_constructor<value_t::object>
using std::begin; using std::begin;
using std::end; using std::end;
auto* created = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
j.m_data.m_value.destroy(j.m_data.m_type); j.m_data.m_value.destroy(j.m_data.m_type);
j.m_data.m_type = value_t::object; j.m_data.m_type = value_t::object;
j.m_data.m_value.object = created; j.m_data.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
j.set_parents(); j.set_parents();
j.assert_invariant(); j.assert_invariant();
} }
@@ -19199,6 +19179,10 @@ class json_pointer
@return const reference to the JSON value pointed to by the JSON @return const reference to the JSON value pointed to by the JSON
pointer pointer
@pre Every object key and array index the pointer refers to exists.
Like the const operator[] for keys and indices, a missing one is
undefined behavior, guarded by a runtime assertion.
@throw parse_error.106 if an array index begins with '0' @throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number @throw parse_error.109 if an array index was not a number
@throw out_of_range.402 if the array index '-' is used @throw out_of_range.402 if the array index '-' is used
@@ -19213,7 +19197,8 @@ class json_pointer
{ {
case detail::value_t::object: case detail::value_t::object:
{ {
// use unchecked object access // use unchecked object access; the const operator[]
// asserts that the key exists
ptr = &ptr->operator[](reference_token); ptr = &ptr->operator[](reference_token);
break; break;
} }
@@ -19226,7 +19211,8 @@ class json_pointer
JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr)); JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr));
} }
// use unchecked array access // use unchecked array access; the const operator[]
// asserts that the index exists
ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token)); ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
break; break;
} }
@@ -27605,8 +27591,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
if (is_an_object) if (is_an_object)
{ {
// the initializer list is a list of pairs -> create an object // the initializer list is a list of pairs -> create an object
m_data.m_value = value_t::object;
m_data.m_type = value_t::object; m_data.m_type = value_t::object;
m_data.m_value = value_t::object;
for (auto& element_ref : init) for (auto& element_ref : init)
{ {
@@ -27628,8 +27614,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
#endif #endif
// the initializer list describes an array -> create an array // the initializer list describes an array -> create an array
m_data.m_value.array = create<array_t>(init.begin(), init.end());
m_data.m_type = value_t::array; m_data.m_type = value_t::array;
m_data.m_value.array = create<array_t>(init.begin(), init.end());
} }
set_parents(); set_parents();
@@ -27642,8 +27628,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static basic_json binary(const typename binary_t::container_type& init) static basic_json binary(const typename binary_t::container_type& init)
{ {
auto res = basic_json(); auto res = basic_json();
res.m_data.m_value = init;
res.m_data.m_type = value_t::binary; res.m_data.m_type = value_t::binary;
res.m_data.m_value = init;
return res; return res;
} }
@@ -27653,8 +27639,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype) static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
{ {
auto res = basic_json(); auto res = basic_json();
res.m_data.m_value = binary_t(init, subtype);
res.m_data.m_type = value_t::binary; res.m_data.m_type = value_t::binary;
res.m_data.m_value = binary_t(init, subtype);
return res; return res;
} }
@@ -27664,8 +27650,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static basic_json binary(typename binary_t::container_type&& init) static basic_json binary(typename binary_t::container_type&& init)
{ {
auto res = basic_json(); auto res = basic_json();
res.m_data.m_value = std::move(init);
res.m_data.m_type = value_t::binary; res.m_data.m_type = value_t::binary;
res.m_data.m_value = std::move(init);
return res; return res;
} }
@@ -27675,8 +27661,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype) static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
{ {
auto res = basic_json(); auto res = basic_json();
res.m_data.m_value = binary_t(std::move(init), subtype);
res.m_data.m_type = value_t::binary; res.m_data.m_type = value_t::binary;
res.m_data.m_value = binary_t(std::move(init), subtype);
return res; return res;
} }
@@ -28722,8 +28708,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// implicitly convert a null value to an empty array // implicitly convert a null value to an empty array
if (is_null()) if (is_null())
{ {
m_data.m_value.array = create<array_t>();
m_data.m_type = value_t::array; m_data.m_type = value_t::array;
m_data.m_value.array = create<array_t>();
assert_invariant(); assert_invariant();
} }
@@ -28769,6 +28755,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// const operator[] only works for arrays // const operator[] only works for arrays
if (JSON_HEDLEY_LIKELY(is_array())) if (JSON_HEDLEY_LIKELY(is_array()))
{ {
JSON_ASSERT(idx < m_data.m_value.array->size());
return m_data.m_value.array->operator[](idx); return m_data.m_value.array->operator[](idx);
} }
@@ -28782,8 +28769,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// implicitly convert a null value to an empty object // implicitly convert a null value to an empty object
if (is_null()) if (is_null())
{ {
m_data.m_value.object = create<object_t>();
m_data.m_type = value_t::object; m_data.m_type = value_t::object;
m_data.m_value.object = create<object_t>();
assert_invariant(); assert_invariant();
} }
@@ -28835,8 +28822,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// implicitly convert a null value to an empty object // implicitly convert a null value to an empty object
if (is_null()) if (is_null())
{ {
m_data.m_value.object = create<object_t>();
m_data.m_type = value_t::object; m_data.m_type = value_t::object;
m_data.m_value.object = create<object_t>();
assert_invariant(); assert_invariant();
} }
@@ -29771,8 +29758,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// transform a null object into an array // transform a null object into an array
if (is_null()) if (is_null())
{ {
m_data.m_value = value_t::array;
m_data.m_type = value_t::array; m_data.m_type = value_t::array;
m_data.m_value = value_t::array;
assert_invariant(); assert_invariant();
} }
@@ -29804,8 +29791,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// transform a null object into an array // transform a null object into an array
if (is_null()) if (is_null())
{ {
m_data.m_value = value_t::array;
m_data.m_type = value_t::array; m_data.m_type = value_t::array;
m_data.m_value = value_t::array;
assert_invariant(); assert_invariant();
} }
@@ -29836,8 +29823,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// transform a null object into an object // transform a null object into an object
if (is_null()) if (is_null())
{ {
m_data.m_value = value_t::object;
m_data.m_type = value_t::object; m_data.m_type = value_t::object;
m_data.m_value = value_t::object;
assert_invariant(); assert_invariant();
} }
@@ -29892,8 +29879,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// transform a null object into an array // transform a null object into an array
if (is_null()) if (is_null())
{ {
m_data.m_value = value_t::array;
m_data.m_type = value_t::array; m_data.m_type = value_t::array;
m_data.m_value = value_t::array;
assert_invariant(); assert_invariant();
} }
@@ -29917,8 +29904,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// transform a null object into an object // transform a null object into an object
if (is_null()) if (is_null())
{ {
m_data.m_value = value_t::object;
m_data.m_type = value_t::object; m_data.m_type = value_t::object;
m_data.m_value = value_t::object;
assert_invariant(); assert_invariant();
} }
@@ -30099,8 +30086,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// implicitly convert a null value to an empty object // implicitly convert a null value to an empty object
if (is_null()) if (is_null())
{ {
m_data.m_value.object = create<object_t>();
m_data.m_type = value_t::object; m_data.m_type = value_t::object;
m_data.m_value.object = create<object_t>();
assert_invariant(); assert_invariant();
} }
-175
View File
@@ -12,11 +12,6 @@
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
using nlohmann::json; using nlohmann::json;
#include <valarray>
#if JSON_HAS_RANGES
#include <ranges>
#endif
namespace namespace
{ {
// special test case to check if memory is leaked if constructor throws // special test case to check if memory is leaked if constructor throws
@@ -399,173 +394,3 @@ TEST_CASE("bad my_allocator::construct")
j["test"].push_back("should not leak"); j["test"].push_back("should not leak");
} }
} }
// the no-exceptions CI job skips every CHECK_THROWS_AS, which would leave
// next_construct_fails set for the next allocation outside a check
#if !defined(JSON_NOEXCEPTION)
TEST_CASE("a failed allocation leaves the value unchanged")
{
// create JSON type using the throwing allocator
using my_json = nlohmann::basic_json<std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
my_allocator>;
// Each of these creates a string, array, object, or binary value. The
// value must be created before the type is changed: otherwise, a failed
// creation left a value of the new type without anything behind it (an
// assertion in its destructor, a null pointer everywhere else) or, when
// an old value was destroyed first, with a pointer to that destroyed one.
SECTION("creating a binary value")
{
const std::vector<std::uint8_t> bytes = {1, 2, 3};
my_json _;
next_construct_fails = true;
CHECK_THROWS_AS(_ = my_json::binary(bytes), std::bad_alloc&);
next_construct_fails = true;
CHECK_THROWS_AS(_ = my_json::binary(bytes, 42), std::bad_alloc&);
next_construct_fails = true;
CHECK_THROWS_AS(_ = my_json::binary(std::vector<std::uint8_t>(bytes)), std::bad_alloc&);
next_construct_fails = true;
CHECK_THROWS_AS(_ = my_json::binary(std::vector<std::uint8_t>(bytes), 42), std::bad_alloc&);
next_construct_fails = false;
}
SECTION("turning a null value into an array or object")
{
my_json j;
next_construct_fails = true;
CHECK_THROWS_AS(j[0], std::bad_alloc&);
CHECK(j.is_null());
next_construct_fails = true;
CHECK_THROWS_AS(j["key"], std::bad_alloc&);
CHECK(j.is_null());
#ifdef JSON_HAS_CPP_17
next_construct_fails = true;
CHECK_THROWS_AS(j[std::string_view("key")], std::bad_alloc&);
CHECK(j.is_null());
#endif
next_construct_fails = true;
CHECK_THROWS_AS(j.push_back(my_json(1)), std::bad_alloc&);
CHECK(j.is_null());
const my_json one = 1;
next_construct_fails = true;
CHECK_THROWS_AS(j.push_back(one), std::bad_alloc&);
CHECK(j.is_null());
next_construct_fails = true;
CHECK_THROWS_AS(j.push_back(my_json::object_t::value_type("key", 1)), std::bad_alloc&);
CHECK(j.is_null());
next_construct_fails = true;
CHECK_THROWS_AS(j.emplace_back(1), std::bad_alloc&);
CHECK(j.is_null());
next_construct_fails = true;
CHECK_THROWS_AS(j.emplace("key", 1), std::bad_alloc&);
CHECK(j.is_null());
const my_json object = {{"key", 1}};
next_construct_fails = true;
CHECK_THROWS_AS(j.update(object), std::bad_alloc&);
CHECK(j.is_null());
next_construct_fails = false;
}
SECTION("converting into an existing value")
{
// to_json replaces the value it is given; the old one must survive a
// failed creation of the new one
my_json j = "old";
next_construct_fails = true;
CHECK_THROWS_AS(nlohmann::to_json(j, std::string("new")), std::bad_alloc&);
CHECK(j == "old");
next_construct_fails = true;
CHECK_THROWS_AS(nlohmann::to_json(j, std::vector<int> {1, 2}), std::bad_alloc&);
CHECK(j == "old");
// with iterator debugging, the default constructor of VS 2015's
// std::vector is noexcept but constructs a proxy with the allocator,
// which terminates when this allocator throws
#if !(defined(_MSC_VER) && _MSC_VER < 1910 && defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL > 0)
next_construct_fails = true;
CHECK_THROWS_AS(nlohmann::to_json(j, std::vector<bool> {true, false}), std::bad_alloc&);
CHECK(j == "old");
#endif
next_construct_fails = true;
CHECK_THROWS_AS(nlohmann::to_json(j, std::map<std::string, int> {{"a", 1}}), std::bad_alloc&);
CHECK(j == "old");
next_construct_fails = true;
CHECK_THROWS_AS(nlohmann::to_json(j, my_json::binary_t({1, 2})), std::bad_alloc&);
CHECK(j == "old");
// the overloads for lvalues of the value types, for the value types
// themselves, and for the remaining compatible types
const std::string string = "new";
next_construct_fails = true;
CHECK_THROWS_AS(nlohmann::to_json(j, string), std::bad_alloc&);
CHECK(j == "old");
next_construct_fails = true;
CHECK_THROWS_AS(nlohmann::to_json(j, "new"), std::bad_alloc&);
CHECK(j == "old");
// to_json only moves a binary value that it converted from another
// container type, which my_json's std::vector<std::uint8_t> is not
using binary_constructor = nlohmann::detail::external_constructor<nlohmann::detail::value_t::binary>;
next_construct_fails = true;
CHECK_THROWS_AS(binary_constructor::construct(j, my_json::binary_t({1, 2})), std::bad_alloc&);
CHECK(j == "old");
my_json::array_t array = {1, 2};
next_construct_fails = true;
CHECK_THROWS_AS(nlohmann::to_json(j, array), std::bad_alloc&);
CHECK(j == "old");
next_construct_fails = true;
CHECK_THROWS_AS(nlohmann::to_json(j, std::move(array)), std::bad_alloc&);
CHECK(j == "old");
my_json::object_t object = {{"a", 1}};
next_construct_fails = true;
CHECK_THROWS_AS(nlohmann::to_json(j, object), std::bad_alloc&);
CHECK(j == "old");
next_construct_fails = true;
CHECK_THROWS_AS(nlohmann::to_json(j, std::move(object)), std::bad_alloc&);
CHECK(j == "old");
next_construct_fails = true;
CHECK_THROWS_AS(nlohmann::to_json(j, std::valarray<int> {1, 2}), std::bad_alloc&);
CHECK(j == "old");
#if JSON_HAS_RANGES && !defined(__MINGW32__)
const std::vector<int> numbers = {1, 2};
next_construct_fails = true;
CHECK_THROWS_AS(nlohmann::to_json(j, numbers | std::views::filter([](int /*unused*/)
{
return true;
})), std::bad_alloc&);
CHECK(j == "old");
#endif
next_construct_fails = false;
nlohmann::to_json(j, std::vector<int> {1, 2});
CHECK(j == my_json({1, 2}));
}
}
#endif