From ad6a740a73378f0b099767d853cba635359e6af2 Mon Sep 17 00:00:00 2001 From: ImBenji Date: Tue, 2 Dec 2025 15:08:10 +0000 Subject: [PATCH] Implement file handling improvements with Unreal compatibility and enhanced read/write methods --- cpp/CMakeLists.txt | 1 + cpp/src/Private/sweepstore/header.cpp | 80 ++++++------- .../Private/sweepstore/utils/file_handle.cpp | 110 ++++++++++++++++++ cpp/src/Public/sweepstore/utils/file_handle.h | 31 +++-- 4 files changed, 170 insertions(+), 52 deletions(-) create mode 100644 cpp/src/Private/sweepstore/utils/file_handle.cpp diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 90d854f..ab4c268 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -21,6 +21,7 @@ add_executable(main src/Private/sweepstore/concurrency.cpp src/Public/sweepstore/utils/file_lock.h src/Public/sweepstore/utils/file_handle.h + src/Private/sweepstore/utils/file_handle.cpp src/Public/sweepstore/header.h src/Private/sweepstore/benchmark.cpp ) diff --git a/cpp/src/Private/sweepstore/header.cpp b/cpp/src/Private/sweepstore/header.cpp index f9a3652..bdde5f6 100644 --- a/cpp/src/Private/sweepstore/header.cpp +++ b/cpp/src/Private/sweepstore/header.cpp @@ -5,9 +5,9 @@ #include "sweepstore/utils/helpers.h" std::string SweepstoreHeader::readMagicNumber() { - file->seekg(0, std::ios::beg); + file.readSeek(0, std::ios::beg); char buffer[4]; - file->read(buffer, 4); + file.readBytes(buffer, 4); return std::string(buffer, 4); } @@ -15,14 +15,14 @@ void SweepstoreHeader::writeMagicNumber(const std::string& magicNumber) { if (magicNumber.size() != 4) { throw std::invalid_argument("Magic number must be exactly 4 characters long."); } - file->seekp(0, std::ios::beg); - file->write(magicNumber.c_str(), 4); + file.writeSeek(0, std::ios::beg); + file.writeBytes(magicNumber.c_str(), 4); } std::string SweepstoreHeader::readVersion() { - file->seekg(4, std::ios::beg); + file.readSeek(4, std::ios::beg); char buffer[12]; - file->read(buffer, 12); + file.readBytes(buffer, 12); // Trim leading and trailing spaces std::string version(buffer, 12); @@ -40,46 +40,46 @@ void SweepstoreHeader::writeVersion(const std::string& version) { std::string paddedVersion = " " + version; paddedVersion.resize(12, ' '); - file->seekp(4, std::ios::beg); - file->write(paddedVersion.c_str(), 12); + file.writeSeek(4, std::ios::beg); + file.writeBytes(paddedVersion.c_str(), 12); } SweepstorePointer SweepstoreHeader::readAddressTablePointer() { - file->seekg(16, std::ios::beg); + file.readSeek(16, std::ios::beg); int64_t address; - file->read(reinterpret_cast(&address), sizeof(address)); + file.readBytes(reinterpret_cast(&address), sizeof(address)); return address; // Implicit conversion to SweepstorePointer } void SweepstoreHeader::writeAddressTablePointer(const SweepstorePointer& ptr) { - file->seekp(16, std::ios::beg); + file.writeSeek(16, std::ios::beg); int64_t address = ptr; - file->write(reinterpret_cast(&address), sizeof(address)); + file.writeBytes(reinterpret_cast(&address), sizeof(address)); } uint32_t SweepstoreHeader::readFreeListCount() { - file->seekg(24, std::ios::beg); + file.readSeek(24, std::ios::beg); uint32_t count; - file->read(reinterpret_cast(&count), sizeof(count)); + file.readBytes(reinterpret_cast(&count), sizeof(count)); return count; } void SweepstoreHeader::writeFreeListCount(uint32_t count) { - file->seekp(24, std::ios::beg); - file->write(reinterpret_cast(&count), sizeof(count)); + file.writeSeek(24, std::ios::beg); + file.writeBytes(reinterpret_cast(&count), sizeof(count)); } bool SweepstoreHeader::readIsFreeListLifted() { - file->seekg(28, std::ios::beg); + file.readSeek(28, std::ios::beg); char flag; - file->read(&flag, sizeof(flag)); + file.readBytes(&flag, sizeof(flag)); return flag != 0; } void SweepstoreHeader::writeIsFreeListLifted(bool isLifted) { - file->seekp(28, std::ios::beg); + file.writeSeek(28, std::ios::beg); char flag = isLifted ? 1 : 0; - file->write(&flag, sizeof(flag)); + file.writeBytes(&flag, sizeof(flag)); } void SweepstoreHeader::initialise() { @@ -92,52 +92,52 @@ void SweepstoreHeader::initialise() { } uint64_t SweepstoreConcurrencyHeader::readMasterIdentifier() { - file->seekg(29, std::ios::beg); + file.readSeek(29, std::ios::beg); uint64_t identifier; - file->read(reinterpret_cast(&identifier), sizeof(identifier)); + file.readBytes(reinterpret_cast(&identifier), sizeof(identifier)); return identifier; } void SweepstoreConcurrencyHeader::writeMasterIdentifier(uint64_t identifier) { - file->seekp(29, std::ios::beg); - file->write(reinterpret_cast(&identifier), sizeof(identifier)); + file.writeSeek(29, std::ios::beg); + file.writeBytes(reinterpret_cast(&identifier), sizeof(identifier)); } uint32_t SweepstoreConcurrencyHeader::readMasterHeartbeat() { - file->seekg(37, std::ios::beg); + file.readSeek(37, std::ios::beg); uint32_t heartbeat; - file->read(reinterpret_cast(&heartbeat), sizeof(heartbeat)); + file.readBytes(reinterpret_cast(&heartbeat), sizeof(heartbeat)); return heartbeat; } void SweepstoreConcurrencyHeader::writeMasterHeartbeat(uint32_t heartbeat) { - file->seekp(37, std::ios::beg); - file->write(reinterpret_cast(&heartbeat), sizeof(heartbeat)); + file.writeSeek(37, std::ios::beg); + file.writeBytes(reinterpret_cast(&heartbeat), sizeof(heartbeat)); } uint32_t SweepstoreConcurrencyHeader::readNumberOfWorkers() { - file->seekg(41, std::ios::beg); + file.readSeek(41, std::ios::beg); uint32_t numWorkers; - file->read(reinterpret_cast(&numWorkers), sizeof(numWorkers)); + file.readBytes(reinterpret_cast(&numWorkers), sizeof(numWorkers)); return numWorkers; } void SweepstoreConcurrencyHeader::writeNumberOfWorkers(uint32_t numWorkers) { - file->seekp(41, std::ios::beg); - file->write(reinterpret_cast(&numWorkers), sizeof(numWorkers)); + file.writeSeek(41, std::ios::beg); + file.writeBytes(reinterpret_cast(&numWorkers), sizeof(numWorkers)); } bool SweepstoreConcurrencyHeader::readIsReadAllowed() { - file->seekg(45, std::ios::beg); + file.readSeek(45, std::ios::beg); char flag; - file->read(&flag, sizeof(flag)); + file.readBytes(&flag, sizeof(flag)); return flag != 0; } void SweepstoreConcurrencyHeader::writeIsReadAllowed(bool isAllowed) { - file->seekp(45, std::ios::beg); + file.writeSeek(45, std::ios::beg); char flag = isAllowed ? 1 : 0; - file->write(&flag, sizeof(flag)); + file.writeBytes(&flag, sizeof(flag)); } void SweepstoreConcurrencyHeader::initialise(int concurrentWorkers) { @@ -187,8 +187,8 @@ void SweepstoreWorkerTicket::write(SweepstoreWorkerTicketSnapshot &snapshot) { char* dataPtr = reinterpret_cast(data.data()); // Write to file - file->seekp(getOffset()); - file->write(dataPtr, data.size()); + file.writeSeek(getOffset()); + file.writeBytes(dataPtr, data.size()); file->flush(); } @@ -200,9 +200,9 @@ bool SweepstoreWorkerTicket::writable() { SweepstoreWorkerTicketSnapshot SweepstoreWorkerTicket::snapshot() { SweepstoreFileLock lock(file.getPath(), SweepstoreFileLock::Mode::Shared); lock.lock(); - file->seekg(getOffset()); + file.readSeek(getOffset()); std::unique_ptr buffer(new char[TICKET_SIZE]); - file->read(buffer.get(), TICKET_SIZE); + file.readBytes(buffer.get(), TICKET_SIZE); lock.unlock(); RandomAccessMemory ram(reinterpret_cast(buffer.get()), TICKET_SIZE); diff --git a/cpp/src/Private/sweepstore/utils/file_handle.cpp b/cpp/src/Private/sweepstore/utils/file_handle.cpp new file mode 100644 index 0000000..25c182a --- /dev/null +++ b/cpp/src/Private/sweepstore/utils/file_handle.cpp @@ -0,0 +1,110 @@ +#include "sweepstore/utils/file_handle.h" + +// Constructor +SweepstoreFileHandle::SweepstoreFileHandle(const std::string& p, std::ios::openmode mode) + : path(p) +#ifdef WITH_UNREAL +{ + IPlatformFile& platformFile = FPlatformFileManager::Get().GetPlatformFile(); + + // Map std::ios flags to Unreal flags + bool read = (mode & std::ios::in) != 0; + bool write = (mode & std::ios::out) != 0; + + if (read && write) { + unrealHandle = platformFile.OpenReadWrite(*FString(path.c_str()), true); + } else if (write) { + unrealHandle = platformFile.OpenWrite(*FString(path.c_str()), false, false); + } else { + unrealHandle = platformFile.OpenRead(*FString(path.c_str()), false); + } + + if (!unrealHandle) { + throw std::runtime_error("Failed to open file: " + path); + } +} +#else + , stream(std::make_unique(p, mode)) +{ + if (!stream->is_open()) { + throw std::runtime_error("Failed to open file: " + path); + } +} +#endif + +// isOpen +bool SweepstoreFileHandle::isOpen() const { +#ifdef WITH_UNREAL + return unrealHandle != nullptr; +#else + return stream && stream->is_open(); +#endif +} + +// close +void SweepstoreFileHandle::close() { +#ifdef WITH_UNREAL + if (unrealHandle) { + delete unrealHandle; + unrealHandle = nullptr; + } +#else + if (stream) { + stream->close(); + } +#endif +} + +// readSeek +void SweepstoreFileHandle::readSeek(std::streampos pos, std::ios::seekdir dir) { +#ifdef WITH_UNREAL + // Unreal doesn't have separate read/write pointers, so just seek + int64 unrealPos = static_cast(pos); + if (dir == std::ios::beg) { + unrealHandle->Seek(unrealPos); + } else if (dir == std::ios::cur) { + unrealHandle->Seek(unrealHandle->Tell() + unrealPos); + } else if (dir == std::ios::end) { + unrealHandle->SeekFromEnd(unrealPos); + } +#else + #ifdef _WIN32 + // On Windows, flush output and sync to invalidate input buffer + stream->flush(); + stream->seekp(pos, dir); // Sync put pointer + #endif + stream->seekg(pos, dir); +#endif +} + +// writeSeek +void SweepstoreFileHandle::writeSeek(std::streampos pos, std::ios::seekdir dir) { +#ifdef WITH_UNREAL + // Same as readSeek for Unreal + readSeek(pos, dir); +#else + #ifdef _WIN32 + stream->flush(); // Flush any pending writes + #endif + stream->seekp(pos, dir); +#endif +} + +// readBytes +void SweepstoreFileHandle::readBytes(char* buffer, std::streamsize size) { +#ifdef WITH_UNREAL + unrealHandle->Read(reinterpret_cast(buffer), size); +#else + stream->read(buffer, size); +#endif +} + +// writeBytes +void SweepstoreFileHandle::writeBytes(const char* buffer, std::streamsize size) { +#ifdef WITH_UNREAL + unrealHandle->Write(reinterpret_cast(buffer), size); + unrealHandle->Flush(); // Unreal requires explicit flush +#else + stream->write(buffer, size); +#endif +} \ No newline at end of file diff --git a/cpp/src/Public/sweepstore/utils/file_handle.h b/cpp/src/Public/sweepstore/utils/file_handle.h index bc98188..5b32b04 100644 --- a/cpp/src/Public/sweepstore/utils/file_handle.h +++ b/cpp/src/Public/sweepstore/utils/file_handle.h @@ -4,21 +4,26 @@ #include #include +#ifdef WITH_UNREAL + #include "HAL/PlatformFileManager.h" + #include "GenericPlatform/GenericPlatformFile.h" +#endif + class SweepstoreFileHandle { private: std::string path; +#ifdef WITH_UNREAL + IFileHandle* unrealHandle; +#else std::unique_ptr stream; +#endif public: - SweepstoreFileHandle(const std::string& p, std::ios::openmode mode = std::ios::in | std::ios::out | std::ios::binary) - : path(p), stream(std::make_unique(p, mode)) { - if (!stream->is_open()) { - throw std::runtime_error("Failed to open file: " + path); - } - } + SweepstoreFileHandle(const std::string& p, std::ios::openmode mode = std::ios::in | std::ios::out | std::ios::binary); const std::string& getPath() const { return path; } +#ifndef WITH_UNREAL std::fstream& getStream() { return *stream; } const std::fstream& getStream() const { return *stream; } @@ -28,14 +33,16 @@ public: std::fstream& operator*() { return *stream; } const std::fstream& operator*() const { return *stream; } +#endif - bool isOpen() const { return stream && stream->is_open(); } + bool isOpen() const; + void close(); - void close() { - if (stream) { - stream->close(); - } - } + // Windows-compatible I/O wrappers + void readSeek(std::streampos pos, std::ios::seekdir dir = std::ios::beg); + void writeSeek(std::streampos pos, std::ios::seekdir dir = std::ios::beg); + void readBytes(char* buffer, std::streamsize size); + void writeBytes(const char* buffer, std::streamsize size); SweepstoreFileHandle(SweepstoreFileHandle&&) noexcept = default; SweepstoreFileHandle& operator=(SweepstoreFileHandle&&) noexcept = default;