Here are some small improvements when creating new BlockInputStream
instances. This reduces the size of the byte[] for the block header to
the actual size and replaces use of ByteArrayInputStream, which has
synchronized methods, with a ByteBuffer, which provides the same
functionality without synchronization.

diff --git a/src/org/tukaani/xz/BlockInputStream.java
b/src/org/tukaani/xz/BlockInputStream.java
index 1931bd6..94342e2 100644
--- a/src/org/tukaani/xz/BlockInputStream.java
+++ b/src/org/tukaani/xz/BlockInputStream.java
@@ -9,13 +9,15 @@

 package org.tukaani.xz;

-import java.io.InputStream;
 import java.io.DataInputStream;
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
 import java.util.Arrays;
-import org.tukaani.xz.common.DecoderUtil;
+
 import org.tukaani.xz.check.Check;
+import org.tukaani.xz.common.DecoderUtil;

 class BlockInputStream extends InputStream {
     private final DataInputStream inData;
@@ -44,17 +46,18 @@ class BlockInputStream extends InputStream {
         this.verifyCheck = verifyCheck;
         inData = new DataInputStream(in);

-        byte[] buf = new byte[DecoderUtil.BLOCK_HEADER_SIZE_MAX];
-
         // Block Header Size or Index Indicator
-        inData.readFully(buf, 0, 1);
+        int b = inData.readUnsignedByte();

         // See if this begins the Index field.
-        if (buf[0] == 0x00)
+        if (b == 0x00)
             throw new IndexIndicatorException();

         // Read the rest of the Block Header.
-        headerSize = 4 * ((buf[0] & 0xFF) + 1);
+        headerSize = ((b & 0xFF) + 1) << 2;
+
+        final byte[] buf = new byte[headerSize];
+        buf[0] = (byte) b;
         inData.readFully(buf, 1, headerSize - 1);

         // Validate the CRC32.
@@ -71,11 +74,9 @@ class BlockInputStream extends InputStream {
         long[] filterIDs = new long[filterCount];
         byte[][] filterProps = new byte[filterCount][];

-        // Use a stream to parse the fields after the Block Flags field.
+        // Use a ByteBuffer to parse the fields after the Block Flags field.
         // Exclude the CRC32 field at the end.
-        ByteArrayInputStream bufStream = new ByteArrayInputStream(
-                buf, 2, headerSize - 6);
-
+        final ByteBuffer bb = ByteBuffer.wrap(buf, 2, headerSize - 6);
         try {
             // Set the maximum valid compressed size. This is overriden
             // by the value from the Compressed Size field if it is present.
@@ -85,7 +86,7 @@ class BlockInputStream extends InputStream {
             // Decode and validate Compressed Size if the relevant flag
             // is set in Block Flags.
             if ((buf[1] & 0x40) != 0x00) {
-                compressedSizeInHeader = DecoderUtil.decodeVLI(bufStream);
+                compressedSizeInHeader = DecoderUtil.decodeVLI(bb);

                 if (compressedSizeInHeader == 0
                         || compressedSizeInHeader > compressedSizeLimit)
@@ -97,27 +98,27 @@ class BlockInputStream extends InputStream {
             // Decode Uncompressed Size if the relevant flag is set
             // in Block Flags.
             if ((buf[1] & 0x80) != 0x00)
-                uncompressedSizeInHeader = DecoderUtil.decodeVLI(bufStream);
+                uncompressedSizeInHeader = DecoderUtil.decodeVLI(bb);

             // Decode Filter Flags.
             for (int i = 0; i < filterCount; ++i) {
-                filterIDs[i] = DecoderUtil.decodeVLI(bufStream);
+                filterIDs[i] = DecoderUtil.decodeVLI(bb);

-                long filterPropsSize = DecoderUtil.decodeVLI(bufStream);
-                if (filterPropsSize > bufStream.available())
+                long filterPropsSize = DecoderUtil.decodeVLI(bb);
+                if (filterPropsSize > bb.remaining())
                     throw new CorruptedInputException();

                 filterProps[i] = new byte[(int)filterPropsSize];
-                bufStream.read(filterProps[i]);
+                bb.get(filterProps[i]);
             }

-        } catch (IOException e) {
+        } catch (IOException | BufferUnderflowException e) {
             throw new CorruptedInputException("XZ Block Header is corrupt");
         }

         // Check that the remaining bytes are zero.
-        for (int i = bufStream.available(); i > 0; --i)
-            if (bufStream.read() != 0x00)
+        for (int i = bb.remaining(); i > 0; --i)
+            if (bb.get() != 0x00)
                 throw new UnsupportedOptionsException(
                         "Unsupported options in XZ Block Header");



diff --git a/src/org/tukaani/xz/common/DecoderUtil.java
b/src/org/tukaani/xz/common/DecoderUtil.java
index 77ba441..c3aa21d 100644
--- a/src/org/tukaani/xz/common/DecoderUtil.java
+++ b/src/org/tukaani/xz/common/DecoderUtil.java
@@ -9,14 +9,17 @@

 package org.tukaani.xz.common;

-import java.io.InputStream;
-import java.io.IOException;
 import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
 import java.util.zip.CRC32;
-import org.tukaani.xz.XZ;
-import org.tukaani.xz.XZFormatException;
+
 import org.tukaani.xz.CorruptedInputException;
 import org.tukaani.xz.UnsupportedOptionsException;
+import org.tukaani.xz.XZ;
+import org.tukaani.xz.XZFormatException;

 public class DecoderUtil extends Util {
     public static boolean isCRC32Valid(byte[] buf, int off, int len,
@@ -118,4 +121,27 @@ public class DecoderUtil extends Util {

         return num;
     }
+
+    public static long decodeVLI(ByteBuffer bb) throws IOException {
+        try {
+            int b = bb.get();
+            long num = b & 0x7F;
+            int i = 0;
+
+            while ((b & 0x80) != 0x00) {
+                if (++i >= VLI_SIZE_MAX)
+                    throw new CorruptedInputException();
+
+                b = bb.get();
+                if (b == 0x00)
+                    throw new CorruptedInputException();
+
+                num |= (long)(b & 0x7F) << (i * 7);
+            }
+
+            return num;
+        } catch (BufferUnderflowException e) {
+            throw new EOFException();
+        }
+    }
 }

Reply via email to