mirror of
https://github.com/nlohmann/json.git
synced 2026-10-05 06:00:29 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
994b83cd17 | ||
|
|
dd2dd45abd | ||
|
|
362a902170 | ||
|
|
ba6bf32f6d | ||
|
|
056d187986 | ||
|
|
90078597c7 | ||
|
|
fa28fff7d6 | ||
|
|
e9c84befa1 | ||
|
|
8a26f2dc8f | ||
|
|
2c108d0b56 |
@@ -141,14 +141,8 @@ The library uses the following mapping from JSON values types to BJData types ac
|
|||||||
parsed back as a regular array,
|
parsed back as a regular array,
|
||||||
- every entry of `"_ArraySize_"` is a positive integer, and their product is representable as a `std::size_t`,
|
- every entry of `"_ArraySize_"` is a positive integer, and their product is representable as a `std::size_t`,
|
||||||
- `"_ArrayData_"` is an array holding exactly that many elements, and
|
- `"_ArrayData_"` is an array holding exactly that many elements, and
|
||||||
- every element of `"_ArrayData_"` is a number of the kind named by `"_ArrayType_"`: for the integer types, a
|
- every element of `"_ArrayData_"` is a number of the kind named by `"_ArrayType_"` (a floating-point number for
|
||||||
value that fits the named width; for `double`, any value; for `single`, a value that survives narrowing to
|
`single` and `double`, an integer otherwise).
|
||||||
`float` and back without change (for instance, `0.1` does not, since it is not exactly representable as
|
|
||||||
`float`).
|
|
||||||
|
|
||||||
An annotated object is always read back with its keys in the order shown above, `"_ArrayType_"`, `"_ArraySize_"`,
|
|
||||||
`"_ArrayData_"`, regardless of the order the ND-array's header stores them in on the wire. This matters for
|
|
||||||
`ordered_json`, whose comparison takes key order into account.
|
|
||||||
|
|
||||||
The current version of this library does not yet support automatic detection of and conversion from a nested JSON
|
The current version of this library does not yet support automatic detection of and conversion from a nested JSON
|
||||||
array input to a BJData ND-array.
|
array input to a BJData ND-array.
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ namespace detail
|
|||||||
* j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an
|
* 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
|
* allocated value (e.g., a string). See bug issue
|
||||||
* https://github.com/nlohmann/json/issues/2865 for more information.
|
* 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;
|
template<value_t> struct external_constructor;
|
||||||
@@ -65,18 +69,20 @@ struct external_constructor<value_t::string>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(s);
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::string;
|
j.m_data.m_type = value_t::string;
|
||||||
j.m_data.m_value = s;
|
j.m_data.m_value = value;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(std::move(s));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::string;
|
j.m_data.m_type = value_t::string;
|
||||||
j.m_data.m_value = std::move(s);
|
j.m_data.m_value = value;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,9 +91,10 @@ struct external_constructor<value_t::string>
|
|||||||
int > = 0 >
|
int > = 0 >
|
||||||
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
||||||
{
|
{
|
||||||
|
auto* created = j.template create<typename BasicJsonType::string_t>(str);
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::string;
|
j.m_data.m_type = value_t::string;
|
||||||
j.m_data.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
|
j.m_data.m_value.string = created;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -98,18 +105,20 @@ struct external_constructor<value_t::binary>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(b);
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::binary;
|
j.m_data.m_type = value_t::binary;
|
||||||
j.m_data.m_value = typename BasicJsonType::binary_t(b);
|
j.m_data.m_value = value;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(std::move(b));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::binary;
|
j.m_data.m_type = value_t::binary;
|
||||||
j.m_data.m_value = typename BasicJsonType::binary_t(std::move(b));
|
j.m_data.m_value = value;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -159,9 +168,10 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(arr);
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = arr;
|
j.m_data.m_value = value;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -169,9 +179,10 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(std::move(arr));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = std::move(arr);
|
j.m_data.m_value = value;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -187,9 +198,10 @@ struct external_constructor<value_t::array>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
|
auto* created = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
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.m_data.m_value.array = created;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -197,15 +209,17 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
||||||
{
|
{
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
typename BasicJsonType::array_t elements;
|
||||||
j.m_data.m_type = value_t::array;
|
elements.reserve(arr.size());
|
||||||
j.m_data.m_value = value_t::array;
|
|
||||||
j.m_data.m_value.array->reserve(arr.size());
|
|
||||||
for (const bool x : arr)
|
for (const bool x : arr)
|
||||||
{
|
{
|
||||||
j.m_data.m_value.array->push_back(x);
|
elements.push_back(x);
|
||||||
j.set_parent(j.m_data.m_value.array->back());
|
|
||||||
}
|
}
|
||||||
|
const typename BasicJsonType::json_value value(std::move(elements));
|
||||||
|
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;
|
||||||
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,11 +227,12 @@ struct external_constructor<value_t::array>
|
|||||||
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
||||||
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
||||||
{
|
{
|
||||||
|
typename BasicJsonType::array_t elements(arr.size());
|
||||||
|
std::copy(std::begin(arr), std::end(arr), elements.begin());
|
||||||
|
const typename BasicJsonType::json_value value(std::move(elements));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = value_t::array;
|
j.m_data.m_value = value;
|
||||||
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.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -227,13 +242,15 @@ struct external_constructor<value_t::array>
|
|||||||
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
|
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
|
||||||
static void construct(BasicJsonType& j, CompatibleArrayType && arr)
|
static void construct(BasicJsonType& j, CompatibleArrayType && arr)
|
||||||
{
|
{
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
typename BasicJsonType::array_t elements;
|
||||||
j.m_data.m_type = value_t::array;
|
|
||||||
j.m_data.m_value = value_t::array;
|
|
||||||
for (auto&& x : std::forward<CompatibleArrayType>(arr))
|
for (auto&& x : std::forward<CompatibleArrayType>(arr))
|
||||||
{
|
{
|
||||||
j.m_data.m_value.array->push_back(x);
|
elements.push_back(x);
|
||||||
}
|
}
|
||||||
|
const typename BasicJsonType::json_value value(std::move(elements));
|
||||||
|
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;
|
||||||
// set the parents only once all elements are in place: a push_back
|
// set the parents only once all elements are in place: a push_back
|
||||||
// that reallocates moves the earlier elements, which does not keep
|
// that reallocates moves the earlier elements, which does not keep
|
||||||
// their parent pointers
|
// their parent pointers
|
||||||
@@ -249,9 +266,10 @@ struct external_constructor<value_t::object>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(obj);
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::object;
|
j.m_data.m_type = value_t::object;
|
||||||
j.m_data.m_value = obj;
|
j.m_data.m_value = value;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -259,9 +277,10 @@ struct external_constructor<value_t::object>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(std::move(obj));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::object;
|
j.m_data.m_type = value_t::object;
|
||||||
j.m_data.m_value = std::move(obj);
|
j.m_data.m_value = value;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -273,9 +292,10 @@ struct external_constructor<value_t::object>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
|
auto* created = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::object;
|
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.m_data.m_value.object = created;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2701,15 +2701,10 @@ class binary_reader
|
|||||||
is_ndarray can only return `true` when its initial value
|
is_ndarray can only return `true` when its initial value
|
||||||
is `false`
|
is `false`
|
||||||
@param[in] prefix type marker if already read, otherwise set to 0
|
@param[in] prefix type marker if already read, otherwise set to 0
|
||||||
@param[in] ndarray_dtype the element type marker of the enclosing bjdata ndarray if
|
|
||||||
already known (it precedes the dimension vector read here),
|
|
||||||
otherwise 0; used to emit the "_ArrayType_" annotation key
|
|
||||||
before "_ArraySize_" if a dimension vector turns out to
|
|
||||||
describe an ndarray
|
|
||||||
|
|
||||||
@return whether size determination completed
|
@return whether size determination completed
|
||||||
*/
|
*/
|
||||||
bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0, char_int_type ndarray_dtype = 0)
|
bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0)
|
||||||
{
|
{
|
||||||
if (prefix == 0)
|
if (prefix == 0)
|
||||||
{
|
{
|
||||||
@@ -2822,34 +2817,8 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// the element type precedes the dimension vector (see get_ubjson_size_type)
|
|
||||||
// and is passed down as ndarray_dtype; emit it here so the annotation keys
|
|
||||||
// follow the documented _ArrayType_, _ArraySize_, _ArrayData_ order
|
|
||||||
if (ndarray_dtype != 0)
|
|
||||||
{
|
|
||||||
const char* type_name = bjd_type_name(ndarray_dtype);
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
|
|
||||||
{
|
|
||||||
auto last_token = get_token_string();
|
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
|
||||||
exception_message(input_format, "invalid byte: 0x" + last_token, "type"), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
string_t type_key = "_ArrayType_";
|
|
||||||
string_t type = type_name; // sax->string() takes a reference
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(type_key) || !sax->string(type)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string_t key = "_ArraySize_";
|
string_t key = "_ArraySize_";
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(dim.size())))
|
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3) || !sax->key(key) || !sax->start_array(dim.size())))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2957,7 +2926,7 @@ class binary_reader
|
|||||||
exception_message(input_format, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
|
exception_message(input_format, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool is_error = get_ubjson_size_value(result.first, is_ndarray, 0, result.second);
|
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
||||||
// an ndarray was read here only if the flag flipped; when it was
|
// an ndarray was read here only if the flag flipped; when it was
|
||||||
// seeded true, get_ubjson_size_value() already rejected the nested
|
// seeded true, get_ubjson_size_value() already rejected the nested
|
||||||
// dimension vector
|
// dimension vector
|
||||||
@@ -3150,17 +3119,27 @@ class binary_reader
|
|||||||
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
||||||
{
|
{
|
||||||
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
||||||
|
const char* type_name = bjd_type_name(size_and_type.second);
|
||||||
|
string_t key = "_ArrayType_";
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
|
||||||
|
{
|
||||||
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(input_format, "invalid byte: 0x" + last_token, "type"), nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
string_t type = type_name; // sax->string() takes a reference
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->string(type)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// the "_ArrayType_" and "_ArraySize_" annotation keys were already emitted by
|
|
||||||
// get_ubjson_size_value() (the type marker is known before the dimension vector
|
|
||||||
// that determines size_and_type.first is read, so it is emitted first there to
|
|
||||||
// match the documented _ArrayType_, _ArraySize_, _ArrayData_ key order)
|
|
||||||
if (size_and_type.second == 'C' || size_and_type.second == 'B')
|
if (size_and_type.second == 'C' || size_and_type.second == 'B')
|
||||||
{
|
{
|
||||||
size_and_type.second = 'U';
|
size_and_type.second = 'U';
|
||||||
}
|
}
|
||||||
|
|
||||||
string_t key = "_ArrayData_";
|
key = "_ArrayData_";
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) ))
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) ))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1817,31 +1817,16 @@ class binary_writer
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief validate (dry_run) or write one BJData ND-array element of dtype 'd' (single precision)
|
@brief validate (dry_run) or write one BJData ND-array element of dtype 'd' (single precision)
|
||||||
@return whether @a el's value survives narrowing to float and back without any change
|
@return whether @a el's value fits a float without overflow; always true when @a dry_run is false
|
||||||
(so the ND-array round-trips exactly), or is infinite or NaN; always true when
|
|
||||||
@a dry_run is false
|
|
||||||
*/
|
*/
|
||||||
bool write_bjdata_ndarray_float_element(const BasicJsonType& el, const bool dry_run)
|
bool write_bjdata_ndarray_float_element(const BasicJsonType& el, const bool dry_run)
|
||||||
{
|
{
|
||||||
const auto dval = el.template get<double>();
|
const auto dval = el.template get<double>();
|
||||||
if (dry_run)
|
if (dry_run)
|
||||||
{
|
{
|
||||||
#ifdef __GNUC__
|
return !std::isfinite(dval) ||
|
||||||
JSON_HEDLEY_DIAGNOSTIC_PUSH
|
(dval >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
|
||||||
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
|
dval <= static_cast<double>((std::numeric_limits<float>::max)()));
|
||||||
#endif
|
|
||||||
// a value that would be rounded (rather than exactly represented) by the
|
|
||||||
// narrowing to float is treated like an out-of-range integer element; this
|
|
||||||
// is the same criterion write_compact_float() uses for CBOR/MessagePack
|
|
||||||
const bool in_range = std::isnan(dval) ||
|
|
||||||
(dval >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
|
|
||||||
dval <= static_cast<double>((std::numeric_limits<float>::max)()) &&
|
|
||||||
static_cast<double>(static_cast<float>(dval)) == dval) ||
|
|
||||||
std::isinf(dval);
|
|
||||||
#ifdef __GNUC__
|
|
||||||
JSON_HEDLEY_DIAGNOSTIC_POP
|
|
||||||
#endif
|
|
||||||
return in_range;
|
|
||||||
}
|
}
|
||||||
write_number(static_cast<float>(dval), true);
|
write_number(static_cast<float>(dval), true);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -2056,8 +2056,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
if (is_an_object)
|
if (is_an_object)
|
||||||
{
|
{
|
||||||
// the initializer list is a list of pairs -> create an object
|
// the initializer list is a list of pairs -> create an object
|
||||||
m_data.m_type = value_t::object;
|
|
||||||
m_data.m_value = value_t::object;
|
m_data.m_value = value_t::object;
|
||||||
|
m_data.m_type = value_t::object;
|
||||||
|
|
||||||
for (auto& element_ref : init)
|
for (auto& element_ref : init)
|
||||||
{
|
{
|
||||||
@@ -2079,8 +2079,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// the initializer list describes an array -> create an array
|
// the initializer list describes an array -> create an array
|
||||||
m_data.m_type = value_t::array;
|
|
||||||
m_data.m_value.array = create<array_t>(init.begin(), init.end());
|
m_data.m_value.array = create<array_t>(init.begin(), init.end());
|
||||||
|
m_data.m_type = value_t::array;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_parents();
|
set_parents();
|
||||||
@@ -2093,8 +2093,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(const typename binary_t::container_type& init)
|
static basic_json binary(const typename binary_t::container_type& init)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_type = value_t::binary;
|
|
||||||
res.m_data.m_value = init;
|
res.m_data.m_value = init;
|
||||||
|
res.m_data.m_type = value_t::binary;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2104,8 +2104,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
|
static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_type = value_t::binary;
|
|
||||||
res.m_data.m_value = binary_t(init, subtype);
|
res.m_data.m_value = binary_t(init, subtype);
|
||||||
|
res.m_data.m_type = value_t::binary;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2115,8 +2115,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(typename binary_t::container_type&& init)
|
static basic_json binary(typename binary_t::container_type&& init)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_type = value_t::binary;
|
|
||||||
res.m_data.m_value = std::move(init);
|
res.m_data.m_value = std::move(init);
|
||||||
|
res.m_data.m_type = value_t::binary;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2126,8 +2126,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
|
static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_type = value_t::binary;
|
|
||||||
res.m_data.m_value = binary_t(std::move(init), subtype);
|
res.m_data.m_value = binary_t(std::move(init), subtype);
|
||||||
|
res.m_data.m_type = value_t::binary;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6747,6 +6747,10 @@ namespace detail
|
|||||||
* j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an
|
* 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
|
* allocated value (e.g., a string). See bug issue
|
||||||
* https://github.com/nlohmann/json/issues/2865 for more information.
|
* 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;
|
template<value_t> struct external_constructor;
|
||||||
@@ -6770,18 +6774,20 @@ struct external_constructor<value_t::string>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(s);
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::string;
|
j.m_data.m_type = value_t::string;
|
||||||
j.m_data.m_value = s;
|
j.m_data.m_value = value;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(std::move(s));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::string;
|
j.m_data.m_type = value_t::string;
|
||||||
j.m_data.m_value = std::move(s);
|
j.m_data.m_value = value;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6790,9 +6796,10 @@ struct external_constructor<value_t::string>
|
|||||||
int > = 0 >
|
int > = 0 >
|
||||||
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
||||||
{
|
{
|
||||||
|
auto* created = j.template create<typename BasicJsonType::string_t>(str);
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::string;
|
j.m_data.m_type = value_t::string;
|
||||||
j.m_data.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
|
j.m_data.m_value.string = created;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -6803,18 +6810,20 @@ struct external_constructor<value_t::binary>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(b);
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::binary;
|
j.m_data.m_type = value_t::binary;
|
||||||
j.m_data.m_value = typename BasicJsonType::binary_t(b);
|
j.m_data.m_value = value;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(std::move(b));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::binary;
|
j.m_data.m_type = value_t::binary;
|
||||||
j.m_data.m_value = typename BasicJsonType::binary_t(std::move(b));
|
j.m_data.m_value = value;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -6864,9 +6873,10 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(arr);
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = arr;
|
j.m_data.m_value = value;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -6874,9 +6884,10 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(std::move(arr));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = std::move(arr);
|
j.m_data.m_value = value;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -6892,9 +6903,10 @@ struct external_constructor<value_t::array>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
|
auto* created = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
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.m_data.m_value.array = created;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -6902,15 +6914,17 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
||||||
{
|
{
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
typename BasicJsonType::array_t elements;
|
||||||
j.m_data.m_type = value_t::array;
|
elements.reserve(arr.size());
|
||||||
j.m_data.m_value = value_t::array;
|
|
||||||
j.m_data.m_value.array->reserve(arr.size());
|
|
||||||
for (const bool x : arr)
|
for (const bool x : arr)
|
||||||
{
|
{
|
||||||
j.m_data.m_value.array->push_back(x);
|
elements.push_back(x);
|
||||||
j.set_parent(j.m_data.m_value.array->back());
|
|
||||||
}
|
}
|
||||||
|
const typename BasicJsonType::json_value value(std::move(elements));
|
||||||
|
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;
|
||||||
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6918,11 +6932,12 @@ struct external_constructor<value_t::array>
|
|||||||
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
||||||
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
||||||
{
|
{
|
||||||
|
typename BasicJsonType::array_t elements(arr.size());
|
||||||
|
std::copy(std::begin(arr), std::end(arr), elements.begin());
|
||||||
|
const typename BasicJsonType::json_value value(std::move(elements));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = value_t::array;
|
j.m_data.m_value = value;
|
||||||
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.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -6932,13 +6947,15 @@ struct external_constructor<value_t::array>
|
|||||||
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
|
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
|
||||||
static void construct(BasicJsonType& j, CompatibleArrayType && arr)
|
static void construct(BasicJsonType& j, CompatibleArrayType && arr)
|
||||||
{
|
{
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
typename BasicJsonType::array_t elements;
|
||||||
j.m_data.m_type = value_t::array;
|
|
||||||
j.m_data.m_value = value_t::array;
|
|
||||||
for (auto&& x : std::forward<CompatibleArrayType>(arr))
|
for (auto&& x : std::forward<CompatibleArrayType>(arr))
|
||||||
{
|
{
|
||||||
j.m_data.m_value.array->push_back(x);
|
elements.push_back(x);
|
||||||
}
|
}
|
||||||
|
const typename BasicJsonType::json_value value(std::move(elements));
|
||||||
|
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;
|
||||||
// set the parents only once all elements are in place: a push_back
|
// set the parents only once all elements are in place: a push_back
|
||||||
// that reallocates moves the earlier elements, which does not keep
|
// that reallocates moves the earlier elements, which does not keep
|
||||||
// their parent pointers
|
// their parent pointers
|
||||||
@@ -6954,9 +6971,10 @@ struct external_constructor<value_t::object>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(obj);
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::object;
|
j.m_data.m_type = value_t::object;
|
||||||
j.m_data.m_value = obj;
|
j.m_data.m_value = value;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -6964,9 +6982,10 @@ struct external_constructor<value_t::object>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
||||||
{
|
{
|
||||||
|
const typename BasicJsonType::json_value value(std::move(obj));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::object;
|
j.m_data.m_type = value_t::object;
|
||||||
j.m_data.m_value = std::move(obj);
|
j.m_data.m_value = value;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -6978,9 +6997,10 @@ struct external_constructor<value_t::object>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
|
auto* created = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::object;
|
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.m_data.m_value.object = created;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -16287,15 +16307,10 @@ class binary_reader
|
|||||||
is_ndarray can only return `true` when its initial value
|
is_ndarray can only return `true` when its initial value
|
||||||
is `false`
|
is `false`
|
||||||
@param[in] prefix type marker if already read, otherwise set to 0
|
@param[in] prefix type marker if already read, otherwise set to 0
|
||||||
@param[in] ndarray_dtype the element type marker of the enclosing bjdata ndarray if
|
|
||||||
already known (it precedes the dimension vector read here),
|
|
||||||
otherwise 0; used to emit the "_ArrayType_" annotation key
|
|
||||||
before "_ArraySize_" if a dimension vector turns out to
|
|
||||||
describe an ndarray
|
|
||||||
|
|
||||||
@return whether size determination completed
|
@return whether size determination completed
|
||||||
*/
|
*/
|
||||||
bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0, char_int_type ndarray_dtype = 0)
|
bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0)
|
||||||
{
|
{
|
||||||
if (prefix == 0)
|
if (prefix == 0)
|
||||||
{
|
{
|
||||||
@@ -16408,34 +16423,8 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// the element type precedes the dimension vector (see get_ubjson_size_type)
|
|
||||||
// and is passed down as ndarray_dtype; emit it here so the annotation keys
|
|
||||||
// follow the documented _ArrayType_, _ArraySize_, _ArrayData_ order
|
|
||||||
if (ndarray_dtype != 0)
|
|
||||||
{
|
|
||||||
const char* type_name = bjd_type_name(ndarray_dtype);
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
|
|
||||||
{
|
|
||||||
auto last_token = get_token_string();
|
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
|
||||||
exception_message(input_format, "invalid byte: 0x" + last_token, "type"), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
string_t type_key = "_ArrayType_";
|
|
||||||
string_t type = type_name; // sax->string() takes a reference
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(type_key) || !sax->string(type)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string_t key = "_ArraySize_";
|
string_t key = "_ArraySize_";
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(dim.size())))
|
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3) || !sax->key(key) || !sax->start_array(dim.size())))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -16543,7 +16532,7 @@ class binary_reader
|
|||||||
exception_message(input_format, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
|
exception_message(input_format, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool is_error = get_ubjson_size_value(result.first, is_ndarray, 0, result.second);
|
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
||||||
// an ndarray was read here only if the flag flipped; when it was
|
// an ndarray was read here only if the flag flipped; when it was
|
||||||
// seeded true, get_ubjson_size_value() already rejected the nested
|
// seeded true, get_ubjson_size_value() already rejected the nested
|
||||||
// dimension vector
|
// dimension vector
|
||||||
@@ -16736,17 +16725,27 @@ class binary_reader
|
|||||||
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
||||||
{
|
{
|
||||||
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
||||||
|
const char* type_name = bjd_type_name(size_and_type.second);
|
||||||
|
string_t key = "_ArrayType_";
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
|
||||||
|
{
|
||||||
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(input_format, "invalid byte: 0x" + last_token, "type"), nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
string_t type = type_name; // sax->string() takes a reference
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->string(type)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// the "_ArrayType_" and "_ArraySize_" annotation keys were already emitted by
|
|
||||||
// get_ubjson_size_value() (the type marker is known before the dimension vector
|
|
||||||
// that determines size_and_type.first is read, so it is emitted first there to
|
|
||||||
// match the documented _ArrayType_, _ArraySize_, _ArrayData_ key order)
|
|
||||||
if (size_and_type.second == 'C' || size_and_type.second == 'B')
|
if (size_and_type.second == 'C' || size_and_type.second == 'B')
|
||||||
{
|
{
|
||||||
size_and_type.second = 'U';
|
size_and_type.second = 'U';
|
||||||
}
|
}
|
||||||
|
|
||||||
string_t key = "_ArrayData_";
|
key = "_ArrayData_";
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) ))
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) ))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -23067,31 +23066,16 @@ class binary_writer
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief validate (dry_run) or write one BJData ND-array element of dtype 'd' (single precision)
|
@brief validate (dry_run) or write one BJData ND-array element of dtype 'd' (single precision)
|
||||||
@return whether @a el's value survives narrowing to float and back without any change
|
@return whether @a el's value fits a float without overflow; always true when @a dry_run is false
|
||||||
(so the ND-array round-trips exactly), or is infinite or NaN; always true when
|
|
||||||
@a dry_run is false
|
|
||||||
*/
|
*/
|
||||||
bool write_bjdata_ndarray_float_element(const BasicJsonType& el, const bool dry_run)
|
bool write_bjdata_ndarray_float_element(const BasicJsonType& el, const bool dry_run)
|
||||||
{
|
{
|
||||||
const auto dval = el.template get<double>();
|
const auto dval = el.template get<double>();
|
||||||
if (dry_run)
|
if (dry_run)
|
||||||
{
|
{
|
||||||
#ifdef __GNUC__
|
return !std::isfinite(dval) ||
|
||||||
JSON_HEDLEY_DIAGNOSTIC_PUSH
|
(dval >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
|
||||||
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
|
dval <= static_cast<double>((std::numeric_limits<float>::max)()));
|
||||||
#endif
|
|
||||||
// a value that would be rounded (rather than exactly represented) by the
|
|
||||||
// narrowing to float is treated like an out-of-range integer element; this
|
|
||||||
// is the same criterion write_compact_float() uses for CBOR/MessagePack
|
|
||||||
const bool in_range = std::isnan(dval) ||
|
|
||||||
(dval >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
|
|
||||||
dval <= static_cast<double>((std::numeric_limits<float>::max)()) &&
|
|
||||||
static_cast<double>(static_cast<float>(dval)) == dval) ||
|
|
||||||
std::isinf(dval);
|
|
||||||
#ifdef __GNUC__
|
|
||||||
JSON_HEDLEY_DIAGNOSTIC_POP
|
|
||||||
#endif
|
|
||||||
return in_range;
|
|
||||||
}
|
}
|
||||||
write_number(static_cast<float>(dval), true);
|
write_number(static_cast<float>(dval), true);
|
||||||
return true;
|
return true;
|
||||||
@@ -29041,8 +29025,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
if (is_an_object)
|
if (is_an_object)
|
||||||
{
|
{
|
||||||
// the initializer list is a list of pairs -> create an object
|
// the initializer list is a list of pairs -> create an object
|
||||||
m_data.m_type = value_t::object;
|
|
||||||
m_data.m_value = value_t::object;
|
m_data.m_value = value_t::object;
|
||||||
|
m_data.m_type = value_t::object;
|
||||||
|
|
||||||
for (auto& element_ref : init)
|
for (auto& element_ref : init)
|
||||||
{
|
{
|
||||||
@@ -29064,8 +29048,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// the initializer list describes an array -> create an array
|
// the initializer list describes an array -> create an array
|
||||||
m_data.m_type = value_t::array;
|
|
||||||
m_data.m_value.array = create<array_t>(init.begin(), init.end());
|
m_data.m_value.array = create<array_t>(init.begin(), init.end());
|
||||||
|
m_data.m_type = value_t::array;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_parents();
|
set_parents();
|
||||||
@@ -29078,8 +29062,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(const typename binary_t::container_type& init)
|
static basic_json binary(const typename binary_t::container_type& init)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_type = value_t::binary;
|
|
||||||
res.m_data.m_value = init;
|
res.m_data.m_value = init;
|
||||||
|
res.m_data.m_type = value_t::binary;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29089,8 +29073,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
|
static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_type = value_t::binary;
|
|
||||||
res.m_data.m_value = binary_t(init, subtype);
|
res.m_data.m_value = binary_t(init, subtype);
|
||||||
|
res.m_data.m_type = value_t::binary;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29100,8 +29084,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(typename binary_t::container_type&& init)
|
static basic_json binary(typename binary_t::container_type&& init)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_type = value_t::binary;
|
|
||||||
res.m_data.m_value = std::move(init);
|
res.m_data.m_value = std::move(init);
|
||||||
|
res.m_data.m_type = value_t::binary;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29111,8 +29095,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
|
static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_type = value_t::binary;
|
|
||||||
res.m_data.m_value = binary_t(std::move(init), subtype);
|
res.m_data.m_value = binary_t(std::move(init), subtype);
|
||||||
|
res.m_data.m_type = value_t::binary;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -140,6 +140,11 @@ json_test_set_test_options(test-unicode4 TEST_PROPERTIES TIMEOUT 3000)
|
|||||||
# only the #972 regression test needs thirdparty/fifo_map on its include path
|
# only the #972 regression test needs thirdparty/fifo_map on its include path
|
||||||
json_test_set_test_options(test-regression1 LINK_LIBRARIES fifo_map_include)
|
json_test_set_test_options(test-regression1 LINK_LIBRARIES fifo_map_include)
|
||||||
|
|
||||||
|
# GCC's false -Warray-bounds error with JSON_DIAGNOSTICS only shows up when optimizing (#5742)
|
||||||
|
json_test_set_test_options(test-diagnostics-optimized
|
||||||
|
COMPILE_OPTIONS $<$<CXX_COMPILER_ID:GNU>:-O3 -Werror=array-bounds>
|
||||||
|
)
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
# add unit tests
|
# add unit tests
|
||||||
#############################################################################
|
#############################################################################
|
||||||
|
|||||||
@@ -12,6 +12,11 @@
|
|||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
|
#include <valarray>
|
||||||
|
#if JSON_HAS_RANGES
|
||||||
|
#include <ranges>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
// special test case to check if memory is leaked if constructor throws
|
// special test case to check if memory is leaked if constructor throws
|
||||||
@@ -602,3 +607,173 @@ TEST_CASE("bad my_allocator::construct")
|
|||||||
j["test"].push_back("should not leak");
|
j["test"].push_back("should not leak");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the no-exceptions CI job skips every CHECK_THROWS_AS, which would leave
|
||||||
|
// next_construct_fails set for the next allocation outside a check
|
||||||
|
#if !defined(JSON_NOEXCEPTION)
|
||||||
|
TEST_CASE("a failed allocation leaves the value unchanged")
|
||||||
|
{
|
||||||
|
// create JSON type using the throwing allocator
|
||||||
|
using my_json = nlohmann::basic_json<std::map,
|
||||||
|
std::vector,
|
||||||
|
std::string,
|
||||||
|
bool,
|
||||||
|
std::int64_t,
|
||||||
|
std::uint64_t,
|
||||||
|
double,
|
||||||
|
my_allocator>;
|
||||||
|
|
||||||
|
// Each of these creates a string, array, object, or binary value. The
|
||||||
|
// value must be created before the type is changed: otherwise, a failed
|
||||||
|
// creation left a value of the new type without anything behind it (an
|
||||||
|
// assertion in its destructor, a null pointer everywhere else) or, when
|
||||||
|
// an old value was destroyed first, with a pointer to that destroyed one.
|
||||||
|
|
||||||
|
SECTION("creating a binary value")
|
||||||
|
{
|
||||||
|
const std::vector<std::uint8_t> bytes = {1, 2, 3};
|
||||||
|
my_json _;
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(_ = my_json::binary(bytes), std::bad_alloc&);
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(_ = my_json::binary(bytes, 42), std::bad_alloc&);
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(_ = my_json::binary(std::vector<std::uint8_t>(bytes)), std::bad_alloc&);
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(_ = my_json::binary(std::vector<std::uint8_t>(bytes), 42), std::bad_alloc&);
|
||||||
|
next_construct_fails = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("turning a null value into an array or object")
|
||||||
|
{
|
||||||
|
my_json j;
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(j[0], std::bad_alloc&);
|
||||||
|
CHECK(j.is_null());
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(j["key"], std::bad_alloc&);
|
||||||
|
CHECK(j.is_null());
|
||||||
|
|
||||||
|
#ifdef JSON_HAS_CPP_17
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(j[std::string_view("key")], std::bad_alloc&);
|
||||||
|
CHECK(j.is_null());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(j.push_back(my_json(1)), std::bad_alloc&);
|
||||||
|
CHECK(j.is_null());
|
||||||
|
|
||||||
|
const my_json one = 1;
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(j.push_back(one), std::bad_alloc&);
|
||||||
|
CHECK(j.is_null());
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(j.push_back(my_json::object_t::value_type("key", 1)), std::bad_alloc&);
|
||||||
|
CHECK(j.is_null());
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(j.emplace_back(1), std::bad_alloc&);
|
||||||
|
CHECK(j.is_null());
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(j.emplace("key", 1), std::bad_alloc&);
|
||||||
|
CHECK(j.is_null());
|
||||||
|
|
||||||
|
const my_json object = {{"key", 1}};
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(j.update(object), std::bad_alloc&);
|
||||||
|
CHECK(j.is_null());
|
||||||
|
|
||||||
|
next_construct_fails = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// With iterator debugging, VS 2015's containers construct a proxy with the
|
||||||
|
// allocator in constructors that cannot report its failure, so a failing
|
||||||
|
// allocator crashes this section there (SIGSEGV with VS 2015 Debug x86).
|
||||||
|
#if !(defined(_MSC_VER) && _MSC_VER < 1910 && defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL > 0)
|
||||||
|
SECTION("converting into an existing value")
|
||||||
|
{
|
||||||
|
// to_json replaces the value it is given; the old one must survive a
|
||||||
|
// failed creation of the new one
|
||||||
|
my_json j = "old";
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, std::string("new")), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, std::vector<int> {1, 2}), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, std::vector<bool> {true, false}), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, std::map<std::string, int> {{"a", 1}}), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, my_json::binary_t({1, 2})), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
|
||||||
|
// the overloads for lvalues of the value types, for the value types
|
||||||
|
// themselves, and for the remaining compatible types
|
||||||
|
const std::string string = "new";
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, string), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, "new"), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
|
||||||
|
// to_json only moves a binary value that it converted from another
|
||||||
|
// container type, which my_json's std::vector<std::uint8_t> is not
|
||||||
|
using binary_constructor = nlohmann::detail::external_constructor<nlohmann::detail::value_t::binary>;
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(binary_constructor::construct(j, my_json::binary_t({1, 2})), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
|
||||||
|
my_json::array_t array = {1, 2};
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, array), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, std::move(array)), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
|
||||||
|
my_json::object_t object = {{"a", 1}};
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, object), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, std::move(object)), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, std::valarray<int> {1, 2}), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
|
||||||
|
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||||
|
const std::vector<int> numbers = {1, 2};
|
||||||
|
next_construct_fails = true;
|
||||||
|
CHECK_THROWS_AS(nlohmann::to_json(j, numbers | std::views::filter([](int /*unused*/)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
})), std::bad_alloc&);
|
||||||
|
CHECK(j == "old");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
next_construct_fails = false;
|
||||||
|
nlohmann::to_json(j, std::vector<int> {1, 2});
|
||||||
|
CHECK(j == my_json({1, 2}));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
#define JSON_TESTS_PRIVATE
|
#define JSON_TESTS_PRIVATE
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
using ordered_json = nlohmann::ordered_json;
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
@@ -2176,33 +2175,29 @@ TEST_CASE("BJData")
|
|||||||
|
|
||||||
SECTION("start_array() in ndarray _ArraySize_")
|
SECTION("start_array() in ndarray _ArraySize_")
|
||||||
{
|
{
|
||||||
// _ArrayType_ (2 events: key + string) is now emitted before
|
|
||||||
// _ArraySize_ (see GitHub issue #5661), which shifts the events
|
|
||||||
// below later by the same 2 events
|
|
||||||
std::vector<uint8_t> const v = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'i', 2, 2, 1, 1, 2};
|
std::vector<uint8_t> const v = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'i', 2, 2, 1, 1, 2};
|
||||||
SaxCountdown scp(4);
|
SaxCountdown scp(2);
|
||||||
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("number_integer() in ndarray _ArraySize_")
|
SECTION("number_integer() in ndarray _ArraySize_")
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> const v = {'[', '$', 'U', '#', '[', '$', 'i', '#', 'i', 2, 2, 1, 1, 2};
|
std::vector<uint8_t> const v = {'[', '$', 'U', '#', '[', '$', 'i', '#', 'i', 2, 2, 1, 1, 2};
|
||||||
SaxCountdown scp(5);
|
SaxCountdown scp(3);
|
||||||
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("key() in ndarray _ArrayType_")
|
SECTION("key() in ndarray _ArrayType_")
|
||||||
{
|
{
|
||||||
// _ArrayType_ is emitted right after start_object(), before _ArraySize_
|
|
||||||
std::vector<uint8_t> const v = {'[', '$', 'U', '#', '[', '$', 'U', '#', 'i', 2, 2, 2, 1, 2, 3, 4};
|
std::vector<uint8_t> const v = {'[', '$', 'U', '#', '[', '$', 'U', '#', 'i', 2, 2, 2, 1, 2, 3, 4};
|
||||||
SaxCountdown scp(1);
|
SaxCountdown scp(6);
|
||||||
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("string() in ndarray _ArrayType_")
|
SECTION("string() in ndarray _ArrayType_")
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> const v = {'[', '$', 'U', '#', '[', '$', 'U', '#', 'i', 2, 2, 2, 1, 2, 3, 4};
|
std::vector<uint8_t> const v = {'[', '$', 'U', '#', '[', '$', 'U', '#', 'i', 2, 2, 2, 1, 2, 3, 4};
|
||||||
SaxCountdown scp(2);
|
SaxCountdown scp(7);
|
||||||
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2805,22 +2800,6 @@ TEST_CASE("BJData")
|
|||||||
CHECK(out_single.at(0) == '{');
|
CHECK(out_single.at(0) == '{');
|
||||||
CHECK(json::from_bjdata(out_single) == j_single);
|
CHECK(json::from_bjdata(out_single) == j_single);
|
||||||
|
|
||||||
// a double element that is finite and within the range of "single"
|
|
||||||
// but is not exactly representable as a float, so narrowing it would
|
|
||||||
// silently round it (0.1 is read back as 0.10000000149011612); this,
|
|
||||||
// like the overflow case above, falls back to a plain object (see
|
|
||||||
// GitHub issue #5661)
|
|
||||||
json const j_single_rounded = json({{"_ArrayType_", "single"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1.5, 0.1}}});
|
|
||||||
const auto out_single_rounded = json::to_bjdata(j_single_rounded);
|
|
||||||
CHECK(out_single_rounded.at(0) == '{');
|
|
||||||
CHECK(json::from_bjdata(out_single_rounded) == j_single_rounded);
|
|
||||||
|
|
||||||
// a double element that underflows to 0 when narrowed to "single"
|
|
||||||
json const j_single_underflow = json({{"_ArrayType_", "single"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1.5, 1e-300}}});
|
|
||||||
const auto out_single_underflow = json::to_bjdata(j_single_underflow);
|
|
||||||
CHECK(out_single_underflow.at(0) == '{');
|
|
||||||
CHECK(json::from_bjdata(out_single_underflow) == j_single_underflow);
|
|
||||||
|
|
||||||
// in-range boundary values still use the compact ndarray encoding
|
// in-range boundary values still use the compact ndarray encoding
|
||||||
json const j_uint8_ok = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {0, 255}}});
|
json const j_uint8_ok = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {0, 255}}});
|
||||||
CHECK(json::to_bjdata(j_uint8_ok) == std::vector<uint8_t>({'[', '$', 'U', '#', '[', 'i', 2, 'i', 1, ']', 0, 255}));
|
CHECK(json::to_bjdata(j_uint8_ok) == std::vector<uint8_t>({'[', '$', 'U', '#', '[', 'i', 2, 'i', 1, ']', 0, 255}));
|
||||||
@@ -2834,23 +2813,6 @@ TEST_CASE("BJData")
|
|||||||
CHECK(json::from_bjdata(out_single_ok) == json({{"_ArrayType_", "single"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1.5f, -1.5f}}}));
|
CHECK(json::from_bjdata(out_single_ok) == json({{"_ArrayType_", "single"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1.5f, -1.5f}}}));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("ndarray annotation keys are read back in the documented order")
|
|
||||||
{
|
|
||||||
// from_bjdata() must emit the annotation object's keys in the order
|
|
||||||
// used throughout the documentation, _ArrayType_, _ArraySize_,
|
|
||||||
// _ArrayData_: the type marker precedes the dimension vector on the
|
|
||||||
// wire (see get_ubjson_size_type()), so it is known, and emitted,
|
|
||||||
// before _ArraySize_. For a plain json this key order is invisible
|
|
||||||
// (its comparison ignores it), but for an ordered_json it is not (see
|
|
||||||
// GitHub issue #5661).
|
|
||||||
const ordered_json o = ordered_json::parse(R"({"_ArrayType_":"uint8","_ArraySize_":[2,2],"_ArrayData_":[1,2,3,4]})");
|
|
||||||
const auto packed = ordered_json::to_bjdata(o);
|
|
||||||
CHECK(packed.at(0) == '[');
|
|
||||||
const ordered_json o_back = ordered_json::from_bjdata(packed);
|
|
||||||
CHECK(o_back == o);
|
|
||||||
CHECK(o_back.dump() == o.dump());
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("ndarray that would not be read back as an annotated object stays as object")
|
SECTION("ndarray that would not be read back as an annotated object stays as object")
|
||||||
{
|
{
|
||||||
// the reader only restores an annotated object from an ND-array
|
// the reader only restores an annotated object from an ND-array
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | JSON for Modern C++ (supporting code)
|
||||||
|
// | | |__ | | | | | | version 3.12.0
|
||||||
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Regression test for https://github.com/nlohmann/json/issues/5742: with
|
||||||
|
// JSON_DIAGNOSTICS, GCC (12 to at least 16) reported a false -Warray-bounds
|
||||||
|
// error in the inlined set_parents() at -O3. The type of a new string was set
|
||||||
|
// before the string was allocated, so GCC had to assume that operator new
|
||||||
|
// could change it again and checked the object branch of set_parents()
|
||||||
|
// against the string's allocation. Setting the type after creating the value
|
||||||
|
// avoids this. The warning depends on GCC's inlining decisions, so the
|
||||||
|
// sections cover two patterns that trigger it on different GCC versions
|
||||||
|
// (#4819 and #5742).
|
||||||
|
// On GCC, this file is compiled with -O3 -Werror=array-bounds (see
|
||||||
|
// tests/CMakeLists.txt), so the test fails to build if the warning returns.
|
||||||
|
|
||||||
|
#include "doctest_compatibility.h"
|
||||||
|
|
||||||
|
#ifdef JSON_DIAGNOSTICS
|
||||||
|
#undef JSON_DIAGNOSTICS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define JSON_DIAGNOSTICS 1
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
using nlohmann::json;
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
enum class diag_color
|
||||||
|
{
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
blue
|
||||||
|
};
|
||||||
|
|
||||||
|
void to_json(json& j, const diag_color& c)
|
||||||
|
{
|
||||||
|
static const std::pair<diag_color, json> m[] = // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||||
|
{
|
||||||
|
{diag_color::red, "r"},
|
||||||
|
{diag_color::green, "g"},
|
||||||
|
{diag_color::blue, "b"},
|
||||||
|
};
|
||||||
|
const auto* it = std::find_if(std::begin(m), std::end(m), [c](const std::pair<diag_color, json>& p)
|
||||||
|
{
|
||||||
|
return p.first == c;
|
||||||
|
});
|
||||||
|
j = it->second;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("diagnostics with optimization")
|
||||||
|
{
|
||||||
|
SECTION("issue #4819 - object in vector")
|
||||||
|
{
|
||||||
|
std::vector<json> jsons{};
|
||||||
|
jsons.emplace_back(json({{"key", "value"}}));
|
||||||
|
CHECK(jsons.back()["key"] == "value");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("issue #5742 - string values from a static table")
|
||||||
|
{
|
||||||
|
json j = json::array();
|
||||||
|
j.push_back(diag_color::red);
|
||||||
|
j.push_back(diag_color::green);
|
||||||
|
j.push_back(diag_color::blue);
|
||||||
|
CHECK(j.dump() == R"(["r","g","b"])");
|
||||||
|
CHECK_THROWS_WITH_AS(j[1].get<int>(), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user