mirror of
https://github.com/nlohmann/json.git
synced 2026-09-30 19:50:34 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
855155d1ab | ||
|
|
5fc53e3e0a | ||
|
|
f30ee4bcfe | ||
|
|
e32de7bb8c | ||
|
|
93231c6c2f | ||
|
|
d254f62764 | ||
|
|
23aa069e63 | ||
|
|
b5fad30499 | ||
|
|
a9cc4243b7 | ||
|
|
03319da529 | ||
|
|
ae9ef9dde1 | ||
|
|
f9d4b51455 | ||
|
|
31883d06a8 | ||
|
|
4c208728e7 |
@@ -53,7 +53,6 @@ cc_library(
|
||||
"include/nlohmann/detail/meta/detected.hpp",
|
||||
"include/nlohmann/detail/meta/identity_tag.hpp",
|
||||
"include/nlohmann/detail/meta/is_sax.hpp",
|
||||
"include/nlohmann/detail/meta/logic.hpp",
|
||||
"include/nlohmann/detail/meta/std_fs.hpp",
|
||||
"include/nlohmann/detail/meta/type_traits.hpp",
|
||||
"include/nlohmann/detail/meta/void_t.hpp",
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <nlohmann/detail/meta/identity_tag.hpp>
|
||||
#include <nlohmann/detail/meta/std_fs.hpp>
|
||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||
#include <nlohmann/detail/meta/logic.hpp>
|
||||
#include <nlohmann/detail/string_concat.hpp>
|
||||
#include <nlohmann/detail/value_t.hpp>
|
||||
|
||||
@@ -211,62 +210,29 @@ inline void from_json(const BasicJsonType& j, std::valarray<T>& l)
|
||||
});
|
||||
}
|
||||
|
||||
// element is not itself a C array: read it directly
|
||||
template<typename BasicJsonType, typename T>
|
||||
auto from_json_c_array_element(const BasicJsonType& j, T& e)
|
||||
-> decltype(e = j.template get<T>(), void())
|
||||
{
|
||||
e = j.template get<T>();
|
||||
}
|
||||
|
||||
// element is itself a C array: recurse one dimension at a time, so any rank is supported
|
||||
template<typename BasicJsonType, typename T, std::size_t N>
|
||||
auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
-> decltype(j.template get<T>(), void())
|
||||
void from_json_c_array_element(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
{
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
{
|
||||
arr[i] = j.at(i).template get<T>();
|
||||
from_json_c_array_element(j.at(i), arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename T, std::size_t N1, std::size_t N2>
|
||||
auto from_json(const BasicJsonType& j, T (&arr)[N1][N2]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
-> decltype(j.template get<T>(), void())
|
||||
template<typename BasicJsonType, typename T, std::size_t N>
|
||||
auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
-> decltype(j.template get<typename std::remove_all_extents<T>::type>(), void())
|
||||
{
|
||||
for (std::size_t i1 = 0; i1 < N1; ++i1)
|
||||
{
|
||||
for (std::size_t i2 = 0; i2 < N2; ++i2)
|
||||
{
|
||||
arr[i1][i2] = j.at(i1).at(i2).template get<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename T, std::size_t N1, std::size_t N2, std::size_t N3>
|
||||
auto from_json(const BasicJsonType& j, T (&arr)[N1][N2][N3]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
-> decltype(j.template get<T>(), void())
|
||||
{
|
||||
for (std::size_t i1 = 0; i1 < N1; ++i1)
|
||||
{
|
||||
for (std::size_t i2 = 0; i2 < N2; ++i2)
|
||||
{
|
||||
for (std::size_t i3 = 0; i3 < N3; ++i3)
|
||||
{
|
||||
arr[i1][i2][i3] = j.at(i1).at(i2).at(i3).template get<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename T, std::size_t N1, std::size_t N2, std::size_t N3, std::size_t N4>
|
||||
auto from_json(const BasicJsonType& j, T (&arr)[N1][N2][N3][N4]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
-> decltype(j.template get<T>(), void())
|
||||
{
|
||||
for (std::size_t i1 = 0; i1 < N1; ++i1)
|
||||
{
|
||||
for (std::size_t i2 = 0; i2 < N2; ++i2)
|
||||
{
|
||||
for (std::size_t i3 = 0; i3 < N3; ++i3)
|
||||
{
|
||||
for (std::size_t i4 = 0; i4 < N4; ++i4)
|
||||
{
|
||||
arr[i1][i2][i3][i4] = j.at(i1).at(i2).at(i3).at(i4).template get<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
from_json_c_array_element(j, arr);
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
@@ -286,20 +252,33 @@ auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,
|
||||
}
|
||||
}
|
||||
|
||||
// reserve() is called through this pair (modeled on from_json_object_reserve)
|
||||
// so from_json_array_impl below has a single body for both ConstructibleArrayType
|
||||
// that support reserve() and those that don't.
|
||||
template<typename ConstructibleArrayType>
|
||||
auto from_json_array_reserve(ConstructibleArrayType& arr, typename ConstructibleArrayType::size_type size, priority_tag<1> /*unused*/)
|
||||
-> decltype(arr.reserve(size), void())
|
||||
{
|
||||
arr.reserve(size);
|
||||
}
|
||||
|
||||
template<typename ConstructibleArrayType>
|
||||
inline void from_json_array_reserve(ConstructibleArrayType& /*arr*/, std::size_t /*size*/, priority_tag<0> /*unused*/)
|
||||
{}
|
||||
|
||||
template<typename BasicJsonType, typename ConstructibleArrayType,
|
||||
enable_if_t<
|
||||
std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
|
||||
int> = 0>
|
||||
auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/)
|
||||
-> decltype(
|
||||
arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()),
|
||||
j.template get<typename ConstructibleArrayType::value_type>(),
|
||||
void())
|
||||
{
|
||||
using std::end;
|
||||
|
||||
ConstructibleArrayType ret;
|
||||
ret.reserve(j.size());
|
||||
from_json_array_reserve(ret, j.size(), priority_tag<1> {});
|
||||
std::transform(j.begin(), j.end(),
|
||||
std::inserter(ret, end(ret)), [](const BasicJsonType & i)
|
||||
{
|
||||
@@ -310,27 +289,6 @@ auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, p
|
||||
arr = std::move(ret);
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename ConstructibleArrayType,
|
||||
enable_if_t<
|
||||
std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
|
||||
int> = 0>
|
||||
inline void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,
|
||||
priority_tag<0> /*unused*/)
|
||||
{
|
||||
using std::end;
|
||||
|
||||
ConstructibleArrayType ret;
|
||||
std::transform(
|
||||
j.begin(), j.end(), std::inserter(ret, end(ret)),
|
||||
[](const BasicJsonType & i)
|
||||
{
|
||||
// get<BasicJsonType>() returns *this, this won't call a from_json
|
||||
// method when value_type is BasicJsonType
|
||||
return i.template get<typename ConstructibleArrayType::value_type>();
|
||||
});
|
||||
arr = std::move(ret);
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename ConstructibleArrayType,
|
||||
enable_if_t <
|
||||
is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value&&
|
||||
@@ -433,9 +391,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
|
||||
}
|
||||
|
||||
// overload for arithmetic types, not chosen for basic_json template arguments
|
||||
// (BooleanType, etc.); note: Is it really necessary to provide explicit
|
||||
// overloads for boolean_t etc. in case of a custom BooleanType which is not
|
||||
// an arithmetic type?
|
||||
// (BooleanType, etc.)
|
||||
template < typename BasicJsonType, typename ArithmeticType,
|
||||
enable_if_t <
|
||||
std::is_arithmetic<ArithmeticType>::value&&
|
||||
@@ -531,7 +487,7 @@ inline void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priori
|
||||
template<typename BasicJsonType, typename... Args>
|
||||
std::tuple<Args...> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)
|
||||
{
|
||||
static_assert(cxpr_and<cxpr_or<cxpr_not<std::is_reference<Args>>, is_compatible_reference_type<BasicJsonType, Args>>...>::value,
|
||||
static_assert(conjunction<disjunction<negation<std::is_reference<Args>>, is_compatible_reference_type<BasicJsonType, Args>>...>::value,
|
||||
"Can not return a tuple containing references to types not contained in a Json, try Json::get_to()");
|
||||
return from_json_tuple_impl_base<1, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
|
||||
}
|
||||
@@ -554,10 +510,10 @@ auto from_json(BasicJsonType&& j, TupleRelated&& t)
|
||||
return from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {});
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
|
||||
typename = enable_if_t < !std::is_constructible <
|
||||
typename BasicJsonType::string_t, Key >::value >>
|
||||
inline void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
|
||||
// shared body for std::map/std::unordered_map with a non-string Key: both
|
||||
// containers are read from an array of [key, value] pairs the same way
|
||||
template<typename BasicJsonType, typename MapType>
|
||||
inline void from_json_pair_array_to_map(const BasicJsonType& j, MapType& m)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
||||
{
|
||||
@@ -570,33 +526,29 @@ inline void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allo
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &p));
|
||||
}
|
||||
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
|
||||
m.emplace(p.at(0).template get<typename MapType::key_type>(), p.at(1).template get<typename MapType::mapped_type>());
|
||||
}
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
|
||||
typename = enable_if_t < !std::is_constructible <
|
||||
typename BasicJsonType::string_t, Key >::value >>
|
||||
inline void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
|
||||
{
|
||||
from_json_pair_array_to_map(j, m);
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,
|
||||
typename = enable_if_t < !std::is_constructible <
|
||||
typename BasicJsonType::string_t, Key >::value >>
|
||||
inline void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
||||
}
|
||||
m.clear();
|
||||
for (const auto& p : j)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &p));
|
||||
}
|
||||
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
|
||||
}
|
||||
from_json_pair_array_to_map(j, m);
|
||||
}
|
||||
|
||||
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
||||
|
||||
// Workaround for MSVC 19.51 (and possibly later): in large in large cpp files, the compiler may fail to resolve with generic has_from_json (issue #4996)
|
||||
// Workaround for MSVC 19.51 (and possibly later): in large cpp files, the compiler may fail to resolve with generic has_from_json (issue #4996)
|
||||
template<typename BasicJsonType>
|
||||
struct has_from_json<BasicJsonType, std_fs::path, void> : std::true_type {};
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@ struct external_constructor<value_t::array>
|
||||
|
||||
template < typename BasicJsonType, typename CompatibleArrayType,
|
||||
enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
#if JSON_HAS_RANGE_VIEW_CONVERSION
|
||||
&& !is_compatible_range_view<CompatibleArrayType>::value
|
||||
#endif
|
||||
, int > = 0 >
|
||||
@@ -222,9 +222,7 @@ struct external_constructor<value_t::array>
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
// std::ranges does not work properly on MinGW due to incomplete C++20 support
|
||||
// see https://github.com/nlohmann/json/issues/4916
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
#if JSON_HAS_RANGE_VIEW_CONVERSION
|
||||
template<typename BasicJsonType, typename CompatibleArrayType,
|
||||
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
|
||||
static void construct(BasicJsonType& j, CompatibleArrayType && arr)
|
||||
@@ -379,7 +377,7 @@ template < typename BasicJsonType, typename CompatibleArrayType,
|
||||
!std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
|
||||
!is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value&&
|
||||
!is_basic_json<CompatibleArrayType>::value
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
#if JSON_HAS_RANGE_VIEW_CONVERSION
|
||||
&& !is_compatible_range_view<CompatibleArrayType>::value
|
||||
#endif
|
||||
,
|
||||
@@ -389,7 +387,7 @@ inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
|
||||
external_constructor<value_t::array>::construct(j, arr);
|
||||
}
|
||||
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
#if JSON_HAS_RANGE_VIEW_CONVERSION
|
||||
template < typename BasicJsonType, typename T,
|
||||
enable_if_t < is_compatible_range_view<std::remove_cvref_t<T>>::value
|
||||
&& !is_compatible_string_type<BasicJsonType, std::remove_cvref_t<T>>::value
|
||||
|
||||
@@ -286,6 +286,27 @@ class other_error : public exception
|
||||
other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
|
||||
};
|
||||
|
||||
/*!
|
||||
@brief helper function to call JSON_THROW from a template
|
||||
@note JSON_THROW is a macro that, depending on the JSON_THROW_USER /
|
||||
JSON_TRY_USER / JSON_NOEXCEPTION configuration, may expand to code
|
||||
that does not reference its argument (e.g. `std::abort()`), which
|
||||
would trigger a compilation error if the argument's type depends on
|
||||
a template parameter that is otherwise unused. Wrapping the call in
|
||||
a templated function avoids this and gives the compiler a single
|
||||
place to see the (possibly unused) parameter.
|
||||
*/
|
||||
template<typename ExceptionType>
|
||||
void templated_json_throw(ExceptionType exception)
|
||||
{
|
||||
JSON_THROW(exception);
|
||||
|
||||
// JSON_THROW may expand to code that discards its argument (e.g. when
|
||||
// exceptions are disabled) - the cast below avoids an unused-parameter
|
||||
// warning with -Werror in that case
|
||||
(void)exception;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
|
||||
@@ -60,9 +60,11 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
|
||||
"iter_impl only accepts (const) basic_json");
|
||||
// superficial check for the LegacyBidirectionalIterator named requirement
|
||||
static_assert(std::is_base_of<std::bidirectional_iterator_tag, std::bidirectional_iterator_tag>::value
|
||||
&& std::is_base_of<std::bidirectional_iterator_tag, typename std::iterator_traits<typename array_t::iterator>::iterator_category>::value,
|
||||
"basic_json iterator assumes array and object type iterators satisfy the LegacyBidirectionalIterator named requirement.");
|
||||
// note: only array_t::iterator is checked here; object_t::iterator may be
|
||||
// a forward-only iterator as long as reverse iteration and operator--
|
||||
// are never used on it
|
||||
static_assert(std::is_base_of<std::bidirectional_iterator_tag, typename std::iterator_traits<typename array_t::iterator>::iterator_category>::value,
|
||||
"basic_json iterator assumes array type iterators satisfy the LegacyBidirectionalIterator named requirement.");
|
||||
|
||||
public:
|
||||
/// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
|
||||
|
||||
@@ -240,6 +240,72 @@ class json_pointer
|
||||
}
|
||||
|
||||
private:
|
||||
/*!
|
||||
@brief result of @ref parse_array_index
|
||||
|
||||
@ref array_index maps each value to the corresponding parse_error/out_of_range
|
||||
exception; @ref contains and @ref get_checked_or_null, which must not throw for
|
||||
an out-of-range or unrepresentable index, switch on it directly instead.
|
||||
*/
|
||||
enum class array_index_status
|
||||
{
|
||||
ok, ///< @a s is a valid, representable array index
|
||||
leading_zero, ///< @a s begins with '0' but has more than one character
|
||||
not_a_number, ///< @a s does not begin with a digit
|
||||
unresolved, ///< @a s could not be converted to an integer
|
||||
exceeds_size_type ///< @a s converts to an integer that exceeds size_type
|
||||
};
|
||||
|
||||
/*!
|
||||
@param[in] s reference token to be converted into an array index
|
||||
@param[out] idx the integer representation of @a s if @ref array_index_status::ok
|
||||
is returned; left unchanged otherwise
|
||||
|
||||
@return whether @a s is a valid array index, and if not, why
|
||||
|
||||
@note this function never throws; @ref array_index and the callers that must not
|
||||
throw (@ref contains, @ref get_checked_or_null) build on it instead of each
|
||||
re-implementing the RFC 6901 digit rules and the @a size_type range check
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
static array_index_status parse_array_index(const string_t& s, typename BasicJsonType::size_type& idx) noexcept
|
||||
{
|
||||
using size_type = typename BasicJsonType::size_type;
|
||||
|
||||
// error condition (cf. RFC 6901, Sect. 4)
|
||||
if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0'))
|
||||
{
|
||||
return array_index_status::leading_zero;
|
||||
}
|
||||
|
||||
// error condition (cf. RFC 6901, Sect. 4)
|
||||
if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))
|
||||
{
|
||||
return array_index_status::not_a_number;
|
||||
}
|
||||
|
||||
const char* p = s.data();
|
||||
char* p_end = nullptr; // NOLINT(misc-const-correctness)
|
||||
errno = 0; // strtoull doesn't reset errno
|
||||
const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int)
|
||||
if (p == p_end // invalid input or empty string
|
||||
|| errno == ERANGE // out of range
|
||||
|| JSON_HEDLEY_UNLIKELY(static_cast<std::size_t>(p_end - p) != s.size())) // incomplete read
|
||||
{
|
||||
return array_index_status::unresolved;
|
||||
}
|
||||
|
||||
// only triggered on special platforms (like 32bit), see also
|
||||
// https://github.com/nlohmann/json/pull/2203
|
||||
if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)())) // NOLINT(runtime/int)
|
||||
{
|
||||
return array_index_status::exceeds_size_type; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
idx = static_cast<size_type>(res);
|
||||
return array_index_status::ok;
|
||||
}
|
||||
|
||||
/*!
|
||||
@param[in] s reference token to be converted into an array index
|
||||
|
||||
@@ -253,39 +319,30 @@ class json_pointer
|
||||
template<typename BasicJsonType>
|
||||
static typename BasicJsonType::size_type array_index(const string_t& s)
|
||||
{
|
||||
using size_type = typename BasicJsonType::size_type;
|
||||
typename BasicJsonType::size_type idx{};
|
||||
const auto status = parse_array_index<BasicJsonType>(s, idx);
|
||||
|
||||
// error condition (cf. RFC 6901, Sect. 4)
|
||||
if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0'))
|
||||
if (JSON_HEDLEY_UNLIKELY(status == array_index_status::leading_zero))
|
||||
{
|
||||
JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr));
|
||||
}
|
||||
|
||||
// error condition (cf. RFC 6901, Sect. 4)
|
||||
if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))
|
||||
if (JSON_HEDLEY_UNLIKELY(status == array_index_status::not_a_number))
|
||||
{
|
||||
JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", s, "' is not a number"), nullptr));
|
||||
}
|
||||
|
||||
const char* p = s.data();
|
||||
char* p_end = nullptr; // NOLINT(misc-const-correctness)
|
||||
errno = 0; // strtoull doesn't reset errno
|
||||
const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int)
|
||||
if (p == p_end // invalid input or empty string
|
||||
|| errno == ERANGE // out of range
|
||||
|| JSON_HEDLEY_UNLIKELY(static_cast<std::size_t>(p_end - p) != s.size())) // incomplete read
|
||||
if (JSON_HEDLEY_UNLIKELY(status == array_index_status::unresolved))
|
||||
{
|
||||
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", s, "'"), nullptr));
|
||||
}
|
||||
|
||||
// only triggered on special platforms (like 32bit), see also
|
||||
// https://github.com/nlohmann/json/pull/2203
|
||||
if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)())) // NOLINT(runtime/int)
|
||||
if (JSON_HEDLEY_UNLIKELY(status == array_index_status::exceeds_size_type))
|
||||
{
|
||||
JSON_THROW(detail::out_of_range::create(410, detail::concat("array index ", s, " exceeds size_type"), nullptr)); // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
return static_cast<size_type>(res);
|
||||
return idx;
|
||||
}
|
||||
|
||||
JSON_PRIVATE_UNLESS_TESTED:
|
||||
@@ -584,63 +641,6 @@ class json_pointer
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
@throw parse_error.106 if an array index begins with '0'
|
||||
@throw parse_error.109 if an array index was not a number
|
||||
@throw out_of_range.402 if the array index '-' is used
|
||||
@throw out_of_range.404 if the JSON pointer can not be resolved
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
const BasicJsonType& get_checked(const BasicJsonType* ptr) const
|
||||
{
|
||||
for (const auto& reference_token : reference_tokens)
|
||||
{
|
||||
switch (ptr->type())
|
||||
{
|
||||
case detail::value_t::object:
|
||||
{
|
||||
// note: at performs range check
|
||||
ptr = &ptr->at(reference_token);
|
||||
break;
|
||||
}
|
||||
|
||||
case detail::value_t::array:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
|
||||
{
|
||||
// "-" always fails the range check
|
||||
JSON_THROW(detail::out_of_range::create(402, detail::concat(
|
||||
"array index '-' (", std::to_string(ptr->m_data.m_value.array->size()),
|
||||
") is out of range"), ptr));
|
||||
}
|
||||
|
||||
const auto idx = array_index<BasicJsonType>(reference_token);
|
||||
// Bounds check before access to avoid exception with JSON_NOEXCEPTION
|
||||
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
||||
{
|
||||
JSON_THROW(detail::out_of_range::create(401, detail::concat(
|
||||
"array index ", std::to_string(idx), " is out of range"), ptr));
|
||||
}
|
||||
ptr = &ptr->operator[](idx);
|
||||
break;
|
||||
}
|
||||
|
||||
case detail::value_t::null:
|
||||
case detail::value_t::string:
|
||||
case detail::value_t::boolean:
|
||||
case detail::value_t::number_integer:
|
||||
case detail::value_t::number_unsigned:
|
||||
case detail::value_t::number_float:
|
||||
case detail::value_t::binary:
|
||||
case detail::value_t::discarded:
|
||||
default:
|
||||
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
|
||||
}
|
||||
}
|
||||
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief return a pointer to the pointed to value, or `nullptr` if the
|
||||
pointer cannot be resolved because a key is missing, an array
|
||||
@@ -679,18 +679,23 @@ class json_pointer
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// may throw parse_error.106/109 for a malformed index; an
|
||||
// a malformed index still throws parse_error.106/109; an
|
||||
// index that is syntactically valid but cannot be
|
||||
// represented (out_of_range.404/410) is treated like an
|
||||
// out-of-range index below
|
||||
typename BasicJsonType::size_type idx{};
|
||||
JSON_TRY
|
||||
switch (parse_array_index<BasicJsonType>(reference_token, idx))
|
||||
{
|
||||
idx = array_index<BasicJsonType>(reference_token);
|
||||
}
|
||||
JSON_INTERNAL_CATCH (detail::out_of_range&)
|
||||
{
|
||||
return nullptr;
|
||||
case array_index_status::leading_zero:
|
||||
JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", reference_token, "' must not begin with '0'"), nullptr));
|
||||
case array_index_status::not_a_number:
|
||||
JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", reference_token, "' is not a number"), nullptr));
|
||||
case array_index_status::unresolved:
|
||||
case array_index_status::exceeds_size_type:
|
||||
return nullptr;
|
||||
case array_index_status::ok:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
||||
@@ -718,8 +723,8 @@ class json_pointer
|
||||
}
|
||||
|
||||
/*!
|
||||
@throw parse_error.106 if an array index begins with '0'
|
||||
@throw parse_error.109 if an array index was not a number
|
||||
@note unlike array_index(), this never throws: a malformed or unrepresentable
|
||||
array index reference token is treated like a missing key (see #5395)
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
bool contains(const BasicJsonType* ptr) const
|
||||
@@ -747,49 +752,17 @@ class json_pointer
|
||||
// "-" always fails the range check
|
||||
return false;
|
||||
}
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_token.empty()))
|
||||
{
|
||||
// an empty reference token is not an array index; array_index()
|
||||
// would throw out_of_range.404 -- contains() must not throw (see #5395)
|
||||
return false;
|
||||
}
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !('0' <= reference_token[0] && reference_token[0] <= '9')))
|
||||
{
|
||||
// invalid char
|
||||
return false;
|
||||
}
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1))
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9')))
|
||||
{
|
||||
// the first char should be between '1' and '9'
|
||||
return false;
|
||||
}
|
||||
for (std::size_t i = 1; i < reference_token.size(); i++)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9')))
|
||||
{
|
||||
// other char should be between '0' and '9'
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the reference token consists only of digits at this point (cf. checks
|
||||
// above); however, its numeric value might not be representable, in which
|
||||
// case array_index() would throw out_of_range.404/410 -- contains() must
|
||||
// not throw (see #5395), so such a reference token is treated as "not found"
|
||||
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 // the value exceeds ULLONG_MAX
|
||||
|| magnitude >= static_cast<unsigned long long>((std::numeric_limits<typename BasicJsonType::size_type>::max)()))) // NOLINT(runtime/int)
|
||||
// any parse failure (malformed index, or one that is syntactically
|
||||
// valid but not representable as size_type) means the reference
|
||||
// token cannot denote an existing array element -- contains() must
|
||||
// not throw (see #5395), so it is treated as "not found"
|
||||
typename BasicJsonType::size_type idx{};
|
||||
if (JSON_HEDLEY_UNLIKELY(parse_array_index<BasicJsonType>(reference_token, idx) != array_index_status::ok))
|
||||
{
|
||||
// the array index cannot be represented as size_type
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto idx = array_index<BasicJsonType>(reference_token);
|
||||
if (idx >= ptr->size())
|
||||
{
|
||||
// index out of range
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility> // declval, pair
|
||||
#include <nlohmann/detail/meta/detected.hpp>
|
||||
#include <nlohmann/thirdparty/hedley/hedley.hpp>
|
||||
|
||||
// This file contains all internal macro definitions (except those affecting ABI)
|
||||
@@ -140,10 +139,12 @@
|
||||
// libstdc++ < 11 has incomplete C++20 ranges (issue #4440)
|
||||
#elif defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE < 11
|
||||
#define JSON_HAS_RANGES 0
|
||||
// libc++ < 16 has incomplete C++20 ranges (issue #4440)
|
||||
// clang < 16 with libstdc++ does not implement the ranges customization
|
||||
// points libstdc++ declares, so its C++20 ranges support is incomplete (issue #5161)
|
||||
#elif defined(__clang__) && !defined(__apple_build_version__) \
|
||||
&& __clang_major__ < 16 && defined(__GLIBCXX__)
|
||||
#define JSON_HAS_RANGES 0
|
||||
// libc++ < 16 has incomplete C++20 ranges (issue #4440)
|
||||
#elif defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 160000
|
||||
#define JSON_HAS_RANGES 0
|
||||
// nvcc CUDA 12.0/12.1 chokes on the enable_borrowed_range variable-template
|
||||
@@ -158,6 +159,18 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// std::ranges view conversion (to_json/is_compatible_array_type_impl) additionally
|
||||
// needs to be disabled on MinGW, whose std::ranges support is incomplete
|
||||
// (issue #4916); this macro combines both conditions so the check and its
|
||||
// reason are not duplicated at every use site.
|
||||
#ifndef JSON_HAS_RANGE_VIEW_CONVERSION
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
#define JSON_HAS_RANGE_VIEW_CONVERSION 1
|
||||
#else
|
||||
#define JSON_HAS_RANGE_VIEW_CONVERSION 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef JSON_HAS_STD_FORMAT
|
||||
#if defined(JSON_HAS_CPP_20) && defined(__cpp_lib_format)
|
||||
#define JSON_HAS_STD_FORMAT 1
|
||||
@@ -286,26 +299,11 @@
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
@brief function to wrap JSON_THROW_MACRO - there can be compilation errors about
|
||||
there being no arguments to JSON_THROW that depend on template arguments
|
||||
if this is not used to call JSON_THROW
|
||||
*/
|
||||
template<typename ExceptionType>
|
||||
void templated_json_throw(ExceptionType exception)
|
||||
{
|
||||
JSON_THROW(exception);
|
||||
|
||||
/* JSON_THROW(exception) discards exception and aborts - void cast needed to supress
|
||||
compilation error if compiled with -Werror and Wunused-parameter */
|
||||
(void)exception;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief macro to briefly define a mapping between an enum and JSON with exception
|
||||
on invalid input
|
||||
@def NLOHMANN_JSON_SERIALIZE_ENUM_STRICT
|
||||
@since version 3.12.0
|
||||
@since version 3.13.0
|
||||
*/
|
||||
#define NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(ENUM_TYPE, ...) \
|
||||
template<typename BasicJsonType> \
|
||||
@@ -321,7 +319,7 @@ void templated_json_throw(ExceptionType exception)
|
||||
return ej_pair.first == e; \
|
||||
}); \
|
||||
if (it != std::end(m)) j = it->second; \
|
||||
else templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(410,"enum value out of range for " #ENUM_TYPE, nullptr)); \
|
||||
else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(410,"enum value out of range for " #ENUM_TYPE, nullptr)); \
|
||||
} \
|
||||
template<typename BasicJsonType> \
|
||||
inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \
|
||||
@@ -336,7 +334,7 @@ void templated_json_throw(ExceptionType exception)
|
||||
return ej_pair.second == j; \
|
||||
}); \
|
||||
if (it != std::end(m)) e = it->first; \
|
||||
else templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(410, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", j.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace)), &j)); \
|
||||
else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(410, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", j.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace)), &j)); \
|
||||
}
|
||||
|
||||
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
|
||||
@@ -876,30 +874,6 @@ void templated_json_throw(ExceptionType exception)
|
||||
\
|
||||
template<typename... T> \
|
||||
using result_of_##std_name = decltype(std_name(std::declval<T>()...)); \
|
||||
} \
|
||||
\
|
||||
namespace detail2 { \
|
||||
struct std_name##_tag \
|
||||
{ \
|
||||
}; \
|
||||
\
|
||||
template<typename... T> \
|
||||
std_name##_tag std_name(T&&...); \
|
||||
\
|
||||
template<typename... T> \
|
||||
using result_of_##std_name = decltype(std_name(std::declval<T>()...)); \
|
||||
\
|
||||
template<typename... T> \
|
||||
struct would_call_std_##std_name \
|
||||
{ \
|
||||
static constexpr auto const value = ::nlohmann::detail:: \
|
||||
is_detected_exact<std_name##_tag, result_of_##std_name, T...>::value; \
|
||||
}; \
|
||||
} /* namespace detail2 */ \
|
||||
\
|
||||
template<typename... T> \
|
||||
struct would_call_std_##std_name : detail2::would_call_std_##std_name<T...> \
|
||||
{ \
|
||||
}
|
||||
|
||||
#ifndef JSON_USE_IMPLICIT_CONVERSIONS
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
||||
#undef JSON_HAS_THREE_WAY_COMPARISON
|
||||
#undef JSON_HAS_RANGES
|
||||
#undef JSON_HAS_RANGE_VIEW_CONVERSION
|
||||
#undef JSON_HAS_STD_FORMAT
|
||||
#undef JSON_HAS_STATIC_RTTI
|
||||
#undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin);
|
||||
NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin)
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end);
|
||||
NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end)
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
@@ -62,9 +62,5 @@ using detected_or_t = typename detected_or<Default, Op, Args...>::type;
|
||||
template<class Expected, template<class...> class Op, class... Args>
|
||||
using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;
|
||||
|
||||
template<class To, template<class...> class Op, class... Args>
|
||||
using is_detected_convertible =
|
||||
std::is_convertible<detected_t<Op, Args...>, To>;
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint> // size_t
|
||||
#include <cstddef> // size_t
|
||||
#include <utility> // declval
|
||||
#include <string> // string
|
||||
|
||||
@@ -70,37 +70,6 @@ using parse_error_function_t = decltype(std::declval<T&>().parse_error(
|
||||
std::declval<std::size_t>(), std::declval<const std::string&>(),
|
||||
std::declval<const Exception&>()));
|
||||
|
||||
template<typename SAX, typename BasicJsonType>
|
||||
struct is_sax
|
||||
{
|
||||
private:
|
||||
static_assert(is_basic_json<BasicJsonType>::value,
|
||||
"BasicJsonType must be of type basic_json<...>");
|
||||
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
using binary_t = typename BasicJsonType::binary_t;
|
||||
using exception_t = typename BasicJsonType::exception;
|
||||
|
||||
public:
|
||||
static constexpr bool value =
|
||||
is_detected_exact<bool, null_function_t, SAX>::value &&
|
||||
is_detected_exact<bool, boolean_function_t, SAX>::value &&
|
||||
is_detected_exact<bool, number_integer_function_t, SAX, number_integer_t>::value &&
|
||||
is_detected_exact<bool, number_unsigned_function_t, SAX, number_unsigned_t>::value &&
|
||||
is_detected_exact<bool, number_float_function_t, SAX, number_float_t, string_t>::value &&
|
||||
is_detected_exact<bool, string_function_t, SAX, string_t>::value &&
|
||||
is_detected_exact<bool, binary_function_t, SAX, binary_t>::value &&
|
||||
is_detected_exact<bool, start_object_function_t, SAX>::value &&
|
||||
is_detected_exact<bool, key_function_t, SAX, string_t>::value &&
|
||||
is_detected_exact<bool, end_object_function_t, SAX>::value &&
|
||||
is_detected_exact<bool, start_array_function_t, SAX>::value &&
|
||||
is_detected_exact<bool, end_array_function_t, SAX>::value &&
|
||||
is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value;
|
||||
};
|
||||
|
||||
template<typename SAX, typename BasicJsonType>
|
||||
struct is_sax_static_asserts
|
||||
{
|
||||
@@ -120,8 +89,6 @@ struct is_sax_static_asserts
|
||||
"Missing/invalid function: bool null()");
|
||||
static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,
|
||||
"Missing/invalid function: bool boolean(bool)");
|
||||
static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,
|
||||
"Missing/invalid function: bool boolean(bool)");
|
||||
static_assert(
|
||||
is_detected_exact<bool, number_integer_function_t, SAX,
|
||||
number_integer_t>::value,
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
#ifdef JSON_HAS_CPP_17
|
||||
|
||||
template<bool... Booleans>
|
||||
struct cxpr_or_impl : std::integral_constant < bool, (Booleans || ...) > {};
|
||||
|
||||
template<bool... Booleans>
|
||||
struct cxpr_and_impl : std::integral_constant < bool, (Booleans &&...) > {};
|
||||
|
||||
#else
|
||||
|
||||
template<bool... Booleans>
|
||||
struct cxpr_or_impl : std::false_type {};
|
||||
|
||||
template<bool... Booleans>
|
||||
struct cxpr_or_impl<true, Booleans...> : std::true_type {};
|
||||
|
||||
template<bool... Booleans>
|
||||
struct cxpr_or_impl<false, Booleans...> : cxpr_or_impl<Booleans...> {};
|
||||
|
||||
template<bool... Booleans>
|
||||
struct cxpr_and_impl : std::true_type {};
|
||||
|
||||
template<bool... Booleans>
|
||||
struct cxpr_and_impl<true, Booleans...> : cxpr_and_impl<Booleans...> {};
|
||||
|
||||
template<bool... Booleans>
|
||||
struct cxpr_and_impl<false, Booleans...> : std::false_type {};
|
||||
|
||||
#endif
|
||||
|
||||
template<class Boolean>
|
||||
struct cxpr_not : std::integral_constant < bool, !Boolean::value > {};
|
||||
|
||||
template<class... Booleans>
|
||||
struct cxpr_or : cxpr_or_impl<Booleans::value...> {};
|
||||
|
||||
template<bool... Booleans>
|
||||
struct cxpr_or_c : cxpr_or_impl<Booleans...> {};
|
||||
|
||||
template<class... Booleans>
|
||||
struct cxpr_and : cxpr_and_impl<Booleans::value...> {};
|
||||
|
||||
template<bool... Booleans>
|
||||
struct cxpr_and_c : cxpr_and_impl<Booleans...> {};
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -283,6 +283,13 @@ template<class B, class... Bn>
|
||||
struct conjunction<B, Bn...>
|
||||
: std::conditional<static_cast<bool>(B::value), conjunction<Bn...>, B>::type {};
|
||||
|
||||
// https://en.cppreference.com/w/cpp/types/disjunction
|
||||
template<class...> struct disjunction : std::false_type { };
|
||||
template<class B> struct disjunction<B> : B { };
|
||||
template<class B, class... Bn>
|
||||
struct disjunction<B, Bn...>
|
||||
: std::conditional<static_cast<bool>(B::value), B, disjunction<Bn...>>::type {};
|
||||
|
||||
// https://en.cppreference.com/w/cpp/types/negation
|
||||
template<class B> struct negation : std::integral_constant < bool, !B::value > { };
|
||||
|
||||
@@ -477,9 +484,7 @@ template<typename T> struct is_range_view_optional_type<std::optional<T>> : std:
|
||||
template<typename T> struct is_range_view_optional_type : std::false_type {};
|
||||
#endif
|
||||
|
||||
// std::ranges does not work properly on MinGW due to incomplete C++20 support
|
||||
// see https://github.com/nlohmann/json/issues/4916
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
#if JSON_HAS_RANGE_VIEW_CONVERSION
|
||||
|
||||
// SafeToCheck guards against types that trigger circular constraints when
|
||||
// std::ranges::view<T> is evaluated on GCC 12 / libstdc++ 12:
|
||||
@@ -518,7 +523,7 @@ struct is_compatible_array_type_impl <
|
||||
// filter_view) can match BOTH this iterator-based specialization AND the view-based one
|
||||
// below, causing ambiguity. Exclude views here so the two specializations are mutually
|
||||
// exclusive: this one handles plain iterable containers, the other handles views.
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
#if JSON_HAS_RANGE_VIEW_CONVERSION
|
||||
&& !is_compatible_range_view<CompatibleArrayType>::value
|
||||
#endif
|
||||
>>
|
||||
@@ -528,7 +533,7 @@ struct is_compatible_array_type_impl <
|
||||
range_value_t<CompatibleArrayType>>::value;
|
||||
};
|
||||
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
#if JSON_HAS_RANGE_VIEW_CONVERSION
|
||||
template<typename BasicJsonType, typename CompatibleArrayType>
|
||||
struct is_compatible_array_type_impl <
|
||||
BasicJsonType, CompatibleArrayType,
|
||||
@@ -604,7 +609,6 @@ struct is_compatible_integer_type_impl <
|
||||
std::is_integral<CompatibleNumberIntegerType>::value&&
|
||||
!std::is_same<bool, CompatibleNumberIntegerType>::value >>
|
||||
{
|
||||
// is there an assert somewhere on overflows?
|
||||
using RealLimits = std::numeric_limits<RealIntegerType>;
|
||||
using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;
|
||||
|
||||
@@ -798,20 +802,7 @@ struct has_capacity : std::integral_constant<bool, is_detected<detect_capacity,
|
||||
// a naive helper to check if a type is an ordered_map (exploits the fact that
|
||||
// ordered_map inherits capacity() from std::vector)
|
||||
template <typename T>
|
||||
struct is_ordered_map
|
||||
{
|
||||
using one = char;
|
||||
|
||||
struct two
|
||||
{
|
||||
char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
};
|
||||
|
||||
template <typename C> static one test( decltype(&C::capacity) ) ;
|
||||
template <typename C> static two test(...);
|
||||
|
||||
enum { value = sizeof(test<T>(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg,cppcoreguidelines-use-enum-class)
|
||||
};
|
||||
struct is_ordered_map : has_capacity<T> {};
|
||||
|
||||
// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
|
||||
template < typename T, typename U, enable_if_t < !std::is_same<T, U>::value, int > = 0 >
|
||||
@@ -835,10 +826,8 @@ using all_signed = conjunction<std::is_signed<Types>...>;
|
||||
template<typename... Types>
|
||||
using all_unsigned = conjunction<std::is_unsigned<Types>...>;
|
||||
|
||||
// there's a disjunction trait in another PR; replace when merged
|
||||
template<typename... Types>
|
||||
using same_sign = std::integral_constant < bool,
|
||||
all_signed<Types...>::value || all_unsigned<Types...>::value >;
|
||||
using same_sign = disjunction<all_signed<Types...>, all_unsigned<Types...>>;
|
||||
|
||||
template<typename OfType, typename T>
|
||||
using never_out_of_range = std::integral_constant < bool,
|
||||
|
||||
+21
-48
@@ -1004,38 +1004,18 @@ 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_t = std::vector<copy_scratch_value_t, AllocatorType<copy_scratch_value_t>>;
|
||||
|
||||
/// @brief tag selecting the constructor below; used only to build the
|
||||
/// elements of a deep copy (@ref copy_array_level, @ref copy_object_level)
|
||||
struct copy_construct_tag {};
|
||||
|
||||
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
|
||||
, start_position(src.start_position)
|
||||
, end_position(src.end_position)
|
||||
#endif
|
||||
/// @brief copy everything of @a src into @a dst but its type and value
|
||||
static void copy_metadata(const basic_json& src, basic_json& dst)
|
||||
{
|
||||
}
|
||||
// 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));
|
||||
|
||||
private:
|
||||
#if JSON_DIAGNOSTIC_POSITIONS
|
||||
dst.start_position = src.start_position;
|
||||
dst.end_position = src.end_position;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief copy the value of @a src into @a dst, which must not be structured
|
||||
@@ -1099,11 +1079,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
}
|
||||
|
||||
/*!
|
||||
@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
|
||||
@brief copy everything of @a src into the null value @a dst but the children
|
||||
|
||||
@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
|
||||
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
|
||||
@@ -1111,6 +1088,8 @@ 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)
|
||||
{
|
||||
copy_metadata(src, dst);
|
||||
|
||||
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
|
||||
@@ -1131,19 +1110,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
{
|
||||
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>();
|
||||
// only now that the array exists may dst stop being a null value
|
||||
dst.m_data.m_type = value_t::array;
|
||||
|
||||
// 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);
|
||||
}
|
||||
dst.m_data.m_value.array->resize(src_array.size());
|
||||
|
||||
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)
|
||||
@@ -1161,14 +1136,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
|
||||
// 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
|
||||
// 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
|
||||
// types that are backed by a vector, such as nlohmann::ordered_map
|
||||
scratch.clear();
|
||||
scratch.reserve(src_object.size());
|
||||
for (const auto& element : src_object)
|
||||
{
|
||||
scratch.emplace_back(element.first, basic_json(copy_construct_tag{}, element.second));
|
||||
scratch.emplace_back(element.first, basic_json());
|
||||
}
|
||||
|
||||
dst.m_data.m_value.object = create<object_t>(std::make_move_iterator(scratch.begin()),
|
||||
|
||||
+317
-528
File diff suppressed because it is too large
Load Diff
@@ -430,6 +430,37 @@ TEST_CASE("value conversion")
|
||||
CHECK(std::equal(std::begin(nbs[0][0][0]), std::end(nbs[1][1][1]), std::begin(nbs2[0][0][0])));
|
||||
}
|
||||
|
||||
SECTION("built-in arrays: 5D")
|
||||
{
|
||||
// NOLINTBEGIN(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
const int nbs[1][1][1][2][2] = {{{{{0, 1}, {2, 3}}}}};
|
||||
int nbs2[1][1][1][2][2] = {{{{{0, 0}, {0, 0}}}}};
|
||||
// NOLINTEND(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
const json j2 = nbs;
|
||||
j2.get_to(nbs2);
|
||||
CHECK(std::equal(std::begin(nbs[0][0][0][0]), std::end(nbs[0][0][0][1]), std::begin(nbs2[0][0][0][0])));
|
||||
}
|
||||
|
||||
SECTION("built-in arrays: mismatched shape")
|
||||
{
|
||||
// NOLINTBEGIN(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
int nbs2[2][3] = {{0, 0, 0}, {0, 0, 0}};
|
||||
// NOLINTEND(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
SECTION("not an array")
|
||||
{
|
||||
const json j2 = 42;
|
||||
CHECK_THROWS_WITH_AS(j2.get_to(nbs2), "[json.exception.type_error.304] cannot use at() with number", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("too few elements")
|
||||
{
|
||||
const json j2 = {{0, 1, 2}};
|
||||
CHECK_THROWS_WITH_AS(j2.get_to(nbs2), "[json.exception.out_of_range.401] array index 1 is out of range", json::out_of_range&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("std::deque<json>")
|
||||
{
|
||||
std::deque<json> a{"previous", "value"};
|
||||
@@ -1726,6 +1757,34 @@ NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(StrictTaskState,
|
||||
{STRICT_TS_COMPLETED, "completed"},
|
||||
})
|
||||
|
||||
// regression test for #5708 item 2: NLOHMANN_JSON_SERIALIZE_ENUM_STRICT must not rely on
|
||||
// unqualified lookup of a helper name that a user's own namespace may also declare
|
||||
namespace ns_with_colliding_name
|
||||
{
|
||||
// NOLINTNEXTLINE(misc-use-internal-linkage) - used to shadow the library's internal helper name
|
||||
inline void templated_json_throw(int /*unused*/) {}
|
||||
|
||||
enum class colliding_enum { a, b };
|
||||
|
||||
// NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(colliding_enum,
|
||||
{
|
||||
{colliding_enum::a, "a"},
|
||||
{colliding_enum::b, "b"}
|
||||
})
|
||||
} // namespace ns_with_colliding_name
|
||||
|
||||
TEST_CASE("NLOHMANN_JSON_SERIALIZE_ENUM_STRICT in a namespace with a colliding name")
|
||||
{
|
||||
using ns_with_colliding_name::colliding_enum;
|
||||
|
||||
CHECK(json(colliding_enum::a) == "a");
|
||||
CHECK(colliding_enum::b == json("b"));
|
||||
|
||||
json _;
|
||||
CHECK_THROWS_WITH_AS(_ = json("nope").get<colliding_enum>(), "[json.exception.out_of_range.410] enum value out of range for colliding_enum: \"nope\"", json::out_of_range&);
|
||||
}
|
||||
|
||||
TEST_CASE("Strict JSON to enum mapping")
|
||||
{
|
||||
SECTION("enum class")
|
||||
|
||||
@@ -405,73 +405,3 @@ TEST_CASE("JSON Visit Node")
|
||||
);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,6 +516,16 @@ TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_j
|
||||
CHECK(j_array.value("/-"_json_pointer, 42) == 42);
|
||||
CHECK(j_array_const.value("/-"_json_pointer, 42) == 42);
|
||||
|
||||
// Test an index that is syntactically valid but exceeds size_type, and one
|
||||
// with a trailing non-digit: both must yield the default value rather than
|
||||
// throw, even with exceptions disabled (regression test for #5708 item 4 /
|
||||
// #5672: get_checked_or_null() no longer relies on a JSON_TRY/
|
||||
// JSON_INTERNAL_CATCH around array_index() to turn these into "not found")
|
||||
CHECK(j_array.value("/18446744073709551615"_json_pointer, 42) == 42);
|
||||
CHECK(j_array_const.value("/18446744073709551615"_json_pointer, 42) == 42);
|
||||
CHECK(j_array.value("/1a"_json_pointer, 42) == 42);
|
||||
CHECK(j_array_const.value("/1a"_json_pointer, 42) == 42);
|
||||
|
||||
#if !defined(JSON_NOEXCEPTION)
|
||||
// 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&);
|
||||
|
||||
@@ -10,10 +10,14 @@
|
||||
|
||||
#if JSON_TEST_USING_MULTIPLE_HEADERS
|
||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||
#include <nlohmann/ordered_map.hpp>
|
||||
#else
|
||||
#include <nlohmann/json.hpp>
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
TEST_CASE("type traits")
|
||||
{
|
||||
SECTION("is_c_string")
|
||||
@@ -83,4 +87,12 @@ TEST_CASE("type traits")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("is_ordered_map")
|
||||
{
|
||||
using nlohmann::detail::is_ordered_map;
|
||||
|
||||
CHECK(is_ordered_map<nlohmann::ordered_map<std::string, int>>::value);
|
||||
CHECK_FALSE(is_ordered_map<std::map<std::string, int>>::value);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user