1 /**
2 License: public domain
3 Authors: Simon Bürger
4 */
5 
6 module jive.bitarray;
7 
8 private import jive.internal;
9 private import core.bitop;
10 private import std.algorithm : max;
11 
12 /**
13  *  Efficient version of Array!bool using only one bit per entry.
14  *  But note that the interface is not compatible with Array!bool. In
15  *  particular no ranges are provided. This is a design choice because using
16  *  a bit-range in general algorithms is often very inefficient. In contrast
17  *  specialized algorithms working on BitArray are typically very fast.
18  */
19 struct BitArray
20 {
21 	@nogc: nothrow: pure: @safe:
22 
23 	alias limb = size_t;
24 	enum limbBits = limb.sizeof*8;
25 
26 	private limb* _ptr;			// unused bits are always 0
27 	private size_t _capacity;	// allocated limbs (not bits)
28 	private size_t _length;
29 
30 	/** constructor for given length */
31 	this(size_t size)
32 	{
33 		_length = size;
34 		_capacity = limbCount;
35 		_ptr = jiveMalloc!limb(limbCount);
36 		reset();
37 	}
38 
39 	/** post-blit that does a full copy */
40 	this(this) @trusted
41 	{
42 		auto newPtr = jiveMalloc!limb(limbCount);
43 		newPtr[0 .. limbCount] = _ptr[0 .. limbCount];
44 		_capacity = limbCount;
45 		_ptr = newPtr;
46 	}
47 
48 	/** destructor */
49 	~this()
50 	{
51 		jiveFree(_ptr);
52 		_ptr = null;
53 	}
54 
55 	/** check for emptiness */
56 	bool empty() const @property
57 	{
58 		return _length == 0;
59 	}
60 
61 	/** number of elements */
62 	size_t length() const @property
63 	{
64 		return _length;
65 	}
66 
67 	/** number of elements this structure can hold without further allocations */
68 	size_t capacity() const @property
69 	{
70 		return _capacity * limbBits;
71 	}
72 
73 	/** make sure this structure can contain given number of elements without further allocs */
74 	void reserve(size_t newCap, bool overEstimate = false) @trusted
75 	{
76 		// bit-count -> limb-count
77 		newCap = (newCap + limbBits - 1) / limbBits;
78 
79 		if(newCap <= _capacity)
80 			return;
81 
82 		if(overEstimate)
83 			newCap = max(newCap, 2*_capacity);
84 
85 		auto newPtr = jiveMalloc!limb(newCap);
86 		newPtr[0..limbCount] = _ptr[0..limbCount];
87 
88 		jiveFree(_ptr);
89 		_ptr = newPtr;
90 		_capacity = newCap;
91 	}
92 
93 	inout(limb)* ptr() inout
94 	{
95 		return _ptr;
96 	}
97 
98 	/** number of limbs in use */
99 	size_t limbCount() const @property
100 	{
101 		return (_length + limbBits - 1) / limbBits;
102 	}
103 
104 	/** returns limbs in use */
105 	inout(limb)[] limbs() inout @property @trusted
106 	{
107 		return _ptr[0 .. limbCount];
108 	}
109 
110 	/** either cuts of or fills new elements with false */
111 	void resize(size_t size)
112 	{
113 		reserve(size);
114 
115 		// reset cut of elements. TODO: optimize
116 		for(size_t i = size; i < _length; ++i)
117 			this[i] = false;
118 
119 		_length = size;
120 	}
121 
122 	/** count number of elements equal to v */
123 	size_t count(bool v) const @trusted
124 	{
125 		// NOTE: relies on unused bits being zero
126 		size_t c;
127 		foreach(x; (cast(uint*)_ptr)[0..(_length+31)/32])
128 			c += popcnt(x); // why is there only a 32 bit version of popcnt in core.bitop?
129 		if(v)
130 			return c;
131 		else
132 			return _length-c;
133 	}
134 
135 	/** set all elements to false */
136 	void reset()
137 	{
138 		limbs[] = 0;
139 	}
140 
141 	/** indexing */
142 	bool opIndex(string file = __FILE__, int line = __LINE__)(size_t i) const @trusted
143 	{
144 		if(boundsChecks && (i >= length))
145 			assert(false, boundsCheckMsg!(file, line));
146 		return bt(ptr, i) != 0; // why does bt return an int (and not a bool)?
147 	}
148 
149 	/** ditto */
150 	void opIndexAssign(string file = __FILE__, int line = __LINE__)(bool v, size_t i) @trusted
151 	{
152 		if(boundsChecks && (i >= length))
153 			assert(false, boundsCheckMsg!(file, line));
154 		if(v)
155 			bts(ptr, i);
156 		else
157 			btr(ptr, i);
158 	}
159 
160 	/** toggle element i, returns old value. */
161 	bool toggle(string file = __FILE__, int line = __LINE__)(size_t i) @trusted
162 	{
163 		if(boundsChecks && (i >= length))
164 			assert(false, boundsCheckMsg!(file, line));
165 		return btc(ptr, i) != 0;
166 	}
167 
168 	// TODO: efficient bitwise operations and some kind of iteration
169 }
170 
171 @nogc nothrow pure @safe unittest
172 {
173 	auto a = BitArray(5);
174 	a[1] = true;
175 	a.toggle(2);
176 	a.resize(6);
177 	assert(a[5] == false);
178 	assert(a.count(true) == 2);
179 	assert(a.count(false) == 4);
180 }