Skip to content

Commit e068943

Browse files
committed
Add IBufferProtocol to mmap
1 parent 9082851 commit e068943

File tree

2 files changed

+106
-16
lines changed

2 files changed

+106
-16
lines changed

Src/IronPython.Modules/mmap.cs

Lines changed: 106 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#if FEATURE_MMAP
66

77
using System;
8+
using System.Buffers;
9+
using System.Collections.Generic;
810
using System.ComponentModel;
911
using System.Diagnostics;
1012
using System.Globalization;
@@ -277,7 +279,7 @@ private static MemoryMappedFileAccess ToMmapFileAccess(int access) {
277279
}
278280

279281
[PythonHidden]
280-
public class MmapDefault : IWeakReferenceable {
282+
public class MmapDefault : IWeakReferenceable, IBufferProtocol {
281283
private MemoryMappedFile _file;
282284
private MemoryMappedViewAccessor _view;
283285
private long _position;
@@ -570,12 +572,20 @@ public void __exit__(CodeContext/*!*/ context, params object[] excinfo) {
570572
public bool closed => _isClosed;
571573

572574
public void close() {
573-
if (!_isClosed) {
574-
lock (this) {
575-
if (!_isClosed) {
576-
_isClosed = true;
577-
CloseWorker();
578-
}
575+
if (_isClosed) return;
576+
577+
if (_refCount > 1) {
578+
GC.Collect();
579+
GC.WaitForPendingFinalizers();
580+
if (_refCount > 1) {
581+
throw PythonOps.BufferError("cannot close exported pointers exist");
582+
}
583+
}
584+
585+
lock (this) {
586+
if (!_isClosed) {
587+
_isClosed = true;
588+
CloseWorker();
579589
}
580590
}
581591
}
@@ -820,6 +830,14 @@ public string readline() {
820830
}
821831

822832
public void resize(long newsize) {
833+
if (_refCount > 1) {
834+
GC.Collect();
835+
GC.WaitForPendingFinalizers();
836+
if (_refCount > 1) {
837+
throw PythonOps.BufferError("mmap can't resize with extant buffers exported.");
838+
}
839+
}
840+
823841
using (new MmapLocker(this)) {
824842
if (_fileAccess is not MemoryMappedFileAccess.ReadWrite and not MemoryMappedFileAccess.ReadWriteExecute) {
825843
throw PythonOps.TypeError("mmap can't resize a readonly or copy-on-write memory map.");
@@ -1076,8 +1094,10 @@ private long Position {
10761094
}
10771095
}
10781096

1097+
private bool IsReadOnly => _fileAccess is MemoryMappedFileAccess.Read or MemoryMappedFileAccess.ReadExecute;
1098+
10791099
private void EnsureWritable() {
1080-
if (_fileAccess is MemoryMappedFileAccess.Read or MemoryMappedFileAccess.ReadExecute) {
1100+
if (IsReadOnly) {
10811101
throw PythonOps.TypeError("mmap can't modify a read-only memory map.");
10821102
}
10831103
}
@@ -1198,6 +1218,84 @@ void IWeakReferenceable.SetFinalizer(WeakRefTracker value) {
11981218
}
11991219

12001220
#endregion
1221+
1222+
#nullable enable
1223+
1224+
public IPythonBuffer GetBuffer(BufferFlags flags = BufferFlags.Simple) {
1225+
if (flags.HasFlag(BufferFlags.Writable) && IsReadOnly)
1226+
throw PythonOps.BufferError("Object is not writable.");
1227+
1228+
return new MmapBuffer(this, flags);
1229+
}
1230+
1231+
private sealed class MmapBuffer : IPythonBuffer {
1232+
private readonly MmapDefault _mmap;
1233+
private readonly BufferFlags _flags;
1234+
private SafeMemoryMappedViewHandle? _handle;
1235+
1236+
public MmapBuffer(MmapDefault mmap, BufferFlags flags) {
1237+
mmap.EnsureOpen();
1238+
1239+
_mmap = mmap;
1240+
_flags = flags;
1241+
Interlocked.Increment(ref _mmap._refCount);
1242+
_handle = _mmap._view.SafeMemoryMappedViewHandle;
1243+
ItemCount = _mmap.__len__() is int i ? i : throw new NotImplementedException();
1244+
}
1245+
1246+
public object Object => _mmap;
1247+
1248+
public bool IsReadOnly => _mmap.IsReadOnly;
1249+
1250+
public int Offset => 0;
1251+
1252+
public string? Format => _flags.HasFlag(BufferFlags.Format) ? "B" : null;
1253+
1254+
public int ItemCount { get; }
1255+
1256+
public int ItemSize => 1;
1257+
1258+
public int NumOfDims => 1;
1259+
1260+
public IReadOnlyList<int>? Shape => null;
1261+
1262+
public IReadOnlyList<int>? Strides => null;
1263+
1264+
public IReadOnlyList<int>? SubOffsets => null;
1265+
1266+
public unsafe ReadOnlySpan<byte> AsReadOnlySpan() {
1267+
if (_handle is null) throw new ObjectDisposedException(nameof(MmapBuffer));
1268+
byte* pointer = null;
1269+
_handle.AcquirePointer(ref pointer);
1270+
return new ReadOnlySpan<byte>(pointer, ItemCount);
1271+
}
1272+
1273+
public unsafe Span<byte> AsSpan() {
1274+
if (_handle is null) throw new ObjectDisposedException(nameof(MmapBuffer));
1275+
if (IsReadOnly) throw new InvalidOperationException("object is not writable");
1276+
byte* pointer = null;
1277+
_handle.AcquirePointer(ref pointer);
1278+
return new Span<byte>(pointer, ItemCount);
1279+
}
1280+
1281+
public unsafe MemoryHandle Pin() {
1282+
if (_handle is null) throw new ObjectDisposedException(nameof(MmapBuffer));
1283+
byte* pointer = null;
1284+
_handle.AcquirePointer(ref pointer);
1285+
return new MemoryHandle(pointer);
1286+
}
1287+
1288+
public void Dispose() {
1289+
var handle = Interlocked.Exchange(ref _handle, null);
1290+
if (handle is null) return;
1291+
handle.Dispose();
1292+
_mmap.CloseWorker();
1293+
}
1294+
1295+
}
1296+
1297+
#nullable restore
1298+
12011299
}
12021300

12031301
#region P/Invoke for allocation granularity

Src/IronPython.Modules/re.cs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -516,11 +516,6 @@ private string ValidateString(object? @string) {
516516
case IList<byte> b:
517517
str = b.MakeString();
518518
break;
519-
#if FEATURE_MMAP
520-
case MmapModule.MmapDefault mmapFile:
521-
str = mmapFile.GetSearchString().MakeString();
522-
break;
523-
#endif
524519
case string _:
525520
case ExtensibleString _:
526521
throw PythonOps.TypeError("cannot use a bytes pattern on a string-like object");
@@ -537,9 +532,6 @@ private string ValidateString(object? @string) {
537532
break;
538533
case IBufferProtocol _:
539534
case IList<byte> _:
540-
#if FEATURE_MMAP
541-
case MmapModule.MmapDefault _:
542-
#endif
543535
throw PythonOps.TypeError("cannot use a string pattern on a bytes-like object");
544536
default:
545537
throw PythonOps.TypeError("expected string or bytes-like object");

0 commit comments

Comments
 (0)