Created
November 9, 2021 14:33
-
-
Save darrenclark/d0a7d9dbe5ebe3dec8ffc3e1f8170b3f to your computer and use it in GitHub Desktop.
Java Byte Buffers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.nio.ByteBuffer; | |
// is a byte[] under the hood | |
ByteBuffer buffer1 = ByteBuffer.allocate(12); | |
// is a block of bytes (similar to malloc) | |
ByteBuffer buffer2 = ByteBuffer.allocateDirect(12); | |
// buffers have a "position" that is advanced by read/write operations | |
buffer1.put((byte)1).put((byte)2).put((byte)3).put((byte)4); | |
buffer1.position(); // 4 because we wrote 4 bytes | |
// buffers also have a "limit" and "capacity" | |
// capacity is how big the buffer is (specified at creation time) | |
buffer1.capacity(); | |
// limit is an adjustable end of the buffer (must be <= capacity) | |
buffer1.limit(); | |
// to read the data we wrote, we can use flip(), it: | |
// - sets limit to the current position | |
// - sets the position back to 0 | |
buffer1.flip() | |
buffer1.limit(); // 4, because the old position was 4 | |
buffer1.position() // 0 | |
// copy the data we wrote into a byte[] | |
byte[] data = new byte[buffer1.limit()]; | |
buffer1.get(data); | |
// reading causes the position to be advanced by how much was read | |
buffer1.position(); | |
// to read again, we can rewind (set position = 0) the buffer. (limit is unchanged) | |
buffer1.rewind(); | |
// to use the buffer for writing again, we can use clear() | |
// - sets position = 0 | |
// - sets limit = capacity | |
// - (doesn't modify any of the contents) | |
buffer1.clear(); | |
// to write other kinds of data, we can use re-interpret the buffer as another type | |
buffer1.asLongBuffer().put(0L); | |
buffer1.asIntBuffer().put(0); | |
// asLongBuffer() and asIntBuffer() return new buffers (using the SAME UNDERLYING MEMORY), | |
// so: | |
// - the position / limit / capacity of buffer1 HAVE NOT changed | |
// - the contents HAVE changed | |
buffer1.position(); // still 0 | |
buffer1.get(data); // all 0s because of buffer1.asLongBuffer().put(0L) | |
// similar to re-interpreting the underlying buffer, we can use slice() to get | |
// additional views into the SAME UNDERLYING MEMORY | |
// slice() takes a slice starting at "position" and going to the "limit" of the buffer | |
// the capacity of the slice is set to the limit | |
buffer1.slice() // position = 0, limit = 8, capacity = 8 | |
.asLongBuffer().put(123L) // writes a long | |
buffer1.asLongBuffer().get() // 123L, since the `slice()` shares the same underlying memory | |
// mark() and reset() can be used to save a position & return to it | |
buffer1.position(5); | |
buffer1.mark(); // sets mark to 5 | |
buffer1.get(); // advanced position to 6 | |
buffer1.reset(); | |
buffer1.position(); // 5 because we called reset() | |
// hasRemaining() and remaining() can be used to see how much more room is available | |
// remaining() is limit - position | |
buffer1.remaining(); // 7 because: 12 (limit) - 5 (position) = 7 | |
// hasRemaining() is remaining() > 0 | |
buffer1.hasRemaining(); // true | |
// to create a full copy of a byte buffer, we need to allocate a new one & copy it | |
buffer1.rewind(); // position = 0 | |
ByteBuffer buffer1Copy = ByteBuffer.allocate(buffer1.remaining()); | |
buffer1Copy.put(buffer1); // copy contents of buffer1 into the copy | |
buffer1.position() // position in buffer1 was advanced by 12 | |
// similarly, to expand a buffer, we need to allocate a new one & copy the original in | |
buffer1.rewind(); | |
ByteBuffer buffer1Expanded = ByteBuffer.allocate(32); | |
buffer1Expanded.put(buffer1); | |
buffer1Expanded.remaining(); // 20 because: 32 (limit of new buffer) - 12 (position, since we copied 12 bytes from buffer1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment