diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp index f57868b9f..9d07ecebb 100644 --- a/include/nlohmann/detail/input/binary_reader.hpp +++ b/include/nlohmann/detail/input/binary_reader.hpp @@ -2975,13 +2975,16 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(read < wanted)) { // premature end of input: shrink to what was actually read and - // report the failure at the first missing byte + // report the failure at the first missing byte (same position + // accounting as get_to() for partial number reads) result.resize(old_size + read); ++chars_read; current = char_traits::eof(); return unexpect_eof(format, context); } - len -= static_cast(wanted); + // a full chunk was read; get_elements() never returns more than requested + JSON_ASSERT(read == wanted); + len = static_cast(len - static_cast(wanted)); } return true; } diff --git a/include/nlohmann/detail/input/input_adapters.hpp b/include/nlohmann/detail/input/input_adapters.hpp index 161adf4da..090f106de 100644 --- a/include/nlohmann/detail/input/input_adapters.hpp +++ b/include/nlohmann/detail/input/input_adapters.hpp @@ -191,11 +191,11 @@ 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) + static constexpr bool iterator_is_contiguous = #if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20) - static constexpr bool iterator_is_contiguous = std::is_pointer::value || std::contiguous_iterator; -#else - static constexpr bool iterator_is_contiguous = std::is_pointer::value; + std::contiguous_iterator || #endif + std::is_pointer::value; // contiguous fast path: bulk copy the remaining range with std::memcpy template @@ -206,7 +206,9 @@ class iterator_input_adapter const std::size_t copied = (std::min)(wanted, available); if (JSON_HEDLEY_LIKELY(copied != 0)) { - std::memcpy(dest, &(*current), copied); + // &*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::difference_type>(copied / sizeof(char_type))); } return copied; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 75d9a1aec..6a7b82224 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -7023,11 +7023,11 @@ 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) + static constexpr bool iterator_is_contiguous = #if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20) - static constexpr bool iterator_is_contiguous = std::is_pointer::value || std::contiguous_iterator; -#else - static constexpr bool iterator_is_contiguous = std::is_pointer::value; + std::contiguous_iterator || #endif + std::is_pointer::value; // contiguous fast path: bulk copy the remaining range with std::memcpy template @@ -7038,7 +7038,9 @@ class iterator_input_adapter const std::size_t copied = (std::min)(wanted, available); if (JSON_HEDLEY_LIKELY(copied != 0)) { - std::memcpy(dest, &(*current), copied); + // &*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::difference_type>(copied / sizeof(char_type))); } return copied; @@ -13174,13 +13176,16 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(read < wanted)) { // premature end of input: shrink to what was actually read and - // report the failure at the first missing byte + // report the failure at the first missing byte (same position + // accounting as get_to() for partial number reads) result.resize(old_size + read); ++chars_read; current = char_traits::eof(); return unexpect_eof(format, context); } - len -= static_cast(wanted); + // a full chunk was read; get_elements() never returns more than requested + JSON_ASSERT(read == wanted); + len = static_cast(len - static_cast(wanted)); } return true; } diff --git a/tests/src/unit-cbor.cpp b/tests/src/unit-cbor.cpp index 8b1ae46ab..52c808027 100644 --- a/tests/src/unit-cbor.cpp +++ b/tests/src/unit-cbor.cpp @@ -2785,10 +2785,10 @@ TEST_CASE("CBOR large strings and binaries (chunked reader)") // sure roundtripping is correct for lengths around and beyond the internal // chunk size (4096 bytes), for both vector (iterator) and pointer inputs. for (const std::size_t len : - { - std::size_t{0}, std::size_t{1}, std::size_t{4095}, std::size_t{4096}, - std::size_t{4097}, std::size_t{8192}, std::size_t{100000} - }) + { + std::size_t{0}, std::size_t{1}, std::size_t{4095}, std::size_t{4096}, + std::size_t{4097}, std::size_t{8192}, std::size_t{100000} + }) { CAPTURE(len);