< prev index next >

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

Print this page

        

*** 28,40 **** --- 28,44 ---- import java.io.Closeable; import java.io.InputStream; import java.io.IOException; import java.io.EOFException; import java.io.File; + import java.nio.BufferOverflowException; + import java.nio.ByteBuffer; + import java.nio.ReadOnlyBufferException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayDeque; + import java.util.Arrays; import java.util.Deque; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map;
*** 65,74 **** --- 69,81 ---- private volatile boolean closeRequested = false; private static final int STORED = ZipEntry.STORED; private static final int DEFLATED = ZipEntry.DEFLATED; + // Max buffer size when returning bytebuffers directly + private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; + /** * Mode flag to open a zip file for reading. */ public static final int OPEN_READ = 0x1;
*** 468,477 **** --- 475,713 ---- // List of available Inflater objects for decompression private Deque<Inflater> inflaterCache = new ArrayDeque<>(); /** + * Uncompress the zip entry into a new ByteBuffer. + * + * The returned ByteBuffer will have position 0 and limit set to the length + * of the uncompressed bytes of the zip entry. + * + * This method can only read entries smaller than 2GB, for larger entries + * use getInputStream. + * + * @param entry the zip file entry + * + * @return the ByteBuffer with the contents of the specified zip file entry. + * + * @throws ZipException if a ZIP format error has occurred + * @throws IOException if an I/O error has occurred + * @throws IllegalStateException if the zip file has been closed + * @throws BufferOverflowException if the zip entry is larger than the + * supplied buffer + * @throws ReadOnlyBufferException if the supplied buffer is read-only + */ + private ByteBuffer getByteBuffer(ZipEntry entry) throws IOException { + if (entry == null) { + throw new NullPointerException("entry"); + } + + long size = entry.getSize(); + + if (size < 0) { + // Uknown size of zip entry (-1) + return getByteBufferUnknownSize(entry); + } + + if (size > MAX_BUFFER_SIZE) { + throw new BufferOverflowException(); + } + + return fillByteBuffer(entry, ByteBuffer.allocate((int) size)).flip(); + } + + /** + * Fill a ByteBuffer with the uncompressed contents of the specified zip + * file entry. + * + * This method transfers the zip entry bytes into this buffer. If there are + * more bytes to be written than remain in this buffer, that is, if + * ZipEntry.getSize() > remaining(), then no bytes are transferred and a + * BufferOverflowException is thrown. + * + * Otherwise, this method copies length bytes from the given array into this + * buffer, starting at the given offset in the array and at the current + * position of this buffer. The position of this buffer is then incremented + * by length. + * + * This method can only read entries smaller than 2GB, for larger entries + * use getInputStream. + * + * @param entry the zip file entry + * @param buffer ByteBuffer to write the zip entry to + * + * @return the ByteBuffer with the contents of the specified zip file entry. + * + * @throws ZipException if a ZIP format error has occurred + * @throws IOException if an I/O error has occurred + * @throws IllegalStateException if the zip file has been closed + * @throws BufferOverflowException if the zip entry is larger than the + * supplied buffer + * @throws ReadOnlyBufferException if the supplied buffer is read-only + */ + private ByteBuffer fillByteBuffer(ZipEntry entry, ByteBuffer buffer) throws IOException { + if (entry == null) { + throw new NullPointerException("entry"); + } + if (buffer.isReadOnly()) { + throw new ReadOnlyBufferException(); + } + + long jzentry = 0; + try { + long csize; + long size; + int cmethod; + + synchronized (this) { + ensureOpen(); + if (!zc.isUTF8() && (entry.flag & EFS) != 0) { + jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), false); + } else { + jzentry = getEntry(jzfile, zc.getBytes(entry.name), false); + } + if (jzentry == 0) { + return null; + } + + csize = getEntryCSize(jzentry); + size = getEntrySize(jzentry); + cmethod = getEntryMethod(jzentry); + } + + if (csize < 0 || size < 0) { + // Uknown size of zip entry (-1) + ByteBuffer tempBuffer = getByteBufferUnknownSize(entry); + buffer.put(tempBuffer); // Throws BufferOverflowException if too large + return buffer; + } + if (csize > MAX_BUFFER_SIZE || size > MAX_BUFFER_SIZE) { + throw new BufferOverflowException(); + } + + switch (cmethod) { + case STORED: + if (csize > buffer.remaining()) { + throw new BufferOverflowException(); + } + return getByteBufferStored(jzentry, (int) csize, buffer); + case DEFLATED: + if (size > buffer.remaining()) { + throw new BufferOverflowException(); + } + + byte[] cbytes = new byte[(int) csize]; + readCompressedBytes(jzentry, (int) csize, cbytes, 0); + + Inflater inf = getInflater(); + try { + inf.setInput(cbytes); + if (buffer.hasArray()) { + inf.inflate(buffer.array(), buffer.arrayOffset(), buffer.remaining()); + buffer.position(buffer.position() + (int) size); + } else { + byte[] bytes = new byte[(int) size]; + inf.inflate(bytes); + buffer.put(bytes); + } + } catch (DataFormatException e) { + String s = e.getMessage(); + throw new ZipException(s != null ? s : "Invalid ZLIB data format"); + } finally { + releaseInflater(inf); + } + return buffer; + default: + throw new ZipException("invalid compression method"); + + } + } finally { + if (jzentry != 0) { + synchronized (this) { + if (jzfile != 0) { + freeEntry(jzfile, jzentry); + } + } + } + } + } + + private ByteBuffer getByteBufferStored(long jzentry, int csize, ByteBuffer buffer) + throws IOException { + assert csize >= buffer.remaining(); + if (buffer.hasArray()) { + int nread = readCompressedBytes(jzentry, csize, buffer.array(), + buffer.arrayOffset()); + buffer.position(buffer.position() + nread); + } else { + byte[] cbytes = new byte[csize]; + readCompressedBytes(jzentry, csize, cbytes, 0); + buffer.put(cbytes); + } + return buffer; + } + + private int readCompressedBytes(long jzentry, int csize, byte[] buf, int off) + throws IOException { + assert csize >= buf.length - off; + synchronized (this) { + ensureOpenOrZipException(); + int n = 0; + int nread = 0; + while (nread < csize + && (n = read(jzfile, jzentry, nread, buf, off + nread, csize - nread)) > 0) { + nread += n; + } + return nread; + } + } + + private ByteBuffer getByteBufferUnknownSize(ZipEntry entry) throws IOException { + final int BUFFER_SIZE = 8192; + InputStream in = getInputStream(entry); + + if (in == null) { + // not found + return null; + } + + try (in) { + int capacity = in.available(); + if (capacity == 0) { + capacity = BUFFER_SIZE; + } + + byte[] buf = new byte[capacity]; + int nread = 0; + int n; + for (;;) { + // read to EOF + while ((n = in.read(buf, nread, capacity - nread)) > 0) { + nread += n; + } + + // if last call to source.read() returned -1, we are done + // otherwise, try to read one more byte; if that failed we're done too + if (n < 0 || (n = in.read()) < 0) { + break; + } + + // one more byte was read; need to allocate a larger buffer + if (capacity < MAX_BUFFER_SIZE) { + capacity = Math.min(capacity << 1, MAX_BUFFER_SIZE); + } else { + throw new BufferOverflowException(); + } + buf = Arrays.copyOf(buf, capacity); + buf[nread++] = (byte) n; + } + + return ByteBuffer.wrap(buf, 0, nread); + } + } + + /** * Returns the path name of the ZIP file. * @return the path name of the ZIP file */ public String getName() { return name;
*** 777,789 **** --- 1013,1038 ---- } static { sun.misc.SharedSecrets.setJavaUtilZipFileAccess( new sun.misc.JavaUtilZipFileAccess() { + @Override public boolean startsWithLocHeader(ZipFile zip) { return zip.startsWithLocHeader(); } + + @Override + public ByteBuffer getByteBuffer(ZipFile zip, ZipEntry entry) + throws IOException { + return zip.getByteBuffer(entry); + } + + @Override + public ByteBuffer fillByteBuffer(ZipFile zip, ZipEntry entry, + ByteBuffer buffer) throws IOException { + return zip.fillByteBuffer(entry, buffer); + } } ); } /**
< prev index next >