1 /** Various extensions to core.bitop and std.bitmanip. 2 * 3 * Copyright: Per Nordlöw 2018-. 4 * License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 5 * Authors: $(WEB Per Nordlöw) 6 * 7 * TODO Add range checking of bit indexes. 8 * 9 * TODO Make use of TZCNT and LZCNT either as inline assembly or as builtins: https://github.com/dlang/dmd/pull/6364 10 */ 11 module nxt.bitop_ex; 12 13 import std.meta : allSatisfy; 14 import std.traits : isIntegral; 15 16 @safe pure nothrow @nogc: 17 18 /** Get an unsigned type of size as `T` if possible. */ 19 template UnsignedOfSameSizeAs(T) 20 { 21 enum nBits = 8*T.sizeof; 22 static if (nBits == 8) alias UnsignedOfSameSizeAs = ubyte; 23 else static if (nBits == 16) alias UnsignedOfSameSizeAs = ushort; 24 else static if (nBits == 32) alias UnsignedOfSameSizeAs = uint; 25 else static if (nBits == 64) alias UnsignedOfSameSizeAs = ulong; 26 else static if (nBits == 128) alias UnsignedOfSameSizeAs = ucent; 27 else 28 { 29 import std.conv: to; 30 static assert(0, "No Unsigned type of size " ~ to!string(nBits) ~ " found"); 31 } 32 } 33 34 /** Returns: `T` with only `bix`:th bit set. */ 35 T makeBit(T, I...)(I bixs) @safe 36 if (isIntegral!T && 37 allSatisfy!(isIntegral, I) && 38 I.length >= 1) 39 in 40 { 41 foreach (n, const bix; bixs) 42 { 43 assert(0 <= bix && bix < 8*T.sizeof, 44 "Bit index " ~ n.stringof ~ " is out of range"); 45 } 46 } 47 do 48 { 49 T x = 0; 50 foreach (const bix; bixs) 51 { 52 x |= cast(T)((cast(T)1) << bix); 53 } 54 return x; 55 } 56 alias btm = makeBit; 57 58 /** Returns: `true` iff all `bix`:th bits of `a` are set. */ 59 pragma(inline, true) 60 bool testBit(T, I...)(in T a, I bixs) @safe 61 if (isIntegral!T && 62 allSatisfy!(isIntegral, I) && 63 I.length >= 1) 64 { 65 return a & makeBit!T(bixs) ? true : false; 66 } 67 68 /** Returns: `true` iff all `bix`:th bits of `a` are set. */ 69 pragma(inline, true) 70 bool testBit(T, I...)(in T a, I bixs) @trusted 71 if ((!(isIntegral!T)) && 72 allSatisfy!(isIntegral, I)) 73 { 74 return (*(cast(UnsignedOfSameSizeAs!T*)&a)).testBit(bixs); // reuse integer variant 75 } 76 77 /** Returns: `true` iff all `bix`:th bits of `*a` are set. */ 78 pragma(inline, true) 79 bool testBit(T, I...)(in T* a, I bixs) @safe 80 if ((!(isIntegral!T)) && 81 !is(T == size_t) && // avoid stealing `core.bitop.bt` 82 allSatisfy!(isIntegral, I) && 83 I.length >= 1) 84 { 85 return testBit(*a, bixs); 86 } 87 alias bt = testBit; 88 89 /// 90 @safe pure nothrow @nogc unittest 91 { 92 static void test(T)() 93 { 94 const mn = T.min, mx = T.max; 95 enum nBits = 8*T.sizeof; 96 foreach (const ix; 0 .. nBits-1) 97 { 98 assert(!mn.bt(ix)); 99 } 100 assert(mn.bt(nBits - 1)); 101 foreach (const ix; 0 .. T.sizeof) 102 { 103 assert(mx.bt(ix)); 104 } 105 } 106 test!byte; 107 test!short; 108 test!int; 109 test!long; 110 } 111 112 /** Test and sets the `bix`:th bit of `a` to one. 113 * 114 * Returns: A non-zero value if the bit was set, and a zero if it was clear. 115 */ 116 pragma(inline, true) 117 void setBit(T, I...)(ref T a, I bixs) @safe 118 if (isIntegral!T && 119 allSatisfy!(isIntegral, I) && 120 I.length >= 1) 121 { 122 a |= makeBit!T(bixs); 123 } 124 125 /** Sets the `bix`:th bit of `*a` to one. 126 */ 127 pragma(inline, true) 128 void setBit(T, I...)(T* a, I bixs) @safe 129 if (isIntegral!T && 130 !is(T == size_t) && // avoid stealing core.bitop.bt 131 allSatisfy!(isIntegral, I) && 132 I.length >= 1) 133 { 134 *a |= makeBit!T(bixs); 135 } 136 137 /** Sets the `bix`:th bit of `*a` to one. 138 */ 139 pragma(inline, true) 140 void setBit(T, I...)(ref T a, I bixs) @trusted 141 if ((!(isIntegral!T)) && 142 allSatisfy!(isIntegral, I) && 143 I.length >= 1) 144 { 145 alias U = UnsignedOfSameSizeAs!T; 146 (*(cast(U*)&a)) |= makeBit!U(bixs); // reuse integer variant 147 } 148 alias bts = setBit; 149 150 /* alias btc = complementBit; */ 151 /* alias btr = resetBit; */ 152 153 /** Set lowest bit of `a` to one. */ 154 pragma(inline, true) 155 void setLowestBit(T)(ref T a) @safe 156 if (isIntegral!T) 157 { 158 setBit(a, 0); 159 } 160 alias setBottomBit = setLowestBit; 161 alias setLsbit = setLowestBit; 162 163 /** Set highest bit of `a` to one. */ 164 pragma(inline, true) 165 void setHighestBit(T)(ref T a) @safe 166 if (isIntegral!T) 167 { 168 setBit(a, 8*T.sizeof - 1); 169 } 170 alias setTopBit = setHighestBit; 171 alias setMsbit = setHighestBit; 172 173 /** Get lowest bit of `a`. */ 174 pragma(inline, true) 175 bool getLowestBit(T)(in T a) @safe 176 if (isIntegral!T) 177 { 178 return (a & 1) != 0; 179 } 180 alias getBottomBit = getLowestBit; 181 alias getLsbit = getLowestBit; 182 183 /** Get highest bit of `a`. */ 184 pragma(inline, true) 185 bool getHighestBit(T)(in T a) @safe 186 if (isIntegral!T) 187 { 188 // TODO use core.bitop.bt when T is a size_t 189 return (a & (cast(T)1 << 8*T.sizeof - 1)) != 0; 190 } 191 alias getTopBit = getHighestBit; 192 alias getMsbit = getHighestBit; 193 194 /// 195 @safe pure nothrow @nogc unittest 196 { 197 const ubyte x = 1; 198 assert(!x.getTopBit); 199 assert(x.getLowestBit); 200 } 201 202 /// 203 @safe pure nothrow @nogc unittest 204 { 205 const ubyte x = 128; 206 assert(x.getTopBit); 207 assert(!x.getLowestBit); 208 } 209 210 /** Reset bits `I` of `a` (to zero). */ 211 pragma(inline, true) 212 void resetBit(T, I...)(ref T a, I bixs) @safe 213 if (isIntegral!T && 214 allSatisfy!(isIntegral, I)) 215 { 216 a &= ~makeBit!T(bixs); 217 } 218 219 /** Reset bits `I` of `*a` (to zero). */ 220 pragma(inline, true) 221 void resetBit(T, I...)(T* a, I bixs) @safe 222 if (isIntegral!T && 223 !is(T == size_t) && // avoid stealing core.bitop.bt 224 allSatisfy!(isIntegral, I)) 225 { 226 *a &= ~makeBit!T(bixs); 227 } 228 229 /** Reset bits `I` of `a` (to zero). */ 230 pragma(inline, true) 231 void resetBit(T, I...)(ref T a, I bixs) 232 if ((!(isIntegral!T)) && 233 allSatisfy!(isIntegral, I)) 234 { 235 alias U = UnsignedOfSameSizeAs!T; 236 (*(cast(U*)&a)) &= ~makeBit!U(bixs); // reuse integer variant 237 } 238 239 /** Reset lowest bit of `a` (to zero). */ 240 pragma(inline, true) 241 void resetLowestBit(T)(ref T a) @safe 242 if (isIntegral!T) 243 { 244 resetBit(a, 0); 245 } 246 alias resetBottomBit = resetLowestBit; 247 248 /** Reset highest bit of `a` (to zero). */ 249 pragma(inline, true) 250 void resetHighestBit(T)(ref T a) @safe 251 if (isIntegral!T) 252 { 253 resetBit(a, 8*T.sizeof - 1); 254 } 255 alias resetTopBit = resetHighestBit; 256 257 alias btr = resetBit; 258 259 /// 260 pure nothrow @nogc unittest 261 { 262 alias T = int; 263 enum nBits = 8*T.sizeof; 264 T a = 0; 265 266 a.bts(0); assert(a == 1); 267 a.bts(1); assert(a == 3); 268 a.bts(2); assert(a == 7); 269 270 a.btr(0); assert(a == 6); 271 a.btr(1); assert(a == 4); 272 a.btr(2); assert(a == 0); 273 274 a.bts(0, 1, 2); assert(a == 7); 275 a.btr(0, 1, 2); assert(a == 0); 276 277 a.bts(8*T.sizeof - 1); assert(a != 0); 278 a.btr(8*T.sizeof - 1); assert(a == 0); 279 280 T b = 0; 281 b.bts(nBits - 1); 282 assert(b == T.min); 283 } 284 285 /// 286 @safe pure nothrow @nogc unittest 287 { 288 static void test(T)() 289 { 290 enum nBits = 8*T.sizeof; 291 T x = 0; 292 x.bts(0); 293 } 294 295 test!float; 296 test!double; 297 } 298 299 /// 300 @safe pure nothrow @nogc unittest 301 { 302 assert(makeBit!int(2) == 4); 303 assert(makeBit!int(2, 3) == 12); 304 assert(makeBit!uint(0, 31) == 2^^31 + 1); 305 306 import std.meta : AliasSeq; 307 foreach (T; AliasSeq!(ubyte, ushort, uint, ulong)) 308 { 309 foreach (const n; 0 .. 8*T.sizeof) 310 { 311 const x = makeBit!T(n); 312 assert(x == 2UL^^n); 313 314 T y = x; 315 y.resetBit(n); 316 assert(y == 0); 317 318 y.setBit(n); 319 assert(y == x); 320 } 321 } 322 }