BSON: Reworked the binary_writer such that it precomputes the size of the BSON-output.

This way, the output_adapter can work on simple output iterators and no longer requires random access iterators.
This commit is contained in:
Julian Becker 2018-10-07 17:57:13 +02:00
parent 81f4b34e06
commit 062aeaf7b6
5 changed files with 488 additions and 384 deletions

View file

@ -9,6 +9,7 @@
#include <nlohmann/detail/input/binary_reader.hpp> #include <nlohmann/detail/input/binary_reader.hpp>
#include <nlohmann/detail/output/output_adapters.hpp> #include <nlohmann/detail/output/output_adapters.hpp>
namespace nlohmann namespace nlohmann
{ {
namespace detail 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<CharType>(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<CharType>(element_type)); // boolean
oa->write_characters( oa->write_characters(
reinterpret_cast<const CharType*>(name.c_str()), reinterpret_cast<const CharType*>(name.c_str()),
name.size() + 1u); name.size() + 1u);
oa->write_character(j.m_value.boolean ? static_cast<CharType>(0x01) : static_cast<CharType>(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<CharType>(0x01)); // double write_bson_entry_header(name, 0x08);
oa->write_characters( oa->write_character(value ? static_cast<CharType>(0x01) : static_cast<CharType>(0x00));
reinterpret_cast<const CharType*>(name.c_str()),
name.size() + 1u);
write_number<decltype(j.m_value.number_float), true>(j.m_value.number_float);
return /*id*/ 1ul + name.size() + 1u + /*double value*/ 8u;
} }
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<CharType>(0x02)); // string (UTF-8) write_bson_entry_header(name, 0x01);
oa->write_characters( write_number<double, true>(value);
reinterpret_cast<const CharType*>(name.c_str()),
name.size() + 1u);
write_number<std::int32_t, true>(static_cast<std::int32_t>(j.m_value.string->size() + 1ul));
oa->write_characters(
reinterpret_cast<const CharType*>(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;
} }
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<CharType>(0x0A)); // null return sizeof(std::int32_t) + value.size() + 1ul;
oa->write_characters(
reinterpret_cast<const CharType*>(name.c_str()),
name.size() + 1u);
return /*id*/ 1ul + name.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; write_bson_entry_header(name, 0x02);
if ((std::numeric_limits<int32_t>::min)() <= n and n <= (std::numeric_limits<int32_t>::max)())
write_number<std::int32_t, true>(static_cast<std::int32_t>(value.size() + 1ul));
oa->write_characters(
reinterpret_cast<const CharType*>(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<std::int32_t>::min)() <= value and value <= (std::numeric_limits<std::int32_t>::max)())
{ {
oa->write_character(static_cast<CharType>(0x10)); // int32 return sizeof(std::int32_t);
oa->write_characters(
reinterpret_cast<const CharType*>(name.c_str()),
name.size() + 1u);
write_number<std::int32_t, true>(static_cast<std::int32_t>(n));
return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int32_t);
} }
else else
{ {
oa->write_character(static_cast<CharType>(0x12)); // int64 return sizeof(std::int64_t);
oa->write_characters(
reinterpret_cast<const CharType*>(name.c_str()),
name.size() + 1u);
write_number<std::int64_t, true>(static_cast<std::int64_t>(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) /*!
@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 ((std::numeric_limits<std::int32_t>::min)() <= value and value <= (std::numeric_limits<std::int32_t>::max)())
if (n <= static_cast<uint64_t>((std::numeric_limits<uint32_t>::max)()))
{ {
oa->write_character(static_cast<CharType>(0x10)); // int32 write_bson_entry_header(name, 0x10); // int32
oa->write_characters( write_number<std::int32_t, true>(static_cast<std::int32_t>(value));
reinterpret_cast<const CharType*>(name.c_str()),
name.size() + 1u);
write_number<std::int32_t, true>(static_cast<std::int32_t>(n));
return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int32_t);
} }
else else
{ {
oa->write_character(static_cast<CharType>(0x12)); // int64 write_bson_entry_header(name, 0x12); // int64
oa->write_characters( write_number<std::int64_t, true>(static_cast<std::int64_t>(value));
reinterpret_cast<const CharType*>(name.c_str()),
name.size() + 1u);
write_number<std::int64_t, true>(static_cast<std::int64_t>(j.m_value.number_integer));
return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int64_t);
} }
} }
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<CharType>(0x03)); // object if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
oa->write_characters( {
reinterpret_cast<const CharType*>(name.c_str()), return sizeof(std::int32_t);
name.size() + 1u); }
else
auto const embedded_document_size = write_bson_object(j); {
return sizeof(std::int64_t);
return /*id*/ 1ul + name.size() + 1ul + embedded_document_size; }
} }
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<CharType>(0x04)); // object if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
oa->write_characters(
reinterpret_cast<const CharType*>(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); write_bson_entry_header(name, 0x10); // int32
write_number<std::int32_t, true>(static_cast<std::int32_t>(value));
}
else
{
write_bson_entry_header(name, 0x12); // int64
write_number<std::int64_t, true>(static_cast<std::int64_t>(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<std::int32_t, true>(calc_bson_array_size(value));
for (const auto& el : value)
{
write_bson_element("", el);
} }
oa->write_character(static_cast<CharType>(0x00)); oa->write_character(static_cast<CharType>(0x00));
write_number_at<std::int32_t, true>(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()) switch (j.type())
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
default: default:
assert(false); assert(false);
break; return;
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
case value_t::object: 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: case value_t::array:
return write_bson_array(name, j); return write_bson_array(name, *j.m_value.array);
case value_t::boolean: case value_t::boolean:
return write_bson_boolean(name, j); return write_bson_boolean(name, j.m_value.boolean);
case value_t::number_float: 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: 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: 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: case value_t::string:
return write_bson_string(name, j); return write_bson_string(name, *j.m_value.string);
case value_t::null: 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 @param[in] j JSON value to serialize
@pre j.type() == value_t::object @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); write_number<std::int32_t, true>(calc_bson_object_size(value));
auto document_size_offset = oa->reserve_characters(4ul);
std::int32_t document_size = 5ul;
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<CharType>(0x00)); oa->write_character(static_cast<CharType>(0x00));
write_number_at<std::int32_t, true>(document_size_offset, document_size);
return document_size;
} }
/*! /*!
@ -873,7 +965,7 @@ class binary_writer
case value_t::discarded: case value_t::discarded:
break; break;
case value_t::object: case value_t::object:
write_bson_object(j); write_bson_object(*j.m_value.object);
break; break;
} }
} }
@ -909,35 +1001,6 @@ class binary_writer
oa->write_characters(vec.data(), sizeof(NumberType)); 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<typename NumberType, bool OutputIsLittleEndian = false>
void write_number_at(std::size_t offset, const NumberType n)
{
// step 1: write number to array of length NumberType
std::array<CharType, sizeof(NumberType)> 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) // UBJSON: write number (floating point)
template<typename NumberType, typename std::enable_if< template<typename NumberType, typename std::enable_if<
std::is_floating_point<NumberType>::value, int>::type = 0> std::is_floating_point<NumberType>::value, int>::type = 0>

View file

@ -18,8 +18,6 @@ template<typename CharType> struct output_adapter_protocol
{ {
virtual void write_character(CharType c) = 0; virtual void write_character(CharType c) = 0;
virtual void write_characters(const CharType* s, std::size_t length) = 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; virtual ~output_adapter_protocol() = default;
}; };
@ -44,18 +42,6 @@ class output_vector_adapter : public output_adapter_protocol<CharType>
std::copy(s, s + length, std::back_inserter(v)); 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<CharType>(0x00));
return position;
}
private: private:
std::vector<CharType>& v; std::vector<CharType>& v;
}; };
@ -77,22 +63,6 @@ class output_stream_adapter : public output_adapter_protocol<CharType>
stream.write(s, static_cast<std::streamsize>(length)); stream.write(s, static_cast<std::streamsize>(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<typename std::basic_ostream<CharType>::pos_type>(position));
stream.write(s, static_cast<std::streamsize>(length));
stream.seekp(orig_offset);
}
std::size_t reserve_characters(std::size_t length) override
{
const auto position = stream.tellp();
std::vector<CharType> empty(length, static_cast<CharType>(0));
stream.write(empty.data(), length);
return static_cast<std::size_t>(position);
}
private: private:
std::basic_ostream<CharType>& stream; std::basic_ostream<CharType>& stream;
}; };
@ -114,19 +84,6 @@ class output_string_adapter : public output_adapter_protocol<CharType>
str.append(s, length); 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<CharType>(0x00));
return position;
}
private: private:
StringType& str; StringType& str;
}; };

View file

@ -6594,9 +6594,45 @@ class basic_json
/*! /*!
@brief Serializes the given JSON object `j` to BSON and returns a vector @brief Serializes the given JSON object `j` to BSON and returns a vector
containing the corresponding BSON-representation. containing the corresponding BSON-representation.
@param j The JSON object to convert to BSON.
@return The BSON representation of the JSON input `j`. BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are
@pre The input `j` shall be an object: `j.is_object() == true` 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<uint8_t> to_bson(const basic_json& j) static std::vector<uint8_t> to_bson(const basic_json& j)
{ {
@ -6611,6 +6647,7 @@ class basic_json
@param j The JSON object to convert to BSON. @param j The JSON object to convert to BSON.
@param o The output adapter that receives the binary BSON representation. @param o The output adapter that receives the binary BSON representation.
@pre The input `j` shall be an object: `j.is_object() == true` @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<uint8_t> o) static void to_bson(const basic_json& j, detail::output_adapter<uint8_t> o)
{ {

View file

@ -5839,8 +5839,6 @@ template<typename CharType> struct output_adapter_protocol
{ {
virtual void write_character(CharType c) = 0; virtual void write_character(CharType c) = 0;
virtual void write_characters(const CharType* s, std::size_t length) = 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; virtual ~output_adapter_protocol() = default;
}; };
@ -5865,18 +5863,6 @@ class output_vector_adapter : public output_adapter_protocol<CharType>
std::copy(s, s + length, std::back_inserter(v)); 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<CharType>(0x00));
return position;
}
private: private:
std::vector<CharType>& v; std::vector<CharType>& v;
}; };
@ -5898,22 +5884,6 @@ class output_stream_adapter : public output_adapter_protocol<CharType>
stream.write(s, static_cast<std::streamsize>(length)); stream.write(s, static_cast<std::streamsize>(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<typename std::basic_ostream<CharType>::pos_type>(position));
stream.write(s, static_cast<std::streamsize>(length));
stream.seekp(orig_offset);
}
std::size_t reserve_characters(std::size_t length) override
{
const auto position = stream.tellp();
std::vector<CharType> empty(length, static_cast<CharType>(0));
stream.write(empty.data(), length);
return static_cast<std::size_t>(position);
}
private: private:
std::basic_ostream<CharType>& stream; std::basic_ostream<CharType>& stream;
}; };
@ -5935,19 +5905,6 @@ class output_string_adapter : public output_adapter_protocol<CharType>
str.append(s, length); 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<CharType>(0x00));
return position;
}
private: private:
StringType& str; StringType& str;
}; };
@ -7896,6 +7853,7 @@ class binary_reader
// #include <nlohmann/detail/output/output_adapters.hpp> // #include <nlohmann/detail/output/output_adapters.hpp>
namespace nlohmann namespace nlohmann
{ {
namespace detail 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<CharType>(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<CharType>(element_type)); // boolean
oa->write_characters( oa->write_characters(
reinterpret_cast<const CharType*>(name.c_str()), reinterpret_cast<const CharType*>(name.c_str()),
name.size() + 1u); name.size() + 1u);
oa->write_character(j.m_value.boolean ? static_cast<CharType>(0x01) : static_cast<CharType>(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<CharType>(0x01)); // double write_bson_entry_header(name, 0x08);
oa->write_characters( oa->write_character(value ? static_cast<CharType>(0x01) : static_cast<CharType>(0x00));
reinterpret_cast<const CharType*>(name.c_str()),
name.size() + 1u);
write_number<decltype(j.m_value.number_float), true>(j.m_value.number_float);
return /*id*/ 1ul + name.size() + 1u + /*double value*/ 8u;
} }
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<CharType>(0x02)); // string (UTF-8) write_bson_entry_header(name, 0x01);
oa->write_characters( write_number<double, true>(value);
reinterpret_cast<const CharType*>(name.c_str()),
name.size() + 1u);
write_number<std::int32_t, true>(static_cast<std::int32_t>(j.m_value.string->size() + 1ul));
oa->write_characters(
reinterpret_cast<const CharType*>(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;
} }
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<CharType>(0x0A)); // null return sizeof(std::int32_t) + value.size() + 1ul;
oa->write_characters(
reinterpret_cast<const CharType*>(name.c_str()),
name.size() + 1u);
return /*id*/ 1ul + name.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; write_bson_entry_header(name, 0x02);
if ((std::numeric_limits<int32_t>::min)() <= n and n <= (std::numeric_limits<int32_t>::max)())
write_number<std::int32_t, true>(static_cast<std::int32_t>(value.size() + 1ul));
oa->write_characters(
reinterpret_cast<const CharType*>(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<std::int32_t>::min)() <= value and value <= (std::numeric_limits<std::int32_t>::max)())
{ {
oa->write_character(static_cast<CharType>(0x10)); // int32 return sizeof(std::int32_t);
oa->write_characters(
reinterpret_cast<const CharType*>(name.c_str()),
name.size() + 1u);
write_number<std::int32_t, true>(static_cast<std::int32_t>(n));
return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int32_t);
} }
else else
{ {
oa->write_character(static_cast<CharType>(0x12)); // int64 return sizeof(std::int64_t);
oa->write_characters(
reinterpret_cast<const CharType*>(name.c_str()),
name.size() + 1u);
write_number<std::int64_t, true>(static_cast<std::int64_t>(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) /*!
@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 ((std::numeric_limits<std::int32_t>::min)() <= value and value <= (std::numeric_limits<std::int32_t>::max)())
if (n <= static_cast<uint64_t>((std::numeric_limits<uint32_t>::max)()))
{ {
oa->write_character(static_cast<CharType>(0x10)); // int32 write_bson_entry_header(name, 0x10); // int32
oa->write_characters( write_number<std::int32_t, true>(static_cast<std::int32_t>(value));
reinterpret_cast<const CharType*>(name.c_str()),
name.size() + 1u);
write_number<std::int32_t, true>(static_cast<std::int32_t>(n));
return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int32_t);
} }
else else
{ {
oa->write_character(static_cast<CharType>(0x12)); // int64 write_bson_entry_header(name, 0x12); // int64
oa->write_characters( write_number<std::int64_t, true>(static_cast<std::int64_t>(value));
reinterpret_cast<const CharType*>(name.c_str()),
name.size() + 1u);
write_number<std::int64_t, true>(static_cast<std::int64_t>(j.m_value.number_integer));
return /*id*/ 1ul + name.size() + 1ul + sizeof(std::int64_t);
} }
} }
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<CharType>(0x03)); // object if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
oa->write_characters( {
reinterpret_cast<const CharType*>(name.c_str()), return sizeof(std::int32_t);
name.size() + 1u); }
else
auto const embedded_document_size = write_bson_object(j); {
return sizeof(std::int64_t);
return /*id*/ 1ul + name.size() + 1ul + embedded_document_size; }
} }
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<CharType>(0x04)); // object if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
oa->write_characters(
reinterpret_cast<const CharType*>(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); write_bson_entry_header(name, 0x10); // int32
write_number<std::int32_t, true>(static_cast<std::int32_t>(value));
}
else
{
write_bson_entry_header(name, 0x12); // int64
write_number<std::int64_t, true>(static_cast<std::int64_t>(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<std::int32_t, true>(calc_bson_array_size(value));
for (const auto& el : value)
{
write_bson_element("", el);
} }
oa->write_character(static_cast<CharType>(0x00)); oa->write_character(static_cast<CharType>(0x00));
write_number_at<std::int32_t, true>(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()) switch (j.type())
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
default: default:
assert(false); assert(false);
break; return;
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
case value_t::object: 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: case value_t::array:
return write_bson_array(name, j); return write_bson_array(name, *j.m_value.array);
case value_t::boolean: case value_t::boolean:
return write_bson_boolean(name, j); return write_bson_boolean(name, j.m_value.boolean);
case value_t::number_float: 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: 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: 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: case value_t::string:
return write_bson_string(name, j); return write_bson_string(name, *j.m_value.string);
case value_t::null: 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 @param[in] j JSON value to serialize
@pre j.type() == value_t::object @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); write_number<std::int32_t, true>(calc_bson_object_size(value));
auto document_size_offset = oa->reserve_characters(4ul);
std::int32_t document_size = 5ul;
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<CharType>(0x00)); oa->write_character(static_cast<CharType>(0x00));
write_number_at<std::int32_t, true>(document_size_offset, document_size);
return document_size;
} }
/*! /*!
@ -8760,7 +8809,7 @@ class binary_writer
case value_t::discarded: case value_t::discarded:
break; break;
case value_t::object: case value_t::object:
write_bson_object(j); write_bson_object(*j.m_value.object);
break; break;
} }
} }
@ -8796,35 +8845,6 @@ class binary_writer
oa->write_characters(vec.data(), sizeof(NumberType)); 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<typename NumberType, bool OutputIsLittleEndian = false>
void write_number_at(std::size_t offset, const NumberType n)
{
// step 1: write number to array of length NumberType
std::array<CharType, sizeof(NumberType)> 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) // UBJSON: write number (floating point)
template<typename NumberType, typename std::enable_if< template<typename NumberType, typename std::enable_if<
std::is_floating_point<NumberType>::value, int>::type = 0> std::is_floating_point<NumberType>::value, int>::type = 0>
@ -18147,9 +18167,45 @@ class basic_json
/*! /*!
@brief Serializes the given JSON object `j` to BSON and returns a vector @brief Serializes the given JSON object `j` to BSON and returns a vector
containing the corresponding BSON-representation. containing the corresponding BSON-representation.
@param j The JSON object to convert to BSON.
@return The BSON representation of the JSON input `j`. BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are
@pre The input `j` shall be an object: `j.is_object() == true` 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<uint8_t> to_bson(const basic_json& j) static std::vector<uint8_t> to_bson(const basic_json& j)
{ {
@ -18164,6 +18220,7 @@ class basic_json
@param j The JSON object to convert to BSON. @param j The JSON object to convert to BSON.
@param o The output adapter that receives the binary BSON representation. @param o The output adapter that receives the binary BSON representation.
@pre The input `j` shall be an object: `j.is_object() == true` @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<uint8_t> o) static void to_bson(const basic_json& j, detail::output_adapter<uint8_t> o)
{ {

View file

@ -102,16 +102,6 @@ class alt_string
str_impl.resize(n, c); str_impl.resize(n, c);
} }
auto begin() -> std::string::iterator
{
return str_impl.begin();
}
auto end() -> std::string::iterator
{
return str_impl.end();
}
template <typename op_type> template <typename op_type>
bool operator<(const op_type& op) const bool operator<(const op_type& op) const
{ {