package org.amse.ys.zip; import java.io.IOException; class DeflatingDecompressor extends Decompressor { static { System.loadLibrary("DeflatingDecompressor-v3"); } // 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 volatile int myInflatorId = -1; public DeflatingDecompressor(MyBufferedInputStream inputStream, LocalFileHeader header) throws IOException { super(); reset(inputStream, header); } void reset(MyBufferedInputStream inputStream, LocalFileHeader header) throws IOException { if (myInflatorId != -1) { endInflating(myInflatorId); myInflatorId = -1; } 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; myInflatorId = startInflating(); if (myInflatorId == -1) { throw new ZipException("cannot start inflating"); } } @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) { 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; } if (len > 0) { myAvailable -= len; } else { myAvailable = 0; } return len; } @Override public int read() throws IOException { if (myAvailable <= 0) { return -1; } if (myOutBufferLength == 0) { fillOutBuffer(); } if (myOutBufferLength == 0) { myAvailable = 0; return -1; } --myAvailable; --myOutBufferLength; return myOutBuffer[myOutBufferOffset++]; } private void fillOutBuffer() throws IOException { if (myInflatorId == -1) { 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(myInflatorId, myInBuffer, myInBufferOffset, myInBufferLength, myOutBuffer); if (result <= 0) { final StringBuffer extraInfo = new StringBuffer() .append(myStream.offset()).append(":") .append(myInBufferOffset).append(":") .append(myInBufferLength).append(":") .append(myOutBuffer.length).append(":"); for (int i = 0; i < Math.min(10, myInBufferLength); ++i) { extraInfo.append(myInBuffer[myInBufferOffset + i]).append(","); } throw new ZipException("Cannot inflate zip-compressed block, code = " + result + ";extra info = " + extraInfo); } final int in = (int)(result >> 16) & 0xFFFF; if (in > myInBufferLength) { throw new ZipException("Invalid inflating result, code = " + result + "; buffer length = " + myInBufferLength); } final int out = (int)result & 0xFFFF; myInBufferOffset += in; myInBufferLength -= in; myOutBufferOffset = 0; myOutBufferLength = out; if ((result & (1L << 32)) != 0) { endInflating(myInflatorId); myInflatorId = -1; myStream.backSkip(myInBufferLength); break; } } } private native int startInflating(); private native void endInflating(int inflatorId); private native long inflate(int inflatorId, byte[] in, int inOffset, int inLength, byte[] out); }