summaryrefslogtreecommitdiff
path: root/include/fud_c_file.hpp
diff options
context:
space:
mode:
authorDominick Allen <djallen@librehumanitas.org>2024-10-01 23:04:25 -0500
committerDominick Allen <djallen@librehumanitas.org>2024-10-01 23:04:25 -0500
commit3a18a6dcab45467e779e91c7b346aa3b148e8b9c (patch)
treeba0d7d521179c2d3fbd7d989eb2033cd2a86dbaf /include/fud_c_file.hpp
parent4ef88103f74a3a6e8e36ae9eff80f641e20bd1a1 (diff)
Fix move assignment operators or delete them to prevent leaks.
Diffstat (limited to 'include/fud_c_file.hpp')
-rw-r--r--include/fud_c_file.hpp262
1 files changed, 237 insertions, 25 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;