/* * Copyright 2021 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package io.netty.buffer.api.tests; import io.netty.buffer.api.Buffer; import io.netty.buffer.api.BufferAllocator; import io.netty.buffer.api.BufferClosedException; import io.netty.buffer.api.CompositeBuffer; import io.netty.buffer.api.internal.ResourceSupport; import io.netty.util.internal.EmptyArrays; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import java.util.concurrent.Future; import java.util.concurrent.ThreadLocalRandom; import java.util.function.Supplier; import static io.netty.buffer.api.internal.Statics.acquire; import static io.netty.buffer.api.internal.Statics.isOwned; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; public class BufferLifeCycleTest extends BufferTestSupport { @ParameterizedTest @MethodSource("allocators") void allocateAndAccessingBuffer(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { buf.writeByte((byte) 1); buf.writeByte((byte) 2); try (Buffer inner = acquire((ResourceSupport) buf)) { inner.writeByte((byte) 3); inner.writeByte((byte) 4); inner.writeByte((byte) 5); inner.writeByte((byte) 6); inner.writeByte((byte) 7); inner.writeByte((byte) 8); var re = assertThrows(RuntimeException.class, () -> inner.writeByte((byte) 9)); assertThat(re).hasMessageContaining("bound"); re = assertThrows(RuntimeException.class, () -> inner.writeByte((byte) 9)); assertThat(re).hasMessageContaining("bound"); re = assertThrows(RuntimeException.class, () -> buf.writeByte((byte) 9)); assertThat(re).hasMessageContaining("bound"); } assertEquals((byte) 1, buf.readByte()); assertEquals((byte) 2, buf.readByte()); assertEquals((byte) 3, buf.readByte()); assertEquals((byte) 4, buf.readByte()); assertEquals((byte) 5, buf.readByte()); assertEquals((byte) 6, buf.readByte()); assertEquals((byte) 7, buf.readByte()); assertEquals((byte) 8, buf.readByte()); var re = assertThrows(RuntimeException.class, buf::readByte); assertThat(re).hasMessageContaining("bound"); assertThat(toByteArray(buf)).containsExactly(1, 2, 3, 4, 5, 6, 7, 8); } } @ParameterizedTest @MethodSource("initialCombinations") public void allocatingZeroSizedBuffer(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator()) { Supplier supplier = allocator.constBufferSupplier(EmptyArrays.EMPTY_BYTES); try (Buffer empty = supplier.get()) { assertThat(empty.capacity()).isZero(); assertTrue(empty.readOnly()); } try (Buffer empty = allocator.allocate(0)) { assertThat(empty.capacity()).isZero(); empty.ensureWritable(8); assertThat(empty.capacity()).isGreaterThanOrEqualTo(8); } } } @ParameterizedTest @MethodSource("allocators") void acquireOnClosedBufferMustThrow(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator()) { var buf = allocator.allocate(8); buf.close(); assertThrows(BufferClosedException.class, () -> acquire((ResourceSupport) buf)); } } @ParameterizedTest @MethodSource("allocators") public void bufferShouldNotBeAccessibleAfterClose(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator()) { Buffer buf = allocator.allocate(24); buf.writeLong(42); buf.close(); verifyInaccessible(buf); } } @ParameterizedTest @MethodSource("allocators") public void bufferMustNotBeThreadConfined(Fixture fixture) throws Exception { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { buf.writeInt(42); Future fut = executor.submit(() -> buf.readInt()); assertEquals(42, fut.get()); fut = executor.submit(() -> { buf.writeInt(32); return buf.readInt(); }); assertEquals(32, fut.get()); } } @ParameterizedTest @MethodSource("allocators") void copyWithoutOffsetAndSizeMustReturnReadableRegion(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { for (byte b : new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }) { buf.writeByte(b); } assertEquals(0x01, buf.readByte()); buf.writerOffset(buf.writerOffset() - 1); try (Buffer copy = buf.copy()) { assertThat(toByteArray(copy)).containsExactly(0x02, 0x03, 0x04, 0x05, 0x06, 0x07); assertEquals(0, copy.readerOffset()); assertEquals(6, copy.readableBytes()); assertEquals(6, copy.writerOffset()); assertEquals(6, copy.capacity()); assertEquals(0x02, copy.readByte()); assertEquals(0x03, copy.readByte()); assertEquals(0x04, copy.readByte()); assertEquals(0x05, copy.readByte()); assertEquals(0x06, copy.readByte()); assertEquals(0x07, copy.readByte()); assertThrows(IndexOutOfBoundsException.class, copy::readByte); } } } @ParameterizedTest @MethodSource("allocators") void copyWithOffsetAndSizeMustReturnGivenRegion(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { for (byte b : new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }) { buf.writeByte(b); } buf.readerOffset(3); // Reader and writer offsets must be ignored. buf.writerOffset(6); try (Buffer copy = buf.copy(1, 6)) { assertThat(toByteArray(copy)).containsExactly(0x02, 0x03, 0x04, 0x05, 0x06, 0x07); assertEquals(0, copy.readerOffset()); assertEquals(6, copy.readableBytes()); assertEquals(6, copy.writerOffset()); assertEquals(6, copy.capacity()); assertEquals(0x02, copy.readByte()); assertEquals(0x03, copy.readByte()); assertEquals(0x04, copy.readByte()); assertEquals(0x05, copy.readByte()); assertEquals(0x06, copy.readByte()); assertEquals(0x07, copy.readByte()); assertThrows(IndexOutOfBoundsException.class, copy::readByte); } } } @ParameterizedTest @MethodSource("allocators") void copyWithoutOffsetAndSizeMustNotInfluenceOwnership(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { try (Buffer copy = buf.copy()) { assertTrue(isOwned((ResourceSupport) buf)); assertTrue(isOwned((ResourceSupport) copy)); copy.send().close(); } assertTrue(isOwned((ResourceSupport) buf)); buf.send().close(); } } @ParameterizedTest @MethodSource("allocators") void copyWithOffsetAndSizeMustNotInfluenceOwnership(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { try (Buffer copy = buf.copy(0, 8)) { assertTrue(isOwned((ResourceSupport) buf)); assertTrue(isOwned((ResourceSupport) copy)); copy.send().close(); } assertTrue(isOwned((ResourceSupport) buf)); buf.send().close(); } } @ParameterizedTest @MethodSource("allocators") void copyWithoutOffsetAndSizeHasSameEndianAsParent(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { buf.writeLong(0x0102030405060708L); try (Buffer copy = buf.copy()) { assertEquals(0x0102030405060708L, copy.readLong()); } } } @ParameterizedTest @MethodSource("allocators") void copyWithOffsetAndSizeHasSameEndianAsParent(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { buf.writeLong(0x0102030405060708L); try (Buffer copy = buf.copy(0, 8)) { assertEquals(0x0102030405060708L, copy.readLong()); } } } @ParameterizedTest @MethodSource("allocators") void sendOnCopyWithoutOffsetAndSizeMustNotThrow(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { try (Buffer copy = buf.copy()) { assertTrue(isOwned((ResourceSupport) buf)); copy.send().close(); } // Verify that the copy is closed properly afterwards. assertTrue(isOwned((ResourceSupport) buf)); buf.send().receive().close(); } } @ParameterizedTest @MethodSource("allocators") void sendOnCopyWithOffsetAndSizeMustThrow(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { try (Buffer copy = buf.copy(0, 8)) { assertTrue(isOwned((ResourceSupport) buf)); copy.send().close(); } // Verify that the copy is closed properly afterwards. assertTrue(isOwned((ResourceSupport) buf)); } } @ParameterizedTest @MethodSource("allocators") void copyWithNegativeOffsetMustThrow(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { assertThrows(IndexOutOfBoundsException.class, () -> buf.copy(-1, 1)); // Verify that the copy is closed properly afterwards. assertTrue(isOwned((ResourceSupport) buf)); } } @ParameterizedTest @MethodSource("allocators") void copyWithNegativeSizeMustThrow(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { assertThrows(IllegalArgumentException.class, () -> buf.copy(0, -1)); assertThrows(IllegalArgumentException.class, () -> buf.copy(2, -1)); // Verify that the copy is closed properly afterwards. assertTrue(isOwned((ResourceSupport) buf)); } } @ParameterizedTest @MethodSource("allocators") void copyWithSizeGreaterThanCapacityMustThrow(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { assertThrows(IndexOutOfBoundsException.class, () -> buf.copy(0, 9)); buf.copy(0, 8).close(); // This is still fine. assertThrows(IndexOutOfBoundsException.class, () -> buf.copy(1, 8)); // Verify that the copy is closed properly afterwards. assertTrue(isOwned((ResourceSupport) buf)); } } @ParameterizedTest @MethodSource("allocators") void copyWithZeroSizeMustBeAllowed(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { buf.copy(0, 0).close(); // This is fine. // Verify that the copy is closed properly afterwards. assertTrue(isOwned((ResourceSupport) buf)); } } @ParameterizedTest @MethodSource("allocators") void copyMustBeOwned(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator()) { Buffer buf = allocator.allocate(8); buf.writeInt(42); try (Buffer copy = buf.copy()) { assertTrue(isOwned((ResourceSupport) copy)); assertTrue(isOwned((ResourceSupport) buf)); buf.close(); assertFalse(buf.isAccessible()); assertTrue(isOwned((ResourceSupport) copy)); try (Buffer receive = copy.send().receive()) { assertTrue(isOwned((ResourceSupport) receive)); assertFalse(copy.isAccessible()); } } } } @ParameterizedTest @MethodSource("allocators") public void copyOfLastByte(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8).writeLong(0x0102030405060708L); Buffer copy = buf.copy(7, 1)) { assertThat(copy.capacity()).isOne(); assertEquals((byte) 0x08, copy.readByte()); } } @ParameterizedTest @MethodSource("allocators") public void pooledBuffersMustResetStateBeforeReuse(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer expected = allocator.allocate(8)) { for (int i = 0; i < 10; i++) { try (Buffer buf = allocator.allocate(8)) { assertEquals(expected.capacity(), buf.capacity()); assertEquals(expected.readableBytes(), buf.readableBytes()); assertEquals(expected.readerOffset(), buf.readerOffset()); assertEquals(expected.writableBytes(), buf.writableBytes()); assertEquals(expected.writerOffset(), buf.writerOffset()); byte[] bytes = new byte[8]; buf.copyInto(0, bytes, 0, 8); assertThat(bytes).containsExactly(0, 0, 0, 0, 0, 0, 0, 0); var tlr = ThreadLocalRandom.current(); for (int j = 0; j < tlr.nextInt(0, 8); j++) { buf.writeByte((byte) 1); } if (buf.readableBytes() > 0) { for (int j = 0; j < tlr.nextInt(0, buf.readableBytes()); j++) { buf.readByte(); } } } } } } @ParameterizedTest @MethodSource("allocators") public void splitWithNegativeOffsetMustThrow(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { buf.split(0).close(); assertThrows(IllegalArgumentException.class, () -> buf.split(-1)); } } @ParameterizedTest @MethodSource("allocators") public void splitWithOversizedOffsetMustThrow(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { assertThrows(IllegalArgumentException.class, () -> buf.split(9)); buf.split(8).close(); } } @ParameterizedTest @MethodSource("allocators") public void splitOfNonOwnedBufferMustThrow(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { buf.writeInt(1); try (Buffer acquired = acquire((ResourceSupport) buf)) { var exc = assertThrows(IllegalStateException.class, () -> acquired.split()); assertThat(exc).hasMessageContaining("owned"); } } } @ParameterizedTest @MethodSource("allocators") public void splitOnOffsetOfNonOwnedBufferMustThrow(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { try (Buffer acquired = acquire((ResourceSupport) buf)) { var exc = assertThrows(IllegalStateException.class, () -> acquired.split(4)); assertThat(exc).hasMessageContaining("owned"); } } } @ParameterizedTest @MethodSource("allocators") public void splitOnOffsetMustTruncateGreaterOffsets(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { buf.writeInt(0x01020304); buf.writeByte((byte) 0x05); buf.readInt(); try (Buffer split = buf.split(2)) { assertThat(buf.readerOffset()).isEqualTo(2); assertThat(buf.writerOffset()).isEqualTo(3); assertThat(split.readerOffset()).isEqualTo(2); assertThat(split.writerOffset()).isEqualTo(2); } } } @ParameterizedTest @MethodSource("allocators") public void splitOnOffsetMustExtendLesserOffsets(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { buf.writeInt(0x01020304); buf.readInt(); try (Buffer split = buf.split(6)) { assertThat(buf.readerOffset()).isEqualTo(0); assertThat(buf.writerOffset()).isEqualTo(0); assertThat(split.readerOffset()).isEqualTo(4); assertThat(split.writerOffset()).isEqualTo(4); } } } @ParameterizedTest @MethodSource("allocators") public void splitPartMustContainFirstHalfOfBuffer(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(16)) { buf.writeLong(0x0102030405060708L); assertThat(buf.readByte()).isEqualTo((byte) 0x01); try (Buffer split = buf.split()) { // Original buffer: assertThat(buf.capacity()).isEqualTo(8); assertThat(buf.readerOffset()).isZero(); assertThat(buf.writerOffset()).isZero(); assertThat(buf.readableBytes()).isZero(); assertThrows(IndexOutOfBoundsException.class, () -> buf.readByte()); // Split part: assertThat(split.capacity()).isEqualTo(8); assertThat(split.readerOffset()).isOne(); assertThat(split.writerOffset()).isEqualTo(8); assertThat(split.readableBytes()).isEqualTo(7); assertThat(split.readByte()).isEqualTo((byte) 0x02); assertThat(split.readInt()).isEqualTo(0x03040506); assertThat(split.readByte()).isEqualTo((byte) 0x07); assertThat(split.readByte()).isEqualTo((byte) 0x08); assertThrows(IndexOutOfBoundsException.class, () -> split.readByte()); } // Split part does NOT return when closed: assertThat(buf.capacity()).isEqualTo(8); assertThat(buf.readerOffset()).isZero(); assertThat(buf.writerOffset()).isZero(); assertThat(buf.readableBytes()).isZero(); assertThrows(IndexOutOfBoundsException.class, () -> buf.readByte()); } } @ParameterizedTest @MethodSource("allocators") public void splitPartsMustBeIndividuallySendable(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(16)) { buf.writeLong(0x0102030405060708L); assertThat(buf.readByte()).isEqualTo((byte) 0x01); try (Buffer sentSplit = buf.split().send().receive()) { try (Buffer sentBuf = buf.send().receive()) { assertThat(sentBuf.capacity()).isEqualTo(8); assertThat(sentBuf.readerOffset()).isZero(); assertThat(sentBuf.writerOffset()).isZero(); assertThat(sentBuf.readableBytes()).isZero(); assertThrows(IndexOutOfBoundsException.class, () -> sentBuf.readByte()); } assertThat(sentSplit.capacity()).isEqualTo(8); assertThat(sentSplit.readerOffset()).isOne(); assertThat(sentSplit.writerOffset()).isEqualTo(8); assertThat(sentSplit.readableBytes()).isEqualTo(7); assertThat(sentSplit.readByte()).isEqualTo((byte) 0x02); assertThat(sentSplit.readInt()).isEqualTo(0x03040506); assertThat(sentSplit.readByte()).isEqualTo((byte) 0x07); assertThat(sentSplit.readByte()).isEqualTo((byte) 0x08); assertThrows(IndexOutOfBoundsException.class, () -> sentSplit.readByte()); } } } @ParameterizedTest @MethodSource("allocators") public void mustBePossibleToSplitMoreThanOnce(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(16)) { buf.writeLong(0x0102030405060708L); try (Buffer a = buf.split()) { a.writerOffset(4); try (Buffer b = a.split()) { assertEquals(0x01020304, b.readInt()); a.writerOffset(4); assertEquals(0x05060708, a.readInt()); assertThrows(IndexOutOfBoundsException.class, () -> b.readByte()); assertThrows(IndexOutOfBoundsException.class, () -> a.readByte()); buf.writeLong(0xA1A2A3A4A5A6A7A8L); buf.writerOffset(4); try (Buffer c = buf.split()) { assertEquals(0xA1A2A3A4, c.readInt()); buf.writerOffset(4); assertEquals(0xA5A6A7A8, buf.readInt()); assertThrows(IndexOutOfBoundsException.class, () -> c.readByte()); assertThrows(IndexOutOfBoundsException.class, () -> buf.readByte()); } } } } } @ParameterizedTest @MethodSource("allocators") public void mustBePossibleToSplitCopies(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator()) { Buffer buf = allocator.allocate(16); buf.writeLong(0x0102030405060708L); try (Buffer copy = buf.copy()) { buf.close(); assertTrue(isOwned((ResourceSupport) copy)); try (Buffer split = copy.split(4)) { split.resetOffsets().ensureWritable(Long.BYTES); copy.resetOffsets().ensureWritable(Long.BYTES); assertThat(split.capacity()).isEqualTo(Long.BYTES); assertThat(copy.capacity()).isEqualTo(Long.BYTES); assertEquals(0x01020304, split.getInt(0)); assertEquals(0x05060708, copy.getInt(0)); } } } } @ParameterizedTest @MethodSource("allocators") public void ensureWritableOnSplitBuffers(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { buf.writeLong(0x0102030405060708L); try (Buffer a = buf.split()) { assertEquals(0x0102030405060708L, a.readLong()); a.ensureWritable(8); a.writeLong(0xA1A2A3A4A5A6A7A8L); assertEquals(0xA1A2A3A4A5A6A7A8L, a.readLong()); buf.ensureWritable(8); buf.writeLong(0xA1A2A3A4A5A6A7A8L); assertEquals(0xA1A2A3A4A5A6A7A8L, buf.readLong()); } } } @ParameterizedTest @MethodSource("allocators") public void ensureWritableOnSplitBuffersWithOddOffsets(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(10)) { buf.writeLong(0x0102030405060708L); buf.writeByte((byte) 0x09); buf.readByte(); try (Buffer a = buf.split()) { assertEquals(0x0203040506070809L, a.readLong()); a.ensureWritable(8); a.writeLong(0xA1A2A3A4A5A6A7A8L); assertEquals(0xA1A2A3A4A5A6A7A8L, a.readLong()); buf.ensureWritable(8); buf.writeLong(0xA1A2A3A4A5A6A7A8L); assertEquals(0xA1A2A3A4A5A6A7A8L, buf.readLong()); } } } @Test public void splitOnEmptyCompositeBuffer() { try (BufferAllocator allocator = BufferAllocator.onHeapUnpooled(); Buffer buf = CompositeBuffer.compose(allocator)) { verifySplitEmptyCompositeBuffer(buf); } } @ParameterizedTest @MethodSource("allocators") public void splitBuffersMustBeAccessibleInOtherThreads(Fixture fixture) throws Exception { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { buf.writeInt(42); var send = buf.split().send(); var fut = executor.submit(() -> { try (Buffer receive = send.receive()) { assertEquals(42, receive.readInt()); receive.readerOffset(0).writerOffset(0).writeInt(24); assertEquals(24, receive.readInt()); } }); fut.get(); buf.writeInt(32); assertEquals(32, buf.readInt()); } } @ParameterizedTest @MethodSource("allocators") public void acquireOfReadOnlyBufferMustBeReadOnly(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { buf.makeReadOnly(); try (Buffer acquire = acquire((ResourceSupport) buf)) { assertTrue(acquire.readOnly()); } } } @ParameterizedTest @MethodSource("allocators") public void splitOfReadOnlyBufferMustBeReadOnly(Fixture fixture) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(16)) { buf.writeLong(0x0102030405060708L); buf.makeReadOnly(); try (Buffer split = buf.split()) { assertTrue(split.readOnly()); assertTrue(buf.readOnly()); } } } @ParameterizedTest @MethodSource("allocators") public void allocatingOnClosedAllocatorMustThrow(Fixture fixture) { BufferAllocator allocator = fixture.createAllocator(); Supplier supplier = allocator.constBufferSupplier(new byte[8]); allocator.close(); assertThrows(IllegalStateException.class, () -> allocator.allocate(8)); assertThrows(IllegalStateException.class, () -> allocator.constBufferSupplier(EmptyArrays.EMPTY_BYTES)); assertThrows(IllegalStateException.class, () -> allocator.constBufferSupplier(new byte[8])); // Existing const suppliers continue to work because they hold on to static memory allocation. supplier.get().close(); } }