Compare commits

..
Author SHA1 Message Date
Niels Lohmann 0c0d5edda3 Make insert(pos, basic_json&&) move its argument instead of copying it
insert(const_iterator pos, basic_json&& val) delegated to
insert(pos, val), but val is a named rvalue reference, so inside the
function it is an lvalue: the call always resolved to
insert(const_iterator, const basic_json&) and deep-copied the value.
This has been the case since the overload was introduced, in every
release. push_back(basic_json&&), by contrast, already moves.

Give the rvalue overload its own body with the same two checks
(type_error.309, invalid_iterator.202), then move the argument into a
local before inserting it. Moving into a local first, rather than
inserting std::move(val) directly, keeps this safe even when val
aliases an element of the same array (e.g.
arr.insert(arr.begin(), std::move(arr[1]))), since
std::vector::insert(pos, T&&) is not guaranteed to handle an argument
that aliases one of its own elements.

This is a deliberate, small behavior change: the moved-from argument
now ends up null afterwards, the same as after push_back(&&), instead
of keeping its old value unchanged. No signature changes, so the
public API and ABI are unaffected. Add unit-modifiers coverage for
the moved-from state and for self-aliasing insertion, both with and
without reallocation of the underlying array.

Part of #5724

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-30 11:11:16 +02:00
Niels Lohmann ff2ec8116d Deduplicate the 16 copied from_cbor/msgpack/ubjson/bjdata/bon8/bson bodies
Each of the 16 binary deserialization overloads (from_cbor,
from_msgpack, from_ubjson, from_bjdata, from_bon8, from_bson, each in
an InputType&& and an iterator/sentinel version, plus the deprecated
span overloads of from_cbor/from_msgpack/from_ubjson/from_bson) had
the same body, differing only in the input_format_t value. Every copy
built a temporary binary_reader from std::move(ia) and called
sax_parse on it in the same expression, which also produced a
false-positive cppcheck accessMoved on all 16 lines and needed a
NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) on
the four span overloads.

Add a private from_binary_impl() helper that builds the reader as a
named local instead, and make each of the 16 overloads a one-line
forward to it. All public signatures, default arguments,
JSON_HEDLEY_WARN_UNUSED_RESULT and JSON_HEDLEY_DEPRECATED_FOR
attributes are unchanged, tag_handler keeps defaulting to
cbor_tag_handler_t::error for the non-CBOR formats (matching
binary_reader::sax_parse's own default), and the helper is placed in
the existing private section before the binary section banner rather
than between the from_* overloads, so from_binary_impl() itself does
not collide with #5688's insertion point. Collapsing the
from_bjdata/from_bon8 bodies into one-line forwards does rewrite the
"return result; }" context lines that #5688 inserts its two
deprecated overloads after, so that PR will need a small manual
rebase (reinserting its overloads after the new one-line bodies)
rather than applying cleanly.

Part of #5724

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-30 11:11:15 +02:00
Niels Lohmann b591304d04 Fix stale and copy-pasted comments in basic_json
Several comments no longer match the code: the class invariant and
assert_invariant()'s doc still named the members m_value/m_type
(now m_data.m_value/m_data.m_type) and did not mention the binary
invariant that assert_invariant() already checks; the json_value note
and the get<PointerType>() @tparam list omitted binary_t even though
binary is a variable-length, pointer-stored type like the others; the
key-based value() overload's brief said "via JSON Pointer", which is
the other overload; and swap(binary_t&)/swap(binary_t::container_type&)
both carried "swap only works for strings", copied from swap(string_t&).

Comment-only change; behavior, the public API and the ABI are
unchanged. The private get_impl() doxygen and the emplace() comments
that border #5585's hunk are intentionally left alone.

Part of #5724

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-30 10:42:29 +02:00
Niels Lohmann c2553bac5a Deduplicate the string/binary cleanup in the two erase() overloads
erase(pos) and erase(first, last) each carried a byte-identical
14-line block that destroys and deallocates a string or binary
value before resetting the type to null. That reimplements the
string/binary cases of json_value::destroy(), so any future change to
how those values are freed would have to be made in three places
instead of one. Both overloads now just call destroy() and reset the
union; for the other primitive types (boolean, numbers) destroy() is
a no-op, so behavior is unchanged.

Also fix erase(first, last)'s error-path branch hint, which used
JSON_HEDLEY_LIKELY where erase(pos), the iterator-range constructor,
and every other error path in the class use JSON_HEDLEY_UNLIKELY.
This only affects code layout, not semantics.

Part of #5724

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-30 10:40:04 +02:00
Niels Lohmann 1274e0f250 Remove stale cppcheck suppressions and name the local parser in parse()
Running the pinned cppcheck (ci_cppcheck's invocation) without
--inline-suppr across all configurations reports no syntaxError, no
ignoredReturnValue and no assertWithSideEffect, so the corresponding
suppressions in json_fwd.hpp, string_concat.hpp and
assert_invariant() no longer match anything (json_fwd.hpp's is kept,
since downstream users who run an older cppcheck against it could
still hit the warning it once silenced).

The three basic_json::parse() overloads still trigger a false-positive
accessMoved/accessForwarded because they build a temporary parser and
call .parse() on it in the same expression; giving that parser a name
makes the warning go away without changing behavior, and removes the
last of the inline suppressions on these functions.

Part of #5724

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-30 10:37:48 +02:00
Niels Lohmann ffd3a3722b Remove four cppcheck accessForwarded suppressions in the move constructor
basic_json(basic_json&&) built its base subobject with
std::forward<json_base_class_t>(other), so cppcheck saw the whole of
other as forwarded and flagged every subsequent access to it as
accessForwarded, three of them still marked "TODO check". Only the
base subobject is actually moved from; cast explicitly to the base
type instead, the way ordered_map already does, so cppcheck can tell
the two are unrelated. Behavior is unchanged: for a non-reference T,
std::forward<T>(x) is defined as static_cast<T&&>(x).

Part of #5724

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-30 10:36:41 +02:00
Niels Lohmann f5e4b9b8cf Stop the noexcept null constructor delegating to a throwing one
basic_json(std::nullptr_t) delegated to basic_json(value_t), whose
underlying json_value(value_t) constructor allocates for other types
and can therefore throw, which is why the noexcept had a
NOLINT(bugprone-exception-escape). The delegated-to constructor also
called assert_invariant() a second time. The default member
initializers of data already produce the same null state (a
value-initialized, i.e. zeroed, union with object == nullptr), so the
delegation and its NOLINT can simply be dropped.

Part of #5724

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-30 10:34:05 +02:00
Niels Lohmann e5c3e81279 Fix meta()'s dead, syntactically invalid HP aCC branch
The HP aCC branch of basic_json::meta() was missing a semicolon and
has therefore never compiled; adding only a semicolon would also make
it throw type_error.305, since it assigned a plain string to
result["compiler"] and then indexed into it like the other branches
do into an object. Make the branch consistent with the others by
assigning an object with "family" and "version" keys, narrow the
condition to __HP_aCC (a C compiler cannot build this header-only
library), and fix meta.md, which documented the old (impossible)
plain-string behavior.

Part of #5724

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-30 10:32:07 +02:00
Niels Lohmann 1d98396a19 Remove unused private aliases from basic_json
The private aliases primitive_iterator_t, internal_iterator and
output_adapter_t are not used anywhere: iter_impl, binary_writer and
the tests refer to the detail:: names directly. As the aliases are
private, no user or derived class can depend on them. The
internal_iterator.hpp include stays because iter_impl needs it.

Part of #5724

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-30 09:47:00 +02:00
9 changed files with 320 additions and 1130 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ JSON object holding version information
| key | description |
|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `compiler` | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version). On HP aCC compilers, `compiler` is instead the plain string `hp`. |
| `compiler` | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `hp`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version). |
| `copyright` | The copyright line for the library as string. |
| `name` | The name of the library as string. |
| `platform` | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`. |
@@ -39,7 +39,6 @@ inline std::size_t concat_length(const char /*c*/, const Args& ... rest)
template<typename... Args>
inline std::size_t concat_length(const char* cstr, const Args& ... rest)
{
// cppcheck-suppress ignoredReturnValue
return ::strlen(cstr) + concat_length(rest...);
}
+138 -423
View File
@@ -110,11 +110,12 @@ struct is_std_optional<std::optional<T>> : std::true_type {};
@brief a class to store JSON values
@internal
@invariant The member variables @a m_value and @a m_type have the following
relationship:
- If `m_type == value_t::object`, then `m_value.object != nullptr`.
- If `m_type == value_t::array`, then `m_value.array != nullptr`.
- If `m_type == value_t::string`, then `m_value.string != nullptr`.
@invariant The member variables @a m_data.m_value and @a m_data.m_type have
the following relationship:
- If `m_data.m_type == value_t::object`, then `m_data.m_value.object != nullptr`.
- If `m_data.m_type == value_t::array`, then `m_data.m_value.array != nullptr`.
- If `m_data.m_type == value_t::string`, then `m_data.m_value.string != nullptr`.
- If `m_data.m_type == value_t::binary`, then `m_data.m_value.binary != nullptr`.
The invariants are checked by member function assert_invariant().
@note ObjectType trick from https://stackoverflow.com/a/9860911
@@ -174,18 +175,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
private:
using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t;
template<typename BasicJsonType>
using internal_iterator = ::nlohmann::detail::internal_iterator<BasicJsonType>;
template<typename BasicJsonType>
using iter_impl = ::nlohmann::detail::iter_impl<BasicJsonType>;
template<typename Iterator>
using iteration_proxy = ::nlohmann::detail::iteration_proxy<Iterator>;
template<typename Base> using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator<Base>;
template<typename CharType>
using output_adapter_t = ::nlohmann::detail::output_adapter_t<CharType>;
template<typename InputType>
using binary_reader = ::nlohmann::detail::binary_reader<basic_json, InputType>;
template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>;
@@ -326,8 +321,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
std::to_string(__GNUC_PATCHLEVEL__))
}
};
#elif defined(__HP_cc) || defined(__HP_aCC)
result["compiler"] = "hp"
#elif defined(__HP_aCC)
result["compiler"] = {{"family", "hp"}, {"version", __HP_aCC}};
#elif defined(__IBMCPP__)
result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}};
#elif defined(_MSC_VER)
@@ -469,9 +464,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
binary | binary | pointer to @ref binary_t
null | null | *no value is stored*
@note Variable-length types (objects, arrays, and strings) are stored as
pointers. The size of the union should not exceed 64 bits if the default
value types are used.
@note Variable-length types (objects, arrays, strings, and binary
values) are stored as pointers. The size of the union should not exceed
64 bits if the default value types are used.
@since version 1.0.0
*/
@@ -723,7 +718,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
end of every constructor to make sure that created objects respect the
invariant. Furthermore, it has to be called each time the type of a JSON
value is changed, because the invariant expresses a relationship between
@a m_type and @a m_value.
@a m_data.m_type and @a m_data.m_value.
Furthermore, the parent relation is checked for arrays and objects: If
@a check_parents true and the value is an array or object, then the
@@ -744,7 +739,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
#if JSON_DIAGNOSTICS
JSON_TRY
{
// cppcheck-suppress assertWithSideEffect
JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j)
{
return j.m_parent == this;
@@ -908,11 +902,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/*!
@brief how many levels the operation going on in this thread has descended into
Copying a value, converting one from another specialization, and comparing
two values share this count. The library never nests one of them inside
another - none of them does either of the other two on the way - and where
user code nests them anyway, sharing the count only ends a descent sooner
than it had to, which costs a little speed and is never wrong.
Copying a value and comparing two values share this count. The library never
nests one inside the other - copying a value does not compare one, and
comparing two values does not copy them - and where user code nests them
anyway, sharing the count only ends a descent sooner than it had to, which
costs a little speed and is never wrong.
A byte is enough: the count never exceeds the limit by more than the single
level that notices the limit has been reached.
@@ -1268,221 +1262,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
copy_iteratively(src);
}
/*!
@brief convert the value @a val of another specialization into this null
value; @a val must be neither an object nor an array
Converting such a value never descends, so both ways of converting an
object or an array (@ref convert_structured) leave their elements of this
kind to the converting constructor, which leaves them to this.
*/
template<typename BasicJsonType>
void convert_leaf(const BasicJsonType& val)
{
using other_boolean_t = typename BasicJsonType::boolean_t;
using other_number_float_t = typename BasicJsonType::number_float_t;
using other_number_integer_t = typename BasicJsonType::number_integer_t;
using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using other_string_t = typename BasicJsonType::string_t;
using other_binary_t = typename BasicJsonType::binary_t;
switch (val.type())
{
case value_t::boolean:
JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());
break;
case value_t::number_float:
JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());
break;
case value_t::number_integer:
JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());
break;
case value_t::number_unsigned:
JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());
break;
case value_t::string:
JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());
break;
case value_t::binary:
JSONSerializer<other_binary_t>::to_json(*this, val.template get_ref<const other_binary_t&>());
break;
case value_t::null:
// this value is null already; assigning null to it would also
// reset its positions (JSON_DIAGNOSTIC_POSITIONS)
break;
case value_t::discarded:
m_data.m_type = value_t::discarded;
break;
case value_t::object: // LCOV_EXCL_LINE
case value_t::array: // LCOV_EXCL_LINE
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
}
/// scratch space for the converted elements of the arrays that
/// @ref convert_iteratively has yet to create
using convert_scratch_t = std::vector<basic_json, AllocatorType<basic_json>>;
/*!
@brief create the object or array @a val converted into this null value
Its converted elements are the last `val.size()` entries of @a elements (an
array) or of @a members (an object); they are moved into the container in
one go and then removed.
*/
template<typename BasicJsonType>
void convert_level(const BasicJsonType& val, convert_scratch_t& elements, copy_scratch_t& members)
{
if (val.is_object())
{
const auto first = members.end() - static_cast<typename copy_scratch_t::difference_type>(val.size());
m_data.m_value.object = create<object_t>(std::make_move_iterator(first),
std::make_move_iterator(members.end()));
// only now that the object exists may this stop being a null value
m_data.m_type = value_t::object;
members.erase(first, members.end());
}
else
{
const auto first = elements.end() - static_cast<typename convert_scratch_t::difference_type>(val.size());
m_data.m_value.array = create<array_t>(std::make_move_iterator(first),
std::make_move_iterator(elements.end()));
// only now that the array exists may this stop being a null value
m_data.m_type = value_t::array;
elements.erase(first, elements.end());
}
set_parents();
}
/*!
@brief convert the object or array @a val of another specialization into
this null value without recursing
The containers whose conversion has begun are kept on an explicit stack
rather than on the call stack. Unlike @ref copy_iteratively, this builds
every container from the bottom up: all its elements are converted first,
and the container is then created from them in one go, the way the range
constructor that converts the levels above the bound does. The two object
types need not enumerate their members in the same order, so the members
could not be paired up by position anyway, and building from a range keeps
what the range constructor does with keys that become equal on conversion.
Every value is complete before it is handed on, and a container gets its
type only once it exists, so whatever throws, every value left behind can
be destroyed.
*/
template<typename BasicJsonType>
void convert_iteratively(const BasicJsonType& val)
{
using other_const_iterator = typename BasicJsonType::const_iterator;
// the containers whose conversion has begun, innermost last, each with
// its element to convert next
std::vector<std::pair<const BasicJsonType*, other_const_iterator>> pending;
// the converted elements of the pending arrays and the converted
// members of the pending objects, those of the innermost one last
convert_scratch_t elements;
copy_scratch_t members;
pending.emplace_back(&val, val.cbegin());
for (;;)
{
const BasicJsonType& container = *pending.back().first;
other_const_iterator& next = pending.back().second;
if (next != container.cend())
{
if (next->is_structured())
{
// convert its elements first; next stays where it is until
// the converted container is handed back to this one
pending.emplace_back(&*next, next->cbegin());
continue;
}
// the converting constructor does not descend into this value
if (container.is_object())
{
members.emplace_back(next.key(), *next);
}
else
{
elements.emplace_back(*next);
}
++next;
continue;
}
// all elements of the container are converted: create it
pending.pop_back();
if (pending.empty())
{
convert_level(container, elements, members);
return;
}
basic_json converted;
converted.convert_level(container, elements, members);
#if JSON_DIAGNOSTIC_POSITIONS
converted.start_position = container.start_pos();
converted.end_position = container.end_pos();
#endif
// hand it to the container it is an element of
if (pending.back().first->is_object())
{
members.emplace_back(pending.back().second.key(), std::move(converted));
}
else
{
elements.push_back(std::move(converted));
}
++pending.back().second;
}
}
/*!
@brief convert the object or array @a val of another specialization into
this null value
Converting a container converts its elements, so a value nested deeply
enough used to exhaust the call stack. The descent is bounded here as in
@ref copy_structured: the first @ref nesting_depth_limit levels are
converted by the containers' range constructors, just as they always were,
and anything below that is converted without the call stack by
@ref convert_iteratively.
@sa https://github.com/nlohmann/json/issues/5650
*/
template<typename BasicJsonType>
void convert_structured(const BasicJsonType& val)
{
const nesting_depth_guard guard;
if (JSON_HEDLEY_LIKELY(guard.okay()))
{
// every element comes back to the converting constructor
if (val.is_object())
{
using other_object_t = typename BasicJsonType::object_t;
JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
}
else
{
using other_array_t = typename BasicJsonType::array_t;
JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
}
return;
}
convert_iteratively(val);
}
/// the result of comparing two values, including values that cannot be
/// ordered at all, such as a discarded value or a NaN
@@ -1799,8 +1578,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @brief create a null object
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
basic_json(std::nullptr_t = nullptr) noexcept // NOLINT(bugprone-exception-escape)
: basic_json(value_t::null)
basic_json(std::nullptr_t = nullptr) noexcept
{
assert_invariant();
}
@@ -1831,13 +1609,49 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
end_position(val.end_pos())
#endif
{
if (val.is_structured())
using other_boolean_t = typename BasicJsonType::boolean_t;
using other_number_float_t = typename BasicJsonType::number_float_t;
using other_number_integer_t = typename BasicJsonType::number_integer_t;
using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using other_string_t = typename BasicJsonType::string_t;
using other_object_t = typename BasicJsonType::object_t;
using other_array_t = typename BasicJsonType::array_t;
using other_binary_t = typename BasicJsonType::binary_t;
switch (val.type())
{
convert_structured(val);
}
else
{
convert_leaf(val);
case value_t::boolean:
JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());
break;
case value_t::number_float:
JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());
break;
case value_t::number_integer:
JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());
break;
case value_t::number_unsigned:
JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());
break;
case value_t::string:
JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());
break;
case value_t::object:
JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
break;
case value_t::array:
JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
break;
case value_t::binary:
JSONSerializer<other_binary_t>::to_json(*this, val.template get_ref<const other_binary_t&>());
break;
case value_t::null:
*this = nullptr;
break;
case value_t::discarded:
m_data.m_type = value_t::discarded;
break;
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
JSON_ASSERT(m_data.m_type == val.type());
@@ -2128,15 +1942,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @brief move constructor
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
basic_json(basic_json&& other) noexcept
: json_base_class_t(std::forward<json_base_class_t>(other)),
m_data(std::move(other.m_data)) // cppcheck-suppress[accessForwarded] TODO check
: json_base_class_t(std::move(static_cast<json_base_class_t&>(other))),
m_data(std::move(other.m_data))
#if JSON_DIAGNOSTIC_POSITIONS
, start_position(other.start_position) // cppcheck-suppress[accessForwarded] TODO check
, end_position(other.end_position) // cppcheck-suppress[accessForwarded] TODO check
, start_position(other.start_position)
, end_position(other.end_position)
#endif
{
// check that the passed value is valid
other.assert_invariant(false); // cppcheck-suppress[accessForwarded]
other.assert_invariant(false);
// invalidate payload
other.m_data.m_type = value_t::null;
@@ -2703,7 +2517,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
@tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
@ref number_unsigned_t, or @ref number_float_t.
@ref number_unsigned_t, @ref number_float_t, or @ref binary_t.
@return pointer to the internally stored JSON value if the requested
pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
@@ -3229,7 +3043,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
}
/// @brief access specified object element via JSON Pointer with default value
/// @brief access specified object element with default value
/// @sa https://json.nlohmann.me/api/basic_json/value/
template < class ValueType, class KeyType, class ReturnType = typename value_return_type<ValueType>::type,
detail::enable_if_t <
@@ -3387,21 +3201,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_THROW(invalid_iterator::create(205, "iterator out of range", this));
}
if (is_string())
{
AllocatorType<string_t> alloc;
std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.string);
std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.string, 1);
m_data.m_value.string = nullptr;
}
else if (is_binary())
{
AllocatorType<binary_t> alloc;
std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.binary);
std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.binary, 1);
m_data.m_value.binary = nullptr;
}
m_data.m_value.destroy(m_data.m_type);
m_data.m_value = {};
m_data.m_type = value_t::null;
assert_invariant();
break;
@@ -3453,27 +3254,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
case value_t::string:
case value_t::binary:
{
if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin()
|| !last.m_it.primitive_iterator.is_end()))
if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
|| !last.m_it.primitive_iterator.is_end()))
{
JSON_THROW(invalid_iterator::create(204, "iterators out of range", this));
}
if (is_string())
{
AllocatorType<string_t> alloc;
std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.string);
std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.string, 1);
m_data.m_value.string = nullptr;
}
else if (is_binary())
{
AllocatorType<binary_t> alloc;
std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.binary);
std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.binary, 1);
m_data.m_value.binary = nullptr;
}
m_data.m_value.destroy(m_data.m_type);
m_data.m_value = {};
m_data.m_type = value_t::null;
assert_invariant();
break;
@@ -4255,7 +4043,22 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/insert/
iterator insert(const_iterator pos, basic_json&& val) // NOLINT(performance-unnecessary-value-param)
{
return insert(std::move(pos), val);
// insert only works for arrays
if (JSON_HEDLEY_LIKELY(is_array()))
{
// check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
}
// moving into a local first keeps this safe even if val aliases
// an element of this array
basic_json tmp(std::move(val));
return insert_iterator(pos, std::move(tmp));
}
JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
}
/// @brief inserts copies of element into array
@@ -4596,7 +4399,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/swap/
void swap(binary_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap)
{
// swap only works for strings
// swap only works for binary values
if (JSON_HEDLEY_LIKELY(is_binary()))
{
using std::swap;
@@ -4612,7 +4415,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/swap/
void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape)
{
// swap only works for strings
// swap only works for binary values
if (JSON_HEDLEY_LIKELY(is_binary()))
{
using std::swap;
@@ -5092,7 +4895,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool ignore_trailing_commas = false)
{
basic_json result;
parser(detail::input_adapter(std::forward<InputType>(i)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved,accessForwarded]
auto p = parser(detail::input_adapter(std::forward<InputType>(i)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas);
p.parse(true, result);
return result;
}
@@ -5109,7 +4913,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool ignore_trailing_commas = false)
{
basic_json result;
parser(detail::input_adapter(std::move(first), std::move(last)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved]
auto p = parser(detail::input_adapter(std::move(first), std::move(last)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas);
p.parse(true, result);
return result;
}
@@ -5122,7 +4927,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool ignore_trailing_commas = false)
{
basic_json result;
parser(i.get(), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved]
auto p = parser(i.get(), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas);
p.parse(true, result);
return result;
}
@@ -5330,6 +5136,31 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
#endif
private:
/*!
@brief shared implementation of the binary from_cbor/from_msgpack/
from_ubjson/from_bjdata/from_bon8/from_bson overloads
Building the @ref binary_reader as a named local, rather than as a
temporary that @ref detail::json_sax_dom_parser::parse is called on in
the same expression, avoids a false-positive cppcheck accessMoved
warning in each of the 16 callers.
*/
template<typename InputAdapterType>
static basic_json from_binary_impl(InputAdapterType ia, const input_format_t format,
const bool strict, const bool allow_exceptions,
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
{
basic_json result;
detail::json_sax_dom_parser<basic_json, InputAdapterType> sdp(result, allow_exceptions);
binary_reader<InputAdapterType> reader(std::move(ia), format);
if (!reader.sax_parse(format, &sdp, strict, tag_handler))
{
result = value_t::discarded;
}
return result;
}
//////////////////////////////////////////
// binary serialization/deserialization //
//////////////////////////////////////////
@@ -5502,14 +5333,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool allow_exceptions = true,
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
{
basic_json result;
auto ia = detail::input_adapter(std::forward<InputType>(i));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::forward<InputType>(i)), input_format_t::cbor, strict, allow_exceptions, tag_handler);
}
/// @brief create a JSON value from an input in CBOR format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
@@ -5522,14 +5346,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool allow_exceptions = true,
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
{
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::cbor, strict, allow_exceptions, tag_handler);
}
template<typename T>
@@ -5550,15 +5367,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool allow_exceptions = true,
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
{
basic_json result;
auto ia = i.get();
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(i.get(), input_format_t::cbor, strict, allow_exceptions, tag_handler);
}
/// @brief create a JSON value from an input in MessagePack format
@@ -5569,14 +5378,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = detail::input_adapter(std::forward<InputType>(i));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::forward<InputType>(i)), input_format_t::msgpack, strict, allow_exceptions);
}
/// @brief create a JSON value from an input in MessagePack format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
@@ -5588,14 +5390,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::msgpack, strict, allow_exceptions);
}
template<typename T>
@@ -5614,15 +5409,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = i.get();
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(i.get(), input_format_t::msgpack, strict, allow_exceptions);
}
/// @brief create a JSON value from an input in UBJSON format
@@ -5633,14 +5420,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = detail::input_adapter(std::forward<InputType>(i));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::forward<InputType>(i)), input_format_t::ubjson, strict, allow_exceptions);
}
/// @brief create a JSON value from an input in UBJSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
@@ -5652,14 +5432,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::ubjson, strict, allow_exceptions);
}
template<typename T>
@@ -5678,15 +5451,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = i.get();
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(i.get(), input_format_t::ubjson, strict, allow_exceptions);
}
/// @brief create a JSON value from an input in BJData format
@@ -5697,14 +5462,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = detail::input_adapter(std::forward<InputType>(i));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::forward<InputType>(i)), input_format_t::bjdata, strict, allow_exceptions);
}
/// @brief create a JSON value from an input in BJData format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
@@ -5716,14 +5474,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::bjdata, strict, allow_exceptions);
}
/// @brief create a JSON value from an input in BON8 format
@@ -5734,14 +5485,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = detail::input_adapter(std::forward<InputType>(i));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bon8).sax_parse(input_format_t::bon8, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::forward<InputType>(i)), input_format_t::bon8, strict, allow_exceptions);
}
/// @brief create a JSON value from an input in BON8 format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
@@ -5753,14 +5497,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bon8).sax_parse(input_format_t::bon8, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::bon8, strict, allow_exceptions);
}
/// @brief create a JSON value from an input in BSON format
@@ -5771,14 +5508,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = detail::input_adapter(std::forward<InputType>(i));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::forward<InputType>(i)), input_format_t::bson, strict, allow_exceptions);
}
/// @brief create a JSON value from an input in BSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
@@ -5790,14 +5520,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::bson, strict, allow_exceptions);
}
template<typename T>
@@ -5816,15 +5539,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = i.get();
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(i.get(), input_format_t::bson, strict, allow_exceptions);
}
/// @}
+138 -424
View File
@@ -5029,7 +5029,6 @@ inline std::size_t concat_length(const char /*c*/, const Args& ... rest)
template<typename... Args>
inline std::size_t concat_length(const char* cstr, const Args& ... rest)
{
// cppcheck-suppress ignoredReturnValue
return ::strlen(cstr) + concat_length(rest...);
}
@@ -26191,11 +26190,12 @@ struct is_std_optional<std::optional<T>> : std::true_type {};
@brief a class to store JSON values
@internal
@invariant The member variables @a m_value and @a m_type have the following
relationship:
- If `m_type == value_t::object`, then `m_value.object != nullptr`.
- If `m_type == value_t::array`, then `m_value.array != nullptr`.
- If `m_type == value_t::string`, then `m_value.string != nullptr`.
@invariant The member variables @a m_data.m_value and @a m_data.m_type have
the following relationship:
- If `m_data.m_type == value_t::object`, then `m_data.m_value.object != nullptr`.
- If `m_data.m_type == value_t::array`, then `m_data.m_value.array != nullptr`.
- If `m_data.m_type == value_t::string`, then `m_data.m_value.string != nullptr`.
- If `m_data.m_type == value_t::binary`, then `m_data.m_value.binary != nullptr`.
The invariants are checked by member function assert_invariant().
@note ObjectType trick from https://stackoverflow.com/a/9860911
@@ -26255,18 +26255,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
private:
using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t;
template<typename BasicJsonType>
using internal_iterator = ::nlohmann::detail::internal_iterator<BasicJsonType>;
template<typename BasicJsonType>
using iter_impl = ::nlohmann::detail::iter_impl<BasicJsonType>;
template<typename Iterator>
using iteration_proxy = ::nlohmann::detail::iteration_proxy<Iterator>;
template<typename Base> using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator<Base>;
template<typename CharType>
using output_adapter_t = ::nlohmann::detail::output_adapter_t<CharType>;
template<typename InputType>
using binary_reader = ::nlohmann::detail::binary_reader<basic_json, InputType>;
template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>;
@@ -26407,8 +26401,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
std::to_string(__GNUC_PATCHLEVEL__))
}
};
#elif defined(__HP_cc) || defined(__HP_aCC)
result["compiler"] = "hp"
#elif defined(__HP_aCC)
result["compiler"] = {{"family", "hp"}, {"version", __HP_aCC}};
#elif defined(__IBMCPP__)
result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}};
#elif defined(_MSC_VER)
@@ -26550,9 +26544,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
binary | binary | pointer to @ref binary_t
null | null | *no value is stored*
@note Variable-length types (objects, arrays, and strings) are stored as
pointers. The size of the union should not exceed 64 bits if the default
value types are used.
@note Variable-length types (objects, arrays, strings, and binary
values) are stored as pointers. The size of the union should not exceed
64 bits if the default value types are used.
@since version 1.0.0
*/
@@ -26804,7 +26798,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
end of every constructor to make sure that created objects respect the
invariant. Furthermore, it has to be called each time the type of a JSON
value is changed, because the invariant expresses a relationship between
@a m_type and @a m_value.
@a m_data.m_type and @a m_data.m_value.
Furthermore, the parent relation is checked for arrays and objects: If
@a check_parents true and the value is an array or object, then the
@@ -26825,7 +26819,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
#if JSON_DIAGNOSTICS
JSON_TRY
{
// cppcheck-suppress assertWithSideEffect
JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j)
{
return j.m_parent == this;
@@ -26989,11 +26982,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/*!
@brief how many levels the operation going on in this thread has descended into
Copying a value, converting one from another specialization, and comparing
two values share this count. The library never nests one of them inside
another - none of them does either of the other two on the way - and where
user code nests them anyway, sharing the count only ends a descent sooner
than it had to, which costs a little speed and is never wrong.
Copying a value and comparing two values share this count. The library never
nests one inside the other - copying a value does not compare one, and
comparing two values does not copy them - and where user code nests them
anyway, sharing the count only ends a descent sooner than it had to, which
costs a little speed and is never wrong.
A byte is enough: the count never exceeds the limit by more than the single
level that notices the limit has been reached.
@@ -27349,221 +27342,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
copy_iteratively(src);
}
/*!
@brief convert the value @a val of another specialization into this null
value; @a val must be neither an object nor an array
Converting such a value never descends, so both ways of converting an
object or an array (@ref convert_structured) leave their elements of this
kind to the converting constructor, which leaves them to this.
*/
template<typename BasicJsonType>
void convert_leaf(const BasicJsonType& val)
{
using other_boolean_t = typename BasicJsonType::boolean_t;
using other_number_float_t = typename BasicJsonType::number_float_t;
using other_number_integer_t = typename BasicJsonType::number_integer_t;
using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using other_string_t = typename BasicJsonType::string_t;
using other_binary_t = typename BasicJsonType::binary_t;
switch (val.type())
{
case value_t::boolean:
JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());
break;
case value_t::number_float:
JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());
break;
case value_t::number_integer:
JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());
break;
case value_t::number_unsigned:
JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());
break;
case value_t::string:
JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());
break;
case value_t::binary:
JSONSerializer<other_binary_t>::to_json(*this, val.template get_ref<const other_binary_t&>());
break;
case value_t::null:
// this value is null already; assigning null to it would also
// reset its positions (JSON_DIAGNOSTIC_POSITIONS)
break;
case value_t::discarded:
m_data.m_type = value_t::discarded;
break;
case value_t::object: // LCOV_EXCL_LINE
case value_t::array: // LCOV_EXCL_LINE
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
}
/// scratch space for the converted elements of the arrays that
/// @ref convert_iteratively has yet to create
using convert_scratch_t = std::vector<basic_json, AllocatorType<basic_json>>;
/*!
@brief create the object or array @a val converted into this null value
Its converted elements are the last `val.size()` entries of @a elements (an
array) or of @a members (an object); they are moved into the container in
one go and then removed.
*/
template<typename BasicJsonType>
void convert_level(const BasicJsonType& val, convert_scratch_t& elements, copy_scratch_t& members)
{
if (val.is_object())
{
const auto first = members.end() - static_cast<typename copy_scratch_t::difference_type>(val.size());
m_data.m_value.object = create<object_t>(std::make_move_iterator(first),
std::make_move_iterator(members.end()));
// only now that the object exists may this stop being a null value
m_data.m_type = value_t::object;
members.erase(first, members.end());
}
else
{
const auto first = elements.end() - static_cast<typename convert_scratch_t::difference_type>(val.size());
m_data.m_value.array = create<array_t>(std::make_move_iterator(first),
std::make_move_iterator(elements.end()));
// only now that the array exists may this stop being a null value
m_data.m_type = value_t::array;
elements.erase(first, elements.end());
}
set_parents();
}
/*!
@brief convert the object or array @a val of another specialization into
this null value without recursing
The containers whose conversion has begun are kept on an explicit stack
rather than on the call stack. Unlike @ref copy_iteratively, this builds
every container from the bottom up: all its elements are converted first,
and the container is then created from them in one go, the way the range
constructor that converts the levels above the bound does. The two object
types need not enumerate their members in the same order, so the members
could not be paired up by position anyway, and building from a range keeps
what the range constructor does with keys that become equal on conversion.
Every value is complete before it is handed on, and a container gets its
type only once it exists, so whatever throws, every value left behind can
be destroyed.
*/
template<typename BasicJsonType>
void convert_iteratively(const BasicJsonType& val)
{
using other_const_iterator = typename BasicJsonType::const_iterator;
// the containers whose conversion has begun, innermost last, each with
// its element to convert next
std::vector<std::pair<const BasicJsonType*, other_const_iterator>> pending;
// the converted elements of the pending arrays and the converted
// members of the pending objects, those of the innermost one last
convert_scratch_t elements;
copy_scratch_t members;
pending.emplace_back(&val, val.cbegin());
for (;;)
{
const BasicJsonType& container = *pending.back().first;
other_const_iterator& next = pending.back().second;
if (next != container.cend())
{
if (next->is_structured())
{
// convert its elements first; next stays where it is until
// the converted container is handed back to this one
pending.emplace_back(&*next, next->cbegin());
continue;
}
// the converting constructor does not descend into this value
if (container.is_object())
{
members.emplace_back(next.key(), *next);
}
else
{
elements.emplace_back(*next);
}
++next;
continue;
}
// all elements of the container are converted: create it
pending.pop_back();
if (pending.empty())
{
convert_level(container, elements, members);
return;
}
basic_json converted;
converted.convert_level(container, elements, members);
#if JSON_DIAGNOSTIC_POSITIONS
converted.start_position = container.start_pos();
converted.end_position = container.end_pos();
#endif
// hand it to the container it is an element of
if (pending.back().first->is_object())
{
members.emplace_back(pending.back().second.key(), std::move(converted));
}
else
{
elements.push_back(std::move(converted));
}
++pending.back().second;
}
}
/*!
@brief convert the object or array @a val of another specialization into
this null value
Converting a container converts its elements, so a value nested deeply
enough used to exhaust the call stack. The descent is bounded here as in
@ref copy_structured: the first @ref nesting_depth_limit levels are
converted by the containers' range constructors, just as they always were,
and anything below that is converted without the call stack by
@ref convert_iteratively.
@sa https://github.com/nlohmann/json/issues/5650
*/
template<typename BasicJsonType>
void convert_structured(const BasicJsonType& val)
{
const nesting_depth_guard guard;
if (JSON_HEDLEY_LIKELY(guard.okay()))
{
// every element comes back to the converting constructor
if (val.is_object())
{
using other_object_t = typename BasicJsonType::object_t;
JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
}
else
{
using other_array_t = typename BasicJsonType::array_t;
JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
}
return;
}
convert_iteratively(val);
}
/// the result of comparing two values, including values that cannot be
/// ordered at all, such as a discarded value or a NaN
@@ -27880,8 +27658,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @brief create a null object
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
basic_json(std::nullptr_t = nullptr) noexcept // NOLINT(bugprone-exception-escape)
: basic_json(value_t::null)
basic_json(std::nullptr_t = nullptr) noexcept
{
assert_invariant();
}
@@ -27912,13 +27689,49 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
end_position(val.end_pos())
#endif
{
if (val.is_structured())
using other_boolean_t = typename BasicJsonType::boolean_t;
using other_number_float_t = typename BasicJsonType::number_float_t;
using other_number_integer_t = typename BasicJsonType::number_integer_t;
using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using other_string_t = typename BasicJsonType::string_t;
using other_object_t = typename BasicJsonType::object_t;
using other_array_t = typename BasicJsonType::array_t;
using other_binary_t = typename BasicJsonType::binary_t;
switch (val.type())
{
convert_structured(val);
}
else
{
convert_leaf(val);
case value_t::boolean:
JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());
break;
case value_t::number_float:
JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());
break;
case value_t::number_integer:
JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());
break;
case value_t::number_unsigned:
JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());
break;
case value_t::string:
JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());
break;
case value_t::object:
JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
break;
case value_t::array:
JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
break;
case value_t::binary:
JSONSerializer<other_binary_t>::to_json(*this, val.template get_ref<const other_binary_t&>());
break;
case value_t::null:
*this = nullptr;
break;
case value_t::discarded:
m_data.m_type = value_t::discarded;
break;
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
JSON_ASSERT(m_data.m_type == val.type());
@@ -28209,15 +28022,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @brief move constructor
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
basic_json(basic_json&& other) noexcept
: json_base_class_t(std::forward<json_base_class_t>(other)),
m_data(std::move(other.m_data)) // cppcheck-suppress[accessForwarded] TODO check
: json_base_class_t(std::move(static_cast<json_base_class_t&>(other))),
m_data(std::move(other.m_data))
#if JSON_DIAGNOSTIC_POSITIONS
, start_position(other.start_position) // cppcheck-suppress[accessForwarded] TODO check
, end_position(other.end_position) // cppcheck-suppress[accessForwarded] TODO check
, start_position(other.start_position)
, end_position(other.end_position)
#endif
{
// check that the passed value is valid
other.assert_invariant(false); // cppcheck-suppress[accessForwarded]
other.assert_invariant(false);
// invalidate payload
other.m_data.m_type = value_t::null;
@@ -28784,7 +28597,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
@tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
@ref number_unsigned_t, or @ref number_float_t.
@ref number_unsigned_t, @ref number_float_t, or @ref binary_t.
@return pointer to the internally stored JSON value if the requested
pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
@@ -29310,7 +29123,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
}
/// @brief access specified object element via JSON Pointer with default value
/// @brief access specified object element with default value
/// @sa https://json.nlohmann.me/api/basic_json/value/
template < class ValueType, class KeyType, class ReturnType = typename value_return_type<ValueType>::type,
detail::enable_if_t <
@@ -29468,21 +29281,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_THROW(invalid_iterator::create(205, "iterator out of range", this));
}
if (is_string())
{
AllocatorType<string_t> alloc;
std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.string);
std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.string, 1);
m_data.m_value.string = nullptr;
}
else if (is_binary())
{
AllocatorType<binary_t> alloc;
std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.binary);
std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.binary, 1);
m_data.m_value.binary = nullptr;
}
m_data.m_value.destroy(m_data.m_type);
m_data.m_value = {};
m_data.m_type = value_t::null;
assert_invariant();
break;
@@ -29534,27 +29334,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
case value_t::string:
case value_t::binary:
{
if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin()
|| !last.m_it.primitive_iterator.is_end()))
if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
|| !last.m_it.primitive_iterator.is_end()))
{
JSON_THROW(invalid_iterator::create(204, "iterators out of range", this));
}
if (is_string())
{
AllocatorType<string_t> alloc;
std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.string);
std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.string, 1);
m_data.m_value.string = nullptr;
}
else if (is_binary())
{
AllocatorType<binary_t> alloc;
std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.binary);
std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.binary, 1);
m_data.m_value.binary = nullptr;
}
m_data.m_value.destroy(m_data.m_type);
m_data.m_value = {};
m_data.m_type = value_t::null;
assert_invariant();
break;
@@ -30336,7 +30123,22 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/insert/
iterator insert(const_iterator pos, basic_json&& val) // NOLINT(performance-unnecessary-value-param)
{
return insert(std::move(pos), val);
// insert only works for arrays
if (JSON_HEDLEY_LIKELY(is_array()))
{
// check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
}
// moving into a local first keeps this safe even if val aliases
// an element of this array
basic_json tmp(std::move(val));
return insert_iterator(pos, std::move(tmp));
}
JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
}
/// @brief inserts copies of element into array
@@ -30677,7 +30479,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/swap/
void swap(binary_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap)
{
// swap only works for strings
// swap only works for binary values
if (JSON_HEDLEY_LIKELY(is_binary()))
{
using std::swap;
@@ -30693,7 +30495,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/swap/
void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape)
{
// swap only works for strings
// swap only works for binary values
if (JSON_HEDLEY_LIKELY(is_binary()))
{
using std::swap;
@@ -31173,7 +30975,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool ignore_trailing_commas = false)
{
basic_json result;
parser(detail::input_adapter(std::forward<InputType>(i)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved,accessForwarded]
auto p = parser(detail::input_adapter(std::forward<InputType>(i)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas);
p.parse(true, result);
return result;
}
@@ -31190,7 +30993,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool ignore_trailing_commas = false)
{
basic_json result;
parser(detail::input_adapter(std::move(first), std::move(last)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved]
auto p = parser(detail::input_adapter(std::move(first), std::move(last)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas);
p.parse(true, result);
return result;
}
@@ -31203,7 +31007,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool ignore_trailing_commas = false)
{
basic_json result;
parser(i.get(), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved]
auto p = parser(i.get(), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas);
p.parse(true, result);
return result;
}
@@ -31411,6 +31216,31 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
#endif
private:
/*!
@brief shared implementation of the binary from_cbor/from_msgpack/
from_ubjson/from_bjdata/from_bon8/from_bson overloads
Building the @ref binary_reader as a named local, rather than as a
temporary that @ref detail::json_sax_dom_parser::parse is called on in
the same expression, avoids a false-positive cppcheck accessMoved
warning in each of the 16 callers.
*/
template<typename InputAdapterType>
static basic_json from_binary_impl(InputAdapterType ia, const input_format_t format,
const bool strict, const bool allow_exceptions,
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
{
basic_json result;
detail::json_sax_dom_parser<basic_json, InputAdapterType> sdp(result, allow_exceptions);
binary_reader<InputAdapterType> reader(std::move(ia), format);
if (!reader.sax_parse(format, &sdp, strict, tag_handler))
{
result = value_t::discarded;
}
return result;
}
//////////////////////////////////////////
// binary serialization/deserialization //
//////////////////////////////////////////
@@ -31583,14 +31413,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool allow_exceptions = true,
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
{
basic_json result;
auto ia = detail::input_adapter(std::forward<InputType>(i));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::forward<InputType>(i)), input_format_t::cbor, strict, allow_exceptions, tag_handler);
}
/// @brief create a JSON value from an input in CBOR format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
@@ -31603,14 +31426,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool allow_exceptions = true,
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
{
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::cbor, strict, allow_exceptions, tag_handler);
}
template<typename T>
@@ -31631,15 +31447,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool allow_exceptions = true,
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
{
basic_json result;
auto ia = i.get();
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(i.get(), input_format_t::cbor, strict, allow_exceptions, tag_handler);
}
/// @brief create a JSON value from an input in MessagePack format
@@ -31650,14 +31458,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = detail::input_adapter(std::forward<InputType>(i));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::forward<InputType>(i)), input_format_t::msgpack, strict, allow_exceptions);
}
/// @brief create a JSON value from an input in MessagePack format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
@@ -31669,14 +31470,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::msgpack, strict, allow_exceptions);
}
template<typename T>
@@ -31695,15 +31489,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = i.get();
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(i.get(), input_format_t::msgpack, strict, allow_exceptions);
}
/// @brief create a JSON value from an input in UBJSON format
@@ -31714,14 +31500,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = detail::input_adapter(std::forward<InputType>(i));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::forward<InputType>(i)), input_format_t::ubjson, strict, allow_exceptions);
}
/// @brief create a JSON value from an input in UBJSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
@@ -31733,14 +31512,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::ubjson, strict, allow_exceptions);
}
template<typename T>
@@ -31759,15 +31531,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = i.get();
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(i.get(), input_format_t::ubjson, strict, allow_exceptions);
}
/// @brief create a JSON value from an input in BJData format
@@ -31778,14 +31542,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = detail::input_adapter(std::forward<InputType>(i));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::forward<InputType>(i)), input_format_t::bjdata, strict, allow_exceptions);
}
/// @brief create a JSON value from an input in BJData format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
@@ -31797,14 +31554,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::bjdata, strict, allow_exceptions);
}
/// @brief create a JSON value from an input in BON8 format
@@ -31815,14 +31565,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = detail::input_adapter(std::forward<InputType>(i));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bon8).sax_parse(input_format_t::bon8, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::forward<InputType>(i)), input_format_t::bon8, strict, allow_exceptions);
}
/// @brief create a JSON value from an input in BON8 format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
@@ -31834,14 +31577,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bon8).sax_parse(input_format_t::bon8, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::bon8, strict, allow_exceptions);
}
/// @brief create a JSON value from an input in BSON format
@@ -31852,14 +31588,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = detail::input_adapter(std::forward<InputType>(i));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::forward<InputType>(i)), input_format_t::bson, strict, allow_exceptions);
}
/// @brief create a JSON value from an input in BSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
@@ -31871,14 +31600,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = detail::input_adapter(std::move(first), std::move(last));
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::bson, strict, allow_exceptions);
}
template<typename T>
@@ -31897,15 +31619,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool strict = true,
const bool allow_exceptions = true)
{
basic_json result;
auto ia = i.get();
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict)) // cppcheck-suppress[accessMoved]
{
result = value_t::discarded;
}
return result;
return from_binary_impl(i.get(), input_format_t::bson, strict, allow_exceptions);
}
/// @}
-71
View File
@@ -352,77 +352,6 @@ TEST_CASE("deep copy uses the provided allocator")
CHECK(copy == j);
}
namespace
{
// the number of constructions countdown_allocator lets happen, including the
// one that fails; 0 means none ever fails
std::size_t constructions_until_failure = 0;
template<class T>
struct countdown_allocator : std::allocator<T>
{
using std::allocator<T>::allocator;
template<class U, class... Args>
void construct(U* p, Args&& ... args)
{
if (constructions_until_failure != 0 && --constructions_until_failure == 0)
{
throw std::bad_alloc();
}
::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
}
template <class U>
struct rebind
{
using other = countdown_allocator<U>;
};
};
} // namespace
TEST_CASE("converting a deeply nested value from another specialization fails cleanly (#5650)")
{
using countdown_json = nlohmann::basic_json<std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
countdown_allocator>;
// deeper than the 128 levels the converting constructor descends into, so
// that failures land on both sides of the bound - or, built with
// JSON_NO_THREAD_LOCAL, all in the iterative conversion
json j = {1, "two", {{"three", 3}}};
for (std::size_t i = 0; i < 150; ++i)
{
j = json{{"a", json::array({j, "sibling"})}};
}
// Fail every construction in turn. Each failure has to reach the caller,
// and everything built until then has to be destroyed cleanly.
std::size_t failures = 0;
for (std::size_t n = 1;; ++n)
{
constructions_until_failure = n;
try
{
const countdown_json converted = j;
constructions_until_failure = 0;
CHECK(converted.dump() == j.dump());
break;
}
catch (const std::bad_alloc&)
{
++failures;
}
}
CHECK(failures > 0);
}
namespace
{
template<class T>
-52
View File
@@ -141,58 +141,6 @@ TEST_CASE("Better diagnostics with positions")
check_objects(300);
}
SECTION("converting keeps the positions of nested values (#5650)")
{
// Values nested deeper than the converting constructor's descent bound
// are converted without the call stack, on a path that has to carry the
// positions of every value over itself. Objects and arrays take turns,
// and the innermost value is null, which used to lose its positions.
const auto check_conversion = [](std::size_t depth)
{
CAPTURE(depth)
std::string text;
std::string closing;
for (std::size_t i = 0; i < depth; ++i)
{
text += (i % 2 == 0) ? "[12, " : R"({"b":1, "a":)";
closing += (i % 2 == 0) ? ']' : '}';
}
text += "null";
text.append(closing.rbegin(), closing.rend());
const json original = json::parse(text);
const nlohmann::ordered_json converted = original;
const json* o = &original;
const nlohmann::ordered_json* c = &converted;
for (std::size_t level = 0; level <= depth; ++level)
{
CAPTURE(level)
REQUIRE(c->start_pos() == o->start_pos());
REQUIRE(c->end_pos() == o->end_pos());
if (level < depth)
{
// the number beside the value nested next
const json& o_number = o->is_object() ? o->at("b") : o->at(0);
const nlohmann::ordered_json& c_number = c->is_object() ? c->at("b") : c->at(0);
REQUIRE(c_number.start_pos() == o_number.start_pos());
REQUIRE(c_number.end_pos() == o_number.end_pos());
o = o->is_object() ? &o->at("a") : &o->at(1);
c = c->is_object() ? &c->at("a") : &c->at(1);
}
}
};
check_conversion(1);
check_conversion(127);
check_conversion(128);
check_conversion(129);
check_conversion(300);
}
SECTION("JSON patch add to primitive parent (#4292)")
{
// the JSON Patch "add" target /foo/bar/baz has a string parent
-30
View File
@@ -331,36 +331,6 @@ TEST_CASE("Regression tests for extended diagnostics")
}
}
SECTION("Regression test for issue #5650 - converting keeps the parents of nested values")
{
// A value nested deeper than the converting constructor's descent bound
// is converted without the call stack. Every container that path creates
// has to have the parents of its children set, or the JSON Pointer in the
// diagnostic is cut short. Objects and arrays take turns.
const std::size_t pairs = 150;
json j = "not a number";
std::string pointer;
for (std::size_t i = 0; i < pairs; ++i)
{
j = json{{"a", json::array({j})}};
pointer += "/a/0";
}
const nlohmann::ordered_json converted = j;
const nlohmann::ordered_json* inner = &converted;
for (std::size_t i = 0; i < pairs; ++i)
{
inner = &inner->at("a").at(0);
}
std::string const expected = "[json.exception.type_error.302] (" + pointer + ") type must be number, but is string";
int i = 0;
CHECK_THROWS_WITH_AS(i = inner->get<int>(), expected.c_str(), nlohmann::ordered_json::type_error);
CHECK(i == 0);
}
SECTION("Regression test - swap(array_t&)/swap(object_t&) must update JSON_DIAGNOSTICS parent pointers")
{
// swap(array_t&)
-128
View File
@@ -13,7 +13,6 @@ using nlohmann::json;
#include <algorithm>
#include <string>
#include <vector>
TEST_CASE("tests on very large JSONs")
{
@@ -54,24 +53,6 @@ const json* innermost_value(const json& j, std::size_t& depth)
return current;
}
// The text of a value nested depth levels deep around the number 0. Level i is
// an array if pattern[i % pattern.size()] is '[', and otherwise an object with
// the single member "a", which every object type enumerates in the same order.
std::string nested_text(std::size_t depth, const std::string& pattern)
{
std::string text;
std::string closing;
for (std::size_t i = 0; i < depth; ++i)
{
const bool array = pattern[i % pattern.size()] == '[';
text += array ? "[" : "{\"a\":";
closing += array ? ']' : '}';
}
text += '0';
text.append(closing.rbegin(), closing.rend());
return text;
}
} // namespace
TEST_CASE("tests on deeply nested JSONs")
@@ -243,114 +224,5 @@ TEST_CASE("tests on deeply nested JSONs")
CHECK(*innermost_value(j, unused) == 0);
}
}
SECTION("issue #5650 - stack overflow converting between specializations")
{
const std::vector<std::string> patterns = {"[", "{", "[{"};
SECTION("json to ordered_json")
{
for (const auto& pattern : patterns)
{
CAPTURE(pattern);
const std::string text = nested_text(depth, pattern);
const json j = json::parse(text);
const nlohmann::ordered_json converted = j;
CHECK(converted.dump() == text);
}
}
SECTION("ordered_json to json")
{
for (const auto& pattern : patterns)
{
CAPTURE(pattern);
const std::string text = nested_text(depth, pattern);
const nlohmann::ordered_json o = nlohmann::ordered_json::parse(text);
const json converted = o;
CHECK(converted.dump() == text);
}
}
SECTION("get<ordered_json>()")
{
for (const auto& pattern : patterns)
{
CAPTURE(pattern);
const std::string text = nested_text(depth, pattern);
const json j = json::parse(text);
CHECK(j.get<nlohmann::ordered_json>().dump() == text);
}
}
SECTION("depths around the bound of the recursive descent")
{
for (std::size_t d = 1; d <= 300; ++d)
{
CAPTURE(d);
for (const auto& pattern : patterns)
{
CAPTURE(pattern);
const std::string text = nested_text(d, pattern);
const json j = json::parse(text);
const nlohmann::ordered_json converted = j;
CHECK(converted.dump() == text);
const json back = converted;
CHECK(back.dump() == text);
}
}
}
SECTION("values below the bound are converted as values above it")
{
// Bury a value below the bound, where it is converted without the
// call stack, and compare it with the same value converted on its
// own by the containers' range constructors. Its objects have
// members that the two object types enumerate in different orders.
const auto bury = [](nlohmann::ordered_json value)
{
for (std::size_t i = 0; i < 200; ++i)
{
value = nlohmann::ordered_json::array({std::move(value)});
}
return value;
};
const auto dig = [](const json & value)
{
const json* current = &value;
for (std::size_t i = 0; i < 200; ++i)
{
current = &current->at(0);
}
return current;
};
nlohmann::ordered_json value = nlohmann::ordered_json::object();
value["z"] = {1, -2, 3U, 4.5, true, nullptr, "six", nlohmann::ordered_json::binary({7, 8}, 9),
nlohmann::ordered_json::binary({10}), nlohmann::ordered_json::array(), nlohmann::ordered_json::object()
};
value["y"] = {{"x", {{"w", 1}, {"v", 2}}}, {"u", {3, {{"t", 4}, {"s", 5}}}}};
value["r"] = nlohmann::ordered_json::array({nlohmann::ordered_json(nlohmann::ordered_json::value_t::discarded)});
const json converted_above = value;
const json buried = bury(value);
const json& converted_below = *dig(buried);
CHECK(converted_below.dump() == converted_above.dump());
CHECK(converted_below.at("z").at(7).get_binary().subtype() == 9);
CHECK_FALSE(converted_below.at("z").at(8).get_binary().has_subtype());
CHECK(converted_below.at("r").at(0).is_discarded());
// a discarded value is never equal to anything, so compare the rest
value.erase("r");
const json without_discarded_above = value;
const json without_discarded_buried = bury(value);
CHECK(*dig(without_discarded_buried) == without_discarded_above);
}
}
}
+43
View File
@@ -618,6 +618,49 @@ TEST_CASE("modifiers")
}
}
SECTION("rvalue at position moves rather than copies")
{
// regression test: insert(pos, basic_json&&) used to forward to
// insert(pos, const basic_json&) because the named rvalue
// reference parameter is itself an lvalue, so it always
// deep-copied its argument instead of moving it
json j_big = std::string(1000, 'x');
const auto* const original_buffer = j_big.get_ref<const std::string&>().data();
auto it = j_array.insert(j_array.begin(), std::move(j_big));
CHECK(j_array.size() == 5);
CHECK(*it == json(std::string(1000, 'x')));
CHECK((*it).get_ref<const std::string&>().data() == original_buffer);
// the moved-from value is null, the same as after push_back(&&)
CHECK(j_big.is_null()); // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved)
}
SECTION("self-aliasing insertion")
{
SECTION("without reallocation")
{
json j_self = {1, 2, 3, 4};
j_self.get_ref<json::array_t&>().reserve(j_self.size() + 1);
auto it = j_self.insert(j_self.begin(), std::move(j_self[1]));
CHECK(j_self.size() == 5);
CHECK(*it == json(2));
CHECK(j_self == json({2, 1, nullptr, 3, 4}));
}
SECTION("with reallocation")
{
json j_self = {1, 2, 3, 4};
j_self.get_ref<json::array_t&>().shrink_to_fit();
auto it = j_self.insert(j_self.begin(), std::move(j_self[1]));
CHECK(j_self.size() == 5);
CHECK(*it == json(2));
CHECK(j_self == json({2, 1, nullptr, 3, 4}));
}
}
SECTION("copies at position")
{
SECTION("insert before begin()")