/* * Copyright © 2010-2011 Rebecca G. Bettencourt / Kreative Software * <p> * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * <a href="http://www.mozilla.org/MPL/">http://www.mozilla.org/MPL/</a> * <p> * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * <p> * Alternatively, the contents of this file may be used under the terms * of the GNU Lesser General Public License (the "LGPL License"), in which * case the provisions of LGPL License are applicable instead of those * above. If you wish to allow use of your version of this file only * under the terms of the LGPL License and not to allow others to use * your version of this file under the MPL, indicate your decision by * deleting the provisions above and replace them with the notice and * other provisions required by the LGPL License. If you do not delete * the provisions above, a recipient may use your version of this file * under either the MPL or the LGPL License. * @since KSFL 1.2 * @author Rebecca G. Bettencourt, Kreative Software */ package com.kreative.binpack; import java.io.*; import java.math.BigInteger; import java.math.MathContext; import java.util.BitSet; public class BitInputStream extends InputStream implements Closeable, DataInput { private DataInputStream in; private int bitpos; private int bittmp; private long bitsread; private int markbitpos; private int markbittmp; private long markbitsread; public BitInputStream(InputStream in) { this.in = new DataInputStream(in); this.bitpos = 0; this.bittmp = 0; this.bitsread = 0L; this.markbitpos = 0; this.markbittmp = 0; this.markbitsread = 0L; } public boolean atBitBoundary(int multiple) { if (multiple < 0) throw new IllegalArgumentException(); else if (multiple == 0 || multiple == 1) return true; else return ((bitsread % (long)multiple) == 0L); } public boolean atByteBoundary(int multiple) { if (multiple < 0) throw new IllegalArgumentException(); else if (multiple == 0) return true; else if (multiple == 1) return (bitpos == 0); else return ((bitpos == 0) && (((bitsread >> 3L) % (long)multiple) == 0L)); } public long bitsRead() { return bitsread; } public long bytesRead() { return (bitsread >> 3L); } public boolean readBit() throws IOException { if (bitpos == 0) { int tmp = in.readByte(); bitpos = 128; bittmp = tmp; } boolean res = ((bittmp & bitpos) != 0); bitpos >>= 1; bitsread++; return res; } public boolean skipBit() throws IOException { if (bitpos == 0) { int tmp = in.read(); if (tmp < 0) return false; bitpos = 128; bittmp = tmp; } bitpos >>= 1; bitsread++; return true; } public boolean availableBit() throws IOException { if (bitpos == 0) { return in.available() > 0; } else { return true; } } public BitSet readBits(int n) throws IOException { if (n < 0) throw new IllegalArgumentException(); else if (n == 0) return new BitSet(); else { BitSet b = new BitSet(); while (n > 0) { n--; if (readBit()) b.set(n); } return b; } } public BitSet readBitsLE(int n) throws IOException { if (n < 0) throw new IllegalArgumentException(); else if (n == 0) return new BitSet(); else if (bitpos != 0 || ((n & 7) != 0)) throw new IOException("Can't read little-endian values unless on a byte boundry with a byte-multiple width"); else { int nb = (n >> 3) - 1; BitSet b = new BitSet(); while (n > 0) { n--; if (readBit()) b.set((n & 7) | ((nb - (n >> 3)) << 3)); } return b; } } public long skipBits(long n) throws IOException { if (n < 0) throw new IllegalArgumentException(); else if (n == 0) return 0; else { long a = 0; while (n > 0 && bitpos > 0) { if (skipBit()) { n--; a++; } else { return a; } } if (n >= 8) { long bs = in.skip(n >> 3); bitsread += (bs << 3); n -= (bs << 3); a += (bs << 3); } while (n > 0) { if (skipBit()) { n--; a++; } else { return a; } } return a; } } public long availableBits() throws IOException { int m = bitpos; int b = 0; while (m > 0) { m >>= 1; b++; } return (long)b + ((long)in.available() << 3L); } public BigInteger readInteger(int n) throws IOException { if (n < 0) throw new IllegalArgumentException(); else if (n == 0) return BigInteger.ZERO; else { n--; BigInteger i = (readBit() ? BigInteger.ONE.negate().shiftLeft(n) : BigInteger.ZERO); while (n > 0) { n--; if (readBit()) i = i.setBit(n); } return i; } } public BigInteger readIntegerLE(int n) throws IOException { if (n < 0) throw new IllegalArgumentException(); else if (n == 0) return BigInteger.ZERO; else if (bitpos != 0 || ((n & 7) != 0)) throw new IOException("Can't read little-endian values unless on a byte boundry with a byte-multiple width"); else { int on = n; int nb = (n >> 3) - 1; BigInteger i = BigInteger.ZERO; while (n > 0) { n--; if (readBit()) i = i.setBit((n & 7) | ((nb - (n >> 3)) << 3)); } if (i.testBit(on-1)) { i = i.xor(BigInteger.ONE.negate().shiftLeft(on)); } return i; } } public BigInteger readUnsignedInteger(int n) throws IOException { if (n < 0) throw new IllegalArgumentException(); else if (n == 0) return BigInteger.ZERO; else { BigInteger i = BigInteger.ZERO; while (n > 0) { n--; if (readBit()) i = i.setBit(n); } return i; } } public BigInteger readUnsignedIntegerLE(int n) throws IOException { if (n < 0) throw new IllegalArgumentException(); else if (n == 0) return BigInteger.ZERO; else if (bitpos != 0 || ((n & 7) != 0)) throw new IOException("Can't read little-endian values unless on a byte boundry with a byte-multiple width"); else { int nb = (n >> 3) - 1; BigInteger i = BigInteger.ZERO; while (n > 0) { n--; if (readBit()) i = i.setBit((n & 7) | ((nb - (n >> 3)) << 3)); } return i; } } public Number readFloat(int n, MathContext mc) throws IOException { if (n < 0) throw new IllegalArgumentException(); else { int s = FPUtilities.optimalSignWidth(n); int e = FPUtilities.optimalExponentWidth(n); int m = FPUtilities.optimalMantissaWidth(n); return readFloat(s, e, m, mc); } } public Number readFloatLE(int n, MathContext mc) throws IOException { if (n < 0) throw new IllegalArgumentException(); else { int s = FPUtilities.optimalSignWidth(n); int e = FPUtilities.optimalExponentWidth(n); int m = FPUtilities.optimalMantissaWidth(n); return readFloatLE(s, e, m, mc); } } public Number readFloat(int s, int e, int m, MathContext mc) throws IOException { if (s < 0 || s > 1 || e < 0 || m < 0) throw new IllegalArgumentException(); else { int b = FPUtilities.optimalBias(e); return readFloat(s, e, m, b, mc); } } public Number readFloatLE(int s, int e, int m, MathContext mc) throws IOException { if (s < 0 || s > 1 || e < 0 || m < 0) throw new IllegalArgumentException(); else { int b = FPUtilities.optimalBias(e); return readFloatLE(s, e, m, b, mc); } } public Number readFloat(int s, int e, int m, int b, MathContext mc) throws IOException { if (s < 0 || s > 1 || e < 0 || m < 0) throw new IllegalArgumentException(); else { BigInteger si = readUnsignedInteger(s); BigInteger ei = readUnsignedInteger(e); BigInteger mi = readUnsignedInteger(m); return FPUtilities.decodeFloat(si, ei, mi, s, e, m, b, mc); } } public Number readFloatLE(int s, int e, int m, int b, MathContext mc) throws IOException { if (s < 0 || s > 1 || e < 0 || m < 0) throw new IllegalArgumentException(); else if (bitpos != 0 || (((s+e+m) & 7) != 0)) throw new IOException("Can't read little-endian values unless on a byte boundry with a byte-multiple width"); else { BigInteger i = readUnsignedIntegerLE(s+e+m); BigInteger[] ii = FPUtilities.splitFloat(i, s, e, m); return FPUtilities.decodeFloat(ii[0], ii[1], ii[2], s, e, m, b, mc); } } public int available() throws IOException { return in.available(); } public void close() throws IOException { in.close(); } public void mark(int readlimit) { in.mark(readlimit); markbitpos = bitpos; markbittmp = bittmp; markbitsread = bitsread; } public boolean markSupported() { return in.markSupported(); } public int read() throws IOException { if (bitpos == 0) { int res = in.read(); if (res >= 0) bitsread += 8L; return res; } else { int tmp = in.read(); if (tmp >= 0) { int res = 0; int rpos = 128; while (bitpos > 0) { if ((bittmp & bitpos) != 0) res |= rpos; rpos >>= 1; bitpos >>= 1; bitsread++; } bitpos = 128; bittmp = tmp; while (rpos > 0) { if ((bittmp & bitpos) != 0) res |= rpos; rpos >>= 1; bitpos >>= 1; bitsread++; } return res; } else { return -1; } } } public int read(byte[] b) throws IOException { if (bitpos == 0) { int res = in.read(b); if (res >= 0) bitsread += ((long)res << 3L); return res; } else { int res = 0; int tmp = 0; while (res < b.length && (tmp = read()) >= 0) { b[res++] = (byte)tmp; } return res; } } public int read(byte[] b, int off, int len) throws IOException { if (bitpos == 0) { int res = in.read(b, off, len); if (res >= 0) bitsread += ((long)res << 3L); return res; } else { int res = 0; int tmp = 0; while (res < len && (tmp = read()) >= 0) { b[off++] = (byte)tmp; res++; } return res; } } public void reset() throws IOException { in.reset(); bitpos = markbitpos; bittmp = markbittmp; bitsread = markbitsread; } public long skip(long n) throws IOException { if (bitpos == 0) { long res = in.skip(n); if (res >= 0) bitsread += ((long)res << 3L); return res; } else { return skipBits(n << 3L) >> 3L; } } public void readFully(byte[] b) throws IOException { if (bitpos == 0) { in.readFully(b); bitsread += ((long)b.length << 3L); } else { int res = 0; while (res < b.length) { b[res++] = readByte(); } } } public void readFully(byte[] b, int off, int len) throws IOException { if (bitpos == 0) { in.readFully(b, off, len); bitsread += ((long)len << 3L); } else { int res = 0; while (res < len) { b[off++] = readByte(); res++; } } } public int skipBytes(int n) throws IOException { return (int)skip((long)n); } public boolean readBoolean() throws IOException { if (bitpos == 0) { boolean res = in.readBoolean(); bitsread += 8L; return res; } else { return (readByte() != 0); } } public byte readByte() throws IOException { if (bitpos == 0) { byte res = in.readByte(); bitsread += 8L; return res; } else { int tmp = in.readByte(); byte res = 0; int rpos = 128; while (bitpos > 0) { if ((bittmp & bitpos) != 0) res |= rpos; rpos >>= 1; bitpos >>= 1; bitsread++; } bitpos = 128; bittmp = tmp; while (rpos > 0) { if ((bittmp & bitpos) != 0) res |= rpos; rpos >>= 1; bitpos >>= 1; bitsread++; } return res; } } public int readUnsignedByte() throws IOException { if (bitpos == 0) { int res = in.readUnsignedByte(); bitsread += 8L; return res; } else { return (readByte() & 0xFF); } } public short readShort() throws IOException { if (bitpos == 0) { short res = in.readShort(); bitsread += 16L; return res; } else { int tmp = in.readShort(); short res = 0; int rpos = 32768; while (bitpos > 0) { if ((bittmp & bitpos) != 0) res |= rpos; rpos >>= 1; bitpos >>= 1; bitsread++; } bitpos = 32768; bittmp = tmp; while (rpos > 0) { if ((bittmp & bitpos) != 0) res |= rpos; rpos >>= 1; bitpos >>= 1; bitsread++; } return res; } } public short readShortLE() throws IOException { if (bitpos == 0) { short res = Short.reverseBytes(in.readShort()); bitsread += 16L; return res; } else { throw new IOException("Can't read little-endian values unless on a byte boundry with a byte-multiple width"); } } public int readUnsignedShort() throws IOException { if (bitpos == 0) { int res = in.readUnsignedShort(); bitsread += 16L; return res; } else { return (readShort() & 0xFFFF); } } public int readUnsignedShortLE() throws IOException { if (bitpos == 0) { int res = Integer.reverseBytes(in.readUnsignedShort()) >>> 16; bitsread += 16L; return res; } else { throw new IOException("Can't read little-endian values unless on a byte boundry with a byte-multiple width"); } } public char readChar() throws IOException { if (bitpos == 0) { char res = in.readChar(); bitsread += 16L; return res; } else { int tmp = in.readChar(); char res = 0; int rpos = 32768; while (bitpos > 0) { if ((bittmp & bitpos) != 0) res |= rpos; rpos >>= 1; bitpos >>= 1; bitsread++; } bitpos = 32768; bittmp = tmp; while (rpos > 0) { if ((bittmp & bitpos) != 0) res |= rpos; rpos >>= 1; bitpos >>= 1; bitsread++; } return res; } } public char readCharLE() throws IOException { if (bitpos == 0) { char res = Character.reverseBytes(in.readChar()); bitsread += 16L; return res; } else { throw new IOException("Can't read little-endian values unless on a byte boundry with a byte-multiple width"); } } public int readInt() throws IOException { if (bitpos == 0) { int res = in.readInt(); bitsread += 32L; return res; } else { int tmp = in.readInt(); int res = 0; int rpos = -2147483648; while (bitpos != 0) { if ((bittmp & bitpos) != 0) res |= rpos; rpos >>>= 1; bitpos >>>= 1; bitsread++; } bitpos = -2147483648; bittmp = tmp; while (rpos != 0) { if ((bittmp & bitpos) != 0) res |= rpos; rpos >>>= 1; bitpos >>>= 1; bitsread++; } return res; } } public int readIntLE() throws IOException { if (bitpos == 0) { int res = Integer.reverseBytes(in.readInt()); bitsread += 32L; return res; } else { throw new IOException("Can't read little-endian values unless on a byte boundry with a byte-multiple width"); } } public long readLong() throws IOException { if (bitpos == 0) { long res = in.readLong(); bitsread += 64L; return res; } else { long tmp = in.readLong(); long res = 0; long rpos = Long.MIN_VALUE; while (bitpos != 0) { if ((bittmp & bitpos) != 0) res |= rpos; rpos >>>= 1L; bitpos >>>= 1; bitsread++; } long bitpos = Long.MIN_VALUE; long bittmp = tmp; while (rpos != 0) { if ((bittmp & bitpos) != 0) res |= rpos; rpos >>>= 1; bitpos >>>= 1; bitsread++; } this.bitpos = (int)bitpos; this.bittmp = (int)bittmp; return res; } } public long readLongLE() throws IOException { if (bitpos == 0) { long res = Long.reverseBytes(in.readLong()); bitsread += 64L; return res; } else { throw new IOException("Can't read little-endian values unless on a byte boundry with a byte-multiple width"); } } public float readFloat() throws IOException { if (bitpos == 0) { float res = in.readFloat(); bitsread += 32L; return res; } else { return Float.intBitsToFloat(readInt()); } } public float readFloatLE() throws IOException { if (bitpos == 0) { float res = Float.intBitsToFloat(Integer.reverseBytes(in.readInt())); bitsread += 32L; return res; } else { throw new IOException("Can't read little-endian values unless on a byte boundry with a byte-multiple width"); } } public double readDouble() throws IOException { if (bitpos == 0) { double res = in.readDouble(); bitsread += 64L; return res; } else { return Double.longBitsToDouble(readLong()); } } public double readDoubleLE() throws IOException { if (bitpos == 0) { double res = Double.longBitsToDouble(Long.reverseBytes(in.readLong())); bitsread += 64L; return res; } else { throw new IOException("Can't read little-endian values unless on a byte boundry with a byte-multiple width"); } } public String readLine() throws IOException { throw new UnsupportedOperationException(); } public String readUTF() throws IOException { throw new UnsupportedOperationException(); } }