77#if FEATURE_MMAP
88
99using System ;
10+ using System . Buffers ;
11+ using System . Collections . Generic ;
12+ using System . ComponentModel ;
1013using System . Diagnostics ;
1114using System . Globalization ;
1215using System . IO ;
@@ -278,7 +281,7 @@ private static MemoryMappedFileAccess ToMmapFileAccess(int access) {
278281 }
279282
280283 [ PythonHidden ]
281- public class MmapDefault : IWeakReferenceable {
284+ public class MmapDefault : IWeakReferenceable , IBufferProtocol {
282285 private MemoryMappedFile _file ;
283286 private MemoryMappedViewAccessor _view ;
284287 private long _position ;
@@ -559,10 +562,8 @@ public void __exit__(CodeContext/*!*/ context, params object[] excinfo) {
559562 close ( ) ;
560563 }
561564
562-
563565 public bool closed => ( _state & StateBits . Closed ) == StateBits . Closed ; // Dispose already requested, will self-dispose when ref count drops to 0.
564566
565-
566567 private bool AddRef ( ) {
567568 int oldState , newState ;
568569 do {
@@ -606,6 +607,14 @@ private void Release() {
606607
607608
608609 public void close ( ) {
610+ if ( ( _state & StateBits . RefCount ) != 0 ) {
611+ GC . Collect ( ) ;
612+ GC . WaitForPendingFinalizers ( ) ;
613+ if ( ( _state & StateBits . RefCount ) != 0 ) {
614+ throw PythonOps . BufferError ( "cannot close exported pointers exist" ) ;
615+ }
616+ }
617+
609618 // close is idempotent; it must never block
610619#if NET5_0_OR_GREATER
611620 if ( ( Interlocked . Or ( ref _state , StateBits . Closed ) & StateBits . Closed ) != StateBits . Closed ) {
@@ -614,12 +623,10 @@ public void close() {
614623 }
615624#else
616625 int current = _state ;
617- while ( true )
618- {
626+ while ( true ) {
619627 int newState = current | StateBits . Closed ;
620628 int oldState = Interlocked . CompareExchange ( ref _state , newState , current ) ;
621- if ( oldState == current )
622- {
629+ if ( oldState == current ) {
623630 // didn't change in the meantime, exchange with newState completed
624631 if ( ( oldState & StateBits . Closed ) != StateBits . Closed ) {
625632 // freshly closed, release the construction time reference
@@ -862,6 +869,14 @@ public string readline() {
862869 }
863870
864871 public void resize ( long newsize ) {
872+ if ( ( _state & StateBits . RefCount ) != 0 ) {
873+ GC . Collect ( ) ;
874+ GC . WaitForPendingFinalizers ( ) ;
875+ if ( ( _state & StateBits . RefCount ) != 0 ) {
876+ throw PythonOps . BufferError ( "mmap can't resize with extant buffers exported." ) ;
877+ }
878+ }
879+
865880 using ( new MmapLocker ( this ) ) {
866881 if ( _fileAccess is not MemoryMappedFileAccess . ReadWrite and not MemoryMappedFileAccess . ReadWriteExecute ) {
867882 throw PythonOps . TypeError ( "mmap can't resize a readonly or copy-on-write memory map." ) ;
@@ -894,13 +909,13 @@ public void resize(long newsize) {
894909 int fd = unchecked ( ( int ) _handle . DangerousGetHandle ( ) ) ;
895910 PythonNT . ftruncateUnix ( fd , newsize ) ;
896911
897- #if NET8_0_OR_GREATER
912+ #if NET8_0_OR_GREATER
898913 _file = MemoryMappedFile . CreateFromFile ( _handle , _mapName , newsize , _fileAccess , HandleInheritability . None , leaveOpen : true ) ;
899- #else
914+ #else
900915 _sourceStream ? . Dispose ( ) ;
901916 _sourceStream = new FileStream ( new SafeFileHandle ( ( IntPtr ) fd , ownsHandle : false ) , FileAccess . ReadWrite ) ;
902917 _file = CreateFromFile ( _sourceStream , _mapName , newsize , _fileAccess , HandleInheritability . None , leaveOpen : true ) ;
903- #endif
918+ #endif
904919 _view = _file . CreateViewAccessor ( _offset , newsize , _fileAccess ) ;
905920 return ;
906921 } catch {
@@ -1130,8 +1145,10 @@ private long Position {
11301145 }
11311146 }
11321147
1148+ private bool IsReadOnly => _fileAccess is MemoryMappedFileAccess . Read or MemoryMappedFileAccess . ReadExecute ;
1149+
11331150 private void EnsureWritable ( ) {
1134- if ( _fileAccess is MemoryMappedFileAccess . Read or MemoryMappedFileAccess . ReadExecute ) {
1151+ if ( IsReadOnly ) {
11351152 throw PythonOps . TypeError ( "mmap can't modify a read-only memory map." ) ;
11361153 }
11371154 }
@@ -1247,6 +1264,77 @@ void IWeakReferenceable.SetFinalizer(WeakRefTracker value) {
12471264 }
12481265
12491266 #endregion
1267+
1268+ public IPythonBuffer GetBuffer ( BufferFlags flags = BufferFlags . Simple ) {
1269+ if ( flags . HasFlag ( BufferFlags . Writable ) && IsReadOnly )
1270+ throw PythonOps . BufferError ( "Object is not writable." ) ;
1271+
1272+ return new MmapBuffer ( this , flags ) ;
1273+ }
1274+
1275+ private sealed unsafe class MmapBuffer : IPythonBuffer {
1276+ private readonly MmapDefault _mmap ;
1277+ private readonly BufferFlags _flags ;
1278+ private SafeMemoryMappedViewHandle ? _handle ;
1279+ private byte * _pointer = null ;
1280+
1281+ public MmapBuffer ( MmapDefault mmap , BufferFlags flags ) {
1282+ _mmap = mmap ;
1283+ _flags = flags ;
1284+ if ( ! _mmap . AddRef ( ) ) {
1285+ throw PythonOps . ValueError ( "mmap closed or invalid" ) ;
1286+ }
1287+ _handle = _mmap . _view . SafeMemoryMappedViewHandle ;
1288+ ItemCount = _mmap . __len__ ( ) is int i ? i : throw new NotImplementedException ( ) ;
1289+ }
1290+
1291+ public object Object => _mmap ;
1292+
1293+ public bool IsReadOnly => _mmap . IsReadOnly ;
1294+
1295+ public int Offset => 0 ;
1296+
1297+ public string ? Format => _flags . HasFlag ( BufferFlags . Format ) ? "B" : null ;
1298+
1299+ public int ItemCount { get ; }
1300+
1301+ public int ItemSize => 1 ;
1302+
1303+ public int NumOfDims => 1 ;
1304+
1305+ public IReadOnlyList < int > ? Shape => null ;
1306+
1307+ public IReadOnlyList < int > ? Strides => null ;
1308+
1309+ public IReadOnlyList < int > ? SubOffsets => null ;
1310+
1311+ public unsafe ReadOnlySpan < byte > AsReadOnlySpan ( ) {
1312+ if ( _handle is null ) throw new ObjectDisposedException ( nameof ( MmapBuffer ) ) ;
1313+ if ( _pointer is null ) _handle . AcquirePointer ( ref _pointer ) ;
1314+ return new ReadOnlySpan < byte > ( _pointer , ItemCount ) ;
1315+ }
1316+
1317+ public unsafe Span < byte > AsSpan ( ) {
1318+ if ( _handle is null ) throw new ObjectDisposedException ( nameof ( MmapBuffer ) ) ;
1319+ if ( IsReadOnly ) throw new InvalidOperationException ( "object is not writable" ) ;
1320+ if ( _pointer is null ) _handle . AcquirePointer ( ref _pointer ) ;
1321+ return new Span < byte > ( _pointer , ItemCount ) ;
1322+ }
1323+
1324+ public unsafe MemoryHandle Pin ( ) {
1325+ if ( _handle is null ) throw new ObjectDisposedException ( nameof ( MmapBuffer ) ) ;
1326+ if ( _pointer is null ) _handle . AcquirePointer ( ref _pointer ) ;
1327+ return new MemoryHandle ( _pointer ) ;
1328+ }
1329+
1330+ public void Dispose ( ) {
1331+ var handle = Interlocked . Exchange ( ref _handle , null ) ;
1332+ if ( handle is null ) return ;
1333+ if ( _pointer is not null ) handle . ReleasePointer ( ) ;
1334+ _mmap . Release ( ) ;
1335+ }
1336+
1337+ }
12501338 }
12511339
12521340
0 commit comments