mirror of
https://github.com/nlohmann/json.git
synced 2026-09-30 11:40:30 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af394fca9c |
@@ -195,7 +195,5 @@ Strong exception safety: if an exception occurs, the original value stays intact
|
||||
1. Added in version 1.0.0.
|
||||
2. Added in version 1.0.0.
|
||||
3. Added in version 1.0.0.
|
||||
4. Added in version 1.0.0. Fixed in version 3.13.0 to copy the values before inserting; before, an `ilist` that
|
||||
referred to elements of the array being inserted into could insert wrong values, because the range insert could
|
||||
move from or shift an element before it was copied.
|
||||
4. Added in version 1.0.0.
|
||||
5. Added in version 3.0.0.
|
||||
|
||||
@@ -19,11 +19,17 @@ namespace detail
|
||||
/*!
|
||||
@brief the number of nesting levels an operation recurses into
|
||||
|
||||
Operations that walk a value (serializing, hashing, merging, ...) recurse once
|
||||
Operations that walk a value (copying, comparing, serializing, hashing, merging,
|
||||
...) recurse once
|
||||
per nesting level, which is fastest, but a value nested deeply enough would
|
||||
exhaust the call stack. So they recurse only this many levels deep and finish
|
||||
whatever lies below with an explicit stack. All of them share this limit.
|
||||
|
||||
Most of them pass the depth down as an argument. The copy constructor and the
|
||||
comparison operators cannot, as their signatures are fixed, so they count it
|
||||
in basic_json::nesting_depth() instead, a byte per thread; the limit must
|
||||
therefore stay below 255.
|
||||
|
||||
@sa https://github.com/nlohmann/json/issues/5387
|
||||
*/
|
||||
constexpr std::size_t recursion_depth_limit() noexcept
|
||||
|
||||
@@ -898,12 +898,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
}
|
||||
|
||||
#ifndef JSON_NO_THREAD_LOCAL
|
||||
/// the number of levels an operation descends into before it finishes the
|
||||
/// value below it without the call stack
|
||||
static constexpr std::uint8_t nesting_depth_limit()
|
||||
{
|
||||
return 128;
|
||||
}
|
||||
// nesting_depth() is a byte and may exceed the limit by one level
|
||||
static_assert(detail::recursion_depth_limit() < 255, "the nesting depth count must fit in a byte");
|
||||
|
||||
/*!
|
||||
@brief how many levels the operation going on in this thread has descended into
|
||||
@@ -945,7 +941,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
static_cast<void>(may_descend);
|
||||
return true;
|
||||
#else
|
||||
return !may_descend || nesting_depth() >= nesting_depth_limit();
|
||||
return !may_descend || nesting_depth() >= detail::recursion_depth_limit();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -969,7 +965,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
#ifdef JSON_NO_THREAD_LOCAL
|
||||
: m_okay(false)
|
||||
#else
|
||||
: m_okay(nesting_depth() < nesting_depth_limit())
|
||||
: m_okay(nesting_depth() < detail::recursion_depth_limit())
|
||||
#endif
|
||||
{
|
||||
#ifndef JSON_NO_THREAD_LOCAL
|
||||
@@ -1173,7 +1169,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
|
||||
The values whose copy has not been created yet are kept on an explicit
|
||||
worklist rather than on the call stack. This is only reached for values
|
||||
nested deeper than @ref nesting_depth_limit levels, which is why it copies
|
||||
nested deeper than @ref detail::recursion_depth_limit levels, which is why it copies
|
||||
every container by hand instead of letting the container do it: the fast
|
||||
ways of doing so would descend into the elements and defeat the purpose.
|
||||
*/
|
||||
@@ -1239,7 +1235,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
|
||||
Copying a container copies its elements, so a value nested deeply enough
|
||||
used to exhaust the call stack. The descent is bounded here: the first
|
||||
@ref nesting_depth_limit levels are copied by the containers themselves, just
|
||||
@ref detail::recursion_depth_limit levels are copied by the containers themselves, just
|
||||
as they always were, and anything below that is copied without the call
|
||||
stack by @ref copy_iteratively. Copying a value can therefore no longer
|
||||
exhaust the stack, however deeply it is nested, just like destroying one
|
||||
@@ -1377,7 +1373,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/*!
|
||||
@brief compare @a lhs and @a rhs without descending into them
|
||||
|
||||
Reached once a comparison has descended @ref nesting_depth_limit levels, so
|
||||
Reached once a comparison has descended @ref detail::recursion_depth_limit levels, so
|
||||
that comparing values cannot exhaust the call stack however deeply they are
|
||||
nested. The two values are walked in lockstep on an explicit stack and
|
||||
compared lexicographically, element by element in the order the containers
|
||||
@@ -4152,16 +4148,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
|
||||
}
|
||||
|
||||
// copy the values first: ilist may refer to elements of this array
|
||||
array_t values;
|
||||
detail::reserve_array(values, ilist.size(), detail::priority_tag<1> {});
|
||||
for (const auto& element : ilist)
|
||||
{
|
||||
values.push_back(element.moved_or_copied());
|
||||
}
|
||||
|
||||
// insert to array and return iterator
|
||||
return insert_iterator(pos, std::make_move_iterator(values.begin()), std::make_move_iterator(values.end()));
|
||||
return insert_iterator(pos, ilist.begin(), ilist.end());
|
||||
}
|
||||
|
||||
/// @brief inserts range of elements into object
|
||||
|
||||
@@ -7281,11 +7281,17 @@ namespace detail
|
||||
/*!
|
||||
@brief the number of nesting levels an operation recurses into
|
||||
|
||||
Operations that walk a value (serializing, hashing, merging, ...) recurse once
|
||||
Operations that walk a value (copying, comparing, serializing, hashing, merging,
|
||||
...) recurse once
|
||||
per nesting level, which is fastest, but a value nested deeply enough would
|
||||
exhaust the call stack. So they recurse only this many levels deep and finish
|
||||
whatever lies below with an explicit stack. All of them share this limit.
|
||||
|
||||
Most of them pass the depth down as an argument. The copy constructor and the
|
||||
comparison operators cannot, as their signatures are fixed, so they count it
|
||||
in basic_json::nesting_depth() instead, a byte per thread; the limit must
|
||||
therefore stay below 255.
|
||||
|
||||
@sa https://github.com/nlohmann/json/issues/5387
|
||||
*/
|
||||
constexpr std::size_t recursion_depth_limit() noexcept
|
||||
@@ -26979,12 +26985,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
}
|
||||
|
||||
#ifndef JSON_NO_THREAD_LOCAL
|
||||
/// the number of levels an operation descends into before it finishes the
|
||||
/// value below it without the call stack
|
||||
static constexpr std::uint8_t nesting_depth_limit()
|
||||
{
|
||||
return 128;
|
||||
}
|
||||
// nesting_depth() is a byte and may exceed the limit by one level
|
||||
static_assert(detail::recursion_depth_limit() < 255, "the nesting depth count must fit in a byte");
|
||||
|
||||
/*!
|
||||
@brief how many levels the operation going on in this thread has descended into
|
||||
@@ -27026,7 +27028,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
static_cast<void>(may_descend);
|
||||
return true;
|
||||
#else
|
||||
return !may_descend || nesting_depth() >= nesting_depth_limit();
|
||||
return !may_descend || nesting_depth() >= detail::recursion_depth_limit();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -27050,7 +27052,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
#ifdef JSON_NO_THREAD_LOCAL
|
||||
: m_okay(false)
|
||||
#else
|
||||
: m_okay(nesting_depth() < nesting_depth_limit())
|
||||
: m_okay(nesting_depth() < detail::recursion_depth_limit())
|
||||
#endif
|
||||
{
|
||||
#ifndef JSON_NO_THREAD_LOCAL
|
||||
@@ -27254,7 +27256,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
|
||||
The values whose copy has not been created yet are kept on an explicit
|
||||
worklist rather than on the call stack. This is only reached for values
|
||||
nested deeper than @ref nesting_depth_limit levels, which is why it copies
|
||||
nested deeper than @ref detail::recursion_depth_limit levels, which is why it copies
|
||||
every container by hand instead of letting the container do it: the fast
|
||||
ways of doing so would descend into the elements and defeat the purpose.
|
||||
*/
|
||||
@@ -27320,7 +27322,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
|
||||
Copying a container copies its elements, so a value nested deeply enough
|
||||
used to exhaust the call stack. The descent is bounded here: the first
|
||||
@ref nesting_depth_limit levels are copied by the containers themselves, just
|
||||
@ref detail::recursion_depth_limit levels are copied by the containers themselves, just
|
||||
as they always were, and anything below that is copied without the call
|
||||
stack by @ref copy_iteratively. Copying a value can therefore no longer
|
||||
exhaust the stack, however deeply it is nested, just like destroying one
|
||||
@@ -27458,7 +27460,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/*!
|
||||
@brief compare @a lhs and @a rhs without descending into them
|
||||
|
||||
Reached once a comparison has descended @ref nesting_depth_limit levels, so
|
||||
Reached once a comparison has descended @ref detail::recursion_depth_limit levels, so
|
||||
that comparing values cannot exhaust the call stack however deeply they are
|
||||
nested. The two values are walked in lockstep on an explicit stack and
|
||||
compared lexicographically, element by element in the order the containers
|
||||
@@ -30233,16 +30235,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
|
||||
}
|
||||
|
||||
// copy the values first: ilist may refer to elements of this array
|
||||
array_t values;
|
||||
detail::reserve_array(values, ilist.size(), detail::priority_tag<1> {});
|
||||
for (const auto& element : ilist)
|
||||
{
|
||||
values.push_back(element.moved_or_copied());
|
||||
}
|
||||
|
||||
// insert to array and return iterator
|
||||
return insert_iterator(pos, std::make_move_iterator(values.begin()), std::make_move_iterator(values.end()));
|
||||
return insert_iterator(pos, ilist.begin(), ilist.end());
|
||||
}
|
||||
|
||||
/// @brief inserts range of elements into object
|
||||
|
||||
@@ -117,22 +117,6 @@ TEST_CASE("array type without capacity()")
|
||||
CHECK(nested.flatten().unflatten() == nested);
|
||||
}
|
||||
|
||||
SECTION("insert(pos, initializer_list) compiles and works without reserve()")
|
||||
{
|
||||
// std::deque has no reserve() either; insert(pos, ilist) must not
|
||||
// require it (regression test for #5656, which also covers an ilist
|
||||
// that refers to elements of the array being inserted into)
|
||||
deque_json j = deque_json::array();
|
||||
j.push_back("a");
|
||||
j.push_back("b");
|
||||
j.push_back("c");
|
||||
|
||||
const deque_json& cj = j;
|
||||
auto it = j.insert(j.begin(), {cj[0], cj[1]});
|
||||
CHECK(*it == deque_json("a"));
|
||||
CHECK(j == deque_json({"a", "b", "a", "b", "c"}));
|
||||
}
|
||||
|
||||
SECTION("references stay valid while the array grows")
|
||||
{
|
||||
deque_json j = deque_json::array();
|
||||
|
||||
@@ -761,34 +761,6 @@ TEST_CASE("modifiers")
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("initializer list referring to the array's own elements (#5656)")
|
||||
{
|
||||
SECTION("sufficient capacity (no reallocation)")
|
||||
{
|
||||
json j_own = json::array();
|
||||
j_own.get_ref<json::array_t&>().reserve(8);
|
||||
j_own.push_back("a");
|
||||
j_own.push_back("b");
|
||||
j_own.push_back("c");
|
||||
|
||||
const json& j_own_cref = j_own;
|
||||
auto it = j_own.insert(j_own.begin(), {j_own_cref[0], j_own_cref[1]});
|
||||
CHECK(*it == json("a"));
|
||||
CHECK(j_own == json({"a", "b", "a", "b", "c"}));
|
||||
}
|
||||
|
||||
SECTION("insufficient capacity (reallocation)")
|
||||
{
|
||||
json j_own = {"a", "b", "c"};
|
||||
j_own.get_ref<json::array_t&>().shrink_to_fit();
|
||||
|
||||
const json& j_own_cref = j_own;
|
||||
auto it = j_own.insert(j_own.begin(), {j_own_cref[2]});
|
||||
CHECK(*it == json("c"));
|
||||
CHECK(j_own == json({"c", "a", "b", "c"}));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("invalid iterator")
|
||||
{
|
||||
// pass iterator to a different array
|
||||
|
||||
Reference in New Issue
Block a user