From e6518ff2d61393d6c536053671523901c9e714a3 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 4 Sep 2021 14:54:54 +0200 Subject: [PATCH] :construction: add integer support --- .../nlohmann/detail/output/binary_writer.hpp | 116 +++++- single_include/nlohmann/json.hpp | 116 +++++- test/src/unit-bon8.cpp | 362 ++++++++++++++++++ 3 files changed, 566 insertions(+), 28 deletions(-) diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 8a999a613..20b553ac5 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -944,27 +944,42 @@ class binary_writer break; } + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned > std::numeric_limits::max()) + { + JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BON8 as it does not fit int64", j)); + } + write_bon8_integer(static_cast(j.m_value.number_unsigned)); + break; + } + + case value_t::number_integer: + { + write_bon8_integer(j.m_value.number_integer); + break; + } + case value_t::number_float: { // special values if (j.m_value.number_float == -1.0) { oa->write_character(to_char_type(0xFB)); - break; } - if (j.m_value.number_float == 0.0) + else if (j.m_value.number_float == 0.0 && !std::signbit(j.m_value.number_float)) { oa->write_character(to_char_type(0xFC)); - break; } - if (j.m_value.number_float == 1.0) + else if (j.m_value.number_float == 1.0) { oa->write_character(to_char_type(0xFD)); - break; } - - // write float with prefix - write_compact_float(j.m_value.number_float, detail::input_format_t::bon8); + else + { + // write float with prefix + write_compact_float(j.m_value.number_float, detail::input_format_t::bon8); + } break; } @@ -1680,6 +1695,79 @@ class binary_writer // BON8 // ////////// + void write_bon8_integer(typename BasicJsonType::number_integer_t value) + { + if (value < std::numeric_limits::min() || value > std::numeric_limits::max()) + { + // 64 bit integers + oa->write_character(0x8D); + write_number(static_cast(value)); + } + else if (value < -33554432 || value > 67108863) + { + // 32 bit integers + oa->write_character(0x8C); + write_number(static_cast(value)); + } + else if (value < -262144) + { + JSON_ASSERT(value >= -33554432); + value = -value - 1; + oa->write_character(0xF0 + (value >> 22 & 0x07)); + oa->write_character(0xC0 + (value >> 16 & 0x3F)); + oa->write_character(value >> 8); + oa->write_character(value); + } + else if (value < -1920) + { + JSON_ASSERT(value >= -262144); + value = -value - 1; + oa->write_character(0xE0 + (value >> 14 & 0x0F)); + oa->write_character(0xC0 + (value >> 8 & 0x3F)); + oa->write_character(value); + } + else if (value < -10) + { + JSON_ASSERT(value >= -1920); + value = -value - 1; + oa->write_character(0xC2 + (value >> 6 & 0x1F)); + oa->write_character(0xC0 + (value & 0x3F)); + } + else if (value < 0) + { + JSON_ASSERT(value >= -10); + value = -value - 1; + oa->write_character(0xB8 + value); + } + else if (value <= 39) + { + JSON_ASSERT(value >= 0); + oa->write_character(0x90 + value); + } + else if (value <= 3839) + { + JSON_ASSERT(value >= 0); + oa->write_character(0xC2 + (value >> 7 & 0x1F)); + oa->write_character(value & 0x7F); + } + else if (value <= 524287) + { + JSON_ASSERT(value >= 0); + oa->write_character(0xE0 + (value >> 15 & 0x0F)); + oa->write_character(value >> 8 & 0x7F); + oa->write_character(value); + } + else + { + JSON_ASSERT(value >= 0); + JSON_ASSERT(value <= 67108863); + oa->write_character(0xF0 + (value >> 23 & 0x17)); + oa->write_character(value >> 16 & 0x7F); + oa->write_character(value >> 8); + oa->write_character(value); + } + } + static constexpr CharType get_bon8_float_prefix(float /*unused*/) { return to_char_type(0x8E); @@ -1735,13 +1823,13 @@ class binary_writer switch (format) { case input_format_t::cbor: - get_cbor_float_prefix(static_cast(n)); + oa->write_character(get_cbor_float_prefix(static_cast(n))); break; case input_format_t::msgpack: - get_msgpack_float_prefix(static_cast(n)); + oa->write_character(get_msgpack_float_prefix(static_cast(n))); break; case input_format_t::bon8: - get_bon8_float_prefix(static_cast(n)); + oa->write_character(get_bon8_float_prefix(static_cast(n))); break; default: break; @@ -1753,13 +1841,13 @@ class binary_writer switch (format) { case input_format_t::cbor: - get_cbor_float_prefix(n); + oa->write_character(get_cbor_float_prefix(n)); break; case input_format_t::msgpack: - get_msgpack_float_prefix(n); + oa->write_character(get_msgpack_float_prefix(n)); break; case input_format_t::bon8: - get_bon8_float_prefix(n); + oa->write_character(get_bon8_float_prefix(n)); break; default: break; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 8c7b3d1b6..1eb2347a9 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -14516,27 +14516,42 @@ class binary_writer break; } + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned > std::numeric_limits::max()) + { + JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BON8 as it does not fit int64", j)); + } + write_bon8_integer(static_cast(j.m_value.number_unsigned)); + break; + } + + case value_t::number_integer: + { + write_bon8_integer(j.m_value.number_integer); + break; + } + case value_t::number_float: { // special values if (j.m_value.number_float == -1.0) { oa->write_character(to_char_type(0xFB)); - break; } - if (j.m_value.number_float == 0.0) + else if (j.m_value.number_float == 0.0 && !std::signbit(j.m_value.number_float)) { oa->write_character(to_char_type(0xFC)); - break; } - if (j.m_value.number_float == 1.0) + else if (j.m_value.number_float == 1.0) { oa->write_character(to_char_type(0xFD)); - break; } - - // write float with prefix - write_compact_float(j.m_value.number_float, detail::input_format_t::bon8); + else + { + // write float with prefix + write_compact_float(j.m_value.number_float, detail::input_format_t::bon8); + } break; } @@ -15252,6 +15267,79 @@ class binary_writer // BON8 // ////////// + void write_bon8_integer(typename BasicJsonType::number_integer_t value) + { + if (value < std::numeric_limits::min() || value > std::numeric_limits::max()) + { + // 64 bit integers + oa->write_character(0x8D); + write_number(static_cast(value)); + } + else if (value < -33554432 || value > 67108863) + { + // 32 bit integers + oa->write_character(0x8C); + write_number(static_cast(value)); + } + else if (value < -262144) + { + JSON_ASSERT(value >= -33554432); + value = -value - 1; + oa->write_character(0xF0 + (value >> 22 & 0x07)); + oa->write_character(0xC0 + (value >> 16 & 0x3F)); + oa->write_character(value >> 8); + oa->write_character(value); + } + else if (value < -1920) + { + JSON_ASSERT(value >= -262144); + value = -value - 1; + oa->write_character(0xE0 + (value >> 14 & 0x0F)); + oa->write_character(0xC0 + (value >> 8 & 0x3F)); + oa->write_character(value); + } + else if (value < -10) + { + JSON_ASSERT(value >= -1920); + value = -value - 1; + oa->write_character(0xC2 + (value >> 6 & 0x1F)); + oa->write_character(0xC0 + (value & 0x3F)); + } + else if (value < 0) + { + JSON_ASSERT(value >= -10); + value = -value - 1; + oa->write_character(0xB8 + value); + } + else if (value <= 39) + { + JSON_ASSERT(value >= 0); + oa->write_character(0x90 + value); + } + else if (value <= 3839) + { + JSON_ASSERT(value >= 0); + oa->write_character(0xC2 + (value >> 7 & 0x1F)); + oa->write_character(value & 0x7F); + } + else if (value <= 524287) + { + JSON_ASSERT(value >= 0); + oa->write_character(0xE0 + (value >> 15 & 0x0F)); + oa->write_character(value >> 8 & 0x7F); + oa->write_character(value); + } + else + { + JSON_ASSERT(value >= 0); + JSON_ASSERT(value <= 67108863); + oa->write_character(0xF0 + (value >> 23 & 0x17)); + oa->write_character(value >> 16 & 0x7F); + oa->write_character(value >> 8); + oa->write_character(value); + } + } + static constexpr CharType get_bon8_float_prefix(float /*unused*/) { return to_char_type(0x8E); @@ -15307,13 +15395,13 @@ class binary_writer switch (format) { case input_format_t::cbor: - get_cbor_float_prefix(static_cast(n)); + oa->write_character(get_cbor_float_prefix(static_cast(n))); break; case input_format_t::msgpack: - get_msgpack_float_prefix(static_cast(n)); + oa->write_character(get_msgpack_float_prefix(static_cast(n))); break; case input_format_t::bon8: - get_bon8_float_prefix(static_cast(n)); + oa->write_character(get_bon8_float_prefix(static_cast(n))); break; default: break; @@ -15325,13 +15413,13 @@ class binary_writer switch (format) { case input_format_t::cbor: - get_cbor_float_prefix(n); + oa->write_character(get_cbor_float_prefix(n)); break; case input_format_t::msgpack: - get_msgpack_float_prefix(n); + oa->write_character(get_msgpack_float_prefix(n)); break; case input_format_t::bon8: - get_bon8_float_prefix(n); + oa->write_character(get_bon8_float_prefix(n)); break; default: break; diff --git a/test/src/unit-bon8.cpp b/test/src/unit-bon8.cpp index 3c17ba46b..e74b0eabd 100644 --- a/test/src/unit-bon8.cpp +++ b/test/src/unit-bon8.cpp @@ -155,6 +155,360 @@ TEST_CASE("BON8") } } + SECTION("unsigned integers") + { + SECTION("0..39") + { + SECTION("0") + { + json j = 0U; + std::vector expected = {0x90}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + + SECTION("39") + { + json j = 39U; + std::vector expected = {0xB7}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + } + + SECTION("40..3839") + { + SECTION("40") + { + json j = 40U; + std::vector expected = {0xC2, 0x28}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + + SECTION("3839") + { + json j = 3839U; + std::vector expected = {0xDF, 0x7F}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + } + + SECTION("3840..524287") + { + SECTION("3840") + { + json j = 3840U; + std::vector expected = {0xE0, 0x0F, 0x00}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + + SECTION("524287") + { + json j = 524287U; + std::vector expected = {0xEF, 0x7F, 0xFF}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + } + + SECTION("524288..67108863") + { + SECTION("524288") + { + json j = 524288U; + std::vector expected = {0xF0, 0x08, 0x00, 0x00}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + + SECTION("67108863") + { + json j = 67108863U; + std::vector expected = {0xF7, 0x7F, 0xFF, 0xFF}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + } + + SECTION("67108864..2147483647 (int32max)") + { + SECTION("67108864") + { + json j = 67108864U; + std::vector expected = {0x8C, 0x04, 0x00, 0x00, 0x00}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + + SECTION("2147483647 (int32max)") + { + json j = 2147483647U; + std::vector expected = {0x8C, 0x7F, 0xFF, 0xFF, 0xFF}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + } + + SECTION("2147483648..9223372036854775807 (int64max)") + { + SECTION("2147483648") + { + json j = 2147483648U; + std::vector expected = {0x8D, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + + SECTION("9223372036854775807 (int64max)") + { + json j = 9223372036854775807U; + std::vector expected = {0x8D, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + } + + SECTION("greater than int64max") + { + json j = 9223372036854775808U; + CHECK_THROWS_WITH_AS(json::to_bon8(j), "[json.exception.out_of_range.407] integer number 9223372036854775808 cannot be represented by BON8 as it does not fit int64", json::out_of_range); + } + } + + SECTION("signed integers") + { + SECTION("-9223372036854775808 (int64min)..-2147483649") + { + SECTION("-9223372036854775808") + { + json j = INT64_MIN; + std::vector expected = {0x8D, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + + SECTION("-2147483649") + { + json j = -2147483649; + std::vector expected = {0x8D, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + } + + SECTION("-2147483648 (int32min)..-33554433") + { + SECTION("-2147483648") + { + json j = -2147483648; + std::vector expected = {0x8C, 0x80, 0x00, 0x00, 0x00}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + + SECTION("-33554433") + { + json j = -33554433; + std::vector expected = {0x8C, 0xFD, 0xFF, 0xFF, 0xFF}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + } + + SECTION("-33554432..-262145") + { + SECTION("-33554432") + { + json j = -33554432; + std::vector expected = {0xF7, 0xFF, 0xFF, 0xFF}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + + SECTION("-262145") + { + json j = -262145; + std::vector expected = {0xF0, 0xC4, 0x00, 0x00}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + } + + SECTION("-262144..-1921") + { + SECTION("-262144") + { + json j = -262144; + std::vector expected = {0xEF, 0xFF, 0xFF}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + + SECTION("-1921") + { + json j = -1921; + std::vector expected = {0xE0, 0xC7, 0x80}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + } + + SECTION("-1920..-11") + { + SECTION("-1920") + { + json j = -1920; + std::vector expected = {0xDF, 0xFF}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + + SECTION("-11") + { + json j = -11; + std::vector expected = {0xC2, 0xCA}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + } + + SECTION("-10..-1") + { + SECTION("-10") + { + json j = -10; + std::vector expected = {0xC1}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + + SECTION("-1") + { + json j = -1; + std::vector expected = {0xB8}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + } + + SECTION("0..39") + { + SECTION("0") + { + json j = 0; + std::vector expected = {0x90}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + + SECTION("39") + { + json j = 39; + std::vector expected = {0xB7}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + } + + SECTION("40..3839") + { + SECTION("40") + { + json j = 40; + std::vector expected = {0xC2, 0x28}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + + SECTION("3839") + { + json j = 3839; + std::vector expected = {0xDF, 0x7F}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + } + + SECTION("3840..524287") + { + SECTION("3840") + { + json j = 3840; + std::vector expected = {0xE0, 0x0F, 0x00}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + + SECTION("524287") + { + json j = 524287; + std::vector expected = {0xEF, 0x7F, 0xFF}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + } + + SECTION("524288..67108863") + { + SECTION("524288") + { + json j = 524288; + std::vector expected = {0xF0, 0x08, 0x00, 0x00}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + + SECTION("67108863") + { + json j = 67108863; + std::vector expected = {0xF7, 0x7F, 0xFF, 0xFF}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + } + + SECTION("67108864..2147483647 (int32max)") + { + SECTION("67108864") + { + json j = 67108864; + std::vector expected = {0x8C, 0x04, 0x00, 0x00, 0x00}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + + SECTION("2147483647 (int32max)") + { + json j = 2147483647; + std::vector expected = {0x8C, 0x7F, 0xFF, 0xFF, 0xFF}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + } + + SECTION("2147483648..9223372036854775807 (int64max)") + { + SECTION("2147483648") + { + json j = 2147483648; + std::vector expected = {0x8D, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + + SECTION("9223372036854775807 (int64max)") + { + json j = 9223372036854775807; + std::vector expected = {0x8D, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } + } + } + SECTION("floating-point numbers") { SECTION("-1.0") @@ -180,6 +534,14 @@ TEST_CASE("BON8") const auto result = json::to_bon8(j); CHECK(result == expected); } + + SECTION("-0.0") + { + json j = -0.0; + std::vector expected = {0x8E, 0x80, 0x00, 0x00, 0x00}; + const auto result = json::to_bon8(j); + CHECK(result == expected); + } } SECTION("string")