mirror of
https://github.com/nlohmann/json.git
synced 2026-10-15 10:47:13 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0570c5b06b | ||
|
|
f674a61318 | ||
|
|
106065e62d | ||
|
|
ebab8a11cc |
No files matched your search
@@ -428,7 +428,7 @@ void templated_json_throw(ExceptionType exception)
|
||||
@throw type_error.302 always
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
JSON_HEDLEY_NO_RETURN inline void throw_type_must_be(const char* expected, const BasicJsonType& j)
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_type_must_be(const char* expected, const BasicJsonType& j)
|
||||
{
|
||||
static_cast<void>(expected); // unused when JSON_NOEXCEPTION is defined
|
||||
static_cast<void>(j);
|
||||
@@ -443,7 +443,7 @@ JSON_HEDLEY_NO_RETURN inline void throw_type_must_be(const char* expected, const
|
||||
@throw type_error always
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
JSON_HEDLEY_NO_RETURN inline void throw_cannot_use_with(const exception_id id_, const char* operation, const BasicJsonType& j)
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_cannot_use_with(const exception_id id_, const char* operation, const BasicJsonType& j)
|
||||
{
|
||||
static_cast<void>(id_); // unused when JSON_NOEXCEPTION is defined
|
||||
static_cast<void>(operation);
|
||||
@@ -451,6 +451,18 @@ JSON_HEDLEY_NO_RETURN inline void throw_cannot_use_with(const exception_id id_,
|
||||
JSON_THROW(type_error::create(id_, concat("cannot use ", operation, " with ", j.type_name()), &j));
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief throws ExceptionType with a fixed message; kept out of line so that callers do not carry the exception construction
|
||||
*/
|
||||
template<typename ExceptionType, typename BasicJsonContext>
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_literal(const exception_id id_, const char* what_arg, BasicJsonContext context)
|
||||
{
|
||||
static_cast<void>(id_); // unused when JSON_NOEXCEPTION is defined
|
||||
static_cast<void>(what_arg);
|
||||
static_cast<void>(context);
|
||||
JSON_THROW(ExceptionType::create(id_, what_arg, context));
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
|
||||
@@ -280,7 +280,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
}
|
||||
|
||||
/// @throw invalid_iterator.214 always
|
||||
JSON_HEDLEY_NO_RETURN void throw_cannot_get_value() const
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_cannot_get_value() const
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(exception_id::iterator_value_unavailable, "cannot get value", m_object));
|
||||
}
|
||||
@@ -484,7 +484,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
// if objects are not the same, the comparison is undefined
|
||||
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object));
|
||||
throw_literal<invalid_iterator>(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object);
|
||||
}
|
||||
|
||||
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
|
||||
@@ -533,7 +533,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
// if objects are not the same, the comparison is undefined
|
||||
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object));
|
||||
throw_literal<invalid_iterator>(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object);
|
||||
}
|
||||
|
||||
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
|
||||
@@ -546,7 +546,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
JSON_THROW(invalid_iterator::create(exception_id::iterator_order_on_object, "cannot compare order of object iterators", m_object));
|
||||
throw_literal<invalid_iterator>(exception_id::iterator_order_on_object, "cannot compare order of object iterators", m_object);
|
||||
|
||||
case value_t::array:
|
||||
return (m_it.array_iterator < other.m_it.array_iterator);
|
||||
@@ -602,7 +602,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
JSON_THROW(invalid_iterator::create(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object));
|
||||
throw_literal<invalid_iterator>(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object);
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
@@ -681,7 +681,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
JSON_THROW(invalid_iterator::create(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object));
|
||||
throw_literal<invalid_iterator>(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object);
|
||||
|
||||
case value_t::array:
|
||||
return m_it.array_iterator - other.m_it.array_iterator;
|
||||
@@ -710,7 +710,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
JSON_THROW(invalid_iterator::create(exception_id::iterator_subscript_on_object, "cannot use operator[] for object iterators", m_object));
|
||||
throw_literal<invalid_iterator>(exception_id::iterator_subscript_on_object, "cannot use operator[] for object iterators", m_object);
|
||||
|
||||
case value_t::array:
|
||||
return *std::next(m_it.array_iterator, n);
|
||||
@@ -750,7 +750,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
return m_it.object_iterator->first;
|
||||
}
|
||||
|
||||
JSON_THROW(invalid_iterator::create(exception_id::iterator_key_not_object, "cannot use key() for non-object iterators", m_object));
|
||||
throw_literal<invalid_iterator>(exception_id::iterator_key_not_object, "cannot use key() for non-object iterators", m_object);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
+384
-119
@@ -585,26 +585,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
switch (t)
|
||||
{
|
||||
case value_t::object:
|
||||
{
|
||||
object = create<object_t>();
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
array = create<array_t>();
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::string:
|
||||
{
|
||||
string = create<string_t>("");
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::binary:
|
||||
{
|
||||
binary = create<binary_t>();
|
||||
// allocating: kept out of line, so the scalar cases below inline
|
||||
init_owned(t);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -644,13 +630,41 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
object = nullptr; // silence warning, see #821
|
||||
if (JSON_HEDLEY_UNLIKELY(t == value_t::null))
|
||||
{
|
||||
JSON_THROW(other_error::create(detail::exception_id::internal_error, "961c151d2e87f2686a955a9be24d316f1362bf21 3.12.0", nullptr)); // LCOV_EXCL_LINE
|
||||
detail::throw_literal<other_error>(detail::exception_id::internal_error, "961c151d2e87f2686a955a9be24d316f1362bf21 3.12.0", nullptr); // LCOV_EXCL_LINE
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NEVER_INLINE
|
||||
void init_owned(value_t t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case value_t::object:
|
||||
object = create<object_t>();
|
||||
break;
|
||||
case value_t::array:
|
||||
array = create<array_t>();
|
||||
break;
|
||||
case value_t::string:
|
||||
string = create<string_t>("");
|
||||
break;
|
||||
case value_t::binary:
|
||||
binary = create<binary_t>();
|
||||
break;
|
||||
case value_t::null:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// constructor for strings
|
||||
json_value(const string_t& value) : string(create<string_t>(value)) {}
|
||||
|
||||
@@ -911,7 +925,18 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// only strings, binaries, arrays and objects own memory; keep the
|
||||
// check for them inline so destroying a scalar is a compare and a
|
||||
// branch, and leave the actual work to an out-of-line function
|
||||
void destroy(value_t t)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(t == value_t::string || t == value_t::binary || t == value_t::object || t == value_t::array))
|
||||
{
|
||||
destroy_owned(t);
|
||||
}
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NEVER_INLINE void destroy_owned(value_t t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
@@ -1079,51 +1104,64 @@ public:
|
||||
return next;
|
||||
}
|
||||
|
||||
/// @brief the capacity of the stored array, or unknown_size()
|
||||
/// @brief the capacity of the stored array or object, or unknown_size()
|
||||
/// Only JSON_DIAGNOSTICS uses the value, to detect a reallocation that
|
||||
/// would invalidate the parent pointers. Array types that do not have a
|
||||
/// capacity() member function report unknown_size(), which is treated as
|
||||
/// "the elements may have moved".
|
||||
/// would invalidate the parent pointers. Containers that do not have a
|
||||
/// capacity() member function report unknown_size() if their elements may
|
||||
/// move (arrays, ordered_map) and 0 if they never do (std::map).
|
||||
#if JSON_DIAGNOSTICS
|
||||
template < typename A = array_t, detail::enable_if_t < detail::has_capacity<A>::value, int > = 0 >
|
||||
std::size_t array_capacity() const noexcept
|
||||
template < typename C, detail::enable_if_t < detail::has_capacity<C>::value, int > = 0 >
|
||||
static std::size_t capacity_of(const C& container) noexcept
|
||||
{
|
||||
return m_data.m_value.array->capacity();
|
||||
return container.capacity();
|
||||
}
|
||||
|
||||
template < typename A = array_t, detail::enable_if_t < !detail::has_capacity<A>::value, int > = 0 >
|
||||
std::size_t array_capacity() const noexcept
|
||||
template < typename C, detail::enable_if_t < !detail::has_capacity<C>::value, int > = 0 >
|
||||
static std::size_t capacity_of(const C& /*container*/) noexcept
|
||||
{
|
||||
return detail::unknown_size();
|
||||
return std::is_same<C, array_t>::value || detail::is_ordered_map<C>::value ? detail::unknown_size() : 0;
|
||||
}
|
||||
|
||||
std::size_t container_capacity() const noexcept
|
||||
{
|
||||
return is_array() ? capacity_of(*m_data.m_value.array) : capacity_of(*m_data.m_value.object);
|
||||
}
|
||||
#else
|
||||
static constexpr std::size_t array_capacity() noexcept
|
||||
static constexpr std::size_t container_capacity() noexcept
|
||||
{
|
||||
return detail::unknown_size();
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief set the parent of a value that has just been added to an array
|
||||
/// @param j the added value
|
||||
/// @param old_capacity the value @ref array_capacity() returned before the
|
||||
/// @brief set the parent of a value that has just been added to an array or object
|
||||
/// @param j the added (or looked-up) value
|
||||
/// @param old_capacity the value @ref container_capacity() returned before the
|
||||
/// insertion
|
||||
reference set_parent_after_array_insert(reference j, std::size_t old_capacity)
|
||||
reference set_parent_after_insert(reference j, std::size_t old_capacity)
|
||||
{
|
||||
#if JSON_DIAGNOSTICS
|
||||
// see https://github.com/nlohmann/json/issues/2838
|
||||
JSON_ASSERT(type() == value_t::array);
|
||||
if (JSON_HEDLEY_UNLIKELY(old_capacity == detail::unknown_size()
|
||||
|| array_capacity() != old_capacity))
|
||||
|| container_capacity() != old_capacity))
|
||||
{
|
||||
// the capacity has changed, or the array type does not let us tell:
|
||||
// the elements may have moved, so update all parents
|
||||
// the capacity has changed, or the container type does not let us
|
||||
// tell: the elements may have moved, so update all parents
|
||||
set_parents();
|
||||
return j;
|
||||
}
|
||||
#else
|
||||
static_cast<void>(old_capacity);
|
||||
#endif
|
||||
return set_parent(j);
|
||||
return set_parent_at(j);
|
||||
}
|
||||
|
||||
/// @brief set the parent of an element accessed via at(); never moves elements
|
||||
reference set_parent_at(reference j) noexcept
|
||||
{
|
||||
#if JSON_DIAGNOSTICS
|
||||
j.m_parent = this;
|
||||
#endif
|
||||
return j;
|
||||
}
|
||||
|
||||
reference set_parent(reference j)
|
||||
@@ -2310,7 +2348,7 @@ public:
|
||||
// if an object is wanted but impossible, throw an exception
|
||||
if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object))
|
||||
{
|
||||
JSON_THROW(type_error::create(detail::exception_id::object_from_non_pairs, "cannot create object from initializer list", nullptr));
|
||||
detail::throw_literal<type_error>(detail::exception_id::object_from_non_pairs, "cannot create object from initializer list", nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2430,7 +2468,7 @@ public:
|
||||
// make sure the iterator fits the current value
|
||||
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterators_incompatible, "iterators are not compatible", nullptr));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterators_incompatible, "iterators are not compatible", nullptr);
|
||||
}
|
||||
|
||||
// copy type from the first iterator
|
||||
@@ -2449,7 +2487,7 @@ public:
|
||||
if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
|
||||
|| !last.m_it.primitive_iterator.is_end()))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_out_of_range, "iterators out of range", first.m_object));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_range_out_of_range, "iterators out of range", first.m_object);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2517,7 +2555,7 @@ public:
|
||||
case value_t::null:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_of_null, detail::concat("cannot construct with iterators from ", first.m_object->type_name()), first.m_object));
|
||||
throw_iterator_range_of_null(first.m_object);
|
||||
}
|
||||
|
||||
set_parents();
|
||||
@@ -2546,18 +2584,17 @@ public:
|
||||
// check of passed value is valid
|
||||
other.assert_invariant();
|
||||
|
||||
if (m_data.m_type == value_t::object || m_data.m_type == value_t::array)
|
||||
if (JSON_HEDLEY_LIKELY(m_data.m_type != value_t::object && m_data.m_type != value_t::array
|
||||
&& m_data.m_type != value_t::string && m_data.m_type != value_t::binary))
|
||||
{
|
||||
// copying the container directly would call this constructor again
|
||||
// for every element, once per nesting level
|
||||
copy_structured(other);
|
||||
// null, boolean, numbers, discarded: plain copy of the union
|
||||
m_data.m_value = other.m_data.m_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
copy_leaf_value(other, *this);
|
||||
copy_owning(other);
|
||||
}
|
||||
|
||||
set_parents();
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -2787,6 +2824,22 @@ public:
|
||||
// value access //
|
||||
//////////////////
|
||||
|
||||
/// out-of-line part of the copy constructor for owning types
|
||||
JSON_HEDLEY_NEVER_INLINE
|
||||
void copy_owning(const basic_json& other)
|
||||
{
|
||||
if (m_data.m_type == value_t::object || m_data.m_type == value_t::array)
|
||||
{
|
||||
// copying the container directly would call this constructor again
|
||||
// for every element, once per nesting level
|
||||
copy_structured(other);
|
||||
}
|
||||
else
|
||||
{
|
||||
copy_leaf_value(other, *this);
|
||||
}
|
||||
}
|
||||
|
||||
/// get a pointer to the value (object)
|
||||
object_t* get_impl_ptr(object_t* /*unused*/) noexcept
|
||||
{
|
||||
@@ -2883,6 +2936,40 @@ public:
|
||||
return is_binary() ? m_data.m_value.binary : nullptr;
|
||||
}
|
||||
|
||||
/// out-of-line throw helpers (message construction kept out of the hot paths)
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||
static void throw_iterator_range_of_null(const basic_json* obj)
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_of_null, detail::concat("cannot construct with iterators from ", obj->type_name()), obj));
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||
static void throw_incompatible_reference_type(const basic_json& obj)
|
||||
{
|
||||
JSON_THROW(type_error::create(detail::exception_id::incompatible_reference_type, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||
static void throw_array_index_exceeds_size_type(size_type idx)
|
||||
{
|
||||
JSON_THROW(std::length_error(detail::concat("array index ", std::to_string(idx), " exceeds size_type")));
|
||||
}
|
||||
|
||||
/// @throw out_of_range.401
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||
static void throw_array_index_out_of_range(size_type idx, const basic_json& j)
|
||||
{
|
||||
JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), &j));
|
||||
}
|
||||
|
||||
/// @throw out_of_range.403
|
||||
template<typename KeyType>
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||
static void throw_key_not_found(const KeyType& key, const basic_json& j)
|
||||
{
|
||||
JSON_THROW(out_of_range::create(detail::exception_id::key_not_found, detail::concat("key '", key, "' not found"), &j));
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief helper function to implement get_ref()
|
||||
|
||||
@@ -2905,7 +2992,7 @@ public:
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
JSON_THROW(type_error::create(detail::exception_id::incompatible_reference_type, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
|
||||
throw_incompatible_reference_type(obj);
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -3353,7 +3440,7 @@ public:
|
||||
// std::map or ordered_map) never moves from its argument, so key is still
|
||||
// valid here regardless of whether KeyType was deduced as an rvalue reference
|
||||
// NOLINTNEXTLINE(bugprone-use-after-move,hicpp-invalid-access-moved)
|
||||
JSON_THROW(out_of_range::create(detail::exception_id::key_not_found, detail::concat("key '", key_for_message(key), "' not found"), &j));
|
||||
throw_key_not_found(key_for_message(key), j);
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
@@ -3390,7 +3477,7 @@ public:
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(idx >= j.m_data.m_value.array->size()))
|
||||
{
|
||||
JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), &j));
|
||||
throw_array_index_out_of_range(idx, j);
|
||||
}
|
||||
|
||||
return (*j.m_data.m_value.array)[idx];
|
||||
@@ -3445,7 +3532,7 @@ public:
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/at/
|
||||
reference at(size_type idx)
|
||||
{
|
||||
return set_parent(array_at(*this, idx));
|
||||
return set_parent_at(array_at(*this, idx));
|
||||
}
|
||||
|
||||
/// @brief access specified array element with bounds checking
|
||||
@@ -3459,7 +3546,7 @@ public:
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/at/
|
||||
reference at(const typename object_t::key_type& key)
|
||||
{
|
||||
return set_parent(object_at(*this, key));
|
||||
return set_parent_at(object_at(*this, key));
|
||||
}
|
||||
|
||||
/// @brief access specified object element with bounds checking
|
||||
@@ -3468,7 +3555,7 @@ public:
|
||||
detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
|
||||
reference at(KeyType && key)
|
||||
{
|
||||
return set_parent(object_at(*this, std::forward<KeyType>(key)));
|
||||
return set_parent_at(object_at(*this, std::forward<KeyType>(key)));
|
||||
}
|
||||
|
||||
/// @brief access specified object element with bounds checking
|
||||
@@ -3508,18 +3595,18 @@ public:
|
||||
// resize() rejects other indices that are too large to represent
|
||||
if (JSON_HEDLEY_UNLIKELY(idx == (std::numeric_limits<size_type>::max)()))
|
||||
{
|
||||
JSON_THROW(std::length_error(detail::concat("array index ", std::to_string(idx), " exceeds size_type")));
|
||||
throw_array_index_exceeds_size_type(idx);
|
||||
}
|
||||
#if JSON_DIAGNOSTICS
|
||||
// remember array size & capacity before resizing
|
||||
const auto old_size = m_data.m_value.array->size();
|
||||
const auto old_capacity = array_capacity();
|
||||
const auto old_capacity = container_capacity();
|
||||
#endif
|
||||
m_data.m_value.array->resize(idx + 1);
|
||||
|
||||
#if JSON_DIAGNOSTICS
|
||||
if (JSON_HEDLEY_UNLIKELY(old_capacity == detail::unknown_size()
|
||||
|| array_capacity() != old_capacity))
|
||||
|| container_capacity() != old_capacity))
|
||||
{
|
||||
// capacity has changed: update all parents
|
||||
set_parents();
|
||||
@@ -3566,8 +3653,9 @@ public:
|
||||
// operator[] only works for objects
|
||||
if (JSON_HEDLEY_LIKELY(is_object()))
|
||||
{
|
||||
const auto old_capacity = container_capacity();
|
||||
auto result = m_data.m_value.object->emplace(std::move(key), nullptr);
|
||||
return set_parent(result.first->second);
|
||||
return set_parent_after_insert(result.first->second, old_capacity);
|
||||
}
|
||||
|
||||
throw_subscript_wrong_type("string");
|
||||
@@ -3617,8 +3705,9 @@ public:
|
||||
// operator[] only works for objects
|
||||
if (JSON_HEDLEY_LIKELY(is_object()))
|
||||
{
|
||||
const auto old_capacity = container_capacity();
|
||||
auto result = m_data.m_value.object->emplace(lookup_key(std::forward<KeyType>(key)), nullptr);
|
||||
return set_parent(result.first->second);
|
||||
return set_parent_after_insert(result.first->second, old_capacity);
|
||||
}
|
||||
|
||||
throw_subscript_wrong_type("string");
|
||||
@@ -3844,7 +3933,7 @@ public:
|
||||
// make sure the iterator fits the current value
|
||||
if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this);
|
||||
}
|
||||
|
||||
IteratorType result = end();
|
||||
@@ -3860,7 +3949,7 @@ public:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin()))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_out_of_range, "iterator out of range", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_out_of_range, "iterator out of range", this);
|
||||
}
|
||||
|
||||
m_data.m_value.destroy(m_data.m_type);
|
||||
@@ -3902,7 +3991,7 @@ public:
|
||||
// make sure the iterator fits the current value
|
||||
if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_from_other_value, "iterators do not fit current value", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_range_from_other_value, "iterators do not fit current value", this);
|
||||
}
|
||||
|
||||
IteratorType result = end();
|
||||
@@ -3919,7 +4008,7 @@ public:
|
||||
if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
|
||||
|| !last.m_it.primitive_iterator.is_end()))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_out_of_range, "iterators out of range", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_range_out_of_range, "iterators out of range", this);
|
||||
}
|
||||
|
||||
m_data.m_value.destroy(m_data.m_type);
|
||||
@@ -4018,7 +4107,7 @@ public:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(idx >= size()))
|
||||
{
|
||||
JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
|
||||
throw_array_index_out_of_range(idx, *this);
|
||||
}
|
||||
|
||||
m_data.m_value.array->erase(m_data.m_value.array->begin() + static_cast<difference_type>(idx));
|
||||
@@ -4524,9 +4613,9 @@ public:
|
||||
}
|
||||
|
||||
// add the element to the array (move semantics)
|
||||
const auto old_capacity = array_capacity();
|
||||
const auto old_capacity = container_capacity();
|
||||
m_data.m_value.array->push_back(std::move(val));
|
||||
set_parent_after_array_insert(m_data.m_value.array->back(), old_capacity);
|
||||
set_parent_after_insert(m_data.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
|
||||
}
|
||||
|
||||
@@ -4555,9 +4644,9 @@ public:
|
||||
}
|
||||
|
||||
// add the element to the array
|
||||
const auto old_capacity = array_capacity();
|
||||
const auto old_capacity = container_capacity();
|
||||
m_data.m_value.array->push_back(val);
|
||||
set_parent_after_array_insert(m_data.m_value.array->back(), old_capacity);
|
||||
set_parent_after_insert(m_data.m_value.array->back(), old_capacity);
|
||||
}
|
||||
|
||||
/// @brief add an object to an array
|
||||
@@ -4585,8 +4674,9 @@ public:
|
||||
}
|
||||
|
||||
// add the element to the object
|
||||
const auto old_capacity = container_capacity();
|
||||
auto res = m_data.m_value.object->insert(val);
|
||||
set_parent(res.first->second);
|
||||
set_parent_after_insert(res.first->second, old_capacity);
|
||||
}
|
||||
|
||||
/// @brief add an object to an object
|
||||
@@ -4639,9 +4729,9 @@ public:
|
||||
}
|
||||
|
||||
// add the element to the array (perfect forwarding)
|
||||
const auto old_capacity = array_capacity();
|
||||
const auto old_capacity = container_capacity();
|
||||
m_data.m_value.array->emplace_back(std::forward<Args>(args)...);
|
||||
return set_parent_after_array_insert(m_data.m_value.array->back(), old_capacity);
|
||||
return set_parent_after_insert(m_data.m_value.array->back(), old_capacity);
|
||||
}
|
||||
|
||||
/// @brief add an object to an object if key does not exist
|
||||
@@ -4662,8 +4752,9 @@ public:
|
||||
}
|
||||
|
||||
// add the element to the array (perfect forwarding)
|
||||
const auto old_capacity = container_capacity();
|
||||
auto res = m_data.m_value.object->emplace(std::forward<Args>(args)...);
|
||||
set_parent(res.first->second);
|
||||
set_parent_after_insert(res.first->second, old_capacity);
|
||||
|
||||
// create a result iterator and set iterator to the result of emplace
|
||||
auto it = begin();
|
||||
@@ -4704,7 +4795,7 @@ public:
|
||||
// check if iterator pos fits to this JSON value
|
||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this);
|
||||
}
|
||||
|
||||
// insert to array and return iterator
|
||||
@@ -4724,7 +4815,7 @@ public:
|
||||
// check if iterator pos fits to this JSON value
|
||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this);
|
||||
}
|
||||
|
||||
// moving into a local first keeps this safe even if val aliases
|
||||
@@ -4746,7 +4837,7 @@ public:
|
||||
// check if iterator pos fits to this JSON value
|
||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this);
|
||||
}
|
||||
|
||||
// insert to array and return iterator
|
||||
@@ -4769,24 +4860,24 @@ public:
|
||||
// check if iterator pos fits to this JSON value
|
||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this);
|
||||
}
|
||||
|
||||
// check if range iterators belong to the same JSON object
|
||||
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::insert_range_incompatible, "iterators do not fit", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::insert_range_incompatible, "iterators do not fit", this);
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(first.m_object == this))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::insert_range_into_itself, "passed iterators may not belong to container", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::insert_range_into_itself, "passed iterators may not belong to container", this);
|
||||
}
|
||||
|
||||
// passed iterators must belong to arrays
|
||||
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_array()))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterators first and last must point to arrays", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterators first and last must point to arrays", this);
|
||||
}
|
||||
|
||||
// insert to array and return iterator
|
||||
@@ -4806,7 +4897,7 @@ public:
|
||||
// check if iterator pos fits to this JSON value
|
||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this);
|
||||
}
|
||||
|
||||
// copy the values first: ilist may refer to elements of this array
|
||||
@@ -4834,13 +4925,13 @@ public:
|
||||
// check if range iterators belong to the same JSON object
|
||||
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::insert_range_incompatible, "iterators do not fit", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::insert_range_incompatible, "iterators do not fit", this);
|
||||
}
|
||||
|
||||
// passed iterators must belong to objects
|
||||
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterators first and last must point to objects", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterators first and last must point to objects", this);
|
||||
}
|
||||
|
||||
m_data.m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);
|
||||
@@ -4875,7 +4966,7 @@ public:
|
||||
// check if range iterators belong to the same JSON object
|
||||
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::insert_range_incompatible, "iterators do not fit", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::insert_range_incompatible, "iterators do not fit", this);
|
||||
}
|
||||
|
||||
// passed iterators must belong to objects
|
||||
@@ -4962,9 +5053,9 @@ public:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// set_parent() also repairs the other members, which ordered_json
|
||||
// relocates when adding a key makes its vector grow
|
||||
set_parent(m_data.m_value.object->operator[](it.key()) = std::move(it.value()));
|
||||
// operator[] repairs the other members if ordered_json relocates
|
||||
// them; assigning through it keeps this O(1) per key
|
||||
operator[](it.key()) = std::move(it.value());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5014,9 +5105,8 @@ public:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// set_parent() also repairs the other members, which ordered_json
|
||||
// relocates when adding a key makes its vector grow
|
||||
target->set_parent(target->m_data.m_value.object->operator[](first.key()) = std::move(first.value()));
|
||||
// operator[] repairs the other members if ordered_json relocates them
|
||||
target->operator[](first.key()) = std::move(first.value());
|
||||
++first;
|
||||
}
|
||||
}
|
||||
@@ -5158,6 +5248,20 @@ public:
|
||||
/// @name lexicographical comparison operators
|
||||
/// @{
|
||||
|
||||
// the same-type cases for non-owning scalars, shared by JSON_IMPLEMENT_OPERATOR
|
||||
// and JSON_IMPLEMENT_SCALAR_FAST
|
||||
#define JSON_IMPLEMENT_SCALAR_CASES(op, null_result) \
|
||||
case value_t::null: \
|
||||
return (null_result); \
|
||||
case value_t::boolean: \
|
||||
return (lhs.m_data.m_value.boolean) op (rhs.m_data.m_value.boolean); \
|
||||
case value_t::number_integer: \
|
||||
return (lhs.m_data.m_value.number_integer) op (rhs.m_data.m_value.number_integer); \
|
||||
case value_t::number_unsigned: \
|
||||
return (lhs.m_data.m_value.number_unsigned) op (rhs.m_data.m_value.number_unsigned); \
|
||||
case value_t::number_float: \
|
||||
return (lhs.m_data.m_value.number_float) op (rhs.m_data.m_value.number_float);
|
||||
|
||||
// note parentheses around operands are necessary; see
|
||||
// https://github.com/nlohmann/json/issues/1530
|
||||
// Mixed signed/unsigned integer comparisons check whether the signed value
|
||||
@@ -5194,24 +5298,11 @@ public:
|
||||
return (*lhs.m_data.m_value.object) op (*rhs.m_data.m_value.object); \
|
||||
} \
|
||||
\
|
||||
case value_t::null: \
|
||||
return (null_result); \
|
||||
\
|
||||
JSON_IMPLEMENT_SCALAR_CASES(op, null_result) \
|
||||
\
|
||||
case value_t::string: \
|
||||
return (*lhs.m_data.m_value.string) op (*rhs.m_data.m_value.string); \
|
||||
\
|
||||
case value_t::boolean: \
|
||||
return (lhs.m_data.m_value.boolean) op (rhs.m_data.m_value.boolean); \
|
||||
\
|
||||
case value_t::number_integer: \
|
||||
return (lhs.m_data.m_value.number_integer) op (rhs.m_data.m_value.number_integer); \
|
||||
\
|
||||
case value_t::number_unsigned: \
|
||||
return (lhs.m_data.m_value.number_unsigned) op (rhs.m_data.m_value.number_unsigned); \
|
||||
\
|
||||
case value_t::number_float: \
|
||||
return (lhs.m_data.m_value.number_float) op (rhs.m_data.m_value.number_float); \
|
||||
\
|
||||
case value_t::binary: \
|
||||
return (*lhs.m_data.m_value.binary) op (*rhs.m_data.m_value.binary); \
|
||||
\
|
||||
@@ -5255,6 +5346,24 @@ public:
|
||||
\
|
||||
return (default_result);
|
||||
|
||||
// inline fast path for two values of the same non-owning scalar type; anything
|
||||
// else falls through to the out-of-line general comparison
|
||||
#define JSON_IMPLEMENT_SCALAR_FAST(op, null_result) \
|
||||
if (lhs.m_data.m_type == rhs.m_data.m_type) \
|
||||
{ \
|
||||
switch (lhs.m_data.m_type) \
|
||||
{ \
|
||||
JSON_IMPLEMENT_SCALAR_CASES(op, null_result) \
|
||||
case value_t::object: \
|
||||
case value_t::array: \
|
||||
case value_t::string: \
|
||||
case value_t::binary: \
|
||||
case value_t::discarded: \
|
||||
default: \
|
||||
break; \
|
||||
} \
|
||||
}
|
||||
|
||||
JSON_PRIVATE_UNLESS_TESTED:
|
||||
// returns true if:
|
||||
// - any operand is NaN and the other operand is of number type
|
||||
@@ -5282,6 +5391,168 @@ public:
|
||||
return compares_unordered(*this, rhs, inverse);
|
||||
}
|
||||
|
||||
/// @brief whether objects and arrays are the standard containers, whose
|
||||
/// elements equal_bounded() may therefore compare itself
|
||||
using equal_bounded_containers = std::integral_constant < bool,
|
||||
std::is_same<object_t, std::map<StringType, basic_json, default_object_comparator_t,
|
||||
AllocatorType<std::pair<const StringType, basic_json>>>>::value &&
|
||||
std::is_same<array_t, std::vector<basic_json, AllocatorType<basic_json>>>::value >;
|
||||
|
||||
// out-of-line general comparisons; the operators below handle same-type
|
||||
// scalars inline and fall through to these
|
||||
JSON_HEDLEY_NEVER_INLINE
|
||||
static bool equal_general(const_reference lhs, const_reference rhs) noexcept
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
JSON_HEDLEY_DIAGNOSTIC_PUSH
|
||||
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
|
||||
#endif
|
||||
JSON_IMPLEMENT_OPERATOR( ==, true, false, false,
|
||||
compare_iteratively<false>(lhs, rhs, false) == compare_result::equal, true)
|
||||
#ifdef __GNUC__
|
||||
JSON_HEDLEY_DIAGNOSTIC_POP
|
||||
#endif
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NEVER_INLINE
|
||||
static bool less_general(const_reference lhs, const_reference rhs) noexcept
|
||||
{
|
||||
// default_result is used if we cannot compare values. In that case,
|
||||
// we compare types. Note we have to call the operator explicitly,
|
||||
// because MSVC has problems otherwise.
|
||||
JSON_IMPLEMENT_OPERATOR( <, false, false, operator<(lhs_type, rhs_type),
|
||||
compare_iteratively<true>(lhs, rhs, true) == compare_result::less, false)
|
||||
}
|
||||
|
||||
#if JSON_HAS_THREE_WAY_COMPARISON
|
||||
JSON_HEDLEY_NEVER_INLINE
|
||||
static std::partial_ordering three_way_general(const_reference lhs, const_reference rhs) noexcept // *NOPAD*
|
||||
{
|
||||
// default_result is used if we cannot compare values. In that case,
|
||||
// we compare types.
|
||||
JSON_IMPLEMENT_OPERATOR(<=>, // *NOPAD*
|
||||
std::partial_ordering::equivalent,
|
||||
std::partial_ordering::unordered,
|
||||
lhs_type <=> rhs_type, // *NOPAD*
|
||||
to_partial_ordering(compare_iteratively<true>(lhs, rhs, false)), true)
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief compare two values of the same type that holds no children
|
||||
/// @return 1 if they are equal, 0 if they are not, and -1 if the values are
|
||||
/// of different types or have children, which this leaves to
|
||||
/// equal_structured()
|
||||
JSON_HEDLEY_ALWAYS_INLINE
|
||||
static int equal_scalar(const_reference lhs, const_reference rhs) noexcept
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
JSON_HEDLEY_DIAGNOSTIC_PUSH
|
||||
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
|
||||
#endif
|
||||
if (lhs.m_data.m_type == rhs.m_data.m_type)
|
||||
{
|
||||
switch (lhs.m_data.m_type)
|
||||
{
|
||||
case value_t::null:
|
||||
return 1;
|
||||
case value_t::boolean:
|
||||
return lhs.m_data.m_value.boolean == rhs.m_data.m_value.boolean;
|
||||
case value_t::number_integer:
|
||||
return lhs.m_data.m_value.number_integer == rhs.m_data.m_value.number_integer;
|
||||
case value_t::number_unsigned:
|
||||
return lhs.m_data.m_value.number_unsigned == rhs.m_data.m_value.number_unsigned;
|
||||
case value_t::number_float:
|
||||
return lhs.m_data.m_value.number_float == rhs.m_data.m_value.number_float;
|
||||
case value_t::string:
|
||||
return *lhs.m_data.m_value.string == *rhs.m_data.m_value.string;
|
||||
case value_t::object:
|
||||
case value_t::array:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef __GNUC__
|
||||
JSON_HEDLEY_DIAGNOSTIC_POP
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// @brief compare for equality, walking objects and arrays here instead of
|
||||
/// in their containers so that the depth is a parameter and needs no
|
||||
/// thread-local count; only used for the standard containers
|
||||
static bool equal_bounded(const_reference lhs, const_reference rhs, std::size_t depth) noexcept
|
||||
{
|
||||
const int scalar = equal_scalar(lhs, rhs);
|
||||
return scalar < 0 ? equal_structured(lhs, rhs, depth) : scalar != 0;
|
||||
}
|
||||
|
||||
static bool equal_structured(const_reference lhs, const_reference rhs, std::size_t depth) noexcept
|
||||
{
|
||||
const auto type = lhs.m_data.m_type;
|
||||
if (type != rhs.m_data.m_type || (type != value_t::array && type != value_t::object))
|
||||
{
|
||||
return equal_general(lhs, rhs);
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(depth >= detail::recursion_depth_limit()))
|
||||
{
|
||||
return compare_iteratively<false>(lhs, rhs, false) == compare_result::equal;
|
||||
}
|
||||
|
||||
if (type == value_t::array)
|
||||
{
|
||||
const auto& a = *lhs.m_data.m_value.array;
|
||||
const auto& b = *rhs.m_data.m_value.array;
|
||||
if (a.size() != b.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto it_b = b.cbegin();
|
||||
for (auto it_a = a.cbegin(); it_a != a.cend(); ++it_a, ++it_b)
|
||||
{
|
||||
const int scalar = equal_scalar(*it_a, *it_b);
|
||||
if (scalar < 0 ? !equal_structured(*it_a, *it_b, depth + 1) : scalar == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto& a = *lhs.m_data.m_value.object;
|
||||
const auto& b = *rhs.m_data.m_value.object;
|
||||
if (a.size() != b.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto it_b = b.cbegin();
|
||||
for (auto it_a = a.cbegin(); it_a != a.cend(); ++it_a, ++it_b)
|
||||
{
|
||||
if (!(it_a->first == it_b->first))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const int scalar = equal_scalar(it_a->second, it_b->second);
|
||||
if (scalar < 0 ? !equal_structured(it_a->second, it_b->second, depth + 1) : scalar == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool equal_dispatch(const_reference lhs, const_reference rhs, std::true_type /*standard containers*/) noexcept
|
||||
{
|
||||
return equal_bounded(lhs, rhs, 0);
|
||||
}
|
||||
|
||||
static bool equal_dispatch(const_reference lhs, const_reference rhs, std::false_type /*other containers*/) noexcept
|
||||
{
|
||||
return equal_general(lhs, rhs);
|
||||
}
|
||||
|
||||
public:
|
||||
#if JSON_HAS_THREE_WAY_COMPARISON
|
||||
/// @brief comparison: equal
|
||||
@@ -5293,11 +5564,11 @@ public:
|
||||
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
|
||||
#endif
|
||||
const_reference lhs = *this;
|
||||
JSON_IMPLEMENT_OPERATOR( ==, true, false, false,
|
||||
compare_iteratively<false>(lhs, rhs, false) == compare_result::equal, true)
|
||||
JSON_IMPLEMENT_SCALAR_FAST( ==, true)
|
||||
#ifdef __GNUC__
|
||||
JSON_HEDLEY_DIAGNOSTIC_POP
|
||||
#endif
|
||||
return equal_dispatch(lhs, rhs, equal_bounded_containers {});
|
||||
}
|
||||
|
||||
/// @brief comparison: equal
|
||||
@@ -5314,13 +5585,8 @@ public:
|
||||
std::partial_ordering operator<=>(const_reference rhs) const noexcept // *NOPAD*
|
||||
{
|
||||
const_reference lhs = *this;
|
||||
// default_result is used if we cannot compare values. In that case,
|
||||
// we compare types.
|
||||
JSON_IMPLEMENT_OPERATOR(<=>, // *NOPAD*
|
||||
std::partial_ordering::equivalent,
|
||||
std::partial_ordering::unordered,
|
||||
lhs_type <=> rhs_type, // *NOPAD*
|
||||
to_partial_ordering(compare_iteratively<true>(lhs, rhs, false)), true)
|
||||
JSON_IMPLEMENT_SCALAR_FAST(<=>, std::partial_ordering::equivalent) // *NOPAD*
|
||||
return three_way_general(lhs, rhs);
|
||||
}
|
||||
|
||||
/// @brief comparison: 3-way
|
||||
@@ -5408,11 +5674,11 @@ public:
|
||||
JSON_HEDLEY_DIAGNOSTIC_PUSH
|
||||
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
|
||||
#endif
|
||||
JSON_IMPLEMENT_OPERATOR( ==, true, false, false,
|
||||
compare_iteratively<false>(lhs, rhs, false) == compare_result::equal, true)
|
||||
JSON_IMPLEMENT_SCALAR_FAST( ==, true)
|
||||
#ifdef __GNUC__
|
||||
JSON_HEDLEY_DIAGNOSTIC_POP
|
||||
#endif
|
||||
return equal_dispatch(lhs, rhs, equal_bounded_containers {});
|
||||
}
|
||||
|
||||
// The friend comparisons with a scalar name the JSON type via decltype of
|
||||
@@ -5467,11 +5733,8 @@ public:
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
|
||||
friend bool operator<(const_reference lhs, const_reference rhs) noexcept
|
||||
{
|
||||
// default_result is used if we cannot compare values. In that case,
|
||||
// we compare types. Note we have to call the operator explicitly,
|
||||
// because MSVC has problems otherwise.
|
||||
JSON_IMPLEMENT_OPERATOR( <, false, false, operator<(lhs_type, rhs_type),
|
||||
compare_iteratively<true>(lhs, rhs, true) == compare_result::less, false)
|
||||
JSON_IMPLEMENT_SCALAR_FAST( <, false)
|
||||
return less_general(lhs, rhs);
|
||||
}
|
||||
|
||||
/// @brief comparison: less than
|
||||
@@ -5582,6 +5845,8 @@ public:
|
||||
#endif
|
||||
|
||||
#undef JSON_IMPLEMENT_OPERATOR
|
||||
#undef JSON_IMPLEMENT_SCALAR_FAST
|
||||
#undef JSON_IMPLEMENT_SCALAR_CASES
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
@@ -92,13 +92,19 @@ private:
|
||||
|
||||
/// @brief shared implementation of the const and non-const at() overloads
|
||||
/// @throw std::out_of_range if @a key is not found
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||
static void throw_key_not_found()
|
||||
{
|
||||
JSON_THROW(std::out_of_range("key not found"));
|
||||
}
|
||||
|
||||
template<typename Self, typename KeyType>
|
||||
static auto at_impl(Self& self, const KeyType& key) -> decltype((self.begin()->second))
|
||||
{
|
||||
const auto it = find_impl(self, key);
|
||||
if (it == self.end())
|
||||
{
|
||||
JSON_THROW(std::out_of_range("key not found"));
|
||||
throw_key_not_found();
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
Generated
+413
-130
@@ -5637,7 +5637,7 @@ void templated_json_throw(ExceptionType exception)
|
||||
@throw type_error.302 always
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
JSON_HEDLEY_NO_RETURN inline void throw_type_must_be(const char* expected, const BasicJsonType& j)
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_type_must_be(const char* expected, const BasicJsonType& j)
|
||||
{
|
||||
static_cast<void>(expected); // unused when JSON_NOEXCEPTION is defined
|
||||
static_cast<void>(j);
|
||||
@@ -5652,7 +5652,7 @@ JSON_HEDLEY_NO_RETURN inline void throw_type_must_be(const char* expected, const
|
||||
@throw type_error always
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
JSON_HEDLEY_NO_RETURN inline void throw_cannot_use_with(const exception_id id_, const char* operation, const BasicJsonType& j)
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_cannot_use_with(const exception_id id_, const char* operation, const BasicJsonType& j)
|
||||
{
|
||||
static_cast<void>(id_); // unused when JSON_NOEXCEPTION is defined
|
||||
static_cast<void>(operation);
|
||||
@@ -5660,6 +5660,18 @@ JSON_HEDLEY_NO_RETURN inline void throw_cannot_use_with(const exception_id id_,
|
||||
JSON_THROW(type_error::create(id_, concat("cannot use ", operation, " with ", j.type_name()), &j));
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief throws ExceptionType with a fixed message; kept out of line so that callers do not carry the exception construction
|
||||
*/
|
||||
template<typename ExceptionType, typename BasicJsonContext>
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_literal(const exception_id id_, const char* what_arg, BasicJsonContext context)
|
||||
{
|
||||
static_cast<void>(id_); // unused when JSON_NOEXCEPTION is defined
|
||||
static_cast<void>(what_arg);
|
||||
static_cast<void>(context);
|
||||
JSON_THROW(ExceptionType::create(id_, what_arg, context));
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
@@ -20054,7 +20066,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
}
|
||||
|
||||
/// @throw invalid_iterator.214 always
|
||||
JSON_HEDLEY_NO_RETURN void throw_cannot_get_value() const
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_cannot_get_value() const
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(exception_id::iterator_value_unavailable, "cannot get value", m_object));
|
||||
}
|
||||
@@ -20258,7 +20270,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
// if objects are not the same, the comparison is undefined
|
||||
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object));
|
||||
throw_literal<invalid_iterator>(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object);
|
||||
}
|
||||
|
||||
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
|
||||
@@ -20307,7 +20319,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
// if objects are not the same, the comparison is undefined
|
||||
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object));
|
||||
throw_literal<invalid_iterator>(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object);
|
||||
}
|
||||
|
||||
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
|
||||
@@ -20320,7 +20332,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
JSON_THROW(invalid_iterator::create(exception_id::iterator_order_on_object, "cannot compare order of object iterators", m_object));
|
||||
throw_literal<invalid_iterator>(exception_id::iterator_order_on_object, "cannot compare order of object iterators", m_object);
|
||||
|
||||
case value_t::array:
|
||||
return (m_it.array_iterator < other.m_it.array_iterator);
|
||||
@@ -20376,7 +20388,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
JSON_THROW(invalid_iterator::create(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object));
|
||||
throw_literal<invalid_iterator>(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object);
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
@@ -20455,7 +20467,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
JSON_THROW(invalid_iterator::create(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object));
|
||||
throw_literal<invalid_iterator>(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object);
|
||||
|
||||
case value_t::array:
|
||||
return m_it.array_iterator - other.m_it.array_iterator;
|
||||
@@ -20484,7 +20496,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
JSON_THROW(invalid_iterator::create(exception_id::iterator_subscript_on_object, "cannot use operator[] for object iterators", m_object));
|
||||
throw_literal<invalid_iterator>(exception_id::iterator_subscript_on_object, "cannot use operator[] for object iterators", m_object);
|
||||
|
||||
case value_t::array:
|
||||
return *std::next(m_it.array_iterator, n);
|
||||
@@ -20524,7 +20536,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
return m_it.object_iterator->first;
|
||||
}
|
||||
|
||||
JSON_THROW(invalid_iterator::create(exception_id::iterator_key_not_object, "cannot use key() for non-object iterators", m_object));
|
||||
throw_literal<invalid_iterator>(exception_id::iterator_key_not_object, "cannot use key() for non-object iterators", m_object);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -29488,13 +29500,19 @@ private:
|
||||
|
||||
/// @brief shared implementation of the const and non-const at() overloads
|
||||
/// @throw std::out_of_range if @a key is not found
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||
static void throw_key_not_found()
|
||||
{
|
||||
JSON_THROW(std::out_of_range("key not found"));
|
||||
}
|
||||
|
||||
template<typename Self, typename KeyType>
|
||||
static auto at_impl(Self& self, const KeyType& key) -> decltype((self.begin()->second))
|
||||
{
|
||||
const auto it = find_impl(self, key);
|
||||
if (it == self.end())
|
||||
{
|
||||
JSON_THROW(std::out_of_range("key not found"));
|
||||
throw_key_not_found();
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
@@ -30325,26 +30343,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
switch (t)
|
||||
{
|
||||
case value_t::object:
|
||||
{
|
||||
object = create<object_t>();
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
array = create<array_t>();
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::string:
|
||||
{
|
||||
string = create<string_t>("");
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::binary:
|
||||
{
|
||||
binary = create<binary_t>();
|
||||
// allocating: kept out of line, so the scalar cases below inline
|
||||
init_owned(t);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -30384,13 +30388,41 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
object = nullptr; // silence warning, see #821
|
||||
if (JSON_HEDLEY_UNLIKELY(t == value_t::null))
|
||||
{
|
||||
JSON_THROW(other_error::create(detail::exception_id::internal_error, "961c151d2e87f2686a955a9be24d316f1362bf21 3.12.0", nullptr)); // LCOV_EXCL_LINE
|
||||
detail::throw_literal<other_error>(detail::exception_id::internal_error, "961c151d2e87f2686a955a9be24d316f1362bf21 3.12.0", nullptr); // LCOV_EXCL_LINE
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NEVER_INLINE
|
||||
void init_owned(value_t t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case value_t::object:
|
||||
object = create<object_t>();
|
||||
break;
|
||||
case value_t::array:
|
||||
array = create<array_t>();
|
||||
break;
|
||||
case value_t::string:
|
||||
string = create<string_t>("");
|
||||
break;
|
||||
case value_t::binary:
|
||||
binary = create<binary_t>();
|
||||
break;
|
||||
case value_t::null:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// constructor for strings
|
||||
json_value(const string_t& value) : string(create<string_t>(value)) {}
|
||||
|
||||
@@ -30651,7 +30683,18 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// only strings, binaries, arrays and objects own memory; keep the
|
||||
// check for them inline so destroying a scalar is a compare and a
|
||||
// branch, and leave the actual work to an out-of-line function
|
||||
void destroy(value_t t)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(t == value_t::string || t == value_t::binary || t == value_t::object || t == value_t::array))
|
||||
{
|
||||
destroy_owned(t);
|
||||
}
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NEVER_INLINE void destroy_owned(value_t t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
@@ -30819,51 +30862,64 @@ public:
|
||||
return next;
|
||||
}
|
||||
|
||||
/// @brief the capacity of the stored array, or unknown_size()
|
||||
/// @brief the capacity of the stored array or object, or unknown_size()
|
||||
/// Only JSON_DIAGNOSTICS uses the value, to detect a reallocation that
|
||||
/// would invalidate the parent pointers. Array types that do not have a
|
||||
/// capacity() member function report unknown_size(), which is treated as
|
||||
/// "the elements may have moved".
|
||||
/// would invalidate the parent pointers. Containers that do not have a
|
||||
/// capacity() member function report unknown_size() if their elements may
|
||||
/// move (arrays, ordered_map) and 0 if they never do (std::map).
|
||||
#if JSON_DIAGNOSTICS
|
||||
template < typename A = array_t, detail::enable_if_t < detail::has_capacity<A>::value, int > = 0 >
|
||||
std::size_t array_capacity() const noexcept
|
||||
template < typename C, detail::enable_if_t < detail::has_capacity<C>::value, int > = 0 >
|
||||
static std::size_t capacity_of(const C& container) noexcept
|
||||
{
|
||||
return m_data.m_value.array->capacity();
|
||||
return container.capacity();
|
||||
}
|
||||
|
||||
template < typename A = array_t, detail::enable_if_t < !detail::has_capacity<A>::value, int > = 0 >
|
||||
std::size_t array_capacity() const noexcept
|
||||
template < typename C, detail::enable_if_t < !detail::has_capacity<C>::value, int > = 0 >
|
||||
static std::size_t capacity_of(const C& /*container*/) noexcept
|
||||
{
|
||||
return detail::unknown_size();
|
||||
return std::is_same<C, array_t>::value || detail::is_ordered_map<C>::value ? detail::unknown_size() : 0;
|
||||
}
|
||||
|
||||
std::size_t container_capacity() const noexcept
|
||||
{
|
||||
return is_array() ? capacity_of(*m_data.m_value.array) : capacity_of(*m_data.m_value.object);
|
||||
}
|
||||
#else
|
||||
static constexpr std::size_t array_capacity() noexcept
|
||||
static constexpr std::size_t container_capacity() noexcept
|
||||
{
|
||||
return detail::unknown_size();
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief set the parent of a value that has just been added to an array
|
||||
/// @param j the added value
|
||||
/// @param old_capacity the value @ref array_capacity() returned before the
|
||||
/// @brief set the parent of a value that has just been added to an array or object
|
||||
/// @param j the added (or looked-up) value
|
||||
/// @param old_capacity the value @ref container_capacity() returned before the
|
||||
/// insertion
|
||||
reference set_parent_after_array_insert(reference j, std::size_t old_capacity)
|
||||
reference set_parent_after_insert(reference j, std::size_t old_capacity)
|
||||
{
|
||||
#if JSON_DIAGNOSTICS
|
||||
// see https://github.com/nlohmann/json/issues/2838
|
||||
JSON_ASSERT(type() == value_t::array);
|
||||
if (JSON_HEDLEY_UNLIKELY(old_capacity == detail::unknown_size()
|
||||
|| array_capacity() != old_capacity))
|
||||
|| container_capacity() != old_capacity))
|
||||
{
|
||||
// the capacity has changed, or the array type does not let us tell:
|
||||
// the elements may have moved, so update all parents
|
||||
// the capacity has changed, or the container type does not let us
|
||||
// tell: the elements may have moved, so update all parents
|
||||
set_parents();
|
||||
return j;
|
||||
}
|
||||
#else
|
||||
static_cast<void>(old_capacity);
|
||||
#endif
|
||||
return set_parent(j);
|
||||
return set_parent_at(j);
|
||||
}
|
||||
|
||||
/// @brief set the parent of an element accessed via at(); never moves elements
|
||||
reference set_parent_at(reference j) noexcept
|
||||
{
|
||||
#if JSON_DIAGNOSTICS
|
||||
j.m_parent = this;
|
||||
#endif
|
||||
return j;
|
||||
}
|
||||
|
||||
reference set_parent(reference j)
|
||||
@@ -32050,7 +32106,7 @@ public:
|
||||
// if an object is wanted but impossible, throw an exception
|
||||
if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object))
|
||||
{
|
||||
JSON_THROW(type_error::create(detail::exception_id::object_from_non_pairs, "cannot create object from initializer list", nullptr));
|
||||
detail::throw_literal<type_error>(detail::exception_id::object_from_non_pairs, "cannot create object from initializer list", nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32170,7 +32226,7 @@ public:
|
||||
// make sure the iterator fits the current value
|
||||
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterators_incompatible, "iterators are not compatible", nullptr));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterators_incompatible, "iterators are not compatible", nullptr);
|
||||
}
|
||||
|
||||
// copy type from the first iterator
|
||||
@@ -32189,7 +32245,7 @@ public:
|
||||
if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
|
||||
|| !last.m_it.primitive_iterator.is_end()))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_out_of_range, "iterators out of range", first.m_object));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_range_out_of_range, "iterators out of range", first.m_object);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -32257,7 +32313,7 @@ public:
|
||||
case value_t::null:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_of_null, detail::concat("cannot construct with iterators from ", first.m_object->type_name()), first.m_object));
|
||||
throw_iterator_range_of_null(first.m_object);
|
||||
}
|
||||
|
||||
set_parents();
|
||||
@@ -32286,18 +32342,17 @@ public:
|
||||
// check of passed value is valid
|
||||
other.assert_invariant();
|
||||
|
||||
if (m_data.m_type == value_t::object || m_data.m_type == value_t::array)
|
||||
if (JSON_HEDLEY_LIKELY(m_data.m_type != value_t::object && m_data.m_type != value_t::array
|
||||
&& m_data.m_type != value_t::string && m_data.m_type != value_t::binary))
|
||||
{
|
||||
// copying the container directly would call this constructor again
|
||||
// for every element, once per nesting level
|
||||
copy_structured(other);
|
||||
// null, boolean, numbers, discarded: plain copy of the union
|
||||
m_data.m_value = other.m_data.m_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
copy_leaf_value(other, *this);
|
||||
copy_owning(other);
|
||||
}
|
||||
|
||||
set_parents();
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -32527,6 +32582,22 @@ public:
|
||||
// value access //
|
||||
//////////////////
|
||||
|
||||
/// out-of-line part of the copy constructor for owning types
|
||||
JSON_HEDLEY_NEVER_INLINE
|
||||
void copy_owning(const basic_json& other)
|
||||
{
|
||||
if (m_data.m_type == value_t::object || m_data.m_type == value_t::array)
|
||||
{
|
||||
// copying the container directly would call this constructor again
|
||||
// for every element, once per nesting level
|
||||
copy_structured(other);
|
||||
}
|
||||
else
|
||||
{
|
||||
copy_leaf_value(other, *this);
|
||||
}
|
||||
}
|
||||
|
||||
/// get a pointer to the value (object)
|
||||
object_t* get_impl_ptr(object_t* /*unused*/) noexcept
|
||||
{
|
||||
@@ -32623,6 +32694,40 @@ public:
|
||||
return is_binary() ? m_data.m_value.binary : nullptr;
|
||||
}
|
||||
|
||||
/// out-of-line throw helpers (message construction kept out of the hot paths)
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||
static void throw_iterator_range_of_null(const basic_json* obj)
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_of_null, detail::concat("cannot construct with iterators from ", obj->type_name()), obj));
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||
static void throw_incompatible_reference_type(const basic_json& obj)
|
||||
{
|
||||
JSON_THROW(type_error::create(detail::exception_id::incompatible_reference_type, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||
static void throw_array_index_exceeds_size_type(size_type idx)
|
||||
{
|
||||
JSON_THROW(std::length_error(detail::concat("array index ", std::to_string(idx), " exceeds size_type")));
|
||||
}
|
||||
|
||||
/// @throw out_of_range.401
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||
static void throw_array_index_out_of_range(size_type idx, const basic_json& j)
|
||||
{
|
||||
JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), &j));
|
||||
}
|
||||
|
||||
/// @throw out_of_range.403
|
||||
template<typename KeyType>
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||
static void throw_key_not_found(const KeyType& key, const basic_json& j)
|
||||
{
|
||||
JSON_THROW(out_of_range::create(detail::exception_id::key_not_found, detail::concat("key '", key, "' not found"), &j));
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief helper function to implement get_ref()
|
||||
|
||||
@@ -32645,7 +32750,7 @@ public:
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
JSON_THROW(type_error::create(detail::exception_id::incompatible_reference_type, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
|
||||
throw_incompatible_reference_type(obj);
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -33093,7 +33198,7 @@ public:
|
||||
// std::map or ordered_map) never moves from its argument, so key is still
|
||||
// valid here regardless of whether KeyType was deduced as an rvalue reference
|
||||
// NOLINTNEXTLINE(bugprone-use-after-move,hicpp-invalid-access-moved)
|
||||
JSON_THROW(out_of_range::create(detail::exception_id::key_not_found, detail::concat("key '", key_for_message(key), "' not found"), &j));
|
||||
throw_key_not_found(key_for_message(key), j);
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
@@ -33130,7 +33235,7 @@ public:
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(idx >= j.m_data.m_value.array->size()))
|
||||
{
|
||||
JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), &j));
|
||||
throw_array_index_out_of_range(idx, j);
|
||||
}
|
||||
|
||||
return (*j.m_data.m_value.array)[idx];
|
||||
@@ -33185,7 +33290,7 @@ public:
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/at/
|
||||
reference at(size_type idx)
|
||||
{
|
||||
return set_parent(array_at(*this, idx));
|
||||
return set_parent_at(array_at(*this, idx));
|
||||
}
|
||||
|
||||
/// @brief access specified array element with bounds checking
|
||||
@@ -33199,7 +33304,7 @@ public:
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/at/
|
||||
reference at(const typename object_t::key_type& key)
|
||||
{
|
||||
return set_parent(object_at(*this, key));
|
||||
return set_parent_at(object_at(*this, key));
|
||||
}
|
||||
|
||||
/// @brief access specified object element with bounds checking
|
||||
@@ -33208,7 +33313,7 @@ public:
|
||||
detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
|
||||
reference at(KeyType && key)
|
||||
{
|
||||
return set_parent(object_at(*this, std::forward<KeyType>(key)));
|
||||
return set_parent_at(object_at(*this, std::forward<KeyType>(key)));
|
||||
}
|
||||
|
||||
/// @brief access specified object element with bounds checking
|
||||
@@ -33248,18 +33353,18 @@ public:
|
||||
// resize() rejects other indices that are too large to represent
|
||||
if (JSON_HEDLEY_UNLIKELY(idx == (std::numeric_limits<size_type>::max)()))
|
||||
{
|
||||
JSON_THROW(std::length_error(detail::concat("array index ", std::to_string(idx), " exceeds size_type")));
|
||||
throw_array_index_exceeds_size_type(idx);
|
||||
}
|
||||
#if JSON_DIAGNOSTICS
|
||||
// remember array size & capacity before resizing
|
||||
const auto old_size = m_data.m_value.array->size();
|
||||
const auto old_capacity = array_capacity();
|
||||
const auto old_capacity = container_capacity();
|
||||
#endif
|
||||
m_data.m_value.array->resize(idx + 1);
|
||||
|
||||
#if JSON_DIAGNOSTICS
|
||||
if (JSON_HEDLEY_UNLIKELY(old_capacity == detail::unknown_size()
|
||||
|| array_capacity() != old_capacity))
|
||||
|| container_capacity() != old_capacity))
|
||||
{
|
||||
// capacity has changed: update all parents
|
||||
set_parents();
|
||||
@@ -33306,8 +33411,9 @@ public:
|
||||
// operator[] only works for objects
|
||||
if (JSON_HEDLEY_LIKELY(is_object()))
|
||||
{
|
||||
const auto old_capacity = container_capacity();
|
||||
auto result = m_data.m_value.object->emplace(std::move(key), nullptr);
|
||||
return set_parent(result.first->second);
|
||||
return set_parent_after_insert(result.first->second, old_capacity);
|
||||
}
|
||||
|
||||
throw_subscript_wrong_type("string");
|
||||
@@ -33357,8 +33463,9 @@ public:
|
||||
// operator[] only works for objects
|
||||
if (JSON_HEDLEY_LIKELY(is_object()))
|
||||
{
|
||||
const auto old_capacity = container_capacity();
|
||||
auto result = m_data.m_value.object->emplace(lookup_key(std::forward<KeyType>(key)), nullptr);
|
||||
return set_parent(result.first->second);
|
||||
return set_parent_after_insert(result.first->second, old_capacity);
|
||||
}
|
||||
|
||||
throw_subscript_wrong_type("string");
|
||||
@@ -33584,7 +33691,7 @@ public:
|
||||
// make sure the iterator fits the current value
|
||||
if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this);
|
||||
}
|
||||
|
||||
IteratorType result = end();
|
||||
@@ -33600,7 +33707,7 @@ public:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin()))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_out_of_range, "iterator out of range", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_out_of_range, "iterator out of range", this);
|
||||
}
|
||||
|
||||
m_data.m_value.destroy(m_data.m_type);
|
||||
@@ -33642,7 +33749,7 @@ public:
|
||||
// make sure the iterator fits the current value
|
||||
if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_from_other_value, "iterators do not fit current value", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_range_from_other_value, "iterators do not fit current value", this);
|
||||
}
|
||||
|
||||
IteratorType result = end();
|
||||
@@ -33659,7 +33766,7 @@ public:
|
||||
if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
|
||||
|| !last.m_it.primitive_iterator.is_end()))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_out_of_range, "iterators out of range", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_range_out_of_range, "iterators out of range", this);
|
||||
}
|
||||
|
||||
m_data.m_value.destroy(m_data.m_type);
|
||||
@@ -33758,7 +33865,7 @@ public:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(idx >= size()))
|
||||
{
|
||||
JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
|
||||
throw_array_index_out_of_range(idx, *this);
|
||||
}
|
||||
|
||||
m_data.m_value.array->erase(m_data.m_value.array->begin() + static_cast<difference_type>(idx));
|
||||
@@ -34264,9 +34371,9 @@ public:
|
||||
}
|
||||
|
||||
// add the element to the array (move semantics)
|
||||
const auto old_capacity = array_capacity();
|
||||
const auto old_capacity = container_capacity();
|
||||
m_data.m_value.array->push_back(std::move(val));
|
||||
set_parent_after_array_insert(m_data.m_value.array->back(), old_capacity);
|
||||
set_parent_after_insert(m_data.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
|
||||
}
|
||||
|
||||
@@ -34295,9 +34402,9 @@ public:
|
||||
}
|
||||
|
||||
// add the element to the array
|
||||
const auto old_capacity = array_capacity();
|
||||
const auto old_capacity = container_capacity();
|
||||
m_data.m_value.array->push_back(val);
|
||||
set_parent_after_array_insert(m_data.m_value.array->back(), old_capacity);
|
||||
set_parent_after_insert(m_data.m_value.array->back(), old_capacity);
|
||||
}
|
||||
|
||||
/// @brief add an object to an array
|
||||
@@ -34325,8 +34432,9 @@ public:
|
||||
}
|
||||
|
||||
// add the element to the object
|
||||
const auto old_capacity = container_capacity();
|
||||
auto res = m_data.m_value.object->insert(val);
|
||||
set_parent(res.first->second);
|
||||
set_parent_after_insert(res.first->second, old_capacity);
|
||||
}
|
||||
|
||||
/// @brief add an object to an object
|
||||
@@ -34379,9 +34487,9 @@ public:
|
||||
}
|
||||
|
||||
// add the element to the array (perfect forwarding)
|
||||
const auto old_capacity = array_capacity();
|
||||
const auto old_capacity = container_capacity();
|
||||
m_data.m_value.array->emplace_back(std::forward<Args>(args)...);
|
||||
return set_parent_after_array_insert(m_data.m_value.array->back(), old_capacity);
|
||||
return set_parent_after_insert(m_data.m_value.array->back(), old_capacity);
|
||||
}
|
||||
|
||||
/// @brief add an object to an object if key does not exist
|
||||
@@ -34402,8 +34510,9 @@ public:
|
||||
}
|
||||
|
||||
// add the element to the array (perfect forwarding)
|
||||
const auto old_capacity = container_capacity();
|
||||
auto res = m_data.m_value.object->emplace(std::forward<Args>(args)...);
|
||||
set_parent(res.first->second);
|
||||
set_parent_after_insert(res.first->second, old_capacity);
|
||||
|
||||
// create a result iterator and set iterator to the result of emplace
|
||||
auto it = begin();
|
||||
@@ -34444,7 +34553,7 @@ public:
|
||||
// check if iterator pos fits to this JSON value
|
||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this);
|
||||
}
|
||||
|
||||
// insert to array and return iterator
|
||||
@@ -34464,7 +34573,7 @@ public:
|
||||
// check if iterator pos fits to this JSON value
|
||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this);
|
||||
}
|
||||
|
||||
// moving into a local first keeps this safe even if val aliases
|
||||
@@ -34486,7 +34595,7 @@ public:
|
||||
// check if iterator pos fits to this JSON value
|
||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this);
|
||||
}
|
||||
|
||||
// insert to array and return iterator
|
||||
@@ -34509,24 +34618,24 @@ public:
|
||||
// check if iterator pos fits to this JSON value
|
||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this);
|
||||
}
|
||||
|
||||
// check if range iterators belong to the same JSON object
|
||||
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::insert_range_incompatible, "iterators do not fit", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::insert_range_incompatible, "iterators do not fit", this);
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(first.m_object == this))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::insert_range_into_itself, "passed iterators may not belong to container", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::insert_range_into_itself, "passed iterators may not belong to container", this);
|
||||
}
|
||||
|
||||
// passed iterators must belong to arrays
|
||||
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_array()))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterators first and last must point to arrays", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterators first and last must point to arrays", this);
|
||||
}
|
||||
|
||||
// insert to array and return iterator
|
||||
@@ -34546,7 +34655,7 @@ public:
|
||||
// check if iterator pos fits to this JSON value
|
||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this);
|
||||
}
|
||||
|
||||
// copy the values first: ilist may refer to elements of this array
|
||||
@@ -34574,13 +34683,13 @@ public:
|
||||
// check if range iterators belong to the same JSON object
|
||||
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::insert_range_incompatible, "iterators do not fit", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::insert_range_incompatible, "iterators do not fit", this);
|
||||
}
|
||||
|
||||
// passed iterators must belong to objects
|
||||
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_from_other_value, "iterators first and last must point to objects", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterators first and last must point to objects", this);
|
||||
}
|
||||
|
||||
m_data.m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);
|
||||
@@ -34615,7 +34724,7 @@ public:
|
||||
// check if range iterators belong to the same JSON object
|
||||
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(detail::exception_id::insert_range_incompatible, "iterators do not fit", this));
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::insert_range_incompatible, "iterators do not fit", this);
|
||||
}
|
||||
|
||||
// passed iterators must belong to objects
|
||||
@@ -34702,9 +34811,9 @@ public:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// set_parent() also repairs the other members, which ordered_json
|
||||
// relocates when adding a key makes its vector grow
|
||||
set_parent(m_data.m_value.object->operator[](it.key()) = std::move(it.value()));
|
||||
// operator[] repairs the other members if ordered_json relocates
|
||||
// them; assigning through it keeps this O(1) per key
|
||||
operator[](it.key()) = std::move(it.value());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34754,9 +34863,8 @@ public:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// set_parent() also repairs the other members, which ordered_json
|
||||
// relocates when adding a key makes its vector grow
|
||||
target->set_parent(target->m_data.m_value.object->operator[](first.key()) = std::move(first.value()));
|
||||
// operator[] repairs the other members if ordered_json relocates them
|
||||
target->operator[](first.key()) = std::move(first.value());
|
||||
++first;
|
||||
}
|
||||
}
|
||||
@@ -34898,6 +35006,20 @@ public:
|
||||
/// @name lexicographical comparison operators
|
||||
/// @{
|
||||
|
||||
// the same-type cases for non-owning scalars, shared by JSON_IMPLEMENT_OPERATOR
|
||||
// and JSON_IMPLEMENT_SCALAR_FAST
|
||||
#define JSON_IMPLEMENT_SCALAR_CASES(op, null_result) \
|
||||
case value_t::null: \
|
||||
return (null_result); \
|
||||
case value_t::boolean: \
|
||||
return (lhs.m_data.m_value.boolean) op (rhs.m_data.m_value.boolean); \
|
||||
case value_t::number_integer: \
|
||||
return (lhs.m_data.m_value.number_integer) op (rhs.m_data.m_value.number_integer); \
|
||||
case value_t::number_unsigned: \
|
||||
return (lhs.m_data.m_value.number_unsigned) op (rhs.m_data.m_value.number_unsigned); \
|
||||
case value_t::number_float: \
|
||||
return (lhs.m_data.m_value.number_float) op (rhs.m_data.m_value.number_float);
|
||||
|
||||
// note parentheses around operands are necessary; see
|
||||
// https://github.com/nlohmann/json/issues/1530
|
||||
// Mixed signed/unsigned integer comparisons check whether the signed value
|
||||
@@ -34934,24 +35056,11 @@ public:
|
||||
return (*lhs.m_data.m_value.object) op (*rhs.m_data.m_value.object); \
|
||||
} \
|
||||
\
|
||||
case value_t::null: \
|
||||
return (null_result); \
|
||||
\
|
||||
JSON_IMPLEMENT_SCALAR_CASES(op, null_result) \
|
||||
\
|
||||
case value_t::string: \
|
||||
return (*lhs.m_data.m_value.string) op (*rhs.m_data.m_value.string); \
|
||||
\
|
||||
case value_t::boolean: \
|
||||
return (lhs.m_data.m_value.boolean) op (rhs.m_data.m_value.boolean); \
|
||||
\
|
||||
case value_t::number_integer: \
|
||||
return (lhs.m_data.m_value.number_integer) op (rhs.m_data.m_value.number_integer); \
|
||||
\
|
||||
case value_t::number_unsigned: \
|
||||
return (lhs.m_data.m_value.number_unsigned) op (rhs.m_data.m_value.number_unsigned); \
|
||||
\
|
||||
case value_t::number_float: \
|
||||
return (lhs.m_data.m_value.number_float) op (rhs.m_data.m_value.number_float); \
|
||||
\
|
||||
case value_t::binary: \
|
||||
return (*lhs.m_data.m_value.binary) op (*rhs.m_data.m_value.binary); \
|
||||
\
|
||||
@@ -34995,6 +35104,24 @@ public:
|
||||
\
|
||||
return (default_result);
|
||||
|
||||
// inline fast path for two values of the same non-owning scalar type; anything
|
||||
// else falls through to the out-of-line general comparison
|
||||
#define JSON_IMPLEMENT_SCALAR_FAST(op, null_result) \
|
||||
if (lhs.m_data.m_type == rhs.m_data.m_type) \
|
||||
{ \
|
||||
switch (lhs.m_data.m_type) \
|
||||
{ \
|
||||
JSON_IMPLEMENT_SCALAR_CASES(op, null_result) \
|
||||
case value_t::object: \
|
||||
case value_t::array: \
|
||||
case value_t::string: \
|
||||
case value_t::binary: \
|
||||
case value_t::discarded: \
|
||||
default: \
|
||||
break; \
|
||||
} \
|
||||
}
|
||||
|
||||
JSON_PRIVATE_UNLESS_TESTED:
|
||||
// returns true if:
|
||||
// - any operand is NaN and the other operand is of number type
|
||||
@@ -35022,6 +35149,168 @@ public:
|
||||
return compares_unordered(*this, rhs, inverse);
|
||||
}
|
||||
|
||||
/// @brief whether objects and arrays are the standard containers, whose
|
||||
/// elements equal_bounded() may therefore compare itself
|
||||
using equal_bounded_containers = std::integral_constant < bool,
|
||||
std::is_same<object_t, std::map<StringType, basic_json, default_object_comparator_t,
|
||||
AllocatorType<std::pair<const StringType, basic_json>>>>::value &&
|
||||
std::is_same<array_t, std::vector<basic_json, AllocatorType<basic_json>>>::value >;
|
||||
|
||||
// out-of-line general comparisons; the operators below handle same-type
|
||||
// scalars inline and fall through to these
|
||||
JSON_HEDLEY_NEVER_INLINE
|
||||
static bool equal_general(const_reference lhs, const_reference rhs) noexcept
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
JSON_HEDLEY_DIAGNOSTIC_PUSH
|
||||
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
|
||||
#endif
|
||||
JSON_IMPLEMENT_OPERATOR( ==, true, false, false,
|
||||
compare_iteratively<false>(lhs, rhs, false) == compare_result::equal, true)
|
||||
#ifdef __GNUC__
|
||||
JSON_HEDLEY_DIAGNOSTIC_POP
|
||||
#endif
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NEVER_INLINE
|
||||
static bool less_general(const_reference lhs, const_reference rhs) noexcept
|
||||
{
|
||||
// default_result is used if we cannot compare values. In that case,
|
||||
// we compare types. Note we have to call the operator explicitly,
|
||||
// because MSVC has problems otherwise.
|
||||
JSON_IMPLEMENT_OPERATOR( <, false, false, operator<(lhs_type, rhs_type),
|
||||
compare_iteratively<true>(lhs, rhs, true) == compare_result::less, false)
|
||||
}
|
||||
|
||||
#if JSON_HAS_THREE_WAY_COMPARISON
|
||||
JSON_HEDLEY_NEVER_INLINE
|
||||
static std::partial_ordering three_way_general(const_reference lhs, const_reference rhs) noexcept // *NOPAD*
|
||||
{
|
||||
// default_result is used if we cannot compare values. In that case,
|
||||
// we compare types.
|
||||
JSON_IMPLEMENT_OPERATOR(<=>, // *NOPAD*
|
||||
std::partial_ordering::equivalent,
|
||||
std::partial_ordering::unordered,
|
||||
lhs_type <=> rhs_type, // *NOPAD*
|
||||
to_partial_ordering(compare_iteratively<true>(lhs, rhs, false)), true)
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief compare two values of the same type that holds no children
|
||||
/// @return 1 if they are equal, 0 if they are not, and -1 if the values are
|
||||
/// of different types or have children, which this leaves to
|
||||
/// equal_structured()
|
||||
JSON_HEDLEY_ALWAYS_INLINE
|
||||
static int equal_scalar(const_reference lhs, const_reference rhs) noexcept
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
JSON_HEDLEY_DIAGNOSTIC_PUSH
|
||||
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
|
||||
#endif
|
||||
if (lhs.m_data.m_type == rhs.m_data.m_type)
|
||||
{
|
||||
switch (lhs.m_data.m_type)
|
||||
{
|
||||
case value_t::null:
|
||||
return 1;
|
||||
case value_t::boolean:
|
||||
return lhs.m_data.m_value.boolean == rhs.m_data.m_value.boolean;
|
||||
case value_t::number_integer:
|
||||
return lhs.m_data.m_value.number_integer == rhs.m_data.m_value.number_integer;
|
||||
case value_t::number_unsigned:
|
||||
return lhs.m_data.m_value.number_unsigned == rhs.m_data.m_value.number_unsigned;
|
||||
case value_t::number_float:
|
||||
return lhs.m_data.m_value.number_float == rhs.m_data.m_value.number_float;
|
||||
case value_t::string:
|
||||
return *lhs.m_data.m_value.string == *rhs.m_data.m_value.string;
|
||||
case value_t::object:
|
||||
case value_t::array:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef __GNUC__
|
||||
JSON_HEDLEY_DIAGNOSTIC_POP
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// @brief compare for equality, walking objects and arrays here instead of
|
||||
/// in their containers so that the depth is a parameter and needs no
|
||||
/// thread-local count; only used for the standard containers
|
||||
static bool equal_bounded(const_reference lhs, const_reference rhs, std::size_t depth) noexcept
|
||||
{
|
||||
const int scalar = equal_scalar(lhs, rhs);
|
||||
return scalar < 0 ? equal_structured(lhs, rhs, depth) : scalar != 0;
|
||||
}
|
||||
|
||||
static bool equal_structured(const_reference lhs, const_reference rhs, std::size_t depth) noexcept
|
||||
{
|
||||
const auto type = lhs.m_data.m_type;
|
||||
if (type != rhs.m_data.m_type || (type != value_t::array && type != value_t::object))
|
||||
{
|
||||
return equal_general(lhs, rhs);
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(depth >= detail::recursion_depth_limit()))
|
||||
{
|
||||
return compare_iteratively<false>(lhs, rhs, false) == compare_result::equal;
|
||||
}
|
||||
|
||||
if (type == value_t::array)
|
||||
{
|
||||
const auto& a = *lhs.m_data.m_value.array;
|
||||
const auto& b = *rhs.m_data.m_value.array;
|
||||
if (a.size() != b.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto it_b = b.cbegin();
|
||||
for (auto it_a = a.cbegin(); it_a != a.cend(); ++it_a, ++it_b)
|
||||
{
|
||||
const int scalar = equal_scalar(*it_a, *it_b);
|
||||
if (scalar < 0 ? !equal_structured(*it_a, *it_b, depth + 1) : scalar == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto& a = *lhs.m_data.m_value.object;
|
||||
const auto& b = *rhs.m_data.m_value.object;
|
||||
if (a.size() != b.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto it_b = b.cbegin();
|
||||
for (auto it_a = a.cbegin(); it_a != a.cend(); ++it_a, ++it_b)
|
||||
{
|
||||
if (!(it_a->first == it_b->first))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const int scalar = equal_scalar(it_a->second, it_b->second);
|
||||
if (scalar < 0 ? !equal_structured(it_a->second, it_b->second, depth + 1) : scalar == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool equal_dispatch(const_reference lhs, const_reference rhs, std::true_type /*standard containers*/) noexcept
|
||||
{
|
||||
return equal_bounded(lhs, rhs, 0);
|
||||
}
|
||||
|
||||
static bool equal_dispatch(const_reference lhs, const_reference rhs, std::false_type /*other containers*/) noexcept
|
||||
{
|
||||
return equal_general(lhs, rhs);
|
||||
}
|
||||
|
||||
public:
|
||||
#if JSON_HAS_THREE_WAY_COMPARISON
|
||||
/// @brief comparison: equal
|
||||
@@ -35033,11 +35322,11 @@ public:
|
||||
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
|
||||
#endif
|
||||
const_reference lhs = *this;
|
||||
JSON_IMPLEMENT_OPERATOR( ==, true, false, false,
|
||||
compare_iteratively<false>(lhs, rhs, false) == compare_result::equal, true)
|
||||
JSON_IMPLEMENT_SCALAR_FAST( ==, true)
|
||||
#ifdef __GNUC__
|
||||
JSON_HEDLEY_DIAGNOSTIC_POP
|
||||
#endif
|
||||
return equal_dispatch(lhs, rhs, equal_bounded_containers {});
|
||||
}
|
||||
|
||||
/// @brief comparison: equal
|
||||
@@ -35054,13 +35343,8 @@ public:
|
||||
std::partial_ordering operator<=>(const_reference rhs) const noexcept // *NOPAD*
|
||||
{
|
||||
const_reference lhs = *this;
|
||||
// default_result is used if we cannot compare values. In that case,
|
||||
// we compare types.
|
||||
JSON_IMPLEMENT_OPERATOR(<=>, // *NOPAD*
|
||||
std::partial_ordering::equivalent,
|
||||
std::partial_ordering::unordered,
|
||||
lhs_type <=> rhs_type, // *NOPAD*
|
||||
to_partial_ordering(compare_iteratively<true>(lhs, rhs, false)), true)
|
||||
JSON_IMPLEMENT_SCALAR_FAST(<=>, std::partial_ordering::equivalent) // *NOPAD*
|
||||
return three_way_general(lhs, rhs);
|
||||
}
|
||||
|
||||
/// @brief comparison: 3-way
|
||||
@@ -35148,11 +35432,11 @@ public:
|
||||
JSON_HEDLEY_DIAGNOSTIC_PUSH
|
||||
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
|
||||
#endif
|
||||
JSON_IMPLEMENT_OPERATOR( ==, true, false, false,
|
||||
compare_iteratively<false>(lhs, rhs, false) == compare_result::equal, true)
|
||||
JSON_IMPLEMENT_SCALAR_FAST( ==, true)
|
||||
#ifdef __GNUC__
|
||||
JSON_HEDLEY_DIAGNOSTIC_POP
|
||||
#endif
|
||||
return equal_dispatch(lhs, rhs, equal_bounded_containers {});
|
||||
}
|
||||
|
||||
// The friend comparisons with a scalar name the JSON type via decltype of
|
||||
@@ -35207,11 +35491,8 @@ public:
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
|
||||
friend bool operator<(const_reference lhs, const_reference rhs) noexcept
|
||||
{
|
||||
// default_result is used if we cannot compare values. In that case,
|
||||
// we compare types. Note we have to call the operator explicitly,
|
||||
// because MSVC has problems otherwise.
|
||||
JSON_IMPLEMENT_OPERATOR( <, false, false, operator<(lhs_type, rhs_type),
|
||||
compare_iteratively<true>(lhs, rhs, true) == compare_result::less, false)
|
||||
JSON_IMPLEMENT_SCALAR_FAST( <, false)
|
||||
return less_general(lhs, rhs);
|
||||
}
|
||||
|
||||
/// @brief comparison: less than
|
||||
@@ -35322,6 +35603,8 @@ public:
|
||||
#endif
|
||||
|
||||
#undef JSON_IMPLEMENT_OPERATOR
|
||||
#undef JSON_IMPLEMENT_SCALAR_FAST
|
||||
#undef JSON_IMPLEMENT_SCALAR_CASES
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -1149,3 +1150,157 @@ TEST_CASE("operator<=> of binary values with a different subtype does not depend
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("comparison of scalars (inline fast path)")
|
||||
{
|
||||
const json::number_integer_t neg = -1;
|
||||
const json::number_integer_t pos = 5;
|
||||
const json::number_unsigned_t big = (std::numeric_limits<json::number_unsigned_t>::max)();
|
||||
|
||||
SECTION("same type")
|
||||
{
|
||||
CHECK(json(pos) == json(pos));
|
||||
CHECK(json(pos) != json(neg));
|
||||
CHECK(json(neg) < json(pos));
|
||||
CHECK_FALSE(json(pos) < json(neg));
|
||||
CHECK_FALSE(json(pos) < json(pos));
|
||||
CHECK(json(pos) <= json(pos));
|
||||
|
||||
CHECK(json(1u) == json(1u));
|
||||
CHECK(json(1u) < json(2u));
|
||||
CHECK_FALSE(json(2u) < json(1u));
|
||||
|
||||
CHECK(json(1.5) == json(1.5));
|
||||
CHECK(json(1.5) < json(2.5));
|
||||
CHECK_FALSE(json(2.5) < json(1.5));
|
||||
CHECK(json(-0.0) == json(0.0));
|
||||
}
|
||||
|
||||
SECTION("mixed number types")
|
||||
{
|
||||
// negative signed vs unsigned must not wrap around
|
||||
CHECK(json(neg) < json(1u));
|
||||
CHECK(json(1u) > json(neg));
|
||||
CHECK(json(neg) != json(big));
|
||||
CHECK(json(neg) < json(big));
|
||||
CHECK_FALSE(json(big) < json(neg));
|
||||
|
||||
CHECK(json(pos) == json(5u));
|
||||
CHECK(json(5u) == json(pos));
|
||||
CHECK(json(pos) < json(6u));
|
||||
CHECK(json(pos) == json(5.0));
|
||||
CHECK(json(5.0) == json(pos));
|
||||
CHECK(json(5u) == json(5.0));
|
||||
CHECK(json(neg) < json(0.5));
|
||||
CHECK(json(0.5) < json(1u));
|
||||
}
|
||||
|
||||
SECTION("boolean and null")
|
||||
{
|
||||
CHECK(json(true) == json(true));
|
||||
CHECK(json(true) != json(false));
|
||||
CHECK(json(false) < json(true));
|
||||
CHECK_FALSE(json(true) < json(false));
|
||||
CHECK_FALSE(json(true) < json(true));
|
||||
|
||||
CHECK(json(nullptr) == json(nullptr));
|
||||
CHECK_FALSE(json(nullptr) < json(nullptr));
|
||||
CHECK(json(nullptr) <= json(nullptr));
|
||||
CHECK(json(nullptr) < json(false));
|
||||
CHECK(json(nullptr) != json(false));
|
||||
CHECK(json(nullptr) != json(0));
|
||||
}
|
||||
|
||||
SECTION("string vs number")
|
||||
{
|
||||
CHECK(json("5") != json(5));
|
||||
CHECK(json(5) != json("5"));
|
||||
CHECK(json(5) < json("5")); // number < string by type order
|
||||
CHECK_FALSE(json("5") < json(5));
|
||||
CHECK(json(1.5) < json(""));
|
||||
CHECK(json("a") == json("a"));
|
||||
CHECK(json("a") < json("b"));
|
||||
}
|
||||
|
||||
SECTION("NaN")
|
||||
{
|
||||
const json nan = std::numeric_limits<json::number_float_t>::quiet_NaN();
|
||||
const json one = 1.0;
|
||||
|
||||
CHECK_FALSE(nan == nan);
|
||||
CHECK(nan != nan);
|
||||
CHECK_FALSE(nan < nan);
|
||||
CHECK_FALSE(nan <= nan);
|
||||
CHECK_FALSE(nan >= nan);
|
||||
CHECK_FALSE(nan > nan);
|
||||
|
||||
CHECK_FALSE(nan == one);
|
||||
CHECK_FALSE(one == nan);
|
||||
CHECK_FALSE(nan < one);
|
||||
CHECK_FALSE(one < nan);
|
||||
CHECK_FALSE(nan <= one);
|
||||
CHECK_FALSE(one <= nan);
|
||||
CHECK_FALSE(nan >= one);
|
||||
CHECK_FALSE(one >= nan);
|
||||
|
||||
// NaN against other number types
|
||||
CHECK_FALSE(nan == json(1));
|
||||
CHECK_FALSE(json(1) == nan);
|
||||
CHECK_FALSE(nan < json(1u));
|
||||
CHECK_FALSE(json(1u) < nan);
|
||||
|
||||
#if JSON_HAS_THREE_WAY_COMPARISON
|
||||
CHECK((nan <=> nan) == std::partial_ordering::unordered); // *NOPAD*
|
||||
CHECK((nan <=> one) == std::partial_ordering::unordered); // *NOPAD*
|
||||
CHECK((one <=> nan) == std::partial_ordering::unordered); // *NOPAD*
|
||||
CHECK((one <=> json(2.0)) == std::partial_ordering::less); // *NOPAD*
|
||||
CHECK((json(2.0) <=> one) == std::partial_ordering::greater); // *NOPAD*
|
||||
CHECK((one <=> one) == std::partial_ordering::equivalent); // *NOPAD*
|
||||
CHECK((json(neg) <=> json(1u)) == std::partial_ordering::less); // *NOPAD*
|
||||
CHECK((json(nullptr) <=> json(nullptr)) == std::partial_ordering::equivalent); // *NOPAD*
|
||||
CHECK((json(true) <=> json(false)) == std::partial_ordering::greater); // *NOPAD*
|
||||
#endif
|
||||
}
|
||||
|
||||
SECTION("discarded")
|
||||
{
|
||||
const json d(json::value_t::discarded);
|
||||
|
||||
CHECK_FALSE(d == d);
|
||||
CHECK_FALSE(d < d);
|
||||
CHECK_FALSE(d == json(1));
|
||||
CHECK_FALSE(json(1) == d);
|
||||
CHECK_FALSE(d < json(1));
|
||||
CHECK_FALSE(json(1) < d);
|
||||
CHECK_FALSE(d == json(nullptr));
|
||||
CHECK_FALSE(json(nullptr) == d);
|
||||
|
||||
#if JSON_HAS_THREE_WAY_COMPARISON
|
||||
CHECK((d <=> d) == std::partial_ordering::unordered); // *NOPAD*
|
||||
CHECK((d <=> json(1)) == std::partial_ordering::unordered); // *NOPAD*
|
||||
CHECK((json(1) <=> d) == std::partial_ordering::unordered); // *NOPAD*
|
||||
#endif
|
||||
}
|
||||
|
||||
SECTION("scalar overloads")
|
||||
{
|
||||
CHECK(json(5) == 5);
|
||||
CHECK(5 == json(5));
|
||||
CHECK(json(5) < 6);
|
||||
CHECK(4 < json(5));
|
||||
CHECK(json(1.5) == 1.5);
|
||||
CHECK(json(true) == true);
|
||||
CHECK(json(5) != 6);
|
||||
CHECK(json(1u) == 1.0);
|
||||
}
|
||||
|
||||
SECTION("structured values still compare")
|
||||
{
|
||||
CHECK(json({1, 2}) == json({1, 2}));
|
||||
CHECK(json({1, 2}) < json({1, 3}));
|
||||
CHECK(json({{"a", 1}}) == json({{"a", 1}}));
|
||||
CHECK(json::binary({1, 2}) == json::binary({1, 2}));
|
||||
CHECK(json(1) < json::array());
|
||||
CHECK(json::object() < json::array());
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,9 @@
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
@@ -184,3 +187,139 @@ TEST_CASE("other constructors and destructor")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("copy constructor and destructor across value types")
|
||||
{
|
||||
SECTION("copy of a scalar is independent of the source")
|
||||
{
|
||||
const json src_null = nullptr;
|
||||
const json src_bool = true;
|
||||
const json src_int = -7;
|
||||
const json src_uint = 7u;
|
||||
const json src_float = 2.5;
|
||||
|
||||
json c_null(src_null);
|
||||
json c_bool(src_bool);
|
||||
json c_int(src_int);
|
||||
json c_uint(src_uint);
|
||||
json c_float(src_float);
|
||||
|
||||
CHECK(c_null.is_null());
|
||||
CHECK(c_bool == true);
|
||||
CHECK(c_int == -7);
|
||||
CHECK(c_uint == 7u);
|
||||
CHECK(c_float == 2.5);
|
||||
|
||||
c_int = 8;
|
||||
c_float = 3.5;
|
||||
CHECK(src_int == -7);
|
||||
CHECK(src_float == 2.5);
|
||||
}
|
||||
|
||||
SECTION("copy of an owning value, then mutate the copy")
|
||||
{
|
||||
const json src_string = "hello";
|
||||
const json src_array = {1, 2, 3};
|
||||
const json src_object = {{"a", 1}, {"b", {{"c", 2}}}};
|
||||
const json src_binary = json::binary({1, 2, 3});
|
||||
|
||||
json c_string(src_string);
|
||||
json c_array(src_array);
|
||||
json c_object(src_object);
|
||||
json c_binary(src_binary);
|
||||
|
||||
CHECK(c_string == src_string);
|
||||
CHECK(c_array == src_array);
|
||||
CHECK(c_object == src_object);
|
||||
CHECK(c_binary == src_binary);
|
||||
|
||||
c_string.get_ref<json::string_t&>() += " world";
|
||||
c_array.push_back(4);
|
||||
c_object["b"]["c"] = 3;
|
||||
c_object["d"] = 4;
|
||||
c_binary.get_binary().push_back(4);
|
||||
|
||||
CHECK(src_string == "hello");
|
||||
CHECK(src_array.size() == 3);
|
||||
CHECK(src_object["b"]["c"] == 2);
|
||||
CHECK_FALSE(src_object.contains("d"));
|
||||
CHECK(src_binary.get_binary().size() == 3);
|
||||
|
||||
CHECK(c_string == "hello world");
|
||||
CHECK(c_array.size() == 4);
|
||||
CHECK(c_object["b"]["c"] == 3);
|
||||
CHECK(c_binary.get_binary().size() == 4);
|
||||
}
|
||||
|
||||
SECTION("copy keeps parent pointers valid (JSON_DIAGNOSTICS)")
|
||||
{
|
||||
const json src = {{"a", {1, 2}}, {"b", "x"}};
|
||||
json c(src);
|
||||
c["a"].push_back(3);
|
||||
CHECK(c["a"].size() == 3);
|
||||
CHECK(src["a"].size() == 2);
|
||||
}
|
||||
|
||||
SECTION("vector of mixed types: build, copy, and destroy")
|
||||
{
|
||||
{
|
||||
std::vector<json> v;
|
||||
for (int i = 0; i < 70; ++i)
|
||||
{
|
||||
switch (i % 9)
|
||||
{
|
||||
case 0:
|
||||
v.emplace_back(nullptr);
|
||||
break;
|
||||
case 1:
|
||||
v.emplace_back(i % 2 == 0);
|
||||
break;
|
||||
case 2:
|
||||
v.emplace_back(-i);
|
||||
break;
|
||||
case 3:
|
||||
v.emplace_back(static_cast<json::number_unsigned_t>(i));
|
||||
break;
|
||||
case 4:
|
||||
v.emplace_back(i * 0.5);
|
||||
break;
|
||||
case 5:
|
||||
v.emplace_back(std::string(static_cast<std::size_t>(i), 'x'));
|
||||
break;
|
||||
case 6:
|
||||
v.emplace_back(json::array({i, "s", nullptr}));
|
||||
break;
|
||||
case 7:
|
||||
v.emplace_back(json::object({{"k", i}}));
|
||||
break;
|
||||
default:
|
||||
v.emplace_back(json::binary({1, 2, 3}));
|
||||
break;
|
||||
}
|
||||
}
|
||||
REQUIRE(v.size() == 70);
|
||||
|
||||
std::vector<json> copy(v);
|
||||
CHECK(copy == v);
|
||||
|
||||
// mutating the copy leaves the original alone
|
||||
copy[5] = "changed";
|
||||
copy[6].push_back(1);
|
||||
CHECK(copy != v);
|
||||
CHECK(v[5] == std::string(5, 'x'));
|
||||
CHECK(v[6].size() == 3);
|
||||
|
||||
v.erase(v.begin(), v.begin() + 10);
|
||||
CHECK(v.size() == 60);
|
||||
}
|
||||
|
||||
// destroying the discarded and default-constructed values
|
||||
std::vector<json> w(4);
|
||||
w.emplace_back(json::value_t::discarded);
|
||||
w.emplace_back(json::value_t::object);
|
||||
w.emplace_back(json::value_t::array);
|
||||
w.emplace_back(json::value_t::string);
|
||||
w.emplace_back(json::value_t::binary);
|
||||
CHECK(w.size() == 9);
|
||||
}
|
||||
}
|
||||
@@ -13,9 +13,11 @@
|
||||
#endif
|
||||
|
||||
#define JSON_DIAGNOSTICS 1
|
||||
#define JSON_TESTS_PRIVATE
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
using nlohmann::ordered_json;
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
@@ -631,3 +633,175 @@ TEST_CASE("Better diagnostics past the descent bound of update(), merge_patch()
|
||||
CHECK_THROWS_WITH_AS(p->at("y").at(0), expected_y.c_str(), json::type_error);
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// check that every child's parent pointer points to the containing value
|
||||
template<typename BasicJsonType>
|
||||
bool parents_consistent(const BasicJsonType& j)
|
||||
{
|
||||
bool ok = true;
|
||||
if (j.is_array() || j.is_object())
|
||||
{
|
||||
for (const auto& child : j)
|
||||
{
|
||||
ok = ok && child.m_parent == &j && parents_consistent(child);
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE_TEMPLATE("Parent pointers stay consistent", J, json, ordered_json)
|
||||
{
|
||||
SECTION("parse, copy, move, assign, swap")
|
||||
{
|
||||
J j = J::parse(R"({"a":[1,{"b":[2,3]},[4]],"c":{"d":{"e":[]}}})");
|
||||
CHECK(parents_consistent(j));
|
||||
|
||||
J copy(j);
|
||||
CHECK(parents_consistent(copy));
|
||||
|
||||
J moved(std::move(copy));
|
||||
CHECK(parents_consistent(moved));
|
||||
|
||||
J assigned;
|
||||
assigned = j;
|
||||
CHECK(parents_consistent(assigned));
|
||||
|
||||
J other = {1, 2, {{"k", J::array({1, 2})}}};
|
||||
other.swap(assigned);
|
||||
CHECK(parents_consistent(other));
|
||||
CHECK(parents_consistent(assigned));
|
||||
}
|
||||
|
||||
SECTION("push_back growth")
|
||||
{
|
||||
J arr = J::array();
|
||||
for (int i = 0; i < 500; ++i)
|
||||
{
|
||||
arr.push_back(J::array({i}));
|
||||
}
|
||||
CHECK(parents_consistent(arr));
|
||||
|
||||
J obj = J::object();
|
||||
for (int i = 0; i < 500; ++i)
|
||||
{
|
||||
obj.push_back({std::to_string(i), J::array({i})});
|
||||
}
|
||||
CHECK(parents_consistent(obj));
|
||||
}
|
||||
|
||||
SECTION("emplace_back and emplace growth")
|
||||
{
|
||||
J arr = J::array();
|
||||
J obj = J::object();
|
||||
for (int i = 0; i < 500; ++i)
|
||||
{
|
||||
arr.emplace_back(J::array({i}));
|
||||
obj.emplace(std::to_string(i), J::array({i}));
|
||||
}
|
||||
CHECK(parents_consistent(arr));
|
||||
CHECK(parents_consistent(obj));
|
||||
}
|
||||
|
||||
SECTION("operator[] growth and at()")
|
||||
{
|
||||
J obj = J::object();
|
||||
for (int i = 0; i < 500; ++i)
|
||||
{
|
||||
obj[std::to_string(i)] = J::array({i});
|
||||
CHECK(obj[std::to_string(i / 2)].m_parent == &obj);
|
||||
}
|
||||
CHECK(parents_consistent(obj));
|
||||
|
||||
J arr = J::array();
|
||||
for (int i = 0; i < 500; ++i)
|
||||
{
|
||||
arr[static_cast<size_t>(i)] = J::object({{"x", i}});
|
||||
}
|
||||
for (size_t i = 0; i < 500; ++i)
|
||||
{
|
||||
CHECK(arr.at(i).m_parent == &arr);
|
||||
}
|
||||
CHECK(obj.at("3").m_parent == &obj);
|
||||
CHECK(parents_consistent(arr));
|
||||
CHECK(parents_consistent(obj));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_TEMPLATE("update() and merge_patch() keep parent pointers and diagnostic paths", J, json, ordered_json)
|
||||
{
|
||||
// enough keys to make the vector behind ordered_json reallocate several times
|
||||
const auto make_object = [](int first, int last)
|
||||
{
|
||||
J o = J::object();
|
||||
for (int i = first; i < last; ++i)
|
||||
{
|
||||
o[std::to_string(i)] = J::object({{"x", J::array({i})}});
|
||||
}
|
||||
return o;
|
||||
};
|
||||
|
||||
// const access, so operator[] cannot repair the parent pointers
|
||||
const auto check_path = [](const J & j, const std::string & key)
|
||||
{
|
||||
const std::string expected = "[json.exception.type_error.304] (/" + key + "/x) cannot use at() with array";
|
||||
CHECK_THROWS_WITH_AS(j[key]["x"].at("y"), expected.c_str(), json::type_error);
|
||||
};
|
||||
|
||||
SECTION("update(const_reference)")
|
||||
{
|
||||
J j = make_object(0, 200);
|
||||
j.update(make_object(100, 400));
|
||||
CHECK(j.size() == 400);
|
||||
CHECK(parents_consistent(j));
|
||||
check_path(j, "0");
|
||||
check_path(j, "150");
|
||||
check_path(j, "399");
|
||||
}
|
||||
|
||||
SECTION("update(const_reference, merge_objects)")
|
||||
{
|
||||
J j = make_object(0, 200);
|
||||
j["50"]["y"] = 1;
|
||||
J other = make_object(100, 400);
|
||||
other["50"] = J::object({{"z", J::array({1})}});
|
||||
j.update(other, true);
|
||||
CHECK(j.size() == 400);
|
||||
CHECK(j["50"].size() == 3);
|
||||
CHECK(parents_consistent(j));
|
||||
check_path(j, "0");
|
||||
check_path(j, "399");
|
||||
}
|
||||
|
||||
SECTION("update(first, last)")
|
||||
{
|
||||
J j = make_object(0, 200);
|
||||
J other = make_object(100, 400);
|
||||
j.update(other.begin(), other.end());
|
||||
CHECK(j.size() == 400);
|
||||
CHECK(parents_consistent(j));
|
||||
check_path(j, "0");
|
||||
check_path(j, "399");
|
||||
|
||||
J k = make_object(0, 200);
|
||||
k.update(other.begin(), other.end(), true);
|
||||
CHECK(k.size() == 400);
|
||||
CHECK(parents_consistent(k));
|
||||
check_path(k, "399");
|
||||
}
|
||||
|
||||
SECTION("merge_patch()")
|
||||
{
|
||||
J j = make_object(0, 200);
|
||||
J patch = make_object(100, 400);
|
||||
patch["0"] = nullptr;
|
||||
j.merge_patch(patch);
|
||||
CHECK(j.size() == 399);
|
||||
CHECK_FALSE(j.contains("0"));
|
||||
CHECK(parents_consistent(j));
|
||||
check_path(j, "1");
|
||||
check_path(j, "399");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++ (supporting code)
|
||||
// | | |__ | | | | | | version 3.12.0
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
// The exceptions in hot core functions are constructed out of line. These tests pin the
|
||||
// exact what() texts of the sites that moved, so the refactoring cannot change them.
|
||||
|
||||
#define CHECK_MSG(expr, msg) CHECK_THROWS_WITH(expr, msg)
|
||||
|
||||
TEST_CASE("exception messages of out-of-line throw sites")
|
||||
{
|
||||
json arr = {1, 2, 3};
|
||||
json arr2 = {4, 5};
|
||||
json obj = {{"a", 1}, {"b", 2}};
|
||||
json obj2 = {{"c", 3}};
|
||||
json num = 1;
|
||||
|
||||
SECTION("iterators")
|
||||
{
|
||||
CHECK_MSG((void)(arr.begin() == arr2.begin()), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
|
||||
CHECK_MSG((void)(arr.begin() < arr2.begin()), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
|
||||
CHECK_MSG((void)(obj.begin() < obj.end()), "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
|
||||
CHECK_MSG(obj.begin() += 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
|
||||
CHECK_MSG((void)(obj.begin() - 1), "[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
|
||||
CHECK_MSG((void)obj.begin()[0], "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators");
|
||||
CHECK_MSG((void)arr.begin().key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators");
|
||||
json null;
|
||||
CHECK_MSG((void)*null.end(), "[json.exception.invalid_iterator.214] cannot get value");
|
||||
}
|
||||
|
||||
SECTION("constructors")
|
||||
{
|
||||
CHECK_MSG(json(arr.begin(), arr2.end()), "[json.exception.invalid_iterator.201] iterators are not compatible");
|
||||
CHECK_MSG(json(num.begin() + 1, num.end() + 1), "[json.exception.invalid_iterator.204] iterators out of range");
|
||||
CHECK_MSG(json(json::initializer_list_t{1, 2}, false, json::value_t::object), "[json.exception.type_error.301] cannot create object from initializer list");
|
||||
}
|
||||
|
||||
SECTION("get_ref and operator[]")
|
||||
{
|
||||
CHECK_MSG((void)num.get_ref<std::string&>(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number");
|
||||
CHECK_MSG((void)num[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number");
|
||||
}
|
||||
|
||||
SECTION("at")
|
||||
{
|
||||
const json carr = arr;
|
||||
const json cobj = obj;
|
||||
CHECK_MSG((void)arr.at(static_cast<json::size_type>(10)), "[json.exception.out_of_range.401] array index 10 is out of range");
|
||||
CHECK_MSG((void)carr.at(static_cast<json::size_type>(3)), "[json.exception.out_of_range.401] array index 3 is out of range");
|
||||
CHECK_MSG((void)num.at(static_cast<json::size_type>(0)), "[json.exception.type_error.304] cannot use at() with number");
|
||||
CHECK_MSG((void)obj.at("z"), "[json.exception.out_of_range.403] key 'z' not found");
|
||||
CHECK_MSG((void)cobj.at("z"), "[json.exception.out_of_range.403] key 'z' not found");
|
||||
CHECK_MSG((void)obj.at(std::string("zz")), "[json.exception.out_of_range.403] key 'zz' not found");
|
||||
CHECK_MSG((void)num.at("a"), "[json.exception.type_error.304] cannot use at() with number");
|
||||
|
||||
nlohmann::ordered_json oobj = {{"a", 1}};
|
||||
const nlohmann::ordered_json coobj = oobj;
|
||||
CHECK_MSG((void)oobj.at("z"), "[json.exception.out_of_range.403] key 'z' not found");
|
||||
CHECK_MSG((void)coobj.at("z"), "[json.exception.out_of_range.403] key 'z' not found");
|
||||
CHECK_MSG((void)coobj.at(std::string("zz")), "[json.exception.out_of_range.403] key 'zz' not found");
|
||||
}
|
||||
|
||||
SECTION("erase")
|
||||
{
|
||||
CHECK_MSG(arr.erase(arr2.begin()), "[json.exception.invalid_iterator.202] iterator does not fit current value");
|
||||
CHECK_MSG(num.erase(num.end()), "[json.exception.invalid_iterator.205] iterator out of range");
|
||||
CHECK_MSG(arr.erase(arr.begin(), arr2.end()), "[json.exception.invalid_iterator.203] iterators do not fit current value");
|
||||
CHECK_MSG(num.erase(num.begin() + 1, num.end()), "[json.exception.invalid_iterator.204] iterators out of range");
|
||||
CHECK_MSG(arr.erase(static_cast<json::size_type>(10)), "[json.exception.out_of_range.401] array index 10 is out of range");
|
||||
}
|
||||
|
||||
SECTION("insert")
|
||||
{
|
||||
CHECK_MSG(arr.insert(arr2.begin(), 1), "[json.exception.invalid_iterator.202] iterator does not fit current value");
|
||||
CHECK_MSG(arr.insert(arr2.begin(), 2, 1), "[json.exception.invalid_iterator.202] iterator does not fit current value");
|
||||
CHECK_MSG(arr.insert(arr2.begin(), arr2.begin(), arr2.end()), "[json.exception.invalid_iterator.202] iterator does not fit current value");
|
||||
CHECK_MSG(arr.insert(arr.begin(), arr.begin(), arr2.end()), "[json.exception.invalid_iterator.210] iterators do not fit");
|
||||
CHECK_MSG(arr.insert(arr.begin(), arr.begin(), arr.end()), "[json.exception.invalid_iterator.211] passed iterators may not belong to container");
|
||||
CHECK_MSG(arr.insert(arr.begin(), obj.begin(), obj.end()), "[json.exception.invalid_iterator.202] iterators first and last must point to arrays");
|
||||
CHECK_MSG(arr.insert(arr2.begin(), {1, 2}), "[json.exception.invalid_iterator.202] iterator does not fit current value");
|
||||
CHECK_MSG(obj.insert(obj2.begin(), arr.end()), "[json.exception.invalid_iterator.210] iterators do not fit");
|
||||
CHECK_MSG(obj.insert(arr.begin(), arr.end()), "[json.exception.invalid_iterator.202] iterators first and last must point to objects");
|
||||
CHECK_MSG(obj.update(arr.begin(), arr2.end()), "[json.exception.invalid_iterator.210] iterators do not fit");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user