1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  */
18 
19 /*
20  * This package is based on the work done by Keiron Liddle, Aftex Software
21  * <keiron@aftexsw.com> to whom the Ant project is very grateful for his
22  * great code.
23  */
24 package org.apache.tools.bzip2;
25 
26 import java.io.IOException;
27 import java.io.InputStream;
28 
29 /**
30  * An input stream that decompresses from the BZip2 format (without the file
31  * header chars) to be read as any other stream.
32  *
33  * <p>The decompression requires large amounts of memory. Thus you
34  * should call the {@link #close() close()} method as soon as
35  * possible, to force <tt>CBZip2InputStream</tt> to release the
36  * allocated memory.  See {@link CBZip2OutputStream
37  * CBZip2OutputStream} for information about memory usage.</p>
38  *
39  * <p><tt>CBZip2InputStream</tt> reads bytes from the compressed
40  * source stream via the single byte {@link java.io.InputStream#read()
41  * read()} method exclusively. Thus you should consider to use a
42  * buffered source stream.</p>
43  *
44  * <p>Instances of this class are not threadsafe.</p>
45  */
46 public class CBZip2InputStream extends InputStream implements BZip2Constants {
47 
48     /**
49      * Index of the last char in the block, so the block size == last + 1.
50      */
51     private int  last;
52 
53     /**
54      * Index in zptr[] of original string after sorting.
55      */
56     private int  origPtr;
57 
58     /**
59      * always: in the range 0 .. 9.
60      * The current block size is 100000 * this number.
61      */
62     private int blockSize100k;
63 
64     private boolean blockRandomised;
65 
66     private int bsBuff;
67     private int bsLive;
68     private final CRC crc = new CRC();
69 
70     private int nInUse;
71 
72     private InputStream in;
73     private final boolean decompressConcatenated;
74 
75     private int currentChar = -1;
76 
77     private static final int EOF                  = 0;
78     private static final int START_BLOCK_STATE = 1;
79     private static final int RAND_PART_A_STATE = 2;
80     private static final int RAND_PART_B_STATE = 3;
81     private static final int RAND_PART_C_STATE = 4;
82     private static final int NO_RAND_PART_A_STATE = 5;
83     private static final int NO_RAND_PART_B_STATE = 6;
84     private static final int NO_RAND_PART_C_STATE = 7;
85 
86     private int currentState = START_BLOCK_STATE;
87 
88     private int storedBlockCRC, storedCombinedCRC;
89     private int computedBlockCRC, computedCombinedCRC;
90 
91     // Variables used by setup* methods exclusively
92 
93     private int su_count;
94     private int su_ch2;
95     private int su_chPrev;
96     private int su_i2;
97     private int su_j2;
98     private int su_rNToGo;
99     private int su_rTPos;
100     private int su_tPos;
101     private char su_z;
102 
103     /**
104      * All memory intensive stuff.
105      * This field is initialized by initBlock().
106      */
107     private CBZip2InputStream.Data data;
108 
109     /**
110      * Constructs a new CBZip2InputStream which decompresses bytes read from
111      * the specified stream. This doesn't suppprt decompressing
112      * concatenated .bz2 files.
113      *
114      * <p>Although BZip2 headers are marked with the magic
115      * <tt>"Bz"</tt> this constructor expects the next byte in the
116      * stream to be the first one after the magic.  Thus callers have
117      * to skip the first two bytes. Otherwise this constructor will
118      * throw an exception. </p>
119      * @param in
120      *
121      * @throws IOException
122      *  if the stream content is malformed or an I/O error occurs.
123      * @throws NullPointerException
124      *  if <tt>in == null</tt>
125      */
CBZip2InputStream(final InputStream in)126     public CBZip2InputStream(final InputStream in) throws IOException {
127         this(in, false);
128     }
129 
130     /**
131      * Constructs a new CBZip2InputStream which decompresses bytes
132      * read from the specified stream.
133      *
134      * <p>Although BZip2 headers are marked with the magic
135      * <tt>"Bz"</tt> this constructor expects the next byte in the
136      * stream to be the first one after the magic.  Thus callers have
137      * to skip the first two bytes. Otherwise this constructor will
138      * throw an exception. </p>
139      *
140      * @param in the InputStream from which this object should be created
141      * @param decompressConcatenated
142      *                     if true, decompress until the end of the input;
143      *                     if false, stop after the first .bz2 stream and
144      *                     leave the input position to point to the next
145      *                     byte after the .bz2 stream
146      *
147      * @throws IOException
148      *             if the stream content is malformed or an I/O error occurs.
149      * @throws NullPointerException
150      *             if <tt>in == null</tt>
151      */
CBZip2InputStream(final InputStream in, final boolean decompressConcatenated)152     public CBZip2InputStream(final InputStream in,
153                              final boolean decompressConcatenated)
154             throws IOException {
155         super();
156 
157         this.in = in;
158         this.decompressConcatenated = decompressConcatenated;
159 
160         init(true);
161         initBlock();
162         setupBlock();
163     }
164 
165   /** {@inheritDoc} */
166   @Override
read()167   public int read() throws IOException {
168     if (this.in == null)
169       throw new IOException("stream closed");
170     return read0();
171   }
172 
173     /*
174      * (non-Javadoc)
175      *
176      * @see java.io.InputStream#read(byte[], int, int)
177      */
178     @Override
read(final byte[] dest, final int offs, final int len)179     public int read(final byte[] dest, final int offs, final int len)
180         throws IOException {
181         if (offs < 0) {
182             throw new IndexOutOfBoundsException("offs(" + offs + ") < 0.");
183         }
184         if (len < 0) {
185             throw new IndexOutOfBoundsException("len(" + len + ") < 0.");
186         }
187         if (offs + len > dest.length) {
188             throw new IndexOutOfBoundsException("offs(" + offs + ") + len("
189                                                 + len + ") > dest.length("
190                                                 + dest.length + ").");
191         }
192         if (this.in == null) {
193             throw new IOException("stream closed");
194         }
195 
196         final int hi = offs + len;
197         int destOffs = offs;
198         for (int b; (destOffs < hi) && ((b = read0()) >= 0);) {
199             dest[destOffs++] = (byte) b;
200         }
201 
202         return (destOffs == offs) ? -1 : (destOffs - offs);
203     }
204 
makeMaps()205     private void makeMaps() {
206         final boolean[] inUse   = this.data.inUse;
207         final byte[] seqToUnseq = this.data.seqToUnseq;
208 
209         int nInUseShadow = 0;
210 
211         for (int i = 0; i < 256; i++) {
212             if (inUse[i]) {
213                 seqToUnseq[nInUseShadow++] = (byte) i;
214             }
215         }
216 
217         this.nInUse = nInUseShadow;
218     }
219 
read0()220     private int read0() throws IOException {
221         final int retChar = this.currentChar;
222 
223         switch (this.currentState) {
224         case EOF:
225             return -1;
226 
227         case START_BLOCK_STATE:
228             throw new IllegalStateException();
229 
230         case RAND_PART_A_STATE:
231             throw new IllegalStateException();
232 
233         case RAND_PART_B_STATE:
234             setupRandPartB();
235             break;
236 
237         case RAND_PART_C_STATE:
238             setupRandPartC();
239             break;
240 
241         case NO_RAND_PART_A_STATE:
242             throw new IllegalStateException();
243 
244         case NO_RAND_PART_B_STATE:
245             setupNoRandPartB();
246             break;
247 
248         case NO_RAND_PART_C_STATE:
249             setupNoRandPartC();
250             break;
251 
252         default:
253             throw new IllegalStateException();
254         }
255 
256         return retChar;
257     }
258 
init(boolean isFirstStream)259     private boolean init(boolean isFirstStream) throws IOException {
260         if (null == in) {
261             throw new IOException("No InputStream");
262         }
263 
264         if (isFirstStream) {
265             if (in.available() == 0) {
266                 throw new IOException("Empty InputStream");
267             }
268         } else {
269             int magic0 = readByteAsInt();
270             if (magic0 == -1) {
271                 return false;
272             }
273             int magic1 = readByteAsInt();
274             if (magic0 != 'B' || magic1 != 'Z') {
275                 throw new IOException("Garbage after a valid BZip2 stream");
276             }
277         }
278 
279         int magic2 = readByteAsInt();
280         if (magic2 != 'h') {
281             throw new IOException(isFirstStream
282                     ? "Stream is not in the BZip2 format"
283                     : "Garbage after a valid BZip2 stream");
284         }
285 
286         int blockSize = readByteAsInt();
287         if ((blockSize < '1') || (blockSize > '9')) {
288             throw new IOException("Stream is not BZip2 formatted: illegal "
289                                   + "blocksize " + (char) blockSize);
290         }
291 
292         this.blockSize100k = blockSize - '0';
293 
294         this.bsLive = 0;
295         this.computedCombinedCRC = 0;
296 
297         return true;
298     }
299 
readByteAsInt()300     public synchronized int readByteAsInt() throws IOException {
301       /**
302        * @j2sNative
303        *
304        * return(this.in.readByteAsInt());
305        */
306       {
307         return in.read();
308       }
309     }
310 
initBlock()311   private void initBlock() throws IOException {
312     char magic0;
313     char magic1;
314     char magic2;
315     char magic3;
316     char magic4;
317     char magic5;
318 
319     while (true) {
320       // Get the block magic bytes.
321       magic0 = bsGetUByte();
322       magic1 = bsGetUByte();
323       magic2 = bsGetUByte();
324       magic3 = bsGetUByte();
325       magic4 = bsGetUByte();
326       magic5 = bsGetUByte();
327 
328       // If isn't end of stream magic, break out of the loop.
329       if (magic0 != 0x17 || magic1 != 0x72 || magic2 != 0x45 || magic3 != 0x38
330           || magic4 != 0x50 || magic5 != 0x90) {
331         break;
332       }
333 
334       // End of stream was reached. Check the combined CRC and
335       // advance to the next .bz2 stream if decoding concatenated
336       // streams.
337       if (complete()) {
338         return;
339       }
340     }
341 
342     if (magic0 != 0x31 || // '1'
343         magic1 != 0x41 || // ')'
344         magic2 != 0x59 || // 'Y'
345         magic3 != 0x26 || // '&'
346         magic4 != 0x53 || // 'S'
347         magic5 != 0x59 // 'Y'
348     ) {
349       this.currentState = EOF;
350       throw new IOException("bad block header");
351     }
352     this.storedBlockCRC = bsGetInt();
353     this.blockRandomised = bsR(1) == 1;
354 
355     /**
356      * Allocate data here instead in constructor, so we do not allocate it if
357      * the input file is empty.
358      */
359     if (this.data == null) {
360       this.data = new Data(this.blockSize100k);
361     }
362 
363     // currBlockNo++;
364     getAndMoveToFrontDecode();
365 
366     this.crc.initialiseCRC();
367     this.currentState = START_BLOCK_STATE;
368   }
369 
endBlock()370     private void endBlock() throws IOException {
371         this.computedBlockCRC = this.crc.getFinalCRC();
372 
373         // A bad CRC is considered a fatal error.
374         if (this.storedBlockCRC != this.computedBlockCRC) {
375             // make next blocks readable without error
376             // (repair feature, not yet documented, not tested)
377             this.computedCombinedCRC
378                 = (this.storedCombinedCRC << 1)
379                 | (this.storedCombinedCRC >>> 31);
380             this.computedCombinedCRC ^= this.storedBlockCRC;
381 
382             reportCRCError();
383         }
384 
385         this.computedCombinedCRC
386             = (this.computedCombinedCRC << 1)
387             | (this.computedCombinedCRC >>> 31);
388         this.computedCombinedCRC ^= this.computedBlockCRC;
389     }
390 
complete()391     private boolean complete() throws IOException {
392         this.storedCombinedCRC = bsGetInt();
393         this.currentState = EOF;
394         this.data = null;
395 
396         if (this.storedCombinedCRC != this.computedCombinedCRC) {
397             reportCRCError();
398         }
399 
400         // Look for the next .bz2 stream if decompressing
401         // concatenated files.
402         return !decompressConcatenated || !init(false);
403     }
404 
405     @Override
close()406     public void close() throws IOException {
407         InputStream inShadow = this.in;
408         if (inShadow != null) {
409             try {
410                 if (inShadow != System.in) {
411                     inShadow.close();
412                 }
413             } finally {
414                 this.data = null;
415                 this.in = null;
416             }
417         }
418     }
419 
bsR(final int n)420     private int bsR(final int n) throws IOException {
421         int bsLiveShadow = this.bsLive;
422         int bsBuffShadow = this.bsBuff;
423 
424         if (bsLiveShadow < n) {
425             final InputStream inShadow = this.in;
426             do {
427                 int thech = readByteAsInt();//inShadow.read();
428 
429                 if (thech < 0) {
430                     throw new IOException("unexpected end of stream");
431                 }
432 
433                 bsBuffShadow = (bsBuffShadow << 8) | thech;
434                 bsLiveShadow += 8;
435             } while (bsLiveShadow < n);
436 
437             this.bsBuff = bsBuffShadow;
438         }
439 
440         this.bsLive = bsLiveShadow - n;
441         return (bsBuffShadow >> (bsLiveShadow - n)) & ((1 << n) - 1);
442     }
443 
bsGetBit()444     private boolean bsGetBit() throws IOException {
445         int bsLiveShadow = this.bsLive;
446         int bsBuffShadow = this.bsBuff;
447 
448         if (bsLiveShadow < 1) {
449             int thech = readByteAsInt();
450 
451             if (thech < 0) {
452                 throw new IOException("unexpected end of stream");
453             }
454 
455             bsBuffShadow = (bsBuffShadow << 8) | thech;
456             bsLiveShadow += 8;
457             this.bsBuff = bsBuffShadow;
458         }
459 
460         this.bsLive = bsLiveShadow - 1;
461         return ((bsBuffShadow >> (bsLiveShadow - 1)) & 1) != 0;
462     }
463 
bsGetUByte()464     private char bsGetUByte() throws IOException {
465         return (char) bsR(8);
466     }
467 
bsGetInt()468     private int bsGetInt() throws IOException {
469         return (((((bsR(8) << 8) | bsR(8)) << 8) | bsR(8)) << 8) | bsR(8);
470     }
471 
472     /**
473      * Called by createHuffmanDecodingTables() exclusively.
474      * @param limit
475      * @param base
476      * @param perm
477      * @param length
478      * @param minLen
479      * @param maxLen
480      * @param alphaSize
481      */
hbCreateDecodeTables(final int[] limit, final int[] base, final int[] perm, final char[] length, final int minLen, final int maxLen, final int alphaSize)482     private static void hbCreateDecodeTables(final int[] limit,
483                                              final int[] base,
484                                              final int[] perm,
485                                              final char[] length,
486                                              final int minLen,
487                                              final int maxLen,
488                                              final int alphaSize) {
489         for (int i = minLen, pp = 0; i <= maxLen; i++) {
490             for (int j = 0; j < alphaSize; j++) {
491                 if (length[j] == i) {
492                     perm[pp++] = j;
493                 }
494             }
495         }
496 
497         for (int i = MAX_CODE_LEN; --i > 0;) {
498             base[i] = 0;
499             limit[i] = 0;
500         }
501 
502         for (int i = 0; i < alphaSize; i++) {
503             base[length[i] + 1]++;
504         }
505 
506         for (int i = 1, b = base[0]; i < MAX_CODE_LEN; i++) {
507             b += base[i];
508             base[i] = b;
509         }
510 
511         for (int i = minLen, vec = 0, b = base[i]; i <= maxLen; i++) {
512             final int nb = base[i + 1];
513             vec += nb - b;
514             b = nb;
515             limit[i] = vec - 1;
516             vec <<= 1;
517         }
518 
519         for (int i = minLen + 1; i <= maxLen; i++) {
520             base[i] = ((limit[i - 1] + 1) << 1) - base[i];
521         }
522     }
523 
recvDecodingTables()524     private void recvDecodingTables() throws IOException {
525         final Data dataShadow     = this.data;
526         final boolean[] inUse     = dataShadow.inUse;
527         final byte[] pos          = dataShadow.recvDecodingTables_pos;
528         final byte[] selector     = dataShadow.selector;
529         final byte[] selectorMtf  = dataShadow.selectorMtf;
530 
531         int inUse16 = 0;
532 
533         /* Receive the mapping table */
534         for (int i = 0; i < 16; i++) {
535             if (bsGetBit()) {
536                 inUse16 |= 1 << i;
537             }
538         }
539 
540         for (int i = 256; --i >= 0;) {
541             inUse[i] = false;
542         }
543 
544         for (int i = 0; i < 16; i++) {
545             if ((inUse16 & (1 << i)) != 0) {
546                 final int i16 = i << 4;
547                 for (int j = 0; j < 16; j++) {
548                     if (bsGetBit()) {
549                         inUse[i16 + j] = true;
550                     }
551                 }
552             }
553         }
554 
555         makeMaps();
556         final int alphaSize = this.nInUse + 2;
557 
558         /* Now the selectors */
559         final int nGroups = bsR(3);
560         final int nSelectors = bsR(15);
561 
562         for (int i = 0; i < nSelectors; i++) {
563             int j = 0;
564             while (bsGetBit()) {
565                 j++;
566             }
567             selectorMtf[i] = (byte) j;
568         }
569 
570         /* Undo the MTF values for the selectors. */
571         for (int v = nGroups; --v >= 0;) {
572             pos[v] = (byte) v;
573         }
574 
575         for (int i = 0; i < nSelectors; i++) {
576             int v = selectorMtf[i] & 0xff;
577             final byte tmp = pos[v];
578             while (v > 0) {
579                 // nearly all times v is zero, 4 in most other cases
580                 pos[v] = pos[v - 1];
581                 v--;
582             }
583             pos[0] = tmp;
584             selector[i] = tmp;
585         }
586 
587         final char[][] len  = dataShadow.temp_charArray2d;
588 
589         /* Now the coding tables */
590         for (int t = 0; t < nGroups; t++) {
591             int curr = bsR(5);
592             final char[] len_t = len[t];
593             for (int i = 0; i < alphaSize; i++) {
594                 while (bsGetBit()) {
595                     curr += bsGetBit() ? -1 : 1;
596                 }
597                 len_t[i] = (char) curr;
598             }
599         }
600 
601         // finally create the Huffman tables
602         createHuffmanDecodingTables(alphaSize, nGroups);
603     }
604 
605     /**
606      * Called by recvDecodingTables() exclusively.
607      * @param alphaSize
608      * @param nGroups
609      */
createHuffmanDecodingTables(final int alphaSize, final int nGroups)610     private void createHuffmanDecodingTables(final int alphaSize,
611                                              final int nGroups) {
612         final Data dataShadow = this.data;
613         final char[][] len  = dataShadow.temp_charArray2d;
614         final int[] minLens = dataShadow.minLens;
615         final int[][] limit = dataShadow.limit;
616         final int[][] base  = dataShadow.base;
617         final int[][] perm  = dataShadow.perm;
618 
619         for (int t = 0; t < nGroups; t++) {
620             int minLen = 32;
621             int maxLen = 0;
622             final char[] len_t = len[t];
623             for (int i = alphaSize; --i >= 0;) {
624                 final char lent = len_t[i];
625                 if (lent > maxLen) {
626                     maxLen = lent;
627                 }
628                 if (lent < minLen) {
629                     minLen = lent;
630                 }
631             }
632             hbCreateDecodeTables(limit[t], base[t], perm[t], len[t], minLen,
633                                  maxLen, alphaSize);
634             minLens[t] = minLen;
635         }
636     }
637 
getAndMoveToFrontDecode()638   private void getAndMoveToFrontDecode() throws IOException {
639     this.origPtr = bsR(24);
640     recvDecodingTables();
641 
642     final InputStream inShadow = this.in;
643     final Data dataShadow = this.data;
644     final byte[] ll8 = dataShadow.ll8;
645     final int[] unzftab = dataShadow.unzftab;
646     final byte[] selector = dataShadow.selector;
647     final byte[] seqToUnseq = dataShadow.seqToUnseq;
648     final char[] yy = dataShadow.getAndMoveToFrontDecode_yy;
649     final int[] minLens = dataShadow.minLens;
650     final int[][] limit = dataShadow.limit;
651     final int[][] base = dataShadow.base;
652     final int[][] perm = dataShadow.perm;
653     final int limitLast = this.blockSize100k * 100000;
654 
655     /*
656       Setting up the unzftab entries here is not strictly
657       necessary, but it does save having to do it later
658       in a separate pass, and so saves a block's worth of
659       cache misses.
660     */
661     for (int i = 256; --i >= 0;) {
662       yy[i] = (char) i;
663       unzftab[i] = 0;
664     }
665 
666     int groupNo = 0;
667     int groupPos = G_SIZE - 1;
668     final int eob = this.nInUse + 1;
669     int nextSym = getAndMoveToFrontDecode0(0);
670     int bsBuffShadow = this.bsBuff;
671     int bsLiveShadow = this.bsLive;
672     int lastShadow = -1;
673     int zt = selector[groupNo] & 0xff;
674     int[] base_zt = base[zt];
675     int[] limit_zt = limit[zt];
676     int[] perm_zt = perm[zt];
677     int minLens_zt = minLens[zt];
678 
679     while (nextSym != eob) {
680       if ((nextSym == RUNA) || (nextSym == RUNB)) {
681         int s = -1;
682 
683         for (int n = 1; true; n <<= 1) {
684           if (nextSym == RUNA) {
685             s += n;
686           } else if (nextSym == RUNB) {
687             s += n << 1;
688           } else {
689             break;
690           }
691 
692           if (groupPos == 0) {
693             groupPos = G_SIZE - 1;
694             zt = selector[++groupNo] & 0xff;
695             base_zt = base[zt];
696             limit_zt = limit[zt];
697             perm_zt = perm[zt];
698             minLens_zt = minLens[zt];
699           } else {
700             groupPos--;
701           }
702 
703           int zn = minLens_zt;
704 
705           // Inlined:
706           // int zvec = bsR(zn);
707           while (bsLiveShadow < zn) {
708             final int thech = readByteAsInt();//inShadow.read();
709             if (thech < 0)
710               throw new IOException("unexpected end of stream");
711 
712             bsBuffShadow = (bsBuffShadow << 8) | thech;
713             bsLiveShadow += 8;
714             continue;
715           }
716           int zvec = (bsBuffShadow >> (bsLiveShadow - zn)) & ((1 << zn) - 1);
717           bsLiveShadow -= zn;
718 
719           while (zvec > limit_zt[zn]) {
720             zn++;
721             while (bsLiveShadow < 1) {
722               final int thech = readByteAsInt();//inShadow.read();
723               if (thech < 0)
724                 throw new IOException("unexpected end of stream");
725               bsBuffShadow = (bsBuffShadow << 8) | thech;
726               bsLiveShadow += 8;
727               continue;
728             }
729             bsLiveShadow--;
730             zvec = (zvec << 1) | ((bsBuffShadow >> bsLiveShadow) & 1);
731           }
732           nextSym = perm_zt[zvec - base_zt[zn]];
733         }
734 
735         final byte ch = seqToUnseq[yy[0]];
736         unzftab[ch & 0xff] += s + 1;
737 
738         while (s-- >= 0) {
739           ll8[++lastShadow] = ch;
740         }
741 
742         if (lastShadow >= limitLast) {
743           throw new IOException("block overrun");
744         }
745       } else {
746         if (++lastShadow >= limitLast) {
747           throw new IOException("block overrun");
748         }
749 
750         final char tmp = yy[nextSym - 1];
751         unzftab[seqToUnseq[tmp] & 0xff]++;
752         ll8[lastShadow] = seqToUnseq[tmp];
753 
754         /*
755           This loop is hammered during decompression,
756           hence avoid native method call overhead of
757           System.arraycopy for very small ranges to copy.
758         */
759         if (nextSym <= 16) {
760           for (int j = nextSym - 1; j > 0;) {
761             yy[j] = yy[--j];
762           }
763         } else {
764           System.arraycopy(yy, 0, yy, 1, nextSym - 1);
765         }
766 
767         yy[0] = tmp;
768 
769         if (groupPos == 0) {
770           groupPos = G_SIZE - 1;
771           zt = selector[++groupNo] & 0xff;
772           base_zt = base[zt];
773           limit_zt = limit[zt];
774           perm_zt = perm[zt];
775           minLens_zt = minLens[zt];
776         } else {
777           groupPos--;
778         }
779 
780         int zn = minLens_zt;
781 
782         // Inlined:
783         // int zvec = bsR(zn);
784         while (bsLiveShadow < zn) {
785           final int thech = readByteAsInt();//inShadow.read();
786           if (thech < 0)
787             throw new IOException("unexpected end of stream");
788           bsBuffShadow = (bsBuffShadow << 8) | thech;
789           bsLiveShadow += 8;
790           continue;
791         }
792         int zvec = (bsBuffShadow >> (bsLiveShadow - zn)) & ((1 << zn) - 1);
793         bsLiveShadow -= zn;
794 
795         while (zvec > limit_zt[zn]) {
796           zn++;
797           while (bsLiveShadow < 1) {
798             final int thech = readByteAsInt();//inShadow.read();
799             if (thech <0)
800               throw new IOException("unexpected end of stream");
801               bsBuffShadow = (bsBuffShadow << 8) | thech;
802               bsLiveShadow += 8;
803               continue;
804           }
805           bsLiveShadow--;
806           zvec = (zvec << 1) | ((bsBuffShadow >> bsLiveShadow) & 1);
807         }
808         nextSym = perm_zt[zvec - base_zt[zn]];
809       }
810     }
811 
812     this.last = lastShadow;
813     this.bsLive = bsLiveShadow;
814     this.bsBuff = bsBuffShadow;
815   }
816 
getAndMoveToFrontDecode0(final int groupNo)817   private int getAndMoveToFrontDecode0(final int groupNo) throws IOException {
818     final InputStream inShadow = this.in;
819     final Data dataShadow = this.data;
820     final int zt = dataShadow.selector[groupNo] & 0xff;
821     final int[] limit_zt = dataShadow.limit[zt];
822     int zn = dataShadow.minLens[zt];
823     int zvec = bsR(zn);
824     int bsLiveShadow = this.bsLive;
825     int bsBuffShadow = this.bsBuff;
826 
827     while (zvec > limit_zt[zn]) {
828       zn++;
829       while (bsLiveShadow < 1) {
830         final int thech = readByteAsInt();//inShadow.read();
831 
832         if (thech < 0)
833           throw new IOException("unexpected end of stream");
834 
835         bsBuffShadow = (bsBuffShadow << 8) | thech;
836         bsLiveShadow += 8;
837         continue;
838       }
839       bsLiveShadow--;
840       zvec = (zvec << 1) | ((bsBuffShadow >> bsLiveShadow) & 1);
841     }
842 
843     this.bsLive = bsLiveShadow;
844     this.bsBuff = bsBuffShadow;
845 
846     return dataShadow.perm[zt][zvec - dataShadow.base[zt][zn]];
847   }
848 
setupBlock()849     private void setupBlock() throws IOException {
850         if (this.data == null) {
851             return;
852         }
853 
854         final int[] cftab = this.data.cftab;
855         final int[] tt    = this.data.initTT(this.last + 1);
856         final byte[] ll8  = this.data.ll8;
857         cftab[0] = 0;
858         System.arraycopy(this.data.unzftab, 0, cftab, 1, 256);
859 
860         for (int i = 1, c = cftab[0]; i <= 256; i++) {
861             c += cftab[i];
862             cftab[i] = c;
863         }
864 
865         for (int i = 0, lastShadow = this.last; i <= lastShadow; i++) {
866             tt[cftab[ll8[i] & 0xff]++] = i;
867         }
868 
869         if ((this.origPtr < 0) || (this.origPtr >= tt.length)) {
870             throw new IOException("stream corrupted");
871         }
872 
873         this.su_tPos = tt[this.origPtr];
874         this.su_count = 0;
875         this.su_i2 = 0;
876         this.su_ch2 = 256;   /* not a char and not EOF */
877 
878         if (this.blockRandomised) {
879             this.su_rNToGo = 0;
880             this.su_rTPos = 0;
881             setupRandPartA();
882         } else {
883             setupNoRandPartA();
884         }
885     }
886 
setupRandPartA()887     private void setupRandPartA() throws IOException {
888         if (this.su_i2 <= this.last) {
889             this.su_chPrev = this.su_ch2;
890             int su_ch2Shadow = this.data.ll8[this.su_tPos] & 0xff;
891             this.su_tPos = this.data.tt[this.su_tPos];
892             if (this.su_rNToGo == 0) {
893                 this.su_rNToGo = BZip2Constants.rNums[this.su_rTPos] - 1;
894                 if (++this.su_rTPos == 512) {
895                     this.su_rTPos = 0;
896                 }
897             } else {
898                 this.su_rNToGo--;
899             }
900             this.su_ch2 = su_ch2Shadow ^= (this.su_rNToGo == 1) ? 1 : 0;
901             this.su_i2++;
902             this.currentChar = su_ch2Shadow;
903             this.currentState = RAND_PART_B_STATE;
904             this.crc.updateCRC(su_ch2Shadow);
905         } else {
906             endBlock();
907             initBlock();
908             setupBlock();
909         }
910     }
911 
setupNoRandPartA()912     private void setupNoRandPartA() throws IOException {
913         if (this.su_i2 <= this.last) {
914             this.su_chPrev = this.su_ch2;
915             int su_ch2Shadow = this.data.ll8[this.su_tPos] & 0xff;
916             this.su_ch2 = su_ch2Shadow;
917             this.su_tPos = this.data.tt[this.su_tPos];
918             this.su_i2++;
919             this.currentChar = su_ch2Shadow;
920             this.currentState = NO_RAND_PART_B_STATE;
921             this.crc.updateCRC(su_ch2Shadow);
922         } else {
923             this.currentState = NO_RAND_PART_A_STATE;
924             endBlock();
925             initBlock();
926             setupBlock();
927         }
928     }
929 
setupRandPartB()930     private void setupRandPartB() throws IOException {
931         if (this.su_ch2 != this.su_chPrev) {
932             this.currentState = RAND_PART_A_STATE;
933             this.su_count = 1;
934             setupRandPartA();
935         } else if (++this.su_count >= 4) {
936             this.su_z = (char) (this.data.ll8[this.su_tPos] & 0xff);
937             this.su_tPos = this.data.tt[this.su_tPos];
938             if (this.su_rNToGo == 0) {
939                 this.su_rNToGo = BZip2Constants.rNums[this.su_rTPos] - 1;
940                 if (++this.su_rTPos == 512) {
941                     this.su_rTPos = 0;
942                 }
943             } else {
944                 this.su_rNToGo--;
945             }
946             this.su_j2 = 0;
947             this.currentState = RAND_PART_C_STATE;
948             if (this.su_rNToGo == 1) {
949                 this.su_z ^= 1;
950             }
951             setupRandPartC();
952         } else {
953             this.currentState = RAND_PART_A_STATE;
954             setupRandPartA();
955         }
956     }
957 
setupRandPartC()958     private void setupRandPartC() throws IOException {
959         if (this.su_j2 < this.su_z) {
960             this.currentChar = this.su_ch2;
961             this.crc.updateCRC(this.su_ch2);
962             this.su_j2++;
963         } else {
964             this.currentState = RAND_PART_A_STATE;
965             this.su_i2++;
966             this.su_count = 0;
967             setupRandPartA();
968         }
969     }
970 
setupNoRandPartB()971     private void setupNoRandPartB() throws IOException {
972         if (this.su_ch2 != this.su_chPrev) {
973             this.su_count = 1;
974             setupNoRandPartA();
975         } else if (++this.su_count >= 4) {
976             this.su_z = (char) (this.data.ll8[this.su_tPos] & 0xff);
977             this.su_tPos = this.data.tt[this.su_tPos];
978             this.su_j2 = 0;
979             setupNoRandPartC();
980         } else {
981             setupNoRandPartA();
982         }
983     }
984 
setupNoRandPartC()985     private void setupNoRandPartC() throws IOException {
986         if (this.su_j2 < this.su_z) {
987             int su_ch2Shadow = this.su_ch2;
988             this.currentChar = su_ch2Shadow;
989             this.crc.updateCRC(su_ch2Shadow);
990             this.su_j2++;
991             this.currentState = NO_RAND_PART_C_STATE;
992         } else {
993             this.su_i2++;
994             this.su_count = 0;
995             setupNoRandPartA();
996         }
997     }
998 
999     private static final class Data extends Object {
1000 
1001         // (with blockSize 900k)
1002         final boolean[] inUse   = new boolean[256];                                   //      256 byte
1003 
1004         final byte[] seqToUnseq   = new byte[256];                                    //      256 byte
1005         final byte[] selector     = new byte[MAX_SELECTORS];                          //    18002 byte
1006         final byte[] selectorMtf  = new byte[MAX_SELECTORS];                          //    18002 byte
1007 
1008         /**
1009          * Freq table collected to save a pass over the data during
1010          * decompression.
1011          */
1012         final int[] unzftab = new int[256];                                           //     1024 byte
1013 
1014         final int[][] limit = new int[N_GROUPS][MAX_ALPHA_SIZE];                      //     6192 byte
1015         final int[][] base  = new int[N_GROUPS][MAX_ALPHA_SIZE];                      //     6192 byte
1016         final int[][] perm  = new int[N_GROUPS][MAX_ALPHA_SIZE];                      //     6192 byte
1017         final int[] minLens = new int[N_GROUPS];                                      //       24 byte
1018 
1019         final int[]     cftab     = new int[257];                                     //     1028 byte
1020         final char[]    getAndMoveToFrontDecode_yy = new char[256];                   //      512 byte
1021         final char[][]  temp_charArray2d  = new char[N_GROUPS][MAX_ALPHA_SIZE];       //     3096 byte
1022         final byte[] recvDecodingTables_pos = new byte[N_GROUPS];                     //        6 byte
1023         //---------------
1024         //    60798 byte
1025 
1026         int[] tt;                                                                     //  3600000 byte
1027         byte[] ll8;                                                                   //   900000 byte
1028         //---------------
1029         //  4560782 byte
1030         //===============
1031 
Data(int blockSize100k)1032         Data(int blockSize100k) {
1033             super();
1034 
1035             this.ll8 = new byte[blockSize100k * BZip2Constants.baseBlockSize];
1036         }
1037 
1038         /**
1039          * Initializes the {@link #tt} array.
1040          *
1041          * This method is called when the required length of the array
1042          * is known.  I don't initialize it at construction time to
1043          * avoid unnecessary memory allocation when compressing small
1044          * files.
1045          * @param length
1046          * @return int array
1047          */
initTT(int length)1048         final int[] initTT(int length) {
1049             int[] ttShadow = this.tt;
1050 
1051             // tt.length should always be >= length, but theoretically
1052             // it can happen, if the compressor mixed small and large
1053             // blocks.  Normally only the last block will be smaller
1054             // than others.
1055             if ((ttShadow == null) || (ttShadow.length < length)) {
1056                 this.tt = ttShadow = new int[length];
1057             }
1058 
1059             return ttShadow;
1060         }
1061 
1062     }
1063 
1064     @SuppressWarnings("unused")
reportCRCError()1065     private static void reportCRCError() throws IOException {
1066         // The clean way would be to throw an exception.
1067         //throw new IOException("crc error");
1068 
1069         // Just print a message, like the previous versions of this class did
1070         System.err.println("BZip2 CRC error");
1071     }
1072 
1073 }
1074 
1075