// Copyright Epic Games, Inc. All Rights Reserved. #include "zenstore/basicfile.h" #include #include #include #include #include #include #include namespace zen { using namespace fmt::literals; BasicFile::~BasicFile() { Close(); } void BasicFile::Open(std::filesystem::path FileName, bool IsCreate) { std::error_code Ec; Open(FileName, IsCreate, Ec); if (Ec) { throw std::system_error(Ec, "failed to open file '{}'"_format(FileName)); } } void BasicFile::Open(std::filesystem::path FileName, bool IsCreate, std::error_code& Ec) { const DWORD dwCreationDisposition = IsCreate ? CREATE_ALWAYS : OPEN_EXISTING; const DWORD dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; const DWORD dwShareMode = FILE_SHARE_READ; const DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; HANDLE hTemplateFile = nullptr; HANDLE FileHandle = CreateFile(FileName.c_str(), dwDesiredAccess, dwShareMode, /* lpSecurityAttributes */ nullptr, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); if (FileHandle == INVALID_HANDLE_VALUE) { Ec = zen::MakeErrorCodeFromLastError(); } m_FileHandle = FileHandle; } void BasicFile::Close() { if (m_FileHandle) { ::CloseHandle(m_FileHandle); } } void BasicFile::Read(void* Data, uint64_t Size, uint64_t Offset) { OVERLAPPED Ovl{}; Ovl.Offset = DWORD(Offset & 0xffff'ffffu); Ovl.OffsetHigh = DWORD(Offset >> 32); DWORD dwNumberOfBytesToRead = gsl::narrow(Size); DWORD dwNumberOfBytesRead = 0; BOOL Success = ::ReadFile(m_FileHandle, Data, dwNumberOfBytesToRead, &dwNumberOfBytesRead, &Ovl); if (!Success) { ThrowLastError("Failed to read from file '{}'"_format(zen::PathFromHandle(m_FileHandle))); } } IoBuffer BasicFile::ReadAll() { IoBuffer Buffer(FileSize()); Read((void*)Buffer.Data(), Buffer.Size(), 0); return Buffer; } void BasicFile::StreamFile(std::function&& ChunkFun) { StreamByteRange(0, FileSize(), std::move(ChunkFun)); } void BasicFile::StreamByteRange(uint64_t FileOffset, uint64_t Size, std::function&& ChunkFun) { const uint64_t ChunkSize = 128 * 1024; IoBuffer ReadBuffer{ChunkSize}; void* BufferPtr = ReadBuffer.MutableData(); uint64_t RemainBytes = Size; uint64_t CurrentOffset = FileOffset; while (RemainBytes) { const uint64_t ThisChunkBytes = zen::Min(ChunkSize, RemainBytes); Read(BufferPtr, ThisChunkBytes, CurrentOffset); ChunkFun(BufferPtr, ThisChunkBytes); CurrentOffset += ThisChunkBytes; RemainBytes -= ThisChunkBytes; } } void BasicFile::Write(const void* Data, uint64_t Size, uint64_t Offset) { OVERLAPPED Ovl{}; Ovl.Offset = DWORD(Offset & 0xffff'ffffu); Ovl.OffsetHigh = DWORD(Offset >> 32); DWORD dwNumberOfBytesToWrite = gsl::narrow(Size); DWORD dwNumberOfBytesWritten = 0; BOOL Success = ::WriteFile(m_FileHandle, Data, dwNumberOfBytesToWrite, &dwNumberOfBytesWritten, &Ovl); if (!Success) { ThrowLastError("Failed to write to file '{}'"_format(zen::PathFromHandle(m_FileHandle))); } } void BasicFile::Flush() { FlushFileBuffers(m_FileHandle); } uint64_t BasicFile::FileSize() { ULARGE_INTEGER liFileSize; liFileSize.LowPart = ::GetFileSize(m_FileHandle, &liFileSize.HighPart); return uint64_t(liFileSize.QuadPart); } #if ZEN_WITH_TESTS TEST_CASE("BasicFile") { ScopedCurrentDirectoryChange _; BasicFile File1; CHECK_THROWS(File1.Open("zonk", false)); CHECK_NOTHROW(File1.Open("zonk", true)); CHECK_NOTHROW(File1.Write("abcd", 4, 0)); CHECK(File1.FileSize() == 4); } void basicfile_forcelink() { } #endif } // namespace zen