View Javadoc
1   /*
2    * Copyright 2021 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty5.buffer.api.bytebuffer;
17  
18  import io.netty5.buffer.api.AllocatorControl;
19  import io.netty5.buffer.api.Buffer;
20  import io.netty5.buffer.api.BufferAllocator;
21  import io.netty5.buffer.api.BufferClosedException;
22  import io.netty5.buffer.api.BufferReadOnlyException;
23  import io.netty5.buffer.api.ByteCursor;
24  import io.netty5.buffer.api.ComponentIterator;
25  import io.netty5.buffer.api.ComponentIterator.Next;
26  import io.netty5.buffer.api.Drop;
27  import io.netty5.buffer.api.Owned;
28  import io.netty5.buffer.api.ReadableComponent;
29  import io.netty5.buffer.api.ReadableComponentProcessor;
30  import io.netty5.buffer.api.WritableComponent;
31  import io.netty5.buffer.api.WritableComponentProcessor;
32  import io.netty5.buffer.api.internal.AdaptableBuffer;
33  import io.netty5.buffer.api.internal.NotReadOnlyReadableComponent;
34  import io.netty5.buffer.api.internal.SingleComponentIterator;
35  import io.netty5.buffer.api.internal.Statics;
36  import io.netty5.buffer.api.internal.Statics.UncheckedLoadByte;
37  
38  import java.io.IOException;
39  import java.lang.ref.Reference;
40  import java.nio.ByteBuffer;
41  import java.nio.ByteOrder;
42  import java.nio.ReadOnlyBufferException;
43  import java.nio.channels.FileChannel;
44  import java.nio.channels.ReadableByteChannel;
45  import java.nio.channels.WritableByteChannel;
46  
47  import static io.netty5.buffer.api.internal.Statics.MAX_BUFFER_SIZE;
48  import static io.netty5.buffer.api.internal.Statics.bbput;
49  import static io.netty5.buffer.api.internal.Statics.bbslice;
50  import static io.netty5.buffer.api.internal.Statics.bufferIsReadOnly;
51  import static io.netty5.buffer.api.internal.Statics.checkImplicitCapacity;
52  import static io.netty5.buffer.api.internal.Statics.checkLength;
53  import static io.netty5.buffer.api.internal.Statics.nativeAddressWithOffset;
54  import static io.netty5.buffer.api.internal.Statics.setMemory;
55  import static io.netty5.util.internal.ObjectUtil.checkPositiveOrZero;
56  import static io.netty5.util.internal.PlatformDependent.roundToPowerOfTwo;
57  
58  final class NioBuffer extends AdaptableBuffer<NioBuffer>
59          implements ReadableComponent, WritableComponent, NotReadOnlyReadableComponent, ComponentIterator.Next {
60      private static final ByteBuffer CLOSED_BUFFER = ByteBuffer.allocate(0);
61  
62      private ByteBuffer base;
63      private ByteBuffer rmem; // For reading.
64      private ByteBuffer wmem; // For writing.
65  
66      private int roff;
67      private int woff;
68      private int implicitCapacityLimit;
69  
70      NioBuffer(ByteBuffer base, ByteBuffer memory, AllocatorControl control, Drop<NioBuffer> drop) {
71          super(drop, control);
72          this.base = base;
73          rmem = memory;
74          wmem = memory;
75          implicitCapacityLimit = MAX_BUFFER_SIZE;
76      }
77  
78      /**
79       * Constructor for {@linkplain BufferAllocator#constBufferSupplier(byte[]) const buffers}.
80       */
81      private NioBuffer(NioBuffer parent, Drop<NioBuffer> drop) {
82          super(drop, parent.control);
83          implicitCapacityLimit = parent.implicitCapacityLimit;
84          base = parent.base;
85          rmem = parent.rmem.duplicate();
86          wmem = CLOSED_BUFFER;
87          roff = parent.roff;
88          woff = parent.woff;
89      }
90  
91      @Override
92      public String toString() {
93          return "Buffer[roff:" + roff + ", woff:" + woff + ", cap:" + rmem.capacity() + ']';
94      }
95  
96      @Override
97      protected RuntimeException createResourceClosedException() {
98          return Statics.bufferIsClosed(this);
99      }
100 
101     @Override
102     public int capacity() {
103         return rmem.capacity();
104     }
105 
106     @Override
107     public int readerOffset() {
108         return roff;
109     }
110 
111     @Override
112     public Buffer readerOffset(int offset) {
113         checkRead(offset, 0);
114         roff = offset;
115         return this;
116     }
117 
118     @Override
119     public int writerOffset() {
120         return woff;
121     }
122 
123     @Override
124     public Buffer writerOffset(int offset) {
125         if (readOnly()) {
126             throw bufferIsReadOnly(this);
127         }
128         checkWrite(offset, 0, false);
129         woff = offset;
130         return this;
131     }
132 
133     @Override
134     public int readableBytes() {
135         return super.readableBytes();
136     }
137 
138     @Override
139     public int writableBytes() {
140         return super.writableBytes();
141     }
142 
143     @Override
144     public NioBuffer skipReadableBytes(int delta) {
145         return (NioBuffer) super.skipReadableBytes(delta);
146     }
147 
148     @Override
149     public NioBuffer skipWritableBytes(int delta) {
150         return (NioBuffer) super.skipWritableBytes(delta);
151     }
152 
153     @Override
154     public Buffer fill(byte value) {
155         int capacity = capacity();
156         checkSet(0, capacity);
157         if (rmem == CLOSED_BUFFER) {
158             throw bufferIsClosed();
159         }
160         final ByteBuffer wmem = this.wmem;
161         setMemory(wmem, capacity, value);
162         return this;
163     }
164 
165     private long nativeAddress() {
166         return Statics.nativeAddressOfDirectByteBuffer(rmem);
167     }
168 
169     @Override
170     public Buffer makeReadOnly() {
171         wmem = CLOSED_BUFFER;
172         return this;
173     }
174 
175     @Override
176     public boolean readOnly() {
177         return wmem == CLOSED_BUFFER && rmem != CLOSED_BUFFER;
178     }
179 
180     @Override
181     public boolean isDirect() {
182         return rmem.isDirect();
183     }
184 
185     @Override
186     public Buffer implicitCapacityLimit(int limit) {
187         checkImplicitCapacity(limit,  capacity());
188         implicitCapacityLimit = limit;
189         return this;
190     }
191 
192     @Override
193     public int implicitCapacityLimit() {
194         return implicitCapacityLimit;
195     }
196 
197     @Override
198     public Buffer copy(int offset, int length, boolean readOnly) {
199         checkLength(length);
200         checkGet(offset, length);
201         if (readOnly && readOnly()) {
202             // If both this buffer and the copy are read-only, they can safely share the memory.
203             NioBuffer copy = newConstChild();
204             if (offset > 0 || length < capacity()) {
205                 copy.rmem = bbslice(copy.rmem, offset, length);
206             }
207             copy.roff = 0;
208             copy.woff = length;
209             return copy;
210         }
211         Buffer copy = control.getAllocator().allocate(length);
212         try {
213             copyInto(offset, copy, 0, length);
214             copy.writerOffset(length);
215             if (readOnly) {
216                 copy.makeReadOnly();
217             }
218             return copy;
219         } catch (Throwable e) {
220             copy.close();
221             throw e;
222         }
223     }
224 
225     @Override
226     public void copyInto(int srcPos, byte[] dest, int destPos, int length) {
227         copyInto(srcPos, ByteBuffer.wrap(dest), destPos, length);
228     }
229 
230     @Override
231     public void copyInto(int srcPos, ByteBuffer dest, int destPos, int length) {
232         if (rmem == CLOSED_BUFFER) {
233             throw bufferIsClosed();
234         }
235         if (srcPos < 0) {
236             throw new IndexOutOfBoundsException("The srcPos cannot be negative: " + srcPos + '.');
237         }
238         if (destPos < 0) {
239             throw new IndexOutOfBoundsException("The destination position cannot be negative: " + destPos);
240         }
241         checkLength(length);
242         if (capacity() < srcPos + length) {
243             throw new IndexOutOfBoundsException("The srcPos + length is beyond the end of the buffer: " +
244                                                "srcPos = " + srcPos + ", length = " + length + '.');
245         }
246         if (dest.capacity() < destPos + length) {
247             throw new IndexOutOfBoundsException("The destPos + length is beyond the end of the buffer: " +
248                                                 "destPos = " + destPos + ", length = " + length + '.');
249         }
250         if (dest.hasArray() && hasReadableArray()) {
251             final byte[] srcArray = rmem.array();
252             final int srcStart = rmem.arrayOffset() + srcPos;
253             final byte[] dstArray = dest.array();
254             final int dstStart = dest.arrayOffset() + destPos;
255             System.arraycopy(srcArray, srcStart, dstArray, dstStart, length);
256             return;
257         }
258         dest = dest.duplicate().clear();
259         bbput(dest, destPos, rmem, srcPos, length);
260     }
261 
262     @Override
263     public void copyInto(int srcPos, Buffer dest, int destPos, int length) {
264         if (!isAccessible()) {
265             throw bufferIsClosed();
266         }
267         if (dest.readOnly()) {
268             throw bufferIsReadOnly(dest);
269         }
270         if (dest instanceof NioBuffer) {
271             var nb = (NioBuffer) dest;
272             nb.checkSet(destPos, length);
273             copyInto(srcPos, nb.wmem, destPos, length);
274             return;
275         }
276 
277         Statics.copyToViaReverseLoop(this, srcPos, dest, destPos, length);
278     }
279 
280     @Override
281     public int transferTo(WritableByteChannel channel, int length) throws IOException {
282         if (!isAccessible()) {
283             throw bufferIsClosed();
284         }
285         length = Math.min(readableBytes(), length);
286         if (length == 0) {
287             return 0;
288         }
289         checkGet(readerOffset(), length);
290         int bytesWritten = channel.write(readableBuffer().limit(length));
291         skipReadableBytes(bytesWritten);
292         return bytesWritten;
293     }
294 
295     @Override
296     public int transferFrom(FileChannel channel, long position, int length) throws IOException {
297         checkPositiveOrZero(position, "position");
298         checkPositiveOrZero(length, "length");
299         if (!isAccessible()) {
300             throw bufferIsClosed();
301         }
302         if (readOnly()) {
303             throw bufferIsReadOnly(this);
304         }
305         length = Math.min(writableBytes(), length);
306         if (length == 0) {
307             return 0;
308         }
309         checkSet(writerOffset(), length);
310         int bytesRead = channel.read(writableBuffer().limit(length), position);
311         if (bytesRead > 0) { // Don't skipWritable if bytesRead is 0 or -1
312             skipWritableBytes(bytesRead);
313         }
314         return bytesRead;
315     }
316 
317     @Override
318     public int transferFrom(ReadableByteChannel channel, int length) throws IOException {
319         if (!isAccessible()) {
320             throw bufferIsClosed();
321         }
322         if (readOnly()) {
323             throw bufferIsReadOnly(this);
324         }
325         length = Math.min(writableBytes(), length);
326         if (length == 0) {
327             return 0;
328         }
329         checkSet(writerOffset(), length);
330         int bytesRead = channel.read(writableBuffer().limit(length));
331         if (bytesRead != -1) {
332             skipWritableBytes(bytesRead);
333         }
334         return bytesRead;
335     }
336 
337     @Override
338     public int bytesBefore(byte needle) {
339         // For the details of this algorithm, see Hacker's Delight, Chapter 6, Searching Words.
340         // Richard Startin also describes this on his blog: https://richardstartin.github.io/posts/finding-bytes
341         if (!isAccessible()) {
342             throw bufferIsClosed();
343         }
344         int offset = roff;
345         final int length = woff - roff;
346         final int end = woff;
347 
348         if (length > 7) {
349             final long pattern = (needle & 0xFFL) * 0x101010101010101L;
350             for (final int longEnd = offset + (length >>> 3) * Long.BYTES;
351                  offset < longEnd;
352                  offset += Long.BYTES) {
353                 final long word = rmem.getLong(offset);
354 
355                 long input = word ^ pattern;
356                 long tmp = (input & 0x7F7F7F7F7F7F7F7FL) + 0x7F7F7F7F7F7F7F7FL;
357                 tmp = ~(tmp | input | 0x7F7F7F7F7F7F7F7FL);
358                 final int binaryPosition = Long.numberOfLeadingZeros(tmp);
359 
360                 int index = binaryPosition >>> 3;
361                 if (index < Long.BYTES) {
362                     return offset + index - roff;
363                 }
364             }
365         }
366         for (; offset < end; offset++) {
367             if (rmem.get(offset) == needle) {
368                 return offset - roff;
369             }
370         }
371 
372         return -1;
373     }
374 
375     @Override
376     public int bytesBefore(Buffer needle) {
377         UncheckedLoadByte uncheckedLoadByte = NioBuffer::uncheckedLoadByte;
378         return Statics.bytesBefore(this, uncheckedLoadByte,
379                                    needle, needle instanceof NioBuffer ? uncheckedLoadByte : null);
380     }
381 
382     /**
383      * Used by {@link #bytesBefore(Buffer)}.
384      */
385     private static byte uncheckedLoadByte(Buffer buffer, int offset) {
386         return ((NioBuffer) buffer).rmem.get(offset);
387     }
388 
389     @Override
390     public ByteCursor openCursor() {
391         return openCursor(readerOffset(), readableBytes());
392     }
393 
394     @Override
395     public ByteCursor openCursor(int fromOffset, int length) {
396         if (rmem == CLOSED_BUFFER) {
397             throw bufferIsClosed();
398         }
399         if (fromOffset < 0) {
400             throw new IndexOutOfBoundsException("The fromOffset cannot be negative: " + fromOffset + '.');
401         }
402         checkLength(length);
403         if (capacity() < fromOffset + length) {
404             throw new IndexOutOfBoundsException("The fromOffset + length is beyond the end of the buffer: " +
405                     "fromOffset = " + fromOffset + ", length = " + length + '.');
406         }
407         return new ForwardNioByteCursor(rmem, fromOffset, length);
408     }
409 
410     @Override
411     public ByteCursor openReverseCursor(int fromOffset, int length) {
412         if (rmem == CLOSED_BUFFER) {
413             throw bufferIsClosed();
414         }
415         if (fromOffset < 0) {
416             throw new IndexOutOfBoundsException("The fromOffset cannot be negative: " + fromOffset + '.');
417         }
418         checkLength(length);
419         if (capacity() <= fromOffset) {
420             throw new IndexOutOfBoundsException("The fromOffset is beyond the end of the buffer: " + fromOffset + '.');
421         }
422         if (fromOffset - length < -1) {
423             throw new IndexOutOfBoundsException("The fromOffset - length would underflow the buffer: " +
424                     "fromOffset = " + fromOffset + ", length = " + length + '.');
425         }
426         return new ReverseNioByteCursor(rmem, fromOffset, length);
427     }
428 
429     @Override
430     public Buffer ensureWritable(int size, int minimumGrowth, boolean allowCompaction) {
431         if (!isAccessible()) {
432             throw bufferIsClosed();
433         }
434         if (!isOwned()) {
435             throw attachTrace(new IllegalStateException(
436                     "Buffer is not owned. Only owned buffers can call ensureWritable."));
437         }
438         if (size < 0) {
439             throw new IllegalArgumentException("Cannot ensure writable for a negative size: " + size + '.');
440         }
441         if (minimumGrowth < 0) {
442             throw new IllegalArgumentException("The minimum growth cannot be negative: " + minimumGrowth + '.');
443         }
444         if (rmem != wmem) {
445             throw bufferIsReadOnly(this);
446         }
447         if (writableBytes() >= size) {
448             // We already have enough space.
449             return this;
450         }
451 
452         if (allowCompaction && writableBytes() + readerOffset() >= size) {
453             // We can solve this with compaction.
454             return compact();
455         }
456 
457         // Allocate a bigger buffer.
458         long newSize = capacity() + (long) Math.max(size - writableBytes(), minimumGrowth);
459         Statics.assertValidBufferSize(newSize);
460         NioBuffer buffer = (NioBuffer) control.getAllocator().allocate((int) newSize);
461 
462         // Copy contents.
463         copyInto(0, buffer, 0, capacity());
464 
465         // Release the old memory and install the new:
466         Drop<NioBuffer> drop = buffer.unsafeGetDrop();
467         disconnectDrop(drop);
468         attachNewBuffer(buffer, drop);
469         return this;
470     }
471 
472     private void disconnectDrop(Drop<NioBuffer> newDrop) {
473         var drop = (Drop<NioBuffer>) unsafeGetDrop();
474         int roff = this.roff;
475         int woff = this.woff;
476         drop.drop(this);
477         unsafeSetDrop(newDrop);
478         this.roff = roff;
479         this.woff = woff;
480     }
481 
482     private void attachNewBuffer(NioBuffer buffer, Drop<NioBuffer> drop) {
483         base = buffer.base;
484         rmem = buffer.rmem;
485         wmem = buffer.wmem;
486         drop.attach(this);
487     }
488 
489     @Override
490     public Buffer split(int splitOffset) {
491         if (splitOffset < 0) {
492             throw new IllegalArgumentException("The split offset cannot be negative: " + splitOffset + '.');
493         }
494         if (capacity() < splitOffset) {
495             throw new IllegalArgumentException("The split offset cannot be greater than the buffer capacity, " +
496                     "but the split offset was " + splitOffset + ", and capacity is " + capacity() + '.');
497         }
498         if (!isAccessible()) {
499             throw bufferIsClosed();
500         }
501         if (!isOwned()) {
502             throw attachTrace(new IllegalStateException("Cannot split a buffer that is not owned."));
503         }
504         var drop = unsafeGetDrop().fork();
505         var splitByteBuffer = bbslice(rmem, 0, splitOffset);
506         var splitBuffer = new NioBuffer(base, splitByteBuffer, control, drop);
507         drop.attach(splitBuffer);
508         splitBuffer.woff = Math.min(woff, splitOffset);
509         splitBuffer.roff = Math.min(roff, splitOffset);
510         boolean readOnly = readOnly();
511         if (readOnly) {
512             splitBuffer.makeReadOnly();
513         }
514         // Split preserves const-state.
515         rmem = bbslice(rmem, splitOffset, rmem.capacity() - splitOffset);
516         if (!readOnly) {
517             wmem = rmem;
518         }
519         woff = Math.max(woff, splitOffset) - splitOffset;
520         roff = Math.max(roff, splitOffset) - splitOffset;
521         return splitBuffer;
522     }
523 
524     @Override
525     public Buffer compact() {
526         if (!isAccessible()) {
527             throw bufferIsClosed();
528         }
529         if (!isOwned()) {
530             throw attachTrace(new IllegalStateException("Buffer must be owned in order to compact."));
531         }
532         if (readOnly()) {
533             throw new BufferReadOnlyException("Buffer must be writable in order to compact, but was read-only.");
534         }
535         if (roff == 0) {
536             return this;
537         }
538         rmem.limit(woff).position(roff).compact().clear();
539         woff -= roff;
540         roff = 0;
541         return this;
542     }
543 
544     @Override
545     public int countComponents() {
546         return 1;
547     }
548 
549     @Override
550     public int countReadableComponents() {
551         return readableBytes() > 0? 1 : 0;
552     }
553 
554     @Override
555     public int countWritableComponents() {
556         return writableBytes() > 0? 1 : 0;
557     }
558 
559     // <editor-fold defaultstate="collapsed" desc="Readable/WritableComponent implementation.">
560     @Override
561     public boolean hasReadableArray() {
562         return rmem.hasArray();
563     }
564 
565     @Override
566     public byte[] readableArray() {
567         return rmem.array();
568     }
569 
570     @Override
571     public int readableArrayOffset() {
572         return rmem.arrayOffset() + roff;
573     }
574 
575     @Override
576     public int readableArrayLength() {
577         return woff - roff;
578     }
579 
580     @Override
581     public long readableNativeAddress() {
582         return nativeAddressWithOffset(nativeAddress(), roff);
583     }
584 
585     @Override
586     public ByteBuffer readableBuffer() {
587         return bbslice(rmem.asReadOnlyBuffer(), readerOffset(), readableBytes());
588     }
589 
590     @Override
591     public ByteBuffer mutableReadableBuffer() {
592         return bbslice(rmem, readerOffset(), readableBytes());
593     }
594 
595     @Override
596     public boolean hasWritableArray() {
597         return wmem.hasArray();
598     }
599 
600     @Override
601     public byte[] writableArray() {
602         return wmem.array();
603     }
604 
605     @Override
606     public int writableArrayOffset() {
607         return wmem.arrayOffset() + woff;
608     }
609 
610     @Override
611     public int writableArrayLength() {
612         return capacity() - woff;
613     }
614 
615     @Override
616     public long writableNativeAddress() {
617         return nativeAddressWithOffset(nativeAddress(), woff);
618     }
619 
620     @Override
621     public ByteBuffer writableBuffer() {
622         return bbslice(wmem, writerOffset(), writableBytes());
623     }
624 
625     @Override
626     public <N extends Next> N next() {
627         return null; // There is no "next" component in our external-iteration of components.
628     }
629     // </editor-fold>
630 
631     @Override
632     public <E extends Exception> int forEachReadable(int initialIndex, ReadableComponentProcessor<E> processor)
633             throws E {
634         if (!isAccessible()) {
635             throw bufferIsClosed();
636         }
637         int readableBytes = readableBytes();
638         if (readableBytes == 0) {
639             return 0;
640         }
641         checkRead(readerOffset(), readableBytes);
642         try {
643             return processor.process(initialIndex, this)? 1 : -1;
644         } finally {
645             Reference.reachabilityFence(this);
646         }
647     }
648 
649     @Override
650     public <T extends ReadableComponent & Next> ComponentIterator<T> forEachReadable() {
651         return new SingleComponentIterator<>(acquire(), readableBytes() > 0 ? this : null);
652     }
653 
654     @Override
655     public <E extends Exception> int forEachWritable(int initialIndex, WritableComponentProcessor<E> processor)
656             throws E {
657         if (!isAccessible()) {
658             throw bufferIsClosed();
659         }
660         int writableBytes = writableBytes();
661         if (writableBytes == 0) {
662             return 0;
663         }
664         checkWrite(writerOffset(), writableBytes, false);
665         try {
666             return processor.process(initialIndex, this)? 1 : -1;
667         } finally {
668             Reference.reachabilityFence(this);
669         }
670     }
671 
672     @Override
673     public <T extends WritableComponent & Next> ComponentIterator<T> forEachWritable() {
674         checkWrite(writerOffset(), writableBytes(), false);
675         return new SingleComponentIterator<>(acquire(), writableBytes() > 0 ? this : null);
676     }
677 
678     // <editor-fold defaultstate="collapsed" desc="Primitive accessors implementation.">
679     @Override
680     public byte readByte() {
681         checkRead(roff, Byte.BYTES);
682         var value = rmem.get(roff);
683         roff += Byte.BYTES;
684         return value;
685     }
686 
687     @Override
688     public byte getByte(int roff) {
689         checkGet(roff, Byte.BYTES);
690         return rmem.get(roff);
691     }
692 
693     @Override
694     public int readUnsignedByte() {
695         return readByte() & 0xFF;
696     }
697 
698     @Override
699     public int getUnsignedByte(int roff) {
700         return getByte(roff) & 0xFF;
701     }
702 
703     @Override
704     public Buffer writeByte(byte value) {
705         checkWrite(woff, Byte.BYTES, true);
706         try {
707             wmem.put(woff, value);
708             woff += Byte.BYTES;
709             return this;
710         } catch (IndexOutOfBoundsException e) {
711             throw checkWriteState(e, woff, Byte.BYTES);
712         } catch (ReadOnlyBufferException e) {
713             throw bufferIsReadOnly(this);
714         }
715     }
716 
717     @Override
718     public Buffer setByte(int woff, byte value) {
719         try {
720             wmem.put(woff, value);
721             return this;
722         } catch (IndexOutOfBoundsException e) {
723             throw checkWriteState(e, woff, Byte.BYTES);
724         } catch (ReadOnlyBufferException e) {
725             throw bufferIsReadOnly(this);
726         }
727     }
728 
729     @Override
730     public Buffer writeUnsignedByte(int value) {
731         checkWrite(woff, Byte.BYTES, true);
732         try {
733             wmem.put(woff, (byte) (value & 0xFF));
734             woff += Byte.BYTES;
735             return this;
736         } catch (IndexOutOfBoundsException e) {
737             throw checkWriteState(e, woff, Byte.BYTES);
738         } catch (ReadOnlyBufferException e) {
739             throw bufferIsReadOnly(this);
740         }
741     }
742 
743     @Override
744     public Buffer setUnsignedByte(int woff, int value) {
745         try {
746             wmem.put(woff, (byte) (value & 0xFF));
747             return this;
748         } catch (IndexOutOfBoundsException e) {
749             throw checkWriteState(e, woff, Byte.BYTES);
750         } catch (ReadOnlyBufferException e) {
751             throw bufferIsReadOnly(this);
752         }
753     }
754 
755     @Override
756     public char readChar() {
757         checkRead(roff, Character.BYTES);
758         var value = rmem.getChar(roff);
759         roff += Character.BYTES;
760         return value;
761     }
762 
763     @Override
764     public char getChar(int roff) {
765         checkGet(roff, Character.BYTES);
766         return rmem.getChar(roff);
767     }
768 
769     @Override
770     public Buffer writeChar(char value) {
771         checkWrite(woff, Character.BYTES, true);
772         try {
773             wmem.putChar(woff, value);
774             woff += Character.BYTES;
775             return this;
776         } catch (IndexOutOfBoundsException e) {
777             throw checkWriteState(e, woff, Character.BYTES);
778         } catch (ReadOnlyBufferException e) {
779             throw bufferIsReadOnly(this);
780         }
781     }
782 
783     @Override
784     public Buffer setChar(int woff, char value) {
785         try {
786             wmem.putChar(woff, value);
787             return this;
788         } catch (IndexOutOfBoundsException e) {
789             throw checkWriteState(e, woff, Character.BYTES);
790         } catch (ReadOnlyBufferException e) {
791             throw bufferIsReadOnly(this);
792         }
793     }
794 
795     @Override
796     public short readShort() {
797         checkRead(roff, Short.BYTES);
798         var value = rmem.getShort(roff);
799         roff += Short.BYTES;
800         return value;
801     }
802 
803     @Override
804     public short getShort(int roff) {
805         checkGet(roff, Short.BYTES);
806         return rmem.getShort(roff);
807     }
808 
809     @Override
810     public int readUnsignedShort() {
811         checkRead(roff, Short.BYTES);
812         var value = rmem.getShort(roff) & 0xFFFF;
813         roff += Short.BYTES;
814         return value;
815     }
816 
817     @Override
818     public int getUnsignedShort(int roff) {
819         checkGet(roff, Short.BYTES);
820         return rmem.getShort(roff) & 0xFFFF;
821     }
822 
823     @Override
824     public Buffer writeShort(short value) {
825         checkWrite(woff, Short.BYTES, true);
826         try {
827             wmem.putShort(woff, value);
828             woff += Short.BYTES;
829             return this;
830         } catch (IndexOutOfBoundsException e) {
831             throw checkWriteState(e, woff, Short.BYTES);
832         } catch (ReadOnlyBufferException e) {
833             throw bufferIsReadOnly(this);
834         }
835     }
836 
837     @Override
838     public Buffer setShort(int woff, short value) {
839         try {
840             wmem.putShort(woff, value);
841             return this;
842         } catch (IndexOutOfBoundsException e) {
843             throw checkWriteState(e, woff, Short.BYTES);
844         } catch (ReadOnlyBufferException e) {
845             throw bufferIsReadOnly(this);
846         }
847     }
848 
849     @Override
850     public Buffer writeUnsignedShort(int value) {
851         checkWrite(woff, Short.BYTES, true);
852         try {
853             wmem.putShort(woff, (short) (value & 0xFFFF));
854             woff += Short.BYTES;
855             return this;
856         } catch (IndexOutOfBoundsException e) {
857             throw checkWriteState(e, woff, Short.BYTES);
858         } catch (ReadOnlyBufferException e) {
859             throw bufferIsReadOnly(this);
860         }
861     }
862 
863     @Override
864     public Buffer setUnsignedShort(int woff, int value) {
865         try {
866             wmem.putShort(woff, (short) (value & 0xFFFF));
867             return this;
868         } catch (IndexOutOfBoundsException e) {
869             throw checkWriteState(e, woff, Short.BYTES);
870         } catch (ReadOnlyBufferException e) {
871             throw bufferIsReadOnly(this);
872         }
873     }
874 
875     @Override
876     public int readMedium() {
877         checkRead(roff, 3);
878         int value = rmem.get(roff) << 16 | (rmem.get(roff + 1) & 0xFF) << 8 | rmem.get(roff + 2) & 0xFF;
879         roff += 3;
880         return value;
881     }
882 
883     @Override
884     public int getMedium(int roff) {
885         checkGet(roff, 3);
886         return rmem.get(roff) << 16 | (rmem.get(roff + 1) & 0xFF) << 8 | rmem.get(roff + 2) & 0xFF;
887     }
888 
889     @Override
890     public int readUnsignedMedium() {
891         checkRead(roff, 3);
892         int value = (rmem.get(roff) << 16 | (rmem.get(roff + 1) & 0xFF) << 8 | rmem.get(roff + 2) & 0xFF) & 0xFFFFFF;
893         roff += 3;
894         return value;
895     }
896 
897     @Override
898     public int getUnsignedMedium(int roff) {
899         checkGet(roff, 3);
900         return (rmem.get(roff) << 16 | (rmem.get(roff + 1) & 0xFF) << 8 | rmem.get(roff + 2) & 0xFF) & 0xFFFFFF;
901     }
902 
903     @Override
904     public Buffer writeMedium(int value) {
905         checkWrite(woff, 3, true);
906         wmem.put(woff, (byte) (value >> 16));
907         wmem.put(woff + 1, (byte) (value >> 8 & 0xFF));
908         wmem.put(woff + 2, (byte) (value & 0xFF));
909         woff += 3;
910         return this;
911     }
912 
913     @Override
914     public Buffer setMedium(int woff, int value) {
915         checkSet(woff, 3);
916         wmem.put(woff, (byte) (value >> 16));
917         wmem.put(woff + 1, (byte) (value >> 8 & 0xFF));
918         wmem.put(woff + 2, (byte) (value & 0xFF));
919         return this;
920     }
921 
922     @Override
923     public Buffer writeUnsignedMedium(int value) {
924         checkWrite(woff, 3, true);
925         wmem.put(woff, (byte) (value >> 16));
926         wmem.put(woff + 1, (byte) (value >> 8 & 0xFF));
927         wmem.put(woff + 2, (byte) (value & 0xFF));
928         woff += 3;
929         return this;
930     }
931 
932     @Override
933     public Buffer setUnsignedMedium(int woff, int value) {
934         checkSet(woff, 3);
935         wmem.put(woff, (byte) (value >> 16));
936         wmem.put(woff + 1, (byte) (value >> 8 & 0xFF));
937         wmem.put(woff + 2, (byte) (value & 0xFF));
938         return this;
939     }
940 
941     @Override
942     public int readInt() {
943         checkRead(roff, Integer.BYTES);
944         var value = rmem.getInt(roff);
945         roff += Integer.BYTES;
946         return value;
947     }
948 
949     @Override
950     public int getInt(int roff) {
951         checkGet(roff, Integer.BYTES);
952         return rmem.getInt(roff);
953     }
954 
955     @Override
956     public long readUnsignedInt() {
957         checkRead(roff, Integer.BYTES);
958         var value = rmem.getInt(roff) & 0xFFFFFFFFL;
959         roff += Integer.BYTES;
960         return value;
961     }
962 
963     @Override
964     public long getUnsignedInt(int roff) {
965         checkGet(roff, Integer.BYTES);
966         return rmem.getInt(roff) & 0xFFFFFFFFL;
967     }
968 
969     @Override
970     public Buffer writeInt(int value) {
971         checkWrite(woff, Integer.BYTES, true);
972         try {
973             wmem.putInt(woff, value);
974             woff += Integer.BYTES;
975             return this;
976         } catch (IndexOutOfBoundsException e) {
977             throw checkWriteState(e, woff, Integer.BYTES);
978         } catch (ReadOnlyBufferException e) {
979             throw bufferIsReadOnly(this);
980         }
981     }
982 
983     @Override
984     public Buffer setInt(int woff, int value) {
985         try {
986             wmem.putInt(woff, value);
987             return this;
988         } catch (IndexOutOfBoundsException e) {
989             throw checkWriteState(e, this.woff, Integer.BYTES);
990         } catch (ReadOnlyBufferException e) {
991             throw bufferIsReadOnly(this);
992         }
993     }
994 
995     @Override
996     public Buffer writeUnsignedInt(long value) {
997         checkWrite(woff, Integer.BYTES, true);
998         try {
999             wmem.putInt(woff, (int) (value & 0xFFFFFFFFL));
1000             woff += Integer.BYTES;
1001             return this;
1002         } catch (IndexOutOfBoundsException e) {
1003             throw checkWriteState(e, woff, Integer.BYTES);
1004         } catch (ReadOnlyBufferException e) {
1005             throw bufferIsReadOnly(this);
1006         }
1007     }
1008 
1009     @Override
1010     public Buffer setUnsignedInt(int woff, long value) {
1011         try {
1012             wmem.putInt(woff, (int) (value & 0xFFFFFFFFL));
1013             return this;
1014         } catch (IndexOutOfBoundsException e) {
1015             throw checkWriteState(e, this.woff, Integer.BYTES);
1016         } catch (ReadOnlyBufferException e) {
1017             throw bufferIsReadOnly(this);
1018         }
1019     }
1020 
1021     @Override
1022     public float readFloat() {
1023         checkRead(roff, Float.BYTES);
1024         var value = rmem.getFloat(roff);
1025         roff += Float.BYTES;
1026         return value;
1027     }
1028 
1029     @Override
1030     public float getFloat(int roff) {
1031         checkGet(roff, Float.BYTES);
1032         return rmem.getFloat(roff);
1033     }
1034 
1035     @Override
1036     public Buffer writeFloat(float value) {
1037         checkWrite(woff, Float.BYTES, true);
1038         try {
1039             wmem.putFloat(woff, value);
1040             woff += Float.BYTES;
1041             return this;
1042         } catch (IndexOutOfBoundsException e) {
1043             throw checkWriteState(e, woff, Float.BYTES);
1044         } catch (ReadOnlyBufferException e) {
1045             throw bufferIsReadOnly(this);
1046         }
1047     }
1048 
1049     @Override
1050     public Buffer setFloat(int woff, float value) {
1051         try {
1052             wmem.putFloat(woff, value);
1053             return this;
1054         } catch (IndexOutOfBoundsException e) {
1055             throw checkWriteState(e, woff, Float.BYTES);
1056         } catch (ReadOnlyBufferException e) {
1057             throw bufferIsReadOnly(this);
1058         }
1059     }
1060 
1061     @Override
1062     public long readLong() {
1063         checkRead(roff, Long.BYTES);
1064         var value = rmem.getLong(roff);
1065         roff += Long.BYTES;
1066         return value;
1067     }
1068 
1069     @Override
1070     public long getLong(int roff) {
1071         checkGet(roff, Long.BYTES);
1072         return rmem.getLong(roff);
1073     }
1074 
1075     @Override
1076     public Buffer writeLong(long value) {
1077         checkWrite(woff, Long.BYTES, true);
1078         try {
1079             wmem.putLong(woff, value);
1080             woff += Long.BYTES;
1081             return this;
1082         } catch (IndexOutOfBoundsException e) {
1083             throw checkWriteState(e, woff, Long.BYTES);
1084         } catch (ReadOnlyBufferException e) {
1085             throw bufferIsReadOnly(this);
1086         }
1087     }
1088 
1089     @Override
1090     public Buffer setLong(int woff, long value) {
1091         try {
1092             wmem.putLong(woff, value);
1093             return this;
1094         } catch (IndexOutOfBoundsException e) {
1095             throw checkWriteState(e, woff, Long.BYTES);
1096         } catch (ReadOnlyBufferException e) {
1097             throw bufferIsReadOnly(this);
1098         }
1099     }
1100 
1101     @Override
1102     public double readDouble() {
1103         checkRead(roff, Double.BYTES);
1104         var value = rmem.getDouble(roff);
1105         roff += Double.BYTES;
1106         return value;
1107     }
1108 
1109     @Override
1110     public double getDouble(int roff) {
1111         checkGet(roff, Double.BYTES);
1112         return rmem.getDouble(roff);
1113     }
1114 
1115     @Override
1116     public Buffer writeDouble(double value) {
1117         checkWrite(woff, Double.BYTES, true);
1118         try {
1119             wmem.putDouble(woff, value);
1120             woff += Double.BYTES;
1121             return this;
1122         } catch (IndexOutOfBoundsException e) {
1123             throw checkWriteState(e, woff, Double.BYTES);
1124         } catch (ReadOnlyBufferException e) {
1125             throw bufferIsReadOnly(this);
1126         }
1127     }
1128 
1129     @Override
1130     public Buffer setDouble(int woff, double value) {
1131         try {
1132             wmem.putDouble(woff, value);
1133             return this;
1134         } catch (IndexOutOfBoundsException e) {
1135             throw checkWriteState(e, woff, Double.BYTES);
1136         } catch (ReadOnlyBufferException e) {
1137             throw bufferIsReadOnly(this);
1138         }
1139     }
1140     // </editor-fold>
1141 
1142     @Override
1143     protected Owned<NioBuffer> prepareSend() {
1144         int roff = this.roff;
1145         int woff = this.woff;
1146         boolean readOnly = readOnly();
1147         int implicitCapacityLimit = this.implicitCapacityLimit;
1148         ByteBuffer base = this.base;
1149         ByteBuffer rmem = this.rmem;
1150         return drop -> {
1151             NioBuffer copy = new NioBuffer(base, rmem, control, drop);
1152             copy.roff = roff;
1153             copy.woff = woff;
1154             copy.implicitCapacityLimit = implicitCapacityLimit;
1155             if (readOnly) {
1156                 copy.makeReadOnly();
1157             }
1158             return copy;
1159         };
1160     }
1161 
1162     @Override
1163     protected void makeInaccessible() {
1164         base = CLOSED_BUFFER;
1165         rmem = CLOSED_BUFFER;
1166         wmem = CLOSED_BUFFER;
1167         roff = 0;
1168         woff = 0;
1169     }
1170 
1171     private void checkRead(int index, int size) {
1172         if (index < 0 | woff < index + size) {
1173             throw readAccessCheckException(index, size);
1174         }
1175     }
1176 
1177     private void checkGet(int index, int size) {
1178         if (index < 0 | capacity() < index + size) {
1179             throw readAccessCheckException(index, size);
1180         }
1181     }
1182 
1183     private void checkWrite(int index, int size, boolean mayExpand) {
1184         if (index < roff | wmem.capacity() < index + size) {
1185             handleWriteAccessBoundsFailure(index, size, mayExpand);
1186         }
1187     }
1188 
1189     private void checkSet(int index, int size) {
1190         if (index < 0 | wmem.capacity() < index + size) {
1191             handleWriteAccessBoundsFailure(index, size, false);
1192         }
1193     }
1194 
1195     private RuntimeException checkWriteState(IndexOutOfBoundsException ioobe, int offset, int size) {
1196         if (rmem == CLOSED_BUFFER) {
1197             return bufferIsClosed();
1198         }
1199         if (wmem != rmem) {
1200             return bufferIsReadOnly(this);
1201         }
1202 
1203         IndexOutOfBoundsException exception = outOfBounds(offset, size);
1204         exception.addSuppressed(ioobe);
1205         return exception;
1206     }
1207 
1208     private RuntimeException readAccessCheckException(int index, int size) {
1209         if (rmem == CLOSED_BUFFER) {
1210             return bufferIsClosed();
1211         }
1212         return outOfBounds(index, size);
1213     }
1214 
1215     private void handleWriteAccessBoundsFailure(int index, int size, boolean mayExpand) {
1216         if (rmem == CLOSED_BUFFER) {
1217             throw bufferIsClosed();
1218         }
1219         if (wmem != rmem) {
1220             throw bufferIsReadOnly(this);
1221         }
1222         int capacity = capacity();
1223         if (mayExpand && index >= 0 && index <= capacity && woff + size <= implicitCapacityLimit && isOwned()) {
1224             // Grow into next power-of-two, but not beyond the implicit limit.
1225             int minimumGrowth = Math.min(
1226                     Math.max(roundToPowerOfTwo(capacity * 2), size),
1227                     implicitCapacityLimit) - capacity;
1228             ensureWritable(size, minimumGrowth, false);
1229             checkSet(index, size); // Verify writing is now possible, without recursing.
1230             return;
1231         }
1232         throw outOfBounds(index, size);
1233     }
1234 
1235     private BufferClosedException bufferIsClosed() {
1236         return attachTrace(Statics.bufferIsClosed(this));
1237     }
1238 
1239     private IndexOutOfBoundsException outOfBounds(int index, int size) {
1240         return new IndexOutOfBoundsException(
1241                 "Access at index " + index + " of size " + size + " is out of bounds: " +
1242                 "[read 0 to " + woff + ", write 0 to " + rmem.capacity() + "].");
1243     }
1244 
1245     ByteBuffer recoverable() {
1246         return base;
1247     }
1248 
1249     NioBuffer newConstChild() {
1250         assert readOnly();
1251         Drop<NioBuffer> drop = unsafeGetDrop().fork();
1252         NioBuffer child = new NioBuffer(this, drop);
1253         drop.attach(child);
1254         return child;
1255     }
1256 
1257     private static final class ForwardNioByteCursor implements ByteCursor {
1258         // Duplicate source buffer to keep our own byte order state.
1259         final ByteBuffer buffer;
1260         int index;
1261         final int end;
1262         byte byteValue;
1263 
1264         ForwardNioByteCursor(ByteBuffer rmem, int fromOffset, int length) {
1265             buffer = rmem.duplicate().order(ByteOrder.BIG_ENDIAN);
1266             index = fromOffset;
1267             end = index + length;
1268             byteValue = -1;
1269         }
1270 
1271         @Override
1272         public boolean readByte() {
1273             if (index < end) {
1274                 byteValue = buffer.get(index);
1275                 index++;
1276                 return true;
1277             }
1278             return false;
1279         }
1280 
1281         @Override
1282         public byte getByte() {
1283             return byteValue;
1284         }
1285 
1286         @Override
1287         public int currentOffset() {
1288             return index;
1289         }
1290 
1291         @Override
1292         public int bytesLeft() {
1293             return end - index;
1294         }
1295     }
1296 
1297     private static final class ReverseNioByteCursor implements ByteCursor {
1298         final ByteBuffer buffer;
1299         int index;
1300         final int end;
1301         byte byteValue;
1302 
1303         ReverseNioByteCursor(ByteBuffer rmem, int fromOffset, int length) {
1304             buffer = rmem.duplicate().order(ByteOrder.LITTLE_ENDIAN);
1305             index = fromOffset;
1306             end = index - length;
1307             byteValue = -1;
1308         }
1309 
1310         @Override
1311         public boolean readByte() {
1312             if (index > end) {
1313                 byteValue = buffer.get(index);
1314                 index--;
1315                 return true;
1316             }
1317             return false;
1318         }
1319 
1320         @Override
1321         public byte getByte() {
1322             return byteValue;
1323         }
1324 
1325         @Override
1326         public int currentOffset() {
1327             return index;
1328         }
1329 
1330         @Override
1331         public int bytesLeft() {
1332             return index - end;
1333         }
1334     }
1335 }