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