mirror of
https://github.com/nlohmann/json.git
synced 2026-11-10 22:31:02 +00:00
Read iterator pairs of std::string and std::vector as pointer ranges (#6029)
* Read iterator pairs of std::string and std::vector as pointer ranges Before C++20, a pair of iterators of a contiguous container cannot be told from any other random-access iterator, so json::parse(s.begin(), s.end()) on a std::string or std::vector<char> was read one character at a time, without the bulk scans of strings, numbers and indentation that the container itself gets (the container overload of input_adapter() already maps it to a pointer range). The iterators of std::string and of a std::vector of 1-byte integral elements are now matched exactly and mapped to the pointer range they cover; an empty range is not dereferenced. The character type stays the same, so the results and error positions do not change. parse(first, last) of a std::string gets 1.7-2.0x as fast in C++17 on an Apple M1 Max, accept(first, last) 2.7-3.1x, and from_cbor(first, last) of a std::vector 8-23% faster. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix CI: clang-tidy, unused result, Infer false positive in tests - unit-deserialization.cpp: raw string literals, and assign the result of parse() in CHECK_THROWS_AS (-Wunused-result) - unit-bson.cpp: mark Infer's NULLPTR_DEREFERENCE on from_bson() with an iterator pair as a false positive Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix implicit char conversion in iterator-pair test The test built std::vector<unsigned char> and <signed char> from a char range, which -fsanitize=integer reports as an implicit conversion. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Tests: sweep_allocator::construct() accepts pointers to const Recent libc++ (emsdk latest) probes construct(const T*, const T&) in an unevaluated context while checking for trivial copies; the static_cast to void* in the noexcept specification then fails to compile. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N5NasEsrzHhSNouxSoEwqz Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Tests: guard sweep_allocator::construct() against const types instead of const_cast The const_cast added in the previous commit is rejected by cppcoreguidelines-pro-type-const-cast (clang-tidy). Make construct() not viable for const U instead, which is what libc++ probes for. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N5NasEsrzHhSNouxSoEwqz Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix CI: astyle and clang-tidy for sweep_allocator::construct() astyle 3.4.13 reformats the enable_if<!...> default template argument (Check amalgamation). Delete the const overload instead, which makes the libc++ probe for const objects fail in the SFINAE context just the same and needs no <type_traits>. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N5NasEsrzHhSNouxSoEwqz Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Iterator ranges: astyle-safe trait members; reserve in the test helper The byte check and the container match are static constexpr members instead of integral_constant arguments and a default template argument with comparisons, which astyle spaced out. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2a4aec3e69
commit
8d591bf720
4 files changed
+193
No files matched your search
@@ -1348,6 +1348,64 @@ struct iterator_input_adapter_factory<IteratorType, SentinelType, enable_if_t<is
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
@brief whether @a IteratorType is an iterator of std::string or of a
|
||||
std::vector of 1-byte elements
|
||||
|
||||
Their elements are contiguous, but before C++20 that cannot be detected from
|
||||
the iterator type (std::contiguous_iterator), so an iterator pair of them would
|
||||
be read one character at a time through the iterators. They are read as the
|
||||
pointer range they are instead, as the containers themselves are (see the
|
||||
container overload of input_adapter()). The types are matched exactly, so no
|
||||
other iterator can be mistaken for one. (std::basic_string is only matched for
|
||||
char: other character types need std::char_traits specializations that the
|
||||
standard library need not have.)
|
||||
*/
|
||||
template<typename CharType>
|
||||
struct is_byte_char_type
|
||||
{
|
||||
// a member, not an integral_constant argument, which astyle misformats
|
||||
static constexpr bool value = sizeof(CharType) == 1 && std::is_integral<CharType>::value && !std::is_same<CharType, bool>::value;
|
||||
};
|
||||
|
||||
template<typename IteratorType, typename CharType, bool IsByte = is_byte_char_type<CharType>::value>
|
||||
struct is_std_contiguous_byte_iterator_impl : std::false_type {};
|
||||
|
||||
// (the containers are only named for byte types)
|
||||
template<typename IteratorType, typename CharType>
|
||||
struct is_std_contiguous_byte_iterator_impl<IteratorType, CharType, true>
|
||||
{
|
||||
static constexpr bool value = !std::is_pointer<IteratorType>::value
|
||||
&& (std::is_same<IteratorType, typename std::vector<CharType>::iterator>::value
|
||||
|| std::is_same<IteratorType, typename std::vector<CharType>::const_iterator>::value
|
||||
|| std::is_same<IteratorType, std::string::iterator>::value
|
||||
|| std::is_same<IteratorType, std::string::const_iterator>::value);
|
||||
};
|
||||
|
||||
template<typename IteratorType>
|
||||
struct is_std_contiguous_byte_iterator
|
||||
: is_std_contiguous_byte_iterator_impl<IteratorType, typename std::remove_cv<typename std::iterator_traits<IteratorType>::value_type>::type> {};
|
||||
|
||||
template<typename IteratorType>
|
||||
struct iterator_input_adapter_factory<IteratorType, IteratorType, enable_if_t<is_std_contiguous_byte_iterator<IteratorType>::value>>
|
||||
{
|
||||
using iterator_type = IteratorType;
|
||||
using sentinel_type = IteratorType;
|
||||
using char_type = typename std::remove_cv<typename std::iterator_traits<iterator_type>::value_type>::type;
|
||||
using adapter_type = iterator_input_adapter<const char_type*>;
|
||||
|
||||
static adapter_type create(IteratorType first, IteratorType last)
|
||||
{
|
||||
if (first == last)
|
||||
{
|
||||
// (an empty range must not be dereferenced)
|
||||
return adapter_type(nullptr, nullptr);
|
||||
}
|
||||
const char_type* const begin = std::addressof(*first);
|
||||
return adapter_type(begin, begin + (last - first));
|
||||
}
|
||||
};
|
||||
|
||||
// General purpose iterator-based input (iterator+sentinel pair; SentinelType
|
||||
// defaults to IteratorType for the common same-type case, but may differ for
|
||||
// C++20 ranges-style iterator+sentinel pairs). Only enable for types that can
|
||||
|
||||
Reference in new issue
Block a user