BitPackingOutputStream.java
- package net.morimekta.io;
- import java.io.IOException;
- import java.io.OutputStream;
- /**
- * Output stream that writes individual bits consecutively to the output stream.
- * The bits will be grouped into bytes and each written when completed. The bits
- * themselves will be assigned from most to least significant per byte.
- *
- * <pre>{@code
- * |---|---|---|---|---|---|---|---|
- * | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
- * |---|---|---|---|---|---|---|---|
- * | a | b | c | d | e | f | g | h |
- * | i | j | k | l | m | n | o | p |
- * |---|---|---|---|---|---|---|---|
- * }</pre>
- */
- public class BitPackingOutputStream extends OutputStream {
- private OutputStream out;
- private int pendingBitsCount;
- private int pendingBits;
- private int writtenBits;
- /**
- * Create a bit packing output stream.
- *
- * @param out Output stream to wrote bits to.
- */
- public BitPackingOutputStream(OutputStream out) {
- this.out = out;
- this.pendingBits = 0;
- this.pendingBitsCount = 0;
- this.writtenBits = 0;
- }
- /**
- * Write a number of bits to the stream, and make the next written bits
- * written just after the last bits with no regard to byte border
- * alignment.
- *
- * @param bitCount Number of bits to be written.
- * @param bitData Data for bits to be written.
- * @throws IOException If unable to write to stream.
- * @throws IllegalArgumentException On invalid input.
- */
- public void writeBits(int bitCount, int bitData) throws IOException {
- if (bitCount < 1 || bitCount > 31) {
- throw new IllegalArgumentException("Illegal writing bit count " + bitCount);
- } else if (bitData < 0) {
- throw new IllegalArgumentException("Writing negative bit data: " + bitData);
- }
- if (out == null) {
- throw new IOException("Writing to closed stream");
- }
- if (bitCount + pendingBitsCount <= 8) {
- // just store away bits.
- pendingBits = pendingBits | ((bitData & (0xff >>> (8 - bitCount))) << (8 - (bitCount + pendingBitsCount)));
- pendingBitsCount = pendingBitsCount + bitCount;
- writtenBits = writtenBits + bitCount;
- if (pendingBitsCount == 8) {
- out.write(pendingBits);
- pendingBits = 0;
- pendingBitsCount = 0;
- }
- } else {
- int writeBits = 8 - pendingBitsCount;
- writeBits(writeBits, bitData >>> (bitCount - writeBits));
- writeBits(bitCount - writeBits, bitData);
- }
- }
- /**
- * Force the next bit to be on a byte (octet) boundary.
- * Write all pending bits to the stream as if the remainder was written with 0 bits.
- *
- * @throws IOException If unable to write to stream.
- */
- public void align() throws IOException {
- if (pendingBitsCount > 0) {
- out.write(pendingBits);
- pendingBitsCount = 0;
- pendingBits = 0;
- }
- }
- /**
- * Number of bits written, including those not written to stream
- * yet, as the byte is not completed yet.
- *
- * @return Number of bits written.
- */
- public int getWrittenBits() {
- return writtenBits;
- }
- @Override
- public void write(int octet) throws IOException {
- writeBits(8, octet);
- }
- @Override
- public void flush() throws IOException {
- if (out != null) {
- out.flush();
- }
- }
- @Override
- public void close() throws IOException {
- if (out != null) {
- try {
- if (pendingBitsCount > 0) {
- out.write(pendingBits);
- }
- out.close();
- } finally {
- pendingBitsCount = 0;
- pendingBits = 0;
- out = null;
- }
- }
- }
- }