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

better zip processing

git-svn-id: https://only.mawhrin.net/repos/FBReaderJ/trunk@1311 6a642e6f-84f6-412e-ac94-c4a38d5a04b0
This commit is contained in:
Nikolay Pultsin 2010-04-30 04:26:10 +00:00
parent dabac4421a
commit 9ebd823406
13 changed files with 688 additions and 711 deletions

View file

@ -4,7 +4,7 @@ include $(CLEAR_VARS)
LOCAL_MODULE := DeflatingDecompressor LOCAL_MODULE := DeflatingDecompressor
LOCAL_SRC_FILES := DeflatingDecompressor.cpp LOCAL_SRC_FILES := DeflatingDecompressor.cpp
#LOCAL_LDLIBS := -lz LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -lz
LOCAL_ALLOW_UNDEFINED_SYMBOLS := true LOCAL_ALLOW_UNDEFINED_SYMBOLS := false
include $(BUILD_SHARED_LIBRARY) include $(BUILD_SHARED_LIBRARY)

View file

@ -39,9 +39,9 @@ void Java_org_amse_ys_zip_NativeDeflatingDecompressor_endInflating(JNIEnv *env,
} }
} }
// returns ((used inLength) << 16) + utLength // returns (endFlag << 32) + ((used inLength) << 16) + outLength
extern "C" extern "C"
jint 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_NativeDeflatingDecompressor_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) {
@ -64,8 +64,12 @@ jint Java_org_amse_ys_zip_NativeDeflatingDecompressor_inflate(JNIEnv *env, jobje
const int code = inflate(stream, Z_SYNC_FLUSH); const int code = inflate(stream, Z_SYNC_FLUSH);
env->ReleaseByteArrayElements(in, inStart, 0); env->ReleaseByteArrayElements(in, inStart, 0);
env->ReleaseByteArrayElements(out, outStart, 0); env->ReleaseByteArrayElements(out, outStart, 0);
if ((code == Z_OK) || (code == Z_STREAM_END)) { if (code == Z_OK || code == Z_STREAM_END) {
return ((inLength - stream->avail_in) << 16) + outLength - stream->avail_out; jlong result = ((inLength - stream->avail_in) << 16) + outLength - stream->avail_out;
if (code == Z_STREAM_END) {
result |= ((jlong)1) << 32;
}
return result;
} }
return 0; return 0;
} }

View file

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

View file

@ -7,6 +7,9 @@ public abstract class Decompressor {
public Decompressor(MyBufferedInputStream is, LocalFileHeader header) { public Decompressor(MyBufferedInputStream is, LocalFileHeader header) {
} }
/**
* byte b[] -- target buffer for bytes; might be null
*/
public abstract int read(byte b[], int off, int len) throws IOException; public abstract int read(byte b[], int off, int len) throws IOException;
public abstract int read() throws IOException; public abstract int read() throws IOException;
@ -35,10 +38,7 @@ public abstract class Decompressor {
return decompressor; return decompressor;
} }
} }
return return new NativeDeflatingDecompressor(is, header);
NativeDeflatingDecompressor.INITIALIZED
? new NativeDeflatingDecompressor(is, header)
: new DeflatingDecompressor(is, header);
default: default:
throw new ZipException("Unsupported method of compression"); throw new ZipException("Unsupported method of compression");
} }

View file

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

View file

@ -8,62 +8,57 @@ package org.amse.ys.zip;
import java.io.IOException; import java.io.IOException;
public class LocalFileHeader { public class LocalFileHeader {
/**
* Initilization of constants. Implements: versions, ...
*/
static final int FILE_HEADER_SIGNATURE = 0x04034b50; static final int FILE_HEADER_SIGNATURE = 0x04034b50;
static final int FOLDER_HEADER_SIGNATURE = 0x02014b50; static final int FOLDER_HEADER_SIGNATURE = 0x02014b50;
static final int DATA_DESCRIPTOR_SIGNATURE = 0x07084b50; static final int DATA_DESCRIPTOR_SIGNATURE = 0x07084b50;
final int VersionNeededToExtract; int Signature;
final int GeneralPurposeFlag;
final int CompressionMethod;
private int myCompressedSize; // not final!
private int myUncompressedSize; // not final!
final int OffsetOfLocalData;
public final String FileName;
private boolean mySizeIsKnown;
LocalFileHeader(int versionNeededToExtract, int generalPurposeFlag, int Version;
int compressionMethod, int compressedSize, int uncompressedSize, int Flags;
int offsetOfLocalData, String fileName) { int CompressionMethod;
VersionNeededToExtract = versionNeededToExtract; int ModificationTime;
GeneralPurposeFlag = generalPurposeFlag; int ModificationDate;
CompressionMethod = compressionMethod; int CRC32;
myCompressedSize = compressedSize; int CompressedSize;
myUncompressedSize = uncompressedSize; int UncompressedSize;
OffsetOfLocalData = offsetOfLocalData; int NameLength;
FileName = fileName; int ExtraLength;
mySizeIsKnown = ((GeneralPurposeFlag & 8) == 0);
public String FileName;
int DataOffset;
LocalFileHeader() {
} }
boolean sizeIsKnown() { void readFrom(MyBufferedInputStream stream) throws IOException {
return mySizeIsKnown; Signature = stream.read4Bytes();
} switch (Signature) {
default:
int getCompressedSize() throws IOException { break;
if (mySizeIsKnown) { case FILE_HEADER_SIGNATURE:
return myCompressedSize; Version = stream.read2Bytes();
} else { Flags = stream.read2Bytes();
throw new ZipException( CompressionMethod = stream.read2Bytes();
"Error in getCompressedSize: file size is not known yet"); ModificationTime = stream.read2Bytes();
} ModificationDate = stream.read2Bytes();
} CRC32 = stream.read4Bytes();
CompressedSize = stream.read4Bytes();
int getUncompressedSize() throws IOException { UncompressedSize = stream.read4Bytes();
if (mySizeIsKnown) { if (CompressionMethod == 0 && CompressedSize != UncompressedSize) {
return myUncompressedSize; CompressedSize = UncompressedSize;
} else { }
throw new ZipException( NameLength = stream.read2Bytes();
"Error in getUncompressedSize: file size is not known yet"); ExtraLength = stream.read2Bytes();
} FileName = stream.readString(NameLength);
} stream.skip(ExtraLength);
break;
void setSizes(int compressedSize, int uncompressedSize) { case DATA_DESCRIPTOR_SIGNATURE:
if (!mySizeIsKnown) { CRC32 = stream.read4Bytes();
myCompressedSize = compressedSize; CompressedSize = stream.read4Bytes();
myUncompressedSize = uncompressedSize; UncompressedSize = stream.read4Bytes();
mySizeIsKnown = true; break;
} }
DataOffset = stream.offset();
} }
} }

View file

@ -114,9 +114,32 @@ final class MyBufferedInputStream extends InputStream {
} }
public void backSkip(int n) throws IOException { public void backSkip(int n) throws IOException {
throw new IOException("Back skip is not implemented"); if (n > 0) {
if (myPositionInBuffer >= n) {
myPositionInBuffer -= n;
myBytesReady += n;
myCurrentPosition -= n;
} else {
myFileInputStream.close();
myFileInputStream = myStreamHolder.getInputStream();
myBytesReady = 0;
myPositionInBuffer = 0;
int position = myCurrentPosition - n;
myCurrentPosition = 0;
skip(position);
}
}
} }
public void setPosition(int position) throws IOException {
if (myCurrentPosition < position) {
skip(position - myCurrentPosition);
} else {
backSkip(myCurrentPosition - position);
}
}
/*
public void setPosition(int position) throws IOException { public void setPosition(int position) throws IOException {
if (myCurrentPosition < position) { if (myCurrentPosition < position) {
skip(position - myCurrentPosition); skip(position - myCurrentPosition);
@ -125,9 +148,10 @@ final class MyBufferedInputStream extends InputStream {
myFileInputStream = myStreamHolder.getInputStream(); myFileInputStream = myStreamHolder.getInputStream();
myBytesReady = 0; myBytesReady = 0;
skip(position); skip(position);
myCurrentPosition = position;
} }
myCurrentPosition = position;
} }
*/
public void close() throws IOException { public void close() throws IOException {
myFileInputStream.close(); myFileInputStream.close();

View file

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

View file

@ -20,13 +20,15 @@ public class NoCompressionDecompressor extends Decompressor {
if (value == -1) { if (value == -1) {
break; break;
} }
b[off + i] = (byte)value; if (b != null) {
b[off + i] = (byte)value;
}
} }
return (i > 0) ? i : -1; return (i > 0) ? i : -1;
} }
public int read() throws IOException { public int read() throws IOException {
if (myCurrentPosition < myHeader.getCompressedSize()) { if (myCurrentPosition < myHeader.CompressedSize) {
myCurrentPosition++; myCurrentPosition++;
return myStream.read(); return myStream.read();
} else { } else {
@ -35,6 +37,6 @@ public class NoCompressionDecompressor extends Decompressor {
} }
public int available() throws IOException { public int available() throws IOException {
return (myHeader.getUncompressedSize() - myCurrentPosition); return (myHeader.UncompressedSize - myCurrentPosition);
} }
} }

View file

@ -42,28 +42,18 @@ public final class ZipFile {
} }
private boolean readFileHeader(MyBufferedInputStream baseStream, String fileToFind) throws IOException { private boolean readFileHeader(MyBufferedInputStream baseStream, String fileToFind) throws IOException {
int version2extract = baseStream.read2Bytes(); LocalFileHeader header = new LocalFileHeader();
int generalFlag = baseStream.read2Bytes(); header.readFrom(baseStream);
int compressionMethod = baseStream.read2Bytes();
baseStream.skip(8);
int compressedSize = baseStream.read4Bytes(); if (header.FileName != null) {
int uncompressedSize = baseStream.read4Bytes(); myFileHeaders.put(header.FileName, header);
int fileNameSize = baseStream.read2Bytes(); }
int extraField = baseStream.read2Bytes(); if ((header.Flags & 0x08) == 0) {
baseStream.skip(header.CompressedSize);
final String fileName = baseStream.readString(fileNameSize);
baseStream.skip(extraField);
LocalFileHeader header = new LocalFileHeader(version2extract, generalFlag,
compressionMethod, compressedSize, uncompressedSize,
baseStream.offset(), fileName);
myFileHeaders.put(fileName, header);
if (header.sizeIsKnown()) {
baseStream.skip(compressedSize);
} else { } else {
findAndReadDescriptor(baseStream, header); findAndReadDescriptor(baseStream, header);
} }
return fileName.equals(fileToFind); return header.FileName != null && header.FileName.equals(fileToFind);
} }
private void readAllHeaders() throws IOException { private void readAllHeaders() throws IOException {
@ -78,16 +68,6 @@ public final class ZipFile {
try { try {
while (true) { while (true) {
int header = baseStream.read4Bytes();
if (header != LocalFileHeader.FILE_HEADER_SIGNATURE) {
if (header == LocalFileHeader.FOLDER_HEADER_SIGNATURE) {
break; // central directory, no more files
} else {
throw new ZipException(
"readHeaders. Wrong signature found = " + header
+ " at position " + baseStream.offset());
}
}
readFileHeader(baseStream, null); readFileHeader(baseStream, null);
} }
} finally { } finally {
@ -99,39 +79,16 @@ public final class ZipFile {
* Finds descriptor of the last header and installs sizes of files * Finds descriptor of the last header and installs sizes of files
*/ */
private void findAndReadDescriptor(MyBufferedInputStream baseStream, LocalFileHeader header) throws IOException { private void findAndReadDescriptor(MyBufferedInputStream baseStream, LocalFileHeader header) throws IOException {
loop: Decompressor decompressor = Decompressor.init(baseStream, header);
while (true) { int uncompressedSize = 0;
int signature = 0; while (true) {
do { int blockSize = decompressor.read(null, 0, 2048);
int nextByte = baseStream.read(); if (blockSize <= 0) {
if (nextByte < 0) { break;
throw new ZipException(
"readFileHeaders. Unexpected end of file when looking for DataDescriptor");
}
signature = ((signature >> 8) & 0x0FFFFFF) | (nextByte << 24);
} while (
signature != LocalFileHeader.FILE_HEADER_SIGNATURE &&
signature != LocalFileHeader.FOLDER_HEADER_SIGNATURE &&
signature != LocalFileHeader.DATA_DESCRIPTOR_SIGNATURE
);
switch (signature) {
case LocalFileHeader.FILE_HEADER_SIGNATURE:
break loop;
case LocalFileHeader.FOLDER_HEADER_SIGNATURE:
break loop;
case LocalFileHeader.DATA_DESCRIPTOR_SIGNATURE:
baseStream.skip(4);
int compressedSize = baseStream.read4Bytes();
int uncompressedSize = baseStream.read4Bytes();
if ((baseStream.offset() - header.OffsetOfLocalData - 16) == compressedSize) {
header.setSizes(compressedSize, uncompressedSize);
break loop;
} else {
baseStream.backSkip(12);
continue loop;
}
} }
} uncompressedSize += blockSize;
}
header.UncompressedSize = uncompressedSize;
} }
private final Queue<MyBufferedInputStream> myStoredStreams = new LinkedList<MyBufferedInputStream>(); private final Queue<MyBufferedInputStream> myStoredStreams = new LinkedList<MyBufferedInputStream>();
@ -150,7 +107,7 @@ loop:
} }
public int getEntrySize(String entryName) throws IOException { public int getEntrySize(String entryName) throws IOException {
return getHeader(entryName).getUncompressedSize(); return getHeader(entryName).UncompressedSize;
} }
public InputStream getInputStream(String entryName) throws IOException { public InputStream getInputStream(String entryName) throws IOException {
@ -171,18 +128,8 @@ loop:
MyBufferedInputStream baseStream = getBaseStream(); MyBufferedInputStream baseStream = getBaseStream();
baseStream.setPosition(0); baseStream.setPosition(0);
try { try {
do { while (!readFileHeader(baseStream, entryName)) {
int signature = baseStream.read4Bytes(); }
if (signature != LocalFileHeader.FILE_HEADER_SIGNATURE) {
if (signature == LocalFileHeader.FOLDER_HEADER_SIGNATURE) {
break; // central directory, no more files
} else {
throw new ZipException(
"Wrong signature " + signature
+ " found at position " + baseStream.offset());
}
}
} while (!readFileHeader(baseStream, entryName));
LocalFileHeader header = myFileHeaders.get(entryName); LocalFileHeader header = myFileHeaders.get(entryName);
if (header != null) { if (header != null) {
return header; return header;

View file

@ -14,7 +14,7 @@ class ZipInputStream extends InputStream {
public ZipInputStream(ZipFile parent, LocalFileHeader header) throws IOException { public ZipInputStream(ZipFile parent, LocalFileHeader header) throws IOException {
myParent = parent; myParent = parent;
myBaseStream = parent.getBaseStream(); myBaseStream = parent.getBaseStream();
myBaseStream.setPosition(header.OffsetOfLocalData); myBaseStream.setPosition(header.DataOffset);
myDecompressor = Decompressor.init(myBaseStream, header); myDecompressor = Decompressor.init(myBaseStream, header);
} }

View file

@ -832,7 +832,7 @@ public abstract class ZLTextView extends ZLTextViewBase {
} }
public final synchronized void gotoPosition(int paragraphIndex, int wordIndex, int charIndex) { public final synchronized void gotoPosition(int paragraphIndex, int wordIndex, int charIndex) {
if (myModel != null) { if (myModel != null && myModel.getParagraphsNumber() > 0) {
myCurrentPage.moveStartCursor(paragraphIndex, wordIndex, charIndex); myCurrentPage.moveStartCursor(paragraphIndex, wordIndex, charIndex);
myPreviousPage.reset(); myPreviousPage.reset();
myNextPage.reset(); myNextPage.reset();