You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by bo...@apache.org on 2013/11/09 22:36:53 UTC

svn commit: r1540399 - in /commons/proper/compress/trunk/src: main/java/org/apache/commons/compress/compressors/snappy/ test/java/org/apache/commons/compress/compressors/ test/resources/

Author: bodewig
Date: Sat Nov  9 21:36:52 2013
New Revision: 1540399

URL: http://svn.apache.org/r1540399
Log:
COMPRESS-147 incomplete InputStreams for Snappy

missing (among other things):

* tests with more than 32k of data
* CRC handling
* other chunk types

Added:
    commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/snappy/FramedSnappyCompressorInputStream.java   (with props)
    commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/snappy/SnappyCompressorInputStream.java
      - copied, changed from r1540086, commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/snappy/SnappyDecompressor.java
    commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/compressors/FramedSnappyTestCase.java   (with props)
    commons/proper/compress/trunk/src/test/resources/bla.tar.sz   (with props)

Added: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/snappy/FramedSnappyCompressorInputStream.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/snappy/FramedSnappyCompressorInputStream.java?rev=1540399&view=auto
==============================================================================
--- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/snappy/FramedSnappyCompressorInputStream.java (added)
+++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/snappy/FramedSnappyCompressorInputStream.java Sat Nov  9 21:36:52 2013
@@ -0,0 +1,226 @@
+/*
+ * 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.snappy;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+import java.util.Arrays;
+
+import org.apache.commons.compress.compressors.CompressorInputStream;
+import org.apache.commons.compress.utils.BoundedInputStream;
+import org.apache.commons.compress.utils.IOUtils;
+
+/**
+ * CompressorInputStream for the framing Snappy format.
+ *
+ * @see "http://code.google.com/p/snappy/source/browse/trunk/framing_format.txt"
+ * @since 1.7
+ */
+public class FramedSnappyCompressorInputStream extends CompressorInputStream {
+    private static final int STREAM_IDENTIFIER_TYPE = 0xff;
+    private static final int COMPRESSED_CHUNK_TYPE = 0;
+    private static final int UNCOMPRESSED_CHUNK_TYPE = 1;
+    private static final int PADDING_CHUNK_TYPE = 0xfe;
+    private static final int MIN_UNSKIPPABLE_TYPE = 2;
+    private static final int MAX_UNSKIPPABLE_TYPE = 0x7f;
+    private static final int MAX_SKIPPABLE_TYPE = 0xfd;
+
+    /** The underlying stream to read compressed data from */
+    private final PushbackInputStream in;
+
+    private SnappyCompressorInputStream currentCompressedChunk;
+
+    // used in no-arg read method
+    private final byte[] oneByte = new byte[1];
+
+    private boolean endReached, inUncompressedChunk;
+
+    private int uncompressedBytesRemaining;
+
+    /**
+     * Constructs a new input stream that decompresses snappy-framed-compressed data
+     * from the specified input stream.
+     * @param in  the InputStream from which to read the compressed data
+     */
+    public FramedSnappyCompressorInputStream(InputStream in) throws IOException {
+        this.in = new PushbackInputStream(in, 1);
+        readStreamIdentifier();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int read() throws IOException {
+        return read(oneByte, 0, 1) == -1 ? -1 : (oneByte[0] & 0xFF);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void close() throws IOException {
+        in.close();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        int read = readOnce(b, off, len);
+        if (read == -1) {
+            readNextBlock();
+            if (endReached) {
+                return -1;
+            }
+            read = readOnce(b, off, len);
+        }
+        return read;
+    }
+
+    /**
+     * Read from the current chunk into the given array.
+     *
+     * @return -1 if there is no current chunk or the number of bytes
+     * read from the current chunk (which may be -1 if the end od the
+     * chunk is reached.
+     */
+    private int readOnce(byte[] b, int off, int len) throws IOException {
+        int read = -1;
+        if (inUncompressedChunk) {
+            int amount = Math.min(uncompressedBytesRemaining, len);
+            read = in.read(b, off, amount);
+            if (read != -1) {
+                uncompressedBytesRemaining -= read;
+                count(read);
+            }
+        } else if (currentCompressedChunk != null) {
+            long before = currentCompressedChunk.getBytesRead();
+            read = currentCompressedChunk.read(b, off, len);
+            if (read == -1) {
+                currentCompressedChunk = null;
+            } else {
+                count(currentCompressedChunk.getBytesRead() - before);
+            }
+        }
+        return read;
+    }
+
+    private void readNextBlock() throws IOException {
+        int type = readOneByte();
+        if (type == -1) {
+            endReached = true;
+        } else if (type == STREAM_IDENTIFIER_TYPE) {
+            in.unread(type);
+            count(-1);
+            readStreamIdentifier();
+            readNextBlock();
+        } else if (type == PADDING_CHUNK_TYPE
+                   || (type > MAX_UNSKIPPABLE_TYPE && type <= MAX_SKIPPABLE_TYPE)) {
+            skipBlock();
+            readNextBlock();
+        } else if (type >= MIN_UNSKIPPABLE_TYPE && type <= MAX_UNSKIPPABLE_TYPE) {
+            throw new IOException("unskippable chunk with type " + type
+                                  + " detected.");
+        } else if (type == UNCOMPRESSED_CHUNK_TYPE) {
+            uncompressedBytesRemaining = readSize();
+            readCrc();
+        } else if (type == COMPRESSED_CHUNK_TYPE) {
+            int size = readSize();
+            readCrc();
+            currentCompressedChunk =
+                new SnappyCompressorInputStream(new BoundedInputStream(in, size));
+        } else {
+            // impossible as all potential byte values have been covered
+            throw new IOException("unknown chunk type " + type
+                                  + " detected.");
+        }
+    }
+
+    private void readCrc() throws IOException {
+        byte[] b = new byte[4];
+        if (IOUtils.readFully(in, b) != 4) {
+            throw new IOException("premature end of stream");
+        }
+        count(4);
+    }
+
+    private int readSize() throws IOException {
+        int b = 0;
+        int sz = 0;
+        for (int i = 0; i < 3; i++) {
+            b = readOneByte();
+            if (b == -1) {
+                throw new IOException("premature end of stream");
+            }
+            sz |= (b << (i * 8));
+        }
+        return sz;
+    }
+
+    private void skipBlock() throws IOException {
+        int size = readSize();
+        if (IOUtils.skip(in, size) != size) {
+            throw new IOException("premature end of stream");
+        }
+        count(size);
+    }
+
+    private void readStreamIdentifier() throws IOException {
+        byte[] b = new byte[10];
+        if (10 != IOUtils.readFully(in, b) || !matches(b, 10)) {
+            throw new IOException("Not a framed Snappy stream");
+        }
+        count(10);
+    }
+
+    private int readOneByte() throws IOException {
+        int b = in.read();
+        if (b != -1) {
+            count(1);
+            return b & 0xFF;
+        }
+        return -1;
+    }
+
+    /**
+     * Checks if the signature matches what is expected for a .sz file.
+     *
+     * <p>.sz files start with a chuck with tag 0xff and content sNaPpY.
+     * 
+     * @param signature the bytes to check
+     * @param length    the number of bytes to check
+     * @return          true if this is a .sz stream, false otherwise
+     */
+    public static boolean matches(byte[] signature, int length) {
+
+        if (length < 10) {
+            return false;
+        }
+
+        byte[] shortenedSig = signature;
+        if (signature.length > 10) {
+            shortenedSig = new byte[10];
+            System.arraycopy(signature, 0, shortenedSig, 0, 10);
+        }
+
+        return Arrays.equals(shortenedSig, new byte[] {
+                (byte) STREAM_IDENTIFIER_TYPE, // tag
+                6, 0, 0, // length
+                's', 'N', 'a', 'P', 'p', 'Y'
+            });
+    }
+
+}

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

Copied: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/snappy/SnappyCompressorInputStream.java (from r1540086, commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/snappy/SnappyDecompressor.java)
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/snappy/SnappyCompressorInputStream.java?p2=commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/snappy/SnappyCompressorInputStream.java&p1=commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/snappy/SnappyDecompressor.java&r1=1540086&r2=1540399&rev=1540399&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/snappy/SnappyDecompressor.java (original)
+++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/snappy/SnappyCompressorInputStream.java Sat Nov  9 21:36:52 2013
@@ -18,85 +18,80 @@
  */
 package org.apache.commons.compress.compressors.snappy;
 
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
+
+import org.apache.commons.compress.compressors.CompressorInputStream;
 
 /**
- * This class implements Snappy decompression. Snappy is a LZ77-type compressor
- * with a fixed, byte-oriented encoding created by Google(tm). It is originally
- * based on text by Zeev Tarantov. This approach works by allocating a buffer 3x
- * the compression block size. As the stream is decompressed, it is written to
- * the buffer. When the buffer becomes 2/3 full, the first third is flushed to
- * the underlying stream and the following bytes are all shifted down the array.
- * In this way, there is always at least one block-size of buffer available for
- * back-references.
+ * CompressorInputStream for the raw Snappy format.
+ *
+ * <p>This implementation uses an internal buffer in order to handle
+ * the back-references that are at the heart of the LZ77 algorithm.
+ * The size of the buffer must be at least as big as the biggest
+ * offset used in the compressed stream.  The current version of the
+ * Snappy algorithm as defined by Google works on 32k blocks and
+ * doesn't contain offsets bigger than 32k which is the default block
+ * size used by this class.</p>
+ *
+ * @see "http://code.google.com/p/snappy/source/browse/trunk/format_description.txt"
+ * @since 1.7
  */
-public class SnappyDecompressor {
+public class SnappyCompressorInputStream extends CompressorInputStream {
 
     /** Mask used to determine the type of "tag" is being processed */
     private static final int TAG_MASK = 0x03;
 
     /** Default block size */
-    public static final int BLOCK_SIZE = 32768;
+    public static final int DEFAULT_BLOCK_SIZE = 32768;
 
     /** Buffer to write decompressed bytes to for back-references */
     private final byte[] decompressBuf;
 
-    /** The index of the next byte in the buffer to write to */
-    private int decompressBufIndex;
+    /** 
+     * One behind the index of the last byte in the buffer that was
+     * written
+     */
+    private int writeIndex;
+
+    /**
+     * Index of the next byte to be read.
+     */
+    private int readIndex;
 
     /** The actual block size specified */
-    protected final int blockSize;
+    private final int blockSize;
 
     /** The underlying stream to read compressed data from */
-    protected final InputStream in;
+    private final InputStream in;
 
     /** The size of the uncompressed data */
-    protected final int size;
+    private final int size;
 
     /**
-     * Constructor
-     * 
-     * @param buf
-     *            An array of compressed data
-     * 
-     * @throws IOException
+     * Number of uncompressed bytes still to be read.
      */
-    public SnappyDecompressor(final byte[] buf) throws IOException {
-        this(new ByteArrayInputStream(buf), BLOCK_SIZE);
-    }
+    private int uncompressedBytesRemaining;
 
-    /**
-     * Constructor
-     * 
-     * @param buf
-     *            An array of compressed data
-     * @param blockSize
-     *            The block size used in compression
-     * 
-     * @throws IOException
-     */
-    public SnappyDecompressor(final byte[] buf, int blockSize)
-            throws IOException {
-        this(new ByteArrayInputStream(buf), blockSize);
-    }
+    // used in no-arg read method
+    private final byte[] oneByte = new byte[1];
+
+    private boolean endReached = false;
 
     /**
-     * Constructor
+     * Constructor using the default buffer size of 32k.
      * 
      * @param is
      *            An InputStream to read compressed data from
      * 
      * @throws IOException
      */
-    public SnappyDecompressor(final InputStream is) throws IOException {
-        this(is, BLOCK_SIZE);
+    public SnappyCompressorInputStream(final InputStream is) throws IOException {
+        this(is, DEFAULT_BLOCK_SIZE);
     }
 
     /**
-     * Constructor
+     * Constructor using a configurable buffer size.
      * 
      * @param is
      *            An InputStream to read compressed data from
@@ -105,32 +100,66 @@ public class SnappyDecompressor {
      * 
      * @throws IOException
      */
-    public SnappyDecompressor(final InputStream is, final int blockSize)
+    public SnappyCompressorInputStream(final InputStream is, final int blockSize)
             throws IOException {
-
         this.in = is;
         this.blockSize = blockSize;
         this.decompressBuf = new byte[blockSize * 3];
-        this.decompressBufIndex = 0;
-        this.size = (int) readSize();
+        this.writeIndex = readIndex = 0;
+        uncompressedBytesRemaining = size = (int) readSize();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int read() throws IOException {
+        return read(oneByte, 0, 1) == -1 ? -1 : (oneByte[0] & 0xFF);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void close() throws IOException {
+        in.close();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int available() {
+        return writeIndex - readIndex;
     }
 
     /**
-     * Decompress the stream into an OutputStream
-     * 
-     * @param os
-     *            The OutputStream to write the decompressed data
-     * 
-     * @throws IOException
-     *             if an I/O error occurs. In particular, an
-     *             <code>IOException</code> is thrown if the output stream is
-     *             closed or the EOF is reached unexpectedly.
+     * {@inheritDoc}
      */
-    public void decompress(final OutputStream os) throws IOException {
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        if (endReached) {
+            return -1;
+        }
+        if (len > available()) {
+            fill(len - available());
+        }
 
-        int uncompressedSizeLength = getSize();
+        int readable = Math.min(len, available());
+        System.arraycopy(decompressBuf, readIndex, b, off, readable);
+        readIndex += readable;
+        if (readIndex > blockSize) {
+            slideBuffer();
+        }
+        return readable;
+    }
+
+    /**
+     * Try to fill the buffer with enoug bytes to satisfy the current read request.
+     *
+     * @param len the number of uncompressed bytes to read
+     */
+    private void fill(int len) throws IOException {
+        if (uncompressedBytesRemaining == 0) {
+            endReached = true;
+        }
+        int readNow = Math.min(len, uncompressedBytesRemaining);
 
-        while (uncompressedSizeLength > 0) {
+        while (readNow > 0) {
             final int b = readOneByte();
             int length = 0;
             int offset = 0;
@@ -139,45 +168,10 @@ public class SnappyDecompressor {
 
             case 0x00:
 
-                /*
-                 * For literals up to and including 60 bytes in length, the
-                 * upper six bits of the tag byte contain (len-1). The literal
-                 * follows immediately thereafter in the bytestream. - For
-                 * longer literals, the (len-1) value is stored after the tag
-                 * byte, little-endian. The upper six bits of the tag byte
-                 * describe how many bytes are used for the length; 60, 61, 62
-                 * or 63 for 1-4 bytes, respectively. The literal itself follows
-                 * after the length.
-                 */
-
-                switch (b >> 2) {
-                case 60:
-                    length = readOneByte();
-                    break;
-                case 61:
-                    length = readOneByte();
-                    length |= (readOneByte() << 8);
-                    break;
-                case 62:
-                    length = readOneByte();
-                    length |= (readOneByte() << 8);
-                    length |= (readOneByte() << 16);
-                    break;
-                case 63:
-                    length = readOneByte();
-                    length |= (readOneByte() << 8);
-                    length |= (readOneByte() << 16);
-                    length |= (readOneByte() << 24);
-                    break;
-                default:
-                    length = b >> 2;
-                    break;
-                }
-
-                length += 1;
+                length = readLiteralLength(b);
 
                 if (expandLiteral(length)) {
-                    flushDecompressBuffer(os);
+                    return;
                 }
                 break;
 
@@ -197,7 +191,7 @@ public class SnappyDecompressor {
                 offset |= readOneByte();
 
                 if (expandCopy(offset, length)) {
-                    flushDecompressBuffer(os);
+                    return;
                 }
                 break;
 
@@ -217,7 +211,7 @@ public class SnappyDecompressor {
                 offset |= readOneByte() << 8;
 
                 if (expandCopy(offset, length)) {
-                    flushDecompressBuffer(os);
+                    return;
                 }
                 break;
 
@@ -238,35 +232,67 @@ public class SnappyDecompressor {
                 offset |= readOneByte() << 24;
 
                 if (expandCopy(offset, length)) {
-                    flushDecompressBuffer(os);
+                    return;
                 }
                 break;
             }
 
-            uncompressedSizeLength -= length;
+            readNow -= length;
+            uncompressedBytesRemaining -= length;
         }
-        os.write(decompressBuf, 0, decompressBufIndex);
     }
 
     /**
-     * Flush the first block of the decompression buffer to the underlying out
-     * stream. All subsequent bytes are moved down to the beginning of the
-     * buffer.
-     * 
-     * @param os
-     *            The output stream to write to
-     * 
-     * @throws IOException
-     *             if an I/O error occurs. In particular, an
-     *             <code>IOException</code> is thrown if the output stream is
-     *             closed.
-     */
-    private void flushDecompressBuffer(final OutputStream os)
-            throws IOException {
-        os.write(decompressBuf, 0, this.blockSize);
-        System.arraycopy(decompressBuf, BLOCK_SIZE, decompressBuf, 0,
-                this.blockSize);
-        decompressBufIndex -= this.blockSize;
+     * Slide buffer.
+     *
+     * <p>Move all bytes of the buffer after the first block down
+     * tothe beginning of the buffer.</p>
+     */
+    private void slideBuffer() {
+        System.arraycopy(decompressBuf, blockSize, decompressBuf, 0,
+                blockSize);
+        writeIndex -= blockSize;
+        readIndex -= blockSize;
+    }
+
+
+    /*
+     * For literals up to and including 60 bytes in length, the
+     * upper six bits of the tag byte contain (len-1). The literal
+     * follows immediately thereafter in the bytestream. - For
+     * longer literals, the (len-1) value is stored after the tag
+     * byte, little-endian. The upper six bits of the tag byte
+     * describe how many bytes are used for the length; 60, 61, 62
+     * or 63 for 1-4 bytes, respectively. The literal itself follows
+     * after the length.
+     */
+    private int readLiteralLength(int b) throws IOException {
+        int length;
+        switch (b >> 2) {
+        case 60:
+            length = readOneByte();
+            break;
+        case 61:
+            length = readOneByte();
+            length |= (readOneByte() << 8);
+            break;
+        case 62:
+            length = readOneByte();
+            length |= (readOneByte() << 8);
+            length |= (readOneByte() << 16);
+            break;
+        case 63:
+            length = readOneByte();
+            length |= (readOneByte() << 8);
+            length |= (readOneByte() << 16);
+            length |= (readOneByte() << 24);
+            break;
+        default:
+            length = b >> 2;
+            break;
+        }
+
+        return length + 1;
     }
 
     /**
@@ -279,16 +305,17 @@ public class SnappyDecompressor {
      *             If the first byte cannot be read for any reason other than
      *             end of file, or if the input stream has been closed, or if
      *             some other I/O error occurs.
-     * 
      * @return True if the decompressed data should be flushed
      */
     private boolean expandLiteral(final int length) throws IOException {
-        if (length != this.in.read(decompressBuf, decompressBufIndex, length)) {
+        int bytesRead = in.read(decompressBuf, writeIndex, length);
+        count(bytesRead);
+        if (length != bytesRead) {
             throw new IOException("Premature end of stream");
         }
 
-        decompressBufIndex += length;
-        return (decompressBufIndex >= (2 * this.blockSize));
+        writeIndex += length;
+        return (writeIndex >= (2 * this.blockSize));
     }
 
     /**
@@ -311,39 +338,37 @@ public class SnappyDecompressor {
      * @return True if the decompressed data should be flushed
      */
     private boolean expandCopy(final int offset, int length) throws IOException {
-
         if (offset > blockSize) {
             throw new IOException("Offset is larger than block size");
         }
 
         if (offset == 1) {
-            byte lastChar = decompressBuf[decompressBufIndex - 1];
+            byte lastChar = decompressBuf[writeIndex - 1];
             for (int i = 0; i < length; i++) {
-                decompressBuf[decompressBufIndex++] = lastChar;
+                decompressBuf[writeIndex++] = lastChar;
             }
         } else if (length < offset) {
-            System.arraycopy(decompressBuf, decompressBufIndex - offset,
-                    decompressBuf, decompressBufIndex, length);
-            decompressBufIndex += length;
+            System.arraycopy(decompressBuf, writeIndex - offset,
+                    decompressBuf, writeIndex, length);
+            writeIndex += length;
         } else {
             int fullRotations = length / offset;
             int pad = length - (offset * fullRotations);
 
             while (fullRotations-- != 0) {
-                System.arraycopy(decompressBuf, decompressBufIndex - offset,
-                        decompressBuf, decompressBufIndex, offset);
-                decompressBufIndex += offset;
+                System.arraycopy(decompressBuf, writeIndex - offset,
+                        decompressBuf, writeIndex, offset);
+                writeIndex += offset;
             }
 
             if (pad > 0) {
-                System.arraycopy(decompressBuf, decompressBufIndex - offset,
-                        decompressBuf, decompressBufIndex, pad);
+                System.arraycopy(decompressBuf, writeIndex - offset,
+                        decompressBuf, writeIndex, pad);
 
-                decompressBufIndex += pad;
+                writeIndex += pad;
             }
         }
-
-        return (decompressBufIndex >= (2 * this.blockSize));
+        return (writeIndex >= (2 * this.blockSize));
     }
 
     /**
@@ -361,6 +386,7 @@ public class SnappyDecompressor {
         if (b == -1) {
             throw new IOException("Premature end of stream");
         }
+        count(1);
         return b & 0xFF;
     }
 

Added: commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/compressors/FramedSnappyTestCase.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/compressors/FramedSnappyTestCase.java?rev=1540399&view=auto
==============================================================================
--- commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/compressors/FramedSnappyTestCase.java (added)
+++ commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/compressors/FramedSnappyTestCase.java Sat Nov  9 21:36:52 2013
@@ -0,0 +1,83 @@
+/*
+ * 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.IOException;
+import java.util.Arrays;
+
+import org.apache.commons.compress.AbstractTestCase;
+import org.apache.commons.compress.compressors.snappy.FramedSnappyCompressorInputStream;
+import org.apache.commons.compress.utils.IOUtils;
+
+public final class FramedSnappyTestCase
+    extends AbstractTestCase {
+
+    public void testMatches() throws IOException {
+        assertFalse(FramedSnappyCompressorInputStream.matches(new byte[10], 10));
+        byte[] b = new byte[12];
+        final File input = getFile("bla.tar.sz");
+        FileInputStream in = new FileInputStream(input);
+        try {
+            IOUtils.readFully(in, b);
+        } finally {
+            in.close();
+        }
+        assertFalse(FramedSnappyCompressorInputStream.matches(b, 9));
+        assertTrue(FramedSnappyCompressorInputStream.matches(b, 10));
+        assertTrue(FramedSnappyCompressorInputStream.matches(b, 12));
+    }
+
+    public void testDefaultExtraction() throws IOException {
+        final File input = getFile("bla.tar.sz");
+        final File output = new File(dir, "bla.tar");
+        final FileInputStream is = new FileInputStream(input);
+        try {
+            final CompressorInputStream in =
+                new FramedSnappyCompressorInputStream(is);
+            FileOutputStream out = null;
+            try {
+                out = new FileOutputStream(output);
+                IOUtils.copy(in, out);
+            } finally {
+                if (out != null) {
+                    out.close();
+                }
+                in.close();
+            }
+        } finally {
+            is.close();
+        }
+        final File original = getFile("bla.tar");
+        final FileInputStream written = new FileInputStream(output);
+        try {
+            FileInputStream orig = new FileInputStream(original);
+            try {
+                assertTrue(Arrays.equals(IOUtils.toByteArray(written),
+                                         IOUtils.toByteArray(orig)));
+            } finally {
+                orig.close();
+            }
+        } finally {
+            written.close();
+        }
+    }
+}

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

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

Propchange: commons/proper/compress/trunk/src/test/resources/bla.tar.sz
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream