Author: bodewig
Date: Mon Jan 20 13:26:08 2014
New Revision: 1559687
URL: http://svn.apache.org/r1559687
Log:
no longer try to read one byte ahead in BZip2CompressorInputStream -
COMPRESS-253
Added:
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/compressors/bzip2/PythonTruncatedBzip2Test.java
(with props)
Modified:
commons/proper/compress/trunk/src/changes/changes.xml
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/bzip2/BZip2CompressorInputStream.java
Modified: commons/proper/compress/trunk/src/changes/changes.xml
URL:
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/changes/changes.xml?rev=1559687&r1=1559686&r2=1559687&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/changes/changes.xml (original)
+++ commons/proper/compress/trunk/src/changes/changes.xml Mon Jan 20 13:26:08
2014
@@ -44,6 +44,10 @@ The <action> type attribute can be add,u
<body>
<release version="1.8" date="not released, yet"
description="Release 1.8">
+ <action issue="COMPRESS-253" type="fix" date="2014-01-20">
+ BZip2CompressorInputStream read fewer bytes than possible from
+ a truncated stream.
+ </action>
</release>
<release version="1.7" date="2014-01-20"
description="Release 1.7">
Modified:
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/bzip2/BZip2CompressorInputStream.java
URL:
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/bzip2/BZip2CompressorInputStream.java?rev=1559687&r1=1559686&r2=1559687&view=diff
==============================================================================
---
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/bzip2/BZip2CompressorInputStream.java
(original)
+++
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/bzip2/BZip2CompressorInputStream.java
Mon Jan 20 13:26:08 2014
@@ -64,8 +64,6 @@ public class BZip2CompressorInputStream
private InputStream in;
private final boolean decompressConcatenated;
- private int currentChar = -1;
-
private static final int EOF = 0;
private static final int START_BLOCK_STATE = 1;
private static final int RAND_PART_A_STATE = 2;
@@ -133,7 +131,6 @@ public class BZip2CompressorInputStream
init(true);
initBlock();
- setupBlock();
}
@Override
@@ -171,12 +168,13 @@ public class BZip2CompressorInputStream
final int hi = offs + len;
int destOffs = offs;
- for (int b; (destOffs < hi) && ((b = read0()) >= 0);) {
+ int b;
+ while (destOffs < hi && ((b = read0()) >= 0)) {
dest[destOffs++] = (byte) b;
+ count(1);
}
int c = (destOffs == offs) ? -1 : (destOffs - offs);
- count(c);
return c;
}
@@ -196,42 +194,34 @@ public class BZip2CompressorInputStream
}
private int read0() throws IOException {
- final int retChar = this.currentChar;
-
- switch (this.currentState) {
+ switch (currentState) {
case EOF:
return -1;
case START_BLOCK_STATE:
- throw new IllegalStateException();
+ return setupBlock();
case RAND_PART_A_STATE:
throw new IllegalStateException();
case RAND_PART_B_STATE:
- setupRandPartB();
- break;
+ return setupRandPartB();
case RAND_PART_C_STATE:
- setupRandPartC();
- break;
+ return setupRandPartC();
case NO_RAND_PART_A_STATE:
throw new IllegalStateException();
case NO_RAND_PART_B_STATE:
- setupNoRandPartB();
- break;
+ return setupNoRandPartB();
case NO_RAND_PART_C_STATE:
- setupNoRandPartC();
- break;
+ return setupNoRandPartC();
default:
throw new IllegalStateException();
}
-
- return retChar;
}
private boolean init(boolean isFirstStream) throws IOException {
@@ -800,9 +790,9 @@ public class BZip2CompressorInputStream
return dataShadow.perm[zt][zvec - dataShadow.base[zt][zn]];
}
- private void setupBlock() throws IOException {
- if (this.data == null) {
- return;
+ private int setupBlock() throws IOException {
+ if (currentState == EOF || this.data == null) {
+ return -1;
}
final int[] cftab = this.data.cftab;
@@ -832,13 +822,12 @@ public class BZip2CompressorInputStream
if (this.blockRandomised) {
this.su_rNToGo = 0;
this.su_rTPos = 0;
- setupRandPartA();
- } else {
- setupNoRandPartA();
+ return setupRandPartA();
}
+ return setupNoRandPartA();
}
- private void setupRandPartA() throws IOException {
+ private int setupRandPartA() throws IOException {
if (this.su_i2 <= this.last) {
this.su_chPrev = this.su_ch2;
int su_ch2Shadow = this.data.ll8[this.su_tPos] & 0xff;
@@ -853,39 +842,39 @@ public class BZip2CompressorInputStream
}
this.su_ch2 = su_ch2Shadow ^= (this.su_rNToGo == 1) ? 1 : 0;
this.su_i2++;
- this.currentChar = su_ch2Shadow;
this.currentState = RAND_PART_B_STATE;
this.crc.updateCRC(su_ch2Shadow);
+ return su_ch2Shadow;
} else {
endBlock();
initBlock();
- setupBlock();
+ return setupBlock();
}
}
- private void setupNoRandPartA() throws IOException {
+ private int setupNoRandPartA() throws IOException {
if (this.su_i2 <= this.last) {
this.su_chPrev = this.su_ch2;
int su_ch2Shadow = this.data.ll8[this.su_tPos] & 0xff;
this.su_ch2 = su_ch2Shadow;
this.su_tPos = this.data.tt[this.su_tPos];
this.su_i2++;
- this.currentChar = su_ch2Shadow;
this.currentState = NO_RAND_PART_B_STATE;
this.crc.updateCRC(su_ch2Shadow);
+ return su_ch2Shadow;
} else {
this.currentState = NO_RAND_PART_A_STATE;
endBlock();
initBlock();
- setupBlock();
+ return setupBlock();
}
}
- private void setupRandPartB() throws IOException {
+ private int setupRandPartB() throws IOException {
if (this.su_ch2 != this.su_chPrev) {
this.currentState = RAND_PART_A_STATE;
this.su_count = 1;
- setupRandPartA();
+ return setupRandPartA();
} else if (++this.su_count >= 4) {
this.su_z = (char) (this.data.ll8[this.su_tPos] & 0xff);
this.su_tPos = this.data.tt[this.su_tPos];
@@ -902,51 +891,51 @@ public class BZip2CompressorInputStream
if (this.su_rNToGo == 1) {
this.su_z ^= 1;
}
- setupRandPartC();
+ return setupRandPartC();
} else {
this.currentState = RAND_PART_A_STATE;
- setupRandPartA();
+ return setupRandPartA();
}
}
- private void setupRandPartC() throws IOException {
+ private int setupRandPartC() throws IOException {
if (this.su_j2 < this.su_z) {
- this.currentChar = this.su_ch2;
this.crc.updateCRC(this.su_ch2);
this.su_j2++;
+ return this.su_ch2;
} else {
this.currentState = RAND_PART_A_STATE;
this.su_i2++;
this.su_count = 0;
- setupRandPartA();
+ return setupRandPartA();
}
}
- private void setupNoRandPartB() throws IOException {
+ private int setupNoRandPartB() throws IOException {
if (this.su_ch2 != this.su_chPrev) {
this.su_count = 1;
- setupNoRandPartA();
+ return setupNoRandPartA();
} else if (++this.su_count >= 4) {
this.su_z = (char) (this.data.ll8[this.su_tPos] & 0xff);
this.su_tPos = this.data.tt[this.su_tPos];
this.su_j2 = 0;
- setupNoRandPartC();
+ return setupNoRandPartC();
} else {
- setupNoRandPartA();
+ return setupNoRandPartA();
}
}
- private void setupNoRandPartC() throws IOException {
+ private int setupNoRandPartC() throws IOException {
if (this.su_j2 < this.su_z) {
int su_ch2Shadow = this.su_ch2;
- this.currentChar = su_ch2Shadow;
this.crc.updateCRC(su_ch2Shadow);
this.su_j2++;
this.currentState = NO_RAND_PART_C_STATE;
+ return su_ch2Shadow;
} else {
this.su_i2++;
this.su_count = 0;
- setupNoRandPartA();
+ return setupNoRandPartA();
}
}
Added:
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/compressors/bzip2/PythonTruncatedBzip2Test.java
URL:
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/compressors/bzip2/PythonTruncatedBzip2Test.java?rev=1559687&view=auto
==============================================================================
---
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/compressors/bzip2/PythonTruncatedBzip2Test.java
(added)
+++
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/compressors/bzip2/PythonTruncatedBzip2Test.java
Mon Jan 20 13:26:08 2014
@@ -0,0 +1,113 @@
+/*
+ * 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.bzip2;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.util.Arrays;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Testcase porting a test from Python's testsuite.
+ * @see "https://issues.apache.org/jira/browse/COMPRESS-253"
+ */
+public class PythonTruncatedBzip2Test {
+
+ private static String TEXT =
"root:x:0:0:root:/root:/bin/bash\nbin:x:1:1:bin:/bin:\ndaemon:x:2:2:daemon:/sbin:\nadm:x:3:4:adm:/var/adm:\nlp:x:4:7:lp:/var/spool/lpd:\nsync:x:5:0:sync:/sbin:/bin/sync\nshutdown:x:6:0:shutdown:/sbin:/sbin/shutdown\nhalt:x:7:0:halt:/sbin:/sbin/halt\nmail:x:8:12:mail:/var/spool/mail:\nnews:x:9:13:news:/var/spool/news:\nuucp:x:10:14:uucp:/var/spool/uucp:\noperator:x:11:0:operator:/root:\ngames:x:12:100:games:/usr/games:\ngopher:x:13:30:gopher:/usr/lib/gopher-data:\nftp:x:14:50:FTP
User:/var/ftp:/bin/bash\nnobody:x:65534:65534:Nobody:/home:\npostfix:x:100:101:postfix:/var/spool/postfix:\nniemeyer:x:500:500::/home/niemeyer:/bin/bash\npostgres:x:101:102:PostgreSQL
Server:/var/lib/pgsql:/bin/bash\nmysql:x:102:103:MySQL
server:/var/lib/mysql:/bin/bash\nwww:x:103:104::/var/www:/bin/false\n";
+
+ private static byte[] DATA;
+ private static byte[] TRUNCATED_DATA;
+ private ReadableByteChannel bz2Channel;
+
+ @BeforeClass
+ public static void initializeTestData() throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ BZip2CompressorOutputStream bz2out = new
BZip2CompressorOutputStream(out);
+ bz2out.write(TEXT.getBytes(), 0, TEXT.getBytes().length);
+ bz2out.close();
+ DATA = out.toByteArray();
+
+ // Drop the eos_magic field (6 bytes) and CRC (4 bytes).
+ TRUNCATED_DATA = Arrays.copyOfRange(DATA, 0, DATA.length - 10);
+ }
+
+ @Before
+ public void initializeChannel() throws IOException {
+ InputStream source = new ByteArrayInputStream(TRUNCATED_DATA);
+ this.bz2Channel = makeBZ2C(source);
+ }
+
+ @After
+ public void closeChannel() throws IOException {
+ bz2Channel.close();
+ bz2Channel = null;
+ }
+
+ @Test(expected = IOException.class)
+ public void testTruncatedData() throws IOException {
+ //with BZ2File(self.filename) as f:
+ // self.assertRaises(EOFError, f.read)
+ System.out.println("Attempt to read the whole thing in, should throw
...");
+ ByteBuffer buffer = ByteBuffer.allocate(8192);
+ bz2Channel.read(buffer);
+ }
+
+ @Test
+ public void testPartialReadTruncatedData() throws IOException {
+ //with BZ2File(self.filename) as f:
+ // self.assertEqual(f.read(len(self.TEXT)), self.TEXT)
+ // self.assertRaises(EOFError, f.read, 1)
+
+ final int length = TEXT.length();
+ ByteBuffer buffer = ByteBuffer.allocate(length);
+ bz2Channel.read(buffer);
+
+ assertArrayEquals(Arrays.copyOfRange(TEXT.getBytes(), 0, length),
+ buffer.array());
+
+ // subsequent read should throw
+ buffer = ByteBuffer.allocate(1);
+ try {
+ bz2Channel.read(buffer);
+ Assert.fail("The read should have thrown.");
+ } catch (IOException e) {
+ // pass
+ }
+ }
+
+ private static ReadableByteChannel makeBZ2C(InputStream source) throws
IOException {
+ BufferedInputStream bin = new BufferedInputStream(source);
+ BZip2CompressorInputStream bZin = new BZip2CompressorInputStream(bin,
true);
+
+ return Channels.newChannel(bZin);
+ }
+}
Propchange:
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/compressors/bzip2/PythonTruncatedBzip2Test.java
------------------------------------------------------------------------------
svn:eol-style = native