mirror of
https://github.com/geometer/FBReaderJ.git
synced 2025-10-04 18:29:23 +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:
parent
9ebd823406
commit
f0ff156107
8 changed files with 103 additions and 680 deletions
Binary file not shown.
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
while (available < toRead) {
|
|
||||||
if (myState == ST_END_OF_FILE) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (myState == ST_HEADER) {
|
if (len > myAvailable) {
|
||||||
readHeader();
|
len = myAvailable;
|
||||||
}
|
}
|
||||||
available += pushNextSymbolToDictionary();
|
for (int toFill = len; toFill > 0; ) {
|
||||||
|
if (myOutBufferLength == 0) {
|
||||||
|
fillOutBuffer();
|
||||||
}
|
}
|
||||||
if (available == 0) {
|
if (myOutBufferLength == 0) {
|
||||||
break;
|
throw new IOException("cannot read from zip");
|
||||||
}
|
}
|
||||||
if (toRead > available) {
|
final int ready = (toFill < myOutBufferLength) ? toFill : myOutBufferLength;
|
||||||
toRead = available;
|
if (b != null) {
|
||||||
|
System.arraycopy(myOutBuffer, myOutBufferOffset, b, off, ready);
|
||||||
}
|
}
|
||||||
myOutputBuffer.read(b, off + i, toRead);
|
off += ready;
|
||||||
i += toRead;
|
myOutBufferOffset += ready;
|
||||||
available -= toRead;
|
toFill -= ready;
|
||||||
|
myOutBufferLength -= ready;
|
||||||
}
|
}
|
||||||
myCurrentPosition += i;
|
myAvailable -= len;
|
||||||
return (i > 0) ? i : -1;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read() throws IOException {
|
public int read() throws IOException {
|
||||||
myCurrentPosition++;
|
if (myAvailable <= 0) {
|
||||||
|
|
||||||
while (myOutputBuffer.available() == 0) {
|
|
||||||
if (myState == ST_HEADER) {
|
|
||||||
readHeader();
|
|
||||||
}
|
|
||||||
pushNextSymbolToDictionary();
|
|
||||||
if (myState == ST_END_OF_FILE) {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if (myOutBufferLength == 0) {
|
||||||
|
fillOutBuffer();
|
||||||
}
|
}
|
||||||
return myOutputBuffer.read() & 0xFF;
|
if (myOutBufferLength == 0) {
|
||||||
|
throw new IOException("cannot read from zip");
|
||||||
|
}
|
||||||
|
--myAvailable;
|
||||||
|
--myOutBufferLength;
|
||||||
|
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:
|
|
||||||
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// reading distanse
|
|
||||||
final int huffmanCode = readHuffmanCode(myDistanceCodes);
|
|
||||||
final int distance;
|
|
||||||
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 {
|
private native boolean startInflating();
|
||||||
int bitsInBuffer = myBitsInBuffer;
|
private native void endInflating();
|
||||||
int buffer = myTempInt;
|
private native long inflate(byte[] in, int inOffset, int inLength, byte[] out);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue