1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // This header provides a type-safe way of storing OR-combinations of enum
6 // values.
7 //
8 // The traditional C++ approach for storing OR-combinations of enum values is to
9 // use an int or unsigned int variable. The inconvenience with this approach is
10 // that there's no type checking at all; any enum value can be OR'd with any
11 // other enum value and passed on to a function that takes an int or unsigned
12 // int.
13
14 #ifndef PARTITION_ALLOC_FLAGS_H_
15 #define PARTITION_ALLOC_FLAGS_H_
16
17 #include <type_traits>
18
19 namespace partition_alloc::internal {
20 // Returns `T` if and only if `EnumType` is a scoped enum.
21 template <typename EnumType, typename T = EnumType>
22 using IfEnum = std::enable_if_t<
23 std::is_enum_v<EnumType> &&
24 !std::is_convertible_v<EnumType, std::underlying_type_t<EnumType>>,
25 T>;
26
27 // We assume `EnumType` defines `kMaxValue` which has the largest value and all
28 // powers of two are represented in `EnumType`.
29 template <typename EnumType>
30 constexpr inline EnumType kAllFlags = static_cast<IfEnum<EnumType>>(
31 (static_cast<std::underlying_type_t<EnumType>>(EnumType::kMaxValue) << 1) -
32 1);
33
34 template <typename EnumType>
AreValidFlags(EnumType flags)35 constexpr inline IfEnum<EnumType, bool> AreValidFlags(EnumType flags) {
36 const auto raw_flags = static_cast<std::underlying_type_t<EnumType>>(flags);
37 const auto raw_all_flags =
38 static_cast<std::underlying_type_t<EnumType>>(kAllFlags<EnumType>);
39 return (raw_flags & ~raw_all_flags) == 0;
40 }
41
42 // Checks `subset` is a subset of `superset` or not.
43 template <typename EnumType>
ContainsFlags(EnumType superset,EnumType subset)44 constexpr inline IfEnum<EnumType, bool> ContainsFlags(EnumType superset,
45 EnumType subset) {
46 return (superset & subset) == subset;
47 }
48
49 // Removes flags `target` from `from`.
50 template <typename EnumType>
RemoveFlags(EnumType from,EnumType target)51 constexpr inline IfEnum<EnumType> RemoveFlags(EnumType from, EnumType target) {
52 return from & ~target;
53 }
54
55 // A macro to define binary arithmetic over `EnumType`.
56 // Use inside `namespace partition_alloc::internal`.
57 #define PA_DEFINE_OPERATORS_FOR_FLAGS(EnumType) \
58 [[maybe_unused]] [[nodiscard]] inline constexpr EnumType operator&( \
59 const EnumType& lhs, const EnumType& rhs) { \
60 return static_cast<EnumType>( \
61 static_cast<std::underlying_type_t<EnumType>>(lhs) & \
62 static_cast<std::underlying_type_t<EnumType>>(rhs)); \
63 } \
64 [[maybe_unused]] inline constexpr EnumType& operator&=( \
65 EnumType& lhs, const EnumType& rhs) { \
66 lhs = lhs & rhs; \
67 return lhs; \
68 } \
69 [[maybe_unused]] [[nodiscard]] inline constexpr EnumType operator|( \
70 const EnumType& lhs, const EnumType& rhs) { \
71 return static_cast<EnumType>( \
72 static_cast<std::underlying_type_t<EnumType>>(lhs) | \
73 static_cast<std::underlying_type_t<EnumType>>(rhs)); \
74 } \
75 [[maybe_unused]] inline constexpr EnumType& operator|=( \
76 EnumType& lhs, const EnumType& rhs) { \
77 lhs = lhs | rhs; \
78 return lhs; \
79 } \
80 [[maybe_unused]] [[nodiscard]] inline constexpr EnumType operator^( \
81 const EnumType& lhs, const EnumType& rhs) { \
82 return static_cast<EnumType>( \
83 static_cast<std::underlying_type_t<EnumType>>(lhs) ^ \
84 static_cast<std::underlying_type_t<EnumType>>(rhs)); \
85 } \
86 [[maybe_unused]] inline constexpr EnumType& operator^=( \
87 EnumType& lhs, const EnumType& rhs) { \
88 lhs = lhs ^ rhs; \
89 return lhs; \
90 } \
91 [[maybe_unused]] [[nodiscard]] inline constexpr EnumType operator~( \
92 const EnumType& val) { \
93 return static_cast<EnumType>( \
94 static_cast<std::underlying_type_t<EnumType>>(kAllFlags<EnumType>) & \
95 ~static_cast<std::underlying_type_t<EnumType>>(val)); \
96 } \
97 static_assert(true) /* semicolon here */
98
99 } // namespace partition_alloc::internal
100
101 #endif // PARTITION_ALLOC_FLAGS_H_
102