From cf485c2907e394aafc50e77bc372603467ee9f33 Mon Sep 17 00:00:00 2001 From: Julian Becker Date: Sat, 15 Sep 2018 13:54:08 +0200 Subject: [PATCH] BSON: Support for arrays --- .../nlohmann/detail/input/binary_reader.hpp | 49 ++++++++++--- .../nlohmann/detail/output/binary_writer.hpp | 24 ++++++ single_include/nlohmann/json.hpp | 73 ++++++++++++++++--- test/src/unit-bson.cpp | 29 +++++++- 4 files changed, 154 insertions(+), 21 deletions(-) diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp index 7deb788a..140cd8ab 100644 --- a/include/nlohmann/detail/input/binary_reader.hpp +++ b/include/nlohmann/detail/input/binary_reader.hpp @@ -157,17 +157,8 @@ class binary_reader return success; } - - bool parse_bson_internal() + void parse_bson_entries() { - std::int32_t documentSize; - get_number_little_endian(documentSize); - - if (not JSON_UNLIKELY(sax->start_object(-1))) - { - return false; - } - while (auto entry_type = get()) { switch (entry_type) @@ -239,8 +230,46 @@ class binary_reader parse_bson_internal(); } break; + case 0x04: // array + { + string_t key; + get_bson_cstr(key); + sax->key(key); + parse_bson_array(); + } + break; } } + } + + bool parse_bson_array() + { + std::int32_t documentSize; + get_number_little_endian(documentSize); + + if (not JSON_UNLIKELY(sax->start_array(-1))) + { + return false; + } + + parse_bson_entries(); + + const auto result = sax->end_array(); + + return result; + } + + bool parse_bson_internal() + { + std::int32_t documentSize; + get_number_little_endian(documentSize); + + if (not JSON_UNLIKELY(sax->start_object(-1))) + { + return false; + } + + parse_bson_entries(); const auto result = sax->end_object(); diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index aa4f34fe..24333935 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -787,6 +787,28 @@ class binary_writer return /*id*/ 1ul + name.size() + 1ul + embedded_document_size; } + std::size_t write_bson_array(const typename BasicJsonType::string_t& name, const BasicJsonType& j) + { + oa->write_character(static_cast(0x04)); // object + oa->write_characters( + reinterpret_cast(name.c_str()), + name.size() + 1u); + + + auto document_size_offset = oa->reserve_characters(4ul); + std::int32_t embedded_document_size = 5ul; + + for (const auto& el : *j.m_value.array) + { + embedded_document_size += write_bson_object_entry("", el); + } + + oa->write_character(static_cast(0x00)); + write_number_little_endian_at(document_size_offset, embedded_document_size); + + return /*id*/ 1ul + name.size() + 1ul + embedded_document_size; + } + std::size_t write_bson_object_entry(const typename BasicJsonType::string_t& name, const BasicJsonType& j) { switch (j.type()) @@ -796,6 +818,8 @@ class binary_writer break; case value_t::object: return write_bson_object_internal(name, j); + case value_t::array: + return write_bson_array(name, j); case value_t::boolean: return write_bson_boolean(name, j); case value_t::number_float: diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 1c966bc3..adb0d294 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -6141,17 +6141,8 @@ class binary_reader return success; } - - bool parse_bson_internal() + void parse_bson_entries() { - std::int32_t documentSize; - get_number_little_endian(documentSize); - - if (not JSON_UNLIKELY(sax->start_object(-1))) - { - return false; - } - while (auto entry_type = get()) { switch (entry_type) @@ -6223,8 +6214,46 @@ class binary_reader parse_bson_internal(); } break; + case 0x04: // array + { + string_t key; + get_bson_cstr(key); + sax->key(key); + parse_bson_array(); + } + break; } } + } + + bool parse_bson_array() + { + std::int32_t documentSize; + get_number_little_endian(documentSize); + + if (not JSON_UNLIKELY(sax->start_array(-1))) + { + return false; + } + + parse_bson_entries(); + + const auto result = sax->end_array(); + + return result; + } + + bool parse_bson_internal() + { + std::int32_t documentSize; + get_number_little_endian(documentSize); + + if (not JSON_UNLIKELY(sax->start_object(-1))) + { + return false; + } + + parse_bson_entries(); const auto result = sax->end_object(); @@ -8628,6 +8657,28 @@ class binary_writer return /*id*/ 1ul + name.size() + 1ul + embedded_document_size; } + std::size_t write_bson_array(const typename BasicJsonType::string_t& name, const BasicJsonType& j) + { + oa->write_character(static_cast(0x04)); // object + oa->write_characters( + reinterpret_cast(name.c_str()), + name.size() + 1u); + + + auto document_size_offset = oa->reserve_characters(4ul); + std::int32_t embedded_document_size = 5ul; + + for (const auto& el : *j.m_value.array) + { + embedded_document_size += write_bson_object_entry("", el); + } + + oa->write_character(static_cast(0x00)); + write_number_little_endian_at(document_size_offset, embedded_document_size); + + return /*id*/ 1ul + name.size() + 1ul + embedded_document_size; + } + std::size_t write_bson_object_entry(const typename BasicJsonType::string_t& name, const BasicJsonType& j) { switch (j.type()) @@ -8637,6 +8688,8 @@ class binary_writer break; case value_t::object: return write_bson_object_internal(name, j); + case value_t::array: + return write_bson_array(name, j); case value_t::boolean: return write_bson_boolean(name, j); case value_t::number_float: diff --git a/test/src/unit-bson.cpp b/test/src/unit-bson.cpp index c016466d..b2886110 100644 --- a/test/src/unit-bson.cpp +++ b/test/src/unit-bson.cpp @@ -354,7 +354,6 @@ TEST_CASE("BSON") SECTION("non-empty object with object member") { - // directly encoding uint64 is not supported in bson (only for timestamp values) json j = { { "entry", json::object() } @@ -381,6 +380,34 @@ TEST_CASE("BSON") CHECK(json::from_bson(result, true, false) == j); } + SECTION("non-empty object with array member") + { + json j = + { + { "entry", json::array() } + }; + + std::vector expected = + { + 0x11, 0x00, 0x00, 0x00, // size (little endian) + 0x04, /// entry: embedded document + 'e', 'n', 't', 'r', 'y', '\x00', + + 0x05, 0x00, 0x00, 0x00, // size (little endian) + // no entries + 0x00, // end marker (embedded document) + + 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("Some more complex document") { // directly encoding uint64 is not supported in bson (only for timestamp values)