• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 package org.apache.commons.compress.utils;
20 
21 import java.io.Closeable;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.nio.ByteOrder;
25 
26 /**
27  * Reads bits from an InputStream.
28  * @since 1.10
29  * @NotThreadSafe
30  */
31 public class BitInputStream implements Closeable {
32     private static final int MAXIMUM_CACHE_SIZE = 63; // bits in long minus sign bit
33     private static final long[] MASKS = new long[MAXIMUM_CACHE_SIZE + 1];
34 
35     static {
36         for (int i = 1; i <= MAXIMUM_CACHE_SIZE; i++) {
37             MASKS[i] = (MASKS[i - 1] << 1) + 1;
38         }
39     }
40 
41     private final CountingInputStream in;
42     private final ByteOrder byteOrder;
43     private long bitsCached = 0;
44     private int bitsCachedSize = 0;
45 
46     /**
47      * Constructor taking an InputStream and its bit arrangement.
48      * @param in the InputStream
49      * @param byteOrder the bit arrangement across byte boundaries,
50      *      either BIG_ENDIAN (aaaaabbb bb000000) or LITTLE_ENDIAN (bbbaaaaa 000000bb)
51      */
BitInputStream(final InputStream in, final ByteOrder byteOrder)52     public BitInputStream(final InputStream in, final ByteOrder byteOrder) {
53         this.in = new CountingInputStream(in);
54         this.byteOrder = byteOrder;
55     }
56 
57     @Override
close()58     public void close() throws IOException {
59         in.close();
60     }
61 
62     /**
63      * Clears the cache of bits that have been read from the
64      * underlying stream but not yet provided via {@link #readBits}.
65      */
clearBitCache()66     public void clearBitCache() {
67         bitsCached = 0;
68         bitsCachedSize = 0;
69     }
70 
71     /**
72      * Returns at most 63 bits read from the underlying stream.
73      *
74      * @param count the number of bits to read, must be a positive
75      * number not bigger than 63.
76      * @return the bits concatenated as a long using the stream's byte order.
77      *         -1 if the end of the underlying stream has been reached before reading
78      *         the requested number of bits
79      * @throws IOException on error
80      */
readBits(final int count)81     public long readBits(final int count) throws IOException {
82         if (count < 0 || count > MAXIMUM_CACHE_SIZE) {
83             throw new IllegalArgumentException("count must not be negative or greater than " + MAXIMUM_CACHE_SIZE);
84         }
85         if (ensureCache(count)) {
86             return -1;
87         }
88 
89         if (bitsCachedSize < count) {
90             return processBitsGreater57(count);
91         }
92         return readCachedBits(count);
93     }
94 
95     /**
96      * Returns the number of bits that can be read from this input
97      * stream without reading from the underlying input stream at all.
98      * @return estimate of the number of bits that can be read without reading from the underlying stream
99      * @since 1.16
100      */
bitsCached()101     public int bitsCached() {
102         return bitsCachedSize;
103     }
104 
105     /**
106      * Returns an estimate of the number of bits that can be read from
107      * this input stream without blocking by the next invocation of a
108      * method for this input stream.
109      * @throws IOException if the underlying stream throws one when calling available
110      * @return estimate of the number of bits that can be read without blocking
111      * @since 1.16
112      */
bitsAvailable()113     public long bitsAvailable() throws IOException {
114         return bitsCachedSize + ((long) Byte.SIZE) * in.available();
115     }
116 
117     /**
118      * Drops bits until the next bits will be read from a byte boundary.
119      * @since 1.16
120      */
alignWithByteBoundary()121     public void alignWithByteBoundary() {
122         int toSkip = bitsCachedSize % Byte.SIZE;
123         if (toSkip > 0) {
124             readCachedBits(toSkip);
125         }
126     }
127 
128     /**
129      * Returns the number of bytes read from the underlying stream.
130      *
131      * <p>This includes the bytes read to fill the current cache and
132      * not read as bits so far.</p>
133      * @return the number of bytes read from the underlying stream
134      * @since 1.17
135      */
getBytesRead()136     public long getBytesRead() {
137         return in.getBytesRead();
138     }
139 
processBitsGreater57(final int count)140     private long processBitsGreater57(final int count) throws IOException {
141         final long bitsOut;
142         int overflowBits = 0;
143         long overflow = 0L;
144 
145         // bitsCachedSize >= 57 and left-shifting it 8 bits would cause an overflow
146         int bitsToAddCount = count - bitsCachedSize;
147         overflowBits = Byte.SIZE - bitsToAddCount;
148         final long nextByte = in.read();
149         if (nextByte < 0) {
150             return nextByte;
151         }
152         if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
153             long bitsToAdd = nextByte & MASKS[bitsToAddCount];
154             bitsCached |= (bitsToAdd << bitsCachedSize);
155             overflow = (nextByte >>> bitsToAddCount) & MASKS[overflowBits];
156         } else {
157             bitsCached <<= bitsToAddCount;
158             long bitsToAdd = (nextByte >>> (overflowBits)) & MASKS[bitsToAddCount];
159             bitsCached |= bitsToAdd;
160             overflow = nextByte & MASKS[overflowBits];
161         }
162         bitsOut = bitsCached & MASKS[count];
163         bitsCached = overflow;
164         bitsCachedSize = overflowBits;
165         return bitsOut;
166     }
167 
readCachedBits(int count)168     private long readCachedBits(int count) {
169         final long bitsOut;
170         if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
171             bitsOut = (bitsCached & MASKS[count]);
172             bitsCached >>>= count;
173         } else {
174             bitsOut = (bitsCached >> (bitsCachedSize - count)) & MASKS[count];
175         }
176         bitsCachedSize -= count;
177         return bitsOut;
178     }
179 
180     /**
181      * Fills the cache up to 56 bits
182      * @param count
183      * @return return true, when EOF
184      * @throws IOException
185      */
ensureCache(final int count)186     private boolean ensureCache(final int count) throws IOException {
187         while (bitsCachedSize < count && bitsCachedSize < 57) {
188             final long nextByte = in.read();
189             if (nextByte < 0) {
190                 return true;
191             }
192             if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
193                 bitsCached |= (nextByte << bitsCachedSize);
194             } else {
195                 bitsCached <<= Byte.SIZE;
196                 bitsCached |= nextByte;
197             }
198             bitsCachedSize += Byte.SIZE;
199         }
200         return false;
201     }
202 
203 }
204