3737import java .nio .file .InvalidPathException ;
3838import java .nio .file .attribute .BasicFileAttributes ;
3939import java .nio .file .Files ;
40- import java .util .ArrayDeque ;
41- import java .util .ArrayList ;
42- import java .util .Arrays ;
43- import java .util .Collections ;
44- import java .util .Deque ;
45- import java .util .Enumeration ;
46- import java .util .HashMap ;
47- import java .util .Iterator ;
48- import java .util .List ;
49- import java .util .Locale ;
50- import java .util .Objects ;
51- import java .util .NoSuchElementException ;
52- import java .util .Set ;
53- import java .util .Spliterator ;
54- import java .util .Spliterators ;
55- import java .util .TreeSet ;
56- import java .util .WeakHashMap ;
40+ import java .util .*;
5741import java .util .function .Consumer ;
5842import java .util .function .IntFunction ;
5943import java .util .jar .JarEntry ;
6448import jdk .internal .access .JavaUtilJarAccess ;
6549import jdk .internal .access .SharedSecrets ;
6650import jdk .internal .util .ArraysSupport ;
51+ import jdk .internal .util .DecimalDigits ;
6752import jdk .internal .util .OperatingSystem ;
6853import jdk .internal .perf .PerfCounter ;
6954import jdk .internal .ref .CleanerFactory ;
@@ -1090,19 +1075,23 @@ private String getManifestName(boolean onlyIfSignatureRelatedFiles) {
10901075 }
10911076
10921077 /**
1093- * Returns the versions for which there exists a non-directory
1094- * entry that begin with "META-INF/versions/" (case ignored).
1078+ * Returns a BitSet where the set bits represents versions found for
1079+ * the given entry name. For performance reasons, the name is looked
1080+ * up only by hashcode, meaning the result is an over-approximation.
10951081 * This method is used in JarFile, via SharedSecrets, as an
10961082 * optimization when looking up potentially versioned entries.
1097- * Returns an empty array if no versioned entries exist.
1083+ * Returns an empty BitSet if no versioned entries exist for this
1084+ * name.
10981085 */
1099- private int [] getMetaInfVersions () {
1086+ private BitSet getMetaInfVersions (String name ) {
11001087 synchronized (this ) {
11011088 ensureOpen ();
1102- return res .zsrc .metaVersions ;
1089+ return res .zsrc .metaVersions . getOrDefault ( ZipCoder . hash ( name ), EMPTY_VERSIONS ) ;
11031090 }
11041091 }
11051092
1093+ private static final BitSet EMPTY_VERSIONS = new BitSet ();
1094+
11061095 /**
11071096 * Returns the value of the System property which indicates whether the
11081097 * Extra ZIP64 validation should be disabled.
@@ -1139,8 +1128,8 @@ public String getManifestName(JarFile jar, boolean onlyIfHasSignatureRelatedFile
11391128 return ((ZipFile )jar ).getManifestName (onlyIfHasSignatureRelatedFiles );
11401129 }
11411130 @ Override
1142- public int [] getMetaInfVersions (JarFile jar ) {
1143- return ((ZipFile )jar ).getMetaInfVersions ();
1131+ public BitSet getMetaInfVersions (JarFile jar , String name ) {
1132+ return ((ZipFile )jar ).getMetaInfVersions (name );
11441133 }
11451134 @ Override
11461135 public Enumeration <JarEntry > entries (ZipFile zip ) {
@@ -1175,7 +1164,8 @@ private static class Source {
11751164 private static final JavaUtilJarAccess JUJA = SharedSecrets .javaUtilJarAccess ();
11761165 // "META-INF/".length()
11771166 private static final int META_INF_LEN = 9 ;
1178- private static final int [] EMPTY_META_VERSIONS = new int [0 ];
1167+ // "META-INF/versions//".length()
1168+ private static final int META_INF_VERSIONS_LEN = 19 ;
11791169 // CEN size is limited to the maximum array size in the JDK
11801170 private static final int MAX_CEN_SIZE = ArraysSupport .SOFT_MAX_ARRAY_LENGTH ;
11811171
@@ -1192,7 +1182,7 @@ private static class Source {
11921182 private int manifestPos = -1 ; // position of the META-INF/MANIFEST.MF, if exists
11931183 private int manifestNum = 0 ; // number of META-INF/MANIFEST.MF, case insensitive
11941184 private int [] signatureMetaNames ; // positions of signature related entries, if such exist
1195- private int [] metaVersions ; // list of unique versions found in META-INF/versions/
1185+ private Map < Integer , BitSet > metaVersions ; // Versions found in META-INF/versions/, by entry name hash
11961186 private final boolean startsWithLoc ; // true, if ZIP file starts with LOCSIG (usually true)
11971187
11981188 // A Hashmap for all entries.
@@ -1574,7 +1564,7 @@ private void close() throws IOException {
15741564 manifestPos = -1 ;
15751565 manifestNum = 0 ;
15761566 signatureMetaNames = null ;
1577- metaVersions = EMPTY_META_VERSIONS ;
1567+ metaVersions = null ;
15781568 }
15791569
15801570 private static final int BUF_SIZE = 8192 ;
@@ -1759,8 +1749,6 @@ private void initCEN(int knownTotal) throws IOException {
17591749
17601750 // list for all meta entries
17611751 ArrayList <Integer > signatureNames = null ;
1762- // Set of all version numbers seen in META-INF/versions/
1763- Set <Integer > metaVersionsSet = null ;
17641752
17651753 // Iterate through the entries in the central directory
17661754 int idx = 0 ; // Index into the entries array
@@ -1799,9 +1787,19 @@ private void initCEN(int knownTotal) throws IOException {
17991787 // performance in multi-release jar files
18001788 int version = getMetaVersion (entryPos + META_INF_LEN , nlen - META_INF_LEN );
18011789 if (version > 0 ) {
1802- if (metaVersionsSet == null )
1803- metaVersionsSet = new TreeSet <>();
1804- metaVersionsSet .add (version );
1790+ try {
1791+ // Compute hash code of name from "META-INF/versions/{version)/{name}
1792+ int prefixLen = META_INF_VERSIONS_LEN + DecimalDigits .stringSize (version );
1793+ int hashCode = zipCoderForPos (pos ).checkedHash (cen ,
1794+ entryPos + prefixLen ,
1795+ nlen - prefixLen );
1796+ // Register version for this hash code
1797+ if (metaVersions == null )
1798+ metaVersions = new HashMap <>();
1799+ metaVersions .computeIfAbsent (hashCode , _ -> new BitSet ()).set (version );
1800+ } catch (Exception e ) {
1801+ throw new IllegalArgumentException (e );
1802+ }
18051803 }
18061804 }
18071805 }
@@ -1819,14 +1817,8 @@ private void initCEN(int knownTotal) throws IOException {
18191817 signatureMetaNames [j ] = signatureNames .get (j );
18201818 }
18211819 }
1822- if (metaVersionsSet != null ) {
1823- metaVersions = new int [metaVersionsSet .size ()];
1824- int c = 0 ;
1825- for (Integer version : metaVersionsSet ) {
1826- metaVersions [c ++] = version ;
1827- }
1828- } else {
1829- metaVersions = EMPTY_META_VERSIONS ;
1820+ if (metaVersions == null ) {
1821+ metaVersions = Map .of ();
18301822 }
18311823 if (pos != cen .length ) {
18321824 zerror ("invalid CEN header (bad header size)" );
0 commit comments