UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
SparseListSet.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3// Port of geometry3cpp small_list_set
4
5#pragma once
6
7#include "HAL/PlatformMath.h"
10#include "Util/DynamicVector.h"
11
12class FArchive;
13
14namespace UE
15{
16namespace Geometry
17{
18
37template<typename ElementType>
39{
40public:
41
43 {
44 ChunkList.Add(MakeUnique<FChunk>());
45 ChunkList[0]->Initialize(BlocksPerChunk * BlockSize);
46 }
47
53 {
54 BlockSize = FMath::Clamp(BlockSizeIn, 4, 65536);
55 BlocksPerChunk = FMath::Clamp(BlocksPerChunkIn, 4, 65536);
56 ChunkList.Add(MakeUnique<FChunk>());
57 ChunkList[0]->Initialize(BlocksPerChunk * BlockSize);
58 }
59
60
64 bool IsAllocated(int32 ListIndex) const
65 {
66 return (ListIndex >= 0 && ListIndex < (int32)Lists.Num() && Lists[ListIndex].CurBlock != nullptr );
67 }
68
69 // handle to an allocated list returned by AllocateAt()
71 {
72 void* ListRef = nullptr;
73 };
74
80 {
81 FList NewList;
82 NewList.Blocks.Add( AllocateNewBlockMemory() );
83 NewList.CurBlock = NewList.Blocks[0];
84 NewList.CurIndex = 0;
85 Lists.InsertAt(NewList, ListIndex);
86 return FListHandle{(void*)&Lists[ListIndex]}; // this pointer is stable because of DynamicVector
87 }
88
92 void Insert(int32 ListIndex, ElementType Value)
93 {
94 checkSlow(IsAllocated(ListIndex));
95 Insert_Internal(Lists[ListIndex], Value);
96 }
97
102 {
103 FList& List = *(FList*)ListHandle.ListRef;
104 Insert_Internal(List, Value);
105 }
106
107
111 void SetValues(int32 ListIndex, const TArray<ElementType>& Values)
112 {
113 checkSlow(IsAllocated(ListIndex));
114 SetValues_Internal(Lists[ListIndex], Values);
115 }
116
117
122 {
123 FList& List = *(FList*)ListHandle.ListRef;
124 SetValues_Internal(List, Values);
125 }
126
127
132 inline bool Remove(int32 ListIndex, ElementType Value);
133
134
138 void Clear(int32 ListIndex)
139 {
140 checkSlow(IsAllocated(ListIndex));
141 FList& List = Lists[ListIndex];
142 Clear_Internal(List);
143 }
144
145
149 int32 GetCount(int32 ListIndex) const
150 {
151 checkSlow(IsAllocated(ListIndex));
152 const FList& List = Lists[ListIndex];
153 return FMath::Max(0, List.Blocks.Num()-1)*BlockSize + List.CurIndex;
154 }
155
156
161 bool Contains(int32 ListIndex, ElementType Value) const;
162
163
167 template<typename FuncType>
168 void Enumerate(int32 ListIndex, FuncType Func) const
169 {
170 checkSlow(IsAllocated(ListIndex));
171 const FList& List = Lists[ListIndex];
172 if ( List.CurBlock != nullptr )
173 {
174 int32 N = List.Blocks.Num();
175 for (int32 BlockIndex = 0; BlockIndex < N; ++BlockIndex)
176 {
177 const ElementType* Elements = List.Blocks[BlockIndex];
178 const ElementType* EndElement = (BlockIndex == N-1) ? (Elements + List.CurIndex) : (Elements + BlockSize);
179 while (Elements != EndElement)
180 {
181 Func(*Elements++);
182 }
183 }
184 }
185 }
186
191 template<typename FuncType>
192 bool EnumerateEarlyOut(int32 ListIndex, FuncType ApplyFunc) const
193 {
194 checkSlow(IsAllocated(ListIndex));
195 const FList& List = Lists[ListIndex];
196 if (List.CurBlock != nullptr)
197 {
198 int32 N = List.Blocks.Num();
199 for (int32 BlockIndex = 0; BlockIndex < N; ++BlockIndex)
200 {
201 const ElementType* Elements = List.Blocks[BlockIndex];
202 const ElementType* EndElement = (BlockIndex == N - 1) ? (Elements + List.CurIndex) : (Elements + BlockSize);
203 while (Elements != EndElement)
204 {
205 if (!ApplyFunc(*Elements++))
206 {
207 return false;
208 }
209 }
210 }
211 }
212 return true;
213 }
214
215
216
217private:
218
219 // size of a list memory block, ie lists are allocated in blocks of this size, so also the minimum size (in memory) of a list even if it has 0 elements
220 int BlockSize = 32;
221 // number of memory blocks in a "chunk", chunks are allocated as needed, so minimum size of a TSparseListSet is (BlocksPerChunk * BlockSize * sizeof(ElementType))
222 int BlocksPerChunk = 256;
223
224 // large block of memory from which smaller blocks are allocated
225 struct FChunk
226 {
227 int BlocksAllocated = 0;
228 void Initialize(int Size) { ElementBuffer.SetNumUninitialized(Size); }
229 ElementType* GetBlockBufferPtr(int32 Offset) { return &ElementBuffer[Offset]; }
230 private:
231 TArray<ElementType> ElementBuffer; // note: using TArray here to auto-delete, but this array cannot be resized or modified after creation
232 };
233 TArray<TUniquePtr<FChunk>> ChunkList; // could use dynamic vector here to avoid realloc's...?
234 TArray<ElementType*> FreeList;
235 FCriticalSection AllocationLock; // lock for ChunkList and FreeList
236
237 struct FList
238 {
239 TArray<ElementType*, TInlineAllocator<8>> Blocks; // todo: could replace this with a linked list packed in with the element buffers? would avoid overflow issue...
240 ElementType* CurBlock = nullptr;
241 int32 CurIndex = 0;
242 };
243
244 TDynamicVector<FList> Lists; // this is the list of small lists
245
246
247 void Insert_Internal(FList& List, ElementType Value)
248 {
249 if (List.CurIndex == BlockSize)
250 {
251 List.Blocks.Add( AllocateNewBlockMemory() );
252 List.CurBlock = List.Blocks.Last();
253 List.CurIndex = 0;
254 }
255 List.CurBlock[List.CurIndex++] = Value;
256 }
257
258 void SetValues_Internal(FList& List, const TArray<ElementType>& Values)
259 {
260 int32 N = Values.Num();
261 if (List.CurIndex != 0)
262 {
263 Clear_Internal(List);
264 }
265 for (int32 k = 0; k < N; ++k)
266 {
267 // could avoid this branch by doing sub-iterations over BlockSize?
268 if (List.CurIndex == BlockSize)
269 {
270 List.Blocks.Add( AllocateNewBlockMemory() );
271 List.CurBlock = List.Blocks.Last();
272 List.CurIndex = 0;
273 }
274 List.CurBlock[List.CurIndex++] = Values[k];
275 }
276 }
277
278
279 void Clear_Internal(FList& List)
280 {
281 for ( int32 k = 1; k < List.Blocks.Num(); ++k)
282 {
283 FreeBlockMemory(List.Blocks[k]);
284 }
285 List.Blocks.SetNum(1);
286 List.CurBlock = List.Blocks[0];
287 List.CurIndex = 0;
288 }
289
290
291 ElementType* AllocateNewBlockMemory()
292 {
293 FScopeLock Lock(&AllocationLock);
294
295 if (FreeList.Num() > 0) // return from free list if available
296 {
297 return FreeList.Pop();
298 }
299
300 FChunk* LastChunk = ChunkList.Last().Get();
301 if (LastChunk->BlocksAllocated == BlocksPerChunk)
302 {
303 ChunkList.Add(MakeUnique<FChunk>());
304 LastChunk = ChunkList.Last().Get();
305 LastChunk->Initialize( BlocksPerChunk * BlockSize );
306 }
307
308 ElementType* BlockBuffer = LastChunk->GetBlockBufferPtr(LastChunk->BlocksAllocated * BlockSize);
309 LastChunk->BlocksAllocated++;
310
311 return BlockBuffer;
312 }
313
314 void FreeBlockMemory(ElementType* Block)
315 {
316 FScopeLock Lock(&AllocationLock);
317 FreeList.Add(Block);
318 }
319
320};
321
322
323
324template<typename ElementType>
326{
327 checkSlow(IsAllocated(ListIndex));
328 FList& List = Lists[ListIndex];
329 if ( List.CurBlock != nullptr )
330 {
331 int32 N = List.Blocks.Num();
332 for (int32 BlockIndex = 0; BlockIndex < N; ++BlockIndex)
333 {
334 ElementType* Elements = List.Blocks[BlockIndex];
335 int Stop = (BlockIndex == N-1) ? List.CurIndex : BlockSize;
336 for (int32 k = 0; k < Stop; ++k)
337 {
338 if (Elements[k] == Value)
339 {
340 bool bIsLastBlock = (Elements == List.CurBlock);
341 if (bIsLastBlock)
342 {
343 if (k == List.CurIndex-1)
344 {
345 // removing last element, just decrement counter
346 List.CurIndex--;
347 }
348 else
349 {
350 // swap with last element, then decrement
351 Swap(Elements[k], Elements[List.CurIndex-1]);
352 List.CurIndex--;
353 }
354 }
355 else
356 {
357 if (List.CurIndex == 0)
358 {
359 check(false); // this should only happen if this is the first block and the list is empty, in which case, how could we have found Value?
360 }
361 Swap(Elements[k], List.CurBlock[List.CurIndex-1]);
362 List.CurIndex--;
363 }
364
365 // if we got back to index 0, we need to release this block
366 if (List.CurIndex == 0 && List.Blocks.Num() > 1)
367 {
368 FreeBlockMemory(List.CurBlock);
369 List.Blocks.Pop();
370 List.CurBlock = List.Blocks.Last();
371 List.CurIndex = BlockSize;
372 }
373
374 return true;
375 }
376 }
377 }
378 }
379
380 return false;
381}
382
383
384
385template<typename ElementType>
386bool TSparseListSet<ElementType>::Contains(int32 ListIndex, ElementType Value) const
387{
388 checkSlow(IsAllocated(ListIndex));
389 const FList& List = Lists[ListIndex];
390 int32 N = List.Blocks.Num();
391 for (int32 BlockIndex = 0; BlockIndex < N; ++BlockIndex)
392 {
393 const ElementType* Elements = List.Blocks[BlockIndex];
394 const ElementType* EndElement = (BlockIndex == N-1) ? (Elements + List.CurIndex) : (Elements + BlockSize);
395 while (Elements != EndElement)
396 {
397 if ((*Elements++) == Value)
398 {
399 return true;
400 }
401 }
402 }
403 return false;
404}
405
406
407} // end namespace UE::Geometry
408} // end namespace UE
#define checkSlow(expr)
Definition AssertionMacros.h:332
#define check(expr)
Definition AssertionMacros.h:314
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::FPlatformRecursiveMutex FCriticalSection
Definition CriticalSection.h:53
@ Stop
Definition PrecomputedVolumetricLightmapStreaming.cpp:26
FRWLock Lock
Definition UnversionedPropertySerialization.cpp:921
uint32 Offset
Definition VulkanMemory.cpp:4033
uint32 Size
Definition VulkanMemory.cpp:4034
int BlockIndex
Definition binka_ue_decode_test.cpp:38
Definition Archive.h:1208
Definition ScopeLock.h:141
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
UE_NODEBUG UE_FORCEINLINE_HINT SizeType Add(ElementType &&Item)
Definition Array.h:2696
ElementType Pop(EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:1196
void InsertAt(const Type &Data, unsigned int Index)
Definition DynamicVector.h:747
size_t Num() const
Definition DynamicVector.h:147
@ List
Definition ITypedTableView.h:38
Definition AdvancedWidgetsModule.cpp:13
static constexpr UE_FORCEINLINE_HINT T Clamp(const T X, const T MinValue, const T MaxValue)
Definition UnrealMathUtility.h:592
Definition SparseListSet.h:71
void * ListRef
Definition SparseListSet.h:72
Definition SparseListSet.h:39
TSparseListSet()
Definition SparseListSet.h:42
bool Remove(int32 ListIndex, ElementType Value)
Definition SparseListSet.h:325
void SetValues(int32 ListIndex, const TArray< ElementType > &Values)
Definition SparseListSet.h:111
TSparseListSet(int BlockSizeIn, int BlocksPerChunkIn)
Definition SparseListSet.h:52
bool EnumerateEarlyOut(int32 ListIndex, FuncType ApplyFunc) const
Definition SparseListSet.h:192
void Insert(FListHandle ListHandle, ElementType Value)
Definition SparseListSet.h:101
void Clear(int32 ListIndex)
Definition SparseListSet.h:138
FListHandle AllocateAt(int32 ListIndex)
Definition SparseListSet.h:79
void Insert(int32 ListIndex, ElementType Value)
Definition SparseListSet.h:92
bool IsAllocated(int32 ListIndex) const
Definition SparseListSet.h:64
int32 GetCount(int32 ListIndex) const
Definition SparseListSet.h:149
bool Contains(int32 ListIndex, ElementType Value) const
Definition SparseListSet.h:386
void SetValues(FListHandle ListHandle, const TArray< ElementType > &Values)
Definition SparseListSet.h:121
void Enumerate(int32 ListIndex, FuncType Func) const
Definition SparseListSet.h:168