| `ByteBuffer` provides a view of an underlying bytes storage. The two most common | |
| implementations are the non-direct byte buffers, which are backed by a bytes | |
| array, and direct byte buffers, which usually are off-heap directly mapped to | |
| memory. Thus, not all `ByteBuffer` implementations are backed by a bytes array, | |
| and when they are, the beginning of the array returned by `.array()` may not | |
| necessarily correspond to the beginning of the `ByteBuffer`. | |
| Since it's so finicky, **use of `.array()` is discouraged. Use `.get(...)` to | |
| copy the underlying data instead.** | |
| But, if you *absolutely must* use `.array()` to look behind the `ByteBuffer` | |
| curtain, check all of the following: | |
| * Ensure there is a backing array with `.hasArray()` | |
| * Check the start position of the backing array by calling `.arrayOffset()` | |
| * Check the end position of the backing array with `.remaining()` | |
| If you know that the `ByteBuffer` was created locally with | |
| `ByteBuffer.wrap(...)` or `ByteBuffer.allocate(...)`, it's safe, you can use the | |
| backing array normally, without checking the above. | |
| Do this: | |
| ```java | |
| // Use `.get(...)` to copy the byte[] without changing the current position. | |
| public void foo(ByteBuffer buffer) throws Exception { | |
| byte[] bytes = new byte[buffer.remaining()]; | |
| buffer.get(bytes); | |
| buffer.position(buffer.position() - bytes.length); // Restores the buffer position | |
| // ... | |
| } | |
| ``` | |
| or this: | |
| ```java | |
| // Use `.array()` only if you also check `.hasArray()`, `.arrayOffset()`, and `.remaining()`. | |
| public void foo(ByteBuffer buffer) throws Exception { | |
| if (buffer.hasArray()) { | |
| int startIndex = buffer.arrayOffset(); | |
| int curIndex = buffer.arrayOffset() + buffer.position(); | |
| int endIndex = curIndex + buffer.remaining(); | |
| // Access elements of `.array()` with the above indices ... | |
| } | |
| } | |
| ``` | |
| or this: | |
| ```java | |
| // No checking necessary when the buffer was constructed locally with `allocate(...)`. | |
| public void foo() throws Exception { | |
| ByteBuffer buffer = ByteBuffer.allocate(Long.SIZE / Byte.SIZE); | |
| buffer.putLong(1L); | |
| // ... | |
| buffer.array(); | |
| // ... | |
| } | |
| ``` | |
| or this: | |
| ```java | |
| // No checking necessary when the buffer was constructed locally with `wrap(...)`. | |
| public void foo(byte[] bytes) throws Exception { | |
| ByteBuffer buffer = ByteBuffer.wrap(bytes); | |
| // ... | |
| buffer.array(); | |
| // ... | |
| } | |
| ``` | |
| Not this: | |
| ```java | |
| public void foo(ByteBuffer buffer) { | |
| byte[] dataAsBytesArray = buffer.array(); | |
| // ... | |
| } | |
| ``` |