mirror of
https://github.com/nlohmann/json.git
synced 2026-10-12 09:27:13 +00:00
Format the out_of_range.407 number without dump()
dump() instantiates the serializer for the whole value type, which does not compile for object key types it cannot write (e.g., the custom_key_test::key_c_str of #5795), even though the value is always a number here. Produce the decimal digits directly instead; they are generated per digit so that the minimum of a signed type is never negated as a whole. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
3 files changed
+109
-6
No files matched your search
@@ -911,15 +911,64 @@ class binary_writer
|
|||||||
return integer_fits_min<TargetType>(n) && integer_fits_max<TargetType>(n);
|
return integer_fits_min<TargetType>(n) && integer_fits_max<TargetType>(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief the decimal digits of the non-negative integer @a n
|
||||||
|
*/
|
||||||
|
template<typename NumberType>
|
||||||
|
static std::string integer_to_string(NumberType n, std::false_type /*is_signed*/)
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
result.push_back(static_cast<char>('0' + n % 10));
|
||||||
|
n = static_cast<NumberType>(n / 10);
|
||||||
|
}
|
||||||
|
while (n != 0);
|
||||||
|
std::reverse(result.begin(), result.end());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief the decimal digits of the integer @a n, with a leading '-' if it
|
||||||
|
is negative
|
||||||
|
*/
|
||||||
|
template<typename NumberType>
|
||||||
|
static std::string integer_to_string(NumberType n, std::true_type /*is_signed*/)
|
||||||
|
{
|
||||||
|
// the remainder of a negative n is not positive; each digit is
|
||||||
|
// negated on its own, because negating the minimum of NumberType
|
||||||
|
// as a whole would overflow
|
||||||
|
const bool negative = n < 0;
|
||||||
|
std::string result;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
const auto remainder = n % 10;
|
||||||
|
result.push_back(static_cast<char>('0' + (remainder < 0 ? -remainder : remainder)));
|
||||||
|
n = static_cast<NumberType>(n / 10);
|
||||||
|
}
|
||||||
|
while (n != 0);
|
||||||
|
if (negative)
|
||||||
|
{
|
||||||
|
result.push_back('-');
|
||||||
|
}
|
||||||
|
std::reverse(result.begin(), result.end());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief throws because the integer @a j is too large for @a format_name
|
@brief throws because the integer @a j is too large for @a format_name
|
||||||
@throw out_of_range.407 always
|
@throw out_of_range.407 always
|
||||||
*/
|
*/
|
||||||
JSON_HEDLEY_NO_RETURN static void throw_integer_out_of_range(const BasicJsonType& j, const char* format_name, const char* range)
|
JSON_HEDLEY_NO_RETURN static void throw_integer_out_of_range(const BasicJsonType& j, const char* format_name, const char* range)
|
||||||
{
|
{
|
||||||
// dump() rather than std::to_string(), which has no overload for
|
// neither std::to_string(), which has no overload for integer types
|
||||||
// integer types wider than 64 bits
|
// wider than 64 bits, nor dump(), which would also instantiate the
|
||||||
const auto number = j.dump();
|
// serializer for objects and fail for key types it cannot write
|
||||||
|
using integer_limits = std::numeric_limits<typename BasicJsonType::number_integer_t>;
|
||||||
|
using unsigned_limits = std::numeric_limits<typename BasicJsonType::number_unsigned_t>;
|
||||||
|
const std::string number = j.is_number_unsigned()
|
||||||
|
? integer_to_string(j.m_data.m_value.number_unsigned, std::integral_constant<bool, unsigned_limits::is_signed>())
|
||||||
|
: integer_to_string(j.m_data.m_value.number_integer, std::integral_constant<bool, integer_limits::is_signed>());
|
||||||
static_cast<void>(number); // unused when JSON_NOEXCEPTION is defined
|
static_cast<void>(number); // unused when JSON_NOEXCEPTION is defined
|
||||||
static_cast<void>(format_name);
|
static_cast<void>(format_name);
|
||||||
static_cast<void>(range);
|
static_cast<void>(range);
|
||||||
|
|||||||
Generated
+52
-3
@@ -22646,15 +22646,64 @@ class binary_writer
|
|||||||
return integer_fits_min<TargetType>(n) && integer_fits_max<TargetType>(n);
|
return integer_fits_min<TargetType>(n) && integer_fits_max<TargetType>(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief the decimal digits of the non-negative integer @a n
|
||||||
|
*/
|
||||||
|
template<typename NumberType>
|
||||||
|
static std::string integer_to_string(NumberType n, std::false_type /*is_signed*/)
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
result.push_back(static_cast<char>('0' + n % 10));
|
||||||
|
n = static_cast<NumberType>(n / 10);
|
||||||
|
}
|
||||||
|
while (n != 0);
|
||||||
|
std::reverse(result.begin(), result.end());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief the decimal digits of the integer @a n, with a leading '-' if it
|
||||||
|
is negative
|
||||||
|
*/
|
||||||
|
template<typename NumberType>
|
||||||
|
static std::string integer_to_string(NumberType n, std::true_type /*is_signed*/)
|
||||||
|
{
|
||||||
|
// the remainder of a negative n is not positive; each digit is
|
||||||
|
// negated on its own, because negating the minimum of NumberType
|
||||||
|
// as a whole would overflow
|
||||||
|
const bool negative = n < 0;
|
||||||
|
std::string result;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
const auto remainder = n % 10;
|
||||||
|
result.push_back(static_cast<char>('0' + (remainder < 0 ? -remainder : remainder)));
|
||||||
|
n = static_cast<NumberType>(n / 10);
|
||||||
|
}
|
||||||
|
while (n != 0);
|
||||||
|
if (negative)
|
||||||
|
{
|
||||||
|
result.push_back('-');
|
||||||
|
}
|
||||||
|
std::reverse(result.begin(), result.end());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief throws because the integer @a j is too large for @a format_name
|
@brief throws because the integer @a j is too large for @a format_name
|
||||||
@throw out_of_range.407 always
|
@throw out_of_range.407 always
|
||||||
*/
|
*/
|
||||||
JSON_HEDLEY_NO_RETURN static void throw_integer_out_of_range(const BasicJsonType& j, const char* format_name, const char* range)
|
JSON_HEDLEY_NO_RETURN static void throw_integer_out_of_range(const BasicJsonType& j, const char* format_name, const char* range)
|
||||||
{
|
{
|
||||||
// dump() rather than std::to_string(), which has no overload for
|
// neither std::to_string(), which has no overload for integer types
|
||||||
// integer types wider than 64 bits
|
// wider than 64 bits, nor dump(), which would also instantiate the
|
||||||
const auto number = j.dump();
|
// serializer for objects and fail for key types it cannot write
|
||||||
|
using integer_limits = std::numeric_limits<typename BasicJsonType::number_integer_t>;
|
||||||
|
using unsigned_limits = std::numeric_limits<typename BasicJsonType::number_unsigned_t>;
|
||||||
|
const std::string number = j.is_number_unsigned()
|
||||||
|
? integer_to_string(j.m_data.m_value.number_unsigned, std::integral_constant<bool, unsigned_limits::is_signed>())
|
||||||
|
: integer_to_string(j.m_data.m_value.number_integer, std::integral_constant<bool, integer_limits::is_signed>());
|
||||||
static_cast<void>(number); // unused when JSON_NOEXCEPTION is defined
|
static_cast<void>(number); // unused when JSON_NOEXCEPTION is defined
|
||||||
static_cast<void>(format_name);
|
static_cast<void>(format_name);
|
||||||
static_cast<void>(range);
|
static_cast<void>(range);
|
||||||
|
|||||||
@@ -183,6 +183,11 @@ TEST_CASE("custom number types: integers beyond 64 bits")
|
|||||||
CHECK_THROWS_WITH_AS(_ = wide_json::to_cbor(int_big), "[json.exception.out_of_range.407] integer number 1267650600228229401496703205376 cannot be represented by CBOR as it does not fit [-2^64, 2^64-1]", out_of_range&);
|
CHECK_THROWS_WITH_AS(_ = wide_json::to_cbor(int_big), "[json.exception.out_of_range.407] integer number 1267650600228229401496703205376 cannot be represented by CBOR as it does not fit [-2^64, 2^64-1]", out_of_range&);
|
||||||
CHECK_THROWS_WITH_AS(_ = wide_json::to_cbor(int_big_negative), "[json.exception.out_of_range.407] integer number -1267650600228229401496703205376 cannot be represented by CBOR as it does not fit [-2^64, 2^64-1]", out_of_range&);
|
CHECK_THROWS_WITH_AS(_ = wide_json::to_cbor(int_big_negative), "[json.exception.out_of_range.407] integer number -1267650600228229401496703205376 cannot be represented by CBOR as it does not fit [-2^64, 2^64-1]", out_of_range&);
|
||||||
CHECK_THROWS_WITH_AS(_ = wide_json::to_cbor(uint_big), "[json.exception.out_of_range.407] integer number 1267650600228229401496703205376 cannot be represented by CBOR as it does not fit uint64", out_of_range&);
|
CHECK_THROWS_WITH_AS(_ = wide_json::to_cbor(uint_big), "[json.exception.out_of_range.407] integer number 1267650600228229401496703205376 cannot be represented by CBOR as it does not fit uint64", out_of_range&);
|
||||||
|
// the extremes of the 128-bit types, whose digits are produced without
|
||||||
|
// negating the minimum as a whole
|
||||||
|
CHECK_THROWS_WITH_AS(_ = wide_json::to_cbor(make_int((std::numeric_limits<int128>::min)())), "[json.exception.out_of_range.407] integer number -170141183460469231731687303715884105728 cannot be represented by CBOR as it does not fit [-2^64, 2^64-1]", out_of_range&);
|
||||||
|
CHECK_THROWS_WITH_AS(_ = wide_json::to_cbor(make_int((std::numeric_limits<int128>::max)())), "[json.exception.out_of_range.407] integer number 170141183460469231731687303715884105727 cannot be represented by CBOR as it does not fit [-2^64, 2^64-1]", out_of_range&);
|
||||||
|
CHECK_THROWS_WITH_AS(_ = wide_json::to_cbor(make_uint((std::numeric_limits<uint128>::max)())), "[json.exception.out_of_range.407] integer number 340282366920938463463374607431768211455 cannot be represented by CBOR as it does not fit uint64", out_of_range&);
|
||||||
CHECK_THROWS_AS(_ = wide_json::to_cbor(make_int(two_64)), out_of_range&);
|
CHECK_THROWS_AS(_ = wide_json::to_cbor(make_int(two_64)), out_of_range&);
|
||||||
CHECK_THROWS_AS(_ = wide_json::to_cbor(make_int(-two_64 - 1)), out_of_range&);
|
CHECK_THROWS_AS(_ = wide_json::to_cbor(make_int(-two_64 - 1)), out_of_range&);
|
||||||
CHECK_THROWS_AS(_ = wide_json::to_cbor(make_uint(static_cast<uint128>(max_uint64) + 1)), out_of_range&);
|
CHECK_THROWS_AS(_ = wide_json::to_cbor(make_uint(static_cast<uint128>(max_uint64) + 1)), out_of_range&);
|
||||||
|
|||||||
Reference in new issue
Block a user