Automatic sources dropoff on 2020-06-10 18:32:38.095721
The change is generated with prebuilt drop tool.
Change-Id: I24cbf6ba6db262a1ae1445db1427a08fee35b3b4
diff --git a/java/util/zip/Adler32.java b/java/util/zip/Adler32.java
new file mode 100644
index 0000000..de279f9
--- /dev/null
+++ b/java/util/zip/Adler32.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 1996, 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.nio.ByteBuffer;
+import sun.nio.ch.DirectBuffer;
+
+/**
+ * A class that can be used to compute the Adler-32 checksum of a data
+ * stream. An Adler-32 checksum is almost as reliable as a CRC-32 but
+ * can be computed much faster.
+ *
+ * <p> Passing a {@code null} argument to a method in this class will cause
+ * a {@link NullPointerException} to be thrown.
+ *
+ * @see Checksum
+ * @author David Connelly
+ */
+public
+class Adler32 implements Checksum {
+
+ private int adler = 1;
+
+ /**
+ * Creates a new Adler32 object.
+ */
+ public Adler32() {
+ }
+
+ /**
+ * Updates the checksum with the specified byte (the low eight
+ * bits of the argument b).
+ *
+ * @param b the byte to update the checksum with
+ */
+ public void update(int b) {
+ adler = update(adler, b);
+ }
+
+ /**
+ * Updates the checksum with the specified array of bytes.
+ *
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code off} is negative, or {@code len} is negative,
+ * or {@code off+len} is greater than the length of the
+ * array {@code b}
+ */
+ public void update(byte[] b, int off, int len) {
+ if (b == null) {
+ throw new NullPointerException();
+ }
+ if (off < 0 || len < 0 || off > b.length - len) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ adler = updateBytes(adler, b, off, len);
+ }
+
+ /**
+ * Updates the checksum with the specified array of bytes.
+ *
+ * @param b the byte array to update the checksum with
+ */
+ public void update(byte[] b) {
+ adler = updateBytes(adler, b, 0, b.length);
+ }
+
+
+ /**
+ * Updates the checksum with the bytes from the specified buffer.
+ *
+ * The checksum is updated using
+ * buffer.{@link java.nio.Buffer#remaining() remaining()}
+ * bytes starting at
+ * buffer.{@link java.nio.Buffer#position() position()}
+ * Upon return, the buffer's position will be updated to its
+ * limit; its limit will not have been changed.
+ *
+ * @param buffer the ByteBuffer to update the checksum with
+ * @since 1.8
+ */
+ public void update(ByteBuffer buffer) {
+ int pos = buffer.position();
+ int limit = buffer.limit();
+ assert (pos <= limit);
+ int rem = limit - pos;
+ if (rem <= 0)
+ return;
+ if (buffer instanceof DirectBuffer) {
+ adler = updateByteBuffer(adler, ((DirectBuffer)buffer).address(), pos, rem);
+ } else if (buffer.hasArray()) {
+ adler = updateBytes(adler, buffer.array(), pos + buffer.arrayOffset(), rem);
+ } else {
+ byte[] b = new byte[rem];
+ buffer.get(b);
+ adler = updateBytes(adler, b, 0, b.length);
+ }
+ buffer.position(limit);
+ }
+
+ /**
+ * Resets the checksum to initial value.
+ */
+ public void reset() {
+ adler = 1;
+ }
+
+ /**
+ * Returns the checksum value.
+ */
+ public long getValue() {
+ return (long)adler & 0xffffffffL;
+ }
+
+ private native static int update(int adler, int b);
+ private native static int updateBytes(int adler, byte[] b, int off,
+ int len);
+ private native static int updateByteBuffer(int adler, long addr,
+ int off, int len);
+}
diff --git a/java/util/zip/CRC32.java b/java/util/zip/CRC32.java
new file mode 100644
index 0000000..0f55579
--- /dev/null
+++ b/java/util/zip/CRC32.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 1996, 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.nio.ByteBuffer;
+import sun.nio.ch.DirectBuffer;
+
+/**
+ * A class that can be used to compute the CRC-32 of a data stream.
+ *
+ * <p> Passing a {@code null} argument to a method in this class will cause
+ * a {@link NullPointerException} to be thrown.
+ *
+ * @see Checksum
+ * @author David Connelly
+ */
+public
+class CRC32 implements Checksum {
+ private int crc;
+
+ /**
+ * Creates a new CRC32 object.
+ */
+ public CRC32() {
+ }
+
+
+ /**
+ * Updates the CRC-32 checksum with the specified byte (the low
+ * eight bits of the argument b).
+ *
+ * @param b the byte to update the checksum with
+ */
+ public void update(int b) {
+ crc = update(crc, b);
+ }
+
+ /**
+ * Updates the CRC-32 checksum with the specified array of bytes.
+ *
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code off} is negative, or {@code len} is negative,
+ * or {@code off+len} is greater than the length of the
+ * array {@code b}
+ */
+ public void update(byte[] b, int off, int len) {
+ if (b == null) {
+ throw new NullPointerException();
+ }
+ if (off < 0 || len < 0 || off > b.length - len) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ crc = updateBytes(crc, b, off, len);
+ }
+
+ /**
+ * Updates the CRC-32 checksum with the specified array of bytes.
+ *
+ * @param b the array of bytes to update the checksum with
+ */
+ public void update(byte[] b) {
+ crc = updateBytes(crc, b, 0, b.length);
+ }
+
+ /**
+ * Updates the checksum with the bytes from the specified buffer.
+ *
+ * The checksum is updated using
+ * buffer.{@link java.nio.Buffer#remaining() remaining()}
+ * bytes starting at
+ * buffer.{@link java.nio.Buffer#position() position()}
+ * Upon return, the buffer's position will
+ * be updated to its limit; its limit will not have been changed.
+ *
+ * @param buffer the ByteBuffer to update the checksum with
+ * @since 1.8
+ */
+ public void update(ByteBuffer buffer) {
+ int pos = buffer.position();
+ int limit = buffer.limit();
+ assert (pos <= limit);
+ int rem = limit - pos;
+ if (rem <= 0)
+ return;
+ if (buffer instanceof DirectBuffer) {
+ crc = updateByteBuffer(crc, ((DirectBuffer)buffer).address(), pos, rem);
+ } else if (buffer.hasArray()) {
+ crc = updateBytes(crc, buffer.array(), pos + buffer.arrayOffset(), rem);
+ } else {
+ byte[] b = new byte[rem];
+ buffer.get(b);
+ crc = updateBytes(crc, b, 0, b.length);
+ }
+ buffer.position(limit);
+ }
+
+ /**
+ * Resets CRC-32 to initial value.
+ */
+ public void reset() {
+ crc = 0;
+ }
+
+ /**
+ * Returns CRC-32 value.
+ */
+ public long getValue() {
+ return (long)crc & 0xffffffffL;
+ }
+
+ private native static int update(int crc, int b);
+ private native static int updateBytes(int crc, byte[] b, int off, int len);
+
+ private native static int updateByteBuffer(int adler, long addr,
+ int off, int len);
+}
diff --git a/java/util/zip/CheckedInputStream.java b/java/util/zip/CheckedInputStream.java
new file mode 100644
index 0000000..e1e5249
--- /dev/null
+++ b/java/util/zip/CheckedInputStream.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 1996, 2006, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.FilterInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * An input stream that also maintains a checksum of the data being read.
+ * The checksum can then be used to verify the integrity of the input data.
+ *
+ * @see Checksum
+ * @author David Connelly
+ */
+public
+class CheckedInputStream extends FilterInputStream {
+ private Checksum cksum;
+
+ /**
+ * Creates an input stream using the specified Checksum.
+ * @param in the input stream
+ * @param cksum the Checksum
+ */
+ public CheckedInputStream(InputStream in, Checksum cksum) {
+ super(in);
+ this.cksum = cksum;
+ }
+
+ /**
+ * Reads a byte. Will block if no input is available.
+ * @return the byte read, or -1 if the end of the stream is reached.
+ * @exception IOException if an I/O error has occurred
+ */
+ public int read() throws IOException {
+ int b = in.read();
+ if (b != -1) {
+ cksum.update(b);
+ }
+ return b;
+ }
+
+ /**
+ * Reads into an array of bytes. If <code>len</code> is not zero, the method
+ * blocks until some input is available; otherwise, no
+ * bytes are read and <code>0</code> is returned.
+ * @param buf the buffer into which the data is read
+ * @param off the start offset in the destination array <code>b</code>
+ * @param len the maximum number of bytes read
+ * @return the actual number of bytes read, or -1 if the end
+ * of the stream is reached.
+ * @exception NullPointerException If <code>buf</code> is <code>null</code>.
+ * @exception IndexOutOfBoundsException If <code>off</code> is negative,
+ * <code>len</code> is negative, or <code>len</code> is greater than
+ * <code>buf.length - off</code>
+ * @exception IOException if an I/O error has occurred
+ */
+ public int read(byte[] buf, int off, int len) throws IOException {
+ len = in.read(buf, off, len);
+ if (len != -1) {
+ cksum.update(buf, off, len);
+ }
+ return len;
+ }
+
+ /**
+ * Skips specified number of bytes of input.
+ * @param n the number of bytes to skip
+ * @return the actual number of bytes skipped
+ * @exception IOException if an I/O error has occurred
+ */
+ public long skip(long n) throws IOException {
+ byte[] buf = new byte[512];
+ long total = 0;
+ while (total < n) {
+ long len = n - total;
+ len = read(buf, 0, len < buf.length ? (int)len : buf.length);
+ if (len == -1) {
+ return total;
+ }
+ total += len;
+ }
+ return total;
+ }
+
+ /**
+ * Returns the Checksum for this input stream.
+ * @return the Checksum value
+ */
+ public Checksum getChecksum() {
+ return cksum;
+ }
+}
diff --git a/java/util/zip/CheckedOutputStream.java b/java/util/zip/CheckedOutputStream.java
new file mode 100644
index 0000000..698b941
--- /dev/null
+++ b/java/util/zip/CheckedOutputStream.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 1996, 1999, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.FilterOutputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * An output stream that also maintains a checksum of the data being
+ * written. The checksum can then be used to verify the integrity of
+ * the output data.
+ *
+ * @see Checksum
+ * @author David Connelly
+ */
+public
+class CheckedOutputStream extends FilterOutputStream {
+ private Checksum cksum;
+
+ /**
+ * Creates an output stream with the specified Checksum.
+ * @param out the output stream
+ * @param cksum the checksum
+ */
+ public CheckedOutputStream(OutputStream out, Checksum cksum) {
+ super(out);
+ this.cksum = cksum;
+ }
+
+ /**
+ * Writes a byte. Will block until the byte is actually written.
+ * @param b the byte to be written
+ * @exception IOException if an I/O error has occurred
+ */
+ public void write(int b) throws IOException {
+ out.write(b);
+ cksum.update(b);
+ }
+
+ /**
+ * Writes an array of bytes. Will block until the bytes are
+ * actually written.
+ * @param b the data to be written
+ * @param off the start offset of the data
+ * @param len the number of bytes to be written
+ * @exception IOException if an I/O error has occurred
+ */
+ public void write(byte[] b, int off, int len) throws IOException {
+ out.write(b, off, len);
+ cksum.update(b, off, len);
+ }
+
+ /**
+ * Returns the Checksum for this output stream.
+ * @return the Checksum
+ */
+ public Checksum getChecksum() {
+ return cksum;
+ }
+}
diff --git a/java/util/zip/Checksum.java b/java/util/zip/Checksum.java
new file mode 100644
index 0000000..0369c53
--- /dev/null
+++ b/java/util/zip/Checksum.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 1996, 1999, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+/**
+ * An interface representing a data checksum.
+ *
+ * @author David Connelly
+ */
+public
+interface Checksum {
+ /**
+ * Updates the current checksum with the specified byte.
+ *
+ * @param b the byte to update the checksum with
+ */
+ public void update(int b);
+
+ /**
+ * Updates the current checksum with the specified array of bytes.
+ * @param b the byte array to update the checksum with
+ * @param off the start offset of the data
+ * @param len the number of bytes to use for the update
+ */
+ public void update(byte[] b, int off, int len);
+
+ /**
+ * Returns the current checksum value.
+ * @return the current checksum value
+ */
+ public long getValue();
+
+ /**
+ * Resets the checksum to its initial value.
+ */
+ public void reset();
+}
diff --git a/java/util/zip/DataFormatException.java b/java/util/zip/DataFormatException.java
new file mode 100644
index 0000000..77adbe9
--- /dev/null
+++ b/java/util/zip/DataFormatException.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 1996, 2008, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+/**
+ * Signals that a data format error has occurred.
+ *
+ * @author David Connelly
+ */
+public
+class DataFormatException extends Exception {
+ private static final long serialVersionUID = 2219632870893641452L;
+
+ /**
+ * Constructs a DataFormatException with no detail message.
+ */
+ public DataFormatException() {
+ super();
+ }
+
+ /**
+ * Constructs a DataFormatException with the specified detail message.
+ * A detail message is a String that describes this particular exception.
+ * @param s the String containing a detail message
+ */
+ public DataFormatException(String s) {
+ super(s);
+ }
+}
diff --git a/java/util/zip/Deflater.java b/java/util/zip/Deflater.java
new file mode 100644
index 0000000..5c7dffd
--- /dev/null
+++ b/java/util/zip/Deflater.java
@@ -0,0 +1,595 @@
+/*
+ * Copyright (c) 1996, 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import dalvik.annotation.optimization.ReachabilitySensitive;
+import dalvik.system.CloseGuard;
+
+/**
+ * This class provides support for general purpose compression using the
+ * popular ZLIB compression library. The ZLIB compression library was
+ * initially developed as part of the PNG graphics standard and is not
+ * protected by patents. It is fully described in the specifications at
+ * the <a href="package-summary.html#package_description">java.util.zip
+ * package description</a>.
+ *
+ * <p>The following code fragment demonstrates a trivial compression
+ * and decompression of a string using <tt>Deflater</tt> and
+ * <tt>Inflater</tt>.
+ *
+ * <blockquote><pre>
+ * try {
+ * // Encode a String into bytes
+ * String inputString = "blahblahblah";
+ * byte[] input = inputString.getBytes("UTF-8");
+ *
+ * // Compress the bytes
+ * byte[] output = new byte[100];
+ * Deflater compresser = new Deflater();
+ * compresser.setInput(input);
+ * compresser.finish();
+ * int compressedDataLength = compresser.deflate(output);
+ * compresser.end();
+ *
+ * // Decompress the bytes
+ * Inflater decompresser = new Inflater();
+ * decompresser.setInput(output, 0, compressedDataLength);
+ * byte[] result = new byte[100];
+ * int resultLength = decompresser.inflate(result);
+ * decompresser.end();
+ *
+ * // Decode the bytes into a String
+ * String outputString = new String(result, 0, resultLength, "UTF-8");
+ * } catch(java.io.UnsupportedEncodingException ex) {
+ * // handle
+ * } catch (java.util.zip.DataFormatException ex) {
+ * // handle
+ * }
+ * </pre></blockquote>
+ *
+ * @see Inflater
+ * @author David Connelly
+ */
+public
+class Deflater {
+
+ // Android-added: @ReachabilitySensitive
+ // Finalization clears zsRef, and thus can't be allowed to occur early.
+ // Unlike some other CloseGuard uses, the spec allows clients to rely on finalization
+ // here. Thus dropping a deflater without calling end() should work correctly.
+ // It thus does not suffice to just rely on the CloseGuard annotation.
+ @ReachabilitySensitive
+ private final ZStreamRef zsRef;
+ private byte[] buf = new byte[0];
+ private int off, len;
+ private int level, strategy;
+ private boolean setParams;
+ private boolean finish, finished;
+ private long bytesRead;
+ private long bytesWritten;
+
+ // Android-added: CloseGuard support.
+ @ReachabilitySensitive
+ private final CloseGuard guard = CloseGuard.get();
+
+ /**
+ * Compression method for the deflate algorithm (the only one currently
+ * supported).
+ */
+ public static final int DEFLATED = 8;
+
+ /**
+ * Compression level for no compression.
+ */
+ public static final int NO_COMPRESSION = 0;
+
+ /**
+ * Compression level for fastest compression.
+ */
+ public static final int BEST_SPEED = 1;
+
+ /**
+ * Compression level for best compression.
+ */
+ public static final int BEST_COMPRESSION = 9;
+
+ /**
+ * Default compression level.
+ */
+ public static final int DEFAULT_COMPRESSION = -1;
+
+ /**
+ * Compression strategy best used for data consisting mostly of small
+ * values with a somewhat random distribution. Forces more Huffman coding
+ * and less string matching.
+ */
+ public static final int FILTERED = 1;
+
+ /**
+ * Compression strategy for Huffman coding only.
+ */
+ public static final int HUFFMAN_ONLY = 2;
+
+ /**
+ * Default compression strategy.
+ */
+ public static final int DEFAULT_STRATEGY = 0;
+
+ /**
+ * Compression flush mode used to achieve best compression result.
+ *
+ * @see Deflater#deflate(byte[], int, int, int)
+ * @since 1.7
+ */
+ public static final int NO_FLUSH = 0;
+
+ /**
+ * Compression flush mode used to flush out all pending output; may
+ * degrade compression for some compression algorithms.
+ *
+ * @see Deflater#deflate(byte[], int, int, int)
+ * @since 1.7
+ */
+ public static final int SYNC_FLUSH = 2;
+
+ /**
+ * Compression flush mode used to flush out all pending output and
+ * reset the deflater. Using this mode too often can seriously degrade
+ * compression.
+ *
+ * @see Deflater#deflate(byte[], int, int, int)
+ * @since 1.7
+ */
+ public static final int FULL_FLUSH = 3;
+
+ // Android-removed: initIDs handled in register method.
+ /*
+ static {
+ /* Zip library is loaded from System.initializeSystemClass *
+ initIDs();
+ }
+ */
+
+ /**
+ * Creates a new compressor using the specified compression level.
+ * If 'nowrap' is true then the ZLIB header and checksum fields will
+ * not be used in order to support the compression format used in
+ * both GZIP and PKZIP.
+ * @param level the compression level (0-9)
+ * @param nowrap if true then use GZIP compatible compression
+ */
+ public Deflater(int level, boolean nowrap) {
+ this.level = level;
+ this.strategy = DEFAULT_STRATEGY;
+ this.zsRef = new ZStreamRef(init(level, DEFAULT_STRATEGY, nowrap));
+ // Android-added: CloseGuard support.
+ guard.open("end");
+ }
+
+ /**
+ * Creates a new compressor using the specified compression level.
+ * Compressed data will be generated in ZLIB format.
+ * @param level the compression level (0-9)
+ */
+ public Deflater(int level) {
+ this(level, false);
+ }
+
+ /**
+ * Creates a new compressor with the default compression level.
+ * Compressed data will be generated in ZLIB format.
+ */
+ public Deflater() {
+ this(DEFAULT_COMPRESSION, false);
+ }
+
+ /**
+ * Sets input data for compression. This should be called whenever
+ * needsInput() returns true indicating that more input data is required.
+ * @param b the input data bytes
+ * @param off the start offset of the data
+ * @param len the length of the data
+ * @see Deflater#needsInput
+ */
+ public void setInput(byte[] b, int off, int len) {
+ if (b== null) {
+ throw new NullPointerException();
+ }
+ if (off < 0 || len < 0 || off > b.length - len) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ synchronized (zsRef) {
+ this.buf = b;
+ this.off = off;
+ this.len = len;
+ }
+ }
+
+ /**
+ * Sets input data for compression. This should be called whenever
+ * needsInput() returns true indicating that more input data is required.
+ * @param b the input data bytes
+ * @see Deflater#needsInput
+ */
+ public void setInput(byte[] b) {
+ setInput(b, 0, b.length);
+ }
+
+ /**
+ * Sets preset dictionary for compression. A preset dictionary is used
+ * when the history buffer can be predetermined. When the data is later
+ * uncompressed with Inflater.inflate(), Inflater.getAdler() can be called
+ * in order to get the Adler-32 value of the dictionary required for
+ * decompression.
+ * @param b the dictionary data bytes
+ * @param off the start offset of the data
+ * @param len the length of the data
+ * @see Inflater#inflate
+ * @see Inflater#getAdler
+ */
+ public void setDictionary(byte[] b, int off, int len) {
+ if (b == null) {
+ throw new NullPointerException();
+ }
+ if (off < 0 || len < 0 || off > b.length - len) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ synchronized (zsRef) {
+ ensureOpen();
+ setDictionary(zsRef.address(), b, off, len);
+ }
+ }
+
+ /**
+ * Sets preset dictionary for compression. A preset dictionary is used
+ * when the history buffer can be predetermined. When the data is later
+ * uncompressed with Inflater.inflate(), Inflater.getAdler() can be called
+ * in order to get the Adler-32 value of the dictionary required for
+ * decompression.
+ * @param b the dictionary data bytes
+ * @see Inflater#inflate
+ * @see Inflater#getAdler
+ */
+ public void setDictionary(byte[] b) {
+ setDictionary(b, 0, b.length);
+ }
+
+ /**
+ * Sets the compression strategy to the specified value.
+ *
+ * <p> If the compression strategy is changed, the next invocation
+ * of {@code deflate} will compress the input available so far with
+ * the old strategy (and may be flushed); the new strategy will take
+ * effect only after that invocation.
+ *
+ * @param strategy the new compression strategy
+ * @exception IllegalArgumentException if the compression strategy is
+ * invalid
+ */
+ public void setStrategy(int strategy) {
+ switch (strategy) {
+ case DEFAULT_STRATEGY:
+ case FILTERED:
+ case HUFFMAN_ONLY:
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ synchronized (zsRef) {
+ if (this.strategy != strategy) {
+ this.strategy = strategy;
+ setParams = true;
+ }
+ }
+ }
+
+ /**
+ * Sets the compression level to the specified value.
+ *
+ * <p> If the compression level is changed, the next invocation
+ * of {@code deflate} will compress the input available so far
+ * with the old level (and may be flushed); the new level will
+ * take effect only after that invocation.
+ *
+ * @param level the new compression level (0-9)
+ * @exception IllegalArgumentException if the compression level is invalid
+ */
+ public void setLevel(int level) {
+ if ((level < 0 || level > 9) && level != DEFAULT_COMPRESSION) {
+ throw new IllegalArgumentException("invalid compression level");
+ }
+ synchronized (zsRef) {
+ if (this.level != level) {
+ this.level = level;
+ setParams = true;
+ }
+ }
+ }
+
+ /**
+ * Returns true if the input data buffer is empty and setInput()
+ * should be called in order to provide more input.
+ * @return true if the input data buffer is empty and setInput()
+ * should be called in order to provide more input
+ */
+ public boolean needsInput() {
+ synchronized (zsRef) {
+ return len <= 0;
+ }
+ }
+
+ /**
+ * When called, indicates that compression should end with the current
+ * contents of the input buffer.
+ */
+ public void finish() {
+ synchronized (zsRef) {
+ finish = true;
+ }
+ }
+
+ /**
+ * Returns true if the end of the compressed data output stream has
+ * been reached.
+ * @return true if the end of the compressed data output stream has
+ * been reached
+ */
+ public boolean finished() {
+ synchronized (zsRef) {
+ return finished;
+ }
+ }
+
+ /**
+ * Compresses the input data and fills specified buffer with compressed
+ * data. Returns actual number of bytes of compressed data. A return value
+ * of 0 indicates that {@link #needsInput() needsInput} should be called
+ * in order to determine if more input data is required.
+ *
+ * <p>This method uses {@link #NO_FLUSH} as its compression flush mode.
+ * An invocation of this method of the form {@code deflater.deflate(b, off, len)}
+ * yields the same result as the invocation of
+ * {@code deflater.deflate(b, off, len, Deflater.NO_FLUSH)}.
+ *
+ * @param b the buffer for the compressed data
+ * @param off the start offset of the data
+ * @param len the maximum number of bytes of compressed data
+ * @return the actual number of bytes of compressed data written to the
+ * output buffer
+ */
+ public int deflate(byte[] b, int off, int len) {
+ return deflate(b, off, len, NO_FLUSH);
+ }
+
+ /**
+ * Compresses the input data and fills specified buffer with compressed
+ * data. Returns actual number of bytes of compressed data. A return value
+ * of 0 indicates that {@link #needsInput() needsInput} should be called
+ * in order to determine if more input data is required.
+ *
+ * <p>This method uses {@link #NO_FLUSH} as its compression flush mode.
+ * An invocation of this method of the form {@code deflater.deflate(b)}
+ * yields the same result as the invocation of
+ * {@code deflater.deflate(b, 0, b.length, Deflater.NO_FLUSH)}.
+ *
+ * @param b the buffer for the compressed data
+ * @return the actual number of bytes of compressed data written to the
+ * output buffer
+ */
+ public int deflate(byte[] b) {
+ return deflate(b, 0, b.length, NO_FLUSH);
+ }
+
+ /**
+ * Compresses the input data and fills the specified buffer with compressed
+ * data. Returns actual number of bytes of data compressed.
+ *
+ * <p>Compression flush mode is one of the following three modes:
+ *
+ * <ul>
+ * <li>{@link #NO_FLUSH}: allows the deflater to decide how much data
+ * to accumulate, before producing output, in order to achieve the best
+ * compression (should be used in normal use scenario). A return value
+ * of 0 in this flush mode indicates that {@link #needsInput()} should
+ * be called in order to determine if more input data is required.
+ *
+ * <li>{@link #SYNC_FLUSH}: all pending output in the deflater is flushed,
+ * to the specified output buffer, so that an inflater that works on
+ * compressed data can get all input data available so far (In particular
+ * the {@link #needsInput()} returns {@code true} after this invocation
+ * if enough output space is provided). Flushing with {@link #SYNC_FLUSH}
+ * may degrade compression for some compression algorithms and so it
+ * should be used only when necessary.
+ *
+ * <li>{@link #FULL_FLUSH}: all pending output is flushed out as with
+ * {@link #SYNC_FLUSH}. The compression state is reset so that the inflater
+ * that works on the compressed output data can restart from this point
+ * if previous compressed data has been damaged or if random access is
+ * desired. Using {@link #FULL_FLUSH} too often can seriously degrade
+ * compression.
+ * </ul>
+ *
+ * <p>In the case of {@link #FULL_FLUSH} or {@link #SYNC_FLUSH}, if
+ * the return value is {@code len}, the space available in output
+ * buffer {@code b}, this method should be invoked again with the same
+ * {@code flush} parameter and more output space.
+ *
+ * @param b the buffer for the compressed data
+ * @param off the start offset of the data
+ * @param len the maximum number of bytes of compressed data
+ * @param flush the compression flush mode
+ * @return the actual number of bytes of compressed data written to
+ * the output buffer
+ *
+ * @throws IllegalArgumentException if the flush mode is invalid
+ * @since 1.7
+ */
+ public int deflate(byte[] b, int off, int len, int flush) {
+ if (b == null) {
+ throw new NullPointerException();
+ }
+ if (off < 0 || len < 0 || off > b.length - len) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ synchronized (zsRef) {
+ ensureOpen();
+ if (flush == NO_FLUSH || flush == SYNC_FLUSH ||
+ flush == FULL_FLUSH) {
+ int thisLen = this.len;
+ int n = deflateBytes(zsRef.address(), b, off, len, flush);
+ bytesWritten += n;
+ bytesRead += (thisLen - this.len);
+ return n;
+ }
+ throw new IllegalArgumentException();
+ }
+ }
+
+ /**
+ * Returns the ADLER-32 value of the uncompressed data.
+ * @return the ADLER-32 value of the uncompressed data
+ */
+ public int getAdler() {
+ synchronized (zsRef) {
+ ensureOpen();
+ return getAdler(zsRef.address());
+ }
+ }
+
+ /**
+ * Returns the total number of uncompressed bytes input so far.
+ *
+ * <p>Since the number of bytes may be greater than
+ * Integer.MAX_VALUE, the {@link #getBytesRead()} method is now
+ * the preferred means of obtaining this information.</p>
+ *
+ * @return the total number of uncompressed bytes input so far
+ */
+ public int getTotalIn() {
+ return (int) getBytesRead();
+ }
+
+ /**
+ * Returns the total number of uncompressed bytes input so far.
+ *
+ * @return the total (non-negative) number of uncompressed bytes input so far
+ * @since 1.5
+ */
+ public long getBytesRead() {
+ synchronized (zsRef) {
+ ensureOpen();
+ return bytesRead;
+ }
+ }
+
+ /**
+ * Returns the total number of compressed bytes output so far.
+ *
+ * <p>Since the number of bytes may be greater than
+ * Integer.MAX_VALUE, the {@link #getBytesWritten()} method is now
+ * the preferred means of obtaining this information.</p>
+ *
+ * @return the total number of compressed bytes output so far
+ */
+ public int getTotalOut() {
+ return (int) getBytesWritten();
+ }
+
+ /**
+ * Returns the total number of compressed bytes output so far.
+ *
+ * @return the total (non-negative) number of compressed bytes output so far
+ * @since 1.5
+ */
+ public long getBytesWritten() {
+ synchronized (zsRef) {
+ ensureOpen();
+ return bytesWritten;
+ }
+ }
+
+ /**
+ * Resets deflater so that a new set of input data can be processed.
+ * Keeps current compression level and strategy settings.
+ */
+ public void reset() {
+ synchronized (zsRef) {
+ ensureOpen();
+ reset(zsRef.address());
+ finish = false;
+ finished = false;
+ off = len = 0;
+ bytesRead = bytesWritten = 0;
+ }
+ }
+
+ /**
+ * Closes the compressor and discards any unprocessed input.
+ * This method should be called when the compressor is no longer
+ * being used, but will also be called automatically by the
+ * finalize() method. Once this method is called, the behavior
+ * of the Deflater object is undefined.
+ */
+ public void end() {
+ synchronized (zsRef) {
+ // Android-added: CloseGuard support.
+ guard.close();
+ long addr = zsRef.address();
+ zsRef.clear();
+ if (addr != 0) {
+ end(addr);
+ buf = null;
+ }
+ }
+ }
+
+ /**
+ * Closes the compressor when garbage is collected.
+ */
+ protected void finalize() {
+ // Android-added: CloseGuard support.
+ if (guard != null) {
+ guard.warnIfOpen();
+ }
+ end();
+ }
+
+ private void ensureOpen() {
+ assert Thread.holdsLock(zsRef);
+ if (zsRef.address() == 0)
+ throw new NullPointerException("Deflater has been closed");
+ }
+
+ // Android-changed: initIDs handled in register method.
+ // private native static void initIDs();
+ private native static long init(int level, int strategy, boolean nowrap);
+ private native static void setDictionary(long addr, byte[] b, int off, int len);
+ private native int deflateBytes(long addr, byte[] b, int off, int len,
+ int flush);
+ private native static int getAdler(long addr);
+ private native static void reset(long addr);
+ private native static void end(long addr);
+}
diff --git a/java/util/zip/DeflaterInputStream.java b/java/util/zip/DeflaterInputStream.java
new file mode 100644
index 0000000..c3f7802
--- /dev/null
+++ b/java/util/zip/DeflaterInputStream.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 2006, 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.FilterInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * Implements an input stream filter for compressing data in the "deflate"
+ * compression format.
+ *
+ * @since 1.6
+ * @author David R Tribble ([email protected])
+ *
+ * @see DeflaterOutputStream
+ * @see InflaterOutputStream
+ * @see InflaterInputStream
+ */
+
+public class DeflaterInputStream extends FilterInputStream {
+ /** Compressor for this stream. */
+ protected final Deflater def;
+
+ /** Input buffer for reading compressed data. */
+ protected final byte[] buf;
+
+ /** Temporary read buffer. */
+ private byte[] rbuf = new byte[1];
+
+ /** Default compressor is used. */
+ private boolean usesDefaultDeflater = false;
+
+ /** End of the underlying input stream has been reached. */
+ private boolean reachEOF = false;
+
+ /**
+ * Check to make sure that this stream has not been closed.
+ */
+ private void ensureOpen() throws IOException {
+ if (in == null) {
+ throw new IOException("Stream closed");
+ }
+ }
+
+ /**
+ * Creates a new input stream with a default compressor and buffer
+ * size.
+ *
+ * @param in input stream to read the uncompressed data to
+ * @throws NullPointerException if {@code in} is null
+ */
+ public DeflaterInputStream(InputStream in) {
+ this(in, new Deflater());
+ usesDefaultDeflater = true;
+ }
+
+ /**
+ * Creates a new input stream with the specified compressor and a
+ * default buffer size.
+ *
+ * @param in input stream to read the uncompressed data to
+ * @param defl compressor ("deflater") for this stream
+ * @throws NullPointerException if {@code in} or {@code defl} is null
+ */
+ public DeflaterInputStream(InputStream in, Deflater defl) {
+ this(in, defl, 512);
+ }
+
+ /**
+ * Creates a new input stream with the specified compressor and buffer
+ * size.
+ *
+ * @param in input stream to read the uncompressed data to
+ * @param defl compressor ("deflater") for this stream
+ * @param bufLen compression buffer size
+ * @throws IllegalArgumentException if {@code bufLen <= 0}
+ * @throws NullPointerException if {@code in} or {@code defl} is null
+ */
+ public DeflaterInputStream(InputStream in, Deflater defl, int bufLen) {
+ super(in);
+
+ // Sanity checks
+ if (in == null)
+ throw new NullPointerException("Null input");
+ if (defl == null)
+ throw new NullPointerException("Null deflater");
+ if (bufLen < 1)
+ throw new IllegalArgumentException("Buffer size < 1");
+
+ // Initialize
+ def = defl;
+ buf = new byte[bufLen];
+ }
+
+ /**
+ * Closes this input stream and its underlying input stream, discarding
+ * any pending uncompressed data.
+ *
+ * @throws IOException if an I/O error occurs
+ */
+ public void close() throws IOException {
+ if (in != null) {
+ try {
+ // Clean up
+ if (usesDefaultDeflater) {
+ def.end();
+ }
+
+ in.close();
+ } finally {
+ in = null;
+ }
+ }
+ }
+
+ /**
+ * Reads a single byte of compressed data from the input stream.
+ * This method will block until some input can be read and compressed.
+ *
+ * @return a single byte of compressed data, or -1 if the end of the
+ * uncompressed input stream is reached
+ * @throws IOException if an I/O error occurs or if this stream is
+ * already closed
+ */
+ public int read() throws IOException {
+ // Read a single byte of compressed data
+ int len = read(rbuf, 0, 1);
+ if (len <= 0)
+ return -1;
+ return (rbuf[0] & 0xFF);
+ }
+
+ /**
+ * Reads compressed data into a byte array.
+ * This method will block until some input can be read and compressed.
+ *
+ * @param b buffer into which the data is read
+ * @param off starting offset of the data within {@code b}
+ * @param len maximum number of compressed bytes to read into {@code b}
+ * @return the actual number of bytes read, or -1 if the end of the
+ * uncompressed input stream is reached
+ * @throws IndexOutOfBoundsException if {@code len > b.length - off}
+ * @throws IOException if an I/O error occurs or if this input stream is
+ * already closed
+ */
+ public int read(byte[] b, int off, int len) throws IOException {
+ // Sanity checks
+ ensureOpen();
+ if (b == null) {
+ throw new NullPointerException("Null buffer for read");
+ } else if (off < 0 || len < 0 || len > b.length - off) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0) {
+ return 0;
+ }
+
+ // Read and compress (deflate) input data bytes
+ int cnt = 0;
+ while (len > 0 && !def.finished()) {
+ int n;
+
+ // Read data from the input stream
+ if (def.needsInput()) {
+ n = in.read(buf, 0, buf.length);
+ if (n < 0) {
+ // End of the input stream reached
+ def.finish();
+ } else if (n > 0) {
+ def.setInput(buf, 0, n);
+ }
+ }
+
+ // Compress the input data, filling the read buffer
+ n = def.deflate(b, off, len);
+ cnt += n;
+ off += n;
+ len -= n;
+ }
+ // BEGIN Android-changed: Return more accurate value from available().
+ // Set reachEOF eagerly when the Deflater has finished, and not just when the number of
+ // bytes is zero so that available is more accurate.
+ // See http://b/111589691
+ /*
+ if (cnt == 0 && def.finished()) {
+ reachEOF = true;
+ cnt = -1;
+ }
+ */
+ if (def.finished()) {
+ reachEOF = true;
+ if (cnt == 0) {
+ cnt = -1;
+ }
+ }
+ // END Android-changed: Return more accurate value from available().
+
+ return cnt;
+ }
+
+ /**
+ * Skips over and discards data from the input stream.
+ * This method may block until the specified number of bytes are read and
+ * skipped. <em>Note:</em> While {@code n} is given as a {@code long},
+ * the maximum number of bytes which can be skipped is
+ * {@code Integer.MAX_VALUE}.
+ *
+ * @param n number of bytes to be skipped
+ * @return the actual number of bytes skipped
+ * @throws IOException if an I/O error occurs or if this stream is
+ * already closed
+ */
+ public long skip(long n) throws IOException {
+ if (n < 0) {
+ throw new IllegalArgumentException("negative skip length");
+ }
+ ensureOpen();
+
+ // Skip bytes by repeatedly decompressing small blocks
+ if (rbuf.length < 512)
+ rbuf = new byte[512];
+
+ int total = (int)Math.min(n, Integer.MAX_VALUE);
+ long cnt = 0;
+ while (total > 0) {
+ // Read a small block of uncompressed bytes
+ int len = read(rbuf, 0, (total <= rbuf.length ? total : rbuf.length));
+
+ if (len < 0) {
+ break;
+ }
+ cnt += len;
+ total -= len;
+ }
+ return cnt;
+ }
+
+ /**
+ * Returns 0 after EOF has been reached, otherwise always return 1.
+ * <p>
+ * Programs should not count on this method to return the actual number
+ * of bytes that could be read without blocking
+ * @return zero after the end of the underlying input stream has been
+ * reached, otherwise always returns 1
+ * @throws IOException if an I/O error occurs or if this stream is
+ * already closed
+ */
+ public int available() throws IOException {
+ ensureOpen();
+ if (reachEOF) {
+ return 0;
+ }
+ return 1;
+ }
+
+ /**
+ * Always returns {@code false} because this input stream does not support
+ * the {@link #mark mark()} and {@link #reset reset()} methods.
+ *
+ * @return false, always
+ */
+ public boolean markSupported() {
+ return false;
+ }
+
+ /**
+ * <i>This operation is not supported</i>.
+ *
+ * @param limit maximum bytes that can be read before invalidating the position marker
+ */
+ public void mark(int limit) {
+ // Operation not supported
+ }
+
+ /**
+ * <i>This operation is not supported</i>.
+ *
+ * @throws IOException always thrown
+ */
+ public void reset() throws IOException {
+ throw new IOException("mark/reset not supported");
+ }
+}
diff --git a/java/util/zip/DeflaterOutputStream.java b/java/util/zip/DeflaterOutputStream.java
new file mode 100644
index 0000000..7821737
--- /dev/null
+++ b/java/util/zip/DeflaterOutputStream.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1996, 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.FilterOutputStream;
+import java.io.OutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * This class implements an output stream filter for compressing data in
+ * the "deflate" compression format. It is also used as the basis for other
+ * types of compression filters, such as GZIPOutputStream.
+ *
+ * @see Deflater
+ * @author David Connelly
+ */
+public
+class DeflaterOutputStream extends FilterOutputStream {
+ /**
+ * Compressor for this stream.
+ */
+ protected Deflater def;
+
+ /**
+ * Output buffer for writing compressed data.
+ */
+ protected byte[] buf;
+
+ /**
+ * Indicates that the stream has been closed.
+ */
+
+ private boolean closed = false;
+
+ private final boolean syncFlush;
+
+ /**
+ * Creates a new output stream with the specified compressor,
+ * buffer size and flush mode.
+
+ * @param out the output stream
+ * @param def the compressor ("deflater")
+ * @param size the output buffer size
+ * @param syncFlush
+ * if {@code true} the {@link #flush()} method of this
+ * instance flushes the compressor with flush mode
+ * {@link Deflater#SYNC_FLUSH} before flushing the output
+ * stream, otherwise only flushes the output stream
+ *
+ * @throws IllegalArgumentException if {@code size <= 0}
+ *
+ * @since 1.7
+ */
+ public DeflaterOutputStream(OutputStream out,
+ Deflater def,
+ int size,
+ boolean syncFlush) {
+ super(out);
+ if (out == null || def == null) {
+ throw new NullPointerException();
+ } else if (size <= 0) {
+ throw new IllegalArgumentException("buffer size <= 0");
+ }
+ this.def = def;
+ this.buf = new byte[size];
+ this.syncFlush = syncFlush;
+ }
+
+
+ /**
+ * Creates a new output stream with the specified compressor and
+ * buffer size.
+ *
+ * <p>The new output stream instance is created as if by invoking
+ * the 4-argument constructor DeflaterOutputStream(out, def, size, false).
+ *
+ * @param out the output stream
+ * @param def the compressor ("deflater")
+ * @param size the output buffer size
+ * @exception IllegalArgumentException if {@code size <= 0}
+ */
+ public DeflaterOutputStream(OutputStream out, Deflater def, int size) {
+ this(out, def, size, false);
+ }
+
+ /**
+ * Creates a new output stream with the specified compressor, flush
+ * mode and a default buffer size.
+ *
+ * @param out the output stream
+ * @param def the compressor ("deflater")
+ * @param syncFlush
+ * if {@code true} the {@link #flush()} method of this
+ * instance flushes the compressor with flush mode
+ * {@link Deflater#SYNC_FLUSH} before flushing the output
+ * stream, otherwise only flushes the output stream
+ *
+ * @since 1.7
+ */
+ public DeflaterOutputStream(OutputStream out,
+ Deflater def,
+ boolean syncFlush) {
+ this(out, def, 512, syncFlush);
+ }
+
+
+ /**
+ * Creates a new output stream with the specified compressor and
+ * a default buffer size.
+ *
+ * <p>The new output stream instance is created as if by invoking
+ * the 3-argument constructor DeflaterOutputStream(out, def, false).
+ *
+ * @param out the output stream
+ * @param def the compressor ("deflater")
+ */
+ public DeflaterOutputStream(OutputStream out, Deflater def) {
+ this(out, def, 512, false);
+ }
+
+ boolean usesDefaultDeflater = false;
+
+
+ /**
+ * Creates a new output stream with a default compressor, a default
+ * buffer size and the specified flush mode.
+ *
+ * @param out the output stream
+ * @param syncFlush
+ * if {@code true} the {@link #flush()} method of this
+ * instance flushes the compressor with flush mode
+ * {@link Deflater#SYNC_FLUSH} before flushing the output
+ * stream, otherwise only flushes the output stream
+ *
+ * @since 1.7
+ */
+ public DeflaterOutputStream(OutputStream out, boolean syncFlush) {
+ this(out, new Deflater(), 512, syncFlush);
+ usesDefaultDeflater = true;
+ }
+
+ /**
+ * Creates a new output stream with a default compressor and buffer size.
+ *
+ * <p>The new output stream instance is created as if by invoking
+ * the 2-argument constructor DeflaterOutputStream(out, false).
+ *
+ * @param out the output stream
+ */
+ public DeflaterOutputStream(OutputStream out) {
+ this(out, false);
+ usesDefaultDeflater = true;
+ }
+
+ /**
+ * Writes a byte to the compressed output stream. This method will
+ * block until the byte can be written.
+ * @param b the byte to be written
+ * @exception IOException if an I/O error has occurred
+ */
+ public void write(int b) throws IOException {
+ byte[] buf = new byte[1];
+ buf[0] = (byte)(b & 0xff);
+ write(buf, 0, 1);
+ }
+
+ /**
+ * Writes an array of bytes to the compressed output stream. This
+ * method will block until all the bytes are written.
+ * @param b the data to be written
+ * @param off the start offset of the data
+ * @param len the length of the data
+ * @exception IOException if an I/O error has occurred
+ */
+ public void write(byte[] b, int off, int len) throws IOException {
+ if (def.finished()) {
+ throw new IOException("write beyond end of stream");
+ }
+ if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0) {
+ return;
+ }
+ if (!def.finished()) {
+ def.setInput(b, off, len);
+ while (!def.needsInput()) {
+ deflate();
+ }
+ }
+ }
+
+ /**
+ * Finishes writing compressed data to the output stream without closing
+ * the underlying stream. Use this method when applying multiple filters
+ * in succession to the same output stream.
+ * @exception IOException if an I/O error has occurred
+ */
+ public void finish() throws IOException {
+ if (!def.finished()) {
+ def.finish();
+ while (!def.finished()) {
+ deflate();
+ }
+ }
+ }
+
+ /**
+ * Writes remaining compressed data to the output stream and closes the
+ * underlying stream.
+ * @exception IOException if an I/O error has occurred
+ */
+ public void close() throws IOException {
+ if (!closed) {
+ finish();
+ if (usesDefaultDeflater)
+ def.end();
+ out.close();
+ closed = true;
+ }
+ }
+
+ /**
+ * Writes next block of compressed data to the output stream.
+ * @throws IOException if an I/O error has occurred
+ */
+ protected void deflate() throws IOException {
+ // Android-changed: Output all available compressed data (b/4005091).
+ // See http://b/111496419 for more details.
+ // int len = def.deflate(buf, 0, buf.length);
+ // if (len > 0) {
+ // out.write(buf, 0, len);
+ // }
+ int len = 0;
+ while ((len = def.deflate(buf, 0, buf.length)) > 0) {
+ out.write(buf, 0, len);
+ }
+ }
+
+ /**
+ * Flushes the compressed output stream.
+ *
+ * If {@link #DeflaterOutputStream(OutputStream, Deflater, int, boolean)
+ * syncFlush} is {@code true} when this compressed output stream is
+ * constructed, this method first flushes the underlying {@code compressor}
+ * with the flush mode {@link Deflater#SYNC_FLUSH} to force
+ * all pending data to be flushed out to the output stream and then
+ * flushes the output stream. Otherwise this method only flushes the
+ * output stream without flushing the {@code compressor}.
+ *
+ * @throws IOException if an I/O error has occurred
+ *
+ * @since 1.7
+ */
+ public void flush() throws IOException {
+ if (syncFlush && !def.finished()) {
+ int len = 0;
+ while ((len = def.deflate(buf, 0, buf.length, Deflater.SYNC_FLUSH)) > 0)
+ {
+ out.write(buf, 0, len);
+ if (len < buf.length)
+ break;
+ }
+ }
+ out.flush();
+ }
+}
diff --git a/java/util/zip/GZIPInputStream.java b/java/util/zip/GZIPInputStream.java
new file mode 100644
index 0000000..109454c
--- /dev/null
+++ b/java/util/zip/GZIPInputStream.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 1996, 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.SequenceInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.FilterInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.EOFException;
+
+/**
+ * This class implements a stream filter for reading compressed data in
+ * the GZIP file format.
+ *
+ * @see InflaterInputStream
+ * @author David Connelly
+ *
+ */
+public
+class GZIPInputStream extends InflaterInputStream {
+ /**
+ * CRC-32 for uncompressed data.
+ */
+ protected CRC32 crc = new CRC32();
+
+ /**
+ * Indicates end of input stream.
+ */
+ protected boolean eos;
+
+ private boolean closed = false;
+
+ /**
+ * Check to make sure that this stream has not been closed
+ */
+ private void ensureOpen() throws IOException {
+ if (closed) {
+ throw new IOException("Stream closed");
+ }
+ }
+
+ /**
+ * Creates a new input stream with the specified buffer size.
+ * @param in the input stream
+ * @param size the input buffer size
+ *
+ * @exception ZipException if a GZIP format error has occurred or the
+ * compression method used is unsupported
+ * @exception IOException if an I/O error has occurred
+ * @exception IllegalArgumentException if {@code size <= 0}
+ */
+ public GZIPInputStream(InputStream in, int size) throws IOException {
+ super(in, new Inflater(true), size);
+ // Android-removed: Unconditionally close external inflaters (b/26462400)
+ // usesDefaultInflater = true;
+ // BEGIN Android-changed: Do not rely on finalization to inf.end().
+ // readHeader(in);
+ try {
+ readHeader(in);
+ } catch (Exception e) {
+ inf.end();
+ throw e;
+ }
+ // END Android-changed: Do not rely on finalization to inf.end().
+ }
+
+ /**
+ * Creates a new input stream with a default buffer size.
+ * @param in the input stream
+ *
+ * @exception ZipException if a GZIP format error has occurred or the
+ * compression method used is unsupported
+ * @exception IOException if an I/O error has occurred
+ */
+ public GZIPInputStream(InputStream in) throws IOException {
+ this(in, 512);
+ }
+
+ /**
+ * Reads uncompressed data into an array of bytes. If <code>len</code> is not
+ * zero, the method will block until some input can be decompressed; otherwise,
+ * no bytes are read and <code>0</code> is returned.
+ * @param buf the buffer into which the data is read
+ * @param off the start offset in the destination array <code>b</code>
+ * @param len the maximum number of bytes read
+ * @return the actual number of bytes read, or -1 if the end of the
+ * compressed input stream is reached
+ *
+ * @exception NullPointerException If <code>buf</code> is <code>null</code>.
+ * @exception IndexOutOfBoundsException If <code>off</code> is negative,
+ * <code>len</code> is negative, or <code>len</code> is greater than
+ * <code>buf.length - off</code>
+ * @exception ZipException if the compressed input data is corrupt.
+ * @exception IOException if an I/O error has occurred.
+ *
+ */
+ public int read(byte[] buf, int off, int len) throws IOException {
+ ensureOpen();
+ if (eos) {
+ return -1;
+ }
+ int n = super.read(buf, off, len);
+ if (n == -1) {
+ if (readTrailer())
+ eos = true;
+ else
+ return this.read(buf, off, len);
+ } else {
+ crc.update(buf, off, n);
+ }
+ return n;
+ }
+
+ /**
+ * Closes this input stream and releases any system resources associated
+ * with the stream.
+ * @exception IOException if an I/O error has occurred
+ */
+ public void close() throws IOException {
+ if (!closed) {
+ super.close();
+ eos = true;
+ closed = true;
+ }
+ }
+
+ /**
+ * GZIP header magic number.
+ */
+ public final static int GZIP_MAGIC = 0x8b1f;
+
+ /*
+ * File header flags.
+ */
+ private final static int FTEXT = 1; // Extra text
+ private final static int FHCRC = 2; // Header CRC
+ private final static int FEXTRA = 4; // Extra field
+ private final static int FNAME = 8; // File name
+ private final static int FCOMMENT = 16; // File comment
+
+ /*
+ * Reads GZIP member header and returns the total byte number
+ * of this member header.
+ */
+ private int readHeader(InputStream this_in) throws IOException {
+ CheckedInputStream in = new CheckedInputStream(this_in, crc);
+ crc.reset();
+ // Check header magic
+ if (readUShort(in) != GZIP_MAGIC) {
+ throw new ZipException("Not in GZIP format");
+ }
+ // Check compression method
+ if (readUByte(in) != 8) {
+ throw new ZipException("Unsupported compression method");
+ }
+ // Read flags
+ int flg = readUByte(in);
+ // Skip MTIME, XFL, and OS fields
+ skipBytes(in, 6);
+ int n = 2 + 2 + 6;
+ // Skip optional extra field
+ if ((flg & FEXTRA) == FEXTRA) {
+ int m = readUShort(in);
+ skipBytes(in, m);
+ n += m + 2;
+ }
+ // Skip optional file name
+ if ((flg & FNAME) == FNAME) {
+ do {
+ n++;
+ } while (readUByte(in) != 0);
+ }
+ // Skip optional file comment
+ if ((flg & FCOMMENT) == FCOMMENT) {
+ do {
+ n++;
+ } while (readUByte(in) != 0);
+ }
+ // Check optional header CRC
+ if ((flg & FHCRC) == FHCRC) {
+ int v = (int)crc.getValue() & 0xffff;
+ if (readUShort(in) != v) {
+ throw new ZipException("Corrupt GZIP header");
+ }
+ n += 2;
+ }
+ crc.reset();
+ return n;
+ }
+
+ /*
+ * Reads GZIP member trailer and returns true if the eos
+ * reached, false if there are more (concatenated gzip
+ * data set)
+ */
+ private boolean readTrailer() throws IOException {
+ InputStream in = this.in;
+ int n = inf.getRemaining();
+ if (n > 0) {
+ in = new SequenceInputStream(
+ new ByteArrayInputStream(buf, len - n, n),
+ new FilterInputStream(in) {
+ public void close() throws IOException {}
+ });
+ }
+ // Uses left-to-right evaluation order
+ if ((readUInt(in) != crc.getValue()) ||
+ // rfc1952; ISIZE is the input size modulo 2^32
+ (readUInt(in) != (inf.getBytesWritten() & 0xffffffffL)))
+ throw new ZipException("Corrupt GZIP trailer");
+
+ // If there are more bytes available in "in" or
+ // the leftover in the "inf" is > 26 bytes:
+ // this.trailer(8) + next.header.min(10) + next.trailer(8)
+ // try concatenated case
+ if (this.in.available() > 0 || n > 26) {
+ int m = 8; // this.trailer
+ try {
+ m += readHeader(in); // next.header
+ } catch (IOException ze) {
+ return true; // ignore any malformed, do nothing
+ }
+ inf.reset();
+ if (n > m)
+ inf.setInput(buf, len - n + m, n - m);
+ return false;
+ }
+ return true;
+ }
+
+ /*
+ * Reads unsigned integer in Intel byte order.
+ */
+ private long readUInt(InputStream in) throws IOException {
+ long s = readUShort(in);
+ return ((long)readUShort(in) << 16) | s;
+ }
+
+ /*
+ * Reads unsigned short in Intel byte order.
+ */
+ private int readUShort(InputStream in) throws IOException {
+ int b = readUByte(in);
+ return (readUByte(in) << 8) | b;
+ }
+
+ /*
+ * Reads unsigned byte.
+ */
+ private int readUByte(InputStream in) throws IOException {
+ int b = in.read();
+ if (b == -1) {
+ throw new EOFException();
+ }
+ if (b < -1 || b > 255) {
+ // Report on this.in, not argument in; see read{Header, Trailer}.
+ throw new IOException(this.in.getClass().getName()
+ + ".read() returned value out of range -1..255: " + b);
+ }
+ return b;
+ }
+
+ private byte[] tmpbuf = new byte[128];
+
+ /*
+ * Skips bytes of input data blocking until all bytes are skipped.
+ * Does not assume that the input stream is capable of seeking.
+ */
+ private void skipBytes(InputStream in, int n) throws IOException {
+ while (n > 0) {
+ int len = in.read(tmpbuf, 0, n < tmpbuf.length ? n : tmpbuf.length);
+ if (len == -1) {
+ throw new EOFException();
+ }
+ n -= len;
+ }
+ }
+}
diff --git a/java/util/zip/GZIPOutputStream.java b/java/util/zip/GZIPOutputStream.java
new file mode 100644
index 0000000..2c1cd40
--- /dev/null
+++ b/java/util/zip/GZIPOutputStream.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 1996, 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * This class implements a stream filter for writing compressed data in
+ * the GZIP file format.
+ * @author David Connelly
+ *
+ */
+public
+class GZIPOutputStream extends DeflaterOutputStream {
+ /**
+ * CRC-32 of uncompressed data.
+ */
+ protected CRC32 crc = new CRC32();
+
+ /*
+ * GZIP header magic number.
+ */
+ private final static int GZIP_MAGIC = 0x8b1f;
+
+ /*
+ * Trailer size in bytes.
+ *
+ */
+ private final static int TRAILER_SIZE = 8;
+
+ /**
+ * Creates a new output stream with the specified buffer size.
+ *
+ * <p>The new output stream instance is created as if by invoking
+ * the 3-argument constructor GZIPOutputStream(out, size, false).
+ *
+ * @param out the output stream
+ * @param size the output buffer size
+ * @exception IOException If an I/O error has occurred.
+ * @exception IllegalArgumentException if {@code size <= 0}
+ */
+ public GZIPOutputStream(OutputStream out, int size) throws IOException {
+ this(out, size, false);
+ }
+
+ /**
+ * Creates a new output stream with the specified buffer size and
+ * flush mode.
+ *
+ * @param out the output stream
+ * @param size the output buffer size
+ * @param syncFlush
+ * if {@code true} invocation of the inherited
+ * {@link DeflaterOutputStream#flush() flush()} method of
+ * this instance flushes the compressor with flush mode
+ * {@link Deflater#SYNC_FLUSH} before flushing the output
+ * stream, otherwise only flushes the output stream
+ * @exception IOException If an I/O error has occurred.
+ * @exception IllegalArgumentException if {@code size <= 0}
+ *
+ * @since 1.7
+ */
+ public GZIPOutputStream(OutputStream out, int size, boolean syncFlush)
+ throws IOException
+ {
+ super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true),
+ size,
+ syncFlush);
+ usesDefaultDeflater = true;
+ writeHeader();
+ crc.reset();
+ }
+
+
+ /**
+ * Creates a new output stream with a default buffer size.
+ *
+ * <p>The new output stream instance is created as if by invoking
+ * the 2-argument constructor GZIPOutputStream(out, false).
+ *
+ * @param out the output stream
+ * @exception IOException If an I/O error has occurred.
+ */
+ public GZIPOutputStream(OutputStream out) throws IOException {
+ this(out, 512, false);
+ }
+
+ /**
+ * Creates a new output stream with a default buffer size and
+ * the specified flush mode.
+ *
+ * @param out the output stream
+ * @param syncFlush
+ * if {@code true} invocation of the inherited
+ * {@link DeflaterOutputStream#flush() flush()} method of
+ * this instance flushes the compressor with flush mode
+ * {@link Deflater#SYNC_FLUSH} before flushing the output
+ * stream, otherwise only flushes the output stream
+ *
+ * @exception IOException If an I/O error has occurred.
+ *
+ * @since 1.7
+ */
+ public GZIPOutputStream(OutputStream out, boolean syncFlush)
+ throws IOException
+ {
+ this(out, 512, syncFlush);
+ }
+
+ /**
+ * Writes array of bytes to the compressed output stream. This method
+ * will block until all the bytes are written.
+ * @param buf the data to be written
+ * @param off the start offset of the data
+ * @param len the length of the data
+ * @exception IOException If an I/O error has occurred.
+ */
+ public synchronized void write(byte[] buf, int off, int len)
+ throws IOException
+ {
+ super.write(buf, off, len);
+ crc.update(buf, off, len);
+ }
+
+ /**
+ * Finishes writing compressed data to the output stream without closing
+ * the underlying stream. Use this method when applying multiple filters
+ * in succession to the same output stream.
+ * @exception IOException if an I/O error has occurred
+ */
+ public void finish() throws IOException {
+ if (!def.finished()) {
+ def.finish();
+ while (!def.finished()) {
+ int len = def.deflate(buf, 0, buf.length);
+ if (def.finished() && len <= buf.length - TRAILER_SIZE) {
+ // last deflater buffer. Fit trailer at the end
+ writeTrailer(buf, len);
+ len = len + TRAILER_SIZE;
+ out.write(buf, 0, len);
+ return;
+ }
+ if (len > 0)
+ out.write(buf, 0, len);
+ }
+ // if we can't fit the trailer at the end of the last
+ // deflater buffer, we write it separately
+ byte[] trailer = new byte[TRAILER_SIZE];
+ writeTrailer(trailer, 0);
+ out.write(trailer);
+ }
+ }
+
+ /*
+ * Writes GZIP member header.
+ */
+ private void writeHeader() throws IOException {
+ out.write(new byte[] {
+ (byte) GZIP_MAGIC, // Magic number (short)
+ (byte)(GZIP_MAGIC >> 8), // Magic number (short)
+ Deflater.DEFLATED, // Compression method (CM)
+ 0, // Flags (FLG)
+ 0, // Modification time MTIME (int)
+ 0, // Modification time MTIME (int)
+ 0, // Modification time MTIME (int)
+ 0, // Modification time MTIME (int)
+ 0, // Extra flags (XFLG)
+ 0 // Operating system (OS)
+ });
+ }
+
+ /*
+ * Writes GZIP member trailer to a byte array, starting at a given
+ * offset.
+ */
+ private void writeTrailer(byte[] buf, int offset) throws IOException {
+ writeInt((int)crc.getValue(), buf, offset); // CRC-32 of uncompr. data
+ writeInt(def.getTotalIn(), buf, offset + 4); // Number of uncompr. bytes
+ }
+
+ /*
+ * Writes integer in Intel byte order to a byte array, starting at a
+ * given offset.
+ */
+ private void writeInt(int i, byte[] buf, int offset) throws IOException {
+ writeShort(i & 0xffff, buf, offset);
+ writeShort((i >> 16) & 0xffff, buf, offset + 2);
+ }
+
+ /*
+ * Writes short integer in Intel byte order to a byte array, starting
+ * at a given offset
+ */
+ private void writeShort(int s, byte[] buf, int offset) throws IOException {
+ buf[offset] = (byte)(s & 0xff);
+ buf[offset + 1] = (byte)((s >> 8) & 0xff);
+ }
+}
diff --git a/java/util/zip/Inflater.java b/java/util/zip/Inflater.java
new file mode 100644
index 0000000..eb8754e
--- /dev/null
+++ b/java/util/zip/Inflater.java
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1996, 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import dalvik.annotation.optimization.ReachabilitySensitive;
+import dalvik.system.CloseGuard;
+
+/**
+ * This class provides support for general purpose decompression using the
+ * popular ZLIB compression library. The ZLIB compression library was
+ * initially developed as part of the PNG graphics standard and is not
+ * protected by patents. It is fully described in the specifications at
+ * the <a href="package-summary.html#package_description">java.util.zip
+ * package description</a>.
+ *
+ * <p>The following code fragment demonstrates a trivial compression
+ * and decompression of a string using <tt>Deflater</tt> and
+ * <tt>Inflater</tt>.
+ *
+ * <blockquote><pre>
+ * try {
+ * // Encode a String into bytes
+ * String inputString = "blahblahblah\u20AC\u20AC";
+ * byte[] input = inputString.getBytes("UTF-8");
+ *
+ * // Compress the bytes
+ * byte[] output = new byte[100];
+ * Deflater compresser = new Deflater();
+ * compresser.setInput(input);
+ * compresser.finish();
+ * int compressedDataLength = compresser.deflate(output);
+ *
+ * // Decompress the bytes
+ * Inflater decompresser = new Inflater();
+ * decompresser.setInput(output, 0, compressedDataLength);
+ * byte[] result = new byte[100];
+ * int resultLength = decompresser.inflate(result);
+ * decompresser.end();
+ *
+ * // Decode the bytes into a String
+ * String outputString = new String(result, 0, resultLength, "UTF-8");
+ * } catch(java.io.UnsupportedEncodingException ex) {
+ * // handle
+ * } catch (java.util.zip.DataFormatException ex) {
+ * // handle
+ * }
+ * </pre></blockquote>
+ *
+ * @see Deflater
+ * @author David Connelly
+ *
+ */
+public
+class Inflater {
+
+ // Android-added: @ReachabilitySensitive
+ // Finalization clears zsRef, and thus can't be allowed to occur early.
+ // Unlike some other CloseGuard uses, the spec allows clients to rely on finalization
+ // here. Thus dropping a deflater without calling end() should work correctly.
+ // It thus does not suffice to just rely on the CloseGuard annotation.
+ @ReachabilitySensitive
+ private final ZStreamRef zsRef;
+ private byte[] buf = defaultBuf;
+ private int off, len;
+ private boolean finished;
+ private boolean needDict;
+ private long bytesRead;
+ private long bytesWritten;
+
+ // Android-added: CloseGuard support.
+ @ReachabilitySensitive
+ private final CloseGuard guard = CloseGuard.get();
+
+ private static final byte[] defaultBuf = new byte[0];
+
+ // Android-removed: initIDs handled in register method.
+ /*
+ static {
+ /* Zip library is loaded from System.initializeSystemClass *
+ initIDs();
+ }
+ */
+
+ /**
+ * Creates a new decompressor. If the parameter 'nowrap' is true then
+ * the ZLIB header and checksum fields will not be used. This provides
+ * compatibility with the compression format used by both GZIP and PKZIP.
+ * <p>
+ * Note: When using the 'nowrap' option it is also necessary to provide
+ * an extra "dummy" byte as input. This is required by the ZLIB native
+ * library in order to support certain optimizations.
+ *
+ * @param nowrap if true then support GZIP compatible compression
+ */
+ public Inflater(boolean nowrap) {
+ zsRef = new ZStreamRef(init(nowrap));
+ // Android-added: CloseGuard support.
+ guard.open("end");
+ }
+
+ /**
+ * Creates a new decompressor.
+ */
+ public Inflater() {
+ this(false);
+ }
+
+ /**
+ * Sets input data for decompression. Should be called whenever
+ * needsInput() returns true indicating that more input data is
+ * required.
+ * @param b the input data bytes
+ * @param off the start offset of the input data
+ * @param len the length of the input data
+ * @see Inflater#needsInput
+ */
+ public void setInput(byte[] b, int off, int len) {
+ if (b == null) {
+ throw new NullPointerException();
+ }
+ if (off < 0 || len < 0 || off > b.length - len) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ synchronized (zsRef) {
+ this.buf = b;
+ this.off = off;
+ this.len = len;
+ }
+ }
+
+ /**
+ * Sets input data for decompression. Should be called whenever
+ * needsInput() returns true indicating that more input data is
+ * required.
+ * @param b the input data bytes
+ * @see Inflater#needsInput
+ */
+ public void setInput(byte[] b) {
+ setInput(b, 0, b.length);
+ }
+
+ /**
+ * Sets the preset dictionary to the given array of bytes. Should be
+ * called when inflate() returns 0 and needsDictionary() returns true
+ * indicating that a preset dictionary is required. The method getAdler()
+ * can be used to get the Adler-32 value of the dictionary needed.
+ * @param b the dictionary data bytes
+ * @param off the start offset of the data
+ * @param len the length of the data
+ * @see Inflater#needsDictionary
+ * @see Inflater#getAdler
+ */
+ public void setDictionary(byte[] b, int off, int len) {
+ if (b == null) {
+ throw new NullPointerException();
+ }
+ if (off < 0 || len < 0 || off > b.length - len) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ synchronized (zsRef) {
+ ensureOpen();
+ setDictionary(zsRef.address(), b, off, len);
+ needDict = false;
+ }
+ }
+
+ /**
+ * Sets the preset dictionary to the given array of bytes. Should be
+ * called when inflate() returns 0 and needsDictionary() returns true
+ * indicating that a preset dictionary is required. The method getAdler()
+ * can be used to get the Adler-32 value of the dictionary needed.
+ * @param b the dictionary data bytes
+ * @see Inflater#needsDictionary
+ * @see Inflater#getAdler
+ */
+ public void setDictionary(byte[] b) {
+ setDictionary(b, 0, b.length);
+ }
+
+ /**
+ * Returns the total number of bytes remaining in the input buffer.
+ * This can be used to find out what bytes still remain in the input
+ * buffer after decompression has finished.
+ * @return the total number of bytes remaining in the input buffer
+ */
+ public int getRemaining() {
+ synchronized (zsRef) {
+ return len;
+ }
+ }
+
+ /**
+ * Returns true if no data remains in the input buffer. This can
+ * be used to determine if #setInput should be called in order
+ * to provide more input.
+ * @return true if no data remains in the input buffer
+ */
+ public boolean needsInput() {
+ synchronized (zsRef) {
+ return len <= 0;
+ }
+ }
+
+ /**
+ * Returns true if a preset dictionary is needed for decompression.
+ * @return true if a preset dictionary is needed for decompression
+ * @see Inflater#setDictionary
+ */
+ public boolean needsDictionary() {
+ synchronized (zsRef) {
+ return needDict;
+ }
+ }
+
+ /**
+ * Returns true if the end of the compressed data stream has been
+ * reached.
+ * @return true if the end of the compressed data stream has been
+ * reached
+ */
+ public boolean finished() {
+ synchronized (zsRef) {
+ return finished;
+ }
+ }
+
+ /**
+ * Uncompresses bytes into specified buffer. Returns actual number
+ * of bytes uncompressed. A return value of 0 indicates that
+ * needsInput() or needsDictionary() should be called in order to
+ * determine if more input data or a preset dictionary is required.
+ * In the latter case, getAdler() can be used to get the Adler-32
+ * value of the dictionary required.
+ * @param b the buffer for the uncompressed data
+ * @param off the start offset of the data
+ * @param len the maximum number of uncompressed bytes
+ * @return the actual number of uncompressed bytes
+ * @exception DataFormatException if the compressed data format is invalid
+ * @see Inflater#needsInput
+ * @see Inflater#needsDictionary
+ */
+ public int inflate(byte[] b, int off, int len)
+ throws DataFormatException
+ {
+ if (b == null) {
+ throw new NullPointerException();
+ }
+ if (off < 0 || len < 0 || off > b.length - len) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ synchronized (zsRef) {
+ ensureOpen();
+ int thisLen = this.len;
+ int n = inflateBytes(zsRef.address(), b, off, len);
+ bytesWritten += n;
+ bytesRead += (thisLen - this.len);
+ return n;
+ }
+ }
+
+ /**
+ * Uncompresses bytes into specified buffer. Returns actual number
+ * of bytes uncompressed. A return value of 0 indicates that
+ * needsInput() or needsDictionary() should be called in order to
+ * determine if more input data or a preset dictionary is required.
+ * In the latter case, getAdler() can be used to get the Adler-32
+ * value of the dictionary required.
+ * @param b the buffer for the uncompressed data
+ * @return the actual number of uncompressed bytes
+ * @exception DataFormatException if the compressed data format is invalid
+ * @see Inflater#needsInput
+ * @see Inflater#needsDictionary
+ */
+ public int inflate(byte[] b) throws DataFormatException {
+ return inflate(b, 0, b.length);
+ }
+
+ /**
+ * Returns the ADLER-32 value of the uncompressed data.
+ * @return the ADLER-32 value of the uncompressed data
+ */
+ public int getAdler() {
+ synchronized (zsRef) {
+ ensureOpen();
+ return getAdler(zsRef.address());
+ }
+ }
+
+ /**
+ * Returns the total number of compressed bytes input so far.
+ *
+ * <p>Since the number of bytes may be greater than
+ * Integer.MAX_VALUE, the {@link #getBytesRead()} method is now
+ * the preferred means of obtaining this information.</p>
+ *
+ * @return the total number of compressed bytes input so far
+ */
+ public int getTotalIn() {
+ return (int) getBytesRead();
+ }
+
+ /**
+ * Returns the total number of compressed bytes input so far.
+ *
+ * @return the total (non-negative) number of compressed bytes input so far
+ * @since 1.5
+ */
+ public long getBytesRead() {
+ synchronized (zsRef) {
+ ensureOpen();
+ return bytesRead;
+ }
+ }
+
+ /**
+ * Returns the total number of uncompressed bytes output so far.
+ *
+ * <p>Since the number of bytes may be greater than
+ * Integer.MAX_VALUE, the {@link #getBytesWritten()} method is now
+ * the preferred means of obtaining this information.</p>
+ *
+ * @return the total number of uncompressed bytes output so far
+ */
+ public int getTotalOut() {
+ return (int) getBytesWritten();
+ }
+
+ /**
+ * Returns the total number of uncompressed bytes output so far.
+ *
+ * @return the total (non-negative) number of uncompressed bytes output so far
+ * @since 1.5
+ */
+ public long getBytesWritten() {
+ synchronized (zsRef) {
+ ensureOpen();
+ return bytesWritten;
+ }
+ }
+
+ /**
+ * Resets inflater so that a new set of input data can be processed.
+ */
+ public void reset() {
+ synchronized (zsRef) {
+ ensureOpen();
+ reset(zsRef.address());
+ buf = defaultBuf;
+ finished = false;
+ needDict = false;
+ off = len = 0;
+ bytesRead = bytesWritten = 0;
+ }
+ }
+
+ /**
+ * Closes the decompressor and discards any unprocessed input.
+ * This method should be called when the decompressor is no longer
+ * being used, but will also be called automatically by the finalize()
+ * method. Once this method is called, the behavior of the Inflater
+ * object is undefined.
+ */
+ public void end() {
+ synchronized (zsRef) {
+ // Android-added: CloseGuard support.
+ guard.close();
+
+ long addr = zsRef.address();
+ zsRef.clear();
+ if (addr != 0) {
+ end(addr);
+ buf = null;
+ }
+ }
+ }
+
+ /**
+ * Closes the decompressor when garbage is collected.
+ */
+ protected void finalize() {
+ // Android-added: CloseGuard support.
+ if (guard != null) {
+ guard.warnIfOpen();
+ }
+
+ end();
+ }
+
+ private void ensureOpen () {
+ assert Thread.holdsLock(zsRef);
+ if (zsRef.address() == 0)
+ throw new NullPointerException("Inflater has been closed");
+ }
+
+ boolean ended() {
+ synchronized (zsRef) {
+ return zsRef.address() == 0;
+ }
+ }
+
+ // Android-changed: initIDs handled in register method.
+ // private native static void initIDs();
+ private native static long init(boolean nowrap);
+ private native static void setDictionary(long addr, byte[] b, int off,
+ int len);
+ private native int inflateBytes(long addr, byte[] b, int off, int len)
+ throws DataFormatException;
+ private native static int getAdler(long addr);
+ private native static void reset(long addr);
+ private native static void end(long addr);
+}
diff --git a/java/util/zip/InflaterInputStream.java b/java/util/zip/InflaterInputStream.java
new file mode 100644
index 0000000..b65adbe
--- /dev/null
+++ b/java/util/zip/InflaterInputStream.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1996, 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.FilterInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.EOFException;
+
+/**
+ * This class implements a stream filter for uncompressing data in the
+ * "deflate" compression format. It is also used as the basis for other
+ * decompression filters, such as GZIPInputStream.
+ *
+ * @see Inflater
+ * @author David Connelly
+ */
+public
+class InflaterInputStream extends FilterInputStream {
+ /**
+ * Decompressor for this stream.
+ */
+ protected Inflater inf;
+
+ /**
+ * Input buffer for decompression.
+ */
+ protected byte[] buf;
+
+ /**
+ * Length of input buffer.
+ */
+ protected int len;
+
+ // Android-changed: Make closed accessible to subclasses.
+ // This was made protected because it needed to be accessed by
+ // StrictJarFile.ZipInflaterInputStream. Unfortunately, it was not marked as @hide and so it
+ // inadvertently became part of the public API. It will be marked as @removed to remove it from
+ // the public API in a future release of Android. See http://b/111592689 for more information.
+ // private boolean closed = false;
+ /**
+ * Indicates whether the {@link #close()} method has been called, internal use only.
+ *
+ * @deprecated This field will be removed from a future version of Android and should not be
+ * used. Subclasses that access this field need to be modified to keep track of their own
+ * closed state by overriding close().
+ */
+ @Deprecated
+ protected boolean closed = false;
+
+ // this flag is set to true after EOF has reached
+ private boolean reachEOF = false;
+
+ /**
+ * Check to make sure that this stream has not been closed
+ */
+ private void ensureOpen() throws IOException {
+ if (closed) {
+ throw new IOException("Stream closed");
+ }
+ }
+
+
+ /**
+ * Creates a new input stream with the specified decompressor and
+ * buffer size.
+ * @param in the input stream
+ * @param inf the decompressor ("inflater")
+ * @param size the input buffer size
+ * @exception IllegalArgumentException if {@code size <= 0}
+ */
+ public InflaterInputStream(InputStream in, Inflater inf, int size) {
+ super(in);
+ if (in == null || inf == null) {
+ throw new NullPointerException();
+ } else if (size <= 0) {
+ throw new IllegalArgumentException("buffer size <= 0");
+ }
+ this.inf = inf;
+ buf = new byte[size];
+ }
+
+ /**
+ * Creates a new input stream with the specified decompressor and a
+ * default buffer size.
+ * @param in the input stream
+ * @param inf the decompressor ("inflater")
+ */
+ public InflaterInputStream(InputStream in, Inflater inf) {
+ this(in, inf, 512);
+ }
+
+ // Android-changed: Unconditionally close external inflaters (b/26462400)
+ // See http://b/111630946 for more details.
+ // boolean usesDefaultInflater = false;
+
+ /**
+ * Creates a new input stream with a default decompressor and buffer size.
+ * @param in the input stream
+ */
+ public InflaterInputStream(InputStream in) {
+ this(in, new Inflater());
+ // Android-changed: Unconditionally close external inflaters (b/26462400)
+ // usesDefaultInflater = true;
+ }
+
+ private byte[] singleByteBuf = new byte[1];
+
+ /**
+ * Reads a byte of uncompressed data. This method will block until
+ * enough input is available for decompression.
+ * @return the byte read, or -1 if end of compressed input is reached
+ * @exception IOException if an I/O error has occurred
+ */
+ public int read() throws IOException {
+ ensureOpen();
+ return read(singleByteBuf, 0, 1) == -1 ? -1 : Byte.toUnsignedInt(singleByteBuf[0]);
+ }
+
+ /**
+ * Reads uncompressed data into an array of bytes. If <code>len</code> is not
+ * zero, the method will block until some input can be decompressed; otherwise,
+ * no bytes are read and <code>0</code> is returned.
+ * @param b the buffer into which the data is read
+ * @param off the start offset in the destination array <code>b</code>
+ * @param len the maximum number of bytes read
+ * @return the actual number of bytes read, or -1 if the end of the
+ * compressed input is reached or a preset dictionary is needed
+ * @exception NullPointerException If <code>b</code> is <code>null</code>.
+ * @exception IndexOutOfBoundsException If <code>off</code> is negative,
+ * <code>len</code> is negative, or <code>len</code> is greater than
+ * <code>b.length - off</code>
+ * @exception ZipException if a ZIP format error has occurred
+ * @exception IOException if an I/O error has occurred
+ */
+ public int read(byte[] b, int off, int len) throws IOException {
+ ensureOpen();
+ if (b == null) {
+ throw new NullPointerException();
+ } else if (off < 0 || len < 0 || len > b.length - off) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0) {
+ return 0;
+ }
+ try {
+ int n;
+ while ((n = inf.inflate(b, off, len)) == 0) {
+ if (inf.finished() || inf.needsDictionary()) {
+ reachEOF = true;
+ return -1;
+ }
+ if (inf.needsInput()) {
+ fill();
+ }
+ }
+ return n;
+ } catch (DataFormatException e) {
+ String s = e.getMessage();
+ throw new ZipException(s != null ? s : "Invalid ZLIB data format");
+ }
+ }
+
+ /**
+ * Returns 0 after EOF has been reached, otherwise always return 1.
+ * <p>
+ * Programs should not count on this method to return the actual number
+ * of bytes that could be read without blocking.
+ *
+ * @return 1 before EOF and 0 after EOF.
+ * @exception IOException if an I/O error occurs.
+ *
+ */
+ public int available() throws IOException {
+ ensureOpen();
+ if (reachEOF) {
+ return 0;
+ // BEGIN Android-added: Return more accurate value from available().
+ // Integrates change http://hg.openjdk.java.net/jdk9/jdk9/jdk/rev/dbcf47bfb044 made as part
+ // of https://bugs.openjdk.java.net/browse/JDK-7031075.
+ } else if (inf.finished()) {
+ // the end of the compressed data stream has been reached
+ reachEOF = true;
+ return 0;
+ // END Android-added: Return more accurate value from available().
+ } else {
+ return 1;
+ }
+ }
+
+ private byte[] b = new byte[512];
+
+ /**
+ * Skips specified number of bytes of uncompressed data.
+ * @param n the number of bytes to skip
+ * @return the actual number of bytes skipped.
+ * @exception IOException if an I/O error has occurred
+ * @exception IllegalArgumentException if {@code n < 0}
+ */
+ public long skip(long n) throws IOException {
+ if (n < 0) {
+ throw new IllegalArgumentException("negative skip length");
+ }
+ ensureOpen();
+ int max = (int)Math.min(n, Integer.MAX_VALUE);
+ int total = 0;
+ while (total < max) {
+ int len = max - total;
+ if (len > b.length) {
+ len = b.length;
+ }
+ len = read(b, 0, len);
+ if (len == -1) {
+ reachEOF = true;
+ break;
+ }
+ total += len;
+ }
+ return total;
+ }
+
+ /**
+ * Closes this input stream and releases any system resources associated
+ * with the stream.
+ * @exception IOException if an I/O error has occurred
+ */
+ public void close() throws IOException {
+ if (!closed) {
+ // Android-changed: Unconditionally close external inflaters (b/26462400)
+ //if (usesDefaultInflater)
+ inf.end();
+ in.close();
+ closed = true;
+ }
+ }
+
+ /**
+ * Fills input buffer with more data to decompress.
+ * @exception IOException if an I/O error has occurred
+ */
+ protected void fill() throws IOException {
+ ensureOpen();
+ len = in.read(buf, 0, buf.length);
+ if (len == -1) {
+ throw new EOFException("Unexpected end of ZLIB input stream");
+ }
+ inf.setInput(buf, 0, len);
+ }
+
+ /**
+ * Tests if this input stream supports the <code>mark</code> and
+ * <code>reset</code> methods. The <code>markSupported</code>
+ * method of <code>InflaterInputStream</code> returns
+ * <code>false</code>.
+ *
+ * @return a <code>boolean</code> indicating if this stream type supports
+ * the <code>mark</code> and <code>reset</code> methods.
+ * @see java.io.InputStream#mark(int)
+ * @see java.io.InputStream#reset()
+ */
+ public boolean markSupported() {
+ return false;
+ }
+
+ /**
+ * Marks the current position in this input stream.
+ *
+ * <p> The <code>mark</code> method of <code>InflaterInputStream</code>
+ * does nothing.
+ *
+ * @param readlimit the maximum limit of bytes that can be read before
+ * the mark position becomes invalid.
+ * @see java.io.InputStream#reset()
+ */
+ public synchronized void mark(int readlimit) {
+ }
+
+ /**
+ * Repositions this stream to the position at the time the
+ * <code>mark</code> method was last called on this input stream.
+ *
+ * <p> The method <code>reset</code> for class
+ * <code>InflaterInputStream</code> does nothing except throw an
+ * <code>IOException</code>.
+ *
+ * @exception IOException if this method is invoked.
+ * @see java.io.InputStream#mark(int)
+ * @see java.io.IOException
+ */
+ public synchronized void reset() throws IOException {
+ throw new IOException("mark/reset not supported");
+ }
+}
diff --git a/java/util/zip/InflaterOutputStream.java b/java/util/zip/InflaterOutputStream.java
new file mode 100644
index 0000000..02900c7
--- /dev/null
+++ b/java/util/zip/InflaterOutputStream.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 2006, 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Implements an output stream filter for uncompressing data stored in the
+ * "deflate" compression format.
+ *
+ * @since 1.6
+ * @author David R Tribble ([email protected])
+ *
+ * @see InflaterInputStream
+ * @see DeflaterInputStream
+ * @see DeflaterOutputStream
+ */
+
+public class InflaterOutputStream extends FilterOutputStream {
+ /** Decompressor for this stream. */
+ protected final Inflater inf;
+
+ /** Output buffer for writing uncompressed data. */
+ protected final byte[] buf;
+
+ /** Temporary write buffer. */
+ private final byte[] wbuf = new byte[1];
+
+ /** Default decompressor is used. */
+ private boolean usesDefaultInflater = false;
+
+ /** true iff {@link #close()} has been called. */
+ private boolean closed = false;
+
+ /**
+ * Checks to make sure that this stream has not been closed.
+ */
+ private void ensureOpen() throws IOException {
+ if (closed) {
+ throw new IOException("Stream closed");
+ }
+ }
+
+ /**
+ * Creates a new output stream with a default decompressor and buffer
+ * size.
+ *
+ * @param out output stream to write the uncompressed data to
+ * @throws NullPointerException if {@code out} is null
+ */
+ public InflaterOutputStream(OutputStream out) {
+ this(out, new Inflater());
+ usesDefaultInflater = true;
+ }
+
+ /**
+ * Creates a new output stream with the specified decompressor and a
+ * default buffer size.
+ *
+ * @param out output stream to write the uncompressed data to
+ * @param infl decompressor ("inflater") for this stream
+ * @throws NullPointerException if {@code out} or {@code infl} is null
+ */
+ public InflaterOutputStream(OutputStream out, Inflater infl) {
+ this(out, infl, 512);
+ }
+
+ /**
+ * Creates a new output stream with the specified decompressor and
+ * buffer size.
+ *
+ * @param out output stream to write the uncompressed data to
+ * @param infl decompressor ("inflater") for this stream
+ * @param bufLen decompression buffer size
+ * @throws IllegalArgumentException if {@code bufLen <= 0}
+ * @throws NullPointerException if {@code out} or {@code infl} is null
+ */
+ public InflaterOutputStream(OutputStream out, Inflater infl, int bufLen) {
+ super(out);
+
+ // Sanity checks
+ if (out == null)
+ throw new NullPointerException("Null output");
+ if (infl == null)
+ throw new NullPointerException("Null inflater");
+ if (bufLen <= 0)
+ throw new IllegalArgumentException("Buffer size < 1");
+
+ // Initialize
+ inf = infl;
+ buf = new byte[bufLen];
+ }
+
+ /**
+ * Writes any remaining uncompressed data to the output stream and closes
+ * the underlying output stream.
+ *
+ * @throws IOException if an I/O error occurs
+ */
+ public void close() throws IOException {
+ if (!closed) {
+ // Complete the uncompressed output
+ try {
+ finish();
+ } finally {
+ out.close();
+ closed = true;
+ }
+ }
+ }
+
+ /**
+ * Flushes this output stream, forcing any pending buffered output bytes to be
+ * written.
+ *
+ * @throws IOException if an I/O error occurs or this stream is already
+ * closed
+ */
+ public void flush() throws IOException {
+ ensureOpen();
+
+ // Finish decompressing and writing pending output data
+ if (!inf.finished()) {
+ try {
+ while (!inf.finished() && !inf.needsInput()) {
+ int n;
+
+ // Decompress pending output data
+ n = inf.inflate(buf, 0, buf.length);
+ if (n < 1) {
+ break;
+ }
+
+ // Write the uncompressed output data block
+ out.write(buf, 0, n);
+ }
+ super.flush();
+ } catch (DataFormatException ex) {
+ // Improperly formatted compressed (ZIP) data
+ String msg = ex.getMessage();
+ if (msg == null) {
+ msg = "Invalid ZLIB data format";
+ }
+ throw new ZipException(msg);
+ }
+ }
+ }
+
+ /**
+ * Finishes writing uncompressed data to the output stream without closing
+ * the underlying stream. Use this method when applying multiple filters in
+ * succession to the same output stream.
+ *
+ * @throws IOException if an I/O error occurs or this stream is already
+ * closed
+ */
+ public void finish() throws IOException {
+ ensureOpen();
+
+ // Finish decompressing and writing pending output data
+ flush();
+ if (usesDefaultInflater) {
+ inf.end();
+ }
+ }
+
+ /**
+ * Writes a byte to the uncompressed output stream.
+ *
+ * @param b a single byte of compressed data to decompress and write to
+ * the output stream
+ * @throws IOException if an I/O error occurs or this stream is already
+ * closed
+ * @throws ZipException if a compression (ZIP) format error occurs
+ */
+ public void write(int b) throws IOException {
+ // Write a single byte of data
+ wbuf[0] = (byte) b;
+ write(wbuf, 0, 1);
+ }
+
+ /**
+ * Writes an array of bytes to the uncompressed output stream.
+ *
+ * @param b buffer containing compressed data to decompress and write to
+ * the output stream
+ * @param off starting offset of the compressed data within {@code b}
+ * @param len number of bytes to decompress from {@code b}
+ * @throws IndexOutOfBoundsException if {@code off < 0}, or if
+ * {@code len < 0}, or if {@code len > b.length - off}
+ * @throws IOException if an I/O error occurs or this stream is already
+ * closed
+ * @throws NullPointerException if {@code b} is null
+ * @throws ZipException if a compression (ZIP) format error occurs
+ */
+ public void write(byte[] b, int off, int len) throws IOException {
+ // Sanity checks
+ ensureOpen();
+ if (b == null) {
+ throw new NullPointerException("Null buffer for read");
+ } else if (off < 0 || len < 0 || len > b.length - off) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0) {
+ return;
+ }
+
+ // Write uncompressed data to the output stream
+ try {
+ for (;;) {
+ int n;
+
+ // Fill the decompressor buffer with output data
+ if (inf.needsInput()) {
+ int part;
+
+ if (len < 1) {
+ break;
+ }
+
+ part = (len < 512 ? len : 512);
+ inf.setInput(b, off, part);
+ off += part;
+ len -= part;
+ }
+
+ // Decompress and write blocks of output data
+ do {
+ n = inf.inflate(buf, 0, buf.length);
+ if (n > 0) {
+ out.write(buf, 0, n);
+ }
+ } while (n > 0);
+
+ // Check the decompressor
+ if (inf.finished()) {
+ break;
+ }
+ if (inf.needsDictionary()) {
+ throw new ZipException("ZLIB dictionary missing");
+ }
+ }
+ } catch (DataFormatException ex) {
+ // Improperly formatted compressed (ZIP) data
+ String msg = ex.getMessage();
+ if (msg == null) {
+ msg = "Invalid ZLIB data format";
+ }
+ throw new ZipException(msg);
+ }
+ }
+}
diff --git a/java/util/zip/ZStreamRef.java b/java/util/zip/ZStreamRef.java
new file mode 100644
index 0000000..09b2f30
--- /dev/null
+++ b/java/util/zip/ZStreamRef.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2009, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+/**
+ * A reference to the native zlib's z_stream structure.
+ */
+
+class ZStreamRef {
+
+ private volatile long address;
+ ZStreamRef (long address) {
+ this.address = address;
+ }
+
+ long address() {
+ return address;
+ }
+
+ void clear() {
+ address = 0;
+ }
+}
diff --git a/java/util/zip/ZipCoder.java b/java/util/zip/ZipCoder.java
new file mode 100644
index 0000000..b920b82
--- /dev/null
+++ b/java/util/zip/ZipCoder.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2009, 2011, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+import java.util.Arrays;
+import sun.nio.cs.ArrayDecoder;
+import sun.nio.cs.ArrayEncoder;
+
+/**
+ * Utility class for zipfile name and comment decoding and encoding
+ */
+
+final class ZipCoder {
+
+ String toString(byte[] ba, int length) {
+ CharsetDecoder cd = decoder().reset();
+ int len = (int)(length * cd.maxCharsPerByte());
+ char[] ca = new char[len];
+ if (len == 0)
+ return new String(ca);
+ // UTF-8 only for now. Other ArrayDeocder only handles
+ // CodingErrorAction.REPLACE mode. ZipCoder uses
+ // REPORT mode.
+ if (isUTF8 && cd instanceof ArrayDecoder) {
+ int clen = ((ArrayDecoder)cd).decode(ba, 0, length, ca);
+ if (clen == -1) // malformed
+ throw new IllegalArgumentException("MALFORMED");
+ return new String(ca, 0, clen);
+ }
+ ByteBuffer bb = ByteBuffer.wrap(ba, 0, length);
+ CharBuffer cb = CharBuffer.wrap(ca);
+ CoderResult cr = cd.decode(bb, cb, true);
+ if (!cr.isUnderflow())
+ throw new IllegalArgumentException(cr.toString());
+ cr = cd.flush(cb);
+ if (!cr.isUnderflow())
+ throw new IllegalArgumentException(cr.toString());
+ return new String(ca, 0, cb.position());
+ }
+
+ String toString(byte[] ba) {
+ return toString(ba, ba.length);
+ }
+
+ byte[] getBytes(String s) {
+ CharsetEncoder ce = encoder().reset();
+ char[] ca = s.toCharArray();
+ int len = (int)(ca.length * ce.maxBytesPerChar());
+ byte[] ba = new byte[len];
+ if (len == 0)
+ return ba;
+ // UTF-8 only for now. Other ArrayDeocder only handles
+ // CodingErrorAction.REPLACE mode.
+ if (isUTF8 && ce instanceof ArrayEncoder) {
+ int blen = ((ArrayEncoder)ce).encode(ca, 0, ca.length, ba);
+ if (blen == -1) // malformed
+ throw new IllegalArgumentException("MALFORMED");
+ return Arrays.copyOf(ba, blen);
+ }
+ ByteBuffer bb = ByteBuffer.wrap(ba);
+ CharBuffer cb = CharBuffer.wrap(ca);
+ CoderResult cr = ce.encode(cb, bb, true);
+ if (!cr.isUnderflow())
+ throw new IllegalArgumentException(cr.toString());
+ cr = ce.flush(bb);
+ if (!cr.isUnderflow())
+ throw new IllegalArgumentException(cr.toString());
+ if (bb.position() == ba.length) // defensive copy?
+ return ba;
+ else
+ return Arrays.copyOf(ba, bb.position());
+ }
+
+ // assume invoked only if "this" is not utf8
+ byte[] getBytesUTF8(String s) {
+ if (isUTF8)
+ return getBytes(s);
+ if (utf8 == null)
+ utf8 = new ZipCoder(StandardCharsets.UTF_8);
+ return utf8.getBytes(s);
+ }
+
+
+ String toStringUTF8(byte[] ba, int len) {
+ if (isUTF8)
+ return toString(ba, len);
+ if (utf8 == null)
+ utf8 = new ZipCoder(StandardCharsets.UTF_8);
+ return utf8.toString(ba, len);
+ }
+
+ boolean isUTF8() {
+ return isUTF8;
+ }
+
+ private Charset cs;
+ private CharsetDecoder dec;
+ private CharsetEncoder enc;
+ private boolean isUTF8;
+ private ZipCoder utf8;
+
+ private ZipCoder(Charset cs) {
+ this.cs = cs;
+ this.isUTF8 = cs.name().equals(StandardCharsets.UTF_8.name());
+ }
+
+ static ZipCoder get(Charset charset) {
+ return new ZipCoder(charset);
+ }
+
+ private CharsetDecoder decoder() {
+ if (dec == null) {
+ dec = cs.newDecoder()
+ .onMalformedInput(CodingErrorAction.REPORT)
+ .onUnmappableCharacter(CodingErrorAction.REPORT);
+ }
+ return dec;
+ }
+
+ private CharsetEncoder encoder() {
+ if (enc == null) {
+ enc = cs.newEncoder()
+ .onMalformedInput(CodingErrorAction.REPORT)
+ .onUnmappableCharacter(CodingErrorAction.REPORT);
+ }
+ return enc;
+ }
+}
diff --git a/java/util/zip/ZipConstants.java b/java/util/zip/ZipConstants.java
new file mode 100644
index 0000000..db7f500
--- /dev/null
+++ b/java/util/zip/ZipConstants.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 1995, 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+/*
+ * This interface defines the constants that are used by the classes
+ * which manipulate ZIP files.
+ *
+ * @author David Connelly
+ */
+interface ZipConstants {
+ /*
+ * Header signatures
+ */
+ static long LOCSIG = 0x04034b50L; // "PK\003\004"
+ static long EXTSIG = 0x08074b50L; // "PK\007\008"
+ static long CENSIG = 0x02014b50L; // "PK\001\002"
+ static long ENDSIG = 0x06054b50L; // "PK\005\006"
+
+ /*
+ * Header sizes in bytes (including signatures)
+ */
+ static final int LOCHDR = 30; // LOC header size
+ static final int EXTHDR = 16; // EXT header size
+ static final int CENHDR = 46; // CEN header size
+ static final int ENDHDR = 22; // END header size
+
+ /*
+ * Local file (LOC) header field offsets
+ */
+ static final int LOCVER = 4; // version needed to extract
+ static final int LOCFLG = 6; // general purpose bit flag
+ static final int LOCHOW = 8; // compression method
+ static final int LOCTIM = 10; // modification time
+ static final int LOCCRC = 14; // uncompressed file crc-32 value
+ static final int LOCSIZ = 18; // compressed size
+ static final int LOCLEN = 22; // uncompressed size
+ static final int LOCNAM = 26; // filename length
+ static final int LOCEXT = 28; // extra field length
+
+ /*
+ * Extra local (EXT) header field offsets
+ */
+ static final int EXTCRC = 4; // uncompressed file crc-32 value
+ static final int EXTSIZ = 8; // compressed size
+ static final int EXTLEN = 12; // uncompressed size
+
+ /*
+ * Central directory (CEN) header field offsets
+ */
+ static final int CENVEM = 4; // version made by
+ static final int CENVER = 6; // version needed to extract
+ static final int CENFLG = 8; // encrypt, decrypt flags
+ static final int CENHOW = 10; // compression method
+ static final int CENTIM = 12; // modification time
+ static final int CENCRC = 16; // uncompressed file crc-32 value
+ static final int CENSIZ = 20; // compressed size
+ static final int CENLEN = 24; // uncompressed size
+ static final int CENNAM = 28; // filename length
+ static final int CENEXT = 30; // extra field length
+ static final int CENCOM = 32; // comment length
+ static final int CENDSK = 34; // disk number start
+ static final int CENATT = 36; // internal file attributes
+ static final int CENATX = 38; // external file attributes
+ static final int CENOFF = 42; // LOC header offset
+
+ /*
+ * End of central directory (END) header field offsets
+ */
+ static final int ENDSUB = 8; // number of entries on this disk
+ static final int ENDTOT = 10; // total number of entries
+ static final int ENDSIZ = 12; // central directory size in bytes
+ static final int ENDOFF = 16; // offset of first CEN header
+ static final int ENDCOM = 20; // zip file comment length
+}
diff --git a/java/util/zip/ZipConstants64.java b/java/util/zip/ZipConstants64.java
new file mode 100644
index 0000000..5d45f77
--- /dev/null
+++ b/java/util/zip/ZipConstants64.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 1995, 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+/*
+ * This class defines the constants that are used by the classes
+ * which manipulate Zip64 files.
+ */
+
+class ZipConstants64 {
+
+ /*
+ * ZIP64 constants
+ */
+ static final long ZIP64_ENDSIG = 0x06064b50L; // "PK\006\006"
+ static final long ZIP64_LOCSIG = 0x07064b50L; // "PK\006\007"
+ static final int ZIP64_ENDHDR = 56; // ZIP64 end header size
+ static final int ZIP64_LOCHDR = 20; // ZIP64 end loc header size
+ static final int ZIP64_EXTHDR = 24; // EXT header size
+ static final int ZIP64_EXTID = 0x0001; // Extra field Zip64 header ID
+
+ static final int ZIP64_MAGICCOUNT = 0xFFFF;
+ static final long ZIP64_MAGICVAL = 0xFFFFFFFFL;
+
+ /*
+ * Zip64 End of central directory (END) header field offsets
+ */
+ static final int ZIP64_ENDLEN = 4; // size of zip64 end of central dir
+ static final int ZIP64_ENDVEM = 12; // version made by
+ static final int ZIP64_ENDVER = 14; // version needed to extract
+ static final int ZIP64_ENDNMD = 16; // number of this disk
+ static final int ZIP64_ENDDSK = 20; // disk number of start
+ static final int ZIP64_ENDTOD = 24; // total number of entries on this disk
+ static final int ZIP64_ENDTOT = 32; // total number of entries
+ static final int ZIP64_ENDSIZ = 40; // central directory size in bytes
+ static final int ZIP64_ENDOFF = 48; // offset of first CEN header
+ static final int ZIP64_ENDEXT = 56; // zip64 extensible data sector
+
+ /*
+ * Zip64 End of central directory locator field offsets
+ */
+ static final int ZIP64_LOCDSK = 4; // disk number start
+ static final int ZIP64_LOCOFF = 8; // offset of zip64 end
+ static final int ZIP64_LOCTOT = 16; // total number of disks
+
+ /*
+ * Zip64 Extra local (EXT) header field offsets
+ */
+ static final int ZIP64_EXTCRC = 4; // uncompressed file crc-32 value
+ static final int ZIP64_EXTSIZ = 8; // compressed size, 8-byte
+ static final int ZIP64_EXTLEN = 16; // uncompressed size, 8-byte
+
+ /*
+ * Language encoding flag EFS
+ */
+ static final int EFS = 0x800; // If this bit is set the filename and
+ // comment fields for this file must be
+ // encoded using UTF-8.
+
+ /*
+ * Constants below are defined here (instead of in ZipConstants)
+ * to avoid being exposed as public fields of ZipFile, ZipEntry,
+ * ZipInputStream and ZipOutputstream.
+ */
+
+ /*
+ * Extra field header ID
+ */
+ static final int EXTID_ZIP64 = 0x0001; // Zip64
+ static final int EXTID_NTFS = 0x000a; // NTFS
+ static final int EXTID_UNIX = 0x000d; // UNIX
+ static final int EXTID_EXTT = 0x5455; // Info-ZIP Extended Timestamp
+
+ /*
+ * EXTT timestamp flags
+ */
+ static final int EXTT_FLAG_LMT = 0x1; // LastModifiedTime
+ static final int EXTT_FLAG_LAT = 0x2; // LastAccessTime
+ static final int EXTT_FLAT_CT = 0x4; // CreationTime
+
+ private ZipConstants64() {}
+}
diff --git a/java/util/zip/ZipEntry.annotated.java b/java/util/zip/ZipEntry.annotated.java
new file mode 100644
index 0000000..66b3616
--- /dev/null
+++ b/java/util/zip/ZipEntry.annotated.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+package java.util.zip;
+
+import java.nio.file.attribute.FileTime;
+
+@SuppressWarnings({"unchecked", "deprecation", "all"})
+public class ZipEntry implements java.lang.Cloneable {
+
+public ZipEntry(java.lang.String name, java.lang.String comment, long crc, long compressedSize, long size, int compressionMethod, int xdostime, byte[] extra, long dataOffset) { throw new RuntimeException("Stub!"); }
+
+public ZipEntry(java.lang.String name) { throw new RuntimeException("Stub!"); }
+
+public ZipEntry(java.util.zip.ZipEntry e) { throw new RuntimeException("Stub!"); }
+
[email protected]
+public long getDataOffset() { throw new RuntimeException("Stub!"); }
+
+public java.lang.String getName() { throw new RuntimeException("Stub!"); }
+
+public void setTime(long time) { throw new RuntimeException("Stub!"); }
+
+public long getTime() { throw new RuntimeException("Stub!"); }
+
+public java.util.zip.ZipEntry setLastModifiedTime(java.nio.file.attribute.FileTime time) { throw new RuntimeException("Stub!"); }
+
+public java.nio.file.attribute.FileTime getLastModifiedTime() { throw new RuntimeException("Stub!"); }
+
+public java.util.zip.ZipEntry setLastAccessTime(java.nio.file.attribute.FileTime time) { throw new RuntimeException("Stub!"); }
+
+public java.nio.file.attribute.FileTime getLastAccessTime() { throw new RuntimeException("Stub!"); }
+
+public java.util.zip.ZipEntry setCreationTime(java.nio.file.attribute.FileTime time) { throw new RuntimeException("Stub!"); }
+
+public java.nio.file.attribute.FileTime getCreationTime() { throw new RuntimeException("Stub!"); }
+
+public void setSize(long size) { throw new RuntimeException("Stub!"); }
+
+public long getSize() { throw new RuntimeException("Stub!"); }
+
+public long getCompressedSize() { throw new RuntimeException("Stub!"); }
+
+public void setCompressedSize(long csize) { throw new RuntimeException("Stub!"); }
+
+public void setCrc(long crc) { throw new RuntimeException("Stub!"); }
+
+public long getCrc() { throw new RuntimeException("Stub!"); }
+
+public void setMethod(int method) { throw new RuntimeException("Stub!"); }
+
+public int getMethod() { throw new RuntimeException("Stub!"); }
+
+public void setExtra(byte[] extra) { throw new RuntimeException("Stub!"); }
+
+public byte[] getExtra() { throw new RuntimeException("Stub!"); }
+
+public void setComment(java.lang.String comment) { throw new RuntimeException("Stub!"); }
+
+public java.lang.String getComment() { throw new RuntimeException("Stub!"); }
+
+public boolean isDirectory() { throw new RuntimeException("Stub!"); }
+
+public java.lang.String toString() { throw new RuntimeException("Stub!"); }
+
+public int hashCode() { throw new RuntimeException("Stub!"); }
+
+public java.lang.Object clone() { throw new RuntimeException("Stub!"); }
+
+public static final int CENATT = 36; // 0x24
+
+public static final int CENATX = 38; // 0x26
+
+public static final int CENCOM = 32; // 0x20
+
+public static final int CENCRC = 16; // 0x10
+
+public static final int CENDSK = 34; // 0x22
+
+public static final int CENEXT = 30; // 0x1e
+
+public static final int CENFLG = 8; // 0x8
+
+public static final int CENHDR = 46; // 0x2e
+
+public static final int CENHOW = 10; // 0xa
+
+public static final int CENLEN = 24; // 0x18
+
+public static final int CENNAM = 28; // 0x1c
+
+public static final int CENOFF = 42; // 0x2a
+
+public static final long CENSIG = 33639248L; // 0x2014b50L
+
+public static final int CENSIZ = 20; // 0x14
+
+public static final int CENTIM = 12; // 0xc
+
+public static final int CENVEM = 4; // 0x4
+
+public static final int CENVER = 6; // 0x6
+
+public static final int DEFLATED = 8; // 0x8
+
+public static final int ENDCOM = 20; // 0x14
+
+public static final int ENDHDR = 22; // 0x16
+
+public static final int ENDOFF = 16; // 0x10
+
+public static final long ENDSIG = 101010256L; // 0x6054b50L
+
+public static final int ENDSIZ = 12; // 0xc
+
+public static final int ENDSUB = 8; // 0x8
+
+public static final int ENDTOT = 10; // 0xa
+
+public static final int EXTCRC = 4; // 0x4
+
+public static final int EXTHDR = 16; // 0x10
+
+public static final int EXTLEN = 12; // 0xc
+
+public static final long EXTSIG = 134695760L; // 0x8074b50L
+
+public static final int EXTSIZ = 8; // 0x8
+
+public static final int LOCCRC = 14; // 0xe
+
+public static final int LOCEXT = 28; // 0x1c
+
+public static final int LOCFLG = 6; // 0x6
+
+public static final int LOCHDR = 30; // 0x1e
+
+public static final int LOCHOW = 8; // 0x8
+
+public static final int LOCLEN = 22; // 0x16
+
+public static final int LOCNAM = 26; // 0x1a
+
+public static final long LOCSIG = 67324752L; // 0x4034b50L
+
+public static final int LOCSIZ = 18; // 0x12
+
+public static final int LOCTIM = 10; // 0xa
+
+public static final int LOCVER = 4; // 0x4
+
+public static final int STORED = 0; // 0x0
+
+public static final long UPPER_DOSTIME_BOUND = 4036608000000L; // 0x3abd8960000L
+}
+
diff --git a/java/util/zip/ZipEntry.java b/java/util/zip/ZipEntry.java
new file mode 100644
index 0000000..0de6756
--- /dev/null
+++ b/java/util/zip/ZipEntry.java
@@ -0,0 +1,636 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import static java.util.zip.ZipUtils.*;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.attribute.FileTime;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+import static java.util.zip.ZipConstants64.*;
+
+/**
+ * This class is used to represent a ZIP file entry.
+ *
+ * @author David Connelly
+ */
+public
+class ZipEntry implements ZipConstants, Cloneable {
+ String name; // entry name
+ long xdostime = -1; // last modification time (in extended DOS time,
+ // where milliseconds lost in conversion might
+ // be encoded into the upper half)
+ FileTime mtime; // last modification time, from extra field data
+ FileTime atime; // last access time, from extra field data
+ FileTime ctime; // creation time, from extra field data
+ long crc = -1; // crc-32 of entry data
+ long size = -1; // uncompressed size of entry data
+ long csize = -1; // compressed size of entry data
+ int method = -1; // compression method
+ int flag = 0; // general purpose flag
+ byte[] extra; // optional extra field data for entry
+ String comment; // optional comment string for entry
+ // Android-added: Add dataOffset for internal use.
+ // Used by android.util.jar.StrictJarFile from frameworks.
+ long dataOffset;
+
+ /**
+ * Compression method for uncompressed entries.
+ */
+ public static final int STORED = 0;
+
+ /**
+ * Compression method for compressed (deflated) entries.
+ */
+ public static final int DEFLATED = 8;
+
+ /**
+ * DOS time constant for representing timestamps before 1980.
+ */
+ static final long DOSTIME_BEFORE_1980 = (1 << 21) | (1 << 16);
+
+ /**
+ * Approximately 128 years, in milliseconds (ignoring leap years etc).
+ *
+ * This establish an approximate high-bound value for DOS times in
+ * milliseconds since epoch, used to enable an efficient but
+ * sufficient bounds check to avoid generating extended last modified
+ * time entries.
+ *
+ * Calculating the exact number is locale dependent, would require loading
+ * TimeZone data eagerly, and would make little practical sense. Since DOS
+ * times theoretically go to 2107 - with compatibility not guaranteed
+ * after 2099 - setting this to a time that is before but near 2099
+ * should be sufficient.
+ * @hide
+ */
+ // Android-changed: Make UPPER_DOSTIME_BOUND public hidden for testing purposes.
+ public static final long UPPER_DOSTIME_BOUND =
+ 128L * 365 * 24 * 60 * 60 * 1000;
+
+ // Android-added: New constructor for use by StrictJarFile native code.
+ /** @hide */
+ public ZipEntry(String name, String comment, long crc, long compressedSize,
+ long size, int compressionMethod, int xdostime, byte[] extra,
+ long dataOffset) {
+ this.name = name;
+ this.comment = comment;
+ this.crc = crc;
+ this.csize = compressedSize;
+ this.size = size;
+ this.method = compressionMethod;
+ this.xdostime = xdostime;
+ this.dataOffset = dataOffset;
+ this.setExtra0(extra, false);
+ }
+
+ /**
+ * Creates a new zip entry with the specified name.
+ *
+ * @param name
+ * The entry name
+ *
+ * @throws NullPointerException if the entry name is null
+ * @throws IllegalArgumentException if the entry name is longer than
+ * 0xFFFF bytes
+ */
+ public ZipEntry(String name) {
+ Objects.requireNonNull(name, "name");
+ // Android-changed: Explicitly use UTF_8 instead of the default charset.
+ // if (name.length() > 0xFFFF) {
+ // throw new IllegalArgumentException("entry name too long");
+ // }
+ if (name.getBytes(StandardCharsets.UTF_8).length > 0xffff) {
+ throw new IllegalArgumentException(name + " too long: " +
+ name.getBytes(StandardCharsets.UTF_8).length);
+ }
+ this.name = name;
+ }
+
+ /**
+ * Creates a new zip entry with fields taken from the specified
+ * zip entry.
+ *
+ * @param e
+ * A zip Entry object
+ *
+ * @throws NullPointerException if the entry object is null
+ */
+ public ZipEntry(ZipEntry e) {
+ Objects.requireNonNull(e, "entry");
+ name = e.name;
+ xdostime = e.xdostime;
+ mtime = e.mtime;
+ atime = e.atime;
+ ctime = e.ctime;
+ crc = e.crc;
+ size = e.size;
+ csize = e.csize;
+ method = e.method;
+ flag = e.flag;
+ extra = e.extra;
+ comment = e.comment;
+ // Android-added: Add dataOffset for internal use.
+ dataOffset = e.dataOffset;
+ }
+
+ /**
+ * Creates a new un-initialized zip entry
+ */
+ ZipEntry() {}
+
+ // Android-added: Add dataOffset for internal use.
+ /** @hide */
+ public long getDataOffset() {
+ return dataOffset;
+ }
+
+ /**
+ * Returns the name of the entry.
+ * @return the name of the entry
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the last modification time of the entry.
+ *
+ * <p> If the entry is output to a ZIP file or ZIP file formatted
+ * output stream the last modification time set by this method will
+ * be stored into the {@code date and time fields} of the zip file
+ * entry and encoded in standard {@code MS-DOS date and time format}.
+ * The {@link java.util.TimeZone#getDefault() default TimeZone} is
+ * used to convert the epoch time to the MS-DOS data and time.
+ *
+ * @param time
+ * The last modification time of the entry in milliseconds
+ * since the epoch
+ *
+ * @see #getTime()
+ * @see #getLastModifiedTime()
+ */
+ public void setTime(long time) {
+ this.xdostime = javaToExtendedDosTime(time);
+ // Avoid setting the mtime field if time is in the valid
+ // range for a DOS time
+ if (xdostime != DOSTIME_BEFORE_1980 && time <= UPPER_DOSTIME_BOUND) {
+ this.mtime = null;
+ } else {
+ this.mtime = FileTime.from(time, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ /**
+ * Returns the last modification time of the entry.
+ *
+ * <p> If the entry is read from a ZIP file or ZIP file formatted
+ * input stream, this is the last modification time from the {@code
+ * date and time fields} of the zip file entry. The
+ * {@link java.util.TimeZone#getDefault() default TimeZone} is used
+ * to convert the standard MS-DOS formatted date and time to the
+ * epoch time.
+ *
+ * @return The last modification time of the entry in milliseconds
+ * since the epoch, or -1 if not specified
+ *
+ * @see #setTime(long)
+ * @see #setLastModifiedTime(FileTime)
+ */
+ public long getTime() {
+ if (mtime != null) {
+ return mtime.toMillis();
+ }
+ return (xdostime != -1) ? extendedDosToJavaTime(xdostime) : -1;
+ }
+
+ /**
+ * Sets the last modification time of the entry.
+ *
+ * <p> When output to a ZIP file or ZIP file formatted output stream
+ * the last modification time set by this method will be stored into
+ * zip file entry's {@code date and time fields} in {@code standard
+ * MS-DOS date and time format}), and the extended timestamp fields
+ * in {@code optional extra data} in UTC time.
+ *
+ * @param time
+ * The last modification time of the entry
+ * @return This zip entry
+ *
+ * @throws NullPointerException if the {@code time} is null
+ *
+ * @see #getLastModifiedTime()
+ * @since 1.8
+ */
+ public ZipEntry setLastModifiedTime(FileTime time) {
+ this.mtime = Objects.requireNonNull(time, "lastModifiedTime");
+ this.xdostime = javaToExtendedDosTime(time.to(TimeUnit.MILLISECONDS));
+ return this;
+ }
+
+ /**
+ * Returns the last modification time of the entry.
+ *
+ * <p> If the entry is read from a ZIP file or ZIP file formatted
+ * input stream, this is the last modification time from the zip
+ * file entry's {@code optional extra data} if the extended timestamp
+ * fields are present. Otherwise the last modification time is read
+ * from the entry's {@code date and time fields}, the {@link
+ * java.util.TimeZone#getDefault() default TimeZone} is used to convert
+ * the standard MS-DOS formatted date and time to the epoch time.
+ *
+ * @return The last modification time of the entry, null if not specified
+ *
+ * @see #setLastModifiedTime(FileTime)
+ * @since 1.8
+ */
+ public FileTime getLastModifiedTime() {
+ if (mtime != null)
+ return mtime;
+ if (xdostime == -1)
+ return null;
+ return FileTime.from(getTime(), TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Sets the last access time of the entry.
+ *
+ * <p> If set, the last access time will be stored into the extended
+ * timestamp fields of entry's {@code optional extra data}, when output
+ * to a ZIP file or ZIP file formatted stream.
+ *
+ * @param time
+ * The last access time of the entry
+ * @return This zip entry
+ *
+ * @throws NullPointerException if the {@code time} is null
+ *
+ * @see #getLastAccessTime()
+ * @since 1.8
+ */
+ public ZipEntry setLastAccessTime(FileTime time) {
+ this.atime = Objects.requireNonNull(time, "lastAccessTime");
+ return this;
+ }
+
+ /**
+ * Returns the last access time of the entry.
+ *
+ * <p> The last access time is from the extended timestamp fields
+ * of entry's {@code optional extra data} when read from a ZIP file
+ * or ZIP file formatted stream.
+ *
+ * @return The last access time of the entry, null if not specified
+
+ * @see #setLastAccessTime(FileTime)
+ * @since 1.8
+ */
+ public FileTime getLastAccessTime() {
+ return atime;
+ }
+
+ /**
+ * Sets the creation time of the entry.
+ *
+ * <p> If set, the creation time will be stored into the extended
+ * timestamp fields of entry's {@code optional extra data}, when
+ * output to a ZIP file or ZIP file formatted stream.
+ *
+ * @param time
+ * The creation time of the entry
+ * @return This zip entry
+ *
+ * @throws NullPointerException if the {@code time} is null
+ *
+ * @see #getCreationTime()
+ * @since 1.8
+ */
+ public ZipEntry setCreationTime(FileTime time) {
+ this.ctime = Objects.requireNonNull(time, "creationTime");
+ return this;
+ }
+
+ /**
+ * Returns the creation time of the entry.
+ *
+ * <p> The creation time is from the extended timestamp fields of
+ * entry's {@code optional extra data} when read from a ZIP file
+ * or ZIP file formatted stream.
+ *
+ * @return the creation time of the entry, null if not specified
+ * @see #setCreationTime(FileTime)
+ * @since 1.8
+ */
+ public FileTime getCreationTime() {
+ return ctime;
+ }
+
+ /**
+ * Sets the uncompressed size of the entry data.
+ *
+ * @param size the uncompressed size in bytes
+ *
+ * @throws IllegalArgumentException if the specified size is less
+ * than 0, is greater than 0xFFFFFFFF when
+ * <a href="package-summary.html#zip64">ZIP64 format</a> is not supported,
+ * or is less than 0 when ZIP64 is supported
+ * @see #getSize()
+ */
+ public void setSize(long size) {
+ if (size < 0) {
+ throw new IllegalArgumentException("invalid entry size");
+ }
+ this.size = size;
+ }
+
+ /**
+ * Returns the uncompressed size of the entry data.
+ *
+ * @return the uncompressed size of the entry data, or -1 if not known
+ * @see #setSize(long)
+ */
+ public long getSize() {
+ return size;
+ }
+
+ /**
+ * Returns the size of the compressed entry data.
+ *
+ * <p> In the case of a stored entry, the compressed size will be the same
+ * as the uncompressed size of the entry.
+ *
+ * @return the size of the compressed entry data, or -1 if not known
+ * @see #setCompressedSize(long)
+ */
+ public long getCompressedSize() {
+ return csize;
+ }
+
+ /**
+ * Sets the size of the compressed entry data.
+ *
+ * @param csize the compressed size to set to
+ *
+ * @see #getCompressedSize()
+ */
+ public void setCompressedSize(long csize) {
+ this.csize = csize;
+ }
+
+ /**
+ * Sets the CRC-32 checksum of the uncompressed entry data.
+ *
+ * @param crc the CRC-32 value
+ *
+ * @throws IllegalArgumentException if the specified CRC-32 value is
+ * less than 0 or greater than 0xFFFFFFFF
+ * @see #getCrc()
+ */
+ public void setCrc(long crc) {
+ if (crc < 0 || crc > 0xFFFFFFFFL) {
+ throw new IllegalArgumentException("invalid entry crc-32");
+ }
+ this.crc = crc;
+ }
+
+ /**
+ * Returns the CRC-32 checksum of the uncompressed entry data.
+ *
+ * @return the CRC-32 checksum of the uncompressed entry data, or -1 if
+ * not known
+ *
+ * @see #setCrc(long)
+ */
+ public long getCrc() {
+ return crc;
+ }
+
+ /**
+ * Sets the compression method for the entry.
+ *
+ * @param method the compression method, either STORED or DEFLATED
+ *
+ * @throws IllegalArgumentException if the specified compression
+ * method is invalid
+ * @see #getMethod()
+ */
+ public void setMethod(int method) {
+ if (method != STORED && method != DEFLATED) {
+ throw new IllegalArgumentException("invalid compression method");
+ }
+ this.method = method;
+ }
+
+ /**
+ * Returns the compression method of the entry.
+ *
+ * @return the compression method of the entry, or -1 if not specified
+ * @see #setMethod(int)
+ */
+ public int getMethod() {
+ return method;
+ }
+
+ /**
+ * Sets the optional extra field data for the entry.
+ *
+ * <p> Invoking this method may change this entry's last modification
+ * time, last access time and creation time, if the {@code extra} field
+ * data includes the extensible timestamp fields, such as {@code NTFS tag
+ * 0x0001} or {@code Info-ZIP Extended Timestamp}, as specified in
+ * <a href="http://www.info-zip.org/doc/appnote-19970311-iz.zip">Info-ZIP
+ * Application Note 970311</a>.
+ *
+ * @param extra
+ * The extra field data bytes
+ *
+ * @throws IllegalArgumentException if the length of the specified
+ * extra field data is greater than 0xFFFF bytes
+ *
+ * @see #getExtra()
+ */
+ public void setExtra(byte[] extra) {
+ setExtra0(extra, false);
+ }
+
+ /**
+ * Sets the optional extra field data for the entry.
+ *
+ * @param extra
+ * the extra field data bytes
+ * @param doZIP64
+ * if true, set size and csize from ZIP64 fields if present
+ */
+ void setExtra0(byte[] extra, boolean doZIP64) {
+ if (extra != null) {
+ if (extra.length > 0xFFFF) {
+ throw new IllegalArgumentException("invalid extra field length");
+ }
+ // extra fields are in "HeaderID(2)DataSize(2)Data... format
+ int off = 0;
+ int len = extra.length;
+ while (off + 4 < len) {
+ int tag = get16(extra, off);
+ int sz = get16(extra, off + 2);
+ off += 4;
+ if (off + sz > len) // invalid data
+ break;
+ switch (tag) {
+ case EXTID_ZIP64:
+ if (doZIP64) {
+ // LOC extra zip64 entry MUST include BOTH original
+ // and compressed file size fields.
+ // If invalid zip64 extra fields, simply skip. Even
+ // it's rare, it's possible the entry size happens to
+ // be the magic value and it "accidently" has some
+ // bytes in extra match the id.
+ if (sz >= 16) {
+ size = get64(extra, off);
+ csize = get64(extra, off + 8);
+ }
+ }
+ break;
+ case EXTID_NTFS:
+ if (sz < 32) // reserved 4 bytes + tag 2 bytes + size 2 bytes
+ break; // m[a|c]time 24 bytes
+ int pos = off + 4; // reserved 4 bytes
+ if (get16(extra, pos) != 0x0001 || get16(extra, pos + 2) != 24)
+ break;
+ mtime = winTimeToFileTime(get64(extra, pos + 4));
+ atime = winTimeToFileTime(get64(extra, pos + 12));
+ ctime = winTimeToFileTime(get64(extra, pos + 20));
+ break;
+ case EXTID_EXTT:
+ int flag = Byte.toUnsignedInt(extra[off]);
+ int sz0 = 1;
+ // The CEN-header extra field contains the modification
+ // time only, or no timestamp at all. 'sz' is used to
+ // flag its presence or absence. But if mtime is present
+ // in LOC it must be present in CEN as well.
+ if ((flag & 0x1) != 0 && (sz0 + 4) <= sz) {
+ mtime = unixTimeToFileTime(get32(extra, off + sz0));
+ sz0 += 4;
+ }
+ if ((flag & 0x2) != 0 && (sz0 + 4) <= sz) {
+ atime = unixTimeToFileTime(get32(extra, off + sz0));
+ sz0 += 4;
+ }
+ if ((flag & 0x4) != 0 && (sz0 + 4) <= sz) {
+ ctime = unixTimeToFileTime(get32(extra, off + sz0));
+ sz0 += 4;
+ }
+ break;
+ default:
+ }
+ off += sz;
+ }
+ }
+ this.extra = extra;
+ }
+
+ /**
+ * Returns the extra field data for the entry.
+ *
+ * @return the extra field data for the entry, or null if none
+ *
+ * @see #setExtra(byte[])
+ */
+ public byte[] getExtra() {
+ return extra;
+ }
+
+ /**
+ * Sets the optional comment string for the entry.
+ *
+ * <p>ZIP entry comments have maximum length of 0xffff. If the length of the
+ * specified comment string is greater than 0xFFFF bytes after encoding, only
+ * the first 0xFFFF bytes are output to the ZIP file entry.
+ *
+ * @param comment the comment string
+ *
+ * @see #getComment()
+ */
+ public void setComment(String comment) {
+ // BEGIN Android-added: Explicitly use UTF_8 instead of the default charset.
+ if (comment != null && comment.getBytes(StandardCharsets.UTF_8).length > 0xffff) {
+ throw new IllegalArgumentException(comment + " too long: " +
+ comment.getBytes(StandardCharsets.UTF_8).length);
+ }
+ // END Android-added: Explicitly use UTF_8 instead of the default charset.
+
+ this.comment = comment;
+ }
+
+ /**
+ * Returns the comment string for the entry.
+ *
+ * @return the comment string for the entry, or null if none
+ *
+ * @see #setComment(String)
+ */
+ public String getComment() {
+ return comment;
+ }
+
+ /**
+ * Returns true if this is a directory entry. A directory entry is
+ * defined to be one whose name ends with a '/'.
+ * @return true if this is a directory entry
+ */
+ public boolean isDirectory() {
+ return name.endsWith("/");
+ }
+
+ /**
+ * Returns a string representation of the ZIP entry.
+ */
+ public String toString() {
+ return getName();
+ }
+
+ /**
+ * Returns the hash code value for this entry.
+ */
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ /**
+ * Returns a copy of this entry.
+ */
+ public Object clone() {
+ try {
+ ZipEntry e = (ZipEntry)super.clone();
+ e.extra = (extra == null) ? null : extra.clone();
+ return e;
+ } catch (CloneNotSupportedException e) {
+ // This should never happen, since we are Cloneable
+ throw new InternalError(e);
+ }
+ }
+}
diff --git a/java/util/zip/ZipError.java b/java/util/zip/ZipError.java
new file mode 100644
index 0000000..2799c8e
--- /dev/null
+++ b/java/util/zip/ZipError.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2006, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+/**
+ * Signals that an unrecoverable error has occurred.
+ *
+ * @author Dave Bristor
+ * @since 1.6
+ */
+public class ZipError extends InternalError {
+ private static final long serialVersionUID = 853973422266861979L;
+
+ /**
+ * Constructs a ZipError with the given detail message.
+ * @param s the {@code String} containing a detail message
+ */
+ public ZipError(String s) {
+ super(s);
+ }
+}
diff --git a/java/util/zip/ZipException.java b/java/util/zip/ZipException.java
new file mode 100644
index 0000000..4bcfe03
--- /dev/null
+++ b/java/util/zip/ZipException.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 1995, 2010, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.IOException;
+
+/**
+ * Signals that a Zip exception of some sort has occurred.
+ *
+ * @author unascribed
+ * @see java.io.IOException
+ * @since JDK1.0
+ */
+
+public
+class ZipException extends IOException {
+ private static final long serialVersionUID = 8000196834066748623L;
+
+ /**
+ * Constructs a <code>ZipException</code> with <code>null</code>
+ * as its error detail message.
+ */
+ public ZipException() {
+ super();
+ }
+
+ /**
+ * Constructs a <code>ZipException</code> with the specified detail
+ * message.
+ *
+ * @param s the detail message.
+ */
+
+ public ZipException(String s) {
+ super(s);
+ }
+}
diff --git a/java/util/zip/ZipFile.java b/java/util/zip/ZipFile.java
new file mode 100644
index 0000000..851aab1
--- /dev/null
+++ b/java/util/zip/ZipFile.java
@@ -0,0 +1,931 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.Closeable;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.WeakHashMap;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import dalvik.system.CloseGuard;
+
+import static java.util.zip.ZipConstants64.*;
+
+/**
+ * This class is used to read entries from a zip file.
+ *
+ * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
+ * or method in this class will cause a {@link NullPointerException} to be
+ * thrown.
+ *
+ * @author David Connelly
+ */
+public
+class ZipFile implements ZipConstants, Closeable {
+ // Android-note: jzfile does not require @ReachabilitySensitive annotation.
+ // The @ReachabilitySensitive annotation is usually added to instance fields that references
+ // native data that is cleaned up when the instance becomes unreachable. Its presence ensures
+ // that the instance object is not finalized until the field is no longer used. Without it an
+ // instance could be finalized during execution of an instance method iff that method's this
+ // variable holds the last reference to the instance and the method had copied all the fields
+ // it needs out of the instance. That would release the native data, invalidating its reference
+ // and would cause serious problems if the method had taken a copy of that field and
+ // then called a native method that would try to use it.
+ //
+ // This field does not require the annotation because all usages of this field are enclosed
+ // within a synchronized(this) block and finalizing of the object referenced in a synchronized
+ // block is not allowed as that would release its monitor that is currently in use.
+ private long jzfile; // address of jzfile data
+ private final String name; // zip file name
+ private final int total; // total number of entries
+ private final boolean locsig; // if zip file starts with LOCSIG (usually true)
+ private volatile boolean closeRequested = false;
+
+ // Android-added: CloseGuard support
+ private final CloseGuard guard = CloseGuard.get();
+
+ // Android-added: Do not use unlink() to implement OPEN_DELETE.
+ // Upstream uses unlink() to cause the file name to be removed from the filesystem after it is
+ // opened but that does not work on fuse fs as it causes problems with lseek. Android simply
+ // keeps a reference to the File so that it can explicitly delete it during close.
+ //
+ // OpenJDK 9+181 has a pure Java implementation of ZipFile that does not use unlink() and
+ // instead does something very similar to what Android does. If Android adopts it then this
+ // patch can be dropped.
+ // See http://b/28950284 and http://b/28901232 for more details.
+ private final File fileToRemoveOnClose;
+
+ private static final int STORED = ZipEntry.STORED;
+ private static final int DEFLATED = ZipEntry.DEFLATED;
+
+ /**
+ * Mode flag to open a zip file for reading.
+ */
+ public static final int OPEN_READ = 0x1;
+
+ /**
+ * Mode flag to open a zip file and mark it for deletion. The file will be
+ * deleted some time between the moment that it is opened and the moment
+ * that it is closed, but its contents will remain accessible via the
+ * <tt>ZipFile</tt> object until either the close method is invoked or the
+ * virtual machine exits.
+ */
+ public static final int OPEN_DELETE = 0x4;
+
+ // Android-removed: initIDs() not used on Android.
+ /*
+ static {
+ /* Zip library is loaded from System.initializeSystemClass *
+ initIDs();
+ }
+
+ private static native void initIDs();
+ */
+
+ private static final boolean usemmap;
+
+ static {
+ // Android-changed: Always use mmap.
+ /*
+ // A system prpperty to disable mmap use to avoid vm crash when
+ // in-use zip file is accidently overwritten by others.
+ String prop = sun.misc.VM.getSavedProperty("sun.zip.disableMemoryMapping");
+ usemmap = (prop == null ||
+ !(prop.length() == 0 || prop.equalsIgnoreCase("true")));
+ */
+ usemmap = true;
+ }
+
+ /**
+ * Opens a zip file for reading.
+ *
+ * <p>First, if there is a security manager, its <code>checkRead</code>
+ * method is called with the <code>name</code> argument as its argument
+ * to ensure the read is allowed.
+ *
+ * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
+ * decode the entry names and comments.
+ *
+ * @param name the name of the zip file
+ * @throws ZipException if a ZIP format error has occurred
+ * @throws IOException if an I/O error has occurred
+ * @throws SecurityException if a security manager exists and its
+ * <code>checkRead</code> method doesn't allow read access to the file.
+ *
+ * @see SecurityManager#checkRead(java.lang.String)
+ */
+ public ZipFile(String name) throws IOException {
+ this(new File(name), OPEN_READ);
+ }
+
+ /**
+ * Opens a new <code>ZipFile</code> to read from the specified
+ * <code>File</code> object in the specified mode. The mode argument
+ * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>.
+ *
+ * <p>First, if there is a security manager, its <code>checkRead</code>
+ * method is called with the <code>name</code> argument as its argument to
+ * ensure the read is allowed.
+ *
+ * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
+ * decode the entry names and comments
+ *
+ * @param file the ZIP file to be opened for reading
+ * @param mode the mode in which the file is to be opened
+ * @throws ZipException if a ZIP format error has occurred
+ * @throws IOException if an I/O error has occurred
+ * @throws SecurityException if a security manager exists and
+ * its <code>checkRead</code> method
+ * doesn't allow read access to the file,
+ * or its <code>checkDelete</code> method doesn't allow deleting
+ * the file when the <tt>OPEN_DELETE</tt> flag is set.
+ * @throws IllegalArgumentException if the <tt>mode</tt> argument is invalid
+ * @see SecurityManager#checkRead(java.lang.String)
+ * @since 1.3
+ */
+ public ZipFile(File file, int mode) throws IOException {
+ this(file, mode, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * Opens a ZIP file for reading given the specified File object.
+ *
+ * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
+ * decode the entry names and comments.
+ *
+ * @param file the ZIP file to be opened for reading
+ * @throws ZipException if a ZIP format error has occurred
+ * @throws IOException if an I/O error has occurred
+ */
+ public ZipFile(File file) throws ZipException, IOException {
+ this(file, OPEN_READ);
+ }
+
+ private ZipCoder zc;
+
+ /**
+ * Opens a new <code>ZipFile</code> to read from the specified
+ * <code>File</code> object in the specified mode. The mode argument
+ * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>.
+ *
+ * <p>First, if there is a security manager, its <code>checkRead</code>
+ * method is called with the <code>name</code> argument as its argument to
+ * ensure the read is allowed.
+ *
+ * @param file the ZIP file to be opened for reading
+ * @param mode the mode in which the file is to be opened
+ * @param charset
+ * the {@linkplain java.nio.charset.Charset charset} to
+ * be used to decode the ZIP entry name and comment that are not
+ * encoded by using UTF-8 encoding (indicated by entry's general
+ * purpose flag).
+ *
+ * @throws ZipException if a ZIP format error has occurred
+ * @throws IOException if an I/O error has occurred
+ *
+ * @throws SecurityException
+ * if a security manager exists and its <code>checkRead</code>
+ * method doesn't allow read access to the file,or its
+ * <code>checkDelete</code> method doesn't allow deleting the
+ * file when the <tt>OPEN_DELETE</tt> flag is set
+ *
+ * @throws IllegalArgumentException if the <tt>mode</tt> argument is invalid
+ *
+ * @see SecurityManager#checkRead(java.lang.String)
+ *
+ * @since 1.7
+ */
+ public ZipFile(File file, int mode, Charset charset) throws IOException
+ {
+ if (((mode & OPEN_READ) == 0) ||
+ ((mode & ~(OPEN_READ | OPEN_DELETE)) != 0)) {
+ throw new IllegalArgumentException("Illegal mode: 0x"+
+ Integer.toHexString(mode));
+ }
+ String name = file.getPath();
+ // Android-removed: SecurityManager is always null
+ /*
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkRead(name);
+ if ((mode & OPEN_DELETE) != 0) {
+ sm.checkDelete(name);
+ }
+ }
+ */
+
+ // Android-added: Do not use unlink() to implement OPEN_DELETE.
+ fileToRemoveOnClose = ((mode & OPEN_DELETE) != 0) ? file : null;
+
+ if (charset == null)
+ throw new NullPointerException("charset is null");
+ this.zc = ZipCoder.get(charset);
+ // Android-removed: Skip perf counters
+ // long t0 = System.nanoTime();
+ jzfile = open(name, mode, file.lastModified(), usemmap);
+ // Android-removed: Skip perf counters
+ // sun.misc.PerfCounter.getZipFileOpenTime().addElapsedTimeFrom(t0);
+ // sun.misc.PerfCounter.getZipFileCount().increment();
+ this.name = name;
+ this.total = getTotal(jzfile);
+ this.locsig = startsWithLOC(jzfile);
+ // Android-added: CloseGuard support
+ guard.open("close");
+ }
+
+ /**
+ * Opens a zip file for reading.
+ *
+ * <p>First, if there is a security manager, its <code>checkRead</code>
+ * method is called with the <code>name</code> argument as its argument
+ * to ensure the read is allowed.
+ *
+ * @param name the name of the zip file
+ * @param charset
+ * the {@linkplain java.nio.charset.Charset charset} to
+ * be used to decode the ZIP entry name and comment that are not
+ * encoded by using UTF-8 encoding (indicated by entry's general
+ * purpose flag).
+ *
+ * @throws ZipException if a ZIP format error has occurred
+ * @throws IOException if an I/O error has occurred
+ * @throws SecurityException
+ * if a security manager exists and its <code>checkRead</code>
+ * method doesn't allow read access to the file
+ *
+ * @see SecurityManager#checkRead(java.lang.String)
+ *
+ * @since 1.7
+ */
+ public ZipFile(String name, Charset charset) throws IOException
+ {
+ this(new File(name), OPEN_READ, charset);
+ }
+
+ /**
+ * Opens a ZIP file for reading given the specified File object.
+ * @param file the ZIP file to be opened for reading
+ * @param charset
+ * The {@linkplain java.nio.charset.Charset charset} to be
+ * used to decode the ZIP entry name and comment (ignored if
+ * the <a href="package-summary.html#lang_encoding"> language
+ * encoding bit</a> of the ZIP entry's general purpose bit
+ * flag is set).
+ *
+ * @throws ZipException if a ZIP format error has occurred
+ * @throws IOException if an I/O error has occurred
+ *
+ * @since 1.7
+ */
+ public ZipFile(File file, Charset charset) throws IOException
+ {
+ this(file, OPEN_READ, charset);
+ }
+
+ /**
+ * Returns the zip file comment, or null if none.
+ *
+ * @return the comment string for the zip file, or null if none
+ *
+ * @throws IllegalStateException if the zip file has been closed
+ *
+ * Since 1.7
+ */
+ public String getComment() {
+ synchronized (this) {
+ ensureOpen();
+ byte[] bcomm = getCommentBytes(jzfile);
+ if (bcomm == null)
+ return null;
+ return zc.toString(bcomm, bcomm.length);
+ }
+ }
+
+ /**
+ * Returns the zip file entry for the specified name, or null
+ * if not found.
+ *
+ * @param name the name of the entry
+ * @return the zip file entry, or null if not found
+ * @throws IllegalStateException if the zip file has been closed
+ */
+ public ZipEntry getEntry(String name) {
+ if (name == null) {
+ throw new NullPointerException("name");
+ }
+ long jzentry = 0;
+ synchronized (this) {
+ ensureOpen();
+ jzentry = getEntry(jzfile, zc.getBytes(name), true);
+ if (jzentry != 0) {
+ ZipEntry ze = getZipEntry(name, jzentry);
+ freeEntry(jzfile, jzentry);
+ return ze;
+ }
+ }
+ return null;
+ }
+
+ private static native long getEntry(long jzfile, byte[] name,
+ boolean addSlash);
+
+ // freeEntry releases the C jzentry struct.
+ private static native void freeEntry(long jzfile, long jzentry);
+
+ // the outstanding inputstreams that need to be closed,
+ // mapped to the inflater objects they use.
+ private final Map<InputStream, Inflater> streams = new WeakHashMap<>();
+
+ /**
+ * Returns an input stream for reading the contents of the specified
+ * zip file entry.
+ *
+ * <p> Closing this ZIP file will, in turn, close all input
+ * streams that have been returned by invocations of this method.
+ *
+ * @param entry the zip file entry
+ * @return the input stream for reading 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
+ */
+ public InputStream getInputStream(ZipEntry entry) throws IOException {
+ if (entry == null) {
+ throw new NullPointerException("entry");
+ }
+ long jzentry = 0;
+ ZipFileInputStream in = null;
+ synchronized (this) {
+ ensureOpen();
+ if (!zc.isUTF8() && (entry.flag & EFS) != 0) {
+ // Android-changed: Find entry by name, falling back to name/ if cannot be found.
+ // Needed for ClassPathURLStreamHandler handling of URLs without trailing slashes.
+ // This was added as part of the work to move StrictJarFile from libcore to
+ // framework, see http://b/111293098 for more details.
+ // It should be possible to revert this after upgrading to OpenJDK 8u144 or above.
+ // jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), false);
+ jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), true);
+ } else {
+ // Android-changed: Find entry by name, falling back to name/ if cannot be found.
+ // jzentry = getEntry(jzfile, zc.getBytes(entry.name), false);
+ jzentry = getEntry(jzfile, zc.getBytes(entry.name), true);
+ }
+ if (jzentry == 0) {
+ return null;
+ }
+ in = new ZipFileInputStream(jzentry);
+
+ switch (getEntryMethod(jzentry)) {
+ case STORED:
+ synchronized (streams) {
+ streams.put(in, null);
+ }
+ return in;
+ case DEFLATED:
+ // MORE: Compute good size for inflater stream:
+ long size = getEntrySize(jzentry) + 2; // Inflater likes a bit of slack
+ // Android-changed: Use 64k buffer size, performs better than 8k.
+ // See http://b/65491407.
+ // if (size > 65536) size = 8192;
+ if (size > 65536) size = 65536;
+ if (size <= 0) size = 4096;
+ Inflater inf = getInflater();
+ InputStream is =
+ new ZipFileInflaterInputStream(in, inf, (int)size);
+ synchronized (streams) {
+ streams.put(is, inf);
+ }
+ return is;
+ default:
+ throw new ZipException("invalid compression method");
+ }
+ }
+ }
+
+ private class ZipFileInflaterInputStream extends InflaterInputStream {
+ private volatile boolean closeRequested = false;
+ private boolean eof = false;
+ private final ZipFileInputStream zfin;
+
+ ZipFileInflaterInputStream(ZipFileInputStream zfin, Inflater inf,
+ int size) {
+ super(zfin, inf, size);
+ this.zfin = zfin;
+ }
+
+ public void close() throws IOException {
+ if (closeRequested)
+ return;
+ closeRequested = true;
+
+ super.close();
+ Inflater inf;
+ synchronized (streams) {
+ inf = streams.remove(this);
+ }
+ if (inf != null) {
+ releaseInflater(inf);
+ }
+ }
+
+ // Override fill() method to provide an extra "dummy" byte
+ // at the end of the input stream. This is required when
+ // using the "nowrap" Inflater option.
+ protected void fill() throws IOException {
+ if (eof) {
+ throw new EOFException("Unexpected end of ZLIB input stream");
+ }
+ len = in.read(buf, 0, buf.length);
+ if (len == -1) {
+ buf[0] = 0;
+ len = 1;
+ eof = true;
+ }
+ inf.setInput(buf, 0, len);
+ }
+
+ public int available() throws IOException {
+ if (closeRequested)
+ return 0;
+ long avail = zfin.size() - inf.getBytesWritten();
+ return (avail > (long) Integer.MAX_VALUE ?
+ Integer.MAX_VALUE : (int) avail);
+ }
+
+ protected void finalize() throws Throwable {
+ close();
+ }
+ }
+
+ /*
+ * Gets an inflater from the list of available inflaters or allocates
+ * a new one.
+ */
+ private Inflater getInflater() {
+ Inflater inf;
+ synchronized (inflaterCache) {
+ while (null != (inf = inflaterCache.poll())) {
+ if (false == inf.ended()) {
+ return inf;
+ }
+ }
+ }
+ return new Inflater(true);
+ }
+
+ /*
+ * Releases the specified inflater to the list of available inflaters.
+ */
+ private void releaseInflater(Inflater inf) {
+ if (false == inf.ended()) {
+ inf.reset();
+ synchronized (inflaterCache) {
+ inflaterCache.add(inf);
+ }
+ }
+ }
+
+ // List of available Inflater objects for decompression
+ private Deque<Inflater> inflaterCache = new ArrayDeque<>();
+
+ /**
+ * Returns the path name of the ZIP file.
+ * @return the path name of the ZIP file
+ */
+ public String getName() {
+ return name;
+ }
+
+ private class ZipEntryIterator implements Enumeration<ZipEntry>, Iterator<ZipEntry> {
+ private int i = 0;
+
+ public ZipEntryIterator() {
+ ensureOpen();
+ }
+
+ public boolean hasMoreElements() {
+ return hasNext();
+ }
+
+ public boolean hasNext() {
+ synchronized (ZipFile.this) {
+ ensureOpen();
+ return i < total;
+ }
+ }
+
+ public ZipEntry nextElement() {
+ return next();
+ }
+
+ public ZipEntry next() {
+ synchronized (ZipFile.this) {
+ ensureOpen();
+ if (i >= total) {
+ throw new NoSuchElementException();
+ }
+ long jzentry = getNextEntry(jzfile, i++);
+ if (jzentry == 0) {
+ String message;
+ if (closeRequested) {
+ message = "ZipFile concurrently closed";
+ } else {
+ message = getZipMessage(ZipFile.this.jzfile);
+ }
+ throw new ZipError("jzentry == 0" +
+ ",\n jzfile = " + ZipFile.this.jzfile +
+ ",\n total = " + ZipFile.this.total +
+ ",\n name = " + ZipFile.this.name +
+ ",\n i = " + i +
+ ",\n message = " + message
+ );
+ }
+ ZipEntry ze = getZipEntry(null, jzentry);
+ freeEntry(jzfile, jzentry);
+ return ze;
+ }
+ }
+ }
+
+ /**
+ * Returns an enumeration of the ZIP file entries.
+ * @return an enumeration of the ZIP file entries
+ * @throws IllegalStateException if the zip file has been closed
+ */
+ public Enumeration<? extends ZipEntry> entries() {
+ return new ZipEntryIterator();
+ }
+
+ /**
+ * Return an ordered {@code Stream} over the ZIP file entries.
+ * Entries appear in the {@code Stream} in the order they appear in
+ * the central directory of the ZIP file.
+ *
+ * @return an ordered {@code Stream} of entries in this ZIP file
+ * @throws IllegalStateException if the zip file has been closed
+ * @since 1.8
+ */
+ public Stream<? extends ZipEntry> stream() {
+ return StreamSupport.stream(Spliterators.spliterator(
+ new ZipEntryIterator(), size(),
+ Spliterator.ORDERED | Spliterator.DISTINCT |
+ Spliterator.IMMUTABLE | Spliterator.NONNULL), false);
+ }
+
+ private ZipEntry getZipEntry(String name, long jzentry) {
+ ZipEntry e = new ZipEntry();
+ e.flag = getEntryFlag(jzentry); // get the flag first
+ if (name != null) {
+ e.name = name;
+ } else {
+ byte[] bname = getEntryBytes(jzentry, JZENTRY_NAME);
+ if (!zc.isUTF8() && (e.flag & EFS) != 0) {
+ e.name = zc.toStringUTF8(bname, bname.length);
+ } else {
+ e.name = zc.toString(bname, bname.length);
+ }
+ }
+ e.xdostime = getEntryTime(jzentry);
+ e.crc = getEntryCrc(jzentry);
+ e.size = getEntrySize(jzentry);
+ e.csize = getEntryCSize(jzentry);
+ e.method = getEntryMethod(jzentry);
+ e.setExtra0(getEntryBytes(jzentry, JZENTRY_EXTRA), false);
+ byte[] bcomm = getEntryBytes(jzentry, JZENTRY_COMMENT);
+ if (bcomm == null) {
+ e.comment = null;
+ } else {
+ if (!zc.isUTF8() && (e.flag & EFS) != 0) {
+ e.comment = zc.toStringUTF8(bcomm, bcomm.length);
+ } else {
+ e.comment = zc.toString(bcomm, bcomm.length);
+ }
+ }
+ return e;
+ }
+
+ private static native long getNextEntry(long jzfile, int i);
+
+ /**
+ * Returns the number of entries in the ZIP file.
+ * @return the number of entries in the ZIP file
+ * @throws IllegalStateException if the zip file has been closed
+ */
+ public int size() {
+ ensureOpen();
+ return total;
+ }
+
+ /**
+ * Closes the ZIP file.
+ * <p> Closing this ZIP file will close all of the input streams
+ * previously returned by invocations of the {@link #getInputStream
+ * getInputStream} method.
+ *
+ * @throws IOException if an I/O error has occurred
+ */
+ public void close() throws IOException {
+ if (closeRequested)
+ return;
+ // Android-added: CloseGuard support
+ if (guard != null) {
+ guard.close();
+ }
+ closeRequested = true;
+
+ synchronized (this) {
+ // Close streams, release their inflaters
+ // BEGIN Android-added: null field check to avoid NullPointerException during finalize.
+ // If the constructor threw an exception then the streams / inflaterCache fields can
+ // be null and close() can be called by the finalizer.
+ if (streams != null) {
+ // END Android-added: null field check to avoid NullPointerException during finalize.
+ synchronized (streams) {
+ if (false == streams.isEmpty()) {
+ Map<InputStream, Inflater> copy = new HashMap<>(streams);
+ streams.clear();
+ for (Map.Entry<InputStream, Inflater> e : copy.entrySet()) {
+ e.getKey().close();
+ Inflater inf = e.getValue();
+ if (inf != null) {
+ inf.end();
+ }
+ }
+ }
+ }
+ // BEGIN Android-added: null field check to avoid NullPointerException during finalize.
+ }
+
+ if (inflaterCache != null) {
+ // END Android-added: null field check to avoid NullPointerException during finalize.
+ // Release cached inflaters
+ Inflater inf;
+ synchronized (inflaterCache) {
+ while (null != (inf = inflaterCache.poll())) {
+ inf.end();
+ }
+ }
+ // BEGIN Android-added: null field check to avoid NullPointerException during finalize.
+ }
+ // END Android-added: null field check to avoid NullPointerException during finalize.
+
+ if (jzfile != 0) {
+ // Close the zip file
+ long zf = this.jzfile;
+ jzfile = 0;
+
+ close(zf);
+ }
+ // Android-added: Do not use unlink() to implement OPEN_DELETE.
+ if (fileToRemoveOnClose != null) {
+ fileToRemoveOnClose.delete();
+ }
+ }
+ }
+
+ /**
+ * Ensures that the system resources held by this ZipFile object are
+ * released when there are no more references to it.
+ *
+ * <p>
+ * Since the time when GC would invoke this method is undetermined,
+ * it is strongly recommended that applications invoke the <code>close</code>
+ * method as soon they have finished accessing this <code>ZipFile</code>.
+ * This will prevent holding up system resources for an undetermined
+ * length of time.
+ *
+ * @throws IOException if an I/O error has occurred
+ * @see java.util.zip.ZipFile#close()
+ */
+ protected void finalize() throws IOException {
+ // Android-added: CloseGuard support
+ if (guard != null) {
+ guard.warnIfOpen();
+ }
+ close();
+ }
+
+ private static native void close(long jzfile);
+
+ private void ensureOpen() {
+ if (closeRequested) {
+ throw new IllegalStateException("zip file closed");
+ }
+
+ if (jzfile == 0) {
+ throw new IllegalStateException("The object is not initialized.");
+ }
+ }
+
+ private void ensureOpenOrZipException() throws IOException {
+ if (closeRequested) {
+ throw new ZipException("ZipFile closed");
+ }
+ }
+
+ /*
+ * Inner class implementing the input stream used to read a
+ * (possibly compressed) zip file entry.
+ */
+ private class ZipFileInputStream extends InputStream {
+ private volatile boolean zfisCloseRequested = false;
+ protected long jzentry; // address of jzentry data
+ private long pos; // current position within entry data
+ protected long rem; // number of remaining bytes within entry
+ protected long size; // uncompressed size of this entry
+
+ ZipFileInputStream(long jzentry) {
+ pos = 0;
+ rem = getEntryCSize(jzentry);
+ size = getEntrySize(jzentry);
+ this.jzentry = jzentry;
+ }
+
+ public int read(byte b[], int off, int len) throws IOException {
+ // Android-added: Always throw an exception when reading from closed zipfile.
+ // Required by the JavaDoc for InputStream.read(byte[], int, int). Upstream version
+ // 8u121-b13 is not compliant but that bug has been fixed in upstream version 9+181
+ // as part of a major change to switch to a pure Java implementation.
+ // See https://bugs.openjdk.java.net/browse/JDK-8145260 and
+ // https://bugs.openjdk.java.net/browse/JDK-8142508.
+ ensureOpenOrZipException();
+
+ synchronized (ZipFile.this) {
+ long rem = this.rem;
+ long pos = this.pos;
+ if (rem == 0) {
+ return -1;
+ }
+ if (len <= 0) {
+ return 0;
+ }
+ if (len > rem) {
+ len = (int) rem;
+ }
+
+ // Android-removed: Always throw an exception when reading from closed zipfile.
+ // Moved to the start of the method.
+ //ensureOpenOrZipException();
+ len = ZipFile.read(ZipFile.this.jzfile, jzentry, pos, b,
+ off, len);
+ if (len > 0) {
+ this.pos = (pos + len);
+ this.rem = (rem - len);
+ }
+ }
+ if (rem == 0) {
+ close();
+ }
+ return len;
+ }
+
+ public int read() throws IOException {
+ byte[] b = new byte[1];
+ if (read(b, 0, 1) == 1) {
+ return b[0] & 0xff;
+ } else {
+ return -1;
+ }
+ }
+
+ public long skip(long n) {
+ if (n > rem)
+ n = rem;
+ pos += n;
+ rem -= n;
+ if (rem == 0) {
+ close();
+ }
+ return n;
+ }
+
+ public int available() {
+ return rem > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) rem;
+ }
+
+ public long size() {
+ return size;
+ }
+
+ public void close() {
+ if (zfisCloseRequested)
+ return;
+ zfisCloseRequested = true;
+
+ rem = 0;
+ synchronized (ZipFile.this) {
+ if (jzentry != 0 && ZipFile.this.jzfile != 0) {
+ freeEntry(ZipFile.this.jzfile, jzentry);
+ jzentry = 0;
+ }
+ }
+ synchronized (streams) {
+ streams.remove(this);
+ }
+ }
+
+ protected void finalize() {
+ close();
+ }
+ }
+
+ // Android-removed: Access startsWithLocHeader() directly.
+ /*
+ static {
+ sun.misc.SharedSecrets.setJavaUtilZipFileAccess(
+ new sun.misc.JavaUtilZipFileAccess() {
+ public boolean startsWithLocHeader(ZipFile zip) {
+ return zip.startsWithLocHeader();
+ }
+ }
+ );
+ }
+ */
+
+ /**
+ * Returns {@code true} if, and only if, the zip file begins with {@code
+ * LOCSIG}.
+ * @hide
+ */
+ // Android-changed: Access startsWithLocHeader() directly.
+ // Make hidden public for use by sun.misc.URLClassPath
+ // private boolean startsWithLocHeader() {
+ public boolean startsWithLocHeader() {
+ return locsig;
+ }
+
+ // BEGIN Android-added: Provide access to underlying file descriptor for testing.
+ // See http://b/111148957 for background information.
+ /** @hide */
+ // @VisibleForTesting
+ public int getFileDescriptor() {
+ return getFileDescriptor(jzfile);
+ }
+
+ private static native int getFileDescriptor(long jzfile);
+ // END Android-added: Provide access to underlying file descriptor for testing.
+
+ private static native long open(String name, int mode, long lastModified,
+ boolean usemmap) throws IOException;
+ private static native int getTotal(long jzfile);
+ private static native boolean startsWithLOC(long jzfile);
+ private static native int read(long jzfile, long jzentry,
+ long pos, byte[] b, int off, int len);
+
+ // access to the native zentry object
+ private static native long getEntryTime(long jzentry);
+ private static native long getEntryCrc(long jzentry);
+ private static native long getEntryCSize(long jzentry);
+ private static native long getEntrySize(long jzentry);
+ private static native int getEntryMethod(long jzentry);
+ private static native int getEntryFlag(long jzentry);
+ private static native byte[] getCommentBytes(long jzfile);
+
+ private static final int JZENTRY_NAME = 0;
+ private static final int JZENTRY_EXTRA = 1;
+ private static final int JZENTRY_COMMENT = 2;
+ private static native byte[] getEntryBytes(long jzentry, int type);
+
+ private static native String getZipMessage(long jzfile);
+}
diff --git a/java/util/zip/ZipInputStream.java b/java/util/zip/ZipInputStream.java
new file mode 100644
index 0000000..aaebb49
--- /dev/null
+++ b/java/util/zip/ZipInputStream.java
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1996, 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.EOFException;
+import java.io.PushbackInputStream;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import static java.util.zip.ZipConstants64.*;
+import static java.util.zip.ZipUtils.*;
+
+/**
+ * This class implements an input stream filter for reading files in the
+ * ZIP file format. Includes support for both compressed and uncompressed
+ * entries.
+ *
+ * @author David Connelly
+ */
+public
+class ZipInputStream extends InflaterInputStream implements ZipConstants {
+ private ZipEntry entry;
+ private int flag;
+ private CRC32 crc = new CRC32();
+ private long remaining;
+ private byte[] tmpbuf = new byte[512];
+
+ private static final int STORED = ZipEntry.STORED;
+ private static final int DEFLATED = ZipEntry.DEFLATED;
+
+ private boolean closed = false;
+ // this flag is set to true after EOF has reached for
+ // one entry
+ private boolean entryEOF = false;
+
+ private ZipCoder zc;
+
+ /**
+ * Check to make sure that this stream has not been closed
+ */
+ private void ensureOpen() throws IOException {
+ if (closed) {
+ throw new IOException("Stream closed");
+ }
+ }
+
+ /**
+ * Creates a new ZIP input stream.
+ *
+ * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
+ * decode the entry names.
+ *
+ * @param in the actual input stream
+ */
+ public ZipInputStream(InputStream in) {
+ this(in, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * Creates a new ZIP input stream.
+ *
+ * @param in the actual input stream
+ *
+ * @param charset
+ * The {@linkplain java.nio.charset.Charset charset} to be
+ * used to decode the ZIP entry name (ignored if the
+ * <a href="package-summary.html#lang_encoding"> language
+ * encoding bit</a> of the ZIP entry's general purpose bit
+ * flag is set).
+ *
+ * @since 1.7
+ */
+ public ZipInputStream(InputStream in, Charset charset) {
+ super(new PushbackInputStream(in, 512), new Inflater(true), 512);
+ // Android-changed: Unconditionally close external inflaters (b/26462400)
+ // usesDefaultInflater = true;
+ if(in == null) {
+ throw new NullPointerException("in is null");
+ }
+ if (charset == null)
+ throw new NullPointerException("charset is null");
+ this.zc = ZipCoder.get(charset);
+ }
+
+ /**
+ * Reads the next ZIP file entry and positions the stream at the
+ * beginning of the entry data.
+ * @return the next ZIP file entry, or null if there are no more entries
+ * @exception ZipException if a ZIP file error has occurred
+ * @exception IOException if an I/O error has occurred
+ */
+ public ZipEntry getNextEntry() throws IOException {
+ ensureOpen();
+ if (entry != null) {
+ closeEntry();
+ }
+ crc.reset();
+ inf.reset();
+ if ((entry = readLOC()) == null) {
+ return null;
+ }
+ // Android-changed: Return more accurate value from available().
+ // Initialize the remaining field with the number of bytes that can be read from the entry
+ // for both uncompressed and compressed entries so that it can be used to provide a more
+ // accurate return value for available().
+ // if (entry.method == STORED) {
+ if (entry.method == STORED || entry.method == DEFLATED) {
+ remaining = entry.size;
+ }
+ entryEOF = false;
+ return entry;
+ }
+
+ /**
+ * Closes the current ZIP entry and positions the stream for reading the
+ * next entry.
+ * @exception ZipException if a ZIP file error has occurred
+ * @exception IOException if an I/O error has occurred
+ */
+ public void closeEntry() throws IOException {
+ ensureOpen();
+ while (read(tmpbuf, 0, tmpbuf.length) != -1) ;
+ entryEOF = true;
+ }
+
+ /**
+ * Returns 0 after EOF has reached for the current entry data,
+ * otherwise always return 1.
+ * <p>
+ * Programs should not count on this method to return the actual number
+ * of bytes that could be read without blocking.
+ *
+ * @return 1 before EOF and 0 after EOF has reached for current entry.
+ * @exception IOException if an I/O error occurs.
+ *
+ */
+ public int available() throws IOException {
+ ensureOpen();
+ // Android-changed: Return more accurate value from available().
+ // Tracks the remaining bytes in order to return a more accurate value for the available
+ // bytes. Given an entry of size N both Android and upstream will return 1 until N bytes
+ // have been read at which point Android will return 0 and upstream will return 1.
+ // Upstream will only return 0 after an attempt to read a byte fails because the EOF has
+ // been reached. See http://b/111439440 for more details.
+ // if (entryEOF) {
+ if (entryEOF || (entry != null && remaining == 0)) {
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+ /**
+ * Reads from the current ZIP entry into an array of bytes.
+ * If <code>len</code> is not zero, the method
+ * blocks until some input is available; otherwise, no
+ * bytes are read and <code>0</code> is returned.
+ * @param b the buffer into which the data is read
+ * @param off the start offset in the destination array <code>b</code>
+ * @param len the maximum number of bytes read
+ * @return the actual number of bytes read, or -1 if the end of the
+ * entry is reached
+ * @exception NullPointerException if <code>b</code> is <code>null</code>.
+ * @exception IndexOutOfBoundsException if <code>off</code> is negative,
+ * <code>len</code> is negative, or <code>len</code> is greater than
+ * <code>b.length - off</code>
+ * @exception ZipException if a ZIP file error has occurred
+ * @exception IOException if an I/O error has occurred
+ */
+ public int read(byte[] b, int off, int len) throws IOException {
+ ensureOpen();
+ if (off < 0 || len < 0 || off > b.length - len) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0) {
+ return 0;
+ }
+
+ if (entry == null) {
+ return -1;
+ }
+ switch (entry.method) {
+ case DEFLATED:
+ len = super.read(b, off, len);
+ if (len == -1) {
+ readEnd(entry);
+ entryEOF = true;
+ entry = null;
+ } else {
+ crc.update(b, off, len);
+ // Android-added: Return more accurate value from available().
+ // Update the remaining field so it is an accurate count of the number of bytes
+ // remaining in this stream, after deflation.
+ remaining -= len;
+ }
+ return len;
+ case STORED:
+ if (remaining <= 0) {
+ entryEOF = true;
+ entry = null;
+ return -1;
+ }
+ if (len > remaining) {
+ len = (int)remaining;
+ }
+ len = in.read(b, off, len);
+ if (len == -1) {
+ throw new ZipException("unexpected EOF");
+ }
+ crc.update(b, off, len);
+ remaining -= len;
+ if (remaining == 0 && entry.crc != crc.getValue()) {
+ throw new ZipException(
+ "invalid entry CRC (expected 0x" + Long.toHexString(entry.crc) +
+ " but got 0x" + Long.toHexString(crc.getValue()) + ")");
+ }
+ return len;
+ default:
+ throw new ZipException("invalid compression method");
+ }
+ }
+
+ /**
+ * Skips specified number of bytes in the current ZIP entry.
+ * @param n the number of bytes to skip
+ * @return the actual number of bytes skipped
+ * @exception ZipException if a ZIP file error has occurred
+ * @exception IOException if an I/O error has occurred
+ * @exception IllegalArgumentException if {@code n < 0}
+ */
+ public long skip(long n) throws IOException {
+ if (n < 0) {
+ throw new IllegalArgumentException("negative skip length");
+ }
+ ensureOpen();
+ int max = (int)Math.min(n, Integer.MAX_VALUE);
+ int total = 0;
+ while (total < max) {
+ int len = max - total;
+ if (len > tmpbuf.length) {
+ len = tmpbuf.length;
+ }
+ len = read(tmpbuf, 0, len);
+ if (len == -1) {
+ entryEOF = true;
+ break;
+ }
+ total += len;
+ }
+ return total;
+ }
+
+ /**
+ * Closes this input stream and releases any system resources associated
+ * with the stream.
+ * @exception IOException if an I/O error has occurred
+ */
+ public void close() throws IOException {
+ if (!closed) {
+ super.close();
+ closed = true;
+ }
+ }
+
+ private byte[] b = new byte[256];
+
+ /*
+ * Reads local file (LOC) header for next entry.
+ */
+ private ZipEntry readLOC() throws IOException {
+ try {
+ readFully(tmpbuf, 0, LOCHDR);
+ } catch (EOFException e) {
+ return null;
+ }
+ if (get32(tmpbuf, 0) != LOCSIG) {
+ return null;
+ }
+ // get flag first, we need check EFS.
+ flag = get16(tmpbuf, LOCFLG);
+ // get the entry name and create the ZipEntry first
+ int len = get16(tmpbuf, LOCNAM);
+ int blen = b.length;
+ if (len > blen) {
+ do {
+ blen = blen * 2;
+ } while (len > blen);
+ b = new byte[blen];
+ }
+ readFully(b, 0, len);
+ // Force to use UTF-8 if the EFS bit is ON, even the cs is NOT UTF-8
+ ZipEntry e = createZipEntry(((flag & EFS) != 0)
+ ? zc.toStringUTF8(b, len)
+ : zc.toString(b, len));
+ // now get the remaining fields for the entry
+ if ((flag & 1) == 1) {
+ throw new ZipException("encrypted ZIP entry not supported");
+ }
+ e.method = get16(tmpbuf, LOCHOW);
+ e.xdostime = get32(tmpbuf, LOCTIM);
+ if ((flag & 8) == 8) {
+ // Android-Changed: Remove the requirement that only DEFLATED entries
+ // can have data descriptors. This is not required by the ZIP spec and
+ // is inconsistent with the behaviour of ZipFile and versions of Android
+ // prior to Android N.
+ //
+ // /* "Data Descriptor" present */
+ // if (e.method != DEFLATED) {
+ // throw new ZipException(
+ // "only DEFLATED entries can have EXT descriptor");
+ // }
+ } else {
+ e.crc = get32(tmpbuf, LOCCRC);
+ e.csize = get32(tmpbuf, LOCSIZ);
+ e.size = get32(tmpbuf, LOCLEN);
+ }
+ len = get16(tmpbuf, LOCEXT);
+ if (len > 0) {
+ byte[] extra = new byte[len];
+ readFully(extra, 0, len);
+ e.setExtra0(extra,
+ e.csize == ZIP64_MAGICVAL || e.size == ZIP64_MAGICVAL);
+ }
+ return e;
+ }
+
+ /**
+ * Creates a new <code>ZipEntry</code> object for the specified
+ * entry name.
+ *
+ * @param name the ZIP file entry name
+ * @return the ZipEntry just created
+ */
+ protected ZipEntry createZipEntry(String name) {
+ return new ZipEntry(name);
+ }
+
+ /*
+ * Reads end of deflated entry as well as EXT descriptor if present.
+ */
+ private void readEnd(ZipEntry e) throws IOException {
+ int n = inf.getRemaining();
+ if (n > 0) {
+ ((PushbackInputStream)in).unread(buf, len - n, n);
+ }
+ if ((flag & 8) == 8) {
+ /* "Data Descriptor" present */
+ if (inf.getBytesWritten() > ZIP64_MAGICVAL ||
+ inf.getBytesRead() > ZIP64_MAGICVAL) {
+ // ZIP64 format
+ readFully(tmpbuf, 0, ZIP64_EXTHDR);
+ long sig = get32(tmpbuf, 0);
+ if (sig != EXTSIG) { // no EXTSIG present
+ e.crc = sig;
+ e.csize = get64(tmpbuf, ZIP64_EXTSIZ - ZIP64_EXTCRC);
+ e.size = get64(tmpbuf, ZIP64_EXTLEN - ZIP64_EXTCRC);
+ ((PushbackInputStream)in).unread(
+ tmpbuf, ZIP64_EXTHDR - ZIP64_EXTCRC - 1, ZIP64_EXTCRC);
+ } else {
+ e.crc = get32(tmpbuf, ZIP64_EXTCRC);
+ e.csize = get64(tmpbuf, ZIP64_EXTSIZ);
+ e.size = get64(tmpbuf, ZIP64_EXTLEN);
+ }
+ } else {
+ readFully(tmpbuf, 0, EXTHDR);
+ long sig = get32(tmpbuf, 0);
+ if (sig != EXTSIG) { // no EXTSIG present
+ e.crc = sig;
+ e.csize = get32(tmpbuf, EXTSIZ - EXTCRC);
+ e.size = get32(tmpbuf, EXTLEN - EXTCRC);
+ ((PushbackInputStream)in).unread(
+ tmpbuf, EXTHDR - EXTCRC - 1, EXTCRC);
+ } else {
+ e.crc = get32(tmpbuf, EXTCRC);
+ e.csize = get32(tmpbuf, EXTSIZ);
+ e.size = get32(tmpbuf, EXTLEN);
+ }
+ }
+ }
+ if (e.size != inf.getBytesWritten()) {
+ throw new ZipException(
+ "invalid entry size (expected " + e.size +
+ " but got " + inf.getBytesWritten() + " bytes)");
+ }
+ if (e.csize != inf.getBytesRead()) {
+ throw new ZipException(
+ "invalid entry compressed size (expected " + e.csize +
+ " but got " + inf.getBytesRead() + " bytes)");
+ }
+ if (e.crc != crc.getValue()) {
+ throw new ZipException(
+ "invalid entry CRC (expected 0x" + Long.toHexString(e.crc) +
+ " but got 0x" + Long.toHexString(crc.getValue()) + ")");
+ }
+ }
+
+ /*
+ * Reads bytes, blocking until all bytes are read.
+ */
+ private void readFully(byte[] b, int off, int len) throws IOException {
+ while (len > 0) {
+ int n = in.read(b, off, len);
+ if (n == -1) {
+ throw new EOFException();
+ }
+ off += n;
+ len -= n;
+ }
+ }
+
+}
diff --git a/java/util/zip/ZipOutputStream.java b/java/util/zip/ZipOutputStream.java
new file mode 100644
index 0000000..dd005ec
--- /dev/null
+++ b/java/util/zip/ZipOutputStream.java
@@ -0,0 +1,752 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1996, 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.OutputStream;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Vector;
+import java.util.HashSet;
+import static java.util.zip.ZipConstants64.*;
+import static java.util.zip.ZipUtils.*;
+
+/**
+ * This class implements an output stream filter for writing files in the
+ * ZIP file format. Includes support for both compressed and uncompressed
+ * entries.
+ *
+ * @author David Connelly
+ */
+public
+class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
+
+ /**
+ * Whether to use ZIP64 for zip files with more than 64k entries.
+ * Until ZIP64 support in zip implementations is ubiquitous, this
+ * system property allows the creation of zip files which can be
+ * read by legacy zip implementations which tolerate "incorrect"
+ * total entry count fields, such as the ones in jdk6, and even
+ * some in jdk7.
+ */
+ // Android-changed: Always allow use of Zip64.
+ private static final boolean inhibitZip64 = false;
+ // Boolean.parseBoolean(
+ // java.security.AccessController.doPrivileged(
+ // new sun.security.action.GetPropertyAction(
+ // "jdk.util.zip.inhibitZip64", "false")));
+
+ private static class XEntry {
+ final ZipEntry entry;
+ final long offset;
+ public XEntry(ZipEntry entry, long offset) {
+ this.entry = entry;
+ this.offset = offset;
+ }
+ }
+
+ private XEntry current;
+ private Vector<XEntry> xentries = new Vector<>();
+ private HashSet<String> names = new HashSet<>();
+ private CRC32 crc = new CRC32();
+ private long written = 0;
+ private long locoff = 0;
+ private byte[] comment;
+ private int method = DEFLATED;
+ private boolean finished;
+
+ private boolean closed = false;
+
+ private final ZipCoder zc;
+
+ private static int version(ZipEntry e) throws ZipException {
+ switch (e.method) {
+ case DEFLATED: return 20;
+ case STORED: return 10;
+ default: throw new ZipException("unsupported compression method");
+ }
+ }
+
+ /**
+ * Checks to make sure that this stream has not been closed.
+ */
+ private void ensureOpen() throws IOException {
+ if (closed) {
+ throw new IOException("Stream closed");
+ }
+ }
+ /**
+ * Compression method for uncompressed (STORED) entries.
+ */
+ public static final int STORED = ZipEntry.STORED;
+
+ /**
+ * Compression method for compressed (DEFLATED) entries.
+ */
+ public static final int DEFLATED = ZipEntry.DEFLATED;
+
+ /**
+ * Creates a new ZIP output stream.
+ *
+ * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used
+ * to encode the entry names and comments.
+ *
+ * @param out the actual output stream
+ */
+ public ZipOutputStream(OutputStream out) {
+ this(out, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * Creates a new ZIP output stream.
+ *
+ * @param out the actual output stream
+ *
+ * @param charset the {@linkplain java.nio.charset.Charset charset}
+ * to be used to encode the entry names and comments
+ *
+ * @since 1.7
+ */
+ public ZipOutputStream(OutputStream out, Charset charset) {
+ super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
+ if (charset == null)
+ throw new NullPointerException("charset is null");
+ this.zc = ZipCoder.get(charset);
+ usesDefaultDeflater = true;
+ }
+
+ /**
+ * Sets the ZIP file comment.
+ * @param comment the comment string
+ * @exception IllegalArgumentException if the length of the specified
+ * ZIP file comment is greater than 0xFFFF bytes
+ */
+ public void setComment(String comment) {
+ if (comment != null) {
+ this.comment = zc.getBytes(comment);
+ if (this.comment.length > 0xffff)
+ throw new IllegalArgumentException("ZIP file comment too long.");
+ }
+ }
+
+ /**
+ * Sets the default compression method for subsequent entries. This
+ * default will be used whenever the compression method is not specified
+ * for an individual ZIP file entry, and is initially set to DEFLATED.
+ * @param method the default compression method
+ * @exception IllegalArgumentException if the specified compression method
+ * is invalid
+ */
+ public void setMethod(int method) {
+ if (method != DEFLATED && method != STORED) {
+ throw new IllegalArgumentException("invalid compression method");
+ }
+ this.method = method;
+ }
+
+ /**
+ * Sets the compression level for subsequent entries which are DEFLATED.
+ * The default setting is DEFAULT_COMPRESSION.
+ * @param level the compression level (0-9)
+ * @exception IllegalArgumentException if the compression level is invalid
+ */
+ public void setLevel(int level) {
+ def.setLevel(level);
+ }
+
+ /**
+ * Begins writing a new ZIP file entry and positions the stream to the
+ * start of the entry data. Closes the current entry if still active.
+ * The default compression method will be used if no compression method
+ * was specified for the entry, and the current time will be used if
+ * the entry has no set modification time.
+ * @param e the ZIP entry to be written
+ * @exception ZipException if a ZIP format error has occurred
+ * @exception IOException if an I/O error has occurred
+ */
+ public void putNextEntry(ZipEntry e) throws IOException {
+ ensureOpen();
+ if (current != null) {
+ closeEntry(); // close previous entry
+ }
+ if (e.xdostime == -1) {
+ // by default, do NOT use extended timestamps in extra
+ // data, for now.
+ e.setTime(System.currentTimeMillis());
+ }
+ if (e.method == -1) {
+ e.method = method; // use default method
+ }
+ // store size, compressed size, and crc-32 in LOC header
+ e.flag = 0;
+ switch (e.method) {
+ case DEFLATED:
+ // store size, compressed size, and crc-32 in data descriptor
+ // immediately following the compressed entry data
+ if (e.size == -1 || e.csize == -1 || e.crc == -1)
+ e.flag = 8;
+
+ break;
+ case STORED:
+ // compressed size, uncompressed size, and crc-32 must all be
+ // set for entries using STORED compression method
+ if (e.size == -1) {
+ e.size = e.csize;
+ } else if (e.csize == -1) {
+ e.csize = e.size;
+ } else if (e.size != e.csize) {
+ throw new ZipException(
+ "STORED entry where compressed != uncompressed size");
+ }
+ if (e.size == -1 || e.crc == -1) {
+ throw new ZipException(
+ "STORED entry missing size, compressed size, or crc-32");
+ }
+ break;
+ default:
+ throw new ZipException("unsupported compression method");
+ }
+ if (! names.add(e.name)) {
+ throw new ZipException("duplicate entry: " + e.name);
+ }
+ if (zc.isUTF8())
+ e.flag |= EFS;
+ current = new XEntry(e, written);
+ xentries.add(current);
+ writeLOC(current);
+ }
+
+ /**
+ * Closes the current ZIP entry and positions the stream for writing
+ * the next entry.
+ * @exception ZipException if a ZIP format error has occurred
+ * @exception IOException if an I/O error has occurred
+ */
+ public void closeEntry() throws IOException {
+ ensureOpen();
+ if (current != null) {
+ ZipEntry e = current.entry;
+ switch (e.method) {
+ case DEFLATED:
+ def.finish();
+ while (!def.finished()) {
+ deflate();
+ }
+ if ((e.flag & 8) == 0) {
+ // verify size, compressed size, and crc-32 settings
+ if (e.size != def.getBytesRead()) {
+ throw new ZipException(
+ "invalid entry size (expected " + e.size +
+ " but got " + def.getBytesRead() + " bytes)");
+ }
+ if (e.csize != def.getBytesWritten()) {
+ throw new ZipException(
+ "invalid entry compressed size (expected " +
+ e.csize + " but got " + def.getBytesWritten() + " bytes)");
+ }
+ if (e.crc != crc.getValue()) {
+ throw new ZipException(
+ "invalid entry CRC-32 (expected 0x" +
+ Long.toHexString(e.crc) + " but got 0x" +
+ Long.toHexString(crc.getValue()) + ")");
+ }
+ } else {
+ e.size = def.getBytesRead();
+ e.csize = def.getBytesWritten();
+ e.crc = crc.getValue();
+ writeEXT(e);
+ }
+ def.reset();
+ written += e.csize;
+ break;
+ case STORED:
+ // we already know that both e.size and e.csize are the same
+ if (e.size != written - locoff) {
+ throw new ZipException(
+ "invalid entry size (expected " + e.size +
+ " but got " + (written - locoff) + " bytes)");
+ }
+ if (e.crc != crc.getValue()) {
+ throw new ZipException(
+ "invalid entry crc-32 (expected 0x" +
+ Long.toHexString(e.crc) + " but got 0x" +
+ Long.toHexString(crc.getValue()) + ")");
+ }
+ break;
+ default:
+ throw new ZipException("invalid compression method");
+ }
+ crc.reset();
+ current = null;
+ }
+ }
+
+ /**
+ * Writes an array of bytes to the current ZIP entry data. This method
+ * will block until all the bytes are written.
+ * @param b the data to be written
+ * @param off the start offset in the data
+ * @param len the number of bytes that are written
+ * @exception ZipException if a ZIP file error has occurred
+ * @exception IOException if an I/O error has occurred
+ */
+ public synchronized void write(byte[] b, int off, int len)
+ throws IOException
+ {
+ ensureOpen();
+ if (off < 0 || len < 0 || off > b.length - len) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0) {
+ return;
+ }
+
+ if (current == null) {
+ throw new ZipException("no current ZIP entry");
+ }
+ ZipEntry entry = current.entry;
+ switch (entry.method) {
+ case DEFLATED:
+ super.write(b, off, len);
+ break;
+ case STORED:
+ written += len;
+ if (written - locoff > entry.size) {
+ throw new ZipException(
+ "attempt to write past end of STORED entry");
+ }
+ out.write(b, off, len);
+ break;
+ default:
+ throw new ZipException("invalid compression method");
+ }
+ crc.update(b, off, len);
+ }
+
+ /**
+ * Finishes writing the contents of the ZIP output stream without closing
+ * the underlying stream. Use this method when applying multiple filters
+ * in succession to the same output stream.
+ * @exception ZipException if a ZIP file error has occurred
+ * @exception IOException if an I/O exception has occurred
+ */
+ public void finish() throws IOException {
+ ensureOpen();
+ if (finished) {
+ return;
+ }
+ if (current != null) {
+ closeEntry();
+ }
+ // write central directory
+ long off = written;
+ for (XEntry xentry : xentries)
+ writeCEN(xentry);
+ writeEND(off, written - off);
+ finished = true;
+ }
+
+ /**
+ * Closes the ZIP output stream as well as the stream being filtered.
+ * @exception ZipException if a ZIP file error has occurred
+ * @exception IOException if an I/O error has occurred
+ */
+ public void close() throws IOException {
+ if (!closed) {
+ super.close();
+ closed = true;
+ }
+ }
+
+ /*
+ * Writes local file (LOC) header for specified entry.
+ */
+ private void writeLOC(XEntry xentry) throws IOException {
+ ZipEntry e = xentry.entry;
+ int flag = e.flag;
+ boolean hasZip64 = false;
+ int elen = getExtraLen(e.extra);
+
+ writeInt(LOCSIG); // LOC header signature
+ if ((flag & 8) == 8) {
+ writeShort(version(e)); // version needed to extract
+ writeShort(flag); // general purpose bit flag
+ writeShort(e.method); // compression method
+ writeInt(e.xdostime); // last modification time
+ // store size, uncompressed size, and crc-32 in data descriptor
+ // immediately following compressed entry data
+ writeInt(0);
+ writeInt(0);
+ writeInt(0);
+ } else {
+ if (e.csize >= ZIP64_MAGICVAL || e.size >= ZIP64_MAGICVAL) {
+ hasZip64 = true;
+ writeShort(45); // ver 4.5 for zip64
+ } else {
+ writeShort(version(e)); // version needed to extract
+ }
+ writeShort(flag); // general purpose bit flag
+ writeShort(e.method); // compression method
+ writeInt(e.xdostime); // last modification time
+ writeInt(e.crc); // crc-32
+ if (hasZip64) {
+ writeInt(ZIP64_MAGICVAL);
+ writeInt(ZIP64_MAGICVAL);
+ elen += 20; //headid(2) + size(2) + size(8) + csize(8)
+ } else {
+ writeInt(e.csize); // compressed size
+ writeInt(e.size); // uncompressed size
+ }
+ }
+ byte[] nameBytes = zc.getBytes(e.name);
+ writeShort(nameBytes.length);
+
+ int elenEXTT = 0; // info-zip extended timestamp
+ int flagEXTT = 0;
+ if (e.mtime != null) {
+ elenEXTT += 4;
+ flagEXTT |= EXTT_FLAG_LMT;
+ }
+ if (e.atime != null) {
+ elenEXTT += 4;
+ flagEXTT |= EXTT_FLAG_LAT;
+ }
+ if (e.ctime != null) {
+ elenEXTT += 4;
+ flagEXTT |= EXTT_FLAT_CT;
+ }
+ if (flagEXTT != 0)
+ elen += (elenEXTT + 5); // headid(2) + size(2) + flag(1) + data
+ writeShort(elen);
+ writeBytes(nameBytes, 0, nameBytes.length);
+ if (hasZip64) {
+ writeShort(ZIP64_EXTID);
+ writeShort(16);
+ writeLong(e.size);
+ writeLong(e.csize);
+ }
+ if (flagEXTT != 0) {
+ writeShort(EXTID_EXTT);
+ writeShort(elenEXTT + 1); // flag + data
+ writeByte(flagEXTT);
+ if (e.mtime != null)
+ writeInt(fileTimeToUnixTime(e.mtime));
+ if (e.atime != null)
+ writeInt(fileTimeToUnixTime(e.atime));
+ if (e.ctime != null)
+ writeInt(fileTimeToUnixTime(e.ctime));
+ }
+ writeExtra(e.extra);
+ locoff = written;
+ }
+
+ /*
+ * Writes extra data descriptor (EXT) for specified entry.
+ */
+ private void writeEXT(ZipEntry e) throws IOException {
+ writeInt(EXTSIG); // EXT header signature
+ writeInt(e.crc); // crc-32
+ if (e.csize >= ZIP64_MAGICVAL || e.size >= ZIP64_MAGICVAL) {
+ writeLong(e.csize);
+ writeLong(e.size);
+ } else {
+ writeInt(e.csize); // compressed size
+ writeInt(e.size); // uncompressed size
+ }
+ }
+
+ /*
+ * Write central directory (CEN) header for specified entry.
+ * REMIND: add support for file attributes
+ */
+ private void writeCEN(XEntry xentry) throws IOException {
+ ZipEntry e = xentry.entry;
+ int flag = e.flag;
+ int version = version(e);
+ long csize = e.csize;
+ long size = e.size;
+ long offset = xentry.offset;
+ int elenZIP64 = 0;
+ boolean hasZip64 = false;
+
+ if (e.csize >= ZIP64_MAGICVAL) {
+ csize = ZIP64_MAGICVAL;
+ elenZIP64 += 8; // csize(8)
+ hasZip64 = true;
+ }
+ if (e.size >= ZIP64_MAGICVAL) {
+ size = ZIP64_MAGICVAL; // size(8)
+ elenZIP64 += 8;
+ hasZip64 = true;
+ }
+ if (xentry.offset >= ZIP64_MAGICVAL) {
+ offset = ZIP64_MAGICVAL;
+ elenZIP64 += 8; // offset(8)
+ hasZip64 = true;
+ }
+ writeInt(CENSIG); // CEN header signature
+ if (hasZip64) {
+ writeShort(45); // ver 4.5 for zip64
+ writeShort(45);
+ } else {
+ writeShort(version); // version made by
+ writeShort(version); // version needed to extract
+ }
+ writeShort(flag); // general purpose bit flag
+ writeShort(e.method); // compression method
+ writeInt(e.xdostime); // last modification time
+ writeInt(e.crc); // crc-32
+ writeInt(csize); // compressed size
+ writeInt(size); // uncompressed size
+ byte[] nameBytes = zc.getBytes(e.name);
+ writeShort(nameBytes.length);
+
+ int elen = getExtraLen(e.extra);
+ if (hasZip64) {
+ elen += (elenZIP64 + 4);// + headid(2) + datasize(2)
+ }
+ // cen info-zip extended timestamp only outputs mtime
+ // but set the flag for a/ctime, if present in loc
+ int flagEXTT = 0;
+ if (e.mtime != null) {
+ elen += 4; // + mtime(4)
+ flagEXTT |= EXTT_FLAG_LMT;
+ }
+ if (e.atime != null) {
+ flagEXTT |= EXTT_FLAG_LAT;
+ }
+ if (e.ctime != null) {
+ flagEXTT |= EXTT_FLAT_CT;
+ }
+ if (flagEXTT != 0) {
+ elen += 5; // headid + sz + flag
+ }
+ writeShort(elen);
+ byte[] commentBytes;
+ if (e.comment != null) {
+ commentBytes = zc.getBytes(e.comment);
+ writeShort(Math.min(commentBytes.length, 0xffff));
+ } else {
+ commentBytes = null;
+ writeShort(0);
+ }
+ writeShort(0); // starting disk number
+ writeShort(0); // internal file attributes (unused)
+ writeInt(0); // external file attributes (unused)
+ writeInt(offset); // relative offset of local header
+ writeBytes(nameBytes, 0, nameBytes.length);
+
+ // take care of EXTID_ZIP64 and EXTID_EXTT
+ if (hasZip64) {
+ writeShort(ZIP64_EXTID);// Zip64 extra
+ writeShort(elenZIP64);
+ if (size == ZIP64_MAGICVAL)
+ writeLong(e.size);
+ if (csize == ZIP64_MAGICVAL)
+ writeLong(e.csize);
+ if (offset == ZIP64_MAGICVAL)
+ writeLong(xentry.offset);
+ }
+ if (flagEXTT != 0) {
+ writeShort(EXTID_EXTT);
+ if (e.mtime != null) {
+ writeShort(5); // flag + mtime
+ writeByte(flagEXTT);
+ writeInt(fileTimeToUnixTime(e.mtime));
+ } else {
+ writeShort(1); // flag only
+ writeByte(flagEXTT);
+ }
+ }
+ writeExtra(e.extra);
+ if (commentBytes != null) {
+ writeBytes(commentBytes, 0, Math.min(commentBytes.length, 0xffff));
+ }
+ }
+
+ /*
+ * Writes end of central directory (END) header.
+ */
+ private void writeEND(long off, long len) throws IOException {
+ boolean hasZip64 = false;
+ long xlen = len;
+ long xoff = off;
+ if (xlen >= ZIP64_MAGICVAL) {
+ xlen = ZIP64_MAGICVAL;
+ hasZip64 = true;
+ }
+ if (xoff >= ZIP64_MAGICVAL) {
+ xoff = ZIP64_MAGICVAL;
+ hasZip64 = true;
+ }
+ int count = xentries.size();
+ if (count >= ZIP64_MAGICCOUNT) {
+ hasZip64 |= !inhibitZip64;
+ if (hasZip64) {
+ count = ZIP64_MAGICCOUNT;
+ }
+ }
+ if (hasZip64) {
+ long off64 = written;
+ //zip64 end of central directory record
+ writeInt(ZIP64_ENDSIG); // zip64 END record signature
+ writeLong(ZIP64_ENDHDR - 12); // size of zip64 end
+ writeShort(45); // version made by
+ writeShort(45); // version needed to extract
+ writeInt(0); // number of this disk
+ writeInt(0); // central directory start disk
+ writeLong(xentries.size()); // number of directory entires on disk
+ writeLong(xentries.size()); // number of directory entires
+ writeLong(len); // length of central directory
+ writeLong(off); // offset of central directory
+
+ //zip64 end of central directory locator
+ writeInt(ZIP64_LOCSIG); // zip64 END locator signature
+ writeInt(0); // zip64 END start disk
+ writeLong(off64); // offset of zip64 END
+ writeInt(1); // total number of disks (?)
+ }
+ writeInt(ENDSIG); // END record signature
+ writeShort(0); // number of this disk
+ writeShort(0); // central directory start disk
+ writeShort(count); // number of directory entries on disk
+ writeShort(count); // total number of directory entries
+ writeInt(xlen); // length of central directory
+ writeInt(xoff); // offset of central directory
+ if (comment != null) { // zip file comment
+ writeShort(comment.length);
+ writeBytes(comment, 0, comment.length);
+ } else {
+ writeShort(0);
+ }
+ }
+
+ /*
+ * Returns the length of extra data without EXTT and ZIP64.
+ */
+ private int getExtraLen(byte[] extra) {
+ if (extra == null)
+ return 0;
+ int skipped = 0;
+ int len = extra.length;
+ int off = 0;
+ while (off + 4 <= len) {
+ int tag = get16(extra, off);
+ int sz = get16(extra, off + 2);
+ if (sz < 0 || (off + 4 + sz) > len) {
+ break;
+ }
+ if (tag == EXTID_EXTT || tag == EXTID_ZIP64) {
+ skipped += (sz + 4);
+ }
+ off += (sz + 4);
+ }
+ return len - skipped;
+ }
+
+ /*
+ * Writes extra data without EXTT and ZIP64.
+ *
+ * Extra timestamp and ZIP64 data is handled/output separately
+ * in writeLOC and writeCEN.
+ */
+ private void writeExtra(byte[] extra) throws IOException {
+ if (extra != null) {
+ int len = extra.length;
+ int off = 0;
+ while (off + 4 <= len) {
+ int tag = get16(extra, off);
+ int sz = get16(extra, off + 2);
+ if (sz < 0 || (off + 4 + sz) > len) {
+ writeBytes(extra, off, len - off);
+ return;
+ }
+ if (tag != EXTID_EXTT && tag != EXTID_ZIP64) {
+ writeBytes(extra, off, sz + 4);
+ }
+ off += (sz + 4);
+ }
+ if (off < len) {
+ writeBytes(extra, off, len - off);
+ }
+ }
+ }
+
+ /*
+ * Writes a 8-bit byte to the output stream.
+ */
+ private void writeByte(int v) throws IOException {
+ OutputStream out = this.out;
+ out.write(v & 0xff);
+ written += 1;
+ }
+
+ /*
+ * Writes a 16-bit short to the output stream in little-endian byte order.
+ */
+ private void writeShort(int v) throws IOException {
+ OutputStream out = this.out;
+ out.write((v >>> 0) & 0xff);
+ out.write((v >>> 8) & 0xff);
+ written += 2;
+ }
+
+ /*
+ * Writes a 32-bit int to the output stream in little-endian byte order.
+ */
+ private void writeInt(long v) throws IOException {
+ OutputStream out = this.out;
+ out.write((int)((v >>> 0) & 0xff));
+ out.write((int)((v >>> 8) & 0xff));
+ out.write((int)((v >>> 16) & 0xff));
+ out.write((int)((v >>> 24) & 0xff));
+ written += 4;
+ }
+
+ /*
+ * Writes a 64-bit int to the output stream in little-endian byte order.
+ */
+ private void writeLong(long v) throws IOException {
+ OutputStream out = this.out;
+ out.write((int)((v >>> 0) & 0xff));
+ out.write((int)((v >>> 8) & 0xff));
+ out.write((int)((v >>> 16) & 0xff));
+ out.write((int)((v >>> 24) & 0xff));
+ out.write((int)((v >>> 32) & 0xff));
+ out.write((int)((v >>> 40) & 0xff));
+ out.write((int)((v >>> 48) & 0xff));
+ out.write((int)((v >>> 56) & 0xff));
+ written += 8;
+ }
+
+ /*
+ * Writes an array of bytes to the output stream.
+ */
+ private void writeBytes(byte[] b, int off, int len) throws IOException {
+ super.out.write(b, off, len);
+ written += len;
+ }
+}
diff --git a/java/util/zip/ZipUtils.java b/java/util/zip/ZipUtils.java
new file mode 100644
index 0000000..fce4d0e
--- /dev/null
+++ b/java/util/zip/ZipUtils.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2013, 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.nio.file.attribute.FileTime;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+class ZipUtils {
+
+ // used to adjust values between Windows and java epoch
+ private static final long WINDOWS_EPOCH_IN_MICROSECONDS = -11644473600000000L;
+
+ /**
+ * Converts Windows time (in microseconds, UTC/GMT) time to FileTime.
+ */
+ public static final FileTime winTimeToFileTime(long wtime) {
+ return FileTime.from(wtime / 10 + WINDOWS_EPOCH_IN_MICROSECONDS,
+ TimeUnit.MICROSECONDS);
+ }
+
+ /**
+ * Converts FileTime to Windows time.
+ */
+ public static final long fileTimeToWinTime(FileTime ftime) {
+ return (ftime.to(TimeUnit.MICROSECONDS) - WINDOWS_EPOCH_IN_MICROSECONDS) * 10;
+ }
+
+ /**
+ * Converts "standard Unix time"(in seconds, UTC/GMT) to FileTime
+ */
+ public static final FileTime unixTimeToFileTime(long utime) {
+ return FileTime.from(utime, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Converts FileTime to "standard Unix time".
+ */
+ public static final long fileTimeToUnixTime(FileTime ftime) {
+ return ftime.to(TimeUnit.SECONDS);
+ }
+
+ /**
+ * Converts DOS time to Java time (number of milliseconds since epoch).
+ */
+ private static long dosToJavaTime(long dtime) {
+ @SuppressWarnings("deprecation") // Use of date constructor.
+ Date d = new Date((int)(((dtime >> 25) & 0x7f) + 80),
+ (int)(((dtime >> 21) & 0x0f) - 1),
+ (int)((dtime >> 16) & 0x1f),
+ (int)((dtime >> 11) & 0x1f),
+ (int)((dtime >> 5) & 0x3f),
+ (int)((dtime << 1) & 0x3e));
+ return d.getTime();
+ }
+
+ /**
+ * Converts extended DOS time to Java time, where up to 1999 milliseconds
+ * might be encoded into the upper half of the returned long.
+ *
+ * @param xdostime the extended DOS time value
+ * @return milliseconds since epoch
+ */
+ public static long extendedDosToJavaTime(long xdostime) {
+ long time = dosToJavaTime(xdostime);
+ return time + (xdostime >> 32);
+ }
+
+ /**
+ * Converts Java time to DOS time.
+ */
+ @SuppressWarnings("deprecation") // Use of date methods
+ private static long javaToDosTime(long time) {
+ Date d = new Date(time);
+ int year = d.getYear() + 1900;
+ if (year < 1980) {
+ return ZipEntry.DOSTIME_BEFORE_1980;
+ }
+ // Android-changed: backport of JDK-8130914 fix
+ return ((year - 1980) << 25 | (d.getMonth() + 1) << 21 |
+ d.getDate() << 16 | d.getHours() << 11 | d.getMinutes() << 5 |
+ d.getSeconds() >> 1) & 0xffffffffL;
+ }
+
+ /**
+ * Converts Java time to DOS time, encoding any milliseconds lost
+ * in the conversion into the upper half of the returned long.
+ *
+ * @param time milliseconds since epoch
+ * @return DOS time with 2s remainder encoded into upper half
+ */
+ public static long javaToExtendedDosTime(long time) {
+ if (time < 0) {
+ return ZipEntry.DOSTIME_BEFORE_1980;
+ }
+ long dostime = javaToDosTime(time);
+ return (dostime != ZipEntry.DOSTIME_BEFORE_1980)
+ ? dostime + ((time % 2000) << 32)
+ : ZipEntry.DOSTIME_BEFORE_1980;
+ }
+
+ /**
+ * Fetches unsigned 16-bit value from byte array at specified offset.
+ * The bytes are assumed to be in Intel (little-endian) byte order.
+ */
+ public static final int get16(byte b[], int off) {
+ return Byte.toUnsignedInt(b[off]) | (Byte.toUnsignedInt(b[off+1]) << 8);
+ }
+
+ /**
+ * Fetches unsigned 32-bit value from byte array at specified offset.
+ * The bytes are assumed to be in Intel (little-endian) byte order.
+ */
+ public static final long get32(byte b[], int off) {
+ return (get16(b, off) | ((long)get16(b, off+2) << 16)) & 0xffffffffL;
+ }
+
+ /**
+ * Fetches signed 64-bit value from byte array at specified offset.
+ * The bytes are assumed to be in Intel (little-endian) byte order.
+ */
+ public static final long get64(byte b[], int off) {
+ return get32(b, off) | (get32(b, off+4) << 32);
+ }
+}