🚧 support for UBJSON high-precision numbers #2286
This commit is contained in:
parent
7360e09830
commit
8aa6da61dc
4 changed files with 86 additions and 35 deletions
|
@ -286,7 +286,7 @@ json.exception.out_of_range.403 | key 'foo' not found | The provided key was not
|
||||||
json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved.
|
json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved.
|
||||||
json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value.
|
json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value.
|
||||||
json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF.
|
json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF.
|
||||||
json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. |
|
json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. (until version 3.8.0) |
|
||||||
json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. |
|
json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. |
|
||||||
json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string |
|
json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string |
|
||||||
|
|
||||||
|
|
|
@ -1319,7 +1319,17 @@ class binary_writer
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(n) + " cannot be represented by UBJSON as it does not fit int64"));
|
if (add_prefix)
|
||||||
|
{
|
||||||
|
oa->write_character(to_char_type('H')); // high-precision number
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto number = BasicJsonType(n).dump();
|
||||||
|
write_number_with_ubjson_prefix(number.size(), true);
|
||||||
|
for (std::size_t i = 0; i < number.size(); ++i)
|
||||||
|
{
|
||||||
|
oa->write_character(to_char_type(number[i]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1373,19 +1383,23 @@ class binary_writer
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(n) + " cannot be represented by UBJSON as it does not fit int64"));
|
if (add_prefix)
|
||||||
|
{
|
||||||
|
oa->write_character(to_char_type('H')); // high-precision number
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto number = BasicJsonType(n).dump();
|
||||||
|
write_number_with_ubjson_prefix(number.size(), true);
|
||||||
|
for (std::size_t i = 0; i < number.size(); ++i)
|
||||||
|
{
|
||||||
|
oa->write_character(to_char_type(number[i]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief determine the type prefix of container values
|
@brief determine the type prefix of container values
|
||||||
|
|
||||||
@note This function does not need to be 100% accurate when it comes to
|
|
||||||
integer limits. In case a number exceeds the limits of int64_t,
|
|
||||||
this will be detected by a later call to function
|
|
||||||
write_number_with_ubjson_prefix. Therefore, we return 'L' for any
|
|
||||||
value that does not fit the previous limits.
|
|
||||||
*/
|
*/
|
||||||
CharType ubjson_prefix(const BasicJsonType& j) const noexcept
|
CharType ubjson_prefix(const BasicJsonType& j) const noexcept
|
||||||
{
|
{
|
||||||
|
@ -1415,9 +1429,13 @@ class binary_writer
|
||||||
{
|
{
|
||||||
return 'l';
|
return 'l';
|
||||||
}
|
}
|
||||||
// no check and assume int64_t (see note above)
|
if ((std::numeric_limits<std::int64_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
|
||||||
|
{
|
||||||
return 'L';
|
return 'L';
|
||||||
}
|
}
|
||||||
|
// anything else is treated as high-precision number
|
||||||
|
return 'H';
|
||||||
|
}
|
||||||
|
|
||||||
case value_t::number_unsigned:
|
case value_t::number_unsigned:
|
||||||
{
|
{
|
||||||
|
@ -1437,9 +1455,13 @@ class binary_writer
|
||||||
{
|
{
|
||||||
return 'l';
|
return 'l';
|
||||||
}
|
}
|
||||||
// no check and assume int64_t (see note above)
|
if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
|
||||||
|
{
|
||||||
return 'L';
|
return 'L';
|
||||||
}
|
}
|
||||||
|
// anything else is treated as high-precision number
|
||||||
|
return 'H';
|
||||||
|
}
|
||||||
|
|
||||||
case value_t::number_float:
|
case value_t::number_float:
|
||||||
return get_ubjson_float_prefix(j.m_value.number_float);
|
return get_ubjson_float_prefix(j.m_value.number_float);
|
||||||
|
|
|
@ -2469,7 +2469,7 @@ json.exception.out_of_range.403 | key 'foo' not found | The provided key was not
|
||||||
json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved.
|
json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved.
|
||||||
json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value.
|
json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value.
|
||||||
json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF.
|
json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF.
|
||||||
json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. |
|
json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. (until version 3.8.0) |
|
||||||
json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. |
|
json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. |
|
||||||
json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string |
|
json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string |
|
||||||
|
|
||||||
|
@ -13889,7 +13889,17 @@ class binary_writer
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(n) + " cannot be represented by UBJSON as it does not fit int64"));
|
if (add_prefix)
|
||||||
|
{
|
||||||
|
oa->write_character(to_char_type('H')); // high-precision number
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto number = BasicJsonType(n).dump();
|
||||||
|
write_number_with_ubjson_prefix(number.size(), true);
|
||||||
|
for (std::size_t i = 0; i < number.size(); ++i)
|
||||||
|
{
|
||||||
|
oa->write_character(to_char_type(number[i]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13943,19 +13953,23 @@ class binary_writer
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(n) + " cannot be represented by UBJSON as it does not fit int64"));
|
if (add_prefix)
|
||||||
|
{
|
||||||
|
oa->write_character(to_char_type('H')); // high-precision number
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto number = BasicJsonType(n).dump();
|
||||||
|
write_number_with_ubjson_prefix(number.size(), true);
|
||||||
|
for (std::size_t i = 0; i < number.size(); ++i)
|
||||||
|
{
|
||||||
|
oa->write_character(to_char_type(number[i]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief determine the type prefix of container values
|
@brief determine the type prefix of container values
|
||||||
|
|
||||||
@note This function does not need to be 100% accurate when it comes to
|
|
||||||
integer limits. In case a number exceeds the limits of int64_t,
|
|
||||||
this will be detected by a later call to function
|
|
||||||
write_number_with_ubjson_prefix. Therefore, we return 'L' for any
|
|
||||||
value that does not fit the previous limits.
|
|
||||||
*/
|
*/
|
||||||
CharType ubjson_prefix(const BasicJsonType& j) const noexcept
|
CharType ubjson_prefix(const BasicJsonType& j) const noexcept
|
||||||
{
|
{
|
||||||
|
@ -13985,9 +13999,13 @@ class binary_writer
|
||||||
{
|
{
|
||||||
return 'l';
|
return 'l';
|
||||||
}
|
}
|
||||||
// no check and assume int64_t (see note above)
|
if ((std::numeric_limits<std::int64_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
|
||||||
|
{
|
||||||
return 'L';
|
return 'L';
|
||||||
}
|
}
|
||||||
|
// anything else is treated as high-precision number
|
||||||
|
return 'H';
|
||||||
|
}
|
||||||
|
|
||||||
case value_t::number_unsigned:
|
case value_t::number_unsigned:
|
||||||
{
|
{
|
||||||
|
@ -14007,9 +14025,13 @@ class binary_writer
|
||||||
{
|
{
|
||||||
return 'l';
|
return 'l';
|
||||||
}
|
}
|
||||||
// no check and assume int64_t (see note above)
|
if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
|
||||||
|
{
|
||||||
return 'L';
|
return 'L';
|
||||||
}
|
}
|
||||||
|
// anything else is treated as high-precision number
|
||||||
|
return 'H';
|
||||||
|
}
|
||||||
|
|
||||||
case value_t::number_float:
|
case value_t::number_float:
|
||||||
return get_ubjson_float_prefix(j.m_value.number_float);
|
return get_ubjson_float_prefix(j.m_value.number_float);
|
||||||
|
|
|
@ -32,6 +32,7 @@ SOFTWARE.
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <test_data.hpp>
|
#include <test_data.hpp>
|
||||||
|
@ -803,6 +804,21 @@ TEST_CASE("UBJSON")
|
||||||
std::vector<uint8_t> vec3 = {'H', 2, '1', '0'};
|
std::vector<uint8_t> vec3 = {'H', 2, '1', '0'};
|
||||||
CHECK_THROWS_WITH_AS(json::from_ubjson(vec3), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON size: expected length type specification (U, i, I, l, L) after '#'; last byte: 0x02", json::parse_error);
|
CHECK_THROWS_WITH_AS(json::from_ubjson(vec3), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON size: expected length type specification (U, i, I, l, L) after '#'; last byte: 0x02", json::parse_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("serialization")
|
||||||
|
{
|
||||||
|
// number that does not fit int64
|
||||||
|
json j = 11111111111111111111ULL;
|
||||||
|
CHECK(j.is_number_unsigned());
|
||||||
|
|
||||||
|
// number will be serialized to high-precision number
|
||||||
|
const auto vec = json::to_ubjson(j);
|
||||||
|
std::vector<uint8_t> expected = {'H', 'i', 0x14, '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1'};
|
||||||
|
CHECK(vec == expected);
|
||||||
|
|
||||||
|
// roundtrip
|
||||||
|
CHECK(json::from_ubjson(vec) == j);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1576,15 +1592,6 @@ TEST_CASE("UBJSON")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("number out of range")
|
|
||||||
{
|
|
||||||
// larger than max int64
|
|
||||||
json j = 9223372036854775808llu;
|
|
||||||
json _;
|
|
||||||
CHECK_THROWS_AS(_ = json::to_ubjson(j), json::out_of_range&);
|
|
||||||
CHECK_THROWS_WITH(_ = json::to_ubjson(j), "[json.exception.out_of_range.407] integer number 9223372036854775808 cannot be represented by UBJSON as it does not fit int64");
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("excessive size")
|
SECTION("excessive size")
|
||||||
{
|
{
|
||||||
SECTION("array")
|
SECTION("array")
|
||||||
|
|
Loading…
Reference in a new issue