/* * Copyright 2010 Jin Kwon. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jinahya.io; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.util.BitSet; /** * * @author <a href="mailto:jinahya@gmail.com">Jin Kwon</a> */ public class BitInput { /** * Creates a new instance. * * @param in source octet input */ public BitInput(final InputStream in) { super(); if (in == null) { throw new IllegalArgumentException("null in"); } this.in = in; } /** * Reads an unsigned byte value. * * @param length bit length between 0 (exclusive) and 8 (inclusive) * @return an unsigned byte value. * @throws IOException if an I/O error occurs. */ private int readUnsignedByte(final int length) throws IOException { if (length <= 0) { throw new IllegalArgumentException( "illegal length: " + length + " <= 0"); } if (length > 8) { throw new IllegalArgumentException( "illegal length: " + length + " > 8"); } if (index == 8) { int octet = in.read(); if (octet == -1) { throw new EOFException("eof"); } for (int i = 7; i >= 0; i--) { set.set(i, (octet & 0x01) == 0x01); octet >>= 1; } index = 0; count++; } final int required = length - (8 - index); if (required > 0) { return (readUnsignedByte(length - required) << required) | readUnsignedByte(required); } int value = 0x00; for (int i = 0; i < length; i++) { value <<= 1; value |= (set.get(index + i) ? 0x01 : 0x00); } index += length; return value; } /** * Reads 1-bit long boolean value. * * @return boolean value * @throws IOException if an I/O error occurs. */ public final boolean readBoolean() throws IOException { return readUnsignedByte(1) == 0x01; } /** * Reads an unsigned short value. * * @param length bit length between 0 (exclusive) and 16 (inclusive). * @return an unsigne short value. * @throws IOException if an I/O error occurs. */ private int readUnsignedShort(final int length) throws IOException { if (length <= 0) { throw new IllegalArgumentException( "illegal length(" + length + ") <= 0"); } if (length > 16) { throw new IllegalArgumentException( "illegal length(" + length + ") > 16"); } final int quotient = length / 0x08; final int remainder = length % 0x08; int value = 0x00; for (int i = 0; i < quotient; i++) { value <<= 0x08; value |= readUnsignedByte(0x08); } if (remainder > 0) { value <<= remainder; value |= readUnsignedByte(remainder); } return value; } /** * Reads an unsigned int. * * @param length bit length between 1 (inclusive) and 32 (exclusive). * @return * @throws IOException */ public int readUnsignedInt(final int length) throws IOException { if (length < 1) { throw new IllegalArgumentException( "illegal length(" + length + ") < 1"); } if (length >= 32) { throw new IllegalArgumentException( "illegal length(" + length + ") >= 32"); } final int quotient = length / 0x10; final int remainder = length % 0x10; int value = 0x00; for (int i = 0; i < quotient; i++) { value <<= 0x10; value |= readUnsignedShort(0x10); } if (remainder > 0) { value <<= remainder; value |= readUnsignedShort(remainder); } return value; } /** * Reads an signed int. * * @param length bit length between 1 (exclusive) and 32 (inclusive). * @return a <code>length</code>bit-long signed integer * @throws IOException if an I/O error occurs. */ public int readInt(final int length) throws IOException { if (length <= 1) { throw new IllegalArgumentException( "illegal length(" + length + ") <= 1"); } if (length > 32) { throw new IllegalArgumentException( "illegal length(" + length + ") > 32"); } int value = readBoolean() ? (-1 << (length - 1)) : 0; value |= readUnsignedInt(length - 1); return value; } /** * * @param length bit length between 1 (inclusive) and 64 (exclusive) * @return <code>length</code>-bit unsigned long value * @throws IOException if an I/O error occurs */ public final long readUnsignedLong(final int length) throws IOException { if (length < 1) { throw new IllegalArgumentException( "illegal length(" + length + ") < 1"); } if (length >= 64) { throw new IllegalArgumentException( "illegal length(" + length + ") >= 64"); } final int quotient = length / 0x10; final int remainder = length % 0x10; long value = 0x00L; for (int i = 0x00; i < quotient; i++) { value <<= 0x10; value |= readUnsignedShort(0x10); } if (remainder > 0x00) { value <<= remainder; value |= readUnsignedShort(remainder); } return value; } /** * Reads a signed long value. * * @param length bit length between 1 (exclusive) and 64 (inclusive). * @return a signed long value. * @throws IOException if an I/O error occurs. */ public final long readLong(final int length) throws IOException { if (length <= 1) { throw new IllegalArgumentException( "illegal length(" + length + ") <= 1"); } if (length > 64) { throw new IllegalArgumentException( "illegal length(" + length + ") > 64"); } long value = readBoolean() ? (-1L << (length - 1)) : 0L; value |= readUnsignedLong(length - 1); return value; } /** * Align to given <code>length</code> bytes. * * @param length number of octets to align * @throws IOException if an I/O error occurs. */ public void aling(final int length) throws IOException { if (length <= 0) { throw new IllegalArgumentException( "illegal length(" + length + ") <= 0"); } int octets = count % length; if (octets > 0) { octets = length - octets; } else if (octets == 0) { octets = length; } else { // mod < 0 octets = 0 - octets; } if (index < 8) { readUnsignedByte(8 - index); octets--; } if (octets == length) { return; } for (int i = 0; i < octets; i++) { readUnsignedByte(8); } } /** * Reads a sequence of bytes. * * @param bytes byte array * @throws IOException if an I/O error occurs. */ public void readBytes(final byte[] bytes) throws IOException { readBytes(bytes, 0, bytes.length); } /** * Reads a sequence of bytes. * * @param bytes byte array * @param offset offset in bytes * @param length byte count to read * @throws IOException if an I/O error occurs. */ public void readBytes(final byte[] bytes, final int offset, final int length) throws IOException { if (bytes == null) { throw new IllegalArgumentException("null bytes"); } if (offset < 0) { throw new IllegalArgumentException("negative offset"); } if (offset >= bytes.length) { throw new IllegalArgumentException( "offset(" + offset + ") >= bytes.length(" + bytes.length + ")"); } if (length < 0) { throw new IllegalArgumentException("length(" + length + ") < 0"); } if ((offset + length) > bytes.length) { throw new IllegalArgumentException( "offset(" + offset + ") + length(" + length + ") > bytes.length(" + bytes.length + ")"); } for (int i = 0; i < length; i++) { bytes[offset + i] = (byte) readUnsignedByte(0x08); } } /** bit set. */ private final BitSet set = new BitSet(8); /** input source. */ private final InputStream in; /** bit index to read. */ private int index = 8; /** octet count. */ private int count = 0; }