/* * 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.unsafe; import io.netty.buffer.api.AllocatorControl; import io.netty.buffer.api.Buffer; import io.netty.buffer.api.BufferAllocator; import io.netty.buffer.api.ByteCursor; import io.netty.buffer.api.Drop; import io.netty.buffer.api.Owned; import io.netty.buffer.api.RcSupport; 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.internal.PlatformDependent; import java.lang.ref.Reference; import java.nio.ByteBuffer; import java.nio.ByteOrder; import static io.netty.buffer.api.internal.Statics.bufferIsClosed; import static io.netty.buffer.api.internal.Statics.bufferIsReadOnly; import static io.netty.util.internal.PlatformDependent.BIG_ENDIAN_NATIVE_ORDER; class UnsafeBuffer extends RcSupport implements Buffer, ReadableComponent, WritableComponent { private static final int CLOSED_SIZE = -1; private static final boolean ACCESS_UNALIGNED = PlatformDependent.isUnaligned(); private UnsafeMemory memory; // The memory liveness; monitored by Cleaner. private Object base; // On-heap address reference object, or null for off-heap. private long baseOffset; // Offset of this buffer into the memory. private long address; // Resolved address (baseOffset + memory.address). private int rsize; private int wsize; private final AllocatorControl control; private ByteOrder order; private boolean flipBytes; private boolean readOnly; private int roff; private int woff; private boolean constBuffer; UnsafeBuffer(UnsafeMemory memory, long offset, int size, AllocatorControl allocatorControl, Drop drop) { super(new MakeInaccisbleOnDrop(ArcDrop.wrap(drop))); this.memory = memory; base = memory.base; baseOffset = offset; address = memory.address + offset; rsize = size; wsize = size; control = allocatorControl; order = ByteOrder.nativeOrder(); } UnsafeBuffer(UnsafeBuffer parent) { super(new MakeInaccisbleOnDrop(new ArcDrop<>(ArcDrop.acquire(parent.unsafeGetDrop())))); control = parent.control; memory = parent.memory; base = parent.base; baseOffset = parent.baseOffset; address = parent.address; rsize = parent.rsize; wsize = parent.wsize; order = parent.order; flipBytes = parent.flipBytes; readOnly = parent.readOnly; roff = parent.roff; woff = parent.woff; constBuffer = true; } @Override public String toString() { return "Buffer[roff:" + roff + ", woff:" + woff + ", cap:" + rsize + ']'; } @Override public Buffer order(ByteOrder order) { this.order = order; flipBytes = order != ByteOrder.nativeOrder(); return this; } @Override public ByteOrder order() { return order; } @Override public int capacity() { return Math.max(0, rsize); // Use Math.max to make capacity of closed buffer equal to zero. } @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) { checkSet(0, capacity()); if (rsize == CLOSED_SIZE) { throw bufferIsClosed(); } try { PlatformDependent.setMemory(base, address, rsize, value); } finally { Reference.reachabilityFence(memory); } return this; } @Override public long nativeAddress() { return base == null? address : 0; } @Override public Buffer makeReadOnly() { readOnly = true; wsize = CLOSED_SIZE; return this; } @Override public boolean readOnly() { return readOnly; } @Override public Buffer slice(int offset, int length) { if (length < 0) { throw new IllegalArgumentException("Length cannot be negative: " + length + '.'); } checkGet(offset, length); ArcDrop drop = (ArcDrop) unsafeGetDrop(); drop.increment(); Buffer sliceBuffer = new UnsafeBuffer(memory, baseOffset + offset, length, control, drop) .writerOffset(length) .order(order); if (readOnly) { sliceBuffer = sliceBuffer.makeReadOnly(); } return sliceBuffer; } @Override public void copyInto(int srcPos, byte[] dest, int destPos, int length) { checkCopyIntoArgs(srcPos, length, destPos, dest.length); copyIntoArray(srcPos, dest, destPos, length); } private void copyIntoArray(int srcPos, byte[] dest, int destPos, int length) { long destOffset = PlatformDependent.byteArrayBaseOffset(); try { PlatformDependent.copyMemory(base, address + srcPos, dest, destOffset + destPos, length); } finally { Reference.reachabilityFence(memory); Reference.reachabilityFence(dest); } } @Override public void copyInto(int srcPos, ByteBuffer dest, int destPos, int length) { checkCopyIntoArgs(srcPos, length, destPos, dest.capacity()); if (dest.hasArray()) { copyIntoArray(srcPos, dest.array(), dest.arrayOffset() + destPos, length); } else { assert dest.isDirect(); long destAddr = PlatformDependent.directBufferAddress(dest); try { PlatformDependent.copyMemory(base, address + srcPos, null, destAddr + destPos, length); } finally { Reference.reachabilityFence(memory); Reference.reachabilityFence(dest); } } } private void checkCopyIntoArgs(int srcPos, int length, int destPos, int destLength) { if (rsize == CLOSED_SIZE) { throw bufferIsClosed(); } if (srcPos < 0) { throw new IllegalArgumentException("The srcPos cannot be negative: " + srcPos + '.'); } if (length < 0) { throw new IllegalArgumentException("The length cannot be negative: " + length + '.'); } if (rsize < srcPos + length) { throw new IllegalArgumentException("The srcPos + length is beyond the end of the buffer: " + "srcPos = " + srcPos + ", length = " + length + '.'); } if (destPos < 0) { throw new IllegalArgumentException("The destPos cannot be negative: " + destPos + '.'); } if (destLength < destPos + length) { throw new IllegalArgumentException("The destPos + length is beyond the end of the destination: " + "destPos = " + destPos + ", length = " + length + '.'); } } @Override public void copyInto(int srcPos, Buffer dest, int destPos, int length) { checkCopyIntoArgs(srcPos, length, destPos, dest.capacity()); if (dest.readOnly()) { throw bufferIsReadOnly(); } long nativeAddress = dest.nativeAddress(); try { if (nativeAddress != 0) { PlatformDependent.copyMemory(base, address + srcPos, null, nativeAddress + destPos, length); } else if (dest instanceof UnsafeBuffer) { UnsafeBuffer destUnsafe = (UnsafeBuffer) dest; PlatformDependent.copyMemory( base, address + srcPos, destUnsafe.base, destUnsafe.address + destPos, length); } else { Statics.copyToViaReverseCursor(this, srcPos, dest, destPos, length); } } finally { Reference.reachabilityFence(memory); Reference.reachabilityFence(dest); } } @Override public ByteCursor openCursor() { return openCursor(readerOffset(), readableBytes()); } @Override public ByteCursor openCursor(int fromOffset, int length) { if (rsize == CLOSED_SIZE) { throw bufferIsClosed(); } 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() { final UnsafeMemory memory = UnsafeBuffer.this.memory; // Keep memory alive. final Object baseObj = base; final long baseAddress = address; int index = fromOffset; final int end = index + length; long longValue = -1; byte byteValue = -1; @Override public boolean readLong() { if (index + Long.BYTES <= end) { try { long value = PlatformDependent.getLong(baseObj, baseAddress + index); longValue = BIG_ENDIAN_NATIVE_ORDER? value : Long.reverseBytes(value); } finally { Reference.reachabilityFence(memory); } index += Long.BYTES; return true; } return false; } @Override public long getLong() { return longValue; } @Override public boolean readByte() { if (index < end) { try { byteValue = PlatformDependent.getByte(baseObj, baseAddress + index); } finally { Reference.reachabilityFence(memory); } 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 (rsize == CLOSED_SIZE) { throw bufferIsClosed(); } 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 UnsafeMemory memory = UnsafeBuffer.this.memory; // Keep memory alive. final Object baseObj = base; final long baseAddress = address; 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; try { long value = PlatformDependent.getLong(baseObj, baseAddress + index); longValue = BIG_ENDIAN_NATIVE_ORDER? Long.reverseBytes(value) : value; } finally { Reference.reachabilityFence(memory); } index--; return true; } return false; } @Override public long getLong() { return longValue; } @Override public boolean readByte() { if (index > end) { try { byteValue = PlatformDependent.getByte(baseObj, baseAddress + index); } finally { Reference.reachabilityFence(memory); } 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 (!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 (rsize != wsize) { throw bufferIsReadOnly(); } 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); UnsafeMemory memory = (UnsafeMemory) control.allocateUntethered(this, (int) newSize); // Copy contents. try { PlatformDependent.copyMemory(base, address, memory.base, memory.address, rsize); } finally { Reference.reachabilityFence(this.memory); Reference.reachabilityFence(memory); } // Release the old memory, and install the new memory: Drop drop = disconnectDrop(); attachNewMemory(memory, drop); } private Drop disconnectDrop() { var drop = (Drop) unsafeGetDrop(); int roff = this.roff; int woff = this.woff; drop.drop(this); drop = ArcDrop.unwrapAllArcs(drop); unsafeSetDrop(new ArcDrop<>(drop)); this.roff = roff; this.woff = woff; return drop; } private void attachNewMemory(UnsafeMemory memory, Drop drop) { this.memory = memory; base = memory.base; baseOffset = 0; address = memory.address; rsize = memory.size; wsize = memory.size; 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 (!isOwned()) { throw attachTrace(new IllegalStateException("Cannot split a buffer that is not owned.")); } var drop = (ArcDrop) unsafeGetDrop(); unsafeSetDrop(new ArcDrop<>(drop)); // TODO maybe incrementing the existing ArcDrop is enough; maybe we don't need to wrap it in another ArcDrop. var splitBuffer = new UnsafeBuffer(memory, baseOffset, splitOffset, control, new ArcDrop<>(drop.increment())); splitBuffer.woff = Math.min(woff, splitOffset); splitBuffer.roff = Math.min(roff, splitOffset); splitBuffer.order(order()); boolean readOnly = readOnly(); if (readOnly) { splitBuffer.makeReadOnly(); } // Note that split, unlike slice, does not deconstify, because data changes in either buffer are not visible // in the other. The split buffers can later deconstify independently if needed. splitBuffer.constBuffer = constBuffer; rsize -= splitOffset; baseOffset += splitOffset; address += splitOffset; if (!readOnly) { wsize = rsize; } woff = Math.max(woff, splitOffset) - splitOffset; roff = Math.max(roff, splitOffset) - splitOffset; return splitBuffer; } @Override public void compact() { if (!isOwned()) { throw attachTrace(new IllegalStateException("Buffer must be owned in order to compact.")); } if (readOnly()) { throw new IllegalStateException("Buffer must be writable in order to compact, but was read-only."); } if (roff == 0) { return; } try { PlatformDependent.copyMemory(base, address + roff, base, address, woff - roff); } finally { Reference.reachabilityFence(memory); } 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 base instanceof byte[]; } @Override public byte[] readableArray() { checkHasReadableArray(); return (byte[]) base; } @Override public int readableArrayOffset() { checkHasReadableArray(); return Math.toIntExact(address + roff - PlatformDependent.byteArrayBaseOffset()); } private void checkHasReadableArray() { if (!hasReadableArray()) { throw new UnsupportedOperationException("No readable array available."); } } @Override public int readableArrayLength() { return woff - roff; } @Override public long readableNativeAddress() { return nativeAddress(); } @Override public ByteBuffer readableBuffer() { final ByteBuffer buf; if (hasReadableArray()) { buf = ByteBuffer.wrap(readableArray()).slice(readableArrayOffset(), readableArrayLength()); } else { buf = PlatformDependent.directBuffer(address + roff, readableBytes()); } return buf.asReadOnlyBuffer().order(order()); } @Override public boolean hasWritableArray() { return hasReadableArray(); } @Override public byte[] writableArray() { checkHasWritableArray(); return (byte[]) base; } @Override public int writableArrayOffset() { checkHasWritableArray(); return Math.toIntExact(address + woff - PlatformDependent.byteArrayBaseOffset()); } private void checkHasWritableArray() { if (!hasReadableArray()) { throw new UnsupportedOperationException("No writable array available."); } } @Override public int writableArrayLength() { return capacity() - woff; } @Override public long writableNativeAddress() { return nativeAddress(); } @Override public ByteBuffer writableBuffer() { final ByteBuffer buf; if (hasWritableArray()) { buf = ByteBuffer.wrap(writableArray()).slice(writableArrayOffset(), writableArrayLength()); } else { buf = PlatformDependent.directBuffer(address + woff, writableBytes()); } return buf.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); try { var value = loadByte(address + roff); roff += Byte.BYTES; return value; } finally { Reference.reachabilityFence(memory); } } @Override public byte getByte(int roff) { checkGet(roff, Byte.BYTES); try { return loadByte(address + roff); } finally { Reference.reachabilityFence(memory); } } @Override public int readUnsignedByte() { return readByte() & 0xFF; } @Override public int getUnsignedByte(int roff) { return getByte(roff) & 0xFF; } @Override public Buffer writeByte(byte value) { checkWrite(woff, Byte.BYTES); long offset = address + woff; woff += Byte.BYTES; try { storeByte(offset, value); } finally { Reference.reachabilityFence(memory); } return this; } @Override public Buffer setByte(int woff, byte value) { checkSet(woff, Byte.BYTES); long offset = address + woff; try { storeByte(offset, value); } finally { Reference.reachabilityFence(memory); } return this; } @Override public Buffer writeUnsignedByte(int value) { checkWrite(woff, Byte.BYTES); long offset = address + woff; woff += Byte.BYTES; try { storeByte(offset, (byte) (value & 0xFF)); } finally { Reference.reachabilityFence(memory); } return this; } @Override public Buffer setUnsignedByte(int woff, int value) { checkSet(woff, Byte.BYTES); long offset = address + woff; try { storeByte(offset, (byte) (value & 0xFF)); } finally { Reference.reachabilityFence(memory); } return this; } @Override public char readChar() { checkRead(roff, Character.BYTES); try { long offset = address + roff; roff += Character.BYTES; return loadChar(offset); } finally { Reference.reachabilityFence(memory); } } @Override public char getChar(int roff) { checkGet(roff, Character.BYTES); try { long offset = address + roff; return loadChar(offset); } finally { Reference.reachabilityFence(memory); } } @Override public Buffer writeChar(char value) { checkWrite(woff, Character.BYTES); long offset = address + woff; woff += Character.BYTES; try { storeChar(offset, value); } finally { Reference.reachabilityFence(memory); } return this; } @Override public Buffer setChar(int woff, char value) { checkSet(woff, Character.BYTES); long offset = address + woff; try { storeChar(offset, value); } finally { Reference.reachabilityFence(memory); } return this; } @Override public short readShort() { checkRead(roff, Short.BYTES); try { long offset = address + roff; roff += Short.BYTES; return loadShort(offset); } finally { Reference.reachabilityFence(memory); } } @Override public short getShort(int roff) { checkGet(roff, Short.BYTES); try { long offset = address + roff; return loadShort(offset); } finally { Reference.reachabilityFence(memory); } } @Override public int readUnsignedShort() { return readShort() & 0xFFFF; } @Override public int getUnsignedShort(int roff) { return getShort(roff) & 0xFFFF; } @Override public Buffer writeShort(short value) { checkWrite(woff, Short.BYTES); long offset = address + woff; woff += Short.BYTES; try { storeShort(offset, value); } finally { Reference.reachabilityFence(memory); } return this; } @Override public Buffer setShort(int woff, short value) { checkSet(woff, Short.BYTES); long offset = address + woff; try { storeShort(offset, value); } finally { Reference.reachabilityFence(memory); } return this; } @Override public Buffer writeUnsignedShort(int value) { checkWrite(woff, Short.BYTES); long offset = address + woff; woff += Short.BYTES; try { storeShort(offset, (short) (value & 0xFFFF)); } finally { Reference.reachabilityFence(memory); } return this; } @Override public Buffer setUnsignedShort(int woff, int value) { checkSet(woff, Short.BYTES); long offset = address + woff; try { storeShort(offset, (short) (value & 0xFFFF)); } finally { Reference.reachabilityFence(memory); } return this; } @Override public int readMedium() { checkRead(roff, 3); long offset = address + roff; int value = order() == ByteOrder.BIG_ENDIAN ? loadByte(offset) << 16 | (loadByte(offset + 1) & 0xFF) << 8 | loadByte(offset + 2) & 0xFF : loadByte(offset) & 0xFF | (loadByte(offset + 1) & 0xFF) << 8 | loadByte(offset + 2) << 16; roff += 3; return value; } @Override public int getMedium(int roff) { checkGet(roff, 3); long offset = address + roff; return order() == ByteOrder.BIG_ENDIAN? loadByte(offset) << 16 | (loadByte(offset + 1) & 0xFF) << 8 | loadByte(offset + 2) & 0xFF : loadByte(offset) & 0xFF | (loadByte(offset + 1) & 0xFF) << 8 | loadByte(offset + 2) << 16; } @Override public int readUnsignedMedium() { checkRead(roff, 3); long offset = address + roff; int value = order() == ByteOrder.BIG_ENDIAN? (loadByte(offset) << 16 | (loadByte(offset + 1) & 0xFF) << 8 | loadByte(offset + 2) & 0xFF) & 0xFFFFFF : (loadByte(offset) & 0xFF | (loadByte(offset + 1) & 0xFF) << 8 | loadByte(offset + 2) << 16) & 0xFFFFFF; roff += 3; return value; } @Override public int getUnsignedMedium(int roff) { checkGet(roff, 3); long offset = address + roff; return order() == ByteOrder.BIG_ENDIAN? (loadByte(offset) << 16 | (loadByte(offset + 1) & 0xFF) << 8 | loadByte(offset + 2) & 0xFF) & 0xFFFFFF : (loadByte(offset) & 0xFF | (loadByte(offset + 1) & 0xFF) << 8 | loadByte(offset + 2) << 16) & 0xFFFFFF; } @Override public Buffer writeMedium(int value) { checkWrite(woff, 3); long offset = address + woff; if (order() == ByteOrder.BIG_ENDIAN) { storeByte(offset, (byte) (value >> 16)); storeByte(offset + 1, (byte) (value >> 8 & 0xFF)); storeByte(offset + 2, (byte) (value & 0xFF)); } else { storeByte(offset, (byte) (value & 0xFF)); storeByte(offset + 1, (byte) (value >> 8 & 0xFF)); storeByte(offset + 2, (byte) (value >> 16 & 0xFF)); } woff += 3; return this; } @Override public Buffer setMedium(int woff, int value) { checkSet(woff, 3); long offset = address + woff; if (order() == ByteOrder.BIG_ENDIAN) { storeByte(offset, (byte) (value >> 16)); storeByte(offset + 1, (byte) (value >> 8 & 0xFF)); storeByte(offset + 2, (byte) (value & 0xFF)); } else { storeByte(offset, (byte) (value & 0xFF)); storeByte(offset + 1, (byte) (value >> 8 & 0xFF)); storeByte(offset + 2, (byte) (value >> 16 & 0xFF)); } return this; } @Override public Buffer writeUnsignedMedium(int value) { checkWrite(woff, 3); long offset = address + woff; if (order() == ByteOrder.BIG_ENDIAN) { storeByte(offset, (byte) (value >> 16)); storeByte(offset + 1, (byte) (value >> 8 & 0xFF)); storeByte(offset + 2, (byte) (value & 0xFF)); } else { storeByte(offset, (byte) (value & 0xFF)); storeByte(offset + 1, (byte) (value >> 8 & 0xFF)); storeByte(offset + 2, (byte) (value >> 16 & 0xFF)); } woff += 3; return this; } @Override public Buffer setUnsignedMedium(int woff, int value) { checkSet(woff, 3); long offset = address + woff; if (order() == ByteOrder.BIG_ENDIAN) { storeByte(offset, (byte) (value >> 16)); storeByte(offset + 1, (byte) (value >> 8 & 0xFF)); storeByte(offset + 2, (byte) (value & 0xFF)); } else { storeByte(offset, (byte) (value & 0xFF)); storeByte(offset + 1, (byte) (value >> 8 & 0xFF)); storeByte(offset + 2, (byte) (value >> 16 & 0xFF)); } return this; } @Override public int readInt() { checkRead(roff, Integer.BYTES); try { long offset = address + roff; roff += Integer.BYTES; return loadInt(offset); } finally { Reference.reachabilityFence(memory); } } @Override public int getInt(int roff) { checkGet(roff, Integer.BYTES); try { long offset = address + roff; return loadInt(offset); } finally { Reference.reachabilityFence(memory); } } @Override public long readUnsignedInt() { return readInt() & 0x0000_0000_FFFF_FFFFL; } @Override public long getUnsignedInt(int roff) { return getInt(roff) & 0x0000_0000_FFFF_FFFFL; } @Override public Buffer writeInt(int value) { checkWrite(woff, Integer.BYTES); long offset = address + woff; woff += Integer.BYTES; try { storeInt(offset, value); } finally { Reference.reachabilityFence(memory); } return this; } @Override public Buffer setInt(int woff, int value) { checkSet(woff, Integer.BYTES); long offset = address + woff; try { storeInt(offset, value); } finally { Reference.reachabilityFence(memory); } return this; } @Override public Buffer writeUnsignedInt(long value) { checkWrite(woff, Integer.BYTES); long offset = address + woff; woff += Integer.BYTES; try { storeInt(offset, (int) (value & 0xFFFF_FFFFL)); } finally { Reference.reachabilityFence(memory); } return this; } @Override public Buffer setUnsignedInt(int woff, long value) { checkSet(woff, Integer.BYTES); long offset = address + woff; try { storeInt(offset, (int) (value & 0xFFFF_FFFFL)); } finally { Reference.reachabilityFence(memory); } return this; } @Override public float readFloat() { checkRead(roff, Float.BYTES); try { long offset = address + roff; roff += Float.BYTES; return loadFloat(offset); } finally { Reference.reachabilityFence(memory); } } @Override public float getFloat(int roff) { checkGet(roff, Float.BYTES); try { long offset = address + roff; return loadFloat(offset); } finally { Reference.reachabilityFence(memory); } } @Override public Buffer writeFloat(float value) { checkWrite(woff, Float.BYTES); long offset = address + woff; woff += Float.BYTES; try { storeFloat(offset, value); } finally { Reference.reachabilityFence(memory); } return this; } @Override public Buffer setFloat(int woff, float value) { checkSet(woff, Float.BYTES); long offset = address + woff; try { storeFloat(offset, value); } finally { Reference.reachabilityFence(memory); } return this; } @Override public long readLong() { checkRead(roff, Long.BYTES); try { long offset = address + roff; roff += Long.BYTES; return loadLong(offset); } finally { Reference.reachabilityFence(memory); } } @Override public long getLong(int roff) { checkGet(roff, Long.BYTES); try { long offset = address + roff; return loadLong(offset); } finally { Reference.reachabilityFence(memory); } } @Override public Buffer writeLong(long value) { checkWrite(woff, Long.BYTES); long offset = address + woff; woff += Long.BYTES; try { storeLong(offset, value); } finally { Reference.reachabilityFence(memory); } return this; } @Override public Buffer setLong(int woff, long value) { checkSet(woff, Long.BYTES); long offset = address + woff; try { storeLong(offset, value); } finally { Reference.reachabilityFence(memory); } return this; } @Override public double readDouble() { checkRead(roff, Double.BYTES); try { long offset = address + roff; roff += Double.BYTES; return loadDouble(offset); } finally { Reference.reachabilityFence(memory); } } @Override public double getDouble(int roff) { checkGet(roff, Double.BYTES); try { long offset = address + roff; return loadDouble(offset); } finally { Reference.reachabilityFence(memory); } } @Override public Buffer writeDouble(double value) { checkWrite(woff, Double.BYTES); long offset = address + woff; woff += Double.BYTES; try { storeDouble(offset, value); } finally { Reference.reachabilityFence(memory); } return this; } @Override public Buffer setDouble(int woff, double value) { checkSet(woff, Double.BYTES); long offset = address + woff; try { storeDouble(offset, value); } finally { Reference.reachabilityFence(memory); } return this; } // @Override protected Owned prepareSend() { var order = order(); var roff = this.roff; var woff = this.woff; var readOnly = readOnly(); var isConst = constBuffer; UnsafeMemory memory = this.memory; AllocatorControl control = this.control; long baseOffset = this.baseOffset; int rsize = this.rsize; makeInaccessible(); return new Owned() { @Override public UnsafeBuffer transferOwnership(Drop drop) { UnsafeBuffer copy = new UnsafeBuffer(memory, baseOffset, rsize, control, drop); copy.order(order); copy.roff = roff; copy.woff = woff; if (readOnly) { copy.makeReadOnly(); } copy.constBuffer = isConst; return copy; } }; } @Override protected Drop unsafeGetDrop() { MakeInaccisbleOnDrop drop = (MakeInaccisbleOnDrop) super.unsafeGetDrop(); return drop.delegate; } @Override protected void unsafeSetDrop(Drop replacement) { super.unsafeSetDrop(new MakeInaccisbleOnDrop(replacement)); } private static final class MakeInaccisbleOnDrop implements Drop { final Drop delegate; private MakeInaccisbleOnDrop(Drop delegate) { this.delegate = delegate; } @Override public void drop(UnsafeBuffer buf) { try { delegate.drop(buf); } finally { buf.makeInaccessible(); } } @Override public void attach(UnsafeBuffer buf) { delegate.attach(buf); } @Override public String toString() { return "UnsafeDrop(" + delegate + ')'; } } void makeInaccessible() { roff = 0; woff = 0; rsize = CLOSED_SIZE; wsize = CLOSED_SIZE; readOnly = false; } @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 || rsize < index + size) { throw readAccessCheckException(index); } } private void checkWrite(int index, int size) { if (index < roff || wsize < index + size) { throw writeAccessCheckException(index); } } private void checkSet(int index, int size) { if (index < 0 || wsize < index + size) { throw writeAccessCheckException(index); } } private RuntimeException readAccessCheckException(int index) { if (rsize == CLOSED_SIZE) { throw bufferIsClosed(); } return outOfBounds(index); } private RuntimeException writeAccessCheckException(int index) { if (rsize == CLOSED_SIZE) { throw bufferIsClosed(); } if (wsize != rsize) { return bufferIsReadOnly(); } return outOfBounds(index); } private IndexOutOfBoundsException outOfBounds(int index) { return new IndexOutOfBoundsException( "Index " + index + " is out of bounds: [read 0 to " + woff + ", write 0 to " + rsize + "]."); } private byte loadByte(long off) { return PlatformDependent.getByte(base, off); } private char loadChar(long offset) { if (ACCESS_UNALIGNED) { var value = PlatformDependent.getChar(base, offset); return flipBytes? Character.reverseBytes(value) : value; } return loadCharUnaligned(offset); } private char loadCharUnaligned(long offset) { final char value; Object b = base; if ((offset & 1) == 0) { value = PlatformDependent.getChar(b, offset); } else { value = (char) (PlatformDependent.getByte(b, offset) << 8 | PlatformDependent.getByte(b, offset + 1)); } return flipBytes? Character.reverseBytes(value) : value; } private short loadShort(long offset) { if (ACCESS_UNALIGNED) { var value = PlatformDependent.getShort(base, offset); return flipBytes? Short.reverseBytes(value) : value; } return loadShortUnaligned(offset); } private short loadShortUnaligned(long offset) { final short value; Object b = base; if ((offset & 1) == 0) { value = PlatformDependent.getShort(b, offset); } else { value = (short) (PlatformDependent.getByte(b, offset) << 8 | PlatformDependent.getByte(b, offset + 1)); } return flipBytes? Short.reverseBytes(value) : value; } private int loadInt(long offset) { if (ACCESS_UNALIGNED) { var value = PlatformDependent.getInt(base, offset); return flipBytes? Integer.reverseBytes(value) : value; } return loadIntUnaligned(offset); } private int loadIntUnaligned(long offset) { final int value; Object b = base; if ((offset & 3) == 0) { value = PlatformDependent.getInt(b, offset); } else if ((offset & 1) == 0) { value = PlatformDependent.getShort(b, offset) << 16 | PlatformDependent.getShort(b, offset + 2); } else { value = PlatformDependent.getByte(b, offset) << 24 | PlatformDependent.getByte(b, offset + 1) << 16 | PlatformDependent.getByte(b, offset + 2) << 8 | PlatformDependent.getByte(b, offset + 3); } return flipBytes? Integer.reverseBytes(value) : value; } private float loadFloat(long offset) { if (ACCESS_UNALIGNED) { if (flipBytes) { var value = PlatformDependent.getInt(base, offset); return Float.intBitsToFloat(Integer.reverseBytes(value)); } return PlatformDependent.getFloat(base, offset); } return loadFloatUnaligned(offset); } private float loadFloatUnaligned(long offset) { return Float.intBitsToFloat(loadIntUnaligned(offset)); } private long loadLong(long offset) { if (ACCESS_UNALIGNED) { var value = PlatformDependent.getLong(base, offset); return flipBytes? Long.reverseBytes(value) : value; } return loadLongUnaligned(offset); } private long loadLongUnaligned(long offset) { final long value; Object b = base; if ((offset & 7) == 0) { value = PlatformDependent.getLong(b, offset); } else if ((offset & 3) == 0) { value = (long) PlatformDependent.getInt(b, offset) << 32 | PlatformDependent.getInt(b, offset + 4); } else if ((offset & 1) == 0) { value = (long) PlatformDependent.getShort(b, offset) << 48 | (long) PlatformDependent.getShort(b, offset + 2) << 32 | (long) PlatformDependent.getShort(b, offset + 4) << 16 | PlatformDependent.getShort(b, offset + 6); } else { value = (long) PlatformDependent.getByte(b, offset) << 54 | (long) PlatformDependent.getByte(b, offset + 1) << 48 | (long) PlatformDependent.getByte(b, offset + 2) << 40 | (long) PlatformDependent.getByte(b, offset + 3) << 32 | (long) PlatformDependent.getByte(b, offset + 4) << 24 | (long) PlatformDependent.getByte(b, offset + 5) << 16 | (long) PlatformDependent.getByte(b, offset + 6) << 8 | PlatformDependent.getByte(b, offset + 7); } return flipBytes? Long.reverseBytes(value) : value; } private double loadDouble(long offset) { if (ACCESS_UNALIGNED) { if (flipBytes) { var value = PlatformDependent.getLong(base, offset); return Double.longBitsToDouble(Long.reverseBytes(value)); } return PlatformDependent.getDouble(base, offset); } return loadDoubleUnaligned(offset); } private double loadDoubleUnaligned(long offset) { return Double.longBitsToDouble(loadLongUnaligned(offset)); } private void storeByte(long offset, byte value) { PlatformDependent.putByte(base, offset, value); } private void storeChar(long offset, char value) { if (flipBytes) { value = Character.reverseBytes(value); } if (ACCESS_UNALIGNED) { PlatformDependent.putChar(base, offset, value); } else { storeCharUnaligned(offset, value); } } private void storeCharUnaligned(long offset, char value) { Object b = base; if ((offset & 1) == 0) { PlatformDependent.putChar(b, offset, value); } else { PlatformDependent.putByte(b, offset, (byte) (value >> 8)); PlatformDependent.putByte(b, offset + 1, (byte) value); } } private void storeShort(long offset, short value) { if (flipBytes) { value = Short.reverseBytes(value); } if (ACCESS_UNALIGNED) { PlatformDependent.putShort(base, offset, value); } else { storeShortUnaligned(offset, value); } } private void storeShortUnaligned(long offset, short value) { Object b = base; if ((offset & 1) == 0) { PlatformDependent.putShort(b, offset, value); } else { PlatformDependent.putByte(b, offset, (byte) (value >> 8)); PlatformDependent.putByte(b, offset + 1, (byte) value); } } private void storeInt(long offset, int value) { if (flipBytes) { value = Integer.reverseBytes(value); } if (ACCESS_UNALIGNED) { PlatformDependent.putInt(base, offset, value); } else { storeIntUnaligned(offset, value); } } private void storeIntUnaligned(long offset, int value) { Object b = base; if ((offset & 3) == 0) { PlatformDependent.putInt(b, offset, value); } else if ((offset & 1) == 0) { PlatformDependent.putShort(b, offset, (short) (value >> 16)); PlatformDependent.putShort(b, offset + 2, (short) value); } else { PlatformDependent.putByte(b, offset, (byte) (value >> 24)); PlatformDependent.putByte(b, offset + 1, (byte) (value >> 16)); PlatformDependent.putByte(b, offset + 2, (byte) (value >> 8)); PlatformDependent.putByte(b, offset + 3, (byte) value); } } private void storeFloat(long offset, float value) { storeInt(offset, Float.floatToRawIntBits(value)); } private void storeLong(long offset, long value) { if (flipBytes) { value = Long.reverseBytes(value); } if (ACCESS_UNALIGNED) { PlatformDependent.putLong(base, offset, value); } else { storeLongUnaligned(offset, value); } } private void storeLongUnaligned(long offset, long value) { Object b = base; if ((offset & 7) == 0) { PlatformDependent.putLong(b, offset, value); } else if ((offset & 3) == 0) { PlatformDependent.putInt(b, offset, (int) (value >> 32)); PlatformDependent.putInt(b, offset + 4, (int) value); } else if ((offset & 1) == 0) { PlatformDependent.putShort(b, offset, (short) (value >> 48)); PlatformDependent.putShort(b, offset + 16, (short) (value >> 32)); PlatformDependent.putShort(b, offset + 32, (short) (value >> 16)); PlatformDependent.putShort(b, offset + 48, (short) value); } else { PlatformDependent.putByte(b, offset, (byte) (value >> 56)); PlatformDependent.putByte(b, offset + 1, (byte) (value >> 48)); PlatformDependent.putByte(b, offset + 2, (byte) (value >> 40)); PlatformDependent.putByte(b, offset + 3, (byte) (value >> 32)); PlatformDependent.putByte(b, offset + 4, (byte) (value >> 24)); PlatformDependent.putByte(b, offset + 5, (byte) (value >> 16)); PlatformDependent.putByte(b, offset + 6, (byte) (value >> 8)); PlatformDependent.putByte(b, offset + 7, (byte) value); } } private void storeDouble(long offset, double value) { storeLong(offset, Double.doubleToRawLongBits(value)); } Object recover() { return memory; } }