From 16de05b9302706a2e415f135a3ab0260ab08d990 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 21 Oct 2026 21:32:53 +0200 Subject: [PATCH] Copy small binary reads straight from the streambuf get area The binary readers read every number and string with get_elements(), which called the virtual std::streambuf::sgetn() each time. When the requested bytes are in the get area, input_stream_adapter now copies them (and moves the get pointer) inline, which is what sgetn() does for them anyway. Instructions relative to the previous commit, from an ifstream (macOS, 5 parses): CBOR CITM 235M -> 226M, twitter 156M -> 151M, canada 495M -> 463M; MessagePack CITM 232M -> 222M; UBJSON CITM 258M -> 241M, canada 638M -> 623M. Linux Ir (GCC / Clang): CBOR twitter 34.0M / 41.9M -> 32.2M / 40.0M, CBOR CITM 51.4M / 58.2M -> 47.7M / 54.6M, MessagePack CITM 50.5M / 57.0M -> 46.8M / 53.4M, UBJSON CITM 56.6M / 64.1M -> 50.7M / 57.9M. Against develop the three binary readers are 5-12 % cheaper in instructions with this series. Signed-off-by: Niels Lohmann --- include/nlohmann/detail/input/input_adapters.hpp | 12 ++++++++++++ single_include/nlohmann/json.hpp | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/include/nlohmann/detail/input/input_adapters.hpp b/include/nlohmann/detail/input/input_adapters.hpp index f9a79e42d..9b3d2d7f4 100644 --- a/include/nlohmann/detail/input/input_adapters.hpp +++ b/include/nlohmann/detail/input/input_adapters.hpp @@ -313,6 +313,18 @@ class input_stream_adapter { #if JSON_PRECISE_STREAM_POSITION commit_lookahead(); +#endif +#if !JSON_DISABLE_STREAM_BULK_SCAN + // the elements are in the get area: copy them, as sgetn() does, but + // without the virtual call + const char* const first = streambuf_access::get_ptr(sb); + const auto available = static_cast(streambuf_access::get_end(sb) - first); + if (JSON_HEDLEY_LIKELY(count * sizeof(T) <= available && count * sizeof(T) <= static_cast((std::numeric_limits::max)()))) + { + std::memcpy(dest, first, count * sizeof(T)); + streambuf_access::bump(sb, static_cast(count * sizeof(T))); + return count * sizeof(T); + } #endif auto res = static_cast(sb->sgetn(reinterpret_cast(dest), static_cast(count * sizeof(T)))); if (JSON_HEDLEY_UNLIKELY(res < count * sizeof(T))) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 4c466ee98..ff1c3a674 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -8778,6 +8778,18 @@ class input_stream_adapter { #if JSON_PRECISE_STREAM_POSITION commit_lookahead(); +#endif +#if !JSON_DISABLE_STREAM_BULK_SCAN + // the elements are in the get area: copy them, as sgetn() does, but + // without the virtual call + const char* const first = streambuf_access::get_ptr(sb); + const auto available = static_cast(streambuf_access::get_end(sb) - first); + if (JSON_HEDLEY_LIKELY(count * sizeof(T) <= available && count * sizeof(T) <= static_cast((std::numeric_limits::max)()))) + { + std::memcpy(dest, first, count * sizeof(T)); + streambuf_access::bump(sb, static_cast(count * sizeof(T))); + return count * sizeof(T); + } #endif auto res = static_cast(sb->sgetn(reinterpret_cast(dest), static_cast(count * sizeof(T)))); if (JSON_HEDLEY_UNLIKELY(res < count * sizeof(T)))