diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 363bb9b2..bce5116d 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -9,6 +9,7 @@ #include #include + namespace nlohmann { namespace detail @@ -676,187 +677,278 @@ class binary_writer } } - std::size_t write_bson_boolean(const typename BasicJsonType::string_t& name, const BasicJsonType& j) + /*! + @return The size of a BSON document entry header, including the id marker and the entry name size (and its null-terminator). + */ + static std::size_t calc_bson_entry_header_size(const typename BasicJsonType::string_t& name) { - oa->write_character(static_cast(0x08)); // boolean + return /*id*/ 1ul + name.size() + /*zero-terminator*/1u; + } + + /*! + @brief Writes the given @a element_type and @a name to the output adapter + */ + void write_bson_entry_header(const typename BasicJsonType::string_t& name, std::uint8_t element_type) + { + oa->write_character(static_cast(element_type)); // boolean oa->write_characters( reinterpret_cast(name.c_str()), name.size() + 1u); - oa->write_character(j.m_value.boolean ? static_cast(0x01) : static_cast(0x00)); - return /*id*/ 1ul + name.size() + 1u + /*boolean value*/ 1u; } - std::size_t write_bson_double(const typename BasicJsonType::string_t& name, const BasicJsonType& j) + /*! + @brief Writes a BSON element with key @a name and boolean value @a value + */ + void write_bson_boolean(const typename BasicJsonType::string_t& name, const bool value) { - oa->write_character(static_cast(0x01)); // double - oa->write_characters( - reinterpret_cast(name.c_str()), - name.size() + 1u); - write_number(j.m_value.number_float); - return /*id*/ 1ul + name.size() + 1u + /*double value*/ 8u; + write_bson_entry_header(name, 0x08); + oa->write_character(value ? static_cast(0x01) : static_cast(0x00)); } - std::size_t write_bson_string(const typename BasicJsonType::string_t& name, const BasicJsonType& j) + /*! + @brief Writes a BSON element with key @a name and double value @a value + */ + void write_bson_double(const typename BasicJsonType::string_t& name, const double value) { - oa->write_character(static_cast(0x02)); // string (UTF-8) - oa->write_characters( - reinterpret_cast(name.c_str()), - name.size() + 1u); - - write_number(static_cast(j.m_value.string->size() + 1ul)); - oa->write_characters( - reinterpret_cast(j.m_value.string->c_str()), - j.m_value.string->size() + 1); - - return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int32_t) + j.m_value.string->size() + 1ul; + write_bson_entry_header(name, 0x01); + write_number(value); } - std::size_t write_bson_null(const typename BasicJsonType::string_t& name, const BasicJsonType&) + /*! + @return The size of the BSON-encoded string in @a value + */ + static std::size_t calc_bson_string_size(const typename BasicJsonType::string_t& value) { - oa->write_character(static_cast(0x0A)); // null - oa->write_characters( - reinterpret_cast(name.c_str()), - name.size() + 1u); - - return /*id*/ 1ul + name.size() + 1ul; + return sizeof(std::int32_t) + value.size() + 1ul; } - std::size_t write_bson_integer(const typename BasicJsonType::string_t& name, const BasicJsonType& j) + /*! + @brief Writes a BSON element with key @a name and string value @a value + */ + void write_bson_string(const typename BasicJsonType::string_t& name, const typename BasicJsonType::string_t& value) { - auto n = j.m_value.number_integer; - if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) + write_bson_entry_header(name, 0x02); + + write_number(static_cast(value.size() + 1ul)); + oa->write_characters( + reinterpret_cast(value.c_str()), + value.size() + 1); + } + + /*! + @brief Writes a BSON element with key @a name and null value + */ + void write_bson_null(const typename BasicJsonType::string_t& name) + { + write_bson_entry_header(name, 0x0A); + } + + /*! + @return The size of the BSON-encoded integer @a value + */ + static std::size_t calc_bson_integer_size(const std::int64_t value) + { + if ((std::numeric_limits::min)() <= value and value <= (std::numeric_limits::max)()) { - oa->write_character(static_cast(0x10)); // int32 - oa->write_characters( - reinterpret_cast(name.c_str()), - name.size() + 1u); - - write_number(static_cast(n)); - - return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int32_t); + return 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(static_cast(j.m_value.number_integer)); - - return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int64_t); + return sizeof(std::int64_t); } } - std::size_t write_bson_unsigned(const typename BasicJsonType::string_t& name, const BasicJsonType& j) + /*! + @brief Writes a BSON element with key @a name and integer @a value + */ + void write_bson_integer(const typename BasicJsonType::string_t& name, const std::int64_t value) { - auto n = j.m_value.number_integer; - if (n <= static_cast((std::numeric_limits::max)())) + if ((std::numeric_limits::min)() <= value and value <= (std::numeric_limits::max)()) { - oa->write_character(static_cast(0x10)); // int32 - oa->write_characters( - reinterpret_cast(name.c_str()), - name.size() + 1u); - - write_number(static_cast(n)); - - return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int32_t); + write_bson_entry_header(name, 0x10); // int32 + write_number(static_cast(value)); } else { - oa->write_character(static_cast(0x12)); // int64 - oa->write_characters( - reinterpret_cast(name.c_str()), - name.size() + 1u); - - write_number(static_cast(j.m_value.number_integer)); - - return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int64_t); + write_bson_entry_header(name, 0x12); // int64 + write_number(static_cast(value)); } } - std::size_t write_bson_object_internal(const typename BasicJsonType::string_t& name, const BasicJsonType& j) + + /*! + @return The size of the BSON-encoded unsigned integer in @a j + */ + static std::size_t calc_bson_unsigned_size(const std::uint64_t value) { - oa->write_character(static_cast(0x03)); // object - oa->write_characters( - reinterpret_cast(name.c_str()), - name.size() + 1u); - - auto const embedded_document_size = write_bson_object(j); - - return /*id*/ 1ul + name.size() + 1ul + embedded_document_size; + if (value <= static_cast((std::numeric_limits::max)())) + { + return sizeof(std::int32_t); + } + else + { + return sizeof(std::int64_t); + } } - std::size_t write_bson_array(const typename BasicJsonType::string_t& name, const BasicJsonType& j) + /*! + @brief Writes a BSON element with key @a name and unsigned @a value + */ + void write_bson_unsigned(const typename BasicJsonType::string_t& name, const std::uint64_t value) { - 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) + if (value <= static_cast((std::numeric_limits::max)())) { - embedded_document_size += write_bson_object_entry("", el); + write_bson_entry_header(name, 0x10); // int32 + write_number(static_cast(value)); + } + else + { + write_bson_entry_header(name, 0x12); // int64 + write_number(static_cast(value)); + } + } + + /*! + @brief Writes a BSON element with key @a name and object @a value + */ + void write_bson_object_entry(const typename BasicJsonType::string_t& name, const typename BasicJsonType::object_t& value) + { + write_bson_entry_header(name, 0x03); // object + write_bson_object(value); + } + + + /*! + @return The size of the BSON-encoded array @a value + */ + static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value) + { + std::size_t embedded_document_size = 0ul; + + for (const auto& el : value) + { + embedded_document_size += calc_bson_element_size("", el); + } + + return sizeof(std::int32_t) + embedded_document_size + 1ul; + } + + /*! + @brief Writes a BSON element with key @a name and array @a value + */ + void write_bson_array(const typename BasicJsonType::string_t& name, const typename BasicJsonType::array_t& value) + { + write_bson_entry_header(name, 0x04); // array + write_number(calc_bson_array_size(value)); + + for (const auto& el : value) + { + write_bson_element("", el); } oa->write_character(static_cast(0x00)); - write_number_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) + + /*! + @brief Calculates the size necessary to serialize the JSON value @a j with its @a name + @return The calculated size for the BSON document entry for @a j with the given @a name. + */ + static std::size_t calc_bson_element_size(const typename BasicJsonType::string_t& name, const BasicJsonType& j) + { + const auto header_size = calc_bson_entry_header_size(name); + switch (j.type()) + { + // LCOV_EXCL_START + default: + assert(false); + return 0ul; + // LCOV_EXCL_STOP + case value_t::object: + return header_size + calc_bson_object_size(*j.m_value.object); + case value_t::array: + return header_size + calc_bson_array_size(*j.m_value.array); + case value_t::boolean: + return header_size + 1ul; + case value_t::number_float: + return header_size + 8ul; + case value_t::number_integer: + return header_size + calc_bson_integer_size(j.m_value.number_integer); + case value_t::number_unsigned: + return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned); + case value_t::string: + return header_size + calc_bson_string_size(*j.m_value.string); + case value_t::null: + return header_size + 0ul; + }; + } + + + /*! + @brief Serializes the JSON value @a j to BSON and associates it with the key @a name. + @param name The name to associate with the JSON entity @a j within the current BSON document + @return The size of the bson entry + */ + void write_bson_element(const typename BasicJsonType::string_t& name, const BasicJsonType& j) { switch (j.type()) { // LCOV_EXCL_START default: assert(false); - break; + return; // LCOV_EXCL_STOP case value_t::object: - return write_bson_object_internal(name, j); + return write_bson_object_entry(name, *j.m_value.object); case value_t::array: - return write_bson_array(name, j); + return write_bson_array(name, *j.m_value.array); case value_t::boolean: - return write_bson_boolean(name, j); + return write_bson_boolean(name, j.m_value.boolean); case value_t::number_float: - return write_bson_double(name, j); + return write_bson_double(name, j.m_value.number_float); case value_t::number_integer: - return write_bson_integer(name, j); + return write_bson_integer(name, j.m_value.number_integer); case value_t::number_unsigned: - return write_bson_unsigned(name, j); + return write_bson_unsigned(name, j.m_value.number_unsigned); case value_t::string: - return write_bson_string(name, j); + return write_bson_string(name, *j.m_value.string); case value_t::null: - return write_bson_null(name, j); + return write_bson_null(name); }; + } - return 0ul; + /*! + @brief Calculates the size of the BSON serialization of the given + JSON-object @a j. + @param[in] j JSON value to serialize + @pre j.type() == value_t::object + */ + static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value) + { + std::size_t document_size = 0; + + for (const auto& el : value) + { + document_size += calc_bson_element_size(el.first, el.second); + } + + return sizeof(std::int32_t) + document_size + 1ul; } /*! @param[in] j JSON value to serialize @pre j.type() == value_t::object */ - std::size_t write_bson_object(const BasicJsonType& j) + void write_bson_object(const typename BasicJsonType::object_t& value) { - assert(j.type() == value_t::object); - auto document_size_offset = oa->reserve_characters(4ul); - std::int32_t document_size = 5ul; + write_number(calc_bson_object_size(value)); - for (const auto& el : *j.m_value.object) + for (const auto& el : value) { - document_size += write_bson_object_entry(el.first, el.second); + write_bson_element(el.first, el.second); } oa->write_character(static_cast(0x00)); - write_number_at(document_size_offset, document_size); - return document_size; } /*! @@ -873,7 +965,7 @@ class binary_writer case value_t::discarded: break; case value_t::object: - write_bson_object(j); + write_bson_object(*j.m_value.object); break; } } @@ -909,35 +1001,6 @@ class binary_writer oa->write_characters(vec.data(), sizeof(NumberType)); } - /* - @brief write a number to output in little endian format - - @param[in] offset The offset where to start writing - @param[in] n number of type @a NumberType - @tparam NumberType the type of the number - @tparam OutputIsLittleEndian Set to true if output data is - required to be little endian - */ - template - void write_number_at(std::size_t offset, const NumberType n) - { - // step 1: write number to array of length NumberType - std::array vec; - std::memcpy(vec.data(), &n, sizeof(NumberType)); - - // step 2: write array to output (with possible reordering) - // LCOV_EXCL_START - if (is_little_endian && !OutputIsLittleEndian) - { - // reverse byte order prior to conversion if necessary - std::reverse(vec.begin(), vec.end()); - } - // LCOV_EXCL_STOP - - oa->write_characters_at(offset, vec.data(), sizeof(NumberType)); - } - - // UBJSON: write number (floating point) template::value, int>::type = 0> diff --git a/include/nlohmann/detail/output/output_adapters.hpp b/include/nlohmann/detail/output/output_adapters.hpp index 64960011..ff86a6e1 100644 --- a/include/nlohmann/detail/output/output_adapters.hpp +++ b/include/nlohmann/detail/output/output_adapters.hpp @@ -18,8 +18,6 @@ template struct output_adapter_protocol { virtual void write_character(CharType c) = 0; virtual void write_characters(const CharType* s, std::size_t length) = 0; - virtual void write_characters_at(std::size_t position, const CharType* s, std::size_t length) = 0; - virtual std::size_t reserve_characters(std::size_t length) = 0; virtual ~output_adapter_protocol() = default; }; @@ -44,18 +42,6 @@ class output_vector_adapter : public output_adapter_protocol std::copy(s, s + length, std::back_inserter(v)); } - void write_characters_at(std::size_t position, const CharType* s, std::size_t length) override - { - std::copy(s, s + length, std::begin(v) + position); - } - - std::size_t reserve_characters(std::size_t length) override - { - const auto position = v.size(); - std::fill_n(std::back_inserter(v), length, static_cast(0x00)); - return position; - } - private: std::vector& v; }; @@ -77,22 +63,6 @@ class output_stream_adapter : public output_adapter_protocol stream.write(s, static_cast(length)); } - void write_characters_at(std::size_t position, const CharType* s, std::size_t length) override - { - const auto orig_offset = stream.tellp(); - stream.seekp(static_cast::pos_type>(position)); - stream.write(s, static_cast(length)); - stream.seekp(orig_offset); - } - - std::size_t reserve_characters(std::size_t length) override - { - const auto position = stream.tellp(); - std::vector empty(length, static_cast(0)); - stream.write(empty.data(), length); - return static_cast(position); - } - private: std::basic_ostream& stream; }; @@ -114,19 +84,6 @@ class output_string_adapter : public output_adapter_protocol str.append(s, length); } - void write_characters_at(std::size_t position, const CharType* s, std::size_t length) override - { - std::copy(s, s + length, std::begin(str) + position); - } - - std::size_t reserve_characters(std::size_t length) override - { - const auto position = str.size(); - std::fill_n(std::back_inserter(str), length, static_cast(0x00)); - return position; - } - - private: StringType& str; }; diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index c5d2e0db..1b61e995 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -6594,9 +6594,45 @@ class basic_json /*! @brief Serializes the given JSON object `j` to BSON and returns a vector containing the corresponding BSON-representation. - @param j The JSON object to convert to BSON. - @return The BSON representation of the JSON input `j`. - @pre The input `j` shall be an object: `j.is_object() == true` + + BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are + stored as a single entity (a so-called document). + + The library uses the following mapping from JSON values types to BSON types: + + JSON value type | value/range | BSON type | marker + --------------- | --------------------------------- | ----------- | ------ + null | `null` | null | 0x0A + boolean | `true`, `false` | boolean | 0x08 + number_integer | -9223372036854775808..-2147483649 | int64 | 0x12 + number_integer | -2147483648..2147483647 | int32 | 0x10 + number_integer | 2147483648..9223372036854775807 | int64 | 0x12 + number_unsigned | 0..2147483647 | int32 | 0x10 + number_unsigned | 2147483648..9223372036854775807 | int64 | 0x12 + number_float | *any value* | double | 0x01 + string | *any value* | string | 0x02 + array | *any value* | document | 0x04 + object | *any value* | document | 0x03 + + @warning The mapping is **incomplete**, since only JSON-objects (and things + contained therein) can be serialized to BSON. + + @pre The input `j` is required to be an object: `j.is_object() == true` + + @note Any BSON output created via @ref to_bson can be successfully parsed + by @ref from_bson. + + @param[in] j JSON value to serialize + @return BSON serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @sa http://bsonspec.org/spec.html + @sa @ref from_bson(detail::input_adapter, const bool strict) for the + analogous deserialization + @sa @ref to_ubjson(const basic_json&) for the related UBJSON format + @sa @ref to_cbor(const basic_json&) for the related CBOR format + @sa @ref to_msgpack(const basic_json&) for the related MessagePack format */ static std::vector to_bson(const basic_json& j) { @@ -6611,6 +6647,7 @@ class basic_json @param j The JSON object to convert to BSON. @param o The output adapter that receives the binary BSON representation. @pre The input `j` shall be an object: `j.is_object() == true` + @sa @ref to_bson(const basic_json&) */ static void to_bson(const basic_json& j, detail::output_adapter o) { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 52fd79ca..48b17d17 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -5839,8 +5839,6 @@ template struct output_adapter_protocol { virtual void write_character(CharType c) = 0; virtual void write_characters(const CharType* s, std::size_t length) = 0; - virtual void write_characters_at(std::size_t position, const CharType* s, std::size_t length) = 0; - virtual std::size_t reserve_characters(std::size_t length) = 0; virtual ~output_adapter_protocol() = default; }; @@ -5865,18 +5863,6 @@ class output_vector_adapter : public output_adapter_protocol std::copy(s, s + length, std::back_inserter(v)); } - void write_characters_at(std::size_t position, const CharType* s, std::size_t length) override - { - std::copy(s, s + length, std::begin(v) + position); - } - - std::size_t reserve_characters(std::size_t length) override - { - const auto position = v.size(); - std::fill_n(std::back_inserter(v), length, static_cast(0x00)); - return position; - } - private: std::vector& v; }; @@ -5898,22 +5884,6 @@ class output_stream_adapter : public output_adapter_protocol stream.write(s, static_cast(length)); } - void write_characters_at(std::size_t position, const CharType* s, std::size_t length) override - { - const auto orig_offset = stream.tellp(); - stream.seekp(static_cast::pos_type>(position)); - stream.write(s, static_cast(length)); - stream.seekp(orig_offset); - } - - std::size_t reserve_characters(std::size_t length) override - { - const auto position = stream.tellp(); - std::vector empty(length, static_cast(0)); - stream.write(empty.data(), length); - return static_cast(position); - } - private: std::basic_ostream& stream; }; @@ -5935,19 +5905,6 @@ class output_string_adapter : public output_adapter_protocol str.append(s, length); } - void write_characters_at(std::size_t position, const CharType* s, std::size_t length) override - { - std::copy(s, s + length, std::begin(str) + position); - } - - std::size_t reserve_characters(std::size_t length) override - { - const auto position = str.size(); - std::fill_n(std::back_inserter(str), length, static_cast(0x00)); - return position; - } - - private: StringType& str; }; @@ -7896,6 +7853,7 @@ class binary_reader // #include + namespace nlohmann { namespace detail @@ -8563,187 +8521,278 @@ class binary_writer } } - std::size_t write_bson_boolean(const typename BasicJsonType::string_t& name, const BasicJsonType& j) + /*! + @return The size of a BSON document entry header, including the id marker and the entry name size (and its null-terminator). + */ + static std::size_t calc_bson_entry_header_size(const typename BasicJsonType::string_t& name) { - oa->write_character(static_cast(0x08)); // boolean + return /*id*/ 1ul + name.size() + /*zero-terminator*/1u; + } + + /*! + @brief Writes the given @a element_type and @a name to the output adapter + */ + void write_bson_entry_header(const typename BasicJsonType::string_t& name, std::uint8_t element_type) + { + oa->write_character(static_cast(element_type)); // boolean oa->write_characters( reinterpret_cast(name.c_str()), name.size() + 1u); - oa->write_character(j.m_value.boolean ? static_cast(0x01) : static_cast(0x00)); - return /*id*/ 1ul + name.size() + 1u + /*boolean value*/ 1u; } - std::size_t write_bson_double(const typename BasicJsonType::string_t& name, const BasicJsonType& j) + /*! + @brief Writes a BSON element with key @a name and boolean value @a value + */ + void write_bson_boolean(const typename BasicJsonType::string_t& name, const bool value) { - oa->write_character(static_cast(0x01)); // double - oa->write_characters( - reinterpret_cast(name.c_str()), - name.size() + 1u); - write_number(j.m_value.number_float); - return /*id*/ 1ul + name.size() + 1u + /*double value*/ 8u; + write_bson_entry_header(name, 0x08); + oa->write_character(value ? static_cast(0x01) : static_cast(0x00)); } - std::size_t write_bson_string(const typename BasicJsonType::string_t& name, const BasicJsonType& j) + /*! + @brief Writes a BSON element with key @a name and double value @a value + */ + void write_bson_double(const typename BasicJsonType::string_t& name, const double value) { - oa->write_character(static_cast(0x02)); // string (UTF-8) - oa->write_characters( - reinterpret_cast(name.c_str()), - name.size() + 1u); - - write_number(static_cast(j.m_value.string->size() + 1ul)); - oa->write_characters( - reinterpret_cast(j.m_value.string->c_str()), - j.m_value.string->size() + 1); - - return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int32_t) + j.m_value.string->size() + 1ul; + write_bson_entry_header(name, 0x01); + write_number(value); } - std::size_t write_bson_null(const typename BasicJsonType::string_t& name, const BasicJsonType&) + /*! + @return The size of the BSON-encoded string in @a value + */ + static std::size_t calc_bson_string_size(const typename BasicJsonType::string_t& value) { - oa->write_character(static_cast(0x0A)); // null - oa->write_characters( - reinterpret_cast(name.c_str()), - name.size() + 1u); - - return /*id*/ 1ul + name.size() + 1ul; + return sizeof(std::int32_t) + value.size() + 1ul; } - std::size_t write_bson_integer(const typename BasicJsonType::string_t& name, const BasicJsonType& j) + /*! + @brief Writes a BSON element with key @a name and string value @a value + */ + void write_bson_string(const typename BasicJsonType::string_t& name, const typename BasicJsonType::string_t& value) { - auto n = j.m_value.number_integer; - if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) + write_bson_entry_header(name, 0x02); + + write_number(static_cast(value.size() + 1ul)); + oa->write_characters( + reinterpret_cast(value.c_str()), + value.size() + 1); + } + + /*! + @brief Writes a BSON element with key @a name and null value + */ + void write_bson_null(const typename BasicJsonType::string_t& name) + { + write_bson_entry_header(name, 0x0A); + } + + /*! + @return The size of the BSON-encoded integer @a value + */ + static std::size_t calc_bson_integer_size(const std::int64_t value) + { + if ((std::numeric_limits::min)() <= value and value <= (std::numeric_limits::max)()) { - oa->write_character(static_cast(0x10)); // int32 - oa->write_characters( - reinterpret_cast(name.c_str()), - name.size() + 1u); - - write_number(static_cast(n)); - - return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int32_t); + return 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(static_cast(j.m_value.number_integer)); - - return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int64_t); + return sizeof(std::int64_t); } } - std::size_t write_bson_unsigned(const typename BasicJsonType::string_t& name, const BasicJsonType& j) + /*! + @brief Writes a BSON element with key @a name and integer @a value + */ + void write_bson_integer(const typename BasicJsonType::string_t& name, const std::int64_t value) { - auto n = j.m_value.number_integer; - if (n <= static_cast((std::numeric_limits::max)())) + if ((std::numeric_limits::min)() <= value and value <= (std::numeric_limits::max)()) { - oa->write_character(static_cast(0x10)); // int32 - oa->write_characters( - reinterpret_cast(name.c_str()), - name.size() + 1u); - - write_number(static_cast(n)); - - return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int32_t); + write_bson_entry_header(name, 0x10); // int32 + write_number(static_cast(value)); } else { - oa->write_character(static_cast(0x12)); // int64 - oa->write_characters( - reinterpret_cast(name.c_str()), - name.size() + 1u); - - write_number(static_cast(j.m_value.number_integer)); - - return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int64_t); + write_bson_entry_header(name, 0x12); // int64 + write_number(static_cast(value)); } } - std::size_t write_bson_object_internal(const typename BasicJsonType::string_t& name, const BasicJsonType& j) + + /*! + @return The size of the BSON-encoded unsigned integer in @a j + */ + static std::size_t calc_bson_unsigned_size(const std::uint64_t value) { - oa->write_character(static_cast(0x03)); // object - oa->write_characters( - reinterpret_cast(name.c_str()), - name.size() + 1u); - - auto const embedded_document_size = write_bson_object(j); - - return /*id*/ 1ul + name.size() + 1ul + embedded_document_size; + if (value <= static_cast((std::numeric_limits::max)())) + { + return sizeof(std::int32_t); + } + else + { + return sizeof(std::int64_t); + } } - std::size_t write_bson_array(const typename BasicJsonType::string_t& name, const BasicJsonType& j) + /*! + @brief Writes a BSON element with key @a name and unsigned @a value + */ + void write_bson_unsigned(const typename BasicJsonType::string_t& name, const std::uint64_t value) { - 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) + if (value <= static_cast((std::numeric_limits::max)())) { - embedded_document_size += write_bson_object_entry("", el); + write_bson_entry_header(name, 0x10); // int32 + write_number(static_cast(value)); + } + else + { + write_bson_entry_header(name, 0x12); // int64 + write_number(static_cast(value)); + } + } + + /*! + @brief Writes a BSON element with key @a name and object @a value + */ + void write_bson_object_entry(const typename BasicJsonType::string_t& name, const typename BasicJsonType::object_t& value) + { + write_bson_entry_header(name, 0x03); // object + write_bson_object(value); + } + + + /*! + @return The size of the BSON-encoded array @a value + */ + static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value) + { + std::size_t embedded_document_size = 0ul; + + for (const auto& el : value) + { + embedded_document_size += calc_bson_element_size("", el); + } + + return sizeof(std::int32_t) + embedded_document_size + 1ul; + } + + /*! + @brief Writes a BSON element with key @a name and array @a value + */ + void write_bson_array(const typename BasicJsonType::string_t& name, const typename BasicJsonType::array_t& value) + { + write_bson_entry_header(name, 0x04); // array + write_number(calc_bson_array_size(value)); + + for (const auto& el : value) + { + write_bson_element("", el); } oa->write_character(static_cast(0x00)); - write_number_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) + + /*! + @brief Calculates the size necessary to serialize the JSON value @a j with its @a name + @return The calculated size for the BSON document entry for @a j with the given @a name. + */ + static std::size_t calc_bson_element_size(const typename BasicJsonType::string_t& name, const BasicJsonType& j) + { + const auto header_size = calc_bson_entry_header_size(name); + switch (j.type()) + { + // LCOV_EXCL_START + default: + assert(false); + return 0ul; + // LCOV_EXCL_STOP + case value_t::object: + return header_size + calc_bson_object_size(*j.m_value.object); + case value_t::array: + return header_size + calc_bson_array_size(*j.m_value.array); + case value_t::boolean: + return header_size + 1ul; + case value_t::number_float: + return header_size + 8ul; + case value_t::number_integer: + return header_size + calc_bson_integer_size(j.m_value.number_integer); + case value_t::number_unsigned: + return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned); + case value_t::string: + return header_size + calc_bson_string_size(*j.m_value.string); + case value_t::null: + return header_size + 0ul; + }; + } + + + /*! + @brief Serializes the JSON value @a j to BSON and associates it with the key @a name. + @param name The name to associate with the JSON entity @a j within the current BSON document + @return The size of the bson entry + */ + void write_bson_element(const typename BasicJsonType::string_t& name, const BasicJsonType& j) { switch (j.type()) { // LCOV_EXCL_START default: assert(false); - break; + return; // LCOV_EXCL_STOP case value_t::object: - return write_bson_object_internal(name, j); + return write_bson_object_entry(name, *j.m_value.object); case value_t::array: - return write_bson_array(name, j); + return write_bson_array(name, *j.m_value.array); case value_t::boolean: - return write_bson_boolean(name, j); + return write_bson_boolean(name, j.m_value.boolean); case value_t::number_float: - return write_bson_double(name, j); + return write_bson_double(name, j.m_value.number_float); case value_t::number_integer: - return write_bson_integer(name, j); + return write_bson_integer(name, j.m_value.number_integer); case value_t::number_unsigned: - return write_bson_unsigned(name, j); + return write_bson_unsigned(name, j.m_value.number_unsigned); case value_t::string: - return write_bson_string(name, j); + return write_bson_string(name, *j.m_value.string); case value_t::null: - return write_bson_null(name, j); + return write_bson_null(name); }; + } - return 0ul; + /*! + @brief Calculates the size of the BSON serialization of the given + JSON-object @a j. + @param[in] j JSON value to serialize + @pre j.type() == value_t::object + */ + static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value) + { + std::size_t document_size = 0; + + for (const auto& el : value) + { + document_size += calc_bson_element_size(el.first, el.second); + } + + return sizeof(std::int32_t) + document_size + 1ul; } /*! @param[in] j JSON value to serialize @pre j.type() == value_t::object */ - std::size_t write_bson_object(const BasicJsonType& j) + void write_bson_object(const typename BasicJsonType::object_t& value) { - assert(j.type() == value_t::object); - auto document_size_offset = oa->reserve_characters(4ul); - std::int32_t document_size = 5ul; + write_number(calc_bson_object_size(value)); - for (const auto& el : *j.m_value.object) + for (const auto& el : value) { - document_size += write_bson_object_entry(el.first, el.second); + write_bson_element(el.first, el.second); } oa->write_character(static_cast(0x00)); - write_number_at(document_size_offset, document_size); - return document_size; } /*! @@ -8760,7 +8809,7 @@ class binary_writer case value_t::discarded: break; case value_t::object: - write_bson_object(j); + write_bson_object(*j.m_value.object); break; } } @@ -8796,35 +8845,6 @@ class binary_writer oa->write_characters(vec.data(), sizeof(NumberType)); } - /* - @brief write a number to output in little endian format - - @param[in] offset The offset where to start writing - @param[in] n number of type @a NumberType - @tparam NumberType the type of the number - @tparam OutputIsLittleEndian Set to true if output data is - required to be little endian - */ - template - void write_number_at(std::size_t offset, const NumberType n) - { - // step 1: write number to array of length NumberType - std::array vec; - std::memcpy(vec.data(), &n, sizeof(NumberType)); - - // step 2: write array to output (with possible reordering) - // LCOV_EXCL_START - if (is_little_endian && !OutputIsLittleEndian) - { - // reverse byte order prior to conversion if necessary - std::reverse(vec.begin(), vec.end()); - } - // LCOV_EXCL_STOP - - oa->write_characters_at(offset, vec.data(), sizeof(NumberType)); - } - - // UBJSON: write number (floating point) template::value, int>::type = 0> @@ -18147,9 +18167,45 @@ class basic_json /*! @brief Serializes the given JSON object `j` to BSON and returns a vector containing the corresponding BSON-representation. - @param j The JSON object to convert to BSON. - @return The BSON representation of the JSON input `j`. - @pre The input `j` shall be an object: `j.is_object() == true` + + BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are + stored as a single entity (a so-called document). + + The library uses the following mapping from JSON values types to BSON types: + + JSON value type | value/range | BSON type | marker + --------------- | --------------------------------- | ----------- | ------ + null | `null` | null | 0x0A + boolean | `true`, `false` | boolean | 0x08 + number_integer | -9223372036854775808..-2147483649 | int64 | 0x12 + number_integer | -2147483648..2147483647 | int32 | 0x10 + number_integer | 2147483648..9223372036854775807 | int64 | 0x12 + number_unsigned | 0..2147483647 | int32 | 0x10 + number_unsigned | 2147483648..9223372036854775807 | int64 | 0x12 + number_float | *any value* | double | 0x01 + string | *any value* | string | 0x02 + array | *any value* | document | 0x04 + object | *any value* | document | 0x03 + + @warning The mapping is **incomplete**, since only JSON-objects (and things + contained therein) can be serialized to BSON. + + @pre The input `j` is required to be an object: `j.is_object() == true` + + @note Any BSON output created via @ref to_bson can be successfully parsed + by @ref from_bson. + + @param[in] j JSON value to serialize + @return BSON serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @sa http://bsonspec.org/spec.html + @sa @ref from_bson(detail::input_adapter, const bool strict) for the + analogous deserialization + @sa @ref to_ubjson(const basic_json&) for the related UBJSON format + @sa @ref to_cbor(const basic_json&) for the related CBOR format + @sa @ref to_msgpack(const basic_json&) for the related MessagePack format */ static std::vector to_bson(const basic_json& j) { @@ -18164,6 +18220,7 @@ class basic_json @param j The JSON object to convert to BSON. @param o The output adapter that receives the binary BSON representation. @pre The input `j` shall be an object: `j.is_object() == true` + @sa @ref to_bson(const basic_json&) */ static void to_bson(const basic_json& j, detail::output_adapter o) { diff --git a/test/src/unit-alt-string.cpp b/test/src/unit-alt-string.cpp index d866ed70..356835c0 100644 --- a/test/src/unit-alt-string.cpp +++ b/test/src/unit-alt-string.cpp @@ -102,16 +102,6 @@ class alt_string str_impl.resize(n, c); } - auto begin() -> std::string::iterator - { - return str_impl.begin(); - } - - auto end() -> std::string::iterator - { - return str_impl.end(); - } - template bool operator<(const op_type& op) const {