@@ -84,8 +84,16 @@ public static GCMemoryInfo GetGCMemoryInfo()
84
84
[ DllImport ( RuntimeHelpers . QCall , CharSet = CharSet . Unicode ) ]
85
85
internal static extern int _EndNoGCRegion ( ) ;
86
86
87
+ // keep in sync with GC_ALLOC_FLAGS in gcinterface.h
88
+ internal enum GC_ALLOC_FLAGS
89
+ {
90
+ GC_ALLOC_NO_FLAGS = 0 ,
91
+ GC_ALLOC_ZEROING_OPTIONAL = 16 ,
92
+ GC_ALLOC_PINNED_OBJECT_HEAP = 64 ,
93
+ } ;
94
+
87
95
[ MethodImpl ( MethodImplOptions . InternalCall ) ]
88
- internal static extern Array AllocateNewArray ( IntPtr typeHandle , int length , bool zeroingOptional ) ;
96
+ internal static extern Array AllocateNewArray ( IntPtr typeHandle , int length , GC_ALLOC_FLAGS flags ) ;
89
97
90
98
[ MethodImpl ( MethodImplOptions . InternalCall ) ]
91
99
private static extern int GetGenerationWR ( IntPtr handle ) ;
@@ -651,31 +659,74 @@ internal static void UnregisterMemoryLoadChangeNotification(Action notification)
651
659
}
652
660
653
661
/// <summary>
654
- /// Skips zero-initialization of the array if possible.
655
- /// If T contains object references, the array is always zero-initialized.
662
+ /// Allocate an array while skipping zero-initialization if possible.
656
663
/// </summary>
664
+ /// <typeparam name="T">Specifies the type of the array element.</typeparam>
665
+ /// <param name="length">Specifies the length of the array.</param>
666
+ /// <param name="pinned">Specifies whether the allocated array must be pinned.</param>
667
+ /// <remarks>
668
+ /// If pinned is set to true, <typeparamref name="T"/> must not be a reference type or a type that contains object references.
669
+ /// </remarks>
657
670
[ MethodImpl ( MethodImplOptions . AggressiveInlining ) ] // forced to ensure no perf drop for small memory buffers (hot path)
658
- internal static T [ ] AllocateUninitializedArray < T > ( int length )
671
+ public static T [ ] AllocateUninitializedArray < T > ( int length , bool pinned = false )
659
672
{
660
- if ( RuntimeHelpers . IsReferenceOrContainsReferences < T > ( ) )
673
+ if ( ! pinned )
661
674
{
662
- return new T [ length ] ;
663
- }
675
+ if ( RuntimeHelpers . IsReferenceOrContainsReferences < T > ( ) )
676
+ {
677
+ return new T [ length ] ;
678
+ }
664
679
665
- // for debug builds we always want to call AllocateNewArray to detect AllocateNewArray bugs
680
+ // for debug builds we always want to call AllocateNewArray to detect AllocateNewArray bugs
666
681
#if ! DEBUG
667
- // small arrays are allocated using `new[]` as that is generally faster.
668
- if ( length < 2048 / Unsafe . SizeOf < T > ( ) )
682
+ // small arrays are allocated using `new[]` as that is generally faster.
683
+ if ( length < 2048 / Unsafe . SizeOf < T > ( ) )
684
+ {
685
+ return new T [ length ] ;
686
+ }
687
+ #endif
688
+ }
689
+ else if ( RuntimeHelpers . IsReferenceOrContainsReferences < T > ( ) )
669
690
{
670
- return new T [ length ] ;
691
+ ThrowHelper . ThrowInvalidTypeWithPointersNotSupported ( typeof ( T ) ) ;
671
692
}
672
- #endif
693
+
673
694
// kept outside of the small arrays hot path to have inlining without big size growth
674
- return AllocateNewUninitializedArray ( length ) ;
695
+ return AllocateNewUninitializedArray ( length , pinned ) ;
675
696
676
697
// remove the local function when https://github.com/dotnet/coreclr/issues/5329 is implemented
677
- static T [ ] AllocateNewUninitializedArray ( int length )
678
- => Unsafe . As < T [ ] > ( AllocateNewArray ( typeof ( T [ ] ) . TypeHandle . Value , length , zeroingOptional : true ) ) ;
698
+ static T [ ] AllocateNewUninitializedArray ( int length , bool pinned )
699
+ {
700
+ GC_ALLOC_FLAGS flags = GC_ALLOC_FLAGS . GC_ALLOC_ZEROING_OPTIONAL ;
701
+ if ( pinned )
702
+ flags |= GC_ALLOC_FLAGS . GC_ALLOC_PINNED_OBJECT_HEAP ;
703
+
704
+ return Unsafe . As < T [ ] > ( AllocateNewArray ( typeof ( T [ ] ) . TypeHandle . Value , length , flags ) ) ;
705
+ }
706
+ }
707
+
708
+ /// <summary>
709
+ /// Allocate an array.
710
+ /// </summary>
711
+ /// <typeparam name="T">Specifies the type of the array element.</typeparam>
712
+ /// <param name="length">Specifies the length of the array.</param>
713
+ /// <param name="pinned">Specifies whether the allocated array must be pinned.</param>
714
+ /// <remarks>
715
+ /// If pinned is set to true, <typeparamref name="T"/> must not be a reference type or a type that contains object references.
716
+ /// </remarks>
717
+ public static T [ ] AllocateArray < T > ( int length , bool pinned = false )
718
+ {
719
+ GC_ALLOC_FLAGS flags = GC_ALLOC_FLAGS . GC_ALLOC_NO_FLAGS ;
720
+
721
+ if ( pinned )
722
+ {
723
+ if ( RuntimeHelpers . IsReferenceOrContainsReferences < T > ( ) )
724
+ ThrowHelper . ThrowInvalidTypeWithPointersNotSupported ( typeof ( T ) ) ;
725
+
726
+ flags = GC_ALLOC_FLAGS . GC_ALLOC_PINNED_OBJECT_HEAP ;
727
+ }
728
+
729
+ return Unsafe . As < T [ ] > ( AllocateNewArray ( typeof ( T [ ] ) . TypeHandle . Value , length , flags ) ) ;
679
730
}
680
731
}
681
732
}
0 commit comments