package jdk.internal.foreign;
import jdk.incubator.foreign.MemorySegment;
import jdk.internal.access.foreign.UnmapperProxy;
import jdk.internal.misc.ScopedMemoryAccess;
import sun.nio.ch.FileChannelImpl;
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Objects;
import java.util.Optional;
public class MappedMemorySegmentImpl extends NativeMemorySegmentImpl {
private final UnmapperProxy unmapper;
static ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess();
MappedMemorySegmentImpl(long min, UnmapperProxy unmapper, long length, int mask, MemoryScope scope) {
super(min, length, mask, scope);
this.unmapper = unmapper;
}
@Override
ByteBuffer makeByteBuffer() {
return nioAccess.newMappedByteBuffer(unmapper, min, (int)length, null, this);
}
@Override
MappedMemorySegmentImpl dup(long offset, long size, int mask, MemoryScope scope) {
return new MappedMemorySegmentImpl(min + offset, unmapper, size, mask, scope);
}
@Override
public MappedMemorySegmentImpl asSlice(long offset, long newSize) {
return (MappedMemorySegmentImpl)super.asSlice(offset, newSize);
}
@Override
public MappedMemorySegmentImpl withAccessModes(int accessModes) {
return (MappedMemorySegmentImpl)super.withAccessModes(accessModes);
}
@Override
public boolean isMapped() {
return true;
}
public MemorySegment segment() {
return MappedMemorySegmentImpl.this;
}
public void load() {
SCOPED_MEMORY_ACCESS.load(scope, min, unmapper.isSync(), length);
}
public void unload() {
SCOPED_MEMORY_ACCESS.unload(scope, min, unmapper.isSync(), length);
}
public boolean isLoaded() {
return SCOPED_MEMORY_ACCESS.isLoaded(scope, min, unmapper.isSync(), length);
}
public void force() {
SCOPED_MEMORY_ACCESS.force(scope, unmapper.fileDescriptor(), min, unmapper.isSync(), 0, length);
}
public static MemorySegment makeMappedSegment(Path path, long bytesOffset, long bytesSize, FileChannel.MapMode mapMode) throws IOException {
Objects.requireNonNull(path);
Objects.requireNonNull(mapMode);
if (bytesSize < 0) throw new IllegalArgumentException("Requested bytes size must be >= 0.");
if (bytesOffset < 0) throw new IllegalArgumentException("Requested bytes offset must be >= 0.");
FileSystem fs = path.getFileSystem();
if (fs != FileSystems.getDefault() ||
fs.getClass().getModule() != Object.class.getModule()) {
throw new IllegalArgumentException("Unsupported file system");
}
try (FileChannel channelImpl = FileChannel.open(path, openOptions(mapMode))) {
UnmapperProxy unmapperProxy = ((FileChannelImpl)channelImpl).mapInternal(mapMode, bytesOffset, bytesSize);
int modes = defaultAccessModes(bytesSize);
if (mapMode == FileChannel.MapMode.READ_ONLY) {
modes &= ~WRITE;
}
if (unmapperProxy != null) {
MemoryScope scope = MemoryScope.createConfined(null, unmapperProxy::unmap, null);
return new MappedMemorySegmentImpl(unmapperProxy.address(), unmapperProxy, bytesSize,
modes, scope);
} else {
return new EmptyMappedMemorySegmentImpl(modes);
}
}
}
private static OpenOption[] openOptions(FileChannel.MapMode mapMode) {
if (mapMode == FileChannel.MapMode.READ_ONLY) {
return new OpenOption[] { StandardOpenOption.READ };
} else if (mapMode == FileChannel.MapMode.READ_WRITE || mapMode == FileChannel.MapMode.PRIVATE) {
return new OpenOption[] { StandardOpenOption.READ, StandardOpenOption.WRITE };
} else {
throw new UnsupportedOperationException("Unsupported map mode: " + mapMode);
}
}
static class EmptyMappedMemorySegmentImpl extends MappedMemorySegmentImpl {
public EmptyMappedMemorySegmentImpl(int modes) {
super(0, null, 0, modes,
MemoryScope.createConfined(null, MemoryScope.DUMMY_CLEANUP_ACTION, null));
}
@Override
public void load() {
}
@Override
public void unload() {
}
@Override
public boolean isLoaded() {
return true;
}
@Override
public void force() {
}
};
}