/**
* GribRecordBMS.java 1.0 01/01/2001
*
* (C) Benjamin Stark
* Simone Giannecchini (simboss@tiscali.it) 2005
*/
package it.geosolutions.io.input;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* This class is an input stream wrapper that can read a specific number of
* bytes and bits from an input stream.
*
* @author Benjamin Stark
* @author Simone Giannecchini
* @version 1.1
*/
public final class BitInputStream extends FilterInputStream {
/**
* Buffer for one byte which will be processed bit by bit.
*/
private int bitBuf = 0;
/**
* Current bit position in <tt>bitBuf</tt>.
*/
private int bitPos = 0;
/**
* Constructs a bit input stream from an <tt>InputStream</tt> object.
*
* @param in input stream that will be wrapped
*/
public BitInputStream(InputStream in) {
super(in);
}
/**
* Read an unsigned 8 bit value.
*
* @return unsigned 8 bit value as integer
*/
public int readUI8()
throws IOException {
int ui8 = in.read();
if (ui8 < 0) {
throw new IOException("BitInputStream::readUI8:End of input.");
}
return ui8;
}
/**
* Read specific number of unsigned bytes from the input stream.
*
* @param length number of bytes to read and return as integers
*
* @return unsigned bytes as integer values
*/
public int[] readUI8(final int length)
throws IOException {
int[] data = new int[length];
int read = 0;
for (int i = 0; (i < length) && (read >= 0); i++) {
data[i] = read = this.read();
}
if (read < 0) {
throw new IOException("BitInputStream::readUI8(final int length): of input.");
}
return data;
}
/**
* Read specific number of bytes from the input stream.
*
* @param length number of bytes to read
*
* @return array of read bytes
*/
public byte[] read(final int length)
throws IOException {
final byte[] data = new byte[length];
final int numRead = this.read(data);
if (numRead < length) {
// retry reading
final int numReadRetry = this.read(data, numRead, data.length - numRead);
if ((numRead + numReadRetry) < length) {
throw new IOException("BitInputStream::read(final int length):Unexpected end of input.");
}
}
return data;
}
/**
* Read an unsigned value from the given number of bits.
*
* @param numBits number of bits used for the unsigned value
*
* @return value read from <tt>numBits</tt> bits as long
*/
public long readUBits(final int numBits)
throws IOException {
if (numBits == 0) {
return 0;
}
int bitsLeft = numBits;
long result = 0;
if (this.bitPos == 0) {
this.bitBuf = in.read();
this.bitPos = 8;
}
int shift=0;
while (true) {
shift = bitsLeft - this.bitPos;
if (shift > 0) {
// Consume the entire buffer
result |= (this.bitBuf << shift);
bitsLeft -= this.bitPos;
// Get the next byte from the input stream
this.bitBuf = in.read();
this.bitPos = 8;
}
else {
// Consume a portion of the buffer
result |= (this.bitBuf >> -shift);
this.bitPos -= bitsLeft;
this.bitBuf &= (0xff >> (8 - this.bitPos)); // mask off consumed bits
return result;
}
}
}
/**
* Read a signed value from the given number of bits
*
* @param numBits number of bits used for the signed value
*
* @return value read from <tt>numBits</tt> bits as integer
*/
public int readSBits(final int numBits)
throws IOException {
// Get the number as an unsigned value.
long uBits = readUBits(numBits);
// Is the number negative?
if ((uBits & (1L << (numBits - 1))) != 0) {
// Yes. Extend the sign.
uBits |= (-1L << numBits);
}
return (int) uBits;
}
}