1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-24 13:57:35 +00:00

AK+LibCore: Make output buffered stream seekable

Just like with input buffered streams, we don't currently have a use
case for output buffered streams which aren't seekable, since the main
application are files.
This commit is contained in:
kleines Filmröllchen 2023-07-04 17:47:23 +02:00 committed by Andrew Kaster
parent c2d9d0277f
commit 001ea22917
3 changed files with 46 additions and 10 deletions

View file

@ -310,10 +310,10 @@ private:
BufferedHelper<T> m_helper; BufferedHelper<T> m_helper;
}; };
template<StreamLike T> template<SeekableStreamLike T>
class OutputBufferedStream final : public Stream { class OutputBufferedSeekable : public SeekableStream {
public: public:
static ErrorOr<NonnullOwnPtr<OutputBufferedStream<T>>> create(NonnullOwnPtr<T> stream, size_t buffer_size = 16 * KiB) static ErrorOr<NonnullOwnPtr<OutputBufferedSeekable<T>>> create(NonnullOwnPtr<T> stream, size_t buffer_size = 16 * KiB)
{ {
if (buffer_size == 0) if (buffer_size == 0)
return Error::from_errno(EINVAL); return Error::from_errno(EINVAL);
@ -322,11 +322,11 @@ public:
auto buffer = TRY(CircularBuffer::create_empty(buffer_size)); auto buffer = TRY(CircularBuffer::create_empty(buffer_size));
return adopt_nonnull_own_or_enomem(new OutputBufferedStream<T>(move(stream), move(buffer))); return adopt_nonnull_own_or_enomem(new OutputBufferedSeekable<T>(move(stream), move(buffer)));
} }
OutputBufferedStream(OutputBufferedStream&& other) = default; OutputBufferedSeekable(OutputBufferedSeekable&& other) = default;
OutputBufferedStream& operator=(OutputBufferedStream&& other) = default; OutputBufferedSeekable& operator=(OutputBufferedSeekable&& other) = default;
virtual ErrorOr<Bytes> read_some(Bytes buffer) override virtual ErrorOr<Bytes> read_some(Bytes buffer) override
{ {
@ -368,13 +368,31 @@ public:
return {}; return {};
} }
virtual ~OutputBufferedStream() override // Since tell() doesn't involve moving the write offset, we can skip flushing the buffer here.
virtual ErrorOr<size_t> tell() const override
{
return TRY(m_stream->tell()) + m_buffer.used_space();
}
virtual ErrorOr<size_t> seek(i64 offset, SeekMode mode) override
{
TRY(flush_buffer());
return m_stream->seek(offset, mode);
}
virtual ErrorOr<void> truncate(size_t length) override
{
TRY(flush_buffer());
return m_stream->truncate(length);
}
virtual ~OutputBufferedSeekable() override
{ {
MUST(flush_buffer()); MUST(flush_buffer());
} }
private: private:
OutputBufferedStream(NonnullOwnPtr<T> stream, CircularBuffer buffer) OutputBufferedSeekable(NonnullOwnPtr<T> stream, CircularBuffer buffer)
: m_stream(move(stream)) : m_stream(move(stream))
, m_buffer(move(buffer)) , m_buffer(move(buffer))
{ {
@ -389,5 +407,5 @@ private:
#if USING_AK_GLOBALLY #if USING_AK_GLOBALLY
using AK::BufferedHelper; using AK::BufferedHelper;
using AK::InputBufferedSeekable; using AK::InputBufferedSeekable;
using AK::OutputBufferedStream; using AK::OutputBufferedSeekable;
#endif #endif

View file

@ -111,6 +111,24 @@ BENCHMARK_CASE(file_tell)
} }
} }
TEST_CASE(file_buffered_write_and_seek)
{
auto file = TRY_OR_FAIL(Core::OutputBufferedFile::create(TRY_OR_FAIL(Core::File::open("/tmp/file-buffered-write-test.txt"sv, Core::File::OpenMode::Truncate | Core::File::OpenMode::ReadWrite))));
TRY_OR_FAIL(file->write_some("0123456789"sv.bytes()));
EXPECT_EQ(file->tell().release_value(), 10ul);
// Reads don't go through the buffer, so after we seek, the data must be available from the underlying file.
TRY_OR_FAIL(file->seek(0, AK::SeekMode::SetPosition));
auto first_byte = TRY_OR_FAIL(file->read_value<u8>());
EXPECT_EQ(first_byte, static_cast<u8>('0'));
TRY_OR_FAIL(file->seek(9, AK::SeekMode::SetPosition));
auto last_byte = TRY_OR_FAIL(file->read_value<u8>());
EXPECT_EQ(last_byte, static_cast<u8>('9'));
EXPECT_EQ(file->tell().release_value(), 10ul);
}
TEST_CASE(file_adopt_fd) TEST_CASE(file_adopt_fd)
{ {
int rc = ::open("/usr/Tests/LibCore/long_lines.txt", O_RDONLY); int rc = ::open("/usr/Tests/LibCore/long_lines.txt", O_RDONLY);

View file

@ -117,6 +117,6 @@ private:
AK_ENUM_BITWISE_OPERATORS(File::OpenMode) AK_ENUM_BITWISE_OPERATORS(File::OpenMode)
using InputBufferedFile = InputBufferedSeekable<File>; using InputBufferedFile = InputBufferedSeekable<File>;
using OutputBufferedFile = OutputBufferedStream<File>; using OutputBufferedFile = OutputBufferedSeekable<File>;
} }