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

fixed zip deflating on android 4.0 devices

This commit is contained in:
Nikolay Pultsin 2011-12-03 01:53:47 +00:00
parent e10597aadb
commit 46ef1eaeb4
4 changed files with 218 additions and 226 deletions

View file

@ -7,52 +7,41 @@
#define SIZE 10 #define SIZE 10
static jobject keys[SIZE] = { 0 }; static z_stream* ourStreams[SIZE] = { 0 };
static z_stream* values[SIZE] = { 0 };
extern "C" extern "C"
jboolean Java_org_amse_ys_zip_DeflatingDecompressor_startInflating(JNIEnv *env, jobject thiz) { jint 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 (ourStreams[i] == 0) {
keys[i] = thiz; ourStreams[i] = new z_stream;
values[i] = new z_stream; memset(ourStreams[i], 0, sizeof(z_stream));
memset(values[i], 0, sizeof(z_stream)); inflateInit2(ourStreams[i], -MAX_WBITS);
inflateInit2(values[i], -MAX_WBITS); return i;
return 1;
} }
} }
return 0; return -1;
} }
extern "C" extern "C"
void Java_org_amse_ys_zip_DeflatingDecompressor_endInflating(JNIEnv *env, jobject thiz) { void Java_org_amse_ys_zip_DeflatingDecompressor_endInflating(JNIEnv *env, jobject thiz, jint inflatorId) {
int i; if (inflatorId >= 0 && inflatorId < SIZE) {
for (i = 0; i < SIZE; ++i) { inflateEnd(ourStreams[inflatorId]);
if (keys[i] == thiz) { delete ourStreams[inflatorId];
keys[i] = 0; ourStreams[inflatorId] = 0;
inflateEnd(values[i]);
delete values[i];
values[i] = 0;
break;
}
} }
} }
// returns (endFlag << 32) + ((used inLength) << 16) + outLength // returns (endFlag << 32) + ((used inLength) << 16) + outLength
extern "C" extern "C"
jlong Java_org_amse_ys_zip_DeflatingDecompressor_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, jint inflatorId, jbyteArray in, jint inOffset, jint inLength, jbyteArray out) {
int i; if (inflatorId < 0 || inflatorId >= SIZE) {
z_stream *stream = 0;
for (i = 0; i < SIZE; ++i) {
if (keys[i] == thiz) {
stream = values[i];
break;
}
}
if (stream == 0) {
return -1; return -1;
} }
z_stream *stream = ourStreams[inflatorId];
if (stream == 0) {
return -2;
}
jbyte* inStart = env->GetByteArrayElements(in, 0); jbyte* inStart = env->GetByteArrayElements(in, 0);
jbyte* outStart = env->GetByteArrayElements(out, 0); jbyte* outStart = env->GetByteArrayElements(out, 0);
@ -71,5 +60,5 @@ jlong Java_org_amse_ys_zip_DeflatingDecompressor_inflate(JNIEnv *env, jobject th
} }
return result; return result;
} }
return -2; return -3;
} }

View file

@ -26,7 +26,7 @@ public abstract class Decompressor {
} }
} }
public static Decompressor init(MyBufferedInputStream is, LocalFileHeader header) throws IOException { static Decompressor init(MyBufferedInputStream is, LocalFileHeader header) throws IOException {
switch (header.CompressionMethod) { switch (header.CompressionMethod) {
case 0: case 0:
return new NoCompressionDecompressor(is, header); return new NoCompressionDecompressor(is, header);

View file

@ -7,10 +7,10 @@ class DeflatingDecompressor extends Decompressor {
System.loadLibrary("DeflatingDecompressor"); System.loadLibrary("DeflatingDecompressor");
} }
// common variables // common variables
private MyBufferedInputStream myStream; private MyBufferedInputStream myStream;
private int myCompressedAvailable; private int myCompressedAvailable;
private int myAvailable; private int myAvailable;
private static final int IN_BUFFER_SIZE = 2048; private static final int IN_BUFFER_SIZE = 2048;
private static final int OUT_BUFFER_SIZE = 32768; private static final int OUT_BUFFER_SIZE = 32768;
@ -22,21 +22,21 @@ class DeflatingDecompressor extends Decompressor {
private int myOutBufferOffset; private int myOutBufferOffset;
private int myOutBufferLength; private int myOutBufferLength;
private boolean myInflatingInProgress; private volatile int myInflatorId = -1;
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 {
if (myInflatingInProgress) { if (myInflatorId != -1) {
endInflating(); endInflating(myInflatorId);
myInflatingInProgress = false; myInflatorId = -1;
} }
myStream = inputStream; myStream = inputStream;
myCompressedAvailable = header.CompressedSize; myCompressedAvailable = header.CompressedSize;
if (myCompressedAvailable <= 0) { if (myCompressedAvailable <= 0) {
myCompressedAvailable = Integer.MAX_VALUE; myCompressedAvailable = Integer.MAX_VALUE;
} }
@ -50,17 +50,19 @@ class DeflatingDecompressor extends Decompressor {
myOutBufferOffset = OUT_BUFFER_SIZE; myOutBufferOffset = OUT_BUFFER_SIZE;
myOutBufferLength = 0; myOutBufferLength = 0;
startInflating(); myInflatorId = startInflating();
myInflatingInProgress = true; if (myInflatorId == -1) {
} throw new IOException("cannot start inflating");
}
}
@Override @Override
public int available() { public int available() {
return myAvailable; return myAvailable;
} }
@Override @Override
public int read(byte[] b, int off, int len) throws IOException { public int read(byte[] b, int off, int len) throws IOException {
if (myAvailable <= 0) { if (myAvailable <= 0) {
return -1; return -1;
} }
@ -72,7 +74,7 @@ class DeflatingDecompressor extends Decompressor {
fillOutBuffer(); fillOutBuffer();
} }
if (myOutBufferLength == 0) { if (myOutBufferLength == 0) {
if (myInflatingInProgress) { if (myInflatorId != -1) {
throw new IOException("cannot read from zip"); throw new IOException("cannot read from zip");
} else { } else {
len -= toFill; len -= toFill;
@ -94,10 +96,10 @@ class DeflatingDecompressor extends Decompressor {
myAvailable = 0; myAvailable = 0;
} }
return len; return len;
} }
@Override @Override
public int read() throws IOException { public int read() throws IOException {
if (myAvailable <= 0) { if (myAvailable <= 0) {
return -1; return -1;
} }
@ -105,7 +107,7 @@ class DeflatingDecompressor extends Decompressor {
fillOutBuffer(); fillOutBuffer();
} }
if (myOutBufferLength == 0) { if (myOutBufferLength == 0) {
if (myInflatingInProgress) { if (myInflatorId != -1) {
throw new IOException("cannot read from zip"); throw new IOException("cannot read from zip");
} else { } else {
myAvailable = 0; myAvailable = 0;
@ -115,10 +117,10 @@ class DeflatingDecompressor extends Decompressor {
--myAvailable; --myAvailable;
--myOutBufferLength; --myOutBufferLength;
return myOutBuffer[myOutBufferOffset++]; return myOutBuffer[myOutBufferOffset++];
} }
private void fillOutBuffer() throws IOException { private void fillOutBuffer() throws IOException {
if (!myInflatingInProgress) { if (myInflatorId == -1) {
return; return;
} }
@ -136,7 +138,7 @@ class DeflatingDecompressor extends Decompressor {
if (myInBufferLength == 0) { if (myInBufferLength == 0) {
break; break;
} }
final long result = inflate(myInBuffer, myInBufferOffset, myInBufferLength, myOutBuffer); final long result = inflate(myInflatorId, myInBuffer, myInBufferOffset, myInBufferLength, myOutBuffer);
if (result <= 0) { if (result <= 0) {
throw new IOException("Cannot inflate zip-compressed block, code = " + result); throw new IOException("Cannot inflate zip-compressed block, code = " + result);
} }
@ -147,15 +149,15 @@ class DeflatingDecompressor extends Decompressor {
myOutBufferOffset = 0; myOutBufferOffset = 0;
myOutBufferLength = out; myOutBufferLength = out;
if ((result & (1L << 32)) != 0) { if ((result & (1L << 32)) != 0) {
endInflating(); endInflating(myInflatorId);
myInflatingInProgress = false; myInflatorId = -1;
myStream.backSkip(myInBufferLength); myStream.backSkip(myInBufferLength);
break; break;
} }
} }
} }
private native boolean startInflating(); private native int startInflating();
private native void endInflating(); private native void endInflating(int inflatorId);
private native long inflate(byte[] in, int inOffset, int inLength, byte[] out); private native long inflate(int inflatorId, byte[] in, int inOffset, int inLength, byte[] out);
} }

View file

@ -1,160 +1,161 @@
package org.amse.ys.zip; package org.amse.ys.zip;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
public final class ZipFile { public final class ZipFile {
public static interface InputStreamHolder { public static interface InputStreamHolder {
InputStream getInputStream() throws IOException; InputStream getInputStream() throws IOException;
} }
private static final class FileInputStreamHolder implements InputStreamHolder { private static final class FileInputStreamHolder implements InputStreamHolder {
private final String myFilePath; private final String myFilePath;
FileInputStreamHolder(String filePath) { FileInputStreamHolder(String filePath) {
myFilePath = filePath; myFilePath = filePath;
} }
public InputStream getInputStream() throws IOException { public InputStream getInputStream() throws IOException {
return new FileInputStream(myFilePath); return new FileInputStream(myFilePath);
} }
} }
private final InputStreamHolder myStreamHolder; private final InputStreamHolder myStreamHolder;
private final LinkedHashMap<String,LocalFileHeader> myFileHeaders = new LinkedHashMap<String,LocalFileHeader>() { private final LinkedHashMap<String,LocalFileHeader> myFileHeaders = new LinkedHashMap<String,LocalFileHeader>() {
private static final long serialVersionUID = -4412796553514902113L; private static final long serialVersionUID = -4412796553514902113L;
@Override @Override
public LocalFileHeader get(Object key) { public LocalFileHeader get(Object key) {
return super.get(((String)key).toLowerCase()); return super.get(((String)key).toLowerCase());
} }
@Override @Override
public LocalFileHeader put(String key, LocalFileHeader value) { public LocalFileHeader put(String key, LocalFileHeader value) {
return super.put(key.toLowerCase(), value); return super.put(key.toLowerCase(), value);
} }
}; };
private boolean myAllFilesAreRead; private boolean myAllFilesAreRead;
public ZipFile(String filePath) { public ZipFile(String filePath) {
this(new FileInputStreamHolder(filePath)); this(new FileInputStreamHolder(filePath));
} }
public ZipFile(InputStreamHolder streamHolder) { public ZipFile(InputStreamHolder streamHolder) {
myStreamHolder = streamHolder; myStreamHolder = streamHolder;
} }
public Collection<LocalFileHeader> headers() { public Collection<LocalFileHeader> headers() {
try { try {
readAllHeaders(); readAllHeaders();
} catch (IOException e) { } catch (IOException e) {
} }
return myFileHeaders.values(); return myFileHeaders.values();
} }
private boolean readFileHeader(MyBufferedInputStream baseStream, String fileToFind) throws IOException { private boolean readFileHeader(MyBufferedInputStream baseStream, String fileToFind) throws IOException {
LocalFileHeader header = new LocalFileHeader(); LocalFileHeader header = new LocalFileHeader();
header.readFrom(baseStream); header.readFrom(baseStream);
if (header.Signature != LocalFileHeader.FILE_HEADER_SIGNATURE) { if (header.Signature != LocalFileHeader.FILE_HEADER_SIGNATURE) {
return false; return false;
} }
if (header.FileName != null) { if (header.FileName != null) {
myFileHeaders.put(header.FileName, header); myFileHeaders.put(header.FileName, header);
if (header.FileName.equalsIgnoreCase(fileToFind)) { if (header.FileName.equalsIgnoreCase(fileToFind)) {
return true; return true;
} }
} }
if ((header.Flags & 0x08) == 0) { if ((header.Flags & 0x08) == 0) {
baseStream.skip(header.CompressedSize); baseStream.skip(header.CompressedSize);
} else { } else {
findAndReadDescriptor(baseStream, header); findAndReadDescriptor(baseStream, header);
} }
return false; return false;
} }
private void readAllHeaders() throws IOException { private void readAllHeaders() throws IOException {
if (myAllFilesAreRead) { if (myAllFilesAreRead) {
return; return;
} }
myAllFilesAreRead = true; myAllFilesAreRead = true;
MyBufferedInputStream baseStream = getBaseStream(); MyBufferedInputStream baseStream = getBaseStream();
baseStream.setPosition(0); baseStream.setPosition(0);
myFileHeaders.clear(); myFileHeaders.clear();
try { try {
while (true) { while (true) {
readFileHeader(baseStream, null); readFileHeader(baseStream, null);
} }
} finally { } finally {
storeBaseStream(baseStream); storeBaseStream(baseStream);
} }
} }
/** /**
* 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 {
Decompressor decompressor = Decompressor.init(baseStream, header); Decompressor decompressor = Decompressor.init(baseStream, header);
int uncompressedSize = 0; int uncompressedSize = 0;
while (true) { while (true) {
int blockSize = decompressor.read(null, 0, 2048); int blockSize = decompressor.read(null, 0, 2048);
if (blockSize <= 0) { if (blockSize <= 0) {
break; break;
} }
uncompressedSize += blockSize; uncompressedSize += blockSize;
} }
header.UncompressedSize = uncompressedSize; header.UncompressedSize = uncompressedSize;
} Decompressor.storeDecompressor(decompressor);
}
private final Queue<MyBufferedInputStream> myStoredStreams = new LinkedList<MyBufferedInputStream>();
private final Queue<MyBufferedInputStream> myStoredStreams = new LinkedList<MyBufferedInputStream>();
synchronized void storeBaseStream(MyBufferedInputStream baseStream) {
myStoredStreams.add(baseStream); synchronized void storeBaseStream(MyBufferedInputStream baseStream) {
} myStoredStreams.add(baseStream);
}
synchronized MyBufferedInputStream getBaseStream() throws IOException {
MyBufferedInputStream baseStream = myStoredStreams.poll(); synchronized MyBufferedInputStream getBaseStream() throws IOException {
return (baseStream != null) ? baseStream : new MyBufferedInputStream(myStreamHolder); MyBufferedInputStream baseStream = myStoredStreams.poll();
} return (baseStream != null) ? baseStream : new MyBufferedInputStream(myStreamHolder);
}
private ZipInputStream createZipInputStream(LocalFileHeader header) throws IOException {
return new ZipInputStream(this, header); private ZipInputStream createZipInputStream(LocalFileHeader header) throws IOException {
} return new ZipInputStream(this, header);
}
public int getEntrySize(String entryName) throws IOException {
return getHeader(entryName).UncompressedSize; public int getEntrySize(String entryName) throws IOException {
} return getHeader(entryName).UncompressedSize;
}
public InputStream getInputStream(String entryName) throws IOException {
return createZipInputStream(getHeader(entryName)); public InputStream getInputStream(String entryName) throws IOException {
} return createZipInputStream(getHeader(entryName));
}
public LocalFileHeader getHeader(String entryName) throws IOException {
if (!myFileHeaders.isEmpty()) { public LocalFileHeader getHeader(String entryName) throws IOException {
LocalFileHeader header = myFileHeaders.get(entryName); if (!myFileHeaders.isEmpty()) {
if (header != null) { LocalFileHeader header = myFileHeaders.get(entryName);
return header; if (header != null) {
} return header;
if (myAllFilesAreRead) { }
throw new ZipException("Entry " + entryName + " is not found"); if (myAllFilesAreRead) {
} throw new ZipException("Entry " + entryName + " is not found");
} }
// ready to read file header }
MyBufferedInputStream baseStream = getBaseStream(); // ready to read file header
baseStream.setPosition(0); MyBufferedInputStream baseStream = getBaseStream();
try { baseStream.setPosition(0);
while (!readFileHeader(baseStream, entryName)) { try {
} while (!readFileHeader(baseStream, entryName)) {
LocalFileHeader header = myFileHeaders.get(entryName); }
if (header != null) { LocalFileHeader header = myFileHeaders.get(entryName);
return header; if (header != null) {
} return header;
} finally { }
storeBaseStream(baseStream); } finally {
} storeBaseStream(baseStream);
throw new ZipException("Entry " + entryName + " is not found"); }
} throw new ZipException("Entry " + entryName + " is not found");
} }
}