mirror of
https://github.com/nlohmann/json.git
synced 2026-10-13 17:57:14 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
735a20e36d | ||
|
|
45b837885e |
No files matched your search
@@ -6,6 +6,9 @@ constexpr bool is_number_unsigned() const noexcept;
|
||||
|
||||
This function returns `#!cpp true` if and only if the JSON value is an unsigned integer number. This excludes
|
||||
floating-point and signed integer values.
|
||||
|
||||
If [`number_unsigned_t`](number_unsigned_t.md) is the same type as [`number_integer_t`](number_integer_t.md), every
|
||||
integer is stored as `number_integer_t`, and this function always returns `#!cpp false`.
|
||||
|
||||
## Return value
|
||||
|
||||
|
||||
@@ -25,7 +25,8 @@ the type to use.
|
||||
`NumberUnsignedType`
|
||||
: the type to store unsigned integers. It must be an **unsigned integral** type (`#!cpp std::is_integral`) with a
|
||||
`#!cpp std::numeric_limits` specialization, and it must be able to represent the absolute value of every
|
||||
[`number_integer_t`](number_integer_t.md) value. See
|
||||
[`number_integer_t`](number_integer_t.md) value. Alternatively, it can be the same type as
|
||||
[`number_integer_t`](number_integer_t.md); then every integer is stored as `number_integer_t`. See
|
||||
[Template Parameter Requirements](../../features/types/template_parameters.md#numberintegertype-and-numberunsignedtype).
|
||||
|
||||
## Notes
|
||||
@@ -89,3 +90,4 @@ Integer number values are stored directly inside a `basic_json` type.
|
||||
## Version history
|
||||
|
||||
- Added in version 2.0.0.
|
||||
- Can be the same type as `number_integer_t` since version 3.13.0.
|
||||
@@ -343,8 +343,11 @@ The number types can be changed with template parameters.
|
||||
in case of overflow.
|
||||
- The type for unsigned integers must be convertible from `#!c unsigned long long`. The type for floating-point
|
||||
numbers is used in case of overflow.
|
||||
- The types for signed and unsigned integers must be distinct, see
|
||||
[#2573](https://github.com/nlohmann/json/issues/2573).
|
||||
- The type for unsigned integers can also be the same signed type as the type for signed integers (e.g.,
|
||||
`#!c std::int64_t` for both). Then every integer is stored as a signed integer, and
|
||||
[`is_number_unsigned()`](../../api/basic_json/is_number_unsigned.md) is always `#!c false`, see
|
||||
[Template Parameter Requirements](template_parameters.md#same-type-for-both).
|
||||
- The type for floating-point numbers must be distinct from the integer types.
|
||||
- Only `#!c double`, `#!c float`, and `#!c long double` are supported for floating-point numbers.
|
||||
|
||||
!!! example
|
||||
|
||||
@@ -40,19 +40,19 @@ Requirements are split into two groups:
|
||||
|
||||
## Overview
|
||||
|
||||
| Template parameter | Default | Notable substitutes |
|
||||
|-------------------------------------------------------------------|-----------------------------------|-----------------------------------------------------------------------|
|
||||
| [`ObjectType`](#objecttype) | `std::map` | [`nlohmann::ordered_map`](../../api/ordered_map.md), Abseil hash maps |
|
||||
| [`ArrayType`](#arraytype) | `std::vector` | `#!cpp std::deque` |
|
||||
| [`StringType`](#stringtype) | `std::string` | `std::string`-like types over `char` |
|
||||
| [`BooleanType`](#booleantype) | `bool` | none worth using |
|
||||
| [`NumberIntegerType`](#numberintegertype-and-numberunsignedtype) | `std::int64_t` | any signed integer type |
|
||||
| [`NumberUnsignedType`](#numberintegertype-and-numberunsignedtype) | `std::uint64_t` | any unsigned integer type at least as wide as `NumberIntegerType` |
|
||||
| [`NumberFloatType`](#numberfloattype) | `double` | `float` (`long double`: no binary formats) |
|
||||
| [`AllocatorType`](#allocatortype) | `std::allocator` | stateless allocators |
|
||||
| [`JSONSerializer`](#jsonserializer) | `adl_serializer` | serializers with the same interface |
|
||||
| [`BinaryType`](#binarytype) | `#!cpp std::vector<std::uint8_t>` | `#!cpp std::vector<char>` |
|
||||
| [`CustomBaseClass`](#custombaseclass) | `void` | any default-constructible class |
|
||||
| Template parameter | Default | Notable substitutes |
|
||||
|-------------------------------------------------------------------|-----------------------------------|--------------------------------------------------------------------------------------------------|
|
||||
| [`ObjectType`](#objecttype) | `std::map` | [`nlohmann::ordered_map`](../../api/ordered_map.md), Abseil hash maps |
|
||||
| [`ArrayType`](#arraytype) | `std::vector` | `#!cpp std::deque` |
|
||||
| [`StringType`](#stringtype) | `std::string` | `std::string`-like types over `char` |
|
||||
| [`BooleanType`](#booleantype) | `bool` | none worth using |
|
||||
| [`NumberIntegerType`](#numberintegertype-and-numberunsignedtype) | `std::int64_t` | any signed integer type |
|
||||
| [`NumberUnsignedType`](#numberintegertype-and-numberunsignedtype) | `std::uint64_t` | any unsigned integer type at least as wide as `NumberIntegerType`, or `NumberIntegerType` itself |
|
||||
| [`NumberFloatType`](#numberfloattype) | `double` | `float` (`long double`: no binary formats) |
|
||||
| [`AllocatorType`](#allocatortype) | `std::allocator` | stateless allocators |
|
||||
| [`JSONSerializer`](#jsonserializer) | `adl_serializer` | serializers with the same interface |
|
||||
| [`BinaryType`](#binarytype) | `#!cpp std::vector<std::uint8_t>` | `#!cpp std::vector<char>` |
|
||||
| [`CustomBaseClass`](#custombaseclass) | `void` | any default-constructible class |
|
||||
|
||||
!!! warning "Third-party containers and incomplete types"
|
||||
|
||||
@@ -499,13 +499,14 @@ Both types are stored **directly** inside `basic_json`'s union.
|
||||
### Always required
|
||||
|
||||
- `#!cpp std::is_integral` must be satisfied: `NumberIntegerType` must be a **signed** integer type,
|
||||
`NumberUnsignedType` an **unsigned** integer type. Class types are not supported -- among others, the constructors
|
||||
taking integer values are constrained on `#!cpp std::is_integral`.
|
||||
`NumberUnsignedType` an **unsigned** integer type or the same type as `NumberIntegerType` (see
|
||||
[below](#same-type-for-both)). Class types are not supported -- among others, the constructors taking integer values
|
||||
are constrained on `#!cpp std::is_integral`.
|
||||
- Trivially default-constructible, trivially copyable, and trivially destructible (union member).
|
||||
- `#!cpp std::numeric_limits` must be specialized for both types.
|
||||
- `NumberUnsignedType` must be able to represent the absolute value of every `NumberIntegerType` value; serialization
|
||||
of negative numbers converts the value to `NumberUnsignedType`. A `#!cpp static_assert` requires it to be at least as
|
||||
wide as `NumberIntegerType`, which is what that amounts to for the standard integer types.
|
||||
- An unsigned `NumberUnsignedType` must be able to represent the absolute value of every `NumberIntegerType` value;
|
||||
serialization of negative numbers converts the value to `NumberUnsignedType`. A `#!cpp static_assert` requires it to
|
||||
be at least as wide as `NumberIntegerType`, which is what that amounts to for the standard integer types.
|
||||
- Both types must fit into the internal 64-character number buffer used by
|
||||
[`dump`](../../api/basic_json/dump.md), which is the case for all standard integer types.
|
||||
- [`std::hash<basic_json>`](../../api/basic_json/std_hash.md) additionally requires `#!cpp std::hash` specializations.
|
||||
@@ -517,6 +518,23 @@ is stored as [`number_float_t`](../../api/basic_json/number_float_t.md) instead.
|
||||
therefore silently changes parse results rather than raising an error. See
|
||||
[Number Handling](number_handling.md) for details.
|
||||
|
||||
#### Same type for both
|
||||
|
||||
`NumberUnsignedType` may be the same type as `NumberIntegerType`, e.g.
|
||||
`#!cpp nlohmann::json::with_integers_t<std::int64_t, std::int64_t>` (see
|
||||
[`with_integers_t`](../../api/basic_json/with_t.md)), for code that only wants to handle one integer type. Such a `basic_json` stores every integer as
|
||||
[`number_integer_t`](../../api/basic_json/number_integer_t.md):
|
||||
|
||||
- [`is_number_unsigned()`](../../api/basic_json/is_number_unsigned.md) is always `#!cpp false`, and
|
||||
[`type()`](../../api/basic_json/type.md) never returns `value_t::number_unsigned`. Constructing a value from
|
||||
`value_t::number_unsigned` gives a `value_t::number_integer` value.
|
||||
- Values of unsigned C++ types (e.g., `#!cpp 5u`) are converted to `NumberIntegerType`, so values above its maximum
|
||||
wrap around, as values outside the range of a narrower `NumberIntegerType` already do.
|
||||
- The parsers store a non-negative integer above the maximum of `NumberIntegerType` (e.g., `18446744073709551615` for
|
||||
`#!cpp std::int64_t`) as [`number_float_t`](../../api/basic_json/number_float_t.md), like any other integer that does
|
||||
not fit.
|
||||
- Converting from a `basic_json` with distinct types turns its unsigned values into `number_integer_t` values.
|
||||
|
||||
### Compatible types
|
||||
|
||||
| Type pair | Support |
|
||||
@@ -525,6 +543,7 @@ therefore silently changes parse results rather than raising an error. See
|
||||
| `#!cpp std::int32_t` / `#!cpp std::uint32_t`, `#!cpp long long` / `#!cpp unsigned long long` | full; narrower types change which literals the parser can represent |
|
||||
| any other pair of standard signed/unsigned integer types | full |
|
||||
| class types, enumerations | not usable; `#!cpp std::is_integral` must hold |
|
||||
| the same signed integer type for both, e.g. `#!cpp std::int64_t` / `#!cpp std::int64_t` | full; every integer is stored as `number_integer_t`, see [above](#same-type-for-both) |
|
||||
| `#!cpp bool`, or a type already used for another member of the union | not usable; `#!cpp std::is_integral<bool>` is in fact `#!cpp true`, but the `get_impl_ptr` overloads for `boolean_t`, `number_integer_t`, `number_unsigned_t` and `number_float_t` would collide |
|
||||
|
||||
## `NumberFloatType`
|
||||
|
||||
@@ -150,7 +150,7 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_flo
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
|
||||
inline void from_json(const BasicJsonType& j, number_unsigned_overload_t<BasicJsonType>& val)
|
||||
{
|
||||
get_arithmetic_value(j, val);
|
||||
}
|
||||
|
||||
@@ -303,14 +303,14 @@ inline void to_json(BasicJsonType& j, FloatType val) noexcept
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
|
||||
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>
|
||||
enable_if_t<is_compatible_integer_type<number_unsigned_overload_t<BasicJsonType>, CompatibleNumberUnsignedType>::value, int> = 0>
|
||||
inline void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
|
||||
{
|
||||
external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleNumberIntegerType,
|
||||
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>
|
||||
enable_if_t<is_compatible_number_integer_type<BasicJsonType, CompatibleNumberIntegerType>::value, int> = 0>
|
||||
inline void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
|
||||
{
|
||||
external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <cstring> // memcpy
|
||||
#include <limits> // numeric_limits
|
||||
#include <string> // string
|
||||
#include <type_traits> // is_signed, true_type, false_type
|
||||
|
||||
#include <nlohmann/detail/bit_ops.hpp>
|
||||
#include <nlohmann/detail/input/pow5_table.hpp>
|
||||
@@ -44,6 +45,20 @@ NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// whether an integer of a signed type is negative; the overload for other types
|
||||
// avoids comparing an unsigned value with zero
|
||||
template<typename NumberType>
|
||||
constexpr bool is_negative_integer(NumberType x, std::true_type /*is_signed*/) noexcept
|
||||
{
|
||||
return x < 0;
|
||||
}
|
||||
|
||||
template<typename NumberType>
|
||||
constexpr bool is_negative_integer(NumberType /*unused*/, std::false_type /*is_signed*/) noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief fast integer parser for an already-validated unsigned integer
|
||||
|
||||
@@ -77,8 +92,10 @@ bool parse_integer_unsigned(const char* first, const char* last, NumberUnsignedT
|
||||
x = (x * 10u) + digit;
|
||||
}
|
||||
value = static_cast<NumberUnsignedType>(x);
|
||||
// reject values that do not round-trip into a narrower NumberUnsignedType
|
||||
return static_cast<std::uint64_t>(value) == x;
|
||||
// reject values that do not round-trip into a narrower NumberUnsignedType,
|
||||
// and values that wrap around to negative numbers if it is a signed type
|
||||
// (number_unsigned_t may be the same type as number_integer_t, #2573)
|
||||
return static_cast<std::uint64_t>(value) == x && !is_negative_integer(value, std::is_signed<NumberUnsignedType>());
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -683,6 +683,22 @@ struct is_compatible_integer_type
|
||||
: is_compatible_integer_type_impl<RealIntegerType,
|
||||
CompatibleNumberIntegerType> {};
|
||||
|
||||
// whether to_json() stores an integer of type CompatibleNumberIntegerType as
|
||||
// number_integer
|
||||
template<typename BasicJsonType, typename CompatibleNumberIntegerType, typename = void>
|
||||
struct is_compatible_number_integer_type
|
||||
: is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType> {};
|
||||
|
||||
// if number_unsigned_t is the same type as number_integer_t, unsigned integers
|
||||
// are stored as number_integer as well (#2573)
|
||||
template<typename BasicJsonType, typename CompatibleNumberIntegerType>
|
||||
struct is_compatible_number_integer_type < BasicJsonType, CompatibleNumberIntegerType,
|
||||
enable_if_t < std::is_same<typename BasicJsonType::number_integer_t, typename BasicJsonType::number_unsigned_t>::value&&
|
||||
std::is_integral<CompatibleNumberIntegerType>::value&&
|
||||
std::is_unsigned<CompatibleNumberIntegerType>::value&&
|
||||
!std::is_same<bool, CompatibleNumberIntegerType>::value >>
|
||||
: std::is_constructible<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType> {};
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleType, typename = void>
|
||||
struct is_compatible_type_impl: std::false_type {};
|
||||
|
||||
@@ -1020,6 +1036,20 @@ constexpr bool value_in_range_of(T val)
|
||||
template<bool Value>
|
||||
using bool_constant = std::integral_constant<bool, Value>;
|
||||
|
||||
// Stands in for number_unsigned_t in overload sets if it is the same type as
|
||||
// number_integer_t, so that the unsigned overloads do not collide with the
|
||||
// integer ones. Never constructed. Such a basic_json stores every integer as
|
||||
// number_integer (#2573).
|
||||
struct same_as_number_integer_type {};
|
||||
|
||||
// number_unsigned_t of BasicJsonType, or same_as_number_integer_type if it is
|
||||
// the same type as number_integer_t
|
||||
template<typename BasicJsonType>
|
||||
using number_unsigned_overload_t = typename std::conditional <
|
||||
std::is_same<typename BasicJsonType::number_integer_t, typename BasicJsonType::number_unsigned_t>::value,
|
||||
same_as_number_integer_type,
|
||||
typename BasicJsonType::number_unsigned_t >::type;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// is_c_string
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -221,7 +221,7 @@ class binary_writer
|
||||
|
||||
case value_t::number_unsigned:
|
||||
{
|
||||
write_cbor_head(0x00, j.m_data.m_value.number_unsigned);
|
||||
write_cbor_head(0x00, conditional_static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1601,7 +1601,7 @@ class binary_writer
|
||||
return calc_bson_integer_size(j.m_data.m_value.number_integer);
|
||||
|
||||
case value_t::number_unsigned:
|
||||
return calc_bson_unsigned_size(j.m_data.m_value.number_unsigned);
|
||||
return calc_bson_unsigned_size(conditional_static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned));
|
||||
|
||||
case value_t::string:
|
||||
return calc_bson_string_size(*j.m_data.m_value.string, j);
|
||||
@@ -1643,7 +1643,7 @@ class binary_writer
|
||||
return write_bson_integer(name, j.m_data.m_value.number_integer);
|
||||
|
||||
case value_t::number_unsigned:
|
||||
return write_bson_unsigned(name, j.m_data.m_value.number_unsigned);
|
||||
return write_bson_unsigned(name, conditional_static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned));
|
||||
|
||||
case value_t::string:
|
||||
return write_bson_string(name, *j.m_data.m_value.string);
|
||||
|
||||
@@ -49,6 +49,12 @@ class serializer
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
/// type of the absolute value of an integer: number_unsigned_t, or its
|
||||
/// unsigned counterpart if it is signed (it may be number_integer_t, #2573)
|
||||
using number_magnitude_t = typename std::conditional <
|
||||
std::is_integral<number_unsigned_t>::value && std::is_signed<number_unsigned_t>::value,
|
||||
std::make_unsigned<number_unsigned_t>,
|
||||
std::enable_if<true, number_unsigned_t >>::type::type;
|
||||
using binary_char_t = typename BasicJsonType::binary_t::value_type;
|
||||
|
||||
public:
|
||||
@@ -1227,7 +1233,7 @@ class serializer
|
||||
@param[in] x unsigned integer number to count its digits
|
||||
@return number of decimal digits
|
||||
*/
|
||||
unsigned int count_digits(number_unsigned_t x) noexcept
|
||||
unsigned int count_digits(number_magnitude_t x) noexcept
|
||||
{
|
||||
unsigned int n_digits = 1;
|
||||
for (;;)
|
||||
@@ -1405,7 +1411,7 @@ class serializer
|
||||
// use a pointer to fill the buffer
|
||||
auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto)
|
||||
|
||||
number_unsigned_t abs_value;
|
||||
number_magnitude_t abs_value;
|
||||
|
||||
unsigned int n_chars{};
|
||||
|
||||
@@ -1419,7 +1425,7 @@ class serializer
|
||||
}
|
||||
else
|
||||
{
|
||||
abs_value = static_cast<number_unsigned_t>(x);
|
||||
abs_value = static_cast<number_magnitude_t>(x);
|
||||
n_chars = count_digits(abs_value);
|
||||
}
|
||||
|
||||
@@ -1562,17 +1568,6 @@ class serializer
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Overload to make the compiler happy while it is instantiating
|
||||
* dump_integer for number_unsigned_t.
|
||||
* Must never be called.
|
||||
*/
|
||||
number_unsigned_t remove_sign(number_unsigned_t x)
|
||||
{
|
||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||
return x; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function for dump_integer
|
||||
*
|
||||
@@ -1582,10 +1577,10 @@ class serializer
|
||||
* absolute values of INT_MIN and INT_MAX are usually not the same. See
|
||||
* #1708 for details.
|
||||
*/
|
||||
number_unsigned_t remove_sign(number_integer_t x) noexcept
|
||||
number_magnitude_t remove_sign(number_integer_t x) noexcept
|
||||
{
|
||||
JSON_ASSERT(x < 0);
|
||||
return static_cast<number_unsigned_t>(-(x + 1)) + 1;
|
||||
return static_cast<number_magnitude_t>(-(x + 1)) + 1;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -500,8 +500,22 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
"NumberUnsignedType must be at least as wide as NumberIntegerType, "
|
||||
"because it has to hold the absolute value of every NumberIntegerType value");
|
||||
|
||||
// NumberUnsignedType may be the same type as NumberIntegerType (#2573), but
|
||||
// a repeated NumberFloatType would give overloads that collide, so it gets
|
||||
// a readable diagnostic instead.
|
||||
static_assert(!std::is_same<NumberFloatType, NumberIntegerType>::value && !std::is_same<NumberFloatType, NumberUnsignedType>::value,
|
||||
"NumberFloatType must be a different type than NumberIntegerType and NumberUnsignedType");
|
||||
|
||||
private:
|
||||
|
||||
/// the kind a value of kind @a t is stored as: if number_unsigned_t is the
|
||||
/// same type as number_integer_t, every integer is stored as number_integer
|
||||
/// (#2573); every other kind is stored as itself
|
||||
static constexpr value_t stored_kind(const value_t t) noexcept
|
||||
{
|
||||
return (std::is_same<number_integer_t, number_unsigned_t>::value && t == value_t::number_unsigned) ? value_t::number_integer : t;
|
||||
}
|
||||
|
||||
/// helper for exception-safe object creation
|
||||
template<typename T, typename... Args>
|
||||
JSON_HEDLEY_RETURNS_NON_NULL
|
||||
@@ -575,8 +589,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
json_value(boolean_t v) noexcept : boolean(v) {}
|
||||
/// constructor for numbers (integer)
|
||||
json_value(number_integer_t v) noexcept : number_integer(v) {}
|
||||
/// constructor for numbers (unsigned)
|
||||
json_value(number_unsigned_t v) noexcept : number_unsigned(v) {}
|
||||
/// constructor for numbers (unsigned); takes a placeholder if number_unsigned_t is number_integer_t
|
||||
json_value(detail::number_unsigned_overload_t<basic_json> v) noexcept : number_unsigned(v) {}
|
||||
/// constructor for numbers (floating-point)
|
||||
json_value(number_float_t v) noexcept : number_float(v) {}
|
||||
/// constructor for empty values of a given type
|
||||
@@ -2181,7 +2195,7 @@ public:
|
||||
/// @brief create an empty value with a given type
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
|
||||
basic_json(const value_t v)
|
||||
: m_data(v)
|
||||
: m_data(stored_kind(v))
|
||||
{
|
||||
assert_invariant();
|
||||
}
|
||||
@@ -2262,7 +2276,7 @@ public:
|
||||
{
|
||||
convert_leaf(val);
|
||||
}
|
||||
JSON_ASSERT(m_data.m_type == val.type());
|
||||
JSON_ASSERT(m_data.m_type == stored_kind(val.type()));
|
||||
|
||||
set_parents();
|
||||
assert_invariant();
|
||||
@@ -2847,14 +2861,16 @@ public:
|
||||
return m_data.m_type == value_t::number_integer ? &m_data.m_value.number_integer : nullptr;
|
||||
}
|
||||
|
||||
/// get a pointer to the value (unsigned number)
|
||||
number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept
|
||||
/// get a pointer to the value (unsigned number); takes a placeholder if
|
||||
/// number_unsigned_t is number_integer_t (the integer overload handles it then)
|
||||
number_unsigned_t* get_impl_ptr(detail::number_unsigned_overload_t<basic_json>* /*unused*/) noexcept
|
||||
{
|
||||
return is_number_unsigned() ? &m_data.m_value.number_unsigned : nullptr;
|
||||
}
|
||||
|
||||
/// get a pointer to the value (unsigned number)
|
||||
constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept
|
||||
/// get a pointer to the value (unsigned number); takes a placeholder if
|
||||
/// number_unsigned_t is number_integer_t (the integer overload handles it then)
|
||||
constexpr const number_unsigned_t* get_impl_ptr(const detail::number_unsigned_overload_t<basic_json>* /*unused*/) const noexcept
|
||||
{
|
||||
return is_number_unsigned() ? &m_data.m_value.number_unsigned : nullptr;
|
||||
}
|
||||
|
||||
Generated
+90
-32
@@ -4687,6 +4687,22 @@ struct is_compatible_integer_type
|
||||
: is_compatible_integer_type_impl<RealIntegerType,
|
||||
CompatibleNumberIntegerType> {};
|
||||
|
||||
// whether to_json() stores an integer of type CompatibleNumberIntegerType as
|
||||
// number_integer
|
||||
template<typename BasicJsonType, typename CompatibleNumberIntegerType, typename = void>
|
||||
struct is_compatible_number_integer_type
|
||||
: is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType> {};
|
||||
|
||||
// if number_unsigned_t is the same type as number_integer_t, unsigned integers
|
||||
// are stored as number_integer as well (#2573)
|
||||
template<typename BasicJsonType, typename CompatibleNumberIntegerType>
|
||||
struct is_compatible_number_integer_type < BasicJsonType, CompatibleNumberIntegerType,
|
||||
enable_if_t < std::is_same<typename BasicJsonType::number_integer_t, typename BasicJsonType::number_unsigned_t>::value&&
|
||||
std::is_integral<CompatibleNumberIntegerType>::value&&
|
||||
std::is_unsigned<CompatibleNumberIntegerType>::value&&
|
||||
!std::is_same<bool, CompatibleNumberIntegerType>::value >>
|
||||
: std::is_constructible<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType> {};
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleType, typename = void>
|
||||
struct is_compatible_type_impl: std::false_type {};
|
||||
|
||||
@@ -5024,6 +5040,20 @@ constexpr bool value_in_range_of(T val)
|
||||
template<bool Value>
|
||||
using bool_constant = std::integral_constant<bool, Value>;
|
||||
|
||||
// Stands in for number_unsigned_t in overload sets if it is the same type as
|
||||
// number_integer_t, so that the unsigned overloads do not collide with the
|
||||
// integer ones. Never constructed. Such a basic_json stores every integer as
|
||||
// number_integer (#2573).
|
||||
struct same_as_number_integer_type {};
|
||||
|
||||
// number_unsigned_t of BasicJsonType, or same_as_number_integer_type if it is
|
||||
// the same type as number_integer_t
|
||||
template<typename BasicJsonType>
|
||||
using number_unsigned_overload_t = typename std::conditional <
|
||||
std::is_same<typename BasicJsonType::number_integer_t, typename BasicJsonType::number_unsigned_t>::value,
|
||||
same_as_number_integer_type,
|
||||
typename BasicJsonType::number_unsigned_t >::type;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// is_c_string
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -5846,7 +5876,7 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_flo
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
|
||||
inline void from_json(const BasicJsonType& j, number_unsigned_overload_t<BasicJsonType>& val)
|
||||
{
|
||||
get_arithmetic_value(j, val);
|
||||
}
|
||||
@@ -7250,14 +7280,14 @@ inline void to_json(BasicJsonType& j, FloatType val) noexcept
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
|
||||
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>
|
||||
enable_if_t<is_compatible_integer_type<number_unsigned_overload_t<BasicJsonType>, CompatibleNumberUnsignedType>::value, int> = 0>
|
||||
inline void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
|
||||
{
|
||||
external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleNumberIntegerType,
|
||||
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>
|
||||
enable_if_t<is_compatible_number_integer_type<BasicJsonType, CompatibleNumberIntegerType>::value, int> = 0>
|
||||
inline void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
|
||||
{
|
||||
external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
|
||||
@@ -9065,6 +9095,7 @@ NLOHMANN_JSON_NAMESPACE_END
|
||||
#include <cstring> // memcpy
|
||||
#include <limits> // numeric_limits
|
||||
#include <string> // string
|
||||
#include <type_traits> // is_signed, true_type, false_type
|
||||
|
||||
// #include <nlohmann/detail/bit_ops.hpp>
|
||||
|
||||
@@ -9466,6 +9497,20 @@ NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// whether an integer of a signed type is negative; the overload for other types
|
||||
// avoids comparing an unsigned value with zero
|
||||
template<typename NumberType>
|
||||
constexpr bool is_negative_integer(NumberType x, std::true_type /*is_signed*/) noexcept
|
||||
{
|
||||
return x < 0;
|
||||
}
|
||||
|
||||
template<typename NumberType>
|
||||
constexpr bool is_negative_integer(NumberType /*unused*/, std::false_type /*is_signed*/) noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief fast integer parser for an already-validated unsigned integer
|
||||
|
||||
@@ -9499,8 +9544,10 @@ bool parse_integer_unsigned(const char* first, const char* last, NumberUnsignedT
|
||||
x = (x * 10u) + digit;
|
||||
}
|
||||
value = static_cast<NumberUnsignedType>(x);
|
||||
// reject values that do not round-trip into a narrower NumberUnsignedType
|
||||
return static_cast<std::uint64_t>(value) == x;
|
||||
// reject values that do not round-trip into a narrower NumberUnsignedType,
|
||||
// and values that wrap around to negative numbers if it is a signed type
|
||||
// (number_unsigned_t may be the same type as number_integer_t, #2573)
|
||||
return static_cast<std::uint64_t>(value) == x && !is_negative_integer(value, std::is_signed<NumberUnsignedType>());
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -22122,7 +22169,7 @@ class binary_writer
|
||||
|
||||
case value_t::number_unsigned:
|
||||
{
|
||||
write_cbor_head(0x00, j.m_data.m_value.number_unsigned);
|
||||
write_cbor_head(0x00, conditional_static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -23502,7 +23549,7 @@ class binary_writer
|
||||
return calc_bson_integer_size(j.m_data.m_value.number_integer);
|
||||
|
||||
case value_t::number_unsigned:
|
||||
return calc_bson_unsigned_size(j.m_data.m_value.number_unsigned);
|
||||
return calc_bson_unsigned_size(conditional_static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned));
|
||||
|
||||
case value_t::string:
|
||||
return calc_bson_string_size(*j.m_data.m_value.string, j);
|
||||
@@ -23544,7 +23591,7 @@ class binary_writer
|
||||
return write_bson_integer(name, j.m_data.m_value.number_integer);
|
||||
|
||||
case value_t::number_unsigned:
|
||||
return write_bson_unsigned(name, j.m_data.m_value.number_unsigned);
|
||||
return write_bson_unsigned(name, conditional_static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned));
|
||||
|
||||
case value_t::string:
|
||||
return write_bson_string(name, *j.m_data.m_value.string);
|
||||
@@ -26364,6 +26411,12 @@ class serializer
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
/// type of the absolute value of an integer: number_unsigned_t, or its
|
||||
/// unsigned counterpart if it is signed (it may be number_integer_t, #2573)
|
||||
using number_magnitude_t = typename std::conditional <
|
||||
std::is_integral<number_unsigned_t>::value && std::is_signed<number_unsigned_t>::value,
|
||||
std::make_unsigned<number_unsigned_t>,
|
||||
std::enable_if<true, number_unsigned_t >>::type::type;
|
||||
using binary_char_t = typename BasicJsonType::binary_t::value_type;
|
||||
|
||||
public:
|
||||
@@ -27542,7 +27595,7 @@ class serializer
|
||||
@param[in] x unsigned integer number to count its digits
|
||||
@return number of decimal digits
|
||||
*/
|
||||
unsigned int count_digits(number_unsigned_t x) noexcept
|
||||
unsigned int count_digits(number_magnitude_t x) noexcept
|
||||
{
|
||||
unsigned int n_digits = 1;
|
||||
for (;;)
|
||||
@@ -27720,7 +27773,7 @@ class serializer
|
||||
// use a pointer to fill the buffer
|
||||
auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto)
|
||||
|
||||
number_unsigned_t abs_value;
|
||||
number_magnitude_t abs_value;
|
||||
|
||||
unsigned int n_chars{};
|
||||
|
||||
@@ -27734,7 +27787,7 @@ class serializer
|
||||
}
|
||||
else
|
||||
{
|
||||
abs_value = static_cast<number_unsigned_t>(x);
|
||||
abs_value = static_cast<number_magnitude_t>(x);
|
||||
n_chars = count_digits(abs_value);
|
||||
}
|
||||
|
||||
@@ -27877,17 +27930,6 @@ class serializer
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Overload to make the compiler happy while it is instantiating
|
||||
* dump_integer for number_unsigned_t.
|
||||
* Must never be called.
|
||||
*/
|
||||
number_unsigned_t remove_sign(number_unsigned_t x)
|
||||
{
|
||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||
return x; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function for dump_integer
|
||||
*
|
||||
@@ -27897,10 +27939,10 @@ class serializer
|
||||
* absolute values of INT_MIN and INT_MAX are usually not the same. See
|
||||
* #1708 for details.
|
||||
*/
|
||||
number_unsigned_t remove_sign(number_integer_t x) noexcept
|
||||
number_magnitude_t remove_sign(number_integer_t x) noexcept
|
||||
{
|
||||
JSON_ASSERT(x < 0);
|
||||
return static_cast<number_unsigned_t>(-(x + 1)) + 1;
|
||||
return static_cast<number_magnitude_t>(-(x + 1)) + 1;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -28784,8 +28826,22 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
"NumberUnsignedType must be at least as wide as NumberIntegerType, "
|
||||
"because it has to hold the absolute value of every NumberIntegerType value");
|
||||
|
||||
// NumberUnsignedType may be the same type as NumberIntegerType (#2573), but
|
||||
// a repeated NumberFloatType would give overloads that collide, so it gets
|
||||
// a readable diagnostic instead.
|
||||
static_assert(!std::is_same<NumberFloatType, NumberIntegerType>::value && !std::is_same<NumberFloatType, NumberUnsignedType>::value,
|
||||
"NumberFloatType must be a different type than NumberIntegerType and NumberUnsignedType");
|
||||
|
||||
private:
|
||||
|
||||
/// the kind a value of kind @a t is stored as: if number_unsigned_t is the
|
||||
/// same type as number_integer_t, every integer is stored as number_integer
|
||||
/// (#2573); every other kind is stored as itself
|
||||
static constexpr value_t stored_kind(const value_t t) noexcept
|
||||
{
|
||||
return (std::is_same<number_integer_t, number_unsigned_t>::value && t == value_t::number_unsigned) ? value_t::number_integer : t;
|
||||
}
|
||||
|
||||
/// helper for exception-safe object creation
|
||||
template<typename T, typename... Args>
|
||||
JSON_HEDLEY_RETURNS_NON_NULL
|
||||
@@ -28859,8 +28915,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
json_value(boolean_t v) noexcept : boolean(v) {}
|
||||
/// constructor for numbers (integer)
|
||||
json_value(number_integer_t v) noexcept : number_integer(v) {}
|
||||
/// constructor for numbers (unsigned)
|
||||
json_value(number_unsigned_t v) noexcept : number_unsigned(v) {}
|
||||
/// constructor for numbers (unsigned); takes a placeholder if number_unsigned_t is number_integer_t
|
||||
json_value(detail::number_unsigned_overload_t<basic_json> v) noexcept : number_unsigned(v) {}
|
||||
/// constructor for numbers (floating-point)
|
||||
json_value(number_float_t v) noexcept : number_float(v) {}
|
||||
/// constructor for empty values of a given type
|
||||
@@ -30465,7 +30521,7 @@ public:
|
||||
/// @brief create an empty value with a given type
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
|
||||
basic_json(const value_t v)
|
||||
: m_data(v)
|
||||
: m_data(stored_kind(v))
|
||||
{
|
||||
assert_invariant();
|
||||
}
|
||||
@@ -30546,7 +30602,7 @@ public:
|
||||
{
|
||||
convert_leaf(val);
|
||||
}
|
||||
JSON_ASSERT(m_data.m_type == val.type());
|
||||
JSON_ASSERT(m_data.m_type == stored_kind(val.type()));
|
||||
|
||||
set_parents();
|
||||
assert_invariant();
|
||||
@@ -31131,14 +31187,16 @@ public:
|
||||
return m_data.m_type == value_t::number_integer ? &m_data.m_value.number_integer : nullptr;
|
||||
}
|
||||
|
||||
/// get a pointer to the value (unsigned number)
|
||||
number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept
|
||||
/// get a pointer to the value (unsigned number); takes a placeholder if
|
||||
/// number_unsigned_t is number_integer_t (the integer overload handles it then)
|
||||
number_unsigned_t* get_impl_ptr(detail::number_unsigned_overload_t<basic_json>* /*unused*/) noexcept
|
||||
{
|
||||
return is_number_unsigned() ? &m_data.m_value.number_unsigned : nullptr;
|
||||
}
|
||||
|
||||
/// get a pointer to the value (unsigned number)
|
||||
constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept
|
||||
/// get a pointer to the value (unsigned number); takes a placeholder if
|
||||
/// number_unsigned_t is number_integer_t (the integer overload handles it then)
|
||||
constexpr const number_unsigned_t* get_impl_ptr(const detail::number_unsigned_overload_t<basic_json>* /*unused*/) const noexcept
|
||||
{
|
||||
return is_number_unsigned() ? &m_data.m_value.number_unsigned : nullptr;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | 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
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// basic_json with the same type for number_integer_t and number_unsigned_t
|
||||
// (#2573): every integer is stored as number_integer
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template<typename IntegerType>
|
||||
using same_types_json = nlohmann::json::with_integers_t<IntegerType, IntegerType>;
|
||||
|
||||
using json64 = same_types_json<std::int64_t>;
|
||||
using json32 = same_types_json<std::int32_t>;
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE_TEMPLATE("same type for number_integer_t and number_unsigned_t", Json, json64, json32)
|
||||
{
|
||||
using integer_t = typename Json::number_integer_t;
|
||||
using limits = std::numeric_limits<integer_t>;
|
||||
|
||||
SECTION("parsing stores integers as number_integer")
|
||||
{
|
||||
const Json j = Json::parse("[0, 1, -1, 2147483647, -2147483648]");
|
||||
for (const auto& element : j)
|
||||
{
|
||||
CHECK(element.is_number_integer());
|
||||
CHECK(!element.is_number_unsigned());
|
||||
CHECK(element.type() == nlohmann::detail::value_t::number_integer);
|
||||
}
|
||||
CHECK(j[1].template get<integer_t>() == 1);
|
||||
CHECK(j[2].template get<integer_t>() == -1);
|
||||
CHECK(j[3].template get<integer_t>() == 2147483647);
|
||||
CHECK(j[4].template get<std::int64_t>() == -2147483648LL);
|
||||
}
|
||||
|
||||
SECTION("integers above the maximum are parsed as floats")
|
||||
{
|
||||
// a positive literal is parsed as unsigned first; it must not wrap
|
||||
// around to a negative number_integer_t
|
||||
const auto above_max = std::to_string(static_cast<std::uint64_t>(limits::max()) + 1);
|
||||
const Json j1 = Json::parse(above_max);
|
||||
CHECK(j1.is_number_float());
|
||||
CHECK(j1.template get<double>() == static_cast<double>(static_cast<std::uint64_t>(limits::max()) + 1));
|
||||
|
||||
const Json j2 = Json::parse("18446744073709551615");
|
||||
CHECK(j2.is_number_float());
|
||||
CHECK(j2.template get<double>() == 18446744073709551615.0);
|
||||
|
||||
const Json j3 = Json::parse(std::to_string(limits::max()));
|
||||
CHECK(j3.is_number_integer());
|
||||
CHECK(j3.template get<integer_t>() == limits::max());
|
||||
}
|
||||
|
||||
SECTION("serialization of the extremes")
|
||||
{
|
||||
CHECK(Json(limits::max()).dump() == std::to_string(limits::max()));
|
||||
// the absolute value of the minimum does not fit into number_unsigned_t
|
||||
CHECK(Json(limits::min()).dump() == std::to_string(limits::min()));
|
||||
CHECK(Json::parse(std::to_string(limits::min())) == Json(limits::min()));
|
||||
CHECK(Json(0).dump() == "0");
|
||||
}
|
||||
|
||||
SECTION("construction from unsigned types")
|
||||
{
|
||||
const Json j1 = 5u;
|
||||
CHECK(j1.is_number_integer());
|
||||
CHECK(!j1.is_number_unsigned());
|
||||
CHECK(j1 == 5);
|
||||
|
||||
const Json j2 = static_cast<std::uint8_t>(200);
|
||||
CHECK(j2.is_number_integer());
|
||||
CHECK(j2.template get<integer_t>() == 200);
|
||||
|
||||
const Json j3 = std::vector<unsigned> {1, 2, 3};
|
||||
CHECK(j3.dump() == "[1,2,3]");
|
||||
CHECK(j3[0].is_number_integer());
|
||||
|
||||
const Json j4 = true;
|
||||
CHECK(j4.is_boolean());
|
||||
}
|
||||
|
||||
SECTION("value_t::number_unsigned creates a number_integer")
|
||||
{
|
||||
const Json j(nlohmann::detail::value_t::number_unsigned);
|
||||
CHECK(j.is_number_integer());
|
||||
CHECK(!j.is_number_unsigned());
|
||||
CHECK(j == 0);
|
||||
CHECK(j.dump() == "0");
|
||||
}
|
||||
|
||||
SECTION("conversions")
|
||||
{
|
||||
Json j = 42;
|
||||
CHECK(j.template get<unsigned>() == 42u);
|
||||
CHECK(j.template get<std::uint64_t>() == 42u);
|
||||
CHECK(j.template get<int>() == 42);
|
||||
CHECK(j.template get<double>() == 42.0);
|
||||
CHECK(j.template get_ptr<integer_t*>() != nullptr);
|
||||
CHECK(*j.template get_ptr<integer_t*>() == 42);
|
||||
CHECK(j.template get_ref<integer_t&>() == 42);
|
||||
j.template get_ref<integer_t&>() = 43;
|
||||
CHECK(j == 43);
|
||||
|
||||
integer_t i = 0;
|
||||
j.get_to(i);
|
||||
CHECK(i == 43);
|
||||
|
||||
const Json object = {{"a", 1}};
|
||||
CHECK(object.value("a", 0u) == 1u);
|
||||
CHECK(object.value("b", 7u) == 7u);
|
||||
}
|
||||
|
||||
SECTION("conversion from a basic_json with distinct types")
|
||||
{
|
||||
const nlohmann::json j = {1, 2u, -3, 4.5};
|
||||
CHECK(j[1].is_number_unsigned());
|
||||
const Json converted = j;
|
||||
CHECK(converted.dump() == "[1,2,-3,4.5]");
|
||||
CHECK(converted[1].is_number_integer());
|
||||
CHECK(!converted[1].is_number_unsigned());
|
||||
|
||||
const nlohmann::json back = converted;
|
||||
CHECK(back == j);
|
||||
}
|
||||
|
||||
SECTION("comparison and hashing")
|
||||
{
|
||||
const Json j1 = 1;
|
||||
const Json j2 = 2u;
|
||||
CHECK(j1 < j2);
|
||||
CHECK(j1 != j2);
|
||||
CHECK(j2 == 2);
|
||||
CHECK(j2 == 2u);
|
||||
CHECK(j1 < 1.5);
|
||||
CHECK(Json(-1) < Json(0u));
|
||||
CHECK(std::hash<Json> {}(Json(5u)) == std::hash<Json> {}(Json(5)));
|
||||
}
|
||||
|
||||
SECTION("JSON Patch, diff, and flatten")
|
||||
{
|
||||
const Json source = Json::parse(R"({"a": [1, 2, 3], "b": 4})");
|
||||
const Json target = Json::parse(R"({"a": [1, 5], "c": 6})");
|
||||
CHECK(source.patch(Json::diff(source, target)) == target);
|
||||
CHECK(source.flatten().unflatten() == source);
|
||||
}
|
||||
|
||||
SECTION("binary formats")
|
||||
{
|
||||
const Json j = {{"min", limits::min()}, {"max", limits::max()}, {"zero", 0}, {"small", 100}, {"negative", -100}, {"float", 1.5}};
|
||||
|
||||
CHECK(Json::from_cbor(Json::to_cbor(j)) == j);
|
||||
CHECK(Json::from_msgpack(Json::to_msgpack(j)) == j);
|
||||
CHECK(Json::from_ubjson(Json::to_ubjson(j)) == j);
|
||||
CHECK(Json::from_bjdata(Json::to_bjdata(j)) == j);
|
||||
CHECK(Json::from_bson(Json::to_bson(j)) == j);
|
||||
CHECK(Json::from_bon8(Json::to_bon8(j)) == j);
|
||||
|
||||
for (const auto& element : Json::from_cbor(Json::to_cbor(j)))
|
||||
{
|
||||
CHECK(!element.is_number_unsigned());
|
||||
}
|
||||
|
||||
// CBOR unsigned integer 2^64-1 does not fit and is read as float
|
||||
const std::vector<std::uint8_t> cbor_max = {0x1B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||
const Json from_cbor = Json::from_cbor(cbor_max);
|
||||
CHECK(from_cbor.is_number_float());
|
||||
CHECK(from_cbor.template get<double>() == 18446744073709551615.0);
|
||||
|
||||
// MessagePack uint 8 is read as number_integer
|
||||
const std::vector<std::uint8_t> msgpack_uint8 = {0xCC, 0xC8};
|
||||
const Json from_msgpack = Json::from_msgpack(msgpack_uint8);
|
||||
CHECK(from_msgpack.is_number_integer());
|
||||
CHECK(from_msgpack == 200);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("same type for number_integer_t and number_unsigned_t: type traits")
|
||||
{
|
||||
CHECK(std::is_same<nlohmann::detail::number_unsigned_overload_t<json64>, nlohmann::detail::same_as_number_integer_type>::value);
|
||||
CHECK(std::is_same<nlohmann::detail::number_unsigned_overload_t<nlohmann::json>, std::uint64_t>::value);
|
||||
CHECK(nlohmann::detail::is_compatible_number_integer_type<json64, unsigned>::value);
|
||||
CHECK(!nlohmann::detail::is_compatible_number_integer_type<nlohmann::json, unsigned>::value);
|
||||
CHECK(!nlohmann::detail::is_compatible_number_integer_type<json64, bool>::value);
|
||||
}
|
||||
Reference in new issue
Block a user