1#pragma once
2
3#include <initializer_list>
4#include <type_traits>
5
6#include "prim/seadBitFlag.h"
7
8namespace sead
9{
10template <typename Enum, typename Storage = std::underlying_type_t<Enum>>
11class TypedBitFlag
12{
13public:
14 static_assert(std::is_enum<Enum>(), "Enum must be an enum");
15 using UnderlyingType = std::underlying_type_t<Enum>;
16
17 TypedBitFlag() : mBits(0) {}
18 explicit TypedBitFlag(UnderlyingType bits) : mBits(bits) {}
19 TypedBitFlag(Enum bits) : mBits(UnderlyingType(bits)) {}
20 template <typename T>
21 explicit TypedBitFlag(const TypedBitFlag<Enum, T>& other)
22 {
23 *this = other;
24 }
25
26 template <typename T>
27 TypedBitFlag& operator=(const TypedBitFlag<Enum, T>& other)
28 {
29 mBits = other.getDirect();
30 return *this;
31 }
32
33 TypedBitFlag& operator=(Enum value)
34 {
35 setDirect(value);
36 return *this;
37 }
38
39 void makeAllZero() { mBits = 0; }
40 void makeAllOne() { mBits = ~UnderlyingType(0); }
41
42 void setDirect(UnderlyingType bits) { mBits = bits; }
43 void setDirect(Enum bits) { mBits = UnderlyingType(bits); }
44 UnderlyingType getDirect() const { return mBits; }
45 Storage& getStorage() { return mBits; }
46
47 bool set(Enum val)
48 {
49 const auto mask = UnderlyingType(val);
50 return ((mBits |= mask) & mask) == 0;
51 }
52
53 bool reset(Enum val)
54 {
55 const auto mask = UnderlyingType(val);
56 return ((mBits &= ~mask) & mask) != 0;
57 }
58
59 UnderlyingType toggle(Enum val) { mBits ^= UnderlyingType(val); }
60 bool change(Enum val, bool on) { return on ? set(val) : reset(val); }
61 bool isZero() const { return mBits == 0; }
62 /// Checks if (at least) one of the bits are set.
63 bool isOn(Enum val) const { return (mBits & UnderlyingType(val)) != 0; }
64 bool isAnyOn(std::initializer_list<Enum> list) const { return (mBits & orEnums_(list)) != 0; }
65 /// Checks if all of the bits are set.
66 bool isOnAll(Enum val) const { return (mBits & UnderlyingType(val)) == UnderlyingType(val); }
67 bool isOff(Enum val) const { return !isOn(val); }
68
69 bool testAndClear(Enum val)
70 {
71 if (!isOn(val))
72 return false;
73 reset(val);
74 return true;
75 }
76
77 /// Popcount.
78 int countOnBit() const
79 {
80 if constexpr (sizeof(UnderlyingType) <= 4)
81 return BitFlagUtil::countOnBit(x: mBits);
82 else
83 return BitFlagUtil::countOnBit64(x: mBits);
84 }
85 /// Count trailing zeroes.
86 int countContinuousOffBitFromRight() const
87 {
88 if constexpr (sizeof(UnderlyingType) <= 4)
89 return BitFlagUtil::countContinuousOffBitFromRight(x: mBits);
90 else
91 return BitFlagUtil::countContinuousOffBitFromRight64(x: mBits);
92 }
93 int countRightOnBit(int bit) const
94 {
95 if constexpr (sizeof(UnderlyingType) <= 4)
96 return BitFlagUtil::countRightOnBit(x: mBits, bit);
97 else
98 return BitFlagUtil::countRightOnBit64(x: mBits, bit);
99 }
100 int findOnBitFromRight(int num) const
101 {
102 if constexpr (sizeof(UnderlyingType) <= 4)
103 return BitFlagUtil::findOnBitFromRight(x: mBits, num);
104 else
105 return BitFlagUtil::findOnBitFromRight64(x: mBits, num);
106 }
107
108protected:
109 static constexpr UnderlyingType orEnums_(std::initializer_list<Enum> list)
110 {
111 UnderlyingType value{};
112 for (auto x : list)
113 value |= static_cast<UnderlyingType>(x);
114 return value;
115 }
116
117 Storage mBits;
118};
119} // namespace sead
120