1 module ithox.qrcode.common.bitarray;
2 
3 import ithox.qrcode.common.bitutils;
4 
5 alias BitArrayBitType = long;
6 
7 /**
8 * A simple, fast array of bits.
9 */
10 class BitArray
11 {
12     /**
13 	* Bits represented as an array of integers.
14 	*
15 	* @var SplFixedArray
16 	*/
17     protected BitArrayBitType[] bits;
18     /**
19 	* Size of the bit array in bits.
20 	*
21 	* @var integer
22 	*/
23     protected BitArrayBitType size;
24     /**
25 	* Creates a new bit array with a given size.
26 	*
27 	* @param integer size
28 	*/
29     this(int size = 0)
30     {
31         this.size = size;
32         this.bits = new BitArrayBitType[(this.size + 31) >> 3];
33     }
34     /**
35 	* Gets the size in bits.
36 	*
37 	* @return integer
38 	*/
39     public BitArrayBitType getSize()
40     {
41         return this.size;
42     }
43     /**
44 	* Gets the size in bytes.
45 	*
46 	* @return integer
47 	*/
48     public BitArrayBitType getSizeInBytes()
49     {
50         return (this.size + 7) >> 3;
51     }
52     /**
53 	* Ensures that the array has a minimum capacity.
54 	*
55 	* @param  integer size
56 	* @return void
57 	*/
58     public void ensureCapacity(BitArrayBitType size)
59     {
60         if (size > bits.length << 5)
61         {
62             this.bits.length = (size + 31) >> 5;
63         }
64     }
65     /**
66 	* Gets a specific bit.
67 	*
68 	* @param  integer i
69 	* @return boolean
70 	*/
71     public bool get(long i)
72     {
73         return (this.bits[i >> 5] & (1L << (i & 0x1f))) != 0;
74     }
75     /**
76 	* Sets a specific bit.
77 	*
78 	* @param  integer i
79 	* @return void
80 	*/
81     public void set(int i)
82     {
83         this.bits[i >> 5] = this.bits[i >> 5] | 1L << (i & 0x1f);
84     }
85     /**
86 	* Flips a specific bit.
87 	*
88 	* @param  integer i
89 	* @return void
90 	*/
91     public void flip(int i)
92     {
93         this.bits[i >> 5] ^= 1L << (i & 0x1f);
94     }
95     /**
96 	* Gets the next set bit position from a given position.
97 	*
98 	* @param  integer from
99 	* @return integer
100 	*/
101     public long getNextSet(long from)
102     {
103         if (from >= this.size)
104         {
105             return this.size;
106         }
107         auto bitsOffset = from >> 5;
108         auto currentBits = this.bits[bitsOffset];
109         auto bitsLength = this.bits.length;
110         currentBits &= ~((1L << (from & 0x1f)) - 1);
111         while (currentBits == 0)
112         {
113             if (++bitsOffset == bitsLength)
114             {
115                 return this.size;
116             }
117             currentBits = this.bits[bitsOffset];
118         }
119         auto result = (bitsOffset << 5) + BitUtils.numberOfTrailingZeros(currentBits);
120         return result > this.size ? this.size : result;
121     }
122     /**
123 	* Gets the next unset bit position from a given position.
124 	*
125 	* @param  integer from
126 	* @return integer
127 	*/
128     public ulong getNextUnset(int from)
129     {
130         if (from >= this.size)
131         {
132             return this.size;
133         }
134         auto bitsOffset = from >> 5;
135         auto currentBits =  ~this.bits[bitsOffset];
136         auto bitsLength = this.bits.length;
137         currentBits &= ~((1L << (from & 0x1f)) - 1);
138         while (currentBits == 0)
139         {
140             if (++bitsOffset == bitsLength)
141             {
142                 return this.size;
143             }
144             currentBits =  ~this.bits[bitsOffset];
145         }
146         auto result = (bitsOffset << 5) + BitUtils.numberOfTrailingZeros(currentBits);
147         return result > this.size ? this.size : result;
148     }
149     /**
150 	* Sets a bulk of bits.
151 	*
152 	* @param  integer i
153 	* @param  integer newBits
154 	* @return void
155 	*/
156     public void setBulk(int i, BitArrayBitType newBits)
157     {
158         this.bits[i >> 5] = newBits;
159     }
160     /**
161 	* Sets a range of bits.
162 	*
163 	* @param  integer start
164 	* @param  integer end
165 	* @return void
166 	* @throws Exception\InvalidArgumentException
167 	*/
168     public void setRange(int start, int end)
169     {
170         if (end < start)
171         {
172             throw new Exception("End must be greater or equal to start");
173         }
174         if (end == start)
175         {
176             return;
177         }
178         end--;
179         auto firstInt = start >> 5;
180         auto lastInt = end >> 5;
181         auto mask = 0;
182         for (auto i = firstInt; i <= lastInt; i++)
183         {
184             auto firstBit = i > firstInt ? 0 : start & 0x1f;
185             auto lastBit = i < lastInt ? 31 : end & 0x1f;
186             if (firstBit == 0 && lastBit == 31)
187             {
188                 mask = 0x7fffffff;
189             }
190             else
191             {
192                 mask = 0;
193                 for (auto j = firstBit; j < lastBit; j++)
194                 {
195                     mask |= 1L << j;
196                 }
197             }
198             this.bits[i] = this.bits[i] | mask;
199         }
200     }
201     /**
202 	* Clears the bit array, unsetting every bit.
203 	*
204 	* @return void
205 	*/
206     public void clear()
207     {
208         this.bits[] = 0;
209     }
210     /**
211 	* Checks if a range of bits is set or not set.
212 	*
213 	* @param  integer start
214 	* @param  integer end
215 	* @param  integer value
216 	* @return boolean
217 	* @throws Exception\InvalidArgumentException
218 	*/
219     public bool isRange(int start, int end, int value)
220     {
221         if (end < start)
222         {
223             throw new Exception("End must be greater or equal to start");
224         }
225         if (end == start)
226         {
227             return false;
228         }
229         end--;
230         auto firstInt = start >> 5;
231         auto lastInt = end >> 5;
232         auto mask = 0;
233         for (auto i = firstInt; i <= lastInt; i++)
234         {
235             auto firstBit = i > firstInt ? 0 : start & 0x1f;
236             auto lastBit = i < lastInt ? 31 : end & 0x1f;
237             if (firstBit == 0 && lastBit == 31)
238             {
239                 mask = 0x7fffffff;
240             }
241             else
242             {
243                 mask = 0;
244                 for (auto j = firstBit; j <= lastBit; j++)
245                 {
246                     mask |= 1L << j;
247                 }
248             }
249             if ((this.bits[i] & mask) != (value ? mask : 0))
250             {
251                 return false;
252             }
253         }
254         return true;
255     }
256     /**
257 	* Appends a bit to the array.
258 	*
259 	* @param  boolean bit
260 	* @return void
261 	*/
262     public void appendBit(bool bit, bool test = false)
263     {
264         this.ensureCapacity(this.size + 1);
265         if (bit)
266         {
267             if (test)
268             {
269                 import std.stdio;
270 
271                 //writeln("---",this.bits, " size:", this.size);
272             }
273             this.bits[this.size >> 5] = this.bits[this.size >> 5] | (1L << (this.size & 0x1f));
274             if (test)
275             {
276                 import std.stdio;
277 
278                 //writeln(this.bits);
279             }
280         }
281         this.size++;
282     }
283     /**
284 	* Appends a number of bits (up to 32) to the array.
285 	*
286 	* @param  integer value
287 	* @param  integer numBits
288 	* @return void
289 	* @throws Exception\InvalidArgumentException
290 	*/
291     public void appendBits(int value, int numBits, bool test = false)
292     {
293         if (numBits < 0 || numBits > 32)
294         {
295             throw new Exception("Num bits must be between 0 and 32");
296         }
297         this.ensureCapacity(this.size + numBits);
298         for (auto numBitsLeft = numBits; numBitsLeft > 0; numBitsLeft--)
299         {
300             if (test)
301             {
302                 import std.stdio;
303 
304                 //writeln("value:", value , " xxx:", ((value >> (numBitsLeft - 1)) & 0x01));
305             }
306             this.appendBit(((value >> (numBitsLeft - 1)) & 0x01) == 1, test);
307         }
308     }
309     /**
310 	* Appends another bit array to this array.
311 	*
312 	* @param  BitArray other
313 	* @return void
314 	*/
315     public void appendBitArray(BitArray other)
316     {
317         this.ensureCapacity(this.size + other.getSize());
318         for (auto i = 0; i < other.getSize(); i++)
319         {
320             this.appendBit(other.get(i));
321         }
322     }
323     /**
324 	* Makes an exclusive-or comparision on the current bit array.
325 	*
326 	* @param  BitArray other
327 	* @return void
328 	* @throws Exception\InvalidArgumentException
329 	*/
330     public void xorBits(BitArray other)
331     {
332         auto bitsLength = this.bits.length;
333         auto otherBits = other.getBitArray();
334         if (bitsLength != otherBits.length)
335         {
336             throw new Exception("Sizes don\'t match");
337         }
338         for (auto i = 0; i < bitsLength; i++)
339         {
340             this.bits[i] = this.bits[i] ^ otherBits[i];
341         }
342     }
343     /**
344 	* Converts the bit array to a byte array.
345 	*
346 	* @param  integer bitOffset
347 	* @param  integer numBytes
348 	* @return SplFixedArray
349 	*/
350     public int[] toBytes(int bitOffset, int numBytes)
351     {
352         auto bytes = new int[numBytes];
353         for (auto i = 0; i < numBytes; i++)
354         {
355             auto _byte = 0;
356             for (auto j = 0; j < 8; j++)
357             {
358                 if (this.get(bitOffset))
359                 {
360                     _byte |= 1L << (7 - j);
361                 }
362                 bitOffset++;
363             }
364             bytes[i] = _byte;
365         }
366         return bytes;
367     }
368     /**
369 	* Gets the internal bit array.
370 	*
371 	* @return SplFixedArray
372 	*/
373     public BitArrayBitType[] getBitArray()
374     {
375         return this.bits;
376     }
377     /**
378 	* Reverses the array.
379 	*
380 	* @return void
381 	*/
382     public void reverse()
383     {
384         auto newBits = new BitArrayBitType[this.bits.length];
385         for (auto i = 0; i < this.size; i++)
386         {
387             if (this.get(this.size - i - 1))
388             {
389                 newBits[i >> 5] = newBits[i >> 5] | (1L << (i & 0x1f));
390             }
391         }
392         this.bits = newBits;
393     }
394     /**
395 	* Returns a string representation of the bit array.
396 	*
397 	* @return string
398 	*/
399     public override string toString()
400     {
401         import std.array;
402 
403         Appender!string result = appender!string();
404         for (auto i = 0; i < this.size; i++)
405         {
406             if ((i & 0x07) == 0)
407             {
408                 result.put(" ");
409             }
410             result.put(this.get(i) ? 'X' : '.');
411         }
412         return result.data;
413     }
414 }