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