/** * Copyright (C) 2007 Rui Shen (rui.shen@gmail.com) All Right Reserved * File : BitsInputStream.java * Created : 2007-3-1 * **************************************************************************** * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************** */ package org.geometerplus.fbreader.formats.chm; import java.io.EOFException; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; /** * Treat byte array as bit stream */ class BitsInputStream extends FilterInputStream { static final int BUFFER_BITS = 32; static final int[]UNSIGNED_MASK = new int[]{ 0, 0x01, 0x03, 0x07, 0x0f, 0x01f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff, }; int bitbuf; int bitsLeft; public BitsInputStream(InputStream in) { super(in); } /** * Read 32-bit little endian int instead of a byte! */ public int read32LE() throws IOException { return in.read() + (in.read() << 8) + (in.read() << 16) + (in.read() << 24); } /** * flush n bytes, and reset bitbuf, bitsLeft * often used to align the byte array * NOTE: n may be negative integer, e.g. -2 */ public void skip(int n) throws IOException { bitbuf = 0; // TODO really want to do this? bitsLeft = 0; super.skip(n); } /** * Make sure there are at least n (<=16) bits in the buffer, * otherwise, read a 16-bit little-endian word from the byte array. * returns bitsLeft; */ public int ensure(int n) throws IOException { while (bitsLeft < n) { // read in two bytes int b1 = in.read(); int b2 = in.read(); if ( (b1 | b2) < 0 ) break; bitbuf |= ( b1 | ( b2 << 8) ) << (BUFFER_BITS - 16 - bitsLeft); bitsLeft += 16; } return bitsLeft; } /** * Read no more than 16 bits big endian, bits are arranged as * <pre> * 00000000 00000000 00000000 00000000, bitsLeft = 0; * ensure(1); * aaaaaaaa 00000000 00000000 00000000, bitsLeft = 8; * read(3) = 00000aaa; * aaaaa000 00000000 00000000 00000000, bitsLeft = 5; * ensure(16); * aaaaabbb bbbbbccc ccccc000 00000000, bitsLeft = 21; * read(8) = aaaaabbb; * bbbbbccc ccccc000 00000000 00000000, bitsLeft = 13; * </pre> */ public int readLE(int n) throws IOException { int ret = peek(n); bitbuf <<= n; bitsLeft -= n; return ret; } /** * Peek n bits, may raise EOFException. */ public int peek(int n) throws IOException { if (ensure(n) < n) throw new EOFException(); return (( bitbuf >> (BUFFER_BITS - n) )) & UNSIGNED_MASK[n]; } /** * Peek no more than n bits, so there is no EOFException. */ public int peekUnder(int n) throws IOException { ensure(n); return (( bitbuf >> (BUFFER_BITS - n) )) & UNSIGNED_MASK[n]; } public void readFully(byte b[]) throws IOException { readFully(b, 0, b.length); } public void readFully(byte b[], int off, int len) throws IOException { for (int n = 0; n < len; ) { int count = read(b, off + n, len - n); if (count < 0) throw new EOFException(); n += count; } } /** * return binary string of bitbuf */ public String toString() { String s = "00000000000000000000000000000000" + Long.toBinaryString(bitbuf); s = s.substring(s.length() - 32); return s.substring(0, 8) + " " + s.substring(8, 16) + " " + s.substring(16, 24) + " " + s.substring(24, 32) + " " + bitsLeft; } }