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
AsyncInputStream::ReadFromBuffer(std::span<std::byte> 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

View File

@@ -152,14 +152,10 @@ ThreadInputStream::Seek([[maybe_unused]] std::unique_lock<Mutex> &lock,
inline std::size_t
ThreadInputStream::ReadFromBuffer(std::span<std::byte> 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

View File

@@ -3,6 +3,7 @@
#pragma once
#include <algorithm> // for std::move()
#include <cassert>
#include <cstddef>
#include <span>
@@ -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;
}
};