|
@@ -24,7 +24,7 @@ public class ZipReader {
|
|
|
* @throws IOException on error
|
|
* @throws IOException on error
|
|
|
*/
|
|
*/
|
|
|
public static JarInputStream openJAR(InputStream istream) throws IOException {
|
|
public static JarInputStream openJAR(InputStream istream) throws IOException {
|
|
|
- return new JarInputStream(new ZipInputStream2(istream));
|
|
|
|
|
|
|
+ return new JarInputStream(new SkipInputStream(istream));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -34,57 +34,115 @@ public class ZipReader {
|
|
|
* @return ZipInputStream
|
|
* @return ZipInputStream
|
|
|
*/
|
|
*/
|
|
|
public static ZipInputStream openZIP(InputStream istream) {
|
|
public static ZipInputStream openZIP(InputStream istream) {
|
|
|
- return new ZipInputStream(new ZipInputStream2(istream));
|
|
|
|
|
|
|
+ return new ZipInputStream(new SkipInputStream(istream));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private static class ZipInputStream2 extends FilterInputStream {
|
|
|
|
|
|
|
+ private static class SkipInputStream extends FilterInputStream {
|
|
|
|
|
|
|
|
- public static final byte[] ZIP_LOCAL = { 0x50, 0x4b, 0x03, 0x04 };
|
|
|
|
|
|
|
+ private static final int BUFFER_SIZE = 512;
|
|
|
|
|
|
|
|
- protected int ip;
|
|
|
|
|
|
|
+ private static final byte[] ZIP_LOCAL = { 0x50, 0x4b, 0x03, 0x04 };
|
|
|
|
|
|
|
|
- protected int op;
|
|
|
|
|
|
|
+ private byte[] buffer;
|
|
|
|
|
+ private int bufferSize;
|
|
|
|
|
+ private int bufferPosition;
|
|
|
|
|
|
|
|
- public ZipInputStream2(InputStream is) {
|
|
|
|
|
|
|
+ private int signatureFound;
|
|
|
|
|
+ private int signatureReturned;
|
|
|
|
|
+
|
|
|
|
|
+ public SkipInputStream(InputStream is) {
|
|
|
super(is);
|
|
super(is);
|
|
|
|
|
+ this.buffer = new byte[BUFFER_SIZE];
|
|
|
|
|
+ this.bufferSize = 0;
|
|
|
|
|
+ this.bufferPosition = 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+
|
|
|
public int read() throws IOException {
|
|
public int read() throws IOException {
|
|
|
- // skip data until full ZIP_LOCAL is found
|
|
|
|
|
- // do nothing if it is already found
|
|
|
|
|
- while(ip < ZIP_LOCAL.length) {
|
|
|
|
|
- int c = super.read();
|
|
|
|
|
- if (c == ZIP_LOCAL[ip]) {
|
|
|
|
|
- ip++;
|
|
|
|
|
|
|
+ // if we already returned ZIP_LOCAL and data from skipBuffer, then delegate to ordinary I/O
|
|
|
|
|
+ if(buffer == null) {
|
|
|
|
|
+ return super.read();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // If ZIP_LOCAL is not found, search it by using buffered "read"
|
|
|
|
|
+ // We are going to skip everything before ZIP_LOCAL, including ZIP_LOCAL itself.
|
|
|
|
|
+ // Skipping ZIP_LOCAL is necessary because it can be stored across two consecutive buffered reads
|
|
|
|
|
+ while(signatureFound < ZIP_LOCAL.length) {
|
|
|
|
|
+ int c = readbuf();
|
|
|
|
|
+ if(c == -1) {
|
|
|
|
|
+ return -1;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (c == ZIP_LOCAL[signatureFound]) {
|
|
|
|
|
+ signatureFound++;
|
|
|
} else {
|
|
} else {
|
|
|
- ip = 0;
|
|
|
|
|
|
|
+ signatureFound = 0;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- // despite the fact, that we physically skipped signature,
|
|
|
|
|
- // we should return it to the reader byte by byte
|
|
|
|
|
- return op < ZIP_LOCAL.length ? ZIP_LOCAL[op++] : super.read();
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // "ZIP_LOCAL" is consumed from "buffer"
|
|
|
|
|
+ // we need to return it literally, by reading ZIP_LOCAL byte by byte
|
|
|
|
|
+ if(signatureReturned < ZIP_LOCAL.length) {
|
|
|
|
|
+ return ZIP_LOCAL[signatureReturned++];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // buffer can contain some bytes after ZIP_LOCAL, let's return them byte by byte
|
|
|
|
|
+ if(bufferPosition < bufferSize) {
|
|
|
|
|
+ // "readbuf" won't fill new buffer because it fills it only if bufferPosition == bufferSize
|
|
|
|
|
+ return readbuf();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // if we returned whole buffer, lets clear it and delegate to ordinary I/O
|
|
|
|
|
+ // method will detect empty buffer on next calls
|
|
|
|
|
+ buffer = null;
|
|
|
|
|
+ return super.read();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private int readbuf() throws IOException {
|
|
|
|
|
+ // read new block if current one is exhausted
|
|
|
|
|
+ if(bufferPosition >= bufferSize) {
|
|
|
|
|
+ bufferSize = super.read(buffer, 0, buffer.length);
|
|
|
|
|
+ if(bufferSize == -1) {
|
|
|
|
|
+ buffer = null;
|
|
|
|
|
+ return -1;
|
|
|
|
|
+ }
|
|
|
|
|
+ bufferPosition = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ // return data from buffer, byte by byte
|
|
|
|
|
+ return buffer[bufferPosition++];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
- public int read(byte[] b, int off, int len) throws IOException {
|
|
|
|
|
- // if we already returned signature, then just return data
|
|
|
|
|
- if (op == ZIP_LOCAL.length) {
|
|
|
|
|
- return super.read(b, off, len);
|
|
|
|
|
|
|
+ public int read(byte[] output, int off, int len) throws IOException {
|
|
|
|
|
+ // if we already returned signature, then use ordinary I/O
|
|
|
|
|
+ if (buffer == null) {
|
|
|
|
|
+ return super.read(output, off, len);
|
|
|
}
|
|
}
|
|
|
- // if we did not return signature, then we delegate work to our "read"
|
|
|
|
|
- // byte by byte and fill buffer with correct bytes
|
|
|
|
|
- //
|
|
|
|
|
- // "read" will skip prefix and will return signature followed by content
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // read byte by byte from our buffer
|
|
|
|
|
+ int rem = Math.min(len, bufferRemaining());
|
|
|
int count = 0;
|
|
int count = 0;
|
|
|
- // @todo #89
|
|
|
|
|
- // probably we will miscalculate number of bytes if signature is partially read
|
|
|
|
|
- // we should use here
|
|
|
|
|
- // int zip = ZIP_LOCAL.length-op
|
|
|
|
|
- // while (count < Math.min(len, zip)) ...
|
|
|
|
|
- while (count < Math.min(len, ZIP_LOCAL.length)) {
|
|
|
|
|
- b[count++] = (byte)read();
|
|
|
|
|
|
|
+ while (count < rem) {
|
|
|
|
|
+ byte b = (byte) read();
|
|
|
|
|
+ if(b == -1) {
|
|
|
|
|
+ return count;
|
|
|
|
|
+ }
|
|
|
|
|
+ output[off + count++] = b;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // if we read "len" bytes, then just return
|
|
|
|
|
+ if(count == len) {
|
|
|
|
|
+ return count;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // if we read less than "len" bytes, then use ordinary I/O for read remaining bytes
|
|
|
|
|
+ return count + super.read(output, off + count, len - count);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private int bufferRemaining() {
|
|
|
|
|
+ if(signatureReturned != ZIP_LOCAL.length) {
|
|
|
|
|
+ return buffer.length + ZIP_LOCAL.length;
|
|
|
}
|
|
}
|
|
|
- return count;
|
|
|
|
|
|
|
+ return bufferSize - bufferPosition;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|