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


Reply via email to