diff --git a/src/input/AsyncInputStream.cxx b/src/input/AsyncInputStream.cxx index 10408fbf81..9a3bd5bafe 100644 --- a/src/input/AsyncInputStream.cxx +++ b/src/input/AsyncInputStream.cxx @@ -162,14 +162,10 @@ AsyncInputStream::IsAvailable() const noexcept inline std::size_t AsyncInputStream::ReadFromBuffer(std::span dest) noexcept { - const auto r = buffer.Read(); - if (r.empty()) + const size_t nbytes = buffer.MoveTo(dest); + if (nbytes == 0) return 0; - const size_t nbytes = std::min(dest.size(), r.size()); - memcpy(dest.data(), r.data(), nbytes); - buffer.Consume(nbytes); - if (buffer.empty()) /* when the buffer becomes empty, reset its head and tail so the next write can fill the whole buffer diff --git a/src/input/ThreadInputStream.cxx b/src/input/ThreadInputStream.cxx index eedcdd183a..bb3b0c82e0 100644 --- a/src/input/ThreadInputStream.cxx +++ b/src/input/ThreadInputStream.cxx @@ -152,14 +152,10 @@ ThreadInputStream::Seek([[maybe_unused]] std::unique_lock &lock, inline std::size_t ThreadInputStream::ReadFromBuffer(std::span dest) noexcept { - const auto r = buffer.Read(); - if (r.empty()) + const size_t nbytes = buffer.MoveTo(dest); + if (nbytes == 0) return 0; - const size_t nbytes = std::min(dest.size(), r.size()); - memcpy(dest.data(), r.data(), nbytes); - buffer.Consume(nbytes); - if (buffer.empty()) /* when the buffer becomes empty, reset its head and tail so the next write can fill the whole buffer diff --git a/src/util/CircularBuffer.hxx b/src/util/CircularBuffer.hxx index e8c1a9a155..2d549c0ca1 100644 --- a/src/util/CircularBuffer.hxx +++ b/src/util/CircularBuffer.hxx @@ -3,6 +3,7 @@ #pragma once +#include // for std::move() #include #include #include @@ -153,4 +154,38 @@ public: if (head == buffer.size()) head = 0; } + + /** + * Move data from the buffer to the destination. This method + * considers ring buffer wraparound. + * + * @return the number of items moved + */ + constexpr size_type MoveTo(Range dest) noexcept { + size_type n = 0; + + auto a = Read(); + if (a.size() > dest.size()) + a = a.first(dest.size()); + + if (!a.empty()) { + dest = {std::move(a.begin(), a.end(), dest.begin()), dest.end()}; + Consume(a.size()); + n += a.size(); + + if (dest.empty()) + return n; + + if (auto b = Read(); !b.empty()) { + if (b.size() > dest.size()) + b = b.first(dest.size()); + + std::move(b.begin(), b.end(), dest.begin()); + Consume(b.size()); + n += b.size(); + } + } + + return n; + } };