1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 package org.apache.commons.compress.compressors.bzip2;
26
27 import java.io.IOException;
28 import java.io.InputStream;
29
30 import org.apache.commons.compress.compressors.CompressorInputStream;
31
32
33
34
35
36
37 public class BZip2CompressorInputStream extends CompressorInputStream implements
38 BZip2Constants {
39
40
41
42
43 private int last;
44
45
46
47
48 private int origPtr;
49
50
51
52
53
54 private int blockSize100k;
55
56 private boolean blockRandomised;
57
58 private int bsBuff;
59 private int bsLive;
60 private final CRC crc = new CRC();
61
62 private int nInUse;
63
64 private InputStream in;
65 private final boolean decompressConcatenated;
66
67 private int currentChar = -1;
68
69 private static final int EOF = 0;
70 private static final int START_BLOCK_STATE = 1;
71 private static final int RAND_PART_A_STATE = 2;
72 private static final int RAND_PART_B_STATE = 3;
73 private static final int RAND_PART_C_STATE = 4;
74 private static final int NO_RAND_PART_A_STATE = 5;
75 private static final int NO_RAND_PART_B_STATE = 6;
76 private static final int NO_RAND_PART_C_STATE = 7;
77
78 private int currentState = START_BLOCK_STATE;
79
80 private int storedBlockCRC, storedCombinedCRC;
81 private int computedBlockCRC, computedCombinedCRC;
82
83
84
85 private int su_count;
86 private int su_ch2;
87 private int su_chPrev;
88 private int su_i2;
89 private int su_j2;
90 private int su_rNToGo;
91 private int su_rTPos;
92 private int su_tPos;
93 private char su_z;
94
95
96
97
98 private BZip2CompressorInputStream.Data data;
99
100
101
102
103
104
105
106
107
108
109
110 public BZip2CompressorInputStream(final InputStream in) throws IOException {
111 this(in, false);
112 }
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130 public BZip2CompressorInputStream(final InputStream in,
131 final boolean decompressConcatenated)
132 throws IOException {
133 super();
134
135 this.in = in;
136 this.decompressConcatenated = decompressConcatenated;
137
138 init(true);
139 initBlock();
140 setupBlock();
141 }
142
143
144 @Override
145 public int read() throws IOException {
146 if (this.in != null) {
147 int r = read0();
148 count(r < 0 ? -1 : 1);
149 return r;
150 } else {
151 throw new IOException("stream closed");
152 }
153 }
154
155
156
157
158
159
160 @Override
161 public int read(final byte[] dest, final int offs, final int len)
162 throws IOException {
163 if (offs < 0) {
164 throw new IndexOutOfBoundsException("offs(" + offs + ") < 0.");
165 }
166 if (len < 0) {
167 throw new IndexOutOfBoundsException("len(" + len + ") < 0.");
168 }
169 if (offs + len > dest.length) {
170 throw new IndexOutOfBoundsException("offs(" + offs + ") + len("
171 + len + ") > dest.length(" + dest.length + ").");
172 }
173 if (this.in == null) {
174 throw new IOException("stream closed");
175 }
176
177 final int hi = offs + len;
178 int destOffs = offs;
179 for (int b; (destOffs < hi) && ((b = read0()) >= 0);) {
180 dest[destOffs++] = (byte) b;
181 }
182
183 int c = (destOffs == offs) ? -1 : (destOffs - offs);
184 count(c);
185 return c;
186 }
187
188 private void makeMaps() {
189 final boolean[] inUse = this.data.inUse;
190 final byte[] seqToUnseq = this.data.seqToUnseq;
191
192 int nInUseShadow = 0;
193
194 for (int i = 0; i < 256; i++) {
195 if (inUse[i]) {
196 seqToUnseq[nInUseShadow++] = (byte) i;
197 }
198 }
199
200 this.nInUse = nInUseShadow;
201 }
202
203 private int read0() throws IOException {
204 final int retChar = this.currentChar;
205
206 switch (this.currentState) {
207 case EOF:
208 return -1;
209
210 case START_BLOCK_STATE:
211 throw new IllegalStateException();
212
213 case RAND_PART_A_STATE:
214 throw new IllegalStateException();
215
216 case RAND_PART_B_STATE:
217 setupRandPartB();
218 break;
219
220 case RAND_PART_C_STATE:
221 setupRandPartC();
222 break;
223
224 case NO_RAND_PART_A_STATE:
225 throw new IllegalStateException();
226
227 case NO_RAND_PART_B_STATE:
228 setupNoRandPartB();
229 break;
230
231 case NO_RAND_PART_C_STATE:
232 setupNoRandPartC();
233 break;
234
235 default:
236 throw new IllegalStateException();
237 }
238
239 return retChar;
240 }
241
242 private boolean init(boolean isFirstStream) throws IOException {
243 if (null == in) {
244 throw new IOException("No InputStream");
245 }
246
247 int magic0 = this.in.read();
248 if (magic0 == -1 && !isFirstStream) {
249 return false;
250 }
251 int magic1 = this.in.read();
252 int magic2 = this.in.read();
253
254 if (magic0 != 'B' || magic1 != 'Z' || magic2 != 'h') {
255 throw new IOException(isFirstStream
256 ? "Stream is not in the BZip2 format"
257 : "Garbage after a valid BZip2 stream");
258 }
259
260 int blockSize = this.in.read();
261 if ((blockSize < '1') || (blockSize > '9')) {
262 throw new IOException("BZip2 block size is invalid");
263 }
264
265 this.blockSize100k = blockSize - '0';
266
267 this.bsLive = 0;
268 this.computedCombinedCRC = 0;
269
270 return true;
271 }
272
273 private void initBlock() throws IOException {
274 char magic0;
275 char magic1;
276 char magic2;
277 char magic3;
278 char magic4;
279 char magic5;
280
281 while (true) {
282
283 magic0 = bsGetUByte();
284 magic1 = bsGetUByte();
285 magic2 = bsGetUByte();
286 magic3 = bsGetUByte();
287 magic4 = bsGetUByte();
288 magic5 = bsGetUByte();
289
290
291 if (magic0 != 0x17 || magic1 != 0x72 || magic2 != 0x45
292 || magic3 != 0x38 || magic4 != 0x50 || magic5 != 0x90) {
293 break;
294 }
295
296
297
298
299 if (complete()) {
300 return;
301 }
302 }
303
304 if (magic0 != 0x31 ||
305 magic1 != 0x41 ||
306 magic2 != 0x59 ||
307 magic3 != 0x26 ||
308 magic4 != 0x53 ||
309 magic5 != 0x59
310 ) {
311 this.currentState = EOF;
312 throw new IOException("bad block header");
313 } else {
314 this.storedBlockCRC = bsGetInt();
315 this.blockRandomised = bsR(1) == 1;
316
317
318
319
320
321 if (this.data == null) {
322 this.data = new Data(this.blockSize100k);
323 }
324
325
326 getAndMoveToFrontDecode();
327
328 this.crc.initialiseCRC();
329 this.currentState = START_BLOCK_STATE;
330 }
331 }
332
333 private void endBlock() throws IOException {
334 this.computedBlockCRC = this.crc.getFinalCRC();
335
336
337 if (this.storedBlockCRC != this.computedBlockCRC) {
338
339
340 this.computedCombinedCRC = (this.storedCombinedCRC << 1)
341 | (this.storedCombinedCRC >>> 31);
342 this.computedCombinedCRC ^= this.storedBlockCRC;
343
344 throw new IOException("BZip2 CRC error");
345 }
346
347 this.computedCombinedCRC = (this.computedCombinedCRC << 1)
348 | (this.computedCombinedCRC >>> 31);
349 this.computedCombinedCRC ^= this.computedBlockCRC;
350 }
351
352 private boolean complete() throws IOException {
353 this.storedCombinedCRC = bsGetInt();
354 this.currentState = EOF;
355 this.data = null;
356
357 if (this.storedCombinedCRC != this.computedCombinedCRC) {
358 throw new IOException("BZip2 CRC error");
359 }
360
361
362
363 return !decompressConcatenated || !init(false);
364 }
365
366 @Override
367 public void close() throws IOException {
368 InputStream inShadow = this.in;
369 if (inShadow != null) {
370 try {
371 if (inShadow != System.in) {
372 inShadow.close();
373 }
374 } finally {
375 this.data = null;
376 this.in = null;
377 }
378 }
379 }
380
381 private int bsR(final int n) throws IOException {
382 int bsLiveShadow = this.bsLive;
383 int bsBuffShadow = this.bsBuff;
384
385 if (bsLiveShadow < n) {
386 final InputStream inShadow = this.in;
387 do {
388 int thech = inShadow.read();
389
390 if (thech < 0) {
391 throw new IOException("unexpected end of stream");
392 }
393
394 bsBuffShadow = (bsBuffShadow << 8) | thech;
395 bsLiveShadow += 8;
396 } while (bsLiveShadow < n);
397
398 this.bsBuff = bsBuffShadow;
399 }
400
401 this.bsLive = bsLiveShadow - n;
402 return (bsBuffShadow >> (bsLiveShadow - n)) & ((1 << n) - 1);
403 }
404
405 private boolean bsGetBit() throws IOException {
406 int bsLiveShadow = this.bsLive;
407 int bsBuffShadow = this.bsBuff;
408
409 if (bsLiveShadow < 1) {
410 int thech = this.in.read();
411
412 if (thech < 0) {
413 throw new IOException("unexpected end of stream");
414 }
415
416 bsBuffShadow = (bsBuffShadow << 8) | thech;
417 bsLiveShadow += 8;
418 this.bsBuff = bsBuffShadow;
419 }
420
421 this.bsLive = bsLiveShadow - 1;
422 return ((bsBuffShadow >> (bsLiveShadow - 1)) & 1) != 0;
423 }
424
425 private char bsGetUByte() throws IOException {
426 return (char) bsR(8);
427 }
428
429 private int bsGetInt() throws IOException {
430 return (((((bsR(8) << 8) | bsR(8)) << 8) | bsR(8)) << 8) | bsR(8);
431 }
432
433
434
435
436 private static void hbCreateDecodeTables(final int[] limit,
437 final int[] base, final int[] perm, final char[] length,
438 final int minLen, final int maxLen, final int alphaSize) {
439 for (int i = minLen, pp = 0; i <= maxLen; i++) {
440 for (int j = 0; j < alphaSize; j++) {
441 if (length[j] == i) {
442 perm[pp++] = j;
443 }
444 }
445 }
446
447 for (int i = MAX_CODE_LEN; --i > 0;) {
448 base[i] = 0;
449 limit[i] = 0;
450 }
451
452 for (int i = 0; i < alphaSize; i++) {
453 base[length[i] + 1]++;
454 }
455
456 for (int i = 1, b = base[0]; i < MAX_CODE_LEN; i++) {
457 b += base[i];
458 base[i] = b;
459 }
460
461 for (int i = minLen, vec = 0, b = base[i]; i <= maxLen; i++) {
462 final int nb = base[i + 1];
463 vec += nb - b;
464 b = nb;
465 limit[i] = vec - 1;
466 vec <<= 1;
467 }
468
469 for (int i = minLen + 1; i <= maxLen; i++) {
470 base[i] = ((limit[i - 1] + 1) << 1) - base[i];
471 }
472 }
473
474 private void recvDecodingTables() throws IOException {
475 final Data dataShadow = this.data;
476 final boolean[] inUse = dataShadow.inUse;
477 final byte[] pos = dataShadow.recvDecodingTables_pos;
478 final byte[] selector = dataShadow.selector;
479 final byte[] selectorMtf = dataShadow.selectorMtf;
480
481 int inUse16 = 0;
482
483
484 for (int i = 0; i < 16; i++) {
485 if (bsGetBit()) {
486 inUse16 |= 1 << i;
487 }
488 }
489
490 for (int i = 256; --i >= 0;) {
491 inUse[i] = false;
492 }
493
494 for (int i = 0; i < 16; i++) {
495 if ((inUse16 & (1 << i)) != 0) {
496 final int i16 = i << 4;
497 for (int j = 0; j < 16; j++) {
498 if (bsGetBit()) {
499 inUse[i16 + j] = true;
500 }
501 }
502 }
503 }
504
505 makeMaps();
506 final int alphaSize = this.nInUse + 2;
507
508
509 final int nGroups = bsR(3);
510 final int nSelectors = bsR(15);
511
512 for (int i = 0; i < nSelectors; i++) {
513 int j = 0;
514 while (bsGetBit()) {
515 j++;
516 }
517 selectorMtf[i] = (byte) j;
518 }
519
520
521 for (int v = nGroups; --v >= 0;) {
522 pos[v] = (byte) v;
523 }
524
525 for (int i = 0; i < nSelectors; i++) {
526 int v = selectorMtf[i] & 0xff;
527 final byte tmp = pos[v];
528 while (v > 0) {
529
530 pos[v] = pos[v - 1];
531 v--;
532 }
533 pos[0] = tmp;
534 selector[i] = tmp;
535 }
536
537 final char[][] len = dataShadow.temp_charArray2d;
538
539
540 for (int t = 0; t < nGroups; t++) {
541 int curr = bsR(5);
542 final char[] len_t = len[t];
543 for (int i = 0; i < alphaSize; i++) {
544 while (bsGetBit()) {
545 curr += bsGetBit() ? -1 : 1;
546 }
547 len_t[i] = (char) curr;
548 }
549 }
550
551
552 createHuffmanDecodingTables(alphaSize, nGroups);
553 }
554
555
556
557
558 private void createHuffmanDecodingTables(final int alphaSize,
559 final int nGroups) {
560 final Data dataShadow = this.data;
561 final char[][] len = dataShadow.temp_charArray2d;
562 final int[] minLens = dataShadow.minLens;
563 final int[][] limit = dataShadow.limit;
564 final int[][] base = dataShadow.base;
565 final int[][] perm = dataShadow.perm;
566
567 for (int t = 0; t < nGroups; t++) {
568 int minLen = 32;
569 int maxLen = 0;
570 final char[] len_t = len[t];
571 for (int i = alphaSize; --i >= 0;) {
572 final char lent = len_t[i];
573 if (lent > maxLen) {
574 maxLen = lent;
575 }
576 if (lent < minLen) {
577 minLen = lent;
578 }
579 }
580 hbCreateDecodeTables(limit[t], base[t], perm[t], len[t], minLen,
581 maxLen, alphaSize);
582 minLens[t] = minLen;
583 }
584 }
585
586 private void getAndMoveToFrontDecode() throws IOException {
587 this.origPtr = bsR(24);
588 recvDecodingTables();
589
590 final InputStream inShadow = this.in;
591 final Data dataShadow = this.data;
592 final byte[] ll8 = dataShadow.ll8;
593 final int[] unzftab = dataShadow.unzftab;
594 final byte[] selector = dataShadow.selector;
595 final byte[] seqToUnseq = dataShadow.seqToUnseq;
596 final char[] yy = dataShadow.getAndMoveToFrontDecode_yy;
597 final int[] minLens = dataShadow.minLens;
598 final int[][] limit = dataShadow.limit;
599 final int[][] base = dataShadow.base;
600 final int[][] perm = dataShadow.perm;
601 final int limitLast = this.blockSize100k * 100000;
602
603
604
605
606
607
608 for (int i = 256; --i >= 0;) {
609 yy[i] = (char) i;
610 unzftab[i] = 0;
611 }
612
613 int groupNo = 0;
614 int groupPos = G_SIZE - 1;
615 final int eob = this.nInUse + 1;
616 int nextSym = getAndMoveToFrontDecode0(0);
617 int bsBuffShadow = this.bsBuff;
618 int bsLiveShadow = this.bsLive;
619 int lastShadow = -1;
620 int zt = selector[groupNo] & 0xff;
621 int[] base_zt = base[zt];
622 int[] limit_zt = limit[zt];
623 int[] perm_zt = perm[zt];
624 int minLens_zt = minLens[zt];
625
626 while (nextSym != eob) {
627 if ((nextSym == RUNA) || (nextSym == RUNB)) {
628 int s = -1;
629
630 for (int n = 1; true; n <<= 1) {
631 if (nextSym == RUNA) {
632 s += n;
633 } else if (nextSym == RUNB) {
634 s += n << 1;
635 } else {
636 break;
637 }
638
639 if (groupPos == 0) {
640 groupPos = G_SIZE - 1;
641 zt = selector[++groupNo] & 0xff;
642 base_zt = base[zt];
643 limit_zt = limit[zt];
644 perm_zt = perm[zt];
645 minLens_zt = minLens[zt];
646 } else {
647 groupPos--;
648 }
649
650 int zn = minLens_zt;
651
652
653
654 while (bsLiveShadow < zn) {
655 final int thech = inShadow.read();
656 if (thech >= 0) {
657 bsBuffShadow = (bsBuffShadow << 8) | thech;
658 bsLiveShadow += 8;
659 continue;
660 } else {
661 throw new IOException("unexpected end of stream");
662 }
663 }
664 int zvec = (bsBuffShadow >> (bsLiveShadow - zn))
665 & ((1 << zn) - 1);
666 bsLiveShadow -= zn;
667
668 while (zvec > limit_zt[zn]) {
669 zn++;
670 while (bsLiveShadow < 1) {
671 final int thech = inShadow.read();
672 if (thech >= 0) {
673 bsBuffShadow = (bsBuffShadow << 8) | thech;
674 bsLiveShadow += 8;
675 continue;
676 } else {
677 throw new IOException(
678 "unexpected end of stream");
679 }
680 }
681 bsLiveShadow--;
682 zvec = (zvec << 1)
683 | ((bsBuffShadow >> bsLiveShadow) & 1);
684 }
685 nextSym = perm_zt[zvec - base_zt[zn]];
686 }
687
688 final byte ch = seqToUnseq[yy[0]];
689 unzftab[ch & 0xff] += s + 1;
690
691 while (s-- >= 0) {
692 ll8[++lastShadow] = ch;
693 }
694
695 if (lastShadow >= limitLast) {
696 throw new IOException("block overrun");
697 }
698 } else {
699 if (++lastShadow >= limitLast) {
700 throw new IOException("block overrun");
701 }
702
703 final char tmp = yy[nextSym - 1];
704 unzftab[seqToUnseq[tmp] & 0xff]++;
705 ll8[lastShadow] = seqToUnseq[tmp];
706
707
708
709
710
711
712 if (nextSym <= 16) {
713 for (int j = nextSym - 1; j > 0;) {
714 yy[j] = yy[--j];
715 }
716 } else {
717 System.arraycopy(yy, 0, yy, 1, nextSym - 1);
718 }
719
720 yy[0] = tmp;
721
722 if (groupPos == 0) {
723 groupPos = G_SIZE - 1;
724 zt = selector[++groupNo] & 0xff;
725 base_zt = base[zt];
726 limit_zt = limit[zt];
727 perm_zt = perm[zt];
728 minLens_zt = minLens[zt];
729 } else {
730 groupPos--;
731 }
732
733 int zn = minLens_zt;
734
735
736
737 while (bsLiveShadow < zn) {
738 final int thech = inShadow.read();
739 if (thech >= 0) {
740 bsBuffShadow = (bsBuffShadow << 8) | thech;
741 bsLiveShadow += 8;
742 continue;
743 } else {
744 throw new IOException("unexpected end of stream");
745 }
746 }
747 int zvec = (bsBuffShadow >> (bsLiveShadow - zn))
748 & ((1 << zn) - 1);
749 bsLiveShadow -= zn;
750
751 while (zvec > limit_zt[zn]) {
752 zn++;
753 while (bsLiveShadow < 1) {
754 final int thech = inShadow.read();
755 if (thech >= 0) {
756 bsBuffShadow = (bsBuffShadow << 8) | thech;
757 bsLiveShadow += 8;
758 continue;
759 } else {
760 throw new IOException("unexpected end of stream");
761 }
762 }
763 bsLiveShadow--;
764 zvec = (zvec << 1) | ((bsBuffShadow >> bsLiveShadow) & 1);
765 }
766 nextSym = perm_zt[zvec - base_zt[zn]];
767 }
768 }
769
770 this.last = lastShadow;
771 this.bsLive = bsLiveShadow;
772 this.bsBuff = bsBuffShadow;
773 }
774
775 private int getAndMoveToFrontDecode0(final int groupNo) throws IOException {
776 final InputStream inShadow = this.in;
777 final Data dataShadow = this.data;
778 final int zt = dataShadow.selector[groupNo] & 0xff;
779 final int[] limit_zt = dataShadow.limit[zt];
780 int zn = dataShadow.minLens[zt];
781 int zvec = bsR(zn);
782 int bsLiveShadow = this.bsLive;
783 int bsBuffShadow = this.bsBuff;
784
785 while (zvec > limit_zt[zn]) {
786 zn++;
787 while (bsLiveShadow < 1) {
788 final int thech = inShadow.read();
789
790 if (thech >= 0) {
791 bsBuffShadow = (bsBuffShadow << 8) | thech;
792 bsLiveShadow += 8;
793 continue;
794 } else {
795 throw new IOException("unexpected end of stream");
796 }
797 }
798 bsLiveShadow--;
799 zvec = (zvec << 1) | ((bsBuffShadow >> bsLiveShadow) & 1);
800 }
801
802 this.bsLive = bsLiveShadow;
803 this.bsBuff = bsBuffShadow;
804
805 return dataShadow.perm[zt][zvec - dataShadow.base[zt][zn]];
806 }
807
808 private void setupBlock() throws IOException {
809 if (this.data == null) {
810 return;
811 }
812
813 final int[] cftab = this.data.cftab;
814 final int[] tt = this.data.initTT(this.last + 1);
815 final byte[] ll8 = this.data.ll8;
816 cftab[0] = 0;
817 System.arraycopy(this.data.unzftab, 0, cftab, 1, 256);
818
819 for (int i = 1, c = cftab[0]; i <= 256; i++) {
820 c += cftab[i];
821 cftab[i] = c;
822 }
823
824 for (int i = 0, lastShadow = this.last; i <= lastShadow; i++) {
825 tt[cftab[ll8[i] & 0xff]++] = i;
826 }
827
828 if ((this.origPtr < 0) || (this.origPtr >= tt.length)) {
829 throw new IOException("stream corrupted");
830 }
831
832 this.su_tPos = tt[this.origPtr];
833 this.su_count = 0;
834 this.su_i2 = 0;
835 this.su_ch2 = 256;
836
837 if (this.blockRandomised) {
838 this.su_rNToGo = 0;
839 this.su_rTPos = 0;
840 setupRandPartA();
841 } else {
842 setupNoRandPartA();
843 }
844 }
845
846 private void setupRandPartA() throws IOException {
847 if (this.su_i2 <= this.last) {
848 this.su_chPrev = this.su_ch2;
849 int su_ch2Shadow = this.data.ll8[this.su_tPos] & 0xff;
850 this.su_tPos = this.data.tt[this.su_tPos];
851 if (this.su_rNToGo == 0) {
852 this.su_rNToGo = Rand.rNums(this.su_rTPos) - 1;
853 if (++this.su_rTPos == 512) {
854 this.su_rTPos = 0;
855 }
856 } else {
857 this.su_rNToGo--;
858 }
859 this.su_ch2 = su_ch2Shadow ^= (this.su_rNToGo == 1) ? 1 : 0;
860 this.su_i2++;
861 this.currentChar = su_ch2Shadow;
862 this.currentState = RAND_PART_B_STATE;
863 this.crc.updateCRC(su_ch2Shadow);
864 } else {
865 endBlock();
866 initBlock();
867 setupBlock();
868 }
869 }
870
871 private void setupNoRandPartA() throws IOException {
872 if (this.su_i2 <= this.last) {
873 this.su_chPrev = this.su_ch2;
874 int su_ch2Shadow = this.data.ll8[this.su_tPos] & 0xff;
875 this.su_ch2 = su_ch2Shadow;
876 this.su_tPos = this.data.tt[this.su_tPos];
877 this.su_i2++;
878 this.currentChar = su_ch2Shadow;
879 this.currentState = NO_RAND_PART_B_STATE;
880 this.crc.updateCRC(su_ch2Shadow);
881 } else {
882 this.currentState = NO_RAND_PART_A_STATE;
883 endBlock();
884 initBlock();
885 setupBlock();
886 }
887 }
888
889 private void setupRandPartB() throws IOException {
890 if (this.su_ch2 != this.su_chPrev) {
891 this.currentState = RAND_PART_A_STATE;
892 this.su_count = 1;
893 setupRandPartA();
894 } else if (++this.su_count >= 4) {
895 this.su_z = (char) (this.data.ll8[this.su_tPos] & 0xff);
896 this.su_tPos = this.data.tt[this.su_tPos];
897 if (this.su_rNToGo == 0) {
898 this.su_rNToGo = Rand.rNums(this.su_rTPos) - 1;
899 if (++this.su_rTPos == 512) {
900 this.su_rTPos = 0;
901 }
902 } else {
903 this.su_rNToGo--;
904 }
905 this.su_j2 = 0;
906 this.currentState = RAND_PART_C_STATE;
907 if (this.su_rNToGo == 1) {
908 this.su_z ^= 1;
909 }
910 setupRandPartC();
911 } else {
912 this.currentState = RAND_PART_A_STATE;
913 setupRandPartA();
914 }
915 }
916
917 private void setupRandPartC() throws IOException {
918 if (this.su_j2 < this.su_z) {
919 this.currentChar = this.su_ch2;
920 this.crc.updateCRC(this.su_ch2);
921 this.su_j2++;
922 } else {
923 this.currentState = RAND_PART_A_STATE;
924 this.su_i2++;
925 this.su_count = 0;
926 setupRandPartA();
927 }
928 }
929
930 private void setupNoRandPartB() throws IOException {
931 if (this.su_ch2 != this.su_chPrev) {
932 this.su_count = 1;
933 setupNoRandPartA();
934 } else if (++this.su_count >= 4) {
935 this.su_z = (char) (this.data.ll8[this.su_tPos] & 0xff);
936 this.su_tPos = this.data.tt[this.su_tPos];
937 this.su_j2 = 0;
938 setupNoRandPartC();
939 } else {
940 setupNoRandPartA();
941 }
942 }
943
944 private void setupNoRandPartC() throws IOException {
945 if (this.su_j2 < this.su_z) {
946 int su_ch2Shadow = this.su_ch2;
947 this.currentChar = su_ch2Shadow;
948 this.crc.updateCRC(su_ch2Shadow);
949 this.su_j2++;
950 this.currentState = NO_RAND_PART_C_STATE;
951 } else {
952 this.su_i2++;
953 this.su_count = 0;
954 setupNoRandPartA();
955 }
956 }
957
958 private static final class Data extends Object {
959
960
961 final boolean[] inUse = new boolean[256];
962
963 final byte[] seqToUnseq = new byte[256];
964 final byte[] selector = new byte[MAX_SELECTORS];
965 final byte[] selectorMtf = new byte[MAX_SELECTORS];
966
967
968
969
970
971 final int[] unzftab = new int[256];
972
973 final int[][] limit = new int[N_GROUPS][MAX_ALPHA_SIZE];
974 final int[][] base = new int[N_GROUPS][MAX_ALPHA_SIZE];
975 final int[][] perm = new int[N_GROUPS][MAX_ALPHA_SIZE];
976 final int[] minLens = new int[N_GROUPS];
977
978 final int[] cftab = new int[257];
979 final char[] getAndMoveToFrontDecode_yy = new char[256];
980 final char[][] temp_charArray2d = new char[N_GROUPS][MAX_ALPHA_SIZE];
981
982 final byte[] recvDecodingTables_pos = new byte[N_GROUPS];
983
984
985
986 int[] tt;
987 byte[] ll8;
988
989
990
991
992
993 Data(int blockSize100k) {
994 super();
995
996 this.ll8 = new byte[blockSize100k * BZip2Constants.BASEBLOCKSIZE];
997 }
998
999
1000
1001
1002
1003
1004
1005
1006 int[] initTT(int length) {
1007 int[] ttShadow = this.tt;
1008
1009
1010
1011
1012
1013 if ((ttShadow == null) || (ttShadow.length < length)) {
1014 this.tt = ttShadow = new int[length];
1015 }
1016
1017 return ttShadow;
1018 }
1019
1020 }
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033 public static boolean matches(byte[] signature, int length) {
1034
1035 if (length < 3) {
1036 return false;
1037 }
1038
1039 if (signature[0] != 'B') {
1040 return false;
1041 }
1042
1043 if (signature[1] != 'Z') {
1044 return false;
1045 }
1046
1047 if (signature[2] != 'h') {
1048 return false;
1049 }
1050
1051 return true;
1052 }
1053 }