Rev 3680 |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* 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
);
}
}