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