aboutsummaryrefslogtreecommitdiff
path: root/src/zenstore/caslog.cpp
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2023-05-02 10:01:47 +0200
committerGitHub <[email protected]>2023-05-02 10:01:47 +0200
commit075d17f8ada47e990fe94606c3d21df409223465 (patch)
treee50549b766a2f3c354798a54ff73404217b4c9af /src/zenstore/caslog.cpp
parentfix: bundle shouldn't append content zip to zen (diff)
downloadzen-075d17f8ada47e990fe94606c3d21df409223465.tar.xz
zen-075d17f8ada47e990fe94606c3d21df409223465.zip
moved source directories into `/src` (#264)
* moved source directories into `/src` * updated bundle.lua for new `src` path * moved some docs, icon * removed old test trees
Diffstat (limited to 'src/zenstore/caslog.cpp')
-rw-r--r--src/zenstore/caslog.cpp236
1 files changed, 236 insertions, 0 deletions
diff --git a/src/zenstore/caslog.cpp b/src/zenstore/caslog.cpp
new file mode 100644
index 000000000..2a978ae12
--- /dev/null
+++ b/src/zenstore/caslog.cpp
@@ -0,0 +1,236 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include <zenstore/caslog.h>
+
+#include "compactcas.h"
+
+#include <zencore/except.h>
+#include <zencore/filesystem.h>
+#include <zencore/fmtutils.h>
+#include <zencore/logging.h>
+#include <zencore/memory.h>
+#include <zencore/string.h>
+#include <zencore/thread.h>
+#include <zencore/uid.h>
+
+#include <xxhash.h>
+
+#include <gsl/gsl-lite.hpp>
+
+#include <filesystem>
+#include <functional>
+
+//////////////////////////////////////////////////////////////////////////
+
+namespace zen {
+
+uint32_t
+CasLogFile::FileHeader::ComputeChecksum()
+{
+ return XXH32(&this->Magic, sizeof(FileHeader) - 4, 0xC0C0'BABA);
+}
+
+CasLogFile::CasLogFile()
+{
+}
+
+CasLogFile::~CasLogFile()
+{
+}
+
+bool
+CasLogFile::IsValid(std::filesystem::path FileName, size_t RecordSize)
+{
+ if (!std::filesystem::is_regular_file(FileName))
+ {
+ return false;
+ }
+ BasicFile File;
+
+ std::error_code Ec;
+ File.Open(FileName, BasicFile::Mode::kRead, Ec);
+ if (Ec)
+ {
+ return false;
+ }
+
+ FileHeader Header;
+ if (File.FileSize() < sizeof(Header))
+ {
+ return false;
+ }
+
+ // Validate header and log contents and prepare for appending/replay
+ File.Read(&Header, sizeof Header, 0);
+
+ if ((0 != memcmp(Header.Magic, FileHeader::MagicSequence, sizeof Header.Magic)) || (Header.Checksum != Header.ComputeChecksum()))
+ {
+ return false;
+ }
+ if (Header.RecordSize != RecordSize)
+ {
+ return false;
+ }
+ return true;
+}
+
+void
+CasLogFile::Open(std::filesystem::path FileName, size_t RecordSize, Mode Mode)
+{
+ m_RecordSize = RecordSize;
+
+ std::error_code Ec;
+ BasicFile::Mode FileMode = BasicFile::Mode::kRead;
+ switch (Mode)
+ {
+ case Mode::kWrite:
+ FileMode = BasicFile::Mode::kWrite;
+ break;
+ case Mode::kTruncate:
+ FileMode = BasicFile::Mode::kTruncate;
+ break;
+ }
+
+ m_File.Open(FileName, FileMode, Ec);
+ if (Ec)
+ {
+ throw std::system_error(Ec, fmt::format("Failed to open log file '{}'", FileName));
+ }
+
+ uint64_t AppendOffset = 0;
+
+ if ((Mode == Mode::kTruncate) || (m_File.FileSize() < sizeof(FileHeader)))
+ {
+ if (Mode == Mode::kRead)
+ {
+ throw std::runtime_error(fmt::format("Mangled log header (file to small) in '{}'", FileName));
+ }
+ // Initialize log by writing header
+ FileHeader Header = {.RecordSize = gsl::narrow<uint32_t>(RecordSize), .LogId = Oid::NewOid(), .ValidatedTail = 0};
+ memcpy(Header.Magic, FileHeader::MagicSequence, sizeof Header.Magic);
+ Header.Finalize();
+
+ m_File.Write(&Header, sizeof Header, 0);
+
+ AppendOffset = sizeof(FileHeader);
+
+ m_Header = Header;
+ }
+ else
+ {
+ FileHeader Header;
+ m_File.Read(&Header, sizeof Header, 0);
+
+ if ((0 != memcmp(Header.Magic, FileHeader::MagicSequence, sizeof Header.Magic)) || (Header.Checksum != Header.ComputeChecksum()))
+ {
+ throw std::runtime_error(fmt::format("Mangled log header (invalid header magic) in '{}'", FileName));
+ }
+ if (Header.RecordSize != RecordSize)
+ {
+ throw std::runtime_error(fmt::format("Mangled log header (mismatch in record size, expected {}, found {}) in '{}'",
+ RecordSize,
+ Header.RecordSize,
+ FileName));
+ }
+
+ AppendOffset = m_File.FileSize();
+
+ // Adjust the offset to ensure we end up on a good boundary, in case there is some garbage appended
+
+ AppendOffset -= sizeof Header;
+ AppendOffset -= AppendOffset % RecordSize;
+ AppendOffset += sizeof Header;
+
+ m_Header = Header;
+ }
+
+ m_AppendOffset = AppendOffset;
+}
+
+void
+CasLogFile::Close()
+{
+ // TODO: update header and maybe add trailer
+ Flush();
+
+ m_File.Close();
+}
+
+uint64_t
+CasLogFile::GetLogSize()
+{
+ return m_File.FileSize();
+}
+
+uint64_t
+CasLogFile::GetLogCount()
+{
+ uint64_t LogFileSize = m_AppendOffset.load(std::memory_order_acquire);
+ if (LogFileSize < sizeof(FileHeader))
+ {
+ return 0;
+ }
+ const uint64_t LogBaseOffset = sizeof(FileHeader);
+ const size_t LogEntryCount = (LogFileSize - LogBaseOffset) / m_RecordSize;
+ return LogEntryCount;
+}
+
+void
+CasLogFile::Replay(std::function<void(const void*)>&& Handler, uint64_t SkipEntryCount)
+{
+ uint64_t LogFileSize = m_File.FileSize();
+
+ // Ensure we end up on a clean boundary
+ uint64_t LogBaseOffset = sizeof(FileHeader);
+ size_t LogEntryCount = (LogFileSize - LogBaseOffset) / m_RecordSize;
+
+ if (LogEntryCount <= SkipEntryCount)
+ {
+ return;
+ }
+
+ LogBaseOffset += SkipEntryCount * m_RecordSize;
+ LogEntryCount -= SkipEntryCount;
+
+ // This should really be streaming the data rather than just
+ // reading it into memory, though we don't tend to get very
+ // large logs so it may not matter
+
+ const uint64_t LogDataSize = LogEntryCount * m_RecordSize;
+
+ std::vector<uint8_t> ReadBuffer;
+ ReadBuffer.resize(LogDataSize);
+
+ m_File.Read(ReadBuffer.data(), LogDataSize, LogBaseOffset);
+
+ for (int i = 0; i < int(LogEntryCount); ++i)
+ {
+ Handler(ReadBuffer.data() + (i * m_RecordSize));
+ }
+
+ m_AppendOffset = LogBaseOffset + (m_RecordSize * LogEntryCount);
+}
+
+void
+CasLogFile::Append(const void* DataPointer, uint64_t DataSize)
+{
+ ZEN_ASSERT((DataSize % m_RecordSize) == 0);
+
+ uint64_t AppendOffset = m_AppendOffset.fetch_add(DataSize);
+
+ std::error_code Ec;
+ m_File.Write(DataPointer, gsl::narrow<uint32_t>(DataSize), AppendOffset, Ec);
+
+ if (Ec)
+ {
+ throw std::system_error(Ec, fmt::format("Failed to write to log file '{}'", PathFromHandle(m_File.Handle())));
+ }
+}
+
+void
+CasLogFile::Flush()
+{
+ m_File.Flush();
+}
+
+} // namespace zen