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 }