// Copyright Epic Games, Inc. All Rights Reserved. #include "internalfile.h" #include #include #include #include #include #if ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC # include # include # include # include #endif #include #define ZEN_USE_SLIST ZEN_PLATFORM_WINDOWS #if ZEN_USE_SLIST == 0 struct FileBufferManager::Impl { zen::RwLock m_Lock; std::list m_FreeBuffers; uint64_t m_BufferSize; uint64_t m_MaxBufferCount; Impl(uint64_t BufferSize, uint64_t MaxBuffers) : m_BufferSize(BufferSize), m_MaxBufferCount(MaxBuffers) {} zen::IoBuffer AllocBuffer() { zen::RwLock::ExclusiveLockScope _(m_Lock); if (m_FreeBuffers.empty()) { return zen::IoBuffer{m_BufferSize, 64 * 1024}; } else { zen::IoBuffer Buffer = std::move(m_FreeBuffers.front()); m_FreeBuffers.pop_front(); return Buffer; } } void ReturnBuffer(zen::IoBuffer Buffer) { zen::RwLock::ExclusiveLockScope _(m_Lock); m_FreeBuffers.push_front(std::move(Buffer)); } }; #else struct FileBufferManager::Impl { struct BufferItem { SLIST_ENTRY ItemEntry; zen::IoBuffer Buffer; }; SLIST_HEADER m_FreeList; uint64_t m_BufferSize; uint64_t m_MaxBufferCount; Impl(uint64_t BufferSize, uint64_t MaxBuffers) : m_BufferSize(BufferSize), m_MaxBufferCount(MaxBuffers) { InitializeSListHead(&m_FreeList); } ~Impl() { while (SLIST_ENTRY* Entry = InterlockedPopEntrySList(&m_FreeList)) { BufferItem* Item = reinterpret_cast(Entry); delete Item; } } zen::IoBuffer AllocBuffer() { SLIST_ENTRY* Entry = InterlockedPopEntrySList(&m_FreeList); if (Entry == nullptr) { return zen::IoBuffer{m_BufferSize, 64 * 1024}; } else { BufferItem* Item = reinterpret_cast(Entry); zen::IoBuffer Buffer = std::move(Item->Buffer); delete Item; // Todo: could keep this around in another list return Buffer; } } void ReturnBuffer(zen::IoBuffer Buffer) { BufferItem* Item = new BufferItem{nullptr, std::move(Buffer)}; InterlockedPushEntrySList(&m_FreeList, &Item->ItemEntry); } }; #endif FileBufferManager::FileBufferManager(uint64_t BufferSize, uint64_t MaxBuffers) { m_Impl = new Impl{BufferSize, MaxBuffers}; } FileBufferManager::~FileBufferManager() { delete m_Impl; } zen::IoBuffer FileBufferManager::AllocBuffer() { return m_Impl->AllocBuffer(); } void FileBufferManager::ReturnBuffer(zen::IoBuffer Buffer) { return m_Impl->ReturnBuffer(Buffer); } ////////////////////////////////////////////////////////////////////////// InternalFile::InternalFile() #if ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC : m_File(nullptr) , m_Mmap(nullptr) #endif { } InternalFile::~InternalFile() { if (m_Memory) zen::Memory::Free(m_Memory); #if ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC if (m_Mmap) munmap(m_Mmap, GetFileSize()); if (m_File) close(int(intptr_t(m_File))); #endif } size_t InternalFile::GetFileSize() { #if ZEN_PLATFORM_WINDOWS ULONGLONG sz; m_File.GetSize(sz); return size_t(sz); #else int Fd = int(intptr_t(m_File)); static_assert(sizeof(decltype(stat::st_size)) == sizeof(uint64_t), "fstat() doesn't support large files"); struct stat Stat; fstat(Fd, &Stat); return size_t(Stat.st_size); #endif } void InternalFile::OpenWrite(std::filesystem::path FileName, bool IsCreate) { bool Success = false; #if ZEN_PLATFORM_WINDOWS const DWORD dwCreationDisposition = IsCreate ? CREATE_ALWAYS : OPEN_EXISTING; HRESULT hRes = m_File.Create(FileName.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, dwCreationDisposition); Success = SUCCEEDED(hRes); #else int OpenFlags = O_RDWR | O_CLOEXEC; OpenFlags |= IsCreate ? O_CREAT | O_TRUNC : 0; int Fd = open(FileName.c_str(), OpenFlags, 0666); if (Fd >= 0) { if (IsCreate) { fchmod(Fd, 0666); } Success = true; m_File = (void*)(intptr_t(Fd)); } #endif // ZEN_PLATFORM_WINDOWS if (Success) { zen::ThrowLastError(fmt::format("Failed to open file for writing: '{}'", FileName)); } } void InternalFile::OpenRead(std::filesystem::path FileName) { bool Success = false; #if ZEN_PLATFORM_WINDOWS const DWORD dwCreationDisposition = OPEN_EXISTING; HRESULT hRes = m_File.Create(FileName.c_str(), GENERIC_READ, FILE_SHARE_READ, dwCreationDisposition); Success = SUCCEEDED(hRes); #else int Fd = open(FileName.c_str(), O_RDONLY); if (Fd >= 0) { Success = true; m_File = (void*)(intptr_t(Fd)); } #endif if (Success) { zen::ThrowLastError(fmt::format("Failed to open file for reading: '{}'", FileName)); } } const void* InternalFile::MemoryMapFile() { auto FileSize = GetFileSize(); if (FileSize <= 100 * 1024 * 1024) { m_Memory = zen::Memory::Alloc(FileSize, 64); Read(m_Memory, FileSize, 0); return m_Memory; } #if ZEN_PLATFORM_WINDOWS m_Mmap.MapFile(m_File); return m_Mmap.GetData(); #else int Fd = int(intptr_t(m_File)); m_Mmap = mmap(nullptr, FileSize, PROT_READ, MAP_PRIVATE, Fd, 0); return m_Mmap; #endif } void InternalFile::Read(void* Data, uint64_t Size, uint64_t Offset) { bool Success; #if ZEN_PLATFORM_WINDOWS OVERLAPPED ovl{}; ovl.Offset = DWORD(Offset & 0xffff'ffffu); ovl.OffsetHigh = DWORD(Offset >> 32); HRESULT hRes = m_File.Read(Data, gsl::narrow(Size), &ovl); Success = SUCCEEDED(hRes); #else int Fd = int(intptr_t(m_File)); int BytesRead = pread(Fd, Data, Size, Offset); Success = (BytesRead > 0); #endif if (Success) { zen::ThrowLastError(fmt::format("Failed to read from file '{}'", "")); // zen::PathFromHandle(m_File))); } } void InternalFile::Write(const void* Data, uint64_t Size, uint64_t Offset) { bool Success; #if ZEN_PLATFORM_WINDOWS OVERLAPPED Ovl{}; Ovl.Offset = DWORD(Offset & 0xffff'ffffu); Ovl.OffsetHigh = DWORD(Offset >> 32); HRESULT hRes = m_File.Write(Data, gsl::narrow(Size), &Ovl); Success = SUCCEEDED(hRes); #else int Fd = int(intptr_t(m_File)); int BytesWritten = pwrite(Fd, Data, Size, Offset); Success = (BytesWritten > 0); #endif if (Success) { zen::ThrowLastError(fmt::format("Failed to write to file '{}'", zen::PathFromHandle(m_File))); } }