Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2022 Project CHIP Authors
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : #pragma once
19 :
20 : #include <lib/support/BitFlags.h>
21 :
22 : namespace chip {
23 :
24 : /**
25 : * Stores bit masks and flags in a more typesafe manner.
26 : *
27 : * Extends BitFlags for boolean flags and also adds support for bit-mask set options.
28 : *
29 : */
30 : template <typename FlagsEnum, typename StorageType = typename std::underlying_type_t<FlagsEnum>>
31 : class BitMask : public BitFlags<FlagsEnum, StorageType>
32 : {
33 : public:
34 : using IntegerType = typename BitFlags<FlagsEnum, StorageType>::IntegerType;
35 :
36 60 : constexpr BitMask() : BitFlags<FlagsEnum, StorageType>() {}
37 : constexpr BitMask(const BitFlags<FlagsEnum, StorageType> & other) : BitFlags<FlagsEnum, StorageType>(other) {}
38 : constexpr BitMask(BitFlags<FlagsEnum, StorageType> && other) : BitFlags<FlagsEnum, StorageType>(std::move(other)) {}
39 : BitMask(const BitMask &) = default;
40 :
41 : explicit BitMask(FlagsEnum value) : BitFlags<FlagsEnum, StorageType>(value) {}
42 : explicit BitMask(IntegerType value) : BitFlags<FlagsEnum, StorageType>(value) {}
43 :
44 : template <typename... Args>
45 : constexpr BitMask(FlagsEnum flag, Args &&... args) : BitFlags<FlagsEnum, StorageType>(flag, std::forward<Args>(args)...)
46 : {}
47 :
48 : template <typename... Args>
49 : constexpr BitMask(IntegerType value, Args &&... args) : BitFlags<FlagsEnum, StorageType>(value, std::forward<Args>(args)...)
50 : {}
51 :
52 : BitMask & operator=(const BitMask &) = default;
53 :
54 : BitMask & operator=(const BitFlags<FlagsEnum, StorageType> & other)
55 : {
56 : BitFlags<FlagsEnum, StorageType>::SetRaw(other.Raw());
57 : return *this;
58 : }
59 :
60 : BitMask & operator=(FlagsEnum value)
61 : {
62 : BitFlags<FlagsEnum, StorageType>::SetRaw(static_cast<StorageType>(value));
63 : return *this;
64 : }
65 :
66 : /**
67 : * Set a field within the bit mask integer.
68 : *
69 : * Fields are assumed to be contiguous bits within the underlying integer, for example:
70 : * A mask of 0x70 == 0b01110000 means that a 3 bit value is encoded at bits [4;6].
71 : *
72 : * Calling SetField(0x70, n) is equivalent to "(value & ~0x70) | (n << 4)"
73 : *
74 : * @param mask The mask, code assumes a continous bit mask
75 : * @param value The value, NOT shifted, MUST fit within the number of bits of the mask
76 : */
77 : constexpr BitMask & SetField(FlagsEnum mask, IntegerType value)
78 : {
79 : IntegerType bitMask = static_cast<IntegerType>(mask);
80 : IntegerType shift = GetShiftToFirstSetBit(bitMask);
81 :
82 : // NOTE: value should be fully contained within the shift mask.
83 : //
84 : // assert((value & (mask >> shift)) == value);
85 :
86 : // Clear bits overlayed by the mask
87 : IntegerType updated = static_cast<IntegerType>(BitFlags<FlagsEnum, StorageType>::Raw() & ~bitMask);
88 :
89 : // Set the right bits
90 : updated = static_cast<IntegerType>(updated | (bitMask & (value << shift)));
91 :
92 : BitFlags<FlagsEnum, StorageType>::SetRaw(updated);
93 :
94 : return *this;
95 : }
96 :
97 : /**
98 : * Gets an underlying field that is contained within a bit subset of the integer.
99 : * Examples:
100 : * GetField(0x70) == GetField(0b01110000) == ((n & 0x70) >> 4) == ((n >> 4) 0x07)
101 : * GetField(0xC0) == GetField(0b11000000) == ((n & 0xC0) >> 6) == ((n >> 6) 0x03)
102 : *
103 : * @param mask The bit mask to be used, assumed to be a contigous bit mask
104 : */
105 : IntegerType GetField(FlagsEnum mask) const
106 : {
107 : IntegerType bitMask = static_cast<IntegerType>(mask);
108 : IntegerType shift = GetShiftToFirstSetBit(bitMask);
109 :
110 : // Forward the right bits
111 : return static_cast<IntegerType>(((BitFlags<FlagsEnum, StorageType>::Raw() & bitMask) >> shift));
112 : }
113 :
114 : protected:
115 : /// Get the shift amount to reach the first non-zero bit in a bitmask
116 : ///
117 : /// Examples:
118 : /// GetShiftToFirstSetBit(0b0000001) == 0
119 : /// GetShiftToFirstSetBit(0b0011100) == 2
120 : /// GetShiftToFirstSetBit(0b0110000) == 4
121 : ///
122 : /// Note: This does NOT validate if the given mask is a valid mask. So:
123 : /// GetShiftToFirstSetBit(0b0100010) == 1
124 : /// GetShiftToFirstSetBit(0b010110100) == 2
125 : static constexpr IntegerType GetShiftToFirstSetBit(IntegerType bitMask)
126 : {
127 : IntegerType count = 0;
128 : IntegerType mask = 0x01;
129 :
130 : if (bitMask == 0)
131 : {
132 : return sizeof(bitMask); // generally invalid value
133 : }
134 :
135 : while ((mask & bitMask) == 0)
136 : {
137 : mask = static_cast<IntegerType>(mask << 1);
138 : count++;
139 : }
140 : return count;
141 : }
142 : };
143 :
144 : } // namespace chip
|