A random and sequential accessible sequence of zero or more bytes (octets).
This interface provides an abstract view for one or more primitive byte
arrays (
byte[]) and
ByteBuffer.
Creation of a buffer
It is recommended to create a new buffer using the helper methods in
Unpooled rather than calling an individual implementation's
constructor.
Random Access Indexing
Just like an ordinary primitive byte array,
ByteBuf uses
zero-based indexing.
It means the index of the first byte is always
0 and the index of the last byte is
always
#capacity(). For example, to iterate all bytes of a buffer, you
can do the following, regardless of its internal implementation:
ByteBuf buffer = ...;
for (int i = 0; i < buffer.capacity(); i ++) {
byte b = buffer.getByte(i);
System.out.println((char) b);
}
Sequential Access Indexing
ByteBuf provides two pointer variables to support sequential
read and write operations -
#readerIndex() for a read
operation and
#writerIndex() for a write operation
respectively. The following diagram shows how a buffer is segmented into
three areas by the two pointers:
+-------------------+------------------+------------------+
| discardable bytes | readable bytes | writable bytes |
| | (CONTENT) | |
+-------------------+------------------+------------------+
| | | |
0
Readable bytes (the actual content)
This segment is where the actual data is stored. Any operation whose name
starts with
read or
skip will get or skip the data at the
current
#readerIndex() and increase it by the number of
read bytes. If the argument of the read operation is also a
ByteBuf and no destination index is specified, the specified
buffer's
#writerIndex() is increased together.
If there's not enough content left,
IndexOutOfBoundsException is
raised. The default value of newly allocated, wrapped or copied buffer's
#readerIndex() is
0.
// Iterates the readable bytes of a buffer.
ByteBuf buffer = ...;
while (buffer.isReadable()) {
System.out.println(buffer.readByte());
}
Writable bytes
This segment is a undefined space which needs to be filled. Any operation
whose name starts with
write will write the data at the current
#writerIndex() and increase it by the number of written
bytes. If the argument of the write operation is also a
ByteBuf,
and no source index is specified, the specified buffer's
#readerIndex() is increased together.
If there's not enough writable bytes left,
IndexOutOfBoundsExceptionis raised. The default value of newly allocated buffer's
#writerIndex() is
0. The default value of
wrapped or copied buffer's
#writerIndex() is the
#capacity() of the buffer.
// Fills the writable bytes of a buffer with random integers.
ByteBuf buffer = ...;
while (buffer.maxWritableBytes() >= 4) {
buffer.writeInt(random.nextInt());
}
Discardable bytes
This segment contains the bytes which were read already by a read operation.
Initially, the size of this segment is
0, but its size increases up
to the
#writerIndex() as read operations are executed.
The read bytes can be discarded by calling
#discardReadBytes() to
reclaim unused area as depicted by the following diagram:
BEFORE discardReadBytes()
+-------------------+------------------+------------------+
| discardable bytes | readable bytes | writable bytes |
+-------------------+------------------+------------------+
| | | |
0
Please note that there is no guarantee about the content of writable bytes
after calling
#discardReadBytes(). The writable bytes will not be
moved in most cases and could even be filled with completely different data
depending on the underlying buffer implementation.
Clearing the buffer indexes
You can set both
#readerIndex() and
#writerIndex() to
0 by calling
#clear().
It does not clear the buffer content (e.g. filling with
0) but just
clears the two pointers. Please also note that the semantic of this
operation is different from
ByteBuffer#clear().
BEFORE clear()
+-------------------+------------------+------------------+
| discardable bytes | readable bytes | writable bytes |
+-------------------+------------------+------------------+
| | | |
0
Search operations
For simple single-byte searches, use
#indexOf(int,int,byte) and
#bytesBefore(int,int,byte).
#bytesBefore(byte) is especially useful when you deal with a
NUL-terminated string.
For complicated searches, use
#forEachByte(int,int,ByteProcessor) with a
ByteProcessorimplementation.
Mark and reset
There are two marker indexes in every buffer. One is for storing
#readerIndex() and the other is for storing
#writerIndex(). You can always reposition one of the
two indexes by calling a reset method. It works in a similar fashion to
the mark and reset methods in
InputStream except that there's no
readlimit.
Derived buffers
You can create a view of an existing buffer by calling one of the following methods:
-
#duplicate()
-
#slice()
-
#slice(int,int)
-
#readSlice(int)
-
#retainedDuplicate()
-
#retainedSlice()
-
#retainedSlice(int,int)
-
#readRetainedSlice(int)
A derived buffer will have an independent
#readerIndex(),
#writerIndex() and marker indexes, while it shares
other internal data representation, just like a NIO buffer does.
In case a completely fresh copy of an existing buffer is required, please
call
#copy() method instead.
Non-retained and retained derived buffers
Note that the
#duplicate(),
#slice(),
#slice(int,int) and
#readSlice(int) does NOT
call
#retain() on the returned derived buffer, and thus its reference count will NOT be increased. If you
need to create a derived buffer with increased reference count, consider using
#retainedDuplicate(),
#retainedSlice(),
#retainedSlice(int,int) and
#readRetainedSlice(int) which may return
a buffer implementation that produces less garbage.
Conversion to existing JDK types
Byte array
If a
ByteBuf is backed by a byte array (i.e.
byte[]),
you can access it directly via the
#array() method. To determine
if a buffer is backed by a byte array,
#hasArray() should be used.
NIO Buffers
If a
ByteBuf can be converted into an NIO
ByteBuffer which shares its
content (i.e. view buffer), you can get it via the
#nioBuffer() method. To determine
if a buffer can be converted into an NIO buffer, use
#nioBufferCount().
Strings
Various
#toString(Charset) methods convert a
ByteBufinto a
String. Please note that
#toString() is not a
conversion method.
I/O Streams
Please refer to
ByteBufInputStream and
ByteBufOutputStream.