--- old/src/java.base/share/classes/java/util/zip/ZipFile.java 2015-05-22 11:32:38.471612772 -0700 +++ new/src/java.base/share/classes/java/util/zip/ZipFile.java 2015-05-22 11:32:38.347612776 -0700 @@ -25,6 +25,7 @@ package java.util.zip; +import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.InputStream; import java.io.IOException; @@ -67,6 +68,9 @@ private static final int STORED = ZipEntry.STORED; private static final int DEFLATED = ZipEntry.DEFLATED; + // Max buffer size when returning bytebuffers directly + private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; + /** * Mode flag to open a zip file for reading. */ @@ -345,8 +349,9 @@ if (entry == null) { throw new NullPointerException("entry"); } - long jzentry = 0; - ZipFileInputStream in = null; + + long jzentry, csize, size; + int cmethod; synchronized (this) { ensureOpen(); if (!zc.isUTF8() && (entry.flag & EFS) != 0) { @@ -357,30 +362,46 @@ if (jzentry == 0) { return null; } - in = new ZipFileInputStream(jzentry); + size = getEntrySize(jzentry); + csize = getEntryCSize(jzentry); + cmethod = getEntryMethod(jzentry); + } - switch (getEntryMethod(jzentry)) { - case STORED: - synchronized (streams) { - streams.put(in, null); - } - return in; - case DEFLATED: - // MORE: Compute good size for inflater stream: - long size = getEntrySize(jzentry) + 2; // Inflater likes a bit of slack - if (size > 65536) size = 8192; - if (size <= 0) size = 4096; - Inflater inf = getInflater(); - InputStream is = - new ZipFileInflaterInputStream(in, inf, (int)size); - synchronized (streams) { - streams.put(is, inf); + if (csize >= 0 && size > 0 && size < 128 * 1024) { + try { + return new ByteArrayInputStream(getBytes(jzentry, csize, size, cmethod)); + } finally { + synchronized (this) { + if (jzfile != 0) { + freeEntry(jzfile, jzentry); + } } - return is; - default: - throw new ZipException("invalid compression method"); } } + + ZipFileInputStream in = new ZipFileInputStream(jzentry, csize, size); + + switch (cmethod) { + case STORED: + synchronized (streams) { + streams.put(in, null); + } + return in; + case DEFLATED: + // MORE: Compute good size for inflater stream: + size += 2; // Inflater likes a bit of slack + if (size > 65536) size = 8192; + if (size <= 0) size = 4096; + Inflater inf = getInflater(); + InputStream is = + new ZipFileInflaterInputStream(in, inf, (int)size); + synchronized (streams) { + streams.put(is, inf); + } + return is; + default: + throw new ZipException("invalid compression method"); + } } private class ZipFileInflaterInputStream extends InflaterInputStream { @@ -469,6 +490,103 @@ // List of available Inflater objects for decompression private Deque inflaterCache = new ArrayDeque<>(); + /* + * Uncompress the zip entry into a new byte[]. + * + * This method can only read entries smaller than 2GB, for larger entries + * use getInputStream. + * + * @param entry the zip file entry + * + * @return the byte[] with the deflated contents of the specified zip file entry. + * + * @throws ZipException if a ZIP format error has occurred + * @throws IOException if an I/O error has occurred + * @throws IllegalStateException if the zip file has been closed + * @throws OutOfMemory if the zip entry is larger than 2GB + */ + private byte[] getBytes(ZipEntry entry) throws IOException { + if (entry == null) { + throw new NullPointerException("entry"); + } + + long jzentry, csize, size; + int cmethod; + synchronized (this) { + ensureOpen(); + if (!zc.isUTF8() && (entry.flag & EFS) != 0) { + jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), false); + } else { + jzentry = getEntry(jzfile, zc.getBytes(entry.name), false); + } + if (jzentry == 0) { + return null; + } + + csize = getEntryCSize(jzentry); + size = getEntrySize(jzentry); + cmethod = getEntryMethod(jzentry); + } + + try { + return getBytes(jzentry, csize, size, cmethod); + } finally { + synchronized (this) { + if (jzfile != 0) { + freeEntry(jzfile, jzentry); + } + } + } + } + + private byte[] getBytes(long jzentry, long csize, long size, int cmethod) + throws IOException { + + if (csize < 0 || size < 0) { + throw new ZipException("Unknown size of ZipEntry"); + } + if (csize > MAX_BUFFER_SIZE || size > MAX_BUFFER_SIZE) { + throw new OutOfMemoryError("ZipEntry too large"); + } + + byte[] cbytes = new byte[(int) csize]; + readCompressedBytes(jzentry, (int) csize, cbytes); + switch (cmethod) { + case STORED: + return cbytes; + case DEFLATED: + byte[] bytes = new byte[(int) size]; + Inflater inf = getInflater(); + try { + inf.setInput(cbytes); + inf.inflate(bytes); + } catch (DataFormatException e) { + String s = e.getMessage(); + throw new ZipException(s != null ? s : "Invalid ZLIB data format"); + } finally { + releaseInflater(inf); + } + return bytes; + default: + throw new ZipException("invalid compression method"); + } + } + + private int readCompressedBytes(long jzentry, int csize, byte[] buf) + throws IOException { + assert csize == buf.length; + synchronized (this) { + ensureOpenOrZipException(); + int n = 0; + int nread = 0; + while (nread < csize + && (n = read(jzfile, jzentry, nread, buf, nread, csize - nread)) > 0) { + nread += n; + } + return nread; + } + } + /** * Returns the path name of the ZIP file. * @return the path name of the ZIP file @@ -691,10 +809,10 @@ protected long rem; // number of remaining bytes within entry protected long size; // uncompressed size of this entry - ZipFileInputStream(long jzentry) { + ZipFileInputStream(long jzentry, long csize, long size) { pos = 0; - rem = getEntryCSize(jzentry); - size = getEntrySize(jzentry); + rem = csize; + this.size = size; this.jzentry = jzentry; } @@ -779,9 +897,16 @@ static { sun.misc.SharedSecrets.setJavaUtilZipFileAccess( new sun.misc.JavaUtilZipFileAccess() { + @Override public boolean startsWithLocHeader(ZipFile zip) { return zip.startsWithLocHeader(); } + + @Override + public byte[] getBytes(ZipFile zip, ZipEntry entry) + throws IOException { + return zip.getBytes(entry); + } } ); }