diff --git a/src/main/java/io/netty/buffer/api/Allocator.java b/src/main/java/io/netty/buffer/api/Allocator.java
index 1dd4db9..c24e096 100644
--- a/src/main/java/io/netty/buffer/api/Allocator.java
+++ b/src/main/java/io/netty/buffer/api/Allocator.java
@@ -70,7 +70,7 @@ public interface Allocator extends AutoCloseable {
*
{@code
* try (Buf a = allocator.allocate(size);
* Buf b = allocator.allocate(size)) {
- * return Buf.compose(a, b); // Reference counts for 'a' and 'b' incremented here.
+ * return allocator.compose(a, b); // Reference counts for 'a' and 'b' incremented here.
* } // Reference count for 'a' and 'b' decremented here; composite buffer now holds the last references.
* }
*
diff --git a/src/main/java/io/netty/buffer/api/Buf.java b/src/main/java/io/netty/buffer/api/Buf.java
index 36d4305..6906be4 100644
--- a/src/main/java/io/netty/buffer/api/Buf.java
+++ b/src/main/java/io/netty/buffer/api/Buf.java
@@ -154,6 +154,7 @@ public interface Buf extends Rc, BufAccessors {
* @return This Buf.
* @throws IndexOutOfBoundsException if the specified {@code offset} is less than the current
* {@link #readerOffset()} or greater than {@link #capacity()}.
+ * @throws IllegalStateException if this buffer is {@linkplain #readOnly() read-only}.
*/
Buf writerOffset(int offset);
@@ -178,6 +179,7 @@ public interface Buf extends Rc, BufAccessors {
*
* @param value The byte value to write at every position in the buffer.
* @return This Buf.
+ * @throws IllegalStateException if this buffer is {@linkplain #readOnly() read-only}.
*/
Buf fill(byte value);
@@ -187,6 +189,20 @@ public interface Buf extends Rc, BufAccessors {
*/
long getNativeAddress();
+ /**
+ * Set the read-only state of this buffer.
+ *
+ * @return this buffer.
+ */
+ Buf readOnly(boolean readOnly);
+
+ /**
+ * Query if this buffer is read-only or not.
+ *
+ * @return {@code true} if this buffer is read-only, {@code false} otherwise.
+ */
+ boolean readOnly();
+
/**
* Returns a slice of this buffer's readable bytes.
* Modifying the content of the returned buffer or this buffer affects each other's content,
@@ -371,8 +387,8 @@ public interface Buf extends Rc, BufAccessors {
* {@code false}.
*
* @param size The requested number of bytes of space that should be available for writing.
- * @throws IllegalStateException if this buffer is not in an owned state.
- * That is, if {@link #isOwned()} is {@code false}.
+ * @throws IllegalStateException if this buffer is not in an {@linkplain #isOwned() owned} state,
+ * or is {@linkplain #readOnly() read-only}.
*/
default void ensureWritable(int size) {
ensureWritable(size, true);
@@ -410,8 +426,8 @@ public interface Buf extends Rc, BufAccessors {
* @param allowCompaction {@code true} if the method is allowed to modify the
* {@linkplain #readerOffset() reader offset} and
* {@linkplain #writerOffset() writer offset}, otherwise {@code false}.
- * @throws IllegalStateException if this buffer is not in an owned state.
- * That is, if {@link #isOwned()} is {@code false}.
+ * @throws IllegalStateException if this buffer is not in an {@linkplain #isOwned() owned} state,
+ * * or is {@linkplain #readOnly() read-only}.
*/
void ensureWritable(int size, boolean allowCompaction);
@@ -462,7 +478,8 @@ public interface Buf extends Rc, BufAccessors {
/**
* Discards the read bytes, and moves the buffer contents to the beginning of the buffer.
*
- * The buffer must be {@linkplain #isOwned() owned}, or an exception will be thrown.
+ * @throws IllegalStateException if this buffer is not in an {@linkplain #isOwned() owned} state,
+ * or is {@linkplain #readOnly() read-only}.
*/
void compact();
}
diff --git a/src/main/java/io/netty/buffer/api/CompositeBuf.java b/src/main/java/io/netty/buffer/api/CompositeBuf.java
index 7dd818d..e7c6208 100644
--- a/src/main/java/io/netty/buffer/api/CompositeBuf.java
+++ b/src/main/java/io/netty/buffer/api/CompositeBuf.java
@@ -20,9 +20,6 @@ import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Objects;
-import static jdk.incubator.foreign.MemoryAccess.setByteAtOffset;
-import static jdk.incubator.foreign.MemoryAccess.setLongAtOffset;
-
final class CompositeBuf extends RcSupport implements Buf {
/**
* The max array size is JVM implementation dependant, but most seem to settle on {@code Integer.MAX_VALUE - 8}.
@@ -48,6 +45,7 @@ final class CompositeBuf extends RcSupport implements Buf {
private int subOffset; // The next offset *within* a consituent buffer to read from or write to.
private ByteOrder order;
private boolean closed;
+ private boolean readOnly;
CompositeBuf(Allocator allocator, Buf[] bufs) {
this(allocator, true, bufs.clone(), COMPOSITE_DROP); // Clone prevents external modification of array.
@@ -68,6 +66,14 @@ final class CompositeBuf extends RcSupport implements Buf {
}
}
order = bufs[0].order();
+
+ boolean targetReadOnly = bufs[0].readOnly();
+ for (Buf buf : bufs) {
+ if (buf.readOnly() != targetReadOnly) {
+ throw new IllegalArgumentException("Constituent buffers have inconsistent read-only state.");
+ }
+ }
+ readOnly = targetReadOnly;
} else {
order = ByteOrder.nativeOrder();
}
@@ -201,6 +207,20 @@ final class CompositeBuf extends RcSupport implements Buf {
return 0;
}
+ @Override
+ public Buf readOnly(boolean readOnly) {
+ for (Buf buf : bufs) {
+ buf.readOnly(readOnly);
+ }
+ this.readOnly = readOnly;
+ return this;
+ }
+
+ @Override
+ public boolean readOnly() {
+ return readOnly;
+ }
+
@Override
public Buf slice(int offset, int length) {
checkWriteBounds(offset, length);
@@ -236,7 +256,7 @@ final class CompositeBuf extends RcSupport implements Buf {
slices = new Buf[] { choice.slice(subOffset, 0) };
}
- return new CompositeBuf(allocator, false, slices, drop).writerOffset(length);
+ return new CompositeBuf(allocator, false, slices, drop);
} catch (Throwable throwable) {
// We called acquire prior to the try-clause. We need to undo that if we're not creating a composite buffer:
close();
@@ -267,10 +287,10 @@ final class CompositeBuf extends RcSupport implements Buf {
throw new IndexOutOfBoundsException("Length cannot be negative: " + length + '.');
}
if (srcPos < 0) {
- throw indexOutOfBounds(srcPos);
+ throw indexOutOfBounds(srcPos, false);
}
if (srcPos + length > capacity) {
- throw indexOutOfBounds(srcPos + length);
+ throw indexOutOfBounds(srcPos + length, false);
}
while (length > 0) {
var buf = (Buf) chooseBuffer(srcPos, 0);
@@ -293,10 +313,10 @@ final class CompositeBuf extends RcSupport implements Buf {
throw new IndexOutOfBoundsException("Length cannot be negative: " + length + '.');
}
if (srcPos < 0) {
- throw indexOutOfBounds(srcPos);
+ throw indexOutOfBounds(srcPos, false);
}
if (srcPos + length > capacity) {
- throw indexOutOfBounds(srcPos + length);
+ throw indexOutOfBounds(srcPos + length, false);
}
// Iterate in reverse to account for src and dest buffer overlap.
@@ -530,6 +550,9 @@ final class CompositeBuf extends RcSupport implements Buf {
if (size < 0) {
throw new IllegalArgumentException("Cannot ensure writable for a negative size: " + size + '.');
}
+ if (readOnly) {
+ throw bufferIsReadOnly();
+ }
if (writableBytes() >= size) {
// We already have enough space.
return;
@@ -586,15 +609,23 @@ final class CompositeBuf extends RcSupport implements Buf {
"This buffer uses " + order() + " byte order, and cannot be extended with " +
"a buffer that uses " + extension.order() + " byte order.");
}
- if (bufs.length == 0) {
- order = extension.order();
+ if (bufs.length > 0 && extension.readOnly() != readOnly()) {
+ throw new IllegalArgumentException(
+ "This buffer is " + (readOnly? "read-only" : "writable") + ", " +
+ "and cannot be extended with a buffer that is " +
+ (extension.readOnly()? "read-only." : "writable."));
}
+
long newSize = capacity() + (long) extension.capacity();
Allocator.checkSize(newSize);
Buf[] restoreTemp = bufs; // We need this to restore our buffer array, in case offset computations fail.
try {
unsafeExtendWith(extension.acquire());
+ if (restoreTemp.length == 0) {
+ order = extension.order();
+ readOnly = extension.readOnly();
+ }
} catch (Exception e) {
bufs = restoreTemp;
throw e;
@@ -642,6 +673,9 @@ final class CompositeBuf extends RcSupport implements Buf {
if (!isOwned()) {
throw new IllegalStateException("Buffer must be owned in order to compact.");
}
+ if (readOnly()) {
+ throw new IllegalStateException("Buffer must be writable in order to compact, but was read-only.");
+ }
int distance = roff;
if (distance == 0) {
return;
@@ -962,6 +996,7 @@ final class CompositeBuf extends RcSupport implements Buf {
received[i] = sends[i].receive();
}
var composite = new CompositeBuf(allocator, true, received, drop);
+ composite.readOnly = readOnly;
drop.attach(composite);
return composite;
}
@@ -1034,7 +1069,7 @@ final class CompositeBuf extends RcSupport implements Buf {
private void checkReadBounds(int index, int size) {
if (index < 0 || woff < index + size) {
- throw indexOutOfBounds(index);
+ throw indexOutOfBounds(index, false);
}
}
@@ -1051,19 +1086,30 @@ final class CompositeBuf extends RcSupport implements Buf {
private void checkWriteBounds(int index, int size) {
if (index < 0 || capacity < index + size) {
- throw indexOutOfBounds(index);
+ throw indexOutOfBounds(index, true);
}
}
- private RuntimeException indexOutOfBounds(int index) {
+ private RuntimeException indexOutOfBounds(int index, boolean write) {
if (closed) {
- return new IllegalStateException("This buffer is closed.");
+ return bufferIsClosed();
+ }
+ if (write && readOnly) {
+ return bufferIsReadOnly();
}
return new IndexOutOfBoundsException(
"Index " + index + " is out of bounds: [read 0 to " + woff + ", write 0 to " +
(capacity - 1) + "].");
}
+ private static IllegalStateException bufferIsClosed() {
+ return new IllegalStateException("This buffer is closed.");
+ }
+
+ private static IllegalStateException bufferIsReadOnly() {
+ return new IllegalStateException("This buffer is read-only.");
+ }
+
private BufAccessors chooseBuffer(int index, int size) {
int i = searchOffsets(index);
if (i == bufs.length) {
diff --git a/src/main/java/io/netty/buffer/api/SizeClassedMemoryPool.java b/src/main/java/io/netty/buffer/api/SizeClassedMemoryPool.java
index 98a7d62..8d82b09 100644
--- a/src/main/java/io/netty/buffer/api/SizeClassedMemoryPool.java
+++ b/src/main/java/io/netty/buffer/api/SizeClassedMemoryPool.java
@@ -43,7 +43,11 @@ class SizeClassedMemoryPool implements Allocator, AllocatorControl, Drop {
var sizeClassPool = getSizeClassPool(size);
Send send = sizeClassPool.poll();
if (send != null) {
- return send.receive().reset().fill((byte) 0).order(ByteOrder.nativeOrder());
+ return send.receive()
+ .reset()
+ .readOnly(false)
+ .fill((byte) 0)
+ .order(ByteOrder.nativeOrder());
}
return createBuf(size, getDrop());
}
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 6484c04..bc901e3 100644
--- a/src/main/java/io/netty/buffer/api/memseg/MemSegBuf.java
+++ b/src/main/java/io/netty/buffer/api/memseg/MemSegBuf.java
@@ -59,6 +59,7 @@ class MemSegBuf extends RcSupport implements Buf {
private final AllocatorControl alloc;
private final boolean isSendable;
private MemorySegment seg;
+ private MemorySegment wseg;
private ByteOrder order;
private int roff;
private int woff;
@@ -71,6 +72,7 @@ class MemSegBuf extends RcSupport implements Buf {
super(drop);
this.alloc = alloc;
seg = segment;
+ wseg = segment;
this.isSendable = isSendable;
order = ByteOrder.nativeOrder();
}
@@ -122,6 +124,7 @@ class MemSegBuf extends RcSupport implements Buf {
@Override
public Buf fill(byte value) {
+ checkWrite(0, capacity());
seg.fill(value);
return this;
}
@@ -135,6 +138,17 @@ class MemSegBuf extends RcSupport implements Buf {
}
}
+ @Override
+ public Buf readOnly(boolean readOnly) {
+ wseg = readOnly? CLOSED_SEGMENT : seg;
+ return this;
+ }
+
+ @Override
+ public boolean readOnly() {
+ return wseg == CLOSED_SEGMENT && seg != CLOSED_SEGMENT;
+ }
+
@Override
public Buf slice(int offset, int length) {
if (length < 0) {
@@ -147,7 +161,10 @@ class MemSegBuf extends RcSupport implements Buf {
b.makeInaccessible();
};
var sendable = false; // Sending implies ownership change, which we can't do for slices.
- return new MemSegBuf(slice, drop, alloc, sendable).writerOffset(length).order(order());
+ return new MemSegBuf(slice, drop, alloc, sendable)
+ .writerOffset(length)
+ .order(order())
+ .readOnly(readOnly());
}
@Override
@@ -165,12 +182,31 @@ class MemSegBuf extends RcSupport implements Buf {
}
private void copyInto(int srcPos, MemorySegment dest, int destPos, int length) {
+ if (seg == CLOSED_SEGMENT) {
+ throw bufferIsClosed();
+ }
+ if (srcPos < 0) {
+ throw new IllegalArgumentException("The srcPos cannot be negative: " + srcPos + '.');
+ }
+ if (length < 0) {
+ throw new IllegalArgumentException("The length cannot be negative: " + length + '.');
+ }
+ if (seg.byteSize() < srcPos + length) {
+ throw new IllegalArgumentException("The srcPos + length is beyond the end of the buffer: " +
+ "srcPos = " + srcPos + ", length = " + length + '.');
+ }
dest.asSlice(destPos, length).copyFrom(seg.asSlice(srcPos, length));
}
@Override
public void copyInto(int srcPos, Buf dest, int destPos, int length) {
- // todo optimise: specialise for MemSegBuf.
+ if (dest instanceof MemSegBuf) {
+ var memSegBuf = (MemSegBuf) dest;
+ memSegBuf.checkWrite(destPos, length);
+ copyInto(srcPos, memSegBuf.seg, destPos, length);
+ return;
+ }
+
// Iterate in reverse to account for src and dest buffer overlap.
var itr = openReverseCursor(srcPos + length - 1, length);
ByteOrder prevOrder = dest.order();
@@ -202,8 +238,8 @@ class MemSegBuf extends RcSupport implements Buf {
throw new IllegalArgumentException("The length cannot be negative: " + length + '.');
}
if (seg.byteSize() < fromOffset + length) {
- throw new IllegalArgumentException("The fromOffset+length is beyond the end of the buffer: " +
- "fromOffset=" + fromOffset + ", length=" + length + '.');
+ throw new IllegalArgumentException("The fromOffset + length is beyond the end of the buffer: " +
+ "fromOffset = " + fromOffset + ", length = " + length + '.');
}
return new ByteCursor() {
final MemorySegment segment = seg;
@@ -269,8 +305,8 @@ class MemSegBuf extends RcSupport implements Buf {
throw new IllegalArgumentException("The fromOffset is beyond the end of the buffer: " + fromOffset + '.');
}
if (fromOffset - length < -1) {
- throw new IllegalArgumentException("The fromOffset-length would underflow the buffer: " +
- "fromOffset=" + fromOffset + ", length=" + length + '.');
+ throw new IllegalArgumentException("The fromOffset - length would underflow the buffer: " +
+ "fromOffset = " + fromOffset + ", length = " + length + '.');
}
return new ByteCursor() {
final MemorySegment segment = seg;
@@ -330,6 +366,9 @@ class MemSegBuf extends RcSupport implements Buf {
if (size < 0) {
throw new IllegalArgumentException("Cannot ensure writable for a negative size: " + size + '.');
}
+ if (seg != wseg) {
+ throw bufferIsReadOnly();
+ }
if (writableBytes() >= size) {
// We already have enough space.
return;
@@ -366,6 +405,7 @@ class MemSegBuf extends RcSupport implements Buf {
}
seg = newSegment;
+ wseg = newSegment;
drop.attach(this);
}
@@ -390,7 +430,12 @@ class MemSegBuf extends RcSupport implements Buf {
bifurcatedBuf.woff = woff;
bifurcatedBuf.roff = roff;
bifurcatedBuf.order(order);
+ boolean readOnly = readOnly();
+ bifurcatedBuf.readOnly(readOnly);
seg = seg.asSlice(woff, seg.byteSize() - woff);
+ if (!readOnly) {
+ wseg = seg;
+ }
woff = 0;
roff = 0;
return bifurcatedBuf;
@@ -401,6 +446,9 @@ class MemSegBuf extends RcSupport implements Buf {
if (!isOwned()) {
throw new IllegalStateException("Buffer must be owned in order to compact.");
}
+ if (readOnly()) {
+ throw new IllegalStateException("Buffer must be writable in order to compact, but was read-only.");
+ }
int distance = roff;
if (distance == 0) {
return;
@@ -442,7 +490,7 @@ class MemSegBuf extends RcSupport implements Buf {
@Override
public Buf writeByte(byte value) {
try {
- setByteAtOffset(seg, woff, value);
+ setByteAtOffset(wseg, woff, value);
woff += Byte.BYTES;
return this;
} catch (IndexOutOfBoundsException e) {
@@ -453,7 +501,7 @@ class MemSegBuf extends RcSupport implements Buf {
@Override
public Buf setByte(int woff, byte value) {
try {
- setByteAtOffset(seg, woff, value);
+ setByteAtOffset(wseg, woff, value);
return this;
} catch (IndexOutOfBoundsException e) {
throw checkWriteState(e);
@@ -463,7 +511,7 @@ class MemSegBuf extends RcSupport implements Buf {
@Override
public Buf writeUnsignedByte(int value) {
try {
- setByteAtOffset(seg, woff, (byte) (value & 0xFF));
+ setByteAtOffset(wseg, woff, (byte) (value & 0xFF));
woff += Byte.BYTES;
return this;
} catch (IndexOutOfBoundsException e) {
@@ -474,7 +522,7 @@ class MemSegBuf extends RcSupport implements Buf {
@Override
public Buf setUnsignedByte(int woff, int value) {
try {
- setByteAtOffset(seg, woff, (byte) (value & 0xFF));
+ setByteAtOffset(wseg, woff, (byte) (value & 0xFF));
return this;
} catch (IndexOutOfBoundsException e) {
throw checkWriteState(e);
@@ -498,7 +546,7 @@ class MemSegBuf extends RcSupport implements Buf {
@Override
public Buf writeChar(char value) {
try {
- setCharAtOffset(seg, woff, order, value);
+ setCharAtOffset(wseg, woff, order, value);
woff += 2;
return this;
} catch (IndexOutOfBoundsException e) {
@@ -509,7 +557,7 @@ class MemSegBuf extends RcSupport implements Buf {
@Override
public Buf setChar(int woff, char value) {
try {
- setCharAtOffset(seg, woff, order, value);
+ setCharAtOffset(wseg, woff, order, value);
return this;
} catch (IndexOutOfBoundsException e) {
throw checkWriteState(e);
@@ -547,7 +595,7 @@ class MemSegBuf extends RcSupport implements Buf {
@Override
public Buf writeShort(short value) {
try {
- setShortAtOffset(seg, woff, order, value);
+ setShortAtOffset(wseg, woff, order, value);
woff += Short.BYTES;
return this;
} catch (IndexOutOfBoundsException e) {
@@ -558,7 +606,7 @@ class MemSegBuf extends RcSupport implements Buf {
@Override
public Buf setShort(int woff, short value) {
try {
- setShortAtOffset(seg, woff, order, value);
+ setShortAtOffset(wseg, woff, order, value);
return this;
} catch (IndexOutOfBoundsException e) {
throw checkWriteState(e);
@@ -568,7 +616,7 @@ class MemSegBuf extends RcSupport implements Buf {
@Override
public Buf writeUnsignedShort(int value) {
try {
- setShortAtOffset(seg, woff, order, (short) (value & 0xFFFF));
+ setShortAtOffset(wseg, woff, order, (short) (value & 0xFFFF));
woff += Short.BYTES;
return this;
} catch (IndexOutOfBoundsException e) {
@@ -579,7 +627,7 @@ class MemSegBuf extends RcSupport implements Buf {
@Override
public Buf setUnsignedShort(int woff, int value) {
try {
- setShortAtOffset(seg, woff, order, (short) (value & 0xFFFF));
+ setShortAtOffset(wseg, woff, order, (short) (value & 0xFFFF));
return this;
} catch (IndexOutOfBoundsException e) {
throw checkWriteState(e);
@@ -642,13 +690,13 @@ class MemSegBuf extends RcSupport implements Buf {
public Buf writeMedium(int value) {
checkWrite(woff, 3);
if (order == ByteOrder.BIG_ENDIAN) {
- setByteAtOffset(seg, woff, (byte) (value >> 16));
- setByteAtOffset(seg, woff + 1, (byte) (value >> 8 & 0xFF));
- setByteAtOffset(seg, woff + 2, (byte) (value & 0xFF));
+ setByteAtOffset(wseg, woff, (byte) (value >> 16));
+ setByteAtOffset(wseg, woff + 1, (byte) (value >> 8 & 0xFF));
+ setByteAtOffset(wseg, woff + 2, (byte) (value & 0xFF));
} else {
- setByteAtOffset(seg, woff, (byte) (value & 0xFF));
- setByteAtOffset(seg, woff + 1, (byte) (value >> 8 & 0xFF));
- setByteAtOffset(seg, woff + 2, (byte) (value >> 16 & 0xFF));
+ setByteAtOffset(wseg, woff, (byte) (value & 0xFF));
+ setByteAtOffset(wseg, woff + 1, (byte) (value >> 8 & 0xFF));
+ setByteAtOffset(wseg, woff + 2, (byte) (value >> 16 & 0xFF));
}
woff += 3;
return this;
@@ -658,13 +706,13 @@ class MemSegBuf extends RcSupport implements Buf {
public Buf setMedium(int woff, int value) {
checkWrite(woff, 3);
if (order == ByteOrder.BIG_ENDIAN) {
- setByteAtOffset(seg, woff, (byte) (value >> 16));
- setByteAtOffset(seg, woff + 1, (byte) (value >> 8 & 0xFF));
- setByteAtOffset(seg, woff + 2, (byte) (value & 0xFF));
+ setByteAtOffset(wseg, woff, (byte) (value >> 16));
+ setByteAtOffset(wseg, woff + 1, (byte) (value >> 8 & 0xFF));
+ setByteAtOffset(wseg, woff + 2, (byte) (value & 0xFF));
} else {
- setByteAtOffset(seg, woff, (byte) (value & 0xFF));
- setByteAtOffset(seg, woff + 1, (byte) (value >> 8 & 0xFF));
- setByteAtOffset(seg, woff + 2, (byte) (value >> 16 & 0xFF));
+ setByteAtOffset(wseg, woff, (byte) (value & 0xFF));
+ setByteAtOffset(wseg, woff + 1, (byte) (value >> 8 & 0xFF));
+ setByteAtOffset(wseg, woff + 2, (byte) (value >> 16 & 0xFF));
}
return this;
}
@@ -673,13 +721,13 @@ class MemSegBuf extends RcSupport implements Buf {
public Buf writeUnsignedMedium(int value) {
checkWrite(woff, 3);
if (order == ByteOrder.BIG_ENDIAN) {
- setByteAtOffset(seg, woff, (byte) (value >> 16));
- setByteAtOffset(seg, woff + 1, (byte) (value >> 8 & 0xFF));
- setByteAtOffset(seg, woff + 2, (byte) (value & 0xFF));
+ setByteAtOffset(wseg, woff, (byte) (value >> 16));
+ setByteAtOffset(wseg, woff + 1, (byte) (value >> 8 & 0xFF));
+ setByteAtOffset(wseg, woff + 2, (byte) (value & 0xFF));
} else {
- setByteAtOffset(seg, woff, (byte) (value & 0xFF));
- setByteAtOffset(seg, woff + 1, (byte) (value >> 8 & 0xFF));
- setByteAtOffset(seg, woff + 2, (byte) (value >> 16 & 0xFF));
+ setByteAtOffset(wseg, woff, (byte) (value & 0xFF));
+ setByteAtOffset(wseg, woff + 1, (byte) (value >> 8 & 0xFF));
+ setByteAtOffset(wseg, woff + 2, (byte) (value >> 16 & 0xFF));
}
woff += 3;
return this;
@@ -689,13 +737,13 @@ class MemSegBuf extends RcSupport implements Buf {
public Buf setUnsignedMedium(int woff, int value) {
checkWrite(woff, 3);
if (order == ByteOrder.BIG_ENDIAN) {
- setByteAtOffset(seg, woff, (byte) (value >> 16));
- setByteAtOffset(seg, woff + 1, (byte) (value >> 8 & 0xFF));
- setByteAtOffset(seg, woff + 2, (byte) (value & 0xFF));
+ setByteAtOffset(wseg, woff, (byte) (value >> 16));
+ setByteAtOffset(wseg, woff + 1, (byte) (value >> 8 & 0xFF));
+ setByteAtOffset(wseg, woff + 2, (byte) (value & 0xFF));
} else {
- setByteAtOffset(seg, woff, (byte) (value & 0xFF));
- setByteAtOffset(seg, woff + 1, (byte) (value >> 8 & 0xFF));
- setByteAtOffset(seg, woff + 2, (byte) (value >> 16 & 0xFF));
+ setByteAtOffset(wseg, woff, (byte) (value & 0xFF));
+ setByteAtOffset(wseg, woff + 1, (byte) (value >> 8 & 0xFF));
+ setByteAtOffset(wseg, woff + 2, (byte) (value >> 16 & 0xFF));
}
return this;
}
@@ -731,7 +779,7 @@ class MemSegBuf extends RcSupport implements Buf {
@Override
public Buf writeInt(int value) {
try {
- setIntAtOffset(seg, woff, order, value);
+ setIntAtOffset(wseg, woff, order, value);
woff += Integer.BYTES;
return this;
} catch (IndexOutOfBoundsException e) {
@@ -742,7 +790,7 @@ class MemSegBuf extends RcSupport implements Buf {
@Override
public Buf setInt(int woff, int value) {
try {
- setIntAtOffset(seg, woff, order, value);
+ setIntAtOffset(wseg, woff, order, value);
return this;
} catch (IndexOutOfBoundsException e) {
throw checkWriteState(e);
@@ -752,7 +800,7 @@ class MemSegBuf extends RcSupport implements Buf {
@Override
public Buf writeUnsignedInt(long value) {
try {
- setIntAtOffset(seg, woff, order, (int) (value & 0xFFFFFFFFL));
+ setIntAtOffset(wseg, woff, order, (int) (value & 0xFFFFFFFFL));
woff += Integer.BYTES;
return this;
} catch (IndexOutOfBoundsException e) {
@@ -763,7 +811,7 @@ class MemSegBuf extends RcSupport implements Buf {
@Override
public Buf setUnsignedInt(int woff, long value) {
try {
- setIntAtOffset(seg, woff, order, (int) (value & 0xFFFFFFFFL));
+ setIntAtOffset(wseg, woff, order, (int) (value & 0xFFFFFFFFL));
return this;
} catch (IndexOutOfBoundsException e) {
throw checkWriteState(e);
@@ -787,7 +835,7 @@ class MemSegBuf extends RcSupport implements Buf {
@Override
public Buf writeFloat(float value) {
try {
- setFloatAtOffset(seg, woff, order, value);
+ setFloatAtOffset(wseg, woff, order, value);
woff += Float.BYTES;
return this;
} catch (IndexOutOfBoundsException e) {
@@ -798,7 +846,7 @@ class MemSegBuf extends RcSupport implements Buf {
@Override
public Buf setFloat(int woff, float value) {
try {
- setFloatAtOffset(seg, woff, order, value);
+ setFloatAtOffset(wseg, woff, order, value);
return this;
} catch (IndexOutOfBoundsException e) {
throw checkWriteState(e);
@@ -822,7 +870,7 @@ class MemSegBuf extends RcSupport implements Buf {
@Override
public Buf writeLong(long value) {
try {
- setLongAtOffset(seg, woff, order, value);
+ setLongAtOffset(wseg, woff, order, value);
woff += Long.BYTES;
return this;
} catch (IndexOutOfBoundsException e) {
@@ -833,7 +881,7 @@ class MemSegBuf extends RcSupport implements Buf {
@Override
public Buf setLong(int woff, long value) {
try {
- setLongAtOffset(seg, woff, order, value);
+ setLongAtOffset(wseg, woff, order, value);
return this;
} catch (IndexOutOfBoundsException e) {
throw checkWriteState(e);
@@ -857,7 +905,7 @@ class MemSegBuf extends RcSupport implements Buf {
@Override
public Buf writeDouble(double value) {
try {
- setDoubleAtOffset(seg, woff, order, value);
+ setDoubleAtOffset(wseg, woff, order, value);
woff += Double.BYTES;
return this;
} catch (IndexOutOfBoundsException e) {
@@ -868,7 +916,7 @@ class MemSegBuf extends RcSupport implements Buf {
@Override
public Buf setDouble(int woff, double value) {
try {
- setDoubleAtOffset(seg, woff, order, value);
+ setDoubleAtOffset(wseg, woff, order, value);
return this;
} catch (IndexOutOfBoundsException e) {
throw checkWriteState(e);
@@ -881,6 +929,7 @@ class MemSegBuf extends RcSupport implements Buf {
var order = this.order;
var roff = this.roff;
var woff = this.woff;
+ var readOnly = readOnly();
boolean isConfined = seg.ownerThread() == null;
MemorySegment transferSegment = isConfined? seg : seg.share();
makeInaccessible();
@@ -891,6 +940,7 @@ class MemSegBuf extends RcSupport implements Buf {
copy.order = order;
copy.roff = roff;
copy.woff = woff;
+ copy.readOnly(readOnly);
return copy;
}
};
@@ -898,6 +948,7 @@ class MemSegBuf extends RcSupport implements Buf {
void makeInaccessible() {
seg = CLOSED_SEGMENT;
+ wseg = CLOSED_SEGMENT;
roff = 0;
woff = 0;
}
@@ -923,7 +974,7 @@ class MemSegBuf extends RcSupport implements Buf {
}
private void checkWrite(int index, int size) {
- if (index < 0 || seg.byteSize() < index + size) {
+ if (index < 0 || wseg.byteSize() < index + size) {
throw accessCheckException(index);
}
}
@@ -932,6 +983,9 @@ class MemSegBuf extends RcSupport implements Buf {
if (seg == CLOSED_SEGMENT) {
return bufferIsClosed();
}
+ if (wseg != seg) {
+ return bufferIsReadOnly();
+ }
return ioobe;
}
@@ -939,15 +993,22 @@ class MemSegBuf extends RcSupport implements Buf {
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) + "].");
}
- private IllegalStateException bufferIsClosed() {
+ private static IllegalStateException bufferIsClosed() {
return new IllegalStateException("This buffer is closed.");
}
+ private static IllegalStateException bufferIsReadOnly() {
+ return new IllegalStateException("This buffer is read-only.");
+ }
+
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 a158ec0..52a2186 100644
--- a/src/test/java/io/netty/buffer/api/BufTest.java
+++ b/src/test/java/io/netty/buffer/api/BufTest.java
@@ -33,7 +33,6 @@ import java.text.ParseException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@@ -90,6 +89,10 @@ public class BufTest {
return fixtureCombinations().filter(f -> f.isDirect() && f.isCleaner() && f.isPooled());
}
+ static Stream pooledAllocators() {
+ return fixtureCombinations().filter(Fixture::isPooled);
+ }
+
private static Stream fixtureCombinations() {
Fixture[] fxs = fixtures;
if (fxs != null) {
@@ -187,7 +190,10 @@ public class BufTest {
}, COMPOSITE));
}
- return builder.build().flatMap(BufTest::injectBifurcations).flatMap(BufTest::injectSlices);
+ var stream = builder.build();
+ return stream.flatMap(BufTest::injectBifurcations)
+ .flatMap(BufTest::injectSlices)
+ .flatMap(BufTest::injectReadOnlyToggling);
}
private static Stream injectBifurcations(Fixture f) {
@@ -251,6 +257,26 @@ public class BufTest {
return builder.build();
}
+ private static Stream injectReadOnlyToggling(Fixture f) {
+ Builder builder = Stream.builder();
+ builder.add(f);
+ builder.add(new Fixture(f + ".readOnly(true/false)", () -> {
+ var allocatorBase = f.get();
+ return new Allocator() {
+ @Override
+ public Buf allocate(int size) {
+ return allocatorBase.allocate(size).readOnly(true).readOnly(false);
+ }
+
+ @Override
+ public void close() {
+ allocatorBase.close();
+ }
+ };
+ }, f.getProperties()));
+ return builder.build();
+ }
+
@BeforeAll
static void startExecutor() throws IOException, ParseException {
executor = Executors.newSingleThreadExecutor();
@@ -375,6 +401,31 @@ public class BufTest {
}
private static void verifyInaccessible(Buf buf) {
+ verifyReadInaccessible(buf);
+
+ verifyWriteInaccessible(buf);
+
+ try (Allocator allocator = Allocator.heap();
+ Buf target = allocator.allocate(24)) {
+ assertThrows(IllegalStateException.class, () -> buf.copyInto(0, target, 0, 1));
+ assertThrows(IllegalStateException.class, () -> buf.copyInto(0, new byte[1], 0, 1));
+ assertThrows(IllegalStateException.class, () -> buf.copyInto(0, ByteBuffer.allocate(1), 0, 1));
+ if (Allocator.isComposite(buf)) {
+ assertThrows(IllegalStateException.class, () -> Allocator.extend(buf, target));
+ }
+ }
+
+ assertThrows(IllegalStateException.class, () -> buf.bifurcate());
+ assertThrows(IllegalStateException.class, () -> buf.send());
+ assertThrows(IllegalStateException.class, () -> buf.acquire());
+ assertThrows(IllegalStateException.class, () -> buf.slice());
+ assertThrows(IllegalStateException.class, () -> buf.openCursor());
+ assertThrows(IllegalStateException.class, () -> buf.openCursor(0, 0));
+ assertThrows(IllegalStateException.class, () -> buf.openReverseCursor());
+ assertThrows(IllegalStateException.class, () -> buf.openReverseCursor(0, 0));
+ }
+
+ private static void verifyReadInaccessible(Buf buf) {
assertThrows(IllegalStateException.class, () -> buf.readByte());
assertThrows(IllegalStateException.class, () -> buf.readUnsignedByte());
assertThrows(IllegalStateException.class, () -> buf.readChar());
@@ -387,6 +438,7 @@ public class BufTest {
assertThrows(IllegalStateException.class, () -> buf.readFloat());
assertThrows(IllegalStateException.class, () -> buf.readLong());
assertThrows(IllegalStateException.class, () -> buf.readDouble());
+
assertThrows(IllegalStateException.class, () -> buf.getByte(0));
assertThrows(IllegalStateException.class, () -> buf.getUnsignedByte(0));
assertThrows(IllegalStateException.class, () -> buf.getChar(0));
@@ -399,6 +451,9 @@ public class BufTest {
assertThrows(IllegalStateException.class, () -> buf.getFloat(0));
assertThrows(IllegalStateException.class, () -> buf.getLong(0));
assertThrows(IllegalStateException.class, () -> buf.getDouble(0));
+ }
+
+ private static void verifyWriteInaccessible(Buf buf) {
assertThrows(IllegalStateException.class, () -> buf.writeByte((byte) 32));
assertThrows(IllegalStateException.class, () -> buf.writeUnsignedByte(32));
assertThrows(IllegalStateException.class, () -> buf.writeChar('3'));
@@ -411,6 +466,7 @@ public class BufTest {
assertThrows(IllegalStateException.class, () -> buf.writeFloat(3.2f));
assertThrows(IllegalStateException.class, () -> buf.writeLong(32));
assertThrows(IllegalStateException.class, () -> buf.writeDouble(32));
+
assertThrows(IllegalStateException.class, () -> buf.setByte(0, (byte) 32));
assertThrows(IllegalStateException.class, () -> buf.setUnsignedByte(0, 32));
assertThrows(IllegalStateException.class, () -> buf.setChar(0, '3'));
@@ -425,22 +481,11 @@ public class BufTest {
assertThrows(IllegalStateException.class, () -> buf.setDouble(0, 32));
assertThrows(IllegalStateException.class, () -> buf.ensureWritable(1));
- try (Allocator allocator = Allocator.heap();
- Buf target = allocator.allocate(24)) {
- assertThrows(IllegalStateException.class, () -> buf.copyInto(0, target, 0, 1));
- if (Allocator.isComposite(buf)) {
- assertThrows(IllegalStateException.class, () -> Allocator.extend(buf, target));
- }
- }
- assertThrows(IllegalStateException.class, () -> buf.bifurcate());
- assertThrows(IllegalStateException.class, () -> buf.send());
- assertThrows(IllegalStateException.class, () -> buf.acquire());
- assertThrows(IllegalStateException.class, () -> buf.slice());
assertThrows(IllegalStateException.class, () -> buf.fill((byte) 0));
- assertThrows(IllegalStateException.class, () -> buf.openCursor());
- assertThrows(IllegalStateException.class, () -> buf.openCursor(0, 0));
- assertThrows(IllegalStateException.class, () -> buf.openReverseCursor());
- assertThrows(IllegalStateException.class, () -> buf.openReverseCursor(0, 0));
+ try (Allocator allocator = Allocator.heap();
+ Buf source = allocator.allocate(8)) {
+ assertThrows(IllegalStateException.class, () -> source.copyInto(0, buf, 0, 1));
+ }
}
@ParameterizedTest
@@ -1893,6 +1938,18 @@ public class BufTest {
}
}
+ @Test
+ public void emptyCompositeBufferMustAllowExtendingWithReadOnlyBuffer() {
+ try (Allocator allocator = Allocator.heap()) {
+ try (Buf composite = allocator.compose()) {
+ try (Buf b = allocator.allocate(8).readOnly(true)) {
+ Allocator.extend(composite, b);
+ assertTrue(composite.readOnly());
+ }
+ }
+ }
+ }
+
@Test
public void whenExtendingCompositeBufferWithWriteOffsetAtCapacityExtensionWriteOffsetCanBeNonZero() {
try (Allocator allocator = Allocator.heap()) {
@@ -2262,6 +2319,255 @@ public class BufTest {
}
}
+ @ParameterizedTest
+ @MethodSource("allocators")
+ public void readOnlyBufferMustPreventWriteAccess(Fixture fixture) {
+ try (Allocator allocator = fixture.createAllocator();
+ Buf buf = allocator.allocate(8)) {
+ var b = buf.readOnly(true);
+ assertThat(b).isSameAs(buf);
+ verifyWriteInaccessible(buf);
+ }
+ }
+
+ @ParameterizedTest
+ @MethodSource("allocators")
+ public void readOnlyBufferMustBecomeWritableAgainAfterTogglingReadOnlyOff(Fixture fixture) {
+ try (Allocator allocator = fixture.createAllocator();
+ Buf buf = allocator.allocate(8)) {
+ assertFalse(buf.readOnly());
+ buf.readOnly(true);
+ assertTrue(buf.readOnly());
+ verifyWriteInaccessible(buf);
+
+ buf.readOnly(false);
+ assertFalse(buf.readOnly());
+
+ verifyWriteAccessible(buf);
+ }
+ }
+
+ private static void verifyWriteAccessible(Buf buf) {
+ buf.writerOffset(0).writeByte((byte) 32);
+ assertThat(buf.readerOffset(0).readByte()).isEqualTo((byte) 32);
+ buf.writerOffset(0).writeUnsignedByte(32);
+ assertThat(buf.readerOffset(0).readUnsignedByte()).isEqualTo(32);
+ buf.writerOffset(0).writeChar('3');
+ assertThat(buf.readerOffset(0).readChar()).isEqualTo('3');
+ buf.writerOffset(0).writeShort((short) 32);
+ assertThat(buf.readerOffset(0).readShort()).isEqualTo((short) 32);
+ buf.writerOffset(0).writeUnsignedShort(32);
+ assertThat(buf.readerOffset(0).readUnsignedShort()).isEqualTo(32);
+ buf.writerOffset(0).writeMedium(32);
+ assertThat(buf.readerOffset(0).readMedium()).isEqualTo(32);
+ buf.writerOffset(0).writeUnsignedMedium(32);
+ assertThat(buf.readerOffset(0).readUnsignedMedium()).isEqualTo(32);
+ buf.writerOffset(0).writeInt(32);
+ assertThat(buf.readerOffset(0).readInt()).isEqualTo(32);
+ buf.writerOffset(0).writeUnsignedInt(32);
+ assertThat(buf.readerOffset(0).readUnsignedInt()).isEqualTo(32L);
+ buf.writerOffset(0).writeFloat(3.2f);
+ assertThat(buf.readerOffset(0).readFloat()).isEqualTo(3.2f);
+ buf.writerOffset(0).writeLong(32);
+ assertThat(buf.readerOffset(0).readLong()).isEqualTo(32L);
+ buf.writerOffset(0).writeDouble(3.2);
+ assertThat(buf.readerOffset(0).readDouble()).isEqualTo(3.2);
+
+ buf.setByte(0, (byte) 32);
+ assertThat(buf.getByte(0)).isEqualTo((byte) 32);
+ buf.setUnsignedByte(0, 32);
+ assertThat(buf.getUnsignedByte(0)).isEqualTo(32);
+ buf.setChar(0, '3');
+ assertThat(buf.getChar(0)).isEqualTo('3');
+ buf.setShort(0, (short) 32);
+ assertThat(buf.getShort(0)).isEqualTo((short) 32);
+ buf.setUnsignedShort(0, 32);
+ assertThat(buf.getUnsignedShort(0)).isEqualTo(32);
+ buf.setMedium(0, 32);
+ assertThat(buf.getMedium(0)).isEqualTo(32);
+ buf.setUnsignedMedium(0, 32);
+ assertThat(buf.getUnsignedMedium(0)).isEqualTo(32);
+ buf.setInt(0, 32);
+ assertThat(buf.getInt(0)).isEqualTo(32);
+ buf.setUnsignedInt(0, 32);
+ assertThat(buf.getUnsignedInt(0)).isEqualTo(32L);
+ buf.setFloat(0, 3.2f);
+ assertThat(buf.getFloat(0)).isEqualTo(3.2f);
+ buf.setLong(0, 32);
+ assertThat(buf.getLong(0)).isEqualTo(32L);
+ buf.setDouble(0, 3.2);
+ assertThat(buf.getDouble(0)).isEqualTo(3.2);
+
+ if (buf.isOwned()) {
+ buf.ensureWritable(1);
+ }
+ buf.fill((byte) 0);
+ try (Allocator allocator = Allocator.heap();
+ Buf source = allocator.allocate(8)) {
+ source.copyInto(0, buf, 0, 1);
+ }
+ }
+
+ @Test
+ public void composingReadOnlyBuffersMustCreateReadOnlyCompositeBuffer() {
+ try (Allocator allocator = Allocator.heap();
+ Buf a = allocator.allocate(4).readOnly(true);
+ Buf b = allocator.allocate(4).readOnly(true);
+ Buf composite = allocator.compose(a, b)) {
+ assertTrue(composite.readOnly());
+ verifyWriteInaccessible(composite);
+ }
+ }
+
+ @ParameterizedTest
+ @MethodSource("nonSliceAllocators")
+ public void readOnlyBufferMustRemainReadOnlyAfterSend(Fixture fixture) {
+ try (Allocator allocator = fixture.createAllocator();
+ Buf buf = allocator.allocate(8)) {
+ buf.readOnly(true);
+ var send = buf.send();
+ try (Buf receive = send.receive()) {
+ assertTrue(receive.readOnly());
+ verifyWriteInaccessible(receive);
+ }
+ }
+ }
+
+ @Test
+ public void readOnlyBufferMustRemainReadOnlyAfterSendForEmptyCompositeBuffer() {
+ try (Allocator allocator = Allocator.heap();
+ Buf buf = allocator.compose()) {
+ buf.readOnly(true);
+ var send = buf.send();
+ try (Buf receive = send.receive()) {
+ assertTrue(receive.readOnly());
+ }
+ }
+ }
+
+ @ParameterizedTest
+ @MethodSource("pooledAllocators")
+ public void readOnlyBufferMustNotBeReadOnlyAfterBeingReusedFromPool(Fixture fixture) {
+ try (Allocator allocator = fixture.createAllocator()) {
+ for (int i = 0; i < 1000; i++) {
+ try (Buf buf = allocator.allocate(8)) {
+ assertFalse(buf.readOnly());
+ buf.readOnly(true);
+ assertTrue(buf.readOnly());
+ }
+ }
+ }
+ }
+
+ @ParameterizedTest
+ @MethodSource("allocators")
+ public void acquireOfReadOnlyBufferMustBeReadOnly(Fixture fixture) {
+ try (Allocator allocator = fixture.createAllocator();
+ Buf buf = allocator.allocate(8)) {
+ buf.readOnly(true);
+ try (Buf acquire = buf.acquire()) {
+ assertTrue(acquire.readOnly());
+ }
+ }
+ }
+
+ @ParameterizedTest
+ @MethodSource("allocators")
+ public void sliceOfReadOnlyBufferMustBeReadOnly(Fixture fixture) {
+ try (Allocator allocator = fixture.createAllocator();
+ Buf buf = allocator.allocate(8)) {
+ buf.writeLong(0x0102030405060708L);
+ buf.readOnly(true);
+ try (Buf slice = buf.slice()) {
+ assertTrue(slice.readOnly());
+ }
+ }
+ }
+
+ @ParameterizedTest
+ @MethodSource("nonSliceAllocators")
+ public void bifurcateOfReadOnlyBufferMustBeReadOnly(Fixture fixture) {
+ try (Allocator allocator = fixture.createAllocator();
+ Buf buf = allocator.allocate(16)) {
+ buf.writeLong(0x0102030405060708L);
+ buf.readOnly(true);
+ try (Buf bifurcate = buf.bifurcate()) {
+ assertTrue(bifurcate.readOnly());
+ assertTrue(buf.readOnly());
+ }
+ }
+ }
+
+ @Test
+ public void composingReadOnlyAndWritableBuffersMustThrow() {
+ try (Allocator allocator = Allocator.heap();
+ Buf a = allocator.allocate(8).readOnly(true);
+ Buf b = allocator.allocate(8)) {
+ assertThrows(IllegalArgumentException.class, () -> allocator.compose(a, b));
+ assertThrows(IllegalArgumentException.class, () -> allocator.compose(b, a));
+ assertThrows(IllegalArgumentException.class, () -> allocator.compose(a, b, a));
+ assertThrows(IllegalArgumentException.class, () -> allocator.compose(b, a, b));
+ }
+ }
+
+ @Test
+ public void compositeWritableBufferCannotBeExtendedWithReadOnlyBuffer() {
+ try (Allocator allocator = Allocator.heap()) {
+ Buf composite;
+ try (Buf a = allocator.allocate(8)) {
+ composite = allocator.compose(a);
+ }
+ try (composite; Buf b = allocator.allocate(8).readOnly(true)) {
+ assertThrows(IllegalArgumentException.class, () -> Allocator.extend(composite, b));
+ }
+ }
+ }
+
+ @Test
+ public void compositeReadOnlyBufferCannotBeExtendedWithWritableBuffer() {
+ try (Allocator allocator = Allocator.heap()) {
+ Buf composite;
+ try (Buf a = allocator.allocate(8).readOnly(true)) {
+ composite = allocator.compose(a);
+ }
+ try (composite; Buf b = allocator.allocate(8)) {
+ assertThrows(IllegalArgumentException.class, () -> Allocator.extend(composite, b));
+ }
+ }
+ }
+
+ @ParameterizedTest
+ @MethodSource("nonSliceAllocators")
+ public void compactOnReadOnlyBufferMustThrow(Fixture fixture) {
+ try (Allocator allocator = fixture.createAllocator();
+ Buf buf = allocator.allocate(8)) {
+ buf.readOnly(true);
+ assertThrows(IllegalStateException.class, () -> buf.compact());
+ }
+ }
+
+ @ParameterizedTest
+ @MethodSource("nonSliceAllocators")
+ public void ensureWritableOnReadOnlyBufferMustThrow(Fixture fixture) {
+ try (Allocator allocator = fixture.createAllocator();
+ Buf buf = allocator.allocate(8)) {
+ buf.readOnly(true);
+ assertThrows(IllegalStateException.class, () -> buf.ensureWritable(1));
+ }
+ }
+
+ @ParameterizedTest
+ @MethodSource("nonSliceAllocators")
+ public void copyIntoOnReadOnlyBufferMustThrow(Fixture fixture) {
+ try (Allocator allocator = fixture.createAllocator();
+ Buf dest = allocator.allocate(8)) {
+ dest.readOnly(true);
+ try (Buf src = allocator.allocate(8)) {
+ assertThrows(IllegalStateException.class, () -> src.copyInto(0, dest, 0, 1));
+ }
+ }
+ }
+
//
@ParameterizedTest
@MethodSource("allocators")