diff --git a/pom.xml b/pom.xml
index e5fb8c5..ae81aca 100644
--- a/pom.xml
+++ b/pom.xml
@@ -78,6 +78,8 @@
-server
-dsa -da -ea:io.netty...
-XX:+HeapDumpOnOutOfMemoryError
+ -Dio.netty.tryReflectionSetAccessible=true
+ --add-opens java.base/java.nio=io.netty.common
diff --git a/src/main/java/io/netty/buffer/api/MemoryManager.java b/src/main/java/io/netty/buffer/api/MemoryManager.java
index 2486d5b..ee0333e 100644
--- a/src/main/java/io/netty/buffer/api/MemoryManager.java
+++ b/src/main/java/io/netty/buffer/api/MemoryManager.java
@@ -26,5 +26,6 @@ public interface MemoryManager {
Drop drop();
Object unwrapRecoverableMemory(Buffer buf);
int capacityOfRecoverableMemory(Object memory);
+ // todo should recoverMemory re-attach a cleaner?
Buffer recoverMemory(AllocatorControl allocatorControl, Object recoverableMemory, Drop drop);
}
diff --git a/src/main/java/io/netty/buffer/api/bytebuffer/NioBuffer.java b/src/main/java/io/netty/buffer/api/bytebuffer/NioBuffer.java
index 84d6b61..7fbb7c6 100644
--- a/src/main/java/io/netty/buffer/api/bytebuffer/NioBuffer.java
+++ b/src/main/java/io/netty/buffer/api/bytebuffer/NioBuffer.java
@@ -34,6 +34,9 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ReadOnlyBufferException;
+import static io.netty.buffer.api.internal.Statics.bufferIsClosed;
+import static io.netty.buffer.api.internal.Statics.bufferIsReadOnly;
+
class NioBuffer extends RcSupport implements Buffer, ReadableComponent, WritableComponent {
private static final ByteBuffer CLOSED_BUFFER = ByteBuffer.allocate(0);
@@ -417,9 +420,9 @@ class NioBuffer extends RcSupport implements Buffer, Readable
}
var drop = (ArcDrop) unsafeGetDrop();
unsafeSetDrop(new ArcDrop<>(drop));
- var bifurcatedSeg = rmem.slice(0, woff);
+ var bifurcatedBuffer = rmem.slice(0, woff);
// TODO maybe incrementing the existing ArcDrop is enough; maybe we don't need to wrap it in another ArcDrop.
- var bifurcatedBuf = new NioBuffer(base, bifurcatedSeg, control, new ArcDrop<>(drop.increment()));
+ var bifurcatedBuf = new NioBuffer(base, bifurcatedBuffer, control, new ArcDrop<>(drop.increment()));
bifurcatedBuf.woff = woff;
bifurcatedBuf.roff = roff;
bifurcatedBuf.order(order());
@@ -893,7 +896,7 @@ class NioBuffer extends RcSupport implements Buffer, Readable
wmem.putInt(woff, value);
return this;
} catch (IndexOutOfBoundsException e) {
- throw checkWriteState(e, NioBuffer.this.woff);
+ throw checkWriteState(e, this.woff);
} catch (ReadOnlyBufferException e) {
throw bufferIsReadOnly();
}
@@ -918,7 +921,7 @@ class NioBuffer extends RcSupport implements Buffer, Readable
wmem.putInt(woff, (int) (value & 0xFFFFFFFFL));
return this;
} catch (IndexOutOfBoundsException e) {
- throw checkWriteState(e, NioBuffer.this.woff);
+ throw checkWriteState(e, this.woff);
} catch (ReadOnlyBufferException e) {
throw bufferIsReadOnly();
}
@@ -1135,14 +1138,6 @@ class NioBuffer extends RcSupport implements Buffer, Readable
return outOfBounds(index);
}
- private static IllegalStateException bufferIsClosed() {
- return new IllegalStateException("This buffer is closed.");
- }
-
- private static IllegalStateException bufferIsReadOnly() {
- return new IllegalStateException("This buffer is read-only.");
- }
-
private IndexOutOfBoundsException outOfBounds(int index) {
return new IndexOutOfBoundsException(
"Index " + index + " is out of bounds: [read 0 to " + woff + ", write 0 to " +
diff --git a/src/main/java/io/netty/buffer/api/internal/Statics.java b/src/main/java/io/netty/buffer/api/internal/Statics.java
index 61d9b16..c440a35 100644
--- a/src/main/java/io/netty/buffer/api/internal/Statics.java
+++ b/src/main/java/io/netty/buffer/api/internal/Statics.java
@@ -68,4 +68,12 @@ public interface Statics {
dest.order(prevOrder);
}
}
+
+ static IllegalStateException bufferIsClosed() {
+ return new IllegalStateException("This buffer is closed.");
+ }
+
+ static IllegalStateException bufferIsReadOnly() {
+ return new IllegalStateException("This buffer is read-only.");
+ }
}
diff --git a/src/main/java/io/netty/buffer/api/memseg/MemSegBuffer.java b/src/main/java/io/netty/buffer/api/memseg/MemSegBuffer.java
index 842e48a..75d2f99 100644
--- a/src/main/java/io/netty/buffer/api/memseg/MemSegBuffer.java
+++ b/src/main/java/io/netty/buffer/api/memseg/MemSegBuffer.java
@@ -38,6 +38,8 @@ import jdk.incubator.foreign.ResourceScope;
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 jdk.incubator.foreign.MemoryAccess.getByteAtOffset;
import static jdk.incubator.foreign.MemoryAccess.getCharAtOffset;
import static jdk.incubator.foreign.MemoryAccess.getDoubleAtOffset;
@@ -1168,14 +1170,6 @@ class MemSegBuffer extends RcSupport implements Buffer, Re
return outOfBounds(index);
}
- private static IllegalStateException bufferIsClosed() {
- return new IllegalStateException("This buffer is closed.");
- }
-
- private static IllegalStateException bufferIsReadOnly() {
- return new IllegalStateException("This buffer is read-only.");
- }
-
private IndexOutOfBoundsException outOfBounds(int index) {
return new IndexOutOfBoundsException(
"Index " + index + " is out of bounds: [read 0 to " + woff + ", write 0 to " +
diff --git a/src/main/java/io/netty/buffer/api/unsafe/CleanerDrop.java b/src/main/java/io/netty/buffer/api/unsafe/CleanerDrop.java
new file mode 100644
index 0000000..d252936
--- /dev/null
+++ b/src/main/java/io/netty/buffer/api/unsafe/CleanerDrop.java
@@ -0,0 +1,55 @@
+/*
+ * 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.Buffer;
+import io.netty.buffer.api.Drop;
+import io.netty.util.internal.PlatformDependent;
+
+import java.lang.ref.Cleaner;
+
+public class CleanerDrop implements Drop {
+ private final Drop drop;
+
+ public CleanerDrop(UnsafeMemory memory, Drop drop, Cleaner cleaner) {
+ this.drop = drop;
+ long address = memory.address;
+ cleaner.register(memory, new FreeAddress(address));
+ }
+
+ @Override
+ public void drop(Buffer obj) {
+ drop.drop(obj);
+ }
+
+ @Override
+ public void attach(Buffer obj) {
+ drop.attach(obj);
+ }
+
+ private static class FreeAddress implements Runnable {
+ private final long address;
+
+ FreeAddress(long address) {
+ this.address = address;
+ }
+
+ @Override
+ public void run() {
+ PlatformDependent.freeMemory(address);
+ }
+ }
+}
diff --git a/src/main/java/io/netty/buffer/api/unsafe/UnsafeBuffer.java b/src/main/java/io/netty/buffer/api/unsafe/UnsafeBuffer.java
new file mode 100644
index 0000000..1b5cf32
--- /dev/null
+++ b/src/main/java/io/netty/buffer/api/unsafe/UnsafeBuffer.java
@@ -0,0 +1,1557 @@
+/*
+ * 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;
+
+public 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;
+
+ public 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();
+ }
+
+ @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 rsize;
+ }
+
+ @Override
+ public int readerOffset() {
+ return roff;
+ }
+
+ @Override
+ public Buffer readerOffset(int offset) {
+ roff = offset;
+ return this;
+ }
+
+ @Override
+ public int writerOffset() {
+ return woff;
+ }
+
+ @Override
+ public Buffer writerOffset(int offset) {
+ woff = offset;
+ return this;
+ }
+
+ @Override
+ public Buffer fill(byte value) {
+ checkSet(0, capacity());
+ 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 readOnly(boolean readOnly) {
+ this.readOnly = readOnly;
+ wsize = readOnly? CLOSED_SIZE : rsize;
+ 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 + '.');
+ }
+ if (!isAccessible()) {
+ throw new IllegalStateException("This buffer is closed: " + this + '.');
+ }
+ ArcDrop drop = (ArcDrop) unsafeGetDrop();
+ drop.increment();
+ return new UnsafeBuffer(memory, baseOffset + offset, length, control, drop)
+ .writerOffset(length)
+ .order(order)
+ .readOnly(readOnly);
+ }
+
+ @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());
+ long nativeAddress = dest.nativeAddress();
+ try {
+ if (nativeAddress != 0) {
+ PlatformDependent.copyMemory(base, address + srcPos, null, nativeAddress, 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? value : Long.reverseBytes(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, 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 (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() + size - (long) writableBytes();
+ 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 old memory:
+ var drop = (Drop) unsafeGetDrop();
+ int roff = this.roff;
+ int woff = this.woff;
+ drop.drop(this);
+ while (drop instanceof ArcDrop) {
+ drop = ((ArcDrop) drop).unwrap();
+ }
+ unsafeSetDrop(new ArcDrop<>(drop));
+ this.roff = roff;
+ this.woff = woff;
+
+ this.memory = memory;
+ base = memory.base;
+ baseOffset = 0;
+ address = memory.address;
+ rsize = memory.size;
+ wsize = memory.size;
+ drop.attach(this);
+ }
+
+ @Override
+ public Buffer bifurcate() {
+ if (!isOwned()) {
+ throw attachTrace(new IllegalStateException("Cannot bifurcate 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 bifurcatedBuf = new UnsafeBuffer(memory, baseOffset, woff, control, new ArcDrop<>(drop.increment()));
+ bifurcatedBuf.woff = woff;
+ bifurcatedBuf.roff = roff;
+ bifurcatedBuf.order(order());
+ boolean readOnly = readOnly();
+ bifurcatedBuf.readOnly(readOnly);
+ rsize -= woff;
+ baseOffset += woff;
+ address += woff;
+ if (!readOnly) {
+ wsize = rsize;
+ }
+ woff = 0;
+ roff = 0;
+ return bifurcatedBuf;
+ }
+
+ @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);
+ int value = order() == ByteOrder.BIG_ENDIAN ?
+ loadByte(roff) << 16 |
+ (loadByte(roff + 1) & 0xFF) << 8 |
+ loadByte(roff + 2) & 0xFF :
+ loadByte(roff) & 0xFF |
+ (loadByte(roff + 1) & 0xFF) << 8 |
+ loadByte(roff + 2) << 16;
+ roff += 3;
+ return value;
+ }
+
+ @Override
+ public int getMedium(int roff) {
+ checkGet(roff, 3);
+ return order() == ByteOrder.BIG_ENDIAN?
+ loadByte(roff) << 16 |
+ (loadByte(roff + 1) & 0xFF) << 8 |
+ loadByte(roff + 2) & 0xFF :
+ loadByte(roff) & 0xFF |
+ (loadByte(roff + 1) & 0xFF) << 8 |
+ loadByte(roff + 2) << 16;
+ }
+
+ @Override
+ public int readUnsignedMedium() {
+ checkRead(roff, 3);
+ int value = order() == ByteOrder.BIG_ENDIAN?
+ (loadByte(roff) << 16 |
+ (loadByte(roff + 1) & 0xFF) << 8 |
+ loadByte(roff + 2) & 0xFF) & 0xFFFFFF :
+ (loadByte(roff) & 0xFF |
+ (loadByte(roff + 1) & 0xFF) << 8 |
+ loadByte(roff + 2) << 16) & 0xFFFFFF;
+ roff += 3;
+ return value;
+ }
+
+ @Override
+ public int getUnsignedMedium(int roff) {
+ checkGet(roff, 3);
+ return order() == ByteOrder.BIG_ENDIAN?
+ (loadByte(roff) << 16 |
+ (loadByte(roff + 1) & 0xFF) << 8 |
+ loadByte(roff + 2) & 0xFF) & 0xFFFFFF :
+ (loadByte(roff) & 0xFF |
+ (loadByte(roff + 1) & 0xFF) << 8 |
+ loadByte(roff + 2) << 16) & 0xFFFFFF;
+ }
+
+ @Override
+ public Buffer writeMedium(int value) {
+ checkWrite(woff, 3);
+ if (order() == ByteOrder.BIG_ENDIAN) {
+ storeByte(woff, (byte) (value >> 16));
+ storeByte(woff + 1, (byte) (value >> 8 & 0xFF));
+ storeByte(woff + 2, (byte) (value & 0xFF));
+ } else {
+ storeByte(woff, (byte) (value & 0xFF));
+ storeByte(woff + 1, (byte) (value >> 8 & 0xFF));
+ storeByte(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) {
+ storeByte(woff, (byte) (value >> 16));
+ storeByte(woff + 1, (byte) (value >> 8 & 0xFF));
+ storeByte(woff + 2, (byte) (value & 0xFF));
+ } else {
+ storeByte(woff, (byte) (value & 0xFF));
+ storeByte(woff + 1, (byte) (value >> 8 & 0xFF));
+ storeByte(woff + 2, (byte) (value >> 16 & 0xFF));
+ }
+ return this;
+ }
+
+ @Override
+ public Buffer writeUnsignedMedium(int value) {
+ checkWrite(woff, 3);
+ if (order() == ByteOrder.BIG_ENDIAN) {
+ storeByte(woff, (byte) (value >> 16));
+ storeByte(woff + 1, (byte) (value >> 8 & 0xFF));
+ storeByte(woff + 2, (byte) (value & 0xFF));
+ } else {
+ storeByte(woff, (byte) (value & 0xFF));
+ storeByte(woff + 1, (byte) (value >> 8 & 0xFF));
+ storeByte(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) {
+ storeByte(woff, (byte) (value >> 16));
+ storeByte(woff + 1, (byte) (value >> 8 & 0xFF));
+ storeByte(woff + 2, (byte) (value & 0xFF));
+ } else {
+ storeByte(woff, (byte) (value & 0xFF));
+ storeByte(woff + 1, (byte) (value >> 8 & 0xFF));
+ storeByte(woff + 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();
+ 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;
+ copy.readOnly(readOnly);
+ 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() {
+ rsize = CLOSED_SIZE;
+ wsize = CLOSED_SIZE;
+ }
+
+ @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 checkWriteState(IndexOutOfBoundsException ioobe, int offset) {
+ if (rsize == CLOSED_SIZE) {
+ return bufferIsClosed();
+ }
+ if (wsize != rsize) {
+ return bufferIsReadOnly();
+ }
+
+ IndexOutOfBoundsException exception = outOfBounds(offset);
+ exception.addSuppressed(ioobe);
+ return exception;
+ }
+
+ 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;
+ }
+}
diff --git a/src/main/java/io/netty/buffer/api/unsafe/UnsafeMemory.java b/src/main/java/io/netty/buffer/api/unsafe/UnsafeMemory.java
new file mode 100644
index 0000000..d4721d9
--- /dev/null
+++ b/src/main/java/io/netty/buffer/api/unsafe/UnsafeMemory.java
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+class UnsafeMemory {
+ final Object base;
+ final long address;
+ final int size;
+
+ UnsafeMemory(Object base, long address, int size) {
+ this.base = base;
+ this.address = address;
+ this.size = size;
+ }
+}
diff --git a/src/main/java/io/netty/buffer/api/unsafe/UnsafeMemoryManager.java b/src/main/java/io/netty/buffer/api/unsafe/UnsafeMemoryManager.java
new file mode 100644
index 0000000..62f594c
--- /dev/null
+++ b/src/main/java/io/netty/buffer/api/unsafe/UnsafeMemoryManager.java
@@ -0,0 +1,90 @@
+/*
+ * 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.Drop;
+import io.netty.buffer.api.MemoryManager;
+import io.netty.buffer.api.internal.Statics;
+import io.netty.util.internal.PlatformDependent;
+
+import java.lang.ref.Cleaner;
+
+import static io.netty.buffer.api.internal.Statics.convert;
+
+public class UnsafeMemoryManager implements MemoryManager {
+ private final boolean offheap;
+
+ public UnsafeMemoryManager(boolean offheap) {
+ this.offheap = offheap;
+ }
+
+ @Override
+ public boolean isNative() {
+ return offheap;
+ }
+
+ @Override
+ public Buffer allocateConfined(AllocatorControl allocatorControl, long size, Drop drop, Cleaner cleaner) {
+ return allocateShared(allocatorControl, size, drop, cleaner);
+ }
+
+ @Override
+ public Buffer allocateShared(AllocatorControl allocatorControl, long size, Drop drop, Cleaner cleaner) {
+ final Object base;
+ final long address;
+ final UnsafeMemory memory;
+ final int size32 = Math.toIntExact(size);
+ if (cleaner == null) {
+ cleaner = Statics.CLEANER;
+ }
+ if (offheap) {
+ base = null;
+ address = PlatformDependent.allocateMemory(size);
+ PlatformDependent.setMemory(address, size, (byte) 0);
+ memory = new UnsafeMemory(base, address, size32);
+ drop = new CleanerDrop(memory, drop, cleaner);
+ } else {
+ base = new byte[size32];
+ address = PlatformDependent.byteArrayBaseOffset();
+ memory = new UnsafeMemory(base, address, size32);
+ }
+ return new UnsafeBuffer(memory, 0, size32, allocatorControl, convert(drop));
+ }
+
+ @Override
+ public Drop drop() {
+ // We cannot reliably drop unsafe memory. We have to rely on the cleaner to do that.
+ return Statics.NO_OP_DROP;
+ }
+
+ @Override
+ public Object unwrapRecoverableMemory(Buffer buf) {
+ return ((UnsafeBuffer) buf).recover();
+ }
+
+ @Override
+ public int capacityOfRecoverableMemory(Object memory) {
+ return ((UnsafeMemory) memory).size;
+ }
+
+ @Override
+ public Buffer recoverMemory(AllocatorControl allocatorControl, Object recoverableMemory, Drop drop) {
+ UnsafeMemory memory = (UnsafeMemory) recoverableMemory;
+ return new UnsafeBuffer(memory, 0, memory.size, allocatorControl, convert(drop));
+ }
+}
diff --git a/src/main/java/io/netty/buffer/api/unsafe/UnsafeMemoryManagers.java b/src/main/java/io/netty/buffer/api/unsafe/UnsafeMemoryManagers.java
new file mode 100644
index 0000000..e1f0ed3
--- /dev/null
+++ b/src/main/java/io/netty/buffer/api/unsafe/UnsafeMemoryManagers.java
@@ -0,0 +1,46 @@
+/*
+ * 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.MemoryManager;
+import io.netty.buffer.api.MemoryManagers;
+import io.netty.util.internal.PlatformDependent;
+
+public class UnsafeMemoryManagers implements MemoryManagers {
+ public UnsafeMemoryManagers() {
+ if (!PlatformDependent.hasUnsafe()) {
+ throw new UnsupportedOperationException("Unsafe is not available.");
+ }
+ if (!PlatformDependent.hasDirectBufferNoCleanerConstructor()) {
+ throw new UnsupportedOperationException("DirectByteBuffer internal constructor is not available.");
+ }
+ }
+
+ @Override
+ public MemoryManager getHeapMemoryManager() {
+ return new UnsafeMemoryManager(false);
+ }
+
+ @Override
+ public MemoryManager getNativeMemoryManager() {
+ return new UnsafeMemoryManager(true);
+ }
+
+ @Override
+ public String toString() {
+ return "US";
+ }
+}
diff --git a/src/main/java/io/netty/buffer/api/unsafe/package-info.java b/src/main/java/io/netty/buffer/api/unsafe/package-info.java
new file mode 100644
index 0000000..3fd081a
--- /dev/null
+++ b/src/main/java/io/netty/buffer/api/unsafe/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * A {@link io.netty.buffer.api.Buffer} implementation that is based on {@code sun.misc.Unsafe}.
+ */
+package io.netty.buffer.api.unsafe;
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index 4063afe..1b4d5a4 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -32,5 +32,6 @@ module netty.incubator.buffer {
provides io.netty.buffer.api.MemoryManagers with
io.netty.buffer.api.memseg.SegmentMemoryManagers,
- io.netty.buffer.api.bytebuffer.ByteBufferMemoryManagers;
+ io.netty.buffer.api.bytebuffer.ByteBufferMemoryManagers,
+ io.netty.buffer.api.unsafe.UnsafeMemoryManagers;
}
\ No newline at end of file