UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
ScriptSparseSet.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "ContainersFwd.h"
10#include "Templates/Function.h"
11
13{
14 // int32 ElementOffset = 0; // always at zero offset from the TSparseSetElement - not stored here
18
20};
21
22// Untyped set type for accessing TSparseSet data, like FScriptArray for TArray.
23// Must have the same memory representation as a TSparseSet.
24template <typename Allocator>
26{
27public:
28 [[nodiscard]] static constexpr FScriptSparseSetLayout GetScriptLayout(int32 ElementSize, int32 ElementAlignment)
29 {
31
32 // TSparseSetElement<TPair<Key, Value>>
34 int32 ElementOffset = SetElementStruct.AddMember(ElementSize, ElementAlignment);
35 Result.HashNextIdOffset = SetElementStruct.AddMember(sizeof(FSetElementId), alignof(FSetElementId));
36 Result.HashIndexOffset = SetElementStruct.AddMember(sizeof(int32), alignof(int32));
37 Result.Size = SetElementStruct.GetSize();
38 Result.SparseArrayLayout = FScriptSparseArray::GetScriptLayout(SetElementStruct.GetSize(), SetElementStruct.GetAlignment());
39
40 checkf(ElementOffset == 0, TEXT("The element inside the TSparseSetElement is expected to be at the start of the struct"));
41
42 return Result;
43 }
44
46 : HashSize(0)
47 {
48 }
49
51 // Start - intrusive TOptional<TScriptSparseSet> state //
53 constexpr static bool bHasIntrusiveUnsetOptionalState = true;
55
57 : Elements(Tag)
58 {
59 }
61 {
62 return Elements == Tag;
63 }
65 // End - intrusive TOptional<TScriptSparseSet> state //
67
69 {
70 return Elements.IsValidIndex(Index);
71 }
72
73 [[nodiscard]] bool IsEmpty() const
74 {
75 return Elements.IsEmpty();
76 }
77
78 [[nodiscard]] bool IsCompact() const
79 {
80 return Elements.IsCompact();
81 }
82
83 [[nodiscard]] int32 Num() const
84 {
85 return Elements.Num();
86 }
87
89 {
90 return Elements.NumUnchecked();
91 }
92
94 [[nodiscard]] int32 Max() const
95 {
96 return Elements.Max();
97 }
98
100 {
101 return Elements.GetMaxIndex();
102 }
103
105 {
106 return Elements.GetData(Index, Layout.SparseArrayLayout);
107 }
108
110 {
111 return Elements.GetData(Index, Layout.SparseArrayLayout);
112 }
113
115 {
116 checkSlow(this != &Other);
117 Empty(0, Layout);
118 Elements.MoveAssign(Other.Elements, Layout.SparseArrayLayout);
119 Hash.MoveToEmpty(Other.Hash);
120 HashSize = Other.HashSize; Other.HashSize = 0;
121 }
122
124 {
125 // Empty the elements array, and reallocate it for the expected number of elements.
126 Elements.Empty(Slack, Layout.SparseArrayLayout);
127
128 // Calculate the desired hash size for the specified number of elements.
129 const int32 DesiredHashSize = Allocator::GetNumberOfHashBuckets(Slack);
130
131 // If the hash hasn't been created yet, or is smaller than the desired hash size, rehash.
132 if (Slack != 0 && (HashSize == 0 || HashSize != DesiredHashSize))
133 {
134 HashSize = DesiredHashSize;
135
136 // Free the old hash.
137 Hash.ResizeAllocation(0, HashSize, sizeof(FSetElementId));
138 }
139
140 FSetElementId* HashPtr = Hash.GetAllocation();
141 for (int32 I = 0; I < HashSize; ++I)
142 {
143 HashPtr[I] = FSetElementId();
144 }
145 }
146
148 {
150
151 void* ElementBeingRemoved = Elements.GetData(Index, Layout.SparseArrayLayout);
152
153 // Remove the element from the hash.
154 for (FSetElementId* NextElementId = &GetTypedHash(GetHashIndexRef(ElementBeingRemoved, Layout)); NextElementId->IsValidId(); NextElementId = &GetHashNextIdRef(Elements.GetData(NextElementId->AsInteger(), Layout.SparseArrayLayout), Layout))
155 {
156 if (NextElementId->AsInteger() == Index)
157 {
158 *NextElementId = GetHashNextIdRef(ElementBeingRemoved, Layout);
159 break;
160 }
161 }
162
163 // Remove the element from the elements array.
164 Elements.RemoveAtUninitialized(Layout.SparseArrayLayout, Index);
165 }
166
174 {
175 return Elements.AddUninitialized(Layout.SparseArrayLayout);
176 }
177
179 {
180 Elements.RemoveAtUninitialized(Layout.SparseArrayLayout, Index);
181 }
182
184 {
185 // Keep consistent interface with ScriptCompactSet
186 }
187
189 {
190 Rehash(Layout, GetKeyHash);
191 }
192
193 void Rehash(const FScriptSparseSetLayout& Layout, TFunctionRef<uint32 (const void*)> GetKeyHash)
194 {
195 // Free the old hash.
196 Hash.ResizeAllocation(0,0,sizeof(FSetElementId));
197
198 HashSize = Allocator::GetNumberOfHashBuckets(Elements.Num());
199 if (HashSize)
200 {
201 // Allocate the new hash.
203 Hash.ResizeAllocation(0, HashSize, sizeof(FSetElementId));
204 for (int32 HashIndex = 0; HashIndex < HashSize; ++HashIndex)
205 {
206 GetTypedHash(HashIndex) = FSetElementId();
207 }
208
209 // Add the existing elements to the new hash.
210 int32 Index = 0;
211 int32 Count = Elements.Num();
212 while (Count)
213 {
214 if (Elements.IsValidIndex(Index))
215 {
217
218 void* Element = (uint8*)Elements.GetData(Index, Layout.SparseArrayLayout);
219
220 // Compute the hash bucket the element goes in.
221 uint32 KeyHash = GetKeyHash(Element);
222 int32 HashIndex = KeyHash & (HashSize - 1);
223 GetHashIndexRef(Element, Layout) = KeyHash & (HashSize - 1);
224
225 // Link the element into the hash bucket.
226 GetHashNextIdRef(Element, Layout) = GetTypedHash(HashIndex);
227 GetTypedHash(HashIndex) = ElementId;
228
229 --Count;
230 }
231
232 ++Index;
233 }
234 }
235 }
236
237private:
238 [[nodiscard]] int32 FindIndexImpl(const void* Element, const FScriptSparseSetLayout& Layout, uint32 KeyHash, TFunctionRef<bool (const void*, const void*)> EqualityFn) const
239 {
240 const int32 HashIndex = KeyHash & (HashSize - 1);
241
242 uint8* CurrentElement = nullptr;
243 for (FSetElementId ElementId = GetTypedHash(HashIndex);
244 ElementId.IsValidId();
245 ElementId = GetHashNextIdRef(CurrentElement, Layout))
246 {
247 CurrentElement = (uint8*)Elements.GetData(ElementId.AsInteger(), Layout.SparseArrayLayout);
248 if (EqualityFn(Element, CurrentElement))
249 {
250 return ElementId.AsInteger();
251 }
252 }
253
254 return INDEX_NONE;
255 }
256
257public:
258 [[nodiscard]] int32 FindIndex(const void* Element, const FScriptSparseSetLayout& Layout, TFunctionRef<uint32 (const void*)> GetKeyHash, TFunctionRef<bool (const void*, const void*)> EqualityFn) const
259 {
260 if (Elements.Num())
261 {
262 return FindIndexImpl(Element, Layout, GetKeyHash(Element), EqualityFn);
263 }
264
265 return INDEX_NONE;
266 }
267
268
269 [[nodiscard]] int32 FindIndexByHash(const void* Element, const FScriptSparseSetLayout& Layout, uint32 KeyHash, TFunctionRef<bool (const void*, const void*)> EqualityFn) const
270 {
271 if (Elements.Num())
272 {
273 return FindIndexImpl(Element, Layout, KeyHash, EqualityFn);
274 }
275
276 return INDEX_NONE;
277 }
278
279 int32 FindOrAdd(const void* Element, const FScriptSparseSetLayout& Layout, TFunctionRef<uint32(const void*)> GetKeyHash, TFunctionRef<bool(const void*, const void*)> EqualityFn, TFunctionRef<void(void*)> ConstructFn)
280 {
281 uint32 KeyHash = GetKeyHash(Element);
284 {
285 return OldElementIndex;
286 }
287
288 return AddNewElement(Layout, GetKeyHash, KeyHash, ConstructFn);
289 }
290
291 void Add(const void* Element, const FScriptSparseSetLayout& Layout, TFunctionRef<uint32(const void*)> GetKeyHash, TFunctionRef<bool(const void*, const void*)> EqualityFn, TFunctionRef<void(void*)> ConstructFn, TFunctionRef<void(void*)> DestructFn)
292 {
293 uint32 KeyHash = GetKeyHash(Element);
296 {
297 void* ElementPtr = Elements.GetData(OldElementIndex, Layout.SparseArrayLayout);
298
299 DestructFn(ElementPtr);
300 ConstructFn(ElementPtr);
301
302 // We don't update the hash because we don't need to - the new element
303 // should have the same hash, but let's just check.
305 // Disable deprecations warnings to stop warnings being thrown by our check macro.
306 checkSlow(KeyHash == GetKeyHash(ElementPtr));
308 }
309 else
310 {
311 AddNewElement(Layout, GetKeyHash, KeyHash, ConstructFn);
312 }
313 }
314
315private:
316 int32 AddNewElement(const FScriptSparseSetLayout& Layout, TFunctionRef<uint32(const void*)> GetKeyHash, uint32 KeyHash, TFunctionRef<void(void*)> ConstructFn)
317 {
318 int32 NewElementIndex = Elements.AddUninitialized(Layout.SparseArrayLayout);
319
320 void* ElementPtr = Elements.GetData(NewElementIndex, Layout.SparseArrayLayout);
321 ConstructFn(ElementPtr);
322
324 if (!HashSize || HashSize < DesiredHashSize)
325 {
326 // rehash, this will link in our new element if needed:
327 Rehash(Layout, GetKeyHash);
328 }
329 else
330 {
331 // link the new element into the set:
332 int32 HashIndex = KeyHash & (HashSize - 1);
333 FSetElementId& TypedHash = GetTypedHash(HashIndex);
334 GetHashIndexRef(ElementPtr, Layout) = HashIndex;
335 GetHashNextIdRef(ElementPtr, Layout) = TypedHash;
337 }
338
339 return NewElementIndex;
340 }
341
343 class TrackedSparseArrayAllocator
344 {
345 public:
346
347 using ElementAllocator = typename Allocator::SparseArrayAllocator::ElementAllocator;
348 using BitArrayAllocator = typename Allocator::SparseArrayAllocator::BitArrayAllocator;
349 };
350
351 using HashAllocator = typename Allocator::HashAllocator;
352
354 using HashType = typename HashAllocator::template ForElementType<FSetElementId>;
355
356 ElementArrayType Elements;
357 HashType Hash;
358 int32 HashSize;
359
360 [[nodiscard]] UE_FORCEINLINE_HINT FSetElementId& GetTypedHash(int32 HashIndex) const
361 {
362 return ((FSetElementId*)Hash.GetAllocation())[HashIndex & (HashSize - 1)];
363 }
364
365 [[nodiscard]] static FSetElementId& GetHashNextIdRef(const void* Element, const FScriptSparseSetLayout& Layout)
366 {
367 return *(FSetElementId*)((uint8*)Element + Layout.HashNextIdOffset);
368 }
369
370 [[nodiscard]] static int32& GetHashIndexRef(const void* Element, const FScriptSparseSetLayout& Layout)
371 {
372 return *(int32*)((uint8*)Element + Layout.HashIndexOffset);
373 }
374
375 // This function isn't intended to be called, just to be compiled to validate the correctness of the type.
376 static void CheckConstraints()
377 {
379 typedef TSparseSet<int32> RealType;
380
381 // Check that the class footprint is the same
382 static_assert(sizeof (ScriptType) == sizeof (RealType), "TScriptSparseSet's size doesn't match TSparseSet");
383 static_assert(alignof(ScriptType) == alignof(RealType), "TScriptSparseSet's alignment doesn't match TSparseSet");
384
385 // Check member sizes
386 static_assert(sizeof(DeclVal<ScriptType>().Elements) == sizeof(DeclVal<RealType>().Elements), "TScriptSparseSet's Elements member size does not match TSparseSet's");
387 static_assert(sizeof(DeclVal<ScriptType>().Hash) == sizeof(DeclVal<RealType>().Hash), "TScriptSparseSet's Hash member size does not match TSparseSet's");
388 static_assert(sizeof(DeclVal<ScriptType>().HashSize) == sizeof(DeclVal<RealType>().HashSize), "TScriptSparseSet's HashSize member size does not match TSparseSet's");
389
390 // Check member offsets
391 static_assert(STRUCT_OFFSET(ScriptType, Elements) == STRUCT_OFFSET(RealType, Elements), "TScriptSparseSet's Elements member offset does not match TSparseSet's");
392 static_assert(STRUCT_OFFSET(ScriptType, Hash) == STRUCT_OFFSET(RealType, Hash), "TScriptSparseSet's Hash member offset does not match TSparseSet's");
393 static_assert(STRUCT_OFFSET(ScriptType, HashSize) == STRUCT_OFFSET(RealType, HashSize), "TScriptSparseSet's FirstFreeIndex member offset does not match TSparseSet's");
394 }
395
396public:
397 // These should really be private, because they shouldn't be called, but there's a bunch of code
398 // that needs to be fixed first.
400 void operator=(const TScriptSparseSet&) { check(false); }
401};
402
403template <typename AllocatorType>
405{
406 enum { Value = true };
407};
#define checkSlow(expr)
Definition AssertionMacros.h:332
#define check(expr)
Definition AssertionMacros.h:314
#define checkf(expr, format,...)
Definition AssertionMacros.h:315
@ INDEX_NONE
Definition CoreMiscDefines.h:150
#define TEXT(x)
Definition Platform.h:1272
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
#define UE_FORCEINLINE_HINT
Definition Platform.h:723
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
#define PRAGMA_ENABLE_DEPRECATION_WARNINGS
Definition GenericPlatformCompilerPreSetup.h:12
#define PRAGMA_DISABLE_DEPRECATION_WARNINGS
Definition GenericPlatformCompilerPreSetup.h:8
#define STRUCT_OFFSET(struc, member)
Definition UnrealTemplate.h:218
uint8_t uint8
Definition binka_ue_file_header.h:8
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition SetUtilities.h:95
static UE_FORCEINLINE_HINT FSetElementId FromInteger(int32 Integer)
Definition SetUtilities.h:121
UE_FORCEINLINE_HINT bool IsValidId() const
Definition SetUtilities.h:101
Definition StructBuilder.h:14
constexpr int32 AddMember(int32 MemberSize, int32 MemberAlignment)
Definition StructBuilder.h:30
Definition AssetRegistryState.h:50
Definition SparseArray.h:1465
static constexpr FScriptSparseArrayLayout GetScriptLayout(int32 ElementSize, int32 ElementAlignment)
Definition SparseArray.h:1469
int32 Num() const
Definition SparseArray.h:1517
int32 AddUninitialized(const FScriptSparseArrayLayout &Layout)
Definition SparseArray.h:1578
void * GetData(int32 Index, const FScriptSparseArrayLayout &Layout)
Definition SparseArray.h:1544
int32 NumUnchecked() const
Definition SparseArray.h:1528
bool IsCompact() const
Definition SparseArray.h:1539
int32 GetMaxIndex() const
Definition SparseArray.h:1534
void RemoveAtUninitialized(const FScriptSparseArrayLayout &Layout, int32 Index, int32 Count=1)
Definition SparseArray.h:1605
void MoveAssign(DerivedType &Other, const FScriptSparseArrayLayout &Layout)
Definition SparseArray.h:1554
bool IsValidIndex(int32 Index) const
Definition SparseArray.h:1502
bool IsEmpty() const
Definition SparseArray.h:1512
int32 Max() const
Definition SparseArray.h:1523
void Empty(int32 Slack, const FScriptSparseArrayLayout &Layout)
Definition SparseArray.h:1564
Definition ScriptSparseSet.h:26
static constexpr bool bHasIntrusiveUnsetOptionalState
Definition ScriptSparseSet.h:53
int32 GetMaxIndex() const
Definition ScriptSparseSet.h:99
void MoveAssign(TScriptSparseSet &Other, const FScriptSparseSetLayout &Layout)
Definition ScriptSparseSet.h:114
TScriptSparseSet(FIntrusiveUnsetOptionalState Tag)
Definition ScriptSparseSet.h:56
int32 FindIndexByHash(const void *Element, const FScriptSparseSetLayout &Layout, uint32 KeyHash, TFunctionRef< bool(const void *, const void *)> EqualityFn) const
Definition ScriptSparseSet.h:269
const void * GetData(int32 Index, const FScriptSparseSetLayout &Layout) const
Definition ScriptSparseSet.h:109
void * GetData(int32 Index, const FScriptSparseSetLayout &Layout)
Definition ScriptSparseSet.h:104
void Rehash(const FScriptSparseSetLayout &Layout, TFunctionRef< uint32(const void *)> GetKeyHash)
Definition ScriptSparseSet.h:193
int32 Num() const
Definition ScriptSparseSet.h:83
int32 NumUnchecked() const
Definition ScriptSparseSet.h:88
void Add(const void *Element, const FScriptSparseSetLayout &Layout, TFunctionRef< uint32(const void *)> GetKeyHash, TFunctionRef< bool(const void *, const void *)> EqualityFn, TFunctionRef< void(void *)> ConstructFn, TFunctionRef< void(void *)> DestructFn)
Definition ScriptSparseSet.h:291
int32 Max() const
Definition ScriptSparseSet.h:94
TScriptSparseSet()
Definition ScriptSparseSet.h:45
void Empty(int32 Slack, const FScriptSparseSetLayout &Layout)
Definition ScriptSparseSet.h:123
bool IsEmpty() const
Definition ScriptSparseSet.h:73
void CommitLastUninitialized(const FScriptSparseSetLayout &Layout, TFunctionRef< uint32(const void *)> GetKeyHash)
Definition ScriptSparseSet.h:183
static constexpr FScriptSparseSetLayout GetScriptLayout(int32 ElementSize, int32 ElementAlignment)
Definition ScriptSparseSet.h:28
int32 FindIndex(const void *Element, const FScriptSparseSetLayout &Layout, TFunctionRef< uint32(const void *)> GetKeyHash, TFunctionRef< bool(const void *, const void *)> EqualityFn) const
Definition ScriptSparseSet.h:258
int32 FindOrAdd(const void *Element, const FScriptSparseSetLayout &Layout, TFunctionRef< uint32(const void *)> GetKeyHash, TFunctionRef< bool(const void *, const void *)> EqualityFn, TFunctionRef< void(void *)> ConstructFn)
Definition ScriptSparseSet.h:279
bool IsCompact() const
Definition ScriptSparseSet.h:78
bool operator==(FIntrusiveUnsetOptionalState Tag) const
Definition ScriptSparseSet.h:60
void operator=(const TScriptSparseSet &)
Definition ScriptSparseSet.h:400
void RemoveAt(int32 Index, const FScriptSparseSetLayout &Layout)
Definition ScriptSparseSet.h:147
void RemoveAtUninitialized(const FScriptSparseSetLayout &Layout, int32 Index)
Definition ScriptSparseSet.h:178
TScriptSparseSet(const TScriptSparseSet &)
Definition ScriptSparseSet.h:399
int32 AddUninitialized(const FScriptSparseSetLayout &Layout)
Definition ScriptSparseSet.h:173
bool IsValidIndex(int32 Index) const
Definition ScriptSparseSet.h:68
void CommitAllUninitialized(const FScriptSparseSetLayout &Layout, TFunctionRef< uint32(const void *)> GetKeyHash)
Definition ScriptSparseSet.h:188
static UE_FORCEINLINE_HINT uint32 GetNumberOfHashBuckets(uint32 NumHashedElements)
Definition ContainerAllocationPolicies.h:1529
@ Element
Definition Visu.h:18
U16 Index
Definition radfft.cpp:71
Definition IntrusiveUnsetOptionalState.h:71
static constexpr UE_FORCEINLINE_HINT bool IsPowerOfTwo(T Value)
Definition UnrealMathUtility.h:519
Definition SparseArray.h:1455
Definition ScriptSparseSet.h:13
int32 HashNextIdOffset
Definition ScriptSparseSet.h:15
int32 Size
Definition ScriptSparseSet.h:17
FScriptSparseArrayLayout SparseArrayLayout
Definition ScriptSparseSet.h:19
int32 HashIndexOffset
Definition ScriptSparseSet.h:16
Definition UnrealTypeTraits.h:172