Skip to content

Instantly share code, notes, and snippets.

@darrenclark
Created November 9, 2021 14:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save darrenclark/d0a7d9dbe5ebe3dec8ffc3e1f8170b3f to your computer and use it in GitHub Desktop.
Save darrenclark/d0a7d9dbe5ebe3dec8ffc3e1f8170b3f to your computer and use it in GitHub Desktop.
Java Byte Buffers
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