// Copied from 4.19 #pragma once #include "SparseArray.h" #include "ChooseClass.h" #include "UnrealTypeTraits.h" template class TSetElement { public: ElementType Value; mutable int32 HashNextId; mutable int32 HashIndex; TSetElement(ElementType InValue, int32 InHashNextId, int32 InHashIndex) : Value(InValue) , HashNextId(InHashNextId) , HashIndex(InHashIndex) { } FORCEINLINE TSetElement& operator=(const TSetElement& Other) { Value = Other.Value; } FORCEINLINE bool operator==(const TSetElement& Other) const { return Value == Other.Value; } FORCEINLINE bool operator!=(const TSetElement& Other) const { return Value != Other.Value; } }; /** Either NULL or an identifier for an element of a set. */ class FSetElementId { public: template friend class TSet; friend class FScriptSet; /** Default constructor. */ FORCEINLINE FSetElementId() : Index(INDEX_NONE) {} /** @return a boolean value representing whether the id is NULL. */ FORCEINLINE bool IsValidId() const { return Index != INDEX_NONE; } /** Comparison operator. */ FORCEINLINE friend bool operator==(const FSetElementId& A, const FSetElementId& B) { return A.Index == B.Index; } FORCEINLINE int32 AsInteger() const { return Index; } FORCEINLINE static FSetElementId FromInteger(int32 Integer) { return FSetElementId(Integer); } private: /** The index of the element in the set's element array. */ int32 Index; /** Initialization constructor. */ FORCEINLINE FSetElementId(int32 InIndex) : Index(InIndex) {} /** Implicit conversion to the element index. */ FORCEINLINE operator int32() const { return Index; } }; template struct BaseKeyFuncs { typedef InKeyType KeyType; typedef typename TCallTraits::ParamType KeyInitType; typedef typename TCallTraits::ParamType ElementInitType; enum { bAllowDuplicateKeys = bInAllowDuplicateKeys }; }; /** * A default implementation of the KeyFuncs used by TSet which uses the element as a key. */ template struct DefaultKeyFuncs : BaseKeyFuncs { typedef typename TCallTraits::ParamType KeyInitType; typedef typename TCallTraits::ParamType ElementInitType; /** * @return The key used to index the given element. */ static FORCEINLINE KeyInitType GetSetKey(ElementInitType Element) { return Element; } /** * @return True if the keys match. */ static FORCEINLINE bool Matches(KeyInitType A, KeyInitType B) { return A == B; } /** Calculates a hash index for a key. */ static FORCEINLINE uint32 GetKeyHash(KeyInitType Key) { return GetTypeHash(Key); } }; template< typename InElementType, typename KeyFuncs = DefaultKeyFuncs, typename Allocator = FDefaultSetAllocator > class TSet; template< typename InElementType, typename KeyFuncs /*= DefaultKeyFuncs*/, typename Allocator /*= FDefaultSetAllocator*/ > class TSet { private: friend TSparseArray; public: typedef typename KeyFuncs::KeyInitType KeyInitType; typedef typename KeyFuncs::ElementInitType ElementInitType; typedef TSetElement SetElementType; typedef InElementType ElementType; public: typedef TSparseArray ElementArrayType; typedef typename Allocator::HashAllocator::template ForElementType HashType; ElementArrayType Elements; mutable HashType Hash; mutable int32 HashSize; FORCEINLINE FSetElementId& GetTypedHash(int32 HashIndex) const { return ((FSetElementId*)Hash.GetAllocation())[HashIndex & (HashSize - 1)]; } public: FORCEINLINE ElementType* Find(KeyInitType Key) { FSetElementId ElementId = FindId(Key); if (ElementId.IsValidId()) { return &Elements[ElementId].Value; } else { return NULL; } } /** * Finds an element with the given key in the set. * @param Key - The key to search for. * @return A const pointer to an element with the given key. If no element in the set has the given key, this will return NULL. */ FORCEINLINE const ElementType* Find(KeyInitType Key) const { FSetElementId ElementId = FindId(Key); if (ElementId.IsValidId()) { return &Elements[ElementId].Value; } else { return NULL; } } /** Adds an element to the hash. */ FORCEINLINE void HashElement(FSetElementId ElementId, const SetElementType& Element) const { // Compute the hash bucket the element goes in. Element.HashIndex = KeyFuncs::GetKeyHash(KeyFuncs::GetSetKey(Element.Value)) & (HashSize - 1); // Link the element into the hash bucket. Element.HashNextId = GetTypedHash(Element.HashIndex); GetTypedHash(Element.HashIndex) = ElementId; } /** * Checks if the hash has an appropriate number of buckets, and if not resizes it. * @param NumHashedElements - The number of elements to size the hash for. * @param bAllowShrinking - true if the hash is allowed to shrink. * @return true if the set was rehashed. */ bool ConditionalRehash(int32 NumHashedElements, bool bAllowShrinking = false) const { // Calculate the desired hash size for the specified number of elements. const int32 DesiredHashSize = Allocator::GetNumberOfHashBuckets(NumHashedElements); // If the hash hasn't been created yet, or is smaller than the desired hash size, rehash. if (NumHashedElements > 0 && (!HashSize || HashSize < DesiredHashSize || (HashSize > DesiredHashSize && bAllowShrinking))) { HashSize = DesiredHashSize; Rehash(); return true; } else { return false; } } /** Resizes the hash. */ void Rehash() const { // Free the old hash. Hash.ResizeAllocation(0, 0, sizeof(FSetElementId)); int32 LocalHashSize = HashSize; if (LocalHashSize) { // Allocate the new hash. // checkSlow(FMath::IsPowerOfTwo(HashSize)); Hash.ResizeAllocation(0, LocalHashSize, sizeof(FSetElementId)); for (int32 HashIndex = 0; HashIndex < LocalHashSize; ++HashIndex) { GetTypedHash(HashIndex) = FSetElementId(); } // Add the existing elements to the new hash. for (typename ElementArrayType::TConstIterator ElementIt(Elements); ElementIt; ++ElementIt) { HashElement(FSetElementId(ElementIt.GetIndex()), *ElementIt); } } } template class TBaseIterator { private: friend class TSet; typedef typename TChooseClass::Result ItElementType; public: typedef typename TChooseClass< bConst, typename TChooseClass::Result, typename TChooseClass::Result >::Result ElementItType; FORCEINLINE TBaseIterator(const ElementItType& InElementIt) : ElementIt(InElementIt) { } /** Advances the iterator to the next element. */ FORCEINLINE TBaseIterator& operator++() { ++ElementIt; return *this; } /** conversion to "bool" returning true if the iterator is valid. */ FORCEINLINE explicit operator bool() const { return !!ElementIt; } /** inverse of the "bool" operator */ FORCEINLINE bool operator !() const { return !(bool)*this; } // Accessors. FORCEINLINE FSetElementId GetId() const { return TSet::IndexToId(ElementIt.GetIndex()); } FORCEINLINE ItElementType* operator->() const { return &ElementIt->Value; } FORCEINLINE ItElementType& operator*() const { return ElementIt->Value; } FORCEINLINE friend bool operator==(const TBaseIterator& Lhs, const TBaseIterator& Rhs) { return Lhs.ElementIt == Rhs.ElementIt; } FORCEINLINE friend bool operator!=(const TBaseIterator& Lhs, const TBaseIterator& Rhs) { return Lhs.ElementIt != Rhs.ElementIt; } ElementItType ElementIt; }; /** The base type of whole set iterators. */ template class TBaseKeyIterator { private: typedef typename TChooseClass::Result SetType; typedef typename TChooseClass::Result ItElementType; public: /** Initialization constructor. */ FORCEINLINE TBaseKeyIterator(SetType& InSet, KeyInitType InKey) : Set(InSet) , Key(InKey) , Id() { // The set's hash needs to be initialized to find the elements with the specified key. Set.ConditionalRehash(Set.Elements.Num()); if (Set.HashSize) { NextId = Set.GetTypedHash(KeyFuncs::GetKeyHash(Key)); ++(*this); } /* // Milxnor: This is only on newer builds? else { NextIndex = INDEX_NONE; } */ } /** Advances the iterator to the next element. */ FORCEINLINE TBaseKeyIterator& operator++() { Id = NextId; while (Id.IsValidId()) { NextId = Set.GetInternalElement(Id).HashNextId; // checkSlow(Id != NextId); if (KeyFuncs::Matches(KeyFuncs::GetSetKey(Set[Id]), Key)) { break; } Id = NextId; } return *this; } /** conversion to "bool" returning true if the iterator is valid. */ FORCEINLINE explicit operator bool() const { return Id.IsValidId(); } /** inverse of the "bool" operator */ FORCEINLINE bool operator !() const { return !(bool)*this; } // Accessors. FORCEINLINE ItElementType* operator->() const { return &Set[Id]; } FORCEINLINE ItElementType& operator*() const { return Set[Id]; } protected: SetType& Set; typename TTypeTraits::ConstPointerType Key; FSetElementId Id; FSetElementId NextId; }; FORCEINLINE const SetElementType& GetInternalElement(FSetElementId Id) const { return Elements[Id]; } FORCEINLINE SetElementType& GetInternalElement(FSetElementId Id) { return Elements[Id]; } public: class TConstIterator : public TBaseIterator { friend class TSet; public: FORCEINLINE TConstIterator(const TSet& InSet) : TBaseIterator(begin(InSet.Elements)) { } }; class TIterator : public TBaseIterator { friend class TSet; public: FORCEINLINE TIterator(TSet& InSet) : TBaseIterator(begin(InSet.Elements)) , Set(InSet) { } /** Removes the current element from the set. */ /* // T(R) FORCEINLINE void RemoveCurrent() { Set.Remove(TBaseIterator::GetId()); } */ private: TSet& Set; }; using TRangedForConstIterator = TBaseIterator; using TRangedForIterator = TBaseIterator; static FORCEINLINE FSetElementId IndexToId(int32 Index) { return FSetElementId(Index); } /** Creates an iterator for the contents of this set */ FORCEINLINE TIterator CreateIterator() { return TIterator(*this); } /** Creates a const iterator for the contents of this set */ FORCEINLINE TConstIterator CreateConstIterator() const { return TConstIterator(*this); } FORCEINLINE int32 Num() const { return Elements.Num(); } FORCEINLINE bool IsValid() const { return Elements.Data.Data != nullptr && Elements.AllocationFlags.MaxBits > 0; } FORCEINLINE TSparseArray& GetElements() { return Elements; } FORCEINLINE const TSparseArray& GetElements() const { return Elements; } FORCEINLINE bool IsIndexValid(int32 IndexToCheck) const { return Elements.IsIndexValid(IndexToCheck); } FSetElementId FindId(KeyInitType Key) const { if (Elements.Num()) { for (FSetElementId ElementId = GetTypedHash(KeyFuncs::GetKeyHash(Key)); ElementId.IsValidId(); ElementId = Elements[ElementId].HashNextId) { if (KeyFuncs::Matches(KeyFuncs::GetSetKey(Elements[ElementId].Value), Key)) { return ElementId; } } } return FSetElementId(); } FORCEINLINE bool Contains(KeyInitType Key) const { return FindId(Key).IsValidId(); } private: /** * DO NOT USE DIRECTLY * STL-like iterators to enable range-based for loop support. */ FORCEINLINE friend TRangedForIterator begin(TSet& Set) { return TRangedForIterator(begin(Set.Elements)); } FORCEINLINE friend TRangedForConstIterator begin(const TSet& Set) { return TRangedForConstIterator(begin(Set.Elements)); } FORCEINLINE friend TRangedForIterator end(TSet& Set) { return TRangedForIterator(end(Set.Elements)); } FORCEINLINE friend TRangedForConstIterator end(const TSet& Set) { return TRangedForConstIterator(end(Set.Elements)); } };