From 3a18a6dcab45467e779e91c7b346aa3b148e8b9c Mon Sep 17 00:00:00 2001 From: Dominick Allen Date: Tue, 1 Oct 2024 23:04:25 -0500 Subject: Fix move assignment operators or delete them to prevent leaks. --- include/fud_c_file.hpp | 262 +++++++++++++++++++++++++++++++++++++++++----- include/fud_directory.hpp | 2 +- include/fud_sqlite.hpp | 2 +- include/fud_string.hpp | 2 + 4 files changed, 241 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/fud_c_file.hpp b/include/fud_c_file.hpp index 9605ff0..45a8b54 100644 --- a/include/fud_c_file.hpp +++ b/include/fud_c_file.hpp @@ -18,8 +18,8 @@ #ifndef FUD_C_FILE_HPP #define FUD_C_FILE_HPP -#include "fud_status.hpp" #include "fud_result.hpp" +#include "fud_status.hpp" #include "fud_string.hpp" #include @@ -88,77 +88,289 @@ struct [[nodiscard]] WriteResult { FudStatus status{FudStatus::Success}; }; -class CBinaryFile { +namespace detail { + +template +class CFile { public: - CBinaryFile(const String& filename, CFileMode mode); + [[nodiscard]] bool isOpen() const + { + const auto& self = static_cast(*this); + return self.m_file != nullptr; + } - CBinaryFile(const String& filename, CFileMode mode, const String& extraFlags); + FudStatus open() + { + auto& self = static_cast(*this); + if (!self.m_filename.valid()) { + return FudStatus::ObjectInvalid; + } + self.m_file = fopen(self.m_filename.c_str(), self.m_mode.c_str()); + return self.m_file != nullptr ? FudStatus::Success : FudStatus::Failure; + } - CBinaryFile(const CBinaryFile& rhs) = delete; + void close() + { + auto& self = static_cast(*this); + if (self.m_file != nullptr) { + fclose(self.m_file); + self.m_file = nullptr; + } + } - ~CBinaryFile(); + const FILE* file() const + { + const auto& self = static_cast(*this); + return self.m_file; + } - CBinaryFile& operator=(const CBinaryFile& rhs) = delete; + FILE* file() + { + auto& self = static_cast(*this); + return self.m_file; + } - CBinaryFile& operator=(CBinaryFile&& rhs); + [[nodiscard]] Result size() const + { + const auto& self = static_cast(*this); + using RetType = Result; + if (!self.isOpen()) { + return RetType::error(FudStatus::OperationInvalid); + } - FudStatus open(); + auto seekStatus = fseek(self.m_file, 0, SEEK_END); + if (seekStatus != 0) { + return RetType::error(FudStatus::Failure); + } - void close(); + auto fileSizeResult = ftell(self.m_file); + if (fileSizeResult < 0) { + return RetType::error(FudStatus::Failure); + } - const FILE* file() const; - [[nodiscard]] FILE* file(); + auto fileSize = static_cast(fileSizeResult); - [[nodiscard]] bool isOpen() const; + auto resetStatus = self.reset(); + if (resetStatus != FudStatus::Success) { + return RetType::error(resetStatus); + } - [[nodiscard]] Result size() const; + return RetType::okay(fileSize); + } - [[nodiscard]] ReadResult read(void* destination, size_t destinationSize, size_t length); + [[nodiscard]] ReadResult read(void* destination, size_t destinationSize, size_t length) + { + auto& self = static_cast(*this); + return self.read(destination, destinationSize, length, 0); + } - [[nodiscard]] ReadResult read(void* destination, size_t destinationSize, size_t length, size_t offset); + [[nodiscard]] ReadResult read(void* destination, size_t destinationSize, size_t length, size_t offset) + { + auto& self = static_cast(*this); + ReadResult result{}; + if (length == 0) { + return result; + } + + if (destination == nullptr) { + result.status = FudStatus::NullPointer; + return result; + } + + if (offset > LONG_MAX || SIZE_MAX - offset < length || destinationSize < length) { + result.status = FudStatus::InvalidInput; + return result; + } + + auto fileSizeResult = self.size(); + if (fileSizeResult.isError()) { + result.status = fileSizeResult.getError(); + return result; + } + + auto fileSize = fileSizeResult.getOkay(); + if (offset + length > fileSize) { + result.status = FudStatus::InvalidInput; + return result; + } + + auto seekResult = fseek(self.m_file, static_cast(offset), SEEK_SET); + if (seekResult != 0) { + result.status = FudStatus::Failure; + return result; + } + + auto* destBytes = static_cast(destination); + result.bytesRead = fread(destBytes, 1, length, self.m_file); + static_cast(self.reset()); + if (result.bytesRead != length) { + result.status = FudStatus::Partial; + } else { + result.status = FudStatus::Success; + } + + return result; + } template [[nodiscard]] ReadResult read(T& destination, size_t length) { - return read(destination, length, 0); + auto& self = static_cast(*this); + return self.read(destination, length, 0); } template [[nodiscard]] ReadResult read(T& destination, size_t length, size_t offset) { - return read(&destination, sizeof(destination), length, offset); + auto& self = static_cast(*this); + return self.read(&destination, sizeof(destination), length, offset); + } + + [[nodiscard]] WriteResult write(const void* source, size_t sourceSize, size_t length) + { + auto& self = static_cast(*this); + auto offsetResult = self.size(); + if (offsetResult.isError()) { + return WriteResult{0, offsetResult.getError()}; + } + + return self.write(source, sourceSize, length, offsetResult.getOkay()); } - [[nodiscard]] WriteResult write(const void* source, size_t sourceSize, size_t length); + [[nodiscard]] WriteResult write(const void* source, size_t sourceSize, size_t length, size_t offset) + { + auto& self = static_cast(*this); + WriteResult result{}; + if (length == 0) { + return result; + } + + if (source == nullptr) { + result.status = FudStatus::NullPointer; + return result; + } + + if (offset > LONG_MAX || SIZE_MAX - offset < length || sourceSize < length) { + result.status = FudStatus::InvalidInput; + return result; + } + + auto fileSizeResult = size(); + if (fileSizeResult.isError()) { + result.status = fileSizeResult.getError(); + return result; + } + + // TODO: find the proper way of handling a write beyond the end of the file + auto fileSize = fileSizeResult.getOkay(); + int seekResult; + if (offset > fileSize) { + seekResult = fseek(self.m_file, 0, SEEK_END); + } else { + seekResult = fseek(self.m_file, static_cast(offset), SEEK_SET); + } + + if (seekResult != 0) { + result.status = FudStatus::Failure; + return result; + } + + auto* sourceBytes = static_cast(source); + result.bytesWritten = fwrite(sourceBytes, 1, length, self.m_file); + static_cast(self.reset()); + if (result.bytesWritten != length) { + result.status = FudStatus::Partial; + } else { + result.status = FudStatus::Success; + } - [[nodiscard]] WriteResult write(const void* source, size_t sourceSize, size_t length, size_t offset); + return result; + } template [[nodiscard]] WriteResult write(const T& source) { - return write(source, sizeof(source), sizeof(source)); + auto& self = static_cast(*this); + return self.write(source, sizeof(source), sizeof(source)); } template [[nodiscard]] WriteResult write(const T& source, size_t sourceSize, size_t length) { + auto& self = static_cast(*this); auto offsetResult = size(); if (offsetResult.isError()) { return WriteResult{0, offsetResult.getError()}; } - return write(source, sourceSize, length, offsetResult.getOkay()); + return self.write(source, sourceSize, length, offsetResult.getOkay()); } template [[nodiscard]] WriteResult write(const T& source, size_t sourceSize, size_t length, size_t offset) { - return write(static_cast(&source), sourceSize, length, offset); + auto& self = static_cast(*this); + return self.write(static_cast(&source), sourceSize, length, offset); } +private: + FudStatus reset() const + { + const auto& self = static_cast(*this); + if (!isOpen()) { + return FudStatus::OperationInvalid; + } + auto result = fseek(self.m_file, 0, SEEK_SET); + return result == 0 ? FudStatus::Success : FudStatus::Failure; + } +}; + +} // namespace detail + +class CBinaryFile : public detail::CFile { + friend class CFile; + + public: + CBinaryFile(const String& filename, CFileMode mode); + + CBinaryFile(const String& filename, CFileMode mode, const String& extraFlags); + + CBinaryFile(const CBinaryFile& rhs) = delete; + + CBinaryFile(CBinaryFile&& rhs); + + ~CBinaryFile(); + + CBinaryFile& operator=(const CBinaryFile& rhs) = delete; + + CBinaryFile& operator=(CBinaryFile&& rhs); private: - FudStatus reset() const; + String m_filename; + String m_extraFlags{}; + String m_mode; + CFileMode m_modeFlags; + FILE* m_file{nullptr}; +}; + +class CTextFile : public detail::CFile { + friend class CFile; + + public: + CTextFile(const String& filename, CFileMode mode); + + CTextFile(const String& filename, CFileMode mode, const String& extraFlags); - const String m_filename; + CTextFile(const CTextFile& rhs) = delete; + + CTextFile(CTextFile&& rhs); + + ~CTextFile(); + + CTextFile& operator=(const CTextFile& rhs) = delete; + + CTextFile& operator=(CTextFile&& rhs); + + private: + String m_filename; String m_extraFlags{}; String m_mode; CFileMode m_modeFlags; diff --git a/include/fud_directory.hpp b/include/fud_directory.hpp index e6052c4..cd3576e 100644 --- a/include/fud_directory.hpp +++ b/include/fud_directory.hpp @@ -96,7 +96,7 @@ class Directory { Directory(Directory&& rhs); ~Directory(); Directory& operator=(const Directory& rhs) = delete; - Directory& operator=(Directory&& rhs); + Directory& operator=(Directory&& rhs) = delete; constexpr const String& name() const { return m_name; diff --git a/include/fud_sqlite.hpp b/include/fud_sqlite.hpp index 26e9de3..555e487 100644 --- a/include/fud_sqlite.hpp +++ b/include/fud_sqlite.hpp @@ -105,7 +105,7 @@ class SqliteStatement { SqliteStatement& operator=(const SqliteStatement&) = delete; - SqliteStatement& operator=(SqliteStatement&& rhs); + SqliteStatement& operator=(SqliteStatement&& rhs) = delete; bool valid() const; diff --git a/include/fud_string.hpp b/include/fud_string.hpp index 9e423ef..cd8e8f1 100644 --- a/include/fud_string.hpp +++ b/include/fud_string.hpp @@ -126,6 +126,8 @@ class String { const utf8* end() const; private: + void cleanup(); + using BufType = Array; union { BufType m_buffer{BufType::constFill(0)}; -- cgit v1.2.3