diff --git a/source/mijin/io/stream.cpp b/source/mijin/io/stream.cpp index 57148db..f4886b6 100644 --- a/source/mijin/io/stream.cpp +++ b/source/mijin/io/stream.cpp @@ -313,16 +313,36 @@ StreamError Stream::copyTo(Stream& other) return StreamError::SUCCESS; } -FileStream::~FileStream() +FileStream::FileStream(FileStream&& other) MIJIN_NOEXCEPT + : handle_(std::exchange(other.handle_, nullptr)), mode_(other.mode_), length_(other.length_) { - if (handle) { + +} + +FileStream::~FileStream() noexcept +{ + if (handle_) { close(); } } -StreamError FileStream::open(const char* path, FileOpenMode mode_) +FileStream& FileStream::operator=(FileStream&& other) MIJIN_NOEXCEPT { - mode = mode_; + if (this != &other) + { + if (handle_) { + close(); + } + handle_ = std::exchange(other.handle_, nullptr); + mode_ = other.mode_; + length_ = other.length_; + } + return *this; +} + +StreamError FileStream::open(const char* path, FileOpenMode mode) +{ + mode_ = mode; const char* modeStr; // NOLINT(cppcoreguidelines-init-variables) switch (mode_) @@ -343,18 +363,18 @@ StreamError FileStream::open(const char* path, FileOpenMode mode_) MIJIN_FATAL("Invalid value for mode."); return StreamError::UNKNOWN_ERROR; } - handle = std::fopen(path, modeStr); // NOLINT(cppcoreguidelines-owning-memory) - if (!handle && mode_ == FileOpenMode::READ_WRITE) { - handle = std::fopen(path, "w+b"); // NOLINT(cppcoreguidelines-owning-memory) + handle_ = std::fopen(path, modeStr); // NOLINT(cppcoreguidelines-owning-memory) + if (!handle_ && mode_ == FileOpenMode::READ_WRITE) { + handle_ = std::fopen(path, "w+b"); // NOLINT(cppcoreguidelines-owning-memory) } - if (!handle) { + if (!handle_) { return StreamError::IO_ERROR; } - int result = std::fseek(handle, 0, SEEK_END); + int result = std::fseek(handle_, 0, SEEK_END); MIJIN_ASSERT(result == 0, "fseek failed."); - length = std::ftell(handle); - result = std::fseek(handle, 0, SEEK_SET); + length_ = std::ftell(handle_); + result = std::fseek(handle_, 0, SEEK_SET); MIJIN_ASSERT(result == 0, "fseek failed."); return StreamError::SUCCESS; @@ -362,19 +382,19 @@ StreamError FileStream::open(const char* path, FileOpenMode mode_) void FileStream::close() { - MIJIN_ASSERT(handle != nullptr, "FileStream is not open."); - [[maybe_unused]] const int result = std::fclose(handle); // NOLINT(cppcoreguidelines-owning-memory) + MIJIN_ASSERT(handle_ != nullptr, "FileStream is not open."); + [[maybe_unused]] const int result = std::fclose(handle_); // NOLINT(cppcoreguidelines-owning-memory) MIJIN_ASSERT(result == 0, "fclose failed."); - handle = nullptr; + handle_ = nullptr; } StreamError FileStream::readRaw(std::span buffer, const ReadOptions& options, std::size_t* outBytesRead) { - MIJIN_ASSERT(handle != nullptr, "FileStream is not open."); - MIJIN_ASSERT(mode == FileOpenMode::READ || mode == FileOpenMode::READ_WRITE, "Cannot read from this stream"); + MIJIN_ASSERT(handle_ != nullptr, "FileStream is not open."); + MIJIN_ASSERT(mode_ == FileOpenMode::READ || mode_ == FileOpenMode::READ_WRITE, "Cannot read from this stream"); - const std::size_t readBytes = std::fread(buffer.data(), 1, buffer.size(), handle); - if (std::ferror(handle)) { + const std::size_t readBytes = std::fread(buffer.data(), 1, buffer.size(), handle_); + if (std::ferror(handle_)) { return StreamError::IO_ERROR; } if (!options.partial && readBytes < buffer.size()) { @@ -395,28 +415,28 @@ StreamError FileStream::readRaw(std::span buffer, const ReadOption StreamError FileStream::writeRaw(std::span buffer) { - MIJIN_ASSERT(handle != nullptr, "FileStream is not open."); - MIJIN_ASSERT(mode == FileOpenMode::WRITE || mode == FileOpenMode::READ_WRITE || mode == FileOpenMode::APPEND, + MIJIN_ASSERT(handle_ != nullptr, "FileStream is not open."); + MIJIN_ASSERT(mode_ == FileOpenMode::WRITE || mode_ == FileOpenMode::READ_WRITE || mode_ == FileOpenMode::APPEND, "Cannot write to this stream"); - const std::size_t written = std::fwrite(buffer.data(), 1, buffer.size(), handle); - if (written != buffer.size() || std::ferror(handle)) { + const std::size_t written = std::fwrite(buffer.data(), 1, buffer.size(), handle_); + if (written != buffer.size() || std::ferror(handle_)) { return StreamError::IO_ERROR; } - length = std::max(length, std::ftell(handle)); + length_ = std::max(length_, std::ftell(handle_)); return StreamError::SUCCESS; } std::size_t FileStream::tell() { - MIJIN_ASSERT(handle != nullptr, "FileStream is not open."); - return std::ftell(handle); + MIJIN_ASSERT(handle_ != nullptr, "FileStream is not open."); + return std::ftell(handle_); } StreamError FileStream::seek(std::intptr_t pos, SeekMode seekMode) { - MIJIN_ASSERT(handle != nullptr, "FileStream is not open."); + MIJIN_ASSERT(handle_ != nullptr, "FileStream is not open."); int origin; // NOLINT(cppcoreguidelines-init-variables) switch (seekMode) { @@ -433,8 +453,8 @@ StreamError FileStream::seek(std::intptr_t pos, SeekMode seekMode) MIJIN_ERROR("Invalid value passed as seekMode!"); return StreamError::UNKNOWN_ERROR; } - const int result = std::fseek(handle, static_cast(pos), origin); - if (result != 0 || std::ferror(handle)) { + const int result = std::fseek(handle_, static_cast(pos), origin); + if (result != 0 || std::ferror(handle_)) { return StreamError::IO_ERROR; } return StreamError::SUCCESS; @@ -442,30 +462,30 @@ StreamError FileStream::seek(std::intptr_t pos, SeekMode seekMode) void FileStream::flush() { - [[maybe_unused]] const int result = std::fflush(handle); + [[maybe_unused]] const int result = std::fflush(handle_); MIJIN_ASSERT(result == 0, "fflush failed."); } bool FileStream::isAtEnd() { - MIJIN_ASSERT(handle != nullptr, "FileStream is not open."); + MIJIN_ASSERT(handle_ != nullptr, "FileStream is not open."); - (void) std::fgetc(handle); - if (std::feof(handle)) { + (void) std::fgetc(handle_); + if (std::feof(handle_)) { return true; } - [[maybe_unused]] const int result = std::fseek(handle, -1, SEEK_CUR); + [[maybe_unused]] const int result = std::fseek(handle_, -1, SEEK_CUR); MIJIN_ASSERT(result == 0, "fseek failed."); return false; } StreamFeatures FileStream::getFeatures() { - if (handle) + if (handle_) { return { - .read = (mode == FileOpenMode::READ || mode == FileOpenMode::READ_WRITE), - .write = (mode == FileOpenMode::WRITE || mode == FileOpenMode::APPEND || mode == FileOpenMode::READ_WRITE), + .read = (mode_ == FileOpenMode::READ || mode_ == FileOpenMode::READ_WRITE), + .write = (mode_ == FileOpenMode::WRITE || mode_ == FileOpenMode::APPEND || mode_ == FileOpenMode::READ_WRITE), .tell = true, .seek = true, .readOptions = { diff --git a/source/mijin/io/stream.hpp b/source/mijin/io/stream.hpp index 8893ba0..072905a 100644 --- a/source/mijin/io/stream.hpp +++ b/source/mijin/io/stream.hpp @@ -306,18 +306,24 @@ public: class FileStream : public Stream { private: - std::FILE* handle = nullptr; // TODO: wrap in gsl::owner<> - FileOpenMode mode; - std::size_t length = 0; + std::FILE* handle_ = nullptr; // TODO: wrap in gsl::owner<> + FileOpenMode mode_; + std::size_t length_ = 0; public: - ~FileStream() override; + FileStream() = default; + FileStream(const FileStream&) = delete; + FileStream(FileStream&& other) MIJIN_NOEXCEPT; + ~FileStream() noexcept override; - StreamError open(const char* path, FileOpenMode mode_); - inline StreamError open(const std::string& path, FileOpenMode mode_) { - return open(path.c_str(), mode_); + FileStream& operator=(const FileStream&) = delete; + FileStream& operator=(FileStream&& other) MIJIN_NOEXCEPT; + + StreamError open(const char* path, FileOpenMode mode); + inline StreamError open(const std::string& path, FileOpenMode mode) { + return open(path.c_str(), mode); } void close(); - [[nodiscard]] inline bool isOpen() const { return handle != nullptr; } + [[nodiscard]] inline bool isOpen() const { return handle_ != nullptr; } // Stream overrides StreamError readRaw(std::span buffer, const ReadOptions& options = {}, std::size_t* outBytesRead = nullptr) override; @@ -337,22 +343,45 @@ private: bool canWrite_ = false; public: void openRW(std::span data); + template + void openRW(const MixinMemoryView& memoryView) { + openRW(memoryView.makeSpan()); + } template void openRO(std::span data) { openROImpl(data.data(), data.size_bytes()); } template - inline void openRO(std::basic_string_view stringView) { + void openRO(std::basic_string_view stringView) { openROImpl(stringView.data(), stringView.size() * sizeof(TChar)); } + template + void openRO(const MixinMemoryView& memoryView) { + openRO(memoryView.makeSpan()); + } void close(); - [[nodiscard]] inline bool isOpen() const { return data_.data() != nullptr; } - [[nodiscard]] inline std::size_t availableBytes() const + [[nodiscard]] bool isOpen() const { return data_.data() != nullptr; } + [[nodiscard]] std::size_t availableBytes() const { MIJIN_ASSERT(isOpen(), "MemoryStream is not open."); return data_.size() - pos_; } + [[nodiscard]] + std::span data() const + { + MIJIN_ASSERT(isOpen(), "MemoryStream is not open."); + MIJIN_ASSERT(canWrite_, "MemoryStream is read-only."); + return data_; + } + + [[nodiscard]] + std::span constData() const + { + MIJIN_ASSERT(isOpen(), "MemoryStream is not open."); + return data_; + } + // Stream overrides StreamError readRaw(std::span buffer, const ReadOptions& options = {}, std::size_t* outBytesRead = nullptr) override; StreamError writeRaw(std::span buffer) override;