8242959: Optimize ZipFile.getEntry by folding lookups for name and name+'/'

Reviewed-by: lancea, redestad
This commit is contained in:
Eirik Bjørsnøs 2020-04-18 20:19:45 +02:00 committed by Claes Redestad
parent d185fe9953
commit fc728278c2

View file

@ -1289,6 +1289,12 @@ public class ZipFile implements ZipConstants, Closeable {
} }
private static final int hashN(byte[] a, int off, int len) { private static final int hashN(byte[] a, int off, int len) {
// Performance optimization: getEntryPos assumes that the hash
// of a name remains unchanged when appending a trailing '/'.
if (len > 0 && a[off + len - 1] == '/') {
len--;
}
int h = 1; int h = 1;
while (len-- > 0) { while (len-- > 0) {
h = 31 * h + a[off++]; h = 31 * h + a[off++];
@ -1296,10 +1302,6 @@ public class ZipFile implements ZipConstants, Closeable {
return h; return h;
} }
private static final int hashAppend(int hash, byte b) {
return hash * 31 + b;
}
private static class End { private static class End {
int centot; // 4 bytes int centot; // 4 bytes
long cenlen; // 4 bytes long cenlen; // 4 bytes
@ -1537,56 +1539,41 @@ public class ZipFile implements ZipConstants, Closeable {
} }
int hsh = hashN(name, 0, name.length); int hsh = hashN(name, 0, name.length);
int idx = table[(hsh & 0x7fffffff) % tablelen]; int idx = table[(hsh & 0x7fffffff) % tablelen];
boolean appendSlash = false;
/* // Search down the target hash chain for a entry whose
* This while loop is an optimization where a double lookup // 32 bit hash matches the hashed name.
* for name and name+/ is being performed. The name byte while (idx != ZIP_ENDCHAIN) {
* array will be updated with an added slash only if the first if (getEntryHash(idx) == hsh) {
* table lookup fails and there is a matching hash value for // The CEN name must match the specfied one
* name+/. int pos = getEntryPos(idx);
*/ final int nlen = CENNAM(cen, pos);
while (true) { final int nstart = pos + CENHDR;
/*
* Search down the target hash chain for a entry whose // If addSlash is true, we're testing for name+/ in
* 32 bit hash matches the hashed name. // addition to name, unless name is the empty string
*/ // or already ends with a slash
while (idx != ZIP_ENDCHAIN) { if (name.length == nlen ||
if (getEntryHash(idx) == hsh) { (addSlash &&
if (appendSlash) { name.length > 0 &&
name = Arrays.copyOf(name, name.length + 1); name.length + 1 == nlen &&
name[name.length - 1] = '/'; cen[nstart + nlen - 1] == '/' &&
appendSlash = false; name[name.length - 1] != '/')) {
boolean matched = true;
int nameoff = pos + CENHDR;
for (int i = 0; i < name.length; i++) {
if (name[i] != cen[nameoff++]) {
matched = false;
break;
}
}
if (matched) {
return pos;
} }
// The CEN name must match the specfied one
int pos = getEntryPos(idx);
if (name.length == CENNAM(cen, pos)) {
boolean matched = true;
int nameoff = pos + CENHDR;
for (int i = 0; i < name.length; i++) {
if (name[i] != cen[nameoff++]) {
matched = false;
break;
}
}
if (matched) {
return pos;
}
}
} }
idx = getEntryNext(idx);
} }
/* If not addSlash, or slash is already there, we are done */ idx = getEntryNext(idx);
if (!addSlash || name.length == 0 || name[name.length - 1] == '/') {
return -1;
}
// Add a slash to the hash code
hsh = hashAppend(hsh, (byte)'/');
// If we find a match on the new hash code, we need to append a
// slash when comparing
appendSlash = true;
idx = table[(hsh & 0x7fffffff) % tablelen];
addSlash = false;
} }
return -1;
} }
/** /**