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 @safe struct StaticBitArray(uint capacity)
11 {
12     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(idx < length)            // TODO: nothrow or not?
36     {
37         version(D_Coverage) {} else pragma(inline, true);
38         return cast(bool)bt(_blocks.ptr, idx);
39     }
40 
41     /** Sets the $(D idx)'th bit. */
42     bool opIndexAssign(bool b, size_t idx) @trusted
43     in(idx < length)            // TODO: nothrow or not?
44     {
45         version(D_Coverage) {} else pragma(inline, true);
46         if (b)
47             bts(_blocks.ptr, cast(size_t)idx);
48         else
49             btr(_blocks.ptr, cast(size_t)idx);
50         return b;
51     }
52 
53     /** Find index of first cleared (zero) bit or `typeof(return).max` if no bit set.
54      *
55      * Optimized for zeros-sparsity.
56      */
57     size_t indexOfFirstZero()() const
58     {
59         import nxt.bitarray_algorithm;
60         enum bool blockAlignedLength = capacity % (8*Block.sizeof) == 0;
61         return nxt.bitarray_algorithm.indexOfFirstZero!(const(Block)[blockCount],
62                                                         blockAlignedLength)(_blocks, length);
63     }
64 
65     /** Find index of first set (one) bit or `typeof(return).max` if no bit set.
66      *
67      * Optimized for ones-sparsity.
68      */
69     size_t indexOfFirstOne()() const
70     {
71         import nxt.bitarray_algorithm;
72         enum bool blockAlignedLength = capacity % (8*Block.sizeof) == 0;
73         return nxt.bitarray_algorithm.indexOfFirstOne!(const(Block)[blockCount],
74                                                        blockAlignedLength)(_blocks, length);
75     }
76 
77     private Block[blockCount] _blocks;
78 }
79 
80 ///
81 @trusted pure unittest
82 {
83     enum blockCount = 2;
84     enum length = blockCount * 8*DefaultBlock.sizeof - 1; // 2 blocks minus one
85 
86     StaticBitArray!(length) x;
87     static assert(x.blockCount == blockCount);
88 
89     // import std.exception: assertThrown;
90     // import core.exception : AssertError;
91     // assertThrown!AssertError(x[length] = false);
92 
93     x[length/2 - 1] = true;
94     assert(x[length/2 - 1]);
95 
96     x[length/2 - 1] = false;
97     assert(!x[length/2 - 1]);
98 
99     x[length - 1] = true;
100     assert(x[length - 1]);
101 
102     x[length - 1] = false;
103     assert(!x[length - 1]);
104 }
105 
106 /// Test `indexOfFirstZero` for multi set zeros.
107 @safe pure nothrow @nogc unittest
108 {
109     static void test(bool blockAlignedLength)()
110     {
111         static if (blockAlignedLength)
112             const n = 2 * 8*DefaultBlock.sizeof;
113         else
114             const n = 2 * 8*DefaultBlock.sizeof + 1;
115         alias BA = StaticBitArray!(n);
116 
117         auto a = BA();
118 
119         a[0] = false;
120         a[BA.bitsPerBlock/2] = false;
121         a[BA.bitsPerBlock - 1] = false;
122         assert(a.indexOfFirstZero == 0);
123     }
124     test!(false)();
125     test!(true)();
126 }
127 
128 /// Test `indexOfFirstOne` for multi set ones.
129 @safe pure nothrow @nogc unittest
130 {
131     static void test(bool blockAlignedLength)()
132     {
133         static if (blockAlignedLength)
134             const n = 2 * 8*Block.sizeof;
135         else
136             const n = 2 * 8*DefaultBlock.sizeof + 1;
137         alias BA = StaticBitArray!(n);
138 
139         auto a = BA();
140 
141         a[0] = true;
142         a[BA.bitsPerBlock/2] = true;
143         a[BA.bitsPerBlock - 1] = true;
144         assert(a.indexOfFirstOne == 0);
145     }
146     test!(false)();
147 }