UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
LumenSparseSpanArray.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3/*=============================================================================
4 LumenSparseSpanArray.h:
5=============================================================================*/
6
7#pragma once
8
9#include "CoreMinimal.h"
10#include "Engine/EngineTypes.h"
11#include "SpanAllocator.h"
12
13// Sparse array with stable indices and contiguous span allocation
14template <typename ElementType>
16{
17public:
18 int32 Num() const
19 {
20 return SpanAllocator.GetMaxSize();
21 }
22
23 void Reserve(int32 NumElements)
24 {
25 Elements.Reserve(NumElements);
26 }
27
28 int32 AddSpan(int32 NumElements)
29 {
30 check(NumElements > 0);
31
32 const int32 InsertIndex = SpanAllocator.Allocate(NumElements);
33
34 // Resize element array
35 if (SpanAllocator.GetMaxSize() > Elements.Num())
36 {
37 const int32 NumElementsToAdd = SpanAllocator.GetMaxSize() - Elements.Num();
39 AllocatedElementsBitArray.Add(false, NumElementsToAdd);
40 }
41
42 // Reuse existing elements
43 for (int32 ElementIndex = InsertIndex; ElementIndex < InsertIndex + NumElements; ++ElementIndex)
44 {
45 checkSlow(!IsAllocated(ElementIndex));
46 Elements[ElementIndex] = ElementType();
47 }
48
49 AllocatedElementsBitArray.SetRange(InsertIndex, NumElements, true);
50
51 return InsertIndex;
52 }
53
55 {
56 check(NumElements > 0);
57
58 for (int32 ElementIndex = FirstElementIndex; ElementIndex < FirstElementIndex + NumElements; ++ElementIndex)
59 {
60 checkSlow(IsAllocated(ElementIndex));
61 Elements[ElementIndex] = ElementType();
62 }
63
64 SpanAllocator.Free(FirstElementIndex, NumElements);
65 AllocatedElementsBitArray.SetRange(FirstElementIndex, NumElements, false);
66 }
67
69 {
70 SpanAllocator.Consolidate();
71
72 if (Elements.Num() > SpanAllocator.GetMaxSize() * ShrinkThreshold)
73 {
74 Elements.SetNum(SpanAllocator.GetMaxSize());
75 AllocatedElementsBitArray.SetNumUninitialized(SpanAllocator.GetMaxSize());
76 }
77 }
78
79 void Reset()
80 {
81 Elements.Reset();
82 SpanAllocator.Reset();
83 AllocatedElementsBitArray.SetNumUninitialized(0);
84 }
85
86 ElementType& operator[](int32 Index)
87 {
89 return Elements[Index];
90 }
91
92 const ElementType& operator[](int32 Index) const
93 {
95 return Elements[Index];
96 }
97
98 bool IsAllocated(int32 ElementIndex) const
99 {
100 if (ElementIndex < Num())
101 {
102 return AllocatedElementsBitArray[ElementIndex];
103 }
104
105 return false;
106 }
107
109 {
110 return Elements.GetAllocatedSize() + AllocatedElementsBitArray.GetAllocatedSize() + SpanAllocator.GetAllocatedSize();
111 }
112
114 {
115 public:
117 : Array(InArray)
118 , ElementIndex(InElementIndex)
119 {
120 // Scan for the first valid element.
121 while (ElementIndex < Array.Num() && !Array.AllocatedElementsBitArray[ElementIndex])
122 {
123 ++ElementIndex;
124 }
125 }
126
128 {
129 // Scan for the next first valid element.
130 do
131 {
132 ++ElementIndex;
133 } while (ElementIndex < Array.Num() && !Array.AllocatedElementsBitArray[ElementIndex]);
134
135 return *this;
136 }
137
139 {
140 return ElementIndex != Other.ElementIndex;
141 }
142
143 ElementType& operator*()
144 {
145 return Array.Elements[ElementIndex];
146 }
147
148 private:
150 int32 ElementIndex;
151 };
152
154 {
155 public:
157 : Array(InArray)
158 , ElementIndex(InElementIndex)
159 {
160 // Scan for the first valid element.
161 while (ElementIndex < Array.Num() && !Array.AllocatedElementsBitArray[ElementIndex])
162 {
163 ++ElementIndex;
164 }
165 }
166
168 {
169 // Scan for the next first valid element.
170 do
171 {
172 ++ElementIndex;
173 } while (ElementIndex < Array.Num() && !Array.AllocatedElementsBitArray[ElementIndex]);
174
175 return *this;
176 }
177
179 {
180 return ElementIndex != Other.ElementIndex;
181 }
182
183 const ElementType& operator*() const
184 {
185 return Array.Elements[ElementIndex];
186 }
187
188 private:
190 int32 ElementIndex;
191 };
192
193 // Iterate over all allocated elements (skip free ones)
198
199private:
200 // Allocated size needs to be this much bigger than used size before we shrink this array
201 static constexpr int32 ShrinkThreshold = 2;
202
203 TArray<ElementType> Elements;
204 TBitArray<> AllocatedElementsBitArray;
205 FSpanAllocator SpanAllocator;
206};
207
208template <typename ElementType, uint32 BytesPerChunk = 2 * 1024 * 1024>
210{
211public:
213
215 {
216 *this = Other;
217 }
218
223
225 {
226 if (&Other != this)
227 {
228 Empty();
229
230 FreeElementIndexHint = Other.FreeElementIndexHint;
231 NumAllocatedElements = Other.NumAllocatedElements;
232 MaxAllocatedElementIndexPlusOne = Other.MaxAllocatedElementIndexPlusOne;
233 AllocatedElementsBitArray = Other.AllocatedElementsBitArray;
234
235 const int32 NumElementChunks = Other.ElementChunks.Num();
236 ElementChunks.Reserve(NumElementChunks);
237 for (int32 ChunkIndex = 0; ChunkIndex < NumElementChunks; ++ChunkIndex)
238 {
239 ElementChunks.Add((ElementType*)FMemory::Malloc(BytesPerChunk));
240 }
241
242 for (TConstSetBitIterator It(AllocatedElementsBitArray); It && It.GetIndex() < GetMaxSize(); ++It)
243 {
244 const int32 ElementIndex = It.GetIndex();
245 ElementType& Element = (*this)[ElementIndex];
246 new (&Element) ElementType(Other[ElementIndex]);
247 }
248 }
249
250 return *this;
251 }
252
254 {
255 if (&Other != this)
256 {
257 Empty();
258
259 FreeElementIndexHint = Other.FreeElementIndexHint;
260 NumAllocatedElements = Other.NumAllocatedElements;
261 MaxAllocatedElementIndexPlusOne = Other.MaxAllocatedElementIndexPlusOne;
262 Other.FreeElementIndexHint = 0;
263 Other.NumAllocatedElements = 0;
264 Other.MaxAllocatedElementIndexPlusOne = 0;
265
266 AllocatedElementsBitArray = MoveTemp(Other.AllocatedElementsBitArray);
267 ElementChunks = MoveTemp(Other.ElementChunks);
268 }
269
270 return *this;
271 }
272
274 {
275 Empty();
276 }
277
278 void Empty()
279 {
280 for (ElementType& Element : *this)
281 {
282 Element.~ElementType();
283 }
284
285 for (ElementType* Chunk : ElementChunks)
286 {
287 FMemory::Free(Chunk);
288 }
289
290 ElementChunks.Empty();
291 AllocatedElementsBitArray.Empty();
292 FreeElementIndexHint = 0;
293 NumAllocatedElements = 0;
294 MaxAllocatedElementIndexPlusOne = 0;
295 }
296
298 {
299 return MaxAllocatedElementIndexPlusOne;
300 }
301
302 void Reserve(int32 NumElements)
303 {
304 const int32 NewNumChunks = FMath::DivideAndRoundUp(NumElements, ElementsPerChunk);
305
306 AllocatedElementsBitArray.SetNum(FMath::Max(ElementChunks.Num(), NewNumChunks) * ElementsPerChunk, false);
307
308 while (ElementChunks.Num() < NewNumChunks)
309 {
310 ElementChunks.Add((ElementType*)FMemory::Malloc(BytesPerChunk));
311 }
312 }
313
315 {
316 // Find the first unused element slot or add to the end
317 int32 InsertIndex;
318 if (NumAllocatedElements == MaxAllocatedElementIndexPlusOne)
319 {
320 check(MaxAllocatedElementIndexPlusOne <= AllocatedElementsBitArray.Num());
321 InsertIndex = MaxAllocatedElementIndexPlusOne;
322 ++MaxAllocatedElementIndexPlusOne;
323
324 if (InsertIndex < AllocatedElementsBitArray.Num())
325 {
326 AllocatedElementsBitArray[InsertIndex] = true;
327 }
328 }
329 else
330 {
331 check(NumAllocatedElements < MaxAllocatedElementIndexPlusOne);
332 InsertIndex = AllocatedElementsBitArray.FindAndSetFirstZeroBit(FreeElementIndexHint);
333 check(InsertIndex < MaxAllocatedElementIndexPlusOne);
334 }
335
336 FreeElementIndexHint = InsertIndex + 1;
337 ++NumAllocatedElements;
338
339 // Grow by one chunk if we run out of space
340 if (MaxAllocatedElementIndexPlusOne > ElementChunks.Num() * ElementsPerChunk)
341 {
342 check(InsertIndex + 1 == MaxAllocatedElementIndexPlusOne);
343 check(InsertIndex == ElementChunks.Num() * ElementsPerChunk);
344 check(AllocatedElementsBitArray.Num() == ElementChunks.Num() * ElementsPerChunk);
345
346 ElementChunks.Add((ElementType*)FMemory::Malloc(BytesPerChunk));
347 AllocatedElementsBitArray.Add(false, ElementsPerChunk);
348 AllocatedElementsBitArray[InsertIndex] = true;
349 }
350
351 check(IsAllocated(InsertIndex));
352 const int32 ChunkIndex = InsertIndex / ElementsPerChunk;
353 const int32 Index = InsertIndex - ChunkIndex * ElementsPerChunk;
354 // We can construct in-place because the element is either uninitialized or has already been destructed
355 new (&ElementChunks[ChunkIndex][Index]) ElementType;
356
357 return InsertIndex;
358 }
359
360 void RemoveAt(int32 ElementIndex)
361 {
362 check(IsAllocated(ElementIndex));
363 const int32 ChunkIndex = ElementIndex / ElementsPerChunk;
364 const int32 Index = ElementIndex - ChunkIndex * ElementsPerChunk;
365 ElementChunks[ChunkIndex][Index].~ElementType();
366
367 AllocatedElementsBitArray[ElementIndex] = false;
368
369 --NumAllocatedElements;
370 FreeElementIndexHint = FMath::Min(FreeElementIndexHint, ElementIndex);
371 if (ElementIndex + 1 == MaxAllocatedElementIndexPlusOne)
372 {
373 MaxAllocatedElementIndexPlusOne = AllocatedElementsBitArray.FindLastFrom(true, ElementIndex - 1) + 1;
374 }
375 check(FreeElementIndexHint <= MaxAllocatedElementIndexPlusOne);
376 }
377
378 void Shrink()
379 {
380 // Try to keep an additional chunk if the last used chunk is more than half full
381 const int32 ChunksToKeep = FMath::DivideAndRoundUp(MaxAllocatedElementIndexPlusOne + ElementsPerChunk / 2, ElementsPerChunk);
382
383 AllocatedElementsBitArray.SetNumUninitialized(FMath::Min(ElementChunks.Num(), ChunksToKeep) * ElementsPerChunk);
384
385 while (ElementChunks.Num() > ChunksToKeep)
386 {
387 FMemory::Free(ElementChunks.Last());
388 ElementChunks.Pop(EAllowShrinking::No);
389 }
390 }
391
392 ElementType& operator[](int32 ElementIndex)
393 {
394 check(IsAllocated(ElementIndex));
395 const int32 ChunkIndex = ElementIndex / ElementsPerChunk;
396 const int32 Index = ElementIndex - ChunkIndex * ElementsPerChunk;
397 return ElementChunks[ChunkIndex][Index];
398 }
399
400 const ElementType& operator[](int32 ElementIndex) const
401 {
402 check(IsAllocated(ElementIndex));
403 const int32 ChunkIndex = ElementIndex / ElementsPerChunk;
404 const int32 Index = ElementIndex - ChunkIndex * ElementsPerChunk;
405 return ElementChunks[ChunkIndex][Index];
406 }
407
408 bool IsAllocated(int32 ElementIndex) const
409 {
410 if (ElementIndex < GetMaxSize())
411 {
412 return AllocatedElementsBitArray[ElementIndex];
413 }
414
415 return false;
416 }
417
419 {
420 return ElementChunks.Num() * BytesPerChunk + ElementChunks.GetAllocatedSize() + AllocatedElementsBitArray.GetAllocatedSize() + sizeof(TChunkedSparseArray);
421 }
422
424 {
425 public:
427 : Array(InArray)
428 , ElementIndex(InElementIndex)
429 {
430 // Scan for the first valid element.
431 while (ElementIndex < Array.GetMaxSize() && !Array.AllocatedElementsBitArray[ElementIndex])
432 {
433 ++ElementIndex;
434 }
435 }
436
438 {
439 // Scan for the next first valid element.
440 do
441 {
442 ++ElementIndex;
443 } while (ElementIndex < Array.GetMaxSize() && !Array.AllocatedElementsBitArray[ElementIndex]);
444
445 return *this;
446 }
447
449 {
450 return ElementIndex != Other.ElementIndex;
451 }
452
453 ElementType& operator*()
454 {
455 return Array[ElementIndex];
456 }
457
458 private:
460 int32 ElementIndex;
461 };
462
464 {
465 public:
467 : Array(InArray)
468 , ElementIndex(InElementIndex)
469 {
470 // Scan for the first valid element.
471 while (ElementIndex < Array.GetMaxSize() && !Array.AllocatedElementsBitArray[ElementIndex])
472 {
473 ++ElementIndex;
474 }
475 }
476
478 {
479 // Scan for the next first valid element.
480 do
481 {
482 ++ElementIndex;
483 } while (ElementIndex < Array.GetMaxSize() && !Array.AllocatedElementsBitArray[ElementIndex]);
484
485 return *this;
486 }
487
489 {
490 return ElementIndex != Other.ElementIndex;
491 }
492
493 const ElementType& operator*() const
494 {
495 return Array[ElementIndex];
496 }
497
498 private:
500 int32 ElementIndex;
501 };
502
503 // Iterate over all allocated elements (skip free ones)
508
509private:
510 static constexpr int32 ElementsPerChunk = BytesPerChunk / sizeof(ElementType);
511
512 int32 FreeElementIndexHint = 0;
513 int32 NumAllocatedElements = 0;
514 int32 MaxAllocatedElementIndexPlusOne = 0;
515
516 TArray<ElementType*> ElementChunks;
517 TBitArray<> AllocatedElementsBitArray;
518};
#define checkSlow(expr)
Definition AssertionMacros.h:332
#define check(expr)
Definition AssertionMacros.h:314
FPlatformTypes::SIZE_T SIZE_T
An unsigned integer the same size as a pointer, the same as UPTRINT.
Definition Platform.h:1150
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
UE_INTRINSIC_CAST UE_REWRITE constexpr std::remove_reference_t< T > && MoveTemp(T &&Obj) noexcept
Definition UnrealTemplate.h:520
Definition SpanAllocator.h:15
void Free(int32 BaseOffset, int32 Num=1)
Definition SpanAllocator.h:63
int32 GetMaxSize() const
Definition SpanAllocator.h:80
ENGINE_API void Consolidate()
Definition SpanAllocator.cpp:58
SIZE_T GetAllocatedSize() const
Definition SpanAllocator.h:95
int32 Allocate(int32 Num=1)
Definition SpanAllocator.h:23
ENGINE_API void Reset()
Definition SpanAllocator.cpp:15
Definition Array.h:670
UE_REWRITE SizeType Num() const
Definition Array.h:1144
UE_NODEBUG UE_FORCEINLINE_HINT ElementType & Last(SizeType IndexFromTheEnd=0) UE_LIFETIMEBOUND
Definition Array.h:1263
void Reset(SizeType NewSize=0)
Definition Array.h:2246
SizeType AddDefaulted()
Definition Array.h:2795
void SetNum(SizeType NewNum, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2308
UE_NODEBUG UE_FORCEINLINE_HINT SizeType Add(ElementType &&Item)
Definition Array.h:2696
UE_NODEBUG UE_FORCEINLINE_HINT SIZE_T GetAllocatedSize(void) const
Definition Array.h:1059
ElementType Pop(EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:1196
void Empty(SizeType Slack=0)
Definition Array.h:2273
UE_FORCEINLINE_HINT void Reserve(SizeType Number)
Definition Array.h:3016
UE_FORCEINLINE_HINT int32 Num() const
Definition BitArray.h:1466
int32 Add(const bool Value)
Definition BitArray.h:615
void Empty(int32 ExpectedNumBits=0)
Definition BitArray.h:779
int32 FindAndSetFirstZeroBit(int32 StartIndex=0)
Definition BitArray.h:1258
uint32 GetAllocatedSize(void) const
Definition BitArray.h:1062
void SetNumUninitialized(int32 InNumBits)
Definition BitArray.h:849
FORCENOINLINE void SetNum(int32 InNumBits, ValueType bValue)
Definition BitArray.h:870
FORCENOINLINE void SetRange(int32 Index, int32 NumBitsToSet, bool Value)
Definition BitArray.h:887
int32 FindLastFrom(bool bValue, IndexType EndIndexInclusive) const
Definition BitArray.h:1228
Definition LumenSparseSpanArray.h:464
const ElementType & operator*() const
Definition LumenSparseSpanArray.h:493
TRangedForConstIterator(const TChunkedSparseArray< ElementType > &InArray, int32 InElementIndex)
Definition LumenSparseSpanArray.h:466
TRangedForConstIterator operator++()
Definition LumenSparseSpanArray.h:477
bool operator!=(const TRangedForConstIterator &Other) const
Definition LumenSparseSpanArray.h:488
Definition LumenSparseSpanArray.h:424
ElementType & operator*()
Definition LumenSparseSpanArray.h:453
TRangedForIterator(TChunkedSparseArray< ElementType > &InArray, int32 InElementIndex)
Definition LumenSparseSpanArray.h:426
bool operator!=(const TRangedForIterator &Other) const
Definition LumenSparseSpanArray.h:448
TRangedForIterator operator++()
Definition LumenSparseSpanArray.h:437
Definition LumenSparseSpanArray.h:210
void Empty()
Definition LumenSparseSpanArray.h:278
void Reserve(int32 NumElements)
Definition LumenSparseSpanArray.h:302
void Shrink()
Definition LumenSparseSpanArray.h:378
TRangedForIterator end()
Definition LumenSparseSpanArray.h:505
const ElementType & operator[](int32 ElementIndex) const
Definition LumenSparseSpanArray.h:400
SIZE_T GetAllocatedSize() const
Definition LumenSparseSpanArray.h:418
TChunkedSparseArray(TChunkedSparseArray &&Other)
Definition LumenSparseSpanArray.h:219
~TChunkedSparseArray()
Definition LumenSparseSpanArray.h:273
TChunkedSparseArray & operator=(TChunkedSparseArray &&Other)
Definition LumenSparseSpanArray.h:253
int32 GetMaxSize() const
Definition LumenSparseSpanArray.h:297
int32 AddDefaulted()
Definition LumenSparseSpanArray.h:314
bool IsAllocated(int32 ElementIndex) const
Definition LumenSparseSpanArray.h:408
TRangedForConstIterator end() const
Definition LumenSparseSpanArray.h:507
TRangedForConstIterator begin() const
Definition LumenSparseSpanArray.h:506
void RemoveAt(int32 ElementIndex)
Definition LumenSparseSpanArray.h:360
TChunkedSparseArray()=default
TRangedForIterator begin()
Definition LumenSparseSpanArray.h:504
TChunkedSparseArray(const TChunkedSparseArray &Other)
Definition LumenSparseSpanArray.h:214
TChunkedSparseArray & operator=(const TChunkedSparseArray &Other)
Definition LumenSparseSpanArray.h:224
ElementType & operator[](int32 ElementIndex)
Definition LumenSparseSpanArray.h:392
Definition BitArray.h:1944
UE_FORCEINLINE_HINT int32 GetIndex() const
Definition BitArray.h:2011
Definition LumenSparseSpanArray.h:154
TRangedForConstIterator(const TSparseSpanArray< ElementType > &InArray, int32 InElementIndex)
Definition LumenSparseSpanArray.h:156
const ElementType & operator*() const
Definition LumenSparseSpanArray.h:183
TRangedForConstIterator operator++()
Definition LumenSparseSpanArray.h:167
bool operator!=(const TRangedForConstIterator &Other) const
Definition LumenSparseSpanArray.h:178
Definition LumenSparseSpanArray.h:114
ElementType & operator*()
Definition LumenSparseSpanArray.h:143
bool operator!=(const TRangedForIterator &Other) const
Definition LumenSparseSpanArray.h:138
TRangedForIterator(TSparseSpanArray< ElementType > &InArray, int32 InElementIndex)
Definition LumenSparseSpanArray.h:116
TRangedForIterator operator++()
Definition LumenSparseSpanArray.h:127
Definition LumenSparseSpanArray.h:16
bool IsAllocated(int32 ElementIndex) const
Definition LumenSparseSpanArray.h:98
void Reserve(int32 NumElements)
Definition LumenSparseSpanArray.h:23
SIZE_T GetAllocatedSize() const
Definition LumenSparseSpanArray.h:108
TRangedForConstIterator begin() const
Definition LumenSparseSpanArray.h:196
ElementType & operator[](int32 Index)
Definition LumenSparseSpanArray.h:86
TRangedForIterator end()
Definition LumenSparseSpanArray.h:195
const ElementType & operator[](int32 Index) const
Definition LumenSparseSpanArray.h:92
TRangedForIterator begin()
Definition LumenSparseSpanArray.h:194
void Reset()
Definition LumenSparseSpanArray.h:79
void RemoveSpan(int32 FirstElementIndex, int32 NumElements)
Definition LumenSparseSpanArray.h:54
int32 AddSpan(int32 NumElements)
Definition LumenSparseSpanArray.h:28
TRangedForConstIterator end() const
Definition LumenSparseSpanArray.h:197
int32 Num() const
Definition LumenSparseSpanArray.h:18
void Consolidate()
Definition LumenSparseSpanArray.h:68
U16 Index
Definition radfft.cpp:71
static constexpr UE_FORCEINLINE_HINT T DivideAndRoundUp(T Dividend, T Divisor)
Definition UnrealMathUtility.h:694
static FORCENOINLINE CORE_API void Free(void *Original)
Definition UnrealMemory.cpp:685