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 * @since 1.10
029 * @NotThreadSafe
030 */
031public class BitInputStream implements Closeable {
032    private static final int MAXIMUM_CACHE_SIZE = 63; // bits in long minus sign bit
033    private static final long[] MASKS = new long[MAXIMUM_CACHE_SIZE + 1];
034
035    static {
036        for (int i = 1; i <= MAXIMUM_CACHE_SIZE; i++) {
037            MASKS[i] = (MASKS[i - 1] << 1) + 1;
038        }
039    }
040
041    private final InputStream in;
042    private final ByteOrder byteOrder;
043    private long bitsCached = 0;
044    private int bitsCachedSize = 0;
045
046    /**
047     * Constructor taking an InputStream and its bit arrangement. 
048     * @param in the InputStream
049     * @param byteOrder the bit arrangement across byte boundaries,
050     *      either BIG_ENDIAN (aaaaabbb bb000000) or LITTLE_ENDIAN (bbbaaaaa 000000bb)
051     */
052    public BitInputStream(final InputStream in, final ByteOrder byteOrder) {
053        this.in = in;
054        this.byteOrder = byteOrder;
055    }
056    
057    public void close() throws IOException {
058        in.close();
059    }
060    
061    /**
062     * Clears the cache of bits that have been read from the
063     * underlying stream but not yet provided via {@link #readBits}.
064     */
065    public void clearBitCache() {
066        bitsCached = 0;
067        bitsCachedSize = 0;
068    }
069    
070    /**
071     * Returns at most 63 bits read from the underlying stream.
072     *
073     * @param count the number of bits to read, must be a positive
074     * number not bigger than 63.
075     * @return the bits concatenated as a long using the stream's byte order.
076     *         -1 if the end of the underlying stream has been reached before reading
077     *         the requested number of bits
078     */
079    public long readBits(final int count) throws IOException {
080        if (count < 0 || count > MAXIMUM_CACHE_SIZE) {
081            throw new IllegalArgumentException("count must not be negative or greater than " + MAXIMUM_CACHE_SIZE);
082        }
083        while (bitsCachedSize < count) {
084            final long nextByte = in.read();
085            if (nextByte < 0) {
086                return nextByte;
087            }
088            if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
089                bitsCached |= (nextByte << bitsCachedSize);
090            } else {
091                bitsCached <<= 8;
092                bitsCached |= nextByte;
093            }
094            bitsCachedSize += 8;
095        }
096        
097        final long bitsOut;
098        if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
099            bitsOut = (bitsCached & MASKS[count]);
100            bitsCached >>>= count;
101        } else {
102            bitsOut = (bitsCached >> (bitsCachedSize - count)) & MASKS[count];
103        }
104        bitsCachedSize -= count;
105        return bitsOut;
106    }
107}