mirror of
https://github.com/nlohmann/json.git
synced 2026-10-03 13:10:33 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8366d86068 | ||
|
|
2edeefaa43 |
@@ -679,29 +679,19 @@ class json_pointer
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// tokens that array_index() rejects with parse_error.106/109
|
// may throw parse_error.106/109 for a malformed index; an
|
||||||
// are passed on to it; all other tokens that it would reject
|
// index that is syntactically valid but cannot be
|
||||||
// with out_of_range.404/410 are detected here, so that this
|
// represented (out_of_range.404/410) is treated like an
|
||||||
// also works without exceptions
|
// out-of-range index below
|
||||||
if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 && !(reference_token[0] >= '1' && reference_token[0] <= '9')))
|
typename BasicJsonType::size_type idx{};
|
||||||
|
JSON_TRY
|
||||||
{
|
{
|
||||||
static_cast<void>(array_index<BasicJsonType>(reference_token)); // throws parse_error.106/109
|
idx = array_index<BasicJsonType>(reference_token);
|
||||||
}
|
}
|
||||||
if (JSON_HEDLEY_UNLIKELY(reference_token.empty() || !std::all_of(reference_token.begin(), reference_token.end(), [](const char c)
|
JSON_INTERNAL_CATCH (detail::out_of_range&)
|
||||||
{
|
|
||||||
return c >= '0' && c <= '9';
|
|
||||||
})))
|
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
errno = 0; // strtoull() does not reset errno on success
|
|
||||||
char* p_end = nullptr; // NOLINT(misc-const-correctness)
|
|
||||||
const unsigned long long magnitude = std::strtoull(reference_token.data(), &p_end, 10); // NOLINT(runtime/int)
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(errno == ERANGE || magnitude >= static_cast<unsigned long long>((std::numeric_limits<typename BasicJsonType::size_type>::max)()))) // NOLINT(runtime/int)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
const auto idx = static_cast<typename BasicJsonType::size_type>(magnitude);
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
||||||
{
|
{
|
||||||
|
|||||||
+46
-19
@@ -1004,19 +1004,39 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
using copy_scratch_value_t = std::pair<typename object_t::key_type, basic_json>;
|
using copy_scratch_value_t = std::pair<typename object_t::key_type, basic_json>;
|
||||||
using copy_scratch_t = std::vector<copy_scratch_value_t, AllocatorType<copy_scratch_value_t>>;
|
using copy_scratch_t = std::vector<copy_scratch_value_t, AllocatorType<copy_scratch_value_t>>;
|
||||||
|
|
||||||
/// @brief copy everything of @a src into @a dst but its type and value
|
/// @brief tag selecting the constructor below; used only to build the
|
||||||
static void copy_metadata(const basic_json& src, basic_json& dst)
|
/// elements of a deep copy (@ref copy_array_level, @ref copy_object_level)
|
||||||
{
|
struct copy_construct_tag {};
|
||||||
// a custom base class is only required to be copy-constructible and
|
|
||||||
// move-assignable, so the copy has to go through a temporary
|
|
||||||
static_cast<json_base_class_t&>(dst) = json_base_class_t(static_cast<const json_base_class_t&>(src));
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
@brief construct a null value whose base class - and, with @ref
|
||||||
|
JSON_DIAGNOSTIC_POSITIONS, positions - are copied from @a src
|
||||||
|
|
||||||
|
Copy-constructing @ref json_base_class_t here, rather than default-
|
||||||
|
constructing the element and assigning its base class afterwards, means
|
||||||
|
that copying a @ref basic_json only ever requires a copy-constructible
|
||||||
|
base class, and never a move-assignable one as well.
|
||||||
|
|
||||||
|
@note this constructor has to be public: @ref copy_array_level and
|
||||||
|
@ref copy_object_level reach it through @ref array_t's or @ref
|
||||||
|
object_t's own emplace_back(), which constructs the element from
|
||||||
|
outside @ref basic_json and so cannot call a private constructor.
|
||||||
|
@ref copy_construct_tag is private, though, and nothing in the
|
||||||
|
public interface hands out a value of it, so outside code can still
|
||||||
|
never name it to call this constructor itself.
|
||||||
|
*/
|
||||||
|
basic_json(copy_construct_tag /*unused*/, const basic_json& src)
|
||||||
|
: json_base_class_t(src)
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
dst.start_position = src.start_position;
|
, start_position(src.start_position)
|
||||||
dst.end_position = src.end_position;
|
, end_position(src.end_position)
|
||||||
#endif
|
#endif
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief copy the value of @a src into @a dst, which must not be structured
|
@brief copy the value of @a src into @a dst, which must not be structured
|
||||||
|
|
||||||
@@ -1079,8 +1099,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief copy everything of @a src into the null value @a dst but the children
|
@brief finish the copy @a dst of @a src that a @ref copy_construct_tag
|
||||||
|
constructor started, other than the children of an object or array
|
||||||
|
|
||||||
|
@a dst already has @a src's base class and, with @ref
|
||||||
|
JSON_DIAGNOSTIC_POSITIONS, positions; only its value is still missing.
|
||||||
Objects and arrays are not copied here; they are appended to @a worklist to
|
Objects and arrays are not copied here; they are appended to @a worklist to
|
||||||
be created later by @ref copy_iteratively. Until that happens, @a dst remains
|
be created later by @ref copy_iteratively. Until that happens, @a dst remains
|
||||||
a null value, so that a partially built copy can be destroyed at any point
|
a null value, so that a partially built copy can be destroyed at any point
|
||||||
@@ -1088,8 +1111,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
*/
|
*/
|
||||||
static void copy_shallow(const basic_json& src, basic_json& dst, copy_worklist_t& worklist)
|
static void copy_shallow(const basic_json& src, basic_json& dst, copy_worklist_t& worklist)
|
||||||
{
|
{
|
||||||
copy_metadata(src, dst);
|
|
||||||
|
|
||||||
if (src.m_data.m_type == value_t::object || src.m_data.m_type == value_t::array)
|
if (src.m_data.m_type == value_t::object || src.m_data.m_type == value_t::array)
|
||||||
{
|
{
|
||||||
// defer: dst stays a null value until its container exists
|
// defer: dst stays a null value until its container exists
|
||||||
@@ -1110,15 +1131,19 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
{
|
{
|
||||||
const array_t& src_array = *src.m_data.m_value.array;
|
const array_t& src_array = *src.m_data.m_value.array;
|
||||||
|
|
||||||
// create all elements up front: growing the array afterwards could
|
|
||||||
// invalidate the pointers that are handed to the worklist; resize()
|
|
||||||
// rather than the fill constructor, because not every array type
|
|
||||||
// provides the latter (e.g., ones without a matching allocator-aware
|
|
||||||
// fill constructor)
|
|
||||||
dst.m_data.m_value.array = create<array_t>();
|
dst.m_data.m_value.array = create<array_t>();
|
||||||
// only now that the array exists may dst stop being a null value
|
// only now that the array exists may dst stop being a null value
|
||||||
dst.m_data.m_type = value_t::array;
|
dst.m_data.m_type = value_t::array;
|
||||||
dst.m_data.m_value.array->resize(src_array.size());
|
|
||||||
|
// create every element - its base class already copy-constructed from
|
||||||
|
// its counterpart in src, via the copy_construct_tag constructor -
|
||||||
|
// before any of their addresses are handed to worklist below: growing
|
||||||
|
// the array while that is going on could reallocate it and invalidate
|
||||||
|
// addresses taken from an earlier iteration
|
||||||
|
for (const auto& src_element : src_array)
|
||||||
|
{
|
||||||
|
dst.m_data.m_value.array->emplace_back(copy_construct_tag{}, src_element);
|
||||||
|
}
|
||||||
|
|
||||||
auto dst_it = dst.m_data.m_value.array->begin();
|
auto dst_it = dst.m_data.m_value.array->begin();
|
||||||
for (auto src_it = src_array.cbegin(); src_it != src_array.cend(); ++src_it, ++dst_it)
|
for (auto src_it = src_array.cbegin(); src_it != src_array.cend(); ++src_it, ++dst_it)
|
||||||
@@ -1136,12 +1161,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
|
|
||||||
// build the complete key skeleton and hand it to the object's range
|
// build the complete key skeleton and hand it to the object's range
|
||||||
// constructor: adding the keys one by one would be quadratic for object
|
// constructor: adding the keys one by one would be quadratic for object
|
||||||
// types that are backed by a vector, such as nlohmann::ordered_map
|
// types that are backed by a vector, such as nlohmann::ordered_map; each
|
||||||
|
// value's base class is already copy-constructed from its counterpart
|
||||||
|
// in src, via the copy_construct_tag constructor
|
||||||
scratch.clear();
|
scratch.clear();
|
||||||
scratch.reserve(src_object.size());
|
scratch.reserve(src_object.size());
|
||||||
for (const auto& element : src_object)
|
for (const auto& element : src_object)
|
||||||
{
|
{
|
||||||
scratch.emplace_back(element.first, basic_json());
|
scratch.emplace_back(element.first, basic_json(copy_construct_tag{}, element.second));
|
||||||
}
|
}
|
||||||
|
|
||||||
dst.m_data.m_value.object = create<object_t>(std::make_move_iterator(scratch.begin()),
|
dst.m_data.m_value.object = create<object_t>(std::make_move_iterator(scratch.begin()),
|
||||||
|
|||||||
@@ -20291,29 +20291,19 @@ class json_pointer
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// tokens that array_index() rejects with parse_error.106/109
|
// may throw parse_error.106/109 for a malformed index; an
|
||||||
// are passed on to it; all other tokens that it would reject
|
// index that is syntactically valid but cannot be
|
||||||
// with out_of_range.404/410 are detected here, so that this
|
// represented (out_of_range.404/410) is treated like an
|
||||||
// also works without exceptions
|
// out-of-range index below
|
||||||
if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 && !(reference_token[0] >= '1' && reference_token[0] <= '9')))
|
typename BasicJsonType::size_type idx{};
|
||||||
|
JSON_TRY
|
||||||
{
|
{
|
||||||
static_cast<void>(array_index<BasicJsonType>(reference_token)); // throws parse_error.106/109
|
idx = array_index<BasicJsonType>(reference_token);
|
||||||
}
|
}
|
||||||
if (JSON_HEDLEY_UNLIKELY(reference_token.empty() || !std::all_of(reference_token.begin(), reference_token.end(), [](const char c)
|
JSON_INTERNAL_CATCH (detail::out_of_range&)
|
||||||
{
|
|
||||||
return c >= '0' && c <= '9';
|
|
||||||
})))
|
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
errno = 0; // strtoull() does not reset errno on success
|
|
||||||
char* p_end = nullptr; // NOLINT(misc-const-correctness)
|
|
||||||
const unsigned long long magnitude = std::strtoull(reference_token.data(), &p_end, 10); // NOLINT(runtime/int)
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(errno == ERANGE || magnitude >= static_cast<unsigned long long>((std::numeric_limits<typename BasicJsonType::size_type>::max)()))) // NOLINT(runtime/int)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
const auto idx = static_cast<typename BasicJsonType::size_type>(magnitude);
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
||||||
{
|
{
|
||||||
@@ -27941,19 +27931,39 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
using copy_scratch_value_t = std::pair<typename object_t::key_type, basic_json>;
|
using copy_scratch_value_t = std::pair<typename object_t::key_type, basic_json>;
|
||||||
using copy_scratch_t = std::vector<copy_scratch_value_t, AllocatorType<copy_scratch_value_t>>;
|
using copy_scratch_t = std::vector<copy_scratch_value_t, AllocatorType<copy_scratch_value_t>>;
|
||||||
|
|
||||||
/// @brief copy everything of @a src into @a dst but its type and value
|
/// @brief tag selecting the constructor below; used only to build the
|
||||||
static void copy_metadata(const basic_json& src, basic_json& dst)
|
/// elements of a deep copy (@ref copy_array_level, @ref copy_object_level)
|
||||||
{
|
struct copy_construct_tag {};
|
||||||
// a custom base class is only required to be copy-constructible and
|
|
||||||
// move-assignable, so the copy has to go through a temporary
|
|
||||||
static_cast<json_base_class_t&>(dst) = json_base_class_t(static_cast<const json_base_class_t&>(src));
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
@brief construct a null value whose base class - and, with @ref
|
||||||
|
JSON_DIAGNOSTIC_POSITIONS, positions - are copied from @a src
|
||||||
|
|
||||||
|
Copy-constructing @ref json_base_class_t here, rather than default-
|
||||||
|
constructing the element and assigning its base class afterwards, means
|
||||||
|
that copying a @ref basic_json only ever requires a copy-constructible
|
||||||
|
base class, and never a move-assignable one as well.
|
||||||
|
|
||||||
|
@note this constructor has to be public: @ref copy_array_level and
|
||||||
|
@ref copy_object_level reach it through @ref array_t's or @ref
|
||||||
|
object_t's own emplace_back(), which constructs the element from
|
||||||
|
outside @ref basic_json and so cannot call a private constructor.
|
||||||
|
@ref copy_construct_tag is private, though, and nothing in the
|
||||||
|
public interface hands out a value of it, so outside code can still
|
||||||
|
never name it to call this constructor itself.
|
||||||
|
*/
|
||||||
|
basic_json(copy_construct_tag /*unused*/, const basic_json& src)
|
||||||
|
: json_base_class_t(src)
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
dst.start_position = src.start_position;
|
, start_position(src.start_position)
|
||||||
dst.end_position = src.end_position;
|
, end_position(src.end_position)
|
||||||
#endif
|
#endif
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief copy the value of @a src into @a dst, which must not be structured
|
@brief copy the value of @a src into @a dst, which must not be structured
|
||||||
|
|
||||||
@@ -28016,8 +28026,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief copy everything of @a src into the null value @a dst but the children
|
@brief finish the copy @a dst of @a src that a @ref copy_construct_tag
|
||||||
|
constructor started, other than the children of an object or array
|
||||||
|
|
||||||
|
@a dst already has @a src's base class and, with @ref
|
||||||
|
JSON_DIAGNOSTIC_POSITIONS, positions; only its value is still missing.
|
||||||
Objects and arrays are not copied here; they are appended to @a worklist to
|
Objects and arrays are not copied here; they are appended to @a worklist to
|
||||||
be created later by @ref copy_iteratively. Until that happens, @a dst remains
|
be created later by @ref copy_iteratively. Until that happens, @a dst remains
|
||||||
a null value, so that a partially built copy can be destroyed at any point
|
a null value, so that a partially built copy can be destroyed at any point
|
||||||
@@ -28025,8 +28038,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
*/
|
*/
|
||||||
static void copy_shallow(const basic_json& src, basic_json& dst, copy_worklist_t& worklist)
|
static void copy_shallow(const basic_json& src, basic_json& dst, copy_worklist_t& worklist)
|
||||||
{
|
{
|
||||||
copy_metadata(src, dst);
|
|
||||||
|
|
||||||
if (src.m_data.m_type == value_t::object || src.m_data.m_type == value_t::array)
|
if (src.m_data.m_type == value_t::object || src.m_data.m_type == value_t::array)
|
||||||
{
|
{
|
||||||
// defer: dst stays a null value until its container exists
|
// defer: dst stays a null value until its container exists
|
||||||
@@ -28047,15 +28058,19 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
{
|
{
|
||||||
const array_t& src_array = *src.m_data.m_value.array;
|
const array_t& src_array = *src.m_data.m_value.array;
|
||||||
|
|
||||||
// create all elements up front: growing the array afterwards could
|
|
||||||
// invalidate the pointers that are handed to the worklist; resize()
|
|
||||||
// rather than the fill constructor, because not every array type
|
|
||||||
// provides the latter (e.g., ones without a matching allocator-aware
|
|
||||||
// fill constructor)
|
|
||||||
dst.m_data.m_value.array = create<array_t>();
|
dst.m_data.m_value.array = create<array_t>();
|
||||||
// only now that the array exists may dst stop being a null value
|
// only now that the array exists may dst stop being a null value
|
||||||
dst.m_data.m_type = value_t::array;
|
dst.m_data.m_type = value_t::array;
|
||||||
dst.m_data.m_value.array->resize(src_array.size());
|
|
||||||
|
// create every element - its base class already copy-constructed from
|
||||||
|
// its counterpart in src, via the copy_construct_tag constructor -
|
||||||
|
// before any of their addresses are handed to worklist below: growing
|
||||||
|
// the array while that is going on could reallocate it and invalidate
|
||||||
|
// addresses taken from an earlier iteration
|
||||||
|
for (const auto& src_element : src_array)
|
||||||
|
{
|
||||||
|
dst.m_data.m_value.array->emplace_back(copy_construct_tag{}, src_element);
|
||||||
|
}
|
||||||
|
|
||||||
auto dst_it = dst.m_data.m_value.array->begin();
|
auto dst_it = dst.m_data.m_value.array->begin();
|
||||||
for (auto src_it = src_array.cbegin(); src_it != src_array.cend(); ++src_it, ++dst_it)
|
for (auto src_it = src_array.cbegin(); src_it != src_array.cend(); ++src_it, ++dst_it)
|
||||||
@@ -28073,12 +28088,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
|
|
||||||
// build the complete key skeleton and hand it to the object's range
|
// build the complete key skeleton and hand it to the object's range
|
||||||
// constructor: adding the keys one by one would be quadratic for object
|
// constructor: adding the keys one by one would be quadratic for object
|
||||||
// types that are backed by a vector, such as nlohmann::ordered_map
|
// types that are backed by a vector, such as nlohmann::ordered_map; each
|
||||||
|
// value's base class is already copy-constructed from its counterpart
|
||||||
|
// in src, via the copy_construct_tag constructor
|
||||||
scratch.clear();
|
scratch.clear();
|
||||||
scratch.reserve(src_object.size());
|
scratch.reserve(src_object.size());
|
||||||
for (const auto& element : src_object)
|
for (const auto& element : src_object)
|
||||||
{
|
{
|
||||||
scratch.emplace_back(element.first, basic_json());
|
scratch.emplace_back(element.first, basic_json(copy_construct_tag{}, element.second));
|
||||||
}
|
}
|
||||||
|
|
||||||
dst.m_data.m_value.object = create<object_t>(std::make_move_iterator(scratch.begin()),
|
dst.m_data.m_value.object = create<object_t>(std::make_move_iterator(scratch.begin()),
|
||||||
|
|||||||
@@ -405,3 +405,73 @@ TEST_CASE("JSON Visit Node")
|
|||||||
);
|
);
|
||||||
CHECK(expected.empty());
|
CHECK(expected.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A custom base class with a const member: copy-constructible (initializing a
|
||||||
|
// const member works fine), but not copy-/move-assignable (assigning one does
|
||||||
|
// not). Used to check that copy construction never requires more than that.
|
||||||
|
struct const_member_base
|
||||||
|
{
|
||||||
|
const int id = 7; // NOLINT(misc-non-private-member-variables-in-classes)
|
||||||
|
};
|
||||||
|
|
||||||
|
using json_with_const_base = nlohmann::basic_json <
|
||||||
|
std::map,
|
||||||
|
std::vector,
|
||||||
|
std::string,
|
||||||
|
bool,
|
||||||
|
std::int64_t,
|
||||||
|
std::uint64_t,
|
||||||
|
double,
|
||||||
|
std::allocator,
|
||||||
|
nlohmann::adl_serializer,
|
||||||
|
std::vector<std::uint8_t>,
|
||||||
|
const_member_base
|
||||||
|
>;
|
||||||
|
|
||||||
|
// build an array nested @a depth levels deep, with the innermost value 1;
|
||||||
|
// every level is constructed (never assigned), since const_member_base does
|
||||||
|
// not support assignment
|
||||||
|
static json_with_const_base make_nested_array(std::size_t depth)
|
||||||
|
{
|
||||||
|
if (depth == 0)
|
||||||
|
{
|
||||||
|
return json_with_const_base(1);
|
||||||
|
}
|
||||||
|
return json_with_const_base::array({make_nested_array(depth - 1)});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Regression test for issue #5674 - copy construction must not require an assignable base class")
|
||||||
|
{
|
||||||
|
SECTION("depth 0")
|
||||||
|
{
|
||||||
|
// as in the original bug report: copy construction only, no assignment
|
||||||
|
const json_with_const_base j = {1, 2};
|
||||||
|
const json_with_const_base copy = j; // NOLINT(performance-unnecessary-copy-initialization)
|
||||||
|
|
||||||
|
CHECK(copy.size() == 2);
|
||||||
|
CHECK(copy.id == 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("nested deeper than the copy constructor's descent bound")
|
||||||
|
{
|
||||||
|
// beyond nesting_depth_limit() (128) levels, the copy constructor
|
||||||
|
// copies without the call stack (copy_iteratively / copy_array_level),
|
||||||
|
// which used to assign the base class of every element it created
|
||||||
|
const std::size_t depth = 300;
|
||||||
|
|
||||||
|
const json_with_const_base j = make_nested_array(depth);
|
||||||
|
const json_with_const_base copy = j; // NOLINT(performance-unnecessary-copy-initialization)
|
||||||
|
|
||||||
|
const json_with_const_base* c = ©
|
||||||
|
for (std::size_t level = 0; level <= depth; ++level)
|
||||||
|
{
|
||||||
|
CAPTURE(level)
|
||||||
|
REQUIRE(c->id == 7);
|
||||||
|
if (level < depth)
|
||||||
|
{
|
||||||
|
c = &c->at(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CHECK(*c == 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -47,21 +47,6 @@ TEST_CASE("Tests with disabled exceptions")
|
|||||||
delete sax_no_exception::error_string; // NOLINT(cppcoreguidelines-owning-memory)
|
delete sax_no_exception::error_string; // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("issue #5672 - value(json_pointer, default) must not abort for array tokens that are not a valid index")
|
|
||||||
{
|
|
||||||
const json j = {1, 2, 3};
|
|
||||||
|
|
||||||
// a syntactically valid index that is out of range for this array
|
|
||||||
CHECK(j.value("/7"_json_pointer, 42) == 42);
|
|
||||||
// a reference token that is not a number at all
|
|
||||||
CHECK(j.value("/1a"_json_pointer, 42) == 42);
|
|
||||||
// the empty reference token (JSON pointer "/")
|
|
||||||
CHECK(j.value("/"_json_pointer, 42) == 42);
|
|
||||||
// an index whose magnitude does not fit into size_type
|
|
||||||
CHECK(j.value("/99999999999999999999999"_json_pointer, 42) == 42);
|
|
||||||
CHECK(j.value("/18446744073709551615"_json_pointer, 42) == 42);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("growing an ordered_json object")
|
SECTION("growing an ordered_json object")
|
||||||
{
|
{
|
||||||
auto j = nlohmann::ordered_json::object();
|
auto j = nlohmann::ordered_json::object();
|
||||||
|
|||||||
@@ -516,21 +516,6 @@ TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_j
|
|||||||
CHECK(j_array.value("/-"_json_pointer, 42) == 42);
|
CHECK(j_array.value("/-"_json_pointer, 42) == 42);
|
||||||
CHECK(j_array_const.value("/-"_json_pointer, 42) == 42);
|
CHECK(j_array_const.value("/-"_json_pointer, 42) == 42);
|
||||||
|
|
||||||
// Test an index with a non-digit after a valid leading digit; this is
|
|
||||||
// out_of_range (not parse_error) and must not throw (see #5672)
|
|
||||||
CHECK(j_array.value("/1a"_json_pointer, 42) == 42);
|
|
||||||
CHECK(j_array_const.value("/1a"_json_pointer, 42) == 42);
|
|
||||||
|
|
||||||
// Test the empty reference token (JSON pointer "/"); see #5672
|
|
||||||
CHECK(j_array.value("/"_json_pointer, 42) == 42);
|
|
||||||
CHECK(j_array_const.value("/"_json_pointer, 42) == 42);
|
|
||||||
|
|
||||||
// Test an index whose magnitude does not fit into size_type (see #5672)
|
|
||||||
CHECK(j_array.value("/99999999999999999999999"_json_pointer, 42) == 42);
|
|
||||||
CHECK(j_array_const.value("/99999999999999999999999"_json_pointer, 42) == 42);
|
|
||||||
CHECK(j_array.value("/18446744073709551615"_json_pointer, 42) == 42);
|
|
||||||
CHECK(j_array_const.value("/18446744073709551615"_json_pointer, 42) == 42);
|
|
||||||
|
|
||||||
#if !defined(JSON_NOEXCEPTION)
|
#if !defined(JSON_NOEXCEPTION)
|
||||||
// Test malformed index (non-numeric) throws parse_error
|
// Test malformed index (non-numeric) throws parse_error
|
||||||
CHECK_THROWS_WITH_AS(j_array.value("/foo"_json_pointer, 1), "[json.exception.parse_error.109] parse error: array index 'foo' is not a number", typename Json::parse_error&);
|
CHECK_THROWS_WITH_AS(j_array.value("/foo"_json_pointer, 1), "[json.exception.parse_error.109] parse error: array index 'foo' is not a number", typename Json::parse_error&);
|
||||||
|
|||||||
Reference in New Issue
Block a user