diff options
author | crupest <crupest@outlook.com> | 2024-10-06 13:57:39 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2024-10-06 13:57:39 +0800 |
commit | dfe62dcf8bcefc523b466e127c3edc4dc2756629 (patch) | |
tree | 1c751a14ba0da07ca2ff805633f97568060aa4c9 /include/cru/base/io | |
parent | f51eb955e188858272230a990565931e7403f23b (diff) | |
download | cru-dfe62dcf8bcefc523b466e127c3edc4dc2756629.tar.gz cru-dfe62dcf8bcefc523b466e127c3edc4dc2756629.tar.bz2 cru-dfe62dcf8bcefc523b466e127c3edc4dc2756629.zip |
Rename common to base.
Diffstat (limited to 'include/cru/base/io')
-rw-r--r-- | include/cru/base/io/AutoReadStream.h | 72 | ||||
-rw-r--r-- | include/cru/base/io/BufferStream.h | 85 | ||||
-rw-r--r-- | include/cru/base/io/CFileStream.h | 39 | ||||
-rw-r--r-- | include/cru/base/io/MemoryStream.h | 36 | ||||
-rw-r--r-- | include/cru/base/io/OpenFileFlag.h | 56 | ||||
-rw-r--r-- | include/cru/base/io/ProxyStream.h | 42 | ||||
-rw-r--r-- | include/cru/base/io/Resource.h | 8 | ||||
-rw-r--r-- | include/cru/base/io/Stream.h | 126 |
8 files changed, 464 insertions, 0 deletions
diff --git a/include/cru/base/io/AutoReadStream.h b/include/cru/base/io/AutoReadStream.h new file mode 100644 index 00000000..759d5026 --- /dev/null +++ b/include/cru/base/io/AutoReadStream.h @@ -0,0 +1,72 @@ +#pragma once + +#include "BufferStream.h" +#include "Stream.h" + +#include <mutex> +#include <thread> + +namespace cru::io { +struct AutoReadStreamOptions { + /** + * @brief Will be passed to BufferStreamOptions::block_size. + */ + Index block_size = 0; + + /** + * @brief Will be passed to BufferStreamOptions::total_size_limit. + */ + Index total_size_limit = 0; + + BufferStreamOptions GetBufferStreamOptions() const { + BufferStreamOptions options; + options.block_size = block_size; + options.total_size_limit = total_size_limit; + return options; + } +}; + +/** + * @brief A stream that wraps another stream and auto read it into a buffer in a + * background thread. + */ +class CRU_BASE_API AutoReadStream : public Stream { + public: + /** + * @brief Wrap a stream and auto read it in background. + * @param stream The stream to auto read. + * @param auto_delete Whether to delete the stream object in destructor. + * @param options Options to modify the behavior. + */ + AutoReadStream( + Stream* stream, bool auto_delete, + const AutoReadStreamOptions& options = AutoReadStreamOptions()); + + ~AutoReadStream() override; + + public: + CRU_STREAM_IMPLEMENT_CLOSE_BY_DO_CLOSE + + void BeginToDrop(bool auto_delete = true); + + protected: + Index DoRead(std::byte* buffer, Index offset, Index size) override; + Index DoWrite(const std::byte* buffer, Index offset, Index size) override; + void DoFlush() override; + + private: + void DoClose(); + + void BackgroundThreadRun(); + + private: + Stream* stream_; + bool auto_delete_; + + Index size_per_read_; + std::unique_ptr<BufferStream> buffer_stream_; + std::mutex buffer_stream_mutex_; + + std::thread background_thread_; +}; +} // namespace cru::io diff --git a/include/cru/base/io/BufferStream.h b/include/cru/base/io/BufferStream.h new file mode 100644 index 00000000..5ebff546 --- /dev/null +++ b/include/cru/base/io/BufferStream.h @@ -0,0 +1,85 @@ +#pragma once + +#include "../Buffer.h" +#include "../Exception.h" +#include "Stream.h" + +#include <condition_variable> +#include <list> +#include <mutex> + +namespace cru::io { +class WriteAfterEofException : public Exception { + public: + using Exception::Exception; + ~WriteAfterEofException() override = default; +}; + +struct BufferStreamOptions { + /** + * Actually I have no ideas about the best value for this. May change it later + * when I get some ideas. + */ + constexpr static Index kDefaultBlockSize = 1024; + + /** + * @brief The size of a single buffer allocated each time new space is needed. + * Use default value if <= 0. + * + * When current buffer is full and there is no space for following data, a new + * buffer will be allocated and appended to the buffer list. Note if sum size + * of all buffers reaches the total_buffer_limit, no more buffer will be + * allocated but wait. + */ + Index block_size = 0; + + /** + * @brief Total size limit of saved data in buffer. No limit if <= 0. + * + * The size will be floor(total_size_limit / block_size). When the buffer is + * filled, it will block and wait for user to read to get free space of buffer + * to continue read. + */ + Index total_size_limit = 0; + + Index GetBlockSizeOrDefault() const { + return block_size <= 0 ? kDefaultBlockSize : block_size; + } + + Index GetMaxBlockCount() const { + return total_size_limit / GetBlockSizeOrDefault(); + } +}; + +/** + * @brief SPSC (Single Producer Single Consumer) buffer stream. + * + * If used by multiple producer or multiple consumer, the behavior is undefined. + */ +class BufferStream : public Stream { + public: + BufferStream(const BufferStreamOptions& options); + ~BufferStream() override; + + CRU_STREAM_IMPLEMENT_CLOSE_BY_DO_CLOSE + + void SetEof(); + + protected: + Index DoRead(std::byte* buffer, Index offset, Index size) override; + Index DoWrite(const std::byte* buffer, Index offset, Index size) override; + + private: + void DoClose(); + + private: + Index block_size_; + Index max_block_count_; + + std::list<Buffer> buffer_list_; + bool eof_; + + std::mutex mutex_; + std::condition_variable condition_variable_; +}; +} // namespace cru::io diff --git a/include/cru/base/io/CFileStream.h b/include/cru/base/io/CFileStream.h new file mode 100644 index 00000000..0b58bdc9 --- /dev/null +++ b/include/cru/base/io/CFileStream.h @@ -0,0 +1,39 @@ +#pragma once + +#include "Stream.h" + +#include <cstdio> + +namespace cru::io { +class CRU_BASE_API CFileStream : public Stream { + public: + CFileStream(const char* path, const char* mode); + explicit CFileStream(std::FILE* file, bool readable = true, + bool writable = true, bool auto_close = true); + + CRU_DELETE_COPY(CFileStream) + CRU_DELETE_MOVE(CFileStream) + + ~CFileStream() override; + + public: + CRU_STREAM_IMPLEMENT_CLOSE_BY_DO_CLOSE + + std::FILE* GetHandle() const; + + protected: + Index DoSeek(Index offset, SeekOrigin origin) override; + Index DoTell() override; + void DoRewind() override; + Index DoRead(std::byte* buffer, Index offset, Index size) override; + Index DoWrite(const std::byte* buffer, Index offset, Index size) override; + void DoFlush() override; + + private: + void DoClose(); + + private: + std::FILE* file_; + bool auto_close_; +}; +} // namespace cru::io diff --git a/include/cru/base/io/MemoryStream.h b/include/cru/base/io/MemoryStream.h new file mode 100644 index 00000000..a1f90c3b --- /dev/null +++ b/include/cru/base/io/MemoryStream.h @@ -0,0 +1,36 @@ +#pragma once + +#include "Stream.h" + +#include <functional> + +namespace cru::io { +class CRU_BASE_API MemoryStream : public Stream { + public: + MemoryStream( + std::byte* buffer, Index size, bool read_only = false, + std::function<void(std::byte* buffer, Index size)> release_func = {}); + + ~MemoryStream() override; + + public: + void Close() override; + + std::byte* GetBuffer() const { return buffer_; } + + protected: + Index DoSeek(Index offset, SeekOrigin origin) override; + Index DoGetSize() override { return size_; } + Index DoRead(std::byte* buffer, Index offset, Index size) override; + Index DoWrite(const std::byte* buffer, Index offset, Index size) override; + + private: + void DoClose(); + + private: + std::byte* buffer_; + Index size_; + Index position_; + std::function<void(std::byte* buffer, Index size)> release_func_; +}; +} // namespace cru::io diff --git a/include/cru/base/io/OpenFileFlag.h b/include/cru/base/io/OpenFileFlag.h new file mode 100644 index 00000000..4a5789fb --- /dev/null +++ b/include/cru/base/io/OpenFileFlag.h @@ -0,0 +1,56 @@ +#pragma once + +#include "../Bitmask.h" + +namespace cru::io { +namespace details { +struct OpenFileFlagTag {}; +} // namespace details +using OpenFileFlag = Bitmask<details::OpenFileFlagTag>; + +struct OpenFileFlags { + /** + * \brief for reading + * If the file does not exist, FileNotExistException should be thrown. + */ + static constexpr OpenFileFlag Read{0x1}; + + /** + * \brief for writing + * If the file does not exist and Create is not specified, + * FileNotExistException should be thrown. + */ + static constexpr OpenFileFlag Write{0x2}; + + /** + * \brief when writing, seek to end first + * Only effective for writing. + */ + static constexpr OpenFileFlag Append{0x4}; + + /** + * \brief when writing, truncate the file to empty + * Only effective for writing. + * So the content is erased! Be careful! + */ + static constexpr OpenFileFlag Truncate{0x8}; + + /** + * \brief when writing, if the file does not exist, create one + * Only effective for writing. When file does not exist, FileNotExistException + * will NOT be thrown and a new file will be created. + */ + static constexpr OpenFileFlag Create{0x10}; + + /** + * TODO: ??? + */ + static constexpr OpenFileFlag Exclusive{0x20}; +}; + +/** + * Append, Truncate, Create must be used with Write. + * Append and Truncate must not be used together. + */ +bool CheckOpenFileFlag(OpenFileFlag flags); +} // namespace cru::io diff --git a/include/cru/base/io/ProxyStream.h b/include/cru/base/io/ProxyStream.h new file mode 100644 index 00000000..42ec9dfd --- /dev/null +++ b/include/cru/base/io/ProxyStream.h @@ -0,0 +1,42 @@ +#pragma once + +#include "Stream.h" + +#include <functional> + +namespace cru::io { +struct ProxyStreamHandlers { + std::function<Index(Index offset, Stream::SeekOrigin origin)> seek; + std::function<Index(std::byte* buffer, Index offset, Index size)> read; + std::function<Index(const std::byte* buffer, Index offset, Index size)> write; + std::function<void()> flush; + + /** + * @brief This method will be only called once when `Close` is called or the + * stream is destructed. + */ + std::function<void()> close; +}; + +class ProxyStream : public Stream { + public: + explicit ProxyStream(ProxyStreamHandlers handlers); + + ~ProxyStream() override; + + public: + CRU_STREAM_IMPLEMENT_CLOSE_BY_DO_CLOSE + + protected: + Index DoSeek(Index offset, SeekOrigin origin) override; + Index DoRead(std::byte* buffer, Index offset, Index size) override; + Index DoWrite(const std::byte* buffer, Index offset, Index size) override; + void DoFlush() override; + + private: + void DoClose(); + + private: + ProxyStreamHandlers handlers_; +}; +} // namespace cru::io diff --git a/include/cru/base/io/Resource.h b/include/cru/base/io/Resource.h new file mode 100644 index 00000000..1d5313a6 --- /dev/null +++ b/include/cru/base/io/Resource.h @@ -0,0 +1,8 @@ +#pragma once +#include "../Base.h" + +#include <filesystem> + +namespace cru::io { +std::filesystem::path CRU_BASE_API GetResourceDir(); +} diff --git a/include/cru/base/io/Stream.h b/include/cru/base/io/Stream.h new file mode 100644 index 00000000..e0b61627 --- /dev/null +++ b/include/cru/base/io/Stream.h @@ -0,0 +1,126 @@ +#pragma once + +#include "../Base.h" + +#include "../Exception.h" +#include "../String.h" + +#include <cstddef> + +namespace cru::io { +class CRU_BASE_API StreamOperationNotSupportedException : public Exception { + public: + explicit StreamOperationNotSupportedException(String operation); + + CRU_DEFAULT_DESTRUCTOR(StreamOperationNotSupportedException) + + public: + String GetOperation() const { return operation_; } + + public: + static void CheckSeek(bool seekable); + static void CheckRead(bool readable); + static void CheckWrite(bool writable); + + private: + String operation_; +}; + +class CRU_BASE_API StreamAlreadyClosedException : public Exception { + public: + StreamAlreadyClosedException(); + + CRU_DEFAULT_DESTRUCTOR(StreamAlreadyClosedException) + + static void Check(bool closed); +}; + +#define CRU_STREAM_IMPLEMENT_CLOSE_BY_DO_CLOSE \ + void Close() override { DoClose(); } + +#define CRU_STREAM_BEGIN_CLOSE \ + if (GetClosed()) return; \ + CloseGuard close_guard(this); + +/** + * All stream is thread-unsafe by default unless being documented. + */ +class CRU_BASE_API Stream : public Object { + protected: + struct SupportedOperations { + std::optional<bool> can_seek; + std::optional<bool> can_read; + std::optional<bool> can_write; + }; + + struct CloseGuard { + explicit CloseGuard(Stream* stream) : stream(stream) {} + ~CloseGuard() { stream->SetClosed(true); } + Stream* stream; + }; + + protected: + explicit Stream(SupportedOperations supported_operations = {}); + Stream(std::optional<bool> can_seek, std::optional<bool> can_read, + std::optional<bool> can_write); + + public: + enum class SeekOrigin { Current, Begin, End }; + + CRU_DELETE_COPY(Stream) + CRU_DELETE_MOVE(Stream) + + ~Stream() override = default; + + public: + bool CanSeek(); + Index Seek(Index offset, SeekOrigin origin = SeekOrigin::Current); + Index Tell(); + void Rewind(); + Index GetSize(); + + bool CanRead(); + Index Read(std::byte* buffer, Index offset, Index size); + Index Read(std::byte* buffer, Index size); + Index Read(char* buffer, Index offset, Index size); + Index Read(char* buffer, Index size); + + bool CanWrite(); + Index Write(const std::byte* buffer, Index offset, Index size); + Index Write(const std::byte* buffer, Index size); + Index Write(const char* buffer, Index offset, Index size); + Index Write(const char* buffer, Index size); + + void Flush(); + virtual void Close() = 0; + + virtual Buffer ReadToEnd(Index grow_size = 256); + + // Utf8 encoding. + String ReadToEndAsUtf8String(); + + protected: + virtual bool DoCanSeek(); + virtual bool DoCanRead(); + virtual bool DoCanWrite(); + virtual Index DoSeek(Index offset, SeekOrigin origin); + virtual Index DoTell(); + virtual void DoRewind(); + virtual Index DoGetSize(); + virtual Index DoRead(std::byte* buffer, Index offset, Index size); + virtual Index DoWrite(const std::byte* buffer, Index offset, Index size); + virtual void DoFlush(); + + void SetSupportedOperations(SupportedOperations supported_operations) { + supported_operations_ = std::move(supported_operations); + } + + bool GetClosed() { return closed_; } + void SetClosed(bool closed) { closed_ = closed; } + void CheckClosed() { StreamAlreadyClosedException::Check(closed_); } + + private: + std::optional<SupportedOperations> supported_operations_; + bool closed_; +}; +} // namespace cru::io |