|
|
@@ -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"); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|