1
2
3
4
5
6 package io.github.olyutorskii.aletojio;
7
8
9
10
11
12
13
14
15
16
17 public class BitPool {
18
19
20
21
22 public static final int DEFAULT_BITS = 512;
23
24 private static final long MASK_LONGINT = (1L << Integer.SIZE) - 1L;
25
26 private static final int BOOLEAN_SIZE = 1;
27 private static final int ELEM_SIZE = Integer.SIZE;
28
29 private static final int RSFT_DIV_E;
30 private static final int MASK_MOD_E;
31
32 static {
33 assert Integer.bitCount(ELEM_SIZE) == 1;
34
35 RSFT_DIV_E = Integer.numberOfTrailingZeros(ELEM_SIZE);
36 MASK_MOD_E = (1 << RSFT_DIV_E) - 1;
37
38 int testVal = ELEM_SIZE * 3 + (0x5555_5555 & MASK_MOD_E);
39 assert testVal >> RSFT_DIV_E == testVal / ELEM_SIZE;
40 assert (testVal & MASK_MOD_E) == testVal % ELEM_SIZE;
41 }
42
43
44
45 private final int[] buf;
46
47 private final int bufCapa;
48
49 private int bufSize;
50 private int headRawBitPos;
51 private int tailRawBitPos;
52
53
54
55
56
57
58
59 public BitPool() {
60 this(DEFAULT_BITS);
61 return;
62 }
63
64
65
66
67
68
69
70 public BitPool(int bitLength) throws IllegalArgumentException {
71 super();
72
73 if (bitLength <= 0) throw new IllegalArgumentException();
74
75 this.bufCapa = bitLength;
76
77 int bufLen = (this.bufCapa - 1) / ELEM_SIZE + 1;
78 this.buf = new int[bufLen];
79
80 this.bufSize = 0;
81 this.headRawBitPos = 0;
82 this.tailRawBitPos = 0;
83
84 return;
85 }
86
87
88
89
90
91
92
93
94
95
96
97
98 static int getMaskInt(int lowerBits) {
99 if (lowerBits >= Integer.SIZE) return -1;
100 int result = (1 << lowerBits) - 1;
101 return result;
102 }
103
104
105
106
107
108
109
110
111
112
113
114 static long getMaskLong(int lowerBits) {
115 if (lowerBits >= Long.SIZE) return -1L;
116 long result = (1L << lowerBits) - 1L;
117 return result;
118 }
119
120
121
122
123
124
125
126 public int size() {
127 return this.bufSize;
128 }
129
130
131
132
133
134
135 public int capacity() {
136 return this.bufCapa;
137 }
138
139
140
141
142
143
144 public boolean isEmpty() {
145 boolean result = this.bufSize <= 0;
146 return result;
147 }
148
149
150
151
152
153
154 public int remaining() {
155 int result = this.bufCapa - this.bufSize;
156 return result;
157 }
158
159
160
161
162
163
164 public boolean hasRemaining() {
165 boolean result = this.bufCapa > this.bufSize;
166 return result;
167 }
168
169
170
171
172 public void clear() {
173 this.headRawBitPos = 0;
174 this.tailRawBitPos = 0;
175 this.bufSize = 0;
176 return;
177 }
178
179
180
181
182
183
184
185
186 private void checkPushSize(int bits, int max) throws IndexOutOfBoundsException {
187 if (bits <= 0 || remaining() < bits || max < bits) {
188 throw new IndexOutOfBoundsException();
189 }
190 return;
191 }
192
193
194
195
196
197
198
199
200 private void checkChopSize(int bits, int max) throws IndexOutOfBoundsException {
201 if (bits <= 0 || this.bufSize < bits || max < bits) {
202 throw new IndexOutOfBoundsException();
203 }
204 return;
205 }
206
207
208
209
210
211
212
213 public void pushBoolean(boolean bool) throws IndexOutOfBoundsException {
214 checkPushSize(BOOLEAN_SIZE, BOOLEAN_SIZE);
215
216 int bufIndex = this.tailRawBitPos >> RSFT_DIV_E;
217 int bposInInt = this.tailRawBitPos & MASK_MOD_E;
218 int lsft = ELEM_SIZE - 1 - bposInInt;
219
220 int mask = 1 << lsft;
221
222 int elem = this.buf[bufIndex];
223 if (bool) {
224 elem |= mask;
225 } else {
226 elem &= ~mask;
227 }
228 this.buf[bufIndex] = elem;
229
230 this.bufSize++;
231 this.tailRawBitPos++;
232 if (this.tailRawBitPos >= this.bufCapa) {
233 this.tailRawBitPos -= this.bufCapa;
234 }
235
236 return;
237 }
238
239
240
241
242
243
244
245 public boolean chopBoolean() throws IndexOutOfBoundsException {
246 checkChopSize(BOOLEAN_SIZE, BOOLEAN_SIZE);
247
248 int bufIndex = this.headRawBitPos >> RSFT_DIV_E;
249 int bposInInt = this.headRawBitPos & MASK_MOD_E;
250 int lsft = ELEM_SIZE - 1 - bposInInt;
251
252 int mask = 1 << lsft;
253 int elem = this.buf[bufIndex];
254 boolean result = (elem & mask) != 0;
255
256 this.bufSize--;
257 this.headRawBitPos++;
258 if (this.headRawBitPos >= this.bufCapa) {
259 this.headRawBitPos -= this.bufCapa;
260 }
261
262 return result;
263 }
264
265
266
267
268
269
270
271 public void pushInt(int iArg) throws IndexOutOfBoundsException {
272 pushInt(iArg, Integer.SIZE);
273 return;
274 }
275
276
277
278
279
280
281
282
283 public void pushInt(int iArg, int bitCt) throws IndexOutOfBoundsException {
284 checkPushSize(bitCt, Integer.SIZE);
285
286 int bits1st = this.bufCapa - this.tailRawBitPos;
287
288 if (bitCt <= bits1st) {
289 pushIntImpl(iArg, bitCt);
290 } else {
291
292 int bits2nd = bitCt - bits1st;
293 int highArg = iArg >> bits2nd;
294 pushIntImpl(highArg, bits1st);
295 pushIntImpl(iArg, bits2nd);
296 }
297
298 return;
299 }
300
301
302
303
304
305
306
307
308
309
310
311 private void pushIntImpl(int iArg, int bitCt) {
312 int bposInLastElem = this.tailRawBitPos & MASK_MOD_E;
313 int restInLastElem = ELEM_SIZE - bposInLastElem;
314
315 if (restInLastElem < bitCt) {
316
317 int bits2nd = bitCt - restInLastElem;
318 int highArg = iArg >> bits2nd;
319 pushIntImpl(highArg, restInLastElem);
320 pushIntImpl(iArg, bits2nd);
321 return;
322 }
323
324 int bufIndex = this.tailRawBitPos >> RSFT_DIV_E;
325
326 int gap = restInLastElem - bitCt;
327 int mask = getMaskInt(bitCt) << gap;
328 int bits = (iArg << gap) & mask;
329
330 int elem = this.buf[bufIndex];
331 elem &= ~mask;
332 elem |= bits;
333 this.buf[bufIndex] = elem;
334
335 this.bufSize += bitCt;
336 this.tailRawBitPos += bitCt;
337 if (this.tailRawBitPos >= this.bufCapa) {
338 this.tailRawBitPos -= this.bufCapa;
339 }
340
341 return;
342 }
343
344
345
346
347
348
349
350 public int chopInt() throws IndexOutOfBoundsException {
351 return chopInt(Integer.SIZE);
352 }
353
354
355
356
357
358
359
360
361 public int chopInt(int bitCt) throws IndexOutOfBoundsException {
362 checkChopSize(bitCt, Integer.SIZE);
363
364 int result;
365
366 if (this.headRawBitPos + bitCt <= this.bufCapa) {
367 result = chopIntImpl(bitCt);
368 } else {
369
370 int bits1st = this.bufCapa - this.headRawBitPos;
371 int bits2nd = bitCt - bits1st;
372 int highVal = chopIntImpl(bits1st);
373 int lowVal = chopIntImpl(bits2nd);
374 result = (highVal << bits2nd) | lowVal;
375 }
376
377 return result;
378 }
379
380
381
382
383
384
385
386
387
388
389
390 private int chopIntImpl(int bitCt) {
391 int bposInLastElem = this.headRawBitPos & MASK_MOD_E;
392 int restInLastElem = ELEM_SIZE - bposInLastElem;
393
394 if (restInLastElem < bitCt) {
395
396 int bits2nd = bitCt - restInLastElem;
397 int highVal = chopIntImpl(restInLastElem);
398 int lowVal = chopIntImpl(bits2nd);
399 int result = (highVal << bits2nd) | lowVal;
400 return result;
401 }
402
403 int bufIndex = this.headRawBitPos >> RSFT_DIV_E;
404
405 int elem = this.buf[bufIndex];
406 int gap = restInLastElem - bitCt;
407 int result = (elem >> gap) & getMaskInt(bitCt);
408
409 this.bufSize -= bitCt;
410 this.headRawBitPos += bitCt;
411 if (this.headRawBitPos >= this.bufCapa) {
412 this.headRawBitPos -= this.bufCapa;
413 }
414
415 return result;
416 }
417
418
419
420
421
422
423
424 public void pushLong(long lArg) throws IndexOutOfBoundsException {
425 pushLong(lArg, Long.SIZE);
426 return;
427 }
428
429
430
431
432
433
434
435
436 public void pushLong(long lArg, int bitCt) throws IndexOutOfBoundsException {
437 checkPushSize(bitCt, Long.SIZE);
438
439 if (bitCt <= Integer.SIZE) {
440 pushInt((int) lArg, bitCt);
441 return;
442 }
443
444 int bits1st = bitCt - Integer.SIZE;
445 int bits2nd = Integer.SIZE;
446
447 int highArg = (int) (lArg >> Integer.SIZE);
448 int lowArg = (int) lArg;
449
450 pushInt(highArg, bits1st);
451 pushInt(lowArg, bits2nd);
452
453 return;
454 }
455
456
457
458
459
460
461
462 public long chopLong() throws IndexOutOfBoundsException {
463 return chopLong(Long.SIZE);
464 }
465
466
467
468
469
470
471
472
473 public long chopLong(int bitCt) throws IndexOutOfBoundsException {
474 checkChopSize(bitCt, Long.SIZE);
475
476 if (bitCt <= Integer.SIZE) {
477 long result = (long) chopInt(bitCt);
478 result &= getMaskLong(bitCt);
479 return result;
480 }
481
482 int bits1st = bitCt - Integer.SIZE;
483
484 long highVal = (long) chopInt(bits1st);
485 long lowVal = (long) chopInt(Integer.SIZE);
486
487 highVal &= getMaskLong(bits1st);
488 lowVal &= MASK_LONGINT;
489
490 long result = (highVal << Integer.SIZE) | lowVal;
491
492 return result;
493 }
494
495
496
497
498
499
500
501 public void pushByte(byte bArg) throws IndexOutOfBoundsException {
502 pushInt(bArg, Byte.SIZE);
503 return;
504 }
505
506
507
508
509
510
511
512 public byte chopByte() throws IndexOutOfBoundsException {
513 byte result = (byte) chopInt(Byte.SIZE);
514 return result;
515 }
516
517 }