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