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    }