1
0
Fork 0
mirror of https://github.com/geometer/FBReaderJ.git synced 2025-10-04 10:19:33 +02:00

code cleanup: pure java DeflatingDecompressor in not used more

git-svn-id: https://only.mawhrin.net/repos/FBReaderJ/trunk@1312 6a642e6f-84f6-412e-ac94-c4a38d5a04b0
This commit is contained in:
Nikolay Pultsin 2010-04-30 04:35:43 +00:00
parent 9ebd823406
commit f0ff156107
8 changed files with 103 additions and 680 deletions

View file

@ -11,7 +11,7 @@ static jobject keys[SIZE] = { 0 };
static z_stream* values[SIZE] = { 0 }; static z_stream* values[SIZE] = { 0 };
extern "C" extern "C"
jboolean Java_org_amse_ys_zip_NativeDeflatingDecompressor_startInflating(JNIEnv *env, jobject thiz) { jboolean Java_org_amse_ys_zip_DeflatingDecompressor_startInflating(JNIEnv *env, jobject thiz) {
int i; int i;
for (i = 0; i < SIZE; ++i) { for (i = 0; i < SIZE; ++i) {
if (keys[i] == 0) { if (keys[i] == 0) {
@ -26,7 +26,7 @@ jboolean Java_org_amse_ys_zip_NativeDeflatingDecompressor_startInflating(JNIEnv
} }
extern "C" extern "C"
void Java_org_amse_ys_zip_NativeDeflatingDecompressor_endInflating(JNIEnv *env, jobject thiz) { void Java_org_amse_ys_zip_DeflatingDecompressor_endInflating(JNIEnv *env, jobject thiz) {
int i; int i;
for (i = 0; i < SIZE; ++i) { for (i = 0; i < SIZE; ++i) {
if (keys[i] == thiz) { if (keys[i] == thiz) {
@ -41,7 +41,7 @@ void Java_org_amse_ys_zip_NativeDeflatingDecompressor_endInflating(JNIEnv *env,
// returns (endFlag << 32) + ((used inLength) << 16) + outLength // returns (endFlag << 32) + ((used inLength) << 16) + outLength
extern "C" extern "C"
jlong Java_org_amse_ys_zip_NativeDeflatingDecompressor_inflate(JNIEnv *env, jobject thiz, jbyteArray in, jint inOffset, jint inLength, jbyteArray out) { jlong Java_org_amse_ys_zip_DeflatingDecompressor_inflate(JNIEnv *env, jobject thiz, jbyteArray in, jint inOffset, jint inLength, jbyteArray out) {
int i; int i;
z_stream *stream = 0; z_stream *stream = 0;
for (i = 0; i < SIZE; ++i) { for (i = 0; i < SIZE; ++i) {

View file

@ -1,7 +0,0 @@
package org.amse.ys.zip;
import java.io.IOException;
abstract class AbstractDeflatingDecompressor extends Decompressor {
abstract void reset(MyBufferedInputStream inputStream, LocalFileHeader header) throws IOException;
}

View file

@ -1,70 +0,0 @@
package org.amse.ys.zip;
import java.io.IOException;
final class CircularBuffer {
static final int DICTIONARY_LENGTH = (1 << 15);
private static final int DICTIONARY_MASK = DICTIONARY_LENGTH - 1;
private final byte[] myBuffer = new byte[DICTIONARY_LENGTH];
private int myBytesReady; // number of bytes can be read
private int myCurrentPosition; // the next byte to read is
// myDictionary[myCurrentPosition]
void reset() {
myBytesReady = 0;
myCurrentPosition = 0;
}
public int available() {
return myBytesReady;
}
public void read(byte[] buffer, int offset, int length) {
int from = myCurrentPosition;
if (from + length > DICTIONARY_LENGTH) {
final int firstPart = DICTIONARY_LENGTH - from;
final int secondPart = length - firstPart;
if (buffer != null) {
System.arraycopy(myBuffer, from, buffer, offset, firstPart);
System.arraycopy(myBuffer, 0, buffer, offset + firstPart, secondPart);
}
myCurrentPosition = secondPart;
} else {
if (buffer != null) {
System.arraycopy(myBuffer, from, buffer, offset, length);
}
myCurrentPosition = from + length;
}
myBytesReady -= length;
}
public byte read() throws IOException {
if (myBytesReady == 0) {
throw new ZipException("reading from empty buffer");
}
final byte result = myBuffer[myCurrentPosition++];
myCurrentPosition &= DICTIONARY_MASK;
myBytesReady--;
return result;
}
public void writeByte(byte toWrite) {
myBuffer[(myCurrentPosition + myBytesReady) & DICTIONARY_MASK] = toWrite;
myBytesReady++;
}
public void repeat(int length, int distance) throws IOException {
if (myBytesReady + length > DICTIONARY_LENGTH) {
throw new ZipException("circular buffer overflow");
}
int writePoint = (myCurrentPosition + myBytesReady) & DICTIONARY_MASK;
int readPoint = (writePoint - distance) & DICTIONARY_MASK;
for (int i = 0; i < length; i++) {
myBuffer[writePoint++] = myBuffer[readPoint++];
writePoint &= DICTIONARY_MASK;
readPoint &= DICTIONARY_MASK;
}
myBytesReady += length;
}
}

View file

@ -1,120 +0,0 @@
package org.amse.ys.zip;
public class CodeBuilder {
public static final int MAX_HUFFMAN_CODE_LENGTH = 15;
final private int[] myLengthArray;
public CodeBuilder(int maxNumberOfCodes) {
myLengthArray = new int[maxNumberOfCodes];
}
public void addCodeLength(int codeNumber, int codeLen) {
myLengthArray[codeNumber] = codeLen;
}
public void buildTable(int[] table) {
final int arrayLength = myLengthArray.length;
int maxCodeLength = 0;
// counting number of codes for definite length
int[] b1_count = new int[MAX_HUFFMAN_CODE_LENGTH + 1];
for (int i = 0; i < arrayLength; ++i) {
final int l = myLengthArray[i];
++b1_count[l];
if (maxCodeLength < l) {
maxCodeLength = l;
}
}
b1_count[0] = 0;
// step 2
int[] next_code = new int[MAX_HUFFMAN_CODE_LENGTH + 1];
int code = 0;
for (int bits = 1; bits <= MAX_HUFFMAN_CODE_LENGTH; bits++) {
code = ((code + b1_count[bits - 1]) << 1);
next_code[bits] = code;
}
// step 3
for (int i = 0; i < arrayLength; ++i) {
final int codeLength = myLengthArray[i];
if (codeLength > 0) {
int revertedCode = next_code[codeLength]++;
int c = 0;
for (int j = 0; j < codeLength; ++j) {
c = (c << 1) + (revertedCode & 1);
revertedCode >>= 1;
}
final int value = (codeLength << 16) + i;
final int num = 1 << maxCodeLength;
final int step = 1 << codeLength;
for (; c < num; c += step) {
table[c] = value;
}
}
}
for (int i = maxCodeLength; i < 15; ++i) {
System.arraycopy(table, 0, table, 1 << i, 1 << i);
}
}
private void buildTable(int[] codeArray, int[] table, int maxCodeLength) {
final int length = codeArray.length;
for (int i = 0; i < length; i++) {
final int codeLength = myLengthArray[i];
if (codeLength > 0) {
int revertedCode = codeArray[i];
int c = 0;
for (int j = 0; j < codeLength; ++j) {
c = (c << 1) + (revertedCode & 1);
revertedCode >>= 1;
}
final int value = (codeLength << 16) + i;
final int num = 1 << maxCodeLength;
final int step = 1 << codeLength;
for (; c < num; c += step) {
table[c] = value;
}
}
}
for (int i = maxCodeLength; i < 15; ++i) {
System.arraycopy(table, 0, table, 1 << i, 1 << i);
}
}
public static void buildFixedHuffmanCodes(int[] table) {
CodeBuilder builder = new CodeBuilder(288);
final int[] codeArray = new int[288];
for (int i = 0; i <= 143; i++) {
builder.myLengthArray[i] = 8;
codeArray[i] = 48 + i;
}
for (int i = 144; i <= 255; i++) {
builder.myLengthArray[i] = 9;
codeArray[i] = 256 + i;
}
for (int i = 256; i <= 279; i++) {
builder.myLengthArray[i] = 7;
codeArray[i] = 0 + i - 256;
}
for (int i = 280; i <= 287; i++) {
builder.myLengthArray[i] = 8;
codeArray[i] = 192 + i - 280;
}
builder.buildTable(codeArray, table, 9);
}
public static void buildFixedDistanceCodes(int[] table) {
CodeBuilder builder = new CodeBuilder(32);
final int[] codeArray = new int[32];
for (int i = 0; i < 32; i++) {
codeArray[i] = i;
builder.myLengthArray[i] = 5;
}
builder.buildTable(codeArray, table, 5);
}
}

View file

@ -16,12 +16,12 @@ public abstract class Decompressor {
protected Decompressor() { protected Decompressor() {
} }
private static Queue<AbstractDeflatingDecompressor> ourDeflators = new LinkedList<AbstractDeflatingDecompressor>(); private static Queue<DeflatingDecompressor> ourDeflators = new LinkedList<DeflatingDecompressor>();
static void storeDecompressor(Decompressor decompressor) { static void storeDecompressor(Decompressor decompressor) {
if (decompressor instanceof AbstractDeflatingDecompressor) { if (decompressor instanceof DeflatingDecompressor) {
synchronized (ourDeflators) { synchronized (ourDeflators) {
ourDeflators.add((AbstractDeflatingDecompressor)decompressor); ourDeflators.add((DeflatingDecompressor)decompressor);
} }
} }
} }
@ -33,12 +33,12 @@ public abstract class Decompressor {
case 8: case 8:
synchronized (ourDeflators) { synchronized (ourDeflators) {
if (!ourDeflators.isEmpty()) { if (!ourDeflators.isEmpty()) {
AbstractDeflatingDecompressor decompressor = ourDeflators.poll(); DeflatingDecompressor decompressor = ourDeflators.poll();
decompressor.reset(is, header); decompressor.reset(is, header);
return decompressor; return decompressor;
} }
} }
return new NativeDeflatingDecompressor(is, header); return new DeflatingDecompressor(is, header);
default: default:
throw new ZipException("Unsupported method of compression"); throw new ZipException("Unsupported method of compression");
} }

View file

@ -2,37 +2,25 @@ package org.amse.ys.zip;
import java.io.*; import java.io.*;
public class DeflatingDecompressor extends AbstractDeflatingDecompressor { public class DeflatingDecompressor extends Decompressor {
private static final int ST_HEADER = 1; static {
private static final int ST_NO_COMPRESSION = 2; System.loadLibrary("DeflatingDecompressor");
private static final int ST_FIXED_CODES = 3; }
private static final int ST_DYNAMIC_CODES = 4;
private static final int ST_END_OF_FILE = 5;
// common variables // common variables
private MyBufferedInputStream myStream; private MyBufferedInputStream myStream;
private LocalFileHeader myHeader; private int myCompressedAvailable;
private int myState; private int myAvailable;
private int myTotalLength;
private int myBytesRead;
private int myCurrentPosition;
private boolean myTheBlockIsFinal;
// for bit reader private static final int IN_BUFFER_SIZE = 2048;
private int myBitsInBuffer; private static final int OUT_BUFFER_SIZE = 32768;
private int myTempInt; // should contain 16 bit available for reading
// output buffer private final byte[] myInBuffer = new byte[IN_BUFFER_SIZE];
private final CircularBuffer myOutputBuffer = new CircularBuffer(); private int myInBufferOffset;
private int myInBufferLength;
// for no compression method private final byte[] myOutBuffer = new byte[OUT_BUFFER_SIZE];
private int myCurrentBlockLength; private int myOutBufferOffset;
private int myReadInBlock; private int myOutBufferLength;
// for Huffman codes
private final int[] myHuffmanCodes = new int[1 << 15];
private final int[] myDistanceCodes = new int[1 << 15];
private final int[] myAuxCodes = new int[1 << 15];
public DeflatingDecompressor(MyBufferedInputStream inputStream, LocalFileHeader header) throws IOException { public DeflatingDecompressor(MyBufferedInputStream inputStream, LocalFileHeader header) throws IOException {
super(); super();
@ -40,341 +28,99 @@ public class DeflatingDecompressor extends AbstractDeflatingDecompressor {
} }
void reset(MyBufferedInputStream inputStream, LocalFileHeader header) throws IOException { void reset(MyBufferedInputStream inputStream, LocalFileHeader header) throws IOException {
endInflating();
myStream = inputStream; myStream = inputStream;
myHeader = header; myCompressedAvailable = header.CompressedSize;
myTotalLength = header.CompressedSize; myAvailable = header.UncompressedSize;
myBytesRead = 0;
myCurrentPosition = 0; myInBufferOffset = IN_BUFFER_SIZE;
myTheBlockIsFinal = false; myInBufferLength = 0;
myState = ST_HEADER; myOutBufferOffset = OUT_BUFFER_SIZE;
myOutputBuffer.reset(); myOutBufferLength = 0;
myBitsInBuffer = 0;
myTempInt = 0; startInflating();
myCurrentBlockLength = 0;
myReadInBlock = 0;
} }
@Override @Override
public int available() throws IOException { public int available() {
return myHeader.UncompressedSize - myCurrentPosition; return myAvailable;
} }
private void ensure16BitsInBuffer() throws IOException {
do {
int tmp = myStream.read();
if (tmp < 0) {
throw new ZipException("getBit: read after end of file");
}
myTempInt += tmp << myBitsInBuffer;
myBitsInBuffer += 8;
myBytesRead++;
} while (myBitsInBuffer <= 16);
}
/**
*
* This code is potentially dangerous, because can read 3 bytes after end of block
*
*/
private int getBit() throws IOException {
if (myBitsInBuffer < 16) {
ensure16BitsInBuffer();
}
myBitsInBuffer--;
int result = (myTempInt & 1);
myTempInt = (myTempInt >> 1);
return result;
}
private int readIntegerByBit(int length) throws IOException {
if (myBitsInBuffer < 16) {
ensure16BitsInBuffer();
}
final int result = myTempInt & ((1 << length) - 1);
myTempInt >>>= length;
myBitsInBuffer -= length;
return result;
}
private static final int MAX_LEN = CircularBuffer.DICTIONARY_LENGTH / 2;
@Override @Override
public int read(byte b[], int off, int len) throws IOException { public int read(byte[] b, int off, int len) throws IOException {
int i = 0; if (myAvailable <= 0) {
int available = myOutputBuffer.available(); return -1;
while (i < len) { }
int toRead = Math.min(MAX_LEN, len - i); if (len > myAvailable) {
while (available < toRead) { len = myAvailable;
if (myState == ST_END_OF_FILE) { }
break; for (int toFill = len; toFill > 0; ) {
} if (myOutBufferLength == 0) {
if (myState == ST_HEADER) { fillOutBuffer();
readHeader(); }
} if (myOutBufferLength == 0) {
available += pushNextSymbolToDictionary(); throw new IOException("cannot read from zip");
} }
if (available == 0) { final int ready = (toFill < myOutBufferLength) ? toFill : myOutBufferLength;
break; if (b != null) {
} System.arraycopy(myOutBuffer, myOutBufferOffset, b, off, ready);
if (toRead > available) { }
toRead = available; off += ready;
} myOutBufferOffset += ready;
myOutputBuffer.read(b, off + i, toRead); toFill -= ready;
i += toRead; myOutBufferLength -= ready;
available -= toRead; }
} myAvailable -= len;
myCurrentPosition += i; return len;
return (i > 0) ? i : -1;
} }
@Override @Override
public int read() throws IOException { public int read() throws IOException {
myCurrentPosition++; if (myAvailable <= 0) {
return -1;
while (myOutputBuffer.available() == 0) { }
if (myState == ST_HEADER) { if (myOutBufferLength == 0) {
readHeader(); fillOutBuffer();
} }
pushNextSymbolToDictionary(); if (myOutBufferLength == 0) {
if (myState == ST_END_OF_FILE) { throw new IOException("cannot read from zip");
return -1; }
} --myAvailable;
} --myOutBufferLength;
return myOutputBuffer.read() & 0xFF; return myOutBuffer[myOutBufferOffset++];
} }
private int pushNextSymbolToDictionary() throws IOException { private void fillOutBuffer() throws IOException {
if (myState == ST_NO_COMPRESSION) { while (myOutBufferLength == 0) {
myOutputBuffer.writeByte((byte)readIntegerByBit(8)); if (myInBufferLength == 0) {
myReadInBlock++; myInBufferOffset = 0;
if (myCurrentBlockLength == myReadInBlock) { final int toRead = (myCompressedAvailable < IN_BUFFER_SIZE) ? myCompressedAvailable : IN_BUFFER_SIZE;
if (myTheBlockIsFinal) { if (myStream.read(myInBuffer, 0, toRead) != toRead) {
myState = ST_END_OF_FILE; throw new IOException("cannot read from base stream");
} else { }
myState = ST_HEADER; myInBufferLength = toRead;
} myCompressedAvailable -= toRead;
} }
return 1; final long result = inflate(myInBuffer, myInBufferOffset, myInBufferLength, myOutBuffer);
} else { if (result == 0) {
int currentHuffmanCode = readHuffmanCode(myHuffmanCodes); throw new IOException("cannot read from base stream");
int length; }
switch (currentHuffmanCode) { final int in = (int)(result >> 16) & 0xFFFF;
default: final int out = (int)result & 0xFFFF;
myOutputBuffer.writeByte((byte)currentHuffmanCode); myInBufferOffset += in;
return 1; myInBufferLength -= in;
case 256: myOutBufferOffset = 0;
myState = myTheBlockIsFinal ? ST_END_OF_FILE : ST_HEADER; myOutBufferLength = out;
return 0; if ((result & (1L << 32)) != 0) {
case 257: endInflating();
case 258: myStream.backSkip(myInBufferLength - myInBufferOffset);
case 259: break;
case 260: }
case 261: }
case 262: }
case 263:
case 264:
length = currentHuffmanCode + 3 - 257;
break;
case 265:
case 266:
case 267:
case 268:
length = ((currentHuffmanCode - 265) << 1) + 11 + getBit();
break;
case 269:
case 270:
case 271:
case 272:
length = ((currentHuffmanCode - 269) << 2) + 19 + readIntegerByBit(2);
break;
case 273:
case 274:
case 275:
case 276:
length = ((currentHuffmanCode - 273) << 3) + 35 + readIntegerByBit(3);
break;
case 277:
case 278:
case 279:
case 280:
length = ((currentHuffmanCode - 277) << 4) + 67 + readIntegerByBit(4);
break;
case 281:
case 282:
case 283:
case 284:
length = ((currentHuffmanCode - 281) << 5) + 131 + readIntegerByBit(5);
break;
case 285:
length = 258;
break;
}
// reading distanse private native boolean startInflating();
final int huffmanCode = readHuffmanCode(myDistanceCodes); private native void endInflating();
final int distance; private native long inflate(byte[] in, int inOffset, int inLength, byte[] out);
if (huffmanCode <= 3) {
distance = huffmanCode + 1;
} else if (huffmanCode <= 29) {
final int extraBits = (huffmanCode / 2) - 1;
int previousCode = (1 << (huffmanCode / 2));
if ((huffmanCode % 2) != 0) {
previousCode += (1 << extraBits);
}
distance = previousCode + 1 + readIntegerByBit(extraBits);
} else {
throw new ZipException("distance code > 29 found");
}
myOutputBuffer.repeat(length, distance);
return length;
}
}
private int readHuffmanCode(int[] table) throws IOException {
int bitsInBuffer = myBitsInBuffer;
int buffer = myTempInt;
while (bitsInBuffer <= 16) {
buffer += myStream.read() << bitsInBuffer;
bitsInBuffer += 8;
myBytesRead++;
}
final int tmp = table[buffer & 0x7FFF];
final int len = tmp >> 16;
myTempInt = buffer >>> len;
myBitsInBuffer = bitsInBuffer - len;
return tmp & 0x0FFFF;
}
private void readHeader() throws IOException {
if ((myState != ST_HEADER) || (myBytesRead >= myTotalLength)) {
throw new ZipException("unexpected case of readheader call");
}
myTheBlockIsFinal = (getBit() != 0);
switch (readIntegerByBit(2)) {
case 0:
myState = ST_NO_COMPRESSION;
readIntegerByBit(myBitsInBuffer % 8);
myCurrentBlockLength = readIntegerByBit(16);
readIntegerByBit(16);
myReadInBlock = 0;
break;
case 1:
myState = ST_FIXED_CODES;
CodeBuilder.buildFixedHuffmanCodes(myHuffmanCodes);
CodeBuilder.buildFixedDistanceCodes(myDistanceCodes);
break;
case 2:
myState = ST_DYNAMIC_CODES;
readCodes();
break;
case 3:
throw new ZipException(
"Code 11 found in header of delflated block. (means error according to specification)");
}
//myHuffmanCodes.print();
}
private void readCodes() throws IOException {
//int headersFound = 0;
final int[] codeLenSequence = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11,
4, 12, 3, 13, 2, 14, 1, 15 };
final int numberOfLiteralCodes = readIntegerByBit(5);
final int numberOfDistanceCodes = readIntegerByBit(5);
final int numberOfLengthCodes = readIntegerByBit(4);
// reading HCLEN codes
final CodeBuilder headerReadingCoder = new CodeBuilder(19);
for (int i = 0; i < (numberOfLengthCodes + 4); i++) {
headerReadingCoder.addCodeLength(codeLenSequence[i],
readIntegerByBit(3));
}
headerReadingCoder.buildTable(myAuxCodes);
CodeBuilder usualCodeBuilder = new CodeBuilder(288);
int previousNumber = 0;
for (int i = 0; i < (numberOfLiteralCodes + 257); i++) {
int currentHuffmanCode = readHuffmanCode(myAuxCodes);
//headersFound++;
if (currentHuffmanCode <= 15) {
usualCodeBuilder.addCodeLength(i, currentHuffmanCode);
previousNumber = currentHuffmanCode;
} else {
// repeating previous codes
boolean previous;
int repeatNumber = 0;
switch (currentHuffmanCode) {
case 16:
repeatNumber = 3 + readIntegerByBit(2);
previous = true;
break;
case 17:
repeatNumber = 3 + readIntegerByBit(3);
previous = false;
break;
case 18:
repeatNumber = 11 + readIntegerByBit(7);
previous = false;
break;
default:
throw new ZipException("error when reading dynamic Huffman codes");
}
previousNumber = previous ? previousNumber : 0;
for (int j = 0; j < repeatNumber; j++) {
usualCodeBuilder.addCodeLength(i + j, previousNumber);
}
i += repeatNumber - 1;
}
}
// we can build huffman codes for charset
usualCodeBuilder.buildTable(myHuffmanCodes);
// building distance codes
CodeBuilder distanceCodeBuilder = new CodeBuilder(32);
previousNumber = 0;
for (int i = 0; i < (numberOfDistanceCodes + 1); i++) {
int currentHuffmanCode = readHuffmanCode(myAuxCodes);
//headersFound++;
if (currentHuffmanCode <= 15) {
distanceCodeBuilder.addCodeLength(i, currentHuffmanCode);
previousNumber = currentHuffmanCode;
} else {
// repeating previous codes
boolean previous;
int repeatNumber = 0;
switch (currentHuffmanCode) {
case 16:
repeatNumber = 3 + readIntegerByBit(2);
previous = true;
break;
case 17:
repeatNumber = 3 + readIntegerByBit(3);
previous = false;
break;
case 18:
repeatNumber = 11 + readIntegerByBit(7);
previous = false;
break;
default:
throw new ZipException("error when reading dynamic Huffman codes");
}
previousNumber = (previous ? previousNumber : 0);
for (int j = 0; j < repeatNumber; j++) {
distanceCodeBuilder.addCodeLength(i + j, previousNumber);
}
i += (repeatNumber - 1);
}
}
distanceCodeBuilder.buildTable(myDistanceCodes);
}
} }

View file

@ -1,126 +0,0 @@
package org.amse.ys.zip;
import java.io.*;
public class NativeDeflatingDecompressor extends AbstractDeflatingDecompressor {
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;
public NativeDeflatingDecompressor(MyBufferedInputStream inputStream, LocalFileHeader header) throws IOException {
super();
reset(inputStream, header);
}
void reset(MyBufferedInputStream inputStream, LocalFileHeader header) throws IOException {
endInflating();
myStream = inputStream;
myCompressedAvailable = header.CompressedSize;
myAvailable = header.UncompressedSize;
myInBufferOffset = IN_BUFFER_SIZE;
myInBufferLength = 0;
myOutBufferOffset = OUT_BUFFER_SIZE;
myOutBufferLength = 0;
startInflating();
}
@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) {
throw new IOException("cannot read from zip");
}
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) {
throw new IOException("cannot read from zip");
}
--myAvailable;
--myOutBufferLength;
return myOutBuffer[myOutBufferOffset++];
}
private void fillOutBuffer() throws IOException {
while (myOutBufferLength == 0) {
if (myInBufferLength == 0) {
myInBufferOffset = 0;
final int toRead = (myCompressedAvailable < IN_BUFFER_SIZE) ? myCompressedAvailable : IN_BUFFER_SIZE;
if (myStream.read(myInBuffer, 0, toRead) != toRead) {
throw new IOException("cannot read from base stream");
}
myInBufferLength = toRead;
myCompressedAvailable -= toRead;
}
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();
myStream.backSkip(myInBufferLength - myInBufferOffset);
break;
}
}
}
private native boolean startInflating();
private native void endInflating();
private native long inflate(byte[] in, int inOffset, int inLength, byte[] out);
}