🎨 clean up binary formats
This commit is contained in:
parent
4d1eaace8c
commit
1968e5c793
5 changed files with 1274 additions and 1122 deletions
|
@ -35,7 +35,34 @@ class binary_writer
|
|||
}
|
||||
|
||||
/*!
|
||||
@brief[in] j JSON value to serialize
|
||||
@param[in] j JSON value to serialize
|
||||
@pre j.type() == value_t::object
|
||||
*/
|
||||
void write_bson(const BasicJsonType& j)
|
||||
{
|
||||
switch (j.type())
|
||||
{
|
||||
case value_t::object:
|
||||
{
|
||||
write_bson_object(*j.m_value.object);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::discarded:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name())));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@param[in] j JSON value to serialize
|
||||
*/
|
||||
void write_cbor(const BasicJsonType& j)
|
||||
{
|
||||
|
@ -279,7 +306,7 @@ class binary_writer
|
|||
}
|
||||
|
||||
/*!
|
||||
@brief[in] j JSON value to serialize
|
||||
@param[in] j JSON value to serialize
|
||||
*/
|
||||
void write_msgpack(const BasicJsonType& j)
|
||||
{
|
||||
|
@ -678,13 +705,19 @@ class binary_writer
|
|||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//////////
|
||||
// BSON //
|
||||
//////////
|
||||
|
||||
/*!
|
||||
@return The size of a BSON document entry header, including the id marker and the entry name size (and its null-terminator).
|
||||
@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)
|
||||
{
|
||||
const auto it = name.find(static_cast<typename BasicJsonType::string_t::value_type>(0));
|
||||
if (it != BasicJsonType::string_t::npos)
|
||||
if (JSON_UNLIKELY(it != BasicJsonType::string_t::npos))
|
||||
{
|
||||
JSON_THROW(out_of_range::create(409,
|
||||
"BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")"));
|
||||
|
@ -696,7 +729,8 @@ class binary_writer
|
|||
/*!
|
||||
@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)
|
||||
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(
|
||||
|
@ -707,7 +741,8 @@ class binary_writer
|
|||
/*!
|
||||
@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)
|
||||
void write_bson_boolean(const typename BasicJsonType::string_t& name,
|
||||
const bool value)
|
||||
{
|
||||
write_bson_entry_header(name, 0x08);
|
||||
oa->write_character(value ? static_cast<CharType>(0x01) : static_cast<CharType>(0x00));
|
||||
|
@ -716,7 +751,8 @@ class binary_writer
|
|||
/*!
|
||||
@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)
|
||||
void write_bson_double(const typename BasicJsonType::string_t& name,
|
||||
const double value)
|
||||
{
|
||||
write_bson_entry_header(name, 0x01);
|
||||
write_number<double, true>(value);
|
||||
|
@ -733,7 +769,8 @@ class binary_writer
|
|||
/*!
|
||||
@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)
|
||||
void write_bson_string(const typename BasicJsonType::string_t& name,
|
||||
const typename BasicJsonType::string_t& value)
|
||||
{
|
||||
write_bson_entry_header(name, 0x02);
|
||||
|
||||
|
@ -769,7 +806,8 @@ class binary_writer
|
|||
/*!
|
||||
@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)
|
||||
void write_bson_integer(const typename BasicJsonType::string_t& name,
|
||||
const std::int64_t value)
|
||||
{
|
||||
if ((std::numeric_limits<std::int32_t>::min)() <= value and value <= (std::numeric_limits<std::int32_t>::max)())
|
||||
{
|
||||
|
@ -783,7 +821,6 @@ class binary_writer
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
@return The size of the BSON-encoded unsigned integer in @a j
|
||||
*/
|
||||
|
@ -802,7 +839,8 @@ class binary_writer
|
|||
/*!
|
||||
@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)
|
||||
void write_bson_unsigned(const typename BasicJsonType::string_t& name,
|
||||
const std::uint64_t value)
|
||||
{
|
||||
if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
|
||||
{
|
||||
|
@ -824,13 +862,13 @@ class binary_writer
|
|||
/*!
|
||||
@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)
|
||||
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
|
||||
*/
|
||||
|
@ -849,10 +887,11 @@ class binary_writer
|
|||
/*!
|
||||
@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)
|
||||
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));
|
||||
write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_array_size(value)));
|
||||
|
||||
for (const auto& el : value)
|
||||
{
|
||||
|
@ -862,75 +901,95 @@ class binary_writer
|
|||
oa->write_character(static_cast<CharType>(0x00));
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
@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)
|
||||
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())
|
||||
{
|
||||
case value_t::discarded:
|
||||
return 0ul;
|
||||
|
||||
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;
|
||||
|
||||
// LCOV_EXCL_START
|
||||
default:
|
||||
assert(false);
|
||||
return 0ul;
|
||||
// LCOV_EXCL_STOP
|
||||
case value_t::discarded:
|
||||
return 0ul;
|
||||
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;
|
||||
// LCOV_EXCL_STOP
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
@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
|
||||
@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)
|
||||
void write_bson_element(const typename BasicJsonType::string_t& name,
|
||||
const BasicJsonType& j)
|
||||
{
|
||||
switch (j.type())
|
||||
{
|
||||
case value_t::discarded:
|
||||
return;
|
||||
|
||||
case value_t::object:
|
||||
return write_bson_object_entry(name, *j.m_value.object);
|
||||
|
||||
case value_t::array:
|
||||
return write_bson_array(name, *j.m_value.array);
|
||||
|
||||
case value_t::boolean:
|
||||
return write_bson_boolean(name, j.m_value.boolean);
|
||||
|
||||
case value_t::number_float:
|
||||
return write_bson_double(name, j.m_value.number_float);
|
||||
|
||||
case value_t::number_integer:
|
||||
return write_bson_integer(name, j.m_value.number_integer);
|
||||
|
||||
case value_t::number_unsigned:
|
||||
return write_bson_unsigned(name, j.m_value.number_unsigned);
|
||||
|
||||
case value_t::string:
|
||||
return write_bson_string(name, *j.m_value.string);
|
||||
|
||||
case value_t::null:
|
||||
return write_bson_null(name);
|
||||
|
||||
// LCOV_EXCL_START
|
||||
default:
|
||||
assert(false);
|
||||
return;
|
||||
// LCOV_EXCL_STOP
|
||||
case value_t::discarded:
|
||||
return;
|
||||
case value_t::object:
|
||||
return write_bson_object_entry(name, *j.m_value.object);
|
||||
case value_t::array:
|
||||
return write_bson_array(name, *j.m_value.array);
|
||||
case value_t::boolean:
|
||||
return write_bson_boolean(name, j.m_value.boolean);
|
||||
case value_t::number_float:
|
||||
return write_bson_double(name, j.m_value.number_float);
|
||||
case value_t::number_integer:
|
||||
return write_bson_integer(name, j.m_value.number_integer);
|
||||
case value_t::number_unsigned:
|
||||
return write_bson_unsigned(name, j.m_value.number_unsigned);
|
||||
case value_t::string:
|
||||
return write_bson_string(name, *j.m_value.string);
|
||||
case value_t::null:
|
||||
return write_bson_null(name);
|
||||
// LCOV_EXCL_STOP
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -958,7 +1017,7 @@ class binary_writer
|
|||
*/
|
||||
void write_bson_object(const typename BasicJsonType::object_t& value)
|
||||
{
|
||||
write_number<std::int32_t, true>(calc_bson_object_size(value));
|
||||
write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_object_size(value)));
|
||||
|
||||
for (const auto& el : value)
|
||||
{
|
||||
|
@ -968,64 +1027,38 @@ class binary_writer
|
|||
oa->write_character(static_cast<CharType>(0x00));
|
||||
}
|
||||
|
||||
/*!
|
||||
@param[in] j JSON value to serialize
|
||||
@pre j.type() == value_t::object
|
||||
*/
|
||||
void write_bson(const BasicJsonType& j)
|
||||
//////////
|
||||
// CBOR //
|
||||
//////////
|
||||
|
||||
static constexpr CharType get_cbor_float_prefix(float /*unused*/)
|
||||
{
|
||||
switch (j.type())
|
||||
{
|
||||
case value_t::object:
|
||||
{
|
||||
write_bson_object(*j.m_value.object);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::discarded:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name())));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return static_cast<CharType>(0xFA); // Single-Precision Float
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
/*
|
||||
@brief write a number to output input
|
||||
|
||||
@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
|
||||
|
||||
@note This function needs to respect the system's endianess, because bytes
|
||||
in CBOR, MessagePack, and UBJSON are stored in network order (big
|
||||
endian) and therefore need reordering on little endian systems.
|
||||
*/
|
||||
template<typename NumberType, bool OutputIsLittleEndian = false>
|
||||
void write_number(const NumberType n)
|
||||
static constexpr CharType get_cbor_float_prefix(double /*unused*/)
|
||||
{
|
||||
// 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)
|
||||
if (is_little_endian and not OutputIsLittleEndian)
|
||||
{
|
||||
// reverse byte order prior to conversion if necessary
|
||||
std::reverse(vec.begin(), vec.end());
|
||||
}
|
||||
|
||||
oa->write_characters(vec.data(), sizeof(NumberType));
|
||||
return static_cast<CharType>(0xFB); // Double-Precision Float
|
||||
}
|
||||
|
||||
/////////////
|
||||
// MsgPack //
|
||||
/////////////
|
||||
|
||||
static constexpr CharType get_msgpack_float_prefix(float /*unused*/)
|
||||
{
|
||||
return static_cast<CharType>(0xCA); // float 32
|
||||
}
|
||||
|
||||
static constexpr CharType get_msgpack_float_prefix(double /*unused*/)
|
||||
{
|
||||
return static_cast<CharType>(0xCB); // float 64
|
||||
}
|
||||
|
||||
////////////
|
||||
// UBJSON //
|
||||
////////////
|
||||
|
||||
// UBJSON: write number (floating point)
|
||||
template<typename NumberType, typename std::enable_if<
|
||||
std::is_floating_point<NumberType>::value, int>::type = 0>
|
||||
|
@ -1226,26 +1259,6 @@ class binary_writer
|
|||
}
|
||||
}
|
||||
|
||||
static constexpr CharType get_cbor_float_prefix(float /*unused*/)
|
||||
{
|
||||
return static_cast<CharType>(0xFA); // Single-Precision Float
|
||||
}
|
||||
|
||||
static constexpr CharType get_cbor_float_prefix(double /*unused*/)
|
||||
{
|
||||
return static_cast<CharType>(0xFB); // Double-Precision Float
|
||||
}
|
||||
|
||||
static constexpr CharType get_msgpack_float_prefix(float /*unused*/)
|
||||
{
|
||||
return static_cast<CharType>(0xCA); // float 32
|
||||
}
|
||||
|
||||
static constexpr CharType get_msgpack_float_prefix(double /*unused*/)
|
||||
{
|
||||
return static_cast<CharType>(0xCB); // float 64
|
||||
}
|
||||
|
||||
static constexpr CharType get_ubjson_float_prefix(float /*unused*/)
|
||||
{
|
||||
return 'd'; // float 32
|
||||
|
@ -1256,6 +1269,39 @@ class binary_writer
|
|||
return 'D'; // float 64
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
// Utility functions //
|
||||
///////////////////////
|
||||
|
||||
/*
|
||||
@brief write a number to output input
|
||||
|
||||
@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
|
||||
|
||||
@note This function needs to respect the system's endianess, because bytes
|
||||
in CBOR, MessagePack, and UBJSON are stored in network order (big
|
||||
endian) and therefore need reordering on little endian systems.
|
||||
*/
|
||||
template<typename NumberType, bool OutputIsLittleEndian = false>
|
||||
void write_number(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)
|
||||
if (is_little_endian and not OutputIsLittleEndian)
|
||||
{
|
||||
// reverse byte order prior to conversion if necessary
|
||||
std::reverse(vec.begin(), vec.end());
|
||||
}
|
||||
|
||||
oa->write_characters(vec.data(), sizeof(NumberType));
|
||||
}
|
||||
|
||||
private:
|
||||
/// whether we can assume little endianess
|
||||
const bool is_little_endian = binary_reader<BasicJsonType>::little_endianess();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue