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