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(); } };