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