util/CircularBuffer: add method MoveTo()

This implements wraparound, so AsyncInputStream and ThreadInputStream
can now return all of the buffer contents in one Read() call.
This commit is contained in:
Max Kellermann
2025-01-29 21:04:52 +01:00
parent 950f5f4d32
commit e06d775af5
3 changed files with 39 additions and 12 deletions

View File

@@ -162,14 +162,10 @@ AsyncInputStream::IsAvailable() const noexcept
inline std::size_t inline std::size_t
AsyncInputStream::ReadFromBuffer(std::span<std::byte> dest) noexcept AsyncInputStream::ReadFromBuffer(std::span<std::byte> dest) noexcept
{ {
const auto r = buffer.Read(); const size_t nbytes = buffer.MoveTo(dest);
if (r.empty()) if (nbytes == 0)
return 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()) if (buffer.empty())
/* when the buffer becomes empty, reset its head and /* when the buffer becomes empty, reset its head and
tail so the next write can fill the whole buffer tail so the next write can fill the whole buffer

View File

@@ -152,14 +152,10 @@ ThreadInputStream::Seek([[maybe_unused]] std::unique_lock<Mutex> &lock,
inline std::size_t inline std::size_t
ThreadInputStream::ReadFromBuffer(std::span<std::byte> dest) noexcept ThreadInputStream::ReadFromBuffer(std::span<std::byte> dest) noexcept
{ {
const auto r = buffer.Read(); const size_t nbytes = buffer.MoveTo(dest);
if (r.empty()) if (nbytes == 0)
return 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()) if (buffer.empty())
/* when the buffer becomes empty, reset its head and /* when the buffer becomes empty, reset its head and
tail so the next write can fill the whole buffer tail so the next write can fill the whole buffer

View File

@@ -3,6 +3,7 @@
#pragma once #pragma once
#include <algorithm> // for std::move()
#include <cassert> #include <cassert>
#include <cstddef> #include <cstddef>
#include <span> #include <span>
@@ -153,4 +154,38 @@ public:
if (head == buffer.size()) if (head == buffer.size())
head = 0; 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;
}
}; };