2020import java .util .Arrays ;
2121import java .util .Objects ;
2222
23- import static jdk .incubator .foreign .MemoryAccess .setByteAtOffset ;
24- import static jdk .incubator .foreign .MemoryAccess .setLongAtOffset ;
25-
2623final 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 ) {
0 commit comments