001 /*
002 * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
003 *
004 * http://uio.imagero.com
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions are met:
008 *
009 * o Redistributions of source code must retain the above copyright notice,
010 * this list of conditions and the following disclaimer.
011 *
012 * o Redistributions in binary form must reproduce the above copyright notice,
013 * this list of conditions and the following disclaimer in the documentation
014 * and/or other materials provided with the distribution.
015 *
016 * o Neither the name of Andrey Kuznetsov nor the names of
017 * its contributors may be used to endorse or promote products derived
018 * from this software without specific prior written permission.
019 *
020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
022 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
023 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
024 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
027 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
028 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
029 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
030 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031 */
032
033 package com.imagero.uio.io;
034
035 import java.io.FilterInputStream;
036 import java.io.IOException;
037 import java.io.InputStream;
038
039 /**
040 * adds ability to read streams bitewise and also to read predefined amount of bits every read() call
041 * @author Andrey Kuznetsov
042 */
043 public class BitInputStream extends FilterInputStream {
044
045 int vbits = 0;
046 int bitbuf = 0;
047
048 int markBitbuf;
049 int markVbits;
050
051 private int bitsToRead = 8;
052 boolean invertBitOrder;
053
054 public BitInputStream(InputStream in) {
055 super(in);
056 }
057
058 /**
059 * how much bits is read every read() call (default - 8)
060 * @return
061 */
062 public int getBitsToRead() {
063 return bitsToRead;
064 }
065
066 /**
067 * set how much bits is read every read() call (max 8)
068 * @param bitsToRead
069 */
070 public void setBitsToRead(int bitsToRead) {
071 if(bitsToRead > 32) {
072 throw new IllegalArgumentException("" + bitsToRead);
073 }
074 this.bitsToRead = bitsToRead;
075 }
076
077 public boolean isInvertBitOrder() {
078 return invertBitOrder;
079 }
080
081 public void setInvertBitOrder(boolean invertBitOrder) {
082 this.invertBitOrder = invertBitOrder;
083 if(invertBitOrder) {
084 createFlipTable();
085 }
086 }
087
088 public int read() throws IOException {
089 return read(bitsToRead);
090 }
091
092 public int read(int nbits) throws IOException {
093 int ret;
094 //nothing to read
095 if (nbits == 0) {
096 return 0;
097 }
098 //too many bits requested
099 if(nbits > 32) {
100 throw new IllegalArgumentException("only 32 bit can be read at once");
101 }
102 if (nbits > 24) {
103 int nbits0 = nbits / 2;
104 int nbits1 = nbits - nbits0;
105 return (read(nbits0) << nbits1) | read(nbits1);
106 }
107 //not anough bits in buffer
108 if (nbits > vbits) {
109 fillBuffer(nbits);
110 }
111 //buffer still empty => we are reached EOF
112 if (vbits == 0) {
113 return -1;
114 }
115 ret = bitbuf << (32 - vbits) >>> (32 - nbits);
116 vbits -= nbits;
117
118 if(vbits < 0) {
119 vbits = 0;
120 }
121
122 return ret;
123 }
124
125 public int read(byte b[]) throws IOException {
126 return read(b, 0, b.length);
127 }
128
129 /**
130 * Reads data from input stream into an byte array.
131 *
132 * @param b the buffer into which the data is read.
133 * @param off the start offset of the data.
134 * @param len the maximum number of bytes read.
135 * @return the total number of bytes read into the buffer, or -1 if the EOF has been reached.
136 * @exception IOException if an I/O error occurs.
137 * @exception NullPointerException if supplied byte array is null
138 */
139 public int read(byte b[], int off, int len) throws IOException {
140 if (len <= 0) {
141 return 0;
142 }
143 int c = read();
144 if (c == -1) {
145 return -1;
146 }
147 b[off] = (byte) c;
148
149 int i = 1;
150 for (; i < len; ++i) {
151 c = read();
152 if (c == -1) {
153 break;
154 }
155 b[off + i] = (byte) c;
156 }
157 return i;
158 }
159
160 /**
161 * empties bit buffer.
162 */
163 public void resetBuffer() {
164 vbits = 0;
165 bitbuf = 0;
166 }
167
168 /**
169 * Skips some bytes from the input stream.
170 * If bit buffer is not empty, n - (vbits + 8) / 8 bytes skipped,
171 * then buffer is resetted and filled with same amount of bits as it has before skipping.
172 * @param n the number of bytes to be skipped.
173 * @return the actual number of bytes skipped.
174 * @exception IOException if an I/O error occurs.
175 */
176 public long skip(long n) throws IOException {
177 if (vbits == 0) {
178 return in.skip(n);
179 }
180 else {
181 int b = (vbits + 7) / 8;
182 in.skip(n - b);
183 int vbits = this.vbits;
184 resetBuffer();
185 fillBuffer(vbits);
186 return n;
187 }
188 }
189
190 /**
191 *
192 * @param n bits to skip
193 * @return number of bits skipped
194 */
195 public int skipBits(int n) throws IOException {
196 int k = n;
197 int nbits = k % 8;
198 read(nbits);
199 k -= nbits;
200 while(k > 0) {
201 try {
202 read(8);
203 k -= 8;
204 }
205 catch(IOException ex) {
206 break;
207 }
208 }
209 return n;
210 }
211
212 public int skipToByteBoundary() throws IOException {
213 int nbits = vbits % 8;
214 read(nbits);
215 return nbits;
216 }
217
218 private void fillBuffer(int nbits) throws IOException {
219 int c;
220 while (vbits < nbits) {
221 c = in.read();
222 if (c == -1) {
223 break;
224 }
225 if(invertBitOrder) {
226 c = flipTable[c] & 0xFF;
227 }
228 bitbuf = (bitbuf << 8) + (c & 0xFF);
229 vbits += 8;
230 }
231 }
232
233 public int getBitOffset() {
234 return 7 - (vbits % 8);
235 }
236
237 public synchronized void mark(int readlimit) {
238 in.mark(readlimit);
239 markBitbuf = bitbuf;
240 markVbits = vbits;
241 }
242
243 public synchronized void reset() throws IOException {
244 in.reset();
245 bitbuf = markBitbuf;
246 vbits = markVbits;
247 }
248
249 static private byte[] flipTable;
250
251 static byte [] getFlipTable() {
252 if(flipTable == null) {
253 createFlipTable();
254 }
255 return flipTable;
256 }
257
258 static private void createFlipTable() {
259 flipTable = new byte[256];
260 for (int i = 0; i < flipTable.length; i++) {
261 int b = 0;
262 for (int j = 0; j < 8; j++) {
263 int k = (i >> j) & 1;
264 b = (b << 1) | k;
265 }
266 flipTable[i] = (byte) b;
267 }
268 }
269 }