/* * Copyright (c) 2021, sin-ack * Copyright (c) 2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include namespace AK { /// The base, abstract class for stream operations. This class defines the /// operations one can perform on every stream. /// Operations without a sensible default that are unsupported by an implementation /// of a Stream should return EBADF as an error. class Stream { public: /// Reads into a buffer, with the maximum size being the size of the buffer. /// The amount of bytes read can be smaller than the size of the buffer. /// Returns either the bytes that were read, or an errno in the case of /// failure. virtual ErrorOr read_some(Bytes) = 0; /// Tries to fill the entire buffer through reading. Returns whether the /// buffer was filled without an error. virtual ErrorOr read_until_filled(Bytes); /// Reads the stream until EOF, storing the contents into a ByteBuffer which /// is returned once EOF is encountered. The block size determines the size /// of newly allocated chunks while reading. virtual ErrorOr read_until_eof(size_t block_size = 4096); /// Discards the given number of bytes from the stream. As this is usually used /// as an efficient version of `read_until_filled`, it returns an error /// if reading failed or if not all bytes could be discarded. /// Unless specifically overwritten, this just uses read() to read into an /// internal stack-based buffer. virtual ErrorOr discard(size_t discarded_bytes); /// Tries to write the entire contents of the buffer. It is possible for /// less than the full buffer to be written. Returns either the amount of /// bytes written into the stream, or an errno in the case of failure. virtual ErrorOr write_some(ReadonlyBytes) = 0; /// Same as write, but does not return until either the entire buffer /// contents are written or an error occurs. virtual ErrorOr write_until_depleted(ReadonlyBytes); template ErrorOr write_until_depleted(T const& buffer) { return write_until_depleted(StringView { buffer }.bytes()); } template requires(requires(Stream& stream) { { T::read_from_stream(stream) } -> SameAs>; }) ErrorOr read_value() { return T::read_from_stream(*this); } template requires(Traits::is_trivially_serializable()) ErrorOr read_value() { alignas(T) u8 buffer[sizeof(T)] = {}; TRY(read_until_filled({ &buffer, sizeof(buffer) })); return bit_cast(buffer); } template requires(requires(T t, Stream& stream) { { t.write_to_stream(stream) } -> SameAs>; }) ErrorOr write_value(T const& value) { return value.write_to_stream(*this); } template requires(Traits::is_trivially_serializable()) ErrorOr write_value(T const& value) { return write_until_depleted({ &value, sizeof(value) }); } template ErrorOr write_formatted(CheckedFormatString&& fmtstr, Parameters const&... parameters) { VariadicFormatParams variadic_format_params { parameters... }; TRY(write_formatted_impl(fmtstr.view(), variadic_format_params)); return {}; } /// Returns whether the stream has reached the end of file. For sockets, /// this most likely means that the protocol has disconnected (in the case /// of TCP). For seekable streams, this means the end of the file. Note that /// is_eof will only return true _after_ a read with 0 length, so this /// method should be called after a read. virtual bool is_eof() const = 0; virtual bool is_open() const = 0; virtual void close() = 0; virtual ~Stream() { } protected: /// Provides a default implementation of read_until_eof that works for streams /// that behave like POSIX file descriptors. expected_file_size can be /// passed as a heuristic for what the Stream subclass expects the file /// content size to be in order to reduce allocations (does not affect /// actual reading). ErrorOr read_until_eof_impl(size_t block_size, size_t expected_file_size = 0); private: ErrorOr write_formatted_impl(StringView, TypeErasedFormatParams&); }; enum class SeekMode { SetPosition, FromCurrentPosition, FromEndPosition, }; /// Adds seekability to a Stream. Classes inheriting from SeekableStream /// will be seekable to any point in the stream. class SeekableStream : public Stream { public: /// Seeks to the given position in the given mode. Returns either the /// current position of the file, or an errno in the case of an error. virtual ErrorOr seek(i64 offset, SeekMode) = 0; /// Returns the current position of the file, or an errno in the case of /// an error. virtual ErrorOr tell() const; /// Returns the total size of the stream, or an errno in the case of an /// error. May not preserve the original position on the stream on failure. virtual ErrorOr size(); /// Shrinks or extends the stream to the given size. Returns an errno in /// the case of an error. virtual ErrorOr truncate(size_t length) = 0; /// Seeks until after the given amount of bytes to be discarded instead of /// reading and discarding everything manually; virtual ErrorOr discard(size_t discarded_bytes) override; }; } #if USING_AK_GLOBALLY using AK::SeekMode; #endif