UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
AttributeArrayContainer.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "CoreMinimal.h"
7#include "Algo/Rotate.h"
8
9
10template <typename AttributeType>
12{
13public:
15
16 explicit TAttributeArrayContainer(const AttributeType& InDefaultValue)
17 : DefaultValue(InDefaultValue)
18 {}
19
21 int32 Num() const { return NumElements; }
22
24 void Initialize(const int32 ElementCount, const AttributeType& Default)
25 {
26 // For unbounded arrays, the default value is an empty subarray.
27 check(ElementCount >= 0);
28 int32 NumChunks = GetNumRequiredChunks(ElementCount);
29 Chunks.Empty(NumChunks);
30 Chunks.SetNum(NumChunks);
31 if (NumChunks > 0)
32 {
33 // Set num elements in the final chunk to the right value.
34 // All previous chunks are default initialized to be 'full'.
35 Chunks.Last().NumElements = GetElementsInLastChunk(ElementCount);
36 }
37
38 NumElements = ElementCount;
39 }
40
42 void SetNum(const int32 ElementCount, const AttributeType& Default)
43 {
44 check(ElementCount >= 0);
45
46 int32 OldNumChunks = Chunks.Num();
47 int32 NumChunks = GetNumRequiredChunks(ElementCount);
48 Chunks.SetNum(NumChunks);
49
50 if (ElementCount < NumElements)
51 {
52 // Case where we're shrinking the unbounded array
53 if (NumChunks > 0)
54 {
55 Chunks.Last().NumElements = GetElementsInLastChunk(ElementCount);
56 }
57 }
58 else if (ElementCount > NumElements)
59 {
60 // Case where we're growing the unbounded array
61 int32 IndexInOldLastChunk = NumElements & ChunkMask;
62 int32 ElementsInLastChunk = GetElementsInLastChunk(ElementCount);
63
64 if (IndexInOldLastChunk > 0)
65 {
66 // If the original final chunk had unused elements, we need to initialize their data pointers up to either:
67 // - the new final element, if it is still the final chunk
68 // - the end (ChunkSize) if it is no longer the final chunk
69 int32 LastIndex = (NumChunks == OldNumChunks) ? ElementsInLastChunk : ChunkSize;
70
71 FChunk& OldLastChunk = Chunks[OldNumChunks - 1];
72
73 // Determine the start index in the data block of the next element,
74 // and resize it to that amount
77
78 for (; IndexInOldLastChunk < LastIndex; IndexInOldLastChunk++)
79 {
83 }
84
85 // Update the element count for the old last chunk
86 OldLastChunk.NumElements = LastIndex;
87 }
88
89 // Any further chunks which have just been initialized will default to being 'full', i.e. with ChunkSize elements
90 // The very last chunk will need initializing to the correct value.
91 // If this happens to coincide with the old last chunk, it doesn't hurt to do this again here.
92 Chunks.Last().NumElements = ElementsInLastChunk;
93 }
94
95 NumElements = ElementCount;
96 }
97
99 {
100 for (const FChunk& Chunk : Chunks)
101 {
102 Crc = FCrc::MemCrc32(Chunk.Data.GetData(), Chunk.Data.Num() * sizeof(AttributeType), Crc);
103 }
104 return Crc;
105 }
106
108 void Insert(const int32 Index, const AttributeType& Default)
109 {
110 check(Index >= 0);
111
112 int32 EndIndex = Index + 1;
113 if (EndIndex > NumElements)
114 {
115 SetNum(EndIndex, Default);
116 }
117 }
118
120 void SetToDefault(const int32 Index, const AttributeType& Default)
121 {
122 checkSlow(Index >= 0 && Index < NumElements);
123 int32 ChunkIndex = Index >> ChunkBits;
124 int32 ElementIndex = Index & ChunkMask;
125
126 // Default value is an empty array. We do not compact the allocations when shrinking the data.
127 Chunks[ChunkIndex].Count[ElementIndex] = 0;
128 }
129
131 void Remap(const TSparseArray<int32>& IndexRemap, const AttributeType& Default);
132
134 {
135 Ar << Array.Chunks;
136 Ar << Array.NumElements;
137 Ar << Array.DefaultValue;
138 return Ar;
139 }
140
143 {
144 checkSlow(Index >= 0 && Index < NumElements);
145 int32 ChunkIndex = Index >> ChunkBits;
146 int32 ElementIndex = Index & ChunkMask;
147 FChunk& Chunk = Chunks[ChunkIndex];
148 return TArrayView<AttributeType>(Chunk.Data.GetData() + Chunk.StartIndex[ElementIndex], Chunk.Count[ElementIndex]);
149 }
150
153 {
154 checkSlow(Index >= 0 && Index < NumElements);
155 int32 ChunkIndex = Index >> ChunkBits;
156 int32 ElementIndex = Index & ChunkMask;
157 const FChunk& Chunk = Chunks[ChunkIndex];
158 return TArrayView<const AttributeType>(Chunk.Data.GetData() + Chunk.StartIndex[ElementIndex], Chunk.Count[ElementIndex]);
159 }
160
163 {
164 checkSlow(Index >= 0 && Index < NumElements);
165 int32 ChunkIndex = Index >> ChunkBits;
166 int32 ElementIndex = Index & ChunkMask;
167 const FChunk& Chunk = Chunks[ChunkIndex];
168 return Chunk.Count[ElementIndex];
169 }
170
173 {
174 checkSlow(Index >= 0 && Index < NumElements);
175 int32 ChunkIndex = Index >> ChunkBits;
176 int32 ElementIndex = Index & ChunkMask;
177 const FChunk& Chunk = Chunks[ChunkIndex];
178 return Chunk.MaxCount[ElementIndex];
179 }
180
183 {
184 const bool bSetCount = true;
185 const bool bSetDefault = false;
187
188 for (int32 I = 0; I < Value.Num(); I++)
189 {
190 Element[I] = Value[I];
191 }
192 }
193
196 {
197 checkSlow(Index >= 0 && Index < NumElements);
198 int32 ChunkIndex = Index >> ChunkBits;
199 int32 ElementIndex = Index & ChunkMask;
200 FChunk& Chunk = Chunks[ChunkIndex];
201
202 AttributeType* BasePtr = Chunk.Data.GetData();
203
204 int32 Extra = Size - Chunk.MaxCount[ElementIndex];
205 if (Extra > 0)
206 {
207 // If we are requesting that MaxCount grows for this element, we have to insert new data elements,
208 // and adjust subsequent start indices accordingly.
209 int32 OldDataSize = Chunk.Data.Num();
210 Chunk.Data.SetNum(OldDataSize + Extra);
211 BasePtr = Chunk.Data.GetData();
212
213 AttributeType* StartPtr = BasePtr + Chunk.StartIndex[ElementIndex] + Chunk.MaxCount[ElementIndex];
214 AttributeType* EndPtr = BasePtr + OldDataSize;
215 FMemory::Memmove(StartPtr + Extra, StartPtr, (EndPtr - StartPtr) * sizeof(AttributeType));
216
217 Chunk.MaxCount[ElementIndex] = Size;
218 for (int32 I = ElementIndex + 1; I < Chunk.NumElements; I++)
219 {
220 Chunk.StartIndex[I] += Extra;
221 }
222 }
223
224 if (bSetDefault)
225 {
226 if (Size > Chunk.Count[ElementIndex])
227 {
228 // Set any newly created members to the default value
229 for (int32 I = Chunk.Count[ElementIndex]; I < Chunk.MaxCount[ElementIndex]; I++)
230 {
231 Chunk.Data[Chunk.StartIndex[ElementIndex] + I] = DefaultValue;
232 }
233 }
234 else
235 {
236 // Set any removed values to the default value
237 for (int32 I = Size; I < Chunk.Count[ElementIndex]; I++)
238 {
239 Chunk.Data[Chunk.StartIndex[ElementIndex] + I] = DefaultValue;
240 }
241 }
242 }
243
244 if (bSetCount)
245 {
246 Chunk.Count[ElementIndex] = Size;
247 }
248
249 return TArrayView<AttributeType>(BasePtr + Chunk.StartIndex[ElementIndex], Chunk.Count[ElementIndex]);
250 }
251
253 {
254 checkSlow(Index >= 0 && Index < NumElements);
255 int32 ChunkIndex = Index >> ChunkBits;
256 int32 ElementIndex = Index & ChunkMask;
257 FChunk& Chunk = Chunks[ChunkIndex];
258
259 int32 CurrentCount = Chunk.Count[ElementIndex];
262
263 if (NewCount > Chunk.MaxCount[ElementIndex])
264 {
265 // Add a few extra slots in anticipation of further growth in the future
266 // TODO: opt in to this behavior?
267 NewCount = NewCount * 4 / 3;
268
269 // Resize the allocated space for this element if necessary
270 const bool bSetCount = false;
271 const bool bSetDefault = true;
273 }
274
275 Chunk.Count[ElementIndex] += InsertCount;
276
277 // Insert the new subarray elements
282
283 return Element;
284 }
285
287 {
289 for (int32 I = SubArrayIndex + Count; I < Element.Num(); I++)
290 {
291 Element[I - Count] = Element[I];
292 }
293
294 const bool bSetCount = true;
295 const bool bSetDefault = true;
296 return SetElementSize(Index, Element.Num() - Count, bSetCount, bSetDefault);
297 }
298
299 AttributeType GetDefault() const { return DefaultValue; }
300
301private:
302
303 static int32 GetNumRequiredChunks(const int32 ElementCount)
304 {
305 return (ElementCount + ChunkSize - 1) >> ChunkBits;
306 }
307
308 static int32 GetElementsInLastChunk(const int32 ElementCount)
309 {
310 return ((ElementCount + ChunkSize - 1) & ChunkMask) + 1;
311 }
312
313 static const int32 ChunkBits = 8;
314 static const int32 ChunkSize = (1 << ChunkBits);
315 static const int32 ChunkMask = ChunkSize - 1;
316
317 struct FChunk
318 {
319 FChunk()
320 {
321 Data.Reserve(ChunkSize);
322 }
323
324 friend FArchive& operator <<(FArchive& Ar, FChunk& Chunk)
325 {
326 Ar << Chunk.Data;
327 Ar << Chunk.NumElements;
328
329 for (int32 Index = 0; Index < Chunk.NumElements; Index++)
330 {
331 Ar << Chunk.StartIndex[Index];
332 }
333
334 for (int32 Index = 0; Index < Chunk.NumElements; Index++)
335 {
336 Ar << Chunk.Count[Index];
337 }
338
339 for (int32 Index = 0; Index < Chunk.NumElements; Index++)
340 {
341 Ar << Chunk.MaxCount[Index];
342 }
343
344 return Ar;
345 }
346
347 // All the data for each element in the chunk, packed contiguously
349
350 // Start, count and allocated count in the Data array for each element in the chunk.
351 // Arranged as SoA for cache optimization, since the most frequent operation is
352 // adding a fixed amount to all the start indices when a value is inserted.
356
357 // Specify number of valid elements in this chunk.
358 // Default to maximum number so that we can add chunks in bulk, and all put the final
359 // chunk will already be correctly initialized.
360 int32 NumElements = ChunkSize;
361 };
362
363 TArray<FChunk> Chunks;
364 int32 NumElements = 0;
365 AttributeType DefaultValue = AttributeType();
366};
367
368
369
370template <typename AttributeType>
372{
374
375 for (TSparseArray<int32>::TConstIterator It(IndexRemap); It; ++It)
376 {
377 const int32 OldElementIndex = It.GetIndex();
378 const int32 NewElementIndex = IndexRemap[OldElementIndex];
379
382 }
383
385}
386
387
391template <typename AttributeType>
393{
394 template <typename T> friend class TArrayAttribute;
396
397public:
398 explicit TArrayAttribute(ArrayType& InArray, int32 InIndex)
399 : Array(&InArray),
401 {}
402
406 template <typename T = AttributeType, typename TEnableIf<std::is_same_v<T, const T>, int>::Type = 0>
408 : Array(InValue.Array),
410 {}
411
415 AttributeType* GetData() const
416 {
417 return Array->Get(Index).GetData();
418 }
419
428 {
429 return Array->Get(Index).IsValidIndex(ArrayAttributeIndex);
430 }
431
437 bool IsEmpty() const
438 {
439 return Array->Get(Index).IsEmpty();
440 }
441
447 int32 Num() const
448 {
449 return Array->Get(Index).Num();
450 }
451
458 {
459 return Array->Get(Index)[ArrayAttributeIndex];
460 }
461
470 AttributeType& Last(int32 IndexFromTheEnd = 0) const
471 {
472 return Array->Get(Index).Last();
473 }
474
479 {
480 const bool bSetCount = true;
481 const bool bSetDefault = true;
482 Array->SetElementSize(Index, Num, bSetCount, bSetDefault);
483 }
484
485
492 {
493 const bool bSetCount = true;
494 const bool bSetDefault = false;
495 Array->SetElementSize(Index, Num, bSetCount, bSetDefault);
496 }
497
498
503 {
504 const bool bSetCount = false;
505 const bool bSetDefault = true;
506 Array->SetElementSize(Index, Num, bSetCount, bSetDefault);
507 }
508
509
513 int32 Add(const AttributeType& Value)
514 {
515 int32 ElementCount = Array->GetElementCount(Index);
516 TArrayView<AttributeType> Element = Array->InsertIntoElement(Index, ElementCount, 1);
517 Element[ElementCount] = Value;
518 return ElementCount;
519 }
520
521
525 int32 AddUnique(const AttributeType& Value)
526 {
527 int32 FoundIndex = Find(Value);
528 if (FoundIndex == INDEX_NONE)
529 {
530 return Add(Value);
531 }
532 else
533 {
534 return FoundIndex;
535 }
536 }
537
538
543 {
544 Array->InsertIntoElement(Index, StartIndex, Count);
545 }
546
547
551 void Insert(const AttributeType& Value, int32 StartIndex)
552 {
553 TArrayView<AttributeType> Element = Array->InsertIntoElement(Index, StartIndex, 1);
554 Element[StartIndex] = Value;
555 }
556
557
561 void RemoveAt(int32 StartIndex, int32 Count)
562 {
563 Array->RemoveFromElement(Index, StartIndex, Count);
564 }
565
566
570 template <typename Predicate>
572 {
573 TArrayView<AttributeType> Element = Array->Get(Index);
574 int32 Count = Element.Num();
575
576 int32 WriteIndex = 0;
577 for (int32 ReadIndex = 0; ReadIndex < Count; ReadIndex++)
578 {
579 if (!Pred(Element[ReadIndex]))
580 {
581 Element[WriteIndex] = Element[ReadIndex];
582 WriteIndex++;
583 }
584 }
585
586 const bool bSetCount = true;
587 const bool bSetDefault = true;
588 Array->SetElementSize(Index, WriteIndex, bSetCount, bSetDefault);
589
590 return Count - WriteIndex;
591 }
592
593
594 int32 Remove(const AttributeType& Value)
595 {
596 return RemoveAll([&Value](const AttributeType& ToCompare) { return Value == ToCompare; });
597 }
598
599
603 int32 Find(const AttributeType& Value) const
604 {
605 return Array->Get(Index).Find(Value);
606 }
607
608
617 template <typename Predicate>
618 AttributeType* FindByPredicate(Predicate Pred) const
619 {
620 return Array->Get(Index).FindByPredicate(MoveTemp(Pred));
621 }
622
623
632 template <typename Predicate>
634 {
635 return Array->Get(Index).IndexOfByPredicate(MoveTemp(Pred));
636 }
637
638
642 bool Contains(const AttributeType& Value) const
643 {
644 return Array->Get(Index).Contains(Value);
645 }
646
647
655 template <typename Predicate>
656 bool ContainsByPredicate(Predicate Pred) const
657 {
658 return Array->Get(Index).ContainsByPredicate(MoveTemp(Pred));
659 }
660
661
665 void Sort()
666 {
667 Array->Get(Index).Sort();
668 }
669
673 template<typename Predicate>
674 void SortByPredicate(Predicate Pred)
675 {
676 Array->Get(Index).template Sort<Predicate>(Pred);
677 }
678
679
684 {
685 Array->Get(Index).StableSort();
686 }
687
688
692 template<typename Predicate>
694 {
695 Array->Get(Index).template StableSort<Predicate>(Pred);
696 }
697
702
706 operator TArrayView<AttributeType>() { return Array->Get(Index); }
707
708public:
713 inline AttributeType* begin() const { return GetData(); }
714 inline AttributeType* end () const { return GetData() + Num(); }
715
716private:
717 ArrayType* Array;
718 int32 Index;
719};
720
721
#define checkSlow(expr)
Definition AssertionMacros.h:332
#define check(expr)
Definition AssertionMacros.h:314
typename TCopyQualifiersFromTo< From, To >::Type TCopyQualifiersFromTo_T
Definition CopyQualifiersFromTo.h:17
@ INDEX_NONE
Definition CoreMiscDefines.h:150
@ InPlace
Definition CoreMiscDefines.h:162
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
uint32 Size
Definition VulkanMemory.cpp:4034
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition Archive.h:1208
Definition AttributeArrayContainer.h:393
bool ContainsByPredicate(Predicate Pred) const
Definition AttributeArrayContainer.h:656
void InsertDefaulted(int32 StartIndex, int32 Count)
Definition AttributeArrayContainer.h:542
void SetNumUninitialized(int32 Num)
Definition AttributeArrayContainer.h:491
int32 Add(const AttributeType &Value)
Definition AttributeArrayContainer.h:513
void Reserve(int32 Num)
Definition AttributeArrayContainer.h:502
void Insert(const AttributeType &Value, int32 StartIndex)
Definition AttributeArrayContainer.h:551
void StableSortByPredicate(Predicate Pred)
Definition AttributeArrayContainer.h:693
int32 RemoveAll(Predicate Pred)
Definition AttributeArrayContainer.h:571
bool IsEmpty() const
Definition AttributeArrayContainer.h:437
bool IsValidIndex(int32 ArrayAttributeIndex) const
Definition AttributeArrayContainer.h:427
AttributeType * end() const
Definition AttributeArrayContainer.h:714
AttributeType & operator[](int32 ArrayAttributeIndex) const
Definition AttributeArrayContainer.h:457
TArrayView< AttributeType > ToArrayView()
Definition AttributeArrayContainer.h:701
int32 Remove(const AttributeType &Value)
Definition AttributeArrayContainer.h:594
int32 AddUnique(const AttributeType &Value)
Definition AttributeArrayContainer.h:525
AttributeType * FindByPredicate(Predicate Pred) const
Definition AttributeArrayContainer.h:618
AttributeType * GetData() const
Definition AttributeArrayContainer.h:415
AttributeType * begin() const
Definition AttributeArrayContainer.h:713
void RemoveAt(int32 StartIndex, int32 Count)
Definition AttributeArrayContainer.h:561
TArrayAttribute(TArrayAttribute< std::remove_cv_t< T > > InValue)
Definition AttributeArrayContainer.h:407
void Sort()
Definition AttributeArrayContainer.h:665
TArrayAttribute(ArrayType &InArray, int32 InIndex)
Definition AttributeArrayContainer.h:398
void StableSort()
Definition AttributeArrayContainer.h:683
void SortByPredicate(Predicate Pred)
Definition AttributeArrayContainer.h:674
AttributeType & Last(int32 IndexFromTheEnd=0) const
Definition AttributeArrayContainer.h:470
bool Contains(const AttributeType &Value) const
Definition AttributeArrayContainer.h:642
int32 IndexOfByPredicate(Predicate Pred) const
Definition AttributeArrayContainer.h:633
void SetNum(int32 Num)
Definition AttributeArrayContainer.h:478
int32 Find(const AttributeType &Value) const
Definition AttributeArrayContainer.h:603
int32 Num() const
Definition AttributeArrayContainer.h:447
Definition ArrayView.h:139
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 SetNum(SizeType NewNum, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2308
void Empty(SizeType Slack=0)
Definition Array.h:2273
Definition AttributeArrayContainer.h:12
TAttributeArrayContainer()=default
uint32 GetHash(uint32 Crc=0) const
Definition AttributeArrayContainer.h:98
TAttributeArrayContainer(const AttributeType &InDefaultValue)
Definition AttributeArrayContainer.h:16
int32 GetElementCount(int32 Index) const
Definition AttributeArrayContainer.h:162
AttributeType GetDefault() const
Definition AttributeArrayContainer.h:299
int32 Num() const
Definition AttributeArrayContainer.h:21
TArrayView< AttributeType > Get(int32 Index)
Definition AttributeArrayContainer.h:142
friend FArchive & operator<<(FArchive &Ar, TAttributeArrayContainer< AttributeType > &Array)
Definition AttributeArrayContainer.h:133
void SetNum(const int32 ElementCount, const AttributeType &Default)
Definition AttributeArrayContainer.h:42
TArrayView< const AttributeType > Get(int32 Index) const
Definition AttributeArrayContainer.h:152
TArrayView< AttributeType > InsertIntoElement(int32 Index, int32 SubArrayIndex, int32 InsertCount=1)
Definition AttributeArrayContainer.h:252
TArrayView< AttributeType > SetElementSize(int32 Index, int32 Size, bool bSetCount, bool bSetDefault=true)
Definition AttributeArrayContainer.h:195
TArrayView< AttributeType > RemoveFromElement(int32 Index, int32 SubArrayIndex, int32 Count=1)
Definition AttributeArrayContainer.h:286
void Initialize(const int32 ElementCount, const AttributeType &Default)
Definition AttributeArrayContainer.h:24
int32 GetReservedElementCount(int32 Index) const
Definition AttributeArrayContainer.h:172
void Set(int32 Index, TArrayView< const AttributeType > Value)
Definition AttributeArrayContainer.h:182
void Remap(const TSparseArray< int32 > &IndexRemap, const AttributeType &Default)
Definition AttributeArrayContainer.h:371
void Insert(const int32 Index, const AttributeType &Default)
Definition AttributeArrayContainer.h:108
void SetToDefault(const int32 Index, const AttributeType &Default)
Definition AttributeArrayContainer.h:120
Definition SparseArray.h:1137
Definition SparseArray.h:524
void Insert(int32 Index, typename TTypeTraits< ElementType >::ConstInitType Element)
Definition SparseArray.h:644
Definition StaticArray.h:26
UE_REWRITE int32 Rotate(RangeType &&Range, int32 Count)
Definition Rotate.h:59
@ Count
Definition AudioMixerDevice.h:90
GeometryCollection::Facades::FMuscleActivationData Data
Definition MuscleActivationConstraints.h:15
U16 Index
Definition radfft.cpp:71
static UE_FORCEINLINE_HINT uint32 MemCrc32(const void *Data, int32 Length, uint32 CRC=0)
Definition Crc.h:31
static UE_FORCEINLINE_HINT void * Memmove(void *Dest, const void *Src, SIZE_T Count)
Definition UnrealMemory.h:109