mirror of
https://github.com/nlohmann/json.git
synced 2026-07-12 13:35:13 +00:00
Extend memcpy fast path to sized sentinels (e.g. std::counted_iterator)
Extend the memcpy fast path in iterator_input_adapter to sized sentinels of a different type, not just same-type iterator pairs. std::counted_iterator paired with std::default_sentinel_t already satisfies std::contiguous_iterator and std::sized_sentinel_for, so std::ranges::distance (C++20) lets that combination reach the fast path too, instead of silently falling back to the byte-by-byte path. - iterator_is_contiguous now also allows std::sized_sentinel_for<SentinelType, IteratorType> under C++20, gated the same way as the existing std::contiguous_iterator detection. - get_elements_impl's fast path uses std::ranges::distance under C++20 (works for both same-type and sized-sentinel pairs) and falls back to std::distance pre-C++20, where SentinelType is always IteratorType. - Add a C++20-only test exercising json::parse/accept with std::counted_iterator + std::default_sentinel_t. - Document std::default_sentinel_t + std::counted_iterator as a SentinelType example across parse.md, accept.md, sax_parse.md, and the five from_*.md pages, replacing an earlier ambiguously worded bullet. Addresses review feedback: https://github.com/nlohmann/json/pull/5265#discussion_r3564237584 Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
@@ -7207,20 +7207,29 @@ class iterator_input_adapter
|
||||
// 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).
|
||||
// The fast path also requires SentinelType == IteratorType so std::distance works.
|
||||
// Computing the available element count needs either same-type iterators
|
||||
// (plain std::distance) or, in C++20, a sized sentinel (std::ranges::distance),
|
||||
// e.g. std::counted_iterator paired with std::default_sentinel_t.
|
||||
static constexpr bool iterator_is_contiguous =
|
||||
std::is_same<IteratorType, SentinelType>::value && (
|
||||
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
||||
std::contiguous_iterator<IteratorType> ||
|
||||
(std::is_same<IteratorType, SentinelType>::value || std::sized_sentinel_for<SentinelType, IteratorType>)
|
||||
&& (std::contiguous_iterator<IteratorType> || std::is_pointer<IteratorType>::value);
|
||||
#else
|
||||
std::is_same<IteratorType, SentinelType>::value && std::is_pointer<IteratorType>::value;
|
||||
#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);
|
||||
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
||||
// std::ranges::distance also supports sized sentinels of a different
|
||||
// type (e.g. std::counted_iterator + std::default_sentinel_t)
|
||||
const std::size_t available = static_cast<std::size_t>(std::ranges::distance(current, end)) * sizeof(char_type);
|
||||
#else
|
||||
const std::size_t available = static_cast<std::size_t>(std::distance(current, end)) * sizeof(char_type);
|
||||
#endif
|
||||
const std::size_t copied = (std::min)(wanted, available);
|
||||
if (JSON_HEDLEY_LIKELY(copied != 0))
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user