package org.amse.ys.zip; import java.io.*; public class DeflatingDecompressor extends Decompressor { static { System.loadLibrary("DeflatingDecompressor"); } // common variables private MyBufferedInputStream myStream; private int myCompressedAvailable; private int myAvailable; private static final int IN_BUFFER_SIZE = 2048; private static final int OUT_BUFFER_SIZE = 32768; private final byte[] myInBuffer = new byte[IN_BUFFER_SIZE]; private int myInBufferOffset; private int myInBufferLength; private final byte[] myOutBuffer = new byte[OUT_BUFFER_SIZE]; private int myOutBufferOffset; private int myOutBufferLength; private boolean myInflatingInProgress; public DeflatingDecompressor(MyBufferedInputStream inputStream, LocalFileHeader header) throws IOException { super(); reset(inputStream, header); } void reset(MyBufferedInputStream inputStream, LocalFileHeader header) throws IOException { if (myInflatingInProgress) { endInflating(); myInflatingInProgress = false; } myStream = inputStream; myCompressedAvailable = header.CompressedSize; if (myCompressedAvailable <= 0) { myCompressedAvailable = Integer.MAX_VALUE; } myAvailable = header.UncompressedSize; if (myAvailable <= 0) { myAvailable = Integer.MAX_VALUE; } myInBufferOffset = IN_BUFFER_SIZE; myInBufferLength = 0; myOutBufferOffset = OUT_BUFFER_SIZE; myOutBufferLength = 0; startInflating(); myInflatingInProgress = true; } @Override public int available() { return myAvailable; } @Override public int read(byte[] b, int off, int len) throws IOException { if (myAvailable <= 0) { return -1; } if (len > myAvailable) { len = myAvailable; } for (int toFill = len; toFill > 0; ) { if (myOutBufferLength == 0) { fillOutBuffer(); } if (myOutBufferLength == 0) { if (myInflatingInProgress) { throw new IOException("cannot read from zip"); } else { len -= toFill; break; } } final int ready = (toFill < myOutBufferLength) ? toFill : myOutBufferLength; if (b != null) { System.arraycopy(myOutBuffer, myOutBufferOffset, b, off, ready); } off += ready; myOutBufferOffset += ready; toFill -= ready; myOutBufferLength -= ready; } myAvailable -= len; return len; } @Override public int read() throws IOException { if (myAvailable <= 0) { return -1; } if (myOutBufferLength == 0) { fillOutBuffer(); } if (myOutBufferLength == 0) { if (myInflatingInProgress) { throw new IOException("cannot read from zip"); } else { myAvailable = 0; return -1; } } --myAvailable; --myOutBufferLength; return myOutBuffer[myOutBufferOffset++]; } private void fillOutBuffer() throws IOException { if (!myInflatingInProgress) { return; } while (myOutBufferLength == 0) { if (myInBufferLength == 0) { myInBufferOffset = 0; final int toRead = (myCompressedAvailable < IN_BUFFER_SIZE) ? myCompressedAvailable : IN_BUFFER_SIZE; myInBufferLength = myStream.read(myInBuffer, 0, toRead); if (myInBufferLength < toRead) { myCompressedAvailable = 0; } else { myCompressedAvailable -= toRead; } } if (myInBufferLength == 0) { break; } final long result = inflate(myInBuffer, myInBufferOffset, myInBufferLength, myOutBuffer); if (result == 0) { throw new IOException("cannot read from base stream"); } final int in = (int)(result >> 16) & 0xFFFF; final int out = (int)result & 0xFFFF; myInBufferOffset += in; myInBufferLength -= in; myOutBufferOffset = 0; myOutBufferLength = out; if ((result & (1L << 32)) != 0) { endInflating(); myInflatingInProgress = false; myStream.backSkip(myInBufferLength); break; } } } private native boolean startInflating(); private native void endInflating(); private native long inflate(byte[] in, int inOffset, int inLength, byte[] out); }