Skip to content

Commit ab28c74

Browse files
committed
Implement 'mmap' builtins.
1 parent e3d588c commit ab28c74

File tree

8 files changed

+382
-109
lines changed

8 files changed

+382
-109
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MMapModuleBuiltins.java

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,24 @@
4343
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.PMMap;
4444
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError;
4545

46-
import java.nio.channels.Channel;
46+
import java.io.IOException;
4747
import java.nio.channels.FileChannel.MapMode;
4848
import java.nio.channels.SeekableByteChannel;
49+
import java.nio.file.StandardOpenOption;
50+
import java.util.HashSet;
4951
import java.util.List;
52+
import java.util.Set;
5053

5154
import com.oracle.graal.python.builtins.Builtin;
5255
import com.oracle.graal.python.builtins.CoreFunctions;
56+
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
5357
import com.oracle.graal.python.builtins.PythonBuiltins;
5458
import com.oracle.graal.python.builtins.objects.PNone;
5559
import com.oracle.graal.python.builtins.objects.mmap.PMMap;
5660
import com.oracle.graal.python.builtins.objects.type.LazyPythonClass;
5761
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
5862
import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
63+
import com.oracle.truffle.api.TruffleFile;
5964
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
6065
import com.oracle.truffle.api.dsl.NodeFactory;
6166
import com.oracle.truffle.api.dsl.Specialization;
@@ -80,26 +85,38 @@ public MMapModuleBuiltins() {
8085
builtinConstants.put("ACCESS_COPY", ACCESS_COPY);
8186
}
8287

83-
@Builtin(name = "mmap", fixedNumOfPositionalArgs = 3, keywordArguments = {"tagname", "access"}, constructsClass = PMMap)
88+
@Builtin(name = "mmap", fixedNumOfPositionalArgs = 3, keywordArguments = {"tagname", "access", "offset"}, constructsClass = PMMap)
8489
@GenerateNodeFactory
8590
public abstract static class MMapNode extends PythonBuiltinNode {
8691

8792
private final ValueProfile classProfile = ValueProfile.createClassProfile();
8893

89-
@Specialization(guards = {"isNoValue(access)"})
90-
PMMap doIt(LazyPythonClass clazz, int fd, int length, Object tagname, @SuppressWarnings("unused") PNone access) {
91-
return doGeneric(clazz, fd, length, tagname, ACCESS_DEFAULT);
94+
@Specialization(guards = {"isNoValue(access)", "isNoValue(offset)"})
95+
PMMap doIt(LazyPythonClass clazz, int fd, int length, Object tagname, @SuppressWarnings("unused") PNone access, @SuppressWarnings("unused") PNone offset) {
96+
return doGeneric(clazz, fd, length, tagname, ACCESS_DEFAULT, 0);
9297
}
9398

9499
// mmap(fileno, length, tagname=None, access=ACCESS_DEFAULT[, offset])
95100
@Specialization
96-
PMMap doGeneric(LazyPythonClass clazz, int fd, int length, @SuppressWarnings("unused") Object tagname, int access) {
97-
Channel fileChannel = getContext().getResources().getFileChannel(fd, classProfile);
98-
if (fileChannel instanceof SeekableByteChannel) {
99-
MapMode mode = convertAccessToMapMode(access);
100-
return factory().createMMap(clazz, (SeekableByteChannel) fileChannel);
101+
PMMap doGeneric(LazyPythonClass clazz, int fd, int length, @SuppressWarnings("unused") Object tagname, int access, long offset) {
102+
String path = getContext().getResources().getFilePath(fd);
103+
TruffleFile truffleFile = getContext().getEnv().getTruffleFile(path);
104+
105+
// TODO(fa) correctly honor access flags
106+
MapMode mode = convertAccessToMapMode(access);
107+
Set<StandardOpenOption> options = new HashSet<>();
108+
options.add(StandardOpenOption.READ);
109+
options.add(StandardOpenOption.WRITE);
110+
111+
// we create a new channel otherwise we cannot guarantee that the cursor is exclusive
112+
SeekableByteChannel fileChannel;
113+
try {
114+
fileChannel = truffleFile.newByteChannel(options);
115+
fileChannel.position(offset);
116+
return new PMMap(PythonBuiltinClassType.PMMap, fileChannel, length, offset);
117+
} catch (IOException e) {
118+
throw raise(ValueError, "cannot mmap file");
101119
}
102-
throw raise(ValueError, "cannot mmap file");
103120
}
104121

105122
private MapMode convertAccessToMapMode(int access) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java

Lines changed: 4 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@
7878
import com.oracle.graal.python.builtins.PythonBuiltins;
7979
import com.oracle.graal.python.builtins.modules.PosixModuleBuiltinsFactory.CastToPathNodeGen;
8080
import com.oracle.graal.python.builtins.modules.PosixModuleBuiltinsFactory.ConvertPathlikeObjectNodeGen;
81-
import com.oracle.graal.python.builtins.modules.PosixModuleBuiltinsFactory.ReadFromChannelNodeGen;
8281
import com.oracle.graal.python.builtins.modules.PosixModuleBuiltinsFactory.StatNodeFactory;
8382
import com.oracle.graal.python.builtins.objects.PNone;
8483
import com.oracle.graal.python.builtins.objects.bytes.BytesNodes.ToBytesNode;
@@ -110,6 +109,7 @@
110109
import com.oracle.graal.python.nodes.truffle.PythonArithmeticTypes;
111110
import com.oracle.graal.python.nodes.util.CastToIndexNode;
112111
import com.oracle.graal.python.nodes.util.CastToIntegerFromIntNode;
112+
import com.oracle.graal.python.nodes.util.ChannelNodes.ReadFromChannelNode;
113113
import com.oracle.graal.python.runtime.PosixResources;
114114
import com.oracle.graal.python.runtime.PythonContext;
115115
import com.oracle.graal.python.runtime.PythonCore;
@@ -1007,90 +1007,10 @@ public static WriteNode create() {
10071007
}
10081008
}
10091009

1010-
abstract static class ReadFromChannelNode extends PNodeWithContext {
1011-
private final BranchProfile gotException = BranchProfile.create();
1012-
1013-
abstract ByteSequenceStorage execute(Channel channel, int size);
1014-
1015-
@Specialization
1016-
ByteSequenceStorage readSeekable(SeekableByteChannel channel, int size) {
1017-
long availableSize;
1018-
try {
1019-
availableSize = availableSize(channel);
1020-
} catch (IOException e) {
1021-
gotException.enter();
1022-
throw raise(OSError, e);
1023-
}
1024-
if (availableSize > ReadNode.MAX_READ) {
1025-
availableSize = ReadNode.MAX_READ;
1026-
}
1027-
int sz = (int) Math.min(availableSize, size);
1028-
return readReadable(channel, sz);
1029-
}
1030-
1031-
@TruffleBoundary(transferToInterpreterOnException = false)
1032-
private static long availableSize(SeekableByteChannel channel) throws IOException {
1033-
return channel.size() - channel.position();
1034-
}
1035-
1036-
@Specialization
1037-
ByteSequenceStorage readReadable(ReadableByteChannel channel, int size) {
1038-
int sz = Math.min(size, ReadNode.MAX_READ);
1039-
ByteBuffer dst = allocateBuffer(sz);
1040-
int readSize = readIntoBuffer(channel, dst);
1041-
byte[] array;
1042-
if (readSize <= 0) {
1043-
array = new byte[0];
1044-
readSize = 0;
1045-
} else {
1046-
array = getByteBufferArray(dst);
1047-
}
1048-
ByteSequenceStorage byteSequenceStorage = new ByteSequenceStorage(array);
1049-
byteSequenceStorage.setNewLength(readSize);
1050-
return byteSequenceStorage;
1051-
}
1052-
1053-
@Specialization
1054-
ByteSequenceStorage readGeneric(Channel channel, int size) {
1055-
if (channel instanceof SeekableByteChannel) {
1056-
return readSeekable((SeekableByteChannel) channel, size);
1057-
} else if (channel instanceof ReadableByteChannel) {
1058-
return readReadable((ReadableByteChannel) channel, size);
1059-
} else {
1060-
throw raise(OSError, "file not opened for reading");
1061-
}
1062-
}
1063-
1064-
@TruffleBoundary(allowInlining = true)
1065-
private static byte[] getByteBufferArray(ByteBuffer dst) {
1066-
return dst.array();
1067-
}
1068-
1069-
@TruffleBoundary(allowInlining = true)
1070-
private int readIntoBuffer(ReadableByteChannel readableChannel, ByteBuffer dst) {
1071-
try {
1072-
return readableChannel.read(dst);
1073-
} catch (IOException e) {
1074-
gotException.enter();
1075-
throw raise(OSError, e);
1076-
}
1077-
}
1078-
1079-
@TruffleBoundary(allowInlining = true)
1080-
private static ByteBuffer allocateBuffer(int sz) {
1081-
return ByteBuffer.allocate(sz);
1082-
}
1083-
1084-
public static ReadFromChannelNode create() {
1085-
return ReadFromChannelNodeGen.create();
1086-
}
1087-
}
1088-
10891010
@Builtin(name = "read", fixedNumOfPositionalArgs = 2)
10901011
@GenerateNodeFactory
10911012
@TypeSystemReference(PythonArithmeticTypes.class)
10921013
public abstract static class ReadNode extends PythonFileNode {
1093-
private static final int MAX_READ = Integer.MAX_VALUE / 2;
10941014

10951015
@CompilationFinal private BranchProfile tooLargeProfile = BranchProfile.create();
10961016

@@ -1100,8 +1020,8 @@ Object readOpaque(@SuppressWarnings("unused") VirtualFrame frame, int fd, long r
11001020
@Cached("create()") ReadFromChannelNode readNode) {
11011021
if (OpaqueBytes.isInOpaqueFilesystem(getResources().getFilePath(fd), getContext())) {
11021022
Channel channel = getResources().getFileChannel(fd, channelClassProfile);
1103-
ByteSequenceStorage bytes = readNode.execute(channel, MAX_READ);
1104-
return new OpaqueBytes(Arrays.copyOf(bytes.getInternalByteArray(), bytes.length()));
1023+
ByteSequenceStorage bytes = readNode.execute(channel, ReadFromChannelNode.MAX_READ);
1024+
return new OpaqueBytes(Arrays.copyOf(bytes.getInternalByteArray(), bytes.length()));
11051025
}
11061026
return read(frame, fd, requestedSize, channelClassProfile, readNode);
11071027
}
@@ -1115,7 +1035,7 @@ Object read(@SuppressWarnings("unused") VirtualFrame frame, int fd, long request
11151035
size = Math.toIntExact(requestedSize);
11161036
} catch (ArithmeticException e) {
11171037
tooLargeProfile.enter();
1118-
size = MAX_READ;
1038+
size = ReadFromChannelNode.MAX_READ;
11191039
}
11201040
Channel channel = getResources().getFileChannel(fd, channelClassProfile);
11211041
ByteSequenceStorage array = readNode.execute(channel, size);

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/PythonObjectNativeWrapperMR.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,8 @@ Object doTpAsBuffer(PythonManagedClass object, @SuppressWarnings("unused") Strin
453453
@Cached("create()") BranchProfile notBytes,
454454
@Cached("create()") BranchProfile notBytearray,
455455
@Cached("create()") BranchProfile notMemoryview,
456-
@Cached("create()") BranchProfile notBuffer) {
456+
@Cached("create()") BranchProfile notBuffer,
457+
@Cached("create()") BranchProfile notMmap) {
457458
PythonCore core = getCore();
458459
PythonBuiltinClass pBytes = core.lookupType(PythonBuiltinClassType.PBytes);
459460
if (isSubtype.execute(object, pBytes)) {
@@ -475,6 +476,11 @@ Object doTpAsBuffer(PythonManagedClass object, @SuppressWarnings("unused") Strin
475476
return new PyBufferProcsWrapper(pBuffer);
476477
}
477478
notBuffer.enter();
479+
PythonBuiltinClass pMmap = core.lookupType(PythonBuiltinClassType.PMMap);
480+
if (isSubtype.execute(object, pMmap)) {
481+
return new PyBufferProcsWrapper(pMemoryview);
482+
}
483+
notMmap.enter();
478484
// NULL pointer
479485
return getToSulongNode().execute(PNone.NO_VALUE);
480486
}

0 commit comments

Comments
 (0)