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:
parent
81f4b34e06
commit
062aeaf7b6
5 changed files with 488 additions and 384 deletions
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue