やるしかなっちゃん

やるしかない!

Javaでのビットの入出力(改善版)

前回(Javaでのビットの入出力 - やるしかなっちゃん)のプログラムは作ってて、何か違うよな感が拭えなかったので改善しました。
I/OでひとまとめにしないでそれぞれをBitInputStreamとBitOutputStreamにするようにしました。
また、Input/OutputStreamを継承ではなくてコンポジションしてます。
JavaI/OはDecoratorパターンが用いられてて、継承のビックウェーブが来てるような気がしましたが、コンポジっちゃいました。
あと、エラー処理の所とかふざけてたので訂正しました

public class BitInputStream {
    private InputStream input;
    private byte bitBuffer;
    private byte bufferPosition;
    private boolean isEOF;

    public BitInputStream(InputStream input) {
        if (input == null) {
            throw new NullPointerException("InputStream is null.");
        }
        this.input = input;
        this.bitBuffer = 0;
        this.bufferPosition = -1;
        this.isEOF = false;
    }

    private void bufferBits() throws IOException {
        if (bufferPosition == -1) {
            bufferPosition = 7;
            bitBuffer = (byte)input.read();
        }
        if (bitBuffer == -1) {
            isEOF = true;
        }
    }

    /**
     * Return 0 or 1 or -1(EOF).
     * @throws IOException
     */
    public int read() throws IOException {
        bufferBits();
        if (isEOF) {
            return -1;
        }
        int mask = 1;
        mask <<= bufferPosition--;
        if ((bitBuffer & mask) == 0) {
            return 0;
        } else {
            return 1;
        }
    }

    /**
     * Close this stream.
     * @throws IOException
     */
    public void close() throws IOException {
        input.close();
    }
}
public class BitOutputStream {
    private OutputStream output;
    private byte[] bitBufferArray;
    private int bufferIndex;

    public BitOutputStream(OutputStream output) {
        if (output == null) {
            throw new NullPointerException("OutputStream is null.");
        }
        this.output = output;
        this.bitBufferArray = new byte[8];
        bufferIndex = 0;
    }

    private byte bufferBits(byte bit, byte bitBuf, byte bufPos) {
        bit = (byte)(bit << bufPos);
        bitBuf = (byte)(bitBuf | bit);
        return bitBuf;
    }

    private void flushBits(byte bitBuf) throws IOException {
        output.write(bitBuf);
    }

    /**
     * Write 8bit to stream.
     * @param bits BitArray(Length must be 8 or less)
     * @throws IOException
     */
    private void write(byte[] bits) throws IOException {
        if (bits.length > 8)
            throw new IllegalArgumentException("Length of Byte[] must be 8 or less");
        byte bitBuf = 0, bufPos = 0;
        for (int i = bits.length - 1; i >= 0; i--) {
            if (!(bits[i] == 0 || bits[i] == 1))
                throw new IllegalArgumentException("Elements of Byte[] must be 0 or 1");
            bitBuf = bufferBits(bits[i], bitBuf, bufPos);
            bufPos++;
        }
        flushBits(bitBuf);
    }

    /**
     * Write 1bit to stream.
     * @param bit Bit
     * @throws IOException
     */
    public void write(int bit) throws IOException {
        if (!(bit == 0 || bit == 1)) {
            throw new IllegalArgumentException("Argument must be 0 or 1");
        }
        if (bufferIndex == 7) {
            bitBufferArray[bufferIndex] = (byte)bit;
            write(bitBufferArray);
            bufferIndex = 0;
        } else {
            bitBufferArray[bufferIndex++] = (byte)bit;
        }
    }

    /**
     * Close this stream.
     * @throws IOException
     */
    public void close() throws IOException {
        if (bufferIndex != 0) {
            for (int i = bufferIndex; i < bitBufferArray.length; i++) {
                bitBufferArray[i] = 0; // 足りない分は0で埋める
            }
            write(bitBufferArray);
        }
        output.close();
    }
}

だいぶ見やすくなった気がしますね。糞から糞コードにはなれた気がします。
特にread()なんかはリストから配列への変換とか糞みたいなことをしていた部分をガッツリ削ることができました。
あの部分は特に作ってて違和感しかなかったので削れてよかったです。
あと、配列単位で色々とやっていたのを基本int型で0と1をやりとりするようにしました。
これで結構便利になったはずです。