1 /** Algorithms for dynamic and static bitarrays based with integral blocks. 2 * 3 * Algorithms are qualified as `package` because they should only be used by 4 * bit-array containers. 5 */ 6 module nxt.bitarray_algorithm; 7 8 pure nothrow @safe @nogc: 9 10 size_t countOnes(Blocks, bool blockAlignedLength = false)(const scope auto ref Blocks blocks, size_t length) @trusted 11 if (isBlocks!Blocks) { 12 alias Block = typeof(Blocks.init[0]); 13 enum bitsPerBlock = 8*Block.sizeof; 14 15 typeof(return) result = 0; 16 17 import core.bitop : popcnt; 18 const fullBlockCount = length / bitsPerBlock; 19 foreach (const block; blocks.ptr[0 .. fullBlockCount]) { 20 result += cast(typeof(result))popcnt(block); 21 } 22 23 // dbg("bitsPerBlock:", bitsPerBlock, 24 // " length:", length, 25 // " fullBlockCount:", fullBlockCount);, 26 27 static if (!blockAlignedLength) { 28 const restBitCount = length % bitsPerBlock; 29 if (restBitCount) { 30 result += cast(typeof(result))popcnt(blocks.ptr[fullBlockCount] & ((1UL << restBitCount)-1)); 31 } 32 33 // dbg(" restBitCount:", restBitCount); 34 } 35 36 return typeof(return)(result); 37 } 38 39 /// Test `countOnes` with full blocks. 40 pure nothrow @safe @nogc unittest { 41 alias Block = size_t; 42 enum bitsPerBlock = 8*Block.sizeof; 43 44 enum blockCount = 3; 45 Block[blockCount] x; 46 const length = 8*x.sizeof; 47 assert(countOnes(x, length) == 0); 48 49 x[0] = 1; 50 assert(countOnes(x, length) == 1); 51 52 x[0] = 1+2; 53 assert(countOnes(x, length) == 2); 54 55 x[0] = 1+2; 56 x[1] = 1+2; 57 assert(countOnes(x, length) == 4); 58 59 x[0] = 1+2; 60 x[1] = 1+2; 61 x[2] = 1+2; 62 assert(countOnes(x, length) == 6); 63 } 64 65 /// Test `countOnes` with partial blocks. 66 pure nothrow @safe @nogc unittest { 67 alias Block = size_t; 68 static void test(uint blockCount)() { 69 Block[blockCount] x; 70 foreach (const length; 1 .. 8*x.sizeof) { 71 assert(countOnes(x, length) == 0); 72 73 x[0] |= 1UL << (length-1); 74 assert(countOnes(x, length) == 1); 75 x[0] = 0; 76 } 77 } 78 test!1(); 79 test!2(); 80 test!3(); 81 } 82 83 /** Find index of first cleared (zero) bit or `length` if no bit cleared. 84 * 85 * Optimized for zeros-sparsity. 86 */ 87 size_t indexOfFirstZero(Blocks, bool blockAlignedLength = false)(const scope auto ref Blocks blocks, size_t length) 88 if (isBlocks!Blocks) { 89 static if (blockAlignedLength) { 90 foreach (const blockIndex, block; blocks) { 91 if (block != block.max) // optimize for zeros-sparsity 92 { 93 import core.bitop : bsf; 94 const hitIndex = blockIndex*8*block.sizeof + bsf(~block); /+ TODO: is there a builtin for `bsf(~block)`? +/ 95 return hitIndex < length ? hitIndex : length; // if hit beyond end miss 96 } 97 } 98 } 99 else 100 { 101 /+ TODO: handle blocks with garbage in the rest block +/ 102 foreach (const blockIndex, block; blocks) { 103 if (block != block.max) // optimize for zeros-sparsity 104 { 105 import core.bitop : bsf; 106 const hitIndex = blockIndex*8*block.sizeof + bsf(~block); /+ TODO: is there a builtin for `bsf(~block)`? +/ 107 return hitIndex < length ? hitIndex : length; // if hit beyond end miss 108 } 109 } 110 } 111 return length; // miss 112 } 113 114 /** Find index of first set (one) bit or `length` if no bit cleared. 115 * 116 * Optimized for ones-sparsity. 117 */ 118 size_t indexOfFirstOne(Blocks, bool blockAlignedLength = false)(const scope auto ref Blocks blocks, size_t length) 119 if (isBlocks!Blocks) { 120 static if (blockAlignedLength) { 121 foreach (const blockIndex, const block; blocks) { 122 if (block != block.min) // optimize for ones-sparsity 123 { 124 import core.bitop : bsf; 125 const hitIndex = blockIndex*8*block.sizeof + bsf(block); 126 return hitIndex < length ? hitIndex : length; // if hit beyond end miss 127 } 128 } 129 } 130 else 131 { 132 /+ TODO: handle blocks with garbage in the rest block +/ 133 foreach (const blockIndex, const block; blocks) { 134 if (block != block.min) // optimize for ones-sparsity 135 { 136 import core.bitop : bsf; 137 const hitIndex = blockIndex*8*block.sizeof + bsf(block); 138 return hitIndex < length ? hitIndex : length; // if hit beyond end miss 139 } 140 } 141 } 142 return length; // miss 143 } 144 145 private enum isBlocks(Blocks) = (is(typeof(Blocks.init[0]) : uint) || 146 is(typeof(Blocks.init[0]) : ulong));