src/java.base/share/classes/java/util/zip/ZipFile.java

Print this page




  33 import java.io.RandomAccessFile;
  34 import java.nio.charset.Charset;
  35 import java.nio.charset.StandardCharsets;
  36 import java.nio.file.attribute.BasicFileAttributes;
  37 import java.nio.file.Path;
  38 import java.nio.file.Files;
  39 
  40 import java.util.ArrayDeque;
  41 import java.util.ArrayList;
  42 import java.util.Arrays;
  43 import java.util.Deque;
  44 import java.util.Enumeration;
  45 import java.util.HashMap;
  46 import java.util.Iterator;
  47 import java.util.Map;
  48 import java.util.Objects;
  49 import java.util.NoSuchElementException;
  50 import java.util.Spliterator;
  51 import java.util.Spliterators;
  52 import java.util.WeakHashMap;





  53 import java.util.stream.Stream;
  54 import java.util.stream.StreamSupport;
  55 import jdk.internal.misc.JavaUtilZipFileAccess;
  56 import jdk.internal.misc.SharedSecrets;
  57 import jdk.internal.misc.JavaIORandomAccessFileAccess;
  58 import jdk.internal.misc.VM;
  59 import jdk.internal.perf.PerfCounter;
  60 
  61 import static java.util.zip.ZipConstants.*;
  62 import static java.util.zip.ZipConstants64.*;
  63 import static java.util.zip.ZipUtils.*;
  64 
  65 /**
  66  * This class is used to read entries from a zip file.
  67  *
  68  * <p> Unless otherwise noted, passing a {@code null} argument to a constructor
  69  * or method in this class will cause a {@link NullPointerException} to be
  70  * thrown.
  71  *
  72  * @author      David Connelly
  73  * @since 1.1
  74  */
  75 public
  76 class ZipFile implements ZipConstants, Closeable {
  77 


 279      */
 280     public String getComment() {
 281         synchronized (this) {
 282             ensureOpen();
 283             if (zsrc.comment == null) {
 284                 return null;
 285             }
 286             return zc.toString(zsrc.comment);
 287         }
 288     }
 289 
 290     /**
 291      * Returns the zip file entry for the specified name, or null
 292      * if not found.
 293      *
 294      * @param name the name of the entry
 295      * @return the zip file entry, or null if not found
 296      * @throws IllegalStateException if the zip file has been closed
 297      */
 298     public ZipEntry getEntry(String name) {














 299         Objects.requireNonNull(name, "name");
 300         synchronized (this) {
 301             ensureOpen();
 302             byte[] bname = zc.getBytes(name);
 303             int pos = zsrc.getEntryPos(bname, true);
 304             if (pos != -1) {
 305                 return getZipEntry(name, bname, pos);
 306             }
 307         }
 308         return null;
 309     }
 310 
 311     // The outstanding inputstreams that need to be closed,
 312     // mapped to the inflater objects they use.
 313     private final Map<InputStream, Inflater> streams = new WeakHashMap<>();
 314 
 315     /**
 316      * Returns an input stream for reading the contents of the specified
 317      * zip file entry.
 318      * <p>
 319      * Closing this ZIP file will, in turn, close all input streams that
 320      * have been returned by invocations of this method.
 321      *
 322      * @param entry the zip file entry
 323      * @return the input stream for reading the contents of the specified
 324      * zip file entry.
 325      * @throws ZipException if a ZIP format error has occurred


 357                     size = 8192;
 358                 }
 359                 if (size <= 0) {
 360                     size = 4096;
 361                 }
 362                 Inflater inf = getInflater();
 363                 InputStream is = new ZipFileInflaterInputStream(in, inf, (int)size);
 364                 synchronized (streams) {
 365                     streams.put(is, inf);
 366                 }
 367                 return is;
 368             default:
 369                 throw new ZipException("invalid compression method");
 370             }
 371         }
 372     }
 373 
 374     private class ZipFileInflaterInputStream extends InflaterInputStream {
 375         private volatile boolean closeRequested;
 376         private boolean eof = false;
 377         private final ZipFileInputStream zfin;
 378 
 379         ZipFileInflaterInputStream(ZipFileInputStream zfin, Inflater inf,
 380                 int size) {
 381             super(zfin, inf, size);
 382             this.zfin = zfin;
 383         }
 384 
 385         public void close() throws IOException {
 386             if (closeRequested)
 387                 return;
 388             closeRequested = true;
 389 
 390             super.close();
 391             Inflater inf;
 392             synchronized (streams) {
 393                 inf = streams.remove(this);
 394             }
 395             if (inf != null) {
 396                 releaseInflater(inf);
 397             }
 398         }
 399 
 400         // Override fill() method to provide an extra "dummy" byte
 401         // at the end of the input stream. This is required when
 402         // using the "nowrap" Inflater option.
 403         protected void fill() throws IOException {
 404             if (eof) {
 405                 throw new EOFException("Unexpected end of ZLIB input stream");
 406             }
 407             len = in.read(buf, 0, buf.length);
 408             if (len == -1) {
 409                 buf[0] = 0;
 410                 len = 1;
 411                 eof = true;
 412             }
 413             inf.setInput(buf, 0, len);
 414         }
 415 
 416         public int available() throws IOException {
 417             if (closeRequested)
 418                 return 0;
 419             long avail = zfin.size() - inf.getBytesWritten();
 420             return (avail > (long) Integer.MAX_VALUE ?
 421                     Integer.MAX_VALUE : (int) avail);
 422         }
 423 
 424         @SuppressWarnings("deprecation")
 425         protected void finalize() throws Throwable {
 426             close();
 427         }
 428     }
 429 
 430     /*
 431      * Gets an inflater from the list of available inflaters or allocates
 432      * a new one.
 433      */
 434     private Inflater getInflater() {
 435         Inflater inf;
 436         synchronized (inflaterCache) {
 437             while ((inf = inflaterCache.poll()) != null) {
 438                 if (!inf.ended()) {
 439                     return inf;


 449     private void releaseInflater(Inflater inf) {
 450         if (!inf.ended()) {
 451             inf.reset();
 452             synchronized (inflaterCache) {
 453                 inflaterCache.add(inf);
 454             }
 455         }
 456     }
 457 
 458     // List of available Inflater objects for decompression
 459     private final Deque<Inflater> inflaterCache = new ArrayDeque<>();
 460 
 461     /**
 462      * Returns the path name of the ZIP file.
 463      * @return the path name of the ZIP file
 464      */
 465     public String getName() {
 466         return name;
 467     }
 468 
 469     private class ZipEntryIterator implements Enumeration<ZipEntry>, Iterator<ZipEntry> {


 470         private int i = 0;
 471         private final int entryCount;

 472 
 473         public ZipEntryIterator() {
 474             synchronized (ZipFile.this) {
 475                 ensureOpen();
 476                 this.entryCount = zsrc.total;
 477             }
 478         }
 479 

 480         public boolean hasMoreElements() {
 481             return hasNext();
 482         }
 483 

 484         public boolean hasNext() {
 485             return i < entryCount;
 486         }
 487 
 488         public ZipEntry nextElement() {

 489             return next();
 490         }
 491 
 492         public ZipEntry next() {


 493             synchronized (ZipFile.this) {
 494                 ensureOpen();
 495                 if (!hasNext()) {
 496                     throw new NoSuchElementException();
 497                 }
 498                 // each "entry" has 3 ints in table entries
 499                 return getZipEntry(null, null, zsrc.getEntryPos(i++ * 3));
 500             }
 501         }
 502 
 503         public Iterator<ZipEntry> asIterator() {

 504             return this;
 505         }
 506     }
 507 
 508     /**
 509      * Returns an enumeration of the ZIP file entries.
 510      * @return an enumeration of the ZIP file entries
 511      * @throws IllegalStateException if the zip file has been closed
 512      */
 513     public Enumeration<? extends ZipEntry> entries() {
 514         return new ZipEntryIterator();







































 515     }
 516 
 517     /**
 518      * Returns an ordered {@code Stream} over the ZIP file entries.

 519      * Entries appear in the {@code Stream} in the order they appear in
 520      * the central directory of the ZIP file.
 521      *
 522      * @return an ordered {@code Stream} of entries in this ZIP file
 523      * @throws IllegalStateException if the zip file has been closed
 524      * @since 1.8
 525      */
 526     public Stream<? extends ZipEntry> stream() {
 527         return StreamSupport.stream(Spliterators.spliterator(
 528                 new ZipEntryIterator(), size(),
 529                 Spliterator.ORDERED | Spliterator.DISTINCT |
 530                         Spliterator.IMMUTABLE | Spliterator.NONNULL), false);


















































 531     }
 532 
 533     private String lastEntryName;
 534     private int lastEntryPos;
 535 
 536     /* Checks ensureOpen() before invoke this method */
 537     private ZipEntry getZipEntry(String name, byte[] bname, int pos) {

 538         byte[] cen = zsrc.cen;
 539         int nlen = CENNAM(cen, pos);
 540         int elen = CENEXT(cen, pos);
 541         int clen = CENCOM(cen, pos);
 542         int flag = CENFLG(cen, pos);
 543         if (name == null || bname.length != nlen) {
 544             // to use the entry name stored in cen, if the passed in name is
 545             // (1) null, invoked from iterator, or
 546             // (2) not equal to the name stored, a slash is appended during
 547             // getEntryPos() search.
 548             if (!zc.isUTF8() && (flag & EFS) != 0) {
 549                 name = zc.toStringUTF8(cen, pos + CENHDR, nlen);
 550             } else {
 551                 name = zc.toString(cen, pos + CENHDR, nlen);
 552             }
 553         }
 554         ZipEntry e = new ZipEntry(name);
 555         e.flag = flag;
 556         e.xdostime = CENTIM(cen, pos);
 557         e.crc = CENCRC(cen, pos);
 558         e.size = CENLEN(cen, pos);
 559         e.csize = CENSIZ(cen, pos);
 560         e.method = CENHOW(cen, pos);
 561         if (elen != 0) {
 562             int start = pos + CENHDR + nlen;
 563             e.setExtra0(Arrays.copyOfRange(cen, start, start + elen), true);
 564         }
 565         if (clen != 0) {
 566             int start = pos + CENHDR + nlen + elen;
 567             if (!zc.isUTF8() && (flag & EFS) != 0) {
 568                 e.comment = zc.toStringUTF8(cen, start, clen);
 569             } else {
 570                 e.comment = zc.toString(cen, start, clen);
 571             }
 572         }
 573         lastEntryName = e.name;
 574         lastEntryPos = pos;


 774                     rem -= len;
 775                 }
 776             }
 777             if (rem == 0) {
 778                 close();
 779             }
 780             return len;
 781         }
 782 
 783         public int read() throws IOException {
 784             byte[] b = new byte[1];
 785             if (read(b, 0, 1) == 1) {
 786                 return b[0] & 0xff;
 787             } else {
 788                 return -1;
 789             }
 790         }
 791 
 792         public long skip(long n) throws IOException {
 793             synchronized (ZipFile.this) {
 794                 ensureOpenOrZipException();
 795                 initDataOffset();
 796                 if (n > rem) {
 797                     n = rem;
 798                 }
 799                 pos += n;
 800                 rem -= n;
 801             }
 802             if (rem == 0) {
 803                 close();
 804             }
 805             return n;
 806         }
 807 
 808         public int available() {
 809             return rem > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) rem;
 810         }
 811 
 812         public long size() {
 813             return size;
 814         }


 840         synchronized (this) {
 841             ensureOpen();
 842             if (zsrc.metanames == null) {
 843                 return null;
 844             }
 845             String[] names = new String[zsrc.metanames.length];
 846             byte[] cen = zsrc.cen;
 847             for (int i = 0; i < names.length; i++) {
 848                 int pos = zsrc.metanames[i];
 849                 names[i] = new String(cen, pos + CENHDR, CENNAM(cen, pos),
 850                                       StandardCharsets.UTF_8);
 851             }
 852             return names;
 853         }
 854     }
 855 
 856     private static boolean isWindows;
 857     static {
 858         SharedSecrets.setJavaUtilZipFileAccess(
 859             new JavaUtilZipFileAccess() {

 860                 public boolean startsWithLocHeader(ZipFile zip) {
 861                     return zip.zsrc.startsWithLoc;
 862                 }

 863                 public String[] getMetaInfEntryNames(ZipFile zip) {
 864                     return zip.getMetaInfEntryNames();
 865                 }



















 866              }
 867         );
 868         isWindows = VM.getSavedProperty("os.name").contains("Windows");
 869     }
 870 
 871     private static class Source {
 872         private final Key key;               // the key in files
 873         private int refs = 1;
 874 
 875         private RandomAccessFile zfile;      // zfile of the underlying zip file
 876         private byte[] cen;                  // CEN & ENDHDR
 877         private long locpos;                 // position of first LOC header (usually 0)
 878         private byte[] comment;              // zip file comment
 879                                              // list of meta entries in META-INF dir
 880         private int[] metanames;
 881         private final boolean startsWithLoc; // true, if zip file starts with LOCSIG (usually true)
 882 
 883         // A Hashmap for all entries.
 884         //
 885         // A cen entry of Zip/JAR file. As we have one for every entry in every active Zip/JAR,




  33 import java.io.RandomAccessFile;
  34 import java.nio.charset.Charset;
  35 import java.nio.charset.StandardCharsets;
  36 import java.nio.file.attribute.BasicFileAttributes;
  37 import java.nio.file.Path;
  38 import java.nio.file.Files;
  39 
  40 import java.util.ArrayDeque;
  41 import java.util.ArrayList;
  42 import java.util.Arrays;
  43 import java.util.Deque;
  44 import java.util.Enumeration;
  45 import java.util.HashMap;
  46 import java.util.Iterator;
  47 import java.util.Map;
  48 import java.util.Objects;
  49 import java.util.NoSuchElementException;
  50 import java.util.Spliterator;
  51 import java.util.Spliterators;
  52 import java.util.WeakHashMap;
  53 
  54 import java.util.function.Consumer;
  55 import java.util.function.Function;
  56 import java.util.function.IntFunction;
  57 import java.util.jar.JarEntry;
  58 import java.util.stream.Stream;
  59 import java.util.stream.StreamSupport;
  60 import jdk.internal.misc.JavaUtilZipFileAccess;
  61 import jdk.internal.misc.SharedSecrets;

  62 import jdk.internal.misc.VM;
  63 import jdk.internal.perf.PerfCounter;
  64 
  65 import static java.util.zip.ZipConstants.*;
  66 import static java.util.zip.ZipConstants64.*;
  67 import static java.util.zip.ZipUtils.*;
  68 
  69 /**
  70  * This class is used to read entries from a zip file.
  71  *
  72  * <p> Unless otherwise noted, passing a {@code null} argument to a constructor
  73  * or method in this class will cause a {@link NullPointerException} to be
  74  * thrown.
  75  *
  76  * @author      David Connelly
  77  * @since 1.1
  78  */
  79 public
  80 class ZipFile implements ZipConstants, Closeable {
  81 


 283      */
 284     public String getComment() {
 285         synchronized (this) {
 286             ensureOpen();
 287             if (zsrc.comment == null) {
 288                 return null;
 289             }
 290             return zc.toString(zsrc.comment);
 291         }
 292     }
 293 
 294     /**
 295      * Returns the zip file entry for the specified name, or null
 296      * if not found.
 297      *
 298      * @param name the name of the entry
 299      * @return the zip file entry, or null if not found
 300      * @throws IllegalStateException if the zip file has been closed
 301      */
 302     public ZipEntry getEntry(String name) {
 303         return getEntry(name, ZipEntry::new);
 304     }
 305 
 306     /*
 307      * Returns the zip file entry for the specified name, or null
 308      * if not found.
 309      *
 310      * @param name the name of the entry
 311      * @param func the function that creates the returned entry
 312      *
 313      * @return the zip file entry, or null if not found
 314      * @throws IllegalStateException if the zip file has been closed
 315      */
 316     private ZipEntry getEntry(String name, Function<String, ? extends ZipEntry> func) {
 317         Objects.requireNonNull(name, "name");
 318         synchronized (this) {
 319             ensureOpen();
 320             byte[] bname = zc.getBytes(name);
 321             int pos = zsrc.getEntryPos(bname, true);
 322             if (pos != -1) {
 323                 return getZipEntry(name, bname, pos, func);
 324             }
 325         }
 326         return null;
 327     }
 328 
 329     // The outstanding inputstreams that need to be closed,
 330     // mapped to the inflater objects they use.
 331     private final Map<InputStream, Inflater> streams = new WeakHashMap<>();
 332 
 333     /**
 334      * Returns an input stream for reading the contents of the specified
 335      * zip file entry.
 336      * <p>
 337      * Closing this ZIP file will, in turn, close all input streams that
 338      * have been returned by invocations of this method.
 339      *
 340      * @param entry the zip file entry
 341      * @return the input stream for reading the contents of the specified
 342      * zip file entry.
 343      * @throws ZipException if a ZIP format error has occurred


 375                     size = 8192;
 376                 }
 377                 if (size <= 0) {
 378                     size = 4096;
 379                 }
 380                 Inflater inf = getInflater();
 381                 InputStream is = new ZipFileInflaterInputStream(in, inf, (int)size);
 382                 synchronized (streams) {
 383                     streams.put(is, inf);
 384                 }
 385                 return is;
 386             default:
 387                 throw new ZipException("invalid compression method");
 388             }
 389         }
 390     }
 391 
 392     private class ZipFileInflaterInputStream extends InflaterInputStream {
 393         private volatile boolean closeRequested;
 394         private boolean eof = false;

 395 
 396         ZipFileInflaterInputStream(ZipFileInputStream zfin, Inflater inf,
 397                 int size) {
 398             super(zfin, inf, size);

 399         }
 400 
 401         public void close() throws IOException {
 402             if (closeRequested)
 403                 return;
 404             closeRequested = true;
 405 
 406             super.close();
 407             Inflater inf;
 408             synchronized (streams) {
 409                 inf = streams.remove(this);
 410             }
 411             if (inf != null) {
 412                 releaseInflater(inf);
 413             }
 414         }
 415 
 416         // Override fill() method to provide an extra "dummy" byte
 417         // at the end of the input stream. This is required when
 418         // using the "nowrap" Inflater option.
 419         protected void fill() throws IOException {
 420             if (eof) {
 421                 throw new EOFException("Unexpected end of ZLIB input stream");
 422             }
 423             len = in.read(buf, 0, buf.length);
 424             if (len == -1) {
 425                 buf[0] = 0;
 426                 len = 1;
 427                 eof = true;
 428             }
 429             inf.setInput(buf, 0, len);
 430         }
 431 
 432         public int available() throws IOException {
 433             if (closeRequested)
 434                 return 0;
 435             long avail = ((ZipFileInputStream)in).size() - inf.getBytesWritten();
 436             return (avail > (long) Integer.MAX_VALUE ?
 437                     Integer.MAX_VALUE : (int) avail);
 438         }
 439 
 440         @SuppressWarnings("deprecation")
 441         protected void finalize() throws Throwable {
 442             close();
 443         }
 444     }
 445 
 446     /*
 447      * Gets an inflater from the list of available inflaters or allocates
 448      * a new one.
 449      */
 450     private Inflater getInflater() {
 451         Inflater inf;
 452         synchronized (inflaterCache) {
 453             while ((inf = inflaterCache.poll()) != null) {
 454                 if (!inf.ended()) {
 455                     return inf;


 465     private void releaseInflater(Inflater inf) {
 466         if (!inf.ended()) {
 467             inf.reset();
 468             synchronized (inflaterCache) {
 469                 inflaterCache.add(inf);
 470             }
 471         }
 472     }
 473 
 474     // List of available Inflater objects for decompression
 475     private final Deque<Inflater> inflaterCache = new ArrayDeque<>();
 476 
 477     /**
 478      * Returns the path name of the ZIP file.
 479      * @return the path name of the ZIP file
 480      */
 481     public String getName() {
 482         return name;
 483     }
 484 
 485     private class ZipEntryIterator<T extends ZipEntry>
 486             implements Enumeration<T>, Iterator<T> {
 487 
 488         private int i = 0;
 489         private final int entryCount;
 490         private final Function<String, T> gen;
 491 
 492         public ZipEntryIterator(int entryCount, Function<String, T> gen) {
 493             this.entryCount = entryCount;
 494             this.gen = gen;


 495         }
 496 
 497         @Override
 498         public boolean hasMoreElements() {
 499             return hasNext();
 500         }
 501 
 502         @Override
 503         public boolean hasNext() {
 504             return i < entryCount;
 505         }
 506 
 507         @Override
 508         public T nextElement() {
 509             return next();
 510         }
 511 
 512         @Override
 513         @SuppressWarnings("unchecked")
 514         public T  next() {
 515             synchronized (ZipFile.this) {
 516                 ensureOpen();
 517                 if (!hasNext()) {
 518                     throw new NoSuchElementException();
 519                 }
 520                 // each "entry" has 3 ints in table entries
 521                 return (T)getZipEntry(null, null, zsrc.getEntryPos(i++ * 3), gen);
 522             }
 523         }
 524 
 525         @Override
 526         public Iterator<T> asIterator() {
 527             return this;
 528         }
 529     }
 530 
 531     /**
 532      * Returns an enumeration of the ZIP file entries.
 533      * @return an enumeration of the ZIP file entries
 534      * @throws IllegalStateException if the zip file has been closed
 535      */
 536     public Enumeration<? extends ZipEntry> entries() {
 537         synchronized (this) {
 538             ensureOpen();
 539             return new ZipEntryIterator<ZipEntry>(zsrc.total, ZipEntry::new);
 540         }
 541     }
 542 
 543     private Enumeration<JarEntry> entries(Function<String, JarEntry> func) {
 544         synchronized (this) {
 545             ensureOpen();
 546             return new ZipEntryIterator<JarEntry>(zsrc.total, func);
 547         }
 548     }
 549 
 550     private class EntrySpliterator<T> extends Spliterators.AbstractSpliterator<T> {
 551         private int index;
 552         private final int fence;
 553         private final IntFunction<T> gen;
 554 
 555         EntrySpliterator(int index, int fence, IntFunction<T> gen) {
 556             super((long)fence,
 557                   Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.IMMUTABLE |
 558                   Spliterator.NONNULL);
 559             this.index = index;
 560             this.fence = fence;
 561             this.gen = gen;
 562         }
 563 
 564         @Override
 565         public boolean tryAdvance(Consumer<? super T> action) {
 566             if (action == null)
 567                 throw new NullPointerException();
 568             if (index >= 0 && index < fence) {
 569                 synchronized (ZipFile.this) {
 570                     ensureOpen();
 571                     action.accept(gen.apply(zsrc.getEntryPos(index++ * 3)));
 572                 }
 573                 return true;
 574             }
 575             return false;
 576         }
 577     }
 578 
 579     /**
 580      * Returns an ordered {@code Stream} over the ZIP file entries.
 581      *
 582      * Entries appear in the {@code Stream} in the order they appear in
 583      * the central directory of the ZIP file.
 584      *
 585      * @return an ordered {@code Stream} of entries in this ZIP file
 586      * @throws IllegalStateException if the zip file has been closed
 587      * @since 1.8
 588      */
 589     public Stream<? extends ZipEntry> stream() {
 590         synchronized (this) {
 591             ensureOpen();
 592             return StreamSupport.stream(new EntrySpliterator<>(0, zsrc.total,
 593                 pos -> getZipEntry(null, null, pos, ZipEntry::new)), false);
 594        }
 595     }
 596 
 597     private String getEntryName(int pos) {
 598         byte[] cen = zsrc.cen;
 599         int nlen = CENNAM(cen, pos);
 600         int clen = CENCOM(cen, pos);
 601         int flag = CENFLG(cen, pos);
 602         if (!zc.isUTF8() && (flag & EFS) != 0) {
 603             return zc.toStringUTF8(cen, pos + CENHDR, nlen);
 604         } else {
 605             return zc.toString(cen, pos + CENHDR, nlen);
 606         }
 607     }
 608 
 609     /*
 610      * Returns an ordered {@code Stream} over the zip file entry names.
 611      *
 612      * Entry names appear in the {@code Stream} in the order they appear in
 613      * the central directory of the ZIP file.
 614      *
 615      * @return an ordered {@code Stream} of entry names in this zip file
 616      * @throws IllegalStateException if the zip file has been closed
 617      * @since 10
 618      */
 619     private Stream<String> entryNameStream() {
 620         synchronized (this) {
 621             ensureOpen();
 622             return StreamSupport.stream(
 623                 new EntrySpliterator<>(0, zsrc.total, this::getEntryName), false);
 624         }
 625     }
 626 
 627     /*
 628      * Returns an ordered {@code Stream} over the zip file entries.
 629      *
 630      * Entries appear in the {@code Stream} in the order they appear in
 631      * the central directory of the jar file.
 632      *
 633      * @param func the function that creates the returned entry
 634      * @return an ordered {@code Stream} of entries in this zip file
 635      * @throws IllegalStateException if the zip file has been closed
 636      * @since 10
 637      */
 638     private Stream<JarEntry> stream(Function<String, JarEntry> func) {
 639         synchronized (this) {
 640             ensureOpen();
 641             return StreamSupport.stream(new EntrySpliterator<>(0, zsrc.total,
 642                 pos -> (JarEntry)getZipEntry(null, null, pos, func)), false);
 643         }
 644     }
 645 
 646     private String lastEntryName;
 647     private int lastEntryPos;
 648 
 649     /* Checks ensureOpen() before invoke this method */
 650     private ZipEntry getZipEntry(String name, byte[] bname, int pos,
 651                                  Function<String, ? extends ZipEntry> func) {
 652         byte[] cen = zsrc.cen;
 653         int nlen = CENNAM(cen, pos);
 654         int elen = CENEXT(cen, pos);
 655         int clen = CENCOM(cen, pos);
 656         int flag = CENFLG(cen, pos);
 657         if (name == null || bname.length != nlen) {
 658             // to use the entry name stored in cen, if the passed in name is
 659             // (1) null, invoked from iterator, or
 660             // (2) not equal to the name stored, a slash is appended during
 661             // getEntryPos() search.
 662             if (!zc.isUTF8() && (flag & EFS) != 0) {
 663                 name = zc.toStringUTF8(cen, pos + CENHDR, nlen);
 664             } else {
 665                 name = zc.toString(cen, pos + CENHDR, nlen);
 666             }
 667         }
 668         ZipEntry e = func.apply(name);    //ZipEntry e = new ZipEntry(name);
 669         e.flag = flag;
 670         e.xdostime = CENTIM(cen, pos);
 671         e.crc = CENCRC(cen, pos);
 672         e.size = CENLEN(cen, pos);
 673         e.csize = CENSIZ(cen, pos);
 674         e.method = CENHOW(cen, pos);
 675         if (elen != 0) {
 676             int start = pos + CENHDR + nlen;
 677             e.setExtra0(Arrays.copyOfRange(cen, start, start + elen), true);
 678         }
 679         if (clen != 0) {
 680             int start = pos + CENHDR + nlen + elen;
 681             if (!zc.isUTF8() && (flag & EFS) != 0) {
 682                 e.comment = zc.toStringUTF8(cen, start, clen);
 683             } else {
 684                 e.comment = zc.toString(cen, start, clen);
 685             }
 686         }
 687         lastEntryName = e.name;
 688         lastEntryPos = pos;


 888                     rem -= len;
 889                 }
 890             }
 891             if (rem == 0) {
 892                 close();
 893             }
 894             return len;
 895         }
 896 
 897         public int read() throws IOException {
 898             byte[] b = new byte[1];
 899             if (read(b, 0, 1) == 1) {
 900                 return b[0] & 0xff;
 901             } else {
 902                 return -1;
 903             }
 904         }
 905 
 906         public long skip(long n) throws IOException {
 907             synchronized (ZipFile.this) {

 908                 initDataOffset();
 909                 if (n > rem) {
 910                     n = rem;
 911                 }
 912                 pos += n;
 913                 rem -= n;
 914             }
 915             if (rem == 0) {
 916                 close();
 917             }
 918             return n;
 919         }
 920 
 921         public int available() {
 922             return rem > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) rem;
 923         }
 924 
 925         public long size() {
 926             return size;
 927         }


 953         synchronized (this) {
 954             ensureOpen();
 955             if (zsrc.metanames == null) {
 956                 return null;
 957             }
 958             String[] names = new String[zsrc.metanames.length];
 959             byte[] cen = zsrc.cen;
 960             for (int i = 0; i < names.length; i++) {
 961                 int pos = zsrc.metanames[i];
 962                 names[i] = new String(cen, pos + CENHDR, CENNAM(cen, pos),
 963                                       StandardCharsets.UTF_8);
 964             }
 965             return names;
 966         }
 967     }
 968 
 969     private static boolean isWindows;
 970     static {
 971         SharedSecrets.setJavaUtilZipFileAccess(
 972             new JavaUtilZipFileAccess() {
 973                 @Override
 974                 public boolean startsWithLocHeader(ZipFile zip) {
 975                     return zip.zsrc.startsWithLoc;
 976                 }
 977                 @Override
 978                 public String[] getMetaInfEntryNames(ZipFile zip) {
 979                     return zip.getMetaInfEntryNames();
 980                 }
 981                 @Override
 982                 public JarEntry getEntry(ZipFile zip, String name,
 983                     Function<String, JarEntry> func) {
 984                     return (JarEntry)zip.getEntry(name, func);
 985                 }
 986                 @Override
 987                 public Enumeration<JarEntry> entries(ZipFile zip,
 988                     Function<String, JarEntry> func) {
 989                     return zip.entries(func);
 990                 }
 991                 @Override
 992                 public Stream<JarEntry> stream(ZipFile zip,
 993                     Function<String, JarEntry> func) {
 994                     return zip.stream(func);
 995                 }
 996                 @Override
 997                 public Stream<String> entryNameStream(ZipFile zip) {
 998                     return zip.entryNameStream();
 999                 }
1000              }
1001         );
1002         isWindows = VM.getSavedProperty("os.name").contains("Windows");
1003     }
1004 
1005     private static class Source {
1006         private final Key key;               // the key in files
1007         private int refs = 1;
1008 
1009         private RandomAccessFile zfile;      // zfile of the underlying zip file
1010         private byte[] cen;                  // CEN & ENDHDR
1011         private long locpos;                 // position of first LOC header (usually 0)
1012         private byte[] comment;              // zip file comment
1013                                              // list of meta entries in META-INF dir
1014         private int[] metanames;
1015         private final boolean startsWithLoc; // true, if zip file starts with LOCSIG (usually true)
1016 
1017         // A Hashmap for all entries.
1018         //
1019         // A cen entry of Zip/JAR file. As we have one for every entry in every active Zip/JAR,