Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
58 files changed
+3400
-2866
No files matched your search
@@ -13,7 +13,6 @@
|
||||
#include <optional> // optional
|
||||
#endif
|
||||
|
||||
#include <algorithm> // copy
|
||||
#include <iterator> // begin, end
|
||||
#include <memory> // allocator_traits
|
||||
#include <string> // basic_string, char_traits
|
||||
@@ -39,10 +38,14 @@ namespace detail
|
||||
//////////////////
|
||||
|
||||
/*
|
||||
* Note all external_constructor<>::construct functions need to call
|
||||
* j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an
|
||||
* allocated value (e.g., a string). See bug issue
|
||||
* Note all external_constructor<>::construct functions need to store the new
|
||||
* value with j.replace_value(), which destroys the old one to avoid a memory
|
||||
* leak in case j contains an allocated value (e.g., a string). See bug issue
|
||||
* https://github.com/nlohmann/json/issues/2865 for more information.
|
||||
*
|
||||
* A value that has to be allocated is created before the old one is destroyed:
|
||||
* were it the other way around, an exception while creating the new value would
|
||||
* leave j with the type of the new value, but the pointer to the destroyed old one.
|
||||
*/
|
||||
|
||||
template<value_t> struct external_constructor;
|
||||
@@ -53,10 +56,7 @@ struct external_constructor<value_t::boolean>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::boolean;
|
||||
j.m_data.m_value = b;
|
||||
j.assert_invariant();
|
||||
j.replace_value(value_t::boolean, b);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -66,19 +66,15 @@ struct external_constructor<value_t::string>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::string;
|
||||
j.m_data.m_value = s;
|
||||
j.assert_invariant();
|
||||
const typename BasicJsonType::json_value value(s);
|
||||
j.replace_value(value_t::string, value);
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::string;
|
||||
j.m_data.m_value = std::move(s);
|
||||
j.assert_invariant();
|
||||
const typename BasicJsonType::json_value value(std::move(s));
|
||||
j.replace_value(value_t::string, value);
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename CompatibleStringType,
|
||||
@@ -86,10 +82,8 @@ struct external_constructor<value_t::string>
|
||||
int > = 0 >
|
||||
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::string;
|
||||
j.m_data.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
|
||||
j.assert_invariant();
|
||||
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::string_t>(str));
|
||||
j.replace_value(value_t::string, value);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -99,19 +93,15 @@ struct external_constructor<value_t::binary>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::binary;
|
||||
j.m_data.m_value = typename BasicJsonType::binary_t(b);
|
||||
j.assert_invariant();
|
||||
const typename BasicJsonType::json_value value(b);
|
||||
j.replace_value(value_t::binary, value);
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::binary;
|
||||
j.m_data.m_value = typename BasicJsonType::binary_t(std::move(b));
|
||||
j.assert_invariant();
|
||||
const typename BasicJsonType::json_value value(std::move(b));
|
||||
j.replace_value(value_t::binary, value);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -121,10 +111,7 @@ struct external_constructor<value_t::number_float>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::number_float;
|
||||
j.m_data.m_value = val;
|
||||
j.assert_invariant();
|
||||
j.replace_value(value_t::number_float, val);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -134,10 +121,7 @@ struct external_constructor<value_t::number_unsigned>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::number_unsigned;
|
||||
j.m_data.m_value = val;
|
||||
j.assert_invariant();
|
||||
j.replace_value(value_t::number_unsigned, val);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -147,10 +131,7 @@ struct external_constructor<value_t::number_integer>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::number_integer;
|
||||
j.m_data.m_value = val;
|
||||
j.assert_invariant();
|
||||
j.replace_value(value_t::number_integer, val);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -160,21 +141,15 @@ struct external_constructor<value_t::array>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = arr;
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
const typename BasicJsonType::json_value value(arr);
|
||||
j.replace_value(value_t::array, value);
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = std::move(arr);
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
const typename BasicJsonType::json_value value(std::move(arr));
|
||||
j.replace_value(value_t::array, value);
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename CompatibleArrayType,
|
||||
@@ -188,39 +163,23 @@ struct external_constructor<value_t::array>
|
||||
using std::begin;
|
||||
using std::end;
|
||||
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr)));
|
||||
j.replace_value(value_t::array, value);
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value_t::array;
|
||||
j.m_data.m_value.array->reserve(arr.size());
|
||||
for (const bool x : arr)
|
||||
{
|
||||
j.m_data.m_value.array->push_back(x);
|
||||
j.set_parent(j.m_data.m_value.array->back());
|
||||
}
|
||||
j.assert_invariant();
|
||||
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::array_t>(arr.begin(), arr.end()));
|
||||
j.replace_value(value_t::array, value);
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename T,
|
||||
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
||||
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value_t::array;
|
||||
j.m_data.m_value.array->resize(arr.size());
|
||||
std::copy(std::begin(arr), std::end(arr), j.m_data.m_value.array->begin());
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::array_t>(std::begin(arr), std::end(arr)));
|
||||
j.replace_value(value_t::array, value);
|
||||
}
|
||||
|
||||
#if JSON_HAS_RANGE_VIEW_CONVERSION
|
||||
@@ -228,18 +187,15 @@ struct external_constructor<value_t::array>
|
||||
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
|
||||
static void construct(BasicJsonType& j, CompatibleArrayType && arr)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value_t::array;
|
||||
// no range constructor: a view's begin() and end() may have different
|
||||
// types, and the view may only be iterable once
|
||||
typename BasicJsonType::array_t elements;
|
||||
for (auto&& x : std::forward<CompatibleArrayType>(arr))
|
||||
{
|
||||
j.m_data.m_value.array->push_back(x);
|
||||
elements.push_back(x);
|
||||
}
|
||||
// set the parents only once all elements are in place: a push_back
|
||||
// that reallocates moves the earlier elements, which does not keep
|
||||
// their parent pointers
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
const typename BasicJsonType::json_value value(std::move(elements));
|
||||
j.replace_value(value_t::array, value);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
@@ -250,21 +206,15 @@ struct external_constructor<value_t::object>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::object;
|
||||
j.m_data.m_value = obj;
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
const typename BasicJsonType::json_value value(obj);
|
||||
j.replace_value(value_t::object, value);
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::object;
|
||||
j.m_data.m_value = std::move(obj);
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
const typename BasicJsonType::json_value value(std::move(obj));
|
||||
j.replace_value(value_t::object, value);
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename CompatibleObjectType,
|
||||
@@ -274,11 +224,8 @@ struct external_constructor<value_t::object>
|
||||
using std::begin;
|
||||
using std::end;
|
||||
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::object;
|
||||
j.m_data.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj)));
|
||||
j.replace_value(value_t::object, value);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1270,6 +1270,20 @@ class binary_reader
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reports a nested indefinite-length CBOR string or byte array
|
||||
@param[in] type_name name of the rejected string type
|
||||
@param[in] context parsing context for the error message
|
||||
@return false, so that the caller stops reading
|
||||
*/
|
||||
bool cbor_indefinite_string_error(const char* type_name, const char* context)
|
||||
{
|
||||
auto last_token = get_token_string();
|
||||
return report_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||
exception_message(concat("indefinite-length ", type_name,
|
||||
" is not allowed inside indefinite-length ", type_name, "; last byte: 0x", last_token), context), nullptr));
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reads a definite-length CBOR string
|
||||
|
||||
@@ -1279,12 +1293,13 @@ class binary_reader
|
||||
into the same string.
|
||||
|
||||
@param[out] result string the bytes are appended to
|
||||
@param[in] inside_indefinite whether the bytes belong to an indefinite-length string
|
||||
|
||||
@return whether string creation completed
|
||||
|
||||
@pre @a current is not EOF
|
||||
*/
|
||||
bool get_cbor_string_chunk(string_t& result)
|
||||
bool get_cbor_string_chunk(string_t& result, const bool inside_indefinite)
|
||||
{
|
||||
switch (current)
|
||||
{
|
||||
@@ -1345,7 +1360,7 @@ class binary_reader
|
||||
{
|
||||
auto last_token = get_token_string();
|
||||
return report_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||
exception_message(concat("expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x", last_token), "string"), nullptr));
|
||||
exception_message(concat("expected length specification (0x60-0x7B)", inside_indefinite ? "" : " or indefinite string type (0x7F)", "; last byte: 0x", last_token), "string"), nullptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1363,13 +1378,9 @@ class binary_reader
|
||||
*/
|
||||
bool get_cbor_string(string_t& result, const char* context = "string")
|
||||
{
|
||||
// number of indefinite-length strings that have been opened and not
|
||||
// closed yet. RFC 8949, Section 3.2.3 does not permit nesting them,
|
||||
// but this reader has always accepted it, so the open levels are
|
||||
// counted instead of recursed through, which overflowed the stack for
|
||||
// an input of repeated 0x7F bytes (see #5104). Every chunk is appended
|
||||
// to the same result, so no per-level state is needed.
|
||||
std::size_t open = 0;
|
||||
// read chunks iteratively, but reject a second indefinite-length
|
||||
// level as required by RFC 8949, Section 3.2.3
|
||||
bool indefinite = false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -1380,29 +1391,28 @@ class binary_reader
|
||||
|
||||
if (current == 0x7F) // UTF-8 string (indefinite length)
|
||||
{
|
||||
++open;
|
||||
get();
|
||||
continue;
|
||||
}
|
||||
|
||||
// a break marker closes the innermost indefinite-length string;
|
||||
// outside of one it is not a string and falls through to the error
|
||||
if (open != 0 && current == 0xFF)
|
||||
{
|
||||
if (--open == 0)
|
||||
if (JSON_HEDLEY_UNLIKELY(indefinite))
|
||||
{
|
||||
return check_string_utf8(result, context);
|
||||
return cbor_indefinite_string_error("string", "string");
|
||||
}
|
||||
indefinite = true;
|
||||
get();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_string_chunk(result)))
|
||||
// a break marker closes the indefinite-length string; outside
|
||||
// of one it falls through to the error below
|
||||
if (indefinite && current == 0xFF)
|
||||
{
|
||||
return check_string_utf8(result, context);
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_string_chunk(result, indefinite)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (open == 0)
|
||||
if (!indefinite)
|
||||
{
|
||||
return check_string_utf8(result, context);
|
||||
}
|
||||
@@ -1498,12 +1508,13 @@ class binary_reader
|
||||
read into the same byte array.
|
||||
|
||||
@param[out] result byte array the bytes are appended to
|
||||
@param[in] inside_indefinite whether the bytes belong to an indefinite-length string
|
||||
|
||||
@return whether byte array creation completed
|
||||
|
||||
@pre @a current is not EOF
|
||||
*/
|
||||
bool get_cbor_binary_chunk(binary_t& result)
|
||||
bool get_cbor_binary_chunk(binary_t& result, const bool inside_indefinite)
|
||||
{
|
||||
switch (current)
|
||||
{
|
||||
@@ -1568,7 +1579,7 @@ class binary_reader
|
||||
{
|
||||
auto last_token = get_token_string();
|
||||
return report_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||
exception_message(concat("expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x", last_token), "binary"), nullptr));
|
||||
exception_message(concat("expected length specification (0x40-0x5B)", inside_indefinite ? "" : " or indefinite binary array type (0x5F)", "; last byte: 0x", last_token), "binary"), nullptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1586,9 +1597,9 @@ class binary_reader
|
||||
*/
|
||||
bool get_cbor_binary(binary_t& result)
|
||||
{
|
||||
// the open indefinite-length byte arrays are counted rather than
|
||||
// recursed through, for the reason given in @ref get_cbor_string
|
||||
std::size_t open = 0;
|
||||
// read chunks iteratively, but reject a second indefinite-length
|
||||
// level as required by RFC 8949, Section 3.2.3
|
||||
bool indefinite = false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -1599,29 +1610,28 @@ class binary_reader
|
||||
|
||||
if (current == 0x5F) // Binary data (indefinite length)
|
||||
{
|
||||
++open;
|
||||
get();
|
||||
continue;
|
||||
}
|
||||
|
||||
// a break marker closes the innermost indefinite-length byte
|
||||
// array; outside of one it falls through to the error below
|
||||
if (open != 0 && current == 0xFF)
|
||||
{
|
||||
if (--open == 0)
|
||||
if (JSON_HEDLEY_UNLIKELY(indefinite))
|
||||
{
|
||||
return true;
|
||||
return cbor_indefinite_string_error("binary array", "binary");
|
||||
}
|
||||
indefinite = true;
|
||||
get();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_binary_chunk(result)))
|
||||
// a break marker closes the indefinite-length string; outside
|
||||
// of one it falls through to the error below
|
||||
if (indefinite && current == 0xFF)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_binary_chunk(result, indefinite)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (open == 0)
|
||||
if (!indefinite)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -3737,6 +3747,15 @@ class binary_reader
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// the lexer would stop at a NUL and accept the digits before it
|
||||
if (JSON_HEDLEY_UNLIKELY(current == '\0'))
|
||||
{
|
||||
static_cast<void>(report_error(chars_read, "00", parse_error::create(115, chars_read,
|
||||
exception_message("invalid number text; last byte: 0x00", "high-precision number"), nullptr)));
|
||||
// when recovering, the number ends at the NUL, as it would in
|
||||
// JSON text, and the rest of its bytes are skipped
|
||||
return recover_high_precision_number(number_vector, size - i - 1);
|
||||
}
|
||||
number_vector.push_back(static_cast<char>(current));
|
||||
}
|
||||
|
||||
@@ -3812,12 +3831,14 @@ class binary_reader
|
||||
parser for JSON text, or passes null if there is none.
|
||||
|
||||
@param[in] number_vector the number's text
|
||||
@param[in] remaining the number of bytes of the number that are not
|
||||
read yet; they are skipped
|
||||
@return whether the SAX parser asked to recover and accepted the value
|
||||
*/
|
||||
template<bool Recover = AllowRecovery, enable_if_t<Recover, int> = 0>
|
||||
bool recover_high_precision_number(const std::vector<char>& number_vector)
|
||||
bool recover_high_precision_number(const std::vector<char>& number_vector, const std::size_t remaining = 0)
|
||||
{
|
||||
if (!repair_requested())
|
||||
if (!repair_requested() || !skip_bytes(remaining, "number"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -3861,7 +3882,7 @@ class binary_reader
|
||||
|
||||
/// @copydoc recover_high_precision_number
|
||||
template < bool Recover = AllowRecovery, enable_if_t < !Recover, int > = 0 >
|
||||
constexpr std::false_type recover_high_precision_number(const std::vector<char>& /*number_vector*/) const noexcept
|
||||
constexpr std::false_type recover_high_precision_number(const std::vector<char>& /*number_vector*/, const std::size_t /*remaining*/ = 0) const noexcept
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ class json_ref
|
||||
|
||||
json_ref(std::initializer_list<json_ref> init)
|
||||
: owned_value(init)
|
||||
, braced_list(true)
|
||||
{}
|
||||
|
||||
template <
|
||||
@@ -69,9 +70,17 @@ class json_ref
|
||||
return &** this;
|
||||
}
|
||||
|
||||
/// whether the value was written as a braced list, such as {"key", 1},
|
||||
/// rather than given as a value
|
||||
bool is_braced_list() const noexcept
|
||||
{
|
||||
return braced_list;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable value_type owned_value = nullptr;
|
||||
value_type const* value_ref = nullptr;
|
||||
bool braced_list = false;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
Reference in new issue
Block a user