// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include #include #include #include #include #include #include #include #include #include namespace spdlog { class logger; } namespace zen { class BasicFile; /** CAS storage strategy using a file-per-chunk storage strategy */ struct FileCasStrategy final : public GcStorage { FileCasStrategy(const CasStoreConfiguration& Config, CasGc& Gc); ~FileCasStrategy(); void Initialize(bool IsNewStore); CasStore::InsertResult InsertChunk(const void* ChunkData, size_t ChunkSize, const IoHash& ChunkHash); CasStore::InsertResult InsertChunk(IoBuffer Chunk, const IoHash& ChunkHash); IoBuffer FindChunk(const IoHash& ChunkHash); bool HaveChunk(const IoHash& ChunkHash); void FilterChunks(CasChunkSet& InOutChunks); void Flush(); void Scrub(ScrubContext& Ctx); virtual void CollectGarbage(GcContext& GcCtx) override; virtual GcStorageSize StorageSize() const override { return {.DiskSize = m_TotalSize.load(std::memory_order::relaxed)}; } private: const CasStoreConfiguration& m_Config; RwLock m_Lock; RwLock m_ShardLocks[256]; // TODO: these should be spaced out so they don't share cache lines spdlog::logger& m_Log; spdlog::logger& Log() { return m_Log; } std::atomic_uint64_t m_TotalSize{}; bool m_IsInitialized = false; struct FileCasIndexEntry { static const uint32_t kTombStone = 0x0000'0001; bool IsFlagSet(const uint32_t Flag) const { return (Flags & kTombStone) == Flag; } IoHash Key; uint32_t Flags = 0; uint64_t Size = 0; }; static_assert(sizeof(FileCasIndexEntry) == 32); TCasLogFile m_CasLog; inline RwLock& LockForHash(const IoHash& Hash) { return m_ShardLocks[Hash.Hash[19]]; } void IterateChunks(std::function&& Callback); void DeleteChunk(const IoHash& ChunkHash, std::error_code& Ec); struct ShardingHelper { ShardingHelper(const std::filesystem::path& RootPath, const IoHash& ChunkHash); size_t Shard2len = 0; ExtendablePathBuilder<128> ShardedPath; }; }; void filecas_forcelink(); } // namespace zen