Encode runs of numbers in BON8 arrays without a call each
A number in a BON8 array went through write_bon8_value(), the ladder of write_bon8_integer() and one sink write per byte of its encoding (through a std::array for each). An array that starts with a number now encodes its runs of integers and floats straight into space reserved from the sink, as the other formats do. An unsigned integer above the int64 range, which BON8 cannot write, is passed to write_bon8_value(), which throws exactly as before after the same bytes. So is an integer of a number type wider than 64 bits that does not fit. Instructions per call (Linux, GCC 13, thousands), to_bon8: 20000 integers 2375 -> 1002, 20000 floats 3173 -> 1607, canada 22130 -> 15558. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
2 files changed
+350
-4
No files matched your search
@@ -4140,9 +4140,16 @@ class binary_writer
|
||||
// 0x80..0x84: array with 0..4 elements; 0x85: array ended by 0xFE
|
||||
write_bon8_marker(static_cast<std::uint8_t>(N <= 4 ? 0x80 + N : 0x85), string_open);
|
||||
|
||||
for (const auto& el : *j.m_data.m_value.array)
|
||||
if (N != 0 && j.m_data.m_value.array->front().is_number())
|
||||
{
|
||||
write_bon8_value(el, string_open, depth + 1);
|
||||
write_bon8_array_elements(*j.m_data.m_value.array, string_open, depth);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& el : *j.m_data.m_value.array)
|
||||
{
|
||||
write_bon8_value(el, string_open, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (N > 4)
|
||||
@@ -4260,6 +4267,172 @@ class binary_writer
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief encode an integer as @ref write_bon8_integer writes it
|
||||
@param[in] out where the encoding goes; at most 9 characters are written
|
||||
@return the end of the encoding
|
||||
*/
|
||||
CharType* encode_bon8_integer(CharType* out, std::int64_t value) const noexcept
|
||||
{
|
||||
if (value < (std::numeric_limits<std::int32_t>::min)() || value > (std::numeric_limits<std::int32_t>::max)())
|
||||
{
|
||||
*out++ = to_char_type(0x8D);
|
||||
return store_number(out, value, false);
|
||||
}
|
||||
if (value < -33818506 || value > 67637031)
|
||||
{
|
||||
*out++ = to_char_type(0x8C);
|
||||
return store_number(out, static_cast<std::int32_t>(value), false);
|
||||
}
|
||||
if (value <= -264075)
|
||||
{
|
||||
value = -(value + 264075);
|
||||
return put_bon8_bytes(out, 0xF0 + ((value >> 22) & 0x07), 0xC0 + ((value >> 16) & 0x3F), value >> 8, value);
|
||||
}
|
||||
if (value <= -1931)
|
||||
{
|
||||
value = -(value + 1931);
|
||||
return put_bon8_bytes(out, 0xE0 + ((value >> 14) & 0x0F), 0xC0 + ((value >> 8) & 0x3F), value);
|
||||
}
|
||||
if (value <= -11)
|
||||
{
|
||||
value = -(value + 11);
|
||||
return put_bon8_bytes(out, 0xC2 + ((value >> 6) & 0x1F), 0xC0 + (value & 0x3F));
|
||||
}
|
||||
if (value <= -1)
|
||||
{
|
||||
return put_bon8_bytes(out, 0xB8 - (value + 1));
|
||||
}
|
||||
if (value <= 39)
|
||||
{
|
||||
return put_bon8_bytes(out, 0x90 + value);
|
||||
}
|
||||
if (value <= 3879)
|
||||
{
|
||||
value -= 40;
|
||||
return put_bon8_bytes(out, 0xC2 + ((value >> 7) & 0x1F), value & 0x7F);
|
||||
}
|
||||
if (value <= 528167)
|
||||
{
|
||||
value -= 3880;
|
||||
return put_bon8_bytes(out, 0xE0 + ((value >> 15) & 0x0F), (value >> 8) & 0x7F, value);
|
||||
}
|
||||
value -= 528168;
|
||||
return put_bon8_bytes(out, 0xF0 + ((value >> 23) & 0x07), (value >> 16) & 0x7F, value >> 8, value);
|
||||
}
|
||||
|
||||
/// store the low byte of each argument at @a out; return the end
|
||||
template<typename... Bytes>
|
||||
CharType* put_bon8_bytes(CharType* out, const Bytes... bytes) const noexcept
|
||||
{
|
||||
const std::array<CharType, sizeof...(Bytes)> buffer{{to_char_type(static_cast<std::uint8_t>(bytes & 0xFF))...}};
|
||||
std::memcpy(out, buffer.data(), buffer.size());
|
||||
return out + buffer.size();
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief encode a float as @ref write_bon8_float writes it
|
||||
@param[in] out where the encoding goes; at most 9 characters are written
|
||||
@return the end of the encoding
|
||||
*/
|
||||
CharType* encode_bon8_float(CharType* out, const number_float_t n) const noexcept
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
JSON_HEDLEY_DIAGNOSTIC_PUSH
|
||||
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
|
||||
#endif
|
||||
if (n == static_cast<number_float_t>(-1))
|
||||
{
|
||||
*out++ = to_char_type(0xFB);
|
||||
return out;
|
||||
}
|
||||
if (n == static_cast<number_float_t>(0) && !std::signbit(n))
|
||||
{
|
||||
*out++ = to_char_type(0xFC);
|
||||
return out;
|
||||
}
|
||||
if (n == static_cast<number_float_t>(1))
|
||||
{
|
||||
*out++ = to_char_type(0xFD);
|
||||
return out;
|
||||
}
|
||||
if (std::isnan(n))
|
||||
{
|
||||
return put_bon8_bytes(out, 0x8E, 0x7F, 0x80, 0x00, 0x01);
|
||||
}
|
||||
return out + encode_compact_float(out, n, to_char_type(0x8E), to_char_type(0x8F));
|
||||
#ifdef __GNUC__
|
||||
JSON_HEDLEY_DIAGNOSTIC_POP
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief write the elements of an array that starts with a number, with runs
|
||||
of numbers encoded without a call of @ref write_bon8_value for each
|
||||
|
||||
Same output as calling write_bon8_value for each element. The numbers of a
|
||||
run are encoded into space reserved from the sink, as in
|
||||
@ref write_array_elements; an integer that BON8 cannot write (an unsigned
|
||||
one above the int64 range, or one of a number type wider than 64 bits) throws
|
||||
from write_bon8_value, like any other value that is not a number. Kept out
|
||||
of line, like @ref write_cbor_array.
|
||||
*/
|
||||
template<typename ArrayType>
|
||||
JSON_HEDLEY_NEVER_INLINE
|
||||
void write_bon8_array_elements(const ArrayType& array, bool& string_open, const std::size_t depth)
|
||||
{
|
||||
constexpr std::size_t max_run = max_reserve_size / 9;
|
||||
auto it = array.begin();
|
||||
const auto end = array.end();
|
||||
while (it != end)
|
||||
{
|
||||
if (!it->is_number())
|
||||
{
|
||||
write_bon8_value(*it, string_open, depth + 1);
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
CharType* const start = oa.reserve(max_run * 9);
|
||||
CharType* out = start;
|
||||
bool unencodable = false;
|
||||
for (std::size_t n = 0; n < max_run && it != end && it->is_number(); ++n)
|
||||
{
|
||||
if (it->is_number_float())
|
||||
{
|
||||
out = encode_bon8_float(out, it->m_data.m_value.number_float);
|
||||
}
|
||||
else if (JSON_HEDLEY_UNLIKELY((it->m_data.m_type == value_t::number_unsigned
|
||||
&& !integer_fits_max<std::int64_t>(it->m_data.m_value.number_unsigned))
|
||||
|| (it->m_data.m_type == value_t::number_integer
|
||||
&& !integer_fits<std::int64_t>(it->m_data.m_value.number_integer))))
|
||||
{
|
||||
unencodable = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
out = encode_bon8_integer(out, it->m_data.m_type == value_t::number_integer
|
||||
? static_cast<std::int64_t>(it->m_data.m_value.number_integer)
|
||||
: static_cast<std::int64_t>(it->m_data.m_value.number_unsigned));
|
||||
}
|
||||
++it;
|
||||
}
|
||||
oa.commit(static_cast<std::size_t>(out - start));
|
||||
if (out != start)
|
||||
{
|
||||
// a number follows a string without terminating it
|
||||
string_open = false;
|
||||
}
|
||||
|
||||
if (unencodable)
|
||||
{
|
||||
write_bon8_value(*it, string_open, depth + 1);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief write a binary value as BON8
|
||||
|
||||
|
||||
Generated
+175
-2
@@ -28968,9 +28968,16 @@ class binary_writer
|
||||
// 0x80..0x84: array with 0..4 elements; 0x85: array ended by 0xFE
|
||||
write_bon8_marker(static_cast<std::uint8_t>(N <= 4 ? 0x80 + N : 0x85), string_open);
|
||||
|
||||
for (const auto& el : *j.m_data.m_value.array)
|
||||
if (N != 0 && j.m_data.m_value.array->front().is_number())
|
||||
{
|
||||
write_bon8_value(el, string_open, depth + 1);
|
||||
write_bon8_array_elements(*j.m_data.m_value.array, string_open, depth);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& el : *j.m_data.m_value.array)
|
||||
{
|
||||
write_bon8_value(el, string_open, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (N > 4)
|
||||
@@ -29088,6 +29095,172 @@ class binary_writer
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief encode an integer as @ref write_bon8_integer writes it
|
||||
@param[in] out where the encoding goes; at most 9 characters are written
|
||||
@return the end of the encoding
|
||||
*/
|
||||
CharType* encode_bon8_integer(CharType* out, std::int64_t value) const noexcept
|
||||
{
|
||||
if (value < (std::numeric_limits<std::int32_t>::min)() || value > (std::numeric_limits<std::int32_t>::max)())
|
||||
{
|
||||
*out++ = to_char_type(0x8D);
|
||||
return store_number(out, value, false);
|
||||
}
|
||||
if (value < -33818506 || value > 67637031)
|
||||
{
|
||||
*out++ = to_char_type(0x8C);
|
||||
return store_number(out, static_cast<std::int32_t>(value), false);
|
||||
}
|
||||
if (value <= -264075)
|
||||
{
|
||||
value = -(value + 264075);
|
||||
return put_bon8_bytes(out, 0xF0 + ((value >> 22) & 0x07), 0xC0 + ((value >> 16) & 0x3F), value >> 8, value);
|
||||
}
|
||||
if (value <= -1931)
|
||||
{
|
||||
value = -(value + 1931);
|
||||
return put_bon8_bytes(out, 0xE0 + ((value >> 14) & 0x0F), 0xC0 + ((value >> 8) & 0x3F), value);
|
||||
}
|
||||
if (value <= -11)
|
||||
{
|
||||
value = -(value + 11);
|
||||
return put_bon8_bytes(out, 0xC2 + ((value >> 6) & 0x1F), 0xC0 + (value & 0x3F));
|
||||
}
|
||||
if (value <= -1)
|
||||
{
|
||||
return put_bon8_bytes(out, 0xB8 - (value + 1));
|
||||
}
|
||||
if (value <= 39)
|
||||
{
|
||||
return put_bon8_bytes(out, 0x90 + value);
|
||||
}
|
||||
if (value <= 3879)
|
||||
{
|
||||
value -= 40;
|
||||
return put_bon8_bytes(out, 0xC2 + ((value >> 7) & 0x1F), value & 0x7F);
|
||||
}
|
||||
if (value <= 528167)
|
||||
{
|
||||
value -= 3880;
|
||||
return put_bon8_bytes(out, 0xE0 + ((value >> 15) & 0x0F), (value >> 8) & 0x7F, value);
|
||||
}
|
||||
value -= 528168;
|
||||
return put_bon8_bytes(out, 0xF0 + ((value >> 23) & 0x07), (value >> 16) & 0x7F, value >> 8, value);
|
||||
}
|
||||
|
||||
/// store the low byte of each argument at @a out; return the end
|
||||
template<typename... Bytes>
|
||||
CharType* put_bon8_bytes(CharType* out, const Bytes... bytes) const noexcept
|
||||
{
|
||||
const std::array<CharType, sizeof...(Bytes)> buffer{{to_char_type(static_cast<std::uint8_t>(bytes & 0xFF))...}};
|
||||
std::memcpy(out, buffer.data(), buffer.size());
|
||||
return out + buffer.size();
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief encode a float as @ref write_bon8_float writes it
|
||||
@param[in] out where the encoding goes; at most 9 characters are written
|
||||
@return the end of the encoding
|
||||
*/
|
||||
CharType* encode_bon8_float(CharType* out, const number_float_t n) const noexcept
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
JSON_HEDLEY_DIAGNOSTIC_PUSH
|
||||
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
|
||||
#endif
|
||||
if (n == static_cast<number_float_t>(-1))
|
||||
{
|
||||
*out++ = to_char_type(0xFB);
|
||||
return out;
|
||||
}
|
||||
if (n == static_cast<number_float_t>(0) && !std::signbit(n))
|
||||
{
|
||||
*out++ = to_char_type(0xFC);
|
||||
return out;
|
||||
}
|
||||
if (n == static_cast<number_float_t>(1))
|
||||
{
|
||||
*out++ = to_char_type(0xFD);
|
||||
return out;
|
||||
}
|
||||
if (std::isnan(n))
|
||||
{
|
||||
return put_bon8_bytes(out, 0x8E, 0x7F, 0x80, 0x00, 0x01);
|
||||
}
|
||||
return out + encode_compact_float(out, n, to_char_type(0x8E), to_char_type(0x8F));
|
||||
#ifdef __GNUC__
|
||||
JSON_HEDLEY_DIAGNOSTIC_POP
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief write the elements of an array that starts with a number, with runs
|
||||
of numbers encoded without a call of @ref write_bon8_value for each
|
||||
|
||||
Same output as calling write_bon8_value for each element. The numbers of a
|
||||
run are encoded into space reserved from the sink, as in
|
||||
@ref write_array_elements; an integer that BON8 cannot write (an unsigned
|
||||
one above the int64 range, or one of a number type wider than 64 bits) throws
|
||||
from write_bon8_value, like any other value that is not a number. Kept out
|
||||
of line, like @ref write_cbor_array.
|
||||
*/
|
||||
template<typename ArrayType>
|
||||
JSON_HEDLEY_NEVER_INLINE
|
||||
void write_bon8_array_elements(const ArrayType& array, bool& string_open, const std::size_t depth)
|
||||
{
|
||||
constexpr std::size_t max_run = max_reserve_size / 9;
|
||||
auto it = array.begin();
|
||||
const auto end = array.end();
|
||||
while (it != end)
|
||||
{
|
||||
if (!it->is_number())
|
||||
{
|
||||
write_bon8_value(*it, string_open, depth + 1);
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
CharType* const start = oa.reserve(max_run * 9);
|
||||
CharType* out = start;
|
||||
bool unencodable = false;
|
||||
for (std::size_t n = 0; n < max_run && it != end && it->is_number(); ++n)
|
||||
{
|
||||
if (it->is_number_float())
|
||||
{
|
||||
out = encode_bon8_float(out, it->m_data.m_value.number_float);
|
||||
}
|
||||
else if (JSON_HEDLEY_UNLIKELY((it->m_data.m_type == value_t::number_unsigned
|
||||
&& !integer_fits_max<std::int64_t>(it->m_data.m_value.number_unsigned))
|
||||
|| (it->m_data.m_type == value_t::number_integer
|
||||
&& !integer_fits<std::int64_t>(it->m_data.m_value.number_integer))))
|
||||
{
|
||||
unencodable = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
out = encode_bon8_integer(out, it->m_data.m_type == value_t::number_integer
|
||||
? static_cast<std::int64_t>(it->m_data.m_value.number_integer)
|
||||
: static_cast<std::int64_t>(it->m_data.m_value.number_unsigned));
|
||||
}
|
||||
++it;
|
||||
}
|
||||
oa.commit(static_cast<std::size_t>(out - start));
|
||||
if (out != start)
|
||||
{
|
||||
// a number follows a string without terminating it
|
||||
string_open = false;
|
||||
}
|
||||
|
||||
if (unencodable)
|
||||
{
|
||||
write_bon8_value(*it, string_open, depth + 1);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief write a binary value as BON8
|
||||
|
||||
|
||||
Reference in new issue
Block a user