You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@ant.apache.org by bo...@apache.org on 2015/01/20 18:02:19 UTC
ant git commit: port Kristian Rosenvold's write performance
improvements from Commons Compress
Repository: ant
Updated Branches:
refs/heads/master 4cbbf3629 -> 2c04d7e83
port Kristian Rosenvold's write performance improvements from Commons Compress
Project: http://git-wip-us.apache.org/repos/asf/ant/repo
Commit: http://git-wip-us.apache.org/repos/asf/ant/commit/2c04d7e8
Tree: http://git-wip-us.apache.org/repos/asf/ant/tree/2c04d7e8
Diff: http://git-wip-us.apache.org/repos/asf/ant/diff/2c04d7e8
Branch: refs/heads/master
Commit: 2c04d7e833b4e5dab1a3c5ddfe14b572aea8c112
Parents: 4cbbf36
Author: Stefan Bodewig <bo...@apache.org>
Authored: Tue Jan 20 17:58:08 2015 +0100
Committer: Stefan Bodewig <bo...@apache.org>
Committed: Tue Jan 20 18:00:58 2015 +0100
----------------------------------------------------------------------
CONTRIBUTORS | 1 +
WHATSNEW | 3 +
contributors.xml | 4 +
.../org/apache/tools/zip/GeneralPurposeBit.java | 31 +-
src/main/org/apache/tools/zip/ZipEntry.java | 113 +++--
src/main/org/apache/tools/zip/ZipLong.java | 24 +-
.../org/apache/tools/zip/ZipOutputStream.java | 436 +++++++++++--------
src/main/org/apache/tools/zip/ZipShort.java | 16 +-
src/main/org/apache/tools/zip/ZipUtil.java | 26 +-
9 files changed, 432 insertions(+), 222 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ant/blob/2c04d7e8/CONTRIBUTORS
----------------------------------------------------------------------
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index ea7c3e0..fffd4e4 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -206,6 +206,7 @@ Kevin Ross
Kevin Z Grey
Kim Hansen
Kirk Wylie
+Kristian Rosenvold
Kyle Adams
Lajos Veres
Larry Shatzer
http://git-wip-us.apache.org/repos/asf/ant/blob/2c04d7e8/WHATSNEW
----------------------------------------------------------------------
diff --git a/WHATSNEW b/WHATSNEW
index f188f26..1c2721d 100644
--- a/WHATSNEW
+++ b/WHATSNEW
@@ -75,6 +75,9 @@ Other changes:
variable.
Bugzilla Report 57371
+ * ported some of the write-optimization of Commons Compress 1.10 to
+ the ZIP package
+
Changes from Ant 1.9.3 TO Ant 1.9.4
===================================
http://git-wip-us.apache.org/repos/asf/ant/blob/2c04d7e8/contributors.xml
----------------------------------------------------------------------
diff --git a/contributors.xml b/contributors.xml
index ae1e6fc..f7a638d 100644
--- a/contributors.xml
+++ b/contributors.xml
@@ -852,6 +852,10 @@
<last>Wylie</last>
</name>
<name>
+ <first>Kristian</first>
+ <last>Rosenvold</last>
+ </name>
+ <name>
<first>Kyle</first>
<last>Adams</last>
</name>
http://git-wip-us.apache.org/repos/asf/ant/blob/2c04d7e8/src/main/org/apache/tools/zip/GeneralPurposeBit.java
----------------------------------------------------------------------
diff --git a/src/main/org/apache/tools/zip/GeneralPurposeBit.java b/src/main/org/apache/tools/zip/GeneralPurposeBit.java
index ab2525d..1d2255f 100644
--- a/src/main/org/apache/tools/zip/GeneralPurposeBit.java
+++ b/src/main/org/apache/tools/zip/GeneralPurposeBit.java
@@ -122,15 +122,28 @@ public final class GeneralPurposeBit implements Cloneable {
* Encodes the set bits in a form suitable for ZIP archives.
*/
public byte[] encode() {
- return
- ZipShort.getBytes((dataDescriptorFlag ? DATA_DESCRIPTOR_FLAG : 0)
- |
- (languageEncodingFlag ? UFT8_NAMES_FLAG : 0)
- |
- (encryptionFlag ? ENCRYPTION_FLAG : 0)
- |
- (strongEncryptionFlag ? STRONG_ENCRYPTION_FLAG : 0)
- );
+ byte[] result = new byte[2];
+ encode(result, 0);
+ return result;
+ }
+
+ /**
+ * Encodes the set bits in a form suitable for ZIP archives.
+ *
+ * @param buf the output buffer
+ * @param offset
+ * The offset within the output buffer of the first byte to be written.
+ * must be non-negative and no larger than <tt>buf.length-2</tt>
+ */
+ public void encode(byte[] buf, int offset) {
+ ZipShort.putShort((dataDescriptorFlag ? DATA_DESCRIPTOR_FLAG : 0)
+ |
+ (languageEncodingFlag ? UFT8_NAMES_FLAG : 0)
+ |
+ (encryptionFlag ? ENCRYPTION_FLAG : 0)
+ |
+ (strongEncryptionFlag ? STRONG_ENCRYPTION_FLAG : 0)
+ , buf, offset);
}
/**
http://git-wip-us.apache.org/repos/asf/ant/blob/2c04d7e8/src/main/org/apache/tools/zip/ZipEntry.java
----------------------------------------------------------------------
diff --git a/src/main/org/apache/tools/zip/ZipEntry.java b/src/main/org/apache/tools/zip/ZipEntry.java
index 9703aca..f463757 100644
--- a/src/main/org/apache/tools/zip/ZipEntry.java
+++ b/src/main/org/apache/tools/zip/ZipEntry.java
@@ -22,7 +22,6 @@ import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.zip.ZipException;
@@ -51,6 +50,7 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable {
public static final int PLATFORM_UNIX = 3;
public static final int PLATFORM_FAT = 0;
+ public static final int CRC_UNKNOWN = -1;
private static final int SHORT_MASK = 0xFFFF;
private static final int SHORT_SHIFT = 16;
private static final byte[] EMPTY = new byte[0];
@@ -75,11 +75,12 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable {
private int internalAttributes = 0;
private int platform = PLATFORM_FAT;
private long externalAttributes = 0;
- private LinkedHashMap<ZipShort, ZipExtraField> extraFields = null;
+ private ZipExtraField[] extraFields;
private UnparseableExtraFieldData unparseableExtra = null;
private String name = null;
private byte[] rawName = null;
private GeneralPurposeBit gpb = new GeneralPurposeBit();
+ private static final ZipExtraField[] noExtraFields = new ZipExtraField[0];
/**
* Creates a new zip entry with the specified name.
@@ -135,7 +136,7 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable {
this((java.util.zip.ZipEntry) entry);
setInternalAttributes(entry.getInternalAttributes());
setExternalAttributes(entry.getExternalAttributes());
- setExtraFields(entry.getExtraFields(true));
+ setExtraFields(getAllExtraFieldsNoCopy());
setPlatform(entry.getPlatform());
GeneralPurposeBit other = entry.getGeneralPurposeBit();
setGeneralPurposeBit(other == null ? null :
@@ -179,7 +180,7 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable {
e.setInternalAttributes(getInternalAttributes());
e.setExternalAttributes(getExternalAttributes());
- e.setExtraFields(getExtraFields(true));
+ e.setExtraFields(getAllExtraFieldsNoCopy());
return e;
}
@@ -300,14 +301,15 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable {
* @since 1.1
*/
public void setExtraFields(final ZipExtraField[] fields) {
- extraFields = new LinkedHashMap<ZipShort, ZipExtraField>();
- for (final ZipExtraField field : fields) {
+ List<ZipExtraField> newFields = new ArrayList<ZipExtraField>();
+ for (ZipExtraField field : fields) {
if (field instanceof UnparseableExtraFieldData) {
unparseableExtra = (UnparseableExtraFieldData) field;
} else {
- extraFields.put(field.getHeaderId(), field);
+ newFields.add( field);
}
}
+ extraFields = newFields.toArray(new ZipExtraField[newFields.size()]);
setExtra();
}
@@ -316,7 +318,7 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable {
* @return an array of the extra fields
*/
public ZipExtraField[] getExtraFields() {
- return getExtraFields(false);
+ return getParseableExtraFields();
}
/**
@@ -328,17 +330,55 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable {
* @since 1.1
*/
public ZipExtraField[] getExtraFields(final boolean includeUnparseable) {
+ return includeUnparseable ?
+ getAllExtraFields() :
+ getParseableExtraFields();
+ }
+
+ private ZipExtraField[] getParseableExtraFieldsNoCopy() {
if (extraFields == null) {
- return !includeUnparseable || unparseableExtra == null
- ? new ZipExtraField[0]
- : new ZipExtraField[] {unparseableExtra};
+ return noExtraFields;
}
- final List<ZipExtraField> result =
- new ArrayList<ZipExtraField>(extraFields.values());
- if (includeUnparseable && unparseableExtra != null) {
- result.add(unparseableExtra);
+ return extraFields;
+ }
+
+ private ZipExtraField[] getParseableExtraFields() {
+ final ZipExtraField[] parseableExtraFields = getParseableExtraFieldsNoCopy();
+ return (parseableExtraFields == extraFields)
+ ? copyOf(parseableExtraFields) : parseableExtraFields;
+ }
+
+ private ZipExtraField[] copyOf(ZipExtraField[] src){
+ return Arrays.copyOf(src, src.length);
+ }
+
+ private ZipExtraField[] getMergedFields() {
+ final ZipExtraField[] zipExtraFields =
+ Arrays.copyOf(extraFields, extraFields.length + 1);
+ zipExtraFields[zipExtraFields.length] = unparseableExtra;
+ return zipExtraFields;
+ }
+
+ private ZipExtraField[] getUnparseableOnly() {
+ return unparseableExtra == null
+ ? noExtraFields : new ZipExtraField[] { unparseableExtra };
+ }
+
+ private ZipExtraField[] getAllExtraFields() {
+ final ZipExtraField[] allExtraFieldsNoCopy = getAllExtraFieldsNoCopy();
+ return (allExtraFieldsNoCopy == extraFields)
+ ? copyOf( allExtraFieldsNoCopy) : allExtraFieldsNoCopy;
+ }
+
+ /**
+ * Get all extra fields, including unparseable ones.
+ * @return An array of all extra fields. Not necessarily a copy of internal data structures, hence private method
+ */
+ private ZipExtraField[] getAllExtraFieldsNoCopy() {
+ if (extraFields == null) {
+ return getUnparseableOnly();
}
- return result.toArray(new ZipExtraField[0]);
+ return unparseableExtra != null ? getMergedFields() : extraFields;
}
/**
@@ -355,9 +395,16 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable {
unparseableExtra = (UnparseableExtraFieldData) ze;
} else {
if (extraFields == null) {
- extraFields = new LinkedHashMap<ZipShort, ZipExtraField>();
+ extraFields = new ZipExtraField[] {ze};
+ } else {
+ if (getExtraField(ze.getHeaderId()) != null){
+ removeExtraField(ze.getHeaderId());
+ }
+ final ZipExtraField[] zipExtraFields =
+ Arrays.copyOf(extraFields, extraFields.length + 1);
+ zipExtraFields[extraFields.length] = ze;
+ extraFields = zipExtraFields;
}
- extraFields.put(ze.getHeaderId(), ze);
}
setExtra();
}
@@ -374,12 +421,15 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable {
if (ze instanceof UnparseableExtraFieldData) {
unparseableExtra = (UnparseableExtraFieldData) ze;
} else {
- final LinkedHashMap<ZipShort, ZipExtraField> copy = extraFields;
- extraFields = new LinkedHashMap<ZipShort, ZipExtraField>();
- extraFields.put(ze.getHeaderId(), ze);
- if (copy != null) {
- copy.remove(ze.getHeaderId());
- extraFields.putAll(copy);
+ if (getExtraField(ze.getHeaderId()) != null){
+ removeExtraField(ze.getHeaderId());
+ }
+ ZipExtraField[] copy = extraFields;
+ int newLen = extraFields != null ? extraFields.length + 1: 1;
+ extraFields = new ZipExtraField[newLen];
+ extraFields[0] = ze;
+ if (copy != null){
+ System.arraycopy(copy, 0, extraFields, 1, extraFields.length - 1);
}
}
setExtra();
@@ -394,9 +444,16 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable {
if (extraFields == null) {
throw new java.util.NoSuchElementException();
}
- if (extraFields.remove(type) == null) {
+ List<ZipExtraField> newResult = new ArrayList<ZipExtraField>();
+ for (ZipExtraField extraField : extraFields) {
+ if (!type.equals(extraField.getHeaderId())){
+ newResult.add(extraField);
+ }
+ }
+ if (extraFields.length == newResult.size()) {
throw new java.util.NoSuchElementException();
}
+ extraFields = newResult.toArray(new ZipExtraField[newResult.size()]);
setExtra();
}
@@ -418,7 +475,11 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable {
*/
public ZipExtraField getExtraField(final ZipShort type) {
if (extraFields != null) {
- return extraFields.get(type);
+ for (ZipExtraField extraField : extraFields) {
+ if (type.equals(extraField.getHeaderId())) {
+ return extraField;
+ }
+ }
}
return null;
}
http://git-wip-us.apache.org/repos/asf/ant/blob/2c04d7e8/src/main/org/apache/tools/zip/ZipLong.java
----------------------------------------------------------------------
diff --git a/src/main/org/apache/tools/zip/ZipLong.java b/src/main/org/apache/tools/zip/ZipLong.java
index 6eba0ae..72af84d 100644
--- a/src/main/org/apache/tools/zip/ZipLong.java
+++ b/src/main/org/apache/tools/zip/ZipLong.java
@@ -114,14 +114,30 @@ public final class ZipLong implements Cloneable {
*/
public static byte[] getBytes(long value) {
byte[] result = new byte[WORD];
- result[0] = (byte) ((value & BYTE_MASK));
- result[BYTE_1] = (byte) ((value & BYTE_1_MASK) >> BYTE_1_SHIFT);
- result[BYTE_2] = (byte) ((value & BYTE_2_MASK) >> BYTE_2_SHIFT);
- result[BYTE_3] = (byte) ((value & BYTE_3_MASK) >> BYTE_3_SHIFT);
+ putLong(value, result, 0);
return result;
}
/**
+ * put the value as four bytes in big endian byte order.
+ * @param value the Java long to convert to bytes
+ * @param buf the output buffer
+ * @param offset
+ * The offset within the output buffer of the first byte to be written.
+ * must be non-negative and no larger than <tt>buf.length-4</tt>
+ */
+ public static void putLong(long value, byte[] buf, int offset) {
+ buf[offset++] = (byte) ((value & BYTE_MASK));
+ buf[offset++] = (byte) ((value & BYTE_1_MASK) >> BYTE_1_SHIFT);
+ buf[offset++] = (byte) ((value & BYTE_2_MASK) >> BYTE_2_SHIFT);
+ buf[offset] = (byte) ((value & BYTE_3_MASK) >> BYTE_3_SHIFT);
+ }
+
+ public void putLong(byte[] buf, int offset) {
+ putLong(value, buf, offset);
+ }
+
+ /**
* Helper method to get the value as a Java long from four bytes starting at given array offset
* @param bytes the array of bytes
* @param offset the offset to start
http://git-wip-us.apache.org/repos/asf/ant/blob/2c04d7e8/src/main/org/apache/tools/zip/ZipOutputStream.java
----------------------------------------------------------------------
diff --git a/src/main/org/apache/tools/zip/ZipOutputStream.java b/src/main/org/apache/tools/zip/ZipOutputStream.java
index 40d06b8..261c717 100644
--- a/src/main/org/apache/tools/zip/ZipOutputStream.java
+++ b/src/main/org/apache/tools/zip/ZipOutputStream.java
@@ -26,7 +26,10 @@ import static org.apache.tools.zip.ZipConstants.WORD;
import static org.apache.tools.zip.ZipConstants.ZIP64_MAGIC;
import static org.apache.tools.zip.ZipConstants.ZIP64_MAGIC_SHORT;
import static org.apache.tools.zip.ZipConstants.ZIP64_MIN_VERSION;
+import static org.apache.tools.zip.ZipLong.putLong;
+import static org.apache.tools.zip.ZipShort.putShort;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilterOutputStream;
@@ -34,6 +37,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
+import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
@@ -72,6 +76,34 @@ import java.util.zip.ZipException;
public class ZipOutputStream extends FilterOutputStream {
private static final int BUFFER_SIZE = 512;
+ private static final int LFH_SIG_OFFSET = 0;
+ private static final int LFH_VERSION_NEEDED_OFFSET = 4;
+ private static final int LFH_GPB_OFFSET = 6;
+ private static final int LFH_METHOD_OFFSET = 8;
+ private static final int LFH_TIME_OFFSET = 10;
+ private static final int LFH_CRC_OFFSET = 14;
+ private static final int LFH_COMPRESSED_SIZE_OFFSET = 18;
+ private static final int LFH_ORIGINAL_SIZE_OFFSET = 22;
+ private static final int LFH_FILENAME_LENGTH_OFFSET = 26;
+ private static final int LFH_EXTRA_LENGTH_OFFSET = 28;
+ private static final int LFH_FILENAME_OFFSET = 30;
+ private static final int CFH_SIG_OFFSET = 0;
+ private static final int CFH_VERSION_MADE_BY_OFFSET = 4;
+ private static final int CFH_VERSION_NEEDED_OFFSET = 6;
+ private static final int CFH_GPB_OFFSET = 8;
+ private static final int CFH_METHOD_OFFSET = 10;
+ private static final int CFH_TIME_OFFSET = 12;
+ private static final int CFH_CRC_OFFSET = 16;
+ private static final int CFH_COMPRESSED_SIZE_OFFSET = 20;
+ private static final int CFH_ORIGINAL_SIZE_OFFSET = 24;
+ private static final int CFH_FILENAME_LENGTH_OFFSET = 28;
+ private static final int CFH_EXTRA_LENGTH_OFFSET = 30;
+ private static final int CFH_COMMENT_LENGTH_OFFSET = 32;
+ private static final int CFH_DISK_NUMBER_OFFSET = 34;
+ private static final int CFH_INTERNAL_ATTRIBUTES_OFFSET = 36;
+ private static final int CFH_EXTERNAL_ATTRIBUTES_OFFSET = 38;
+ private static final int CFH_LFH_OFFSET = 42;
+ private static final int CFH_FILENAME_OFFSET = 46;
/**
* indicates if this archive is finished.
@@ -208,6 +240,8 @@ public class ZipOutputStream extends FilterOutputStream {
*/
private static final byte[] LZERO = {0, 0, 0, 0};
+ private static final byte[] ONE = ZipLong.getBytes(1L);
+
/**
* Holds the offsets of the LFH starts for each entry.
*
@@ -287,6 +321,8 @@ public class ZipOutputStream extends FilterOutputStream {
private Zip64Mode zip64Mode = Zip64Mode.AsNeeded;
+ private final Calendar calendarInstance = Calendar.getInstance();
+
/**
* Creates a new ZIP OutputStream filtering the underlying stream.
* @param out the outputstream to zip
@@ -459,9 +495,7 @@ public class ZipOutputStream extends FilterOutputStream {
}
cdOffset = written;
- for (ZipEntry ze : entries) {
- writeCentralFileHeader(ze);
- }
+ writeCentralDirectoryInChunks();
cdLength = written - cdOffset;
writeZip64CentralDirectory();
writeCentralDirectoryEnd();
@@ -471,6 +505,21 @@ public class ZipOutputStream extends FilterOutputStream {
finished = true;
}
+ private void writeCentralDirectoryInChunks() throws IOException {
+ final int NUM_PER_WRITE = 1000;
+ final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(70 * NUM_PER_WRITE);
+ int count = 0;
+ for (ZipEntry ze : entries) {
+ byteArrayOutputStream.write(createCentralFileHeader(ze));
+ if (++count > NUM_PER_WRITE){
+ writeCounted(byteArrayOutputStream.toByteArray());
+ byteArrayOutputStream.reset();
+ count = 0;
+ }
+ }
+ writeCounted(byteArrayOutputStream.toByteArray());
+ }
+
/**
* Writes all necessary data for this entry.
*
@@ -481,17 +530,7 @@ public class ZipOutputStream extends FilterOutputStream {
* is {@link Zip64Mode#Never}.
*/
public void closeEntry() throws IOException {
- if (finished) {
- throw new IOException("Stream has already been finished");
- }
-
- if (entry == null) {
- throw new IOException("No current entry to close");
- }
-
- if (!entry.hasWritten) {
- write(EMPTY, 0, 0);
- }
+ preClose();
flushDeflater();
@@ -503,6 +542,10 @@ public class ZipOutputStream extends FilterOutputStream {
final boolean actuallyNeedsZip64 =
handleSizesAndCrc(bytesWritten, realCrc, effectiveMode);
+ closeEntry(actuallyNeedsZip64);
+ }
+
+ private void closeEntry(boolean actuallyNeedsZip64) throws IOException {
if (raf != null) {
rewriteSizesAndCrc(actuallyNeedsZip64);
}
@@ -511,6 +554,20 @@ public class ZipOutputStream extends FilterOutputStream {
entry = null;
}
+ private void preClose() throws IOException {
+ if (finished) {
+ throw new IOException("Stream has already been finished");
+ }
+
+ if (entry == null) {
+ throw new IOException("No current entry to close");
+ }
+
+ if (!entry.hasWritten) {
+ write(EMPTY, 0, 0);
+ }
+ }
+
/**
* Ensures all bytes sent to the deflater are written to the stream.
*/
@@ -564,9 +621,19 @@ public class ZipOutputStream extends FilterOutputStream {
entry.entry.setCrc(crc);
}
- final boolean actuallyNeedsZip64 = effectiveMode == Zip64Mode.Always
- || entry.entry.getSize() >= ZIP64_MAGIC
- || entry.entry.getCompressedSize() >= ZIP64_MAGIC;
+ return checkIfNeedsZip64(effectiveMode);
+ }
+
+ /**
+ * Ensures the current entry's size and CRC information is set to
+ * the values just written, verifies it isn't too big in the
+ * Zip64Mode.Never case and returns whether the entry would
+ * require a Zip64 extra field.
+ */
+ private boolean checkIfNeedsZip64(Zip64Mode effectiveMode)
+ throws ZipException {
+ final boolean actuallyNeedsZip64 = isZip64Required(entry.entry,
+ effectiveMode);
if (actuallyNeedsZip64 && effectiveMode == Zip64Mode.Never) {
throw new Zip64RequiredException(Zip64RequiredException
.getEntryTooBigMessage(entry.entry));
@@ -574,6 +641,15 @@ public class ZipOutputStream extends FilterOutputStream {
return actuallyNeedsZip64;
}
+ private boolean isZip64Required(ZipEntry entry1, Zip64Mode requestedMode) {
+ return requestedMode == Zip64Mode.Always || isTooLageForZip32(entry1);
+ }
+
+ private boolean isTooLageForZip32(ZipEntry zipArchiveEntry){
+ return zipArchiveEntry.getSize() >= ZIP64_MAGIC
+ || zipArchiveEntry.getCompressedSize() >= ZIP64_MAGIC;
+ }
+
/**
* When using random access output, write the local file header
* and potentiall the ZIP64 extra containing the correct CRC and
@@ -654,13 +730,15 @@ public class ZipOutputStream extends FilterOutputStream {
// just a placeholder, real data will be in data
// descriptor or inserted later via RandomAccessFile
ZipEightByteInteger size = ZipEightByteInteger.ZERO;
+ ZipEightByteInteger compressedSize = ZipEightByteInteger.ZERO;
if (entry.entry.getMethod() == STORED
&& entry.entry.getSize() != -1) {
// actually, we already know the sizes
size = new ZipEightByteInteger(entry.entry.getSize());
+ compressedSize = size;
}
z64.setSize(size);
- z64.setCompressedSize(size);
+ z64.setCompressedSize(compressedSize);
entry.entry.setExtra();
}
@@ -794,18 +872,34 @@ public class ZipOutputStream extends FilterOutputStream {
*/
@Override
public void write(byte[] b, int offset, int length) throws IOException {
+ if (entry == null) {
+ throw new IllegalStateException("No current entry");
+ }
ZipUtil.checkRequestedFeatures(entry.entry);
entry.hasWritten = true;
if (entry.entry.getMethod() == DEFLATED) {
writeDeflated(b, offset, length);
} else {
- writeOut(b, offset, length);
- written += length;
+ writeCounted(b, offset, length);
}
crc.update(b, offset, length);
}
/**
+ * Write bytes to output or random access file.
+ * @param data the byte array to write
+ * @throws IOException on error
+ */
+ private void writeCounted(byte[] data) throws IOException {
+ writeCounted(data, 0, data.length);
+ }
+
+ private void writeCounted(byte[] data, int offset, int length) throws IOException {
+ writeOut(data, offset, length);
+ written += length;
+ }
+
+ /**
* write implementation for DEFLATED entries.
*/
private void writeDeflated(byte[]b, int offset, int length)
@@ -906,8 +1000,7 @@ public class ZipOutputStream extends FilterOutputStream {
protected final void deflate() throws IOException {
int len = def.deflate(buf, 0, buf.length);
if (len > 0) {
- writeOut(buf, 0, len);
- written += len;
+ writeCounted(buf, 0, len);
}
}
@@ -927,76 +1020,71 @@ public class ZipOutputStream extends FilterOutputStream {
addUnicodeExtraFields(ze, encodable, name);
}
- offsets.put(ze, Long.valueOf(written));
+ final byte[] localHeader = createLocalFileHeader(ze, name, encodable);
+ final long localHeaderStart = written;
+ offsets.put(ze, localHeaderStart);
+ entry.localDataStart = localHeaderStart + LFH_CRC_OFFSET; // At crc offset
+ writeCounted(localHeader);
+ entry.dataStart = written;
+ }
+
+ private byte[] createLocalFileHeader(ZipEntry ze, ByteBuffer name, boolean encodable) {
+ byte[] extra = ze.getLocalFileDataExtra();
+ final int nameLen = name.limit() - name.position();
+ int len= LFH_FILENAME_OFFSET + nameLen + extra.length;
+ byte[] buf = new byte[len];
- writeOut(LFH_SIG);
- written += WORD;
+ System.arraycopy(LFH_SIG, 0, buf, LFH_SIG_OFFSET, WORD);
//store method in local variable to prevent multiple method calls
final int zipMethod = ze.getMethod();
- writeVersionNeededToExtractAndGeneralPurposeBits(zipMethod,
- !encodable
- && fallbackToUTF8,
- hasZip64Extra(ze));
- written += WORD;
+ putShort(versionNeededToExtract(zipMethod, hasZip64Extra(ze)),
+ buf, LFH_VERSION_NEEDED_OFFSET);
+
+ GeneralPurposeBit generalPurposeBit =
+ getGeneralPurposeBits(zipMethod, !encodable && fallbackToUTF8);
+ generalPurposeBit.encode(buf, LFH_GPB_OFFSET);
// compression method
- writeOut(ZipShort.getBytes(zipMethod));
- written += SHORT;
+ putShort(zipMethod, buf, LFH_METHOD_OFFSET);
- // last mod. time and date
- writeOut(ZipUtil.toDosTime(ze.getTime()));
- written += WORD;
+ ZipUtil.toDosTime(calendarInstance, ze.getTime(), buf, LFH_TIME_OFFSET);
// CRC
- // compressed length
- // uncompressed length
- entry.localDataStart = written;
if (zipMethod == DEFLATED || raf != null) {
- writeOut(LZERO);
- if (hasZip64Extra(entry.entry)) {
- // point to ZIP64 extended information extra field for
- // sizes, may get rewritten once sizes are known if
- // stream is seekable
- writeOut(ZipLong.ZIP64_MAGIC.getBytes());
- writeOut(ZipLong.ZIP64_MAGIC.getBytes());
- } else {
- writeOut(LZERO);
- writeOut(LZERO);
- }
+ System.arraycopy(LZERO, 0, buf, LFH_CRC_OFFSET, WORD);
} else {
- writeOut(ZipLong.getBytes(ze.getCrc()));
- byte[] size = ZipLong.ZIP64_MAGIC.getBytes();
- if (!hasZip64Extra(ze)) {
- size = ZipLong.getBytes(ze.getSize());
- }
- writeOut(size);
- writeOut(size);
+ putLong(ze.getCrc(), buf, LFH_CRC_OFFSET);
}
- // CheckStyle:MagicNumber OFF
- written += 12;
- // CheckStyle:MagicNumber ON
+ // compressed length
+ // uncompressed length
+ if (hasZip64Extra(entry.entry)){
+ // point to ZIP64 extended information extra field for
+ // sizes, may get rewritten once sizes are known if
+ // stream is seekable
+ ZipLong.ZIP64_MAGIC.putLong(buf, LFH_COMPRESSED_SIZE_OFFSET);
+ ZipLong.ZIP64_MAGIC.putLong(buf, LFH_ORIGINAL_SIZE_OFFSET);
+ } else if (zipMethod == DEFLATED || raf != null) {
+ System.arraycopy(LZERO, 0, buf, LFH_COMPRESSED_SIZE_OFFSET, WORD);
+ System.arraycopy(LZERO, 0, buf, LFH_ORIGINAL_SIZE_OFFSET, WORD);
+ } else { // Stored
+ putLong(ze.getSize(), buf, LFH_COMPRESSED_SIZE_OFFSET);
+ putLong(ze.getSize(), buf, LFH_ORIGINAL_SIZE_OFFSET);
+ }
// file name length
- writeOut(ZipShort.getBytes(name.limit()));
- written += SHORT;
+ putShort(nameLen, buf, LFH_FILENAME_LENGTH_OFFSET);
// extra field length
- byte[] extra = ze.getLocalFileDataExtra();
- writeOut(ZipShort.getBytes(extra.length));
- written += SHORT;
+ putShort(extra.length, buf, LFH_EXTRA_LENGTH_OFFSET);
// file name
- writeOut(name.array(), name.arrayOffset(),
- name.limit() - name.position());
- written += name.limit();
+ System.arraycopy(name.array(), name.arrayOffset(), buf,
+ LFH_FILENAME_OFFSET, nameLen);
- // extra field
- writeOut(extra);
- written += extra.length;
-
- entry.dataStart = written;
+ System.arraycopy(extra, 0, buf, LFH_FILENAME_OFFSET + nameLen, extra.length);
+ return buf;
}
/**
@@ -1045,18 +1133,15 @@ public class ZipOutputStream extends FilterOutputStream {
if (ze.getMethod() != DEFLATED || raf != null) {
return;
}
- writeOut(DD_SIG);
- writeOut(ZipLong.getBytes(ze.getCrc()));
- int sizeFieldSize = WORD;
+ writeCounted(DD_SIG);
+ writeCounted(ZipLong.getBytes(ze.getCrc()));
if (!hasZip64Extra(ze)) {
- writeOut(ZipLong.getBytes(ze.getCompressedSize()));
- writeOut(ZipLong.getBytes(ze.getSize()));
+ writeCounted(ZipLong.getBytes(ze.getCompressedSize()));
+ writeCounted(ZipLong.getBytes(ze.getSize()));
} else {
- sizeFieldSize = DWORD;
- writeOut(ZipEightByteInteger.getBytes(ze.getCompressedSize()));
- writeOut(ZipEightByteInteger.getBytes(ze.getSize()));
+ writeCounted(ZipEightByteInteger.getBytes(ze.getCompressedSize()));
+ writeCounted(ZipEightByteInteger.getBytes(ze.getSize()));
}
- written += 2 * WORD + 2 * sizeFieldSize;
}
/**
@@ -1068,114 +1153,116 @@ public class ZipOutputStream extends FilterOutputStream {
* Zip64Mode#Never}.
*/
protected void writeCentralFileHeader(ZipEntry ze) throws IOException {
- writeOut(CFH_SIG);
- written += WORD;
+ byte[] centralFileHeader = createCentralFileHeader(ze);
+ writeCounted(centralFileHeader);
+ }
- final long lfhOffset = offsets.get(ze).longValue();
+ private byte[] createCentralFileHeader(ZipEntry ze) throws IOException {
+ final long lfhOffset = offsets.get(ze);
final boolean needsZip64Extra = hasZip64Extra(ze)
- || ze.getCompressedSize() >= ZIP64_MAGIC
- || ze.getSize() >= ZIP64_MAGIC
- || lfhOffset >= ZIP64_MAGIC;
+ || ze.getCompressedSize() >= ZIP64_MAGIC
+ || ze.getSize() >= ZIP64_MAGIC
+ || lfhOffset >= ZIP64_MAGIC;
if (needsZip64Extra && zip64Mode == Zip64Mode.Never) {
// must be the offset that is too big, otherwise an
- // exception would have been throw in putNextEntry or
- // closeEntry
+ // exception would have been throw in putArchiveEntry or
+ // closeArchiveEntry
throw new Zip64RequiredException(Zip64RequiredException
- .ARCHIVE_TOO_BIG_MESSAGE);
+ .ARCHIVE_TOO_BIG_MESSAGE);
}
+
handleZip64Extra(ze, lfhOffset, needsZip64Extra);
+ return createCentralFileHeader(ze, getName(ze), lfhOffset, needsZip64Extra);
+ }
+
+ /**
+ * Writes the central file header entry.
+ * @param ze the entry to write
+ * @param name The encoded name
+ * @param lfhOffset Local file header offset for this file
+ * @throws IOException on error
+ */
+ private byte[] createCentralFileHeader(ZipEntry ze, ByteBuffer name, long lfhOffset,
+ boolean needsZip64Extra) throws IOException {
+ byte[] extra = ze.getCentralDirectoryExtra();
+
+ // file comment length
+ String comm = ze.getComment();
+ if (comm == null) {
+ comm = "";
+ }
+
+ ByteBuffer commentB = getEntryEncoding(ze).encode(comm);
+ final int nameLen = name.limit() - name.position();
+ final int commentLen = commentB.limit() - commentB.position();
+ int len= CFH_FILENAME_OFFSET + nameLen + extra.length + commentLen;
+ byte[] buf = new byte[len];
+
+ System.arraycopy(CFH_SIG, 0, buf, CFH_SIG_OFFSET, WORD);
+
// version made by
// CheckStyle:MagicNumber OFF
- writeOut(ZipShort.getBytes((ze.getPlatform() << 8) |
- (!hasUsedZip64 ? DATA_DESCRIPTOR_MIN_VERSION
- : ZIP64_MIN_VERSION)));
- written += SHORT;
+ putShort((ze.getPlatform() << 8) | (!hasUsedZip64 ? DATA_DESCRIPTOR_MIN_VERSION : ZIP64_MIN_VERSION),
+ buf, CFH_VERSION_MADE_BY_OFFSET);
final int zipMethod = ze.getMethod();
final boolean encodable = zipEncoding.canEncode(ze.getName());
- writeVersionNeededToExtractAndGeneralPurposeBits(zipMethod,
- !encodable
- && fallbackToUTF8,
- needsZip64Extra);
- written += WORD;
+ putShort(versionNeededToExtract(zipMethod, needsZip64Extra), buf, CFH_VERSION_NEEDED_OFFSET);
+ getGeneralPurposeBits(zipMethod, !encodable && fallbackToUTF8).encode(buf, CFH_GPB_OFFSET);
// compression method
- writeOut(ZipShort.getBytes(zipMethod));
- written += SHORT;
+ putShort(zipMethod, buf, CFH_METHOD_OFFSET);
+
// last mod. time and date
- writeOut(ZipUtil.toDosTime(ze.getTime()));
- written += WORD;
+ ZipUtil.toDosTime(calendarInstance, ze.getTime(), buf, CFH_TIME_OFFSET);
// CRC
// compressed length
// uncompressed length
- writeOut(ZipLong.getBytes(ze.getCrc()));
+ putLong(ze.getCrc(), buf, CFH_CRC_OFFSET);
if (ze.getCompressedSize() >= ZIP64_MAGIC
- || ze.getSize() >= ZIP64_MAGIC) {
- writeOut(ZipLong.ZIP64_MAGIC.getBytes());
- writeOut(ZipLong.ZIP64_MAGIC.getBytes());
+ || ze.getSize() >= ZIP64_MAGIC) {
+ ZipLong.ZIP64_MAGIC.putLong(buf, CFH_COMPRESSED_SIZE_OFFSET);
+ ZipLong.ZIP64_MAGIC.putLong(buf, CFH_ORIGINAL_SIZE_OFFSET);
} else {
- writeOut(ZipLong.getBytes(ze.getCompressedSize()));
- writeOut(ZipLong.getBytes(ze.getSize()));
+ putLong(ze.getCompressedSize(), buf, CFH_COMPRESSED_SIZE_OFFSET);
+ putLong(ze.getSize(), buf, CFH_ORIGINAL_SIZE_OFFSET);
}
- // CheckStyle:MagicNumber OFF
- written += 12;
- // CheckStyle:MagicNumber ON
-
- ByteBuffer name = getName(ze);
- writeOut(ZipShort.getBytes(name.limit()));
- written += SHORT;
+ putShort(nameLen, buf, CFH_FILENAME_LENGTH_OFFSET);
// extra field length
- byte[] extra = ze.getCentralDirectoryExtra();
- writeOut(ZipShort.getBytes(extra.length));
- written += SHORT;
+ putShort(extra.length, buf, CFH_EXTRA_LENGTH_OFFSET);
- // file comment length
- String comm = ze.getComment();
- if (comm == null) {
- comm = "";
- }
-
- ByteBuffer commentB = getEntryEncoding(ze).encode(comm);
-
- writeOut(ZipShort.getBytes(commentB.limit()));
- written += SHORT;
+ putShort(commentLen, buf, CFH_COMMENT_LENGTH_OFFSET);
// disk number start
- writeOut(ZERO);
- written += SHORT;
+ System.arraycopy(ZERO, 0, buf, CFH_DISK_NUMBER_OFFSET, SHORT);
// internal file attributes
- writeOut(ZipShort.getBytes(ze.getInternalAttributes()));
- written += SHORT;
+ putShort(ze.getInternalAttributes(), buf, CFH_INTERNAL_ATTRIBUTES_OFFSET);
// external file attributes
- writeOut(ZipLong.getBytes(ze.getExternalAttributes()));
- written += WORD;
+ putLong(ze.getExternalAttributes(), buf, CFH_EXTERNAL_ATTRIBUTES_OFFSET);
// relative offset of LFH
- writeOut(ZipLong.getBytes(Math.min(lfhOffset, ZIP64_MAGIC)));
- written += WORD;
+ putLong(Math.min(lfhOffset, ZIP64_MAGIC), buf, CFH_LFH_OFFSET);
// file name
- writeOut(name.array(), name.arrayOffset(),
- name.limit() - name.position());
- written += name.limit();
+ System.arraycopy(name.array(), name.arrayOffset(), buf, CFH_FILENAME_OFFSET, nameLen);
- // extra field
- writeOut(extra);
- written += extra.length;
+ int extraStart = CFH_FILENAME_OFFSET + nameLen;
+ System.arraycopy(extra, 0, buf, extraStart, extra.length);
+
+ int commentStart = extraStart + commentLen;
// file comment
- writeOut(commentB.array(), commentB.arrayOffset(),
- commentB.limit() - commentB.position());
- written += commentB.limit();
+ System.arraycopy(commentB.array(), commentB.arrayOffset(), buf, commentStart, commentLen);
+ return buf;
}
/**
@@ -1210,11 +1297,11 @@ public class ZipOutputStream extends FilterOutputStream {
* and {@link Zip64Mode #setUseZip64} is {@link Zip64Mode#Never}.
*/
protected void writeCentralDirectoryEnd() throws IOException {
- writeOut(EOCD_SIG);
+ writeCounted(EOCD_SIG);
// disk numbers
- writeOut(ZERO);
- writeOut(ZERO);
+ writeCounted(ZERO);
+ writeCounted(ZERO);
// number of entries
int numberOfEntries = entries.size();
@@ -1230,18 +1317,18 @@ public class ZipOutputStream extends FilterOutputStream {
byte[] num = ZipShort.getBytes(Math.min(numberOfEntries,
ZIP64_MAGIC_SHORT));
- writeOut(num);
- writeOut(num);
+ writeCounted(num);
+ writeCounted(num);
// length and location of CD
- writeOut(ZipLong.getBytes(Math.min(cdLength, ZIP64_MAGIC)));
- writeOut(ZipLong.getBytes(Math.min(cdOffset, ZIP64_MAGIC)));
+ writeCounted(ZipLong.getBytes(Math.min(cdLength, ZIP64_MAGIC)));
+ writeCounted(ZipLong.getBytes(Math.min(cdOffset, ZIP64_MAGIC)));
// ZIP file comment
ByteBuffer data = this.zipEncoding.encode(comment);
- writeOut(ZipShort.getBytes(data.limit()));
- writeOut(data.array(), data.arrayOffset(),
- data.limit() - data.position());
+ int dataLen = data.limit() - data.position();
+ writeCounted(ZipShort.getBytes(dataLen));
+ writeCounted(data.array(), data.arrayOffset(), dataLen);
}
/**
@@ -1292,8 +1379,6 @@ public class ZipOutputStream extends FilterOutputStream {
}
}
- private static final byte[] ONE = ZipLong.getBytes(1L);
-
/**
* Writes the "ZIP64 End of central dir record" and
* "ZIP64 End of central dir locator".
@@ -1409,33 +1494,28 @@ public class ZipOutputStream extends FilterOutputStream {
}
}
- private void writeVersionNeededToExtractAndGeneralPurposeBits(final int
- zipMethod,
- final boolean
- utfFallback,
- final boolean
- zip64)
- throws IOException {
-
- // CheckStyle:MagicNumber OFF
- int versionNeededToExtract = INITIAL_VERSION;
+ private GeneralPurposeBit getGeneralPurposeBits(final int zipMethod, final boolean utfFallback) {
GeneralPurposeBit b = new GeneralPurposeBit();
b.useUTF8ForNames(useUTF8Flag || utfFallback);
- if (zipMethod == DEFLATED && raf == null) {
- // requires version 2 as we are going to store length info
- // in the data descriptor
- versionNeededToExtract = DATA_DESCRIPTOR_MIN_VERSION;
+ if (isDeflatedToOutputStream(zipMethod)) {
b.useDataDescriptor(true);
}
+ return b;
+ }
+
+ private int versionNeededToExtract(final int zipMethod, final boolean zip64) {
if (zip64) {
- versionNeededToExtract = ZIP64_MIN_VERSION;
+ return ZIP64_MIN_VERSION;
}
- // CheckStyle:MagicNumber ON
+ // requires version 2 as we are going to store length info
+ // in the data descriptor
+ return (isDeflatedToOutputStream(zipMethod)) ?
+ DATA_DESCRIPTOR_MIN_VERSION :
+ INITIAL_VERSION;
+ }
- // version needed to extract
- writeOut(ZipShort.getBytes(versionNeededToExtract));
- // general purpose bit flag
- writeOut(b.encode());
+ private boolean isDeflatedToOutputStream(int zipMethod) {
+ return zipMethod == DEFLATED && raf == null;
}
/**
http://git-wip-us.apache.org/repos/asf/ant/blob/2c04d7e8/src/main/org/apache/tools/zip/ZipShort.java
----------------------------------------------------------------------
diff --git a/src/main/org/apache/tools/zip/ZipShort.java b/src/main/org/apache/tools/zip/ZipShort.java
index 5d1d0f4..e52c570 100644
--- a/src/main/org/apache/tools/zip/ZipShort.java
+++ b/src/main/org/apache/tools/zip/ZipShort.java
@@ -66,12 +66,24 @@ public final class ZipShort implements Cloneable {
*/
public byte[] getBytes() {
byte[] result = new byte[2];
- result[0] = (byte) (value & BYTE_MASK);
- result[1] = (byte) ((value & BYTE_1_MASK) >> BYTE_1_SHIFT);
+ putShort(value, result, 0);
return result;
}
/**
+ * put the value as two bytes in big endian byte order.
+ * @param value the Java int to convert to bytes
+ * @param buf the output buffer
+ * @param offset
+ * The offset within the output buffer of the first byte to be written.
+ * must be non-negative and no larger than <tt>buf.length-2</tt>
+ */
+ public static void putShort(int value, byte[] buf, int offset) {
+ buf[offset] = (byte) (value & BYTE_MASK);
+ buf[offset+1] = (byte) ((value & BYTE_1_MASK) >> BYTE_1_SHIFT);
+ }
+
+ /**
* Get value as Java int.
* @return value as a Java int
* @since 1.1
http://git-wip-us.apache.org/repos/asf/ant/blob/2c04d7e8/src/main/org/apache/tools/zip/ZipUtil.java
----------------------------------------------------------------------
diff --git a/src/main/org/apache/tools/zip/ZipUtil.java b/src/main/org/apache/tools/zip/ZipUtil.java
index f07e1ee..c25b8c7 100644
--- a/src/main/org/apache/tools/zip/ZipUtil.java
+++ b/src/main/org/apache/tools/zip/ZipUtil.java
@@ -49,12 +49,32 @@ public abstract class ZipUtil {
* @return the date as a byte array
*/
public static byte[] toDosTime(long t) {
- Calendar c = Calendar.getInstance();
+ byte[] result = new byte[4];
+ toDosTime(t, result, 0);
+ return result;
+ }
+
+ /**
+ * Convert a Date object to a DOS date/time field.
+ *
+ * <p>Stolen from InfoZip's <code>fileio.c</code></p>
+ * @param t number of milliseconds since the epoch
+ * @param buf the output buffer
+ * @param offset
+ * The offset within the output buffer of the first byte to be written.
+ * must be non-negative and no larger than <tt>buf.length-4</tt>
+ */
+ public static void toDosTime(long t, byte[] buf, int offset) {
+ toDosTime(Calendar.getInstance(), t, buf, offset);
+ }
+
+ static void toDosTime(Calendar c, long t, byte[] buf, int offset) {
c.setTimeInMillis(t);
int year = c.get(Calendar.YEAR);
if (year < 1980) {
- return copy(DOS_TIME_MIN); // stop callers from changing the array
+ System.arraycopy(DOS_TIME_MIN, 0, buf, offset, DOS_TIME_MIN.length);// stop callers from changing the array
+ return;
}
int month = c.get(Calendar.MONTH) + 1;
long value = ((year - 1980) << 25)
@@ -63,7 +83,7 @@ public abstract class ZipUtil {
| (c.get(Calendar.HOUR_OF_DAY) << 11)
| (c.get(Calendar.MINUTE) << 5)
| (c.get(Calendar.SECOND) >> 1);
- return ZipLong.getBytes(value);
+ ZipLong.putLong(value, buf, offset);
}
/**