1 /** 2 * Static bit array container for internal usage. 3 */ 4 module simple_static_bitarray; 5 6 @safe: 7 8 alias DefaultBlock = size_t; ///< Default block type. 9 10 struct StaticBitArray(uint capacity) 11 { 12 @safe pure nothrow @nogc: 13 import core.bitop : bt, bts, btr; 14 15 /** Number of bits. */ 16 enum length = capacity; 17 18 alias Block = DefaultBlock; ///< Block type. 19 20 /** Number of bits per `Block`. */ 21 enum bitsPerBlock = 8*Block.sizeof; 22 23 /** Number of blocks of type `Block`. */ 24 enum blockCount = (capacity + (bitsPerBlock-1)) / bitsPerBlock; 25 26 /** Reset all bits (to zero). */ 27 void reset() 28 { 29 version(D_Coverage) {} else pragma(inline, true); 30 _blocks[] = 0; // TODO is this the fastest way? 31 } 32 33 /** Gets the $(D idx)'th bit. */ 34 bool opIndex(size_t idx) const @trusted 35 in 36 { 37 assert(idx < length); // TODO nothrow or not? 38 } 39 body 40 { 41 version(D_Coverage) {} else pragma(inline, true); 42 return cast(bool)bt(_blocks.ptr, idx); 43 } 44 45 /** Sets the $(D idx)'th bit. */ 46 bool opIndexAssign(bool b, size_t idx) @trusted 47 in 48 { 49 assert(idx < length); // TODO nothrow or not? 50 } 51 body 52 { 53 version(D_Coverage) {} else pragma(inline, true); 54 if (b) 55 { 56 bts(_blocks.ptr, cast(size_t)idx); 57 } 58 else 59 { 60 btr(_blocks.ptr, cast(size_t)idx); 61 } 62 return b; 63 } 64 65 /** Find index of first cleared (zero) bit or `typeof(return).max` if no bit set. 66 * 67 * Optimized for zeros-sparsity. 68 */ 69 size_t indexOfFirstZero()() const 70 { 71 import nxt.bitarray_algorithm; 72 enum bool blockAlignedLength = capacity % (8*Block.sizeof) == 0; 73 return nxt.bitarray_algorithm.indexOfFirstZero!(const(Block)[blockCount], 74 blockAlignedLength)(_blocks, length); 75 } 76 77 /** Find index of first set (one) bit or `typeof(return).max` if no bit set. 78 * 79 * Optimized for ones-sparsity. 80 */ 81 size_t indexOfFirstOne()() const 82 { 83 import nxt.bitarray_algorithm; 84 enum bool blockAlignedLength = capacity % (8*Block.sizeof) == 0; 85 return nxt.bitarray_algorithm.indexOfFirstOne!(const(Block)[blockCount], 86 blockAlignedLength)(_blocks, length); 87 } 88 89 private Block[blockCount] _blocks; 90 } 91 92 /// 93 @trusted pure unittest 94 { 95 enum blockCount = 2; 96 enum length = blockCount * 8*DefaultBlock.sizeof - 1; // 2 blocks minus one 97 98 StaticBitArray!(length) x; 99 static assert(x.blockCount == blockCount); 100 101 // import std.exception: assertThrown; 102 // import core.exception : AssertError; 103 // assertThrown!AssertError(x[length] = false); 104 105 x[length/2 - 1] = true; 106 assert(x[length/2 - 1]); 107 108 x[length/2 - 1] = false; 109 assert(!x[length/2 - 1]); 110 111 x[length - 1] = true; 112 assert(x[length - 1]); 113 114 x[length - 1] = false; 115 assert(!x[length - 1]); 116 } 117 118 /// Test `indexOfFirstZero` for multi set zeros. 119 @safe pure nothrow @nogc unittest 120 { 121 static void test(bool blockAlignedLength)() 122 { 123 static if (blockAlignedLength) 124 { 125 const n = 2 * 8*DefaultBlock.sizeof; 126 } 127 else 128 { 129 const n = 2 * 8*DefaultBlock.sizeof + 1; 130 } 131 alias BA = StaticBitArray!(n); 132 133 auto a = BA(); 134 135 a[0] = false; 136 a[BA.bitsPerBlock/2] = false; 137 a[BA.bitsPerBlock - 1] = false; 138 assert(a.indexOfFirstZero == 0); 139 } 140 test!(false)(); 141 test!(true)(); 142 } 143 144 /// Test `indexOfFirstOne` for multi set ones. 145 @safe pure nothrow @nogc unittest 146 { 147 static void test(bool blockAlignedLength)() 148 { 149 static if (blockAlignedLength) 150 { 151 const n = 2 * 8*Block.sizeof; 152 } 153 else 154 { 155 const n = 2 * 8*DefaultBlock.sizeof + 1; 156 } 157 alias BA = StaticBitArray!(n); 158 159 auto a = BA(); 160 161 a[0] = true; 162 a[BA.bitsPerBlock/2] = true; 163 a[BA.bitsPerBlock - 1] = true; 164 assert(a.indexOfFirstOne == 0); 165 } 166 test!(false)(); 167 }