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/11/30 09:27:57 UTC

svn commit: r1546698 - in /commons/proper/compress/trunk: ./ src/main/java/org/apache/commons/compress/compressors/z/ src/site/xdoc/ src/test/java/org/apache/commons/compress/compressors/ src/test/resources/

Author: damjan
Date: Sat Nov 30 08:27:56 2013
New Revision: 1546698

URL: http://svn.apache.org/r1546698
Log:
Add read-only support for the .Z file format.


Added:
    commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/z/
    commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/z/ZCompressorInputStream.java   (with props)
    commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/z/package.html   (with props)
    commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/compressors/ZTestCase.java   (with props)
    commons/proper/compress/trunk/src/test/resources/bla.tar.Z   (with props)
Modified:
    commons/proper/compress/trunk/RELEASE-NOTES.txt
    commons/proper/compress/trunk/src/site/xdoc/examples.xml
    commons/proper/compress/trunk/src/site/xdoc/index.xml

Modified: commons/proper/compress/trunk/RELEASE-NOTES.txt
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/RELEASE-NOTES.txt?rev=1546698&r1=1546697&r2=1546698&view=diff
==============================================================================
--- commons/proper/compress/trunk/RELEASE-NOTES.txt (original)
+++ commons/proper/compress/trunk/RELEASE-NOTES.txt Sat Nov 30 08:27:56 2013
@@ -5,7 +5,7 @@ commons-compress-1.6 release!
 
 Apache Commons Compress software defines an API for working with
 compression and archive formats.  These include: bzip2, gzip, pack200,
-xz and ar, cpio, jar, tar, zip, dump, 7z, arj.
+xz and ar, cpio, jar, tar, zip, dump, 7z, arj, Z.
 
 Version 1.6 introduces changes to the internal API of the tar package that 
 break backwards compatibility in the following rare cases.  This version 

Added: 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=1546698&view=auto
==============================================================================
--- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/z/ZCompressorInputStream.java (added)
+++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/z/ZCompressorInputStream.java Sat Nov 30 08:27:56 2013
@@ -0,0 +1,240 @@
+/*
+ * 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.compressors.z;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.compress.compressors.CompressorInputStream;
+
+/**
+ * Input stream that decompresses .Z files.
+ * @NotThreadSafe
+ * @since 1.7
+ */
+public class ZCompressorInputStream extends CompressorInputStream {
+    private static final int MAGIC_1 = 0x1f;
+    private static final int MAGIC_2 = 0x9d;
+    private static final int BLOCK_MODE_MASK = 0x80;
+    private static final int MAX_CODE_SIZE_MASK = 0x1f;
+    private final InputStream in;
+    private final boolean blockMode;
+    private final int clearCode;
+    private final int maxCodeSize;
+    private int codeSize = 9;
+    private int bitsCached = 0;
+    private int bitsCachedSize = 0;
+    private long totalCodesRead = 0;
+    private int previousEntry = -1;
+    private int tableSize = 0;
+    private final int[] prefixes;
+    private final byte[] characters;
+    private final byte[] outputStack;
+    private int outputStackLocation;
+    
+    public ZCompressorInputStream(InputStream inputStream) throws IOException {
+        this.in = inputStream;
+        int firstByte = in.read();
+        int secondByte = in.read();
+        int thirdByte = in.read();
+        if (firstByte != MAGIC_1 || secondByte != MAGIC_2 || thirdByte < 0) {
+            throw new IOException("Input is not in .Z format");
+        }
+        blockMode = ((thirdByte & BLOCK_MODE_MASK) != 0);
+        maxCodeSize = thirdByte & MAX_CODE_SIZE_MASK;
+        if (blockMode) {
+            clearCode = (1 << (codeSize - 1));
+        } else {
+            clearCode = -1; // unused
+        }
+        final int maxTableSize = 1 << maxCodeSize;
+        prefixes = new int[maxTableSize];
+        characters = new byte[maxTableSize];
+        outputStack = new byte[maxTableSize];
+        outputStackLocation = maxTableSize;
+        for (int i = 0; i < (1 << 8); i++) {
+            prefixes[i] = -1;
+            characters[i] = (byte)i;
+        }
+        clearEntries();
+    }
+    
+    private void clearEntries() {
+        tableSize = (1 << 8);
+        if (blockMode) {
+            tableSize++;
+        }
+    }
+
+    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;
+        ++totalCodesRead;
+        return code;
+    }
+    
+    private void reAlignReading() throws IOException {
+        // "compress" works in multiples of 8 symbols, each codeBits bits long.
+        // When codeBits changes, the remaining unused symbols in the current
+        // group of 8 are still written out, in the old codeSize,
+        // as garbage values (usually zeroes) that need to be skipped.
+        long codeReadsToThrowAway = 8 - (totalCodesRead % 8);
+        if (codeReadsToThrowAway == 8) {
+            codeReadsToThrowAway = 0;
+        }
+        for (long i = 0; i < codeReadsToThrowAway; i++) {
+            readNextCode();
+        }
+        bitsCached = 0;
+        bitsCachedSize = 0;
+    }
+    
+    private void addEntry(int previousEntry, byte character) throws IOException {
+        if (tableSize >= ((1 << codeSize) - 1)) {
+            if (tableSize == ((1 << codeSize) - 1)) {
+                if (codeSize < maxCodeSize) {
+                    reAlignReading();
+                    codeSize++;
+                    prefixes[tableSize] = previousEntry;
+                    characters[tableSize] = character;
+                    tableSize++;
+                } else {
+                    prefixes[tableSize] = previousEntry;
+                    characters[tableSize] = character;
+                    tableSize++;
+                }
+            }
+        } else {
+            prefixes[tableSize] = previousEntry;
+            characters[tableSize] = character;
+            tableSize++;
+        }
+    }
+
+    public int read() throws IOException {
+        byte[] b = new byte[1];
+        int ret = read(b);
+        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) {
+                return (bytesRead > 0) ? bytesRead : 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;
+            }
+        }
+        return bytesRead;
+    }
+    
+    private int decompressNextSymbol() throws IOException {
+        //
+        //                   table entry    table entry
+        //                  _____________   _____
+        //    table entry  /             \ /     \
+        //    ____________/               \       \
+        //   /           / \             / \       \
+        //  +---+---+---+---+---+---+---+---+---+---+
+        //  | . | . | . | . | . | . | . | . | . | . |
+        //  +---+---+---+---+---+---+---+---+---+---+
+        //  |<--------->|<------------->|<----->|<->|
+        //     symbol        symbol      symbol  symbol
+        //
+        // Symbols are indexes into a table of table entries. Indexes
+        // sequentially increase up to the maximum size of the table.
+        // The bit count used by each index increases up to the minimum
+        // size needed the index the highest table entry.
+        //
+        // To construct a table entry for a symbol,
+        // we need the symbol's text, and the first character of the
+        // next symbol's text. When a symbol is the immediately previous
+        // table entry's symbol, that symbol's text is the previous symbol's text + 1 character.
+        //
+        // The compression process adds table entries after writing the symbol.
+        // Since adding entries can increase the code size, the 
+        //
+        final int code = readNextCode();
+        if (code < 0) {
+            return -1;
+        } else if (blockMode && code == clearCode) {
+            clearEntries();
+            reAlignReading();
+            codeSize = 9;
+            previousEntry = -1;
+            return 0;
+        } else {
+            boolean addedUnfinishedEntry = false;
+            if (code == tableSize) {
+                // must be a repeat of the previous entry we haven't added yet
+                if (previousEntry == -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");
+                }
+                byte firstCharacter = 0;
+                for (int last = previousEntry; last >= 0; last = prefixes[last]) {
+                    firstCharacter = characters[last];
+                }
+                addEntry(previousEntry, firstCharacter);
+                addedUnfinishedEntry = true;
+            } else if (code > tableSize) {
+                throw new IOException(String.format("Invalid %d bit code 0x%x", codeSize, code));
+            }
+            for (int entry = code; entry >= 0; entry = prefixes[entry]) {
+                outputStack[--outputStackLocation] = characters[entry];
+            }
+            if (previousEntry != -1 && !addedUnfinishedEntry) {
+                addEntry(previousEntry, outputStack[outputStackLocation]);
+            }
+            previousEntry = code;
+            return outputStackLocation;
+        }
+    }
+}

Propchange: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/z/ZCompressorInputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/z/package.html
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/z/package.html?rev=1546698&view=auto
==============================================================================
--- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/z/package.html (added)
+++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/z/package.html Sat Nov 30 08:27:56 2013
@@ -0,0 +1,24 @@
+<html>
+<!--
+
+   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.
+
+-->
+  <body>
+    <p>Provides stream classes for decompressing
+      streams using the "compress" algorithm used to write .Z files.</p>
+  </body>
+</html>

Propchange: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/z/package.html
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: commons/proper/compress/trunk/src/site/xdoc/examples.xml
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/site/xdoc/examples.xml?rev=1546698&r1=1546697&r2=1546698&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/site/xdoc/examples.xml (original)
+++ commons/proper/compress/trunk/src/site/xdoc/examples.xml Sat Nov 30 08:27:56 2013
@@ -31,13 +31,13 @@
         collect multiple entries inside a single (potentially
         compressed) archive are archiver formats.</p>
 
-        <p>The compressor formats supported are gzip, bzip2, xz, lzma
-        and Pack200, the archiver formats are 7z, ar, arj, cpio, dump,
+        <p>The compressor formats supported are gzip, bzip2, xz, lzma,
+        Pack200a and Z, the archiver formats are 7z, ar, arj, cpio, dump,
         tar and zip.  Pack200 is a special case as it can only
         compress JAR files.</p>
 
-        <p>We currently only provide read support for lzma, arj and
-        dump.  arj can only read uncompressed archives, 7z can read
+        <p>We currently only provide read support for lzma, arj,
+        dump and Z.  arj can only read uncompressed archives, 7z can read
         archives with many compression and encryption algorithms
         supported by 7z but doesn't support encryption when writing
         archives.</p>
@@ -433,6 +433,27 @@ out.close();
 xzIn.close();
 ]]></source>
       </subsection>
+      
+      <subsection name="Z">
+
+        <p>Uncompressing a given Z compressed file (you would
+          certainly add exception handling and make sure all streams
+          get closed properly):</p>
+<source><![CDATA[
+FileInputStream fin = new FileInputStream("archive.tar.Z");
+BufferedInputStream in = new BufferedInputStream(fin);
+FileOutputStream out = new FileOutputStream("archive.tar");
+ZCompressorInputStream zIn = new ZCompressorInputStream(in);
+final byte[] buffer = new byte[buffersize];
+int n = 0;
+while (-1 != (n = zIn.read(buffer))) {
+    out.write(buffer, 0, n);
+}
+out.close();
+zIn.close();
+]]></source>
+
+      </subsection>
 
       <subsection name="lzma">
 

Modified: commons/proper/compress/trunk/src/site/xdoc/index.xml
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/site/xdoc/index.xml?rev=1546698&r1=1546697&r2=1546698&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/site/xdoc/index.xml (original)
+++ commons/proper/compress/trunk/src/site/xdoc/index.xml Sat Nov 30 08:27:56 2013
@@ -27,7 +27,7 @@
             <p>
                 The Apache Commons Compress library defines an API for
                 working with ar, cpio, Unix dump, tar, zip, gzip, XZ, Pack200,
-                bzip2, 7z, arj and lzma files.
+                bzip2, 7z, arj, lzma and Z files.
             </p>
             <p>
                 The code in this component has many origins:
@@ -78,7 +78,7 @@
             by <code>ArchiveEntry</code> instances which in turn
             usually correspond to single files or directories.</p>
 
-          <p>Currently the bzip2, Pack200, XZ, gzip and lzma formats are
+          <p>Currently the bzip2, Pack200, XZ, gzip, lzma and Z formats are
             supported as compressors where gzip support is mostly provided by
             the <code>java.util.zip</code> package and Pack200 support
             by the <code>java.util.jar</code> package of the Java

Added: commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/compressors/ZTestCase.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/compressors/ZTestCase.java?rev=1546698&view=auto
==============================================================================
--- commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/compressors/ZTestCase.java (added)
+++ commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/compressors/ZTestCase.java Sat Nov 30 08:27:56 2013
@@ -0,0 +1,52 @@
+/*
+ * 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.compressors;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+
+import org.apache.commons.compress.AbstractTestCase;
+import org.apache.commons.compress.compressors.z.ZCompressorInputStream;
+import org.apache.commons.compress.utils.IOUtils;
+
+public final class ZTestCase extends AbstractTestCase {
+
+    public void testZUnarchive() throws Exception {
+        final File input = getFile("bla.tar.Z");
+        final File output = new File(dir, "bla.tar");
+        final InputStream is = new FileInputStream(input);
+        try {
+            final CompressorInputStream in = new ZCompressorInputStream(is);
+            FileOutputStream out = null;
+            try {
+                out = new FileOutputStream(output);
+                IOUtils.copy(in, out);
+            } finally {
+                if (out != null) {
+                    out.close();
+                }
+                in.close();
+            }
+        } finally {
+            is.close();
+        }
+    }
+}

Propchange: commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/compressors/ZTestCase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: commons/proper/compress/trunk/src/test/resources/bla.tar.Z
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/resources/bla.tar.Z?rev=1546698&view=auto
==============================================================================
Binary file - no diff available.

Propchange: commons/proper/compress/trunk/src/test/resources/bla.tar.Z
------------------------------------------------------------------------------
    svn:mime-type = application/x-compress