Skip to content
This repository was archived by the owner on Dec 12, 2022. It is now read-only.

Commit 8cdcfd5

Browse files
authored
Merge pull request #23 from netty/read-only
Add support for read-only buffers
2 parents f862220 + 617d9cc commit 8cdcfd5

File tree

6 files changed

+523
-89
lines changed

6 files changed

+523
-89
lines changed

src/main/java/io/netty/buffer/api/Allocator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ default Buf allocate(int size, ByteOrder order) {
7070
* <pre>{@code
7171
* try (Buf a = allocator.allocate(size);
7272
* Buf b = allocator.allocate(size)) {
73-
* return Buf.compose(a, b); // Reference counts for 'a' and 'b' incremented here.
73+
* return allocator.compose(a, b); // Reference counts for 'a' and 'b' incremented here.
7474
* } // Reference count for 'a' and 'b' decremented here; composite buffer now holds the last references.
7575
* }</pre>
7676
* <p>

src/main/java/io/netty/buffer/api/Buf.java

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ public interface Buf extends Rc<Buf>, BufAccessors {
154154
* @return This Buf.
155155
* @throws IndexOutOfBoundsException if the specified {@code offset} is less than the current
156156
* {@link #readerOffset()} or greater than {@link #capacity()}.
157+
* @throws IllegalStateException if this buffer is {@linkplain #readOnly() read-only}.
157158
*/
158159
Buf writerOffset(int offset);
159160

@@ -178,6 +179,7 @@ default int writableBytes() {
178179
*
179180
* @param value The byte value to write at every position in the buffer.
180181
* @return This Buf.
182+
* @throws IllegalStateException if this buffer is {@linkplain #readOnly() read-only}.
181183
*/
182184
Buf fill(byte value);
183185

@@ -187,6 +189,20 @@ default int writableBytes() {
187189
*/
188190
long getNativeAddress();
189191

192+
/**
193+
* Set the read-only state of this buffer.
194+
*
195+
* @return this buffer.
196+
*/
197+
Buf readOnly(boolean readOnly);
198+
199+
/**
200+
* Query if this buffer is read-only or not.
201+
*
202+
* @return {@code true} if this buffer is read-only, {@code false} otherwise.
203+
*/
204+
boolean readOnly();
205+
190206
/**
191207
* Returns a slice of this buffer's readable bytes.
192208
* Modifying the content of the returned buffer or this buffer affects each other's content,
@@ -371,8 +387,8 @@ default ByteCursor openReverseCursor() {
371387
* {@code false}.
372388
*
373389
* @param size The requested number of bytes of space that should be available for writing.
374-
* @throws IllegalStateException if this buffer is not in an owned state.
375-
* That is, if {@link #isOwned()} is {@code false}.
390+
* @throws IllegalStateException if this buffer is not in an {@linkplain #isOwned() owned} state,
391+
* or is {@linkplain #readOnly() read-only}.
376392
*/
377393
default void ensureWritable(int size) {
378394
ensureWritable(size, true);
@@ -410,8 +426,8 @@ default void ensureWritable(int size) {
410426
* @param allowCompaction {@code true} if the method is allowed to modify the
411427
* {@linkplain #readerOffset() reader offset} and
412428
* {@linkplain #writerOffset() writer offset}, otherwise {@code false}.
413-
* @throws IllegalStateException if this buffer is not in an owned state.
414-
* That is, if {@link #isOwned()} is {@code false}.
429+
* @throws IllegalStateException if this buffer is not in an {@linkplain #isOwned() owned} state,
430+
* * or is {@linkplain #readOnly() read-only}.
415431
*/
416432
void ensureWritable(int size, boolean allowCompaction);
417433

@@ -462,7 +478,8 @@ default void ensureWritable(int size) {
462478
/**
463479
* Discards the read bytes, and moves the buffer contents to the beginning of the buffer.
464480
*
465-
* The buffer must be {@linkplain #isOwned() owned}, or an exception will be thrown.
481+
* @throws IllegalStateException if this buffer is not in an {@linkplain #isOwned() owned} state,
482+
* or is {@linkplain #readOnly() read-only}.
466483
*/
467484
void compact();
468485
}

src/main/java/io/netty/buffer/api/CompositeBuf.java

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@
2020
import java.util.Arrays;
2121
import java.util.Objects;
2222

23-
import static jdk.incubator.foreign.MemoryAccess.setByteAtOffset;
24-
import static jdk.incubator.foreign.MemoryAccess.setLongAtOffset;
25-
2623
final class CompositeBuf extends RcSupport<Buf, CompositeBuf> implements Buf {
2724
/**
2825
* 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<Buf, CompositeBuf> implements Buf {
4845
private int subOffset; // The next offset *within* a consituent buffer to read from or write to.
4946
private ByteOrder order;
5047
private boolean closed;
48+
private boolean readOnly;
5149

5250
CompositeBuf(Allocator allocator, Buf[] bufs) {
5351
this(allocator, true, bufs.clone(), COMPOSITE_DROP); // Clone prevents external modification of array.
@@ -68,6 +66,14 @@ private CompositeBuf(Allocator allocator, boolean isSendable, Buf[] bufs, Drop<C
6866
}
6967
}
7068
order = bufs[0].order();
69+
70+
boolean targetReadOnly = bufs[0].readOnly();
71+
for (Buf buf : bufs) {
72+
if (buf.readOnly() != targetReadOnly) {
73+
throw new IllegalArgumentException("Constituent buffers have inconsistent read-only state.");
74+
}
75+
}
76+
readOnly = targetReadOnly;
7177
} else {
7278
order = ByteOrder.nativeOrder();
7379
}
@@ -201,6 +207,20 @@ public long getNativeAddress() {
201207
return 0;
202208
}
203209

210+
@Override
211+
public Buf readOnly(boolean readOnly) {
212+
for (Buf buf : bufs) {
213+
buf.readOnly(readOnly);
214+
}
215+
this.readOnly = readOnly;
216+
return this;
217+
}
218+
219+
@Override
220+
public boolean readOnly() {
221+
return readOnly;
222+
}
223+
204224
@Override
205225
public Buf slice(int offset, int length) {
206226
checkWriteBounds(offset, length);
@@ -236,7 +256,7 @@ public Buf slice(int offset, int length) {
236256
slices = new Buf[] { choice.slice(subOffset, 0) };
237257
}
238258

239-
return new CompositeBuf(allocator, false, slices, drop).writerOffset(length);
259+
return new CompositeBuf(allocator, false, slices, drop);
240260
} catch (Throwable throwable) {
241261
// We called acquire prior to the try-clause. We need to undo that if we're not creating a composite buffer:
242262
close();
@@ -267,10 +287,10 @@ private void copyInto(int srcPos, CopyInto dest, int destPos, int length) {
267287
throw new IndexOutOfBoundsException("Length cannot be negative: " + length + '.');
268288
}
269289
if (srcPos < 0) {
270-
throw indexOutOfBounds(srcPos);
290+
throw indexOutOfBounds(srcPos, false);
271291
}
272292
if (srcPos + length > capacity) {
273-
throw indexOutOfBounds(srcPos + length);
293+
throw indexOutOfBounds(srcPos + length, false);
274294
}
275295
while (length > 0) {
276296
var buf = (Buf) chooseBuffer(srcPos, 0);
@@ -293,10 +313,10 @@ public void copyInto(int srcPos, Buf dest, int destPos, int length) {
293313
throw new IndexOutOfBoundsException("Length cannot be negative: " + length + '.');
294314
}
295315
if (srcPos < 0) {
296-
throw indexOutOfBounds(srcPos);
316+
throw indexOutOfBounds(srcPos, false);
297317
}
298318
if (srcPos + length > capacity) {
299-
throw indexOutOfBounds(srcPos + length);
319+
throw indexOutOfBounds(srcPos + length, false);
300320
}
301321

302322
// Iterate in reverse to account for src and dest buffer overlap.
@@ -530,6 +550,9 @@ public void ensureWritable(int size, boolean allowCompaction) {
530550
if (size < 0) {
531551
throw new IllegalArgumentException("Cannot ensure writable for a negative size: " + size + '.');
532552
}
553+
if (readOnly) {
554+
throw bufferIsReadOnly();
555+
}
533556
if (writableBytes() >= size) {
534557
// We already have enough space.
535558
return;
@@ -586,15 +609,23 @@ void extendWith(Buf extension) {
586609
"This buffer uses " + order() + " byte order, and cannot be extended with " +
587610
"a buffer that uses " + extension.order() + " byte order.");
588611
}
589-
if (bufs.length == 0) {
590-
order = extension.order();
612+
if (bufs.length > 0 && extension.readOnly() != readOnly()) {
613+
throw new IllegalArgumentException(
614+
"This buffer is " + (readOnly? "read-only" : "writable") + ", " +
615+
"and cannot be extended with a buffer that is " +
616+
(extension.readOnly()? "read-only." : "writable."));
591617
}
618+
592619
long newSize = capacity() + (long) extension.capacity();
593620
Allocator.checkSize(newSize);
594621

595622
Buf[] restoreTemp = bufs; // We need this to restore our buffer array, in case offset computations fail.
596623
try {
597624
unsafeExtendWith(extension.acquire());
625+
if (restoreTemp.length == 0) {
626+
order = extension.order();
627+
readOnly = extension.readOnly();
628+
}
598629
} catch (Exception e) {
599630
bufs = restoreTemp;
600631
throw e;
@@ -642,6 +673,9 @@ public void compact() {
642673
if (!isOwned()) {
643674
throw new IllegalStateException("Buffer must be owned in order to compact.");
644675
}
676+
if (readOnly()) {
677+
throw new IllegalStateException("Buffer must be writable in order to compact, but was read-only.");
678+
}
645679
int distance = roff;
646680
if (distance == 0) {
647681
return;
@@ -962,6 +996,7 @@ public CompositeBuf transferOwnership(Drop<CompositeBuf> drop) {
962996
received[i] = sends[i].receive();
963997
}
964998
var composite = new CompositeBuf(allocator, true, received, drop);
999+
composite.readOnly = readOnly;
9651000
drop.attach(composite);
9661001
return composite;
9671002
}
@@ -1034,7 +1069,7 @@ private BufAccessors prepRead(int index, int size) {
10341069

10351070
private void checkReadBounds(int index, int size) {
10361071
if (index < 0 || woff < index + size) {
1037-
throw indexOutOfBounds(index);
1072+
throw indexOutOfBounds(index, false);
10381073
}
10391074
}
10401075

@@ -1051,19 +1086,30 @@ private BufAccessors prepWrite(int index, int size) {
10511086

10521087
private void checkWriteBounds(int index, int size) {
10531088
if (index < 0 || capacity < index + size) {
1054-
throw indexOutOfBounds(index);
1089+
throw indexOutOfBounds(index, true);
10551090
}
10561091
}
10571092

1058-
private RuntimeException indexOutOfBounds(int index) {
1093+
private RuntimeException indexOutOfBounds(int index, boolean write) {
10591094
if (closed) {
1060-
return new IllegalStateException("This buffer is closed.");
1095+
return bufferIsClosed();
1096+
}
1097+
if (write && readOnly) {
1098+
return bufferIsReadOnly();
10611099
}
10621100
return new IndexOutOfBoundsException(
10631101
"Index " + index + " is out of bounds: [read 0 to " + woff + ", write 0 to " +
10641102
(capacity - 1) + "].");
10651103
}
10661104

1105+
private static IllegalStateException bufferIsClosed() {
1106+
return new IllegalStateException("This buffer is closed.");
1107+
}
1108+
1109+
private static IllegalStateException bufferIsReadOnly() {
1110+
return new IllegalStateException("This buffer is read-only.");
1111+
}
1112+
10671113
private BufAccessors chooseBuffer(int index, int size) {
10681114
int i = searchOffsets(index);
10691115
if (i == bufs.length) {

src/main/java/io/netty/buffer/api/SizeClassedMemoryPool.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@ public Buf allocate(int size) {
4343
var sizeClassPool = getSizeClassPool(size);
4444
Send<Buf> send = sizeClassPool.poll();
4545
if (send != null) {
46-
return send.receive().reset().fill((byte) 0).order(ByteOrder.nativeOrder());
46+
return send.receive()
47+
.reset()
48+
.readOnly(false)
49+
.fill((byte) 0)
50+
.order(ByteOrder.nativeOrder());
4751
}
4852
return createBuf(size, getDrop());
4953
}

0 commit comments

Comments
 (0)