1 /** Bitarray.
2  */
3 module nxt.container.bitarray;
4 
5 @safe:
6 
7 /** Array of bits.
8  *
9  * Like `std.bitmanip.BitArray` but pure nothrow @safe @nogc.
10  *
11  * Set `blockAlignedLength` to true if `this.length` is always a multiple of
12  * `Block.size`.
13  *
14  * TODO: use `Flag` instead, or wrap in `BlockAlignedBitArray` where this class
15  * is made private _BitArray and alias BitArray = _BitArray!(true).
16  *
17  * TODO: support append bit via `pushBack(bool)`.
18  */
19 struct BitArray(bool blockAlignedLength = false,
20 				alias Allocator = null) /* TODO: use Allocator */
21 {
22 	import core.bitop : bt, bts, btr;
23 	import nxt.bitarray_algorithm;
24 
25 	pure nothrow @safe @nogc:
26 
27 	/** Helper constructor for `length` number of bits. */
28 	private this(size_t length) @trusted
29 	in {
30 		static if (blockAlignedLength)
31 			assert(length % bitsPerBlock == 0,
32 				   "Parameter `length` is not a multiple `Block` bit size " ~ bitsPerBlock.stringof);
33 	} do {
34 		static if (blockAlignedLength)
35 			_blockCount = length / bitsPerBlock; // number of whole blocks
36 		else {
37 			_blockCount = (length + bitsPerBlock-1) / bitsPerBlock;
38 			_length = length;
39 		}
40 		_blockPtr = cast(Block*)fakePureCalloc(bitsPerBlock, _blockCount); /* TODO: use `Allocator` */
41 	}
42 
43 	/** Helper constructor. */
44 	private this(size_t length, const scope Block[] blocks) @trusted {
45 		_blockCount = blocks.length;
46 		_blockPtr = cast(Block*)fakePureMalloc(bitsPerBlock * _blockCount); /* TODO: use `Allocator` */
47 		_blocks[] = blocks; // copy block array
48 		static if (!blockAlignedLength) {
49 			_length = length;
50 		}
51 	}
52 
53 	/** Construct with `length` number of zero bits stored in `blocks`. */
54 	private static typeof(this) withLengthAndBlocks(size_t length, const scope Block[] blocks)
55 		=> typeof(return)(length, blocks);
56 
57 	/// Destroy.
58 	~this() nothrow @nogc => release();
59 
60 	/// Explicit copying (duplicate).
61 	typeof(this) dup() => typeof(this).withLengthAndBlocks(_length, _blocks);
62 
63 	/// Empty.
64 	void clear() {
65 		release();
66 		resetInternalData();
67 	}
68 
69 	/// Release internal store.
70 	private void release() @trusted @nogc => fakePureFree(_blockPtr);
71 
72 	/// Reset internal data.
73 	private void resetInternalData() {
74 		_blockPtr = null;
75 		_blockCount = 0;
76 		static if (!blockAlignedLength)
77 			_length = 0;
78 	}
79 
80 	/// Set length.
81 	@property void length(size_t newLength) {
82 		if (newLength == length)
83 			return;
84 		auto that = typeof(this)(newLength);
85 		that._blocks[0 .. _blocks.length] = this._blocks;
86 		import std.algorithm.mutation : move;
87 		this = move(that);
88 	}
89 
90 	/// Get length.
91 	@property size_t length() const => _length;
92 	alias opDollar = length;	/// ditto
93 
94 	/// Get capacity in number of bits.
95 	@property size_t capacity() const => bitsPerBlock*_blockCount;
96 
97 	/** Get the `i`'th bit. */
98 	bool opIndex(size_t i) const @trusted {
99 		version (D_Coverage) {} else pragma(inline, true);
100 		assert(i < length);		/* TODO: nothrow or not? */
101 		return cast(bool)bt(_blockPtr, i);
102 	}
103 
104 	/** Set the `i`'th bit to `value`. */
105 	bool opIndexAssign(bool value, size_t i) @trusted {
106 		version (D_Coverage) {} else pragma(inline, true);
107 		if (value)
108 			bts(_blockPtr, i);
109 		else
110 			btr(_blockPtr, i);
111 		return value;
112 	}
113 
114 	/** Set all bits to `value` via slice assignment syntax. */
115 	ref typeof(this) opSliceAssign(bool value) {
116 		if (value)
117 			one();
118 		else
119 			zero();
120 		return this;
121 	}
122 
123 	/** Clear all bits (to zero). */
124 	private void zero() {
125 		foreach (ref block; _blocks)
126 			block = Block.min;
127 	}
128 
129 	/** Set all bits (to one). */
130 	private void one() {
131 		foreach (ref block; _blocks)
132 			block = Block.max;
133 	}
134 
135 	version (none)			   /* TODO: activate? */
136 	bool opCast(T : bool)() const => !this.allZero;
137 
138 	/** Check if `this` has only zeros. */
139 	bool allZero()() const pure nothrow @safe @nogc {
140 		foreach (const block; _fullBlocks)
141 			if (block != Block.min)
142 				return false;
143 		static if (!blockAlignedLength)
144 			if (_restBlockZeroPadded != Block.min)
145 				return false;
146 		return true;
147 	}
148 
149 	/** Check if `this` has only ones. */
150 	bool allOne()() const pure nothrow @safe @nogc {
151 		foreach (const block; _fullBlocks)
152 			if (block != Block.max)
153 				return false;
154 		static if (!blockAlignedLength)
155 			if (_restBlockOnePadded != Block.max)
156 				return false;
157 		return true;
158 	}
159 
160 	/** Get number of bits set (to one). */
161 	size_t countOnes()() const /*tlm*/
162 		=> nxt.bitarray_algorithm.countOnes!(const(Block)[], blockAlignedLength)(_blocks, length);
163 
164 	/** Get number of bits cleared (to zero). */
165 	size_t countZeros()() const /*tlm*/
166 		=> length - countOnes;
167 
168 	/** Find index of first set (one) bit or `length` if no bit set.
169 	 *
170 	 * Optimized for ones-sparsity.
171 	 */
172 	size_t indexOfFirstOne()() const /*tlm*/
173 		=> nxt.bitarray_algorithm.indexOfFirstOne!(const(Block)[], blockAlignedLength)(_blocks, length);
174 
175 	/** Find index of first cleared (zero) bit or `length` if no bit cleared.
176 	 *
177 	 * Optimized for zeros-sparsity.
178 	 */
179 	size_t indexOfFirstZero()() const /*tlm*/
180 		=> nxt.bitarray_algorithm.indexOfFirstZero!(const(Block)[], blockAlignedLength)(_blocks, length);
181 
182 	/** Equality, operators == and !=. */
183 	bool opEquals(const scope ref typeof(this) rhs) const @trusted /* TODO: use `in ref` when it compiles */
184 	{
185 		static if (!blockAlignedLength)
186 			if (length != rhs.length)
187 				return false;
188 		if (_fullBlocks != rhs._fullBlocks)
189 			return false;
190 		static if (!blockAlignedLength) {
191 			const restBitCount = length % bitsPerBlock;
192 			if (restBitCount)
193 				return _restBlockZeroPadded == rhs._restBlockZeroPadded;
194 		}
195 		return true;
196 	}
197 
198 	/** Only explicit copying via `.dup` for now. */
199 	this(this) @disable;
200 
201 private:
202 
203 	/** Get all blocks including the last being potentially not fully occupied. */
204 	inout(Block)[] _blocks() inout @trusted => _blockPtr[0 .. _blockCount];
205 
206 	/** Get Blocks where all the bits are used (not ignored). */
207 	static if (blockAlignedLength)
208 		inout(Block)[] _fullBlocks() inout @trusted => _blocks;	 // all bits of all blocks are used
209 	else {
210 		inout(Block)[] _fullBlocks() inout @trusted => _blocks.ptr[0 .. (length / bitsPerBlock)];
211 		/** Return rest `Block` with all padding bits set to zero. */
212 		Block _restBlockZeroPadded() const @trusted => _blocks[$-1] & ((1UL << (length % bitsPerBlock)) - 1);
213 	}
214 
215 	alias Block = size_t;
216 	enum bitsPerBlock = 8*Block.sizeof; /// Number of bits per `Block`.
217 
218 	/** Number of Block's allocated at `_blockPtr`. */
219 	size_t _blockCount;
220 
221 	static if (is(Allocator == std.experimental.allocator.gc_allocator.GCAllocator))
222 		Block* _blockPtr;	   // GC-allocated store pointer
223 	else {
224 		import nxt.gc_traits : NoGc;
225 		@NoGc Block* _blockPtr; // non-GC-allocated store pointer
226 	}
227 
228 	static if (blockAlignedLength)
229 		@property size_t _length() const => bitsPerBlock * _blockCount;
230 	else
231 		size_t _length;
232 }
233 
234 /// Test `_blockCount` and `_fullBlocks.length`.
235 pure nothrow @safe @nogc unittest {
236 	static void test(bool blockAlignedLength)() {
237 		import nxt.construction : makeOfLength;
238 		alias BA = BitArray!(blockAlignedLength);
239 
240 		assert(makeOfLength!BA(0)._blockCount == 0);
241 		assert(makeOfLength!BA(1)._blockCount == 1);
242 
243 		{
244 			auto a = makeOfLength!BA(1*BA.bitsPerBlock - 1);
245 			assert(a._blockCount == 1);
246 			assert(a._fullBlocks.length == 0);
247 		}
248 
249 		{
250 			auto a = makeOfLength!BA(1*BA.bitsPerBlock + 0);
251 			assert(a._blockCount == 1);
252 			assert(a._fullBlocks.length == 1);
253 		}
254 
255 		{
256 			auto a = makeOfLength!BA(1*BA.bitsPerBlock + 1);
257 			assert(a._blockCount == 2);
258 			assert(a._fullBlocks.length == 1);
259 		}
260 
261 		{
262 			auto a = makeOfLength!BA(2*BA.bitsPerBlock - 1);
263 			assert(a._blockCount == 2);
264 			assert(a._fullBlocks.length == 1);
265 		}
266 
267 		{
268 			auto a = makeOfLength!BA(2*BA.bitsPerBlock + 0);
269 			assert(a._blockCount == 2);
270 			assert(a._fullBlocks.length == 2);
271 		}
272 
273 		{
274 			auto a = makeOfLength!BA(2*BA.bitsPerBlock + 1);
275 			assert(a._blockCount == 3);
276 			assert(a._fullBlocks.length == 2);
277 		}
278 	}
279 	test!(false)();
280 }
281 
282 /// Test indexing and element assignment.
283 pure nothrow @safe @nogc unittest {
284 	static void test(bool blockAlignedLength)(size_t length) {
285 		alias BA = BitArray!(blockAlignedLength);
286 
287 		auto a = makeOfLength!BA(length);
288 
289 		assert(a.length == length);
290 		foreach (const i; 0 .. length)
291 			assert(!a[i]);
292 
293 		a[0] = true;
294 		assert(a[0]);
295 		foreach (const i; 1 .. length)
296 			assert(!a[i]);
297 
298 		assert(!a[1]);
299 		a[1] = true;
300 		assert(a[1]);
301 		a[1] = false;
302 		assert(!a[1]);
303 	}
304 	test!(false)(100);
305 	test!(true)(64);
306 }
307 
308 /// Test `countOnes` and `countZeros`.
309 pure nothrow @safe @nogc unittest {
310 	static void test(bool blockAlignedLength)() {
311 		alias BA = BitArray!(blockAlignedLength);
312 
313 		foreach (const n; 1 .. 5*BA.bitsPerBlock) {
314 			static if (blockAlignedLength)
315 				if (n % BA.bitsPerBlock != 0) // if block aligned length
316 					continue;
317 
318 			auto a = makeOfLength!BA(n);
319 
320 			// set bits forwards
321 			foreach (const i; 0 .. n) {
322 				assert(a.countOnes == i);
323 				assert(a.countZeros == n - i);
324 				a[i] = true;
325 				assert(a.countOnes == i + 1);
326 				assert(a.countZeros == n - (i + 1));
327 			}
328 
329 			assert(a.countOnes == n);
330 			assert(a.countZeros == 0);
331 
332 			auto b = a.dup;
333 			assert(b.countOnes == n);
334 			assert(b.countZeros == 0);
335 
336 			assert(a == b);
337 
338 			// clear `a` bits forwards
339 			foreach (const i; 0 .. n) {
340 				assert(a.countOnes == n - i);
341 				assert(a.countZeros == i);
342 				a[i] = false;
343 				assert(a.countOnes == n - (i + 1));
344 				assert(a.countZeros == i + 1);
345 			}
346 
347 			b[] = false;
348 			assert(a == b);
349 
350 			// set bits backwards
351 			foreach (const i; 0 .. n) {
352 				assert(a.countOnes == i);
353 				a[n-1 - i] = true;
354 				assert(a.countOnes == i + 1);
355 			}
356 
357 			b[] = true;
358 			assert(a == b);
359 		}
360 	}
361 	test!(false)();
362 	test!(true)();
363 }
364 
365 /// Test emptying (resetting) via `.clear` and explicit copying with `.dup`.
366 pure nothrow @safe @nogc unittest {
367 	static void test(bool blockAlignedLength)() {
368 		alias BA = BitArray!(blockAlignedLength);
369 
370 		static if (blockAlignedLength)
371 			const n = 5 * BA.bitsPerBlock;
372 		else
373 			const n = 5 * BA.bitsPerBlock + 1;
374 		auto a = makeOfLength!BA(n);
375 
376 		assert(a.length == n);
377 
378 		a.clear();
379 		assert(a.length == 0);
380 
381 		a = makeOfLength!BA(n);
382 		assert(a.length == n);
383 
384 		auto b = a.dup;
385 		assert(b.length == n);
386 
387 		a.clear();
388 		assert(a.length == 0);
389 	}
390 	test!(false)();
391 	test!(true)();
392 }
393 
394 /// Test `indexOfFirstOne` for single set ones.
395 pure nothrow @safe @nogc unittest {
396 	static void test(bool blockAlignedLength)() {
397 		alias BA = BitArray!(blockAlignedLength);
398 
399 		static if (blockAlignedLength)
400 			const n = 2 * BA.bitsPerBlock;
401 		else
402 			const n = 2 * BA.bitsPerBlock + 1;
403 		auto a = makeOfLength!BA(n);
404 
405 		assert(a.length == n);
406 		assert(a.indexOfFirstOne == n); // miss
407 
408 		a[0] = true;
409 		assert(a.indexOfFirstOne == 0);
410 		a[] = false;
411 
412 		a[2] = true;
413 		assert(a.indexOfFirstOne == 2);
414 		a[] = false;
415 
416 		a[n/2-1] = true;
417 		assert(a.indexOfFirstOne == n/2-1);
418 		a[] = false;
419 
420 		a[n/2] = true;
421 		assert(a.indexOfFirstOne == n/2);
422 		a[] = false;
423 
424 		a[n/2+1] = true;
425 		assert(a.indexOfFirstOne == n/2+1);
426 		a[] = false;
427 
428 		a[n-1] = true;
429 		assert(a.indexOfFirstOne == n-1);
430 		a[] = false;
431 
432 		assert(a.indexOfFirstOne == n); // miss
433 	}
434 	test!(false)();
435 	test!(true)();
436 }
437 
438 /// Test `indexOfFirstOne` for multi set ones.
439 pure nothrow @safe @nogc unittest {
440 	static void test(bool blockAlignedLength)() {
441 		alias BA = BitArray!(blockAlignedLength);
442 
443 		static if (blockAlignedLength)
444 			const n = 2 * BA.bitsPerBlock;
445 		else
446 			const n = 2 * BA.bitsPerBlock + 1;
447 		auto a = makeOfLength!BA(n);
448 
449 		a[0] = true;
450 		a[BA.bitsPerBlock/2] = true;
451 		a[BA.bitsPerBlock - 1] = true;
452 		assert(a.indexOfFirstOne == 0);
453 	}
454 	test!(false)();
455 	test!(true)();
456 }
457 
458 /// Test `indexOfFirstZero` for single set zeros.
459 pure nothrow @safe @nogc unittest {
460 	static void test(bool blockAlignedLength)() {
461 		alias BA = BitArray!(blockAlignedLength);
462 
463 		static if (blockAlignedLength)
464 			const n = 2 * BA.bitsPerBlock;
465 		else
466 			const n = 2 * BA.bitsPerBlock + 1;
467 		auto a = makeOfLength!BA(n);
468 
469 		a[] = true;
470 
471 		assert(a.length == n);
472 		assert(a.indexOfFirstZero == n); // miss
473 
474 		a[0] = false;
475 		assert(a.indexOfFirstZero == 0);
476 		a[0] = true;
477 
478 		a[2] = false;
479 		assert(a.indexOfFirstZero == 2);
480 		a[2] = true;
481 
482 		a[n/2-1] = false;
483 		assert(a.indexOfFirstZero == n/2-1);
484 		a[n/2-1] = true;
485 
486 		a[n/2] = false;
487 		assert(a.indexOfFirstZero == n/2);
488 		a[n/2] = true;
489 
490 		a[n/2+1] = false;
491 		assert(a.indexOfFirstZero == n/2+1);
492 		a[n/2+1] = true;
493 
494 		a[n-1] = false;
495 		assert(a.indexOfFirstZero == n-1);
496 		a[n-1] = true;
497 
498 		assert(a.indexOfFirstZero == n); // miss
499 	}
500 	test!(false)();
501 	test!(true)();
502 }
503 
504 pure nothrow @safe @nogc unittest {
505 	alias BA = BitArray!(false);
506 	static assert(BitArray!(false).sizeof == 3*BA.Block.sizeof); // one extra word for `length`
507 	static assert(BitArray!(true).sizeof == 2*BA.Block.sizeof);
508 }
509 
510 /// Test `indexOfFirstZero` for multi set zeros.
511 pure nothrow @safe @nogc unittest {
512 	static void test(bool blockAlignedLength)() {
513 		alias BA = BitArray!(blockAlignedLength);
514 
515 		static if (blockAlignedLength)
516 			const n = 2 * BA.bitsPerBlock;
517 		else
518 			const n = 2 * BA.bitsPerBlock + 1;
519 		auto a = makeOfLength!BA(n);
520 
521 		a[] = true;
522 
523 		a[0] = false;
524 		a[BA.bitsPerBlock/2] = false;
525 		a[BA.bitsPerBlock - 1] = false;
526 		assert(a.indexOfFirstZero == 0);
527 	}
528 	test!(false)();
529 	test!(true)();
530 }
531 
532 /// Test `indexOfFirstOne` for multi set ones.
533 pure nothrow @safe @nogc unittest {
534 	static void test(bool blockAlignedLength)() {
535 		alias BA = BitArray!(blockAlignedLength);
536 
537 		static if (blockAlignedLength)
538 			const n = 2 * BA.bitsPerBlock;
539 		else
540 			const n = 2 * BA.bitsPerBlock + 1;
541 		auto a = makeOfLength!BA(n);
542 
543 		a[] = false;
544 
545 		a[0] = true;
546 		a[BA.bitsPerBlock/2] = true;
547 		a[BA.bitsPerBlock - 1] = true;
548 		assert(a.indexOfFirstOne == 0);
549 	}
550 	test!(false)();
551 	test!(true)();
552 }
553 
554 /// Test casting to `bool`.
555 pure nothrow @safe @nogc unittest {
556 	static void test(bool blockAlignedLength)() {
557 		alias BA = BitArray!(blockAlignedLength);
558 
559 		static if (blockAlignedLength)
560 			const n = 2 * BA.bitsPerBlock;
561 		else
562 			const n = 2 * BA.bitsPerBlock + 1;
563 		auto a = makeOfLength!BA(n);
564 
565 		assert(a.allZero);
566 
567 		a[0] = true;
568 		assert(!a.allZero);
569 		a[0] = false;
570 		assert(a.allZero);
571 	}
572 	test!(false)();
573 }
574 
575 ///
576 @trusted pure unittest {
577 	import std.exception: assertThrown;
578 	import core.exception : AssertError;
579 	alias BA = BitArray!(true);
580 	assertThrown!AssertError(makeOfLength!BA(1));
581 }
582 
583 extern (C) private pure @system @nogc nothrow {
584 	pragma(mangle, "malloc") void* fakePureMalloc(size_t);
585 	pragma(mangle, "calloc") void* fakePureCalloc(size_t nmemb, size_t size);
586 	pragma(mangle, "realloc") void* fakePureRealloc(void* ptr, size_t size);
587 	pragma(mangle, "free") void fakePureFree(void* ptr);
588 }
589 
590 version (unittest) {
591 	import nxt.construction : makeOfLength;
592 }