From 282ff6ce5ce8754e0d9262e6b09d7e5864eaacf8 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 4 Jul 2026 08:57:45 +0200 Subject: [PATCH] Fix GCC narrowing warning and address review feedback - get_bytes(): replace `len -= static_cast(wanted)` with an explicit `len = static_cast(len - ...)`. The compound assignment promoted the operands to int and narrowed back, which GCC rejects under -Werror=arith-conversion when NumberType is narrower than int (e.g. UBJSON's int16 length). This also fixes the CodeQL build. - get_bytes(): add JSON_ASSERT(read == wanted) documenting that get_elements() never returns more than requested (per review). - iterator_input_adapter: de-duplicate the iterator_is_contiguous definition with the #if inside the initializer (per review). - Add a comment explaining why the memcpy source is &*current (needed for non-pointer contiguous iterators), and drop the redundant parentheses. - Run astyle on the new unit test so the amalgamation/format check passes. Signed-off-by: Niels Lohmann Co-Authored-By: Claude Opus 4.8 --- include/nlohmann/detail/input/binary_reader.hpp | 7 +++++-- .../nlohmann/detail/input/input_adapters.hpp | 10 ++++++---- single_include/nlohmann/json.hpp | 17 +++++++++++------ tests/src/unit-cbor.cpp | 8 ++++---- 4 files changed, 26 insertions(+), 16 deletions(-) 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);