// Copyright Epic Games, Inc. All Rights Reserved. #include #include "CompactCas.h" #include #include #include #include #include #include #include #include #include #include #include struct IUnknown; // Workaround for "combaseapi.h(229): error C2187: syntax error: 'identifier' was unexpected here" when using /permissive- #include #include ////////////////////////////////////////////////////////////////////////// namespace zen { using namespace fmt::literals; uint32_t CasLogFile::FileHeader::ComputeChecksum() { return XXH32(&this->Magic, sizeof(FileHeader) - 4, 0xC0C0'BABA); } CasLogFile::CasLogFile() { } CasLogFile::~CasLogFile() { } void CasLogFile::Open(std::filesystem::path FileName, size_t RecordSize, bool IsCreate) { m_RecordSize = RecordSize; const DWORD dwCreationDisposition = IsCreate ? CREATE_ALWAYS : OPEN_EXISTING; HRESULT hRes = m_File.Create(FileName.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, dwCreationDisposition); if (FAILED(hRes)) { throw std::system_error(GetLastError(), std::system_category(), "Failed to open log file '{}'"_format(FileName)); } uint64_t AppendOffset = 0; if (IsCreate) { // Initialize log by writing header FileHeader Header = {.RecordSize = gsl::narrow(RecordSize), .LogId = zen::Oid::NewOid(), .ValidatedTail = 0}; memcpy(Header.Magic, FileHeader::MagicSequence, sizeof Header.Magic); Header.Finalize(); m_File.Write(&Header, sizeof Header); AppendOffset = sizeof(FileHeader); m_Header = Header; } else { // Validate header and log contents and prepare for appending/replay FileHeader Header; m_File.Read(&Header, sizeof Header); if ((0 != memcmp(Header.Magic, FileHeader::MagicSequence, sizeof Header.Magic)) || (Header.Checksum != Header.ComputeChecksum())) { // TODO: provide more context! throw std::runtime_error("Mangled log header"); } ULONGLONG Sz; m_File.GetSize(Sz); AppendOffset = Sz; m_Header = Header; } m_AppendOffset = AppendOffset; } void CasLogFile::Close() { // TODO: update header and maybe add trailer Flush(); m_File.Close(); } void CasLogFile::Replay(std::function&& Handler) { ULONGLONG LogFileSize; m_File.GetSize(LogFileSize); // Ensure we end up on a clean boundary const uint64_t LogBaseOffset = sizeof(FileHeader); const size_t LogEntryCount = (LogFileSize - LogBaseOffset) / m_RecordSize; if (LogEntryCount == 0) { return; } const uint64_t LogDataSize = LogEntryCount * m_RecordSize; std::vector ReadBuffer; ReadBuffer.resize(LogDataSize); m_File.Seek(LogBaseOffset, FILE_BEGIN); HRESULT hRes = m_File.Read(ReadBuffer.data(), gsl::narrow(LogDataSize)); if (FAILED(hRes)) { zen::ThrowSystemException(hRes, "Failed to read log file"); } for (int i = 0; i < LogEntryCount; ++i) { Handler(ReadBuffer.data() + (i * m_RecordSize)); } } void CasLogFile::Append(const void* DataPointer, uint64_t DataSize) { HRESULT hRes = m_File.Write(DataPointer, gsl::narrow(DataSize)); if (FAILED(hRes)) { zen::ThrowSystemException(hRes, "Failed to write to log file '{}'"_format(zen::PathFromHandle(m_File))); } } void CasLogFile::Flush() { m_File.Flush(); } } // namespace zen