22// Licensed under the Six Labors Split License.
33
44using System . Buffers ;
5+ using System . Runtime . CompilerServices ;
56
67namespace SixLabors . ImageSharp . Memory ;
78
@@ -10,6 +11,8 @@ namespace SixLabors.ImageSharp.Memory;
1011/// </summary>
1112public abstract class MemoryAllocator
1213{
14+ private const int OneGigabyte = 1 << 30 ;
15+
1316 /// <summary>
1417 /// Gets the default platform-specific global <see cref="MemoryAllocator"/> instance that
1518 /// serves as the default value for <see cref="Configuration.MemoryAllocator"/>.
@@ -20,6 +23,10 @@ public abstract class MemoryAllocator
2023 /// </summary>
2124 public static MemoryAllocator Default { get ; } = Create ( ) ;
2225
26+ internal long MemoryGroupAllocationLimitBytes { get ; private set ; } = Environment . Is64BitProcess ? 4L * OneGigabyte : OneGigabyte ;
27+
28+ internal int SingleBufferAllocationLimitBytes { get ; private set ; } = OneGigabyte ;
29+
2330 /// <summary>
2431 /// Gets the length of the largest contiguous buffer that can be handled by this allocator instance in bytes.
2532 /// </summary>
@@ -30,16 +37,24 @@ public abstract class MemoryAllocator
3037 /// Creates a default instance of a <see cref="MemoryAllocator"/> optimized for the executing platform.
3138 /// </summary>
3239 /// <returns>The <see cref="MemoryAllocator"/>.</returns>
33- public static MemoryAllocator Create ( ) =>
34- new UniformUnmanagedMemoryPoolMemoryAllocator ( null ) ;
40+ public static MemoryAllocator Create ( ) => Create ( default ) ;
3541
3642 /// <summary>
3743 /// Creates the default <see cref="MemoryAllocator"/> using the provided options.
3844 /// </summary>
3945 /// <param name="options">The <see cref="MemoryAllocatorOptions"/>.</param>
4046 /// <returns>The <see cref="MemoryAllocator"/>.</returns>
41- public static MemoryAllocator Create ( MemoryAllocatorOptions options ) =>
42- new UniformUnmanagedMemoryPoolMemoryAllocator ( options . MaximumPoolSizeMegabytes ) ;
47+ public static MemoryAllocator Create ( MemoryAllocatorOptions options )
48+ {
49+ UniformUnmanagedMemoryPoolMemoryAllocator allocator = new ( options . MaximumPoolSizeMegabytes ) ;
50+ if ( options . AllocationLimitMegabytes . HasValue )
51+ {
52+ allocator . MemoryGroupAllocationLimitBytes = options . AllocationLimitMegabytes . Value * 1024 * 1024 ;
53+ allocator . SingleBufferAllocationLimitBytes = ( int ) Math . Min ( allocator . SingleBufferAllocationLimitBytes , allocator . MemoryGroupAllocationLimitBytes ) ;
54+ }
55+
56+ return allocator ;
57+ }
4358
4459 /// <summary>
4560 /// Allocates an <see cref="IMemoryOwner{T}" />, holding a <see cref="Memory{T}"/> of length <paramref name="length"/>.
@@ -64,15 +79,34 @@ public virtual void ReleaseRetainedResources()
6479 /// <summary>
6580 /// Allocates a <see cref="MemoryGroup{T}"/>.
6681 /// </summary>
82+ /// <typeparam name="T">The type of element to allocate.</typeparam>
6783 /// <param name="totalLength">The total length of the buffer.</param>
6884 /// <param name="bufferAlignment">The expected alignment (eg. to make sure image rows fit into single buffers).</param>
6985 /// <param name="options">The <see cref="AllocationOptions"/>.</param>
7086 /// <returns>A new <see cref="MemoryGroup{T}"/>.</returns>
7187 /// <exception cref="InvalidMemoryOperationException">Thrown when 'blockAlignment' converted to bytes is greater than the buffer capacity of the allocator.</exception>
72- internal virtual MemoryGroup < T > AllocateGroup < T > (
88+ internal MemoryGroup < T > AllocateGroup < T > (
7389 long totalLength ,
7490 int bufferAlignment ,
7591 AllocationOptions options = AllocationOptions . None )
7692 where T : struct
77- => MemoryGroup < T > . Allocate ( this , totalLength , bufferAlignment , options ) ;
93+ {
94+ if ( totalLength < 0 )
95+ {
96+ InvalidMemoryOperationException . ThrowNegativeAllocationException ( totalLength ) ;
97+ }
98+
99+ ulong totalLengthInBytes = ( ulong ) totalLength * ( ulong ) Unsafe . SizeOf < T > ( ) ;
100+ if ( totalLengthInBytes > ( ulong ) this . MemoryGroupAllocationLimitBytes )
101+ {
102+ InvalidMemoryOperationException . ThrowAllocationOverLimitException ( totalLengthInBytes , this . MemoryGroupAllocationLimitBytes ) ;
103+ }
104+
105+ // Cast to long is safe because we already checked that the total length is within the limit.
106+ return this . AllocateGroupCore < T > ( totalLength , ( long ) totalLengthInBytes , bufferAlignment , options ) ;
107+ }
108+
109+ internal virtual MemoryGroup < T > AllocateGroupCore < T > ( long totalLengthInElements , long totalLengthInBytes , int bufferAlignment , AllocationOptions options )
110+ where T : struct
111+ => MemoryGroup < T > . Allocate ( this , totalLengthInElements , bufferAlignment , options ) ;
78112}
0 commit comments