1
0
mirror of https://github.com/nlohmann/json synced 2024-11-23 04:20:06 +00:00
nlohmannjson/include/nlohmann/detail/output/binary_writer.hpp

1260 lines
45 KiB
C++

#pragma once
#include <algorithm> // reverse
#include <array> // array
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
#include <cstring> // memcpy
#include <limits> // numeric_limits
#include <nlohmann/detail/input/binary_reader.hpp>
#include <nlohmann/detail/output/output_adapters.hpp>
namespace nlohmann
{
namespace detail
{
///////////////////
// binary writer //
///////////////////
/*!
@brief serialization to CBOR and MessagePack values
*/
template<typename BasicJsonType, typename CharType>
class binary_writer
{
public:
/*!
@brief create a binary writer
@param[in] adapter output adapter to write to
*/
explicit binary_writer(output_adapter_t<CharType> adapter) : oa(adapter)
{
assert(oa);
}
/*!
@brief[in] j JSON value to serialize
*/
void write_cbor(const BasicJsonType& j)
{
switch (j.type())
{
case value_t::null:
{
oa->write_character(static_cast<CharType>(0xF6));
break;
}
case value_t::boolean:
{
oa->write_character(j.m_value.boolean
? static_cast<CharType>(0xF5)
: static_cast<CharType>(0xF4));
break;
}
case value_t::number_integer:
{
if (j.m_value.number_integer >= 0)
{
// CBOR does not differentiate between positive signed
// integers and unsigned integers. Therefore, we used the
// code from the value_t::number_unsigned case here.
if (j.m_value.number_integer <= 0x17)
{
write_number(static_cast<uint8_t>(j.m_value.number_integer));
}
else if (j.m_value.number_integer <= (std::numeric_limits<uint8_t>::max)())
{
oa->write_character(static_cast<CharType>(0x18));
write_number(static_cast<uint8_t>(j.m_value.number_integer));
}
else if (j.m_value.number_integer <= (std::numeric_limits<uint16_t>::max)())
{
oa->write_character(static_cast<CharType>(0x19));
write_number(static_cast<uint16_t>(j.m_value.number_integer));
}
else if (j.m_value.number_integer <= (std::numeric_limits<uint32_t>::max)())
{
oa->write_character(static_cast<CharType>(0x1A));
write_number(static_cast<uint32_t>(j.m_value.number_integer));
}
else
{
oa->write_character(static_cast<CharType>(0x1B));
write_number(static_cast<uint64_t>(j.m_value.number_integer));
}
}
else
{
// The conversions below encode the sign in the first
// byte, and the value is converted to a positive number.
const auto positive_number = -1 - j.m_value.number_integer;
if (j.m_value.number_integer >= -24)
{
write_number(static_cast<uint8_t>(0x20 + positive_number));
}
else if (positive_number <= (std::numeric_limits<uint8_t>::max)())
{
oa->write_character(static_cast<CharType>(0x38));
write_number(static_cast<uint8_t>(positive_number));
}
else if (positive_number <= (std::numeric_limits<uint16_t>::max)())
{
oa->write_character(static_cast<CharType>(0x39));
write_number(static_cast<uint16_t>(positive_number));
}
else if (positive_number <= (std::numeric_limits<uint32_t>::max)())
{
oa->write_character(static_cast<CharType>(0x3A));
write_number(static_cast<uint32_t>(positive_number));
}
else
{
oa->write_character(static_cast<CharType>(0x3B));
write_number(static_cast<uint64_t>(positive_number));
}
}
break;
}
case value_t::number_unsigned:
{
if (j.m_value.number_unsigned <= 0x17)
{
write_number(static_cast<uint8_t>(j.m_value.number_unsigned));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
{
oa->write_character(static_cast<CharType>(0x18));
write_number(static_cast<uint8_t>(j.m_value.number_unsigned));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
{
oa->write_character(static_cast<CharType>(0x19));
write_number(static_cast<uint16_t>(j.m_value.number_unsigned));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
{
oa->write_character(static_cast<CharType>(0x1A));
write_number(static_cast<uint32_t>(j.m_value.number_unsigned));
}
else
{
oa->write_character(static_cast<CharType>(0x1B));
write_number(static_cast<uint64_t>(j.m_value.number_unsigned));
}
break;
}
case value_t::number_float:
{
oa->write_character(get_cbor_float_prefix(j.m_value.number_float));
write_number(j.m_value.number_float);
break;
}
case value_t::string:
{
// step 1: write control byte and the string length
const auto N = j.m_value.string->size();
if (N <= 0x17)
{
write_number(static_cast<uint8_t>(0x60 + N));
}
else if (N <= (std::numeric_limits<uint8_t>::max)())
{
oa->write_character(static_cast<CharType>(0x78));
write_number(static_cast<uint8_t>(N));
}
else if (N <= (std::numeric_limits<uint16_t>::max)())
{
oa->write_character(static_cast<CharType>(0x79));
write_number(static_cast<uint16_t>(N));
}
else if (N <= (std::numeric_limits<uint32_t>::max)())
{
oa->write_character(static_cast<CharType>(0x7A));
write_number(static_cast<uint32_t>(N));
}
// LCOV_EXCL_START
else if (N <= (std::numeric_limits<uint64_t>::max)())
{
oa->write_character(static_cast<CharType>(0x7B));
write_number(static_cast<uint64_t>(N));
}
// LCOV_EXCL_STOP
// step 2: write the string
oa->write_characters(
reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
j.m_value.string->size());
break;
}
case value_t::array:
{
// step 1: write control byte and the array size
const auto N = j.m_value.array->size();
if (N <= 0x17)
{
write_number(static_cast<uint8_t>(0x80 + N));
}
else if (N <= (std::numeric_limits<uint8_t>::max)())
{
oa->write_character(static_cast<CharType>(0x98));
write_number(static_cast<uint8_t>(N));
}
else if (N <= (std::numeric_limits<uint16_t>::max)())
{
oa->write_character(static_cast<CharType>(0x99));
write_number(static_cast<uint16_t>(N));
}
else if (N <= (std::numeric_limits<uint32_t>::max)())
{
oa->write_character(static_cast<CharType>(0x9A));
write_number(static_cast<uint32_t>(N));
}
// LCOV_EXCL_START
else if (N <= (std::numeric_limits<uint64_t>::max)())
{
oa->write_character(static_cast<CharType>(0x9B));
write_number(static_cast<uint64_t>(N));
}
// LCOV_EXCL_STOP
// step 2: write each element
for (const auto& el : *j.m_value.array)
{
write_cbor(el);
}
break;
}
case value_t::object:
{
// step 1: write control byte and the object size
const auto N = j.m_value.object->size();
if (N <= 0x17)
{
write_number(static_cast<uint8_t>(0xA0 + N));
}
else if (N <= (std::numeric_limits<uint8_t>::max)())
{
oa->write_character(static_cast<CharType>(0xB8));
write_number(static_cast<uint8_t>(N));
}
else if (N <= (std::numeric_limits<uint16_t>::max)())
{
oa->write_character(static_cast<CharType>(0xB9));
write_number(static_cast<uint16_t>(N));
}
else if (N <= (std::numeric_limits<uint32_t>::max)())
{
oa->write_character(static_cast<CharType>(0xBA));
write_number(static_cast<uint32_t>(N));
}
// LCOV_EXCL_START
else if (N <= (std::numeric_limits<uint64_t>::max)())
{
oa->write_character(static_cast<CharType>(0xBB));
write_number(static_cast<uint64_t>(N));
}
// LCOV_EXCL_STOP
// step 2: write each element
for (const auto& el : *j.m_value.object)
{
write_cbor(el.first);
write_cbor(el.second);
}
break;
}
default:
break;
}
}
/*!
@brief[in] j JSON value to serialize
*/
void write_msgpack(const BasicJsonType& j)
{
switch (j.type())
{
case value_t::null: // nil
{
oa->write_character(static_cast<CharType>(0xC0));
break;
}
case value_t::boolean: // true and false
{
oa->write_character(j.m_value.boolean
? static_cast<CharType>(0xC3)
: static_cast<CharType>(0xC2));
break;
}
case value_t::number_integer:
{
if (j.m_value.number_integer >= 0)
{
// MessagePack does not differentiate between positive
// signed integers and unsigned integers. Therefore, we used
// the code from the value_t::number_unsigned case here.
if (j.m_value.number_unsigned < 128)
{
// positive fixnum
write_number(static_cast<uint8_t>(j.m_value.number_integer));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
{
// uint 8
oa->write_character(static_cast<CharType>(0xCC));
write_number(static_cast<uint8_t>(j.m_value.number_integer));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
{
// uint 16
oa->write_character(static_cast<CharType>(0xCD));
write_number(static_cast<uint16_t>(j.m_value.number_integer));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
{
// uint 32
oa->write_character(static_cast<CharType>(0xCE));
write_number(static_cast<uint32_t>(j.m_value.number_integer));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)())
{
// uint 64
oa->write_character(static_cast<CharType>(0xCF));
write_number(static_cast<uint64_t>(j.m_value.number_integer));
}
}
else
{
if (j.m_value.number_integer >= -32)
{
// negative fixnum
write_number(static_cast<int8_t>(j.m_value.number_integer));
}
else if (j.m_value.number_integer >= (std::numeric_limits<int8_t>::min)() and
j.m_value.number_integer <= (std::numeric_limits<int8_t>::max)())
{
// int 8
oa->write_character(static_cast<CharType>(0xD0));
write_number(static_cast<int8_t>(j.m_value.number_integer));
}
else if (j.m_value.number_integer >= (std::numeric_limits<int16_t>::min)() and
j.m_value.number_integer <= (std::numeric_limits<int16_t>::max)())
{
// int 16
oa->write_character(static_cast<CharType>(0xD1));
write_number(static_cast<int16_t>(j.m_value.number_integer));
}
else if (j.m_value.number_integer >= (std::numeric_limits<int32_t>::min)() and
j.m_value.number_integer <= (std::numeric_limits<int32_t>::max)())
{
// int 32
oa->write_character(static_cast<CharType>(0xD2));
write_number(static_cast<int32_t>(j.m_value.number_integer));
}
else if (j.m_value.number_integer >= (std::numeric_limits<int64_t>::min)() and
j.m_value.number_integer <= (std::numeric_limits<int64_t>::max)())
{
// int 64
oa->write_character(static_cast<CharType>(0xD3));
write_number(static_cast<int64_t>(j.m_value.number_integer));
}
}
break;
}
case value_t::number_unsigned:
{
if (j.m_value.number_unsigned < 128)
{
// positive fixnum
write_number(static_cast<uint8_t>(j.m_value.number_integer));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
{
// uint 8
oa->write_character(static_cast<CharType>(0xCC));
write_number(static_cast<uint8_t>(j.m_value.number_integer));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
{
// uint 16
oa->write_character(static_cast<CharType>(0xCD));
write_number(static_cast<uint16_t>(j.m_value.number_integer));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
{
// uint 32
oa->write_character(static_cast<CharType>(0xCE));
write_number(static_cast<uint32_t>(j.m_value.number_integer));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)())
{
// uint 64
oa->write_character(static_cast<CharType>(0xCF));
write_number(static_cast<uint64_t>(j.m_value.number_integer));
}
break;
}
case value_t::number_float:
{
oa->write_character(get_msgpack_float_prefix(j.m_value.number_float));
write_number(j.m_value.number_float);
break;
}
case value_t::string:
{
// step 1: write control byte and the string length
const auto N = j.m_value.string->size();
if (N <= 31)
{
// fixstr
write_number(static_cast<uint8_t>(0xA0 | N));
}
else if (N <= (std::numeric_limits<uint8_t>::max)())
{
// str 8
oa->write_character(static_cast<CharType>(0xD9));
write_number(static_cast<uint8_t>(N));
}
else if (N <= (std::numeric_limits<uint16_t>::max)())
{
// str 16
oa->write_character(static_cast<CharType>(0xDA));
write_number(static_cast<uint16_t>(N));
}
else if (N <= (std::numeric_limits<uint32_t>::max)())
{
// str 32
oa->write_character(static_cast<CharType>(0xDB));
write_number(static_cast<uint32_t>(N));
}
// step 2: write the string
oa->write_characters(
reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
j.m_value.string->size());
break;
}
case value_t::array:
{
// step 1: write control byte and the array size
const auto N = j.m_value.array->size();
if (N <= 15)
{
// fixarray
write_number(static_cast<uint8_t>(0x90 | N));
}
else if (N <= (std::numeric_limits<uint16_t>::max)())
{
// array 16
oa->write_character(static_cast<CharType>(0xDC));
write_number(static_cast<uint16_t>(N));
}
else if (N <= (std::numeric_limits<uint32_t>::max)())
{
// array 32
oa->write_character(static_cast<CharType>(0xDD));
write_number(static_cast<uint32_t>(N));
}
// step 2: write each element
for (const auto& el : *j.m_value.array)
{
write_msgpack(el);
}
break;
}
case value_t::object:
{
// step 1: write control byte and the object size
const auto N = j.m_value.object->size();
if (N <= 15)
{
// fixmap
write_number(static_cast<uint8_t>(0x80 | (N & 0xF)));
}
else if (N <= (std::numeric_limits<uint16_t>::max)())
{
// map 16
oa->write_character(static_cast<CharType>(0xDE));
write_number(static_cast<uint16_t>(N));
}
else if (N <= (std::numeric_limits<uint32_t>::max)())
{
// map 32
oa->write_character(static_cast<CharType>(0xDF));
write_number(static_cast<uint32_t>(N));
}
// step 2: write each element
for (const auto& el : *j.m_value.object)
{
write_msgpack(el.first);
write_msgpack(el.second);
}
break;
}
default:
break;
}
}
/*!
@param[in] j JSON value to serialize
@param[in] use_count whether to use '#' prefixes (optimized format)
@param[in] use_type whether to use '$' prefixes (optimized format)
@param[in] add_prefix whether prefixes need to be used for this value
*/
void write_ubjson(const BasicJsonType& j, const bool use_count,
const bool use_type, const bool add_prefix = true)
{
switch (j.type())
{
case value_t::null:
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('Z'));
}
break;
}
case value_t::boolean:
{
if (add_prefix)
oa->write_character(j.m_value.boolean
? static_cast<CharType>('T')
: static_cast<CharType>('F'));
break;
}
case value_t::number_integer:
{
write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix);
break;
}
case value_t::number_unsigned:
{
write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix);
break;
}
case value_t::number_float:
{
write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix);
break;
}
case value_t::string:
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('S'));
}
write_number_with_ubjson_prefix(j.m_value.string->size(), true);
oa->write_characters(
reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
j.m_value.string->size());
break;
}
case value_t::array:
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('['));
}
bool prefix_required = true;
if (use_type and not j.m_value.array->empty())
{
assert(use_count);
const CharType first_prefix = ubjson_prefix(j.front());
const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
[this, first_prefix](const BasicJsonType & v)
{
return ubjson_prefix(v) == first_prefix;
});
if (same_prefix)
{
prefix_required = false;
oa->write_character(static_cast<CharType>('$'));
oa->write_character(first_prefix);
}
}
if (use_count)
{
oa->write_character(static_cast<CharType>('#'));
write_number_with_ubjson_prefix(j.m_value.array->size(), true);
}
for (const auto& el : *j.m_value.array)
{
write_ubjson(el, use_count, use_type, prefix_required);
}
if (not use_count)
{
oa->write_character(static_cast<CharType>(']'));
}
break;
}
case value_t::object:
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('{'));
}
bool prefix_required = true;
if (use_type and not j.m_value.object->empty())
{
assert(use_count);
const CharType first_prefix = ubjson_prefix(j.front());
const bool same_prefix = std::all_of(j.begin(), j.end(),
[this, first_prefix](const BasicJsonType & v)
{
return ubjson_prefix(v) == first_prefix;
});
if (same_prefix)
{
prefix_required = false;
oa->write_character(static_cast<CharType>('$'));
oa->write_character(first_prefix);
}
}
if (use_count)
{
oa->write_character(static_cast<CharType>('#'));
write_number_with_ubjson_prefix(j.m_value.object->size(), true);
}
for (const auto& el : *j.m_value.object)
{
write_number_with_ubjson_prefix(el.first.size(), true);
oa->write_characters(
reinterpret_cast<const CharType*>(el.first.c_str()),
el.first.size());
write_ubjson(el.second, use_count, use_type, prefix_required);
}
if (not use_count)
{
oa->write_character(static_cast<CharType>('}'));
}
break;
}
default:
break;
}
}
/*!
@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)
{
if (name.find(static_cast<CharType>(0)) != BasicJsonType::string_t::npos)
{
JSON_THROW(out_of_range::create(409, "BSON key cannot contain code point U+0000"));
}
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(
reinterpret_cast<const CharType*>(name.c_str()),
name.size() + 1u);
}
/*!
@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)
{
write_bson_entry_header(name, 0x08);
oa->write_character(value ? static_cast<CharType>(0x01) : static_cast<CharType>(0x00));
}
/*!
@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)
{
write_bson_entry_header(name, 0x01);
write_number<double, true>(value);
}
/*!
@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)
{
return sizeof(std::int32_t) + value.size() + 1ul;
}
/*!
@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)
{
write_bson_entry_header(name, 0x02);
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)())
{
return sizeof(std::int32_t);
}
else
{
return sizeof(std::int64_t);
}
}
/*!
@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)
{
if ((std::numeric_limits<std::int32_t>::min)() <= value and value <= (std::numeric_limits<std::int32_t>::max)())
{
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));
}
}
/*!
@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)
{
if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
{
return sizeof(std::int32_t);
}
else
{
return sizeof(std::int64_t);
}
}
/*!
@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)
{
if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
{
write_bson_entry_header(name, 0x10); // int32
write_number<std::int32_t, true>(static_cast<std::int32_t>(value));
}
else if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
{
write_bson_entry_header(name, 0x12); // int64
write_number<std::int64_t, true>(static_cast<std::int64_t>(value));
}
else
{
JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(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));
}
/*!
@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::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;
};
}
/*!
@brief Serializes the JSON value @a j to BSON and associates it with the key @a name.
@param name The name to associate with the JSON entity @a j within the current BSON document
@return The size of the bson entry
*/
void write_bson_element(const typename BasicJsonType::string_t& name, const BasicJsonType& j)
{
switch (j.type())
{
// LCOV_EXCL_START
default:
assert(false);
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);
};
}
/*!
@brief Calculates the size of the BSON serialization of the given
JSON-object @a j.
@param[in] j JSON value to serialize
@pre j.type() == value_t::object
*/
static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value)
{
std::size_t document_size = 0;
for (const auto& el : value)
{
document_size += calc_bson_element_size(el.first, el.second);
}
return sizeof(std::int32_t) + document_size + 1ul;
}
/*!
@param[in] j JSON value to serialize
@pre j.type() == value_t::object
*/
void write_bson_object(const typename BasicJsonType::object_t& value)
{
write_number<std::int32_t, true>(calc_bson_object_size(value));
for (const auto& el : value)
{
write_bson_element(el.first, el.second);
}
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)
{
switch (j.type())
{
default:
JSON_THROW(type_error::create(317, "JSON value of type " + std::to_string(static_cast<std::uint8_t>(j.type())) + " cannot be serialized to requested format"));
break;
case value_t::discarded:
break;
case value_t::object:
write_bson_object(*j.m_value.object);
break;
}
}
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)
{
// 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 && !OutputIsLittleEndian)
{
// reverse byte order prior to conversion if necessary
std::reverse(vec.begin(), vec.end());
}
oa->write_characters(vec.data(), sizeof(NumberType));
}
// UBJSON: write number (floating point)
template<typename NumberType, typename std::enable_if<
std::is_floating_point<NumberType>::value, int>::type = 0>
void write_number_with_ubjson_prefix(const NumberType n,
const bool add_prefix)
{
if (add_prefix)
{
oa->write_character(get_ubjson_float_prefix(n));
}
write_number(n);
}
// UBJSON: write number (unsigned integer)
template<typename NumberType, typename std::enable_if<
std::is_unsigned<NumberType>::value, int>::type = 0>
void write_number_with_ubjson_prefix(const NumberType n,
const bool add_prefix)
{
if (n <= static_cast<uint64_t>((std::numeric_limits<int8_t>::max)()))
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('i')); // int8
}
write_number(static_cast<uint8_t>(n));
}
else if (n <= (std::numeric_limits<uint8_t>::max)())
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('U')); // uint8
}
write_number(static_cast<uint8_t>(n));
}
else if (n <= static_cast<uint64_t>((std::numeric_limits<int16_t>::max)()))
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('I')); // int16
}
write_number(static_cast<int16_t>(n));
}
else if (n <= static_cast<uint64_t>((std::numeric_limits<int32_t>::max)()))
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('l')); // int32
}
write_number(static_cast<int32_t>(n));
}
else if (n <= static_cast<uint64_t>((std::numeric_limits<int64_t>::max)()))
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('L')); // int64
}
write_number(static_cast<int64_t>(n));
}
else
{
JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n)));
}
}
// UBJSON: write number (signed integer)
template<typename NumberType, typename std::enable_if<
std::is_signed<NumberType>::value and
not std::is_floating_point<NumberType>::value, int>::type = 0>
void write_number_with_ubjson_prefix(const NumberType n,
const bool add_prefix)
{
if ((std::numeric_limits<int8_t>::min)() <= n and n <= (std::numeric_limits<int8_t>::max)())
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('i')); // int8
}
write_number(static_cast<int8_t>(n));
}
else if (static_cast<int64_t>((std::numeric_limits<uint8_t>::min)()) <= n and n <= static_cast<int64_t>((std::numeric_limits<uint8_t>::max)()))
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('U')); // uint8
}
write_number(static_cast<uint8_t>(n));
}
else if ((std::numeric_limits<int16_t>::min)() <= n and n <= (std::numeric_limits<int16_t>::max)())
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('I')); // int16
}
write_number(static_cast<int16_t>(n));
}
else if ((std::numeric_limits<int32_t>::min)() <= n and n <= (std::numeric_limits<int32_t>::max)())
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('l')); // int32
}
write_number(static_cast<int32_t>(n));
}
else if ((std::numeric_limits<int64_t>::min)() <= n and n <= (std::numeric_limits<int64_t>::max)())
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('L')); // int64
}
write_number(static_cast<int64_t>(n));
}
// LCOV_EXCL_START
else
{
JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n)));
}
// LCOV_EXCL_STOP
}
/*!
@brief determine the type prefix of container values
@note This function does not need to be 100% accurate when it comes to
integer limits. In case a number exceeds the limits of int64_t,
this will be detected by a later call to function
write_number_with_ubjson_prefix. Therefore, we return 'L' for any
value that does not fit the previous limits.
*/
CharType ubjson_prefix(const BasicJsonType& j) const noexcept
{
switch (j.type())
{
case value_t::null:
return 'Z';
case value_t::boolean:
return j.m_value.boolean ? 'T' : 'F';
case value_t::number_integer:
{
if ((std::numeric_limits<int8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int8_t>::max)())
{
return 'i';
}
else if ((std::numeric_limits<uint8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<uint8_t>::max)())
{
return 'U';
}
else if ((std::numeric_limits<int16_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int16_t>::max)())
{
return 'I';
}
else if ((std::numeric_limits<int32_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int32_t>::max)())
{
return 'l';
}
else // no check and assume int64_t (see note above)
{
return 'L';
}
}
case value_t::number_unsigned:
{
if (j.m_value.number_unsigned <= (std::numeric_limits<int8_t>::max)())
{
return 'i';
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
{
return 'U';
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<int16_t>::max)())
{
return 'I';
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<int32_t>::max)())
{
return 'l';
}
else // no check and assume int64_t (see note above)
{
return 'L';
}
}
case value_t::number_float:
return get_ubjson_float_prefix(j.m_value.number_float);
case value_t::string:
return 'S';
case value_t::array:
return '[';
case value_t::object:
return '{';
default: // discarded values
return 'N';
}
}
static constexpr CharType get_cbor_float_prefix(float)
{
return static_cast<CharType>(0xFA); // Single-Precision Float
}
static constexpr CharType get_cbor_float_prefix(double)
{
return static_cast<CharType>(0xFB); // Double-Precision Float
}
static constexpr CharType get_msgpack_float_prefix(float)
{
return static_cast<CharType>(0xCA); // float 32
}
static constexpr CharType get_msgpack_float_prefix(double)
{
return static_cast<CharType>(0xCB); // float 64
}
static constexpr CharType get_ubjson_float_prefix(float)
{
return 'd'; // float 32
}
static constexpr CharType get_ubjson_float_prefix(double)
{
return 'D'; // float 64
}
private:
/// whether we can assume little endianess
const bool is_little_endian = binary_reader<BasicJsonType>::little_endianess();
/// the output
output_adapter_t<CharType> oa = nullptr;
};
}
}