serenity/AK/CircularDuplexStream.h
Andreas Kling ae3ffdd521 AK: Make it possible to not using AK classes into the global namespace
This patch adds the `USING_AK_GLOBALLY` macro which is enabled by
default, but can be overridden by build flags.

This is a step towards integrating Jakt and AK types.
2022-11-26 15:51:34 +01:00

126 lines
3 KiB
C++

/*
* Copyright (c) 2020, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/CircularQueue.h>
#include <AK/Stream.h>
namespace AK {
// FIXME: There are a lot of raw loops here, that's not necessary an issue but it
// has to be verified that the optimizer is able to insert memcpy instead.
template<size_t Capacity>
class CircularDuplexStream : public AK::DuplexStream {
public:
size_t write(ReadonlyBytes bytes) override
{
auto const nwritten = min(bytes.size(), Capacity - m_queue.size());
for (size_t idx = 0; idx < nwritten; ++idx)
m_queue.enqueue(bytes[idx]);
m_total_written += nwritten;
return nwritten;
}
bool write_or_error(ReadonlyBytes bytes) override
{
if (Capacity - m_queue.size() < bytes.size()) {
set_recoverable_error();
return false;
}
auto const nwritten = write(bytes);
VERIFY(nwritten == bytes.size());
return true;
}
size_t read(Bytes bytes) override
{
if (has_any_error())
return 0;
auto const nread = min(bytes.size(), m_queue.size());
for (size_t idx = 0; idx < nread; ++idx)
bytes[idx] = m_queue.dequeue();
return nread;
}
size_t read(Bytes bytes, size_t seekback)
{
if (seekback > Capacity || seekback > m_total_written) {
set_recoverable_error();
return 0;
}
auto const nread = min(bytes.size(), seekback);
for (size_t idx = 0; idx < nread; ++idx) {
auto const index = (m_total_written - seekback + idx) % Capacity;
bytes[idx] = m_queue.m_storage[index];
}
return nread;
}
bool read_or_error(Bytes bytes) override
{
if (m_queue.size() < bytes.size()) {
set_recoverable_error();
return false;
}
read(bytes);
return true;
}
bool discard_or_error(size_t count) override
{
if (m_queue.size() < count) {
set_recoverable_error();
return false;
}
for (size_t idx = 0; idx < count; ++idx)
m_queue.dequeue();
return true;
}
bool unreliable_eof() const override { return eof(); }
bool eof() const { return m_queue.size() == 0; }
size_t remaining_contiguous_space() const
{
return min(Capacity - m_queue.size(), m_queue.capacity() - (m_queue.head_index() + m_queue.size()) % Capacity);
}
Bytes reserve_contiguous_space(size_t count)
{
VERIFY(count <= remaining_contiguous_space());
Bytes bytes { m_queue.m_storage + (m_queue.head_index() + m_queue.size()) % Capacity, count };
m_queue.m_size += count;
m_total_written += count;
return bytes;
}
private:
CircularQueue<u8, Capacity> m_queue;
size_t m_total_written { 0 };
};
}
#if USING_AK_GLOBALLY
using AK::CircularDuplexStream;
#endif