/* * Copyright 2020 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.MemoryManagers; import io.netty.buffer.api.internal.ResourceSupport; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ReadOnlyBufferException; import java.text.ParseException; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.SplittableRandom; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.Stream.Builder; import static io.netty.buffer.api.internal.Statics.acquire; import static java.nio.ByteOrder.BIG_ENDIAN; import static java.nio.ByteOrder.LITTLE_ENDIAN; 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; import static org.junit.jupiter.api.Assertions.fail; public abstract class BufferTestSupport { public static ExecutorService executor; private static final Memoize INITIAL_NO_CONST = new Memoize<>( () -> initialFixturesForEachImplementation().stream().filter(f -> !f.isConst()).toArray(Fixture[]::new)); private static final Memoize ALL_COMBINATIONS = new Memoize<>( () -> fixtureCombinations(initialFixturesForEachImplementation()).toArray(Fixture[]::new)); private static final Memoize ALL_ALLOCATORS = new Memoize<>( () -> Arrays.stream(ALL_COMBINATIONS.get()) .filter(sample()) .toArray(Fixture[]::new)); private static final Memoize NON_COMPOSITE = new Memoize<>( () -> Arrays.stream(ALL_COMBINATIONS.get()) .filter(f -> !f.isComposite()) .filter(sample()) .toArray(Fixture[]::new)); private static final Memoize HEAP_ALLOCS = new Memoize<>( () -> Arrays.stream(ALL_COMBINATIONS.get()) .filter(f -> f.isHeap()) .filter(sample()) .toArray(Fixture[]::new)); private static final Memoize DIRECT_ALLOCS = new Memoize<>( () -> Arrays.stream(ALL_COMBINATIONS.get()) .filter(f -> f.isDirect()) .filter(sample()) .toArray(Fixture[]::new)); private static final Memoize POOLED_ALLOCS = new Memoize<>( () -> Arrays.stream(ALL_COMBINATIONS.get()) .filter(f -> f.isPooled()) .filter(sample()) .toArray(Fixture[]::new)); private static final Memoize POOLED_DIRECT_ALLOCS = new Memoize<>( () -> Arrays.stream(ALL_COMBINATIONS.get()) .filter(f -> f.isPooled() && f.isDirect()) .filter(sample()) .toArray(Fixture[]::new)); private static Predicate sample() { String sampleSetting = System.getProperty("sample"); if ("nosample".equalsIgnoreCase(sampleSetting)) { return fixture -> true; } // Filter out 85% of tests. return filterOfTheDay(15); } protected static Predicate filterOfTheDay(int percentage) { Instant today = Instant.now().truncatedTo(ChronoUnit.DAYS); // New seed every day. SplittableRandom rng = new SplittableRandom(today.hashCode()); AtomicInteger counter = new AtomicInteger(); return fixture -> counter.getAndIncrement() < 1 || rng.nextInt(0, 100) < percentage; } static Fixture[] allocators() { return ALL_ALLOCATORS.get(); } static Fixture[] nonCompositeAllocators() { return NON_COMPOSITE.get(); } static Fixture[] heapAllocators() { return HEAP_ALLOCS.get(); } static Fixture[] directAllocators() { return DIRECT_ALLOCS.get(); } static Fixture[] pooledAllocators() { return POOLED_ALLOCS.get(); } static Fixture[] pooledDirectAllocators() { return POOLED_DIRECT_ALLOCS.get(); } static Fixture[] initialNoConstAllocators() { return INITIAL_NO_CONST.get(); } static List initialAllocators() { return List.of( new Fixture("heap", BufferAllocator::heap, Fixture.Properties.HEAP), new Fixture("direct", BufferAllocator::direct, Fixture.Properties.DIRECT, Fixture.Properties.CLEANER), new Fixture("pooledHeap", BufferAllocator::pooledHeap, Fixture.Properties.POOLED, Fixture.Properties.HEAP), new Fixture("pooledDirect", BufferAllocator::pooledDirect, Fixture.Properties.POOLED, Fixture.Properties.DIRECT, Fixture.Properties.CLEANER)); } static List initialFixturesForEachImplementation() { List initFixtures = initialAllocators(); // Multiply by all MemoryManagers. List loadableManagers = new ArrayList<>(); MemoryManagers.getAllManagers().forEach(provider -> { loadableManagers.add(provider.get()); }); initFixtures = initFixtures.stream().flatMap(f -> { Builder builder = Stream.builder(); for (MemoryManagers managers : loadableManagers) { builder.add(new Fixture(f + "/" + managers, () -> MemoryManagers.using(managers, f), f.getProperties())); } return builder.build(); }).collect(Collectors.toList()); return initFixtures; } static Stream fixtureCombinations(List initFixtures) { Builder builder = Stream.builder(); initFixtures.forEach(builder); // Add 2-way composite buffers of all combinations. for (Fixture first : initFixtures) { for (Fixture second : initFixtures) { builder.add(new Fixture("compose(" + first + ", " + second + ')', () -> { return new BufferAllocator() { BufferAllocator a; BufferAllocator b; @Override public Buffer allocate(int size) { a = first.get(); b = second.get(); int half = size / 2; try (Buffer firstHalf = a.allocate(half); Buffer secondHalf = b.allocate(size - half)) { return CompositeBuffer.compose(a, firstHalf.send(), secondHalf.send()); } } @Override public void close() { if (a != null) { a.close(); a = null; } if (b != null) { b.close(); b = null; } } }; }, Fixture.Properties.COMPOSITE)); } } // Also add a 3-way composite buffer. builder.add(new Fixture("compose(heap,heap,heap)", () -> { return new BufferAllocator() { BufferAllocator alloc; @Override public Buffer allocate(int size) { alloc = BufferAllocator.heap(); int part = size / 3; try (Buffer a = alloc.allocate(part); Buffer b = alloc.allocate(part); Buffer c = alloc.allocate(size - part * 2)) { return CompositeBuffer.compose(alloc, a.send(), b.send(), c.send()); } } @Override public void close() { if (alloc != null) { alloc.close(); alloc = null; } } }; }, Fixture.Properties.COMPOSITE)); for (Fixture fixture : initFixtures) { builder.add(new Fixture(fixture + ".ensureWritable", () -> { return new BufferAllocator() { BufferAllocator allocator; @Override public Buffer allocate(int size) { allocator = fixture.createAllocator(); if (size < 2) { return allocator.allocate(size); } var buf = allocator.allocate(size - 1); buf.ensureWritable(size); return buf; } @Override public void close() { if (allocator != null) { allocator.close(); allocator = null; } } }; }, fixture.getProperties())); builder.add(new Fixture(fixture + ".compose.ensureWritable", () -> { return new BufferAllocator() { BufferAllocator allocator; @Override public Buffer allocate(int size) { allocator = fixture.createAllocator(); if (size < 2) { return allocator.allocate(size); } var buf = CompositeBuffer.compose(allocator); buf.ensureWritable(size); return buf; } @Override public void close() { if (allocator != null) { allocator.close(); allocator = null; } } }; }, Fixture.Properties.COMPOSITE)); } var stream = builder.build(); return stream.flatMap(BufferTestSupport::injectSplits); } private static Stream injectSplits(Fixture f) { Builder builder = Stream.builder(); builder.add(f); builder.add(new Fixture(f + ".split", () -> { return new BufferAllocator() { BufferAllocator allocatorBase; @Override public Buffer allocate(int size) { allocatorBase = f.get(); try (Buffer buf = allocatorBase.allocate(size + 1)) { buf.writerOffset(size); return buf.split().writerOffset(0); } } @Override public void close() { if (allocatorBase != null) { allocatorBase.close(); allocatorBase = null; } } }; }, f.getProperties())); return builder.build(); } @BeforeAll static void startExecutor() throws IOException, ParseException { executor = Executors.newSingleThreadExecutor(new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread thread = Executors.defaultThreadFactory().newThread(r); thread.setName("BufferTest-" + thread.getName()); thread.setDaemon(true); // Do not prevent shut down of test runner. return thread; } }); } @AfterAll static void stopExecutor() throws IOException { executor.shutdown(); } public static void verifyInaccessible(Buffer buf) { verifyReadInaccessible(buf); verifyWriteInaccessible(buf, BufferClosedException.class); try (BufferAllocator allocator = BufferAllocator.heap(); Buffer target = allocator.allocate(24)) { assertThrows(BufferClosedException.class, () -> buf.copyInto(0, target, 0, 1)); assertThrows(BufferClosedException.class, () -> buf.copyInto(0, new byte[1], 0, 1)); assertThrows(BufferClosedException.class, () -> buf.copyInto(0, ByteBuffer.allocate(1), 0, 1)); if (CompositeBuffer.isComposite(buf)) { assertThrows(BufferClosedException.class, () -> ((CompositeBuffer) buf).extendWith(target.send())); } } assertThrows(BufferClosedException.class, () -> buf.split()); assertThrows(BufferClosedException.class, () -> buf.send()); assertThrows(BufferClosedException.class, () -> acquire((ResourceSupport) buf)); assertThrows(BufferClosedException.class, () -> buf.copy()); assertThrows(BufferClosedException.class, () -> buf.openCursor()); assertThrows(BufferClosedException.class, () -> buf.openCursor(0, 0)); assertThrows(BufferClosedException.class, () -> buf.openReverseCursor()); assertThrows(BufferClosedException.class, () -> buf.openReverseCursor(0, 0)); } public static void verifyReadInaccessible(Buffer buf) { assertThrows(BufferClosedException.class, () -> buf.readByte()); assertThrows(BufferClosedException.class, () -> buf.readUnsignedByte()); assertThrows(BufferClosedException.class, () -> buf.readChar()); assertThrows(BufferClosedException.class, () -> buf.readShort()); assertThrows(BufferClosedException.class, () -> buf.readUnsignedShort()); assertThrows(BufferClosedException.class, () -> buf.readMedium()); assertThrows(BufferClosedException.class, () -> buf.readUnsignedMedium()); assertThrows(BufferClosedException.class, () -> buf.readInt()); assertThrows(BufferClosedException.class, () -> buf.readUnsignedInt()); assertThrows(BufferClosedException.class, () -> buf.readFloat()); assertThrows(BufferClosedException.class, () -> buf.readLong()); assertThrows(BufferClosedException.class, () -> buf.readDouble()); assertThrows(BufferClosedException.class, () -> buf.getByte(0)); assertThrows(BufferClosedException.class, () -> buf.getUnsignedByte(0)); assertThrows(BufferClosedException.class, () -> buf.getChar(0)); assertThrows(BufferClosedException.class, () -> buf.getShort(0)); assertThrows(BufferClosedException.class, () -> buf.getUnsignedShort(0)); assertThrows(BufferClosedException.class, () -> buf.getMedium(0)); assertThrows(BufferClosedException.class, () -> buf.getUnsignedMedium(0)); assertThrows(BufferClosedException.class, () -> buf.getInt(0)); assertThrows(BufferClosedException.class, () -> buf.getUnsignedInt(0)); assertThrows(BufferClosedException.class, () -> buf.getFloat(0)); assertThrows(BufferClosedException.class, () -> buf.getLong(0)); assertThrows(BufferClosedException.class, () -> buf.getDouble(0)); } public static void verifyWriteInaccessible(Buffer buf, Class expected) { assertThrows(expected, () -> buf.writeByte((byte) 32)); assertThrows(expected, () -> buf.writeUnsignedByte(32)); assertThrows(expected, () -> buf.writeChar('3')); assertThrows(expected, () -> buf.writeShort((short) 32)); assertThrows(expected, () -> buf.writeUnsignedShort(32)); assertThrows(expected, () -> buf.writeMedium(32)); assertThrows(expected, () -> buf.writeUnsignedMedium(32)); assertThrows(expected, () -> buf.writeInt(32)); assertThrows(expected, () -> buf.writeUnsignedInt(32)); assertThrows(expected, () -> buf.writeFloat(3.2f)); assertThrows(expected, () -> buf.writeLong(32)); assertThrows(expected, () -> buf.writeDouble(32)); assertThrows(expected, () -> buf.setByte(0, (byte) 32)); assertThrows(expected, () -> buf.setUnsignedByte(0, 32)); assertThrows(expected, () -> buf.setChar(0, '3')); assertThrows(expected, () -> buf.setShort(0, (short) 32)); assertThrows(expected, () -> buf.setUnsignedShort(0, 32)); assertThrows(expected, () -> buf.setMedium(0, 32)); assertThrows(expected, () -> buf.setUnsignedMedium(0, 32)); assertThrows(expected, () -> buf.setInt(0, 32)); assertThrows(expected, () -> buf.setUnsignedInt(0, 32)); assertThrows(expected, () -> buf.setFloat(0, 3.2f)); assertThrows(expected, () -> buf.setLong(0, 32)); assertThrows(expected, () -> buf.setDouble(0, 32)); assertThrows(expected, () -> buf.ensureWritable(1)); assertThrows(expected, () -> buf.fill((byte) 0)); try (BufferAllocator allocator = BufferAllocator.heap(); Buffer source = allocator.allocate(8)) { assertThrows(expected, () -> source.copyInto(0, buf, 0, 1)); } } public static void testCopyIntoByteBuffer(Fixture fixture, Function bbAlloc) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { buf.order(BIG_ENDIAN).writeLong(0x0102030405060708L); ByteBuffer buffer = bbAlloc.apply(8); buf.copyInto(0, buffer, 0, buffer.capacity()); assertEquals((byte) 0x01, buffer.get()); assertEquals((byte) 0x02, buffer.get()); assertEquals((byte) 0x03, buffer.get()); assertEquals((byte) 0x04, buffer.get()); assertEquals((byte) 0x05, buffer.get()); assertEquals((byte) 0x06, buffer.get()); assertEquals((byte) 0x07, buffer.get()); assertEquals((byte) 0x08, buffer.get()); buffer.clear(); buf.writerOffset(0).order(LITTLE_ENDIAN).writeLong(0x0102030405060708L); buf.copyInto(0, buffer, 0, buffer.capacity()); assertEquals((byte) 0x08, buffer.get()); assertEquals((byte) 0x07, buffer.get()); assertEquals((byte) 0x06, buffer.get()); assertEquals((byte) 0x05, buffer.get()); assertEquals((byte) 0x04, buffer.get()); assertEquals((byte) 0x03, buffer.get()); assertEquals((byte) 0x02, buffer.get()); assertEquals((byte) 0x01, buffer.get()); buffer.clear(); buffer = bbAlloc.apply(6); buf.copyInto(1, buffer, 1, 3); assertEquals((byte) 0x00, buffer.get()); assertEquals((byte) 0x07, buffer.get()); assertEquals((byte) 0x06, buffer.get()); assertEquals((byte) 0x05, buffer.get()); assertEquals((byte) 0x00, buffer.get()); assertEquals((byte) 0x00, buffer.get()); buffer.clear(); buffer = bbAlloc.apply(6); buffer.position(3).limit(3); buf.copyInto(1, buffer, 1, 3); assertEquals(3, buffer.position()); assertEquals(3, buffer.limit()); buffer.clear(); assertEquals((byte) 0x00, buffer.get()); assertEquals((byte) 0x07, buffer.get()); assertEquals((byte) 0x06, buffer.get()); assertEquals((byte) 0x05, buffer.get()); assertEquals((byte) 0x00, buffer.get()); assertEquals((byte) 0x00, buffer.get()); } } public static void testCopyIntoBuf(Fixture fixture, Function bbAlloc) { try (BufferAllocator allocator = fixture.createAllocator(); Buffer buf = allocator.allocate(8)) { buf.order(BIG_ENDIAN).writeLong(0x0102030405060708L); Buffer buffer = bbAlloc.apply(8); buffer.writerOffset(8); buf.copyInto(0, buffer, 0, buffer.capacity()); assertEquals((byte) 0x01, buffer.readByte()); assertEquals((byte) 0x02, buffer.readByte()); assertEquals((byte) 0x03, buffer.readByte()); assertEquals((byte) 0x04, buffer.readByte()); assertEquals((byte) 0x05, buffer.readByte()); assertEquals((byte) 0x06, buffer.readByte()); assertEquals((byte) 0x07, buffer.readByte()); assertEquals((byte) 0x08, buffer.readByte()); buffer.reset(); buf.writerOffset(0).order(LITTLE_ENDIAN).writeLong(0x0102030405060708L); buf.copyInto(0, buffer, 0, buffer.capacity()); buffer.writerOffset(8); assertEquals((byte) 0x08, buffer.readByte()); assertEquals((byte) 0x07, buffer.readByte()); assertEquals((byte) 0x06, buffer.readByte()); assertEquals((byte) 0x05, buffer.readByte()); assertEquals((byte) 0x04, buffer.readByte()); assertEquals((byte) 0x03, buffer.readByte()); assertEquals((byte) 0x02, buffer.readByte()); assertEquals((byte) 0x01, buffer.readByte()); buffer.reset(); buffer.close(); buffer = bbAlloc.apply(6); buf.copyInto(1, buffer, 1, 3); buffer.writerOffset(6); assertEquals((byte) 0x00, buffer.readByte()); assertEquals((byte) 0x07, buffer.readByte()); assertEquals((byte) 0x06, buffer.readByte()); assertEquals((byte) 0x05, buffer.readByte()); assertEquals((byte) 0x00, buffer.readByte()); assertEquals((byte) 0x00, buffer.readByte()); buffer.close(); buffer = bbAlloc.apply(6); buffer.writerOffset(3).readerOffset(3); buf.copyInto(1, buffer, 1, 3); assertEquals(3, buffer.readerOffset()); assertEquals(3, buffer.writerOffset()); buffer.reset(); buffer.writerOffset(6); assertEquals((byte) 0x00, buffer.readByte()); assertEquals((byte) 0x07, buffer.readByte()); assertEquals((byte) 0x06, buffer.readByte()); assertEquals((byte) 0x05, buffer.readByte()); assertEquals((byte) 0x00, buffer.readByte()); assertEquals((byte) 0x00, buffer.readByte()); buffer.close(); buf.reset(); buf.order(BIG_ENDIAN).writeLong(0x0102030405060708L); // Testing copyInto for overlapping writes: // // 0x0102030405060708 // └──┬──┬──┘ │ // └─▶└┬───────┘ // ▼ // 0x0102030102030405 buf.copyInto(0, buf, 3, 5); assertThat(toByteArray(buf)).containsExactly(0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x04, 0x05); } } public static void checkByteIteration(Buffer buf) { var cursor = buf.openCursor(); assertFalse(cursor.readByte()); assertFalse(cursor.readLong()); assertEquals(0, cursor.bytesLeft()); assertEquals((byte) -1, cursor.getByte()); assertEquals(-1L, cursor.getLong()); for (int i = 0; i < 0x27; i++) { buf.writeByte((byte) (i + 1)); } int roff = buf.readerOffset(); int woff = buf.writerOffset(); cursor = buf.openCursor(); assertEquals(0x27, cursor.bytesLeft()); assertTrue(cursor.readByte()); assertEquals((byte) 0x01, cursor.getByte()); assertEquals((byte) 0x01, cursor.getByte()); assertTrue(cursor.readLong()); assertEquals(0x0203040506070809L, cursor.getLong()); assertEquals(0x0203040506070809L, cursor.getLong()); assertEquals(0x1E, cursor.bytesLeft()); assertTrue(cursor.readLong()); assertEquals(0x0A0B0C0D0E0F1011L, cursor.getLong()); assertEquals(0x16, cursor.bytesLeft()); assertTrue(cursor.readLong()); assertEquals(0x1213141516171819L, cursor.getLong()); assertEquals(0x0E, cursor.bytesLeft()); assertTrue(cursor.readLong()); assertEquals(0x1A1B1C1D1E1F2021L, cursor.getLong()); assertEquals(6, cursor.bytesLeft()); assertFalse(cursor.readLong()); assertEquals(6, cursor.bytesLeft()); assertEquals(0x1A1B1C1D1E1F2021L, cursor.getLong()); assertTrue(cursor.readByte()); assertEquals((byte) 0x22, cursor.getByte()); assertEquals((byte) 0x22, cursor.getByte()); assertEquals(5, cursor.bytesLeft()); assertTrue(cursor.readByte()); assertEquals((byte) 0x23, cursor.getByte()); assertEquals(4, cursor.bytesLeft()); assertTrue(cursor.readByte()); assertEquals((byte) 0x24, cursor.getByte()); assertEquals(3, cursor.bytesLeft()); assertTrue(cursor.readByte()); assertEquals((byte) 0x25, cursor.getByte()); assertEquals(2, cursor.bytesLeft()); assertTrue(cursor.readByte()); assertEquals((byte) 0x26, cursor.getByte()); assertEquals(1, cursor.bytesLeft()); assertTrue(cursor.readByte()); assertEquals((byte) 0x27, cursor.getByte()); assertEquals(0, cursor.bytesLeft()); assertFalse(cursor.readByte()); assertEquals((byte) 0x27, cursor.getByte()); assertEquals((byte) 0x27, cursor.getByte()); assertFalse(cursor.readLong()); assertEquals(roff, buf.readerOffset()); assertEquals(woff, buf.writerOffset()); } public static void checkByteIterationOfRegion(Buffer buf) { assertThrows(IllegalArgumentException.class, () -> buf.openCursor(-1, 1)); assertThrows(IllegalArgumentException.class, () -> buf.openCursor(1, -1)); assertThrows(IllegalArgumentException.class, () -> buf.openCursor(buf.capacity(), 1)); assertThrows(IllegalArgumentException.class, () -> buf.openCursor(buf.capacity() - 1, 2)); assertThrows(IllegalArgumentException.class, () -> buf.openCursor(buf.capacity() - 2, 3)); var cursor = buf.openCursor(1, 0); assertFalse(cursor.readByte()); assertFalse(cursor.readLong()); assertEquals(0, cursor.bytesLeft()); assertEquals((byte) -1, cursor.getByte()); assertEquals(-1L, cursor.getLong()); for (int i = 0; i < 0x27; i++) { buf.writeByte((byte) (i + 1)); } int roff = buf.readerOffset(); int woff = buf.writerOffset(); cursor = buf.openCursor(buf.readerOffset() + 1, buf.readableBytes() - 2); assertEquals(0x25, cursor.bytesLeft()); assertTrue(cursor.readByte()); assertEquals((byte) 0x02, cursor.getByte()); assertEquals((byte) 0x02, cursor.getByte()); assertTrue(cursor.readLong()); assertEquals(0x030405060708090AL, cursor.getLong()); assertEquals(0x1C, cursor.bytesLeft()); assertTrue(cursor.readLong()); assertEquals(0x0B0C0D0E0F101112L, cursor.getLong()); assertEquals(0x14, cursor.bytesLeft()); assertTrue(cursor.readLong()); assertEquals(0x131415161718191AL, cursor.getLong()); assertEquals(0x0C, cursor.bytesLeft()); assertTrue(cursor.readLong()); assertEquals(0x1B1C1D1E1F202122L, cursor.getLong()); assertEquals(4, cursor.bytesLeft()); assertFalse(cursor.readLong()); assertEquals(4, cursor.bytesLeft()); assertEquals(0x1B1C1D1E1F202122L, cursor.getLong()); assertTrue(cursor.readByte()); assertEquals((byte) 0x23, cursor.getByte()); assertEquals(3, cursor.bytesLeft()); assertTrue(cursor.readByte()); assertEquals((byte) 0x24, cursor.getByte()); assertEquals(2, cursor.bytesLeft()); assertTrue(cursor.readByte()); assertEquals((byte) 0x25, cursor.getByte()); assertEquals(1, cursor.bytesLeft()); assertTrue(cursor.readByte()); assertEquals((byte) 0x26, cursor.getByte()); assertEquals(0, cursor.bytesLeft()); assertFalse(cursor.readByte()); assertEquals(0, cursor.bytesLeft()); assertEquals(0x1B1C1D1E1F202122L, cursor.getLong()); assertEquals((byte) 0x26, cursor.getByte()); cursor = buf.openCursor(buf.readerOffset() + 1, 2); assertEquals(2, cursor.bytesLeft()); assertFalse(cursor.readLong()); assertTrue(cursor.readByte()); assertEquals((byte) 0x02, cursor.getByte()); assertEquals(1, cursor.bytesLeft()); assertFalse(cursor.readLong()); assertTrue(cursor.readByte()); assertEquals((byte) 0x03, cursor.getByte()); assertEquals(0, cursor.bytesLeft()); assertFalse(cursor.readByte()); assertFalse(cursor.readLong()); assertEquals(roff, buf.readerOffset()); assertEquals(woff, buf.writerOffset()); } public static void checkReverseByteIteration(Buffer buf) { var cursor = buf.openReverseCursor(); assertFalse(cursor.readByte()); assertFalse(cursor.readLong()); assertEquals(0, cursor.bytesLeft()); assertEquals((byte) -1, cursor.getByte()); assertEquals(-1L, cursor.getLong()); for (int i = 0; i < 0x27; i++) { buf.writeByte((byte) (i + 1)); } int roff = buf.readerOffset(); int woff = buf.writerOffset(); cursor = buf.openReverseCursor(); assertEquals(0x27, cursor.bytesLeft()); assertTrue(cursor.readByte()); assertEquals((byte) 0x27, cursor.getByte()); assertEquals((byte) 0x27, cursor.getByte()); assertTrue(cursor.readLong()); assertEquals(0x262524232221201FL, cursor.getLong()); assertEquals(0x1E, cursor.bytesLeft()); assertTrue(cursor.readLong()); assertEquals(0x1E1D1C1B1A191817L, cursor.getLong()); assertEquals(0x16, cursor.bytesLeft()); assertTrue(cursor.readLong()); assertEquals(0x161514131211100FL, cursor.getLong()); assertEquals(0x0E, cursor.bytesLeft()); assertTrue(cursor.readLong()); assertEquals(0x0E0D0C0B0A090807L, cursor.getLong()); assertEquals(6, cursor.bytesLeft()); assertFalse(cursor.readLong()); assertEquals(6, cursor.bytesLeft()); assertEquals(0x0E0D0C0B0A090807L, cursor.getLong()); assertTrue(cursor.readByte()); assertEquals((byte) 0x06, cursor.getByte()); assertEquals(5, cursor.bytesLeft()); assertTrue(cursor.readByte()); assertEquals((byte) 0x05, cursor.getByte()); assertEquals(4, cursor.bytesLeft()); assertTrue(cursor.readByte()); assertEquals((byte) 0x04, cursor.getByte()); assertEquals(3, cursor.bytesLeft()); assertTrue(cursor.readByte()); assertEquals((byte) 0x03, cursor.getByte()); assertEquals(2, cursor.bytesLeft()); assertTrue(cursor.readByte()); assertEquals((byte) 0x02, cursor.getByte()); assertEquals(1, cursor.bytesLeft()); assertTrue(cursor.readByte()); assertEquals((byte) 0x01, cursor.getByte()); assertEquals(0, cursor.bytesLeft()); assertFalse(cursor.readByte()); assertEquals((byte) 0x01, cursor.getByte()); assertFalse(cursor.readByte()); assertEquals(0, cursor.bytesLeft()); assertEquals(0x0E0D0C0B0A090807L, cursor.getLong()); assertEquals(roff, buf.readerOffset()); assertEquals(woff, buf.writerOffset()); } public static void checkReverseByteIterationOfRegion(Buffer buf) { assertThrows(IllegalArgumentException.class, () -> buf.openReverseCursor(-1, 0)); assertThrows(IllegalArgumentException.class, () -> buf.openReverseCursor(0, -1)); assertThrows(IllegalArgumentException.class, () -> buf.openReverseCursor(0, 2)); assertThrows(IllegalArgumentException.class, () -> buf.openReverseCursor(1, 3)); assertThrows(IllegalArgumentException.class, () -> buf.openReverseCursor(buf.capacity(), 0)); var cursor = buf.openReverseCursor(1, 0); assertFalse(cursor.readByte()); assertFalse(cursor.readLong()); assertEquals(0, cursor.bytesLeft()); assertEquals((byte) -1, cursor.getByte()); assertEquals(-1L, cursor.getLong()); for (int i = 0; i < 0x27; i++) { buf.writeByte((byte) (i + 1)); } int roff = buf.readerOffset(); int woff = buf.writerOffset(); cursor = buf.openReverseCursor(buf.writerOffset() - 2, buf.readableBytes() - 2); assertEquals(0x25, cursor.bytesLeft()); assertTrue(cursor.readByte()); assertEquals((byte) 0x26, cursor.getByte()); assertEquals((byte) 0x26, cursor.getByte()); assertTrue(cursor.readLong()); assertEquals(0x2524232221201F1EL, cursor.getLong()); assertEquals(0x1C, cursor.bytesLeft()); assertTrue(cursor.readLong()); assertEquals(0x1D1C1B1A19181716L, cursor.getLong()); assertEquals(0x14, cursor.bytesLeft()); assertTrue(cursor.readLong()); assertEquals(0x1514131211100F0EL, cursor.getLong()); assertEquals(0x0C, cursor.bytesLeft()); assertTrue(cursor.readLong()); assertEquals(0x0D0C0B0A09080706L, cursor.getLong()); assertEquals(4, cursor.bytesLeft()); assertFalse(cursor.readLong()); assertEquals(4, cursor.bytesLeft()); assertEquals(0x0D0C0B0A09080706L, cursor.getLong()); assertTrue(cursor.readByte()); assertEquals((byte) 0x05, cursor.getByte()); assertEquals(3, cursor.bytesLeft()); assertTrue(cursor.readByte()); assertEquals((byte) 0x04, cursor.getByte()); assertEquals(2, cursor.bytesLeft()); assertTrue(cursor.readByte()); assertEquals((byte) 0x03, cursor.getByte()); assertEquals(1, cursor.bytesLeft()); assertTrue(cursor.readByte()); assertEquals((byte) 0x02, cursor.getByte()); assertEquals(0, cursor.bytesLeft()); assertFalse(cursor.readByte()); assertEquals((byte) 0x02, cursor.getByte()); assertFalse(cursor.readByte()); assertEquals(0, cursor.bytesLeft()); assertEquals(0x0D0C0B0A09080706L, cursor.getLong()); cursor = buf.openReverseCursor(buf.readerOffset() + 2, 2); assertEquals(2, cursor.bytesLeft()); assertFalse(cursor.readLong()); assertTrue(cursor.readByte()); assertEquals((byte) 0x03, cursor.getByte()); assertEquals(1, cursor.bytesLeft()); assertFalse(cursor.readLong()); assertTrue(cursor.readByte()); assertEquals((byte) 0x02, cursor.getByte()); assertEquals(0, cursor.bytesLeft()); assertFalse(cursor.readByte()); assertFalse(cursor.readLong()); assertEquals(roff, buf.readerOffset()); assertEquals(woff, buf.writerOffset()); } public static void verifySplitEmptyCompositeBuffer(Buffer buf) { try (Buffer a = buf.split()) { a.ensureWritable(4); buf.ensureWritable(4); a.writeInt(1); buf.writeInt(2); assertEquals(1, a.readInt()); assertEquals(2, buf.readInt()); assertThat(a.order()).isEqualTo(buf.order()); } } public static void verifyForEachReadableSingleComponent(Fixture fixture, Buffer buf) { buf.forEachReadable(0, (index, component) -> { var buffer = component.readableBuffer(); assertThat(buffer.position()).isZero(); assertThat(buffer.limit()).isEqualTo(8); assertThat(buffer.capacity()).isEqualTo(8); assertEquals(0x0102030405060708L, buffer.getLong()); if (fixture.isDirect()) { assertThat(component.readableNativeAddress()).isNotZero(); } else { assertThat(component.readableNativeAddress()).isZero(); } if (component.hasReadableArray()) { byte[] array = component.readableArray(); byte[] arrayCopy = new byte[component.readableArrayLength()]; System.arraycopy(array, component.readableArrayOffset(), arrayCopy, 0, arrayCopy.length); if (buffer.order() == BIG_ENDIAN) { assertThat(arrayCopy).containsExactly(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08); } else { assertThat(arrayCopy).containsExactly(0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01); } } assertThrows(ReadOnlyBufferException.class, () -> buffer.put(0, (byte) 0xFF)); return true; }); } public static void verifyForEachWritableSingleComponent(Fixture fixture, Buffer buf) { buf.forEachWritable(0, (index, component) -> { var buffer = component.writableBuffer(); assertThat(buffer.position()).isZero(); assertThat(buffer.limit()).isEqualTo(8); assertThat(buffer.capacity()).isEqualTo(8); buffer.putLong(0x0102030405060708L); buffer.flip(); assertEquals(0x0102030405060708L, buffer.getLong()); buf.writerOffset(8); assertEquals(0x0102030405060708L, buf.getLong(0)); if (fixture.isDirect()) { assertThat(component.writableNativeAddress()).isNotZero(); } else { assertThat(component.writableNativeAddress()).isZero(); } buf.writerOffset(0); if (component.hasWritableArray()) { byte[] array = component.writableArray(); int offset = component.writableArrayOffset(); byte[] arrayCopy = new byte[component.writableArrayLength()]; System.arraycopy(array, offset, arrayCopy, 0, arrayCopy.length); if (buffer.order() == BIG_ENDIAN) { assertThat(arrayCopy).containsExactly(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08); } else { assertThat(arrayCopy).containsExactly(0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01); } } buffer.put(0, (byte) 0xFF); assertEquals((byte) 0xFF, buffer.get(0)); assertEquals((byte) 0xFF, buf.getByte(0)); return true; }); } public static byte[] toByteArray(Buffer buf) { byte[] bs = new byte[buf.capacity()]; buf.copyInto(0, bs, 0, bs.length); return bs; } public static byte[] readByteArray(Buffer buf) { byte[] bs = new byte[buf.readableBytes()]; buf.copyInto(buf.readerOffset(), bs, 0, bs.length); buf.readerOffset(buf.writerOffset()); return bs; } public static void assertEquals(Buffer expected, Buffer actual) { assertThat(toByteArray(actual)).containsExactly(toByteArray(expected)); } public static void assertReadableEquals(Buffer expected, Buffer actual) { assertThat(readByteArray(actual)).containsExactly(readByteArray(expected)); } public static void assertEquals(byte expected, byte actual) { if (expected != actual) { fail(String.format("expected: %1$s (0x%1$X) but was: %2$s (0x%2$X)", expected, actual)); } } public static void assertEquals(char expected, char actual) { if (expected != actual) { fail(String.format("expected: %s (0x%X) but was: %s (0x%X)", expected, (int) expected, actual, (int) actual)); } } public static void assertEquals(short expected, short actual) { if (expected != actual) { fail(String.format("expected: %1$s (0x%1$X) but was: %2$s (0x%2$X)", expected, actual)); } } public static void assertEquals(int expected, int actual) { if (expected != actual) { fail(String.format("expected: %1$s (0x%1$X) but was: %2$s (0x%2$X)", expected, actual)); } } public static void assertEquals(long expected, long actual) { if (expected != actual) { fail(String.format("expected: %1$s (0x%1$X) but was: %2$s (0x%2$X)", expected, actual)); } } public static void assertEquals(float expected, float actual) { //noinspection FloatingPointEquality if (expected != actual) { fail(String.format("expected: %s (0x%X) but was: %s (0x%X)", expected, Float.floatToRawIntBits(expected), actual, Float.floatToRawIntBits(actual))); } } public static void assertEquals(double expected, double actual) { //noinspection FloatingPointEquality if (expected != actual) { fail(String.format("expected: %s (0x%X) but was: %s (0x%X)", expected, Double.doubleToRawLongBits(expected), actual, Double.doubleToRawLongBits(actual))); } } }