Author: krosenvold
Date: Wed Dec 24 14:00:44 2014
New Revision: 1647787

URL: http://svn.apache.org/r1647787
Log:
Cut overall object instantiation in half by changing file
header generation algorithm, for a 10-15 percent performance
improvement when archive contains lots of small files.

Also extracted two private methods createLocalFileHeader
and createCentralFileHeader in ZipArchiveOutputStream.

These may have some interesting additional usages in the near future.

This is basically a "tactical refactoring"; scatter/gather had a
wonderful performance in the "scatter" algorithm, but spent
an *equal* amount of time in the "gather" phase

Modified:
    commons/proper/compress/trunk/src/changes/changes.xml
    
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/GeneralPurposeBit.java
    
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
    
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipLong.java
    
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java
    
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipUtil.java
    
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipLongTest.java
    
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipShortTest.java
    
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipUtilTest.java

Modified: commons/proper/compress/trunk/src/changes/changes.xml
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/changes/changes.xml?rev=1647787&r1=1647786&r2=1647787&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/changes/changes.xml (original)
+++ commons/proper/compress/trunk/src/changes/changes.xml Wed Dec 24 14:00:44 
2014
@@ -44,6 +44,18 @@ The <action> type attribute can be add,u
   <body>
     <release version="1.10" date="not released, yet"
              description="Release 1.10">
+      <action type="add" date="2014-12-24"
+              due-to="Kristian Rosenvold">
+        Cut overall object instantiation in half by changing file
+        header generation algorithm, for a 10-15 percent performance
+        improvement.
+
+        Also extracted two private methods createLocalFileHeader
+        and createCentralFileHeader in ZipArchiveOutputStream.
+        These may have some interesting additional usages in the
+        near future.
+      </action>
+
       <action type="update" date="2014-10-28"
               due-to="Damjan Jovanovic">
         Moved the package

Modified: 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/GeneralPurposeBit.java
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/GeneralPurposeBit.java?rev=1647787&r1=1647786&r2=1647787&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/GeneralPurposeBit.java
 (original)
+++ 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/GeneralPurposeBit.java
 Wed Dec 24 14:00:44 2014
@@ -158,15 +158,29 @@ public final class GeneralPurposeBit {
      * 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);
     }
 
     /**

Modified: 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java?rev=1647787&r1=1647786&r2=1647787&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
 (original)
+++ 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
 Wed Dec 24 14:00:44 2014
@@ -24,7 +24,6 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
-import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -45,6 +44,8 @@ import static org.apache.commons.compres
 import static 
org.apache.commons.compress.archivers.zip.ZipConstants.ZIP64_MAGIC;
 import static 
org.apache.commons.compress.archivers.zip.ZipConstants.ZIP64_MAGIC_SHORT;
 import static 
org.apache.commons.compress.archivers.zip.ZipConstants.ZIP64_MIN_VERSION;
+import static org.apache.commons.compress.archivers.zip.ZipLong.putLong;
+import static org.apache.commons.compress.archivers.zip.ZipShort.putShort;
 
 /**
  * Reimplementation of {@link java.util.zip.ZipOutputStream
@@ -77,6 +78,34 @@ import static org.apache.commons.compres
 public class ZipArchiveOutputStream extends ArchiveOutputStream {
 
     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. protected for use in Jar 
implementation */
     protected boolean finished = false;
@@ -663,7 +692,7 @@ public class ZipArchiveOutputStream exte
             def.setLevel(level);
             hasCompressionLevelChanged = false;
         }
-        writeLocalFileHeader(entry.entry);
+        writeLocalFileHeader((ZipArchiveEntry)archiveEntry);
     }
 
     /**
@@ -931,7 +960,6 @@ public class ZipArchiveOutputStream exte
      * @throws IOException on error
      */
     protected void writeLocalFileHeader(ZipArchiveEntry ze) throws IOException 
{
-
         boolean encodable = zipEncoding.canEncode(ze.getName());
         ByteBuffer name = getName(ze);
 
@@ -939,78 +967,79 @@ public class ZipArchiveOutputStream exte
             addUnicodeExtraFields(ze, encodable, name);
         }
 
-        offsets.put(ze, Long.valueOf(written));
+        final byte[] localHeader = createLocalFileHeader(ze, name, encodable);
+        offsets.put(ze, written);
+        entry.localDataStart = written + 14; // Right before crc
+        writeOut( localHeader);
+        written += localHeader.length;
+        entry.dataStart = written;
+    }
+
+
+    private byte[] createLocalFileHeader(ZipArchiveEntry ze, ByteBuffer name, 
boolean encodable)  {
 
-        writeOut(LFH_SIG);
-        written += WORD;
+        byte[] extra = ze.getLocalFileDataExtra();
+        int len= LFH_FILENAME_OFFSET + name.limit() + extra.length;
+        byte[] buf = new byte[len];
+
+        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(ze.getTime(), buf, LFH_TIME_OFFSET);
 
         // CRC
         // compressed length
         // uncompressed length
-        entry.localDataStart = written;
         if (zipMethod == DEFLATED || raf != null) {
-            writeOut(LZERO);
+            System.arraycopy(LZERO, 0, buf, LFH_CRC_OFFSET, WORD);
             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());
+                ZipLong.ZIP64_MAGIC.putLong(buf, LFH_COMPRESSED_SIZE_OFFSET);
+                ZipLong.ZIP64_MAGIC.putLong(buf, LFH_ORIGINAL_SIZE_OFFSET);
             } else {
-                writeOut(LZERO);
-                writeOut(LZERO);
+                System.arraycopy(LZERO, 0, buf, LFH_COMPRESSED_SIZE_OFFSET, 
WORD);
+                System.arraycopy(LZERO, 0, buf, LFH_ORIGINAL_SIZE_OFFSET, 
WORD);
             }
         } else {
-            writeOut(ZipLong.getBytes(ze.getCrc()));
-            byte[] size = ZipLong.ZIP64_MAGIC.getBytes();
+            putLong(ze.getCrc(), buf, LFH_CRC_OFFSET);
             if (!hasZip64Extra(ze)) {
-                size = ZipLong.getBytes(ze.getSize());
+                putLong(ze.getSize(), buf, LFH_COMPRESSED_SIZE_OFFSET);
+                putLong(ze.getSize(), buf, LFH_ORIGINAL_SIZE_OFFSET);
+            } else {
+                ZipLong.ZIP64_MAGIC.putLong(buf, LFH_COMPRESSED_SIZE_OFFSET);
+                ZipLong.ZIP64_MAGIC.putLong(buf, LFH_ORIGINAL_SIZE_OFFSET);
             }
-            writeOut(size);
-            writeOut(size);
         }
-        // CheckStyle:MagicNumber OFF
-        written += 12;
-        // CheckStyle:MagicNumber ON
-
         // file name length
-        writeOut(ZipShort.getBytes(name.limit()));
-        written += SHORT;
+        putShort(name.limit(), 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();
-
-        // extra field
-        writeOut(extra);
-        written += extra.length;
+        final int nameLen = name.limit() - name.position();
+        System.arraycopy( name.array(), name.arrayOffset(), buf, 
LFH_FILENAME_OFFSET, nameLen);
 
-        entry.dataStart = written;
+        System.arraycopy(extra, 0, buf, LFH_FILENAME_OFFSET + nameLen, 
extra.length);
+        return buf;
     }
 
+
     /**
      * Adds UnicodeExtra fields for name and file comment if mode is
      * ALWAYS or the data cannot be encoded using the configured
@@ -1078,8 +1107,6 @@ public class ZipArchiveOutputStream exte
      * Zip64Mode#Never}.
      */
     protected void writeCentralFileHeader(ZipArchiveEntry ze) throws 
IOException {
-        writeOut(CFH_SIG);
-        written += WORD;
 
         final long lfhOffset = offsets.get(ze).longValue();
         final boolean needsZip64Extra = hasZip64Extra(ze)
@@ -1095,97 +1122,114 @@ public class ZipArchiveOutputStream exte
                                              .ARCHIVE_TOO_BIG_MESSAGE);
         }
 
+
         handleZip64Extra(ze, lfhOffset, needsZip64Extra);
 
+        byte[] centralFileHeader = createCentralFileHeader(ze, getName(ze), 
lfhOffset);
+        writeOut(centralFileHeader);
+        written += centralFileHeader.length;
+    }
+    /**
+     * 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
+     * @throws Zip64RequiredException if the archive's size exceeds 4
+     * GByte and {@link Zip64Mode #setUseZip64} is {@link
+     * Zip64Mode#Never}.
+     */
+    private byte[] createCentralFileHeader(ZipArchiveEntry ze, ByteBuffer 
name, long lfhOffset) throws IOException {
+        byte[] extra = ze.getCentralDirectoryExtra();
+
+        // file comment length
+        String comm = ze.getComment();
+        if (comm == null) {
+            comm = "";
+        }
+
+        ByteBuffer commentB = getEntryEncoding(ze).encode(comm);
+        int len= CFH_FILENAME_OFFSET + name.limit() + extra.length + 
commentB.limit();
+        byte[] buf = new byte[len];
+
+        System.arraycopy(CFH_SIG,  0, buf, CFH_SIG_OFFSET, WORD);
+
+        final boolean needsZip64Extra = hasZip64Extra(ze)
+                || 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 putArchiveEntry or
+            // closeArchiveEntry
+            throw new Zip64RequiredException(Zip64RequiredException
+                    .ARCHIVE_TOO_BIG_MESSAGE);
+        }
+
+        // todo: Do in caller !  handleZip64Extra(ze, lfhOffset, 
needsZip64Extra);
+
         // 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(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(name.limit(), 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(commentB.limit(), 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();
-
-        // extra field
-        writeOut(extra);
-        written += extra.length;
+        System.arraycopy(name.array(), name.arrayOffset(), buf, 
CFH_FILENAME_OFFSET, name.limit() - name.position());
+
+        int extraStart = CFH_FILENAME_OFFSET + name.limit();
+        System.arraycopy(extra, 0, buf, extraStart, extra.length);
+
+        int commentLength = commentB.limit() - commentB.position();
+        int commentStart = extraStart + commentLength;
 
         // file comment
-        writeOut(commentB.array(), commentB.arrayOffset(),
-                 commentB.limit() - commentB.position());
-        written += commentB.limit();
+        System.arraycopy(commentB.array(), commentB.arrayOffset(), buf, 
commentStart, commentLength);
+        return buf;
     }
 
     /**
@@ -1355,35 +1399,31 @@ public class ZipArchiveOutputStream exte
         }
     }
 
-    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;
     }
 
+
     /**
      * Creates a new zip entry taking some information from the given
      * file and using the provided name.

Modified: 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipLong.java
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipLong.java?rev=1647787&r1=1647786&r2=1647787&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipLong.java
 (original)
+++ 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipLong.java
 Wed Dec 24 14:00:44 2014
@@ -134,14 +134,31 @@ public final class ZipLong implements Cl
      */
     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

Modified: 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java?rev=1647787&r1=1647786&r2=1647787&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java
 (original)
+++ 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java
 Wed Dec 24 14:00:44 2014
@@ -85,12 +85,24 @@ public final class ZipShort implements C
      */
     public static byte[] getBytes(int value) {
         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);
+    }
+
+    /**
      * Helper method to get the value as a java int from two bytes starting at 
given array offset
      * @param bytes the array of bytes
      * @param offset the offset to start

Modified: 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipUtil.java
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipUtil.java?rev=1647787&r1=1647786&r2=1647787&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipUtil.java
 (original)
+++ 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipUtil.java
 Wed Dec 24 14:00:44 2014
@@ -51,21 +51,38 @@ public abstract class ZipUtil {
      * @return the date as a byte array
      */
     public static byte[] toDosTime(long t) {
+        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) {
         Calendar c = Calendar.getInstance();
         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)
-            |         (month << 21)
-            |         (c.get(Calendar.DAY_OF_MONTH) << 16)
-            |         (c.get(Calendar.HOUR_OF_DAY) << 11)
-            |         (c.get(Calendar.MINUTE) << 5)
-            |         (c.get(Calendar.SECOND) >> 1);
-        return ZipLong.getBytes(value);
+                |         (month << 21)
+                |         (c.get(Calendar.DAY_OF_MONTH) << 16)
+                |         (c.get(Calendar.HOUR_OF_DAY) << 11)
+                |         (c.get(Calendar.MINUTE) << 5)
+                |         (c.get(Calendar.SECOND) >> 1);
+        ZipLong.putLong(value, buf, offset);
     }
 
     /**
@@ -276,6 +293,12 @@ public abstract class ZipUtil {
         }
         return null;
     }
+    static void copy(byte[] from, byte[] to, int offset) {
+        if (from != null) {
+            System.arraycopy(from, 0, to, offset, from.length);
+        }
+    }
+
 
     /**
      * Whether this library is able to read or write the given entry.

Modified: 
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipLongTest.java
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipLongTest.java?rev=1647787&r1=1647786&r2=1647787&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipLongTest.java
 (original)
+++ 
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipLongTest.java
 Wed Dec 24 14:00:44 2014
@@ -44,6 +44,18 @@ public class ZipLongTest extends TestCas
     }
 
     /**
+     * Test conversion to bytes.
+     */
+    public void testPut() {
+        byte[] arr = new byte[5];
+        ZipLong.putLong(0x12345678, arr, 1);
+        assertEquals("first byte getBytes", 0x78, arr[1]);
+        assertEquals("second byte getBytes", 0x56, arr[2]);
+        assertEquals("third byte getBytes", 0x34, arr[3]);
+        assertEquals("fourth byte getBytes", 0x12, arr[4]);
+    }
+
+    /**
      * Test conversion from bytes.
      */
     public void testFromBytes() {

Modified: 
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipShortTest.java
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipShortTest.java?rev=1647787&r1=1647786&r2=1647787&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipShortTest.java
 (original)
+++ 
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipShortTest.java
 Wed Dec 24 14:00:44 2014
@@ -41,6 +41,18 @@ public class ZipShortTest extends TestCa
         assertEquals("second byte getBytes", 0x12, result[1]);
     }
 
+
+    /**
+     * Test conversion to bytes.
+     */
+    public void testPut() {
+        byte[] arr = new byte[3];
+        ZipShort.putShort(0x1234, arr, 1);
+        assertEquals("first byte getBytes", 0x34, arr[1]);
+        assertEquals("second byte getBytes", 0x12, arr[2]);
+    }
+
+
     /**
      * Test conversion from bytes.
      */

Modified: 
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipUtilTest.java
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipUtilTest.java?rev=1647787&r1=1647786&r2=1647787&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipUtilTest.java
 (original)
+++ 
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipUtilTest.java
 Wed Dec 24 14:00:44 2014
@@ -86,6 +86,22 @@ public class ZipUtilTest extends TestCas
         assertEquals(b10,b2[0]); // first byte should still be the same
     }
 
+    public void testOutsideCalendar(){
+        byte[] b1 = ZipUtil.toDosTime(160441200000L); // 1.1..1975
+        assertEquals(0, b1[0]);
+        assertEquals(33, b1[1]);
+        assertEquals(0, b1[2]);
+        assertEquals(0, b1[3]);
+    }
+
+    public void testInsideCalendar(){
+        byte[] b1 = ZipUtil.toDosTime(476096400000L); // 1.1.1985, 10:00 am
+        assertEquals(0, b1[0]);
+        assertEquals(80, b1[1]);
+        assertEquals(65, b1[2]);
+        assertEquals(10, b1[3]);
+    }
+
     public void testReverse() {
         byte[][] bTest = new byte[6][];
         bTest[0] = new byte[]{};


Reply via email to