3
3
4
4
using System ;
5
5
using System . Buffers ;
6
+ using System . Collections . Generic ;
7
+ using System . Runtime . CompilerServices ;
6
8
7
9
namespace SixLabors . ImageSharp . Memory
8
10
{
@@ -11,6 +13,8 @@ namespace SixLabors.ImageSharp.Memory
11
13
/// </summary>
12
14
public abstract class MemoryAllocator
13
15
{
16
+ private const int OneGigabyte = 1 << 30 ;
17
+
14
18
/// <summary>
15
19
/// Gets the default platform-specific global <see cref="MemoryAllocator"/> instance that
16
20
/// serves as the default value for <see cref="Configuration.MemoryAllocator"/>.
@@ -21,6 +25,10 @@ public abstract class MemoryAllocator
21
25
/// </summary>
22
26
public static MemoryAllocator Default { get ; } = Create ( ) ;
23
27
28
+ internal long MemoryGroupAllocationLimitBytes { get ; private set ; } = Environment . Is64BitProcess ? 4L * OneGigabyte : OneGigabyte ;
29
+
30
+ internal int SingleBufferAllocationLimitBytes { get ; private set ; } = OneGigabyte ;
31
+
24
32
/// <summary>
25
33
/// Gets the length of the largest contiguous buffer that can be handled by this allocator instance in bytes.
26
34
/// </summary>
@@ -31,16 +39,24 @@ public abstract class MemoryAllocator
31
39
/// Creates a default instance of a <see cref="MemoryAllocator"/> optimized for the executing platform.
32
40
/// </summary>
33
41
/// <returns>The <see cref="MemoryAllocator"/>.</returns>
34
- public static MemoryAllocator Create ( ) =>
35
- new UniformUnmanagedMemoryPoolMemoryAllocator ( null ) ;
42
+ public static MemoryAllocator Create ( ) => Create ( default ) ;
36
43
37
44
/// <summary>
38
45
/// Creates the default <see cref="MemoryAllocator"/> using the provided options.
39
46
/// </summary>
40
47
/// <param name="options">The <see cref="MemoryAllocatorOptions"/>.</param>
41
48
/// <returns>The <see cref="MemoryAllocator"/>.</returns>
42
- public static MemoryAllocator Create ( MemoryAllocatorOptions options ) =>
43
- new UniformUnmanagedMemoryPoolMemoryAllocator ( options . MaximumPoolSizeMegabytes ) ;
49
+ public static MemoryAllocator Create ( MemoryAllocatorOptions options )
50
+ {
51
+ UniformUnmanagedMemoryPoolMemoryAllocator allocator = new ( options . MaximumPoolSizeMegabytes ) ;
52
+ if ( options . AllocationLimitMegabytes . HasValue )
53
+ {
54
+ allocator . MemoryGroupAllocationLimitBytes = options . AllocationLimitMegabytes . Value * 1024 * 1024 ;
55
+ allocator . SingleBufferAllocationLimitBytes = ( int ) Math . Min ( allocator . SingleBufferAllocationLimitBytes , allocator . MemoryGroupAllocationLimitBytes ) ;
56
+ }
57
+
58
+ return allocator ;
59
+ }
44
60
45
61
/// <summary>
46
62
/// Allocates an <see cref="IMemoryOwner{T}" />, holding a <see cref="Memory{T}"/> of length <paramref name="length"/>.
@@ -65,16 +81,35 @@ public virtual void ReleaseRetainedResources()
65
81
/// <summary>
66
82
/// Allocates a <see cref="MemoryGroup{T}"/>.
67
83
/// </summary>
84
+ /// <typeparam name="T">The type of element to allocate.</typeparam>
68
85
/// <param name="totalLength">The total length of the buffer.</param>
69
86
/// <param name="bufferAlignment">The expected alignment (eg. to make sure image rows fit into single buffers).</param>
70
87
/// <param name="options">The <see cref="AllocationOptions"/>.</param>
71
88
/// <returns>A new <see cref="MemoryGroup{T}"/>.</returns>
72
89
/// <exception cref="InvalidMemoryOperationException">Thrown when 'blockAlignment' converted to bytes is greater than the buffer capacity of the allocator.</exception>
73
- internal virtual MemoryGroup < T > AllocateGroup < T > (
90
+ internal MemoryGroup < T > AllocateGroup < T > (
74
91
long totalLength ,
75
92
int bufferAlignment ,
76
93
AllocationOptions options = AllocationOptions . None )
77
94
where T : struct
78
- => MemoryGroup < T > . Allocate ( this , totalLength , bufferAlignment , options ) ;
95
+ {
96
+ if ( totalLength < 0 )
97
+ {
98
+ throw new InvalidMemoryOperationException ( $ "Attempted to allocate a buffer of negative length={ totalLength } .") ;
99
+ }
100
+
101
+ ulong totalLengthInBytes = ( ulong ) totalLength * ( ulong ) Unsafe . SizeOf < T > ( ) ;
102
+ if ( totalLengthInBytes > ( ulong ) this . MemoryGroupAllocationLimitBytes )
103
+ {
104
+ throw new InvalidMemoryOperationException ( $ "Attempted to allocate a buffer of length={ totalLengthInBytes } that exceeded the limit { this . MemoryGroupAllocationLimitBytes } .") ;
105
+ }
106
+
107
+ // Cast to long is safe because we already checked that the total length is within the limit.
108
+ return this . AllocateGroupCore < T > ( totalLength , ( long ) totalLengthInBytes , bufferAlignment , options ) ;
109
+ }
110
+
111
+ internal virtual MemoryGroup < T > AllocateGroupCore < T > ( long totalLengthInElements , long totalLengthInBytes , int bufferAlignment , AllocationOptions options )
112
+ where T : struct
113
+ => MemoryGroup < T > . Allocate ( this , totalLengthInElements , bufferAlignment , options ) ;
79
114
}
80
115
}
0 commit comments