/* * $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 sound.jarnbjo.util.io; import java.io.IOException; /** * Implementation of the <code>BitInputStream</code> interface, * using a byte array as data source. */ public class ByteArrayBitInputStream implements BitInputStream { 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; } 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; } } 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]; } 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; } public int getSignedInt(int bits) throws IOException { int raw=getInt(bits); if(raw>=1<<(bits-1)) { raw-=1<<bits; } return raw; } 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.intValue(); } 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; } /** * <p>reads an integer encoded as "signed rice" as described in * the FLAC audio format specification</p> * * <p><b>not supported for little endian</b></p> * * @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 */ 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); } /** * <p>fills the array from <code>offset</code> with <code>len</code> * integers encoded as "signed rice" as described in * the FLAC audio format specification</p> * * <p><b>not supported for little endian</b></p> * * @param order * @param buffer * @param offset * @param len * @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 */ 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(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; } } int res=(msbs<<order)|lsbs; buffer[i]=(res&1)==1?-(res>>1)-1:(res>>1); } } } public void align() { if(endian==BIG_ENDIAN && bitIndex>=0) { bitIndex=7; byteIndex++; } else if(endian==LITTLE_ENDIAN && bitIndex<=7) { bitIndex=0; byteIndex++; } } 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; } }