< prev index next >
src/java.base/share/classes/java/util/zip/ZipFile.java
Print this page
*** 28,37 ****
--- 28,40 ----
import java.io.Closeable;
import java.io.InputStream;
import java.io.IOException;
import java.io.EOFException;
import java.io.File;
+ import java.nio.ByteBuffer;
+ import java.nio.ByteOrder;
+ import java.nio.IntBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Enumeration;
*** 57,66 ****
--- 60,72 ----
* @author David Connelly
*/
public
class ZipFile implements ZipConstants, Closeable {
private long jzfile; // address of jzfile data
+ private IntBuffer table; // direct buffer mapping jzfile->table
+ private ByteBuffer entries; // direct buffer mapping jzfile->entries
+ private int falsePositiveProbes;
private final String name; // zip file name
private final int total; // total number of entries
private final boolean locsig; // if zip file starts with LOCSIG (usually true)
private volatile boolean closeRequested = false;
*** 87,103 ****
--- 93,116 ----
}
private static native void initIDs();
private static final boolean usemmap;
+ private static final boolean useNativeTableProbe;
static {
// A system prpperty to disable mmap use to avoid vm crash when
// in-use zip file is accidently overwritten by others.
String prop = sun.misc.VM.getSavedProperty("sun.zip.disableMemoryMapping");
usemmap = (prop == null ||
!(prop.length() == 0 || prop.equalsIgnoreCase("true")));
+ // A system property to enable mapping of native hash table of entries
+ // using direct byte buffers to facilitate a probe that speeds-up lookups
+ // for non-existent entries (important for class loading with large
+ // class-paths where JAR files are tried in order to find a class/resource)
+ prop = System.getProperty("sun.zip.useNativeTableProbe");
+ useNativeTableProbe = prop != null && prop.equalsIgnoreCase("true");
}
/**
* Opens a zip file for reading.
*
*** 214,224 ****
}
if (charset == null)
throw new NullPointerException("charset is null");
this.zc = ZipCoder.get(charset);
long t0 = System.nanoTime();
! jzfile = open(name, mode, file.lastModified(), usemmap);
sun.misc.PerfCounter.getZipFileOpenTime().addElapsedTimeFrom(t0);
sun.misc.PerfCounter.getZipFileCount().increment();
this.name = name;
this.total = getTotal(jzfile);
this.locsig = startsWithLOC(jzfile);
--- 227,242 ----
}
if (charset == null)
throw new NullPointerException("charset is null");
this.zc = ZipCoder.get(charset);
long t0 = System.nanoTime();
! ByteBuffer[] tableAndEntries = useNativeTableProbe ? new ByteBuffer[2] : null;
! jzfile = open(name, mode, file.lastModified(), usemmap, tableAndEntries);
! if (tableAndEntries != null) {
! table = tableAndEntries[0].order(ByteOrder.nativeOrder()).asIntBuffer();
! entries = tableAndEntries[1].order(ByteOrder.nativeOrder());
! }
sun.misc.PerfCounter.getZipFileOpenTime().addElapsedTimeFrom(t0);
sun.misc.PerfCounter.getZipFileCount().increment();
this.name = name;
this.total = getTotal(jzfile);
this.locsig = startsWithLOC(jzfile);
*** 302,324 ****
*/
public ZipEntry getEntry(String name) {
if (name == null) {
throw new NullPointerException("name");
}
- long jzentry = 0;
synchronized (this) {
ensureOpen();
! jzentry = getEntry(jzfile, zc.getBytes(name), true);
if (jzentry != 0) {
ZipEntry ze = getZipEntry(name, jzentry);
freeEntry(jzfile, jzentry);
return ze;
}
}
return null;
}
private static native long getEntry(long jzfile, byte[] name,
boolean addSlash);
// freeEntry releases the C jzentry struct.
private static native void freeEntry(long jzfile, long jzentry);
--- 320,406 ----
*/
public ZipEntry getEntry(String name) {
if (name == null) {
throw new NullPointerException("name");
}
synchronized (this) {
ensureOpen();
! byte[] nameBytes = zc.getBytes(name);
! int h = hash(nameBytes);
! if (mayContainEntry(h) ||
! (name.charAt(name.length()-1) != '/' && mayContainEntry(31 * h + '/'))) {
! long jzentry = getEntry(jzfile, nameBytes, true);
if (jzentry != 0) {
ZipEntry ze = getZipEntry(name, jzentry);
freeEntry(jzfile, jzentry);
return ze;
+ } else {
+ falsePositiveProbes++;
+ }
}
}
return null;
}
+ /**
+ * Lookup into a hash table maintained in two native arrays (table and entries)
+ * to probe whether there may be an entry present with specified 32 bit hash.
+ */
+ private boolean mayContainEntry(int h) {
+ // when sun.zip.useNativeTableProbe is not defined,
+ // any entry hash may be valid...
+ if (table == null) return true;
+
+ // use unsigned modulus to calculate index into hash table
+ int i = Integer.remainderUnsigned(h, table.capacity());
+ // obtain index into entries array which holds a head of the hash table bucket
+ int head = table.get(i);
+ // special -1 value marks end of chain
+ while (head != -1) {
+ //
+ // entries array is composed of the following 16-byte entries (zip_util.h):
+ //
+ // typedef struct jzcell {
+ // unsigned int hash; /* 32 bit hashcode on name */
+ // unsigned int next; /* hash chain: index into jzfile->entries */
+ // jlong cenpos; /* Offset of central directory file header */
+ // } jzcell;
+ //
+ int hi = head << 4; // index -> byte offset
+ if (entries.getInt(hi) == h) { // entries[head].hash
+ // got it
+ // TODO: return jzcell.cenpos to save native code from re-executing the same lookup
+ return true;
+ }
+ // advance to next in chain
+ head = entries.getInt(hi + 4); // entries[head].next
+ }
+ // no luck
+ return false;
+ }
+
+ /**
+ * Equivalent to the following from zip_util.c:
+ * <pre>
+ * static unsigned int
+ * hashN(const char *s, int length)
+ * {
+ * int h = 0;
+ * while (length-- > 0)
+ * h = 31*h + *s++;
+ * return h;
+ * }
+ * </pre>
+ */
+ private static int hash(byte[] bytes) {
+ int h = 0;
+ for (int i = 0; i < bytes.length; i++) {
+ h = 31 * h + ((int) bytes[i] & 0xFF);
+ }
+ return h;
+ }
+
private static native long getEntry(long jzfile, byte[] name,
boolean addSlash);
// freeEntry releases the C jzentry struct.
private static native void freeEntry(long jzfile, long jzentry);
*** 640,650 ****
if (jzfile != 0) {
// Close the zip file
long zf = this.jzfile;
jzfile = 0;
!
close(zf);
}
}
}
--- 722,733 ----
if (jzfile != 0) {
// Close the zip file
long zf = this.jzfile;
jzfile = 0;
! table = null;
! entries = null;
close(zf);
}
}
}
*** 797,807 ****
private boolean startsWithLocHeader() {
return locsig;
}
private static native long open(String name, int mode, long lastModified,
! boolean usemmap) throws IOException;
private static native int getTotal(long jzfile);
private static native boolean startsWithLOC(long jzfile);
private static native int read(long jzfile, long jzentry,
long pos, byte[] b, int off, int len);
--- 880,891 ----
private boolean startsWithLocHeader() {
return locsig;
}
private static native long open(String name, int mode, long lastModified,
! boolean usemmap,
! ByteBuffer[] tableAndEntries) throws IOException;
private static native int getTotal(long jzfile);
private static native boolean startsWithLOC(long jzfile);
private static native int read(long jzfile, long jzentry,
long pos, byte[] b, int off, int len);
< prev index next >