diff --git a/src/main/java/io/netty/buffer/api/Buf.java b/src/main/java/io/netty/buffer/api/Buf.java index 6906be4..979a8d1 100644 --- a/src/main/java/io/netty/buffer/api/Buf.java +++ b/src/main/java/io/netty/buffer/api/Buf.java @@ -15,6 +15,11 @@ */ package io.netty.buffer.api; +import io.netty.buffer.api.ComponentProcessor.ReadableComponentProcessor; +import io.netty.buffer.api.ComponentProcessor.WritableComponentProcessor; +import io.netty.buffer.api.ComponentProcessor.ReadableComponent; +import io.netty.buffer.api.ComponentProcessor.WritableComponent; + import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -187,7 +192,7 @@ public interface Buf extends Rc, BufAccessors { * Give the native memory address backing this buffer, or return 0 if this buffer has no native memory address. * @return The native memory address, if any, otherwise 0. */ - long getNativeAddress(); + long nativeAddress(); /** * Set the read-only state of this buffer. @@ -482,4 +487,119 @@ public interface Buf extends Rc, BufAccessors { * or is {@linkplain #readOnly() read-only}. */ void compact(); + + /** + * Get the number of "components" in this buffer. For composite buffers, this is the number of transitive + * constituent buffers, while non-composite buffers only have one component. + * + * @return The number of components in this buffer. + */ + int countComponents(); + + /** + * Get the number of "components" in this buffer, that are readable. These are the components that would be + * processed by {@link #forEachReadable(int, ReadableComponentProcessor)}. For composite buffers, this is the + * number of transitive constituent buffers that are readable, while non-composite buffers only have at most one + * readable component. + *

+ * The number of readable components may be less than the {@link #countComponents() component count}, if not all of + * them have readable data. + * + * @return The number of readable components in this buffer. + */ + int countReadableComponents(); + + /** + * Get the number of "components" in this buffer, that are writable. These are the components that would be + * processed by {@link #forEachWritable(int, WritableComponentProcessor)}. For composite buffers, this is the + * number of transitive constituent buffers that are writable, while non-composite buffers only have at most one + * writable component. + *

+ * The number of writable components may be less than the {@link #countComponents() component count}, if not all of + * them have space for writing. + * + * @return The number of writable components in this buffer. + */ + int countWritableComponents(); + + /** + * Process all readable components of this buffer, and return the number of components processed. + *

+ * The given {@linkplain ReadableComponentProcessor processor} is called for each readable component in this buffer, + * and passed a component index, for the given component in the iteration, and a {@link ReadableComponent} object + * for accessing the data within the given component. + *

+ * The component index is specific to the particular invokation of this method. The first call to the consumer will + * be passed the given initial index, and the next call will be passed the initial index plus one, and so on. + *

+ * The {@linkplain ReadableComponentProcessor component processor} may stop the iteration at any time by returning + * {@code false}. + * This will cause the number of components processed to be returned as a negative number (to signal early return), + * and the number of components processed may then be less than the + * {@linkplain #countReadableComponents() readable component count}. + *

+ * Note that the {@link ReadableComponent} instance passed to the consumer could be reused for + * multiple calls, so the data must be extracted from the component in the context of the iteration. + *

+ * The {@link ByteBuffer} instances obtained from the component, share life time with that internal component. + * This means they can be accessed as long as the internal memory store remain unchanged. Methods that may cause + * such changes, are any method that requires the buffer to be {@linkplain #isOwned() owned}. + *

+ * The best way to ensure this doesn't cause any trouble, is to use the buffers directly as part of the iteration, + * or immediately after the iteration while we are still in the scope of the method that triggered the iteration. + *

+ * Note that the arrays, memory addresses, and byte buffers exposed as components by this method, + * should not be used for changing the buffer contents. Doing so may cause undefined behaviour. + *

+ * Changes to position and limit of the byte buffers exposed via the processed components, are not reflected back to + * this buffer instance. + * + * @param initialIndex The initial index of the iteration, and the index that will be passed to the first call to + * the {@linkplain ReadableComponentProcessor#process(int, ReadableComponent) processor}. + * @param processor The processor that will be used to process the buffer components. + * @return The number of readable components processed, as a positive number of all readable components were + * processed, or as a negative number if the iteration was stopped because + * {@link ReadableComponentProcessor#process(int, ReadableComponent)} returned {@code false}. + * In any case, the number of components processed may be less than {@link #countComponents()}. + */ + int forEachReadable(int initialIndex, ReadableComponentProcessor processor); + + /** + * Process all writable components of this buffer, and return the number of components processed. + *

+ * The given {@linkplain WritableComponentProcessor processor} is called for each writable component in this buffer, + * and passed a component index, for the given component in the iteration, and a {@link WritableComponent} object + * for accessing the data within the given component. + *

+ * The component index is specific to the particular invokation of this method. The first call to the consumer will + * be passed the given initial index, and the next call will be passed the initial index plus one, and so on. + *

+ * The {@link WritableComponentProcessor component processor} may stop the iteration at any time by returning + * {@code false}. + * This will cause the number of components processed to be returned as a negative number (to signal early return), + * and the number of components processed may then be less than the + * {@linkplain #countReadableComponents() readable component count}. + *

+ * Note that the {@link WritableComponent} instance passed to the consumer could be reused for + * multiple calls, so the data must be extracted from the component in the context of the iteration. + *

+ * The {@link ByteBuffer} instances obtained from the component, share life time with that internal component. + * This means they can be accessed as long as the internal memory store remain unchanged. Methods that may cause + * such changes, are any method that requires the buffer to be {@linkplain #isOwned() owned}. + *

+ * The best way to ensure this doesn't cause any trouble, is to use the buffers directly as part of the iteration, + * or immediately after the iteration while we are still in the scope of the method that triggered the iteration. + *

+ * Changes to position and limit of the byte buffers exposed via the processed components, are not reflected back to + * this buffer instance. + * + * @param initialIndex The initial index of the iteration, and the index that will be passed to the first call to + * the {@linkplain WritableComponentProcessor#process(int, WritableComponent) processor}. + * @param processor The processor that will be used to process the buffer components. + * @return The number of writable components processed, as a positive number of all writable components were + * processed, or as a negative number if the iteration was stopped because + * {@link WritableComponentProcessor#process(int, WritableComponent)} returned {@code false}. + * In any case, the number of components processed may be less than {@link #countComponents()}. + */ + int forEachWritable(int initialIndex, WritableComponentProcessor processor); } diff --git a/src/main/java/io/netty/buffer/api/ComponentProcessor.java b/src/main/java/io/netty/buffer/api/ComponentProcessor.java new file mode 100644 index 0000000..7e41314 --- /dev/null +++ b/src/main/java/io/netty/buffer/api/ComponentProcessor.java @@ -0,0 +1,170 @@ +/* + * 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; + +import java.nio.ByteBuffer; + +/** + * This interface contain a collection of APIs used in the {@link Buf#forEachReadable(int, ReadableComponentProcessor)} + * and {@link Buf#forEachWritable(int, WritableComponentProcessor)} methods. + */ +public interface ComponentProcessor { + /** + * A processor of {@linkplain ReadableComponent readable components}. + */ + @FunctionalInterface + interface ReadableComponentProcessor extends ComponentProcessor { + /** + * Process the given component at the given index in the + * {@link Buf#forEachReadable(int, ReadableComponentProcessor) iteration}. + *

+ * The component object itself is only valid during this call, but the {@link ByteBuffer byte buffers}, arrays, + * and native address pointers obtained from it, will be valid until any + * {@link Buf#isOwned() ownership} requiring operation is performed on the buffer. + * + * @param index The current index of the given buffer component, based on the initial index passed to the + * {@link Buf#forEachReadable(int, ReadableComponentProcessor)} method. + * @param component The current buffer component being processed. + * @return {@code true} if the iteration should continue and more components should be processed, otherwise + * {@code false} to stop the iteration early. + */ + boolean process(int index, ReadableComponent component); + } + + /** + * A processor of {@linkplain WritableComponent writable components}. + */ + @FunctionalInterface + interface WritableComponentProcessor extends ComponentProcessor { + /** + * Process the given component at the given index in the + * {@link Buf#forEachWritable(int, WritableComponentProcessor)} iteration}. + *

+ * The component object itself is only valid during this call, but the {@link ByteBuffer byte buffers}, arrays, + * and native address pointers obtained from it, will be valid until any + * {@link Buf#isOwned() ownership} requiring operation is performed on the buffer. + * + * @param index The current index of the given buffer component, based on the initial index passed to the + * {@link Buf#forEachWritable(int, WritableComponentProcessor)} method. + * @param component The current buffer component being processed. + * @return {@code true} if the iteration should continue and more components should be processed, otherwise + * {@code false} to stop the iteration early. + */ + boolean process(int index, WritableComponent component); + } + + /** + * A view onto the buffer component being processed in a given iteration of + * {@link Buf#forEachReadable(int, ReadableComponentProcessor)}. + */ + interface ReadableComponent { + + /** + * Check if this component is backed by a cached byte array than can be accessed cheaply. + *

+ * Note that regardless of what this method returns, the array should not be used to modify the + * contents of this buffer component. + * + * @return {@code true} if {@link #readableArray()} is a cheap operation, otherwise {@code false}. + */ + boolean hasReadableArray(); + + /** + * Get a byte array of the contents of this component. + *

+ * Note that the array is meant to be read-only. It may either be a direct reference to the + * concrete array instance that is backing this component, or it is a fresh copy. + * Writing to the array may produce undefined behaviour. + * + * @return A byte array of the contents of this component. + * @throws UnsupportedOperationException if {@link #hasReadableArray()} returns {@code false}. + */ + byte[] readableArray(); + + /** + * An offset into the {@link #readableArray()} where this component starts. + * + * @return An offset into {@link #readableArray()}. + * @throws UnsupportedOperationException if {@link #hasReadableArray()} returns {@code false}. + */ + int readableArrayOffset(); + + /** + * Give the native memory address backing this buffer, or return 0 if this buffer has no native memory address. + *

+ * Note that the address should not be used for writing to the buffer memory, and doing so may + * produce undefined behaviour. + * + * @return The native memory address, if any, otherwise 0. + */ + long readableNativeAddress(); + + /** + * Get a {@link ByteBuffer} instance for this memory component. + *

+ * Note that the {@link ByteBuffer} is read-only, to prevent write accesses to the memory, + * when the buffer component is obtained through {@link Buf#forEachReadable(int, ReadableComponentProcessor)}. + * + * @return A new {@link ByteBuffer}, with its own position and limit, for this memory component. + */ + ByteBuffer readableBuffer(); + } + + /** + * A view onto the buffer component being processed in a given iteration of + * {@link Buf#forEachWritable(int, WritableComponentProcessor)}. + */ + interface WritableComponent { + + /** + * Check if this component is backed by a cached byte array than can be accessed cheaply. + * + * @return {@code true} if {@link #writableArray()} is a cheap operation, otherwise {@code false}. + */ + boolean hasWritableArray(); + + /** + * Get a byte array of the contents of this component. + * + * @return A byte array of the contents of this component. + * @throws UnsupportedOperationException if {@link #hasWritableArray()} returns {@code false}. + */ + byte[] writableArray(); + + /** + * An offset into the {@link #writableArray()} where this component starts. + * + * @return An offset into {@link #writableArray()}. + * @throws UnsupportedOperationException if {@link #hasWritableArray()} returns {@code false}. + */ + int writableArrayOffset(); + + /** + * Give the native memory address backing this buffer, or return 0 if this buffer has no native memory address. + * + * @return The native memory address, if any, otherwise 0. + */ + long writableNativeAddress(); + + /** + * Get a {@link ByteBuffer} instance for this memory component, which can be used for modifying the buffer + * contents. + * + * @return A new {@link ByteBuffer}, with its own position and limit, for this memory component. + */ + ByteBuffer writableBuffer(); + } +} diff --git a/src/main/java/io/netty/buffer/api/CompositeBuf.java b/src/main/java/io/netty/buffer/api/CompositeBuf.java index e7c6208..b4011b8 100644 --- a/src/main/java/io/netty/buffer/api/CompositeBuf.java +++ b/src/main/java/io/netty/buffer/api/CompositeBuf.java @@ -15,6 +15,9 @@ */ package io.netty.buffer.api; +import io.netty.buffer.api.ComponentProcessor.ReadableComponentProcessor; +import io.netty.buffer.api.ComponentProcessor.WritableComponentProcessor; + import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; @@ -48,7 +51,19 @@ final class CompositeBuf extends RcSupport implements Buf { private boolean readOnly; CompositeBuf(Allocator allocator, Buf[] bufs) { - this(allocator, true, bufs.clone(), COMPOSITE_DROP); // Clone prevents external modification of array. + this(allocator, true, filterExternalBufs(bufs), COMPOSITE_DROP); + } + + private static Buf[] filterExternalBufs(Buf[] bufs) { + // We filter out all zero-capacity buffers because they wouldn't contribute to the composite buffer anyway, + // and also, by ensuring that all constituent buffers contribute to the size of the composite buffer, + // we make sure that the number of composite buffers will never become greater than the number of bytes in + // the composite buffer. + // This restriction guarantees that methods like countComponents, forEachReadable and forEachWritable, + // will never overflow their component counts. + // Allocating a new array unconditionally also prevents external modification of the array. + // TODO if any buffer is itself a composite buffer, then we should unwrap its sub-buffers + return Arrays.stream(bufs).filter(b -> b.capacity() > 0).toArray(Buf[]::new); } private CompositeBuf(Allocator allocator, boolean isSendable, Buf[] bufs, Drop drop) { @@ -203,7 +218,7 @@ final class CompositeBuf extends RcSupport implements Buf { } @Override - public long getNativeAddress() { + public long nativeAddress() { return 0; } @@ -616,7 +631,17 @@ final class CompositeBuf extends RcSupport implements Buf { (extension.readOnly()? "read-only." : "writable.")); } - long newSize = capacity() + (long) extension.capacity(); + long extensionCapacity = extension.capacity(); + if (extensionCapacity == 0) { + // Extending by a zero-sized buffer makes no difference. Especially since it's not allowed to change the + // capacity of buffers that are constiuents of composite buffers. + // This also ensures that methods like countComponents, and forEachReadable, do not have to worry about + // overflow in their component counters. + return; + } + // TODO if extension is itself a composite buffer, then we should extend ourselves by all of the sub-buffers + + long newSize = capacity() + extensionCapacity; Allocator.checkSize(newSize); Buf[] restoreTemp = bufs; // We need this to restore our buffer array, in case offset computations fail. @@ -700,6 +725,69 @@ final class CompositeBuf extends RcSupport implements Buf { writerOffset(woff - distance); } + @Override + public int countComponents() { + int sum = 0; + for (Buf buf : bufs) { + sum += buf.countComponents(); + } + return sum; + } + + @Override + public int countReadableComponents() { + int sum = 0; + for (Buf buf : bufs) { + sum += buf.countReadableComponents(); + } + return sum; + } + + @Override + public int countWritableComponents() { + int sum = 0; + for (Buf buf : bufs) { + sum += buf.countWritableComponents(); + } + return sum; + } + + @Override + public int forEachReadable(int initialIndex, ReadableComponentProcessor processor) { + checkReadBounds(readerOffset(), Math.max(1, readableBytes())); + int visited = 0; + for (Buf buf : bufs) { + if (buf.readableBytes() > 0) { + int count = buf.forEachReadable(visited + initialIndex, processor); + if (count > 0) { + visited += count; + } else { + visited = -visited + count; + break; + } + } + } + return visited; + } + + @Override + public int forEachWritable(int initialIndex, WritableComponentProcessor processor) { + checkWriteBounds(writerOffset(), Math.max(1, writableBytes())); + int visited = 0; + for (Buf buf : bufs) { + if (buf.writableBytes() > 0) { + int count = buf.forEachWritable(visited + initialIndex, processor); + if (count > 0) { + visited += count; + } else { + visited = -visited + count; + break; + } + } + } + return visited; + } + // @Override public byte readByte() { diff --git a/src/main/java/io/netty/buffer/api/memseg/MemSegBuf.java b/src/main/java/io/netty/buffer/api/memseg/MemSegBuf.java index bc901e3..8ba7408 100644 --- a/src/main/java/io/netty/buffer/api/memseg/MemSegBuf.java +++ b/src/main/java/io/netty/buffer/api/memseg/MemSegBuf.java @@ -19,6 +19,10 @@ import io.netty.buffer.api.Allocator; import io.netty.buffer.api.AllocatorControl; import io.netty.buffer.api.Buf; import io.netty.buffer.api.ByteCursor; +import io.netty.buffer.api.ComponentProcessor.ReadableComponent; +import io.netty.buffer.api.ComponentProcessor.ReadableComponentProcessor; +import io.netty.buffer.api.ComponentProcessor.WritableComponent; +import io.netty.buffer.api.ComponentProcessor.WritableComponentProcessor; import io.netty.buffer.api.Drop; import io.netty.buffer.api.Owned; import io.netty.buffer.api.RcSupport; @@ -42,7 +46,7 @@ import static jdk.incubator.foreign.MemoryAccess.setIntAtOffset; import static jdk.incubator.foreign.MemoryAccess.setLongAtOffset; import static jdk.incubator.foreign.MemoryAccess.setShortAtOffset; -class MemSegBuf extends RcSupport implements Buf { +class MemSegBuf extends RcSupport implements Buf, ReadableComponent, WritableComponent { private static final MemorySegment CLOSED_SEGMENT; static final Drop SEGMENT_CLOSE; @@ -129,8 +133,78 @@ class MemSegBuf extends RcSupport implements Buf { return this; } + // @Override - public long getNativeAddress() { + public boolean hasReadableArray() { + return false; + } + + @Override + public byte[] readableArray() { + throw new UnsupportedOperationException("This component has no backing array."); + } + + @Override + public int readableArrayOffset() { + throw new UnsupportedOperationException("This component has no backing array."); + } + + @Override + public long readableNativeAddress() { + return nativeAddress(); + } + + @Override + public ByteBuffer readableBuffer() { + var buffer = seg.asByteBuffer(); + if (buffer.isDirect()) { + // TODO Remove this when the slicing of shared, native segments JDK bug is fixed. + // See https://mail.openjdk.java.net/pipermail/panama-dev/2021-January/011810.html + ByteBuffer tmp = ByteBuffer.allocateDirect(buffer.capacity()); + tmp.put(buffer); + buffer = tmp.position(0); + } + buffer = buffer.asReadOnlyBuffer(); + // TODO avoid slicing and just set position+limit when JDK bug is fixed. + return buffer.slice(readerOffset(), readableBytes()).order(order); + } + + @Override + public boolean hasWritableArray() { + return false; + } + + @Override + public byte[] writableArray() { + throw new UnsupportedOperationException("This component has no backing array."); + } + + @Override + public int writableArrayOffset() { + throw new UnsupportedOperationException("This component has no backing array."); + } + + @Override + public long writableNativeAddress() { + return nativeAddress(); + } + + @Override + public ByteBuffer writableBuffer() { + var buffer = wseg.asByteBuffer(); + + if (buffer.isDirect()) { + buffer = buffer.position(writerOffset()).limit(writerOffset() + writableBytes()); + } else { + // TODO avoid slicing and just set position when JDK bug is fixed. + buffer = buffer.slice(writerOffset(), writableBytes()); + } + return buffer.order(order); + } + // + + @Override + public long nativeAddress() { try { return seg.address().toRawLongValue(); } catch (UnsupportedOperationException e) { @@ -458,6 +532,33 @@ class MemSegBuf extends RcSupport implements Buf { woff -= distance; } + @Override + public int countComponents() { + return 1; + } + + @Override + public int countReadableComponents() { + return readableBytes() > 0? 1 : 0; + } + + @Override + public int countWritableComponents() { + return writableBytes() > 0? 1 : 0; + } + + @Override + public int forEachReadable(int initialIndex, ReadableComponentProcessor processor) { + checkRead(readerOffset(), Math.max(1, readableBytes())); + return processor.process(initialIndex, this)? 1 : -1; + } + + @Override + public int forEachWritable(int initialIndex, WritableComponentProcessor processor) { + checkWrite(writerOffset(), Math.max(1, writableBytes())); + return processor.process(initialIndex, this)? 1 : -1; + } + // @Override public byte readByte() { @@ -969,13 +1070,13 @@ class MemSegBuf extends RcSupport implements Buf { private void checkRead(int index, int size) { if (index < 0 || woff < index + size) { - throw accessCheckException(index); + throw readAccessCheckException(index); } } private void checkWrite(int index, int size) { if (index < 0 || wseg.byteSize() < index + size) { - throw accessCheckException(index); + throw writeAccessCheckException(index); } } @@ -989,16 +1090,21 @@ class MemSegBuf extends RcSupport implements Buf { return ioobe; } - private RuntimeException accessCheckException(int index) { + private RuntimeException readAccessCheckException(int index) { + if (seg == CLOSED_SEGMENT) { + throw bufferIsClosed(); + } + return outOfBounds(index); + } + + private RuntimeException writeAccessCheckException(int index) { if (seg == CLOSED_SEGMENT) { throw bufferIsClosed(); } if (wseg != seg) { return bufferIsReadOnly(); } - return new IndexOutOfBoundsException( - "Index " + index + " is out of bounds: [read 0 to " + woff + ", write 0 to " + - (seg.byteSize() - 1) + "]."); + return outOfBounds(index); } private static IllegalStateException bufferIsClosed() { @@ -1009,6 +1115,12 @@ class MemSegBuf extends RcSupport implements Buf { return new IllegalStateException("This buffer is read-only."); } + private IndexOutOfBoundsException outOfBounds(int index) { + return new IndexOutOfBoundsException( + "Index " + index + " is out of bounds: [read 0 to " + woff + ", write 0 to " + + (seg.byteSize() - 1) + "]."); + } + Object recoverableMemory() { return new RecoverableMemory(seg, alloc); } diff --git a/src/test/java/io/netty/buffer/api/BufTest.java b/src/test/java/io/netty/buffer/api/BufTest.java index 52a2186..e5c4bf5 100644 --- a/src/test/java/io/netty/buffer/api/BufTest.java +++ b/src/test/java/io/netty/buffer/api/BufTest.java @@ -29,8 +29,10 @@ import org.junit.jupiter.params.provider.MethodSource; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.nio.ReadOnlyBufferException; import java.text.ParseException; import java.util.Arrays; +import java.util.LinkedList; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; @@ -38,6 +40,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.stream.Stream; import java.util.stream.Stream.Builder; @@ -47,6 +50,8 @@ import static io.netty.buffer.api.Fixture.Properties.COMPOSITE; import static io.netty.buffer.api.Fixture.Properties.DIRECT; import static io.netty.buffer.api.Fixture.Properties.HEAP; import static io.netty.buffer.api.Fixture.Properties.POOLED; +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; @@ -77,6 +82,10 @@ public class BufTest { return fixtureCombinations().filter(f -> !f.isSlice()); } + static Stream nonCompositeAllocators() { + return fixtureCombinations().filter(f -> !f.isComposite()); + } + static Stream heapAllocators() { return fixtureCombinations().filter(Fixture::isHeap); } @@ -222,6 +231,7 @@ public class BufTest { private static Stream injectSlices(Fixture f) { Builder builder = Stream.builder(); builder.add(f); + var props = concat(f.getProperties(), Properties.SLICE); builder.add(new Fixture(f + ".slice(0, capacity())", () -> { var allocatorBase = f.get(); return new Allocator() { @@ -237,7 +247,7 @@ public class BufTest { allocatorBase.close(); } }; - }, Properties.SLICE)); + }, props)); builder.add(new Fixture(f + ".slice(1, capacity() - 2)", () -> { var allocatorBase = f.get(); return new Allocator() { @@ -253,7 +263,7 @@ public class BufTest { allocatorBase.close(); } }; - }, Properties.SLICE)); + }, props)); return builder.build(); } @@ -277,6 +287,12 @@ public class BufTest { return builder.build(); } + private static Properties[] concat(Properties[] props, Properties prop) { + props = Arrays.copyOf(props, props.length + 1); + props[props.length - 1] = prop; + return props; + } + @BeforeAll static void startExecutor() throws IOException, ParseException { executor = Executors.newSingleThreadExecutor(); @@ -754,12 +770,12 @@ public class BufTest { void sliceWithoutOffsetAndSizeHasSameEndianAsParent(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); buf.writeLong(0x0102030405060708L); try (Buf slice = buf.slice()) { assertEquals(0x0102030405060708L, slice.readLong()); } - buf.order(ByteOrder.LITTLE_ENDIAN); + buf.order(LITTLE_ENDIAN); try (Buf slice = buf.slice()) { assertEquals(0x0807060504030201L, slice.readLong()); } @@ -771,12 +787,12 @@ public class BufTest { void sliceWithOffsetAndSizeHasSameEndianAsParent(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); buf.writeLong(0x0102030405060708L); try (Buf slice = buf.slice(0, 8)) { assertEquals(0x0102030405060708L, slice.readLong()); } - buf.order(ByteOrder.LITTLE_ENDIAN); + buf.order(LITTLE_ENDIAN); try (Buf slice = buf.slice(0, 8)) { assertEquals(0x0807060504030201L, slice.readLong()); } @@ -888,12 +904,12 @@ public class BufTest { void copyIntoByteArray(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN).writeLong(0x0102030405060708L); + buf.order(BIG_ENDIAN).writeLong(0x0102030405060708L); byte[] array = new byte[8]; buf.copyInto(0, array, 0, array.length); assertThat(array).containsExactly(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08); - buf.writerOffset(0).order(ByteOrder.LITTLE_ENDIAN).writeLong(0x0102030405060708L); + buf.writerOffset(0).order(LITTLE_ENDIAN).writeLong(0x0102030405060708L); buf.copyInto(0, array, 0, array.length); assertThat(array).containsExactly(0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01); @@ -918,7 +934,7 @@ public class BufTest { private static void testCopyIntoByteBuffer(Fixture fixture, Function bbAlloc) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN).writeLong(0x0102030405060708L); + buf.order(BIG_ENDIAN).writeLong(0x0102030405060708L); ByteBuffer buffer = bbAlloc.apply(8); buf.copyInto(0, buffer, 0, buffer.capacity()); assertEquals((byte) 0x01, buffer.get()); @@ -931,7 +947,7 @@ public class BufTest { assertEquals((byte) 0x08, buffer.get()); buffer.clear(); - buf.writerOffset(0).order(ByteOrder.LITTLE_ENDIAN).writeLong(0x0102030405060708L); + 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()); @@ -1131,7 +1147,7 @@ public class BufTest { private static void testCopyIntoBuf(Fixture fixture, Function bbAlloc) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN).writeLong(0x0102030405060708L); + buf.order(BIG_ENDIAN).writeLong(0x0102030405060708L); Buf buffer = bbAlloc.apply(8); buffer.writerOffset(8); buf.copyInto(0, buffer, 0, buffer.capacity()); @@ -1145,7 +1161,7 @@ public class BufTest { assertEquals((byte) 0x08, buffer.readByte()); buffer.reset(); - buf.writerOffset(0).order(ByteOrder.LITTLE_ENDIAN).writeLong(0x0102030405060708L); + buf.writerOffset(0).order(LITTLE_ENDIAN).writeLong(0x0102030405060708L); buf.copyInto(0, buffer, 0, buffer.capacity()); buffer.writerOffset(8); assertEquals((byte) 0x08, buffer.readByte()); @@ -1186,7 +1202,7 @@ public class BufTest { buffer.close(); buf.reset(); - buf.order(ByteOrder.BIG_ENDIAN).writeLong(0x0102030405060708L); + buf.order(BIG_ENDIAN).writeLong(0x0102030405060708L); // Testing copyInto for overlapping writes: // // 0x0102030405060708 @@ -1216,7 +1232,7 @@ public class BufTest { void byteIterationOfBigEndianBuffers(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(0x28)) { - buf.order(ByteOrder.BIG_ENDIAN); // The byte order should have no impact. + buf.order(BIG_ENDIAN); // The byte order should have no impact. checkByteIteration(buf); buf.reset(); checkByteIterationOfRegion(buf); @@ -1228,7 +1244,7 @@ public class BufTest { void byteIterationOfLittleEndianBuffers(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(0x28)) { - buf.order(ByteOrder.LITTLE_ENDIAN); // The byte order should have no impact. + buf.order(LITTLE_ENDIAN); // The byte order should have no impact. checkByteIteration(buf); buf.reset(); checkByteIterationOfRegion(buf); @@ -1373,7 +1389,7 @@ public class BufTest { void reverseByteIterationOfBigEndianBuffers(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(0x28)) { - buf.order(ByteOrder.BIG_ENDIAN); // The byte order should have no impact. + buf.order(BIG_ENDIAN); // The byte order should have no impact. checkReverseByteIteration(buf); buf.reset(); checkReverseByteIterationOfRegion(buf); @@ -1385,7 +1401,7 @@ public class BufTest { void reverseByteIterationOfLittleEndianBuffers(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(0x28)) { - buf.order(ByteOrder.LITTLE_ENDIAN); // The byte order should have no impact. + buf.order(LITTLE_ENDIAN); // The byte order should have no impact. checkReverseByteIteration(buf); buf.reset(); checkReverseByteIterationOfRegion(buf); @@ -1530,7 +1546,7 @@ public class BufTest { public void heapBufferMustHaveZeroAddress(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - assertThat(buf.getNativeAddress()).isZero(); + assertThat(buf.nativeAddress()).isZero(); } } @@ -1539,7 +1555,7 @@ public class BufTest { public void directBufferMustHaveNonZeroAddress(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - assertThat(buf.getNativeAddress()).isNotZero(); + assertThat(buf.nativeAddress()).isNotZero(); } } @@ -1712,7 +1728,7 @@ public class BufTest { public void ensureWritableOnCompositeBuffersMustRespectExistingBigEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator()) { Buf composite; - try (Buf a = allocator.allocate(4, ByteOrder.BIG_ENDIAN)) { + try (Buf a = allocator.allocate(4, BIG_ENDIAN)) { composite = allocator.compose(a); } try (composite) { @@ -1729,7 +1745,7 @@ public class BufTest { public void ensureWritableOnCompositeBuffersMustRespectExistingLittleEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator()) { Buf composite; - try (Buf a = allocator.allocate(4, ByteOrder.LITTLE_ENDIAN)) { + try (Buf a = allocator.allocate(4, LITTLE_ENDIAN)) { composite = allocator.compose(a); } try (composite) { @@ -1782,7 +1798,7 @@ public class BufTest { assertThat(bytes).containsExactly(0, 0, 0, 0, 0, 0, 0, 0); var tlr = ThreadLocalRandom.current(); - buf.order(tlr.nextBoolean()? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN); + buf.order(tlr.nextBoolean()? LITTLE_ENDIAN : BIG_ENDIAN); for (int j = 0; j < tlr.nextInt(0, 8); j++) { buf.writeByte((byte) 1); } @@ -1859,7 +1875,7 @@ public class BufTest { try (Allocator allocator = Allocator.heap(); Buf composite = allocator.compose()) { assertThat(composite.capacity()).isZero(); - try (Buf buf = allocator.allocate(8, ByteOrder.BIG_ENDIAN)) { + try (Buf buf = allocator.allocate(8, BIG_ENDIAN)) { Allocator.extend(composite, buf); } assertThat(composite.capacity()).isEqualTo(8); @@ -1873,7 +1889,7 @@ public class BufTest { try (Allocator allocator = Allocator.heap(); Buf composite = allocator.compose()) { assertThat(composite.capacity()).isZero(); - try (Buf buf = allocator.allocate(8, ByteOrder.LITTLE_ENDIAN)) { + try (Buf buf = allocator.allocate(8, LITTLE_ENDIAN)) { Allocator.extend(composite, buf); } assertThat(composite.capacity()).isEqualTo(8); @@ -1886,11 +1902,11 @@ public class BufTest { public void extendingBigEndianCompositeBufferMustThrowIfExtensionIsLittleEndian() { try (Allocator allocator = Allocator.heap()) { Buf composite; - try (Buf a = allocator.allocate(8, ByteOrder.BIG_ENDIAN)) { + try (Buf a = allocator.allocate(8, BIG_ENDIAN)) { composite = allocator.compose(a); } try (composite) { - try (Buf b = allocator.allocate(8, ByteOrder.LITTLE_ENDIAN)) { + try (Buf b = allocator.allocate(8, LITTLE_ENDIAN)) { var exc = assertThrows(IllegalArgumentException.class, () -> Allocator.extend(composite, b)); assertThat(exc).hasMessageContaining("byte order"); } @@ -1902,11 +1918,11 @@ public class BufTest { public void extendingLittleEndianCompositeBufferMustThrowIfExtensionIsBigEndian() { try (Allocator allocator = Allocator.heap()) { Buf composite; - try (Buf a = allocator.allocate(8, ByteOrder.LITTLE_ENDIAN)) { + try (Buf a = allocator.allocate(8, LITTLE_ENDIAN)) { composite = allocator.compose(a); } try (composite) { - try (Buf b = allocator.allocate(8, ByteOrder.BIG_ENDIAN)) { + try (Buf b = allocator.allocate(8, BIG_ENDIAN)) { var exc = assertThrows(IllegalArgumentException.class, () -> Allocator.extend(composite, b)); assertThat(exc).hasMessageContaining("byte order"); } @@ -1918,9 +1934,9 @@ public class BufTest { public void emptyCompositeBufferMustAllowExtendingWithBufferWithBigEndianByteOrder() { try (Allocator allocator = Allocator.heap()) { try (Buf composite = allocator.compose()) { - try (Buf b = allocator.allocate(8, ByteOrder.BIG_ENDIAN)) { + try (Buf b = allocator.allocate(8, BIG_ENDIAN)) { Allocator.extend(composite, b); - assertThat(composite.order()).isEqualTo(ByteOrder.BIG_ENDIAN); + assertThat(composite.order()).isEqualTo(BIG_ENDIAN); } } } @@ -1930,9 +1946,9 @@ public class BufTest { public void emptyCompositeBufferMustAllowExtendingWithBufferWithLittleEndianByteOrder() { try (Allocator allocator = Allocator.heap()) { try (Buf composite = allocator.compose()) { - try (Buf b = allocator.allocate(8, ByteOrder.LITTLE_ENDIAN)) { + try (Buf b = allocator.allocate(8, LITTLE_ENDIAN)) { Allocator.extend(composite, b); - assertThat(composite.order()).isEqualTo(ByteOrder.LITTLE_ENDIAN); + assertThat(composite.order()).isEqualTo(LITTLE_ENDIAN); } } } @@ -2040,8 +2056,8 @@ public class BufTest { @Test public void composeMustThrowWhenBuffersHaveMismatchedByteOrder() { try (Allocator allocator = Allocator.heap(); - Buf a = allocator.allocate(4, ByteOrder.BIG_ENDIAN); - Buf b = allocator.allocate(4, ByteOrder.LITTLE_ENDIAN)) { + Buf a = allocator.allocate(4, BIG_ENDIAN); + Buf b = allocator.allocate(4, LITTLE_ENDIAN)) { assertThrows(IllegalArgumentException.class, () -> allocator.compose(a, b)); } } @@ -2063,7 +2079,7 @@ public class BufTest { @MethodSource("nonSliceAllocators") public void bifurcatedPartMustContainFirstHalfOfBuffer(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); - Buf buf = allocator.allocate(16).order(ByteOrder.BIG_ENDIAN)) { + Buf buf = allocator.allocate(16).order(BIG_ENDIAN)) { buf.writeLong(0x0102030405060708L); assertThat(buf.readByte()).isEqualTo((byte) 0x01); try (Buf bif = buf.bifurcate()) { @@ -2099,7 +2115,7 @@ public class BufTest { @MethodSource("nonSliceAllocators") public void bifurcatedPartsMustBeIndividuallySendable(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); - Buf buf = allocator.allocate(16).order(ByteOrder.BIG_ENDIAN)) { + Buf buf = allocator.allocate(16).order(BIG_ENDIAN)) { buf.writeLong(0x0102030405060708L); assertThat(buf.readByte()).isEqualTo((byte) 0x01); try (Buf sentBif = buf.bifurcate().send().receive()) { @@ -2128,7 +2144,7 @@ public class BufTest { @MethodSource("nonSliceAllocators") public void mustBePossibleToBifurcateMoreThanOnce(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); - Buf buf = allocator.allocate(16).order(ByteOrder.BIG_ENDIAN)) { + Buf buf = allocator.allocate(16).order(BIG_ENDIAN)) { buf.writeLong(0x0102030405060708L); try (Buf a = buf.bifurcate()) { a.writerOffset(4); @@ -2156,15 +2172,15 @@ public class BufTest { @MethodSource("nonSliceAllocators") public void bifurcatedBufferMustHaveSameByteOrderAsParent(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); - Buf buf = allocator.allocate(8).order(ByteOrder.BIG_ENDIAN)) { + Buf buf = allocator.allocate(8).order(BIG_ENDIAN)) { buf.writeLong(0x0102030405060708L); try (Buf a = buf.bifurcate()) { - assertThat(a.order()).isEqualTo(ByteOrder.BIG_ENDIAN); - a.order(ByteOrder.LITTLE_ENDIAN); + assertThat(a.order()).isEqualTo(BIG_ENDIAN); + a.order(LITTLE_ENDIAN); a.writerOffset(4); try (Buf b = a.bifurcate()) { - assertThat(b.order()).isEqualTo(ByteOrder.LITTLE_ENDIAN); - assertThat(buf.order()).isEqualTo(ByteOrder.BIG_ENDIAN); + assertThat(b.order()).isEqualTo(LITTLE_ENDIAN); + assertThat(buf.order()).isEqualTo(BIG_ENDIAN); } } } @@ -2193,7 +2209,7 @@ public class BufTest { @MethodSource("nonSliceAllocators") public void ensureWritableOnBifurcatedBuffersWithOddOffsets(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); - Buf buf = allocator.allocate(10).order(ByteOrder.BIG_ENDIAN)) { + Buf buf = allocator.allocate(10).order(BIG_ENDIAN)) { buf.writeLong(0x0102030405060708L); buf.writeByte((byte) 0x09); buf.readByte(); @@ -2213,7 +2229,7 @@ public class BufTest { @Test public void bifurcateOnEmptyBigEndianCompositeBuffer() { try (Allocator allocator = Allocator.heap(); - Buf buf = allocator.compose().order(ByteOrder.BIG_ENDIAN)) { + Buf buf = allocator.compose().order(BIG_ENDIAN)) { verifyBifurcateEmptyCompositeBuffer(buf); } } @@ -2221,7 +2237,7 @@ public class BufTest { @Test public void bifurcateOnEmptyLittleEndianCompositeBuffer() { try (Allocator allocator = Allocator.heap(); - Buf buf = allocator.compose().order(ByteOrder.LITTLE_ENDIAN)) { + Buf buf = allocator.compose().order(LITTLE_ENDIAN)) { verifyBifurcateEmptyCompositeBuffer(buf); } } @@ -2286,7 +2302,7 @@ public class BufTest { @MethodSource("nonSliceAllocators") public void compactMustDiscardReadBytes(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); - Buf buf = allocator.allocate(16, ByteOrder.BIG_ENDIAN)) { + Buf buf = allocator.allocate(16, BIG_ENDIAN)) { buf.writeLong(0x0102030405060708L).writeInt(0x090A0B0C); assertEquals(0x01020304, buf.readInt()); assertEquals(12, buf.writerOffset()); @@ -2308,7 +2324,7 @@ public class BufTest { @MethodSource("nonSliceAllocators") public void compactMustThrowForUnownedBuffer(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); - Buf buf = allocator.allocate(8, ByteOrder.BIG_ENDIAN)) { + Buf buf = allocator.allocate(8, BIG_ENDIAN)) { buf.writeLong(0x0102030405060708L); assertEquals((byte) 0x01, buf.readByte()); try (Buf ignore = buf.acquire()) { @@ -2568,6 +2584,394 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("nonCompositeAllocators") + public void componentCountOfNonCompositeBufferMustBeOne(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThat(buf.countComponents()).isOne(); + } + } + + @ParameterizedTest + @MethodSource("nonCompositeAllocators") + public void readableComponentCountMustBeOneIfThereAreReadableBytes(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThat(buf.countReadableComponents()).isZero(); + buf.writeByte((byte) 1); + assertThat(buf.countReadableComponents()).isOne(); + } + } + + @ParameterizedTest + @MethodSource("nonCompositeAllocators") + public void writableComponentCountMustBeOneIfThereAreWritableBytes(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThat(buf.countWritableComponents()).isOne(); + buf.writeLong(1); + assertThat(buf.countWritableComponents()).isZero(); + } + } + + @Test + public void compositeBufferComponentCountMustBeTransitiveSum() { + try (Allocator allocator = Allocator.heap()) { + Buf buf; + try (Buf a = allocator.allocate(8); + Buf b = allocator.allocate(8); + Buf c = allocator.allocate(8); + Buf x = allocator.compose(b, c)) { + buf = allocator.compose(a, x); + } + assertThat(buf.countComponents()).isEqualTo(3); + assertThat(buf.countReadableComponents()).isZero(); + assertThat(buf.countWritableComponents()).isEqualTo(3); + buf.writeInt(1); + assertThat(buf.countReadableComponents()).isOne(); + assertThat(buf.countWritableComponents()).isEqualTo(3); + buf.writeInt(1); + assertThat(buf.countReadableComponents()).isOne(); + assertThat(buf.countWritableComponents()).isEqualTo(2); + buf.writeInt(1); + assertThat(buf.countReadableComponents()).isEqualTo(2); + assertThat(buf.countWritableComponents()).isEqualTo(2); + buf.writeInt(1); + assertThat(buf.countReadableComponents()).isEqualTo(2); + assertThat(buf.countWritableComponents()).isOne(); + buf.writeInt(1); + assertThat(buf.countReadableComponents()).isEqualTo(3); + assertThat(buf.countWritableComponents()).isOne(); + buf.writeInt(1); + assertThat(buf.countReadableComponents()).isEqualTo(3); + assertThat(buf.countWritableComponents()).isZero(); + } + } + + @ParameterizedTest + @MethodSource("nonCompositeAllocators") + public void forEachReadableMustVisitBuffer(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf bufBERW = allocator.allocate(8).order(BIG_ENDIAN).writeLong(0x0102030405060708L); + Buf bufLERW = allocator.allocate(8).order(LITTLE_ENDIAN).writeLong(0x0102030405060708L); + Buf bufBERO = allocator.allocate(8).order(BIG_ENDIAN).writeLong(0x0102030405060708L).readOnly(true); + Buf bufLERO = allocator.allocate(8).order(LITTLE_ENDIAN).writeLong(0x0102030405060708L).readOnly(true)) { + verifyForEachReadableSingleComponent(fixture, bufBERW); + verifyForEachReadableSingleComponent(fixture, bufLERW); + verifyForEachReadableSingleComponent(fixture, bufBERO); + verifyForEachReadableSingleComponent(fixture, bufLERO); + } + } + + private static void verifyForEachReadableSingleComponent(Fixture fixture, Buf 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(); + if (buffer.order() == BIG_ENDIAN) { + assertThat(array).containsExactly(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08); + } else { + assertThat(array).containsExactly(0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01); + } + } + + assertThrows(ReadOnlyBufferException.class, () -> buffer.put(0, (byte) 0xFF)); + return true; + }); + } + + @Test + public void forEachReadableMustVisitAllReadableConstituentBuffersInOrder() { + try (Allocator allocator = Allocator.heap()) { + Buf composite; + try (Buf a = allocator.allocate(4); + Buf b = allocator.allocate(4); + Buf c = allocator.allocate(4)) { + a.writeInt(1); + b.writeInt(2); + c.writeInt(3); + composite = allocator.compose(a, b, c); + } + var list = new LinkedList(List.of(1, 2, 3)); + int count = composite.forEachReadable(0, (index, component) -> { + var buffer = component.readableBuffer(); + int bufferValue = buffer.getInt(); + assertEquals(list.pollFirst().intValue(), bufferValue); + assertEquals(bufferValue, index + 1); + assertThrows(ReadOnlyBufferException.class, () -> buffer.put(0, (byte) 0xFF)); + return true; + }); + assertEquals(3, count); + assertThat(list).isEmpty(); + } + } + + @ParameterizedTest + @MethodSource("allocators") + public void forEachReadableMustReturnNegativeCountWhenProcessorReturnsFalse(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + buf.writeLong(0x0102030405060708L); + int count = buf.forEachReadable(0, (index, component) -> false); + assertEquals(-1, count); + } + } + + @Test + public void forEachReadableMustStopIterationWhenProcessorReturnsFalse() { + try (Allocator allocator = Allocator.heap()) { + Buf composite; + try (Buf a = allocator.allocate(4); + Buf b = allocator.allocate(4); + Buf c = allocator.allocate(4)) { + a.writeInt(1); + b.writeInt(2); + c.writeInt(3); + composite = allocator.compose(a, b, c); + } + int readPos = composite.readerOffset(); + int writePos = composite.writerOffset(); + var list = new LinkedList(List.of(1, 2, 3)); + int count = composite.forEachReadable(0, (index, component) -> { + var buffer = component.readableBuffer(); + int bufferValue = buffer.getInt(); + assertEquals(list.pollFirst().intValue(), bufferValue); + assertEquals(bufferValue, index + 1); + return false; + }); + assertEquals(-1, count); + assertThat(list).containsExactly(2, 3); + assertEquals(readPos, composite.readerOffset()); + assertEquals(writePos, composite.writerOffset()); + } + } + + @ParameterizedTest + @MethodSource("allocators") + public void forEachReadableOnClosedBufferMustThrow(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator()) { + var buf = allocator.allocate(8); + buf.writeLong(0); + buf.close(); + assertThrows(IllegalStateException.class, () -> buf.forEachReadable(0, (component, index) -> true)); + } + } + + @ParameterizedTest + @MethodSource("allocators") + public void forEachReadableMustAllowCollectingBuffersInArray(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator()) { + Buf buf; + try (Buf a = allocator.allocate(4); + Buf b = allocator.allocate(4); + Buf c = allocator.allocate(4)) { + buf = allocator.compose(a, b, c); + } + int i = 1; + while (buf.writableBytes() > 0) { + buf.writeByte((byte) i++); + } + ByteBuffer[] buffers = new ByteBuffer[buf.countReadableComponents()]; + buf.forEachReadable(0, (index, component) -> { + buffers[index] = component.readableBuffer(); + return true; + }); + i = 1; + assertThat(buffers.length).isGreaterThanOrEqualTo(1); + for (ByteBuffer buffer : buffers) { + while (buffer.hasRemaining()) { + assertEquals((byte) i++, buffer.get()); + } + } + } + } + + @ParameterizedTest + @MethodSource("nonCompositeAllocators") + public void forEachWritableMustVisitBuffer(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf bufBERW = allocator.allocate(8).order(BIG_ENDIAN); + Buf bufLERW = allocator.allocate(8).order(LITTLE_ENDIAN)) { + verifyForEachWritableSingleComponent(fixture, bufBERW); + verifyForEachWritableSingleComponent(fixture, bufLERW); + } + } + + private static void verifyForEachWritableSingleComponent(Fixture fixture, Buf 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(); + } + + if (component.hasWritableArray()) { + byte[] array = component.writableArray(); + if (buffer.order() == BIG_ENDIAN) { + assertThat(array).containsExactly(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08); + } else { + assertThat(array).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; + }); + } + + @Test + public void forEachWritableMustVisitAllWritableConstituentBuffersInOrder() { + try (Allocator allocator = Allocator.heap()) { + Buf buf; + try (Buf a = allocator.allocate(8); + Buf b = allocator.allocate(8); + Buf c = allocator.allocate(8)) { + buf = allocator.compose(a, b, c); + } + buf.order(BIG_ENDIAN); + buf.forEachWritable(0, (index, component) -> { + component.writableBuffer().putLong(0x0102030405060708L + 0x1010101010101010L * index); + return true; + }); + buf.writerOffset(3 * 8); + assertEquals(0x0102030405060708L, buf.readLong()); + assertEquals(0x1112131415161718L, buf.readLong()); + assertEquals(0x2122232425262728L, buf.readLong()); + } + } + + @ParameterizedTest + @MethodSource("allocators") + public void forEachWritableMustReturnNegativeCountWhenProcessorReturnsFalse(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + int count = buf.forEachWritable(0, (index, component) -> false); + assertEquals(-1, count); + } + } + + @ParameterizedTest + @MethodSource("allocators") + public void forEachWritableMustStopIterationWhenProcessorRetursFalse(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + AtomicInteger counter = new AtomicInteger(); + buf.forEachWritable(0, (index, component) -> { + counter.incrementAndGet(); + return false; + }); + assertEquals(1, counter.get()); + } + } + + @ParameterizedTest + @MethodSource("allocators") + public void forEachWritableChangesMadeToByteBufferComponentMustBeReflectedInBuffer(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(9).order(BIG_ENDIAN)) { + buf.writeByte((byte) 0xFF); + AtomicInteger writtenCounter = new AtomicInteger(); + buf.forEachWritable(0, (index, component) -> { + var buffer = component.writableBuffer(); + while (buffer.hasRemaining()) { + buffer.put((byte) writtenCounter.incrementAndGet()); + } + return true; + }); + buf.writerOffset(9); + assertEquals((byte) 0xFF, buf.readByte()); + assertEquals(0x0102030405060708L, buf.readLong()); + } + } + + @ParameterizedTest + @MethodSource("allocators") + public void changesMadeToByteBufferComponentsShouldBeReflectedInBuffer(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + AtomicInteger counter = new AtomicInteger(); + buf.forEachWritable(0, (index, component) -> { + var buffer = component.writableBuffer(); + while (buffer.hasRemaining()) { + buffer.put((byte) counter.incrementAndGet()); + } + return true; + }); + buf.writerOffset(buf.capacity()); + for (int i = 0; i < 8; i++) { + assertEquals((byte) i + 1, buf.getByte(i)); + } + } + } + + @ParameterizedTest + @MethodSource("allocators") + public void forEachWritableOnClosedBufferMustThrow(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator()) { + Buf buf = allocator.allocate(8); + buf.close(); + assertThrows(IllegalStateException.class, () -> buf.forEachWritable(0, (index, component) -> true)); + } + } + + @ParameterizedTest + @MethodSource("allocators") + public void forEachWritableOnReadOnlyBufferMustThrow(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8).readOnly(true)) { + assertThrows(IllegalStateException.class, () -> buf.forEachWritable(0, (index, component) -> true)); + } + } + + @ParameterizedTest + @MethodSource("allocators") + public void forEachWritableMustAllowCollectingBuffersInArray(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + ByteBuffer[] buffers = new ByteBuffer[buf.countWritableComponents()]; + buf.forEachWritable(0, (index, component) -> { + buffers[index] = component.writableBuffer(); + return true; + }); + assertThat(buffers.length).isGreaterThanOrEqualTo(1); + int i = 1; + for (ByteBuffer buffer : buffers) { + while (buffer.hasRemaining()) { + buffer.put((byte) i++); + } + } + buf.writerOffset(buf.capacity()); + i = 1; + while (buf.readableBytes() > 0) { + assertEquals((byte) i++, buf.readByte()); + } + } + } + // @ParameterizedTest @MethodSource("allocators") @@ -2590,7 +2994,7 @@ public class BufTest { void relativeReadOfByteMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); assertEquals(0, buf.readableBytes()); assertEquals(Long.BYTES, buf.writableBytes()); byte value = 0x01; @@ -2629,6 +3033,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfByteReadOnlyMustBoundsCheckOnNegativeOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getByte(-1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfByteMustNotBoundsCheckWhenReadOffsetAndSizeIsEqualToWriteOffset(Fixture fixture) { @@ -2645,7 +3058,7 @@ public class BufTest { void offsettedGetOfByteMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); byte value = 0x01; buf.writeByte(value); buf.setByte(0, (byte) 0x10); @@ -2664,6 +3077,17 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfByteReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsGreaterThanWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + byte value = 0x01; + buf.writeByte(value); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getByte(-1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfByteMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { @@ -2673,6 +3097,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfByteReadOnlyMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getByte(0)); + } + } + @ParameterizedTest @MethodSource("allocators") void relativeReadOfUnsignedByteMustNotBoundsCheckWhenReadOffsetAndSizeIsEqualToWriteOffset(Fixture fixture) { @@ -2694,7 +3127,7 @@ public class BufTest { void relativeReadOfUnsignedByteMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); assertEquals(0, buf.readableBytes()); assertEquals(Long.BYTES, buf.writableBytes()); int value = 0x01; @@ -2724,6 +3157,23 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void relativeReadOfUnsignedByteReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsBeyondWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertEquals(0, buf.readableBytes()); + assertEquals(Long.BYTES, buf.writableBytes()); + int value = 0x01; + buf.writeUnsignedByte(value); + buf.readerOffset(1); + assertEquals(0, buf.readableBytes()); + assertEquals(7, buf.writableBytes()); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).readUnsignedByte()); + assertEquals(0, buf.readableBytes()); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfUnsignedByteMustBoundsCheckOnNegativeOffset(Fixture fixture) { @@ -2733,6 +3183,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfUnsignedByteReadOnlyMustBoundsCheckOnNegativeOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getUnsignedByte(-1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfUnsignedByteMustNotBoundsCheckWhenReadOffsetAndSizeIsEqualToWriteOffset(Fixture fixture) { @@ -2749,7 +3208,7 @@ public class BufTest { void offsettedGetOfUnsignedByteMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); int value = 0x01; buf.writeUnsignedByte(value); buf.setByte(0, (byte) 0x10); @@ -2768,6 +3227,18 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfUnsignedByteReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsGreaterThanWriteOffset( + Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + int value = 0x01; + buf.writeUnsignedByte(value); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getUnsignedByte(1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfUnsignedByteMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { @@ -2777,6 +3248,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfUnsignedByteReadOnlyMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getUnsignedByte(0)); + } + } + @ParameterizedTest @MethodSource("allocators") void relativeWriteOfByteMustBoundsCheckWhenWriteOffsetAndSizeIsBeyondCapacity(Fixture fixture) { @@ -2797,7 +3277,7 @@ public class BufTest { void relativeWriteOfByteMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); byte value = 0x01; buf.writeByte(value); buf.writerOffset(Long.BYTES); @@ -2845,7 +3325,7 @@ public class BufTest { void offsettedSetOfByteMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); byte value = 0x01; buf.setByte(0, value); buf.writerOffset(Long.BYTES); @@ -2880,7 +3360,7 @@ public class BufTest { void relativeWriteOfUnsignedByteMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); int value = 0x01; buf.writeUnsignedByte(value); buf.writerOffset(Long.BYTES); @@ -2928,7 +3408,7 @@ public class BufTest { void offsettedSetOfUnsignedByteMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); int value = 0x01; buf.setUnsignedByte(0, value); buf.writerOffset(Long.BYTES); @@ -2964,7 +3444,7 @@ public class BufTest { void relativeReadOfCharMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); assertEquals(0, buf.readableBytes()); assertEquals(Long.BYTES, buf.writableBytes()); char value = 0x0102; @@ -2994,6 +3474,23 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void relativeReadOfCharReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsBeyondWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertEquals(0, buf.readableBytes()); + assertEquals(Long.BYTES, buf.writableBytes()); + char value = 0x0102; + buf.writeChar(value); + buf.readerOffset(1); + assertEquals(1, buf.readableBytes()); + assertEquals(6, buf.writableBytes()); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).readChar()); + assertEquals(1, buf.readableBytes()); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfCharMustBoundsCheckOnNegativeOffset(Fixture fixture) { @@ -3003,6 +3500,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfCharReadOnlyMustBoundsCheckOnNegativeOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getChar(-1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfCharMustNotBoundsCheckWhenReadOffsetAndSizeIsEqualToWriteOffset(Fixture fixture) { @@ -3019,7 +3525,7 @@ public class BufTest { void offsettedGetOfCharMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); char value = 0x0102; buf.writeChar(value); buf.setByte(0, (byte) 0x10); @@ -3038,6 +3544,17 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfCharReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsGreaterThanWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + char value = 0x0102; + buf.writeChar(value); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getChar(1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfCharMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { @@ -3047,6 +3564,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfCharReadOnlyMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getChar(0)); + } + } + @ParameterizedTest @MethodSource("allocators") void relativeWriteOfCharMustBoundsCheckWhenWriteOffsetAndSizeIsBeyondCapacity(Fixture fixture) { @@ -3067,7 +3593,7 @@ public class BufTest { void relativeWriteOfCharMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); char value = 0x0102; buf.writeChar(value); buf.writerOffset(Long.BYTES); @@ -3115,7 +3641,7 @@ public class BufTest { void offsettedSetOfCharMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); char value = 0x0102; buf.setChar(0, value); buf.writerOffset(Long.BYTES); @@ -3151,7 +3677,7 @@ public class BufTest { void relativeReadOfShortMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); assertEquals(0, buf.readableBytes()); assertEquals(Long.BYTES, buf.writableBytes()); short value = 0x0102; @@ -3181,6 +3707,23 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void relativeReadOfShortReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsBeyondWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertEquals(0, buf.readableBytes()); + assertEquals(Long.BYTES, buf.writableBytes()); + short value = 0x0102; + buf.writeShort(value); + buf.readerOffset(1); + assertEquals(1, buf.readableBytes()); + assertEquals(6, buf.writableBytes()); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).readShort()); + assertEquals(1, buf.readableBytes()); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfShortMustBoundsCheckOnNegativeOffset(Fixture fixture) { @@ -3190,6 +3733,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfShortReadOnlyMustBoundsCheckOnNegativeOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getShort(-1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfShortMustNotBoundsCheckWhenReadOffsetAndSizeIsEqualToWriteOffset(Fixture fixture) { @@ -3206,7 +3758,7 @@ public class BufTest { void offsettedGetOfShortMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); short value = 0x0102; buf.writeShort(value); buf.setByte(0, (byte) 0x10); @@ -3225,6 +3777,17 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfShortReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsGreaterThanWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + short value = 0x0102; + buf.writeShort(value); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getShort(1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfShortMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { @@ -3234,6 +3797,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfShortReadOnlyMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getShort(0)); + } + } + @ParameterizedTest @MethodSource("allocators") void relativeReadOfUnsignedShortMustNotBoundsCheckWhenReadOffsetAndSizeIsEqualToWriteOffset(Fixture fixture) { @@ -3255,7 +3827,7 @@ public class BufTest { void relativeReadOfUnsignedShortMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); assertEquals(0, buf.readableBytes()); assertEquals(Long.BYTES, buf.writableBytes()); int value = 0x0102; @@ -3285,6 +3857,23 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void relativeReadOfUnsignedShortReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsBeyondWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertEquals(0, buf.readableBytes()); + assertEquals(Long.BYTES, buf.writableBytes()); + int value = 0x0102; + buf.writeUnsignedShort(value); + buf.readerOffset(1); + assertEquals(1, buf.readableBytes()); + assertEquals(6, buf.writableBytes()); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).readUnsignedShort()); + assertEquals(1, buf.readableBytes()); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfUnsignedShortMustBoundsCheckOnNegativeOffset(Fixture fixture) { @@ -3294,6 +3883,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfUnsignedShortReadOnlyMustBoundsCheckOnNegativeOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getUnsignedShort(-1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfUnsignedShortMustNotBoundsCheckWhenReadOffsetAndSizeIsEqualToWriteOffset(Fixture fixture) { @@ -3310,7 +3908,7 @@ public class BufTest { void offsettedGetOfUnsignedShortMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); int value = 0x0102; buf.writeUnsignedShort(value); buf.setByte(0, (byte) 0x10); @@ -3329,6 +3927,18 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfUnsignedShortReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsGreaterThanWriteOffset( + Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + int value = 0x0102; + buf.writeUnsignedShort(value); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getUnsignedShort(1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfUnsignedShortMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { @@ -3338,6 +3948,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfUnsignedShortReadOnlyMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getUnsignedShort(0)); + } + } + @ParameterizedTest @MethodSource("allocators") void relativeWriteOfShortMustBoundsCheckWhenWriteOffsetAndSizeIsBeyondCapacity(Fixture fixture) { @@ -3358,7 +3977,7 @@ public class BufTest { void relativeWriteOfShortMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); short value = 0x0102; buf.writeShort(value); buf.writerOffset(Long.BYTES); @@ -3406,7 +4025,7 @@ public class BufTest { void offsettedSetOfShortMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); short value = 0x0102; buf.setShort(0, value); buf.writerOffset(Long.BYTES); @@ -3441,7 +4060,7 @@ public class BufTest { void relativeWriteOfUnsignedShortMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); int value = 0x0102; buf.writeUnsignedShort(value); buf.writerOffset(Long.BYTES); @@ -3489,7 +4108,7 @@ public class BufTest { void offsettedSetOfUnsignedShortMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); int value = 0x0102; buf.setUnsignedShort(0, value); buf.writerOffset(Long.BYTES); @@ -3525,7 +4144,7 @@ public class BufTest { void relativeReadOfMediumMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); assertEquals(0, buf.readableBytes()); assertEquals(Long.BYTES, buf.writableBytes()); int value = 0x010203; @@ -3555,6 +4174,23 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void relativeReadOfMediumReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsBeyondWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertEquals(0, buf.readableBytes()); + assertEquals(Long.BYTES, buf.writableBytes()); + int value = 0x010203; + buf.writeMedium(value); + buf.readerOffset(1); + assertEquals(2, buf.readableBytes()); + assertEquals(5, buf.writableBytes()); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).readMedium()); + assertEquals(2, buf.readableBytes()); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfMediumMustBoundsCheckOnNegativeOffset(Fixture fixture) { @@ -3564,6 +4200,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfMediumReadOnlyMustBoundsCheckOnNegativeOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getMedium(-1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfMediumMustNotBoundsCheckWhenReadOffsetAndSizeIsEqualToWriteOffset(Fixture fixture) { @@ -3580,7 +4225,7 @@ public class BufTest { void offsettedGetOfMediumMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); int value = 0x010203; buf.writeMedium(value); buf.setByte(0, (byte) 0x10); @@ -3599,6 +4244,17 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfMediumReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsGreaterThanWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + int value = 0x010203; + buf.writeMedium(value); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getMedium(1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfMediumMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { @@ -3608,6 +4264,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfMediumReadOnlyMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getMedium(0)); + } + } + @ParameterizedTest @MethodSource("allocators") void relativeReadOfUnsignedMediumMustNotBoundsCheckWhenReadOffsetAndSizeIsEqualToWriteOffset(Fixture fixture) { @@ -3629,7 +4294,7 @@ public class BufTest { void relativeReadOfUnsignedMediumMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); assertEquals(0, buf.readableBytes()); assertEquals(Long.BYTES, buf.writableBytes()); int value = 0x010203; @@ -3659,6 +4324,23 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void relativeReadOfUnsignedMediumReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsBeyondWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertEquals(0, buf.readableBytes()); + assertEquals(Long.BYTES, buf.writableBytes()); + int value = 0x010203; + buf.writeUnsignedMedium(value); + buf.readerOffset(1); + assertEquals(2, buf.readableBytes()); + assertEquals(5, buf.writableBytes()); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).readUnsignedMedium()); + assertEquals(2, buf.readableBytes()); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfUnsignedMediumMustBoundsCheckOnNegativeOffset(Fixture fixture) { @@ -3668,6 +4350,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfUnsignedMediumReadOnlyMustBoundsCheckOnNegativeOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getUnsignedMedium(-1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfUnsignedMediumMustNotBoundsCheckWhenReadOffsetAndSizeIsEqualToWriteOffset(Fixture fixture) { @@ -3684,7 +4375,7 @@ public class BufTest { void offsettedGetOfUnsignedMediumMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); int value = 0x010203; buf.writeUnsignedMedium(value); buf.setByte(0, (byte) 0x10); @@ -3703,6 +4394,18 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfUnsignedMediumReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsGreaterThanWriteOffset( + Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + int value = 0x010203; + buf.writeUnsignedMedium(value); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getUnsignedMedium(1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfUnsignedMediumMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { @@ -3712,6 +4415,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfUnsignedMediumReadOnlyMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getUnsignedMedium(0)); + } + } + @ParameterizedTest @MethodSource("allocators") void relativeWriteOfMediumMustBoundsCheckWhenWriteOffsetAndSizeIsBeyondCapacity(Fixture fixture) { @@ -3732,7 +4444,7 @@ public class BufTest { void relativeWriteOfMediumMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); int value = 0x010203; buf.writeMedium(value); buf.writerOffset(Long.BYTES); @@ -3780,7 +4492,7 @@ public class BufTest { void offsettedSetOfMediumMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); int value = 0x010203; buf.setMedium(0, value); buf.writerOffset(Long.BYTES); @@ -3815,7 +4527,7 @@ public class BufTest { void relativeWriteOfUnsignedMediumMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); int value = 0x010203; buf.writeUnsignedMedium(value); buf.writerOffset(Long.BYTES); @@ -3863,7 +4575,7 @@ public class BufTest { void offsettedSetOfUnsignedMediumMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); int value = 0x010203; buf.setUnsignedMedium(0, value); buf.writerOffset(Long.BYTES); @@ -3899,7 +4611,7 @@ public class BufTest { void relativeReadOfIntMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); assertEquals(0, buf.readableBytes()); assertEquals(Long.BYTES, buf.writableBytes()); int value = 0x01020304; @@ -3929,6 +4641,23 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void relativeReadOfIntReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsBeyondWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertEquals(0, buf.readableBytes()); + assertEquals(Long.BYTES, buf.writableBytes()); + int value = 0x01020304; + buf.writeInt(value); + buf.readerOffset(1); + assertEquals(3, buf.readableBytes()); + assertEquals(4, buf.writableBytes()); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).readInt()); + assertEquals(3, buf.readableBytes()); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfIntMustBoundsCheckOnNegativeOffset(Fixture fixture) { @@ -3938,6 +4667,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfIntReadOnlyMustBoundsCheckOnNegativeOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getInt(-1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfIntMustNotBoundsCheckWhenReadOffsetAndSizeIsEqualToWriteOffset(Fixture fixture) { @@ -3954,7 +4692,7 @@ public class BufTest { void offsettedGetOfIntMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); int value = 0x01020304; buf.writeInt(value); buf.setByte(0, (byte) 0x10); @@ -3973,6 +4711,17 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfIntReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsGreaterThanWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + int value = 0x01020304; + buf.writeInt(value); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getInt(-1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfIntMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { @@ -3982,6 +4731,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfIntReadOnlyMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getInt(0)); + } + } + @ParameterizedTest @MethodSource("allocators") void relativeReadOfUnsignedIntMustNotBoundsCheckWhenReadOffsetAndSizeIsEqualToWriteOffset(Fixture fixture) { @@ -4003,7 +4761,7 @@ public class BufTest { void relativeReadOfUnsignedIntMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); assertEquals(0, buf.readableBytes()); assertEquals(Long.BYTES, buf.writableBytes()); long value = 0x01020304; @@ -4033,6 +4791,23 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void relativeReadOfUnsignedIntReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsBeyondWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertEquals(0, buf.readableBytes()); + assertEquals(Long.BYTES, buf.writableBytes()); + long value = 0x01020304; + buf.writeUnsignedInt(value); + buf.readerOffset(1); + assertEquals(3, buf.readableBytes()); + assertEquals(4, buf.writableBytes()); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).readUnsignedInt()); + assertEquals(3, buf.readableBytes()); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfUnsignedIntMustBoundsCheckOnNegativeOffset(Fixture fixture) { @@ -4042,6 +4817,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfUnsignedIntReadOnlyMustBoundsCheckOnNegativeOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getUnsignedInt(-1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfUnsignedIntMustNotBoundsCheckWhenReadOffsetAndSizeIsEqualToWriteOffset(Fixture fixture) { @@ -4058,7 +4842,7 @@ public class BufTest { void offsettedGetOfUnsignedIntMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); long value = 0x01020304; buf.writeUnsignedInt(value); buf.setByte(0, (byte) 0x10); @@ -4077,6 +4861,18 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfUnsignedIntReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsGreaterThanWriteOffset( + Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + long value = 0x01020304; + buf.writeUnsignedInt(value); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getUnsignedInt(1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfUnsignedIntMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { @@ -4086,6 +4882,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfUnsignedIntReadOnlyMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getUnsignedInt(0)); + } + } + @ParameterizedTest @MethodSource("allocators") void relativeWriteOfIntMustBoundsCheckWhenWriteOffsetAndSizeIsBeyondCapacity(Fixture fixture) { @@ -4106,7 +4911,7 @@ public class BufTest { void relativeWriteOfIntMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); int value = 0x01020304; buf.writeInt(value); buf.writerOffset(Long.BYTES); @@ -4154,7 +4959,7 @@ public class BufTest { void offsettedSetOfIntMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); int value = 0x01020304; buf.setInt(0, value); buf.writerOffset(Long.BYTES); @@ -4189,7 +4994,7 @@ public class BufTest { void relativeWriteOfUnsignedIntMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); long value = 0x01020304; buf.writeUnsignedInt(value); buf.writerOffset(Long.BYTES); @@ -4237,7 +5042,7 @@ public class BufTest { void offsettedSetOfUnsignedIntMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); long value = 0x01020304; buf.setUnsignedInt(0, value); buf.writerOffset(Long.BYTES); @@ -4273,7 +5078,7 @@ public class BufTest { void relativeReadOfFloatMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); assertEquals(0, buf.readableBytes()); assertEquals(Long.BYTES, buf.writableBytes()); float value = Float.intBitsToFloat(0x01020304); @@ -4303,6 +5108,23 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void relativeReadOfFloatReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsBeyondWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertEquals(0, buf.readableBytes()); + assertEquals(Long.BYTES, buf.writableBytes()); + float value = Float.intBitsToFloat(0x01020304); + buf.writeFloat(value); + buf.readerOffset(1); + assertEquals(3, buf.readableBytes()); + assertEquals(4, buf.writableBytes()); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).readFloat()); + assertEquals(3, buf.readableBytes()); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfFloatMustBoundsCheckOnNegativeOffset(Fixture fixture) { @@ -4312,6 +5134,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfFloatReadOnlyMustBoundsCheckOnNegativeOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getFloat(-1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfFloatMustNotBoundsCheckWhenReadOffsetAndSizeIsEqualToWriteOffset(Fixture fixture) { @@ -4328,7 +5159,7 @@ public class BufTest { void offsettedGetOfFloatMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); float value = Float.intBitsToFloat(0x01020304); buf.writeFloat(value); buf.setByte(0, (byte) 0x10); @@ -4347,6 +5178,17 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfFloatReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsGreaterThanWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + float value = Float.intBitsToFloat(0x01020304); + buf.writeFloat(value); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getFloat(1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfFloatMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { @@ -4356,6 +5198,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfFloatReadOnlyMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getFloat(0)); + } + } + @ParameterizedTest @MethodSource("allocators") void relativeWriteOfFloatMustBoundsCheckWhenWriteOffsetAndSizeIsBeyondCapacity(Fixture fixture) { @@ -4376,7 +5227,7 @@ public class BufTest { void relativeWriteOfFloatMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); float value = Float.intBitsToFloat(0x01020304); buf.writeFloat(value); buf.writerOffset(Long.BYTES); @@ -4424,7 +5275,7 @@ public class BufTest { void offsettedSetOfFloatMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); float value = Float.intBitsToFloat(0x01020304); buf.setFloat(0, value); buf.writerOffset(Long.BYTES); @@ -4460,7 +5311,7 @@ public class BufTest { void relativeReadOfLongMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); assertEquals(0, buf.readableBytes()); assertEquals(Long.BYTES, buf.writableBytes()); long value = 0x0102030405060708L; @@ -4490,6 +5341,23 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void relativeReadOfLongReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsBeyondWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertEquals(0, buf.readableBytes()); + assertEquals(Long.BYTES, buf.writableBytes()); + long value = 0x0102030405060708L; + buf.writeLong(value); + buf.readerOffset(1); + assertEquals(7, buf.readableBytes()); + assertEquals(0, buf.writableBytes()); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).readLong()); + assertEquals(7, buf.readableBytes()); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfLongMustBoundsCheckOnNegativeOffset(Fixture fixture) { @@ -4499,6 +5367,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfLongReadOnlyMustBoundsCheckOnNegativeOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getLong(-1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfLongMustNotBoundsCheckWhenReadOffsetAndSizeIsEqualToWriteOffset(Fixture fixture) { @@ -4515,7 +5392,7 @@ public class BufTest { void offsettedGetOfLongMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); long value = 0x0102030405060708L; buf.writeLong(value); buf.setByte(0, (byte) 0x10); @@ -4534,6 +5411,17 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfLongReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsGreaterThanWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + long value = 0x0102030405060708L; + buf.writeLong(value); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getLong(1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfLongMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { @@ -4543,6 +5431,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfLongReadOnlyMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getLong(0)); + } + } + @ParameterizedTest @MethodSource("allocators") void relativeWriteOfLongMustBoundsCheckWhenWriteOffsetAndSizeIsBeyondCapacity(Fixture fixture) { @@ -4563,7 +5460,7 @@ public class BufTest { void relativeWriteOfLongMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); long value = 0x0102030405060708L; buf.writeLong(value); buf.writerOffset(Long.BYTES); @@ -4611,7 +5508,7 @@ public class BufTest { void offsettedSetOfLongMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); long value = 0x0102030405060708L; buf.setLong(0, value); buf.writerOffset(Long.BYTES); @@ -4647,7 +5544,7 @@ public class BufTest { void relativeReadOfDoubleMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); assertEquals(0, buf.readableBytes()); assertEquals(Long.BYTES, buf.writableBytes()); double value = Double.longBitsToDouble(0x0102030405060708L); @@ -4677,6 +5574,23 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void relativeReadOfDoubleReadOnllyMustBoundsCheckWhenReadOffsetAndSizeIsBeyondWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertEquals(0, buf.readableBytes()); + assertEquals(Long.BYTES, buf.writableBytes()); + double value = Double.longBitsToDouble(0x0102030405060708L); + buf.writeDouble(value); + buf.readerOffset(1); + assertEquals(7, buf.readableBytes()); + assertEquals(0, buf.writableBytes()); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).readDouble()); + assertEquals(7, buf.readableBytes()); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfDoubleMustBoundsCheckOnNegativeOffset(Fixture fixture) { @@ -4686,6 +5600,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfDoubleReadOnlyMustBoundsCheckOnNegativeOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getDouble(-1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfDoubleMustNotBoundsCheckWhenReadOffsetAndSizeIsEqualToWriteOffset(Fixture fixture) { @@ -4702,7 +5625,7 @@ public class BufTest { void offsettedGetOfDoubleMustReadWithDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); double value = Double.longBitsToDouble(0x0102030405060708L); buf.writeDouble(value); buf.setByte(0, (byte) 0x10); @@ -4721,6 +5644,17 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfDoubleReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsGreaterThanWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + double value = Double.longBitsToDouble(0x0102030405060708L); + buf.writeDouble(value); + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getDouble(1)); + } + } + @ParameterizedTest @MethodSource("allocators") void offsettedGetOfDoubleMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { @@ -4730,6 +5664,15 @@ public class BufTest { } } + @ParameterizedTest + @MethodSource("allocators") + void offsettedGetOfDoubleReadOnlyMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset(Fixture fixture) { + try (Allocator allocator = fixture.createAllocator(); + Buf buf = allocator.allocate(8)) { + assertThrows(IndexOutOfBoundsException.class, () -> buf.readOnly(true).getDouble(0)); + } + } + @ParameterizedTest @MethodSource("allocators") void relativeWriteOfDoubleMustBoundsCheckWhenWriteOffsetAndSizeIsBeyondCapacity(Fixture fixture) { @@ -4750,7 +5693,7 @@ public class BufTest { void relativeWriteOfDoubleMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); double value = Double.longBitsToDouble(0x0102030405060708L); buf.writeDouble(value); buf.writerOffset(Long.BYTES); @@ -4798,7 +5741,7 @@ public class BufTest { void offsettedSetOfDoubleMustHaveDefaultEndianByteOrder(Fixture fixture) { try (Allocator allocator = fixture.createAllocator(); Buf buf = allocator.allocate(8)) { - buf.order(ByteOrder.BIG_ENDIAN); + buf.order(BIG_ENDIAN); double value = Double.longBitsToDouble(0x0102030405060708L); buf.setDouble(0, value); buf.writerOffset(Long.BYTES); diff --git a/src/test/java/io/netty/buffer/api/Fixture.java b/src/test/java/io/netty/buffer/api/Fixture.java index ba63ab4..9bcf4ad 100644 --- a/src/test/java/io/netty/buffer/api/Fixture.java +++ b/src/test/java/io/netty/buffer/api/Fixture.java @@ -56,6 +56,10 @@ public final class Fixture implements Supplier { return properties.contains(Properties.DIRECT); } + public boolean isComposite() { + return properties.contains(Properties.COMPOSITE); + } + public boolean isPooled() { return properties.contains(Properties.POOLED); }