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

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

Go to download

The DSI utilities are a mishmash of classes accumulated during the last twenty years in projects developed at the DSI (Dipartimento di Scienze dell'Informazione, i.e., Information Sciences Department), now DI (Dipartimento di Informatica, i.e., Informatics Department), of the Universita` degli Studi di Milano.

There is a newer version: 2.7.3
Show newest version
/*
 * DSI utilities
 *
 * Copyright (C) 2002-2020 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 .
 *
 */

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;

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 static final 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); final 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 DataInputStream dis = new DataInputStream(InputBitStream.class.getResourceAsStream(resouceFullPath)); final int actualLength = IntIterators.unwrap(BinIO.asIntIterator(dis)).length; assert array.length == actualLength : resource + " is long " + actualLength + " but we think it should rather be " + array.length; try { dis.close(); } catch (final IOException e) { throw new RuntimeException(e); } 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 (final IOException e) { throw new RuntimeException(e); } } /** The default size of the byte buffer in bytes (8Ki). */ public static final 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(final IllegalAccessException e) {} catch(final IllegalArgumentException e) {} catch(final NoSuchMethodException e) {} catch(final InvocationTargetException e) {} catch(final 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(final 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(final 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(final 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(final 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, final 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 (final 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 (final 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