Implement file handling improvements with Unreal compatibility and enhanced read/write methods

This commit is contained in:
ImBenji
2025-12-02 15:08:10 +00:00
parent 55d5ab7a7b
commit ad6a740a73
4 changed files with 170 additions and 52 deletions

View File

@@ -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
)

View File

@@ -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<char*>(&address), sizeof(address));
file.readBytes(reinterpret_cast<char*>(&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<const char*>(&address), sizeof(address));
file.writeBytes(reinterpret_cast<const char*>(&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<char*>(&count), sizeof(count));
file.readBytes(reinterpret_cast<char*>(&count), sizeof(count));
return count;
}
void SweepstoreHeader::writeFreeListCount(uint32_t count) {
file->seekp(24, std::ios::beg);
file->write(reinterpret_cast<const char*>(&count), sizeof(count));
file.writeSeek(24, std::ios::beg);
file.writeBytes(reinterpret_cast<const char*>(&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<char*>(&identifier), sizeof(identifier));
file.readBytes(reinterpret_cast<char*>(&identifier), sizeof(identifier));
return identifier;
}
void SweepstoreConcurrencyHeader::writeMasterIdentifier(uint64_t identifier) {
file->seekp(29, std::ios::beg);
file->write(reinterpret_cast<const char*>(&identifier), sizeof(identifier));
file.writeSeek(29, std::ios::beg);
file.writeBytes(reinterpret_cast<const char*>(&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<char*>(&heartbeat), sizeof(heartbeat));
file.readBytes(reinterpret_cast<char*>(&heartbeat), sizeof(heartbeat));
return heartbeat;
}
void SweepstoreConcurrencyHeader::writeMasterHeartbeat(uint32_t heartbeat) {
file->seekp(37, std::ios::beg);
file->write(reinterpret_cast<const char*>(&heartbeat), sizeof(heartbeat));
file.writeSeek(37, std::ios::beg);
file.writeBytes(reinterpret_cast<const char*>(&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<char*>(&numWorkers), sizeof(numWorkers));
file.readBytes(reinterpret_cast<char*>(&numWorkers), sizeof(numWorkers));
return numWorkers;
}
void SweepstoreConcurrencyHeader::writeNumberOfWorkers(uint32_t numWorkers) {
file->seekp(41, std::ios::beg);
file->write(reinterpret_cast<const char*>(&numWorkers), sizeof(numWorkers));
file.writeSeek(41, std::ios::beg);
file.writeBytes(reinterpret_cast<const char*>(&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<char*>(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<char[]> buffer(new char[TICKET_SIZE]);
file->read(buffer.get(), TICKET_SIZE);
file.readBytes(buffer.get(), TICKET_SIZE);
lock.unlock();
RandomAccessMemory ram(reinterpret_cast<uint8_t*>(buffer.get()), TICKET_SIZE);

View File

@@ -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<std::fstream>(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<int64>(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<uint8*>(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<const uint8*>(buffer), size);
unrealHandle->Flush(); // Unreal requires explicit flush
#else
stream->write(buffer, size);
#endif
}

View File

@@ -4,21 +4,26 @@
#include <string>
#include <memory>
#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<std::fstream> 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<std::fstream>(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;