de.jarnbjo.util.io.ByteArrayBitInputStream Maven / Gradle / Ivy
/*
* $ProjectName$
* $ProjectRevision$
* -----------------------------------------------------------
* $Id: ByteArrayBitInputStream.java,v 1.3 2003/04/10 19:48:31 jarnbjo Exp $
* -----------------------------------------------------------
*
* $Author: jarnbjo $
*
* Description:
*
* Copyright 2002-2003 Tor-Einar Jarnbjo
* -----------------------------------------------------------
*
* Change History
* -----------------------------------------------------------
* $Log: ByteArrayBitInputStream.java,v $
* Revision 1.3 2003/04/10 19:48:31 jarnbjo
* no message
*
* Revision 1.2 2003/03/16 01:11:39 jarnbjo
* no message
*
* Revision 1.1 2003/03/03 21:02:20 jarnbjo
* no message
*/
package de.jarnbjo.util.io;
import java.io.IOException;
/**
* Implementation of the {@code BitInputStream} interface, using a byte array as
* data source.
*/
public class ByteArrayBitInputStream implements BitInputStream {
final private byte[] source;
private byte currentByte;
private int endian;
private int byteIndex = 0;
private int bitIndex = 0;
public ByteArrayBitInputStream(byte[] source) {
this(source, LITTLE_ENDIAN);
}
public ByteArrayBitInputStream(byte[] source, int endian) {
this.endian = endian;
this.source = source;
currentByte = source[0];
bitIndex = (endian == LITTLE_ENDIAN) ? 0 : 7;
}
@Override
public boolean getBit() throws IOException {
if (endian == LITTLE_ENDIAN) {
if (bitIndex > 7) {
bitIndex = 0;
currentByte = source[++byteIndex];
}
return (currentByte & (1 << (bitIndex++))) != 0;
} else {
if (bitIndex < 0) {
bitIndex = 7;
currentByte = source[++byteIndex];
}
return (currentByte & (1 << (bitIndex--))) != 0;
}
}
@Override
public int getInt(int bits) throws IOException {
if (bits > 32) {
throw new IllegalArgumentException(
"Argument \"bits\" must be <= 32");
}
int res = 0;
if (endian == LITTLE_ENDIAN) {
for (int i = 0; i < bits; i++) {
if (getBit()) {
res |= (1 << i);
}
}
} else {
if (bitIndex < 0) {
bitIndex = 7;
currentByte = source[++byteIndex];
} else if (bitIndex >= 7) {
currentByte = source[--byteIndex];
}
if (bits <= bitIndex + 1) {
int ci = ((int) currentByte) & 0xff;
int offset = 1 + bitIndex - bits;
int mask = ((1 << bits) - 1) << offset;
res = (ci & mask) >> offset;
bitIndex -= bits;
} else {
res = (((int) currentByte) & 0xff & ((1 << (bitIndex + 1)) - 1))
<< (bits - bitIndex - 1);
bits -= bitIndex + 1;
currentByte = source[++byteIndex];
while (bits >= 8) {
bits -= 8;
res |= (((int) source[byteIndex]) & 0xff) << bits;
currentByte = source[++byteIndex];
}
if (bits > 0) {
int ci = ((int) source[byteIndex]) & 0xff;
res |= (ci >> (8 - bits)) & ((1 << bits) - 1);
bitIndex = 7 - bits;
} else {
currentByte = source[--byteIndex];
bitIndex = -1;
}
}
}
return res;
}
@Override
public int getSignedInt(int bits) throws IOException {
int raw = getInt(bits);
if (raw >= 1 << (bits - 1)) {
raw -= 1 << bits;
}
return raw;
}
@Override
public int getInt(HuffmanNode root) throws IOException {
while (root.value == null) {
if (bitIndex > 7) {
bitIndex = 0;
currentByte = source[++byteIndex];
}
root = (currentByte & (1 << (bitIndex++))) != 0 ? root.o1 : root.o0;
}
return root.value;
}
@Override
public long getLong(int bits) throws IOException {
if (bits > 64) {
throw new IllegalArgumentException(
"Argument \"bits\" must be <= 64");
}
long res = 0;
if (endian == LITTLE_ENDIAN) {
for (int i = 0; i < bits; i++) {
if (getBit()) {
res |= (1L << i);
}
}
} else {
for (int i = bits - 1; i >= 0; i--) {
if (getBit()) {
res |= (1L << i);
}
}
}
return res;
}
/**
*
* reads an integer encoded as "signed rice" as described in the FLAC audio
* format specification
*
*
* not supported for little endian
*
* @param order
* @return the decoded integer value read from the stream
*
* @throws IOException if an I/O error occurs
* @throws UnsupportedOperationException if the method is not supported by
* the implementation
*/
@Override
public int readSignedRice(int order) throws IOException {
int msbs = -1, lsbs = 0, res = 0;
if (endian == LITTLE_ENDIAN) {
// little endian
throw new UnsupportedOperationException(
"ByteArrayBitInputStream.readSignedRice() "
+ "is only supported in big endian mode");
} else {
// big endian
byte cb = source[byteIndex];
do {
msbs++;
if (bitIndex < 0) {
bitIndex = 7;
byteIndex++;
cb = source[byteIndex];
}
} while ((cb & (1 << bitIndex--)) == 0);
int bits = order;
if (bitIndex < 0) {
bitIndex = 7;
byteIndex++;
}
if (bits <= bitIndex + 1) {
int ci = ((int) source[byteIndex]) & 0xff;
int offset = 1 + bitIndex - bits;
int mask = ((1 << bits) - 1) << offset;
lsbs = (ci & mask) >> offset;
bitIndex -= bits;
} else {
lsbs = (((int) source[byteIndex]) & 0xff
& ((1 << (bitIndex + 1)) - 1)) << (bits - bitIndex - 1);
bits -= bitIndex + 1;
byteIndex++;
while (bits >= 8) {
bits -= 8;
lsbs |= (((int) source[byteIndex]) & 0xff) << bits;
byteIndex++;
}
if (bits > 0) {
int ci = ((int) source[byteIndex]) & 0xff;
lsbs |= (ci >> (8 - bits)) & ((1 << bits) - 1);
bitIndex = 7 - bits;
} else {
byteIndex--;
bitIndex = -1;
}
}
res = (msbs << order) | lsbs;
}
return (res & 1) == 1 ? -(res >> 1) - 1 : (res >> 1);
}
/**
*
* fills the array from {@code offset} with {@code len} integers encoded as
* "signed rice" as described in the FLAC audio format specification
*
*
* not supported for little endian
*
* @param order
* @param buffer
* @param off offset
* @param len
*
* @throws IOException if an I/O error occurs
* @throws UnsupportedOperationException if the method is not supported by
* the implementation
*/
@Override
public void readSignedRice(int order, int[] buffer, int off, int len)
throws IOException {
if (endian == LITTLE_ENDIAN) {
// little endian
throw new UnsupportedOperationException(
"ByteArrayBitInputStream.readSignedRice() "
+ "is only supported in big endian mode");
} else {
// big endian
for (int i = off; i < off + len; i++) {
int msbs = -1, lsbs = 0;
byte cb = source[byteIndex];
do {
msbs++;
if (bitIndex < 0) {
bitIndex = 7;
byteIndex++;
cb = source[byteIndex];
}
} while ((cb & (1 << bitIndex--)) == 0);
int bits = order;
if (bits <= bitIndex + 1) {
int ci = ((int) source[byteIndex]) & 0xff;
int offset = 1 + bitIndex - bits;
int mask = ((1 << bits) - 1) << offset;
lsbs = (ci & mask) >> offset;
bitIndex -= bits;
} else {
lsbs = (((int) source[byteIndex])
& 0xff & ((1 << (bitIndex + 1)) - 1))
<< (bits - bitIndex - 1);
bits -= bitIndex + 1;
byteIndex++;
while (bits >= 8) {
bits -= 8;
lsbs |= (((int) source[byteIndex]) & 0xff) << bits;
byteIndex++;
}
if (bits > 0) {
int ci = ((int) source[byteIndex]) & 0xff;
lsbs |= (ci >> (8 - bits)) & ((1 << bits) - 1);
bitIndex = 7 - bits;
} else {
byteIndex--;
bitIndex = -1;
}
}
int res = (msbs << order) | lsbs;
buffer[i] = (res & 1) == 1 ? -(res >> 1) - 1 : (res >> 1);
}
}
}
@Override
public void align() {
if (endian == BIG_ENDIAN && bitIndex >= 0) {
bitIndex = 7;
byteIndex++;
} else if (endian == LITTLE_ENDIAN && bitIndex <= 7) {
bitIndex = 0;
byteIndex++;
}
}
@Override
public void setEndian(int endian) {
if (this.endian == BIG_ENDIAN && endian == LITTLE_ENDIAN) {
bitIndex = 0;
byteIndex++;
} else if (this.endian == LITTLE_ENDIAN && endian == BIG_ENDIAN) {
bitIndex = 7;
byteIndex++;
}
this.endian = endian;
}
/**
* @return the byte array used as a source for this instance
*/
public byte[] getSource() {
return source;
}
@Override
public int position() {
return byteIndex;
}
@Override
public void skip(int length) {
if (this.endian == BIG_ENDIAN) {
bitIndex = 7;
} else if (this.endian == LITTLE_ENDIAN) {
bitIndex = 0;
}
byteIndex += length;
}
}