KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > bzip2 > CBZip2InputStream


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.InputStream JavaDoc;
27 import java.io.IOException JavaDoc;
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 JavaDoc implements BZip2Constants {
47
48     private static void reportCRCError() throws IOException JavaDoc {
49         // The clean way would be to throw an exception.
50
//throw new IOException("crc error");
51

52         // Just print a message, like the previous versions of this class did
53
System.err.println("BZip2 CRC error");
54     }
55
56     private void makeMaps() {
57         final boolean[] inUse = this.data.inUse;
58         final byte[] seqToUnseq = this.data.seqToUnseq;
59
60         int nInUseShadow = 0;
61
62         for (int i = 0; i < 256; i++) {
63             if (inUse[i])
64                 seqToUnseq[nInUseShadow++] = (byte) i;
65         }
66
67         this.nInUse = nInUseShadow;
68     }
69
70     /**
71      * Index of the last char in the block, so the block size == last + 1.
72      */

73     private int last;
74
75     /**
76      * Index in zptr[] of original string after sorting.
77      */

78     private int origPtr;
79
80     /**
81      * always: in the range 0 .. 9.
82      * The current block size is 100000 * this number.
83      */

84     private int blockSize100k;
85
86     private boolean blockRandomised;
87
88     private int bsBuff;
89     private int bsLive;
90     private final CRC crc = new CRC();
91
92     private int nInUse;
93
94     private InputStream JavaDoc in;
95
96     private int currentChar = -1;
97
98     private static final int EOF = 0;
99     private static final int START_BLOCK_STATE = 1;
100     private static final int RAND_PART_A_STATE = 2;
101     private static final int RAND_PART_B_STATE = 3;
102     private static final int RAND_PART_C_STATE = 4;
103     private static final int NO_RAND_PART_A_STATE = 5;
104     private static final int NO_RAND_PART_B_STATE = 6;
105     private static final int NO_RAND_PART_C_STATE = 7;
106
107     private int currentState = START_BLOCK_STATE;
108
109     private int storedBlockCRC, storedCombinedCRC;
110     private int computedBlockCRC, computedCombinedCRC;
111
112     // Variables used by setup* methods exclusively
113

114     private int su_count;
115     private int su_ch2;
116     private int su_chPrev;
117     private int su_i2;
118     private int su_j2;
119     private int su_rNToGo;
120     private int su_rTPos;
121     private int su_tPos;
122     private char su_z;
123
124     /**
125      * All memory intensive stuff.
126      * This field is initialized by initBlock().
127      */

128     private CBZip2InputStream.Data data;
129
130     /**
131      * Constructs a new CBZip2InputStream which decompresses bytes read from
132      * 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      * @throws IOException
141      * if the stream content is malformed or an I/O error occurs.
142      * @throws NullPointerException
143      * if <tt>in == null</tt>
144      */

145     public CBZip2InputStream(final InputStream JavaDoc in) throws IOException JavaDoc {
146         super();
147
148         this.in = in;
149         init();
150     }
151
152     public int read() throws IOException JavaDoc {
153         if (this.in != null) {
154             return read0();
155         } else {
156             throw new IOException JavaDoc("stream closed");
157         }
158     }
159
160     public int read(final byte[] dest, final int offs, final int len)
161         throws IOException JavaDoc {
162         if (offs < 0) {
163             throw new IndexOutOfBoundsException JavaDoc("offs(" + offs + ") < 0.");
164         }
165         if (len < 0) {
166             throw new IndexOutOfBoundsException JavaDoc("len(" + len + ") < 0.");
167         }
168         if (offs + len > dest.length) {
169             throw new IndexOutOfBoundsException JavaDoc("offs(" + offs + ") + len("
170                                                 + len + ") > dest.length("
171                                                 + dest.length + ").");
172         }
173         if (this.in == null) {
174             throw new IOException JavaDoc("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         return (destOffs == offs) ? -1 : (destOffs - offs);
184     }
185
186     private int read0() throws IOException JavaDoc {
187         final int retChar = this.currentChar;
188
189         switch (this.currentState) {
190         case EOF:
191             return -1;
192
193         case START_BLOCK_STATE:
194             throw new IllegalStateException JavaDoc();
195
196         case RAND_PART_A_STATE:
197             throw new IllegalStateException JavaDoc();
198
199         case RAND_PART_B_STATE:
200             setupRandPartB();
201             break;
202
203         case RAND_PART_C_STATE:
204             setupRandPartC();
205             break;
206
207         case NO_RAND_PART_A_STATE:
208             throw new IllegalStateException JavaDoc();
209
210         case NO_RAND_PART_B_STATE:
211             setupNoRandPartB();
212             break;
213
214         case NO_RAND_PART_C_STATE:
215             setupNoRandPartC();
216             break;
217
218         default:
219             throw new IllegalStateException JavaDoc();
220         }
221
222         return retChar;
223     }
224
225     private void init() throws IOException JavaDoc {
226         int magic2 = this.in.read();
227         if (magic2 != 'h') {
228             throw new IOException JavaDoc("Stream is not BZip2 formatted: expected 'h'"
229                                   + " as first byte but got '" + (char) magic2
230                                   + "'");
231         }
232
233         int blockSize = this.in.read();
234         if ((blockSize < '1') || (blockSize > '9')) {
235             throw new IOException JavaDoc("Stream is not BZip2 formatted: illegal "
236                                   + "blocksize " + (char) blockSize);
237         }
238
239         this.blockSize100k = blockSize - '0';
240
241         initBlock();
242         setupBlock();
243     }
244
245     private void initBlock() throws IOException JavaDoc {
246         char magic0 = bsGetUByte();
247         char magic1 = bsGetUByte();
248         char magic2 = bsGetUByte();
249         char magic3 = bsGetUByte();
250         char magic4 = bsGetUByte();
251         char magic5 = bsGetUByte();
252
253         if (magic0 == 0x17 &&
254             magic1 == 0x72 &&
255             magic2 == 0x45 &&
256             magic3 == 0x38 &&
257             magic4 == 0x50 &&
258             magic5 == 0x90) {
259             complete(); // end of file
260
} else if (magic0 != 0x31 || // '1'
261
magic1 != 0x41 || // ')'
262
magic2 != 0x59 || // 'Y'
263
magic3 != 0x26 || // '&'
264
magic4 != 0x53 || // 'S'
265
magic5 != 0x59 // 'Y'
266
) {
267             this.currentState = EOF;
268             throw new IOException JavaDoc("bad block header");
269         } else {
270             this.storedBlockCRC = bsGetInt();
271             this.blockRandomised = bsR(1) == 1;
272
273             /**
274              * Allocate data here instead in constructor, so we do not
275              * allocate it if the input file is empty.
276              */

277             if (this.data == null) {
278                 this.data = new Data(this.blockSize100k);
279             }
280
281             // currBlockNo++;
282
getAndMoveToFrontDecode();
283
284             this.crc.initialiseCRC();
285             this.currentState = START_BLOCK_STATE;
286         }
287     }
288
289     private void endBlock() throws IOException JavaDoc {
290         this.computedBlockCRC = this.crc.getFinalCRC();
291
292         // A bad CRC is considered a fatal error.
293
if (this.storedBlockCRC != this.computedBlockCRC) {
294             // make next blocks readable without error
295
// (repair feature, not yet documented, not tested)
296
this.computedCombinedCRC
297                 = (this.storedCombinedCRC << 1)
298                 | (this.storedCombinedCRC >>> 31);
299             this.computedCombinedCRC ^= this.storedBlockCRC;
300
301             reportCRCError();
302         }
303
304         this.computedCombinedCRC
305             = (this.computedCombinedCRC << 1)
306             | (this.computedCombinedCRC >>> 31);
307         this.computedCombinedCRC ^= this.computedBlockCRC;
308     }
309
310     private void complete() throws IOException JavaDoc {
311         this.storedCombinedCRC = bsGetInt();
312         this.currentState = EOF;
313         this.data = null;
314
315         if (this.storedCombinedCRC != this.computedCombinedCRC) {
316             reportCRCError();
317         }
318     }
319
320     public void close() throws IOException JavaDoc {
321         InputStream JavaDoc inShadow = this.in;
322         if (inShadow != null) {
323             try {
324                 if (inShadow != System.in) {
325                     inShadow.close();
326                 }
327             } finally {
328                 this.data = null;
329                 this.in = null;
330             }
331         }
332     }
333
334     private int bsR(final int n) throws IOException JavaDoc {
335         int bsLiveShadow = this.bsLive;
336         int bsBuffShadow = this.bsBuff;
337
338         if (bsLiveShadow < n) {
339             final InputStream JavaDoc inShadow = this.in;
340             do {
341                 int thech = inShadow.read();
342
343                 if (thech < 0) {
344                     throw new IOException JavaDoc("unexpected end of stream");
345                 }
346
347                 bsBuffShadow = (bsBuffShadow << 8) | thech;
348                 bsLiveShadow += 8;
349             } while (bsLiveShadow < n);
350
351             this.bsBuff = bsBuffShadow;
352         }
353
354         this.bsLive = bsLiveShadow - n;
355         return (bsBuffShadow >> (bsLiveShadow - n)) & ((1 << n) - 1);
356     }
357
358     private boolean bsGetBit() throws IOException JavaDoc {
359         int bsLiveShadow = this.bsLive;
360         int bsBuffShadow = this.bsBuff;
361
362         if (bsLiveShadow < 1) {
363             int thech = this.in.read();
364
365             if (thech < 0) {
366                 throw new IOException JavaDoc("unexpected end of stream");
367             }
368
369             bsBuffShadow = (bsBuffShadow << 8) | thech;
370             bsLiveShadow += 8;
371             this.bsBuff = bsBuffShadow;
372         }
373
374         this.bsLive = bsLiveShadow - 1;
375         return ((bsBuffShadow >> (bsLiveShadow - 1)) & 1) != 0;
376     }
377
378     private char bsGetUByte() throws IOException JavaDoc {
379         return (char) bsR(8);
380     }
381
382     private int bsGetInt() throws IOException JavaDoc {
383         return (((((bsR(8) << 8) | bsR(8)) << 8) | bsR(8)) << 8) | bsR(8);
384     }
385
386     /**
387      * Called by createHuffmanDecodingTables() exclusively.
388      */

389     private static void hbCreateDecodeTables(final int[] limit,
390                                              final int[] base,
391                                              final int[] perm,
392                                              final char[] length,
393                                              final int minLen,
394                                              final int maxLen,
395                                              final int alphaSize) {
396         for (int i = minLen, pp = 0; i <= maxLen; i++) {
397             for (int j = 0; j < alphaSize; j++) {
398                 if (length[j] == i) {
399                     perm[pp++] = j;
400                 }
401             }
402         }
403
404         for (int i = MAX_CODE_LEN; --i > 0;) {
405             base[i] = 0;
406             limit[i] = 0;
407         }
408
409         for (int i = 0; i < alphaSize; i++) {
410             base[length[i] + 1]++;
411         }
412
413         for (int i = 1, b = base[0]; i < MAX_CODE_LEN; i++) {
414             b += base[i];
415             base[i] = b;
416         }
417
418         for (int i = minLen, vec = 0, b = base[i]; i <= maxLen; i++) {
419             final int nb = base[i + 1];
420             vec += nb - b;
421             b = nb;
422             limit[i] = vec - 1;
423             vec <<= 1;
424         }
425
426         for (int i = minLen + 1; i <= maxLen; i++) {
427             base[i] = ((limit[i - 1] + 1) << 1) - base[i];
428         }
429     }
430
431     private void recvDecodingTables() throws IOException JavaDoc {
432         final Data dataShadow = this.data;
433         final boolean[] inUse = dataShadow.inUse;
434         final byte[] pos = dataShadow.recvDecodingTables_pos;
435         final byte[] selector = dataShadow.selector;
436         final byte[] selectorMtf = dataShadow.selectorMtf;
437
438         int inUse16 = 0;
439
440         /* Receive the mapping table */
441         for (int i = 0; i < 16; i++) {
442             if (bsGetBit()) {
443                 inUse16 |= 1 << i;
444             }
445         }
446
447         for (int i = 256; --i >= 0;) {
448             inUse[i] = false;
449         }
450
451         for (int i = 0; i < 16; i++) {
452             if ((inUse16 & (1 << i)) != 0) {
453                 final int i16 = i << 4;
454                 for (int j = 0; j < 16; j++) {
455                     if (bsGetBit()) {
456                         inUse[i16 + j] = true;
457                     }
458                 }
459             }
460         }
461
462         makeMaps();
463         final int alphaSize = this.nInUse + 2;
464
465         /* Now the selectors */
466         final int nGroups = bsR(3);
467         final int nSelectors = bsR(15);
468
469         for (int i = 0; i < nSelectors; i++) {
470             int j = 0;
471             while (bsGetBit()) {
472                 j++;
473             }
474             selectorMtf[i] = (byte) j;
475         }
476
477         /* Undo the MTF values for the selectors. */
478         for (int v = nGroups; --v >= 0;) {
479             pos[v] = (byte) v;
480         }
481
482         for (int i = 0; i < nSelectors; i++) {
483             int v = selectorMtf[i] & 0xff;
484             final byte tmp = pos[v];
485             while (v > 0) {
486                 // nearly all times v is zero, 4 in most other cases
487
pos[v] = pos[v - 1];
488                 v--;
489             }
490             pos[0] = tmp;
491             selector[i] = tmp;
492         }
493
494         final char[][] len = dataShadow.temp_charArray2d;
495
496         /* Now the coding tables */
497         for (int t = 0; t < nGroups; t++) {
498             int curr = bsR(5);
499             final char[] len_t = len[t];
500             for (int i = 0; i < alphaSize; i++) {
501                 while (bsGetBit()) {
502                     curr += bsGetBit() ? -1 : 1;
503                 }
504                 len_t[i] = (char) curr;
505             }
506         }
507
508         // finally create the Huffman tables
509
createHuffmanDecodingTables(alphaSize, nGroups);
510     }
511
512     /**
513      * Called by recvDecodingTables() exclusively.
514      */

515     private void createHuffmanDecodingTables(final int alphaSize,
516                                              final int nGroups) {
517         final Data dataShadow = this.data;
518         final char[][] len = dataShadow.temp_charArray2d;
519         final int[] minLens = dataShadow.minLens;
520         final int[][] limit = dataShadow.limit;
521         final int[][] base = dataShadow.base;
522         final int[][] perm = dataShadow.perm;
523
524         for (int t = 0; t < nGroups; t++) {
525             int minLen = 32;
526             int maxLen = 0;
527             final char[] len_t = len[t];
528             for (int i = alphaSize; --i >= 0;) {
529                 final char lent = len_t[i];
530                 if (lent > maxLen) {
531                     maxLen = lent;
532                 }
533                 if (lent < minLen) {
534                     minLen = lent;
535                 }
536             }
537             hbCreateDecodeTables(limit[t], base[t], perm[t], len[t], minLen,
538                                  maxLen, alphaSize);
539             minLens[t] = minLen;
540         }
541     }
542
543     private void getAndMoveToFrontDecode() throws IOException JavaDoc {
544         this.origPtr = bsR(24);
545         recvDecodingTables();
546
547         final InputStream JavaDoc inShadow = this.in;
548         final Data dataShadow = this.data;
549         final byte[] ll8 = dataShadow.ll8;
550         final int[] unzftab = dataShadow.unzftab;
551         final byte[] selector = dataShadow.selector;
552         final byte[] seqToUnseq = dataShadow.seqToUnseq;
553         final char[] yy = dataShadow.getAndMoveToFrontDecode_yy;
554         final int[] minLens = dataShadow.minLens;
555         final int[][] limit = dataShadow.limit;
556         final int[][] base = dataShadow.base;
557         final int[][] perm = dataShadow.perm;
558         final int limitLast = this.blockSize100k * 100000;
559
560         /*
561           Setting up the unzftab entries here is not strictly
562           necessary, but it does save having to do it later
563           in a separate pass, and so saves a block's worth of
564           cache misses.
565         */

566         for (int i = 256; --i >= 0;) {
567             yy[i] = (char) i;
568             unzftab[i] = 0;
569         }
570
571         int groupNo = 0;
572         int groupPos = G_SIZE - 1;
573         final int eob = this.nInUse + 1;
574         int nextSym = getAndMoveToFrontDecode0(0);
575         int bsBuffShadow = this.bsBuff;
576         int bsLiveShadow = this.bsLive;
577         int lastShadow = -1;
578         int zt = selector[groupNo] & 0xff;
579         int[] base_zt = base[zt];
580         int[] limit_zt = limit[zt];
581         int[] perm_zt = perm[zt];
582         int minLens_zt = minLens[zt];
583
584         while (nextSym != eob) {
585             if ((nextSym == RUNA) || (nextSym == RUNB)) {
586                 int s = -1;
587
588                 for (int n = 1; true; n <<= 1) {
589                     if (nextSym == RUNA) {
590                         s += n;
591                     } else if (nextSym == RUNB) {
592                         s += n << 1;
593                     } else {
594                         break;
595                     }
596
597                     if (groupPos == 0) {
598                         groupPos = G_SIZE - 1;
599                         zt = selector[++groupNo] & 0xff;
600                         base_zt = base[zt];
601                         limit_zt = limit[zt];
602                         perm_zt = perm[zt];
603                         minLens_zt = minLens[zt];
604                     } else {
605                         groupPos--;
606                     }
607
608                     int zn = minLens_zt;
609
610                     // Inlined:
611
// int zvec = bsR(zn);
612
while (bsLiveShadow < zn) {
613                         final int thech = inShadow.read();
614                         if (thech >= 0) {
615                             bsBuffShadow = (bsBuffShadow << 8) | thech;
616                             bsLiveShadow += 8;
617                             continue;
618                         } else {
619                             throw new IOException JavaDoc("unexpected end of stream");
620                         }
621                     }
622                     int zvec = (bsBuffShadow >> (bsLiveShadow - zn)) & ((1 << zn) - 1);
623                     bsLiveShadow -= zn;
624
625                     while (zvec > limit_zt[zn]) {
626                         zn++;
627                         while (bsLiveShadow < 1) {
628                             final int thech = inShadow.read();
629                             if (thech >= 0) {
630                                 bsBuffShadow = (bsBuffShadow << 8) | thech;
631                                 bsLiveShadow += 8;
632                                 continue;
633                             } else {
634                                 throw new IOException JavaDoc("unexpected end of stream");
635                             }
636                         }
637                         bsLiveShadow--;
638                         zvec = (zvec << 1) | ((bsBuffShadow >> bsLiveShadow) & 1);
639                     }
640                     nextSym = perm_zt[zvec - base_zt[zn]];
641                 }
642
643                 final byte ch = seqToUnseq[yy[0]];
644                 unzftab[ch & 0xff] += s + 1;
645
646                 while (s-- >= 0) {
647                     ll8[++lastShadow] = ch;
648                 }
649
650                 if (lastShadow >= limitLast) {
651                     throw new IOException JavaDoc("block overrun");
652                 }
653             } else {
654                 if (++lastShadow >= limitLast) {
655                     throw new IOException JavaDoc("block overrun");
656                 }
657
658                 final char tmp = yy[nextSym - 1];
659                 unzftab[seqToUnseq[tmp] & 0xff]++;
660                 ll8[lastShadow] = seqToUnseq[tmp];
661
662                 /*
663                   This loop is hammered during decompression,
664                   hence avoid native method call overhead of
665                   System.arraycopy for very small ranges to copy.
666                 */

667                 if (nextSym <= 16) {
668                     for (int j = nextSym - 1; j > 0;) {
669                         yy[j] = yy[--j];
670                     }
671                 } else {
672                     System.arraycopy(yy, 0, yy, 1, nextSym - 1);
673                 }
674
675                 yy[0] = tmp;
676
677                 if (groupPos == 0) {
678                     groupPos = G_SIZE - 1;
679                     zt = selector[++groupNo] & 0xff;
680                     base_zt = base[zt];
681                     limit_zt = limit[zt];
682                     perm_zt = perm[zt];
683                     minLens_zt = minLens[zt];
684                 } else {
685                     groupPos--;
686                 }
687
688                 int zn = minLens_zt;
689
690                 // Inlined:
691
// int zvec = bsR(zn);
692
while (bsLiveShadow < zn) {
693                     final int thech = inShadow.read();
694                     if (thech >= 0) {
695                         bsBuffShadow = (bsBuffShadow << 8) | thech;
696                         bsLiveShadow += 8;
697                         continue;
698                     } else {
699                         throw new IOException JavaDoc("unexpected end of stream");
700                     }
701                 }
702                 int zvec = (bsBuffShadow >> (bsLiveShadow - zn)) & ((1 << zn) - 1);
703                 bsLiveShadow -= zn;
704
705                 while (zvec > limit_zt[zn]) {
706                     zn++;
707                     while (bsLiveShadow < 1) {
708                         final int thech = inShadow.read();
709                         if (thech >= 0) {
710                             bsBuffShadow = (bsBuffShadow << 8) | thech;
711                             bsLiveShadow += 8;
712                             continue;
713                         } else {
714                             throw new IOException JavaDoc("unexpected end of stream");
715                         }
716                     }
717                     bsLiveShadow--;
718                     zvec = (zvec << 1) | ((bsBuffShadow >> bsLiveShadow) & 1);
719                 }
720                 nextSym = perm_zt[zvec - base_zt[zn]];
721             }
722         }
723
724         this.last = lastShadow;
725         this.bsLive = bsLiveShadow;
726         this.bsBuff = bsBuffShadow;
727     }
728
729     private int getAndMoveToFrontDecode0(final int groupNo)
730         throws IOException JavaDoc {
731         final InputStream JavaDoc inShadow = this.in;
732         final Data dataShadow = this.data;
733         final int zt = dataShadow.selector[groupNo] & 0xff;
734         final int[] limit_zt = dataShadow.limit[zt];
735         int zn = dataShadow.minLens[zt];
736         int zvec = bsR(zn);
737         int bsLiveShadow = this.bsLive;
738         int bsBuffShadow = this.bsBuff;
739
740         while (zvec > limit_zt[zn]) {
741             zn++;
742             while (bsLiveShadow < 1) {
743                 final int thech = inShadow.read();
744
745                 if (thech >= 0) {
746                     bsBuffShadow = (bsBuffShadow << 8) | thech;
747                     bsLiveShadow += 8;
748                     continue;
749                 } else {
750                     throw new IOException JavaDoc("unexpected end of stream");
751                 }
752             }
753             bsLiveShadow--;
754             zvec = (zvec << 1) | ((bsBuffShadow >> bsLiveShadow) & 1);
755         }
756
757         this.bsLive = bsLiveShadow;
758         this.bsBuff = bsBuffShadow;
759
760         return dataShadow.perm[zt][zvec - dataShadow.base[zt][zn]];
761     }
762
763     private void setupBlock() throws IOException JavaDoc {
764         if (this.data == null) {
765             return;
766         }
767
768         final int[] cftab = this.data.cftab;
769         final int[] tt = this.data.initTT(this.last + 1);
770         final byte[] ll8 = this.data.ll8;
771         cftab[0] = 0;
772         System.arraycopy(this.data.unzftab, 0, cftab, 1, 256);
773
774         for (int i = 1, c = cftab[0]; i <= 256; i++) {
775             c += cftab[i];
776             cftab[i] = c;
777         }
778
779         for (int i = 0, lastShadow = this.last; i <= lastShadow; i++) {
780             tt[cftab[ll8[i] & 0xff]++] = i;
781         }
782
783         if ((this.origPtr < 0) || (this.origPtr >= tt.length)) {
784             throw new IOException JavaDoc("stream corrupted");
785         }
786
787         this.su_tPos = tt[this.origPtr];
788         this.su_count = 0;
789         this.su_i2 = 0;
790         this.su_ch2 = 256; /* not a char and not EOF */
791
792         if (this.blockRandomised) {
793             this.su_rNToGo = 0;
794             this.su_rTPos = 0;
795             setupRandPartA();
796         } else {
797             setupNoRandPartA();
798         }
799     }
800
801     private void setupRandPartA() throws IOException JavaDoc {
802         if (this.su_i2 <= this.last) {
803             this.su_chPrev = this.su_ch2;
804             int su_ch2Shadow = this.data.ll8[this.su_tPos] & 0xff;
805             this.su_tPos = this.data.tt[this.su_tPos];
806             if (this.su_rNToGo == 0) {
807                 this.su_rNToGo = BZip2Constants.rNums[this.su_rTPos] - 1;
808                 if (++this.su_rTPos == 512) {
809                     this.su_rTPos = 0;
810                 }
811             } else {
812                 this.su_rNToGo--;
813             }
814             this.su_ch2 = su_ch2Shadow ^= (this.su_rNToGo == 1) ? 1 : 0;
815             this.su_i2++;
816             this.currentChar = su_ch2Shadow;
817             this.currentState = RAND_PART_B_STATE;
818             this.crc.updateCRC(su_ch2Shadow);
819         } else {
820             endBlock();
821             initBlock();
822             setupBlock();
823         }
824     }
825
826     private void setupNoRandPartA() throws IOException JavaDoc {
827         if (this.su_i2 <= this.last) {
828             this.su_chPrev = this.su_ch2;
829             int su_ch2Shadow = this.data.ll8[this.su_tPos] & 0xff;
830             this.su_ch2 = su_ch2Shadow;
831             this.su_tPos = this.data.tt[this.su_tPos];
832             this.su_i2++;
833             this.currentChar = su_ch2Shadow;
834             this.currentState = NO_RAND_PART_B_STATE;
835             this.crc.updateCRC(su_ch2Shadow);
836         } else {
837             this.currentState = NO_RAND_PART_A_STATE;
838             endBlock();
839             initBlock();
840             setupBlock();
841         }
842     }
843
844     private void setupRandPartB() throws IOException JavaDoc {
845         if (this.su_ch2 != this.su_chPrev) {
846             this.currentState = RAND_PART_A_STATE;
847             this.su_count = 1;
848             setupRandPartA();
849         } else if (++this.su_count >= 4) {
850             this.su_z = (char) (this.data.ll8[this.su_tPos] & 0xff);
851             this.su_tPos = this.data.tt[this.su_tPos];
852             if (this.su_rNToGo == 0) {
853                 this.su_rNToGo = BZip2Constants.rNums[this.su_rTPos] - 1;
854                 if (++this.su_rTPos == 512) {
855                     this.su_rTPos = 0;
856                 }
857             } else {
858                 this.su_rNToGo--;
859             }
860             this.su_j2 = 0;
861             this.currentState = RAND_PART_C_STATE;
862             if (this.su_rNToGo == 1) {
863                 this.su_z ^= 1;
864             }
865             setupRandPartC();
866         } else {
867             this.currentState = RAND_PART_A_STATE;
868             setupRandPartA();
869         }
870     }
871
872     private void setupRandPartC() throws IOException JavaDoc {
873         if (this.su_j2 < this.su_z) {
874             this.currentChar = this.su_ch2;
875             this.crc.updateCRC(this.su_ch2);
876             this.su_j2++;
877         } else {
878             this.currentState = RAND_PART_A_STATE;
879             this.su_i2++;
880             this.su_count = 0;
881             setupRandPartA();
882         }
883     }
884
885     private void setupNoRandPartB() throws IOException JavaDoc {
886         if (this.su_ch2 != this.su_chPrev) {
887             this.su_count = 1;
888             setupNoRandPartA();
889         } else if (++this.su_count >= 4) {
890             this.su_z = (char) (this.data.ll8[this.su_tPos] & 0xff);
891             this.su_tPos = this.data.tt[this.su_tPos];
892             this.su_j2 = 0;
893             setupNoRandPartC();
894         } else {
895             setupNoRandPartA();
896         }
897     }
898
899     private void setupNoRandPartC() throws IOException JavaDoc {
900         if (this.su_j2 < this.su_z) {
901             int su_ch2Shadow = this.su_ch2;
902             this.currentChar = su_ch2Shadow;
903             this.crc.updateCRC(su_ch2Shadow);
904             this.su_j2++;
905             this.currentState = NO_RAND_PART_C_STATE;
906         } else {
907             this.su_i2++;
908             this.su_count = 0;
909             setupNoRandPartA();
910         }
911     }
912
913     private static final class Data extends Object JavaDoc {
914
915         // (with blockSize 900k)
916
final boolean[] inUse = new boolean[256]; // 256 byte
917

918         final byte[] seqToUnseq = new byte[256]; // 256 byte
919
final byte[] selector = new byte[MAX_SELECTORS]; // 18002 byte
920
final byte[] selectorMtf = new byte[MAX_SELECTORS]; // 18002 byte
921

922         /**
923          * Freq table collected to save a pass over the data during
924          * decompression.
925          */

926         final int[] unzftab = new int[256]; // 1024 byte
927

928         final int[][] limit = new int[N_GROUPS][MAX_ALPHA_SIZE]; // 6192 byte
929
final int[][] base = new int[N_GROUPS][MAX_ALPHA_SIZE]; // 6192 byte
930
final int[][] perm = new int[N_GROUPS][MAX_ALPHA_SIZE]; // 6192 byte
931
final int[] minLens = new int[N_GROUPS]; // 24 byte
932

933         final int[] cftab = new int[257]; // 1028 byte
934
final char[] getAndMoveToFrontDecode_yy = new char[256]; // 512 byte
935
final char[][] temp_charArray2d = new char[N_GROUPS][MAX_ALPHA_SIZE]; // 3096 byte
936
final byte[] recvDecodingTables_pos = new byte[N_GROUPS]; // 6 byte
937
//---------------
938
// 60798 byte
939

940         int[] tt; // 3600000 byte
941
byte[] ll8; // 900000 byte
942
//---------------
943
// 4560782 byte
944
//===============
945

946         Data(int blockSize100k) {
947             super();
948
949             this.ll8 = new byte[blockSize100k * BZip2Constants.baseBlockSize];
950         }
951
952         /**
953          * Initializes the {@link #tt} array.
954          *
955          * This method is called when the required length of the array
956          * is known. I don't initialize it at construction time to
957          * avoid unneccessary memory allocation when compressing small
958          * files.
959          */

960         final int[] initTT(int length) {
961             int[] ttShadow = this.tt;
962
963             // tt.length should always be >= length, but theoretically
964
// it can happen, if the compressor mixed small and large
965
// blocks. Normally only the last block will be smaller
966
// than others.
967
if ((ttShadow == null) || (ttShadow.length < length)) {
968                 this.tt = ttShadow = new int[length];
969             }
970
971             return ttShadow;
972         }
973
974     }
975 }
976
977
Popular Tags