77#if FEATURE_MMAP
88
99using System ;
10+ using System . Buffers ;
11+ using System . Collections . Generic ;
1012using System . Diagnostics ;
1113using System . Globalization ;
1214using System . IO ;
@@ -278,7 +280,7 @@ private static MemoryMappedFileAccess ToMmapFileAccess(int access) {
278280 }
279281
280282 [ PythonHidden ]
281- public class MmapDefault : IWeakReferenceable {
283+ public class MmapDefault : IWeakReferenceable , IBufferProtocol {
282284 private MemoryMappedFile _file ;
283285 private MemoryMappedViewAccessor _view ;
284286 private long _position ;
@@ -559,10 +561,8 @@ public void __exit__(CodeContext/*!*/ context, params object[] excinfo) {
559561 close ( ) ;
560562 }
561563
562-
563564 public bool closed => ( _state & StateBits . Closed ) == StateBits . Closed ; // Dispose already requested, will self-dispose when ref count drops to 0.
564565
565-
566566 private bool AddRef ( ) {
567567 int oldState , newState ;
568568 do {
@@ -606,6 +606,14 @@ private void Release() {
606606
607607
608608 public void close ( ) {
609+ if ( ( _state & StateBits . RefCount ) != 0 ) {
610+ GC . Collect ( ) ;
611+ GC . WaitForPendingFinalizers ( ) ;
612+ if ( ( _state & StateBits . RefCount ) != 0 ) {
613+ throw PythonOps . BufferError ( "cannot close exported pointers exist" ) ;
614+ }
615+ }
616+
609617 // close is idempotent; it must never block
610618#if NET5_0_OR_GREATER
611619 if ( ( Interlocked . Or ( ref _state , StateBits . Closed ) & StateBits . Closed ) != StateBits . Closed ) {
@@ -614,12 +622,10 @@ public void close() {
614622 }
615623#else
616624 int current = _state ;
617- while ( true )
618- {
625+ while ( true ) {
619626 int newState = current | StateBits . Closed ;
620627 int oldState = Interlocked . CompareExchange ( ref _state , newState , current ) ;
621- if ( oldState == current )
622- {
628+ if ( oldState == current ) {
623629 // didn't change in the meantime, exchange with newState completed
624630 if ( ( oldState & StateBits . Closed ) != StateBits . Closed ) {
625631 // freshly closed, release the construction time reference
@@ -862,6 +868,14 @@ public string readline() {
862868 }
863869
864870 public void resize ( long newsize ) {
871+ if ( ( _state & StateBits . RefCount ) != 0 ) {
872+ GC . Collect ( ) ;
873+ GC . WaitForPendingFinalizers ( ) ;
874+ if ( ( _state & StateBits . RefCount ) != 0 ) {
875+ throw PythonOps . BufferError ( "mmap can't resize with extant buffers exported." ) ;
876+ }
877+ }
878+
865879 using ( new MmapLocker ( this ) ) {
866880 if ( _fileAccess is not MemoryMappedFileAccess . ReadWrite and not MemoryMappedFileAccess . ReadWriteExecute ) {
867881 throw PythonOps . TypeError ( "mmap can't resize a readonly or copy-on-write memory map." ) ;
@@ -894,13 +908,13 @@ public void resize(long newsize) {
894908 int fd = unchecked ( ( int ) _handle . DangerousGetHandle ( ) ) ;
895909 PythonNT . ftruncateUnix ( fd , newsize ) ;
896910
897- #if NET8_0_OR_GREATER
911+ #if NET8_0_OR_GREATER
898912 _file = MemoryMappedFile . CreateFromFile ( _handle , _mapName , newsize , _fileAccess , HandleInheritability . None , leaveOpen : true ) ;
899- #else
913+ #else
900914 _sourceStream ? . Dispose ( ) ;
901915 _sourceStream = new FileStream ( new SafeFileHandle ( ( IntPtr ) fd , ownsHandle : false ) , FileAccess . ReadWrite ) ;
902916 _file = CreateFromFile ( _sourceStream , _mapName , newsize , _fileAccess , HandleInheritability . None , leaveOpen : true ) ;
903- #endif
917+ #endif
904918 _view = _file . CreateViewAccessor ( _offset , newsize , _fileAccess ) ;
905919 return ;
906920 } catch {
@@ -1130,8 +1144,10 @@ private long Position {
11301144 }
11311145 }
11321146
1147+ private bool IsReadOnly => _fileAccess is MemoryMappedFileAccess . Read or MemoryMappedFileAccess . ReadExecute ;
1148+
11331149 private void EnsureWritable ( ) {
1134- if ( _fileAccess is MemoryMappedFileAccess . Read or MemoryMappedFileAccess . ReadExecute ) {
1150+ if ( IsReadOnly ) {
11351151 throw PythonOps . TypeError ( "mmap can't modify a read-only memory map." ) ;
11361152 }
11371153 }
@@ -1247,6 +1263,77 @@ void IWeakReferenceable.SetFinalizer(WeakRefTracker value) {
12471263 }
12481264
12491265 #endregion
1266+
1267+ public IPythonBuffer GetBuffer ( BufferFlags flags = BufferFlags . Simple ) {
1268+ if ( flags . HasFlag ( BufferFlags . Writable ) && IsReadOnly )
1269+ throw PythonOps . BufferError ( "Object is not writable." ) ;
1270+
1271+ return new MmapBuffer ( this , flags ) ;
1272+ }
1273+
1274+ private sealed unsafe class MmapBuffer : IPythonBuffer {
1275+ private readonly MmapDefault _mmap ;
1276+ private readonly BufferFlags _flags ;
1277+ private SafeMemoryMappedViewHandle ? _handle ;
1278+ private byte * _pointer = null ;
1279+
1280+ public MmapBuffer ( MmapDefault mmap , BufferFlags flags ) {
1281+ _mmap = mmap ;
1282+ _flags = flags ;
1283+ if ( ! _mmap . AddRef ( ) ) {
1284+ throw PythonOps . ValueError ( "mmap closed or invalid" ) ;
1285+ }
1286+ _handle = _mmap . _view . SafeMemoryMappedViewHandle ;
1287+ ItemCount = _mmap . __len__ ( ) is int i ? i : throw new NotImplementedException ( ) ;
1288+ }
1289+
1290+ public object Object => _mmap ;
1291+
1292+ public bool IsReadOnly => _mmap . IsReadOnly ;
1293+
1294+ public int Offset => 0 ;
1295+
1296+ public string ? Format => _flags . HasFlag ( BufferFlags . Format ) ? "B" : null ;
1297+
1298+ public int ItemCount { get ; }
1299+
1300+ public int ItemSize => 1 ;
1301+
1302+ public int NumOfDims => 1 ;
1303+
1304+ public IReadOnlyList < int > ? Shape => null ;
1305+
1306+ public IReadOnlyList < int > ? Strides => null ;
1307+
1308+ public IReadOnlyList < int > ? SubOffsets => null ;
1309+
1310+ public unsafe ReadOnlySpan < byte > AsReadOnlySpan ( ) {
1311+ if ( _handle is null ) throw new ObjectDisposedException ( nameof ( MmapBuffer ) ) ;
1312+ if ( _pointer is null ) _handle . AcquirePointer ( ref _pointer ) ;
1313+ return new ReadOnlySpan < byte > ( _pointer , ItemCount ) ;
1314+ }
1315+
1316+ public unsafe Span < byte > AsSpan ( ) {
1317+ if ( _handle is null ) throw new ObjectDisposedException ( nameof ( MmapBuffer ) ) ;
1318+ if ( IsReadOnly ) throw new InvalidOperationException ( "object is not writable" ) ;
1319+ if ( _pointer is null ) _handle . AcquirePointer ( ref _pointer ) ;
1320+ return new Span < byte > ( _pointer , ItemCount ) ;
1321+ }
1322+
1323+ public unsafe MemoryHandle Pin ( ) {
1324+ if ( _handle is null ) throw new ObjectDisposedException ( nameof ( MmapBuffer ) ) ;
1325+ if ( _pointer is null ) _handle . AcquirePointer ( ref _pointer ) ;
1326+ return new MemoryHandle ( _pointer ) ;
1327+ }
1328+
1329+ public void Dispose ( ) {
1330+ var handle = Interlocked . Exchange ( ref _handle , null ) ;
1331+ if ( handle is null ) return ;
1332+ if ( _pointer is not null ) handle . ReleasePointer ( ) ;
1333+ _mmap . Release ( ) ;
1334+ }
1335+
1336+ }
12501337 }
12511338
12521339
0 commit comments