diff options
author | Dominick Allen <djallen@librehumanitas.org> | 2024-10-01 23:04:25 -0500 |
---|---|---|
committer | Dominick Allen <djallen@librehumanitas.org> | 2024-10-01 23:04:25 -0500 |
commit | 3a18a6dcab45467e779e91c7b346aa3b148e8b9c (patch) | |
tree | ba0d7d521179c2d3fbd7d989eb2033cd2a86dbaf | |
parent | 4ef88103f74a3a6e8e36ae9eff80f641e20bd1a1 (diff) |
Fix move assignment operators or delete them to prevent leaks.
-rw-r--r-- | include/fud_c_file.hpp | 262 | ||||
-rw-r--r-- | include/fud_directory.hpp | 2 | ||||
-rw-r--r-- | include/fud_sqlite.hpp | 2 | ||||
-rw-r--r-- | include/fud_string.hpp | 2 | ||||
-rw-r--r-- | source/fud_c_file.cpp | 199 | ||||
-rw-r--r-- | source/fud_directory.cpp | 12 | ||||
-rw-r--r-- | source/fud_sqlite.cpp | 20 | ||||
-rw-r--r-- | source/fud_string.cpp | 21 |
8 files changed, 305 insertions, 215 deletions
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 <cstdint> @@ -88,77 +88,289 @@ struct [[nodiscard]] WriteResult { FudStatus status{FudStatus::Success}; }; -class CBinaryFile { +namespace detail { + +template <typename Derived> +class CFile { public: - CBinaryFile(const String& filename, CFileMode mode); + [[nodiscard]] bool isOpen() const + { + const auto& self = static_cast<const Derived&>(*this); + return self.m_file != nullptr; + } - CBinaryFile(const String& filename, CFileMode mode, const String& extraFlags); + FudStatus open() + { + auto& self = static_cast<Derived&>(*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<Derived&>(*this); + if (self.m_file != nullptr) { + fclose(self.m_file); + self.m_file = nullptr; + } + } - ~CBinaryFile(); + const FILE* file() const + { + const auto& self = static_cast<const Derived&>(*this); + return self.m_file; + } - CBinaryFile& operator=(const CBinaryFile& rhs) = delete; + FILE* file() + { + auto& self = static_cast<Derived&>(*this); + return self.m_file; + } - CBinaryFile& operator=(CBinaryFile&& rhs); + [[nodiscard]] Result<size_t, FudStatus> size() const + { + const auto& self = static_cast<const Derived&>(*this); + using RetType = Result<size_t, FudStatus>; + 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<size_t>(fileSizeResult); - [[nodiscard]] bool isOpen() const; + auto resetStatus = self.reset(); + if (resetStatus != FudStatus::Success) { + return RetType::error(resetStatus); + } - [[nodiscard]] Result<size_t, FudStatus> 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<Derived&>(*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<Derived&>(*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<long>(offset), SEEK_SET); + if (seekResult != 0) { + result.status = FudStatus::Failure; + return result; + } + + auto* destBytes = static_cast<char*>(destination); + result.bytesRead = fread(destBytes, 1, length, self.m_file); + static_cast<void>(self.reset()); + if (result.bytesRead != length) { + result.status = FudStatus::Partial; + } else { + result.status = FudStatus::Success; + } + + return result; + } template <typename T> [[nodiscard]] ReadResult read(T& destination, size_t length) { - return read(destination, length, 0); + auto& self = static_cast<Derived&>(*this); + return self.read(destination, length, 0); } template <typename T> [[nodiscard]] ReadResult read(T& destination, size_t length, size_t offset) { - return read(&destination, sizeof(destination), length, offset); + auto& self = static_cast<Derived&>(*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<Derived&>(*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<Derived&>(*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<long>(offset), SEEK_SET); + } + + if (seekResult != 0) { + result.status = FudStatus::Failure; + return result; + } + + auto* sourceBytes = static_cast<const char*>(source); + result.bytesWritten = fwrite(sourceBytes, 1, length, self.m_file); + static_cast<void>(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 <typename T> [[nodiscard]] WriteResult write(const T& source) { - return write(source, sizeof(source), sizeof(source)); + auto& self = static_cast<Derived&>(*this); + return self.write(source, sizeof(source), sizeof(source)); } template <typename T> [[nodiscard]] WriteResult write(const T& source, size_t sourceSize, size_t length) { + auto& self = static_cast<Derived&>(*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 <typename T> [[nodiscard]] WriteResult write(const T& source, size_t sourceSize, size_t length, size_t offset) { - return write(static_cast<const void*>(&source), sourceSize, length, offset); + auto& self = static_cast<Derived&>(*this); + return self.write(static_cast<const void*>(&source), sourceSize, length, offset); } +private: + FudStatus reset() const + { + const auto& self = static_cast<const Derived&>(*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<CBinaryFile> { + 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<CTextFile> { + 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<utf8, SSO_BUF_SIZE>; union { BufType m_buffer{BufType::constFill(0)}; diff --git a/source/fud_c_file.cpp b/source/fud_c_file.cpp index b323847..e2e71bf 100644 --- a/source/fud_c_file.cpp +++ b/source/fud_c_file.cpp @@ -34,186 +34,71 @@ CBinaryFile::CBinaryFile(const String& filename, CFileMode mode, const String& e { } -CBinaryFile::~CBinaryFile() { - close(); -} - -FudStatus CBinaryFile::open() +CBinaryFile::CBinaryFile(CBinaryFile&& rhs) : + m_filename{std::move(rhs.m_filename)}, + m_extraFlags{std::move(rhs.m_extraFlags)}, + m_mode{std::move(rhs.m_mode)}, + m_modeFlags{std::move(rhs.m_modeFlags)}, + m_file{rhs.m_file} { - if (!m_filename.valid()) { - return FudStatus::ObjectInvalid; - } - m_file = fopen(m_filename.c_str(), m_mode.c_str()); - return m_file != nullptr ? FudStatus::Success : FudStatus::Failure; } -void CBinaryFile::close() -{ - if (m_file != nullptr) { - fclose(m_file); - m_file = nullptr; - } +CBinaryFile::~CBinaryFile() { + close(); } -const FILE* CBinaryFile::file() const +CBinaryFile& CBinaryFile::operator=(CBinaryFile&& rhs) { - return m_file; -} + close(); -FILE* CBinaryFile::file() -{ - return m_file; -} + m_filename = std::move(rhs.m_filename); + m_extraFlags = std::move(rhs.m_extraFlags); + m_mode = std::move(rhs.m_mode); + m_modeFlags = std::move(rhs.m_modeFlags); + m_file = rhs.m_file; -bool CBinaryFile::isOpen() const -{ - return m_file != nullptr; + return *this; } -Result<size_t, FudStatus> CBinaryFile::size() const +CTextFile::CTextFile(const String& filename, CFileMode mode) + : m_filename{filename}, + m_mode{CTextFileModeFromFlags(mode)}, + m_modeFlags{mode} { - using RetType = Result<size_t, FudStatus>; - if (!isOpen()) { - return RetType::error(FudStatus::OperationInvalid); - } - - auto seekStatus = fseek(m_file, 0, SEEK_END); - if (seekStatus != 0) { - return RetType::error(FudStatus::Failure); - } - - auto fileSizeResult = ftell(m_file); - if (fileSizeResult < 0) { - return RetType::error(FudStatus::Failure); - } - - auto fileSize = static_cast<size_t>(fileSizeResult); - - auto resetStatus = reset(); - if (resetStatus != FudStatus::Success) { - return RetType::error(resetStatus); - } - - return RetType::okay(fileSize); } -ReadResult CBinaryFile::read(void* destination, size_t destinationSize, size_t length) +CTextFile::CTextFile(const String& filename, CFileMode mode, const String& extraFlags) + : m_filename{filename}, + m_extraFlags{extraFlags}, + m_mode{String(CTextFileModeFromFlags(mode)).catenate(extraFlags)}, + m_modeFlags{mode} { - return read(destination, destinationSize, length, 0); } -ReadResult CBinaryFile::read(void* destination, size_t destinationSize, size_t length, size_t offset) +CTextFile::CTextFile(CTextFile&& rhs) : + m_filename{std::move(rhs.m_filename)}, + m_extraFlags{std::move(rhs.m_extraFlags)}, + m_mode{std::move(rhs.m_mode)}, + m_modeFlags{std::move(rhs.m_modeFlags)}, + m_file{rhs.m_file} { - 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 = 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(m_file, static_cast<long>(offset), SEEK_SET); - if (seekResult != 0) { - result.status = FudStatus::Failure; - return result; - } - - auto* destBytes = static_cast<char*>(destination); - result.bytesRead = fread(destBytes, 1, length, m_file); - static_cast<void>(reset()); - if (result.bytesRead != length) { - result.status = FudStatus::Partial; - } else { - result.status = FudStatus::Success; - } - - return result; } -WriteResult CBinaryFile::write(const void* source, size_t sourceSize, size_t length) -{ - auto offsetResult = size(); - if (offsetResult.isError()) { - return WriteResult{0, offsetResult.getError()}; - } - - return write(source, sourceSize, length, offsetResult.getOkay()); +CTextFile::~CTextFile() { + close(); } -WriteResult CBinaryFile::write(const void* source, size_t sourceSize, size_t length, size_t offset) +CTextFile& CTextFile::operator=(CTextFile&& rhs) { - 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: proper way of handling this - auto fileSize = fileSizeResult.getOkay(); - int seekResult; - if (offset > fileSize) { - seekResult = fseek(m_file, 0, SEEK_END); - } else { - seekResult = fseek(m_file, static_cast<long>(offset), SEEK_SET); - } - - if (seekResult != 0) { - result.status = FudStatus::Failure; - return result; - } - - auto* sourceBytes = static_cast<const char*>(source); - result.bytesWritten = fwrite(sourceBytes, 1, length, m_file); - static_cast<void>(reset()); - if (result.bytesWritten != length) { - result.status = FudStatus::Partial; - } else { - result.status = FudStatus::Success; - } - - return result; -} + close(); + + m_filename = std::move(rhs.m_filename); + m_extraFlags = std::move(rhs.m_extraFlags); + m_mode = std::move(rhs.m_mode); + m_modeFlags = std::move(rhs.m_modeFlags); + m_file = rhs.m_file; -FudStatus CBinaryFile::reset() const { - if (!isOpen()) { - return FudStatus::OperationInvalid; - } - auto result = fseek(m_file, 0, SEEK_SET); - return result == 0 ? FudStatus::Success : FudStatus::Failure; + return *this; } } // namespace fud diff --git a/source/fud_directory.cpp b/source/fud_directory.cpp index 6df5bcc..6851158 100644 --- a/source/fud_directory.cpp +++ b/source/fud_directory.cpp @@ -115,18 +115,6 @@ Directory::Directory(Directory&& rhs) : rhs.m_dirFd = -1; } -Directory& Directory::operator=(Directory&& rhs) -{ - m_name = std::move(rhs.m_name); - m_directory = rhs.m_directory; - m_dirFd = rhs.m_dirFd; - - rhs.m_directory = nullptr; - rhs.m_dirFd = -1; - - return *this; -} - Directory::~Directory() { if (m_directory != nullptr) { diff --git a/source/fud_sqlite.cpp b/source/fud_sqlite.cpp index 7ad8a12..f573f1c 100644 --- a/source/fud_sqlite.cpp +++ b/source/fud_sqlite.cpp @@ -49,6 +49,11 @@ SqliteDb::~SqliteDb() SqliteDb& SqliteDb::operator=(SqliteDb&& rhs) { + if (m_dbHandle != nullptr) { + sqlite3_close(m_dbHandle); + m_dbHandle = nullptr; + } + m_name = std::move(rhs.m_name); m_nameValid = rhs.m_nameValid; m_dbHandle = rhs.m_dbHandle; @@ -162,21 +167,6 @@ SqliteStatement::SqliteStatement(SqliteStatement&& rhs) : rhs.m_preparedStatement = nullptr; } -SqliteStatement& SqliteStatement::operator=(SqliteStatement&& rhs) -{ - m_input = std::move(rhs.m_input); - m_tail = rhs.m_tail; - m_status = rhs.m_status; - m_errorCode = rhs.m_errorCode; - m_preparedStatement = rhs.m_preparedStatement; - - rhs.m_tail = nullptr; - rhs.m_status = FudStatus::ObjectInvalid; - rhs.m_preparedStatement = nullptr; - - return *this; -} - SqliteStatement::~SqliteStatement() { if (m_preparedStatement != nullptr) { diff --git a/source/fud_string.cpp b/source/fud_string.cpp index 97acb52..b10f6ee 100644 --- a/source/fud_string.cpp +++ b/source/fud_string.cpp @@ -95,14 +95,17 @@ String::String(String&& rhs) : m_length{rhs.m_length}, m_capacity{rhs.m_capacity String::~String() { - if (isLarge() && m_data != nullptr) { - fudFree(m_data); - m_data = nullptr; - } + cleanup(); } String& String::operator=(const String& rhs) { + if (this == &rhs) { + return *this; + } + + cleanup(); + m_length = rhs.m_length; m_capacity = rhs.m_capacity; if (rhs.valid()) { @@ -118,6 +121,8 @@ String& String::operator=(const String& rhs) String& String::operator=(String&& rhs) { + cleanup(); + m_length = rhs.m_length; m_capacity = rhs.m_capacity; if (rhs.isLarge()) { @@ -130,6 +135,14 @@ String& String::operator=(String&& rhs) return *this; } +void String::cleanup() +{ + if (isLarge() && m_data != nullptr) { + fudFree(m_data); + m_data = nullptr; + } +} + bool String::nullTerminated() const { return data() != nullptr && m_length < m_capacity && data()[m_length] == '\0'; |