Compare commits

...
Author SHA1 Message Date
Niels Lohmann 759a847178 Fix CI: bugprone-signed-char-misuse in append_from_block
Convert the (positive) length to its unsigned type before widening it
to std::size_t, as clang-tidy flags the int8_t to std::size_t cast.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-06 12:13:09 +01:00
Niels Lohmann a33af0a51b Binary readers: keep the tag argument of append_from_block() astyle-proof
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-06 08:40:38 +01:00
Niels Lohmann baf0bee38f Binary readers: append strings straight from contiguous input
get_bytes() read every string into a zero-filled resize() and copied the
bytes over it, so each byte was written twice. A string that the input holds
in one block (bulk_remaining() at least its length) is now appended from it
directly. The claimed length is checked against the input first, so a huge
claimed length still allocates nothing it cannot fill; other inputs and
binary values keep the chunked loop.

from_cbor() and from_msgpack() of twitter.json and citm_catalog.json get
5.5-9.1% faster on an Apple M1 Max and 2.7-8.6% on x86-64 (GCC 15, Clang 21);
canada.json, which has few strings, is unchanged.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-06 06:43:54 +01:00
2 changed files with 76 additions and 0 deletions

No files matched your search

@@ -4887,6 +4887,14 @@ class binary_reader
const char* context, const char* context,
ContainerType& result) ContainerType& result)
{ {
// a string that is in the block of input as a whole is appended from
// it, without the zero-filling resize() below
constexpr bool from_block = bulk_scan && std::is_same<ContainerType, string_t>::value;
if (append_from_block(len, result, std::integral_constant<bool, from_block> {}))
{
return true;
}
// upper bound on the number of bytes read (and allocated) per chunk // upper bound on the number of bytes read (and allocated) per chunk
constexpr std::size_t chunk_size = 4096; constexpr std::size_t chunk_size = 4096;
@@ -4923,6 +4931,36 @@ class binary_reader
return true; return true;
} }
/// append the @a len bytes at the current position to @a result, if the
/// input holds them in one block; false (with nothing read) otherwise
template<typename NumberType>
bool append_from_block(const NumberType len, string_t& result, std::true_type /*block*/)
{
if (len <= 0)
{
return false;
}
// (len > 0, so the unsigned type holds it; casting a signed char
// straight to std::size_t would trip bugprone-signed-char-misuse)
const auto ulen = static_cast<typename std::make_unsigned<NumberType>::type>(len);
const std::size_t remaining = ia.bulk_remaining();
if (static_cast<std::uintmax_t>(ulen) > static_cast<std::uintmax_t>(remaining))
{
return false; // (the loop reports the premature end)
}
const auto n = static_cast<std::size_t>(ulen);
result.append(reinterpret_cast<const typename string_t::value_type*>(ia.bulk_data()), n);
ia.bulk_skip(n);
chars_read += n;
return true;
}
template<typename NumberType, typename ContainerType>
static bool append_from_block(const NumberType /*len*/, ContainerType& /*result*/, std::false_type /*block*/) noexcept
{
return false;
}
/*! /*!
@param[in] context further context information (for diagnostics) @param[in] context further context information (for diagnostics)
@return whether the last read character is not EOF @return whether the last read character is not EOF
+38
View File
@@ -22903,6 +22903,14 @@ class binary_reader
const char* context, const char* context,
ContainerType& result) ContainerType& result)
{ {
// a string that is in the block of input as a whole is appended from
// it, without the zero-filling resize() below
constexpr bool from_block = bulk_scan && std::is_same<ContainerType, string_t>::value;
if (append_from_block(len, result, std::integral_constant<bool, from_block> {}))
{
return true;
}
// upper bound on the number of bytes read (and allocated) per chunk // upper bound on the number of bytes read (and allocated) per chunk
constexpr std::size_t chunk_size = 4096; constexpr std::size_t chunk_size = 4096;
@@ -22939,6 +22947,36 @@ class binary_reader
return true; return true;
} }
/// append the @a len bytes at the current position to @a result, if the
/// input holds them in one block; false (with nothing read) otherwise
template<typename NumberType>
bool append_from_block(const NumberType len, string_t& result, std::true_type /*block*/)
{
if (len <= 0)
{
return false;
}
// (len > 0, so the unsigned type holds it; casting a signed char
// straight to std::size_t would trip bugprone-signed-char-misuse)
const auto ulen = static_cast<typename std::make_unsigned<NumberType>::type>(len);
const std::size_t remaining = ia.bulk_remaining();
if (static_cast<std::uintmax_t>(ulen) > static_cast<std::uintmax_t>(remaining))
{
return false; // (the loop reports the premature end)
}
const auto n = static_cast<std::size_t>(ulen);
result.append(reinterpret_cast<const typename string_t::value_type*>(ia.bulk_data()), n);
ia.bulk_skip(n);
chars_read += n;
return true;
}
template<typename NumberType, typename ContainerType>
static bool append_from_block(const NumberType /*len*/, ContainerType& /*result*/, std::false_type /*block*/) noexcept
{
return false;
}
/*! /*!
@param[in] context further context information (for diagnostics) @param[in] context further context information (for diagnostics)
@return whether the last read character is not EOF @return whether the last read character is not EOF