    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one
    003 * or more contributor license agreements.  See the NOTICE file
    004 * distributed with this work for additional information
    005 * regarding copyright ownership.  The ASF licenses this file
    006 * to you under the Apache License, Version 2.0 (the
    007 * "License"); you may not use this file except in compliance
    008 * with the License.  You may obtain a copy of the License at
    009 *
    010 * http://www.apache.org/licenses/LICENSE-2.0
    011 *
    012 * Unless required by applicable law or agreed to in writing,
    013 * software distributed under the License is distributed on an
    014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015 * KIND, either express or implied.  See the License for the
    016 * specific language governing permissions and limitations
    017 * under the License.
    018 */
    019package org.apache.commons.compress.utils;
    020
    021import java.io.Closeable;
    022import java.io.IOException;
    023import java.io.InputStream;
    024import java.nio.ByteOrder;
    025
    026/**
    027 * Reads bits from an InputStream.
    028 *
    029 * @since 1.10
    030 * @NotThreadSafe
    031 */
    032public class BitInputStream implements Closeable {
    033    private static final int MAXIMUM_CACHE_SIZE = 63; // bits in long minus sign bit
    034    private static final long[] MASKS = new long[MAXIMUM_CACHE_SIZE + 1];
    035
    036    static {
    037        for (int i = 1; i <= MAXIMUM_CACHE_SIZE; i++) {
    038            MASKS[i] = (MASKS[i - 1] << 1) + 1;
    039        }
    040    }
    041
    042    private final org.apache.commons.io.input.BoundedInputStream in;
    043    private final ByteOrder byteOrder;
    044    private long bitsCached;
    045    private int bitsCachedSize;
    046
    047    /**
    048     * Constructor taking an InputStream and its bit arrangement.
    049     *
    050     * @param in        the InputStream
    051     * @param byteOrder the bit arrangement across byte boundaries, either BIG_ENDIAN (aaaaabbb bb000000) or LITTLE_ENDIAN (bbbaaaaa 000000bb)
    052     */
    053    public BitInputStream(final InputStream in, final ByteOrder byteOrder) {
    054        this.in = org.apache.commons.io.input.BoundedInputStream.builder().setInputStream(in).asSupplier().get();
    055        this.byteOrder = byteOrder;
    056    }
    057
    058    /**
    059     * Drops bits until the next bits will be read from a byte boundary.
    060     *
    061     * @since 1.16
    062     */
    063    public void alignWithByteBoundary() {
    064        final int toSkip = bitsCachedSize % Byte.SIZE;
    065        if (toSkip > 0) {
    066            readCachedBits(toSkip);
    067        }
    068    }
    069
    070    /**
    071     * Returns an estimate of the number of bits that can be read from this input stream without blocking by the next invocation of a method for this input
    072     * stream.
    073     *
    074     * @throws IOException if the underlying stream throws one when calling available
    075     * @return estimate of the number of bits that can be read without blocking
    076     * @since 1.16
    077     */
    078    public long bitsAvailable() throws IOException {
    079        return bitsCachedSize + (long) Byte.SIZE * in.available();
    080    }
    081
    082    /**
    083     * Returns the number of bits that can be read from this input stream without reading from the underlying input stream at all.
    084     *
    085     * @return estimate of the number of bits that can be read without reading from the underlying stream
    086     * @since 1.16
    087     */
    088    public int bitsCached() {
    089        return bitsCachedSize;
    090    }
    091
    092    /**
    093     * Clears the cache of bits that have been read from the underlying stream but not yet provided via {@link #readBits}.
    094     */
    095    public void clearBitCache() {
    096        bitsCached = 0;
    097        bitsCachedSize = 0;
    098    }
    099
    100    @Override
    101    public void close() throws IOException {
    102        in.close();
    103    }
    104
    105    /**
    106     * Fills the cache up to 56 bits
    107     *
    108     * @param count
    109     * @return return true, when EOF
    110     * @throws IOException
    111     */
    112    private boolean ensureCache(final int count) throws IOException {
    113        while (bitsCachedSize < count && bitsCachedSize < 57) {
    114            final long nextByte = in.read();
    115            if (nextByte < 0) {
    116                return true;
    117            }
    118            if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
    119                bitsCached |= nextByte << bitsCachedSize;
    120            } else {
    121                bitsCached <<= Byte.SIZE;
    122                bitsCached |= nextByte;
    123            }
    124            bitsCachedSize += Byte.SIZE;
    125        }
    126        return false;
    127    }
    128
    129    /**
    130     * Returns the number of bytes read from the underlying stream.
    131     * <p>
    132     * This includes the bytes read to fill the current cache and not read as bits so far.
    133     * </p>
    134     *
    135     * @return the number of bytes read from the underlying stream
    136     * @since 1.17
    137     */
    138    public long getBytesRead() {
    139        return in.getCount();
    140    }
    141
    142    private long processBitsGreater57(final int count) throws IOException {
    143        final long bitsOut;
    144        final int overflowBits;
    145        long overflow = 0L;
    146
    147        // bitsCachedSize >= 57 and left-shifting it 8 bits would cause an overflow
    148        final int bitsToAddCount = count - bitsCachedSize;
    149        overflowBits = Byte.SIZE - bitsToAddCount;
    150        final long nextByte = in.read();
    151        if (nextByte < 0) {
    152            return nextByte;
    153        }
    154        if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
    155            final long bitsToAdd = nextByte & MASKS[bitsToAddCount];
    156            bitsCached |= bitsToAdd << bitsCachedSize;
    157            overflow = nextByte >>> bitsToAddCount & MASKS[overflowBits];
    158        } else {
    159            bitsCached <<= bitsToAddCount;
    160            final long bitsToAdd = nextByte >>> overflowBits & MASKS[bitsToAddCount];
    161            bitsCached |= bitsToAdd;
    162            overflow = nextByte & MASKS[overflowBits];
    163        }
    164        bitsOut = bitsCached & MASKS[count];
    165        bitsCached = overflow;
    166        bitsCachedSize = overflowBits;
    167        return bitsOut;
    168    }
    169
    170    /**
    171     * Returns at most 63 bits read from the underlying stream.
    172     *
    173     * @param count the number of bits to read, must be a positive number not bigger than 63.
    174     * @return the bits concatenated as a long using the stream's byte order. -1 if the end of the underlying stream has been reached before reading the
    175     *         requested number of bits
    176     * @throws IOException on error
    177     */
    178    public long readBits(final int count) throws IOException {
    179        if (count < 0 || count > MAXIMUM_CACHE_SIZE) {
    180            throw new IOException("count must not be negative or greater than " + MAXIMUM_CACHE_SIZE);
    181        }
    182        if (ensureCache(count)) {
    183            return -1;
    184        }
    185
    186        if (bitsCachedSize < count) {
    187            return processBitsGreater57(count);
    188        }
    189        return readCachedBits(count);
    190    }
    191
    192    private long readCachedBits(final int count) {
    193        final long bitsOut;
    194        if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
    195            bitsOut = bitsCached & MASKS[count];
    196            bitsCached >>>= count;
    197        } else {
    198            bitsOut = bitsCached >> bitsCachedSize - count & MASKS[count];
    199        }
    200        bitsCachedSize -= count;
    201        return bitsOut;
    202    }
    203
    204}