package jdk.internal.foreign;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.Unsafe;
import java.nio.ByteBuffer;
import java.util.Objects;
import java.util.Random;
public final class MemorySegmentImpl implements MemorySegment, MemorySegmentProxy {
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
private static final int BYTE_ARR_BASE = UNSAFE.arrayBaseOffset(byte[].class);
final long length;
final int mask;
final long min;
final Object base;
final Thread owner;
final MemoryScope scope;
final static int READ_ONLY = 1;
final static long NONCE = new Random().nextLong();
public MemorySegmentImpl(long min, Object base, long length, int mask, Thread owner, MemoryScope scope) {
this.length = length;
this.mask = mask;
this.min = min;
this.base = base;
this.owner = owner;
this.scope = scope;
}
@Override
public final MemorySegmentImpl asSlice(long offset, long newSize) {
checkValidState();
checkBounds(offset, newSize);
return new MemorySegmentImpl(min + offset, base, newSize, mask, owner, scope);
}
@Override
public MemorySegment acquire() {
return new MemorySegmentImpl(min, base, length, mask, Thread.currentThread(), scope.acquire());
}
@Override
public final MemoryAddress baseAddress() {
return new MemoryAddressImpl(this, 0);
}
@Override
public final long byteSize() {
return length;
}
@Override
public final MemorySegmentImpl asReadOnly() {
checkValidState();
return new MemorySegmentImpl(min, base, length, mask | READ_ONLY, owner, scope);
}
@Override
public final boolean isAlive() {
return scope.isAliveThreadSafe();
}
@Override
public final boolean isReadOnly() {
return isSet(READ_ONLY);
}
@Override
public Thread ownerThread() {
return owner;
}
@Override
public final void close() {
checkValidState();
scope.close();
}
@Override
public ByteBuffer asByteBuffer() {
checkValidState();
checkIntSize("ByteBuffer");
JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
ByteBuffer _bb;
if (base() != null) {
if (!(base() instanceof byte[])) {
throw new UnsupportedOperationException("Not an address to an heap-allocated byte array");
}
_bb = nioAccess.newHeapByteBuffer((byte[]) base(), (int)min - BYTE_ARR_BASE, (int) length, this);
} else {
_bb = nioAccess.newDirectByteBuffer(min, (int) length, null, this);
}
if (isReadOnly()) {
_bb = _bb.asReadOnlyBuffer();
}
return _bb;
}
@Override
public byte[] toByteArray() {
checkValidState();
checkIntSize("byte[]");
byte[] arr = new byte[(int)length];
MemorySegment arrSegment = MemorySegment.ofArray(arr);
MemoryAddress.copy(this.baseAddress(), arrSegment.baseAddress(), length);
return arr;
}
@Override
public final void checkValidState() {
if (owner != Thread.currentThread()) {
throw new IllegalStateException("Attempt to access segment outside owning thread");
}
scope.checkAliveConfined();
}
@Override
public String toString() {
return "MemorySegment{ id=0x" + Long.toHexString(id()) + " limit: " + byteSize() + " }";
}
void checkRange(long offset, long length, boolean writeAccess) {
checkValidState();
if (isReadOnly() && writeAccess) {
throw new UnsupportedOperationException("Cannot write to read-only memory segment");
}
checkBounds(offset, length);
}
Object base() {
return base;
}
private boolean isSet(int mask) {
return (this.mask & mask) != 0;
}
private void checkIntSize(String typeName) {
if (length > (Integer.MAX_VALUE - 8)) {
throw new UnsupportedOperationException(String.format("Segment is too large to wrap as %s. Size: %d", typeName, length));
}
}
private void checkBounds(long offset, long length) {
if (length < 0 ||
offset < 0 ||
offset > this.length - length) {
throw new IndexOutOfBoundsException(String.format("Out of bound access on segment %s; new offset = %d; new length = %d",
this, offset, length));
}
}
private int id() {
return Math.abs(Objects.hash(base, min, NONCE));
}
}