diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 24426d48..770c7dd3 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -723,14 +723,42 @@ class binary_writer std::size_t write_bson_integer(const typename BasicJsonType::string_t& name, const BasicJsonType& j) { - if (j.m_value.number_integer <= static_cast((std::numeric_limits::max)())) + auto n = j.m_value.number_integer; + if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x10)); // int32 oa->write_characters( reinterpret_cast(name.c_str()), name.size() + 1u); - write_number_little_endian(static_cast(j.m_value.number_integer)); + write_number_little_endian(static_cast(n)); + + return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int32_t); + } + else + { + oa->write_character(static_cast(0x12)); // int64 + oa->write_characters( + reinterpret_cast(name.c_str()), + name.size() + 1u); + + write_number_little_endian(static_cast(j.m_value.number_integer)); + + return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int64_t); + } + } + + std::size_t write_bson_unsigned(const typename BasicJsonType::string_t& name, const BasicJsonType& j) + { + auto n = j.m_value.number_integer; + if (n <= static_cast((std::numeric_limits::max)())) + { + oa->write_character(static_cast(0x10)); // int32 + oa->write_characters( + reinterpret_cast(name.c_str()), + name.size() + 1u); + + write_number_little_endian(static_cast(n)); return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int32_t); } @@ -760,6 +788,8 @@ class binary_writer return write_bson_double(name, j); case value_t::number_integer: return write_bson_integer(name, j); + case value_t::number_unsigned: + return write_bson_unsigned(name, j); case value_t::string: return write_bson_string(name, j); case value_t::null: diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index c564e57f..fa1d8e39 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -8557,14 +8557,42 @@ class binary_writer std::size_t write_bson_integer(const typename BasicJsonType::string_t& name, const BasicJsonType& j) { - if (j.m_value.number_integer <= static_cast((std::numeric_limits::max)())) + auto n = j.m_value.number_integer; + if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x10)); // int32 oa->write_characters( reinterpret_cast(name.c_str()), name.size() + 1u); - write_number_little_endian(static_cast(j.m_value.number_integer)); + write_number_little_endian(static_cast(n)); + + return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int32_t); + } + else + { + oa->write_character(static_cast(0x12)); // int64 + oa->write_characters( + reinterpret_cast(name.c_str()), + name.size() + 1u); + + write_number_little_endian(static_cast(j.m_value.number_integer)); + + return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int64_t); + } + } + + std::size_t write_bson_unsigned(const typename BasicJsonType::string_t& name, const BasicJsonType& j) + { + auto n = j.m_value.number_integer; + if (n <= static_cast((std::numeric_limits::max)())) + { + oa->write_character(static_cast(0x10)); // int32 + oa->write_characters( + reinterpret_cast(name.c_str()), + name.size() + 1u); + + write_number_little_endian(static_cast(n)); return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int32_t); } @@ -8594,6 +8622,8 @@ class binary_writer return write_bson_double(name, j); case value_t::number_integer: return write_bson_integer(name, j); + case value_t::number_unsigned: + return write_bson_unsigned(name, j); case value_t::string: return write_bson_string(name, j); case value_t::null: diff --git a/test/src/unit-bson.cpp b/test/src/unit-bson.cpp index 73989769..8de05e80 100644 --- a/test/src/unit-bson.cpp +++ b/test/src/unit-bson.cpp @@ -279,5 +279,77 @@ TEST_CASE("BSON") CHECK(json::from_bson(result, true, false) == j); } + SECTION("non-empty object with negative integer (32-bit) member") + { + json j = + { + { "entry", std::int32_t{-1} } + }; + + std::vector expected = + { + 0x10, 0x00, 0x00, 0x00, // size (little endian) + 0x10, /// entry: int32 + 'e', 'n', 't', 'r', 'y', '\x00', + 0xFF, 0xFF, 0xFF, 0xFF, + 0x00 // end marker + }; + + const auto result = json::to_bson(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bson(result) == j); + CHECK(json::from_bson(result, true, false) == j); + } + + SECTION("non-empty object with negative integer (64-bit) member") + { + json j = + { + { "entry", std::int64_t{-1} } + }; + + std::vector expected = + { + 0x10, 0x00, 0x00, 0x00, // size (little endian) + 0x10, /// entry: int32 + 'e', 'n', 't', 'r', 'y', '\x00', + 0xFF, 0xFF, 0xFF, 0xFF, + 0x00 // end marker + }; + + const auto result = json::to_bson(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bson(result) == j); + CHECK(json::from_bson(result, true, false) == j); + } + + SECTION("non-empty object with unsigned integer (64-bit) member") + { + // directly encoding uint64 is not supported in bson (only for timestamp values) + json j = + { + { "entry", std::uint64_t{0x1234567804030201} } + }; + + std::vector expected = + { + 0x14, 0x00, 0x00, 0x00, // size (little endian) + 0x12, /// entry: int64 + 'e', 'n', 't', 'r', 'y', '\x00', + 0x01, 0x02, 0x03, 0x04, 0x78, 0x56, 0x34, 0x12, + 0x00 // end marker + }; + + const auto result = json::to_bson(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bson(result) == j); + CHECK(json::from_bson(result, true, false) == j); + } } }