mirror of
https://github.com/nlohmann/json.git
synced 2026-07-21 19:23:03 +04:00
Extend memcpy fast path to sized sentinels (e.g. std::counted_iterator) (#5268)
This commit is contained in:
@@ -49,7 +49,7 @@ Unlike the [`parse()`](parse.md) function, this function neither throws an excep
|
|||||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||||
|
|
||||||
- a custom sentinel type for C++20 ranges
|
- a custom sentinel type for C++20 ranges
|
||||||
- `std::counted_iterator` with a different sentinel type
|
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
|
|||||||
@@ -36,8 +36,10 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
: a compatible iterator type
|
: a compatible iterator type
|
||||||
|
|
||||||
`SentinelType`
|
`SentinelType`
|
||||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance a
|
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||||
custom sentinel type for C++20 ranges
|
|
||||||
|
- a custom sentinel type for C++20 ranges
|
||||||
|
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
|
|||||||
@@ -36,8 +36,10 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
: a compatible iterator type
|
: a compatible iterator type
|
||||||
|
|
||||||
`SentinelType`
|
`SentinelType`
|
||||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance a
|
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||||
custom sentinel type for C++20 ranges
|
|
||||||
|
- a custom sentinel type for C++20 ranges
|
||||||
|
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
|
|||||||
@@ -39,8 +39,10 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
: a compatible iterator type
|
: a compatible iterator type
|
||||||
|
|
||||||
`SentinelType`
|
`SentinelType`
|
||||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance a
|
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||||
custom sentinel type for C++20 ranges
|
|
||||||
|
- a custom sentinel type for C++20 ranges
|
||||||
|
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
|
|||||||
@@ -36,8 +36,10 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
: a compatible iterator type
|
: a compatible iterator type
|
||||||
|
|
||||||
`SentinelType`
|
`SentinelType`
|
||||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance a
|
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||||
custom sentinel type for C++20 ranges
|
|
||||||
|
- a custom sentinel type for C++20 ranges
|
||||||
|
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
|
|||||||
@@ -36,8 +36,10 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
: a compatible iterator type
|
: a compatible iterator type
|
||||||
|
|
||||||
`SentinelType`
|
`SentinelType`
|
||||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance a
|
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||||
custom sentinel type for C++20 ranges
|
|
||||||
|
- a custom sentinel type for C++20 ranges
|
||||||
|
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ static basic_json parse(IteratorType first, SentinelType last,
|
|||||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||||
|
|
||||||
- a custom sentinel type for C++20 ranges
|
- a custom sentinel type for C++20 ranges
|
||||||
- `std::counted_iterator` with a different sentinel type
|
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,10 @@ The SAX event lister must follow the interface of [`json_sax`](../json_sax/index
|
|||||||
with a size of 1, 2, or 4 bytes (interpreted respectively as UTF-8, UTF-16, and UTF-32)
|
with a size of 1, 2, or 4 bytes (interpreted respectively as UTF-8, UTF-16, and UTF-32)
|
||||||
|
|
||||||
`SentinelType`
|
`SentinelType`
|
||||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for overload (2)
|
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for overload (2), for instance.
|
||||||
|
|
||||||
|
- a custom sentinel type for C++20 ranges
|
||||||
|
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||||
|
|
||||||
`SAX`
|
`SAX`
|
||||||
: a class fulfilling the SAX event listener interface; see [`json_sax`](../json_sax/index.md)
|
: a class fulfilling the SAX event listener interface; see [`json_sax`](../json_sax/index.md)
|
||||||
|
|||||||
@@ -220,20 +220,29 @@ class iterator_input_adapter
|
|||||||
// whether IteratorType refers to a contiguous range and therefore supports
|
// 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
|
// 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).
|
// 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 =
|
static constexpr bool iterator_is_contiguous =
|
||||||
std::is_same<IteratorType, SentinelType>::value && (
|
|
||||||
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
#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
|
#endif
|
||||||
std::is_pointer<IteratorType>::value);
|
|
||||||
|
|
||||||
// contiguous fast path: bulk copy the remaining range with std::memcpy
|
// contiguous fast path: bulk copy the remaining range with std::memcpy
|
||||||
template<class T>
|
template<class T>
|
||||||
std::size_t get_elements_impl(T* dest, std::size_t count, std::true_type /*contiguous*/)
|
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 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);
|
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);
|
const std::size_t copied = (std::min)(wanted, available);
|
||||||
if (JSON_HEDLEY_LIKELY(copied != 0))
|
if (JSON_HEDLEY_LIKELY(copied != 0))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7207,20 +7207,29 @@ class iterator_input_adapter
|
|||||||
// whether IteratorType refers to a contiguous range and therefore supports
|
// 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
|
// 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).
|
// 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 =
|
static constexpr bool iterator_is_contiguous =
|
||||||
std::is_same<IteratorType, SentinelType>::value && (
|
|
||||||
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
#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
|
#endif
|
||||||
std::is_pointer<IteratorType>::value);
|
|
||||||
|
|
||||||
// contiguous fast path: bulk copy the remaining range with std::memcpy
|
// contiguous fast path: bulk copy the remaining range with std::memcpy
|
||||||
template<class T>
|
template<class T>
|
||||||
std::size_t get_elements_impl(T* dest, std::size_t count, std::true_type /*contiguous*/)
|
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 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);
|
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);
|
const std::size_t copied = (std::min)(wanted, available);
|
||||||
if (JSON_HEDLEY_LIKELY(copied != 0))
|
if (JSON_HEDLEY_LIKELY(copied != 0))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,6 +6,13 @@
|
|||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// cmake/test.cmake selects the C++ standard versions with which to build a
|
||||||
|
// unit test based on the presence of JSON_HAS_CPP_<VERSION> macros.
|
||||||
|
// When using macros that are only defined for particular versions of the standard
|
||||||
|
// (e.g., JSON_HAS_FILESYSTEM for C++17 and up), please mention the corresponding
|
||||||
|
// version macro in a comment close by, like this:
|
||||||
|
// JSON_HAS_CPP_<VERSION> (do not remove; see note at top of file)
|
||||||
|
|
||||||
#include "doctest_compatibility.h"
|
#include "doctest_compatibility.h"
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
@@ -13,6 +20,10 @@ using nlohmann::json;
|
|||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
|
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
||||||
|
#include <iterator>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
TEST_CASE("Use arbitrary stdlib container")
|
TEST_CASE("Use arbitrary stdlib container")
|
||||||
@@ -201,4 +212,22 @@ TEST_CASE("Parse with heterogeneous iterator and sentinel types")
|
|||||||
CHECK(j2.at(0) == 1);
|
CHECK(j2.at(0) == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
||||||
|
// JSON_HAS_CPP_20 (do not remove; see note at top of file)
|
||||||
|
TEST_CASE("Parse with std::counted_iterator and std::default_sentinel_t")
|
||||||
|
{
|
||||||
|
using iterator_type = std::string::const_iterator;
|
||||||
|
const std::string json_str = R"({"key":"value","array":[1,2,3]})";
|
||||||
|
const auto len = static_cast<std::iter_difference_t<iterator_type>>(json_str.size());
|
||||||
|
|
||||||
|
const std::counted_iterator<iterator_type> first(json_str.begin(), len);
|
||||||
|
const json j = json::parse(first, std::default_sentinel);
|
||||||
|
CHECK(j["key"] == "value");
|
||||||
|
CHECK(j["array"].size() == 3);
|
||||||
|
|
||||||
|
const std::counted_iterator<iterator_type> first2(json_str.begin(), len);
|
||||||
|
CHECK(json::accept(first2, std::default_sentinel));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
Reference in New Issue
Block a user