> > I think you could tune away a significant part of that up front cost by > using JUZFA.entryNameStream(this) instead of > this.stream().map(ZipEntry::getName). This will avoid expanding each > entry into a JarEntry internally. Perhaps this gets the up-front > overhead down to more acceptable levels..? >
I found JUZFA.getMetaInfEntryNames which made the up front scanning cost evaporate. Should be fast for other jars as well, at least when META-INF/ is sparsely populated. Here's an updated patch: diff --git a/src/java.base/share/classes/java/util/jar/JarFile.java b/src/java.base/share/classes/java/util/jar/JarFile.java index 1ec0f5bdae..95472604c0 100644 --- a/src/java.base/share/classes/java/util/jar/JarFile.java +++ b/src/java.base/share/classes/java/util/jar/JarFile.java @@ -161,7 +161,7 @@ public class JarFile extends ZipFile { private final Runtime.Version version; // current version private final int versionFeature; // version.feature() private boolean isMultiRelease; // is jar multi-release? - + private int[] versions; // which versions does the jar contain // indicates if Class-Path attribute present private boolean hasClassPathAttribute; // true if manifest checked for special attributes @@ -599,12 +599,13 @@ public class JarFile extends ZipFile { } private JarEntry getVersionedEntry(String name, JarEntry je) { - if (BASE_VERSION_FEATURE < versionFeature) { + int[] versions = this.versions; + if (BASE_VERSION_FEATURE < versionFeature && versions != null && versions.length > 0) { if (!name.startsWith(META_INF)) { // search for versioned entry - int v = versionFeature; - while (v > BASE_VERSION_FEATURE) { - JarFileEntry vje = getEntry0(META_INF_VERSIONS + v + "/" + name); + int v = versions.length - 1; + while (v >= 0) { + JarFileEntry vje = getEntry0(META_INF_VERSIONS + versions[v] + "/" + name); if (vje != null) { return vje.withBasename(name); } @@ -1016,9 +1017,18 @@ public class JarFile extends ZipFile { byte[] lbuf = new byte[512]; Attributes attr = new Attributes(); attr.read(new Manifest.FastInputStream( - new ByteArrayInputStream(b)), lbuf); - isMultiRelease = Boolean.parseBoolean( - attr.getValue(Attributes.Name.MULTI_RELEASE)); + new ByteArrayInputStream(b)), lbuf); + if(Boolean.parseBoolean( + attr.getValue(Attributes.Name.MULTI_RELEASE))) { + isMultiRelease = true; + versions = Stream.of(JUZFA.getMetaInfEntryNames(this)) + .mapToInt(this::parseVersion) + .filter(v -> v != -1 && v >= BASE_VERSION_FEATURE && v <= versionFeature) + .distinct() + .sorted() + .toArray(); + } + } } } @@ -1026,6 +1036,27 @@ public class JarFile extends ZipFile { } } + /** + * If {@code entryName} is a a versioned entry, parse and return the version as an integer, otherwise return -1 + */ + private int parseVersion(String entryName) { + if(!entryName.startsWith(META_INF_VERSIONS)) { + return -1; + } + + int separator = entryName.indexOf("/", META_INF_VERSIONS.length()); + + if(separator == -1) { + return -1; + } + + try { + return Integer.parseInt(entryName, META_INF_VERSIONS.length(), separator, 10); + } catch (NumberFormatException e) { + return -1; + } + } + synchronized void ensureInitialization() { try { maybeInstantiateVerifier(); Eirik.