174 lines
4.8 KiB
C++
174 lines
4.8 KiB
C++
#include "sweepstore/utils/file_handle.h"
|
|
#include "sweepstore/utils/timing.h"
|
|
|
|
// Constructor - just stores path and mode, actual stream is created per-thread
|
|
SweepstoreFileHandle::SweepstoreFileHandle(const std::string& p, std::ios::openmode mode)
|
|
: path(p)
|
|
, openMode(mode)
|
|
#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
|
|
{
|
|
// Thread-local streams created on demand in getThreadStream()
|
|
}
|
|
#endif
|
|
|
|
#ifndef WITH_UNREAL
|
|
// Get or create the fstream for this thread
|
|
std::fstream& SweepstoreFileHandle::getThreadStream() {
|
|
auto it = streamCache.find(path);
|
|
if (it == streamCache.end() || !it->second || !it->second->is_open()) {
|
|
// Create new stream for this thread
|
|
auto stream = std::make_unique<std::fstream>(path, openMode);
|
|
if (!stream->is_open()) {
|
|
throw std::runtime_error("Failed to open file: " + path);
|
|
}
|
|
streamCache[path] = std::move(stream);
|
|
return *streamCache[path];
|
|
}
|
|
return *it->second;
|
|
}
|
|
|
|
const std::fstream& SweepstoreFileHandle::getThreadStream() const {
|
|
// Use const_cast to reuse the non-const version
|
|
return const_cast<SweepstoreFileHandle*>(this)->getThreadStream();
|
|
}
|
|
#endif
|
|
|
|
// isOpen
|
|
bool SweepstoreFileHandle::isOpen() const {
|
|
#ifdef WITH_UNREAL
|
|
return unrealHandle != nullptr;
|
|
#else
|
|
auto it = streamCache.find(path);
|
|
return it != streamCache.end() && it->second && it->second->is_open();
|
|
#endif
|
|
}
|
|
|
|
// close
|
|
void SweepstoreFileHandle::close() {
|
|
#ifdef WITH_UNREAL
|
|
if (unrealHandle) {
|
|
delete unrealHandle;
|
|
unrealHandle = nullptr;
|
|
}
|
|
#else
|
|
// Close this thread's stream if it exists
|
|
auto it = streamCache.find(path);
|
|
if (it != streamCache.end() && it->second && it->second->is_open()) {
|
|
it->second->close();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if defined(_WIN32) || defined(WITH_UNREAL)
|
|
// flush
|
|
void SweepstoreFileHandle::flush() {
|
|
#ifdef WITH_UNREAL
|
|
if (unrealHandle) {
|
|
unrealHandle->Flush();
|
|
}
|
|
#else
|
|
// Windows-specific implementation for guaranteed flush to disk
|
|
auto& stream = getThreadStream();
|
|
stream.flush();
|
|
|
|
// On Windows, also call sync to push to OS buffers
|
|
// Then open a Windows HANDLE to the same file and call FlushFileBuffers
|
|
// This is more reliable than trying to extract the HANDLE from fstream
|
|
HANDLE h = CreateFileA(
|
|
path.c_str(),
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
if (h != INVALID_HANDLE_VALUE) {
|
|
FlushFileBuffers(h);
|
|
CloseHandle(h);
|
|
}
|
|
#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
|
|
// Windows - simplified to only seek read pointer
|
|
auto& stream = getThreadStream();
|
|
stream.seekg(pos, dir);
|
|
if (stream.fail()) {
|
|
stream.clear();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// writeSeek
|
|
void SweepstoreFileHandle::writeSeek(std::streampos pos, std::ios::seekdir dir) {
|
|
#ifdef WITH_UNREAL
|
|
// Same as readSeek for Unreal
|
|
readSeek(pos, dir);
|
|
#else
|
|
// Windows - simplified to only seek write pointer
|
|
auto& stream = getThreadStream();
|
|
stream.seekp(pos, dir);
|
|
if (stream.fail()) {
|
|
stream.clear();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// readBytes
|
|
void SweepstoreFileHandle::readBytes(char* buffer, std::streamsize size) {
|
|
#ifdef WITH_UNREAL
|
|
unrealHandle->Read(reinterpret_cast<uint8*>(buffer), size);
|
|
#else
|
|
// Windows
|
|
auto& stream = getThreadStream();
|
|
stream.read(buffer, size);
|
|
#endif
|
|
}
|
|
|
|
// writeBytes
|
|
void SweepstoreFileHandle::writeBytes(const char* buffer, std::streamsize size) {
|
|
SWEEPSTORE_TIME_FUNCTION();
|
|
#ifdef WITH_UNREAL
|
|
unrealHandle->Write(reinterpret_cast<const uint8*>(buffer), size);
|
|
unrealHandle->Flush(); // Unreal requires explicit flush
|
|
#else
|
|
// Windows
|
|
auto& stream = getThreadStream();
|
|
stream.write(buffer, size);
|
|
#endif
|
|
}
|
|
#endif // _WIN32 || WITH_UNREAL
|