Skip to content

Recipes

Jin Kwon edited this page Oct 31, 2022 · 25 revisions

Table of Contents

How can I read/write non-primitive values?

You can use your own Bit(Reader|Writer)<T>.

class User {

    @NotNull String name;

    @Max(127) @PositiveOrZero int age;
}

class UserProcessor implements BitReader<User>, BitWriter<User> {

    @Override
    public User read(BitInput input) throws IOException { // BitReader<User>
        var value = new User();
        value.name = nameReader.read(input);
        value.age = input.readInt(true, 7); // 0 - 127
        return value;
    }

    @Override
    public void write(BitOutput output, User value) throws IOException { // BitWriter<User>
        nameWriter.write(output, value.name);
        output.writeInt(true, 7, value.age);
    }

    BitReader<String> nameReader = StringReader.compressedUtf8();
    BitWriter<String> nameWriter = StringWriter.compressedUtf8();
}

Now you can write/read using the processor.

BitOutput output = ;
output.writeObject(new UserProcessor(), new User("Jane", 19));

BitInput input = ;
var user = input.readObject(new UserProcessor());

assert "Jane".equals(user.name);
assert user.name = 19;

How can I read/write bits from/to (In|Out)putStreams?

Here come how to read bits from a File.

try(InputStream stream = new FileInputStream("file")) {
    BitInput input = BitInputFactory.from(stream);
    // read bits here
    input.align(1);
}

Here comes how to write bits to a (connected) Socket.

try(Socket socket = connect()) {
    BitOutput output = BitOutputFactory.from(socket.getOutputStream());
    // write bits here!
    output.align(1);
}

How can I read/write bits from/to byte[]s?

You can use the ByteArrayInputStream and/or the ByteArrayOutputStream.

How can I read/write byte[] values?

There are a reader and a writer for reading/writing byte[] values.

BitWriter<byte[]> writer = new ByteArrayWriter(Byte.SIZE);
BitOutput output = ;
output.writeObject(writer, new byte[0]);

BitReader<byte[]> reader = new ByteArrayReader(Byte.SIZE);
BitInput input = ;
byte[] value = input.readObject(reader);

How can I read/write bits from/to (Readable|Writable)ByteChannels?

You can use ChannelByte(Input|Output) for reading/writing bits from (Readable|Writable)ByteChannels.

In case you have noticed that the those classes use a ByteBuffer whose capacity is 1, you can make your own Byte(Reader|Writer) which uses a bigger ByteBuffer.

Writing to WritableByteChannel using bigger buffer

Let's start with a channel to write.

try (var channel = openChannelToWrite()) {
}

Now you want to use a buffer whose capacity is greater than 1.

try (var channel = openChannelToWrite()) {
    final var buffer = ByteBuffer.allocate(2); // right?
}

Now you can use the BufferByteOutput whose write(int) method is overridden to write the buffer's content to the channel when it gets full.

try (var channel = openChannelToWrite()) {
    final var buffer = ByteBuffer.allocate(2);
    final var byteOutput = new BufferByteOutput(buffer) {
        @Override
        public void write(final int value) throws IOException {
            if (!buffer.hasRemaining()) { // the buffer is full
                for (buffer.flip(); buffer.position() == 0; ) { // single space is enough
                    channel.write(buffer);
                }
                buffer.compact();
            }
            super.write(value); // put next octet to the buffer
        }
    };
}

Now you can write bits to a BitOutput which writes bytes to the byteOutput we just created.

    final var bitOutput = new ByteOutputAdapter(byteOutput);
    // write some bits here!
    final var padded = bitOutput.align(1); // pads zero bits to align 8-bits

And here comes the most import part of draining all remaining bytes in the buffer to the channel.

    for (buffer.flip(); buffer.hasRemaining(); ) {
        channel.write(buffer);
    }

Reading from ReadableByteChannel using bigger buffer

Using bigger buffer for reading bits has same manner with that of writing.

try (var channel = openChannelToRead()) {
    final var buffer = ByteBuffer.allocate(2);
    buffer.position(buffer.capacity()); // important! the buffer needs to be filled first!
    final var byteInput = new BufferByteInput(buffer) {
        @Override
        public int read() throws IOException {
            if (!buffer.hasRemaining()) {
                for (buffer.clear(); buffer.position() == 0; ) { // single byte should be enough
                    if (channel.read(buffer) == -1) {
                        throw new EOFException("reached to an end");
                    }
                }
                buffer.flip();
            }
            return super.read(); // get next octet from the buffer
        }
    };
    final var bitInput = new ByteInputAdapter(byteInput);
    // read some bits here
    final var discarded = bitInput.align(1);
}