Compare commits

..
Author SHA1 Message Date
Niels LohmannandClaude Opus 4.8 9ea2d28ef9 Reserve output capacity up front for binary serialization
The vector-returning to_cbor/to_msgpack/to_ubjson/to_bjdata/to_bson grew
the output buffer purely by geometric reallocation. Reserving an estimate
up front avoids the early reallocations, which is the dominant per-byte
cost for array/object-heavy output.

The estimate (binary_reserve_hint) is deliberately conservative and safe
against untrusted input: it consults only the top-level element count
(O(1), no walk of the DOM), guards the multiplication against overflow,
and clamps the result to a fixed 1 MiB ceiling, so a large or hostile DOM
can never force an oversized allocation here. The buffer still grows
geometrically past the hint, so an underestimate only costs a few later
reallocations; scalars/strings/binary are written in one shot and get no
hint. Reserving capacity does not change the bytes produced.

Throughput (g++/clang -O3, vs the previous commit):
  cbor int array     +10% / +13%
  cbor object array  +20% / +38%

Output is byte-for-byte identical to develop across the binary
differential corpus.

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>
2026-07-22 11:01:03 +00:00
Niels LohmannandClaude Opus 4.8 ac6b505923 Encode big-endian numbers with a byte swap instead of std::reverse
write_number() reordered multi-byte numbers for the big-endian formats
(CBOR/MessagePack/UBJSON) with std::reverse over the byte array. GCC
lowered only some sizes to a bswap; clang kept a scalar byte shuffle
(0 bswap instructions in the CBOR number path). Replace the reverse with
size-dispatched __builtin_bswap16/32/64 helpers (portable shift fallback
for other compilers; std::reverse retained for exotic sizes such as a
long double number_float_t).

Codegen: the CBOR number path now emits bswap on both compilers
(gcc 2 -> 16, clang 0 -> 4). Output is byte-for-byte identical to the
previous implementation across the binary differential corpus.

Throughput (isolated vs the std::reverse version, best of 9):
  CBOR int64 array   gcc +7%   clang +10%
  CBOR uint16 array  gcc +27%  clang flat

Modest but consistent on number-dense encodings; negligible on
string/blob-heavy output, as expected.

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>
2026-07-22 10:20:33 +00:00
Niels LohmannandClaude Opus 4.8 7374730aed Fix CI failures from binary_writer output-sink change
Four CI jobs failed on the initial commit; all are addressed here without
changing any output (binary encodings remain byte-for-byte identical to
develop across the differential corpus):

1. ci_test_gcc / cuda (-Werror=duplicated-branches): for number_float_t ==
   float, static_cast<float>(n) is the identity, so write_compact_float's
   two branches are intentionally identical. Once the concrete vector sink
   is inlined, GCC constant-folds and diagnoses this (the type-erased path
   hid it behind a non-inlined virtual call). Silence -Wduplicated-branches
   for GCC (clang has no such warning) alongside the existing -Wfloat-equal
   pragma.

2. ci_static_analysis_clang (UBSan nonnull-attribute): binary_writer passes
   a null pointer with length 0 for empty strings/binary. output_vector_sink
   / output_adapter_sink declared write_characters JSON_HEDLEY_NON_NULL, so
   the sanitizer flagged the (harmless) zero-length call once the sink was
   called directly rather than through the attribute-free virtual base. Drop
   the attribute from both sinks, matching the pre-existing behavior.

3. ci_cpplint (build/include_what_you_use): output_adapter_sink uses
   std::move; add #include <utility>.

4. ci_cuda_example (nvcc 11.8): NVCC's front end rejects the default
   template argument on the binary_writer alias template. Revert the alias
   to its original single-parameter form (relying on binary_writer's own
   defaulted OutputSinkType) and spell out the full type in the vector-sink
   convenience functions.

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>
2026-07-22 08:47:24 +00:00
Niels LohmannandClaude Opus 4.8 ebb3abba41 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>
2026-07-20 23:27:51 +00:00
6 changed files with 706 additions and 338 deletions
-8
View File
@@ -15,14 +15,6 @@ guidance.
For vulnerabilities in third-party dependencies or modules, please report them directly to the respective maintainers. For vulnerabilities in third-party dependencies or modules, please report them directly to the respective maintainers.
## Unofficial packages
This project does not publish an official npm package. The npm package
[`nlohmann-json`](https://www.npmjs.com/package/nlohmann-json) (or similarly named packages) is not maintained or
endorsed by this project. See the
[package managers documentation](https://json.nlohmann.me/integration/package_managers/#npm) for supported
integration options.
## Additional Resources ## Additional Resources
- Explore security-related topics and contribute to tools and projects through - Explore security-related topics and contribute to tools and projects through
@@ -930,12 +930,6 @@ If you are using [CocoaPods](https://cocoapods.org), you can use the library by
to your podfile (see [an example](https://bitbucket.org/benman/nlohmann_json-cocoapod/src/master/)). Please file issues to your podfile (see [an example](https://bitbucket.org/benman/nlohmann_json-cocoapod/src/master/)). Please file issues
[here](https://bitbucket.org/benman/nlohmann_json-cocoapod/issues?status=new&status=open). [here](https://bitbucket.org/benman/nlohmann_json-cocoapod/issues?status=new&status=open).
## npm
This project does not publish an official [npm](https://www.npmjs.com) package. The npm package
[`nlohmann-json`](https://www.npmjs.com/package/nlohmann-json) (or similarly named packages) is not maintained or
endorsed by this project. Use one of the package managers listed above, or integrate the single header directly.
## ESP-IDF and PlatformIO ## ESP-IDF and PlatformIO
There is no official package published to the [ESP-IDF Component Registry](https://components.espressif.com) or the There is no official package published to the [ESP-IDF Component Registry](https://components.espressif.com) or the
+242 -128
View File
@@ -39,10 +39,39 @@ enum class bjdata_version_t
// binary writer // // binary writer //
/////////////////// ///////////////////
/*!
@brief conservative capacity hint for binary serialization into a std::vector
Returns an approximate number of bytes to reserve up front so that serializing
an array/object of many elements does not repeatedly reallocate the output
buffer. Only the top-level element count is consulted (O(1), no walk of the
DOM), and the result is clamped to a fixed ceiling: a large or untrusted DOM can
therefore never trigger an oversized allocation here, and the multiplication
cannot overflow. The buffer still grows geometrically beyond the hint, so a hint
that is too small only costs a few later reallocations. A single scalar, string,
or binary value is written in one shot and needs no hint.
*/
template<typename BasicJsonType>
std::size_t binary_reserve_hint(const BasicJsonType& j)
{
constexpr std::size_t max_hint = static_cast<std::size_t>(1) << 20; // 1 MiB
if (j.is_array() || j.is_object())
{
const std::size_t elements = j.size();
// guard the multiplication against overflow and cap the reservation
if (elements > max_hint / 4)
{
return max_hint;
}
return (elements * 4) + 2;
}
return 0;
}
/*! /*!
@brief serialization to CBOR and MessagePack values @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 class binary_writer
{ {
using string_t = typename BasicJsonType::string_t; using string_t = typename BasicJsonType::string_t;
@@ -53,12 +82,25 @@ class binary_writer
/*! /*!
@brief create a 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 @param[in] adapter output adapter to write to
*/ */
explicit binary_writer(output_adapter_t<CharType> adapter) : oa(std::move(adapter)) explicit binary_writer(output_adapter_t<CharType> adapter) : oa(OutputSinkType(std::move(adapter)))
{ {}
JSON_ASSERT(oa);
}
/*! /*!
@param[in] j JSON value to serialize @param[in] j JSON value to serialize
@@ -99,13 +141,13 @@ class binary_writer
{ {
case value_t::null: case value_t::null:
{ {
oa->write_character(to_char_type(0xF6)); oa.write_character(to_char_type(0xF6));
break; break;
} }
case value_t::boolean: case value_t::boolean:
{ {
oa->write_character(j.m_data.m_value.boolean oa.write_character(j.m_data.m_value.boolean
? to_char_type(0xF5) ? to_char_type(0xF5)
: to_char_type(0xF4)); : to_char_type(0xF4));
break; break;
@@ -124,22 +166,22 @@ class binary_writer
} }
else if (j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)()) 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)); 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)()) 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)); 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)()) 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)); write_number(static_cast<std::uint32_t>(j.m_data.m_value.number_integer));
} }
else 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)); write_number(static_cast<std::uint64_t>(j.m_data.m_value.number_integer));
} }
} }
@@ -154,22 +196,22 @@ class binary_writer
} }
else if (positive_number <= (std::numeric_limits<std::uint8_t>::max)()) 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)); write_number(static_cast<std::uint8_t>(positive_number));
} }
else if (positive_number <= (std::numeric_limits<std::uint16_t>::max)()) 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)); write_number(static_cast<std::uint16_t>(positive_number));
} }
else if (positive_number <= (std::numeric_limits<std::uint32_t>::max)()) 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)); write_number(static_cast<std::uint32_t>(positive_number));
} }
else else
{ {
oa->write_character(to_char_type(0x3B)); oa.write_character(to_char_type(0x3B));
write_number(static_cast<std::uint64_t>(positive_number)); write_number(static_cast<std::uint64_t>(positive_number));
} }
} }
@@ -184,22 +226,22 @@ class binary_writer
} }
else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)()) 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)); 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)()) 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)); 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)()) 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)); write_number(static_cast<std::uint32_t>(j.m_data.m_value.number_unsigned));
} }
else 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)); write_number(static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned));
} }
break; break;
@@ -210,16 +252,16 @@ class binary_writer
if (std::isnan(j.m_data.m_value.number_float)) if (std::isnan(j.m_data.m_value.number_float))
{ {
// NaN is 0xf97e00 in CBOR // NaN is 0xf97e00 in CBOR
oa->write_character(to_char_type(0xF9)); oa.write_character(to_char_type(0xF9));
oa->write_character(to_char_type(0x7E)); oa.write_character(to_char_type(0x7E));
oa->write_character(to_char_type(0x00)); oa.write_character(to_char_type(0x00));
} }
else if (std::isinf(j.m_data.m_value.number_float)) else if (std::isinf(j.m_data.m_value.number_float))
{ {
// Infinity is 0xf97c00, -Infinity is 0xf9fc00 // Infinity is 0xf97c00, -Infinity is 0xf9fc00
oa->write_character(to_char_type(0xf9)); 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(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(0x00));
} }
else else
{ {
@@ -238,29 +280,29 @@ class binary_writer
} }
else if (N <= (std::numeric_limits<std::uint8_t>::max)()) 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)); write_number(static_cast<std::uint8_t>(N));
} }
else if (N <= (std::numeric_limits<std::uint16_t>::max)()) 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)); write_number(static_cast<std::uint16_t>(N));
} }
else if (N <= (std::numeric_limits<std::uint32_t>::max)()) 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)); write_number(static_cast<std::uint32_t>(N));
} }
// LCOV_EXCL_START // LCOV_EXCL_START
else if (N <= (std::numeric_limits<std::uint64_t>::max)()) 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)); write_number(static_cast<std::uint64_t>(N));
} }
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
// step 2: write the string // step 2: write the string
oa->write_characters( oa.write_characters(
reinterpret_cast<const CharType*>(j.m_data.m_value.string->c_str()), reinterpret_cast<const CharType*>(j.m_data.m_value.string->c_str()),
j.m_data.m_value.string->size()); j.m_data.m_value.string->size());
break; break;
@@ -276,23 +318,23 @@ class binary_writer
} }
else if (N <= (std::numeric_limits<std::uint8_t>::max)()) 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)); write_number(static_cast<std::uint8_t>(N));
} }
else if (N <= (std::numeric_limits<std::uint16_t>::max)()) 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)); write_number(static_cast<std::uint16_t>(N));
} }
else if (N <= (std::numeric_limits<std::uint32_t>::max)()) 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)); write_number(static_cast<std::uint32_t>(N));
} }
// LCOV_EXCL_START // LCOV_EXCL_START
else if (N <= (std::numeric_limits<std::uint64_t>::max)()) 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)); write_number(static_cast<std::uint64_t>(N));
} }
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
@@ -339,29 +381,29 @@ class binary_writer
} }
else if (N <= (std::numeric_limits<std::uint8_t>::max)()) 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)); write_number(static_cast<std::uint8_t>(N));
} }
else if (N <= (std::numeric_limits<std::uint16_t>::max)()) 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)); write_number(static_cast<std::uint16_t>(N));
} }
else if (N <= (std::numeric_limits<std::uint32_t>::max)()) 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)); write_number(static_cast<std::uint32_t>(N));
} }
// LCOV_EXCL_START // LCOV_EXCL_START
else if (N <= (std::numeric_limits<std::uint64_t>::max)()) 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)); write_number(static_cast<std::uint64_t>(N));
} }
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
// step 2: write each element // step 2: write each element
oa->write_characters( oa.write_characters(
reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()), reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()),
N); N);
@@ -378,23 +420,23 @@ class binary_writer
} }
else if (N <= (std::numeric_limits<std::uint8_t>::max)()) 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)); write_number(static_cast<std::uint8_t>(N));
} }
else if (N <= (std::numeric_limits<std::uint16_t>::max)()) 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)); write_number(static_cast<std::uint16_t>(N));
} }
else if (N <= (std::numeric_limits<std::uint32_t>::max)()) 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)); write_number(static_cast<std::uint32_t>(N));
} }
// LCOV_EXCL_START // LCOV_EXCL_START
else if (N <= (std::numeric_limits<std::uint64_t>::max)()) 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)); write_number(static_cast<std::uint64_t>(N));
} }
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
@@ -423,13 +465,13 @@ class binary_writer
{ {
case value_t::null: // nil case value_t::null: // nil
{ {
oa->write_character(to_char_type(0xC0)); oa.write_character(to_char_type(0xC0));
break; break;
} }
case value_t::boolean: // true and false case value_t::boolean: // true and false
{ {
oa->write_character(j.m_data.m_value.boolean oa.write_character(j.m_data.m_value.boolean
? to_char_type(0xC3) ? to_char_type(0xC3)
: to_char_type(0xC2)); : to_char_type(0xC2));
break; break;
@@ -450,25 +492,25 @@ class binary_writer
else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)()) else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
{ {
// uint 8 // 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)); 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)()) else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
{ {
// uint 16 // 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)); 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)()) else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
{ {
// uint 32 // 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)); 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)()) else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
{ {
// uint 64 // 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)); write_number(static_cast<std::uint64_t>(j.m_data.m_value.number_integer));
} }
} }
@@ -483,28 +525,28 @@ class binary_writer
j.m_data.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)()) j.m_data.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
{ {
// int 8 // 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)); 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)() && 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)()) j.m_data.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
{ {
// int 16 // 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)); 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)() && 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)()) j.m_data.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
{ {
// int 32 // 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)); 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)() && 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)()) j.m_data.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
{ {
// int 64 // 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)); write_number(static_cast<std::int64_t>(j.m_data.m_value.number_integer));
} }
} }
@@ -521,25 +563,25 @@ class binary_writer
else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)()) else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
{ {
// uint 8 // 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)); 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)()) else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
{ {
// uint 16 // 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)); 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)()) else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
{ {
// uint 32 // 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)); 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)()) else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
{ {
// uint 64 // 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)); write_number(static_cast<std::uint64_t>(j.m_data.m_value.number_integer));
} }
break; break;
@@ -563,24 +605,24 @@ class binary_writer
else if (N <= (std::numeric_limits<std::uint8_t>::max)()) else if (N <= (std::numeric_limits<std::uint8_t>::max)())
{ {
// str 8 // str 8
oa->write_character(to_char_type(0xD9)); oa.write_character(to_char_type(0xD9));
write_number(static_cast<std::uint8_t>(N)); write_number(static_cast<std::uint8_t>(N));
} }
else if (N <= (std::numeric_limits<std::uint16_t>::max)()) else if (N <= (std::numeric_limits<std::uint16_t>::max)())
{ {
// str 16 // str 16
oa->write_character(to_char_type(0xDA)); oa.write_character(to_char_type(0xDA));
write_number(static_cast<std::uint16_t>(N)); write_number(static_cast<std::uint16_t>(N));
} }
else if (N <= (std::numeric_limits<std::uint32_t>::max)()) else if (N <= (std::numeric_limits<std::uint32_t>::max)())
{ {
// str 32 // str 32
oa->write_character(to_char_type(0xDB)); oa.write_character(to_char_type(0xDB));
write_number(static_cast<std::uint32_t>(N)); write_number(static_cast<std::uint32_t>(N));
} }
// step 2: write the string // step 2: write the string
oa->write_characters( oa.write_characters(
reinterpret_cast<const CharType*>(j.m_data.m_value.string->c_str()), reinterpret_cast<const CharType*>(j.m_data.m_value.string->c_str()),
j.m_data.m_value.string->size()); j.m_data.m_value.string->size());
break; break;
@@ -598,13 +640,13 @@ class binary_writer
else if (N <= (std::numeric_limits<std::uint16_t>::max)()) else if (N <= (std::numeric_limits<std::uint16_t>::max)())
{ {
// array 16 // array 16
oa->write_character(to_char_type(0xDC)); oa.write_character(to_char_type(0xDC));
write_number(static_cast<std::uint16_t>(N)); write_number(static_cast<std::uint16_t>(N));
} }
else if (N <= (std::numeric_limits<std::uint32_t>::max)()) else if (N <= (std::numeric_limits<std::uint32_t>::max)())
{ {
// array 32 // array 32
oa->write_character(to_char_type(0xDD)); oa.write_character(to_char_type(0xDD));
write_number(static_cast<std::uint32_t>(N)); write_number(static_cast<std::uint32_t>(N));
} }
@@ -660,7 +702,7 @@ class binary_writer
fixed = false; fixed = false;
} }
oa->write_character(to_char_type(output_type)); oa.write_character(to_char_type(output_type));
if (!fixed) if (!fixed)
{ {
write_number(static_cast<std::uint8_t>(N)); write_number(static_cast<std::uint8_t>(N));
@@ -672,7 +714,7 @@ class binary_writer
? 0xC8 // ext 16 ? 0xC8 // ext 16
: 0xC5; // bin 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)); write_number(static_cast<std::uint16_t>(N));
} }
else if (N <= (std::numeric_limits<std::uint32_t>::max)()) else if (N <= (std::numeric_limits<std::uint32_t>::max)())
@@ -681,7 +723,7 @@ class binary_writer
? 0xC9 // ext 32 ? 0xC9 // ext 32
: 0xC6; // bin 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)); write_number(static_cast<std::uint32_t>(N));
} }
@@ -692,7 +734,7 @@ class binary_writer
} }
// step 2: write the byte string // step 2: write the byte string
oa->write_characters( oa.write_characters(
reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()), reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()),
N); N);
@@ -711,13 +753,13 @@ class binary_writer
else if (N <= (std::numeric_limits<std::uint16_t>::max)()) else if (N <= (std::numeric_limits<std::uint16_t>::max)())
{ {
// map 16 // map 16
oa->write_character(to_char_type(0xDE)); oa.write_character(to_char_type(0xDE));
write_number(static_cast<std::uint16_t>(N)); write_number(static_cast<std::uint16_t>(N));
} }
else if (N <= (std::numeric_limits<std::uint32_t>::max)()) else if (N <= (std::numeric_limits<std::uint32_t>::max)())
{ {
// map 32 // map 32
oa->write_character(to_char_type(0xDF)); oa.write_character(to_char_type(0xDF));
write_number(static_cast<std::uint32_t>(N)); write_number(static_cast<std::uint32_t>(N));
} }
@@ -756,7 +798,7 @@ class binary_writer
{ {
if (add_prefix) if (add_prefix)
{ {
oa->write_character(to_char_type('Z')); oa.write_character(to_char_type('Z'));
} }
break; break;
} }
@@ -765,7 +807,7 @@ class binary_writer
{ {
if (add_prefix) if (add_prefix)
{ {
oa->write_character(j.m_data.m_value.boolean oa.write_character(j.m_data.m_value.boolean
? to_char_type('T') ? to_char_type('T')
: to_char_type('F')); : to_char_type('F'));
} }
@@ -794,10 +836,10 @@ class binary_writer
{ {
if (add_prefix) 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); write_number_with_ubjson_prefix(j.m_data.m_value.string->size(), true, use_bjdata);
oa->write_characters( oa.write_characters(
reinterpret_cast<const CharType*>(j.m_data.m_value.string->c_str()), reinterpret_cast<const CharType*>(j.m_data.m_value.string->c_str()),
j.m_data.m_value.string->size()); j.m_data.m_value.string->size());
break; break;
@@ -807,7 +849,7 @@ class binary_writer
{ {
if (add_prefix) if (add_prefix)
{ {
oa->write_character(to_char_type('[')); oa.write_character(to_char_type('['));
} }
bool prefix_required = true; bool prefix_required = true;
@@ -826,14 +868,14 @@ class binary_writer
if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end())) if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end()))
{ {
prefix_required = false; prefix_required = false;
oa->write_character(to_char_type('$')); oa.write_character(to_char_type('$'));
oa->write_character(first_prefix); oa.write_character(first_prefix);
} }
} }
if (use_count) 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); write_number_with_ubjson_prefix(j.m_data.m_value.array->size(), true, use_bjdata);
} }
@@ -844,7 +886,7 @@ class binary_writer
if (!use_count) if (!use_count)
{ {
oa->write_character(to_char_type(']')); oa.write_character(to_char_type(']'));
} }
break; break;
@@ -854,25 +896,25 @@ class binary_writer
{ {
if (add_prefix) 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())) if (use_type && (bjdata_draft3 || !j.m_data.m_value.binary->empty()))
{ {
JSON_ASSERT(use_count); JSON_ASSERT(use_count);
oa->write_character(to_char_type('$')); oa.write_character(to_char_type('$'));
oa->write_character(bjdata_draft3 ? 'B' : 'U'); oa.write_character(bjdata_draft3 ? 'B' : 'U');
} }
if (use_count) 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); write_number_with_ubjson_prefix(j.m_data.m_value.binary->size(), true, use_bjdata);
} }
if (use_type) if (use_type)
{ {
oa->write_characters( oa.write_characters(
reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()), reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()),
j.m_data.m_value.binary->size()); j.m_data.m_value.binary->size());
} }
@@ -880,14 +922,14 @@ class binary_writer
{ {
for (size_t i = 0; i < j.m_data.m_value.binary->size(); ++i) 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(bjdata_draft3 ? 'B' : 'U'));
oa->write_character(to_char_type(j.m_data.m_value.binary->data()[i])); oa.write_character(to_char_type(j.m_data.m_value.binary->data()[i]));
} }
} }
if (!use_count) if (!use_count)
{ {
oa->write_character(to_char_type(']')); oa.write_character(to_char_type(']'));
} }
break; break;
@@ -905,7 +947,7 @@ class binary_writer
if (add_prefix) if (add_prefix)
{ {
oa->write_character(to_char_type('{')); oa.write_character(to_char_type('{'));
} }
bool prefix_required = true; bool prefix_required = true;
@@ -924,21 +966,21 @@ class binary_writer
if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end())) if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end()))
{ {
prefix_required = false; prefix_required = false;
oa->write_character(to_char_type('$')); oa.write_character(to_char_type('$'));
oa->write_character(first_prefix); oa.write_character(first_prefix);
} }
} }
if (use_count) 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); 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) for (const auto& el : *j.m_data.m_value.object)
{ {
write_number_with_ubjson_prefix(el.first.size(), true, use_bjdata); write_number_with_ubjson_prefix(el.first.size(), true, use_bjdata);
oa->write_characters( oa.write_characters(
reinterpret_cast<const CharType*>(el.first.c_str()), reinterpret_cast<const CharType*>(el.first.c_str()),
el.first.size()); el.first.size());
write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata, bjdata_version); write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata, bjdata_version);
@@ -946,7 +988,7 @@ class binary_writer
if (!use_count) if (!use_count)
{ {
oa->write_character(to_char_type('}')); oa.write_character(to_char_type('}'));
} }
break; break;
@@ -985,8 +1027,8 @@ class binary_writer
void write_bson_entry_header(const string_t& name, void write_bson_entry_header(const string_t& name,
const std::uint8_t element_type) const std::uint8_t element_type)
{ {
oa->write_character(to_char_type(element_type)); // boolean oa.write_character(to_char_type(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);
} }
@@ -998,7 +1040,7 @@ class binary_writer
const bool value) const bool value)
{ {
write_bson_entry_header(name, 0x08); 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,7 +1070,7 @@ class binary_writer
write_bson_entry_header(name, 0x02); write_bson_entry_header(name, 0x02);
write_number<std::int32_t>(static_cast<std::int32_t>(value.size() + 1ul), true); write_number<std::int32_t>(static_cast<std::int32_t>(value.size() + 1ul), true);
oa->write_characters( oa.write_characters(
reinterpret_cast<const CharType*>(value.c_str()), reinterpret_cast<const CharType*>(value.c_str()),
value.size() + 1); value.size() + 1);
} }
@@ -1151,7 +1193,7 @@ class binary_writer
write_bson_element(std::to_string(array_index++), el); 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 +1207,7 @@ class binary_writer
write_number<std::int32_t>(static_cast<std::int32_t>(value.size()), true); 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)); 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 +1333,7 @@ class binary_writer
write_bson_element(el.first, el.second); write_bson_element(el.first, el.second);
} }
oa->write_character(to_char_type(0x00)); oa.write_character(to_char_type(0x00));
} }
////////// //////////
@@ -1335,7 +1377,7 @@ class binary_writer
{ {
if (add_prefix) if (add_prefix)
{ {
oa->write_character(get_ubjson_float_prefix(n)); oa.write_character(get_ubjson_float_prefix(n));
} }
write_number(n, use_bjdata); write_number(n, use_bjdata);
} }
@@ -1351,7 +1393,7 @@ class binary_writer
{ {
if (add_prefix) 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); write_number(static_cast<std::uint8_t>(n), use_bjdata);
} }
@@ -1359,7 +1401,7 @@ class binary_writer
{ {
if (add_prefix) 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); write_number(static_cast<std::uint8_t>(n), use_bjdata);
} }
@@ -1367,7 +1409,7 @@ class binary_writer
{ {
if (add_prefix) 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); write_number(static_cast<std::int16_t>(n), use_bjdata);
} }
@@ -1375,7 +1417,7 @@ class binary_writer
{ {
if (add_prefix) 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); write_number(static_cast<std::uint16_t>(n), use_bjdata);
} }
@@ -1383,7 +1425,7 @@ class binary_writer
{ {
if (add_prefix) 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); write_number(static_cast<std::int32_t>(n), use_bjdata);
} }
@@ -1391,7 +1433,7 @@ class binary_writer
{ {
if (add_prefix) 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); write_number(static_cast<std::uint32_t>(n), use_bjdata);
} }
@@ -1399,7 +1441,7 @@ class binary_writer
{ {
if (add_prefix) 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); write_number(static_cast<std::int64_t>(n), use_bjdata);
} }
@@ -1407,7 +1449,7 @@ class binary_writer
{ {
if (add_prefix) 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); write_number(static_cast<std::uint64_t>(n), use_bjdata);
} }
@@ -1415,14 +1457,14 @@ class binary_writer
{ {
if (add_prefix) 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(); const auto number = BasicJsonType(n).dump();
write_number_with_ubjson_prefix(number.size(), true, use_bjdata); write_number_with_ubjson_prefix(number.size(), true, use_bjdata);
for (std::size_t i = 0; i < number.size(); ++i) 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 +1481,7 @@ class binary_writer
{ {
if (add_prefix) 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); write_number(static_cast<std::int8_t>(n), use_bjdata);
} }
@@ -1447,7 +1489,7 @@ class binary_writer
{ {
if (add_prefix) 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); write_number(static_cast<std::uint8_t>(n), use_bjdata);
} }
@@ -1455,7 +1497,7 @@ class binary_writer
{ {
if (add_prefix) 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); write_number(static_cast<std::int16_t>(n), use_bjdata);
} }
@@ -1463,7 +1505,7 @@ class binary_writer
{ {
if (add_prefix) 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); write_number(static_cast<uint16_t>(n), use_bjdata);
} }
@@ -1471,7 +1513,7 @@ class binary_writer
{ {
if (add_prefix) 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); write_number(static_cast<std::int32_t>(n), use_bjdata);
} }
@@ -1479,7 +1521,7 @@ class binary_writer
{ {
if (add_prefix) 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); write_number(static_cast<uint32_t>(n), use_bjdata);
} }
@@ -1487,7 +1529,7 @@ class binary_writer
{ {
if (add_prefix) 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); write_number(static_cast<std::int64_t>(n), use_bjdata);
} }
@@ -1496,14 +1538,14 @@ class binary_writer
{ {
if (add_prefix) 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(); const auto number = BasicJsonType(n).dump();
write_number_with_ubjson_prefix(number.size(), true, use_bjdata); write_number_with_ubjson_prefix(number.size(), true, use_bjdata);
for (std::size_t i = 0; i < number.size(); ++i) 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 // LCOV_EXCL_STOP
@@ -1656,10 +1698,10 @@ class binary_writer
return true; return true;
} }
oa->write_character('['); oa.write_character('[');
oa->write_character('$'); oa.write_character('$');
oa->write_character(dtype); oa.write_character(dtype);
oa->write_character('#'); oa.write_character('#');
key = "_ArraySize_"; key = "_ArraySize_";
write_ubjson(value.at(key), use_count, use_type, true, true, bjdata_version); write_ubjson(value.at(key), use_count, use_type, true, true, bjdata_version);
@@ -1755,6 +1797,71 @@ class binary_writer
On the other hand, BSON and BJData use little endian and should reorder On the other hand, BSON and BJData use little endian and should reorder
on big endian systems. on big endian systems.
*/ */
// single-instruction byte swaps (compilers lower these to bswap/rev/movbe);
// used to emit big-endian numbers without a per-byte std::reverse loop
static std::uint16_t byte_swap(std::uint16_t x) noexcept
{
#if defined(__GNUC__) || defined(__clang__)
return __builtin_bswap16(x);
#else
return static_cast<std::uint16_t>((x >> 8) | (x << 8));
#endif
}
static std::uint32_t byte_swap(std::uint32_t x) noexcept
{
#if defined(__GNUC__) || defined(__clang__)
return __builtin_bswap32(x);
#else
return ((x & 0x000000FFu) << 24) | ((x & 0x0000FF00u) << 8)
| ((x & 0x00FF0000u) >> 8) | ((x & 0xFF000000u) >> 24);
#endif
}
static std::uint64_t byte_swap(std::uint64_t x) noexcept
{
#if defined(__GNUC__) || defined(__clang__)
return __builtin_bswap64(x);
#else
x = ((x & 0x00000000FFFFFFFFull) << 32) | ((x & 0xFFFFFFFF00000000ull) >> 32);
x = ((x & 0x0000FFFF0000FFFFull) << 16) | ((x & 0xFFFF0000FFFF0000ull) >> 16);
x = ((x & 0x00FF00FF00FF00FFull) << 8) | ((x & 0xFF00FF00FF00FF00ull) >> 8);
return x;
#endif
}
// reverse the bytes of a fixed-size buffer; a single byte_swap() for the
// common 2/4/8-byte number payloads, std::reverse for any other size
static void reverse_bytes(std::array<CharType, 2>& a) noexcept
{
std::uint16_t v{};
std::memcpy(&v, a.data(), sizeof(v));
v = byte_swap(v);
std::memcpy(a.data(), &v, sizeof(v));
}
static void reverse_bytes(std::array<CharType, 4>& a) noexcept
{
std::uint32_t v{};
std::memcpy(&v, a.data(), sizeof(v));
v = byte_swap(v);
std::memcpy(a.data(), &v, sizeof(v));
}
static void reverse_bytes(std::array<CharType, 8>& a) noexcept
{
std::uint64_t v{};
std::memcpy(&v, a.data(), sizeof(v));
v = byte_swap(v);
std::memcpy(a.data(), &v, sizeof(v));
}
template<std::size_t N>
static void reverse_bytes(std::array<CharType, N>& a) noexcept
{
std::reverse(a.begin(), a.end());
}
template<typename NumberType> template<typename NumberType>
void write_number(const NumberType n, const bool OutputIsLittleEndian = false) void write_number(const NumberType n, const bool OutputIsLittleEndian = false)
{ {
@@ -1766,10 +1873,10 @@ class binary_writer
if (is_little_endian != OutputIsLittleEndian) if (is_little_endian != OutputIsLittleEndian)
{ {
// reverse byte order prior to conversion if necessary // reverse byte order prior to conversion if necessary
std::reverse(vec.begin(), vec.end()); reverse_bytes(vec);
} }
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) void write_compact_float(const number_float_t n, detail::input_format_t format)
@@ -1777,19 +1884,26 @@ class binary_writer
#ifdef __GNUC__ #ifdef __GNUC__
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal" #pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
// When number_float_t is float, static_cast<float>(n) is the identity and
// both branches below are intentionally identical (the "compact" float
// representation is the value itself). Only GCC diagnoses this, and only
// when the sink calls are inlined; clang has no such warning.
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic ignored "-Wduplicated-branches"
#endif #endif
if (!std::isfinite(n) || ((static_cast<double>(n) >= static_cast<double>(std::numeric_limits<float>::lowest()) && if (!std::isfinite(n) || ((static_cast<double>(n) >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
static_cast<double>(n) <= static_cast<double>((std::numeric_limits<float>::max)()) && static_cast<double>(n) <= static_cast<double>((std::numeric_limits<float>::max)()) &&
static_cast<double>(static_cast<float>(n)) == static_cast<double>(n)))) static_cast<double>(static_cast<float>(n)) == static_cast<double>(n))))
{ {
oa->write_character(format == detail::input_format_t::cbor oa.write_character(format == detail::input_format_t::cbor
? get_cbor_float_prefix(static_cast<float>(n)) ? get_cbor_float_prefix(static_cast<float>(n))
: get_msgpack_float_prefix(static_cast<float>(n))); : get_msgpack_float_prefix(static_cast<float>(n)));
write_number(static_cast<float>(n)); write_number(static_cast<float>(n));
} }
else else
{ {
oa->write_character(format == detail::input_format_t::cbor oa.write_character(format == detail::input_format_t::cbor
? get_cbor_float_prefix(n) ? get_cbor_float_prefix(n)
: get_msgpack_float_prefix(n)); : get_msgpack_float_prefix(n));
write_number(n); write_number(n);
@@ -1858,7 +1972,7 @@ class binary_writer
const bool is_little_endian = little_endianness(); const bool is_little_endian = little_endianness();
/// the output /// the output
output_adapter_t<CharType> oa = nullptr; OutputSinkType oa;
}; };
} // namespace detail } // namespace detail
@@ -13,6 +13,7 @@
#include <iterator> // back_inserter #include <iterator> // back_inserter
#include <memory> // shared_ptr, make_shared #include <memory> // shared_ptr, make_shared
#include <string> // basic_string #include <string> // basic_string
#include <utility> // move
#include <vector> // vector #include <vector> // vector
#ifndef JSON_NO_IO #ifndef JSON_NO_IO
@@ -118,6 +119,72 @@ class output_string_adapter : public output_adapter_protocol<CharType>
StringType& str; 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);
}
// no JSON_HEDLEY_NON_NULL here: binary_writer legitimately passes a null
// pointer with length 0 for empty strings/binary values. Appending an empty
// range is a no-op; the type-erased path tolerates this via the (unattributed)
// virtual base, and the concrete sink must do the same.
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);
}
// no JSON_HEDLEY_NON_NULL: forwards (null, 0) for empty payloads, exactly as
// the type-erased path already did before this sink existed
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>> template<typename CharType, typename StringType = std::basic_string<CharType>>
class output_adapter class output_adapter
{ {
+16 -6
View File
@@ -140,7 +140,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
friend ::nlohmann::detail::serializer<basic_json>; friend ::nlohmann::detail::serializer<basic_json>;
template<typename BasicJsonType> template<typename BasicJsonType>
friend class ::nlohmann::detail::iter_impl; friend class ::nlohmann::detail::iter_impl;
template<typename BasicJsonType, typename CharType> template<typename BasicJsonType, typename CharType, typename OutputSinkType>
friend class ::nlohmann::detail::binary_writer; friend class ::nlohmann::detail::binary_writer;
template<typename BasicJsonType, typename InputType, typename SAX> template<typename BasicJsonType, typename InputType, typename SAX>
friend class ::nlohmann::detail::binary_reader; friend class ::nlohmann::detail::binary_reader;
@@ -4327,7 +4327,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static std::vector<std::uint8_t> to_cbor(const basic_json& j) static std::vector<std::uint8_t> to_cbor(const basic_json& j)
{ {
std::vector<std::uint8_t> result; std::vector<std::uint8_t> result;
to_cbor(j, result); result.reserve(detail::binary_reserve_hint(j));
detail::binary_writer<basic_json, std::uint8_t, detail::output_vector_sink<std::uint8_t>>(
detail::output_vector_sink<std::uint8_t>(result)).write_cbor(j);
return result; return result;
} }
@@ -4350,7 +4352,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static std::vector<std::uint8_t> to_msgpack(const basic_json& j) static std::vector<std::uint8_t> to_msgpack(const basic_json& j)
{ {
std::vector<std::uint8_t> result; std::vector<std::uint8_t> result;
to_msgpack(j, result); result.reserve(detail::binary_reserve_hint(j));
detail::binary_writer<basic_json, std::uint8_t, detail::output_vector_sink<std::uint8_t>>(
detail::output_vector_sink<std::uint8_t>(result)).write_msgpack(j);
return result; return result;
} }
@@ -4375,7 +4379,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool use_type = false) const bool use_type = false)
{ {
std::vector<std::uint8_t> result; std::vector<std::uint8_t> result;
to_ubjson(j, result, use_size, use_type); result.reserve(detail::binary_reserve_hint(j));
detail::binary_writer<basic_json, 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; return result;
} }
@@ -4403,7 +4409,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bjdata_version_t version = bjdata_version_t::draft2) const bjdata_version_t version = bjdata_version_t::draft2)
{ {
std::vector<std::uint8_t> result; std::vector<std::uint8_t> result;
to_bjdata(j, result, use_size, use_type, version); result.reserve(detail::binary_reserve_hint(j));
detail::binary_writer<basic_json, 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; return result;
} }
@@ -4430,7 +4438,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static std::vector<std::uint8_t> to_bson(const basic_json& j) static std::vector<std::uint8_t> to_bson(const basic_json& j)
{ {
std::vector<std::uint8_t> result; std::vector<std::uint8_t> result;
to_bson(j, result); result.reserve(detail::binary_reserve_hint(j));
detail::binary_writer<basic_json, std::uint8_t, detail::output_vector_sink<std::uint8_t>>(
detail::output_vector_sink<std::uint8_t>(result)).write_bson(j);
return result; return result;
} }
File diff suppressed because it is too large Load Diff