Fixed FileStream move semantics.

This commit is contained in:
Patrick Wuttke
2026-02-09 10:17:29 +01:00
parent 96765103b7
commit dffff4b5ad
2 changed files with 96 additions and 47 deletions

View File

@@ -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<std::uint8_t> 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<std::uint8_t> buffer, const ReadOption
StreamError FileStream::writeRaw(std::span<const std::uint8_t> 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<std::size_t>(length, std::ftell(handle));
length_ = std::max<std::size_t>(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<long>(pos), origin);
if (result != 0 || std::ferror(handle)) {
const int result = std::fseek(handle_, static_cast<long>(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 = {

View File

@@ -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<std::uint8_t> buffer, const ReadOptions& options = {}, std::size_t* outBytesRead = nullptr) override;
@@ -337,22 +343,45 @@ private:
bool canWrite_ = false;
public:
void openRW(std::span<std::uint8_t> data);
template<typename TConcrete>
void openRW(const MixinMemoryView<TConcrete>& memoryView) {
openRW(memoryView.makeSpan<std::uint8_t>());
}
template<typename T>
void openRO(std::span<T> data) {
openROImpl(data.data(), data.size_bytes());
}
template<typename TChar>
inline void openRO(std::basic_string_view<TChar> stringView) {
void openRO(std::basic_string_view<TChar> stringView) {
openROImpl(stringView.data(), stringView.size() * sizeof(TChar));
}
template<typename TConcrete>
void openRO(const MixinMemoryView<TConcrete>& memoryView) {
openRO(memoryView.makeSpan<const std::uint8_t>());
}
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<std::uint8_t> data() const
{
MIJIN_ASSERT(isOpen(), "MemoryStream is not open.");
MIJIN_ASSERT(canWrite_, "MemoryStream is read-only.");
return data_;
}
[[nodiscard]]
std::span<const std::uint8_t> constData() const
{
MIJIN_ASSERT(isOpen(), "MemoryStream is not open.");
return data_;
}
// Stream overrides
StreamError readRaw(std::span<std::uint8_t> buffer, const ReadOptions& options = {}, std::size_t* outBytesRead = nullptr) override;
StreamError writeRaw(std::span<const std::uint8_t> buffer) override;