Code example for Inflater

Methods: finished, getBytesWritten, getRemaining, inflate, setInput

0
      // hand off user data (or what's left of it) to Inflater--but note that 
      // Inflater may not have consumed all of previous bufferload (e.g., if 
      // data highly compressed or output buffer very small), in which case 
      // userBufLen will be zero 
      if (userBufLen > 0) {
        inflater.setInput(userBuf, userBufOff, userBufLen);
        userBufOff += userBufLen;
        userBufLen = 0;
      } 
 
      // now decompress it into b[] 
      try { 
        numAvailBytes = inflater.inflate(b, off, len);
      } catch (DataFormatException dfe) {
        throw new IOException(dfe.getMessage());
      } 
      crc.update(b, off, numAvailBytes);  // CRC-32 is on _uncompressed_ data
      if (inflater.finished()) {
        state = GzipStateLabel.TRAILER_CRC;
        int bytesRemaining = inflater.getRemaining();
        assert (bytesRemaining >= 0) :
          "logic error: Inflater finished; byte-count is inconsistent"; 
          // could save a copy of userBufLen at call to inflater.setInput() and 
          // verify that bytesRemaining <= origUserBufLen, but would have to 
          // be a (class) member variable...seems excessive for a sanity check 
        userBufOff -= bytesRemaining;
        userBufLen = bytesRemaining;   // or "+=", but guaranteed 0 coming in
      } else { 
        return numAvailBytes;  // minor optimization
      } 
    } 
 
    executeTrailerState(); 
 
    return numAvailBytes;
  } 
 
  /** 
   * Parse the gzip header (assuming we're in the appropriate state). 
   * In order to deal with degenerate cases (e.g., user buffer is one byte 
   * long), we copy (some) header bytes to another buffer.  (Filename, 
   * comment, and extra-field bytes are simply skipped.)</p> 
   * 
   * See http://www.ietf.org/rfc/rfc1952.txt for the gzip spec.  Note that 
   * no version of gzip to date (at least through 1.4.0, 2010-01-20) supports 
   * the FHCRC header-CRC16 flagbit; instead, the implementation treats it 
   * as a multi-file continuation flag (which it also doesn't support). :-( 
   * Sun's JDK v6 (1.6) supports the header CRC, however, and so do we. 
   */ 
  private void executeHeaderState() throws IOException { 
 
    // this can happen because DecompressorStream's decompress() is written 
    // to call decompress() first, setInput() second: 
    if (userBufLen <= 0) {
      return; 
    } 
 
    // "basic"/required header:  somewhere in first 10 bytes 
    if (state == GzipStateLabel.HEADER_BASIC) {
      int n = Math.min(userBufLen, 10-localBufOff);  // (or 10-headerBytesRead)
      checkAndCopyBytesToLocal(n);  // modifies userBufLen, etc.
      if (localBufOff >= 10) {      // should be strictly ==
        processBasicHeader();       // sig, compression method, flagbits 
        localBufOff = 0;            // no further need for basic header
        state = GzipStateLabel.HEADER_EXTRA_FIELD;
      } 
    } 
 
    if (userBufLen <= 0) {
      return; 
    } 
 
    // optional header stuff (extra field, filename, comment, header CRC) 
 
    if (state == GzipStateLabel.HEADER_EXTRA_FIELD) {
      if (hasExtraField) {
        // 2 substates:  waiting for 2 bytes => get numExtraFieldBytesRemaining, 
        // or already have 2 bytes & waiting to finish skipping specified length 
        if (numExtraFieldBytesRemaining < 0) {
          int n = Math.min(userBufLen, 2-localBufOff);
          checkAndCopyBytesToLocal(n);
          if (localBufOff >= 2) {
            numExtraFieldBytesRemaining = readUShortLE(localBuf, 0);
            localBufOff = 0;
          } 
        } 
        if (numExtraFieldBytesRemaining > 0 && userBufLen > 0) {
          int n = Math.min(userBufLen, numExtraFieldBytesRemaining);
          checkAndSkipBytes(n);     // modifies userBufLen, etc.
          numExtraFieldBytesRemaining -= n;
        } 
        if (numExtraFieldBytesRemaining == 0) {
          state = GzipStateLabel.HEADER_FILENAME;
        } 
      } else { 
        state = GzipStateLabel.HEADER_FILENAME;
      } 
    } 
 
    if (userBufLen <= 0) {
      return; 
    } 
 
    if (state == GzipStateLabel.HEADER_FILENAME) {
      if (hasFilename) {
        boolean doneWithFilename = checkAndSkipBytesUntilNull();
        if (!doneWithFilename) {
          return;  // exit early:  used up entire buffer without hitting NULL 
        } 
      } 
      state = GzipStateLabel.HEADER_COMMENT;
    } 
 
    if (userBufLen <= 0) {
      return; 
    } 
 
    if (state == GzipStateLabel.HEADER_COMMENT) {
      if (hasComment) {
        boolean doneWithComment = checkAndSkipBytesUntilNull();
        if (!doneWithComment) {
          return;  // exit early:  used up entire buffer 
        } 
      } 
      state = GzipStateLabel.HEADER_CRC;
    } 
 
    if (userBufLen <= 0) {
      return; 
    } 
 
    if (state == GzipStateLabel.HEADER_CRC) {
      if (hasHeaderCRC) {
        assert (localBufOff < 2);
        int n = Math.min(userBufLen, 2-localBufOff);
        copyBytesToLocal(n);
        if (localBufOff >= 2) {
          long headerCRC = readUShortLE(localBuf, 0);
          if (headerCRC != (crc.getValue() & 0xffff)) {
            throw new IOException("gzip header CRC failure");
          } 
          localBufOff = 0;
          crc.reset();
          state = GzipStateLabel.DEFLATE_STREAM;
        } 
      } else { 
        crc.reset();   // will reuse for CRC-32 of uncompressed data
        state = GzipStateLabel.DEFLATE_STREAM;  // switching to Inflater now
      } 
    } 
  } 
 
  /** 
   * Parse the gzip trailer (assuming we're in the appropriate state). 
   * In order to deal with degenerate cases (e.g., user buffer is one byte 
   * long), we copy trailer bytes (all 8 of 'em) to a local buffer.</p> 
   * 
   * See http://www.ietf.org/rfc/rfc1952.txt for the gzip spec. 
   */ 
  private void executeTrailerState() throws IOException { 
 
    if (userBufLen <= 0) {
      return; 
    } 
 
    // verify that the CRC-32 of the decompressed stream matches the value 
    // stored in the gzip trailer 
    if (state == GzipStateLabel.TRAILER_CRC) {
      // localBuf was empty before we handed off to Inflater, so we handle this 
      // exactly like header fields 
      assert (localBufOff < 4);  // initially 0, but may need multiple calls
      int n = Math.min(userBufLen, 4-localBufOff);
      copyBytesToLocal(n);
      if (localBufOff >= 4) {
        long streamCRC = readUIntLE(localBuf, 0);
        if (streamCRC != crc.getValue()) {
          throw new IOException("gzip stream CRC failure");
        } 
        localBufOff = 0;
        crc.reset();
        state = GzipStateLabel.TRAILER_SIZE;
      } 
    } 
 
    if (userBufLen <= 0) {
      return; 
    } 
 
    // verify that the mod-2^32 decompressed stream size matches the value 
    // stored in the gzip trailer 
    if (state == GzipStateLabel.TRAILER_SIZE) {
      assert (localBufOff < 4);  // initially 0, but may need multiple calls
      int n = Math.min(userBufLen, 4-localBufOff);
      copyBytesToLocal(n);       // modifies userBufLen, etc.
      if (localBufOff >= 4) {    // should be strictly ==
        long inputSize = readUIntLE(localBuf, 0);
        if (inputSize != (inflater.getBytesWritten() & 0xffffffff)) {
          throw new IOException(
            "stored gzip size doesn't match decompressed size"); 
        } 
        localBufOff = 0;
        state = GzipStateLabel.FINISHED;