From a67cf020bd250a1fb2acecf7cfd0159f25200dc2 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Sat, 16 Jun 2012 08:08:51 +0000 Subject: [PATCH] Merge bzip2 input from Compress, bringing support for files with multiple streams git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@1350882 13f79535-47bb-0310-9956-ffa450edef68 --- WHATSNEW | 3 + .../apache/tools/bzip2/CBZip2InputStream.java | 188 +++++++++++++----- 2 files changed, 137 insertions(+), 54 deletions(-) diff --git a/WHATSNEW b/WHATSNEW index f2aa4f8e8..ef140703c 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -52,6 +52,9 @@ Other changes: * merged the TAR package from Commons Compress, it can now read archives using POSIX extension headers and STAR extensions. + * merged the BZIP2 package from Commons Compress, it can now + optionally read files that contain multiple streams properly. + Changes from Ant 1.8.3 TO Ant 1.8.4 =================================== diff --git a/src/main/org/apache/tools/bzip2/CBZip2InputStream.java b/src/main/org/apache/tools/bzip2/CBZip2InputStream.java index d977b4a60..1ce0ce0a4 100644 --- a/src/main/org/apache/tools/bzip2/CBZip2InputStream.java +++ b/src/main/org/apache/tools/bzip2/CBZip2InputStream.java @@ -45,28 +45,6 @@ import java.io.InputStream; */ public class CBZip2InputStream extends InputStream implements BZip2Constants { - private static void reportCRCError() throws IOException { - // The clean way would be to throw an exception. - //throw new IOException("crc error"); - - // Just print a message, like the previous versions of this class did - System.err.println("BZip2 CRC error"); - } - - private void makeMaps() { - final boolean[] inUse = this.data.inUse; - final byte[] seqToUnseq = this.data.seqToUnseq; - - int nInUseShadow = 0; - - for (int i = 0; i < 256; i++) { - if (inUse[i]) - seqToUnseq[nInUseShadow++] = (byte) i; - } - - this.nInUse = nInUseShadow; - } - /** * Index of the last char in the block, so the block size == last + 1. */ @@ -92,6 +70,7 @@ public class CBZip2InputStream extends InputStream implements BZip2Constants { private int nInUse; private InputStream in; + private final boolean decompressConcatenated; private int currentChar = -1; @@ -129,7 +108,8 @@ public class CBZip2InputStream extends InputStream implements BZip2Constants { /** * Constructs a new CBZip2InputStream which decompresses bytes read from - * the specified stream. + * the specified stream. This doesn't suppprt decompressing + * concatenated .bz2 files. * *

Although BZip2 headers are marked with the magic * "Bz" this constructor expects the next byte in the @@ -143,12 +123,46 @@ public class CBZip2InputStream extends InputStream implements BZip2Constants { * if in == null */ public CBZip2InputStream(final InputStream in) throws IOException { + this(in, false); + } + + /** + * Constructs a new CBZip2InputStream which decompresses bytes + * read from the specified stream. + * + *

Although BZip2 headers are marked with the magic + * "Bz" this constructor expects the next byte in the + * stream to be the first one after the magic. Thus callers have + * to skip the first two bytes. Otherwise this constructor will + * throw an exception.

+ * + * @param in the InputStream from which this object should be created + * @param decompressConcatenated + * if true, decompress until the end of the input; + * if false, stop after the first .bz2 stream and + * leave the input position to point to the next + * byte after the .bz2 stream + * + * @throws IOException + * if the stream content is malformed or an I/O error occurs. + * @throws NullPointerException + * if in == null + */ + public CBZip2InputStream(final InputStream in, + final boolean decompressConcatenated) + throws IOException { super(); this.in = in; - init(); + this.decompressConcatenated = decompressConcatenated; + + init(true); + initBlock(); + setupBlock(); } + /** {@inheritDoc} */ + @Override public int read() throws IOException { if (this.in != null) { return read0(); @@ -157,6 +171,12 @@ public class CBZip2InputStream extends InputStream implements BZip2Constants { } } + /* + * (non-Javadoc) + * + * @see java.io.InputStream#read(byte[], int, int) + */ + @Override public int read(final byte[] dest, final int offs, final int len) throws IOException { if (offs < 0) { @@ -183,6 +203,21 @@ public class CBZip2InputStream extends InputStream implements BZip2Constants { return (destOffs == offs) ? -1 : (destOffs - offs); } + private void makeMaps() { + final boolean[] inUse = this.data.inUse; + final byte[] seqToUnseq = this.data.seqToUnseq; + + int nInUseShadow = 0; + + for (int i = 0; i < 256; i++) { + if (inUse[i]) { + seqToUnseq[nInUseShadow++] = (byte) i; + } + } + + this.nInUse = nInUseShadow; + } + private int read0() throws IOException { final int retChar = this.currentChar; @@ -222,18 +257,31 @@ public class CBZip2InputStream extends InputStream implements BZip2Constants { return retChar; } - private void init() throws IOException { + private boolean init(boolean isFirstStream) throws IOException { if (null == in) { throw new IOException("No InputStream"); } - if (in.available() == 0) { - throw new IOException("Empty InputStream"); + + if (isFirstStream) { + if (in.available() == 0) { + throw new IOException("Empty InputStream"); + } + } else { + int magic0 = this.in.read(); + if (magic0 == -1) { + return false; + } + int magic1 = this.in.read(); + if (magic0 != 'B' || magic1 != 'Z') { + throw new IOException("Garbage after a valid BZip2 stream"); + } } + int magic2 = this.in.read(); if (magic2 != 'h') { - throw new IOException("Stream is not BZip2 formatted: expected 'h'" - + " as first byte but got '" + (char) magic2 - + "'"); + throw new IOException(isFirstStream + ? "Stream is not in the BZip2 format" + : "Garbage after a valid BZip2 stream"); } int blockSize = this.in.read(); @@ -244,32 +292,50 @@ public class CBZip2InputStream extends InputStream implements BZip2Constants { this.blockSize100k = blockSize - '0'; - initBlock(); - setupBlock(); + this.bsLive = 0; + this.computedCombinedCRC = 0; + + return true; } private void initBlock() throws IOException { - char magic0 = bsGetUByte(); - char magic1 = bsGetUByte(); - char magic2 = bsGetUByte(); - char magic3 = bsGetUByte(); - char magic4 = bsGetUByte(); - char magic5 = bsGetUByte(); - - if (magic0 == 0x17 && - magic1 == 0x72 && - magic2 == 0x45 && - magic3 == 0x38 && - magic4 == 0x50 && - magic5 == 0x90) { - complete(); // end of file - } else if (magic0 != 0x31 || // '1' - magic1 != 0x41 || // ')' - magic2 != 0x59 || // 'Y' - magic3 != 0x26 || // '&' - magic4 != 0x53 || // 'S' - magic5 != 0x59 // 'Y' - ) { + char magic0; + char magic1; + char magic2; + char magic3; + char magic4; + char magic5; + + while (true) { + // Get the block magic bytes. + magic0 = bsGetUByte(); + magic1 = bsGetUByte(); + magic2 = bsGetUByte(); + magic3 = bsGetUByte(); + magic4 = bsGetUByte(); + magic5 = bsGetUByte(); + + // If isn't end of stream magic, break out of the loop. + if (magic0 != 0x17 || magic1 != 0x72 || magic2 != 0x45 + || magic3 != 0x38 || magic4 != 0x50 || magic5 != 0x90) { + break; + } + + // End of stream was reached. Check the combined CRC and + // advance to the next .bz2 stream if decoding concatenated + // streams. + if (complete()) { + return; + } + } + + if (magic0 != 0x31 || // '1' + magic1 != 0x41 || // ')' + magic2 != 0x59 || // 'Y' + magic3 != 0x26 || // '&' + magic4 != 0x53 || // 'S' + magic5 != 0x59 // 'Y' + ) { this.currentState = EOF; throw new IOException("bad block header"); } else { @@ -313,7 +379,7 @@ public class CBZip2InputStream extends InputStream implements BZip2Constants { this.computedCombinedCRC ^= this.computedBlockCRC; } - private void complete() throws IOException { + private boolean complete() throws IOException { this.storedCombinedCRC = bsGetInt(); this.currentState = EOF; this.data = null; @@ -321,8 +387,13 @@ public class CBZip2InputStream extends InputStream implements BZip2Constants { if (this.storedCombinedCRC != this.computedCombinedCRC) { reportCRCError(); } + + // Look for the next .bz2 stream if decompressing + // concatenated files. + return !decompressConcatenated || !init(false); } + @Override public void close() throws IOException { InputStream inShadow = this.in; if (inShadow != null) { @@ -978,5 +1049,14 @@ public class CBZip2InputStream extends InputStream implements BZip2Constants { } } + + private static void reportCRCError() throws IOException { + // The clean way would be to throw an exception. + //throw new IOException("crc error"); + + // Just print a message, like the previous versions of this class did + System.err.println("BZip2 CRC error"); + } + }