/* * Copyright (c) 1997-2001 Alexandros Eleftheriadis, Yuntai Kyong and * Danny Hong * * This file is part of Flavor, developed at Columbia University * (www.ee.columbia.edu/flavor) * * Flavor is free software; you can redistribute it and/or modify * it under the terms of the Flavor Artistic License as described in * the file COPYING.txt. * */ /* * Authors: * Alexandros Eleftheriadis <eleft@ee.columbia.edu> * Yuntai Kyong <yuntaikyong@ieee.org> * Danny Hong <danny@ee.columbia.edu> * */ package edu.columbia.ee.flavor; import java.io.*; /** * Implementation of class to access a bitstream. */ public class Bitstream implements IBitstream { public static final int BUF_LEN = 1024; // Default buffer size public static long LONG_SIGN = (long)1 << 31; // Constants for double operations public static int MAX_SIZE_OF_BITS = 32; public static final int[] mask = { // Masks for bitstream manipulation 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff }; // Complement masks (used for sign extension) public static final int[] cmask = { 0xffffffff, 0xfffffffe, 0xfffffffc, 0xfffffff8, 0xfffffff0, 0xffffffe0, 0xffffffc0, 0xffffff80, 0xffffff00, 0xfffffe00, 0xfffffc00, 0xfffff800, 0xfffff000, 0xffffe000, 0xffffc000, 0xffff8000, 0xffff0000, 0xfffe0000, 0xfffc0000, 0xfff80000, 0xfff00000, 0xffe00000, 0xffc00000, 0xff800000, 0xff000000, 0xfe000000, 0xfc000000, 0xf8000000, 0xf0000000, 0xe0000000, 0xc0000000, 0x80000000, 0x00000000 }; // Sign masks (used for sign extension) public static final int[] smask = { 0x00000000, 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x00000080, 0x00000100, 0x00000200, 0x00000400, 0x00000800, 0x00001000, 0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000, 0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000, 0x00800000, 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000 }; protected boolean close_fd = false; // True if file needs to be closed protected int buf_len = BUF_LEN; // Usable buffer size (for partially filled buffers protected byte[] buf; // Input buffer protected int cur_bit; // Current bit position in buffer protected int total_bits; // Total bits read/written protected boolean eof = false; // EOF of data flag protected InputStream in = null; // Input file handle protected OutputStream out = null; // Output file handle protected int type; // Input or Output /** * Constructs input bitstream. * @param input * @author Jonathan Shneier * @throws FlIOException */ public Bitstream(InputStream input) throws FlIOException { this(input, BUF_LEN); } /** * Constructs output bitstream. * @param output * @author Jonathan Shneier * @throws FlIOException */ public Bitstream(OutputStream output) throws FlIOException { this(output, BUF_LEN); } /** * Constructs input bitstream with the given buffer length. * @param input * @param _buf_len * @author Jonathan Shneier * @throws FlIOException */ public Bitstream(InputStream input, int _buf_len) throws FlIOException { cur_bit = 0; total_bits = 0; type = BS_INPUT; buf_len = _buf_len; buf = new byte[buf_len]; try { in = input; cur_bit = BUF_LEN << 3; // Fake we are at the eof of buffer fill_buf(); } catch(Exception e) { e.printStackTrace(); throw new FlIOException(FlIOException.FILEOPENFAILED,e.toString()); } close_fd = false; } /** * Constructs an output bitstream with the given buffer length. * @param output * @param _buf_len * @author Jonathan Shneier * @throws FlIOException */ public Bitstream(OutputStream output, int _buf_len) throws FlIOException { cur_bit = 0; total_bits = 0; type = BS_OUTPUT; buf_len = _buf_len; buf = new byte[buf_len]; try { out = new BufferedOutputStream(output); } catch(Exception e) { throw new FlIOException(FlIOException.FILEOPENFAILED,e.toString()); } close_fd = false; } /** * Constructs either an output or an input stream, depending on the type * @param filename file name to open * @param _type I/O type( BS_INPUT or BS_OUTPUT ) * @throws FlIOException */ public Bitstream(String filename, int _type) throws FlIOException { this(filename, _type, BUF_LEN); } /** * Constructs either an output or an input stream, depending * on the type, with the given buffer length. * @param filename file name to open * @param _type I/O type( BS_INPUT or BS_OUTPUT ) * @param _buf_len buffer size * @throws FlIOException */ public Bitstream(String filename, int _type, int _buf_len) throws FlIOException { cur_bit = 0; total_bits = 0; type = _type; buf_len = _buf_len; buf = new byte[buf_len]; switch (type) { case BS_INPUT: try { in = new BufferedInputStream(new FileInputStream(filename)); cur_bit = BUF_LEN << 3; // Fake we are at the eof of buffer fill_buf(); } catch(IOException e) { throw new FlIOException(FlIOException.FILEOPENFAILED,e.toString()); } break; case BS_OUTPUT: try { out = new BufferedOutputStream(new FileOutputStream(filename)); } catch(IOException e) { throw new FlIOException(FlIOException.FILEOPENFAILED,e.toString()); } break; default: throw new FlIOException(FlIOException.INVALIDIOTYPE); } close_fd = true; } /** * close opened files * @see Object#finalize() */ protected void finalize() throws Throwable { super.finalize(); close(); } /** * close file * @throws FlIOException */ public void close() throws FlIOException { try { if (type==BS_OUTPUT) { flushbits(); if (close_fd) { out.close(); close_fd = false; } } else { if (close_fd) { in.close(); close_fd = false; } } } catch(IOException e) { throw new FlIOException(FlIOException.SYSTEMIOFAILED,e.toString()); } } /** * Check eof-of-file flag * @return <code>true</code> if at end of file */ public boolean atend() { return eof; } /** * Current bit position * @return the count of bits so far */ public int getpos() { return total_bits; } /**************/ /* Big endian */ /**************/ /** * Get next n bits * @param n number of bits to be fetched * @return the value of the fetched bits * @exception FlIOException * when n > 32 */ public int getbits(int n) throws FlIOException { int x = nextbits(n); cur_bit += n; total_bits += n; return x; } /** * Get next n bits * Java does not support unsigned type * Remains only for compatibility with C++ language * Blindly calls sgetbits * @see #sgetbits(int n) throws FlIOException */ public int sgetbits(int n) throws FlIOException { int x = snextbits(n); cur_bit += n; total_bits += n; return x; } /** * Peek next n bits * @param n number of bits to peek * @return the value of the peeked bits * @exception FlIOException * when n > 32 * eof-of-file * not enough data */ public int nextbits(int n) throws FlIOException { if (n > MAX_SIZE_OF_BITS || n < 1) throw new FlIOException(FlIOException.INVALIDBITSIZE); if (cur_bit + n > buf_len << 3) fill_buf(); if ((buf_len << 3) - cur_bit < n) throw new FlIOException(FlIOException.NOTENOUGHDATA); int x = 0; // Output value int j = cur_bit >>> 3; // Current byte position int end = (cur_bit + n - 1) >>> 3; // End byte position int room = 8 - (cur_bit % 8); // Room in the first byte if (room >= n) { x = (buf[j] >> room - n)&mask[n]; return x; } int leftover = (cur_bit + n) % 8; // Leftover bits in the last byte x |= buf[j]&mask[room]; // Fill out first byte for (j++; j<end; j++) { x <<= 8; // Shift and x |= buf[j]&mask[8]; // Put next byte } if (leftover > 0) { x <<= leftover; // Make room for remaining bits x |= (buf[j] >> (8-leftover))&mask[leftover]; // And put } else { x <<= 8; // Shift x |= buf[j]&mask[8]; // Put next byte } return x; } /** * Peek next n bits - signed value * @param n number of bits to peek * @return the signed value of the peeked bits * @exception FlIOException * when n > 32 * eof-of-file * not enough data */ public int snextbits(int n) throws FlIOException { int x = nextbits(n); if (n>1 && ((smask[n]&x) != 0)) return x|cmask[n]; else return x; } /** * Get float value(32 bits) from the bistream * @return float value * @exception FlIOException */ public float getfloat() throws FlIOException { float f = nextfloat(); skipbits(32); return f; } /** * Peak float value(32 bits) from the bistream * @return float value * @exception FlIOException */ public float nextfloat() throws FlIOException { return Float.intBitsToFloat(nextbits(32)); } /** * Get double value(64 bits) from bistream * @return double value * @exception FlIOException */ public double getdouble() throws FlIOException { double d = nextdouble(); skipbits(64); return d; } /** * Get next double value from bistream * @return double value * @throws FlIOException */ public double nextdouble() throws FlIOException { if (cur_bit + 64 > (buf_len << 3)) fill_buf(); long l = sgetbits(32); l <<= 32; int i = sgetbits(32); int j = 0; if (i < 0) j = i & mask[31]; l |= j; if (i < 0 ) l |= LONG_SIGN; cur_bit -= 64; total_bits -= 64; return Double.longBitsToDouble(l); } /** * Put n bits * @param y value to put * @param n number of bits to put ( <= 32 bits) * @return <code>y</code> * @exception FlIOException */ public int putbits(int y, int n) throws FlIOException { int x = y; // Sanity check if(n > MAX_SIZE_OF_BITS || n < 1) throw new FlIOException(FlIOException.INVALIDBITSIZE); // Make sure we have enough room if ((cur_bit + n) > (buf_len << 3)) { flush_buf(); } int j = (cur_bit + n - 1) >>> 3; // End byte position int begin = cur_bit >>> 3; // Current byte position int room = 8 - (cur_bit % 8); // Room in the first byte of the buffer if (room >= n) { // Enough room buf[begin] &= cmask[room]; buf[begin] |= mask[room] & (x << room - n); cur_bit += n; total_bits += n; return y; } int remain = (n-room)%8; // Number of bits to put in the last byte if (remain > 0) { buf[j] = 0; buf[j] |= (x << 8-remain)&mask[8]; // Put the bits in the head of byte x >>= remain; // And eat up j--; } for (; j>begin; j--) { buf[j] = 0; buf[j] |= x&mask[8]; // Put next byte x >>>= 8; // Shift } buf[j] &= cmask[room]; buf[j] |= x&mask[room]; cur_bit += n; total_bits += n; return y; } /** * Put float value(32 bits) into bistream * @param f float value * @return float value * @exception FlIOException */ public float putfloat(float f) throws FlIOException { putbits(Float.floatToIntBits(f),32); return f; } /** * Put a double value into bitstream * @param d double variable * @return the double value * @throws FlIOException if putbits() fails */ public double putdouble(double d) throws FlIOException { long l = Double.doubleToLongBits(d); int i = (int)(l >>> 32); putbits(i, 32); i = (int)(l & 0x00000000FFFFFFFF); putbits(i, 32); return d; } /* DH 9/12/2001 ++ */ /*****************/ /* Little endian */ /*****************/ /** * Gets bits in little order. * @param n bits to get * @return value * @throws FlIOException */ public int little_getbits(int n) throws FlIOException { int x = little_nextbits(n); cur_bit += n; total_bits += n; return x; } public int little_sgetbits(int n) throws FlIOException { int x = little_snextbits(n); cur_bit += n; total_bits += n; return x; } public int little_nextbits(int n) throws FlIOException { if (n > MAX_SIZE_OF_BITS || n < 1) throw new FlIOException(FlIOException.INVALIDBITSIZE); int x = 0; // Output value int bytes = n >>> 3; // Number of bytes to read + int leftbits = n%8; // Number of bits to read int byte_x = 0; int i = 0; for (; i < bytes; i++) { byte_x = nextbits(8); cur_bit += 8; byte_x <<= (8*i); x |= byte_x; } if (leftbits > 0) { byte_x = nextbits(leftbits); byte_x <<= (8*i); x |= byte_x; } cur_bit -= i*8; return x; } public int little_snextbits(int n) throws FlIOException { int x = little_nextbits(n); if (n>1 && ((smask[n]&x) != 0)) return x|cmask[n]; else return x; } public float little_getfloat() throws FlIOException { float f = little_nextfloat(); skipbits(32); return f; } public float little_nextfloat() throws FlIOException { return Float.intBitsToFloat(little_nextbits(32)); } public double little_getdouble() throws FlIOException { double d = little_nextdouble(); skipbits(64); return d; } public double little_nextdouble() throws FlIOException { if (cur_bit + 64 > (buf_len << 3)) fill_buf(); int i = little_sgetbits(32); long l = little_sgetbits(32); l <<= 32; int j = 0; if (i < 0) j = i & mask[31]; l |= j; if (i < 0 ) l |= LONG_SIGN; cur_bit -= 64; total_bits -= 64; return Double.longBitsToDouble(l); } public int little_putbits(int y, int n) throws FlIOException { int x = y; // Sanity check if(n > MAX_SIZE_OF_BITS || n < 1) throw new FlIOException(FlIOException.INVALIDBITSIZE); int bytes = n >>> 3; // Number of bytes to write + int leftbits = n%8; // NUmber of bits to write int byte_x = 0; int i = 0; for (; i < bytes; i++) { byte_x = (x >> (8*i)) & mask[8]; putbits(byte_x, 8); } if (leftbits > 0) { byte_x = (x >> (8*i)) & mask[leftbits]; putbits(byte_x, leftbits); } return y; } public float little_putfloat(float f) throws FlIOException { putbits(Float.floatToIntBits(f),32); return f; } public double little_putdouble(double d) throws FlIOException { long l = Double.doubleToLongBits(d); int i = (int)(l & 0x00000000FFFFFFFF); little_putbits(i, 32); i = (int)(l >>> 32); little_putbits(i, 32); return d; } /* DH 9/12/2001 -- */ /** * Skip n bits * @param n number of bits to skip * @exception FlIOException */ public void skipbits (int n) throws FlIOException { int x = n; int buf_size = buf_len << 3; while (cur_bit + x > buf_size) { x -= buf_size - cur_bit; cur_bit = buf_size; if (type == BS_INPUT) fill_buf(); else flush_buf(); } cur_bit += x; total_bits += n; } /** * Align bitstream * @param n number of bits to align on * @return the number of skipped bits * @throws FlIOException */ public int align(int n) throws FlIOException { if ((n % 8) != 0) // We only allow alignment on multiples of bytes throw new FlIOException(FlIOException.INVALIDALIGNMENT); int s = 0; // align on next byte boundary if ((total_bits % 8) != 0) { s = (total_bits % 8); skipbits (8-s); } // skip bits until total_bits are aligned on n while ((total_bits % n) != 0) { s += 8; skipbits(8); } return s; } /** * Flush all content in the buffer * @throws FlIOException */ public void flushbits() throws FlIOException { flush_buf(); if(cur_bit == 0) return; try { out.write(buf, 0, 1); } catch(IOException e) { throw new FlIOException(FlIOException.SYSTEMIOFAILED,e.toString()); } buf[0] = 0; cur_bit = 0; // Now only the left-over bits } /** * Return current bit position * @return current bit position */ public int getCurrentBit() { return cur_bit; } /** * Fill out internal buffer from the file * @exception FlIOException * System I/O failed or End of File */ private void fill_buf() throws FlIOException { int n, u, l; n = cur_bit >>> 3; // Current byte offset u = buf_len - n; // Remaining bytes System.arraycopy(buf, n, buf, 0, u); // Copy remaining data into the head of the buffer try { // Now we have a room for n bytes from offset u in the buffer l = in.read(buf, u, n); } catch(IOException e) { throw new FlIOException(FlIOException.SYSTEMIOFAILED, e.toString()); } if (l == -1) { // EOF? eof = true; throw new FlIOException(FlIOException.ENDOFDATA); } if (l < n) buf_len = u + l; // Adjust buffer size cur_bit &= 7; // Now we are at the first byte } /** * Flush the buffer excluding the left-over bits * @exception FlIOException * System I/O failed or End of File */ private void flush_buf() throws FlIOException { int l = cur_bit >>> 3; // Size of data in the buffer try { out.write(buf, 0, l); // File output } catch(IOException e) { throw new FlIOException(FlIOException.SYSTEMIOFAILED,e.toString()); } cur_bit &= 0x7; // Keep left-over bits only // Are there any left-over bits? if (cur_bit != 0) buf[0] = buf[l]; } private static void integerTest() { int val = 0; int number_of_loop = 1000; try { System.out.println("Writing..."); Bitstream output = new Bitstream("test.bs",BS_OUTPUT); for (int j=0; j<number_of_loop; j++) for (int i=1; i<=32; i++) { val = i==2?1:i; output.putbits(val,i); val = i==1?0:-i; output.putbits(val,i); } output.close(); System.out.println("Reading..."); Bitstream input = new Bitstream("test.bs",BS_INPUT); for (int j=0; j<number_of_loop; j++) { for (int i=1; i<=32; i++) { val = input.getbits(i); if (val != (i==2?1:i)) { System.out.println("ERROR"); System.exit(-1); } val = input.sgetbits(i); if (val != (i==1 ? 0 : -i)) { System.out.println("ERROR 2! " + j + i + val); System.exit(-1); } } } input.close(); } catch( Exception e) { System.out.println(e.toString()); } } public static void main(String args[]) { float cf = (float)-1823000.3123, f=0; double cd = 22123.233, d=0; Bitstream.integerTest(); try { Bitstream output = new Bitstream("test.bs",BS_OUTPUT); output.putdouble(cd); output.putfloat(cf); output.close(); Bitstream input = new Bitstream("test.bs",BS_INPUT); d = input.getdouble(); f = input.getfloat(); input.close(); System.out.println("d: " + d); System.out.println("f: " + f); } catch( Exception e) { System.out.println(e.toString()); } } /** * Return mode(BS_INPUT or BS_OUTPUT) * @return BS_INPUT or BS_OUPUT */ public int getmode() { return type; } }