1 /++ 2 This module contains a collection of bit-level operations. 3 4 Authors: Ilia Ki, Phobos & LDC Authors (original Phobos unittests, docs, conventions). 5 +/ 6 module mir.bitop; 7 8 version(LDC) 9 import ldc.intrinsics; 10 version(GNU) 11 import gcc.builtins; 12 13 import mir.math.common: fastmath; 14 15 /// Right shift vallue for bit index to get element's index (5 for `uint`). 16 enum uint bitElemShift(T : ubyte) = 3; 17 /// ditto 18 enum uint bitElemShift(T : byte) = 3; 19 /// ditto 20 enum uint bitElemShift(T : ushort) = 4; 21 /// ditto 22 enum uint bitElemShift(T : short) = 4; 23 /// ditto 24 enum uint bitElemShift(T : uint) = 5; 25 /// ditto 26 enum uint bitElemShift(T : int) = 5; 27 /// ditto 28 enum uint bitElemShift(T : ulong) = 6; 29 /// ditto 30 enum uint bitElemShift(T : long) = 6; 31 static if (is(ucent)) 32 /// ditto 33 enum uint bitElemShift(T : ucent) = 7; 34 /// ditto 35 static if (is(cent)) 36 enum uint bitElemShift(T : cent) = 7; 37 38 /// Bit mask for bit index to get element's bit shift (31 for uint). 39 enum uint bitShiftMask(T : ubyte) = 7; 40 /// ditto 41 enum uint bitShiftMask(T : byte) = 7; 42 /// ditto 43 enum uint bitShiftMask(T : ushort) = 15; 44 /// ditto 45 enum uint bitShiftMask(T : short) = 15; 46 /// ditto 47 enum uint bitShiftMask(T : uint) = 31; 48 /// ditto 49 enum uint bitShiftMask(T : int) = 31; 50 /// ditto 51 enum uint bitShiftMask(T : ulong) = 63; 52 /// ditto 53 enum uint bitShiftMask(T : long) = 63; 54 static if (is(ucent)) 55 /// ditto 56 enum uint bitShiftMask(T : ucent) = 127; 57 static if (is(cent)) 58 /// ditto 59 enum uint bitShiftMask(T : cent) = 127; 60 61 // no effect on this function, but better for optimization of other @fastmath code that uses this 62 @fastmath: 63 64 65 /++ 66 +/ 67 T nTrailingBitsToCount(T)(in T value, in T popcnt) 68 if (__traits(isUnsigned, T)) 69 { 70 import std.traits; 71 import mir.internal.utility: Iota; 72 alias S = Signed!(CommonType!(int, T)); 73 S mask = S(-1) << T.sizeof * 4; 74 foreach_reverse (s; Iota!(bitElemShift!T - 1)) 75 {{ 76 enum shift = 1 << s; 77 if (S(popcnt) > S(ctpop(cast(T)(value & ~mask)))) 78 mask <<= shift; 79 else 80 mask >>= shift; 81 }} 82 return cttz(cast(T)mask) + (S(popcnt) != ctpop(cast(T)(value & ~mask))); 83 } 84 85 /// 86 version(mir_core_test) unittest 87 { 88 assert(nTrailingBitsToCount(0xF0u, 3u) == 7); 89 assert(nTrailingBitsToCount(0xE00u, 3u) == 12); 90 91 foreach(uint i; 1 .. 32) 92 assert(nTrailingBitsToCount(uint.max, i) == i); 93 } 94 95 /++ 96 +/ 97 T nLeadingBitsToCount(T)(in T value, in T popcnt) 98 if (__traits(isUnsigned, T)) 99 { 100 import std.traits; 101 import mir.internal.utility: Iota; 102 alias S = Signed!(CommonType!(int, T)); 103 S mask = S(-1) << T.sizeof * 4; 104 foreach_reverse (s; Iota!(bitElemShift!T - 1)) 105 {{ 106 enum shift = 1 << s; 107 if (S(popcnt) > S(ctpop(cast(T)(value & mask)))) 108 mask >>= shift; 109 else 110 mask <<= shift; 111 }} 112 return ctlz(cast(T)~mask) + (S(popcnt) != ctpop(cast(T)(value & mask))); 113 } 114 115 /// 116 version(mir_core_test) unittest 117 { 118 assert(nLeadingBitsToCount(0xF0u, 3u) == 32 - 5); 119 assert(nLeadingBitsToCount(0x700u, 3u) == 32 - 8); 120 121 foreach(uint i; 1 .. 32) 122 assert(nLeadingBitsToCount(uint.max, i) == i); 123 } 124 125 /++ 126 Tests the bit. 127 Returns: 128 A non-zero value if the bit was set, and a zero 129 if it was clear. 130 +/ 131 auto bt(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum) 132 if (__traits(isUnsigned, T)) 133 { 134 auto index = bitnum >> bitElemShift!T; 135 auto mask = T(1) << (bitnum & bitShiftMask!T); 136 return p[index] & mask; 137 } 138 139 /// 140 @system pure version(mir_core_test) unittest 141 { 142 size_t[2] array; 143 144 array[0] = 2; 145 array[1] = 0x100; 146 147 assert(bt(array.ptr, 1)); 148 assert(array[0] == 2); 149 assert(array[1] == 0x100); 150 } 151 152 /++ 153 Tests and assign the bit. 154 Returns: 155 A non-zero value if the bit was set, and a zero if it was clear. 156 +/ 157 auto bta(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum, bool value) 158 if (__traits(isUnsigned, T)) 159 { 160 auto index = bitnum >> bitElemShift!T; 161 auto shift = bitnum & bitShiftMask!T; 162 auto mask = T(1) << shift; 163 static if (__traits(compiles, &p[size_t.init])) 164 { 165 auto qp = &p[index]; 166 auto q = *qp; 167 auto ret = q & mask; 168 *qp = cast(T)((q & ~mask) ^ (T(value) << shift)); 169 } 170 else 171 { 172 auto q = p[index]; 173 auto ret = q & mask; 174 p[index] = cast(T)((q & ~mask) ^ (T(value) << shift)); 175 } 176 return ret; 177 } 178 179 /++ 180 Tests and complements the bit. 181 Returns: 182 A non-zero value if the bit was set, and a zero if it was clear. 183 +/ 184 auto btc(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum) 185 if (__traits(isUnsigned, T)) 186 { 187 auto index = bitnum >> bitElemShift!T; 188 auto mask = T(1) << (bitnum & bitShiftMask!T); 189 static if (__traits(compiles, &p[size_t.init])) 190 { 191 auto qp = &p[index]; 192 auto q = *qp; 193 auto ret = q & mask; 194 *qp = cast(T)(q ^ mask); 195 } 196 else 197 { 198 auto q = p[index]; 199 auto ret = q & mask; 200 p[index] = cast(T)(q ^ mask); 201 } 202 return ret; 203 } 204 205 /++ 206 Tests and resets (sets to 0) the bit. 207 Returns: 208 A non-zero value if the bit was set, and a zero if it was clear. 209 +/ 210 auto btr(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum) 211 if (__traits(isUnsigned, T)) 212 { 213 auto index = bitnum >> bitElemShift!T; 214 auto mask = T(1) << (bitnum & bitShiftMask!T); 215 static if (__traits(compiles, &p[size_t.init])) 216 { 217 auto qp = &p[index]; 218 auto q = *qp; 219 auto ret = q & mask; 220 *qp = cast(T)(q & ~mask); 221 } 222 else 223 { 224 auto q = p[index]; 225 auto ret = q & mask; 226 p[index] = cast(T)(q & ~mask); 227 } 228 return ret; 229 } 230 231 /++ 232 Tests and sets the bit. 233 Params: 234 p = a non-NULL field / pointer to an array of unsigned integers. 235 bitnum = a bit number, starting with bit 0 of p[0], 236 and progressing. It addresses bits like the expression: 237 --- 238 p[index / (T.sizeof*8)] & (1 << (index & ((T.sizeof*8) - 1))) 239 --- 240 Returns: 241 A non-zero value if the bit was set, and a zero if it was clear. 242 +/ 243 auto bts(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum) 244 if (__traits(isUnsigned, T)) 245 { 246 auto index = bitnum >> bitElemShift!T; 247 auto mask = T(1) << (bitnum & bitShiftMask!T); 248 static if (__traits(compiles, &p[size_t.init])) 249 { 250 auto qp = &p[index]; 251 auto q = *qp; 252 auto ret = q & mask; 253 *qp = cast(T)(q | mask); 254 } 255 else 256 { 257 auto q = p[index]; 258 auto ret = q & mask; 259 p[index] = cast(T)(q | mask); 260 } 261 return ret; 262 } 263 264 /// 265 @system pure version(mir_core_test) unittest 266 { 267 size_t[2] array; 268 269 array[0] = 2; 270 array[1] = 0x100; 271 272 assert(btc(array.ptr, 35) == 0); 273 if (size_t.sizeof == 8) 274 { 275 assert(array[0] == 0x8_0000_0002); 276 assert(array[1] == 0x100); 277 } 278 else 279 { 280 assert(array[0] == 2); 281 assert(array[1] == 0x108); 282 } 283 284 assert(btc(array.ptr, 35)); 285 assert(array[0] == 2); 286 assert(array[1] == 0x100); 287 288 assert(bts(array.ptr, 35) == 0); 289 if (size_t.sizeof == 8) 290 { 291 assert(array[0] == 0x8_0000_0002); 292 assert(array[1] == 0x100); 293 } 294 else 295 { 296 assert(array[0] == 2); 297 assert(array[1] == 0x108); 298 } 299 300 assert(btr(array.ptr, 35)); 301 assert(array[0] == 2); 302 assert(array[1] == 0x100); 303 } 304 305 /// The 'ctpop' family of intrinsics counts the number of bits set in a value. 306 T ctpop(T)(in T src) 307 if (__traits(isUnsigned, T)) 308 { 309 version(LDC) if (!__ctfe) 310 return llvm_ctpop(src); 311 version(GNU) if (!__ctfe) 312 { 313 static if (T.sizeof < __builtin_clong.sizeof) 314 return cast(T) __builtin_popcount(src); 315 else static if (T.sizeof <= __builtin_clong.sizeof) 316 return cast(T) __builtin_popcountl(src); 317 else 318 return cast(T) __builtin_popcountll(src); 319 } 320 import core.bitop: popcnt; 321 return cast(T) popcnt(src); 322 } 323 324 /++ 325 The 'ctlz' family of intrinsic functions counts the number of leading zeros in a variable. 326 Result is undefined if the argument is zero. 327 +/ 328 T ctlz(T)(in T src) 329 if (__traits(isUnsigned, T)) 330 { 331 version(LDC) if (!__ctfe) 332 return llvm_ctlz(src, true); 333 version(GNU) if (!__ctfe) 334 { 335 // Do not zero-extend when counting leading zeroes. 336 static if (T.sizeof < __builtin_clong.sizeof && T.sizeof >= uint.sizeof) 337 return cast(T) __builtin_clz(src); 338 else static if (T.sizeof == __builtin_clong.sizeof) 339 return cast(T) __builtin_clzl(src); 340 else static if (T.sizeof > __builtin_clong.sizeof) 341 return cast(T) __builtin_clzll(src); 342 } 343 import core.bitop: bsr; 344 return cast(T)(T.sizeof * 8 - 1 - bsr(src)); 345 } 346 347 /// 348 version (mir_core_test) @nogc nothrow pure @safe version(mir_core_test) unittest 349 { 350 assert(ctlz(cast(ubyte) 0b0011_1111) == 2); 351 assert(ctlz(cast(ushort) 0b0000_0001_1111_1111) == 7); 352 } 353 354 /++ 355 The 'ctlzp' family of intrinsic functions counts the number of leading zeros in a variable. 356 Result is properly defined if the argument is zero. 357 +/ 358 T ctlzp(T)(in T src) 359 if (__traits(isUnsigned, T)) 360 { 361 version(LDC) if (!__ctfe) 362 return llvm_ctlz(src, false); 363 return src ? ctlz(src) : T.sizeof * 8; 364 } 365 366 /// 367 version (mir_core_test) @nogc nothrow pure @safe version(mir_core_test) unittest 368 { 369 assert(ctlzp(cast(ubyte) 0b0000_0000) == 8); 370 assert(ctlzp(cast(ubyte) 0b0011_1111) == 2); 371 assert(ctlzp(cast(ushort) 0b0000_0001_1111_1111) == 7); 372 assert(ctlzp(cast(ushort) 0) == 16); 373 assert(ctlzp(cast(ulong) 0) == 64); 374 } 375 376 /++ 377 The 'cttz' family of intrinsic functions counts the number of trailing zeros. 378 Result is undefined if the argument is zero. 379 +/ 380 T cttz(T)(in T src) 381 if (__traits(isUnsigned, T)) 382 { 383 version(LDC) if (!__ctfe) 384 return llvm_cttz(src, true); 385 version(GNU) if (!__ctfe) 386 { 387 static if (T.sizeof <__builtin_clong.sizeof) 388 return cast(T) __builtin_ctz(src); 389 else static if (T.sizeof <=__builtin_clong.sizeof) 390 return cast(T) __builtin_ctzl(src); 391 else 392 return cast(T) __builtin_ctzll(src); 393 } 394 import core.bitop: bsf; 395 return cast(T) bsf(src); 396 } 397 398 /// 399 version (mir_core_test) @nogc nothrow pure @safe version(mir_core_test) unittest 400 { 401 assert(cttzp(cast(ubyte) 0b11111100) == 2); 402 assert(cttzp(cast(ushort) 0b1111111110000000) == 7); 403 } 404 405 /++ 406 The 'cttz' family of intrinsic functions counts the number of trailing zeros. 407 Result is properly defined if the argument is zero. 408 +/ 409 T cttzp(T)(in T src) 410 if (__traits(isUnsigned, T)) 411 { 412 version(LDC) if (!__ctfe) 413 return llvm_cttz(src, false); 414 return src ? cttz(src) : T.sizeof * 8; 415 } 416 417 /// 418 version (mir_core_test) @nogc nothrow pure @safe version(mir_core_test) unittest 419 { 420 assert(cttzp(cast(ubyte) 0b0000_0000) == 8); 421 assert(cttzp(cast(ubyte) 0b11111100) == 2); 422 assert(cttzp(cast(ushort) 0b1111111110000000) == 7); 423 assert(cttzp(cast(ushort) 0) == 16); 424 assert(cttzp(cast(ulong) 0) == 64); 425 }