// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include #include #include #include #include #include #include #include #include #include #if ZEN_PLATFORM_WINDOWS # include #endif #include #include namespace spdlog { class logger; } namespace zen { ////////////////////////////////////////////////////////////////////////// #pragma pack(push) #pragma pack(1) struct CasDiskLocation { CasDiskLocation(uint64_t InOffset, uint64_t InSize) { ZEN_ASSERT(InOffset <= 0xff'ffff'ffff); ZEN_ASSERT(InSize <= 0xff'ffff'ffff); memcpy(&m_Offset[0], &InOffset, sizeof m_Offset); memcpy(&m_Size[0], &InSize, sizeof m_Size); } CasDiskLocation() = default; inline uint64_t GetOffset() const { uint64_t Offset = 0; memcpy(&Offset, &m_Offset, sizeof m_Offset); return Offset; } inline uint64_t GetSize() const { uint64_t Size = 0; memcpy(&Size, &m_Size, sizeof m_Size); return Size; } private: uint8_t m_Offset[5]; uint8_t m_Size[5]; }; struct CasDiskIndexEntry { static const uint8_t kTombstone = 0x01; IoHash Key; CasDiskLocation Location; ZenContentType ContentType = ZenContentType::kUnknownContentType; uint8_t Flags = 0; }; #pragma pack(pop) static_assert(sizeof(CasDiskIndexEntry) == 32); /** This implements a storage strategy for small CAS values * * New chunks are simply appended to a small object file, and an index is * maintained to allow chunks to be looked up within the active small object * files * */ struct CasContainerStrategy final : public GcStorage { CasContainerStrategy(const CasStoreConfiguration& Config, CasGc& Gc); ~CasContainerStrategy(); 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 Initialize(const std::string_view ContainerBaseName, uint64_t Alignment, bool IsNewStore); 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: void OpenContainer(bool IsNewStore); void CloseContainer(); spdlog::logger& Log() { return m_Log; } const CasStoreConfiguration& m_Config; spdlog::logger& m_Log; uint64_t m_PayloadAlignment = 1 << 4; bool m_IsInitialized = false; BasicFile m_SmallObjectFile; BasicFile m_SmallObjectIndex; TCasLogFile m_CasLog; std::string m_ContainerBaseName; RwLock m_LocationMapLock; std::unordered_map m_LocationMap; RwLock m_InsertLock; // used to serialize inserts std::atomic_uint64_t m_CurrentInsertOffset{}; std::atomic_uint64_t m_CurrentIndexOffset{}; std::atomic_uint64_t m_TotalSize{}; void MakeSnapshot(); }; void compactcas_forcelink(); } // namespace zen