1
0
mirror of https://github.com/nlohmann/json.git synced 2026-07-21 19:23:03 +04:00

Devirtualize binary_writer via a value-type output sink

to_cbor/to_msgpack/to_ubjson/to_bjdata/to_bson wrote every byte through
output_adapter_t, a shared_ptr<output_adapter_protocol> whose
write_character/write_characters are virtual. Unlike the lexer (templated
on a concrete InputAdapterType), the binary writer never got that
treatment, so binary output paid a vtable lookup per byte and a
make_shared per call.

Template binary_writer on an OutputSinkType and give it two concrete,
non-virtual sinks:

- output_vector_sink: appends straight into a std::vector (push_back /
  insert), used by the vector-returning to_* convenience functions. No
  vtable, no shared_ptr; the writes inline.
- output_adapter_sink: forwards to a type-erased output_adapter_t, so the
  existing to_*(j, output_adapter) overloads (streams, strings, custom
  adapters) keep working exactly as before -- one virtual call each,
  unchanged.

binary_writer keeps a convenience constructor taking output_adapter_t
(building the default output_adapter_sink), so the adapter overloads are
untouched; only the convenience functions switch to the vector sink. The
friend declaration and the basic_json binary_writer alias gain the new
(defaulted) template parameter.

Output is byte-for-byte identical: verified across ~3000 randomized
values plus curated edge cases (all scalar widths, strings with invalid
UTF-8, binary, nested arrays/objects) for CBOR, MessagePack, UBJSON (both
size/type settings), BJData, and BSON, plus the output_adapter path, in
C++11/17/20. Warning-clean under clang -Weverything and the gcc pedantic
set; clang-tidy clean on the changed headers; make check-amalgamation
clean.

Throughput (g++ -O3, vs develop): scalar-dense binary output such as
integer arrays ~1.4x; many small to_cbor calls ~1.04x (DOM traversal
bound); string/blob-heavy output unchanged (already bulk-bound). No
workload regressed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAYM1qhSA2FDaDcGfPW3fG
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann
2026-07-20 23:27:51 +00:00
parent 3565f40229
commit ebb3abba41
4 changed files with 486 additions and 324 deletions
+168 -155
View File
@@ -42,7 +42,7 @@ enum class bjdata_version_t
/*!
@brief serialization to CBOR and MessagePack values
*/
template<typename BasicJsonType, typename CharType>
template<typename BasicJsonType, typename CharType, typename OutputSinkType = output_adapter_sink<CharType>>
class binary_writer
{
using string_t = typename BasicJsonType::string_t;
@@ -53,12 +53,25 @@ class binary_writer
/*!
@brief create a binary writer
@param[in] sink output sink to write to (a value-type sink such as
output_vector_sink, or output_adapter_sink wrapping a
type-erased output adapter)
*/
explicit binary_writer(OutputSinkType sink) : oa(std::move(sink))
{}
/*!
@brief create a binary writer from a type-erased output adapter
Convenience constructor for the default (output_adapter_sink) sink so the
`output_adapter`-based overloads keep constructing the writer directly from
an adapter. Only participates in overload resolution when the sink can be
built from an adapter.
@param[in] adapter output adapter to write to
*/
explicit binary_writer(output_adapter_t<CharType> adapter) : oa(std::move(adapter))
{
JSON_ASSERT(oa);
}
explicit binary_writer(output_adapter_t<CharType> adapter) : oa(OutputSinkType(std::move(adapter)))
{}
/*!
@param[in] j JSON value to serialize
@@ -99,15 +112,15 @@ class binary_writer
{
case value_t::null:
{
oa->write_character(to_char_type(0xF6));
oa.write_character(to_char_type(0xF6));
break;
}
case value_t::boolean:
{
oa->write_character(j.m_data.m_value.boolean
? to_char_type(0xF5)
: to_char_type(0xF4));
oa.write_character(j.m_data.m_value.boolean
? to_char_type(0xF5)
: to_char_type(0xF4));
break;
}
@@ -124,22 +137,22 @@ class binary_writer
}
else if (j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())
{
oa->write_character(to_char_type(0x18));
oa.write_character(to_char_type(0x18));
write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_integer));
}
else if (j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)())
{
oa->write_character(to_char_type(0x19));
oa.write_character(to_char_type(0x19));
write_number(static_cast<std::uint16_t>(j.m_data.m_value.number_integer));
}
else if (j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)())
{
oa->write_character(to_char_type(0x1A));
oa.write_character(to_char_type(0x1A));
write_number(static_cast<std::uint32_t>(j.m_data.m_value.number_integer));
}
else
{
oa->write_character(to_char_type(0x1B));
oa.write_character(to_char_type(0x1B));
write_number(static_cast<std::uint64_t>(j.m_data.m_value.number_integer));
}
}
@@ -154,22 +167,22 @@ class binary_writer
}
else if (positive_number <= (std::numeric_limits<std::uint8_t>::max)())
{
oa->write_character(to_char_type(0x38));
oa.write_character(to_char_type(0x38));
write_number(static_cast<std::uint8_t>(positive_number));
}
else if (positive_number <= (std::numeric_limits<std::uint16_t>::max)())
{
oa->write_character(to_char_type(0x39));
oa.write_character(to_char_type(0x39));
write_number(static_cast<std::uint16_t>(positive_number));
}
else if (positive_number <= (std::numeric_limits<std::uint32_t>::max)())
{
oa->write_character(to_char_type(0x3A));
oa.write_character(to_char_type(0x3A));
write_number(static_cast<std::uint32_t>(positive_number));
}
else
{
oa->write_character(to_char_type(0x3B));
oa.write_character(to_char_type(0x3B));
write_number(static_cast<std::uint64_t>(positive_number));
}
}
@@ -184,22 +197,22 @@ class binary_writer
}
else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
{
oa->write_character(to_char_type(0x18));
oa.write_character(to_char_type(0x18));
write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_unsigned));
}
else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
{
oa->write_character(to_char_type(0x19));
oa.write_character(to_char_type(0x19));
write_number(static_cast<std::uint16_t>(j.m_data.m_value.number_unsigned));
}
else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
{
oa->write_character(to_char_type(0x1A));
oa.write_character(to_char_type(0x1A));
write_number(static_cast<std::uint32_t>(j.m_data.m_value.number_unsigned));
}
else
{
oa->write_character(to_char_type(0x1B));
oa.write_character(to_char_type(0x1B));
write_number(static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned));
}
break;
@@ -210,16 +223,16 @@ class binary_writer
if (std::isnan(j.m_data.m_value.number_float))
{
// NaN is 0xf97e00 in CBOR
oa->write_character(to_char_type(0xF9));
oa->write_character(to_char_type(0x7E));
oa->write_character(to_char_type(0x00));
oa.write_character(to_char_type(0xF9));
oa.write_character(to_char_type(0x7E));
oa.write_character(to_char_type(0x00));
}
else if (std::isinf(j.m_data.m_value.number_float))
{
// Infinity is 0xf97c00, -Infinity is 0xf9fc00
oa->write_character(to_char_type(0xf9));
oa->write_character(j.m_data.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC));
oa->write_character(to_char_type(0x00));
oa.write_character(to_char_type(0xf9));
oa.write_character(j.m_data.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC));
oa.write_character(to_char_type(0x00));
}
else
{
@@ -238,31 +251,31 @@ class binary_writer
}
else if (N <= (std::numeric_limits<std::uint8_t>::max)())
{
oa->write_character(to_char_type(0x78));
oa.write_character(to_char_type(0x78));
write_number(static_cast<std::uint8_t>(N));
}
else if (N <= (std::numeric_limits<std::uint16_t>::max)())
{
oa->write_character(to_char_type(0x79));
oa.write_character(to_char_type(0x79));
write_number(static_cast<std::uint16_t>(N));
}
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
{
oa->write_character(to_char_type(0x7A));
oa.write_character(to_char_type(0x7A));
write_number(static_cast<std::uint32_t>(N));
}
// LCOV_EXCL_START
else if (N <= (std::numeric_limits<std::uint64_t>::max)())
{
oa->write_character(to_char_type(0x7B));
oa.write_character(to_char_type(0x7B));
write_number(static_cast<std::uint64_t>(N));
}
// LCOV_EXCL_STOP
// step 2: write the string
oa->write_characters(
reinterpret_cast<const CharType*>(j.m_data.m_value.string->c_str()),
j.m_data.m_value.string->size());
oa.write_characters(
reinterpret_cast<const CharType*>(j.m_data.m_value.string->c_str()),
j.m_data.m_value.string->size());
break;
}
@@ -276,23 +289,23 @@ class binary_writer
}
else if (N <= (std::numeric_limits<std::uint8_t>::max)())
{
oa->write_character(to_char_type(0x98));
oa.write_character(to_char_type(0x98));
write_number(static_cast<std::uint8_t>(N));
}
else if (N <= (std::numeric_limits<std::uint16_t>::max)())
{
oa->write_character(to_char_type(0x99));
oa.write_character(to_char_type(0x99));
write_number(static_cast<std::uint16_t>(N));
}
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
{
oa->write_character(to_char_type(0x9A));
oa.write_character(to_char_type(0x9A));
write_number(static_cast<std::uint32_t>(N));
}
// LCOV_EXCL_START
else if (N <= (std::numeric_limits<std::uint64_t>::max)())
{
oa->write_character(to_char_type(0x9B));
oa.write_character(to_char_type(0x9B));
write_number(static_cast<std::uint64_t>(N));
}
// LCOV_EXCL_STOP
@@ -339,31 +352,31 @@ class binary_writer
}
else if (N <= (std::numeric_limits<std::uint8_t>::max)())
{
oa->write_character(to_char_type(0x58));
oa.write_character(to_char_type(0x58));
write_number(static_cast<std::uint8_t>(N));
}
else if (N <= (std::numeric_limits<std::uint16_t>::max)())
{
oa->write_character(to_char_type(0x59));
oa.write_character(to_char_type(0x59));
write_number(static_cast<std::uint16_t>(N));
}
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
{
oa->write_character(to_char_type(0x5A));
oa.write_character(to_char_type(0x5A));
write_number(static_cast<std::uint32_t>(N));
}
// LCOV_EXCL_START
else if (N <= (std::numeric_limits<std::uint64_t>::max)())
{
oa->write_character(to_char_type(0x5B));
oa.write_character(to_char_type(0x5B));
write_number(static_cast<std::uint64_t>(N));
}
// LCOV_EXCL_STOP
// step 2: write each element
oa->write_characters(
reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()),
N);
oa.write_characters(
reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()),
N);
break;
}
@@ -378,23 +391,23 @@ class binary_writer
}
else if (N <= (std::numeric_limits<std::uint8_t>::max)())
{
oa->write_character(to_char_type(0xB8));
oa.write_character(to_char_type(0xB8));
write_number(static_cast<std::uint8_t>(N));
}
else if (N <= (std::numeric_limits<std::uint16_t>::max)())
{
oa->write_character(to_char_type(0xB9));
oa.write_character(to_char_type(0xB9));
write_number(static_cast<std::uint16_t>(N));
}
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
{
oa->write_character(to_char_type(0xBA));
oa.write_character(to_char_type(0xBA));
write_number(static_cast<std::uint32_t>(N));
}
// LCOV_EXCL_START
else if (N <= (std::numeric_limits<std::uint64_t>::max)())
{
oa->write_character(to_char_type(0xBB));
oa.write_character(to_char_type(0xBB));
write_number(static_cast<std::uint64_t>(N));
}
// LCOV_EXCL_STOP
@@ -423,15 +436,15 @@ class binary_writer
{
case value_t::null: // nil
{
oa->write_character(to_char_type(0xC0));
oa.write_character(to_char_type(0xC0));
break;
}
case value_t::boolean: // true and false
{
oa->write_character(j.m_data.m_value.boolean
? to_char_type(0xC3)
: to_char_type(0xC2));
oa.write_character(j.m_data.m_value.boolean
? to_char_type(0xC3)
: to_char_type(0xC2));
break;
}
@@ -450,25 +463,25 @@ class binary_writer
else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
{
// uint 8
oa->write_character(to_char_type(0xCC));
oa.write_character(to_char_type(0xCC));
write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_integer));
}
else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
{
// uint 16
oa->write_character(to_char_type(0xCD));
oa.write_character(to_char_type(0xCD));
write_number(static_cast<std::uint16_t>(j.m_data.m_value.number_integer));
}
else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
{
// uint 32
oa->write_character(to_char_type(0xCE));
oa.write_character(to_char_type(0xCE));
write_number(static_cast<std::uint32_t>(j.m_data.m_value.number_integer));
}
else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
{
// uint 64
oa->write_character(to_char_type(0xCF));
oa.write_character(to_char_type(0xCF));
write_number(static_cast<std::uint64_t>(j.m_data.m_value.number_integer));
}
}
@@ -483,28 +496,28 @@ class binary_writer
j.m_data.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
{
// int 8
oa->write_character(to_char_type(0xD0));
oa.write_character(to_char_type(0xD0));
write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
}
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() &&
j.m_data.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
{
// int 16
oa->write_character(to_char_type(0xD1));
oa.write_character(to_char_type(0xD1));
write_number(static_cast<std::int16_t>(j.m_data.m_value.number_integer));
}
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() &&
j.m_data.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
{
// int 32
oa->write_character(to_char_type(0xD2));
oa.write_character(to_char_type(0xD2));
write_number(static_cast<std::int32_t>(j.m_data.m_value.number_integer));
}
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int64_t>::min)() &&
j.m_data.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
{
// int 64
oa->write_character(to_char_type(0xD3));
oa.write_character(to_char_type(0xD3));
write_number(static_cast<std::int64_t>(j.m_data.m_value.number_integer));
}
}
@@ -521,25 +534,25 @@ class binary_writer
else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
{
// uint 8
oa->write_character(to_char_type(0xCC));
oa.write_character(to_char_type(0xCC));
write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_integer));
}
else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
{
// uint 16
oa->write_character(to_char_type(0xCD));
oa.write_character(to_char_type(0xCD));
write_number(static_cast<std::uint16_t>(j.m_data.m_value.number_integer));
}
else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
{
// uint 32
oa->write_character(to_char_type(0xCE));
oa.write_character(to_char_type(0xCE));
write_number(static_cast<std::uint32_t>(j.m_data.m_value.number_integer));
}
else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
{
// uint 64
oa->write_character(to_char_type(0xCF));
oa.write_character(to_char_type(0xCF));
write_number(static_cast<std::uint64_t>(j.m_data.m_value.number_integer));
}
break;
@@ -563,26 +576,26 @@ class binary_writer
else if (N <= (std::numeric_limits<std::uint8_t>::max)())
{
// str 8
oa->write_character(to_char_type(0xD9));
oa.write_character(to_char_type(0xD9));
write_number(static_cast<std::uint8_t>(N));
}
else if (N <= (std::numeric_limits<std::uint16_t>::max)())
{
// str 16
oa->write_character(to_char_type(0xDA));
oa.write_character(to_char_type(0xDA));
write_number(static_cast<std::uint16_t>(N));
}
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
{
// str 32
oa->write_character(to_char_type(0xDB));
oa.write_character(to_char_type(0xDB));
write_number(static_cast<std::uint32_t>(N));
}
// step 2: write the string
oa->write_characters(
reinterpret_cast<const CharType*>(j.m_data.m_value.string->c_str()),
j.m_data.m_value.string->size());
oa.write_characters(
reinterpret_cast<const CharType*>(j.m_data.m_value.string->c_str()),
j.m_data.m_value.string->size());
break;
}
@@ -598,13 +611,13 @@ class binary_writer
else if (N <= (std::numeric_limits<std::uint16_t>::max)())
{
// array 16
oa->write_character(to_char_type(0xDC));
oa.write_character(to_char_type(0xDC));
write_number(static_cast<std::uint16_t>(N));
}
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
{
// array 32
oa->write_character(to_char_type(0xDD));
oa.write_character(to_char_type(0xDD));
write_number(static_cast<std::uint32_t>(N));
}
@@ -660,7 +673,7 @@ class binary_writer
fixed = false;
}
oa->write_character(to_char_type(output_type));
oa.write_character(to_char_type(output_type));
if (!fixed)
{
write_number(static_cast<std::uint8_t>(N));
@@ -672,7 +685,7 @@ class binary_writer
? 0xC8 // ext 16
: 0xC5; // bin 16
oa->write_character(to_char_type(output_type));
oa.write_character(to_char_type(output_type));
write_number(static_cast<std::uint16_t>(N));
}
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
@@ -681,7 +694,7 @@ class binary_writer
? 0xC9 // ext 32
: 0xC6; // bin 32
oa->write_character(to_char_type(output_type));
oa.write_character(to_char_type(output_type));
write_number(static_cast<std::uint32_t>(N));
}
@@ -692,9 +705,9 @@ class binary_writer
}
// step 2: write the byte string
oa->write_characters(
reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()),
N);
oa.write_characters(
reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()),
N);
break;
}
@@ -711,13 +724,13 @@ class binary_writer
else if (N <= (std::numeric_limits<std::uint16_t>::max)())
{
// map 16
oa->write_character(to_char_type(0xDE));
oa.write_character(to_char_type(0xDE));
write_number(static_cast<std::uint16_t>(N));
}
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
{
// map 32
oa->write_character(to_char_type(0xDF));
oa.write_character(to_char_type(0xDF));
write_number(static_cast<std::uint32_t>(N));
}
@@ -756,7 +769,7 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(to_char_type('Z'));
oa.write_character(to_char_type('Z'));
}
break;
}
@@ -765,9 +778,9 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(j.m_data.m_value.boolean
? to_char_type('T')
: to_char_type('F'));
oa.write_character(j.m_data.m_value.boolean
? to_char_type('T')
: to_char_type('F'));
}
break;
}
@@ -794,12 +807,12 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(to_char_type('S'));
oa.write_character(to_char_type('S'));
}
write_number_with_ubjson_prefix(j.m_data.m_value.string->size(), true, use_bjdata);
oa->write_characters(
reinterpret_cast<const CharType*>(j.m_data.m_value.string->c_str()),
j.m_data.m_value.string->size());
oa.write_characters(
reinterpret_cast<const CharType*>(j.m_data.m_value.string->c_str()),
j.m_data.m_value.string->size());
break;
}
@@ -807,7 +820,7 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(to_char_type('['));
oa.write_character(to_char_type('['));
}
bool prefix_required = true;
@@ -826,14 +839,14 @@ class binary_writer
if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end()))
{
prefix_required = false;
oa->write_character(to_char_type('$'));
oa->write_character(first_prefix);
oa.write_character(to_char_type('$'));
oa.write_character(first_prefix);
}
}
if (use_count)
{
oa->write_character(to_char_type('#'));
oa.write_character(to_char_type('#'));
write_number_with_ubjson_prefix(j.m_data.m_value.array->size(), true, use_bjdata);
}
@@ -844,7 +857,7 @@ class binary_writer
if (!use_count)
{
oa->write_character(to_char_type(']'));
oa.write_character(to_char_type(']'));
}
break;
@@ -854,40 +867,40 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(to_char_type('['));
oa.write_character(to_char_type('['));
}
if (use_type && (bjdata_draft3 || !j.m_data.m_value.binary->empty()))
{
JSON_ASSERT(use_count);
oa->write_character(to_char_type('$'));
oa->write_character(bjdata_draft3 ? 'B' : 'U');
oa.write_character(to_char_type('$'));
oa.write_character(bjdata_draft3 ? 'B' : 'U');
}
if (use_count)
{
oa->write_character(to_char_type('#'));
oa.write_character(to_char_type('#'));
write_number_with_ubjson_prefix(j.m_data.m_value.binary->size(), true, use_bjdata);
}
if (use_type)
{
oa->write_characters(
reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()),
j.m_data.m_value.binary->size());
oa.write_characters(
reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()),
j.m_data.m_value.binary->size());
}
else
{
for (size_t i = 0; i < j.m_data.m_value.binary->size(); ++i)
{
oa->write_character(to_char_type(bjdata_draft3 ? 'B' : 'U'));
oa->write_character(to_char_type(j.m_data.m_value.binary->data()[i]));
oa.write_character(to_char_type(bjdata_draft3 ? 'B' : 'U'));
oa.write_character(to_char_type(j.m_data.m_value.binary->data()[i]));
}
}
if (!use_count)
{
oa->write_character(to_char_type(']'));
oa.write_character(to_char_type(']'));
}
break;
@@ -905,7 +918,7 @@ class binary_writer
if (add_prefix)
{
oa->write_character(to_char_type('{'));
oa.write_character(to_char_type('{'));
}
bool prefix_required = true;
@@ -924,29 +937,29 @@ class binary_writer
if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end()))
{
prefix_required = false;
oa->write_character(to_char_type('$'));
oa->write_character(first_prefix);
oa.write_character(to_char_type('$'));
oa.write_character(first_prefix);
}
}
if (use_count)
{
oa->write_character(to_char_type('#'));
oa.write_character(to_char_type('#'));
write_number_with_ubjson_prefix(j.m_data.m_value.object->size(), true, use_bjdata);
}
for (const auto& el : *j.m_data.m_value.object)
{
write_number_with_ubjson_prefix(el.first.size(), true, use_bjdata);
oa->write_characters(
reinterpret_cast<const CharType*>(el.first.c_str()),
el.first.size());
oa.write_characters(
reinterpret_cast<const CharType*>(el.first.c_str()),
el.first.size());
write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata, bjdata_version);
}
if (!use_count)
{
oa->write_character(to_char_type('}'));
oa.write_character(to_char_type('}'));
}
break;
@@ -985,10 +998,10 @@ class binary_writer
void write_bson_entry_header(const string_t& name,
const std::uint8_t element_type)
{
oa->write_character(to_char_type(element_type)); // boolean
oa->write_characters(
reinterpret_cast<const CharType*>(name.c_str()),
name.size() + 1u);
oa.write_character(to_char_type(element_type)); // boolean
oa.write_characters(
reinterpret_cast<const CharType*>(name.c_str()),
name.size() + 1u);
}
/*!
@@ -998,7 +1011,7 @@ class binary_writer
const bool value)
{
write_bson_entry_header(name, 0x08);
oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00));
oa.write_character(value ? to_char_type(0x01) : to_char_type(0x00));
}
/*!
@@ -1028,9 +1041,9 @@ class binary_writer
write_bson_entry_header(name, 0x02);
write_number<std::int32_t>(static_cast<std::int32_t>(value.size() + 1ul), true);
oa->write_characters(
reinterpret_cast<const CharType*>(value.c_str()),
value.size() + 1);
oa.write_characters(
reinterpret_cast<const CharType*>(value.c_str()),
value.size() + 1);
}
/*!
@@ -1151,7 +1164,7 @@ class binary_writer
write_bson_element(std::to_string(array_index++), el);
}
oa->write_character(to_char_type(0x00));
oa.write_character(to_char_type(0x00));
}
/*!
@@ -1165,7 +1178,7 @@ class binary_writer
write_number<std::int32_t>(static_cast<std::int32_t>(value.size()), true);
write_number(value.has_subtype() ? static_cast<std::uint8_t>(value.subtype()) : static_cast<std::uint8_t>(0x00));
oa->write_characters(reinterpret_cast<const CharType*>(value.data()), value.size());
oa.write_characters(reinterpret_cast<const CharType*>(value.data()), value.size());
}
/*!
@@ -1291,7 +1304,7 @@ class binary_writer
write_bson_element(el.first, el.second);
}
oa->write_character(to_char_type(0x00));
oa.write_character(to_char_type(0x00));
}
//////////
@@ -1335,7 +1348,7 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(get_ubjson_float_prefix(n));
oa.write_character(get_ubjson_float_prefix(n));
}
write_number(n, use_bjdata);
}
@@ -1351,7 +1364,7 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(to_char_type('i')); // int8
oa.write_character(to_char_type('i')); // int8
}
write_number(static_cast<std::uint8_t>(n), use_bjdata);
}
@@ -1359,7 +1372,7 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(to_char_type('U')); // uint8
oa.write_character(to_char_type('U')); // uint8
}
write_number(static_cast<std::uint8_t>(n), use_bjdata);
}
@@ -1367,7 +1380,7 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(to_char_type('I')); // int16
oa.write_character(to_char_type('I')); // int16
}
write_number(static_cast<std::int16_t>(n), use_bjdata);
}
@@ -1375,7 +1388,7 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(to_char_type('u')); // uint16 - bjdata only
oa.write_character(to_char_type('u')); // uint16 - bjdata only
}
write_number(static_cast<std::uint16_t>(n), use_bjdata);
}
@@ -1383,7 +1396,7 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(to_char_type('l')); // int32
oa.write_character(to_char_type('l')); // int32
}
write_number(static_cast<std::int32_t>(n), use_bjdata);
}
@@ -1391,7 +1404,7 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(to_char_type('m')); // uint32 - bjdata only
oa.write_character(to_char_type('m')); // uint32 - bjdata only
}
write_number(static_cast<std::uint32_t>(n), use_bjdata);
}
@@ -1399,7 +1412,7 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(to_char_type('L')); // int64
oa.write_character(to_char_type('L')); // int64
}
write_number(static_cast<std::int64_t>(n), use_bjdata);
}
@@ -1407,7 +1420,7 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(to_char_type('M')); // uint64 - bjdata only
oa.write_character(to_char_type('M')); // uint64 - bjdata only
}
write_number(static_cast<std::uint64_t>(n), use_bjdata);
}
@@ -1415,14 +1428,14 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(to_char_type('H')); // high-precision number
oa.write_character(to_char_type('H')); // high-precision number
}
const auto number = BasicJsonType(n).dump();
write_number_with_ubjson_prefix(number.size(), true, use_bjdata);
for (std::size_t i = 0; i < number.size(); ++i)
{
oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));
oa.write_character(to_char_type(static_cast<std::uint8_t>(number[i])));
}
}
}
@@ -1439,7 +1452,7 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(to_char_type('i')); // int8
oa.write_character(to_char_type('i')); // int8
}
write_number(static_cast<std::int8_t>(n), use_bjdata);
}
@@ -1447,7 +1460,7 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(to_char_type('U')); // uint8
oa.write_character(to_char_type('U')); // uint8
}
write_number(static_cast<std::uint8_t>(n), use_bjdata);
}
@@ -1455,7 +1468,7 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(to_char_type('I')); // int16
oa.write_character(to_char_type('I')); // int16
}
write_number(static_cast<std::int16_t>(n), use_bjdata);
}
@@ -1463,7 +1476,7 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(to_char_type('u')); // uint16 - bjdata only
oa.write_character(to_char_type('u')); // uint16 - bjdata only
}
write_number(static_cast<uint16_t>(n), use_bjdata);
}
@@ -1471,7 +1484,7 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(to_char_type('l')); // int32
oa.write_character(to_char_type('l')); // int32
}
write_number(static_cast<std::int32_t>(n), use_bjdata);
}
@@ -1479,7 +1492,7 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(to_char_type('m')); // uint32 - bjdata only
oa.write_character(to_char_type('m')); // uint32 - bjdata only
}
write_number(static_cast<uint32_t>(n), use_bjdata);
}
@@ -1487,7 +1500,7 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(to_char_type('L')); // int64
oa.write_character(to_char_type('L')); // int64
}
write_number(static_cast<std::int64_t>(n), use_bjdata);
}
@@ -1496,14 +1509,14 @@ class binary_writer
{
if (add_prefix)
{
oa->write_character(to_char_type('H')); // high-precision number
oa.write_character(to_char_type('H')); // high-precision number
}
const auto number = BasicJsonType(n).dump();
write_number_with_ubjson_prefix(number.size(), true, use_bjdata);
for (std::size_t i = 0; i < number.size(); ++i)
{
oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));
oa.write_character(to_char_type(static_cast<std::uint8_t>(number[i])));
}
}
// LCOV_EXCL_STOP
@@ -1656,10 +1669,10 @@ class binary_writer
return true;
}
oa->write_character('[');
oa->write_character('$');
oa->write_character(dtype);
oa->write_character('#');
oa.write_character('[');
oa.write_character('$');
oa.write_character(dtype);
oa.write_character('#');
key = "_ArraySize_";
write_ubjson(value.at(key), use_count, use_type, true, true, bjdata_version);
@@ -1769,7 +1782,7 @@ class binary_writer
std::reverse(vec.begin(), vec.end());
}
oa->write_characters(vec.data(), sizeof(NumberType));
oa.write_characters(vec.data(), sizeof(NumberType));
}
void write_compact_float(const number_float_t n, detail::input_format_t format)
@@ -1782,16 +1795,16 @@ class binary_writer
static_cast<double>(n) <= static_cast<double>((std::numeric_limits<float>::max)()) &&
static_cast<double>(static_cast<float>(n)) == static_cast<double>(n))))
{
oa->write_character(format == detail::input_format_t::cbor
? get_cbor_float_prefix(static_cast<float>(n))
: get_msgpack_float_prefix(static_cast<float>(n)));
oa.write_character(format == detail::input_format_t::cbor
? get_cbor_float_prefix(static_cast<float>(n))
: get_msgpack_float_prefix(static_cast<float>(n)));
write_number(static_cast<float>(n));
}
else
{
oa->write_character(format == detail::input_format_t::cbor
? get_cbor_float_prefix(n)
: get_msgpack_float_prefix(n));
oa.write_character(format == detail::input_format_t::cbor
? get_cbor_float_prefix(n)
: get_msgpack_float_prefix(n));
write_number(n);
}
#ifdef __GNUC__
@@ -1858,7 +1871,7 @@ class binary_writer
const bool is_little_endian = little_endianness();
/// the output
output_adapter_t<CharType> oa = nullptr;
OutputSinkType oa;
};
} // namespace detail
@@ -118,6 +118,68 @@ class output_string_adapter : public output_adapter_protocol<CharType>
StringType& str;
};
/// @brief non-virtual output sink writing into a std::vector
///
/// Unlike output_vector_adapter, this sink is not part of the virtual
/// output_adapter_protocol hierarchy: it is passed to binary_writer by value as
/// a template parameter, so write_character()/write_characters() are ordinary
/// (inlinable) calls with no vtable lookup and no shared_ptr. It is used for the
/// common `to_cbor`/`to_msgpack`/... into a std::vector.
template<typename CharType, typename AllocatorType = std::allocator<CharType>>
class output_vector_sink
{
public:
explicit output_vector_sink(std::vector<CharType, AllocatorType>& vec) noexcept
: v(vec)
{}
void write_character(CharType c)
{
v.push_back(c);
}
JSON_HEDLEY_NON_NULL(2)
void write_characters(const CharType* s, std::size_t length)
{
v.insert(v.end(), s, s + length);
}
private:
std::vector<CharType, AllocatorType>& v;
};
/// @brief output sink forwarding to a type-erased output adapter
///
/// Wraps the polymorphic output_adapter_t so the same binary_writer template can
/// also target arbitrary adapters (output streams, strings, user-provided
/// adapters) via the `output_adapter`-based overloads. Each write still goes
/// through one virtual call, exactly as before; only the concrete sinks above
/// avoid it.
template<typename CharType>
class output_adapter_sink
{
public:
explicit output_adapter_sink(output_adapter_t<CharType> adapter)
: oa(std::move(adapter))
{
JSON_ASSERT(oa);
}
void write_character(CharType c)
{
oa->write_character(c);
}
JSON_HEDLEY_NON_NULL(2)
void write_characters(const CharType* s, std::size_t length)
{
oa->write_characters(s, length);
}
private:
output_adapter_t<CharType> oa = nullptr;
};
template<typename CharType, typename StringType = std::basic_string<CharType>>
class output_adapter
{
+13 -7
View File
@@ -140,7 +140,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
friend ::nlohmann::detail::serializer<basic_json>;
template<typename BasicJsonType>
friend class ::nlohmann::detail::iter_impl;
template<typename BasicJsonType, typename CharType>
template<typename BasicJsonType, typename CharType, typename OutputSinkType>
friend class ::nlohmann::detail::binary_writer;
template<typename BasicJsonType, typename InputType, typename SAX>
friend class ::nlohmann::detail::binary_reader;
@@ -186,7 +186,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
template<typename InputType>
using binary_reader = ::nlohmann::detail::binary_reader<basic_json, InputType>;
template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>;
template<typename CharType, typename OutputSinkType = ::nlohmann::detail::output_adapter_sink<CharType>>
using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType, OutputSinkType>;
JSON_PRIVATE_UNLESS_TESTED:
using serializer = ::nlohmann::detail::serializer<basic_json>;
@@ -4327,7 +4328,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static std::vector<std::uint8_t> to_cbor(const basic_json& j)
{
std::vector<std::uint8_t> result;
to_cbor(j, result);
binary_writer<std::uint8_t, detail::output_vector_sink<std::uint8_t>>(
detail::output_vector_sink<std::uint8_t>(result)).write_cbor(j);
return result;
}
@@ -4350,7 +4352,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static std::vector<std::uint8_t> to_msgpack(const basic_json& j)
{
std::vector<std::uint8_t> result;
to_msgpack(j, result);
binary_writer<std::uint8_t, detail::output_vector_sink<std::uint8_t>>(
detail::output_vector_sink<std::uint8_t>(result)).write_msgpack(j);
return result;
}
@@ -4375,7 +4378,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool use_type = false)
{
std::vector<std::uint8_t> result;
to_ubjson(j, result, use_size, use_type);
binary_writer<std::uint8_t, detail::output_vector_sink<std::uint8_t>>(
detail::output_vector_sink<std::uint8_t>(result)).write_ubjson(j, use_size, use_type);
return result;
}
@@ -4403,7 +4407,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bjdata_version_t version = bjdata_version_t::draft2)
{
std::vector<std::uint8_t> result;
to_bjdata(j, result, use_size, use_type, version);
binary_writer<std::uint8_t, detail::output_vector_sink<std::uint8_t>>(
detail::output_vector_sink<std::uint8_t>(result)).write_ubjson(j, use_size, use_type, true, true, version);
return result;
}
@@ -4430,7 +4435,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static std::vector<std::uint8_t> to_bson(const basic_json& j)
{
std::vector<std::uint8_t> result;
to_bson(j, result);
binary_writer<std::uint8_t, detail::output_vector_sink<std::uint8_t>>(
detail::output_vector_sink<std::uint8_t>(result)).write_bson(j);
return result;
}
File diff suppressed because it is too large Load Diff