/*
* Copyright (C) 2006 Steve Ratcliffe
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* Author: Steve Ratcliffe
* Create date: 14-Dec-2006
*/
package uk.me.parabola.imgfmt.app;
import uk.me.parabola.log.Logger;
/**
* A class to write the bitstream.
*
* @author Steve Ratcliffe
*/
public class BitWriter {
private static final Logger log = Logger.getLogger(BitWriter.class);
// Choose so that most roads will not fill it.
private static final int INITIAL_BUF_SIZE = 20;
// The byte buffer and its current length (allocated length)
private byte[] buf; // The buffer
private int bufsize; // The allocated size
private int buflen; // The actual used length
// The bit offset into the byte array.
private int bitoff;
private static final int BUFSIZE_INC = 50;
public BitWriter() {
bufsize = INITIAL_BUF_SIZE;
buf = new byte[bufsize];
}
/**
* Put exactly one bit into the buffer.
*
* @param b The bottom bit of the integer is set at the current bit position.
*/
private void put1(int b) {
ensureSize(bitoff + 1);
int off = getByteOffset(bitoff);
// Get the remaining bits into the byte.
int rem = bitoff - 8 * off;
// Or it in, we are assuming that the position is never turned back.
buf[off] |= (b & 0x1) << rem;
// Increment position
bitoff++;
// If we are in a new byte, increase the byte length.
if ((bitoff & 0x7) == 1)
buflen++;
debugPrint(b, 1);
}
public void put1(boolean b) {
put1(b ? 1 : 0);
}
/**
* Put a number of bits into the buffer, growing it if necessary.
*
* @param bval The bits to add, the lowest <b>n</b> bits will be added to
* the buffer.
* @param nb The number of bits.
*/
public void putn(int bval, int nb) {
int val = bval & ((1<<nb) - 1);
int n = nb;
// We need to be able to deal with more than 24 bits, but now we can't yet
if (n >= 24)
throw new IllegalArgumentException();
ensureSize(bitoff + n);
// Get each affected byte and set bits into it until we are done.
while (n > 0) {
int ind = getByteOffset(bitoff);
int rem = bitoff - 8*ind;
buf[ind] |= ((val << rem) & 0xff);
// Shift down in preparation for next byte.
val >>>= 8-rem;
// Account for change so far
int nput = 8 - rem;
if (nput > n)
nput = n;
bitoff += nput;
n -= nput;
}
buflen = (bitoff+7)/8;
}
/**
* Write a signed value. If the value doesn't fit into nb bits, write one or more 1 << (nb-1)
* as a flag for extended range.
*/
public void sputn(int bval, int nb) {
int top = 1 << (nb - 1);
int mask = top - 1;
int val = Math.abs(bval);
while (val > mask) {
putn(top, nb);
val -= mask;
}
if (bval < 0) {
putn((top - val) | top, nb);
} else {
putn(val, nb);
}
}
public byte[] getBytes() {
return buf;
}
public int getBitPosition() {
return bitoff;
}
/**
* Get the number of bytes actually used to hold the bit stream. This therefore can be and usually
* is less than the length of the buffer returned by {@link #getBytes()}.
* @return Number of bytes required to hold the output.
*/
public int getLength() {
return buflen;
}
/**
* Get the byte offset for the given bit number.
*
* @param boff The number of the bit in question.
* @return The index into the byte array where the bit resides.
*/
private int getByteOffset(int boff) {
return boff/8;
}
/**
* Set everything up so that the given size can be accommodated.
* The buffer is re-sized if necessary.
*
* @param newlen The new length of the bit buffer in bits.
*/
private void ensureSize(int newlen) {
if (newlen/8 >= bufsize)
reallocBuffer();
}
/**
* Reallocate the byte buffer.
*/
private void reallocBuffer() {
log.debug("reallocating buffer");
bufsize += BUFSIZE_INC;
byte[] newbuf = new byte[bufsize];
System.arraycopy(this.buf, 0, newbuf, 0, this.buf.length);
this.buf = newbuf;
}
private void debugPrint(int b, int i) {
if (log.isDebugEnabled())
log.debug("after put", i, "of", b, " bufsize=", bufsize, ", len=",
buflen, ", pos=", bitoff);
}
}