BitStreamOutputStream.java |
| //---------------------------- // Class BitStreamOutputStream // --------------------------- // Implements an enhanced OutputStream which allows you to write a // stream of bit fields ranging in size from 1 bit (a true bit // stream) to 32 bits (a stream of integers). The size of the current // bitfield can be changed at any point while writing the stream. // (c) Laurence Vanhelsuwe 1996. E-Mail: LVA@telework.demon.co.uk //------------------------------------------------------------------ package jlittle_ex9; import java.io.*; public class BitStreamOutputStream extends FilterOutputStream { final static int EIGHT = 8; // 8 bits per byte protected short buffer; // our BYTE bitstream write buffer protected int bitsInCache; // how many cached bits in our byte protected int fieldSize; // current size of bitstream fields protected long maxFieldValue; // max value that fits bitfield //------------------------------------------------------------------ public BitStreamOutputStream(OutputStream out) { this(out, EIGHT); // default to a normal byte stream } //------------------------------------------------------------------ public BitStreamOutputStream(OutputStream out, int bitFieldSize) { super(out); // call FilterOutputStream constr. setBitFieldSize(bitFieldSize); bitsInCache = 0; // we haven't got any cached bits buffer = 0; // start w/ clean buffer (for ORs !) } //------------------------------------------------------------------ public void setBitFieldSize(int bits) throws IllegalArgumentException { if (bits>32 || bits<1) throw new IllegalArgumentException ( "BitField size ("+ bits + ") no good. Has to be between 1 and 32." ); this.fieldSize = bits; this.maxFieldValue = (1L << bits) - 1; // precalc max bf value } //------------------------------------------------------------------ public int getBitFieldSize() { return this.fieldSize; } //------------------------------------------------------------------ // Write a bitfield to the output stream. The number of bits written is // the current bitfield length. Bitfield can be on arbitrary bit boundaries. //------------------------------------------------------------------ public void writeBitField(int bf) throws IOException { int bitsToWrite; // how many bits left to write int capacity; // how many bits fit in write buffer int partial, partialSize; // partial bitfield and its size in bits int bfExtractPos; // bitfield extract position (bit number) // check that bitfield fits in current bitfield size if (bf > maxFieldValue ) { throw new IllegalArgumentException ( "Can not pack bitfield " + bf + " in " + fieldSize + " bits." ); } bitsToWrite = fieldSize; bfExtractPos = fieldSize; // a single bitfield might have to be written out in several // passes since the lot has to pass through the single byte // write buffer. This inefficient situation is a result of // the complex aligning required to append any bitfield to // the currently written stream. while (bitsToWrite > 0) { if (bitsInCache != EIGHT) { // if capacity left capacity = EIGHT - bitsInCache; // in write buffer... partialSize = Math.min( bitsToWrite, capacity); bfExtractPos -= partialSize; partial = extract (bf, partialSize, bfExtractPos); buffer |= partial << (capacity - partialSize); bitsToWrite -= partialSize; bitsInCache += partialSize; } if (bitsInCache == EIGHT) { // if write buffer full, out.write((int) buffer); // send it on its way bitsInCache = 0; // and continue with buffer = 0; // clean buffer } } } //------------------------------------------------------------------ // extract a bitfield of length 'bits' from an integer source. // bitfield starts at bit 'pos' and is returned right-aligned to bitpos 0 //------------------------------------------------------------------ private int extract (int source, int bits, int pos) { source = source >> pos; // align bitfield to bit 0 int mask = ~( (-1) << bits);// create a mask to get clean bitfld return source & mask; // return bitfield (0 bits padded) } //--------------------------------------------- // The remaining methods are methods we override from // our parent class: FilterOutputStream //--------------------------------------------- //------------------------------------------------------------------ // Override write() method to write a byte on any bit boundary. //------------------------------------------------------------------ public void write(int b) throws IOException { int previousBFSize; previousBFSize = getBitFieldSize(); setBitFieldSize(EIGHT); try { writeBitField( b & 0xFF ); } // if writeBitField threw an exception, make sure we reset // the current bitfield size before letting the exception thru finally { setBitFieldSize(previousBFSize); } } //------------------------------------------------------------------ // Override block write() methods to use basic write() as building block. //------------------------------------------------------------------ public void write(byte b[], int off, int len) throws IOException { for (int i = 0 ; i < len ; i++) { write(b[off + i]); } } public void write(byte b[]) throws IOException { write(b, 0, b.length); } //------------------------------------------------------------------ // Override flush() method. //------------------------------------------------------------------ public void flush() throws IOException { // REFUSE to flush as this advances file pointer of underlying stream ! } //------------------------------------------------------------------ // Override close() method to correctly flush any remaining bitfields // in write buffer before closing output chain. //------------------------------------------------------------------ public void close() throws IOException { if (bitsInCache != 0) { out.write((int) buffer); } out.flush(); out.close(); } } // End of class BitStreamOutputStream |
James Little |