// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "HAL/CriticalSection.h" #include "Pow2ChunkedArray.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // TStateStreamStore is a threadsafe, reference stable storage. User can hold on to pointers of // elements while other elements are added/removed. // Store is backed by a Pow2ChunkedArray and use a "free list" to be able to reuse removed elements template class TStateStreamStore { public: inline TStateStreamStore(); inline ~TStateStreamStore(); inline uint32 Add(const T& Value); inline void* AddUninitialized(uint32& OutIndex); inline void Remove(uint32 Index); template inline uint32 Emplace(ArgsType&&... Args); inline T& operator[](uint32 Index); inline uint32 GetUsedCount() const; private: FRWLock Lock; FPow2ChunkedArray Array; uint32 FirstFreeIndex = ~0u; }; //////////////////////////////////////////////////////////////////////////////////////////////////// // Implementation template TStateStreamStore::TStateStreamStore() { static_assert(sizeof(T) >= sizeof(uint32)); } template TStateStreamStore::~TStateStreamStore() { if (!std::is_trivially_destructible_v) { while (FirstFreeIndex != ~0u) { T& Element = Array[FirstFreeIndex]; FirstFreeIndex = *reinterpret_cast(&Element); new (&Element) T(); } } } template uint32 TStateStreamStore::Add(const T& Value) { uint32 Index; new (AddUninitialized(Index)) T(Value); return Index; } template void* TStateStreamStore::AddUninitialized(uint32& OutIndex) { Lock.WriteLock(); if (FirstFreeIndex == ~0u) { Lock.WriteUnlock(); return Array.AddUninitialized(OutIndex); } uint32 Index = FirstFreeIndex; void* Element = &Array[Index]; FirstFreeIndex = *reinterpret_cast(Element); Lock.WriteUnlock(); OutIndex = Index; return &Array[Index]; } template void TStateStreamStore::Remove(uint32 Index) { T& Element = Array[Index]; Element.~T(); Lock.WriteLock(); *reinterpret_cast(&Element) = FirstFreeIndex; FirstFreeIndex = Index; Lock.WriteUnlock(); } template template uint32 TStateStreamStore::Emplace(ArgsType&&... Args) { uint32 OutIndex; new (AddUninitialized(OutIndex)) T(Forward(Args)...); return OutIndex; } template T& TStateStreamStore::operator[](uint32 Index) { return Array[Index]; } template uint32 TStateStreamStore::GetUsedCount() const { uint32 UsedCount = Array.Num(); uint32 Index = FirstFreeIndex; while (Index != ~0u) { --UsedCount; Index = *(uint32*)&Array[Index]; } return UsedCount; } ////////////////////////////////////////////////////////////////////////////////////////////////////