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 *
29 * @since 1.10
30 * @NotThreadSafe
31 */
32 public class BitInputStream implements Closeable {
33 private static final int MAXIMUM_CACHE_SIZE = 63; // bits in long minus sign bit
34 private static final long[] MASKS = new long[MAXIMUM_CACHE_SIZE + 1];
35
36 static {
37 for (int i = 1; i <= MAXIMUM_CACHE_SIZE; i++) {
38 MASKS[i] = (MASKS[i - 1] << 1) + 1;
39 }
40 }
41
42 private final org.apache.commons.io.input.BoundedInputStream in;
43 private final ByteOrder byteOrder;
44 private long bitsCached;
45 private int bitsCachedSize;
46
47 /**
48 * Constructor taking an InputStream and its bit arrangement.
49 *
50 * @param in the InputStream
51 * @param byteOrder the bit arrangement across byte boundaries, either BIG_ENDIAN (aaaaabbb bb000000) or LITTLE_ENDIAN (bbbaaaaa 000000bb)
52 */
53 public BitInputStream(final InputStream in, final ByteOrder byteOrder) {
54 this.in = org.apache.commons.io.input.BoundedInputStream.builder().setInputStream(in).asSupplier().get();
55 this.byteOrder = byteOrder;
56 }
57
58 /**
59 * Drops bits until the next bits will be read from a byte boundary.
60 *
61 * @since 1.16
62 */
63 public void alignWithByteBoundary() {
64 final int toSkip = bitsCachedSize % Byte.SIZE;
65 if (toSkip > 0) {
66 readCachedBits(toSkip);
67 }
68 }
69
70 /**
71 * 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
72 * stream.
73 *
74 * @throws IOException if the underlying stream throws one when calling available
75 * @return estimate of the number of bits that can be read without blocking
76 * @since 1.16
77 */
78 public long bitsAvailable() throws IOException {
79 return bitsCachedSize + (long) Byte.SIZE * in.available();
80 }
81
82 /**
83 * Returns the number of bits that can be read from this input stream without reading from the underlying input stream at all.
84 *
85 * @return estimate of the number of bits that can be read without reading from the underlying stream
86 * @since 1.16
87 */
88 public int bitsCached() {
89 return bitsCachedSize;
90 }
91
92 /**
93 * Clears the cache of bits that have been read from the underlying stream but not yet provided via {@link #readBits}.
94 */
95 public void clearBitCache() {
96 bitsCached = 0;
97 bitsCachedSize = 0;
98 }
99
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 }