/* * Copyright 2021 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package io.netty.buffer.api.bytebuffer; import io.netty.buffer.ByteBuf; import io.netty.buffer.api.AllocatorControl; import io.netty.buffer.api.Buffer; import io.netty.buffer.api.BufferAllocator; import io.netty.buffer.api.BufferReadOnlyException; import io.netty.buffer.api.ByteCursor; import io.netty.buffer.api.Drop; import io.netty.buffer.api.Owned; import io.netty.buffer.api.adaptor.BufferIntegratable; import io.netty.buffer.api.adaptor.ByteBufAdaptor; import io.netty.buffer.api.adaptor.ByteBufAllocatorAdaptor; import io.netty.buffer.api.internal.ResourceSupport; import io.netty.buffer.api.ReadableComponent; import io.netty.buffer.api.ReadableComponentProcessor; import io.netty.buffer.api.WritableComponent; import io.netty.buffer.api.WritableComponentProcessor; import io.netty.buffer.api.internal.ArcDrop; import io.netty.buffer.api.internal.Statics; import io.netty.util.IllegalReferenceCountException; import io.netty.util.ReferenceCounted; import io.netty.util.internal.PlatformDependent; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.ReadOnlyBufferException; import static io.netty.buffer.api.internal.Statics.bbput; import static io.netty.buffer.api.internal.Statics.bbslice; import static io.netty.buffer.api.internal.Statics.bufferIsClosed; import static io.netty.buffer.api.internal.Statics.bufferIsReadOnly; class NioBuffer extends ResourceSupport implements Buffer, ReadableComponent, WritableComponent, BufferIntegratable { private static final ByteBuffer CLOSED_BUFFER = ByteBuffer.allocate(0); private final AllocatorControl control; private ByteBuffer base; private ByteBuffer rmem; // For reading. private ByteBuffer wmem; // For writing. private int roff; private int woff; private boolean constBuffer; NioBuffer(ByteBuffer base, ByteBuffer memory, AllocatorControl control, Drop drop) { super(new MakeInaccessibleOnDrop(ArcDrop.wrap(drop))); this.base = base; rmem = memory; wmem = memory; this.control = control; } /** * Constructor for {@linkplain BufferAllocator#constBufferSupplier(byte[]) const buffers}. */ NioBuffer(NioBuffer parent) { super(new MakeInaccessibleOnDrop(new ArcDrop<>(ArcDrop.acquire(parent.unsafeGetDrop())))); control = parent.control; base = parent.base; rmem = bbslice(parent.rmem, 0, parent.rmem.capacity()); // Need to slice to get independent byte orders. assert parent.wmem == CLOSED_BUFFER; wmem = CLOSED_BUFFER; roff = parent.roff; woff = parent.woff; order(parent.order()); constBuffer = true; } private static final class MakeInaccessibleOnDrop implements Drop { final Drop delegate; private MakeInaccessibleOnDrop(Drop delegate) { this.delegate = delegate; } @Override public void drop(NioBuffer buf) { try { delegate.drop(buf); } finally { buf.makeInaccessible(); } } @Override public void attach(NioBuffer buf) { delegate.attach(buf); } @Override public String toString() { return "MakeInaccessibleOnDrop(" + delegate + ')'; } } @Override protected Drop unsafeGetDrop() { MakeInaccessibleOnDrop drop = (MakeInaccessibleOnDrop) super.unsafeGetDrop(); return drop.delegate; } @Override protected void unsafeSetDrop(Drop replacement) { super.unsafeSetDrop(new MakeInaccessibleOnDrop(replacement)); } @Override public String toString() { return "Buffer[roff:" + roff + ", woff:" + woff + ", cap:" + rmem.capacity() + ']'; } @Override protected RuntimeException createResourceClosedException() { return bufferIsClosed(this); } @Override public Buffer order(ByteOrder order) { rmem.order(order); return this; } @Override public ByteOrder order() { return rmem.order(); } @Override public int capacity() { return rmem.capacity(); } @Override public int readerOffset() { return roff; } @Override public Buffer readerOffset(int offset) { checkRead(offset, 0); roff = offset; return this; } @Override public int writerOffset() { return woff; } @Override public Buffer writerOffset(int offset) { checkWrite(offset, 0); woff = offset; return this; } @Override public Buffer fill(byte value) { int capacity = capacity(); checkSet(0, capacity); if (rmem == CLOSED_BUFFER) { throw bufferIsClosed(this); } for (int i = 0; i < capacity; i++) { wmem.put(i, value); } return this; } @Override public long nativeAddress() { return rmem.isDirect() && PlatformDependent.hasUnsafe()? PlatformDependent.directBufferAddress(rmem) : 0; } @Override public Buffer makeReadOnly() { wmem = CLOSED_BUFFER; return this; } @Override public boolean readOnly() { return wmem == CLOSED_BUFFER && rmem != CLOSED_BUFFER; } @Override public Buffer copy(int offset, int length) { checkGet(offset, length); int allocSize = Math.max(length, 1); // Allocators don't support allocating zero-sized buffers. AllocatorControl.UntetheredMemory memory = control.allocateUntethered(this, allocSize); ByteBuffer base = memory.memory(); ByteBuffer buffer = length == 0? bbslice(base, 0, 0) : base; Buffer copy = new NioBuffer(base, buffer, control, memory.drop()); copyInto(offset, copy, 0, length); copy.writerOffset(length).order(order()); if (readOnly()) { copy = copy.makeReadOnly(); } return copy; } @Override public void copyInto(int srcPos, byte[] dest, int destPos, int length) { copyInto(srcPos, ByteBuffer.wrap(dest), destPos, length); } @Override public void copyInto(int srcPos, ByteBuffer dest, int destPos, int length) { if (rmem == CLOSED_BUFFER) { throw bufferIsClosed(this); } 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 (capacity() < srcPos + length) { throw new IllegalArgumentException("The srcPos + length is beyond the end of the buffer: " + "srcPos = " + srcPos + ", length = " + length + '.'); } dest = dest.duplicate().clear(); bbput(dest, destPos, rmem, srcPos, length); } @Override public void copyInto(int srcPos, Buffer dest, int destPos, int length) { if (dest instanceof NioBuffer) { var nb = (NioBuffer) dest; nb.checkSet(destPos, length); copyInto(srcPos, nb.wmem, destPos, length); return; } Statics.copyToViaReverseCursor(this, srcPos, dest, destPos, length); } @Override public ByteCursor openCursor() { return openCursor(readerOffset(), readableBytes()); } @Override public ByteCursor openCursor(int fromOffset, int length) { if (rmem == CLOSED_BUFFER) { throw bufferIsClosed(this); } if (fromOffset < 0) { throw new IllegalArgumentException("The fromOffset cannot be negative: " + fromOffset + '.'); } if (length < 0) { throw new IllegalArgumentException("The length cannot be negative: " + length + '.'); } if (capacity() < fromOffset + length) { throw new IllegalArgumentException("The fromOffset + length is beyond the end of the buffer: " + "fromOffset = " + fromOffset + ", length = " + length + '.'); } return new ByteCursor() { // Duplicate source buffer to keep our own byte order state. final ByteBuffer buffer = rmem.duplicate().order(ByteOrder.BIG_ENDIAN); int index = fromOffset; final int end = index + length; long longValue = -1; byte byteValue = -1; @Override public boolean readLong() { if (index + Long.BYTES <= end) { longValue = buffer.getLong(index); index += Long.BYTES; return true; } return false; } @Override public long getLong() { return longValue; } @Override public boolean readByte() { if (index < end) { byteValue = buffer.get(index); index++; return true; } return false; } @Override public byte getByte() { return byteValue; } @Override public int currentOffset() { return index; } @Override public int bytesLeft() { return end - index; } }; } @Override public ByteCursor openReverseCursor(int fromOffset, int length) { if (rmem == CLOSED_BUFFER) { throw bufferIsClosed(this); } if (fromOffset < 0) { throw new IllegalArgumentException("The fromOffset cannot be negative: " + fromOffset + '.'); } if (length < 0) { throw new IllegalArgumentException("The length cannot be negative: " + length + '.'); } if (capacity() <= fromOffset) { 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 + '.'); } return new ByteCursor() { final ByteBuffer buffer = rmem.duplicate().order(ByteOrder.LITTLE_ENDIAN); int index = fromOffset; final int end = index - length; long longValue = -1; byte byteValue = -1; @Override public boolean readLong() { if (index - Long.BYTES >= end) { index -= 7; longValue = buffer.getLong(index); index--; return true; } return false; } @Override public long getLong() { return longValue; } @Override public boolean readByte() { if (index > end) { byteValue = buffer.get(index); index--; return true; } return false; } @Override public byte getByte() { return byteValue; } @Override public int currentOffset() { return index; } @Override public int bytesLeft() { return index - end; } }; } @Override public void ensureWritable(int size, int minimumGrowth, boolean allowCompaction) { if (!isAccessible()) { throw bufferIsClosed(this); } if (!isOwned()) { throw attachTrace(new IllegalStateException( "Buffer is not owned. Only owned buffers can call ensureWritable.")); } if (size < 0) { throw new IllegalArgumentException("Cannot ensure writable for a negative size: " + size + '.'); } if (minimumGrowth < 0) { throw new IllegalArgumentException("The minimum growth cannot be negative: " + minimumGrowth + '.'); } if (rmem != wmem) { throw bufferIsReadOnly(this); } if (writableBytes() >= size) { // We already have enough space. return; } if (allowCompaction && writableBytes() + readerOffset() >= size) { // We can solve this with compaction. compact(); return; } // Allocate a bigger buffer. long newSize = capacity() + (long) Math.max(size - writableBytes(), minimumGrowth); BufferAllocator.checkSize(newSize); var untethered = control.allocateUntethered(this, (int) newSize); ByteBuffer buffer = untethered.memory(); buffer.order(order()); // Copy contents. copyInto(0, buffer, 0, capacity()); // Release the old memory and install the new: Drop drop = untethered.drop(); disconnectDrop(drop); attachNewBuffer(buffer, drop); } private void disconnectDrop(Drop newDrop) { var drop = (Drop) unsafeGetDrop(); int roff = this.roff; int woff = this.woff; drop.drop(this); unsafeSetDrop(new ArcDrop<>(newDrop)); this.roff = roff; this.woff = woff; } private void attachNewBuffer(ByteBuffer buffer, Drop drop) { base = buffer; rmem = buffer; wmem = buffer; constBuffer = false; drop.attach(this); } @Override public Buffer split(int splitOffset) { if (splitOffset < 0) { throw new IllegalArgumentException("The split offset cannot be negative: " + splitOffset + '.'); } if (capacity() < splitOffset) { throw new IllegalArgumentException("The split offset cannot be greater than the buffer capacity, " + "but the split offset was " + splitOffset + ", and capacity is " + capacity() + '.'); } if (!isAccessible()) { throw attachTrace(bufferIsClosed(this)); } if (!isOwned()) { throw attachTrace(new IllegalStateException("Cannot split a buffer that is not owned.")); } var drop = (ArcDrop) unsafeGetDrop(); unsafeSetDrop(new ArcDrop<>(drop)); var splitByteBuffer = bbslice(rmem, 0, splitOffset); // TODO maybe incrementing the existing ArcDrop is enough; maybe we don't need to wrap it in another ArcDrop. var splitBuffer = new NioBuffer(base, splitByteBuffer, control, new ArcDrop<>(drop.increment())); splitBuffer.woff = Math.min(woff, splitOffset); splitBuffer.roff = Math.min(roff, splitOffset); ByteOrder order = order(); splitBuffer.order(order); boolean readOnly = readOnly(); if (readOnly) { splitBuffer.makeReadOnly(); } // Split preserves const-state. splitBuffer.constBuffer = constBuffer; rmem = bbslice(rmem, splitOffset, rmem.capacity() - splitOffset); if (!readOnly) { wmem = rmem; } woff = Math.max(woff, splitOffset) - splitOffset; roff = Math.max(roff, splitOffset) - splitOffset; order(order); return splitBuffer; } @Override public void compact() { if (!isOwned()) { throw attachTrace(new IllegalStateException("Buffer must be owned in order to compact.")); } if (readOnly()) { throw new BufferReadOnlyException("Buffer must be writable in order to compact, but was read-only."); } if (roff == 0) { return; } rmem.limit(woff).position(roff).compact().clear(); woff -= roff; roff = 0; } @Override public int countComponents() { return 1; } @Override public int countReadableComponents() { return readableBytes() > 0? 1 : 0; } @Override public int countWritableComponents() { return writableBytes() > 0? 1 : 0; } // @Override public boolean hasReadableArray() { return rmem.hasArray(); } @Override public byte[] readableArray() { return rmem.array(); } @Override public int readableArrayOffset() { return rmem.arrayOffset() + roff; } @Override public int readableArrayLength() { return woff - roff; } @Override public long readableNativeAddress() { return nativeAddress(); } @Override public ByteBuffer readableBuffer() { return bbslice(rmem.asReadOnlyBuffer(), readerOffset(), readableBytes()).order(order()); } @Override public boolean hasWritableArray() { return wmem.hasArray(); } @Override public byte[] writableArray() { return wmem.array(); } @Override public int writableArrayOffset() { return wmem.arrayOffset() + woff; } @Override public int writableArrayLength() { return capacity() - woff; } @Override public long writableNativeAddress() { return nativeAddress(); } @Override public ByteBuffer writableBuffer() { return bbslice(wmem, writerOffset(), writableBytes()).order(order()); } // @Override public int forEachReadable(int initialIndex, ReadableComponentProcessor processor) throws E { checkRead(readerOffset(), Math.max(1, readableBytes())); return processor.process(initialIndex, this)? 1 : -1; } @Override public int forEachWritable(int initialIndex, WritableComponentProcessor processor) throws E { checkWrite(writerOffset(), Math.max(1, writableBytes())); return processor.process(initialIndex, this)? 1 : -1; } // @Override public byte readByte() { checkRead(roff, Byte.BYTES); var value = rmem.get(roff); roff += Byte.BYTES; return value; } @Override public byte getByte(int roff) { checkGet(roff, Byte.BYTES); return rmem.get(roff); } @Override public int readUnsignedByte() { return readByte() & 0xFF; } @Override public int getUnsignedByte(int roff) { return getByte(roff) & 0xFF; } @Override public Buffer writeByte(byte value) { try { wmem.put(woff, value); woff += Byte.BYTES; return this; } catch (IndexOutOfBoundsException e) { throw checkWriteState(e, woff); } catch (ReadOnlyBufferException e) { throw bufferIsReadOnly(this); } } @Override public Buffer setByte(int woff, byte value) { try { wmem.put(woff, value); return this; } catch (IndexOutOfBoundsException e) { throw checkWriteState(e, woff); } catch (ReadOnlyBufferException e) { throw bufferIsReadOnly(this); } } @Override public Buffer writeUnsignedByte(int value) { try { wmem.put(woff, (byte) (value & 0xFF)); woff += Byte.BYTES; return this; } catch (IndexOutOfBoundsException e) { throw checkWriteState(e, woff); } catch (ReadOnlyBufferException e) { throw bufferIsReadOnly(this); } } @Override public Buffer setUnsignedByte(int woff, int value) { try { wmem.put(woff, (byte) (value & 0xFF)); return this; } catch (IndexOutOfBoundsException e) { throw checkWriteState(e, woff); } catch (ReadOnlyBufferException e) { throw bufferIsReadOnly(this); } } @Override public char readChar() { checkRead(roff, 2); var value = rmem.getChar(roff); roff += 2; return value; } @Override public char getChar(int roff) { checkGet(roff, 2); return rmem.getChar(roff); } @Override public Buffer writeChar(char value) { try { wmem.putChar(woff, value); woff += 2; return this; } catch (IndexOutOfBoundsException e) { throw checkWriteState(e, woff); } catch (ReadOnlyBufferException e) { throw bufferIsReadOnly(this); } } @Override public Buffer setChar(int woff, char value) { try { wmem.putChar(woff, value); return this; } catch (IndexOutOfBoundsException e) { throw checkWriteState(e, woff); } catch (ReadOnlyBufferException e) { throw bufferIsReadOnly(this); } } @Override public short readShort() { checkRead(roff, Short.BYTES); var value = rmem.getShort(roff); roff += 2; return value; } @Override public short getShort(int roff) { checkGet(roff, Short.BYTES); return rmem.getShort(roff); } @Override public int readUnsignedShort() { checkRead(roff, Short.BYTES); var value = rmem.getShort(roff) & 0xFFFF; roff += 2; return value; } @Override public int getUnsignedShort(int roff) { checkGet(roff, Short.BYTES); return rmem.getShort(roff) & 0xFFFF; } @Override public Buffer writeShort(short value) { try { wmem.putShort(woff, value); woff += Short.BYTES; return this; } catch (IndexOutOfBoundsException e) { throw checkWriteState(e, woff); } catch (ReadOnlyBufferException e) { throw bufferIsReadOnly(this); } } @Override public Buffer setShort(int woff, short value) { try { wmem.putShort(woff, value); return this; } catch (IndexOutOfBoundsException e) { throw checkWriteState(e, woff); } catch (ReadOnlyBufferException e) { throw bufferIsReadOnly(this); } } @Override public Buffer writeUnsignedShort(int value) { try { wmem.putShort(woff, (short) (value & 0xFFFF)); woff += Short.BYTES; return this; } catch (IndexOutOfBoundsException e) { throw checkWriteState(e, woff); } catch (ReadOnlyBufferException e) { throw bufferIsReadOnly(this); } } @Override public Buffer setUnsignedShort(int woff, int value) { try { wmem.putShort(woff, (short) (value & 0xFFFF)); return this; } catch (IndexOutOfBoundsException e) { throw checkWriteState(e, woff); } catch (ReadOnlyBufferException e) { throw bufferIsReadOnly(this); } } @Override public int readMedium() { checkRead(roff, 3); int value = order() == ByteOrder.BIG_ENDIAN? rmem.get(roff) << 16 | (rmem.get(roff + 1) & 0xFF) << 8 | rmem.get(roff + 2) & 0xFF : rmem.get(roff) & 0xFF | (rmem.get(roff + 1) & 0xFF) << 8 | rmem.get(roff + 2) << 16; roff += 3; return value; } @Override public int getMedium(int roff) { checkGet(roff, 3); return order() == ByteOrder.BIG_ENDIAN? rmem.get(roff) << 16 | (rmem.get(roff + 1) & 0xFF) << 8 | rmem.get(roff + 2) & 0xFF : rmem.get(roff) & 0xFF | (rmem.get(roff + 1) & 0xFF) << 8 | rmem.get(roff + 2) << 16; } @Override public int readUnsignedMedium() { checkRead(roff, 3); int value = order() == ByteOrder.BIG_ENDIAN? (rmem.get(roff) << 16 | (rmem.get(roff + 1) & 0xFF) << 8 | rmem.get(roff + 2) & 0xFF) & 0xFFFFFF : (rmem.get(roff) & 0xFF | (rmem.get(roff + 1) & 0xFF) << 8 | rmem.get(roff + 2) << 16) & 0xFFFFFF; roff += 3; return value; } @Override public int getUnsignedMedium(int roff) { checkGet(roff, 3); return order() == ByteOrder.BIG_ENDIAN? (rmem.get(roff) << 16 | (rmem.get(roff + 1) & 0xFF) << 8 | rmem.get(roff + 2) & 0xFF) & 0xFFFFFF : (rmem.get(roff) & 0xFF | (rmem.get(roff + 1) & 0xFF) << 8 | rmem.get(roff + 2) << 16) & 0xFFFFFF; } @Override public Buffer writeMedium(int value) { checkWrite(woff, 3); if (order() == ByteOrder.BIG_ENDIAN) { wmem.put(woff, (byte) (value >> 16)); wmem.put(woff + 1, (byte) (value >> 8 & 0xFF)); wmem.put(woff + 2, (byte) (value & 0xFF)); } else { wmem.put(woff, (byte) (value & 0xFF)); wmem.put(woff + 1, (byte) (value >> 8 & 0xFF)); wmem.put(woff + 2, (byte) (value >> 16 & 0xFF)); } woff += 3; return this; } @Override public Buffer setMedium(int woff, int value) { checkSet(woff, 3); if (order() == ByteOrder.BIG_ENDIAN) { wmem.put(woff, (byte) (value >> 16)); wmem.put(woff + 1, (byte) (value >> 8 & 0xFF)); wmem.put(woff + 2, (byte) (value & 0xFF)); } else { wmem.put(woff, (byte) (value & 0xFF)); wmem.put(woff + 1, (byte) (value >> 8 & 0xFF)); wmem.put(woff + 2, (byte) (value >> 16 & 0xFF)); } return this; } @Override public Buffer writeUnsignedMedium(int value) { checkWrite(woff, 3); if (order() == ByteOrder.BIG_ENDIAN) { wmem.put(woff, (byte) (value >> 16)); wmem.put(woff + 1, (byte) (value >> 8 & 0xFF)); wmem.put(woff + 2, (byte) (value & 0xFF)); } else { wmem.put(woff, (byte) (value & 0xFF)); wmem.put(woff + 1, (byte) (value >> 8 & 0xFF)); wmem.put(woff + 2, (byte) (value >> 16 & 0xFF)); } woff += 3; return this; } @Override public Buffer setUnsignedMedium(int woff, int value) { checkSet(woff, 3); if (order() == ByteOrder.BIG_ENDIAN) { wmem.put(woff, (byte) (value >> 16)); wmem.put(woff + 1, (byte) (value >> 8 & 0xFF)); wmem.put(woff + 2, (byte) (value & 0xFF)); } else { wmem.put(woff, (byte) (value & 0xFF)); wmem.put(woff + 1, (byte) (value >> 8 & 0xFF)); wmem.put(woff + 2, (byte) (value >> 16 & 0xFF)); } return this; } @Override public int readInt() { checkRead(roff, Integer.BYTES); var value = rmem.getInt(roff); roff += Integer.BYTES; return value; } @Override public int getInt(int roff) { checkGet(roff, Integer.BYTES); return rmem.getInt(roff); } @Override public long readUnsignedInt() { checkRead(roff, Integer.BYTES); var value = rmem.getInt(roff) & 0xFFFFFFFFL; roff += Integer.BYTES; return value; } @Override public long getUnsignedInt(int roff) { checkGet(roff, Integer.BYTES); return rmem.getInt(roff) & 0xFFFFFFFFL; } @Override public Buffer writeInt(int value) { try { wmem.putInt(woff, value); woff += Integer.BYTES; return this; } catch (IndexOutOfBoundsException e) { throw checkWriteState(e, woff); } catch (ReadOnlyBufferException e) { throw bufferIsReadOnly(this); } } @Override public Buffer setInt(int woff, int value) { try { wmem.putInt(woff, value); return this; } catch (IndexOutOfBoundsException e) { throw checkWriteState(e, this.woff); } catch (ReadOnlyBufferException e) { throw bufferIsReadOnly(this); } } @Override public Buffer writeUnsignedInt(long value) { try { wmem.putInt(woff, (int) (value & 0xFFFFFFFFL)); woff += Integer.BYTES; return this; } catch (IndexOutOfBoundsException e) { throw checkWriteState(e, woff); } catch (ReadOnlyBufferException e) { throw bufferIsReadOnly(this); } } @Override public Buffer setUnsignedInt(int woff, long value) { try { wmem.putInt(woff, (int) (value & 0xFFFFFFFFL)); return this; } catch (IndexOutOfBoundsException e) { throw checkWriteState(e, this.woff); } catch (ReadOnlyBufferException e) { throw bufferIsReadOnly(this); } } @Override public float readFloat() { checkRead(roff, Float.BYTES); var value = rmem.getFloat(roff); roff += Float.BYTES; return value; } @Override public float getFloat(int roff) { checkGet(roff, Float.BYTES); return rmem.getFloat(roff); } @Override public Buffer writeFloat(float value) { try { wmem.putFloat(woff, value); woff += Float.BYTES; return this; } catch (IndexOutOfBoundsException e) { throw checkWriteState(e, woff); } catch (ReadOnlyBufferException e) { throw bufferIsReadOnly(this); } } @Override public Buffer setFloat(int woff, float value) { try { wmem.putFloat(woff, value); return this; } catch (IndexOutOfBoundsException e) { throw checkWriteState(e, woff); } catch (ReadOnlyBufferException e) { throw bufferIsReadOnly(this); } } @Override public long readLong() { checkRead(roff, Long.BYTES); var value = rmem.getLong(roff); roff += Long.BYTES; return value; } @Override public long getLong(int roff) { checkGet(roff, Long.BYTES); return rmem.getLong(roff); } @Override public Buffer writeLong(long value) { try { wmem.putLong(woff, value); woff += Long.BYTES; return this; } catch (IndexOutOfBoundsException e) { throw checkWriteState(e, woff); } catch (ReadOnlyBufferException e) { throw bufferIsReadOnly(this); } } @Override public Buffer setLong(int woff, long value) { try { wmem.putLong(woff, value); return this; } catch (IndexOutOfBoundsException e) { throw checkWriteState(e, woff); } catch (ReadOnlyBufferException e) { throw bufferIsReadOnly(this); } } @Override public double readDouble() { checkRead(roff, Double.BYTES); var value = rmem.getDouble(roff); roff += Double.BYTES; return value; } @Override public double getDouble(int roff) { checkGet(roff, Double.BYTES); return rmem.getDouble(roff); } @Override public Buffer writeDouble(double value) { try { wmem.putDouble(woff, value); woff += Double.BYTES; return this; } catch (IndexOutOfBoundsException e) { throw checkWriteState(e, woff); } catch (ReadOnlyBufferException e) { throw bufferIsReadOnly(this); } } @Override public Buffer setDouble(int woff, double value) { try { wmem.putDouble(woff, value); return this; } catch (IndexOutOfBoundsException e) { throw checkWriteState(e, woff); } catch (ReadOnlyBufferException e) { throw bufferIsReadOnly(this); } } // @Override protected Owned prepareSend() { var order = order(); var roff = this.roff; var woff = this.woff; var readOnly = readOnly(); var isConst = constBuffer; ByteBuffer base = this.base; ByteBuffer rmem = this.rmem; makeInaccessible(); return new Owned() { @Override public NioBuffer transferOwnership(Drop drop) { NioBuffer copy = new NioBuffer(base, rmem, control, drop); copy.order(order); copy.roff = roff; copy.woff = woff; if (readOnly) { copy.makeReadOnly(); } copy.constBuffer = isConst; return copy; } }; } void makeInaccessible() { base = CLOSED_BUFFER; rmem = CLOSED_BUFFER; wmem = CLOSED_BUFFER; roff = 0; woff = 0; } @Override public boolean isOwned() { return super.isOwned() && ((ArcDrop) unsafeGetDrop()).isOwned(); } @Override public int countBorrows() { return super.countBorrows() + ((ArcDrop) unsafeGetDrop()).countBorrows(); } private void checkRead(int index, int size) { if (index < 0 || woff < index + size) { throw readAccessCheckException(index); } } private void checkGet(int index, int size) { if (index < 0 || capacity() < index + size) { throw readAccessCheckException(index); } } private void checkWrite(int index, int size) { if (index < roff || wmem.capacity() < index + size) { throw writeAccessCheckException(index); } } private void checkSet(int index, int size) { if (index < 0 || wmem.capacity() < index + size) { throw writeAccessCheckException(index); } } private RuntimeException checkWriteState(IndexOutOfBoundsException ioobe, int offset) { if (rmem == CLOSED_BUFFER) { return bufferIsClosed(this); } if (wmem != rmem) { return bufferIsReadOnly(this); } IndexOutOfBoundsException exception = outOfBounds(offset); exception.addSuppressed(ioobe); return exception; } private RuntimeException readAccessCheckException(int index) { if (rmem == CLOSED_BUFFER) { throw bufferIsClosed(this); } return outOfBounds(index); } private RuntimeException writeAccessCheckException(int index) { if (rmem == CLOSED_BUFFER) { throw bufferIsClosed(this); } if (wmem != rmem) { return bufferIsReadOnly(this); } return outOfBounds(index); } private IndexOutOfBoundsException outOfBounds(int index) { return new IndexOutOfBoundsException( "Index " + index + " is out of bounds: [read 0 to " + woff + ", write 0 to " + rmem.capacity() + "]."); } ByteBuffer recoverable() { return base; } // private ByteBufAdaptor adaptor; @Override public ByteBuf asByteBuf() { ByteBufAdaptor bba = adaptor; if (bba == null) { ByteBufAllocatorAdaptor alloc = new ByteBufAllocatorAdaptor( BufferAllocator.heap(), BufferAllocator.direct()); return adaptor = new ByteBufAdaptor(alloc, this); } return bba; } @Override public int refCnt() { return isAccessible()? 1 + countBorrows() : 0; } @Override public ReferenceCounted retain() { return retain(1); } @Override public ReferenceCounted retain(int increment) { for (int i = 0; i < increment; i++) { acquire(); } return this; } @Override public ReferenceCounted touch() { return this; } @Override public ReferenceCounted touch(Object hint) { return this; } @Override public boolean release() { return release(1); } @Override public boolean release(int decrement) { int refCount = 1 + countBorrows(); if (!isAccessible() || decrement > refCount) { throw new IllegalReferenceCountException(refCount, -decrement); } for (int i = 0; i < decrement; i++) { try { close(); } catch (RuntimeException e) { throw new IllegalReferenceCountException(e); } } return !isAccessible(); } // }