44using System . Buffers ;
55using System . Collections . Generic ;
66using System . Diagnostics ;
7+ using System . Diagnostics . CodeAnalysis ;
78using System . IO . Strategies ;
89using System . Numerics ;
910using System . Runtime . InteropServices ;
@@ -437,8 +438,8 @@ private static bool CanUseScatterGatherWindowsAPIs(SafeFileHandle handle)
437438 // The pinned MemoryHandles and the pointer to the segments must be cleaned-up
438439 // with the CleanupScatterGatherBuffers method.
439440 private static unsafe bool TryPrepareScatterGatherBuffers < T , THandler > ( IReadOnlyList < T > buffers ,
440- THandler handler , out MemoryHandle [ ] handlesToDispose , out IntPtr segmentsPtr , out int totalBytes )
441- where THandler : struct , IMemoryHandler < T >
441+ THandler handler , [ NotNullWhen ( true ) ] out MemoryHandle [ ] ? handlesToDispose , out IntPtr segmentsPtr , out int totalBytes )
442+ where THandler : struct , IMemoryHandler < T >
442443 {
443444 int pageSize = s_cachedPageSize ;
444445 Debug . Assert ( BitOperations . IsPow2 ( pageSize ) , "Page size is not a power of two." ) ;
@@ -447,13 +448,11 @@ private static unsafe bool TryPrepareScatterGatherBuffers<T, THandler>(IReadOnly
447448 long alignedAtPageSizeMask = pageSize - 1 ;
448449
449450 int buffersCount = buffers . Count ;
450- handlesToDispose = new MemoryHandle [ buffersCount ] ;
451+ handlesToDispose = null ;
451452 segmentsPtr = IntPtr . Zero ;
452453 totalBytes = 0 ;
453454
454- // "The array must contain enough elements to store nNumberOfBytesToWrite bytes of data, and one element for the terminating NULL. "
455- long * segments = ( long * ) NativeMemory . Alloc ( ( nuint ) ( buffersCount + 1 ) , sizeof ( long ) ) ;
456- segments [ buffersCount ] = 0 ;
455+ long * segments = null ;
457456
458457 bool success = false ;
459458 try
@@ -469,36 +468,55 @@ private static unsafe bool TryPrepareScatterGatherBuffers<T, THandler>(IReadOnly
469468 return false ;
470469 }
471470
472- MemoryHandle handle = handlesToDispose [ i ] = handler . Pin ( in buffer ) ;
473- long ptr = segments [ i ] = ( long ) handle . Pointer ;
471+ MemoryHandle handle = handler . Pin ( in buffer ) ;
472+ long ptr = ( long ) handle . Pointer ;
474473 if ( ( ptr & alignedAtPageSizeMask ) != 0 )
475474 {
475+ handle . Dispose ( ) ;
476476 return false ;
477477 }
478+
479+ // We avoid allocations if there are no
480+ // buffers or the first one is unacceptable.
481+ ( handlesToDispose ??= new MemoryHandle [ buffersCount ] ) [ i ] = handle ;
482+ if ( segments == null )
483+ {
484+ // "The array must contain enough elements to store nNumberOfBytesToWrite
485+ // bytes of data, and one element for the terminating NULL."
486+ segments = ( long * ) NativeMemory . Alloc ( ( nuint ) buffersCount + 1 , sizeof ( long ) ) ;
487+ segments [ buffersCount ] = 0 ;
488+ }
489+ segments [ i ] = ptr ;
478490 }
479491
480492 segmentsPtr = ( IntPtr ) segments ;
481493 totalBytes = ( int ) totalBytes64 ;
482494 success = true ;
483- return true ;
495+ return handlesToDispose != null ;
484496 }
485497 finally
486498 {
487499 if ( ! success )
488500 {
489- CleanupScatterGatherBuffers ( handlesToDispose , ( IntPtr ) segments ) ;
501+ CleanupScatterGatherBuffers ( handlesToDispose , ( IntPtr ) segments ) ;
490502 }
491503 }
492504 }
493505
494- private static unsafe void CleanupScatterGatherBuffers ( MemoryHandle [ ] handlesToDispose , IntPtr segmentsPtr )
506+ private static unsafe void CleanupScatterGatherBuffers ( MemoryHandle [ ] ? handlesToDispose , IntPtr segmentsPtr )
495507 {
496- foreach ( MemoryHandle handle in handlesToDispose )
508+ if ( handlesToDispose != null )
497509 {
498- handle . Dispose ( ) ;
510+ foreach ( MemoryHandle handle in handlesToDispose )
511+ {
512+ handle . Dispose ( ) ;
513+ }
499514 }
500515
501- NativeMemory . Free ( ( void * ) segmentsPtr ) ;
516+ if ( segmentsPtr != IntPtr . Zero )
517+ {
518+ NativeMemory . Free ( ( void * ) segmentsPtr ) ;
519+ }
502520 }
503521
504522 private static ValueTask < long > ReadScatterAtOffsetAsync ( SafeFileHandle handle , IReadOnlyList < Memory < byte > > buffers ,
@@ -510,7 +528,7 @@ private static ValueTask<long> ReadScatterAtOffsetAsync(SafeFileHandle handle, I
510528 }
511529
512530 if ( CanUseScatterGatherWindowsAPIs ( handle )
513- && TryPrepareScatterGatherBuffers ( buffers , default ( MemoryHandler ) , out MemoryHandle [ ] handlesToDispose , out IntPtr segmentsPtr , out int totalBytes ) )
531+ && TryPrepareScatterGatherBuffers ( buffers , default ( MemoryHandler ) , out MemoryHandle [ ] ? handlesToDispose , out IntPtr segmentsPtr , out int totalBytes ) )
514532 {
515533 return ReadScatterAtOffsetSingleSyscallAsync ( handle , handlesToDispose , segmentsPtr , fileOffset , totalBytes , cancellationToken ) ;
516534 }
@@ -607,7 +625,7 @@ private static ValueTask WriteGatherAtOffsetAsync(SafeFileHandle handle, IReadOn
607625 }
608626
609627 if ( CanUseScatterGatherWindowsAPIs ( handle )
610- && TryPrepareScatterGatherBuffers ( buffers , default ( ReadOnlyMemoryHandler ) , out MemoryHandle [ ] handlesToDispose , out IntPtr segmentsPtr , out int totalBytes ) )
628+ && TryPrepareScatterGatherBuffers ( buffers , default ( ReadOnlyMemoryHandler ) , out MemoryHandle [ ] ? handlesToDispose , out IntPtr segmentsPtr , out int totalBytes ) )
611629 {
612630 return WriteGatherAtOffsetSingleSyscallAsync ( handle , handlesToDispose , segmentsPtr , fileOffset , totalBytes , cancellationToken ) ;
613631 }
@@ -671,13 +689,12 @@ private static unsafe ValueTask WriteFileGatherAsync(SafeFileHandle handle, IntP
671689
672690 private static async ValueTask WriteGatherAtOffsetMultipleSyscallsAsync ( SafeFileHandle handle , IReadOnlyList < ReadOnlyMemory < byte > > buffers , long fileOffset , CancellationToken cancellationToken )
673691 {
674- long bytesWritten = 0 ;
675692 int buffersCount = buffers . Count ;
676693 for ( int i = 0 ; i < buffersCount ; i ++ )
677694 {
678695 ReadOnlyMemory < byte > rom = buffers [ i ] ;
679- await WriteAtOffsetAsync ( handle , rom , fileOffset + bytesWritten , cancellationToken ) . ConfigureAwait ( false ) ;
680- bytesWritten += rom . Length ;
696+ await WriteAtOffsetAsync ( handle , rom , fileOffset , cancellationToken ) . ConfigureAwait ( false ) ;
697+ fileOffset += rom . Length ;
681698 }
682699 }
683700
0 commit comments