1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /** 6 * Bit manipulation. 7 * 8 * Copyright: Eugene Wissner 2018-2020. 9 * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 * Mozilla Public License, v. 2.0). 11 * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/bitmanip.d, 13 * tanya/bitmanip.d) 14 */ 15 module tanya.bitmanip; 16 17 import tanya.meta.metafunction; 18 import tanya.meta.trait; 19 import tanya.meta.transform; 20 21 /** 22 * Determines whether $(D_PARAM E) is a $(D_KEYWORD enum), whose members can be 23 * used as bit flags. 24 * 25 * This is the case if all members of $(D_PARAM E) are integral numbers that 26 * are either 0 or positive integral powers of 2. 27 * 28 * Params: 29 * E = Some $(D_KEYWORD enum). 30 * 31 * Returns: $(D_KEYWORD true) if $(D_PARAM E) contains only bit flags, 32 * $(D_KEYWORD false) otherwise. 33 */ 34 template isBitFlagEnum(E) 35 { 36 enum bool isValid(OriginalType!E x) = x == 0 37 || (x > 0 && ((x & (x - 1)) == 0)); 38 static if (isIntegral!E) 39 { 40 enum bool isBitFlagEnum = allSatisfy!(isValid, EnumMembers!E); 41 } 42 else 43 { 44 enum bool isBitFlagEnum = false; 45 } 46 } 47 48 /// 49 @nogc nothrow pure @safe unittest 50 { 51 enum Valid 52 { 53 none = 0, 54 one = 1 << 0, 55 two = 1 << 1, 56 } 57 static assert(isBitFlagEnum!Valid); 58 59 enum Invalid 60 { 61 one, 62 two, 63 three, 64 four, 65 } 66 static assert(!isBitFlagEnum!Invalid); 67 68 enum Negative 69 { 70 one = -1, 71 two = -2, 72 } 73 static assert(!isBitFlagEnum!Negative); 74 } 75 76 /** 77 * Validates that $(D_PARAM field) contains only bits from $(D_PARAM E). 78 * 79 * Params: 80 * E = Some $(D_KEYWORD enum). 81 * field = Bit field. 82 * 83 * Returns: $(D_KEYWORD true) if $(D_PARAM field) is valid, $(D_KEYWORD false) 84 * otherwise. 85 */ 86 bool containsBitFlags(E)(E field) 87 if (isBitFlagEnum!E) 88 { 89 OriginalType!E fillField() 90 { 91 typeof(return) full; 92 static foreach (member; EnumMembers!E) 93 { 94 full |= member; 95 } 96 return full; 97 } 98 enum OriginalType!E full = fillField(); 99 return (field & ~full) == OriginalType!E.init; 100 } 101 102 /// 103 @nogc nothrow pure @safe unittest 104 { 105 enum E 106 { 107 one, 108 two, 109 three, 110 } 111 assert(containsBitFlags(E.one | E.two)); 112 assert(!containsBitFlags(cast(E) 0x8)); 113 } 114 115 /** 116 * Allows to use $(D_KEYWORD enum) values as a set of bit flags. 117 * 118 * $(D_PSYMBOL BitFlags) behaves the same as a bit field of type $(D_PARAM E), 119 * but does additional cheks to ensure that the bit field contains only valid 120 * values, this is only values from $(D_PARAM E). 121 * 122 * Params: 123 * E = Some $(D_KEYWORD enum). 124 */ 125 struct BitFlags(E) 126 if (isBitFlagEnum!E) 127 { 128 private OriginalType!E field; 129 130 /** 131 * Constructs $(D_PSYMBOL BitFlags) from $(D_PARAM field). 132 * 133 * Params: 134 * field = Bits to be set. 135 */ 136 this(E field) 137 { 138 this.field = field; 139 } 140 141 /** 142 * Converts $(D_PSYMBOL BitFlags) to a boolean. 143 * 144 * It is $(D_KEYWORD true) if any bit is set, $(D_KEYWORD false) otherwise. 145 * 146 * Returns: $(D_KEYWORD true) if this $(D_PSYMBOL BitFlags) contains any 147 * set bits, $(D_KEYWORD false) otherwise. 148 */ 149 bool opCast(T : bool)() 150 { 151 return this.field != 0; 152 } 153 154 /** 155 * Converts to the original type of $(D_PARAM E) ($(D_KEYWORD int) by 156 * default). 157 * 158 * Returns: $(D_KEYWORD this) as $(D_INLINECODE OriginalType!T). 159 */ 160 OriginalType!E opCast(T : OriginalType!E)() const 161 { 162 return this.field; 163 } 164 165 /** 166 * Tests (&), sets (|) or toggles (^) bits. 167 * 168 * Params: 169 * op = Operation. 170 * that = 0 or more bit flags. 171 * 172 * Returns: New $(D_PSYMBOL BitFlags) object. 173 */ 174 BitFlags opBinary(string op)(E that) const 175 if (op == "&" || op == "|" || op == "^") 176 { 177 BitFlags result = this; 178 mixin("return result " ~ op ~ "= that;"); 179 } 180 181 /// ditto 182 BitFlags opBinary(string op)(BitFlags that) const 183 if (op == "&" || op == "|" || op == "^") 184 { 185 BitFlags result = this; 186 mixin("return result " ~ op ~ "= that;"); 187 } 188 189 /// ditto 190 BitFlags opBinaryRight(string op)(E that) const 191 if (op == "&" || op == "|" || op == "^") 192 { 193 BitFlags result = this; 194 mixin("return result " ~ op ~ "= that;"); 195 } 196 197 /** 198 * Tests (&), sets (|) or toggles (^) bits. 199 * 200 * Params: 201 * op = Operation. 202 * that = 0 or more bit flags. 203 * 204 * Returns: $(D_KEYWORD this). 205 */ 206 ref BitFlags opOpAssign(string op)(E that) 207 if (op == "&" || op == "|" || op == "^") 208 { 209 mixin("this.field " ~ op ~ "= that;"); 210 return this; 211 } 212 213 /// ditto 214 ref BitFlags opOpAssign(string op)(BitFlags that) 215 if (op == "&" || op == "|" || op == "^") 216 { 217 mixin("this.field " ~ op ~ "= that.field;"); 218 return this; 219 } 220 221 /** 222 * Inverts all bit flags. 223 * 224 * Returns: New $(D_PSYMBOL BitFlags) object with all bits inverted. 225 */ 226 BitFlags opUnary(string op : "~")() const 227 { 228 BitFlags result; 229 result.field = ~this.field; 230 return result; 231 } 232 233 /** 234 * Assigns a bit field. 235 * 236 * Params: 237 * that = Bit field of type $(D_PARAM E). 238 * 239 * Returns: $(D_KEYWORD this). 240 */ 241 ref BitFlags opAssign(E that) 242 { 243 this.field = that; 244 return this; 245 } 246 247 /** 248 * Compares this $(D_PSYMBOL BitFlags) object to another bit field. 249 * 250 * Params: 251 * that = $(D_PSYMBOL BitFlags) object or a bit field of type 252 * $(D_PARAM E). 253 * 254 * Returns: $(D_KEYWORD true) if $(D_KEYWORD this) and $(D_PARAM that) 255 * contain the same bits ,$(D_KEYWORD false) otherwise. 256 */ 257 bool opEquals(E that) const 258 { 259 return this.field == that; 260 } 261 262 /// ditto 263 bool opEquals(BitFlags that) const 264 { 265 return this.field == that.field; 266 } 267 268 /** 269 * Generates a hash value of this object. 270 * 271 * Returns: Hash value. 272 */ 273 size_t toHash() const 274 { 275 return cast(size_t) this.field; 276 } 277 } 278 279 /** 280 * Creates a $(D_PSYMBOL BitFlags) object initialized with $(D_PARAM field). 281 * 282 * Params: 283 * E = Some $(D_KEYWORD enum). 284 * field = Bits to be set. 285 */ 286 BitFlags!E bitFlags(E)(E field) 287 if (isBitFlagEnum!E) 288 { 289 return BitFlags!E(field); 290 } 291 292 /// 293 @nogc nothrow pure @safe unittest 294 { 295 enum E 296 { 297 one = 1 << 0, 298 two = 1 << 1, 299 three = 1 << 2, 300 } 301 // Construct with E.one and E.two set 302 auto flags = bitFlags(E.one | E.two); 303 304 // Test wheter E.one is set 305 assert(flags & E.one); 306 307 // Toggle E.one 308 flags ^= E.one; 309 assert(!(flags & E.one)); 310 311 // Set E.three 312 flags |= E.three; 313 assert(flags & E.three); 314 315 // Clear E.three 316 flags &= ~E.three; 317 assert(!(flags & E.three)); 318 }