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

it.unimi.dsi.io.InputBitStream Maven / Gradle / Ivy

Go to download

Blazegraph Modifications to the DSI utils. This are forked from version 1.10.0 under LGPLv2.1.

There is a newer version: 2.1.4
Show newest version
package it.unimi.dsi.io;

/*		 
 * DSI utilities
 *
 * Copyright (C) 2002-2009 Sebastiano Vigna 
 *
 *  This library is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU Lesser General Public License as published by the Free
 *  Software Foundation; either version 2.1 of the License, or (at your option)
 *  any later version.
 *
 *  This library 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 Lesser General Public License
 *  for more details.
 *
 *  You should have received a copy of the GNU Lesser 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.
 *
 */

import it.unimi.dsi.bits.Fast;
import it.unimi.dsi.fastutil.booleans.AbstractBooleanIterator;
import it.unimi.dsi.fastutil.booleans.BooleanIterator;
import it.unimi.dsi.fastutil.ints.IntIterators;
import it.unimi.dsi.fastutil.io.BinIO;
import it.unimi.dsi.fastutil.io.FastBufferedInputStream;
import it.unimi.dsi.fastutil.io.RepositionableStream;

import java.io.Closeable;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.Flushable;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.nio.channels.FileChannel;


/** Bit-level input stream.
 *
 * 

This class wraps any {@link InputStream} so that you can treat it as * bit stream. Constructors and methods closely resemble those of * {@link InputStream}. Data can be read from such a stream in several ways: * reading an integer or long in fixed-width, unary, γ, shifted γ, δ, ζ and (skewed) * Golomb coding, or reading a number of bits that will be stored in a vector of * bytes. There is limited support for {@link #mark(int)}/{@link #reset()} * operations. * *

This class can also {@linkplain #InputBitStream(byte[]) wrap a byte * array}; this is much more lightweight than wrapping a {@link * it.unimi.dsi.fastutil.io.FastByteArrayInputStream} wrapping the array. Overflowing the array * will cause an {@link java.io.EOFException}. * *

Note that when reading using a vector of bytes bits are read in the * stream format (see {@link OutputBitStream}): the first bit is bit 7 of the * first byte, the eighth bit is bit 0 of the first byte, the ninth bit is bit * 7 of the second byte and so on. When reading integers using some coding, * instead, they are stored in the standard way, that is, in the lower * bits. * *

Additional features: * *

    * *
  • This class provides an internal buffer. By setting a buffer of * length 0 at creation time, you can actually bypass the buffering system: * Note, however, that several classes providing buffering have synchronised * methods, so using a wrapper instead of the internal buffer is likely to lead * to a performance drop. * *
  • To work around the schizophrenic relationship between streams and random * access files in {@link java.io}, this class provides a {@link #flush()} * method that resets the internal state. At this point, you can safely reposition * the underlying stream and read again afterwards. For instance, this is safe * and will perform as expected: *
     * FileInputStream fis = new FileInputStream( ... );
     * InputBitStream ibs = new InputBitStream( fis );
     * ... read operations on ibs ...
     * ibs.flush();
     * fis.getChannel().position( ... );
     * ... other read operations on ibs ...
     * 
    * *

    As a commodity, an instance of this class will try to cast the underlying byte * stream to a {@link RepositionableStream} and to fetch by reflection the {@link * java.nio.channels.FileChannel} underlying the given input stream, in this * order. If either reference can be successfully fetched, you can use * directly the {@link #position(long) position()} method with argument * pos with the same semantics of a {@link #flush()}, followed by * a call to position(pos / 8) (where the latter method belongs * either to the underlying stream or to its underlying file channel), followed * by a {@link #skip(long) skip(pos % 8)}. However, since the reflective checks are quite * heavy they can be disabled using a {@linkplain InputBitStream#InputBitStream(InputStream, boolean) suitable constructor}. * *

  • Finally, this class implements partially the interface of a boolean iterator. * More precisely, {@link #nextBoolean()} will return the same bit as {@link #readBit()}, * and also the same exceptions, whereas {@link #hasNext()} will always return true: * you must be prepared to catch a {@link java.lang.RuntimeException} wrapping an {@link IOException} * in case the file ends. It * is very difficult to implement completely an eager operator using a input-stream * based model. * *
* *

This class is not synchronised. If multiple threads * access an instance of this class concurrently, they must be synchronised externally. * * @see java.io.InputStream * @see it.unimi.dsi.io.OutputBitStream * @author Sebastiano Vigna * @since 0.1 */ public class InputBitStream extends AbstractBooleanIterator implements Flushable, Closeable { private final static boolean DEBUG = false; private final static boolean ASSERTS = false; /* Precomputed tables: the i-th entry decodes the stream fragment of 16 bits given by the binary reprentation of i. * The upper 16 bits contain code lengths, the lower 16 bits decoded values. 0 means undecodable. */ public final static int[] GAMMA = new int[ 256 * 256 ], DELTA = new int[ 256 * 256 ], ZETA_3 = new int[ 256 * 256 ], SHIFTED_GAMMA = new int[ 256 * 256 ]; static void fillArrayFromResource( final String resource, final int array[] ) throws IOException { final String resouceFullPath = "/it/unimi/dsi/io/" + resource; final InputStream ris = InputBitStream.class.getResourceAsStream( resouceFullPath ); if ( ris == null ) throw new IOException( "Cannot open resource " + resouceFullPath ); DataInputStream dis = new DataInputStream( new FastBufferedInputStream( ris ) ); BinIO.loadInts( dis, array, 0, array.length ); dis.close(); if ( ASSERTS ) { final int actualLength = IntIterators.unwrap( BinIO.asIntIterator( new DataInputStream( InputBitStream.class.getResourceAsStream( resouceFullPath ) ) ) ).length; assert array.length == actualLength : resource + " is long " + actualLength + " but we think it should rather be " + array.length; } } static { /* We load all precomputed arrays from resource files, * to work around the limit on static initialiser code. */ try { fillArrayFromResource( "gamma.in.16", GAMMA ); fillArrayFromResource( "delta.in.16", DELTA ); fillArrayFromResource( "zeta3.in.16", ZETA_3 ); fillArrayFromResource( "shiftedgamma.in.16", SHIFTED_GAMMA ); } catch ( IOException e ) { throw new RuntimeException( e ); } } /** The default size of the byte buffer in bytes (8Ki). */ public final static int DEFAULT_BUFFER_SIZE = 8 * 1024; /** The underlying {@link InputStream}. */ protected final InputStream is; /** Whether we should use the byte buffer. */ private final boolean noBuffer; /** The cached file channel underlying {@link #is}, if any. */ protected final FileChannel fileChannel; /** {@link #is} cast to a positionable stream, if possible. */ protected final RepositionableStream repositionableStream; /** True if we are wrapping an array. */ protected final boolean wrapping; /** The number of bits actually read from this bit stream. */ private long readBits; /** Current bit buffer: the lowest {@link #fill} bits represent the current content (the remaining bits are undefined). */ private int current; /** The stream buffer. */ protected byte[] buffer; /** Current number of bits in the bit buffer (stored low). */ protected int fill; /** Current position in the byte buffer. */ protected int pos; /** Offset into byte[] slice and otherwise 0. BBT 8/30/2009. */ private int off = 0; /** Current number of bytes available in the byte buffer. */ protected int avail; /** Current position of the first byte in the byte buffer. */ protected long position; /** This (non-public) constructor exists just to provide fake initialisation for classes such as {@link DebugInputBitStream}. */ protected InputBitStream() { is = null; noBuffer = true; repositionableStream = null; fileChannel = null; wrapping = false; } /** Creates a new input bit stream wrapping a given input stream using a buffer of size {@link #DEFAULT_BUFFER_SIZE}. * *

This constructor performs the reflective tests that are necessary to support {@link #position(long)}. * * @param is the input stream to wrap. */ public InputBitStream( final InputStream is ) { this( is, true ); } /** Creates a new input bit stream wrapping a given input stream using a buffer of size {@link #DEFAULT_BUFFER_SIZE}. * * @param is the input stream to wrap. * @param testForPosition if false, the reflective tests that are necessary to support {@link #position(long)} will not be performed. */ public InputBitStream( final InputStream is, final boolean testForPosition ) { this( is, DEFAULT_BUFFER_SIZE, testForPosition ); } /** Creates a new input bit stream wrapping a given input stream with a specified buffer size. * *

This constructor performs the reflective tests that are necessary to support {@link #position(long)}. * * @param is the input stream to wrap. * @param bufSize the size in byte of the buffer; it may be 0, denoting no buffering. */ public InputBitStream( final InputStream is, final int bufSize ) { this( is, bufSize, true ); } /** Creates a new input bit stream wrapping a given input stream with a specified buffer size. * * @param is the input stream to wrap. * @param bufSize the size in byte of the buffer; it may be 0, denoting no buffering. * @param testForPosition if false, the reflective tests that are necessary to support {@link #position(long)} will not be performed. */ public InputBitStream( final InputStream is, final int bufSize, final boolean testForPosition ) { this.is = is; wrapping = false; if ( ! ( this.noBuffer = bufSize == 0 ) ) this.buffer = new byte[ bufSize ]; // Cheap test, we do it all the time if ( is instanceof RepositionableStream ) { repositionableStream = (RepositionableStream)is; fileChannel = null; } else if ( testForPosition ) { FileChannel fc = null; try { fc = (FileChannel)( is.getClass().getMethod( "getChannel" ) ).invoke( is ); } catch( IllegalAccessException e ) {} catch( IllegalArgumentException e ) {} catch( NoSuchMethodException e ) {} catch( InvocationTargetException e ) {} catch( ClassCastException e ) {} fileChannel = fc; repositionableStream = null; } else { repositionableStream = null; fileChannel = null; } } /** Creates a new input bit stream wrapping a given file input stream using a buffer of size {@link #DEFAULT_BUFFER_SIZE}. * *

This constructor invokes directly {@link FileInputStream#getChannel()} to support {@link #position(long)}. * * @param is the file input stream to wrap. */ public InputBitStream( final FileInputStream is ) { this( is, DEFAULT_BUFFER_SIZE ); } /** Creates a new input bit stream wrapping a given file input stream with a specified buffer size. * *

This constructor invokes directly {@link FileInputStream#getChannel()} to support {@link #position(long)}. * * @param is the file input stream to wrap. * @param bufSize the size in byte of the buffer; it may be 0, denoting no buffering. */ public InputBitStream( final FileInputStream is, final int bufSize ) { this.is = is; wrapping = false; if ( ! ( this.noBuffer = bufSize == 0 ) ) this.buffer = new byte[ bufSize ]; repositionableStream = null; fileChannel = is.getChannel(); } /** Creates a new input bit stream wrapping a given byte array. * * @param a the byte array to wrap. */ public InputBitStream( final byte[] a ) { this(a,0,a.length); } /** * Creates a new input bit stream wrapping a slice of a given byte array * (BBT 8/30/2009). * * @param a * the byte array to wrap. * @param off * the byte offset of the first addressable byte in the array. * @param len * the #of addressable bytes in the array. */ public InputBitStream(final byte[] a, final int off, final int len) { if (a == null) throw new IllegalArgumentException(); if (off < 0) throw new IllegalArgumentException(); if (off + len > a.length) throw new IllegalArgumentException(); is = NullInputStream.getInstance(); repositionableStream = null; fileChannel = null; if ( a.length > 0 ) { buffer = a; avail = len; // was a.length; modified BBT 8/30/2009 this.off = pos = off; // added BBT 8/30/2009 wrapping = true; noBuffer = false; } else { // A zero-length buffer is like having no buffer buffer = null; avail = 0; wrapping = false; noBuffer = true; } } /** Creates a new input bit stream reading from a file. * *

This constructor invokes directly {@link FileInputStream#getChannel()} to support {@link #position(long)}. * * @param name the name of the file. * @param bufSize the size in byte of the buffer; it may be 0, denoting no buffering. */ public InputBitStream( final String name, final int bufSize ) throws FileNotFoundException { this( new FileInputStream( name ), bufSize ); } /** Creates a new input bit stream reading from a file. * *

This constructor invokes directly {@link FileInputStream#getChannel()} to support {@link #position(long)}. * * @param name the name of the file. */ public InputBitStream( final String name ) throws FileNotFoundException { this( new FileInputStream( name ), DEFAULT_BUFFER_SIZE ); } /** Creates a new input bit stream reading from a file. * *

This constructor invokes directly {@link FileInputStream#getChannel()} to support {@link #position(long)}. * * @param file the file. */ public InputBitStream( final File file ) throws FileNotFoundException { this( new FileInputStream( file ), DEFAULT_BUFFER_SIZE ); } /** Creates a new input bit stream reading from a file. * *

This constructor invokes directly {@link FileInputStream#getChannel()} to support {@link #position(long)}. * * @param file the file. * @param bufSize the size in byte of the buffer; it may be 0, denoting no buffering. */ public InputBitStream( final File file, final int bufSize ) throws FileNotFoundException { this( new FileInputStream( file ), bufSize ); } /** Flushes the bit stream. All state information associated to the stream is reset. This * includes bytes prefetched from the stream, bits in the bit buffer and unget'd bits. * *

This method is provided so that users of this class can easily wrap repositionable * streams (for instance, file-based streams, which can be repositioned using * the underlying {@link java.nio.channels.FileChannel}). It is guaranteed that after calling * this method the underlying stream can be repositioned, and that the next read * will draw data from the stream. */ public void flush() { if ( ! wrapping ) { position += pos; avail = 0; pos = 0; } fill = 0; } /** Closes the bit stream. All resources associated to the stream are released. */ public void close() throws IOException { if ( is != System.in ) is.close(); buffer = null; } /** Returns the number of bits that can be read (or skipped over) from this * bit stream without blocking by the next caller of a method. * * @return the number of bits that can be read from this bit stream without blocking. */ public long available() throws IOException { return ( is.available() + avail ) * 8 + fill; } /** Returns the number of bits read from this bit stream. * * @return the number of bits read so far. */ public long readBits() { return readBits; } /** Sets the number of bits read from this bit stream. * *

This method is provided so that, for instance, the * user can reset via readBits(0) the read-bits count * after a {@link #flush()}. * * @param readBits the new value for the number of bits read so far. */ public void readBits( final long readBits ) { this.readBits = readBits; } /** Reads the next byte from the stream. * *

This method takes care of managing the buffering logic * transparently. * *

However, this method does not update {@link #readBits}. * The caller should increment {@link #readBits} by 8 at each call, unless * the bit are used to load {@link #current}. */ private final int read() throws IOException { if ( noBuffer ) { final int t = is.read(); if ( t == -1 ) throw new EOFException(); else position++; return t; } if ( avail == 0 ) { avail = is.read( buffer ); if ( avail == -1 ) { avail = 0; throw new EOFException(); } else { position += pos; pos = 0; } } avail--; return buffer[ pos++ ] & 0xFF; } /** Feeds 16 more bits into {@link #current}, assuming that {@link #fill} is less than 16. * *

This method will never throw an {@link EOFException}—simply, it will refill less than 16 bits. * * @return {@link #fill}. */ private final int refill() throws IOException { if ( ASSERTS ) assert fill < 16; if ( avail > 1 ) { // If there is a byte in the buffer, we use it directly. avail -= 2; current = current << 16 | ( buffer[ pos++ ] & 0xFF ) << 8 | buffer[ pos++ ] & 0xFF; return fill += 16; } try{ current = ( current << 8 ) | read(); fill += 8; current = ( current << 8 ) | read(); fill += 8; } catch( EOFException dontCare ) {} return fill; } /** Reads bits from the bit buffer, possibly refilling it. * *

This method is the basic mean for extracting bits from the underlying stream. * *

You cannot read more than {@link #fill} bits with this method (unless {@link #fill} is 0, * and len is nonzero, in which case the buffer will be refilled for you with 8 bits), and if you * read exactly {@link #fill} bits the buffer will be empty afterwards. In particular, * there will never be 8 bits in the buffer. * *

The bit buffer stores its content in the lower {@link #fill} bits. The content * of the remaining bits is undefined. * *

This method updates {@link #readBits}. * * @param len the number of bits to read. * @return the bits read (in the lower positions). * @throws AssertionError if one tries to read more bits than available in the buffer and assertions are enabled. */ private final int readFromCurrent( final int len ) throws IOException { if ( len == 0 ) return 0; if ( fill == 0 ) { current = read(); fill = 8; } if ( ASSERTS ) assert len <= fill : len + " bit(s) requested, " + fill + " available"; readBits += len; return current >>> ( fill -= len ) & ( 1 << len ) - 1; } /** Aligns the stream. * * After a call to this function, the stream is byte aligned. Bits that have been * read to align are discarded. */ public void align() { if ( ( fill & 7 ) == 0 ) return; readBits += fill & 7; fill &= ~7; } /** Reads a sequence of bits. * * Bits will be read in the natural way: the first bit is bit 7 of the * first byte, the eightth bit is bit 0 of the first byte, the ninth bit is * bit 7 of the second byte and so on. * * @param bits an array of bytes to store the result. * @param len the number of bits to read. */ public void read( final byte[] bits, int len ) throws IOException { if ( ASSERTS ) assert fill < 32 : fill + " >= " + 32; if ( len <= fill ) { if ( len <= 8 ) { bits[ 0 ] = (byte)( readFromCurrent( len ) << 8 - len ); return; } else if ( len <= 16 ){ bits[ 0 ] = (byte)( readFromCurrent( 8 ) ); bits[ 1 ] = (byte)( readFromCurrent( len - 8 ) << 16 - len ); return; } else if ( len <= 24 ) { bits[ 0 ] = (byte)( readFromCurrent( 8 ) ); bits[ 1 ] = (byte)( readFromCurrent( 8 ) ); bits[ 2 ] = (byte)( readFromCurrent( len - 16 ) << 24 - len ); return; } else { bits[ 0 ] = (byte)( readFromCurrent( 8 ) ); bits[ 1 ] = (byte)( readFromCurrent( 8 ) ); bits[ 2 ] = (byte)( readFromCurrent( 8 ) ); bits[ 3 ] = (byte)( readFromCurrent( len - 24 ) << 32 - len ); return; } } else { int i, j = 0, b; if ( fill >= 24 ) { bits[ j++ ] = (byte)( readFromCurrent( 8 ) ); bits[ j++ ] = (byte)( readFromCurrent( 8 ) ); bits[ j++ ] = (byte)( readFromCurrent( 8 ) ); len -= 24; } else if ( fill >= 16 ) { bits[ j++ ] = (byte)( readFromCurrent( 8 ) ); bits[ j++ ] = (byte)( readFromCurrent( 8 ) ); len -= 16; } else if ( fill >= 8 ) { bits[ j++ ] = (byte)( readFromCurrent( 8 ) ); len -= 8; } final int shift = fill; if ( shift != 0 ) { bits[ j ] = (byte)( readFromCurrent( shift ) << 8 - shift ); len -= shift; i = len >> 3; while( i-- != 0 ) { b = read(); bits[ j ] |= ( b & 0xFF ) >>> shift; bits[ ++j ] = (byte)( b << 8 - shift ); } } else { i = len >> 3; while( i-- != 0 ) bits[ j++ ] = (byte)read(); } readBits += len & ~7; len &= 7; if ( len != 0 ) { if ( shift == 0 ) bits[ j ] = 0; // We must zero the next byte before OR'ing stuff in if ( len <= 8 - shift ) { bits[ j ] |= (byte)( readFromCurrent( len ) << 8 - shift - len ); } else { bits[ j ] |= (byte)( readFromCurrent( 8 - shift ) ); bits[ j + 1 ] = (byte)( readFromCurrent( len + shift - 8 ) << 16 - shift - len ); } } } } /** Reads a bit. * * @return the next bit from the stream. */ public int readBit() throws IOException { return readFromCurrent( 1 ); } /** Reads a fixed number of bits into an integer. * * @param len a bit length. * @return an integer whose lower len bits are taken from the stream; the rest is zeroed. */ public int readInt( int len ) throws IOException { int i, x = 0; if ( len < 0 || len > 32 ) throw new IllegalArgumentException( "You cannot read " + len + " bits into an integer." ); if ( fill < 16 ) refill(); if ( len <= fill ) return readFromCurrent( len ); len -= fill; x = readFromCurrent( fill ); i = len >> 3; while( i-- != 0 ) x = x << 8 | read(); readBits += len & ~7; len &= 7; return ( x << len ) | readFromCurrent( len ); } /** Reads a fixed number of bits into a long. * * @param len a bit length. * @return a long whose lower len bits are taken from the stream; the rest is zeroed. */ public long readLong( int len ) throws IOException { int i; long x = 0; if ( len < 0 || len > 64 ) throw new IllegalArgumentException( "You cannot read " + len + " bits into a long." ); if ( fill < 16 ) refill(); if ( len <= fill ) return readFromCurrent( len ); len -= fill; x = readFromCurrent( fill ); i = len >> 3; while( i-- != 0 ) x = x << 8 | read(); readBits += len & ~7; len &= 7; return ( x << len ) | readFromCurrent( len ); } /** Skips the given number of bits. * * @param n the number of bits to skip. * @return the actual number of skipped bits. */ public long skip( long n ) throws IOException { if ( n <= fill ) { if ( n < 0 ) throw new IllegalArgumentException( "Negative bit skip value: " + n ); fill -= n; readBits += n; return n; } else { final long prevReadBits = readBits; n -= fill; readBits += fill; fill = 0; long nb = n >> 3; // TODO: A real evaluation of the usefulness of this block of code if ( buffer != null && nb > avail && nb < avail + buffer.length ) { /* If we can skip by simply filling the buffer and skipping some bytes, we do it. Usually the next block has already been fetched by a read-ahead logic. */ readBits += ( avail + 1 ) << 3; n -= ( avail + 1 ) << 3; nb -= avail + 1; position += pos + avail; pos = avail = 0; read(); } if ( nb <= avail ) { // We skip bytes directly inside the buffer. pos += (int)nb; avail -= (int)nb; readBits += n & ~7; } else { // No way, we have to pass the byte skip to the underlying stream. n -= avail << 3; readBits += avail << 3; final long toSkip = nb - avail; // ALERT: the semantics of skip is flawed--this should be somehow fixed. final long skipped = is.skip( toSkip ); if ( skipped < toSkip ) throw new IOException( "skip() has skipped " + skipped + " instead of " + toSkip + " bytes" ); position += ( avail + pos ) + skipped; pos = 0; avail = 0; readBits += skipped << 3; if ( skipped != toSkip ) return readBits - prevReadBits; } final int residual = (int)( n & 7 ); if ( residual != 0 ) { current = read(); fill = 8 - residual; readBits += residual; } return readBits - prevReadBits; } } /** Sets this stream bit position, if it is based on a {@link RepositionableStream} or on a {@link java.nio.channels.FileChannel}. * *

Given an underlying stream that implements {@link * RepositionableStream} or that can provide a {@link * java.nio.channels.FileChannel} via the getChannel() method, * a call to this method has the same semantics of a {@link #flush()}, * followed by a call to {@link * java.nio.channels.FileChannel#position(long) position(position / 8)} on * the byte stream, followed by a {@link #skip(long) skip(position % 8)}. * * @param position the new position expressed as a bit offset. * @throws UnsupportedOperationException if the underlying byte stream does not implement * {@link RepositionableStream} and if the channel it returns is not a {@link java.nio.channels.FileChannel}. * @see FileChannel#position(long) */ public void position( /*final*/ long position ) throws IOException { if ( DEBUG ) System.err.println( this + ".position(" + position + ")" ); // adjust for slice offset (BBT). position += (off << 3); if ( position < 0 ) throw new IllegalArgumentException( "Illegal position: " + position ); final long bitDelta = ( ( this.position + pos ) << 3 ) - position; if ( bitDelta >= 0 && bitDelta <= fill ) { if ( DEBUG ) System.err.println( "Bit positioning... position: " + position + " this.position: " + this.position + " pos: " + pos + " bitDelta: " + bitDelta + " fill: " + fill ); fill = (int)bitDelta; //System.err.println( "Post: " + position + " fill: " + fill ); return; } final long delta = ( position >> 3 ) - ( this.position + pos ); if ( DEBUG ) System.err.println( this + ".position(" + position + "); curr: " + this.position + " delta: " + delta + " pos: " + pos + " avail: " + avail ); // TODO: check for delta < number of bits in current if ( delta <= avail && delta >= - pos ) { // We can reposition just by moving into the buffer. avail -= delta; pos += delta; fill = 0; if ( DEBUG ) System.err.println( this + ": moved internally; pos: " + pos + " avail: " + avail ); } else if ( repositionableStream != null ) { flush(); repositionableStream.position( this.position = position >> 3 ); } else if ( fileChannel != null ) { flush(); fileChannel.position( this.position = position >> 3 ); } else { if ( wrapping ) throw new UnsupportedOperationException( "Illegal position: " + position ); throw new UnsupportedOperationException( "position() can only be called if the underlying byte stream implements the RepositionableStream interface or if the getChannel() method of the underlying byte stream exists and returns a FileChannel" ); } final int residual = (int)( position & 7 ); if ( DEBUG ) System.err.println( this + ": residual=" + residual ); if ( residual != 0 ) { current = read(); fill = 8 - residual; } } /** Tests if this stream supports the {@link #mark(int)} and {@link #reset()} methods. * *

This method will just delegate the test to the underlying {@link InputStream}. * @return whether this stream supports {@link #mark(int)}/{@link #reset()}. */ public boolean markSupported() { return is.markSupported(); } /** Marks the current position in this input stream. A subsequent call to * the {@link #reset()} method repositions this stream at the last marked position so * that subsequent reads re-read the same bits. * *

This method will just delegate the mark to the underlying {@link InputStream}. * Moreover, it will throw an exception if you try to mark outsite byte boundaries. * * @param readLimit the maximum limit of bytes that can be read before the mark position becomes invalid. * @throws IOException if you try to mark outside byte boundaries. */ public void mark( final int readLimit ) throws IOException { if ( fill != 0 ) throw new IOException( "You cannot mark a bit stream outside of byte boundaries." ); is.mark( readLimit ); } /** Repositions this bit stream to the position at the time the {@link #mark(int)} method was last called. * *

This method will just {@link #flush() flush the stream} and delegate * the reset to the underlying {@link InputStream}. */ public void reset() throws IOException { flush(); is.reset(); } /** Reads a natural number in unary coding. * * @return the next unary-encoded natural number. * @see OutputBitStream#writeUnary(int) */ public int readUnary() throws IOException { if ( ASSERTS ) assert fill < 32 : fill + " >= " + 32; int x; if ( fill < 16 ) refill(); if ( fill != 0 ) { // Clean up current and check whether it is nonzero final int currentLeftAligned = current << 32 - fill; if ( currentLeftAligned != 0 ) { //System.err.println( Integer.toBinaryString( currentLeftAligned ) + ", " + Integer.toHexString( currentLeftAligned ) ); if ( ( currentLeftAligned & 0xFF000000 ) != 0 ) x = 8 - Fast.BYTEMSB[ currentLeftAligned >>> 24 ]; else if ( ( currentLeftAligned & 0xFF0000 ) != 0 ) x = 16 - Fast.BYTEMSB[ currentLeftAligned >>> 16 ]; else if ( ( currentLeftAligned & 0xFF00 ) != 0 ) x = 24 - Fast.BYTEMSB[ currentLeftAligned >>> 8 ]; else x = 32 - Fast.BYTEMSB[ currentLeftAligned & 0xFF ]; readBits += x; fill -= x; return x - 1; } } x = fill; while( ( current = read() ) == 0 ) x += 8; x += 7 - ( fill = Fast.BYTEMSB[ current ] ); readBits += x + 1; return x; } /** Reads a long natural number in unary coding. * * Note that by unary coding we mean that 1 encodes 0, 01 encodes 1 and so on. * * @return the next unary-encoded long natural number. * @see OutputBitStream#writeUnary(int) */ public long readLongUnary() throws IOException { if ( ASSERTS ) assert fill < 32 : fill + " >= " + 32; // Clean up current and check whether it is nonzero if ( ( current & ( 1 << fill ) - 1 ) != 0 ) return readUnary(); long x = fill; while( ( current = read() ) == 0 ) x += 8; x += 7 - ( fill = Fast.BYTEMSB[ current ] ); readBits += x + 1; return x; } /** Reads a natural number in γ coding. * * @return the next γ-encoded natural number. * @see OutputBitStream#writeGamma(int) * @see #skipGammas(int) */ public int readGamma() throws IOException { int preComp; if ( ( fill >= 16 || refill() >= 16 ) && ( preComp = GAMMA[ current >> ( fill - 16 ) & 0xFFFF ] ) != 0 ) { readBits += preComp >> 16; fill -= preComp >> 16; return preComp & 0xFFFF; } final int msb = readUnary(); return ( ( 1 << msb ) | readInt( msb ) ) - 1; } /** Reads a long natural number in γ coding. * * @return the next γ-encoded long natural number. * @see OutputBitStream#writeGamma(int) * @see #skipGammas(int) */ public long readLongGamma() throws IOException { int preComp; if ( ( fill >= 16 || refill() >= 16 ) && ( preComp = GAMMA[ current >> ( fill - 16 ) & 0xFFFF ] ) != 0 ) { readBits += preComp >> 16; fill -= preComp >> 16; return preComp & 0xFFFF; } final int msb = readUnary(); return ( ( 1L << msb ) | readLong( msb ) ) - 1; } /** Skips a given number of γ-coded integers. * *

This method should be significantly quicker than iterating n times on * {@link #readGamma()} or {@link #readLongGamma()}, as precomputed tables are used directly, * so the number of method calls is greatly reduced, and the result is discarded, so * {@link #skip(long)} can be invoked instead of more specific decoding methods. * * @param n the number of γ-coded integers to be skipped. * @see #readGamma() */ public void skipGammas( int n ) throws IOException { int preComp; while( n-- != 0) { if ( ( fill >= 16 || refill() >= 16 ) && ( preComp = GAMMA[ current >> ( fill - 16 ) & 0xFFFF ] >> 16 ) != 0 ) { readBits += preComp; fill -= preComp; continue; } skip( (long)readUnary() ); } } /** Reads a given number of γ-coded integers. * *

This method should be significantly quicker than iterating n times on * {@link #readGamma()}, as precomputed tables are used directly, * so the number of method calls is greatly reduced. * * @param a an array of at least count integers where the result * wil be written starting at the first position. * @param count the number of γ-coded integers to be read. * @see #readGamma() */ public void readGammas( final int[] a, final int count ) throws IOException { int preComp, msb; for( int i = 0; i < count; i++ ) { if ( ( fill >= 16 || refill() >= 16 ) && ( preComp = GAMMA[ current >> ( fill - 16 ) & 0xFFFF ] ) != 0 ) { readBits += preComp >> 16; fill -= preComp >> 16; a[ i ] = preComp & 0xFFFF; continue; } a[ i ] = ( ( 1 << ( msb = readUnary() ) ) | readInt( msb ) ) - 1; } } /** Reads a natural number in shifted γ coding. * * @return the next shifted-γ–encoded natural number. * @see OutputBitStream#writeShiftedGamma(int) * @see #skipShiftedGammas(int) */ public int readShiftedGamma() throws IOException { int preComp; if ( ( fill >= 16 || refill() >= 16 ) && ( preComp = SHIFTED_GAMMA[ current >> ( fill - 16 ) & 0xFFFF ] ) != 0 ) { readBits += preComp >> 16; fill -= preComp >> 16; return preComp & 0xFFFF; } final int msb = readUnary() - 1; return msb == -1 ? 0 : ( ( 1 << msb ) | readInt( msb ) ); } /** Reads a natural number in shifted γ coding. * * @return the next shifted-γ–encoded natural number. * @see OutputBitStream#writeShiftedGamma(int) * @see #skipShiftedGammas(int) */ public long readLongShiftedGamma() throws IOException { int preComp; if ( ( fill >= 16 || refill() >= 16 ) && ( preComp = SHIFTED_GAMMA[ current >> ( fill - 16 ) & 0xFFFF ] ) != 0 ) { readBits += preComp >> 16; fill -= preComp >> 16; return preComp & 0xFFFF; } final int msb = readUnary() - 1; return msb == -1 ? 0 : ( ( 1L << msb ) | readLong( msb ) ); } /** Skips a given number of shited-γ-coded integers. * *

This method should be significantly quicker than iterating n times on * {@link #readShiftedGamma()} or {@link #readLongShiftedGamma()}, as precomputed tables are used directly, * so the number of method calls is greatly reduced, and the result is discarded, so * {@link #skip(long)} can be invoked instead of more specific decoding methods. * * @param n the number of shited-γ-coded integers to be skipped. * @see #readShiftedGamma() */ public void skipShiftedGammas( int n ) throws IOException { int preComp; while( n-- != 0) { if ( ( fill >= 16 || refill() >= 16 ) && ( preComp = SHIFTED_GAMMA[ current >> ( fill - 16 ) & 0xFFFF ] >> 16 ) != 0 ) { readBits += preComp; fill -= preComp; continue; } final long msb = readUnary() - 1; if ( msb > 0 ) skip( msb ); } } /** Reads a given number of shifted-γ-coded integers. * *

This method should be significantly quicker than iterating n times on * {@link #readShiftedGamma()}, as precomputed tables are used directly, * so the number of method calls is greatly reduced. * * @param a an array of at least count integers where the result * wil be written starting at the first position. * @param count the number of shifted-γ-coded integers to be read. * @see #readShiftedGamma() */ public void readShiftedGammas( final int[] a, final int count ) throws IOException { int preComp, msb; for( int i = 0; i < count; i++ ) { if ( ( fill >= 16 || refill() >= 16 ) && ( preComp = SHIFTED_GAMMA[ current >> ( fill - 16 ) & 0xFFFF ] ) != 0 ) { readBits += preComp >> 16; fill -= preComp >> 16; a[ i ] = preComp & 0xFFFF; continue; } msb = readUnary() - 1; a[ i ] = msb == -1 ? 0 : ( ( 1 << msb ) | readInt( msb ) ); } } /** Reads a natural number in δ coding. * * @return the next δ-encoded natural number. * @see OutputBitStream#writeDelta(int) * @see #skipDeltas(int) */ public int readDelta() throws IOException { int preComp; if ( ( fill >= 16 || refill() >= 16 ) && ( preComp = DELTA[ current >> ( fill - 16 ) & 0xFFFF ] ) != 0 ) { readBits += preComp >> 16; fill -= preComp >> 16; return preComp & 0xFFFF; } final int msb = readGamma(); return ( ( 1 << msb ) | readInt( msb ) ) - 1; } /** Reads a long natural number in δ coding. * * @return the next δ-encoded long natural number. * @see OutputBitStream#writeDelta(int) * @see #skipDeltas(int) */ public long readLongDelta() throws IOException { int preComp; if ( ( fill >= 16 || refill() >= 16 ) && ( preComp = DELTA[ current >> ( fill - 16 ) & 0xFFFF ] ) != 0 ) { readBits += preComp >> 16; fill -= preComp >> 16; return preComp & 0xFFFF; } final int msb = readGamma(); return ( ( 1L << msb ) | readLong( msb ) ) - 1; } /** Skips a given number of δ-coded integers. * *

This method should be significantly quicker than iterating n times on * {@link #readDelta()} or {@link #readLongDelta()}, as precomputed tables are used directly, * so the number of method calls is greatly reduced, and the result is discarded, so * {@link #skip(long)} can be invoked instead of more specific decoding methods. * * @param n the number of δ-coded integers to be skipped. * @see #readDelta() */ public void skipDeltas( int n ) throws IOException { int preComp; while( n-- != 0) { if ( ( fill >= 16 || refill() >= 16 ) && ( preComp = DELTA[ current >> ( fill - 16 ) & 0xFFFF ] >> 16 ) != 0 ) { readBits += preComp; fill -= preComp; continue; } skip( (long)readGamma() ); } } /** Reads a given number of δ-coded integers. * *

This method should be significantly quicker than iterating n times on * {@link #readDelta()}, as precomputed tables are used directly, * so the number of method calls is greatly reduced. * * @param a an array of at least count integers where the result * wil be written starting at the first position. * @param count the number of δ-coded integers to be read. * @see #readDelta() */ public void readDeltas( final int[] a, final int count ) throws IOException { int preComp, msb; for( int i = 0; i < count; i++ ) { if ( ( fill >= 16 || refill() >= 16 ) && ( preComp = DELTA[ current >> ( fill - 16 ) & 0xFFFF ] ) != 0 ) { readBits += preComp >> 16; fill -= preComp >> 16; a[ i ] = preComp & 0xFFFF; continue; } a[ i ] = ( ( 1 << ( msb = readGamma() ) ) | readInt( msb ) ) - 1; } } /** Reads a natural number in a limited range using a minimal binary coding. * * @param b a strict upper bound. * @return the next minimally binary encoded natural number. * @throws IllegalArgumentException if you try to read a negative number or use a nonpositive base. * @see OutputBitStream#writeMinimalBinary(int, int) */ public int readMinimalBinary( final int b ) throws IOException { return readMinimalBinary( b, Fast.mostSignificantBit( b ) ); } /** Reads a natural number in a limited range using a minimal binary coding. * * This method is faster than {@link #readMinimalBinary(int)} because it does not * have to compute log2b. * * @param b a strict upper bound. * @param log2b the floor of the base-2 logarithm of the bound. * @return the next minimally binary encoded natural number. * @throws IllegalArgumentException if you try to read a negative number or use a nonpositive base. * @see OutputBitStream#writeMinimalBinary(int, int) */ public int readMinimalBinary( final int b, final int log2b ) throws IOException { if ( b < 1 ) throw new IllegalArgumentException( "The bound " + b + " is not positive" ); final int m = ( 1 << log2b + 1 ) - b; final int x = readInt( log2b ); if ( x < m ) return x; else return ( ( x << 1 ) + readBit() - m ); } /** Reads a long natural number in a limited range using a minimal binary coding. * * @param b a strict upper bound. * @return the next minimally binary encoded long natural number. * @throws IllegalArgumentException if you try to read a negative number or use a nonpositive base. * @see OutputBitStream#writeMinimalBinary(int, int) */ public long readLongMinimalBinary( final long b ) throws IOException { return readLongMinimalBinary( b, Fast.mostSignificantBit( b ) ); } /** Reads a long natural number in a limited range using a minimal binary coding. * * This method is faster than {@link #readLongMinimalBinary(long)} because it does not * have to compute log2b. * * @param b a strict upper bound. * @param log2b the floor of the base-2 logarithm of the bound. * @return the next minimally binary encoded long natural number. * @throws IllegalArgumentException if you try to read a negative number or use a nonpositive base. * @see OutputBitStream#writeMinimalBinary(int, int) */ public long readLongMinimalBinary( final long b, final int log2b ) throws IOException { if ( b < 1 ) throw new IllegalArgumentException( "The bound " + b + " is not positive" ); final long m = ( 1L << log2b + 1 ) - b; final long x = readLong( log2b ); if ( x < m ) return x; else return ( ( x << 1 ) + readBit() - m ); } /** Reads a natural number in Golomb coding. * *

This method implements also the case in which b is 0: in this case, * nothing will be read, and 0 will be returned. * * @param b the modulus for the coding. * @return the next Golomb-encoded natural number. * @throws IllegalArgumentException if you use a nonpositive modulus. * @see OutputBitStream#writeGolomb(int, int) */ public int readGolomb( final int b ) throws IOException { return readGolomb( b, Fast.mostSignificantBit( b ) ); } /** Reads a natural number in Golomb coding. * * This method is faster than {@link #readGolomb(int)} because it does not * have to compute log2b. * *

This method implements also the case in which b is 0: in this case, * nothing will be read, and 0 will be returned. * * @param b the modulus for the coding. * @param log2b the floor of the base-2 logarithm of the coding modulus. * @return the next Golomb-encoded natural number. * @throws IllegalArgumentException if you use a nonpositive modulus. * @see OutputBitStream#writeGolomb(int, int) */ public int readGolomb( final int b, final int log2b ) throws IOException { if ( b < 0 ) throw new IllegalArgumentException( "The modulus " + b + " is negative" ); if ( b == 0 ) return 0; return readUnary() * b + readMinimalBinary( b, log2b ); } /** Reads a long natural number in Golomb coding. * *

This method implements also the case in which b is 0: in this case, * nothing will be read, and 0 will be returned. * * @param b the modulus for the coding. * @return the next Golomb-encoded long natural number. * @throws IllegalArgumentException if you use a nonpositive modulus. * @see OutputBitStream#writeGolomb(int, int) */ public long readLongGolomb( final long b ) throws IOException { return readLongGolomb( b, Fast.mostSignificantBit( b ) ); } /** Reads a long natural number in Golomb coding. * * This method is faster than {@link #readLongGolomb(long)} because it does not * have to compute log2b. * *

This method implements also the case in which b is 0: in this case, * nothing will be read, and 0 will be returned. * * @param b the modulus for the coding. * @param log2b the floor of the base-2 logarithm of the coding modulus. * @return the next Golomb-encoded long natural number. * @throws IllegalArgumentException if you use a nonpositive modulus. * @see OutputBitStream#writeGolomb(int, int) */ public long readLongGolomb( final long b, final int log2b ) throws IOException { if ( b < 0 ) throw new IllegalArgumentException( "The modulus " + b + " is negative" ); if ( b == 0 ) return 0; return readUnary() * b + readLongMinimalBinary( b, log2b ); } /** Reads a natural number in skewed Golomb coding. * *

This method implements also the case in which b is 0: in this case, * nothing will be read, and 0 will be returned. * * @param b the modulus for the coding. * @return the next skewed Golomb-encoded natural number. * @throws IllegalArgumentException if you use a negative modulus. * @see OutputBitStream#writeSkewedGolomb(int, int) */ public int readSkewedGolomb( final int b ) throws IOException { if ( b < 0 ) throw new IllegalArgumentException( "The modulus " + b + " is negative" ); if ( b == 0 ) return 0; final int M = ( ( 1 << readUnary() + 1 ) - 1 ) * b; final int m = ( M / ( 2 * b ) ) * b; return m + readMinimalBinary( M - m ); } /** Reads a long natural number in skewed Golomb coding. * *

This method implements also the case in which b is 0: in this case, * nothing will be read, and 0 will be returned. * * @param b the modulus for the coding. * @return the next skewed Golomb-encoded long natural number. * @throws IllegalArgumentException if you use a negative modulus. * @see OutputBitStream#writeSkewedGolomb(int, int) */ public long readLongSkewedGolomb( final long b ) throws IOException { if ( b < 0 ) throw new IllegalArgumentException( "The modulus " + b + " is negative" ); if ( b == 0 ) return 0; final long M = ( ( 1 << readUnary() + 1 ) - 1 ) * b; final long m = ( M / ( 2 * b ) ) * b; return m + readLongMinimalBinary( M - m ); } /** Reads a natural number in ζ coding. * * @param k the shrinking factor. * @return the next ζ-encoded natural number. * @throws IllegalArgumentException if you use a nonpositive shrinking factor. * @see OutputBitStream#writeZeta(int, int) */ public int readZeta( final int k ) throws IOException { if ( k < 1 ) throw new IllegalArgumentException( "The shrinking factor " + k + " is not positive" ); if ( k == 3 ) { int preComp; if ( ( fill >= 16 || refill() >= 16 ) && ( preComp = ZETA_3[ current >> ( fill - 16 ) & 0xFFFF ] ) != 0 ) { readBits += preComp >> 16; fill -= preComp >> 16; return preComp & 0xFFFF; } } final int h = readUnary(); final int left = 1 << h * k; final int m = readInt( h * k + k - 1 ); if ( m < left ) return m + left - 1; return ( m << 1 ) + readBit() - 1; } /** Reads a long natural number in ζ coding. * * @param k the shrinking factor. * @return the next ζ-encoded long natural number. * @throws IllegalArgumentException if you use a nonpositive shrinking factor. * @see OutputBitStream#writeZeta(int, int) */ public long readLongZeta( final int k ) throws IOException { if ( k < 1 ) throw new IllegalArgumentException( "The shrinking factor " + k + " is not positive" ); if ( k == 3 ) { int preComp; if ( ( fill >= 16 || refill() >= 16 ) && ( preComp = ZETA_3[ current >> ( fill - 16 ) & 0xFFFF ] ) != 0 ) { readBits += preComp >> 16; fill -= preComp >> 16; return preComp & 0xFFFF; } } final int h = readUnary(); final long left = 1L << h * k; final long m = readLong( h * k + k - 1 ); if ( m < left ) return m + left - 1; return ( m << 1 ) + readBit() - 1; } /** Skips a given number of ζ-coded integers. * *

This method should be significantly quicker than iterating n times on * {@link #readZeta(int)} or {@link #readLongZeta(int)}, as precomputed tables are used directly, * so the number of method calls is greatly reduced, and the result is discarded, so * {@link #skip(long)} can be invoked instead of more specific decoding methods. * * * @param k the shrinking factor. * @param n the number of ζ-coded integers to be skipped. * @see #readZeta(int) */ public void skipZetas( final int k, int n ) throws IOException { int h, preComp; while( n-- != 0) { if ( k == 3 && ( fill >= 16 || refill() >= 16 ) && ( preComp = ZETA_3[ current >> ( fill - 16 ) & 0xFFFF ] >> 16 ) != 0 ) { readBits += preComp; fill -= preComp; continue; } h = readUnary(); if ( readInt( h * k + k - 1 ) >= 1 << h * k ) skip( 1L ); } } /** Reads a given number of γ-coded integers. * *

This method should be significantly quicker than iterating n times on * {@link #readGamma()}, as precomputed tables are used directly, * so the number of method calls is greatly reduced. * * @param k the shrinking factor. * @param a an array of at least count integers where the result * wil be written starting at the first position. * @param count the number of ζ-coded integers to be read. * @see #readGamma() */ public void readZetas( final int k, final int[] a, final int count ) throws IOException { int h, left, m; int preComp; for( int i = 0; i < count; i++ ) { if ( k == 3 && ( fill >= 16 || refill() >= 16 ) && ( preComp = ZETA_3[ current >> ( fill - 16 ) & 0xFFFF ] ) != 0 ) { readBits += preComp >> 16; fill -= preComp >> 16; a[ i ] = preComp & 0xFFFF; continue; } h = readUnary(); left = 1 << h * k; m = readInt( h * k + k - 1 ); a[ i ] = m < left ? m + left - 1 : ( m << 1 ) + readBit() - 1; } } /** Reads a natural number in variable-length nibble coding. * * @return the next variable-length nibble-encoded natural number. * @see OutputBitStream#writeNibble(int) */ public int readNibble() throws IOException { int b; int x = 0; do { x <<= 3; b = readBit(); x |= readInt( 3 ); } while( b == 0 ); return x; } /** Reads a long natural number in variable-length nibble coding. * * @return the next variable-length nibble-encoded long natural number. * @see OutputBitStream#writeNibble(int) */ public long readLongNibble() throws IOException { int b; long x = 0; do { x <<= 3; b = readBit(); x |= readInt( 3 ); } while( b == 0 ); return x; } public boolean hasNext() { return true; } public boolean nextBoolean() { try { return readBit() != 0; } catch ( IOException e ) { throw new RuntimeException( e ); } } /** Skips over the given number of bits. * * @param n the number of bits to skip. * @return the number of bits actually skipped. * @deprecated This method is simply an expensive, try/catch-surrounded version * of {@link #skip(long)} that is made necessary by the interface * by {@link BooleanIterator}. */ @Deprecated public int skip( final int n ) { try { return (int)skip( (long)n ); } catch ( IOException e ) { throw new RuntimeException( e ); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy