Exemple #1
0
  /**
   * Generate the fixed huffman trees as described in the rfc
   *
   * @param huffmanCode - pointer to where the code shall be inserted
   * @param huffmanCodeLength
   * @param distHuffCode
   * @param distHuffCodeLength
   */
  public static void genFixedTree(
      int[] huffmanCode, byte[] huffmanCodeLength, int[] distHuffCode, byte[] distHuffCodeLength) {

    int i;
    // huffmanCodes
    for (i = 0; i <= 143; i++) {
      huffmanCode[i] = 48 + i; // 48==00110000
      huffmanCodeLength[i] = 8;
    }
    for (i = 144; i <= 255; i++) {
      huffmanCode[i] = 400 + i - 144; // 400==110010000
      huffmanCodeLength[i] = 9;
    }
    for (i = 256; i <= 279; i++) {
      huffmanCode[i] = i - 256; // 0==0
      huffmanCodeLength[i] = 7;
    }
    for (i = 280; i < 286; i++) {
      huffmanCode[i] = 192 + i - 280; // 192==11000000
      huffmanCodeLength[i] = 8;
    }
    // reverse all:
    ZipHelper.revHuffTree(huffmanCode, huffmanCodeLength);

    // distHuffCode for non fixed DISTANCE tree
    for (int j = 0; j < distHuffCode.length; j++) {
      distHuffCode[j] = j;
      distHuffCodeLength[j] = 5;
    }
    // reverse all:
    ZipHelper.revHuffTree(distHuffCode, distHuffCodeLength);
  }
  /**
   * Creates an input stream capable of GZIP and Deflate.
   *
   * @param inputStream the stream that contains the compressed data.
   * @param size the size of the internally used buffer
   * @param compressionType TYPE_GZIP or TYPE_DEFLATE
   * @param hash set true for data checking, set false for speed reading
   * @throws IOException when the header of a GZIP stream cannot be skipped
   * @see #TYPE_DEFLATE
   * @see #TYPE_GZIP
   */
  public GZipInputStream(InputStream inputStream, int size, int compressionType, boolean hash)
      throws IOException {
    // System.out.println("creating GZipInputStream with hash=" + hash);
    this.inStream = inputStream;

    this.inStreamEnded = false;
    this.status = GZipInputStream.EXPECTING_HEADER;

    this.hash = hash;
    this.type = compressionType;

    this.smallCodeBuffer = new long[2];
    this.huffmanTree = new short[288 * 4];

    this.distHuffTree = new short[32 * 4];

    this.buffsize = size;
    this.outBuff = new byte[size + 300];
    // #debug
    System.out.println("creating outbuff, size=" + size + ", actual lenth=" + this.outBuff.length);

    if (this.type == GZipInputStream.TYPE_GZIP) {
      ZipHelper.skipheader(inputStream);
    }

    this.crc32 = 0;
  }
  private void processHeader() throws IOException {
    // #debug
    System.out.println("processHeader()");
    int val;

    int HLIT; // number of miniHuff fragments
    int HDIST; // number of distance codes (should somehow lead to the same)
    int HCLEN; // number of length codes

    int[] distHuffCode = new int[30];
    int[] distHuffData = new int[30];
    byte[] distHuffCodeLength = new byte[30];

    int[] huffmanCode =
        new int[286]; // this contains the codes according to the huffman tree/mapping
    int[] huffmanData = new int[286]; // this contains the data referring to the code.
    byte[] huffmanCodeLength = new byte[286];

    this.BFINAL = (popSmallBuffer(1) == 1);

    this.BTYPE = popSmallBuffer(2);

    if (this.BTYPE == 3) {
      throw new IllegalArgumentException();
    } else if (this.BTYPE == 1) {
      // System.out.println(this.allPocessed +  ": fixed tree");

      ZipHelper.genFixedTree(huffmanCode, huffmanCodeLength, distHuffCode, distHuffCodeLength);
      for (int i = 0; i < 286; i++) {
        huffmanData[i] = i;
      }
      for (int i = 0; i < 30; i++) {
        distHuffData[i] = i;
      }

      // convert literal table to tree
      ZipHelper.convertTable2Tree(huffmanCode, huffmanCodeLength, huffmanData, this.huffmanTree);

      // convert distance table to tree
      ZipHelper.convertTable2Tree(
          distHuffCode, distHuffCodeLength, distHuffData, this.distHuffTree);

    } else if (this.BTYPE == 2) {
      // System.out.println(this.allPocessed + ": dynamic tree");

      // read/parse the length codes

      HLIT = popSmallBuffer(5);
      HDIST = popSmallBuffer(5);
      HCLEN = popSmallBuffer(4);

      // miniTree
      int[] miniHuffData = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
      int[] seq = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18};

      byte[] miniHuffCodeLength = new byte[19];
      int[] miniHuffCode = new int[19];

      // read the miniHuffCodeLength
      for (int i = 0; i < HCLEN + 4; i++) {
        miniHuffCodeLength[miniHuffData[i]] = (byte) popSmallBuffer(3);
      }

      ZipHelper.genHuffTree(miniHuffCode, miniHuffCodeLength);
      ZipHelper.revHuffTree(miniHuffCode, miniHuffCodeLength);

      short[] miniTree = new short[19 * 4];
      ZipHelper.convertTable2Tree(miniHuffCode, miniHuffCodeLength, seq, miniTree);

      // parse the length code for the normal Tree and the distance Tree using the miniTree
      for (int i = 0; i < huffmanCodeLength.length; i++) {
        huffmanCodeLength[i] = 0;
      }
      for (int i = 0; i < distHuffCodeLength.length; i++) {
        distHuffCodeLength[i] = 0;
      }
      byte lastVal = 0;
      for (int j = 0; j < HLIT + 257 + HDIST + 1; ) {

        if (this.smallCodeBuffer[1] < 15) {
          refillSmallCodeBuffer();
        }
        val = ZipHelper.deHuffNext(this.smallCodeBuffer, miniTree);

        // data
        if (val < 16) {
          lastVal = (byte) val;
          val = 1;
        } else {
          // repeat code
          if (val == 16) {
            val = popSmallBuffer(2) + 3;
          } else if (val == 17) {
            lastVal = 0;
            val = popSmallBuffer(3) + 3;
          } else if (val == 18) {
            lastVal = 0;
            val = popSmallBuffer(7) + 11;
          }
        }
        // fill the value in
        for (int k = 0; k < val; k++, j++) {
          if (j < HLIT + 257) {
            huffmanCodeLength[j] = lastVal;
          } else {
            distHuffCodeLength[j - (HLIT + 257)] = lastVal;
          }
        }
      }

      // final tree:		 fill this.huffmanCode this.huffmanData
      ZipHelper.genHuffTree(huffmanCode, huffmanCodeLength);
      for (int i = 0; i < huffmanData.length; i++) {
        huffmanData[i] = i;
      }
      // converting literal table to tree
      ZipHelper.revHuffTree(huffmanCode, huffmanCodeLength);
      ZipHelper.convertTable2Tree(huffmanCode, huffmanCodeLength, huffmanData, this.huffmanTree);

      // Distance Tree
      // distHuffData for non fixed distance tree
      // this.distHuffCodeLength is read together with this.huffmanCodeLength
      for (int j = 0; j < distHuffCode.length; j++) {
        distHuffData[j] = j;
      }
      ZipHelper.genHuffTree(distHuffCode, distHuffCodeLength);
      ZipHelper.revHuffTree(distHuffCode, distHuffCodeLength);
      ZipHelper.convertTable2Tree(
          distHuffCode, distHuffCodeLength, distHuffData, this.distHuffTree);

    } else {
      // just skip bits up to the next boundary
      popSmallBuffer(this.smallCodeBuffer[1] & 7); // &7 == %8

      // read and check the header
      this.B0len = popSmallBuffer(8) | popSmallBuffer(8) << 8;
      if (this.smallCodeBuffer[1] < 15) {
        refillSmallCodeBuffer();
      }
      if (this.B0len + (popSmallBuffer(8) | popSmallBuffer(8) << 8) != 0xffff) {
        // Error:  the header for the uncompressed is wrong;
        throw new IOException("3");
      }

      // clear the buffer
      while (this.smallCodeBuffer[1] != 0 && this.B0len > 0) {
        val = popSmallBuffer(8);
        this.window[this.pProcessed] = (byte) val;
        this.pProcessed = (this.pProcessed + 1) & 32767; // == % (1<<15);

        this.outBuff[this.outEnd] = (byte) val;
        this.outEnd++;

        this.B0len--;
      }
    }

    this.status = GZipInputStream.EXPECTING_DATA;

    distHuffCode = null;
    distHuffData = null;
    distHuffCodeLength = null;

    huffmanCodeLength = null;
    huffmanCode = null;
    huffmanData = null;
  }
  /**
   * This function fills the internal outputbuffer reading data form the this.inputStream and
   * inflating it.
   */
  private void inflate() throws IOException {
    // #debug
    System.out.println("inflate - outbuff.length=" + this.outBuff.length);
    int val = 0;

    int rep;
    int rem;

    int cLen;
    int cPos;
    int aPos;

    int copyBytes;

    byte[] myWindow = this.window;
    byte[] myOutBuff = this.outBuff;

    // shift outputbuffer to the beginning
    System.arraycopy(myOutBuff, this.outStart, myOutBuff, 0, this.outEnd - this.outStart);
    this.outEnd -= this.outStart;
    this.outStart = 0;

    this.lastEnd = this.outEnd;

    if (this.B0len == 0) {
      if (this.smallCodeBuffer[1] < 15) {
        refillSmallCodeBuffer();
      }
    }

    // and fill it by parsing the input-stream
    while ((myOutBuff.length - this.outEnd > 300 && (this.smallCodeBuffer[1] > 0 || this.B0len > 0))
        && this.status != GZipInputStream.FINISHED) {

      // parse block header
      if (this.status == GZipInputStream.EXPECTING_HEADER) {
        processHeader();
      }
      // deal with the data

      if (this.status == GZipInputStream.EXPECTING_DATA) {
        // just copy data
        if (this.BTYPE == 0) {

          if (this.B0len > 0) {
            // copy directly
            copyBytes =
                (myOutBuff.length - this.outEnd) > this.B0len
                    ? this.B0len
                    : myOutBuff.length - this.outEnd;

            // at most myOutBuff.length-this.outEnd

            copyBytes = this.inStream.read(myOutBuff, this.outEnd, copyBytes);
            copyIntoWindow(this.pProcessed, copyBytes, myOutBuff, this.outEnd);

            this.outEnd += copyBytes;
            this.pProcessed = (this.pProcessed + copyBytes) & 32767; // % (1<<15);

            this.B0len -= copyBytes;

          } else {
            if (this.BFINAL) {
              this.status = GZipInputStream.EXPECTING_CHECK;
            } else {
              this.status = GZipInputStream.EXPECTING_HEADER;
            }
            if (this.smallCodeBuffer[1] < 15) {
              refillSmallCodeBuffer();
            }
          }

        } // inflate
        else {

          if (this.smallCodeBuffer[1] < 15) {
            refillSmallCodeBuffer();
          }

          val = ZipHelper.deHuffNext(this.smallCodeBuffer, this.huffmanTree);

          // normal single byte
          if (val < 256) {

            // this.window[this.pProcessed]=(byte)val;
            myWindow[this.pProcessed] = (byte) val;
            this.pProcessed = (this.pProcessed + 1) & 32767; // % (1<<15);

            myOutBuff[this.outEnd] = (byte) val;
            this.outEnd++;

          } // copy: pointer + len
          else if (val != 256) {
            if (val > 285) {
              // ERROR: data > 285 was decoded. This is invalid data.;
              throw new IOException("1");
            }

            // parse the pointer

            // cLen
            // 		read some bits
            cLen = popSmallBuffer(ZipHelper.LENGTH_CODE[(val - 257) << 1]);
            //		add the offset
            cLen += ZipHelper.LENGTH_CODE[((val - 257) << 1) + 1];

            // cPos
            //    	resolve the index
            if (this.smallCodeBuffer[1] < 15) {
              refillSmallCodeBuffer();
            }
            // DISTANCE
            val = ZipHelper.deHuffNext(this.smallCodeBuffer, this.distHuffTree);

            //	 	resolve the value
            cPos = popSmallBuffer(ZipHelper.DISTANCE_CODE[val << 1]);

            cPos += ZipHelper.DISTANCE_CODE[(val << 1) + 1];

            // process the pointer (the data does always fit)

            // the absolute starting position for copying data
            aPos = this.pProcessed - cPos;
            aPos += aPos < 0 ? myWindow.length : 0;

            // how often will the data be copied?
            rep = cLen / cPos;
            rem = cLen - cPos * rep;

            for (int j = 0; j < rep; j++) {
              // cPos < cLen
              copyFromWindow(aPos, cPos, myOutBuff, this.outEnd);
              copyIntoWindow(this.pProcessed, cPos, myOutBuff, this.outEnd);
              this.outEnd += cPos;
              this.pProcessed = (this.pProcessed + cPos) & 32767; // % (1<<15);
            }

            // cPos > cLen OR remainder
            copyFromWindow(
                aPos,
                rem,
                myOutBuff,
                this.outEnd); // from window into buffer, and again into window
            copyIntoWindow(this.pProcessed, rem, myOutBuff, this.outEnd);
            this.outEnd += rem;
            this.pProcessed = (this.pProcessed + rem) & 32767; // % (1<<15);

          } // val=256
          else {
            if (this.BFINAL) {
              this.status = GZipInputStream.EXPECTING_CHECK;
            } else {
              this.status = GZipInputStream.EXPECTING_HEADER;
            }
          }
          if (this.smallCodeBuffer[1] < 15) {
            refillSmallCodeBuffer();
          }
        }
      }
      if (this.status == GZipInputStream.EXPECTING_CHECK) {
        // System.out.println(this.allPocessed + " data check");

        this.status = GZipInputStream.FINISHED;

        this.allPocessed = (this.allPocessed + this.outEnd - this.lastEnd) & 4294967295L;
        if (this.hash) {
          // lastEnd -> End in CRC32 einbeziehen
          this.crc32 =
              ZipHelper.crc32(
                  this.crc32Table, this.crc32, myOutBuff, this.lastEnd, this.outEnd - this.lastEnd);
        }

        // skip till next byte boundary, read CRC , isize
        popSmallBuffer(this.smallCodeBuffer[1] & 7);
        int cCrc =
            popSmallBuffer(8)
                | (popSmallBuffer(8) << 8)
                | (popSmallBuffer(8) << 16)
                | (popSmallBuffer(8) << 24);
        int iSize =
            popSmallBuffer(8)
                | (popSmallBuffer(8) << 8)
                | (popSmallBuffer(8) << 16)
                | (popSmallBuffer(8) << 24);

        this.validData = (iSize == this.allPocessed);
        if (this.hash) {
          this.validData &= (this.crc32 == cCrc);
        }

        if (!this.validData) {
          // ERROR: the data check (size & hash) are wrong
          throw new IOException("2");
        }
      }
    }

    // refresh the checksum at once
    if (this.status != GZipInputStream.FINISHED) {
      this.allPocessed = (this.allPocessed + this.outEnd - this.lastEnd) & 4294967295L;
      if (this.hash) {
        // lastEnd -> End in CRC32 einbeziehen
        this.crc32 =
            ZipHelper.crc32(
                this.crc32Table, this.crc32, myOutBuff, this.lastEnd, this.outEnd - this.lastEnd);
      }
    }
  }