mirror of
https://github.com/nlohmann/json.git
synced 2026-08-22 09:03:18 +00:00
Read binary strings/blobs in bulk chunks with a memcpy fast path (#5233)
This commit is contained in:
@@ -203,9 +203,51 @@ class iterator_input_adapter
|
||||
out.insert(out.end(), from, to);
|
||||
}
|
||||
|
||||
// for general iterators, we cannot really do something better than falling back to processing the range one-by-one
|
||||
// Copy up to count * sizeof(T) bytes into dest, returning the number of
|
||||
// bytes actually read. For contiguous iterators (e.g. pointers) this is a
|
||||
// single std::memcpy; for general iterators we fall back to processing the
|
||||
// range one-by-one.
|
||||
template<class T>
|
||||
std::size_t get_elements(T* dest, std::size_t count = 1)
|
||||
{
|
||||
return get_elements_impl(dest, count, std::integral_constant<bool, iterator_is_contiguous> {});
|
||||
}
|
||||
|
||||
private:
|
||||
// whether IteratorType refers to a contiguous range and therefore supports
|
||||
// a std::memcpy fast path (pointers always do; in C++20 we can also detect
|
||||
// library iterators such as those of std::vector and std::string)
|
||||
static constexpr bool iterator_is_contiguous =
|
||||
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
||||
std::contiguous_iterator<IteratorType> ||
|
||||
#endif
|
||||
std::is_pointer<IteratorType>::value;
|
||||
|
||||
// contiguous fast path: bulk copy the remaining range with std::memcpy
|
||||
template<class T>
|
||||
std::size_t get_elements_impl(T* dest, std::size_t count, std::true_type /*contiguous*/)
|
||||
{
|
||||
const std::size_t wanted = count * sizeof(T);
|
||||
const std::size_t available = static_cast<std::size_t>(std::distance(current, end)) * sizeof(char_type);
|
||||
const std::size_t copied = (std::min)(wanted, available);
|
||||
if (JSON_HEDLEY_LIKELY(copied != 0))
|
||||
{
|
||||
// the copy must stay within both buffers: the caller-provided
|
||||
// destination holds `wanted` bytes and the remaining input range
|
||||
// holds `available` bytes, and `copied` is the minimum of the two
|
||||
JSON_ASSERT(copied <= wanted); // does not overrun the destination
|
||||
JSON_ASSERT(copied <= available); // does not read past the input end
|
||||
// &*current yields the raw address for both raw pointers and
|
||||
// non-pointer contiguous iterators (e.g. std::vector's iterator)
|
||||
std::memcpy(dest, &*current, copied);
|
||||
std::advance(current, static_cast<typename std::iterator_traits<IteratorType>::difference_type>(copied / sizeof(char_type)));
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
|
||||
// general fallback: copy the range one element at a time
|
||||
template<class T>
|
||||
std::size_t get_elements_impl(T* dest, std::size_t count, std::false_type /*contiguous*/)
|
||||
{
|
||||
auto* ptr = reinterpret_cast<char*>(dest);
|
||||
for (std::size_t read_index = 0; read_index < count * sizeof(T); ++read_index)
|
||||
@@ -223,7 +265,6 @@ class iterator_input_adapter
|
||||
return count * sizeof(T);
|
||||
}
|
||||
|
||||
private:
|
||||
IteratorType begin;
|
||||
IteratorType current;
|
||||
IteratorType end;
|
||||
|
||||
Reference in New Issue
Block a user