From 7b0e8641654c40452eeb9952bf4b5caad02617b6 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Sat, 29 Nov 2025 18:34:35 +0000 Subject: [PATCH] Add read_from_single_buffer helper for ReadSource implementations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This helper function assists models of the ReadSource concept by handling iteration over a mutable buffer sequence and invoking a user-provided function for each buffer. The function integrates the results and properly handles error::eof. The implementation follows the pattern used in any_read_source::read() and provides special EOF handling: when EOF occurs after reading some bytes, it returns success with the byte count instead of propagating the EOF error. Fixes #59 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Vinnie Falco --- include/boost/buffers/read_source.hpp | 81 ++++++++++++++++++ test/unit/read_source.cpp | 119 ++++++++++++++++++++++++++ 2 files changed, 200 insertions(+) diff --git a/include/boost/buffers/read_source.hpp b/include/boost/buffers/read_source.hpp index 694fb72..1a7f93f 100644 --- a/include/boost/buffers/read_source.hpp +++ b/include/boost/buffers/read_source.hpp @@ -95,6 +95,87 @@ struct has_rewind +std::size_t +read_from_single_buffer( + MutableBufferSequence const& buffers, + system::error_code& ec, + Function f) +{ + std::size_t total = 0; + auto it = buffers::begin(buffers); + auto const end_ = buffers::end(buffers); + + while(it != end_) + { + mutable_buffer mb = *it; + if(mb.size() == 0) + { + ++it; + continue; + } + + system::error_code ec_tmp; + auto const nread = f(mb, ec_tmp); + total += nread; + + if(ec_tmp.failed()) + { + if(ec_tmp == error::eof && total > 0) + { + // EOF after reading some bytes - return success + ec = {}; + return total; + } + // Propagate the error + ec = ec_tmp; + return total; + } + + // Successfully read from this buffer + ++it; + } + + ec = {}; + return total; +} + } // buffers } // boost diff --git a/test/unit/read_source.cpp b/test/unit/read_source.cpp index 694752b..b20f3ec 100644 --- a/test/unit/read_source.cpp +++ b/test/unit/read_source.cpp @@ -15,6 +15,9 @@ #include #include +#include + +#include #include "test_suite.hpp" @@ -139,8 +142,124 @@ BOOST_CORE_STATIC_ASSERT( has_rewind::value); struct read_source_test { + void + testReadFromSingleBuffer() + { + // Test basic functionality + { + core::string_view s = "Hello, World!"; + std::size_t pos = 0; + + auto read_fn = [&](mutable_buffer mb, system::error_code& ec) -> std::size_t + { + if(pos >= s.size()) + { + ec = error::eof; + return 0; + } + auto const n = (std::min)(mb.size(), s.size() - pos); + std::memcpy(mb.data(), s.data() + pos, n); + pos += n; + if(pos >= s.size()) + ec = error::eof; + return n; + }; + + char buf[20]; + mutable_buffer mb(buf, sizeof(buf)); + system::error_code ec; + auto const nread = read_from_single_buffer(mb, ec, read_fn); + + BOOST_TEST(nread == s.size()); + BOOST_TEST(! ec.failed()); + BOOST_TEST(core::string_view(buf, nread) == s); + } + + // Test EOF on first call + { + auto read_fn = [](mutable_buffer, system::error_code& ec) -> std::size_t + { + ec = error::eof; + return 0; + }; + + char buf[10]; + mutable_buffer mb(buf, sizeof(buf)); + system::error_code ec; + auto const nread = read_from_single_buffer(mb, ec, read_fn); + + BOOST_TEST(nread == 0); + BOOST_TEST(ec == error::eof); + } + + // Test EOF after partial read + { + core::string_view s = "Hi"; + std::size_t call_count = 0; + + auto read_fn = [&](mutable_buffer mb, system::error_code& ec) -> std::size_t + { + ++call_count; + if(call_count == 1) + { + // First call: read 2 bytes + auto const n = (std::min)(mb.size(), s.size()); + std::memcpy(mb.data(), s.data(), n); + return n; + } + // Second call: EOF + ec = error::eof; + return 0; + }; + + char buf1[5]; + char buf2[5]; + mutable_buffer bufs[2] = { mutable_buffer(buf1, 5), mutable_buffer(buf2, 5) }; + system::error_code ec; + auto const nread = read_from_single_buffer( + span(bufs, 2), ec, read_fn); + + BOOST_TEST(nread == 2); + BOOST_TEST(! ec.failed()); + } + + // Test empty buffers + { + auto read_fn = [](mutable_buffer mb, system::error_code&) -> std::size_t + { + return mb.size(); + }; + + mutable_buffer empty_buf(nullptr, 0); + system::error_code ec; + auto const nread = read_from_single_buffer(empty_buf, ec, read_fn); + + BOOST_TEST(nread == 0); + BOOST_TEST(! ec.failed()); + } + + // Test error propagation + { + auto read_fn = [](mutable_buffer, system::error_code& ec) -> std::size_t + { + ec = system::error_code(22, system::generic_category()); + return 0; + }; + + char buf[10]; + mutable_buffer mb(buf, sizeof(buf)); + system::error_code ec; + auto const nread = read_from_single_buffer(mb, ec, read_fn); + + BOOST_TEST(nread == 0); + BOOST_TEST(ec.failed()); + BOOST_TEST(ec.value() == 22); + } + } + void run() { + testReadFromSingleBuffer(); } };