All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.aliasi.io.BitInput Maven / Gradle / Ivy

Go to download

This is the original Lingpipe: http://alias-i.com/lingpipe/web/download.html There were not made any changes to the source code.

There is a newer version: 4.1.2-JL1.0
Show newest version
package com.aliasi.io;

import com.aliasi.util.Math;

import java.io.InputStream;
import java.io.IOException;

/** 
 * A BitInput wraps an underlying input stream to provide
 * bit-level input.  Input is read through the method {@link
 * #readBit()}, which returns a boolean value.  Bits are coded
 * as booleans, with true=1 and false=0.
 *
 * @author Bob Carpenter
 * @version 2.1.1
 * @since 2.1.1
 */
public class BitInput {

    private final InputStream mIn;
    
    private int mNextByte; // implied = 0;
    
    private int mNextBitIndex;

    private boolean mEndOfStream = false;

    /** 
     * Constructs a bit input wrapping the specified input stream.
     * The input stream is accessed by the constructor to fill
     * a read-ahead buffer.  Thus the constructor will throw an
     * exception if there is a read exception

     * @param in Input stream backing this bit input.
     * @throws IOException If there is an exception reading from the
     * specified input stream.
     */
    public BitInput(InputStream in) throws IOException {
        mIn = in;
        readAhead();
    }

    /** 
     * Returns number of bits available for reading without blocking.
     * This is the sum of the number of bits buffered and the result
     * of querying the number of bytes available from the underlying
     * input stream using {@link InputStream#available()}.
     * 
     * @return Number of bits available for reading.
     * @throws IOException If there is an exception while checking
     * availability in the underlying input stream.
     */
    public long available() throws IOException {
        return mEndOfStream
            ? 0l
            : ((mNextBitIndex + 1l) + (8l * (long) mIn.available()));
    }

    /** 
     * Closes this input stream and releases associated resources.
     * This method will close the underlying input stream for this bit
     * input by calling {@link InputStream#close()}.  After calls to
     * this method, {@link #available()} will return zero and {@link
     * #endOfStream()} will return true.
     * 
     * @throws IOException If there is an exception closing the
     * underlying input stream.
     */
    public void close() throws IOException { 
        mEndOfStream = true;
        mIn.close(); 
    }

    /** 
     * Returns true if all of the available bits have
     * been read or if this stream has been closed.
     *
     * @return true if all of the available bits have
     * been read.
     */
    public boolean endOfStream() { 
        return mEndOfStream; 
    }


    /**
     * Skips and discards the specified number of bits in the input.
     * The return value is the number of bits skipped, which may be
     * less than the number requested if the end-of-stream is reached
     * or if the underlying input stream cannot skip the required
     * number of bytes.
     *
     * @param numBits Number of bits to skip.
     * @return Number of bits actually skipped.
     * @throws IOException If there is an I/O error skipping on the
     * underlying input stream.
     * @throws IllegalArgumentException If the number of bits argument
     * is less than zero.
     */
    public long skip(long numBits) throws IOException {
        if (numBits < 0) {
            String msg = "Require positive number of bits to skip."
                + " Found numBits=" + numBits;
            throw new IllegalArgumentException(msg);
        }
        if (mNextBitIndex >= numBits) {  
            mNextBitIndex -= numBits; // decrement within byte buffer
            return numBits;
        }

        long numBitsSkipped = mNextBitIndex + 1;
        long numBitsLeft = numBits - numBitsSkipped;

        long bytesToSkip = numBitsLeft / 8;
        long bytesSkipped = mIn.skip(bytesToSkip);

        numBitsSkipped += 8 * bytesSkipped;
        if (bytesSkipped < bytesToSkip) {
            mEndOfStream = true;  // exhausted input stream
            return numBitsSkipped;
        }

        readAhead();
        if (mEndOfStream) 
            return numBitsSkipped; // nothing left
        mNextBitIndex = 7 - (((int)numBitsLeft) % 8);
        return numBits;
    }

    /** 
     * Reads the next bit from the input stream.  Bits are encoded
     * as binary values, using true for 1
     * and false for 0.
     *
     * 

The return value is undefined if the end of stream has been * reached, but this method will not throw an exception. * * @return The boolean value of the next bit. * @throws IOException If there is an exception reading from the * underlying stream. */ public boolean readBit() throws IOException { switch (mNextBitIndex--) { case 0: boolean result = ((mNextByte & 1) != 0); readAhead(); return result; case 1: return ((mNextByte & 2) != 0); case 2: return ((mNextByte & 4) != 0); case 3: return ((mNextByte & 8) != 0); case 4: return ((mNextByte & 16) != 0); case 5: return ((mNextByte & 32) != 0); case 6: return ((mNextByte & 64) != 0); case 7: return ((mNextByte & 128) != 0); default: String msg = "Index out of bounds. mNextBitIndex=" + mNextBitIndex; throw new IOException(msg); } } /** * Reads the next value from the input using a unary code. If all * of the remaining bits are zeros, it throws an I/O exception. * If the end of stream has been reached, it returns -1. * *

See {@link BitOutput#writeUnary(int)} for a description of * unary coding. * * @return The next integer read with unary code. * @throws IOException If the remaining bits are all zeros. */ public int readUnary() throws IOException { int result = 1; // look through remaining buffered bits for ( ; !endOfStream() && mNextBitIndex != 7; ++result) if (readBit()) return result; // jump over whole 0 bytes while (!endOfStream() && mNextByte == ZERO_BYTE) { result += 8; mNextByte = mIn.read(); if (mNextByte == -1) { String msg = "Final sequence of 0 bits with no 1"; throw new IOException(msg); } } // read to last one bit (could do the search trick here!) while (!readBit()) // know we'll find it given last test ++result; return result; } /** * Skips over and discards the next value from the input using a * unary code. * * @throws IOException If there is a format error in the input stream * or an error reading from the underlying stream. */ public void skipUnary() throws IOException { // cut and paste from readUnary without result increments // look through remaining buffered bits while (!endOfStream() && mNextBitIndex != 7) if (readBit()) return; // jump over whole 0 bytes while (!endOfStream() && mNextByte == ZERO_BYTE) { mNextByte = mIn.read(); if (mNextByte == -1) { String msg = "Final sequence of 0 bits with no 1"; throw new IOException(msg); } } // read to last one bit (could do the search trick here!) while (!readBit()) ; // know we'll find it given last test // } /** * Reads the next value from the input using an Elias gamma code. * If the code is incomplete or not well-formed, an I/O exception * is raised. If the end of stream has been reached, the value * returned is -1. * *

See {@link BitOutput#writeGamma(long)} for a description of * gamma coding. * * @return The next integer read using gamma coding. * @throws IOException If the prefix of the remaining bits is not * a valid gamma code or if there is an error reading from the * underlying input stream. */ public long readGamma() throws IOException { int numBits = readUnary(); if (numBits > 63) { String msg = "Gamma code binary part must be <= 63 bits." + " Found numBits=" + numBits; throw new IOException(msg); } return readRest(numBits-1,1l); } /** * Skips over and discards the next value from the input using an * Elias gamma code. * * @throws IOException If there is a format error in the input stream * or an error reading from the underlying stream. */ public void skipGamma() throws IOException { int numBits = readUnary(); checkGamma(numBits); skip(numBits-1); } /** * Reads the next value from the input using an Elias delta code. * If the code is incomplete or not well-formed, an I/O exception * is raised. If the end of stream has been reached, the value * returned is -1. * *

See {@link BitOutput#writeDelta(long)} for a description of * gamma coding. * * @return The next integer read using delta coding. * @throws IOException If the prefix of the remaining bits is not * a valid delta code or there is an error reading from the * underlying input stream. */ public long readDelta() throws IOException { long numBits = readGamma(); checkDelta(numBits); if (numBits > 63l) { String msg = "Delta code must use <= 63 bits for fixed portion." + " Found number of remaining bits=" + numBits; throw new IOException(msg); } return readRest((int)numBits-1,1l); } /** * Skips over and discards the next value from the input using an * Elias delta code. * * @throws IOException If there is a format error in the input * stream or an error reading from the underlying stream. */ public void skipDelta() throws IOException { long numBits = readGamma(); checkDelta(numBits); skip(numBits-1); } /** * Reads the next value from the input using a fixed-length binary * code of the specified number of bits. This method throws an * exception if there are not enough bits remaining in the input. * The number of bits must be greater than zero and not more than * 63, so that the result will fit into a long integer (note that * the negative numbers are not used). * *

To skip over a binary encoding of a specified number of * bits, just call {@link #skip(long)} on the number of bits. * *

See {@link BitOutput#writeBinary(long,int)} for a definition * of binary encoding and examples. * * @param numBits Number of bits in encoding. * @return The integer read in binary using the specified number * of bits. * @throws IOException If there are not enough bits remaining in * the input or there is an error reading from the underlying * input stream. * @throws IllegalArgumentException If the number of bits is less * than 1 or greater than 63. */ public long readBinary(int numBits) throws IOException { if (numBits > 63) { String msg = "Cannot read more than 63 bits into positive long." + " Found numBits=" + numBits; throw new IllegalArgumentException(msg); } if (numBits < 1) { String msg = "Number of bits to read must be > 0." + " Found numBits=" + numBits; throw new IllegalArgumentException(msg); } long result = readBit() ? 1l : 0l; return readRest(numBits-1,result); } /** * Reads the next number from the input using a Rice coding with * the specified number of fixed bits for the remainder. This * method will throw an exception if the number of fixed bits is * greater than 63 or less than 1. * * @param numFixedBits The number of fixed bits in the Rice code. * @return The next number read using a Rice code with the * specified number of fixed bits. * @throws IOException If the coding is illegal. * @throws IllegalArgumentException If the number of fixed bits is * less than 1 or greater than 63. */ public long readRice(int numFixedBits) throws IOException { if (numFixedBits < 1) { String msg = "Rice coding requires a number of fixed bits > 0." + " Found numFixedBits=" + numFixedBits; throw new IllegalArgumentException(msg); } if (numFixedBits > 63) { String msg = "Rice coding requires a number of fixed bits < 64." + "Found numFixedBits=" + numFixedBits; throw new IllegalArgumentException(msg); } long prefixBits = readUnary(); long remainder = readBinary(numFixedBits); long q = prefixBits - 1l; long div = (q << numFixedBits); return div + (remainder + 1l); } /** * Skips over and discards the next value from the input using a * Rice code with the specified number of fixed bits. * * @throws IOException If there is a format error in the input * stream or an error reading from the underlying stream. */ public void skipRice(int numFixedBits) throws IOException { skipUnary(); skip(numFixedBits); } /** * Reads the next value from the input using a Fibonacci code. If * the code is not legal, this method throws an I/O exception. * *

See {@link BitOutput#writeFibonacci(long)} for more information * on Fibonacci codes. * * @return The next number read from the input using a Fibonacci * code. * @throws IOException If the remaining bits do not contain * a pair of consecutive 1 bits or there is an exception reading from * the underlying input stream. */ public long readFibonacci() throws IOException { long[] fibs = Math.FIBONACCI_SEQUENCE; long sum = 0l; for (int i = 0; i < fibs.length && !endOfStream(); ++i) { if (readBit()) { sum += fibs[i++]; if (!endOfStream() && readBit()) return sum; } } String msg = "Ran off end of input or beyond maximum length " + " without finding two consecutive 1s"; throw new IOException(msg); } /** * Skips over and discards the next value from the input using a * Fibonacci code. * * @throws IOException If there is a format error in the input * stream or an error reading from the underlying stream. */ public void skipFibonacci() throws IOException { while (!endOfStream()) if (readBit() && !endOfStream() && readBit()) return; String msg = "Ran off end of input without finding two consecutive 1s"; throw new IOException(msg); } long readRest(int numBits, long result) throws IOException { /* simple working version: while (--numBits >= 0) { notEndOfStream(); result = result << 1; if (readBit()) result |= 1l; } return result; */ if (numBits == 0) return result; notEndOfStream(); // can read from currently buffered byte if (mNextBitIndex >= numBits) { mNextBitIndex -= numBits; return (result << numBits) | sliceBits2(mNextByte,mNextBitIndex+1,numBits+1); } // numBits > mNextBitIndex // read rest of current byte numBits -= (mNextBitIndex+1); result = (result << (mNextBitIndex+1)) | sliceBits2(mNextByte,0,mNextBitIndex+1); for ( ; numBits >= 8; numBits -= 8) { int nextByte = mIn.read(); if (nextByte == -1) { mEndOfStream = true; String msg = "Premature end of stream reading binary - mid."; throw new IOException(msg); } result = (result << 8) | nextByte; } readAhead(); if (numBits == 0) return result; notEndOfStream(); mNextBitIndex = 7 - numBits; return (result << numBits) | sliceBits2(mNextByte,mNextBitIndex+1,numBits); } private void readAhead() throws IOException { if (mEndOfStream) return; mNextByte = mIn.read(); if (mNextByte == -1) { mEndOfStream = true; return; } mNextBitIndex = 7; } private void notEndOfStream() throws IOException { if (endOfStream()) { String msg = "End of stream reached prematurely."; throw new IOException(msg); } } static final byte ZERO_BYTE = (byte) 0; static int ALL_ONES_INT = ~0; static long leastSignificantBits2(int n, int numBits) { return (ALL_ONES_INT >>> (32-numBits)) & n; } static long sliceBits2(int n, int leastSignificantBit, int numBits) { return leastSignificantBits2(n >>> leastSignificantBit, numBits); } static void checkGamma(int numBits) throws IOException { if (numBits <= 63) return; String msg = "Gamma code binary part must be <= 63 bits." + " Found numBits=" + numBits; throw new IOException(msg); } static void checkDelta(long numBits) throws IOException { if (numBits <= 63l) return; String msg = "Delta code binary part must be <= 63 bits." + " Number of bits specified=" + numBits; throw new IOException(msg); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy