1 module bitranged;
2 
3 struct BitRange
4 {
5 	private {
6 		// storage of bits
7 		ubyte[] _bits;
8 		// real capacity
9 		ulong   _capacity;
10 		// counter of bits
11 		ulong   _counter;
12 		
13 		// get nth bit of byte
14 		ubyte bitOf(ubyte value, ubyte n)
15 		{
16 			return (value >> n) & 1;
17 		}
18 		
19 		// nearest power of two
20 		ulong cpl2(ulong x)
21 		{
22 			if (x == 0)
23 			{
24 				return 0;
25 			}
26 		
27 			x -= 1UL;
28 			
29 			x = x | (x >> 1UL);
30 			x = x | (x >> 2UL);
31 			x = x | (x >> 4UL);
32 			x = x | (x >> 8UL);
33 			x = x | (x >> 16UL);
34 			x = x | (x >> 32UL);
35 		
36 			return (x == ulong.max) ? ulong.max : (x + 1UL);
37 		}
38 		
39 		// get indexes of byte and bit in bit array
40 		auto indices(ulong index)
41 		{
42 			auto numberOfByte = index >> 3;
43 			auto numberOfBit = 0x07u - cast(ubyte) (index - (numberOfByte << 3));
44 			
45 			return [numberOfByte, numberOfBit];
46 		}
47 		
48 		// set bit to 1
49 		ubyte setBit(ubyte value, ubyte n)
50 		{
51 			return cast(ubyte) (value | (1 << n));
52 		}
53 		
54 		// deconstruct value to little-endian byte array
55 		ubyte[] toLEBytes(T)(T value)
56 		{
57 			if (T.sizeof <= 1)
58 			{
59 				return [cast(ubyte) value];
60 			}
61 			else
62 			{
63 				ubyte[] bytes;
64 				
65 				T MASK    = cast(T) 0xff;
66 				T NSHIFTS = (T.sizeof - 1) << 3;
67 				
68 				foreach (i; 0..T.sizeof)
69 				{
70 					bytes ~= cast(ubyte) ((value & (MASK << NSHIFTS)) >> NSHIFTS);
71 					NSHIFTS -= 8;
72 				}
73 				
74 				return bytes;
75 			}
76 		}
77 		
78 		// set bit to 0
79 		ubyte unsetBit(ubyte value, ubyte n)
80 		{
81 			return cast(ubyte) (value & ~(1u << n));
82 		}
83 	}
84 
85 	this(ulong n)
86 	{
87 		_capacity     = n;
88 		_counter      = 0;
89 
90 		if ((0 < n) & (n < 8))
91 		{
92 			_bits ~= 0x00;	
93 		}
94 		else
95 		{
96 			foreach (i; 0..(cpl2(n) >> 3))
97 			{
98 				_bits ~= 0x00;
99 			}
100 		}
101 	}
102 	
103 	ubyte[] asBytes()
104 	{
105 		return _bits;
106 	}
107 
108 	bool empty() 
109 	{
110 		return (_counter == _capacity);
111 	}
112 	
113 	void fromString(string s)
114 	{
115 		auto length = s.length;
116 		auto size   = (length < 8) ? 1 : cpl2(length) >> 3;
117 		
118 		_bits = [];
119 		
120 		foreach (_; 0..size)
121 		{
122 			_bits ~= 0x00u;
123 		}
124 		
125 		foreach (e; s)
126 		{
127 			if (e == 0x30)
128 			{
129 				this.opIndexAssign(0, _counter);
130 			}
131 			else
132 			{
133 				if (e == 0x31)
134 				{
135 					this.opIndexAssign(1, _counter);
136 				}
137 				else
138 				{
139 					continue;
140 				}
141 			}
142 			_counter++;
143 		}
144 		
145 		_capacity = _counter;
146 		_counter  = 0;
147 	}
148 
149 	ubyte front()
150 	{
151 		auto indexes = indices(_counter);
152 		
153 		return bitOf(_bits[indexes[0]], cast(ubyte) indexes[1]);
154 	}
155 	
156 	void grow(ulong size)
157 	{
158 		if (size > 0)
159 		{
160 			if (_capacity >= size)
161 			{
162 				throw new Exception("Invalid size for growing BitRange. New size conflicts with real capacity of bit array.");
163 			}
164 			else
165 			{
166 				auto realSize = (size < 8) ? 1 : cpl2(size) >> 3;
167 				auto newSize = realSize - _bits.length;
168 				
169 				foreach (_; 0..newSize)
170 				{
171 					_bits ~= 0x00u;
172 				}
173 				
174 				_capacity = size;
175 			}
176 		}
177 	}
178 	
179 	ulong length()
180 	{
181 		return _capacity;
182 	}
183 	
184 	ubyte opIndex(size_t index)
185 	{
186 		auto indexes = indices(index);
187 		
188 		return bitOf(_bits[indexes[0]], cast(ubyte) indexes[1]);	
189 	}
190 	
191 	void opIndexAssign(ubyte value, size_t index)
192 	{
193 		auto indexes = indices(index);
194 		auto currentByte = _bits[indexes[0]];
195 		
196 		if (value)
197 		{
198 			currentByte =  setBit(currentByte, cast(ubyte) indexes[1]);
199 		}
200 		else
201 		{
202 			currentByte = unsetBit(currentByte, cast(ubyte) indexes[1]);
203 		}
204 	
205 		_bits[indexes[0]] = currentByte;
206 	}
207 
208 	void popFront()
209 	{
210 		_counter++;
211 	}
212 
213 	void push(T)(T value)
214 	{
215 		auto bytes = toLEBytes(value);
216 		
217 		_capacity += bytes.length * 8;
218 		_bits ~= bytes;
219 	}
220 	
221 	void push(T)(T[] values)
222 	{
223 		foreach (e; values)
224 		{
225 			push(e);
226 		}
227 	}
228 	
229 	void shrink(ulong size)
230 	{
231 		if (size == 0)
232 		{
233 			_bits = [];
234 			_capacity = 0;
235 		}
236 		else
237 		{
238 			if (size >= _capacity)
239 			{
240 				throw new Exception("Invalid size for shrinking BitRange. New size conflicts with real capacity of bit array.");
241 			}
242 			else
243 			{
244 				auto realSize = (size < 8) ? 1 : cpl2(size) >> 3;
245 				_bits = _bits[0..realSize];
246 				_capacity = size;
247 			}
248 		}
249 	}
250 	
251 	string toString()
252 	{
253 		string bits;
254 		
255 		foreach (i; 0.._capacity)
256 		{
257 			bits ~= (opIndex(i) == 0 ? "0" : "1");
258 		}
259 	
260 		return bits;
261 	}
262 }