UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
PropertyCombinationSet.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "Algo/IsSorted.h"
8#include "HAL/UnrealMemory.h"
10
31template<int BitWidth>
33{
34public:
35 static constexpr uint32 StorageBitCount = 1 << BitWidth;
37 static constexpr uint32 MaxValue = StorageBitCount - 1;
38
39public:
41 {
42 Construct();
43 FMemory::Memset(Storage, 0);
44 AddNoCheck(0); // Empty PropertyCombinationSets include 0
45 }
46
48 {
49 Construct();
50 FMemory::Memcpy(Storage, Other.Storage, sizeof(Storage));
51 }
52
54 {
55 Construct();
56 Load(ArchivedBits, BitOffset);
57 }
58
59 void Load(const TBitArray<>& ArchiveBits, uint32 BitOffset)
60 {
61 ArchiveBits.GetRange(BitOffset, StorageBitCount, Storage);
62 }
63
64 void Save(TBitArray<>& ArchiveBits, uint32 BitOffset) const
65 {
66 ArchiveBits.SetRangeFromRange(BitOffset, StorageBitCount, Storage);
67 }
68
69 void Load(const uint32* ArchiveBits)
70 {
71 FMemory::Memcpy(Storage, ArchiveBits, sizeof(Storage));
72 }
73
74 void Save(uint32* ArchiveBits) const
75 {
76 FMemory::Memcpy(ArchiveBits, Storage, sizeof(Storage));
77 }
78
83 {
85 if (IsRedundantPropertyCombinationNoCheck(PropertyCombination))
86 {
87 return;
88 }
89 AddNoCheck(PropertyCombination);
90 RemoveRedundantPropertyCombinationsNoCheck(PropertyCombination);
91 }
92
94 {
95 for (uint32 Value : Other)
96 {
97 Add(Value);
98 }
99 }
100
102 {
103 check(Value <= MaxValue);
104 return ContainsNoCheck(Value);
105 }
106
108 {
109 // Note that all bits after our ending bit are memset to 0, so it's okay to compare words
110 for (int n = 0; n < StorageWordCount; ++n)
111 {
112 if (Storage[n] != Other.Storage[n])
113 {
114 return false;
115 }
116 }
117 return true;
118 }
119
121 {
122 public:
124 {
125 ++Value;
127 while (Value < Max && !Array.ContainsNoCheck(Value))
128 {
129 ++Value;
130 }
131 return *this;
132 }
133 uint32 operator*() { return Value; }
134 bool operator!=(const FIterator& Other) const { return Value != Other.Value; }
135 private:
137
139 : Array(InArray)
141 {
142 ++(*this);
143 }
144
147 };
148
150 {
151 return FIterator(*this);
152 }
154 {
155 return FIterator(*this, MaxValue); // Note that MaxValue is a valid value, and the FIterator constructor adds one to it to get the first invalid value
156 }
157
158private:
159 void Construct()
160 {
161 // TPropertyCombinationSet has exponential storage requirements with the number of bits, and having n >= 32 is not feasible.
162 // We take advantage of this to assume that a bit combination can fit in uint32 and that 1 << BitWidth fits in a uint32
163 static_assert(BitWidth < sizeof(uint32) * 8, "TPropertyCombinationSet cannot be used with BitWidths >= 32.");
164 }
165
166 void AddNoCheck(uint32 Value)
167 {
168 Storage[Value / NumBitsPerDWORD] |= (1 << (Value & (NumBitsPerDWORD - 1)));
169 }
170
171 void RemoveNoCheck(uint32 Value)
172 {
173 Storage[Value / NumBitsPerDWORD] &= ~((1 << (Value & (NumBitsPerDWORD - 1))));
174 }
175
176 bool ContainsNoCheck(uint32 Value) const
177 {
178 return (Storage[Value / NumBitsPerDWORD] & (1 << (Value & (NumBitsPerDWORD - 1)))) != 0;
179 }
180
181 bool IsRedundantPropertyCombinationNoCheck(uint32 PropertyCombination) const
182 {
183 return IsRedundantPropertyCombinationRecursive(PropertyCombination, 0x1);
184 }
185
186 bool IsRedundantPropertyCombinationRecursive(uint32 PropertyCombination, uint32 StartBit) const
187 {
188 if (ContainsNoCheck(PropertyCombination))
189 {
190 return true;
191 }
192
193 for (uint32 Bit = StartBit; Bit < StorageBitCount; Bit <<= 1)
194 {
195 if (!(PropertyCombination & Bit))
196 {
197 if (IsRedundantPropertyCombinationRecursive(PropertyCombination | Bit, StartBit << 1))
198 {
199 return true;
200 }
201 }
202 }
203 return false;
204 }
205
206 void RemoveRedundantPropertyCombinationsNoCheck(uint32 PropertyCombination)
207 {
208 RemoveRedundantPropertyCombinationsRecursive(PropertyCombination, 0x1);
209 }
210
211 void RemoveRedundantPropertyCombinationsRecursive(uint32 PropertyCombination, uint32 StartBit)
212 {
213 for (uint32 Bit = StartBit; Bit < StorageBitCount; Bit <<= 1)
214 {
215 if (PropertyCombination & Bit)
216 {
218 RemoveNoCheck(CombinationToRemove);
219 RemoveRedundantPropertyCombinationsRecursive(CombinationToRemove, Bit << 1);
220 }
221 }
222 }
223
224private:
225 uint32 Storage[StorageWordCount];
226};
227
233template<>
235{
236public:
237 static constexpr uint32 BitWidth = 1;
238 static constexpr uint32 StorageBitCount = 1;
239 static constexpr uint32 StorageWordCount = 1;
240 static constexpr uint32 MaxValue = 1;
241 static constexpr uint32 NumPackedValues = 2;
242
243public:
245 {
246 Storage = 0;
247 }
248
250 {
251 Storage = Other.Storage;
252 }
253
255 {
256 Load(ArchivedBits, BitOffset);
257 }
258
259 void Load(const TBitArray<>& ArchiveBits, uint32 BitOffset)
260 {
261 ArchiveBits.GetRange(BitOffset, StorageBitCount, &Storage);
262 }
263
264 void Save(TBitArray<>& ArchiveBits, uint32 BitOffset) const
265 {
266 ArchiveBits.SetRangeFromRange(BitOffset, StorageBitCount, &Storage);
267 }
268
270 {
271 Storage = ArchiveBits[0];
272 }
273
275 {
276 ArchiveBits[0] = Storage;
277 }
278
280 {
282 Storage = Storage | (PropertyCombination != 0);
283 }
284
286 {
287 Storage = Storage | Other.Storage;
288 }
289
291 {
292 check(Value <= MaxValue);
293 return Value == Storage;
294 }
295
297 {
298 return Storage == Other.Storage;
299 }
300
301 struct FIterator
302 {
303 public:
304 FIterator& operator++()
305 {
306 ++Index;
307 return *this;
308 }
309 uint32 operator*() { return Array.Storage; }
310 bool operator!=(const FIterator& Other) const { return Index != Other.Index; }
311 private:
313
315 : Array(InArray)
316 , Index(InIndex)
317 {
318 }
319
321 int32 Index;
322 };
323
324 FIterator begin()
325 {
326 return FIterator(*this, 0);
327 }
328 FIterator end()
329 {
330 return FIterator(*this, 1);
331 }
332
333private:
334 friend class FPropertyCombinationSetTest;
335
336 uint32 Storage;
337};
338
339
340template<typename PackerClass>
342{
343public:
344 static constexpr uint32 BitWidth = PackerClass::BitWidth;
345 static constexpr uint32 StorageBitCount = PackerClass::StorageBitCount;
346 static constexpr uint32 StorageWordCount = 1;
347 static constexpr uint32 MaxValue = (1 << BitWidth) - 1;
348 static constexpr uint32 ArrayMax = PackerClass::ArrayMax; // This value is analytically computable - n choose floor(n/2) - but we manually hardcode it rather than calculating it in the compiler
349 static constexpr uint32 NumPackedValues = PackerClass::NumPackedValues;
350
352 {
353 Storage = 0;
354 }
355
360
362 {
363 Storage = 0;
364 Load(ArchivedBits, BitOffset);
365 }
366
367 void Load(const TBitArray<>& ArchiveBits, uint32 BitOffset)
368 {
369 ArchiveBits.GetRange(BitOffset, StorageBitCount, &Storage);
370 }
371
372 void Save(TBitArray<>& ArchiveBits, uint32 BitOffset) const
373 {
374 ArchiveBits.SetRangeFromRange(BitOffset, StorageBitCount, &Storage);
375 }
376
378 {
379 Storage = ArchiveBits[0];
380 }
381
383 {
384 ArchiveBits[0] = Storage;
385 }
386
388 {
390 const uint32* OldValues;
391 int OldNum;
392 PackerClass::Unpack(Storage, OldValues, OldNum);
393
395 int NewNum;
396 if (AddNonRedundant(OldValues, OldNum, PropertyCombination, NewValues, NewNum))
397 {
398 Storage = PackerClass::Pack(NewValues, NewNum);
399 }
400 }
401
403 {
404 const uint32* ExistingValues;
405 int ExistingNum;
406 PackerClass::Unpack(Storage, ExistingValues, ExistingNum);
407
408 const uint32* AddingValues;
409 int AddingNum;
410 PackerClass::Unpack(Other.Storage, AddingValues, AddingNum);
411
415 int OldNum = ExistingNum;
417 int NewNum;
419 {
420 if (AddNonRedundant(OldValues, OldNum, AddingValue, NewValues, NewNum))
421 {
423 OldNum = NewNum;
425 }
426 }
427 Storage = PackerClass::Pack(OldValues, OldNum);
428 }
429
431 {
432 check(Value <= MaxValue);
433 const uint32* Values;
434 int Num;
435 PackerClass::Unpack(Storage, Values, Num);
437 {
438 if (Existing == Value)
439 {
440 return true;
441 }
442 }
443 return false;
444 }
445
447 {
448 return Storage == Other.Storage;
449 }
450
452 {
453 public:
455 {
456 ++Index;
457 return *this;
458 }
460 {
461 return Values[Index];
462 }
463 bool operator!=(const FIterator& Other) const { return Index != Other.Index; }
464 private:
466
467 FIterator() = default;
468
469 const uint32* Values;
470 int Num;
471 int32 Index;
472 };
473
475 {
476 FIterator It;
477 PackerClass::Unpack(Storage, It.Values, It.Num);
478 It.Index = 0;
479 return It;
480 }
482 {
483 FIterator It;
484 PackerClass::Unpack(Storage, It.Values, It.Num);
485 It.Index = It.Num;
486 return It;
487 }
488
489private:
491
492 static bool AddNonRedundant(const uint32* OldValues, const int OldNum, const uint32 AddingValue, uint32* NewValues, int& NewNum)
493 {
494 bool bAdded = false;
495 NewNum = 0;
496 for (const uint32 OldValue : TArrayView<const uint32>(OldValues, OldNum))
497 {
498 const uint32 Overlap = OldValue & AddingValue;
499 if (Overlap == AddingValue)
500 {
501 // Adding value is redundant
502 return false;
503 }
504 if (OldValue > AddingValue && !bAdded)
505 {
508 bAdded = true;
509 }
510 if (Overlap != OldValue)
511 {
513 NewValues[NewNum++] = OldValue;
514 }
515 }
516 if (!bAdded)
517 {
520 }
521 check(1 <= NewNum && NewNum <= ArrayMax);
523 return true;
524 }
525
526 uint32 Storage;
527};
528
529
535{
536public:
537 static constexpr uint32 BitWidth = 2;
538 static constexpr uint32 StorageBitCount = 3;
539 static constexpr uint32 ArrayMax = 2;
540 static constexpr uint32 MaxValue = (1 << BitWidth) - 1;
541 static constexpr uint32 NumPackedValues = 5;
542
543 static void Unpack(const uint32 Compressed, const uint32*& OutValues, int& OutNum)
544 {
545 if (Compressed <= MaxValue)
546 {
547 static uint32 Values[] = { 0,1,2,3 };
548 OutValues = &Values[Compressed];
549 OutNum = 1;
550 }
551 else
552 {
553 check(Compressed == 4);
554 static uint32 Values[] = { 1,2 };
555 OutValues = Values;
556 OutNum = 2;
557 }
558 }
559
560 static uint32 Pack(const uint32* Values, const int Num)
561 {
562 if (Num == 1)
563 {
564 check(Values[0] <= MaxValue);
565 return Values[0];
566 }
567 else
568 {
569 check(Num == 2);
570 check(Values[0] == 1 && Values[1] == 2);
571 return 4;
572 }
573 }
574};
575template<>
581
582
604{
605public:
606 static constexpr uint32 BitWidth = 3;
607 static constexpr uint32 StorageBitCount = 5;
608 static constexpr uint32 ArrayMax = 3;
609 static constexpr uint32 MaxValue = (1 << BitWidth) - 1;
610 static constexpr uint32 NumPackedValues = 19;
611
612 static void Unpack(const uint32 Compressed, const uint32*& OutValues, int& OutNum)
613 {
614 /*0-7: [000] ->[111]
615 * 8 : [001, 010]
616 * 9 : [001, 100]
617 * 10 : [001, 110]
618 * 11 : [010, 100]
619 * 12 : [010, 101]
620 * 13 : [011, 100]
621 * 14 : [011, 101]
622 * 15 : [011, 110]
623 * 16 : [101, 110]
624 * 17 : [001, 010, 100]
625 * 18 : [011, 101, 110] */
626 if (Compressed < 8)
627 {
628 static uint32 Values[] = { 0,1,2,3,4,5,6,7 };
629 OutValues = &Values[Compressed];
630 OutNum = 1;
631 }
632 else
633 {
634 switch (Compressed)
635 {
636 case 8:
637 {
638 static uint32 Values[] = { 1,2 };
639 OutValues = Values;
640 OutNum = 2;
641 break;
642 }
643 case 9:
644 {
645 static uint32 Values[] = { 1,4 };
646 OutValues = Values;
647 OutNum = 2;
648 break;
649 }
650 case 10:
651 {
652 static uint32 Values[] = { 1,6 };
653 OutValues = Values;
654 OutNum = 2;
655 break;
656 }
657 case 11:
658 {
659 static uint32 Values[] = { 2,4 };
660 OutValues = Values;
661 OutNum = 2;
662 break;
663 }
664 case 12:
665 {
666 static uint32 Values[] = { 2,5 };
667 OutValues = Values;
668 OutNum = 2;
669 break;
670 }
671 case 13:
672 {
673 static uint32 Values[] = { 3,4 };
674 OutValues = Values;
675 OutNum = 2;
676 break;
677 }
678 case 14:
679 {
680 static uint32 Values[] = { 3,5 };
681 OutValues = Values;
682 OutNum = 2;
683 break;
684 }
685 case 15:
686 {
687 static uint32 Values[] = { 3,6 };
688 OutValues = Values;
689 OutNum = 2;
690 break;
691 }
692 case 16:
693 {
694 static uint32 Values[] = { 5,6 };
695 OutValues = Values;
696 OutNum = 2;
697 break;
698 }
699 case 17:
700 {
701 static uint32 Values[] = { 1,2,4 };
702 OutValues = Values;
703 OutNum = 3;
704 break;
705 }
706 case 18:
707 {
708 static uint32 Values[] = { 3,5,6 };
709 OutValues = Values;
710 OutNum = 3;
711 break;
712 }
713 default:
714 check(false);
715 break;
716 }
717 }
718 }
719
720 static uint32 Pack(const uint32* Values, const int Num)
721 {
722 if (Num == 1)
723 {
724 check(Values[0] <= MaxValue);
725 return Values[0];
726 }
727 else if (Num == 2)
728 {
729 switch (Values[0])
730 {
731 case 1:
732 check(Values[1] == 2 || Values[1] == 4 || Values[1] == 6);
733 return 8 + (Values[1] - 2) / 2;
734 case 2:
735 check(Values[1] == 4 || Values[1] == 5);
736 return 11 + Values[1] - 4;
737 case 3:
738 check(Values[1] == 4 || Values[1] == 5 || Values[1] == 6);
739 return 13 + Values[1] - 4;
740 default:
741 check(Values[0] == 5 && Values[1] == 6);
742 return 16;
743 }
744 }
745 else
746 {
747 check(Num == 3);
748 if (Values[0] == 1)
749 {
750 check(Values[1] == 2 && Values[2] == 4);
751 return 17;
752 }
753 else
754 {
755 check(Values[0] == 3 && Values[1] == 5 && Values[2] == 6);
756 return 18;
757 }
758 }
759 }
760};
761
762template<>
#define check(expr)
Definition AssertionMacros.h:314
#define NumBitsPerDWORD
Definition ContainerAllocationPolicies.h:1371
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
@ Num
Definition MetalRHIPrivate.h:234
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition PropertyCombinationSet.h:535
static constexpr uint32 NumPackedValues
Definition PropertyCombinationSet.h:541
static void Unpack(const uint32 Compressed, const uint32 *&OutValues, int &OutNum)
Definition PropertyCombinationSet.h:543
static constexpr uint32 MaxValue
Definition PropertyCombinationSet.h:540
static constexpr uint32 BitWidth
Definition PropertyCombinationSet.h:537
static constexpr uint32 ArrayMax
Definition PropertyCombinationSet.h:539
static uint32 Pack(const uint32 *Values, const int Num)
Definition PropertyCombinationSet.h:560
static constexpr uint32 StorageBitCount
Definition PropertyCombinationSet.h:538
Definition PropertyCombinationSet.h:604
static constexpr uint32 MaxValue
Definition PropertyCombinationSet.h:609
static constexpr uint32 StorageBitCount
Definition PropertyCombinationSet.h:607
static constexpr uint32 ArrayMax
Definition PropertyCombinationSet.h:608
static void Unpack(const uint32 Compressed, const uint32 *&OutValues, int &OutNum)
Definition PropertyCombinationSet.h:612
static constexpr uint32 BitWidth
Definition PropertyCombinationSet.h:606
static constexpr uint32 NumPackedValues
Definition PropertyCombinationSet.h:610
static uint32 Pack(const uint32 *Values, const int Num)
Definition PropertyCombinationSet.h:720
Definition ArrayView.h:139
Definition PropertyCombinationSet.h:342
void Load(const TBitArray<> &ArchiveBits, uint32 BitOffset)
Definition PropertyCombinationSet.h:367
FIterator end()
Definition PropertyCombinationSet.h:481
void Add(uint32 PropertyCombination)
Definition PropertyCombinationSet.h:387
FIterator begin()
Definition PropertyCombinationSet.h:474
static constexpr uint32 MaxValue
Definition PropertyCombinationSet.h:347
TPropertyCombinationSetHardcoded(const TPropertyCombinationSetHardcoded< PackerClass > &Other)
Definition PropertyCombinationSet.h:356
TPropertyCombinationSetHardcoded(const TBitArray<> &ArchivedBits, uint32 BitOffset=0)
Definition PropertyCombinationSet.h:361
bool operator==(const TPropertyCombinationSetHardcoded< PackerClass > &Other) const
Definition PropertyCombinationSet.h:446
bool Contains(uint32 Value) const
Definition PropertyCombinationSet.h:430
friend class FPropertyCombinationSetTest
Definition PropertyCombinationSet.h:490
void Save(uint32 *ArchiveBits) const
Definition PropertyCombinationSet.h:382
static constexpr uint32 ArrayMax
Definition PropertyCombinationSet.h:348
static constexpr uint32 NumPackedValues
Definition PropertyCombinationSet.h:349
void Save(TBitArray<> &ArchiveBits, uint32 BitOffset) const
Definition PropertyCombinationSet.h:372
void Load(const uint32 *ArchiveBits)
Definition PropertyCombinationSet.h:377
static constexpr uint32 StorageWordCount
Definition PropertyCombinationSet.h:346
static constexpr uint32 StorageBitCount
Definition PropertyCombinationSet.h:345
void AddRange(TPropertyCombinationSetHardcoded< PackerClass > &Other)
Definition PropertyCombinationSet.h:402
static constexpr uint32 BitWidth
Definition PropertyCombinationSet.h:344
TPropertyCombinationSetHardcoded()
Definition PropertyCombinationSet.h:351
bool operator==(const TPropertyCombinationSet< 1 > &Other) const
Definition PropertyCombinationSet.h:296
FIterator end()
Definition PropertyCombinationSet.h:328
void Save(TBitArray<> &ArchiveBits, uint32 BitOffset) const
Definition PropertyCombinationSet.h:264
FIterator begin()
Definition PropertyCombinationSet.h:324
void Add(uint32 PropertyCombination)
Definition PropertyCombinationSet.h:279
TPropertyCombinationSet(const TBitArray<> &ArchivedBits, uint32 BitOffset=0)
Definition PropertyCombinationSet.h:254
bool Contains(uint32 Value) const
Definition PropertyCombinationSet.h:290
void Load(const TBitArray<> &ArchiveBits, uint32 BitOffset)
Definition PropertyCombinationSet.h:259
TPropertyCombinationSet()
Definition PropertyCombinationSet.h:244
void Save(uint32 *ArchiveBits) const
Definition PropertyCombinationSet.h:274
TPropertyCombinationSet(const TPropertyCombinationSet< 1 > &Other)
Definition PropertyCombinationSet.h:249
void Load(const uint32 *ArchiveBits)
Definition PropertyCombinationSet.h:269
void AddRange(TPropertyCombinationSet< 1 > &Other)
Definition PropertyCombinationSet.h:285
Definition PropertyCombinationSet.h:33
void Load(const uint32 *ArchiveBits)
Definition PropertyCombinationSet.h:69
void AddRange(TPropertyCombinationSet< BitWidth > &Other)
Definition PropertyCombinationSet.h:93
static constexpr uint32 StorageWordCount
Definition PropertyCombinationSet.h:36
void Save(TBitArray<> &ArchiveBits, uint32 BitOffset) const
Definition PropertyCombinationSet.h:64
static constexpr uint32 StorageBitCount
Definition PropertyCombinationSet.h:35
TPropertyCombinationSet(const TPropertyCombinationSet< BitWidth > &Other)
Definition PropertyCombinationSet.h:47
FIterator begin()
Definition PropertyCombinationSet.h:149
bool Contains(uint32 Value) const
Definition PropertyCombinationSet.h:101
TPropertyCombinationSet(const TBitArray<> &ArchivedBits, uint32 BitOffset=0)
Definition PropertyCombinationSet.h:53
TPropertyCombinationSet()
Definition PropertyCombinationSet.h:40
void Add(uint32 PropertyCombination)
Definition PropertyCombinationSet.h:82
FIterator end()
Definition PropertyCombinationSet.h:153
void Load(const TBitArray<> &ArchiveBits, uint32 BitOffset)
Definition PropertyCombinationSet.h:59
static constexpr uint32 MaxValue
Definition PropertyCombinationSet.h:37
bool operator==(const TPropertyCombinationSet< BitWidth > &Other) const
Definition PropertyCombinationSet.h:107
void Save(uint32 *ArchiveBits) const
Definition PropertyCombinationSet.h:74
UE_REWRITE bool IsSorted(const RangeType &Range)
Definition IsSorted.h:66
U16 Index
Definition radfft.cpp:71
static UE_FORCEINLINE_HINT void * Memcpy(void *Dest, const void *Src, SIZE_T Count)
Definition UnrealMemory.h:160
static UE_FORCEINLINE_HINT void * Memset(void *Dest, uint8 Char, SIZE_T Count)
Definition UnrealMemory.h:119
Definition PropertyCombinationSet.h:452
FIterator & operator++()
Definition PropertyCombinationSet.h:454
uint32 operator*()
Definition PropertyCombinationSet.h:459
bool operator!=(const FIterator &Other) const
Definition PropertyCombinationSet.h:463
Definition PropertyCombinationSet.h:121
FIterator & operator++()
Definition PropertyCombinationSet.h:123
bool operator!=(const FIterator &Other) const
Definition PropertyCombinationSet.h:134
uint32 operator*()
Definition PropertyCombinationSet.h:133
bool operator!=(const FIterator &Other) const
Definition PropertyCombinationSet.h:310
uint32 operator*()
Definition PropertyCombinationSet.h:309
FIterator & operator++()
Definition PropertyCombinationSet.h:304