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 Andrei Kouznetsov 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.FilterOutputStream;
036    import java.io.IOException;
037    import java.io.OutputStream;
038    
039    /**
040     * adds ability to write streams bitewise
041     * @author Andrey Kuznetsov
042     */
043    public class BitOutputStream extends FilterOutputStream {
044    
045        protected static final int[] mask = new int[32];
046    
047        static {
048            for (int i = 0; i < mask.length; i++) {
049                mask[i] =  (1 << (i + 1)) - 1;
050            }
051        }
052    
053        protected int bitbuf;
054        protected int vbits;
055    
056        private int bitsToWrite = 8;
057    
058        protected byte [] flipTable = BitInputStream.getFlipTable();
059    
060        protected boolean invertBitOrder;
061    
062        protected int fillByte = 0;
063    
064        public BitOutputStream(OutputStream out) {
065            super(out);
066        }
067    
068        public int getBitsToWrite() {
069            return bitsToWrite;
070        }
071    
072        /**
073         * set how much bits should be written to stream every write() call
074         * @param bitsToWrite
075         */
076        public void setBitsToWrite(int bitsToWrite) {
077            this.bitsToWrite = bitsToWrite;
078        }
079    
080        public boolean isInvertBitOrder() {
081            return invertBitOrder;
082        }
083    
084        public void setInvertBitOrder(boolean invertBitOrder) {
085            this.invertBitOrder = invertBitOrder;
086        }
087    
088        /**
089         * Writes some bits from the specified int to stream.
090         * @param b int which should be written
091         * @throws IOException if an I/O error occurs
092         * @see #setBitsToWrite
093         * @see #getBitsToWrite
094         */
095        public void write(int b) throws IOException {
096            write(b, bitsToWrite);
097        }
098    
099        /**
100         * Writes some bits from the specified int to stream.
101         * @param b int which should be written
102         * @param nbits bit count to write
103         * @throws IOException if an I/O error occurs
104         */
105        public void write(int b, int nbits) throws IOException {
106            if (nbits == 0) {
107                return;
108            }
109            final int k = b & mask[nbits];
110            bitbuf = (bitbuf << nbits) | k;
111            vbits += nbits;
112    
113            write8();
114        }
115    
116        protected void write8() throws IOException {
117            while (vbits > 8) {
118                int c = (int) (bitbuf << (32 - vbits) >>> 24);
119                vbits -= 8;
120                if(invertBitOrder) {
121                    c = flipTable[c] & 0xFF;
122                }
123                out.write(c);
124            }
125        }
126    
127        /**
128         * get fill byte used to adjust stream to byte boundary.
129         * @return int
130         */
131        public int getFillByte() {
132            return fillByte;
133        }
134    
135        /**
136         * set fill byte used to adjust stream to byte boundary
137         * @param fillByte int
138         */
139        public void setFillByte(int fillByte) {
140            this.fillByte = fillByte & 0xFF;
141        }
142    
143        /**
144         * writes bits from buffer to output stream
145         * @throws IOException if I/O error occurs
146         */
147        public void flush() throws IOException {
148            write8();   //rather not needed, just to ensure
149            if(vbits > 0) {
150                write(fillByte, 8);
151            }
152            vbits = 0;
153            bitbuf = 0;
154            out.flush();
155        }
156    }