You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by da...@apache.org on 2013/12/06 20:20:00 UTC

svn commit: r1548677 - in /commons/proper/compress/trunk/src: main/java/org/apache/commons/compress/archivers/zip/ main/java/org/apache/commons/compress/compressors/z/ site/xdoc/

Author: damjan
Date: Fri Dec  6 19:20:00 2013
New Revision: 1548677

URL: http://svn.apache.org/r1548677
Log:
Add an UnshrinkingInputStream based on ZCompressorInputStream,
to decompress ZIP method 1.


Added:
    commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnshrinkingInputStream.java   (with props)
Modified:
    commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java
    commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipUtil.java
    commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/z/ZCompressorInputStream.java
    commons/proper/compress/trunk/src/site/xdoc/zip.xml

Added: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnshrinkingInputStream.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnshrinkingInputStream.java?rev=1548677&view=auto
==============================================================================
--- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnshrinkingInputStream.java (added)
+++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnshrinkingInputStream.java Fri Dec  6 19:20:00 2013
@@ -0,0 +1,222 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.commons.compress.archivers.zip;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.compress.compressors.CompressorInputStream;
+
+/**
+ * Input stream that decompresses ZIP method 1 (unshrinking). A variation of the LZW algorithm, with some twists.
+ * @NotThreadSafe
+ * @since 1.7
+ */
+public class UnshrinkingInputStream extends CompressorInputStream {
+    private final InputStream in;
+    private final int clearCode;
+    private final int MAX_CODE_SIZE = 13;
+    private int codeSize = 9;
+    private int bitsCached = 0;
+    private int bitsCachedSize = 0;
+    private int previousCode = -1;
+    private int tableSize = 0;
+    private final int[] prefixes;
+    private final byte[] characters;
+    private final boolean[] isUsed;
+    private final byte[] outputStack;
+    private int outputStackLocation;
+    
+    public UnshrinkingInputStream(InputStream inputStream) throws IOException {
+        this.in = inputStream;
+        clearCode = (1 << (codeSize - 1));
+        final int maxTableSize = 1 << MAX_CODE_SIZE;
+        prefixes = new int[maxTableSize];
+        characters = new byte[maxTableSize];
+        isUsed = new boolean[maxTableSize];
+        outputStack = new byte[maxTableSize];
+        outputStackLocation = maxTableSize;
+        for (int i = 0; i < (1 << 8); i++) {
+            prefixes[i] = -1;
+            characters[i] = (byte)i;
+            isUsed[i] = true;
+        }
+        tableSize = clearCode + 1;
+    }
+    
+    public void close() throws IOException {
+        in.close();
+    }
+    
+    private int readNextCode() throws IOException {
+        while (bitsCachedSize < codeSize) {
+            final int nextByte = in.read();
+            if (nextByte < 0) {
+                return nextByte;
+            }
+            bitsCached |= (nextByte << bitsCachedSize);
+            bitsCachedSize += 8;
+        }
+        final int mask = (1 << codeSize) - 1;
+        final int code = (bitsCached & mask);
+        bitsCached >>>= codeSize;
+        bitsCachedSize -= codeSize;
+        return code;
+    }
+    
+    private int addEntry(int previousCode, byte character) throws IOException {
+        final int maxTableSize = 1 << MAX_CODE_SIZE;
+        while ((tableSize < maxTableSize) && isUsed[tableSize]) {
+            tableSize++;
+        }
+        if (tableSize < maxTableSize) {
+            final int index = tableSize;
+            prefixes[tableSize] = previousCode;
+            characters[tableSize] = character;
+            isUsed[tableSize] = true;
+            tableSize++;
+            return index;
+        } else {
+            return -1;
+        }
+    }
+    
+    private void partialClear() throws IOException {
+        final boolean[] isParent = new boolean[1 << MAX_CODE_SIZE];
+        for (int i = 0; i < isUsed.length; i++) {
+            if (isUsed[i] && prefixes[i] != -1) {
+                isParent[prefixes[i]] = true;
+            }
+        }
+        for (int i = clearCode + 1; i < isParent.length; i++) {
+            if (!isParent[i]) {
+                isUsed[i] = false;
+                prefixes[i] = -1;
+            }
+        }
+    }
+
+    private int decompressNextSymbol() throws IOException {
+        //
+        //                   table entry    table entry
+        //                  _____________   _____
+        //    table entry  /             \ /     \
+        //    ____________/               \       \
+        //   /           / \             / \       \
+        //  +---+---+---+---+---+---+---+---+---+---+
+        //  | . | . | . | . | . | . | . | . | . | . |
+        //  +---+---+---+---+---+---+---+---+---+---+
+        //  |<--------->|<------------->|<----->|<->|
+        //     symbol        symbol      symbol  symbol
+        //
+        final int code = readNextCode();
+        if (code < 0) {
+            return -1;
+        } else if (code == clearCode) {
+            final int subCode = readNextCode();
+            if (subCode < 0) {
+                throw new IOException("Unexpected EOF;");
+            } else if (subCode == 1) {
+                if (codeSize < MAX_CODE_SIZE) {
+                    codeSize++;
+                } else {
+                    throw new IOException("Attempt to increase code size beyond maximum");
+                }
+            } else if (subCode == 2) {
+                partialClear();
+                tableSize = clearCode + 1;
+            } else {
+                throw new IOException("Invalid clear code subcode " + subCode);
+            }
+            return 0;
+        } else {
+            boolean addedUnfinishedEntry = false;
+            final int effectiveCode;
+            if (isUsed[code]) {
+                effectiveCode = code;
+            } else {
+                // must be a repeat of the previous entry we haven't added yet
+                if (previousCode == -1) {
+                    // ... which isn't possible for the very first code
+                    throw new IOException("The first code can't be a reference to its preceding code");
+                }
+                byte firstCharacter = 0;
+                for (int last = previousCode; last >= 0; last = prefixes[last]) {
+                    firstCharacter = characters[last];
+                }
+                effectiveCode = addEntry(previousCode, firstCharacter);
+                addedUnfinishedEntry = true;
+            }
+            for (int entry = effectiveCode; entry >= 0; entry = prefixes[entry]) {
+                outputStack[--outputStackLocation] = characters[entry];
+            }
+            if (previousCode != -1 && !addedUnfinishedEntry) {
+                addEntry(previousCode, outputStack[outputStackLocation]);
+            }
+            previousCode = code;
+            return outputStackLocation;
+        }
+    }
+    
+    public int read() throws IOException {
+        byte[] b = new byte[1];
+        int ret;
+        while ((ret = read(b)) == 0) {
+        }
+        if (ret < 0) {
+            return ret;
+        }
+        return 0xff & b[0];
+    }
+    
+    public int read(byte[] b, int off, int len) throws IOException {
+        int bytesRead = 0;
+        int remainingInStack = outputStack.length - outputStackLocation;
+        if (remainingInStack > 0) {
+            int maxLength = Math.min(remainingInStack, len);
+            System.arraycopy(outputStack, outputStackLocation, b, off, maxLength);
+            outputStackLocation += maxLength;
+            off += maxLength;
+            len -= maxLength;
+            bytesRead += maxLength;
+        }
+        while (len > 0) {
+            int result = decompressNextSymbol();
+            if (result < 0) {
+                if (bytesRead > 0) {
+                    count(bytesRead);
+                    return bytesRead;
+                } else {
+                    return result;
+                }
+            }
+            remainingInStack = outputStack.length - outputStackLocation;
+            if (remainingInStack > 0) {
+                int maxLength = Math.min(remainingInStack, len);
+                System.arraycopy(outputStack, outputStackLocation, b, off, maxLength);
+                outputStackLocation += maxLength;
+                off += maxLength;
+                len -= maxLength;
+                bytesRead += maxLength;
+            }
+        }
+        count(bytesRead);
+        return bytesRead;
+    }
+}

Propchange: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnshrinkingInputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java?rev=1548677&r1=1548676&r2=1548677&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java (original)
+++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java Fri Dec  6 19:20:00 2013
@@ -374,10 +374,12 @@ public class ZipFile {
         long start = offsetEntry.dataOffset;
         BoundedInputStream bis =
             new BoundedInputStream(start, ze.getCompressedSize());
-        switch (ze.getMethod()) {
-            case ZipEntry.STORED:
+        switch (ZipMethod.getMethodByCode(ze.getMethod())) {
+            case STORED:
                 return bis;
-            case ZipEntry.DEFLATED:
+            case UNSHRINKING:
+                return new UnshrinkingInputStream(bis);
+            case DEFLATED:
                 bis.addDummy();
                 final Inflater inflater = new Inflater(true);
                 return new InflaterInputStream(bis, inflater) {

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=1548677&r1=1548676&r2=1548677&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 Fri Dec  6 19:20:00 2013
@@ -302,6 +302,7 @@ public abstract class ZipUtil {
      */
     private static boolean supportsMethodOf(ZipArchiveEntry entry) {
         return entry.getMethod() == ZipEntry.STORED
+            || entry.getMethod() == ZipMethod.UNSHRINKING.getCode()
             || entry.getMethod() == ZipEntry.DEFLATED;
     }
 

Modified: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/z/ZCompressorInputStream.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/z/ZCompressorInputStream.java?rev=1548677&r1=1548676&r2=1548677&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/z/ZCompressorInputStream.java (original)
+++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/z/ZCompressorInputStream.java Fri Dec  6 19:20:00 2013
@@ -162,7 +162,7 @@ public class ZCompressorInputStream exte
                 // must be a repeat of the previous entry we haven't added yet
                 if (previousCode == -1) {
                     // ... which isn't possible for the very first code
-                    throw new IOException("The first code can't be a reference to code before itself");
+                    throw new IOException("The first code can't be a reference to its preceding code");
                 }
                 byte firstCharacter = 0;
                 for (int last = previousCode; last >= 0; last = prefixes[last]) {

Modified: commons/proper/compress/trunk/src/site/xdoc/zip.xml
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/site/xdoc/zip.xml?rev=1548677&r1=1548676&r2=1548677&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/site/xdoc/zip.xml (original)
+++ commons/proper/compress/trunk/src/site/xdoc/zip.xml Fri Dec  6 19:20:00 2013
@@ -280,7 +280,8 @@
         <p>In most cases entries of an archive are not encrypted and
         are either not compressed at all or use the DEFLATED
         algorithm, Commons Compress' ZIP archiver will handle them
-        just fine.</p>
+        just fine. As of version 1.7, Commons Compress can also decompress
+        entries compressed with the UNSHRINKING algorithm.</p>
 
         <p>The ZIP specification allows for various other compression
         algorithms and also supports several different ways of



Re: svn commit: r1548677 - in /commons/proper/compress/trunk/src: main/java/org/apache/commons/compress/archivers/zip/ main/java/org/apache/commons/compress/compressors/z/ site/xdoc/

Posted by Damjan Jovanovic <da...@apache.org>.
On Thu, Dec 12, 2013 at 4:11 PM, Emmanuel Bourg <eb...@apache.org> wrote:
> Le 06/12/2013 20:20, damjan@apache.org a écrit :
>
>> +public class UnshrinkingInputStream extends CompressorInputStream {
>
> What about keeping this class package private? I don't think it's
> necessary to make it part of the public API.

Ok.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: svn commit: r1548677 - in /commons/proper/compress/trunk/src: main/java/org/apache/commons/compress/archivers/zip/ main/java/org/apache/commons/compress/compressors/z/ site/xdoc/

Posted by Emmanuel Bourg <eb...@apache.org>.
Le 06/12/2013 20:20, damjan@apache.org a écrit :

> +public class UnshrinkingInputStream extends CompressorInputStream {

What about keeping this class package private? I don't think it's
necessary to make it part of the public API.

Emmanuel Bourg


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org