/** * @param compressed_size or use null if not known * @param uncompressed_size or use null if not known * @return uncompressed ByteArrayOutputStream * @throws IOException * @throws DataFormatException */ private ByteArrayOutputStream unCompress(Integer compressed_size, Integer uncompressed_size) throws IOException, DataFormatException { byte[] uncompressed_data = null; byte[] input_data = null; ByteArrayOutputStream ret = new ByteArrayOutputStream(); Inflater decompresser = new Inflater(false); long first_seek = fileChannel.position(); Boolean uncompressing = true; while (uncompressing) { if (decompresser.needsInput()) { input_data = new byte[(compressed_size != null) ? compressed_size.intValue() : 1024]; fileChannel.read(ByteBuffer.wrap(input_data)); decompresser.setInput(input_data, 0, input_data.length); } uncompressed_data = new byte [(uncompressed_size != null) ? uncompressed_size.intValue() : (input_data.length * 4)]; decompresser.inflate(uncompressed_data); int op = (int) (decompresser.getBytesWritten() - (long) ret.size()); if (op > 0) ret.write(uncompressed_data, 0, op); if (decompresser.finished()) uncompressing = false; } fileChannel.position( (first_seek + decompresser.getBytesRead())); // move file pointer to start of next stream decompresser.end(); return ret; }
/** * {@inheritDoc} * * <p>If the decoding did not produce any output, for example because it consumed gzip header or * trailer bytes, it returns a buffer with zero capacity. * * <p>This method never returns null. * * <p>The given {@code buffer}'s position will be modified to reflect the bytes consumed during * the decoding. * * <p>The decoding may be finished without consuming the buffer completely if the buffer contains * gzip bytes plus other bytes (either plain or gzipped). */ @Override public ByteBuffer decode(ByteBuffer buffer) { try { while (buffer.hasRemaining()) { byte currByte = buffer.get(); switch (state) { case INITIAL: { buffer.position(buffer.position() - 1); state = State.ID; break; } case ID: { value += (currByte & 0xFF) << 8 * size; ++size; if (size == 2) { if (value != 0x8B1F) throw new ZipException("Invalid gzip bytes"); state = State.CM; } break; } case CM: { if ((currByte & 0xFF) != 0x08) throw new ZipException("Invalid gzip compression method"); state = State.FLG; break; } case FLG: { flags = currByte; state = State.MTIME; size = 0; value = 0; break; } case MTIME: { // Skip the 4 MTIME bytes ++size; if (size == 4) state = State.XFL; break; } case XFL: { // Skip XFL state = State.OS; break; } case OS: { // Skip OS state = State.FLAGS; break; } case FLAGS: { buffer.position(buffer.position() - 1); if ((flags & 0x04) == 0x04) { state = State.EXTRA_LENGTH; size = 0; value = 0; } else if ((flags & 0x08) == 0x08) state = State.NAME; else if ((flags & 0x10) == 0x10) state = State.COMMENT; else if ((flags & 0x2) == 0x2) { state = State.HCRC; size = 0; value = 0; } else state = State.DATA; break; } case EXTRA_LENGTH: { value += (currByte & 0xFF) << 8 * size; ++size; if (size == 2) state = State.EXTRA; break; } case EXTRA: { // Skip EXTRA bytes --value; if (value == 0) { // Clear the EXTRA flag and loop on the flags flags &= ~0x04; state = State.FLAGS; } break; } case NAME: { // Skip NAME bytes if (currByte == 0) { // Clear the NAME flag and loop on the flags flags &= ~0x08; state = State.FLAGS; } break; } case COMMENT: { // Skip COMMENT bytes if (currByte == 0) { // Clear the COMMENT flag and loop on the flags flags &= ~0x10; state = State.FLAGS; } break; } case HCRC: { // Skip HCRC ++size; if (size == 2) { // Clear the HCRC flag and loop on the flags flags &= ~0x02; state = State.FLAGS; } break; } case DATA: { buffer.position(buffer.position() - 1); while (true) { int decoded = inflate(bytes); if (decoded == 0) { if (inflater.needsInput()) { if (buffer.hasRemaining()) { byte[] input = new byte[buffer.remaining()]; buffer.get(input); inflater.setInput(input); } else { if (output != null) { ByteBuffer result = ByteBuffer.wrap(output); output = null; return result; } break; } } else if (inflater.finished()) { int remaining = inflater.getRemaining(); buffer.position(buffer.limit() - remaining); state = State.CRC; size = 0; value = 0; break; } else { throw new ZipException("Invalid inflater state"); } } else { if (output == null) { // Save the inflated bytes and loop to see if we have finished output = Arrays.copyOf(bytes, decoded); } else { // Accumulate inflated bytes and loop to see if we have finished byte[] newOutput = Arrays.copyOf(output, output.length + decoded); System.arraycopy(bytes, 0, newOutput, output.length, decoded); output = newOutput; } } } break; } case CRC: { value += (currByte & 0xFF) << 8 * size; ++size; if (size == 4) { // From RFC 1952, compliant decoders need not to verify the CRC state = State.ISIZE; size = 0; value = 0; } break; } case ISIZE: { value += (currByte & 0xFF) << 8 * size; ++size; if (size == 4) { if (value != inflater.getBytesWritten()) throw new ZipException("Invalid input size"); ByteBuffer result = output == null ? BufferUtil.EMPTY_BUFFER : ByteBuffer.wrap(output); reset(); return result; } break; } default: throw new ZipException(); } } return BufferUtil.EMPTY_BUFFER; } catch (ZipException x) { throw new RuntimeException(x); } }