Apparatus Version 1.0.0
ECS-like data-driven workflow for Unreal Engine.
BitMask.h
1 /*
2  * ░▒▓ APPARATUS ▓▒░
3  *
4  * File: BitMask.h
5  * Created: Friday, 23rd October 2020 7:00:48 pm
6  * Author: Vladislav Dmitrievich Turbanov (vladislav@turbanov.ru)
7  * ───────────────────────────────────────────────────────────────────
8  * Last Modified: Friday, 26th March 2021 3:32:07 pm
9  * Modified By: Vladislav Dmitrievich Turbanov (vladislav@turbanov.ru)
10  * ───────────────────────────────────────────────────────────────────
11  *
12  * The Apparatus source code is for your internal usage only.
13  * Redistribution of this file is strictly prohibited.
14  *
15  * Community forums: https://talk.turbanov.ru
16  *
17  * Copyright 2020 - 2021, SP Vladislav Dmitrievich Turbanov
18  * Made in Russia, Moscow City, Chekhov City
19  */
20 
21 #pragma once
22 
23 #include <algorithm>
24 #include <limits>
25 #include <ostream>
26 #include <typeinfo>
27 #include <vector>
28 
29 #include "CoreMinimal.h"
30 
31 #include "BitMask.generated.h"
32 
36 USTRUCT(BlueprintType)
37 struct APPARATUSRUNTIME_API FBitMask
38 {
39  GENERATED_BODY()
40 
41  private:
45  typedef uint64 GroupType;
46 
50  static const int32 GroupSizeBits = sizeof(GroupType) * 8;
51 
55  static const int32 GroupShift = 6;
56 
60  static const int32 GroupBitIndexMask = GroupSizeBits - 1;
61 
65  static const FBitMask Zero;
66 
70  UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Bit Mask",
71  Meta = (AllowPrivateAccess = "true"))
72  int32 Count = 0;
73 
77  UPROPERTY(VisibleAnywhere, Category = "Bit Mask",
78  Meta = (AllowPrivateAccess = "true"))
79  TArray<uint64> BitGroups;
80 
85  FORCEINLINE GroupType GetBitGroup(int32 index) const
86  {
87  checkf(index >= 0, TEXT("An index can't be negative: %d"), index);
88  if (index >= BitGroups.Num())
89  return 0;
90  return BitGroups[index];
91  }
92 
93  friend class StaticConstructor;
94 
95  struct StaticConstructor
96  {
97  FORCEINLINE StaticConstructor()
98  {
99  for (size_t i = 0; i < bitsCountLUTSize; ++i)
100  {
101  int32 c = 0;
102  for (int32 j = 0; j < 8; ++j)
103  {
104  if (((i >> j) & 1) != 0)
105  ++c;
106  }
107  bitsCountLUT[i] = c;
108  }
109  }
110  };
111 
112  static StaticConstructor staticConstructor;
113 
117  static const int32 bitsCountLUTSize = 256;
118 
122  static int32 bitsCountLUT[bitsCountLUTSize];
123 
127  FORCEINLINE static int32 GetOneBitsCount(GroupType bg)
128  {
129  return bitsCountLUT[0xff & bg] + bitsCountLUT[0xff & (bg >> 8)] +
130  bitsCountLUT[0xff & (bg >> 16)] +
131  bitsCountLUT[0xff & (bg >> 24)] +
132  bitsCountLUT[0xff & (bg >> 32)] +
133  bitsCountLUT[0xff & (bg >> 40)] +
134  bitsCountLUT[0xff & (bg >> 48)] +
135  bitsCountLUT[0xff & (bg >> 56)];
136  }
137 
138  FORCEINLINE bool FastAt(const int32 Index) const {
139  checkf(Index >= 0, TEXT("An index must be non-negative: %d"),
140  Index);
141 
142  checkf(Index < Count,
143  TEXT("FastAt can work only with indexes less than Count: %d >= %d"), Index, Count);
144 
145  const GroupType bg = BitGroups[Index >> GroupShift];
146  return (bg & (((GroupType)1) << (Index & GroupBitIndexMask))) !=
147  (GroupType)0;
148  }
149 
150  friend uint32 GetTypeHash(const FBitMask &bm);
151 
152  public:
156  FORCEINLINE int32 Num() const { return Count; }
157 
161  FORCEINLINE int32 GetCount() const { return Count; }
162 
163  FORCEINLINE FBitMask() {}
164 
165  FORCEINLINE FBitMask(const int32 Capacity) { EnsureBitGroups(Capacity); }
166 
167  FORCEINLINE bool operator[](const int32 Index) const { return At(Index); }
168 
176  FORCEINLINE bool At(const int32 Index) const
177  {
178  checkf(Index >= 0, TEXT("An index must be non-negative: %d"),
179  Index);
180 
181  if (Index >= Count)
182  return false;
183 
184  return FastAt(Index);
185  }
186 
187  FORCEINLINE void Set(const int32 Index, const bool value)
188  {
189  checkf(Index >= 0, TEXT("An index must be non-negative: %d"),
190  Index);
191 
192  auto gi = Index >> GroupShift;
193 
194  if (value)
195  {
196  if (Index >= Count)
197  {
198  EnsureBitGroups(Index);
199  Count = Index + 1;
200  }
201  BitGroups[gi] |= (((GroupType)1) << (Index & GroupBitIndexMask));
202  }
203  else
204  {
205  if (gi < BitGroups.Num())
206  BitGroups[gi] &=
207  ~(((GroupType)1) << (Index & GroupBitIndexMask));
208  }
209  }
210 
214  FORCEINLINE void Set(const FBitMask &BitMask)
215  {
216  BitGroups = BitMask.BitGroups;
217  Count = BitMask.Count;
218  }
219 
220  FORCEINLINE FBitMask &operator=(const FBitMask &BitMask)
221  {
222  Set(BitMask);
223  return *this;
224  }
225 
229  int32 DifferencesCount(const FBitMask &BitMask) const;
230 
234  int32 InclusionsCount(const FBitMask &BitMask) const;
235 
241  FORCEINLINE bool Includes(const FBitMask &BitMask) const
242  {
243  for (int32 gi = 0; gi < BitMask.BitGroups.Num(); ++gi)
244  {
245  const GroupType bitGroup = GetBitGroup(gi);
246  const GroupType otherBitGroup = BitMask.BitGroups[gi];
247 
248  const GroupType masked = otherBitGroup & bitGroup;
249  if (masked != otherBitGroup)
250  return false;
251  }
252  return true;
253  }
254 
260  bool IncludesPartially(const FBitMask &BitMask) const
261  {
262  int32 Size = FMath::Min(BitGroups.Num(), BitMask.BitGroups.Num());
263  if (Size == 0) return false;
264  for (int32 gi = 0; gi < Size; ++gi)
265  {
266  const GroupType bitGroup = BitGroups[gi];
267  const GroupType otherBitGroup = BitMask.BitGroups[gi];
268 
269  const GroupType masked = otherBitGroup & bitGroup;
270  if (masked != (GroupType)0)
271  return true;
272  }
273  return false;
274  }
275 
279  bool Contains(const bool bit) const;
280 
284  struct Iterator
285  {
286  int32 index = -1;
287 
288  const FBitMask &owner;
289 
290  FORCEINLINE Iterator(const FBitMask &o, int32 i = 0) : owner(o)
291  {
292  this->index = i;
293  }
294 
295  public:
296  FORCEINLINE bool operator*() const { return owner.At(index); }
297 
298  FORCEINLINE bool next() { return (++index) < owner.Count; }
299 
300  FORCEINLINE bool operator==(const Iterator &i) const
301  {
302  return &owner == &i.owner && index == i.index;
303  }
304 
305  FORCEINLINE bool operator!=(const Iterator &i) const
306  {
307  return &owner != &i.owner || index != i.index;
308  }
309  };
310 
311  friend Iterator;
312 
313  FORCEINLINE Iterator begin() { return Iterator(*this); }
314 
315  FORCEINLINE Iterator end() { return Iterator(*this, Count); }
316 
320  int32 IndexOf(const bool bit) const;
321 
325  FORCEINLINE void EnsureBitGroupsCount(int32 Groups)
326  {
327  BitGroups.Reserve(Groups);
328 
329  while (BitGroups.Num() < Groups)
330  BitGroups.Add(0);
331  }
332 
336  FORCEINLINE void EnsureBitGroups(const int32 index)
337  {
338  auto gi = index >> GroupShift;
339  EnsureBitGroupsCount(gi + 1);
340  }
341 
342  friend std::ostream &operator<<(std::ostream &Str, const FBitMask &v);
343  friend bool operator==(const FBitMask &a, const FBitMask &b);
344  friend bool operator!=(const FBitMask &a, const FBitMask &b);
345 
350  FORCEINLINE int32 Max() const { return BitGroups.Max() << GroupShift; }
351 
355  FORCEINLINE void Reserve(const int32 Capacity)
356  {
357  BitGroups.Reserve(Capacity >> GroupShift);
358  }
359 
363  void Add(const bool Bit);
364 
371  FORCEINLINE void Empty(const int32 Slack = 0)
372  {
373  checkf(Slack >= 0, TEXT("A Slack must be non-negative: %d"), Slack);
374  if (Count == 0)
375  return;
376  BitGroups.Empty(Slack >> GroupShift);
377  Count = 0;
378  }
379 
386  FORCEINLINE void Reset(const int32 NewSize = 0)
387  {
388  checkf(NewSize >= 0, TEXT("A NewSize must be non-negative: %d"), NewSize);
389  if (Count == 0)
390  return;
391  BitGroups.Reset(NewSize >> GroupShift);
392  Count = 0;
393  }
394 
398  FORCEINLINE void CopyTo(bool *Array, int32 ArrayIndex) const
399  {
400  for (int32 i = 0; i < Count; ++i)
401  {
402  Array[ArrayIndex + i] = FastAt(i);
403  }
404  }
405 
409  void Insert(const int32 Index, const bool Bit);
410 
414  void Remove(const bool Bit);
415 
421  void RemoveAt(const int32 Index);
422 
428  FORCEINLINE void Erase(const int32 Index)
429  {
430  checkf(Index >= 0 && Index < Count,
431  TEXT("An index is out of range: %d"), Index);
432 
433  if (Index == --Count)
434  {
435  this->Set(Index, false);
436  }
437  else
438  {
439  for (auto s = Index; s < Count; ++s)
440  {
441  this->Set(s, this->At(s + 1));
442  }
443  }
444  }
445 
449  FORCEINLINE void CopyTo(TArray<bool> &array, const int32 index = 0)
450  {
451  for (int32 i = 0; i < Count; ++i)
452  {
453  array[index + i] = this->FastAt(i);
454  }
455  }
456 
460  FORCEINLINE FBitMask &AndWith(const FBitMask &mask)
461  {
462  int32 Size = FMath::Min(BitGroups.Num(), mask.BitGroups.Num());
463  EnsureBitGroupsCount(Size);
464 
465  for (int32 i = 0; i < Size; ++i)
466  {
467  BitGroups[i] &= mask.BitGroups[i];
468  }
469 
470  this->Count = FMath::Min(this->Count, mask.Count);
471 
472  return *this;
473  }
474 
478  FORCEINLINE FBitMask &OrWith(const FBitMask &mask)
479  {
480  int32 Size = FMath::Max(BitGroups.Num(), mask.BitGroups.Num());
481  EnsureBitGroupsCount(Size);
482 
483  for (int32 i = 0; i < mask.BitGroups.Num(); ++i)
484  {
485  BitGroups[i] |= mask.BitGroups[i];
486  }
487 
488  this->Count = FMath::Max(this->Count, mask.Count);
489 
490  return *this;
491  }
492 
496  FORCEINLINE FString ToString() const
497  {
498  FString str;
499  for (int32 i = 0; i < Count; ++i)
500  {
501  auto b = FastAt(i);
502  str += b ? TEXT("1") : TEXT("0");
503  }
504  return str;
505  }
506 
507  friend FBitMask operator&(const FBitMask &a, const FBitMask &b);
508  friend FBitMask operator|(const FBitMask &a, const FBitMask &b);
509 }; // struct FBitMask
510 
511 FORCEINLINE bool operator==(const FBitMask &a, const FBitMask &b)
512 {
513  if (std::addressof(a) == std::addressof(b))
514  return true;
515 
516  const auto count = FMath::Max(a.BitGroups.Num(), b.BitGroups.Num());
517  for (int32 gi = 0; gi < count; ++gi)
518  {
519  if (a.GetBitGroup(gi) != b.GetBitGroup(gi))
520  return false;
521  }
522  return true;
523 }
524 
525 FORCEINLINE bool operator!=(const FBitMask &a, const FBitMask &b)
526 {
527  if (std::addressof(a) == std::addressof(b))
528  return false;
529 
530  const auto count = FMath::Max(a.BitGroups.Num(), b.BitGroups.Num());
531  for (int32 gi = 0; gi < count; ++gi)
532  {
533  if (a.GetBitGroup(gi) != b.GetBitGroup(gi))
534  return true;
535  }
536  return false;
537 }
538 
539 FORCEINLINE FBitMask operator&(const FBitMask &a, const FBitMask &b)
540 {
541  auto count = FMath::Min(a.BitGroups.Num(), b.BitGroups.Num());
542  auto r = FBitMask();
543  r.EnsureBitGroupsCount(count);
544  for (int32 i = 0; i < count; ++i)
545  {
546  r.BitGroups[i] = a.BitGroups[i] & b.BitGroups[i];
547  }
548  r.Count = FMath::Min(a.Count, b.Count);
549  return r;
550 }
551 
552 FORCEINLINE FBitMask operator|(const FBitMask &a, const FBitMask &b)
553 {
554  auto count = FMath::Max(a.BitGroups.Num(), b.BitGroups.Num());
555  auto r = FBitMask();
556  r.EnsureBitGroupsCount(count);
557  for (int32 i = 0; i < count; ++i)
558  {
559  r.BitGroups[i] = a.GetBitGroup(i) | b.GetBitGroup(i);
560  }
561  r.Count = FMath::Max(a.Count, b.Count);
562  return r;
563 }
564 
565 FORCEINLINE uint32 GetTypeHash(const FBitMask &bm)
566 {
567  auto hashCode = 1799407316;
568  hashCode = hashCode * -1521134295 + bm.Count;
569  for (auto bg : bm.BitGroups)
570  {
571  hashCode = hashCode * -1521134295 + (int32)(bg >> 32);
572  hashCode = hashCode * -1521134295 + (int32)(bg & 0xFFFFFFFF);
573  }
574  return hashCode;
575 }
The bit mask iterator.
Definition: BitMask.h:285
Iterator(const FBitMask &o, int32 i=0)
Definition: BitMask.h:290
bool operator*() const
Definition: BitMask.h:296
bool operator==(const Iterator &i) const
Definition: BitMask.h:300
const FBitMask & owner
Definition: BitMask.h:288
int32 index
Definition: BitMask.h:286
bool next()
Definition: BitMask.h:298
bool operator!=(const Iterator &i) const
Definition: BitMask.h:305
A memory-efficient bit mask.
Definition: BitMask.h:38
void EnsureBitGroups(const int32 index)
Ensure that there is enough bit groups for an index.
Definition: BitMask.h:336
void CopyTo(bool *Array, int32 ArrayIndex) const
Copy the bits to an array, with a specified offset.
Definition: BitMask.h:398
FBitMask & operator=(const FBitMask &BitMask)
Definition: BitMask.h:220
Iterator begin()
Definition: BitMask.h:313
void Reset(const int32 NewSize=0)
Same as empty, but doesn't change memory allocations, unless the new size is larger than the current ...
Definition: BitMask.h:386
friend std::ostream & operator<<(std::ostream &Str, const FBitMask &v)
void Reserve(const int32 Capacity)
Reserve space for a given number of bits.
Definition: BitMask.h:355
int32 Max() const
Get the current maximum number of bits that can be stored without the new allocations.
Definition: BitMask.h:350
int32 Num() const
The current number of bits in the mask.
Definition: BitMask.h:156
FBitMask & AndWith(const FBitMask &mask)
And the bitmask with a given mask.
Definition: BitMask.h:460
void Set(const FBitMask &BitMask)
Set this bit mask equal to another bit mask.
Definition: BitMask.h:214
void Empty(const int32 Slack=0)
Empties the array.
Definition: BitMask.h:371
bool operator[](const int32 Index) const
Definition: BitMask.h:167
void Set(const int32 Index, const bool value)
Definition: BitMask.h:187
bool At(const int32 Index) const
Get the bit flag at the specified index.
Definition: BitMask.h:176
Iterator end()
Definition: BitMask.h:315
friend Iterator
Definition: BitMask.h:311
FBitMask(const int32 Capacity)
Definition: BitMask.h:165
FString ToString() const
Convert the bit mask to a string representation.
Definition: BitMask.h:496
bool IncludesPartially(const FBitMask &BitMask) const
Check if the mask has any of the bits set in the supplied mask.
Definition: BitMask.h:260
FBitMask & OrWith(const FBitMask &mask)
Or the bitmask with a given mask.
Definition: BitMask.h:478
bool Includes(const FBitMask &BitMask) const
Does the mask has all of the bits set in the supplied mask.
Definition: BitMask.h:241
void Erase(const int32 Index)
Remove an element at the specified index.
Definition: BitMask.h:428
FBitMask()
Definition: BitMask.h:163
void EnsureBitGroupsCount(int32 Groups)
Ensure a bit group count.
Definition: BitMask.h:325
void CopyTo(TArray< bool > &array, const int32 index=0)
Copy to an array with a supplied offset.
Definition: BitMask.h:449
int32 GetCount() const
Get the current number of bits in the mask.
Definition: BitMask.h:161