diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp index 9f684273..9b00cc49 100644 --- a/include/nlohmann/detail/input/binary_reader.hpp +++ b/include/nlohmann/detail/input/binary_reader.hpp @@ -127,25 +127,18 @@ class binary_reader bool parse_bson_internal() { - int docLen = 0; - int byte; - for (int i = 0; i < 4; ++i) + std::int32_t documentSize; + get_number_little_endian(documentSize); + + if (not JSON_UNLIKELY(sax->start_object(documentSize - 5))) { - byte = get(); - if (JSON_UNLIKELY(current == std::char_traits::eof())) - { - if (i == 1) - { - return sax->boolean(docLen != 0x00); - } - return false; - } - docLen |= static_cast(byte) << 8 * i; + return false; } - //sax->null(); + const auto result = sax->end_object(); + get(); - return true; + return result; } /*! @@ -927,6 +920,35 @@ class binary_reader return true; } + template + bool get_number_little_endian(NumberType& result) + { + // step 1: read input into array with system's byte order + std::array vec; + for (std::size_t i = 0; i < sizeof(NumberType); ++i) + { + get(); + if (JSON_UNLIKELY(not unexpect_eof())) + { + return false; + } + + // reverse byte order prior to conversion if necessary + if (!is_little_endian) + { + vec[sizeof(NumberType) - i - 1] = static_cast(current); + } + else + { + vec[i] = static_cast(current); // LCOV_EXCL_LINE + } + } + + // step 2: convert array into number of type T and return + std::memcpy(&result, vec.data(), sizeof(NumberType)); + return true; + } + /*! @brief create a string by reading characters from the input diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index f58213f5..4b9a1343 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -684,7 +684,8 @@ class binary_writer void write_bson_object(const BasicJsonType& j) { assert(j.type() == value_t::object); - + write_number_little_endian(5); + oa->write_character(static_cast(0x00)); } /*! diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 606a3574..a68c64a9 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -6068,25 +6068,18 @@ class binary_reader bool parse_bson_internal() { - int docLen = 0; - int byte; - for (int i = 0; i < 4; ++i) + std::int32_t documentSize; + get_number_little_endian(documentSize); + + if (not JSON_UNLIKELY(sax->start_object(documentSize - 5))) { - byte = get(); - if (JSON_UNLIKELY(current == std::char_traits::eof())) - { - if (i == 1) - { - return sax->boolean(docLen != 0x00); - } - return false; - } - docLen |= static_cast(byte) << 8 * i; + return false; } - //sax->null(); + const auto result = sax->end_object(); + get(); - return true; + return result; } /*! @@ -6868,6 +6861,35 @@ class binary_reader return true; } + template + bool get_number_little_endian(NumberType& result) + { + // step 1: read input into array with system's byte order + std::array vec; + for (std::size_t i = 0; i < sizeof(NumberType); ++i) + { + get(); + if (JSON_UNLIKELY(not unexpect_eof())) + { + return false; + } + + // reverse byte order prior to conversion if necessary + if (!is_little_endian) + { + vec[sizeof(NumberType) - i - 1] = static_cast(current); + } + else + { + vec[i] = static_cast(current); // LCOV_EXCL_LINE + } + } + + // step 2: convert array into number of type T and return + std::memcpy(&result, vec.data(), sizeof(NumberType)); + return true; + } + /*! @brief create a string by reading characters from the input @@ -8354,7 +8376,8 @@ class binary_writer void write_bson_object(const BasicJsonType& j) { assert(j.type() == value_t::object); - + write_number_little_endian(5); + oa->write_character(static_cast(0x00)); } /*! diff --git a/test/src/unit-bson.cpp b/test/src/unit-bson.cpp index 4e17f233..78a30139 100644 --- a/test/src/unit-bson.cpp +++ b/test/src/unit-bson.cpp @@ -36,7 +36,7 @@ using nlohmann::json; TEST_CASE("BSON") { - SECTION("individual values") + SECTION("individual values not supported") { SECTION("discarded") { @@ -66,5 +66,50 @@ TEST_CASE("BSON") REQUIRE_THROWS_AS(json::to_bson(j), json::type_error); } } + + SECTION("number") + { + json j = 42; + REQUIRE_THROWS_AS(json::to_bson(j), json::type_error); + } + + SECTION("float") + { + json j = 4.2; + REQUIRE_THROWS_AS(json::to_bson(j), json::type_error); + } + + SECTION("string") + { + json j = "not supported"; + REQUIRE_THROWS_AS(json::to_bson(j), json::type_error); + } + + SECTION("array") + { + json j = std::vector {1, 2, 3, 4, 5, 6, 7}; + REQUIRE_THROWS_AS(json::to_bson(j), json::type_error); + } + } + + SECTION("objects") + { + SECTION("empty object") + { + json j = json::object(); + std::vector expected = + { + 0x05, 0x00, 0x00, 0x00, // size (little endian) + // no entries + 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); + } } }