mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8242596: Improve JarFile.getEntry performance for multi-release jar files
Co-authored-by: Eirik Bjørsnøs <eirbjo@gmail.com> Reviewed-by: lancea, redestad
This commit is contained in:
parent
07156dd25b
commit
aeb2f9492e
3 changed files with 123 additions and 30 deletions
|
@ -431,10 +431,6 @@ public class JarFile extends ZipFile {
|
|||
return man;
|
||||
}
|
||||
|
||||
private String[] getMetaInfEntryNames() {
|
||||
return JUZFA.getMetaInfEntryNames((ZipFile)this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code JarEntry} for the given base entry name or
|
||||
* {@code null} if not found.
|
||||
|
@ -504,11 +500,15 @@ public class JarFile extends ZipFile {
|
|||
* </div>
|
||||
*/
|
||||
public ZipEntry getEntry(String name) {
|
||||
JarFileEntry je = getEntry0(name);
|
||||
if (isMultiRelease()) {
|
||||
return getVersionedEntry(name, je);
|
||||
JarEntry je = getVersionedEntry(name, null);
|
||||
if (je == null) {
|
||||
je = getEntry0(name);
|
||||
}
|
||||
return je;
|
||||
} else {
|
||||
return getEntry0(name);
|
||||
}
|
||||
return je;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -598,21 +598,29 @@ public class JarFile extends ZipFile {
|
|||
return name;
|
||||
}
|
||||
|
||||
private JarEntry getVersionedEntry(String name, JarEntry je) {
|
||||
if (BASE_VERSION_FEATURE < versionFeature) {
|
||||
if (!name.startsWith(META_INF)) {
|
||||
private JarEntry getVersionedEntry(String name, JarEntry defaultEntry) {
|
||||
if (!name.startsWith(META_INF)) {
|
||||
int[] versions = JUZFA.getMetaInfVersions(this);
|
||||
if (BASE_VERSION_FEATURE < versionFeature && versions.length > 0) {
|
||||
// search for versioned entry
|
||||
int v = versionFeature;
|
||||
while (v > BASE_VERSION_FEATURE) {
|
||||
JarFileEntry vje = getEntry0(META_INF_VERSIONS + v + "/" + name);
|
||||
for (int i = versions.length - 1; i >= 0; i--) {
|
||||
int version = versions[i];
|
||||
// skip versions above versionFeature
|
||||
if (version > versionFeature) {
|
||||
continue;
|
||||
}
|
||||
// skip versions below base version
|
||||
if (version < BASE_VERSION_FEATURE) {
|
||||
break;
|
||||
}
|
||||
JarFileEntry vje = getEntry0(META_INF_VERSIONS + version + "/" + name);
|
||||
if (vje != null) {
|
||||
return vje.withBasename(name);
|
||||
}
|
||||
v--;
|
||||
}
|
||||
}
|
||||
}
|
||||
return je;
|
||||
return defaultEntry;
|
||||
}
|
||||
|
||||
// placeholder for now
|
||||
|
@ -707,7 +715,7 @@ public class JarFile extends ZipFile {
|
|||
}
|
||||
|
||||
if (verify) {
|
||||
String[] names = getMetaInfEntryNames();
|
||||
String[] names = JUZFA.getMetaInfEntryNames(this);
|
||||
if (names != null) {
|
||||
for (String nameLower : names) {
|
||||
String name = nameLower.toUpperCase(Locale.ENGLISH);
|
||||
|
@ -738,7 +746,7 @@ public class JarFile extends ZipFile {
|
|||
|
||||
// Verify "META-INF/" entries...
|
||||
try {
|
||||
String[] names = getMetaInfEntryNames();
|
||||
String[] names = JUZFA.getMetaInfEntryNames(this);
|
||||
if (names != null) {
|
||||
for (String name : names) {
|
||||
String uname = name.toUpperCase(Locale.ENGLISH);
|
||||
|
@ -932,7 +940,7 @@ public class JarFile extends ZipFile {
|
|||
if (manEntry == null) {
|
||||
// If not found, then iterate through all the "META-INF/"
|
||||
// entries to find a match.
|
||||
String[] names = getMetaInfEntryNames();
|
||||
String[] names = JUZFA.getMetaInfEntryNames(this);
|
||||
if (names != null) {
|
||||
for (String name : names) {
|
||||
if (MANIFEST_NAME.equals(name.toUpperCase(Locale.ENGLISH))) {
|
||||
|
@ -1016,7 +1024,7 @@ public class JarFile extends ZipFile {
|
|||
byte[] lbuf = new byte[512];
|
||||
Attributes attr = new Attributes();
|
||||
attr.read(new Manifest.FastInputStream(
|
||||
new ByteArrayInputStream(b)), lbuf);
|
||||
new ByteArrayInputStream(b)), lbuf);
|
||||
isMultiRelease = Boolean.parseBoolean(
|
||||
attr.getValue(Attributes.Name.MULTI_RELEASE));
|
||||
}
|
||||
|
@ -1068,7 +1076,7 @@ public class JarFile extends ZipFile {
|
|||
*/
|
||||
JarEntry newEntry(String name) {
|
||||
if (isMultiRelease()) {
|
||||
JarEntry vje = getVersionedEntry(name, (JarEntry)null);
|
||||
JarEntry vje = getVersionedEntry(name, null);
|
||||
if (vje != null) {
|
||||
return vje;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -50,14 +50,15 @@ import java.util.NoSuchElementException;
|
|||
import java.util.Set;
|
||||
import java.util.Spliterator;
|
||||
import java.util.Spliterators;
|
||||
import java.util.TreeSet;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
import jdk.internal.access.JavaLangAccess;
|
||||
import jdk.internal.access.JavaUtilZipFileAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.VM;
|
||||
|
@ -1048,8 +1049,21 @@ public class ZipFile implements ZipConstants, Closeable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the versions for which there exists a non-directory
|
||||
* entry that begin with "META-INF/versions/" (case ignored).
|
||||
* This method is used in JarFile, via SharedSecrets, as an
|
||||
* optimization when looking up potentially versioned entries.
|
||||
* Returns an empty array if no versioned entries exist.
|
||||
*/
|
||||
private int[] getMetaInfVersions() {
|
||||
synchronized (this) {
|
||||
ensureOpen();
|
||||
return res.zsrc.metaVersions;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isWindows;
|
||||
private static final JavaLangAccess JLA;
|
||||
|
||||
static {
|
||||
SharedSecrets.setJavaUtilZipFileAccess(
|
||||
|
@ -1059,8 +1073,12 @@ public class ZipFile implements ZipConstants, Closeable {
|
|||
return zip.res.zsrc.startsWithLoc;
|
||||
}
|
||||
@Override
|
||||
public String[] getMetaInfEntryNames(ZipFile zip) {
|
||||
return zip.getMetaInfEntryNames();
|
||||
public String[] getMetaInfEntryNames(JarFile jar) {
|
||||
return ((ZipFile)jar).getMetaInfEntryNames();
|
||||
}
|
||||
@Override
|
||||
public int[] getMetaInfVersions(JarFile jar) {
|
||||
return ((ZipFile)jar).getMetaInfVersions();
|
||||
}
|
||||
@Override
|
||||
public JarEntry getEntry(ZipFile zip, String name,
|
||||
|
@ -1083,11 +1101,14 @@ public class ZipFile implements ZipConstants, Closeable {
|
|||
}
|
||||
}
|
||||
);
|
||||
JLA = SharedSecrets.getJavaLangAccess();
|
||||
isWindows = VM.getSavedProperty("os.name").contains("Windows");
|
||||
}
|
||||
|
||||
private static class Source {
|
||||
// "META-INF/".length()
|
||||
private static final int META_INF_LENGTH = 9;
|
||||
private static final int[] EMPTY_META_VERSIONS = new int[0];
|
||||
|
||||
private final Key key; // the key in files
|
||||
private int refs = 1;
|
||||
|
||||
|
@ -1097,6 +1118,7 @@ public class ZipFile implements ZipConstants, Closeable {
|
|||
private byte[] comment; // zip file comment
|
||||
// list of meta entries in META-INF dir
|
||||
private int[] metanames;
|
||||
private int[] metaVersions; // list of unique versions found in META-INF/versions/
|
||||
private final boolean startsWithLoc; // true, if zip file starts with LOCSIG (usually true)
|
||||
|
||||
// A Hashmap for all entries.
|
||||
|
@ -1237,6 +1259,7 @@ public class ZipFile implements ZipConstants, Closeable {
|
|||
entries = null;
|
||||
table = null;
|
||||
metanames = null;
|
||||
metaVersions = EMPTY_META_VERSIONS;
|
||||
}
|
||||
|
||||
private static final int BUF_SIZE = 8192;
|
||||
|
@ -1424,6 +1447,8 @@ public class ZipFile implements ZipConstants, Closeable {
|
|||
|
||||
// list for all meta entries
|
||||
ArrayList<Integer> metanamesList = null;
|
||||
// Set of all version numbers seen in META-INF/versions/
|
||||
Set<Integer> metaVersionsSet = null;
|
||||
|
||||
// Iterate through the entries in the central directory
|
||||
int i = 0;
|
||||
|
@ -1461,6 +1486,17 @@ public class ZipFile implements ZipConstants, Closeable {
|
|||
if (metanamesList == null)
|
||||
metanamesList = new ArrayList<>(4);
|
||||
metanamesList.add(pos);
|
||||
|
||||
// If this is a versioned entry, parse the version
|
||||
// and store it for later. This optimizes lookup
|
||||
// performance in multi-release jar files
|
||||
int version = getMetaVersion(cen,
|
||||
pos + CENHDR + META_INF_LENGTH, nlen - META_INF_LENGTH);
|
||||
if (version > 0) {
|
||||
if (metaVersionsSet == null)
|
||||
metaVersionsSet = new TreeSet<>();
|
||||
metaVersionsSet.add(version);
|
||||
}
|
||||
}
|
||||
// skip ext and comment
|
||||
pos += (CENHDR + nlen + elen + clen);
|
||||
|
@ -1473,6 +1509,15 @@ public class ZipFile implements ZipConstants, Closeable {
|
|||
metanames[j] = metanamesList.get(j);
|
||||
}
|
||||
}
|
||||
if (metaVersionsSet != null) {
|
||||
metaVersions = new int[metaVersionsSet.size()];
|
||||
int c = 0;
|
||||
for (Integer version : metaVersionsSet) {
|
||||
metaVersions[c++] = version;
|
||||
}
|
||||
} else {
|
||||
metaVersions = EMPTY_META_VERSIONS;
|
||||
}
|
||||
if (pos + ENDHDR != cen.length) {
|
||||
zerror("invalid CEN header (bad header size)");
|
||||
}
|
||||
|
@ -1550,7 +1595,7 @@ public class ZipFile implements ZipConstants, Closeable {
|
|||
*/
|
||||
private static boolean isMetaName(byte[] name, int off, int len) {
|
||||
// Use the "oldest ASCII trick in the book"
|
||||
return len > 9 // "META-INF/".length()
|
||||
return len > META_INF_LENGTH // "META-INF/".length()
|
||||
&& name[off + len - 1] != '/' // non-directory
|
||||
&& (name[off++] | 0x20) == 'm'
|
||||
&& (name[off++] | 0x20) == 'e'
|
||||
|
@ -1563,6 +1608,45 @@ public class ZipFile implements ZipConstants, Closeable {
|
|||
&& (name[off] ) == '/';
|
||||
}
|
||||
|
||||
/*
|
||||
* If the bytes represents a non-directory name beginning
|
||||
* with "versions/", continuing with a positive integer,
|
||||
* followed by a '/', then return that integer value.
|
||||
* Otherwise, return 0
|
||||
*/
|
||||
private static int getMetaVersion(byte[] name, int off, int len) {
|
||||
int nend = off + len;
|
||||
if (!(len > 10 // "versions//".length()
|
||||
&& name[off + len - 1] != '/' // non-directory
|
||||
&& (name[off++] | 0x20) == 'v'
|
||||
&& (name[off++] | 0x20) == 'e'
|
||||
&& (name[off++] | 0x20) == 'r'
|
||||
&& (name[off++] | 0x20) == 's'
|
||||
&& (name[off++] | 0x20) == 'i'
|
||||
&& (name[off++] | 0x20) == 'o'
|
||||
&& (name[off++] | 0x20) == 'n'
|
||||
&& (name[off++] | 0x20) == 's'
|
||||
&& (name[off++] ) == '/')) {
|
||||
return 0;
|
||||
}
|
||||
int version = 0;
|
||||
while (off < nend) {
|
||||
final byte c = name[off++];
|
||||
if (c == '/') {
|
||||
return version;
|
||||
}
|
||||
if (c < '0' || c > '9') {
|
||||
return 0;
|
||||
}
|
||||
version = version * 10 + c - '0';
|
||||
// Check for overflow and leading zeros
|
||||
if (version <= 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of CEN headers in a central directory.
|
||||
* Will not throw, even if the zip file is corrupt.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -25,16 +25,17 @@
|
|||
|
||||
package jdk.internal.access;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.function.Function;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
public interface JavaUtilZipFileAccess {
|
||||
public boolean startsWithLocHeader(ZipFile zip);
|
||||
public String[] getMetaInfEntryNames(ZipFile zip);
|
||||
public String[] getMetaInfEntryNames(JarFile zip);
|
||||
public int[] getMetaInfVersions(JarFile zip);
|
||||
public JarEntry getEntry(ZipFile zip, String name, Function<String, JarEntry> func);
|
||||
public Enumeration<JarEntry> entries(ZipFile zip, Function<String, JarEntry> func);
|
||||
public Stream<JarEntry> stream(ZipFile zip, Function<String, JarEntry> func);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue