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

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

package it.unimi.dsi.io;

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;

/*
 * DSI utilities
 *
 * Copyright (C) 2002-2017 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 3 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, see .
 *
 */

import it.unimi.dsi.bits.Fast;
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;


/** 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 a (long) natural number 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 natural numbers 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 implements BooleanIterator, Flushable, Closeable { private final static boolean DEBUG = 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(); assert checkLength(resource, array, resouceFullPath); } public static boolean checkLength(final String resource, final int[] array, final String resouceFullPath) { 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; return true; } 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; /** 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 test that is necessary to support {@link #position(long)} * in case is does not implement {@link RepositionableStream} 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 test that is necessary to support {@link #position(long)} * in case is does not implement {@link RepositionableStream} 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) { is = NullInputStream.getInstance(); repositionableStream = null; fileChannel = null; if (a.length > 0) { buffer = a; avail = a.length; 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 with 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. */ @Override public void flush() { if (! wrapping) { position += pos; avail = 0; pos = 0; } fill = 0; } /** Closes the bit stream. All resources associated with the stream are released. */ @Override public void close() throws IOException { if (is != null && 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 { 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; } 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 { 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)}. * *

Note that this method does not change the value returned by {@link #readBits()}. * * @param position the new position expressed as a bit offset. * @throws UnsupportedOperationException if the underlying byte stream does not implement * {@link RepositionableStream} or 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 + ")"); 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); 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; } } /** Returns this stream bit position. * @return this stream bit position. */ public long position() { return ((this.position + pos) << 3) - fill; } /** 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 { assert fill < 32 : fill + " >= " + 32; int x; if (fill < 16) refill(); x = Integer.numberOfLeadingZeros(current << (32 - fill)); if (x < fill) { // This works also when fill = 0 readBits += x + 1; fill -= x + 1; return x; } x = fill; while((current = read()) == 0) x += 8; x += 7 - (fill = 31 - Integer.numberOfLeadingZeros(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 { assert fill < 32 : fill + " >= " + 32; if (fill < 16) refill(); long x = Integer.numberOfLeadingZeros(current << (32 - fill)); if (x < fill) { // This works also when fill = 0 readBits += x + 1; fill -= x + 1; return x; } x = fill; while((current = read()) == 0) x += 8; x += 7 - (fill = 31 - Integer.numberOfLeadingZeros(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 amount of γ-coded natural numbers. * *

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 natural numbers to be skipped. * @see #readGamma() */ public void skipGammas(long 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()); } } /** Skips a given amount of γ-coded natural numbers. * *

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 natural numbers to be skipped. * @see #readGamma() */ public void skipGammas(int n) throws IOException { skipGammas((long)n); } /** Reads a given amount of γ-coded natural numbers. * *

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 * will be written starting at the first position. * @param count the number of γ-coded natural numbers 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 amount of shifted-γ-coded natural numbers. * *

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 shifted-γ-coded natural numbers to be skipped. * @see #readShiftedGamma() */ public void skipShiftedGammas(long 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); } } /** Skips a given amount of shifted-γ-coded natural numbers. * *

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 shifted-γ-coded natural numbers to be skipped. * @see #readShiftedGamma() */ public void skipShiftedGammas(int n) throws IOException { skipShiftedGammas((long)n); } /** Reads a given amount of shifted-γ-coded natural numbers. * *

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 * will be written starting at the first position. * @param count the number of shifted-γ-coded natural numbers 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 amount of δ-coded natural numbers. * *

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 natural numbers to be skipped. * @see #readDelta() */ public void skipDeltas(long 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()); } } /** Skips a given amount of δ-coded natural numbers. * *

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 natural numbers to be skipped. * @see #readDelta() */ public void skipDeltas(int n) throws IOException { skipDeltas((long)n); } /** Reads a given amount of δ-coded natural numbers. * *

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 * will be written starting at the first position. * @param count the number of δ-coded natural numbers 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 = ((1L << 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 amount of ζ-coded natural numbers. * *

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 natural numbers to be skipped. * @see #readZeta(int) */ public void skipZetas(final int k, long 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); } } /** Skips a given amount of ζ-coded natural numbers. * *

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 natural numbers to be skipped. * @see #readZeta(int) */ public void skipZetas(final int k, int n) throws IOException { skipZetas(k, (long)n); } /** Reads a given amount of γ-coded natural numbers. * *

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 * will be written starting at the first position. * @param count the number of ζ-coded natural numbers 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; } @Override public boolean hasNext() { return true; } @Override 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}. */ @Override @Deprecated public int skip(final int n) { try { return (int)skip((long)n); } catch (IOException e) { throw new RuntimeException(e); } } /** Copies a given number of bits from this input bit stream into a given output bit stream. * * @param obs an output bit stream. * @param length the number of bits to copy. * @throws EOFException if there are not enough bits to copy. */ public void copyTo(final OutputBitStream obs, long length) throws IOException { final byte[] buffer = new byte[64 * 1024]; while(length > 0) { final int toRead = (int)Math.min(length, buffer.length * Byte.SIZE); read(buffer, toRead); obs.write(buffer, 0, toRead); length -= toRead; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy