Repository: commons-compress
Updated Branches:
  refs/heads/master a793612b9 -> 22fe7f3c8


[COMPRESS-392] Add Brotli decoder based on the Google Brotli library.

Project: http://git-wip-us.apache.org/repos/asf/commons-compress/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-compress/commit/22fe7f3c
Tree: http://git-wip-us.apache.org/repos/asf/commons-compress/tree/22fe7f3c
Diff: http://git-wip-us.apache.org/repos/asf/commons-compress/diff/22fe7f3c

Branch: refs/heads/master
Commit: 22fe7f3c8b3fa204c2406c3d15811b2581e1b7b5
Parents: a793612
Author: Philippe Mouawad <p.moua...@ubik-ingenierie.com>
Authored: Tue May 2 12:23:55 2017 -0700
Committer: Gary Gregory <garydgreg...@gmail.com>
Committed: Tue May 2 12:23:55 2017 -0700

----------------------------------------------------------------------
 .../brotli/BrotliCompressorInputStream.java     | 151 +++++++++++++++++++
 .../compressors/brotli/BrotliUtils.java         |  88 +++++++++++
 .../brotli/BrotliCompressorInputStreamTest.java | 133 ++++++++++++++++
 src/test/resources/brotli.testdata.compressed   | Bin 0 -> 12 bytes
 src/test/resources/brotli.testdata.uncompressed |   1 +
 5 files changed, 373 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-compress/blob/22fe7f3c/src/main/java/org/apache/commons/compress/compressors/brotli/BrotliCompressorInputStream.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/compress/compressors/brotli/BrotliCompressorInputStream.java
 
b/src/main/java/org/apache/commons/compress/compressors/brotli/BrotliCompressorInputStream.java
new file mode 100644
index 0000000..26b56d9
--- /dev/null
+++ 
b/src/main/java/org/apache/commons/compress/compressors/brotli/BrotliCompressorInputStream.java
@@ -0,0 +1,151 @@
+/*
+ * 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.brotli;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.compress.compressors.CompressorInputStream;
+
+/**
+ * {@link FilterInputStream} implementation to decode Brotli encoded stream.
+ * Library relies on <a href="https://github.com/google/brotli";>Google 
brotli</a>
+ * 
+ * @since 1.14
+ */
+public class BrotliCompressorInputStream extends CompressorInputStream {
+    
+    private org.brotli.dec.BrotliInputStream decIS;
+
+    public BrotliCompressorInputStream(InputStream in) throws IOException {
+        this.decIS = new org.brotli.dec.BrotliInputStream(in);
+    }
+
+    /**
+     * @return
+     * @throws IOException
+     * @see java.io.InputStream#available()
+     */
+    public int available() throws IOException {
+        return decIS.available();
+    }
+
+    /**
+     * @throws IOException
+     * @see org.brotli.dec.BrotliInputStream#close()
+     */
+    public void close() throws IOException {
+        decIS.close();
+    }
+
+    /**
+     * @return
+     * @see java.lang.Object#hashCode()
+     */
+    public int hashCode() {
+        return decIS.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int read(byte[] b) throws IOException {
+        return decIS.read(b);
+    }
+
+    /**
+     * @param obj
+     * @return
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public boolean equals(Object obj) {
+        return decIS.equals(obj);
+    }
+
+    /**
+     * @param n
+     * @return
+     * @throws IOException
+     * @see java.io.InputStream#skip(long)
+     */
+    public long skip(long n) throws IOException {
+        return decIS.skip(n);
+    }
+
+    /**
+     * @param readlimit
+     * @see java.io.InputStream#mark(int)
+     */
+    public void mark(int readlimit) {
+        decIS.mark(readlimit);
+    }
+
+    /**
+     * @return
+     * @see java.io.InputStream#markSupported()
+     */
+    public boolean markSupported() {
+        return decIS.markSupported();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int read() throws IOException {
+        final int ret = decIS.read();
+        count(ret == -1 ? 0 : 1);
+        return ret;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int read(byte[] buf, int off, int len) throws IOException {
+        final int ret = decIS.read(buf, off, len);
+        count(ret);
+        return ret;
+    }
+
+    /**
+     * @return
+     * @see java.lang.Object#toString()
+     */
+    public String toString() {
+        return decIS.toString();
+    }
+
+    /**
+     * @throws IOException
+     * @see java.io.InputStream#reset()
+     */
+    public void reset() throws IOException {
+        decIS.reset();
+    }
+    
+
+    /**
+     * There is no magic for Brotli
+     * 
+     * @param signature
+     *            the bytes to check
+     * @param length
+     *            the number of bytes to check
+     * @return true
+     */
+    static boolean matches(final byte[] signature, final int length) {
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/22fe7f3c/src/main/java/org/apache/commons/compress/compressors/brotli/BrotliUtils.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/compress/compressors/brotli/BrotliUtils.java 
b/src/main/java/org/apache/commons/compress/compressors/brotli/BrotliUtils.java
new file mode 100644
index 0000000..1f7ebca
--- /dev/null
+++ 
b/src/main/java/org/apache/commons/compress/compressors/brotli/BrotliUtils.java
@@ -0,0 +1,88 @@
+/*
+ * 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.brotli;
+
+/**
+ * Utility code for the Brotli compression format.
+ * @ThreadSafe
+ * @since 1.14
+ */
+public class BrotliUtils {
+
+    static enum CachedAvailability {
+        DONT_CACHE, CACHED_AVAILABLE, CACHED_UNAVAILABLE
+    }
+
+    private static volatile CachedAvailability cachedBrotliAvailability;
+
+    static {
+        cachedBrotliAvailability = CachedAvailability.DONT_CACHE;
+        try {
+            Class.forName("org.osgi.framework.BundleEvent");
+        } catch (final Exception ex) {
+            setCacheBrotliAvailablity(true);
+        }
+    }
+
+    /** Private constructor to prevent instantiation of this utility class. */
+    private BrotliUtils() {
+    }
+
+
+    /**
+     * Are the classes required to support Brotli compression available?
+     * @return true if the classes required to support Brotli compression are 
available
+     */
+    public static boolean isBrotliCompressionAvailable() {
+        final CachedAvailability cachedResult = cachedBrotliAvailability;
+        if (cachedResult != CachedAvailability.DONT_CACHE) {
+            return cachedResult == CachedAvailability.CACHED_AVAILABLE;
+        }
+        return internalIsBrotliCompressionAvailable();
+    }
+
+    private static boolean internalIsBrotliCompressionAvailable() {
+        try {
+            return BrotliCompressorInputStream.matches(null, 0);
+        } catch (final NoClassDefFoundError error) {
+            return false;
+        }
+    }
+
+    /**
+     * Whether to cache the result of the Brotli for Java check.
+     *
+     * <p>This defaults to {@code false} in an OSGi environment and {@code 
true} otherwise.</p>
+     * @param doCache whether to cache the result
+     */
+    public static void setCacheBrotliAvailablity(final boolean doCache) {
+        if (!doCache) {
+            cachedBrotliAvailability = CachedAvailability.DONT_CACHE;
+        } else if (cachedBrotliAvailability == CachedAvailability.DONT_CACHE) {
+            final boolean hasXz = internalIsBrotliCompressionAvailable();
+            cachedBrotliAvailability = hasXz ? 
CachedAvailability.CACHED_AVAILABLE
+                : CachedAvailability.CACHED_UNAVAILABLE;
+        }
+    }
+
+    // only exists to support unit tests
+    static CachedAvailability getCachedBrotliAvailability() {
+        return cachedBrotliAvailability;
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/22fe7f3c/src/test/java/org/apache/commons/compress/compressors/brotli/BrotliCompressorInputStreamTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/commons/compress/compressors/brotli/BrotliCompressorInputStreamTest.java
 
b/src/test/java/org/apache/commons/compress/compressors/brotli/BrotliCompressorInputStreamTest.java
new file mode 100644
index 0000000..3aca075
--- /dev/null
+++ 
b/src/test/java/org/apache/commons/compress/compressors/brotli/BrotliCompressorInputStreamTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.brotli;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.compress.AbstractTestCase;
+import org.apache.commons.compress.utils.IOUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class BrotliCompressorInputStreamTest {
+
+    /**
+     * Test bridge works fine 
+     * @throws {@link IOException}
+     */
+    @Test
+    public void testBrotliDecode() throws IOException {
+        final File input = 
AbstractTestCase.getFile("brotli.testdata.compressed");
+        final File expected = 
AbstractTestCase.getFile("brotli.testdata.uncompressed");
+        try (InputStream inputStream = new FileInputStream(input);
+                InputStream expectedStream = new FileInputStream(expected);
+                BrotliCompressorInputStream brotliInputStream = new 
BrotliCompressorInputStream(inputStream)) {
+            final byte[] b = new byte[20];
+            IOUtils.readFully(expectedStream, b);
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            int readByte = -1;
+            while((readByte = brotliInputStream.read()) != -1) {
+                bos.write(readByte);
+            }
+            Assert.assertArrayEquals(b, bos.toByteArray());
+        } 
+    }
+    
+    @Test
+    public void testCachingIsEnabledByDefaultAndXZIsPresent() {
+        assertEquals(BrotliUtils.CachedAvailability.CACHED_AVAILABLE, 
BrotliUtils.getCachedBrotliAvailability());
+        assertTrue(BrotliUtils.isBrotliCompressionAvailable());
+    }
+
+    @Test
+    public void testCanTurnOffCaching() {
+        try {
+            BrotliUtils.setCacheBrotliAvailablity(false);
+            assertEquals(BrotliUtils.CachedAvailability.DONT_CACHE, 
BrotliUtils.getCachedBrotliAvailability());
+            assertTrue(BrotliUtils.isBrotliCompressionAvailable());
+        } finally {
+            BrotliUtils.setCacheBrotliAvailablity(true);
+        }
+    }
+
+    @Test
+    public void testTurningOnCachingReEvaluatesAvailability() {
+        try {
+            BrotliUtils.setCacheBrotliAvailablity(false);
+            assertEquals(BrotliUtils.CachedAvailability.DONT_CACHE, 
BrotliUtils.getCachedBrotliAvailability());
+            BrotliUtils.setCacheBrotliAvailablity(true);
+            assertEquals(BrotliUtils.CachedAvailability.CACHED_AVAILABLE, 
BrotliUtils.getCachedBrotliAvailability());
+        } finally {
+            BrotliUtils.setCacheBrotliAvailablity(true);
+        }
+    }
+    
+
+    @Test
+    public void availableShouldReturnZero() throws IOException {
+        final File input = 
AbstractTestCase.getFile("brotli.testdata.compressed");
+        try (InputStream is = new FileInputStream(input)) {
+            final BrotliCompressorInputStream in =
+                    new BrotliCompressorInputStream(is);
+            Assert.assertTrue(in.available() == 0);
+            in.close();
+        }
+    }
+
+    @Test
+    public void shouldBeAbleToSkipAByte() throws IOException {
+        final File input = 
AbstractTestCase.getFile("brotli.testdata.compressed");
+        try (InputStream is = new FileInputStream(input)) {
+            final BrotliCompressorInputStream in =
+                    new BrotliCompressorInputStream(is);
+            Assert.assertEquals(1, in.skip(1));
+            in.close();
+        }
+    }
+
+    @Test
+    public void singleByteReadWorksAsExpected() throws IOException {
+        final File input = 
AbstractTestCase.getFile("brotli.testdata.compressed");
+        try (InputStream is = new FileInputStream(input)) {
+            final BrotliCompressorInputStream in =
+                    new BrotliCompressorInputStream(is);
+            //  starts with filename "XXX"
+            Assert.assertEquals('X', in.read());
+            in.close();
+        }
+    }
+
+    @Test
+    public void singleByteReadReturnsMinusOneAtEof() throws IOException {
+        final File input = 
AbstractTestCase.getFile("brotli.testdata.compressed");
+        try (InputStream is = new FileInputStream(input)) {
+            final BrotliCompressorInputStream in =
+                    new BrotliCompressorInputStream(is);
+            IOUtils.toByteArray(in);
+            Assert.assertEquals(-1, in.read());
+            in.close();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/22fe7f3c/src/test/resources/brotli.testdata.compressed
----------------------------------------------------------------------
diff --git a/src/test/resources/brotli.testdata.compressed 
b/src/test/resources/brotli.testdata.compressed
new file mode 100644
index 0000000..3769516
Binary files /dev/null and b/src/test/resources/brotli.testdata.compressed 
differ

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/22fe7f3c/src/test/resources/brotli.testdata.uncompressed
----------------------------------------------------------------------
diff --git a/src/test/resources/brotli.testdata.uncompressed 
b/src/test/resources/brotli.testdata.uncompressed
new file mode 100644
index 0000000..3f9cf86
--- /dev/null
+++ b/src/test/resources/brotli.testdata.uncompressed
@@ -0,0 +1 @@
+XXXXXXXXXXYYYYYYYYYY
\ No newline at end of file

Reply via email to