COMPRESS-380 Deflate64CompressorInputStream#available promised too much
Project: http://git-wip-us.apache.org/repos/asf/commons-compress/repo Commit: http://git-wip-us.apache.org/repos/asf/commons-compress/commit/07cc1a27 Tree: http://git-wip-us.apache.org/repos/asf/commons-compress/tree/07cc1a27 Diff: http://git-wip-us.apache.org/repos/asf/commons-compress/diff/07cc1a27 Branch: refs/heads/master Commit: 07cc1a278b217d45cb090ff6cb3a7934105cb2d0 Parents: 79e76d5 Author: Stefan Bodewig <bode...@apache.org> Authored: Thu Jan 4 16:51:20 2018 +0100 Committer: Stefan Bodewig <bode...@apache.org> Committed: Thu Jan 4 16:51:20 2018 +0100 ---------------------------------------------------------------------- .../commons/compress/archivers/zip/ZipFile.java | 2 +- .../Deflate64CompressorInputStream.java | 21 ++------ .../compressors/deflate64/HuffmanDecoder.java | 20 +++++++ .../commons/compress/utils/BitInputStream.java | 10 ++++ .../Deflate64CompressorInputStreamTest.java | 56 ++++++++------------ 5 files changed, 58 insertions(+), 51 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/commons-compress/blob/07cc1a27/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java b/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java index c661eeb..f77339b 100644 --- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java +++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java @@ -504,7 +504,7 @@ public class ZipFile implements Closeable { case BZIP2: return new BZip2CompressorInputStream(bis); case ENHANCED_DEFLATED: - return new Deflate64CompressorInputStream(bis, ze.getSize()); + return new Deflate64CompressorInputStream(bis); case AES_ENCRYPTED: case EXPANDING_LEVEL_1: case EXPANDING_LEVEL_2: http://git-wip-us.apache.org/repos/asf/commons-compress/blob/07cc1a27/src/main/java/org/apache/commons/compress/compressors/deflate64/Deflate64CompressorInputStream.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/compress/compressors/deflate64/Deflate64CompressorInputStream.java b/src/main/java/org/apache/commons/compress/compressors/deflate64/Deflate64CompressorInputStream.java index c097ab3..e909f13 100644 --- a/src/main/java/org/apache/commons/compress/compressors/deflate64/Deflate64CompressorInputStream.java +++ b/src/main/java/org/apache/commons/compress/compressors/deflate64/Deflate64CompressorInputStream.java @@ -30,21 +30,17 @@ import static org.apache.commons.compress.utils.IOUtils.closeQuietly; */ public class Deflate64CompressorInputStream extends CompressorInputStream { private HuffmanDecoder decoder; - private long uncompressedSize; - private long totalRead = 0; /** * Constructs a Deflate64CompressorInputStream. * * @param in the stream to read from - * @param uncompressedSize the uncompressed size of the data to be read from in */ - public Deflate64CompressorInputStream(InputStream in, long uncompressedSize) { - this(new HuffmanDecoder(in), uncompressedSize); + public Deflate64CompressorInputStream(InputStream in) { + this(new HuffmanDecoder(in)); } - Deflate64CompressorInputStream(HuffmanDecoder decoder, long uncompressedSize) { - this.uncompressedSize = uncompressedSize; + Deflate64CompressorInputStream(HuffmanDecoder decoder) { this.decoder = decoder; } @@ -74,8 +70,6 @@ public class Deflate64CompressorInputStream extends CompressorInputStream { count(read); if (read == -1) { close(); - } else { - totalRead += read; } } return read; @@ -83,14 +77,7 @@ public class Deflate64CompressorInputStream extends CompressorInputStream { @Override public int available() throws IOException { - long available = 0; - if (decoder != null) { - available = uncompressedSize - totalRead; - if (Long.compare(available, Integer.MAX_VALUE) > 0) { - available = Integer.MAX_VALUE; - } - } - return (int) available; + return decoder != null ? decoder.available() : 0; } @Override http://git-wip-us.apache.org/repos/asf/commons-compress/blob/07cc1a27/src/main/java/org/apache/commons/compress/compressors/deflate64/HuffmanDecoder.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/compress/compressors/deflate64/HuffmanDecoder.java b/src/main/java/org/apache/commons/compress/compressors/deflate64/HuffmanDecoder.java index de909db..3a41613 100644 --- a/src/main/java/org/apache/commons/compress/compressors/deflate64/HuffmanDecoder.java +++ b/src/main/java/org/apache/commons/compress/compressors/deflate64/HuffmanDecoder.java @@ -168,6 +168,9 @@ class HuffmanDecoder implements Closeable { return -1; } + int available() throws IOException { + return state == null ? 0 : state.available(); + } private static abstract class DecoderState { abstract HuffmanState state(); @@ -175,6 +178,8 @@ class HuffmanDecoder implements Closeable { abstract int read(byte[] b, int off, int len) throws IOException; abstract boolean hasData(); + + abstract int available() throws IOException ; } private class UncompressedState extends DecoderState { @@ -205,6 +210,11 @@ class HuffmanDecoder implements Closeable { boolean hasData() { return read < blockLength; } + + @Override + int available() throws IOException { + return (int) Math.min(blockLength - read, reader.bitsAvailable() / Byte.SIZE); + } } private class InitialState extends DecoderState { @@ -222,6 +232,11 @@ class HuffmanDecoder implements Closeable { boolean hasData() { return false; } + + @Override + int available() { + return 0; + } } private class HuffmanCodes extends DecoderState { @@ -301,6 +316,11 @@ class HuffmanDecoder implements Closeable { boolean hasData() { return !endOfBlock; } + + @Override + int available() { + return runBuffer.length - runBufferPos; + } } private static int nextSymbol(BitInputStream reader, BinaryTreeNode tree) throws IOException { http://git-wip-us.apache.org/repos/asf/commons-compress/blob/07cc1a27/src/main/java/org/apache/commons/compress/utils/BitInputStream.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/compress/utils/BitInputStream.java b/src/main/java/org/apache/commons/compress/utils/BitInputStream.java index 45d30ec..f4beaae 100644 --- a/src/main/java/org/apache/commons/compress/utils/BitInputStream.java +++ b/src/main/java/org/apache/commons/compress/utils/BitInputStream.java @@ -100,6 +100,16 @@ public class BitInputStream implements Closeable { return bitsOut; } + /** + * Returns an estimate of the number of bits that can be read from + * this input stream without blocking by the next invocation of a + * method for this input stream. + * @since 1.16 + */ + public long bitsAvailable() throws IOException { + return bitsCachedSize + 8l * in.available(); + } + private long processBitsGreater57(final int count) throws IOException { final long bitsOut; int overflowBits = 0; http://git-wip-us.apache.org/repos/asf/commons-compress/blob/07cc1a27/src/test/java/org/apache/commons/compress/compressors/deflate64/Deflate64CompressorInputStreamTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/commons/compress/compressors/deflate64/Deflate64CompressorInputStreamTest.java b/src/test/java/org/apache/commons/compress/compressors/deflate64/Deflate64CompressorInputStreamTest.java index 92e7b90..1e32b42 100644 --- a/src/test/java/org/apache/commons/compress/compressors/deflate64/Deflate64CompressorInputStreamTest.java +++ b/src/test/java/org/apache/commons/compress/compressors/deflate64/Deflate64CompressorInputStreamTest.java @@ -40,8 +40,7 @@ public class Deflate64CompressorInputStreamTest { @Test public void readWhenClosed() throws Exception { - long size = Integer.MAX_VALUE - 1; - Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(nullDecoder, size); + Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(nullDecoder); assertEquals(-1, input.read()); assertEquals(-1, input.read(new byte[1])); assertEquals(-1, input.read(new byte[1], 0, 1)); @@ -50,47 +49,24 @@ public class Deflate64CompressorInputStreamTest { @Test public void properSizeWhenClosed() throws Exception { - long size = Integer.MAX_VALUE - 1; - Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(nullDecoder, size); + Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(nullDecoder); assertEquals(0, input.available()); } @Test - public void properSizeWhenInRange() throws Exception + public void delegatesAvailable() throws Exception { - long size = Integer.MAX_VALUE - 1; - Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(decoder, size); - assertEquals(size, input.available()); - } - - @Test - public void properSizeWhenOutOfRange() throws Exception - { - long size = Integer.MAX_VALUE + 1L; - Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(decoder, size); - assertEquals(Integer.MAX_VALUE, input.available()); - } + Mockito.when(decoder.available()).thenReturn(1024); - @Test - public void properSizeAfterReading() throws Exception - { - byte[] buf = new byte[4096]; - int offset = 1000; - int length = 3096; - - Mockito.when(decoder.decode(buf, offset, length)).thenReturn(2048); - - long size = Integer.MAX_VALUE + 2047L; - Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(decoder, size); - assertEquals(2048, input.read(buf, offset, length)); - assertEquals(Integer.MAX_VALUE - 1, input.available()); + Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(decoder); + assertEquals(1024, input.available()); } @Test public void closeCallsDecoder() throws Exception { - Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(decoder, 10); + Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(decoder); input.close(); Mockito.verify(decoder, times(1)).close(); @@ -100,7 +76,7 @@ public class Deflate64CompressorInputStreamTest { public void closeIsDelegatedJustOnce() throws Exception { - Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(decoder, 10); + Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(decoder); input.close(); input.close(); @@ -116,7 +92,7 @@ public class Deflate64CompressorInputStreamTest { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' }; - try (Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(new ByteArrayInputStream(data), 11); + try (Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(new ByteArrayInputStream(data)); BufferedReader br = new BufferedReader(new InputStreamReader(input))) { assertEquals("Hello World", br.readLine()); @@ -124,4 +100,18 @@ public class Deflate64CompressorInputStreamTest { } } + @Test + public void uncompressedBlockAvailable() throws Exception + { + byte[] data = { + 1, 11, 0, -12, -1, + 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' + }; + + try (Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(new ByteArrayInputStream(data))) { + assertEquals('H', input.read()); + assertEquals(10, input.available()); + } + } + }