mirror of
https://github.com/nlohmann/json.git
synced 2026-07-07 19:15:10 +00:00
Read binary strings/blobs in bulk chunks with a memcpy fast path (#5233)
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
#include <array> // array
|
||||
#include <cmath> // ldexp
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
|
||||
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t, uintmax_t
|
||||
#include <cstdio> // snprintf
|
||||
#include <cstring> // memcpy
|
||||
#include <iterator> // back_inserter
|
||||
@@ -2908,18 +2908,7 @@ class binary_reader
|
||||
const NumberType len,
|
||||
string_t& result)
|
||||
{
|
||||
bool success = true;
|
||||
for (NumberType i = 0; i < len; i++)
|
||||
{
|
||||
get();
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "string")))
|
||||
{
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
result.push_back(static_cast<typename string_t::value_type>(current));
|
||||
}
|
||||
return success;
|
||||
return get_bytes(format, len, "string", result);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -2941,18 +2930,66 @@ class binary_reader
|
||||
const NumberType len,
|
||||
binary_t& result)
|
||||
{
|
||||
bool success = true;
|
||||
for (NumberType i = 0; i < len; i++)
|
||||
return get_bytes(format, len, "binary", result);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief read @a len bytes from the input into a string or byte container
|
||||
|
||||
@tparam NumberType the type of the length
|
||||
@tparam ContainerType the destination container (string_t or binary_t)
|
||||
@param[in] format the current format (for diagnostics)
|
||||
@param[in] len number of bytes to read
|
||||
@param[in] context further context information (for diagnostics)
|
||||
@param[out] result container the bytes are appended to
|
||||
|
||||
@return whether reading completed
|
||||
|
||||
@note We cannot reserve @a len bytes for the result up front, because
|
||||
@a len may be far larger than the actual input. Instead we read in
|
||||
bounded chunks, so the peak allocation is capped regardless of the
|
||||
claimed length while the per-byte loop is replaced by block copies
|
||||
(a std::memcpy for contiguous inputs). @ref unexpect_eof() still
|
||||
detects a premature end of input.
|
||||
*/
|
||||
template<typename NumberType, typename ContainerType>
|
||||
bool get_bytes(const input_format_t format,
|
||||
NumberType len,
|
||||
const char* context,
|
||||
ContainerType& result)
|
||||
{
|
||||
// upper bound on the number of bytes read (and allocated) per chunk
|
||||
constexpr std::size_t chunk_size = 4096;
|
||||
|
||||
while (len > 0)
|
||||
{
|
||||
get();
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "binary")))
|
||||
// number of bytes to read this iteration: min(chunk_size, len),
|
||||
// computed without truncating chunk_size to a narrow NumberType
|
||||
const std::size_t wanted = (static_cast<std::uintmax_t>(len) < static_cast<std::uintmax_t>(chunk_size))
|
||||
? static_cast<std::size_t>(len)
|
||||
: chunk_size;
|
||||
const std::size_t old_size = result.size();
|
||||
result.resize(old_size + wanted);
|
||||
// resize() is required to make size() exactly old_size + wanted;
|
||||
// that is the room get_elements() is allowed to write into
|
||||
JSON_ASSERT(result.size() == old_size + wanted);
|
||||
const std::size_t bytes_read = ia.get_elements(&result[old_size], wanted);
|
||||
chars_read += bytes_read;
|
||||
if (JSON_HEDLEY_UNLIKELY(bytes_read < wanted))
|
||||
{
|
||||
success = false;
|
||||
break;
|
||||
// premature end of input: shrink to what was actually read and
|
||||
// report the failure at the first missing byte (same position
|
||||
// accounting as get_to() for partial number reads)
|
||||
result.resize(old_size + bytes_read);
|
||||
++chars_read;
|
||||
current = char_traits<char_type>::eof();
|
||||
return unexpect_eof(format, context);
|
||||
}
|
||||
result.push_back(static_cast<typename binary_t::value_type>(current));
|
||||
// a full chunk was read; get_elements() never returns more than requested
|
||||
JSON_ASSERT(bytes_read == wanted);
|
||||
len = static_cast<NumberType>(len - static_cast<NumberType>(wanted));
|
||||
}
|
||||
return success;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
Reference in New Issue
Block a user