xref: /aosp_15_r20/frameworks/native/include/ftl/flags.h (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
1*38e8c45fSAndroid Build Coastguard Worker /*
2*38e8c45fSAndroid Build Coastguard Worker  * Copyright 2020 The Android Open Source Project
3*38e8c45fSAndroid Build Coastguard Worker  *
4*38e8c45fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*38e8c45fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*38e8c45fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*38e8c45fSAndroid Build Coastguard Worker  *
8*38e8c45fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*38e8c45fSAndroid Build Coastguard Worker  *
10*38e8c45fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*38e8c45fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*38e8c45fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*38e8c45fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*38e8c45fSAndroid Build Coastguard Worker  * limitations under the License.
15*38e8c45fSAndroid Build Coastguard Worker  */
16*38e8c45fSAndroid Build Coastguard Worker 
17*38e8c45fSAndroid Build Coastguard Worker #pragma once
18*38e8c45fSAndroid Build Coastguard Worker 
19*38e8c45fSAndroid Build Coastguard Worker #include <ftl/enum.h>
20*38e8c45fSAndroid Build Coastguard Worker #include <ftl/string.h>
21*38e8c45fSAndroid Build Coastguard Worker 
22*38e8c45fSAndroid Build Coastguard Worker #include <bitset>
23*38e8c45fSAndroid Build Coastguard Worker #include <cstdint>
24*38e8c45fSAndroid Build Coastguard Worker #include <iterator>
25*38e8c45fSAndroid Build Coastguard Worker #include <initializer_list>
26*38e8c45fSAndroid Build Coastguard Worker #include <string>
27*38e8c45fSAndroid Build Coastguard Worker #include <type_traits>
28*38e8c45fSAndroid Build Coastguard Worker 
29*38e8c45fSAndroid Build Coastguard Worker // TODO(b/185536303): Align with FTL style.
30*38e8c45fSAndroid Build Coastguard Worker 
31*38e8c45fSAndroid Build Coastguard Worker namespace android::ftl {
32*38e8c45fSAndroid Build Coastguard Worker 
33*38e8c45fSAndroid Build Coastguard Worker /* A class for handling flags defined by an enum or enum class in a type-safe way. */
34*38e8c45fSAndroid Build Coastguard Worker template <typename F>
35*38e8c45fSAndroid Build Coastguard Worker class Flags {
36*38e8c45fSAndroid Build Coastguard Worker     // F must be an enum or its underlying type is undefined. Theoretically we could specialize this
37*38e8c45fSAndroid Build Coastguard Worker     // further to avoid this restriction but in general we want to encourage the use of enums
38*38e8c45fSAndroid Build Coastguard Worker     // anyways.
39*38e8c45fSAndroid Build Coastguard Worker     static_assert(std::is_enum_v<F>, "Flags type must be an enum");
40*38e8c45fSAndroid Build Coastguard Worker     using U = std::underlying_type_t<F>;
41*38e8c45fSAndroid Build Coastguard Worker 
42*38e8c45fSAndroid Build Coastguard Worker public:
Flags(F f)43*38e8c45fSAndroid Build Coastguard Worker     constexpr Flags(F f) : mFlags(static_cast<U>(f)) {}
Flags(std::initializer_list<F> fs)44*38e8c45fSAndroid Build Coastguard Worker     constexpr Flags(std::initializer_list<F> fs) : mFlags(combine(fs)) {}
Flags()45*38e8c45fSAndroid Build Coastguard Worker     constexpr Flags() : mFlags(0) {}
Flags(const Flags<F> & f)46*38e8c45fSAndroid Build Coastguard Worker     constexpr Flags(const Flags<F>& f) : mFlags(f.mFlags) {}
47*38e8c45fSAndroid Build Coastguard Worker 
48*38e8c45fSAndroid Build Coastguard Worker     // Provide a non-explicit construct for non-enum classes since they easily convert to their
49*38e8c45fSAndroid Build Coastguard Worker     // underlying types (e.g. when used with bitwise operators). For enum classes, however, we
50*38e8c45fSAndroid Build Coastguard Worker     // should force them to be explicitly constructed from their underlying types to make full use
51*38e8c45fSAndroid Build Coastguard Worker     // of the type checker.
52*38e8c45fSAndroid Build Coastguard Worker     template <typename T = U>
mFlags(t)53*38e8c45fSAndroid Build Coastguard Worker     constexpr Flags(T t, std::enable_if_t<!is_scoped_enum_v<F>, T>* = nullptr) : mFlags(t) {}
54*38e8c45fSAndroid Build Coastguard Worker 
55*38e8c45fSAndroid Build Coastguard Worker     template <typename T = U>
56*38e8c45fSAndroid Build Coastguard Worker     explicit constexpr Flags(T t, std::enable_if_t<is_scoped_enum_v<F>, T>* = nullptr)
mFlags(t)57*38e8c45fSAndroid Build Coastguard Worker           : mFlags(t) {}
58*38e8c45fSAndroid Build Coastguard Worker 
59*38e8c45fSAndroid Build Coastguard Worker     class Iterator {
60*38e8c45fSAndroid Build Coastguard Worker         using Bits = std::uint64_t;
61*38e8c45fSAndroid Build Coastguard Worker         static_assert(sizeof(U) <= sizeof(Bits));
62*38e8c45fSAndroid Build Coastguard Worker 
63*38e8c45fSAndroid Build Coastguard Worker     public:
64*38e8c45fSAndroid Build Coastguard Worker         constexpr Iterator() = default;
Iterator(Flags<F> flags)65*38e8c45fSAndroid Build Coastguard Worker         Iterator(Flags<F> flags) : mRemainingFlags(flags.mFlags) { (*this)++; }
66*38e8c45fSAndroid Build Coastguard Worker 
67*38e8c45fSAndroid Build Coastguard Worker         // Pre-fix ++
68*38e8c45fSAndroid Build Coastguard Worker         Iterator& operator++() {
69*38e8c45fSAndroid Build Coastguard Worker             if (mRemainingFlags.none()) {
70*38e8c45fSAndroid Build Coastguard Worker                 mCurrFlag = 0;
71*38e8c45fSAndroid Build Coastguard Worker             } else {
72*38e8c45fSAndroid Build Coastguard Worker                 // TODO: Replace with std::countr_zero in C++20.
73*38e8c45fSAndroid Build Coastguard Worker                 const Bits bit = static_cast<Bits>(__builtin_ctzll(mRemainingFlags.to_ullong()));
74*38e8c45fSAndroid Build Coastguard Worker                 mRemainingFlags.reset(static_cast<std::size_t>(bit));
75*38e8c45fSAndroid Build Coastguard Worker                 mCurrFlag = static_cast<U>(static_cast<Bits>(1) << bit);
76*38e8c45fSAndroid Build Coastguard Worker             }
77*38e8c45fSAndroid Build Coastguard Worker             return *this;
78*38e8c45fSAndroid Build Coastguard Worker         }
79*38e8c45fSAndroid Build Coastguard Worker 
80*38e8c45fSAndroid Build Coastguard Worker         // Post-fix ++
81*38e8c45fSAndroid Build Coastguard Worker         Iterator operator++(int) {
82*38e8c45fSAndroid Build Coastguard Worker             Iterator iter = *this;
83*38e8c45fSAndroid Build Coastguard Worker             ++*this;
84*38e8c45fSAndroid Build Coastguard Worker             return iter;
85*38e8c45fSAndroid Build Coastguard Worker         }
86*38e8c45fSAndroid Build Coastguard Worker 
87*38e8c45fSAndroid Build Coastguard Worker         bool operator==(Iterator other) const {
88*38e8c45fSAndroid Build Coastguard Worker             return mCurrFlag == other.mCurrFlag && mRemainingFlags == other.mRemainingFlags;
89*38e8c45fSAndroid Build Coastguard Worker         }
90*38e8c45fSAndroid Build Coastguard Worker 
91*38e8c45fSAndroid Build Coastguard Worker         bool operator!=(Iterator other) const { return !(*this == other); }
92*38e8c45fSAndroid Build Coastguard Worker 
93*38e8c45fSAndroid Build Coastguard Worker         F operator*() const { return F{mCurrFlag}; }
94*38e8c45fSAndroid Build Coastguard Worker 
95*38e8c45fSAndroid Build Coastguard Worker         // iterator traits
96*38e8c45fSAndroid Build Coastguard Worker 
97*38e8c45fSAndroid Build Coastguard Worker         // In the future we could make this a bidirectional const iterator instead of a forward
98*38e8c45fSAndroid Build Coastguard Worker         // iterator but it doesn't seem worth the added complexity at this point. This could not,
99*38e8c45fSAndroid Build Coastguard Worker         // however, be made a non-const iterator as assigning one flag to another is a non-sensical
100*38e8c45fSAndroid Build Coastguard Worker         // operation.
101*38e8c45fSAndroid Build Coastguard Worker         using iterator_category = std::input_iterator_tag;
102*38e8c45fSAndroid Build Coastguard Worker         using value_type = F;
103*38e8c45fSAndroid Build Coastguard Worker         // Per the C++ spec, because input iterators are not assignable the iterator's reference
104*38e8c45fSAndroid Build Coastguard Worker         // type does not actually need to be a reference. In fact, making it a reference would imply
105*38e8c45fSAndroid Build Coastguard Worker         // that modifying it would change the underlying Flags object, which is obviously wrong for
106*38e8c45fSAndroid Build Coastguard Worker         // the same reason this can't be a non-const iterator.
107*38e8c45fSAndroid Build Coastguard Worker         using reference = F;
108*38e8c45fSAndroid Build Coastguard Worker         using difference_type = void;
109*38e8c45fSAndroid Build Coastguard Worker         using pointer = void;
110*38e8c45fSAndroid Build Coastguard Worker 
111*38e8c45fSAndroid Build Coastguard Worker     private:
112*38e8c45fSAndroid Build Coastguard Worker         std::bitset<sizeof(Bits) * 8> mRemainingFlags;
113*38e8c45fSAndroid Build Coastguard Worker         U mCurrFlag = 0;
114*38e8c45fSAndroid Build Coastguard Worker     };
115*38e8c45fSAndroid Build Coastguard Worker 
116*38e8c45fSAndroid Build Coastguard Worker     /*
117*38e8c45fSAndroid Build Coastguard Worker      * Tests whether the given flag is set.
118*38e8c45fSAndroid Build Coastguard Worker      */
test(F flag)119*38e8c45fSAndroid Build Coastguard Worker     bool test(F flag) const {
120*38e8c45fSAndroid Build Coastguard Worker         U f = static_cast<U>(flag);
121*38e8c45fSAndroid Build Coastguard Worker         return (f & mFlags) == f;
122*38e8c45fSAndroid Build Coastguard Worker     }
123*38e8c45fSAndroid Build Coastguard Worker 
124*38e8c45fSAndroid Build Coastguard Worker     /* Tests whether any of the given flags are set */
125*38e8c45fSAndroid Build Coastguard Worker     bool any(Flags<F> f = ~Flags<F>()) const { return (mFlags & f.mFlags) != 0; }
126*38e8c45fSAndroid Build Coastguard Worker 
127*38e8c45fSAndroid Build Coastguard Worker     /* Tests whether all of the given flags are set */
all(Flags<F> f)128*38e8c45fSAndroid Build Coastguard Worker     bool all(Flags<F> f) const { return (mFlags & f.mFlags) == f.mFlags; }
129*38e8c45fSAndroid Build Coastguard Worker 
130*38e8c45fSAndroid Build Coastguard Worker     constexpr Flags<F> operator|(Flags<F> rhs) const { return static_cast<F>(mFlags | rhs.mFlags); }
131*38e8c45fSAndroid Build Coastguard Worker     Flags<F>& operator|=(Flags<F> rhs) {
132*38e8c45fSAndroid Build Coastguard Worker         mFlags = mFlags | rhs.mFlags;
133*38e8c45fSAndroid Build Coastguard Worker         return *this;
134*38e8c45fSAndroid Build Coastguard Worker     }
135*38e8c45fSAndroid Build Coastguard Worker 
136*38e8c45fSAndroid Build Coastguard Worker     Flags<F> operator&(Flags<F> rhs) const { return static_cast<F>(mFlags & rhs.mFlags); }
137*38e8c45fSAndroid Build Coastguard Worker     Flags<F>& operator&=(Flags<F> rhs) {
138*38e8c45fSAndroid Build Coastguard Worker         mFlags = mFlags & rhs.mFlags;
139*38e8c45fSAndroid Build Coastguard Worker         return *this;
140*38e8c45fSAndroid Build Coastguard Worker     }
141*38e8c45fSAndroid Build Coastguard Worker 
142*38e8c45fSAndroid Build Coastguard Worker     Flags<F> operator^(Flags<F> rhs) const { return static_cast<F>(mFlags ^ rhs.mFlags); }
143*38e8c45fSAndroid Build Coastguard Worker     Flags<F>& operator^=(Flags<F> rhs) {
144*38e8c45fSAndroid Build Coastguard Worker         mFlags = mFlags ^ rhs.mFlags;
145*38e8c45fSAndroid Build Coastguard Worker         return *this;
146*38e8c45fSAndroid Build Coastguard Worker     }
147*38e8c45fSAndroid Build Coastguard Worker 
148*38e8c45fSAndroid Build Coastguard Worker     Flags<F> operator~() { return static_cast<F>(~mFlags); }
149*38e8c45fSAndroid Build Coastguard Worker 
150*38e8c45fSAndroid Build Coastguard Worker     bool operator==(Flags<F> rhs) const { return mFlags == rhs.mFlags; }
151*38e8c45fSAndroid Build Coastguard Worker     bool operator!=(Flags<F> rhs) const { return !operator==(rhs); }
152*38e8c45fSAndroid Build Coastguard Worker 
153*38e8c45fSAndroid Build Coastguard Worker     Flags<F>& operator=(const Flags<F>& rhs) {
154*38e8c45fSAndroid Build Coastguard Worker         mFlags = rhs.mFlags;
155*38e8c45fSAndroid Build Coastguard Worker         return *this;
156*38e8c45fSAndroid Build Coastguard Worker     }
157*38e8c45fSAndroid Build Coastguard Worker 
158*38e8c45fSAndroid Build Coastguard Worker     inline Flags<F>& clear(Flags<F> f = static_cast<F>(~static_cast<U>(0))) {
159*38e8c45fSAndroid Build Coastguard Worker         return *this &= ~f;
160*38e8c45fSAndroid Build Coastguard Worker     }
161*38e8c45fSAndroid Build Coastguard Worker 
begin()162*38e8c45fSAndroid Build Coastguard Worker     Iterator begin() const { return Iterator(*this); }
163*38e8c45fSAndroid Build Coastguard Worker 
end()164*38e8c45fSAndroid Build Coastguard Worker     Iterator end() const { return Iterator(); }
165*38e8c45fSAndroid Build Coastguard Worker 
166*38e8c45fSAndroid Build Coastguard Worker     /*
167*38e8c45fSAndroid Build Coastguard Worker      * Returns the stored set of flags.
168*38e8c45fSAndroid Build Coastguard Worker      *
169*38e8c45fSAndroid Build Coastguard Worker      * Note that this returns the underlying type rather than the base enum class. This is because
170*38e8c45fSAndroid Build Coastguard Worker      * the value is no longer necessarily a strict member of the enum since the returned value could
171*38e8c45fSAndroid Build Coastguard Worker      * be multiple enum variants OR'd together.
172*38e8c45fSAndroid Build Coastguard Worker      */
get()173*38e8c45fSAndroid Build Coastguard Worker     U get() const { return mFlags; }
174*38e8c45fSAndroid Build Coastguard Worker 
string()175*38e8c45fSAndroid Build Coastguard Worker     std::string string() const {
176*38e8c45fSAndroid Build Coastguard Worker         std::string result;
177*38e8c45fSAndroid Build Coastguard Worker         bool first = true;
178*38e8c45fSAndroid Build Coastguard Worker         U unstringified = 0;
179*38e8c45fSAndroid Build Coastguard Worker         for (const F f : *this) {
180*38e8c45fSAndroid Build Coastguard Worker             if (const auto flagName = flag_name(f)) {
181*38e8c45fSAndroid Build Coastguard Worker                 appendFlag(result, flagName.value(), first);
182*38e8c45fSAndroid Build Coastguard Worker             } else {
183*38e8c45fSAndroid Build Coastguard Worker                 unstringified |= static_cast<U>(f);
184*38e8c45fSAndroid Build Coastguard Worker             }
185*38e8c45fSAndroid Build Coastguard Worker         }
186*38e8c45fSAndroid Build Coastguard Worker 
187*38e8c45fSAndroid Build Coastguard Worker         if (unstringified != 0) {
188*38e8c45fSAndroid Build Coastguard Worker             constexpr auto radix = sizeof(U) == 1 ? Radix::kBin : Radix::kHex;
189*38e8c45fSAndroid Build Coastguard Worker             appendFlag(result, to_string(unstringified, radix), first);
190*38e8c45fSAndroid Build Coastguard Worker         }
191*38e8c45fSAndroid Build Coastguard Worker 
192*38e8c45fSAndroid Build Coastguard Worker         if (first) {
193*38e8c45fSAndroid Build Coastguard Worker             result += "0x0";
194*38e8c45fSAndroid Build Coastguard Worker         }
195*38e8c45fSAndroid Build Coastguard Worker 
196*38e8c45fSAndroid Build Coastguard Worker         return result;
197*38e8c45fSAndroid Build Coastguard Worker     }
198*38e8c45fSAndroid Build Coastguard Worker 
199*38e8c45fSAndroid Build Coastguard Worker private:
200*38e8c45fSAndroid Build Coastguard Worker     U mFlags;
201*38e8c45fSAndroid Build Coastguard Worker 
combine(std::initializer_list<F> fs)202*38e8c45fSAndroid Build Coastguard Worker     static constexpr U combine(std::initializer_list<F> fs) {
203*38e8c45fSAndroid Build Coastguard Worker         U result = 0;
204*38e8c45fSAndroid Build Coastguard Worker         for (const F f : fs) {
205*38e8c45fSAndroid Build Coastguard Worker             result |= static_cast<U>(f);
206*38e8c45fSAndroid Build Coastguard Worker         }
207*38e8c45fSAndroid Build Coastguard Worker         return result;
208*38e8c45fSAndroid Build Coastguard Worker     }
209*38e8c45fSAndroid Build Coastguard Worker 
appendFlag(std::string & str,const std::string_view & flag,bool & first)210*38e8c45fSAndroid Build Coastguard Worker     static void appendFlag(std::string& str, const std::string_view& flag, bool& first) {
211*38e8c45fSAndroid Build Coastguard Worker         if (first) {
212*38e8c45fSAndroid Build Coastguard Worker             first = false;
213*38e8c45fSAndroid Build Coastguard Worker         } else {
214*38e8c45fSAndroid Build Coastguard Worker             str += " | ";
215*38e8c45fSAndroid Build Coastguard Worker         }
216*38e8c45fSAndroid Build Coastguard Worker         str += flag;
217*38e8c45fSAndroid Build Coastguard Worker     }
218*38e8c45fSAndroid Build Coastguard Worker };
219*38e8c45fSAndroid Build Coastguard Worker 
220*38e8c45fSAndroid Build Coastguard Worker // This namespace provides operator overloads for enum classes to make it easier to work with them
221*38e8c45fSAndroid Build Coastguard Worker // as flags. In order to use these, add them via a `using namespace` declaration.
222*38e8c45fSAndroid Build Coastguard Worker namespace flag_operators {
223*38e8c45fSAndroid Build Coastguard Worker 
224*38e8c45fSAndroid Build Coastguard Worker template <typename F, typename = std::enable_if_t<is_scoped_enum_v<F>>>
225*38e8c45fSAndroid Build Coastguard Worker inline Flags<F> operator~(F f) {
226*38e8c45fSAndroid Build Coastguard Worker     return static_cast<F>(~to_underlying(f));
227*38e8c45fSAndroid Build Coastguard Worker }
228*38e8c45fSAndroid Build Coastguard Worker 
229*38e8c45fSAndroid Build Coastguard Worker template <typename F, typename = std::enable_if_t<is_scoped_enum_v<F>>>
230*38e8c45fSAndroid Build Coastguard Worker constexpr Flags<F> operator|(F lhs, F rhs) {
231*38e8c45fSAndroid Build Coastguard Worker     return static_cast<F>(to_underlying(lhs) | to_underlying(rhs));
232*38e8c45fSAndroid Build Coastguard Worker }
233*38e8c45fSAndroid Build Coastguard Worker 
234*38e8c45fSAndroid Build Coastguard Worker } // namespace flag_operators
235*38e8c45fSAndroid Build Coastguard Worker } // namespace android::ftl
236