@@ -102,35 +102,23 @@ private protected void ApplyOptions(MemoryAllocatorOptions options)
102102 /// <param name="length">Size of the buffer to allocate.</param>
103103 /// <param name="options">The allocation options.</param>
104104 /// <returns>A buffer of values of type <typeparamref name="T"/>.</returns>
105- /// <exception cref="ArgumentOutOfRangeException">When length is negative.</exception>
106- /// <exception cref="InvalidMemoryOperationException">When length is over the capacity of the allocator.</exception>
105+ /// <exception cref="InvalidMemoryOperationException">When length is negative or over the capacity of the allocator.</exception>
107106 public IMemoryOwner < T > Allocate < T > ( int length , AllocationOptions options = AllocationOptions . None )
108107 where T : struct
109108 {
110- if ( length < 0 )
111- {
112- InvalidMemoryOperationException . ThrowNegativeAllocationException ( length ) ;
113- }
114-
115- ulong lengthInBytes = ( ulong ) length * ( ulong ) Unsafe . SizeOf < T > ( ) ;
116- if ( lengthInBytes > ( ulong ) this . SingleBufferAllocationLimitBytes )
117- {
118- InvalidMemoryOperationException . ThrowAllocationOverLimitException ( lengthInBytes , this . SingleBufferAllocationLimitBytes ) ;
119- }
120-
121- long lengthInBytesLong = ( long ) lengthInBytes ;
122- bool shouldTrack = lengthInBytesLong != 0 ;
109+ long lengthInBytes = this . GetValidatedAllocationLengthInBytes < T > ( length ) ;
110+ bool shouldTrack = this . AccumulativeAllocationLimitBytes != long . MaxValue && lengthInBytes != 0 ;
123111 if ( shouldTrack )
124112 {
125- this . ReserveAllocation ( lengthInBytesLong ) ;
113+ this . ReserveAllocation ( lengthInBytes ) ;
126114 }
127115
128116 try
129117 {
130118 AllocationTrackedMemoryManager < T > owner = this . AllocateCore < T > ( length , options ) ;
131119 if ( shouldTrack )
132120 {
133- owner . AttachAllocationTracking ( this , lengthInBytesLong ) ;
121+ owner . AttachAllocationTracking ( this , lengthInBytes ) ;
134122 }
135123
136124 return owner ;
@@ -139,7 +127,7 @@ public IMemoryOwner<T> Allocate<T>(int length, AllocationOptions options = Alloc
139127 {
140128 if ( shouldTrack )
141129 {
142- this . ReleaseAccumulatedBytes ( lengthInBytesLong ) ;
130+ this . ReleaseAccumulatedBytes ( lengthInBytes ) ;
143131 }
144132
145133 throw ;
@@ -199,7 +187,7 @@ internal MemoryGroup<T> AllocateGroup<T>(
199187 }
200188
201189 long totalLengthInBytesLong = ( long ) totalLengthInBytes ;
202- bool shouldTrack = totalLengthInBytesLong != 0 ;
190+ bool shouldTrack = this . AccumulativeAllocationLimitBytes != long . MaxValue && totalLengthInBytesLong != 0 ;
203191 if ( shouldTrack )
204192 {
205193 this . ReserveAllocation ( totalLengthInBytesLong ) ;
@@ -238,12 +226,38 @@ internal virtual MemoryGroup<T> AllocateGroupCore<T>(long totalLengthInElements,
238226 /// <param name="options">The allocation options.</param>
239227 /// <returns>A segment owner for the requested buffer length.</returns>
240228 /// <remarks>
241- /// The default implementation uses <see cref="Allocate {T}(int, AllocationOptions)"/>. Built-in allocators
242- /// can override this to supply raw segment owners when group construction must bypass nested tracking .
229+ /// The default implementation validates the segment size then calls <see cref="AllocateCore {T}(int, AllocationOptions)"/>
230+ /// directly so group construction can reserve and release the total allocation once .
243231 /// </remarks>
244232 internal virtual IMemoryOwner < T > AllocateGroupBuffer < T > ( int length , AllocationOptions options = AllocationOptions . None )
245233 where T : struct
246- => this . AllocateCore < T > ( length , options ) ;
234+ {
235+ _ = this . GetValidatedAllocationLengthInBytes < T > ( length ) ;
236+ return this . AllocateCore < T > ( length , options ) ;
237+ }
238+
239+ /// <summary>
240+ /// Returns the validated allocation length in bytes.
241+ /// </summary>
242+ /// <typeparam name="T">Type of the data stored in the buffer.</typeparam>
243+ /// <param name="length">Size of the buffer to allocate.</param>
244+ /// <returns>The allocation length in bytes.</returns>
245+ private long GetValidatedAllocationLengthInBytes < T > ( int length )
246+ where T : struct
247+ {
248+ if ( length < 0 )
249+ {
250+ InvalidMemoryOperationException . ThrowNegativeAllocationException ( length ) ;
251+ }
252+
253+ ulong lengthInBytes = ( ulong ) length * ( ulong ) Unsafe . SizeOf < T > ( ) ;
254+ if ( lengthInBytes > ( ulong ) this . SingleBufferAllocationLimitBytes )
255+ {
256+ InvalidMemoryOperationException . ThrowAllocationOverLimitException ( lengthInBytes , this . SingleBufferAllocationLimitBytes ) ;
257+ }
258+
259+ return ( long ) lengthInBytes ;
260+ }
247261
248262 /// <summary>
249263 /// Reserves accumulative allocation bytes before creating the underlying buffer.
@@ -260,7 +274,7 @@ private void ReserveAllocation(long lengthInBytes)
260274 if ( total > this . AccumulativeAllocationLimitBytes )
261275 {
262276 _ = Interlocked . Add ( ref this . accumulativeAllocatedBytes , - lengthInBytes ) ;
263- InvalidMemoryOperationException . ThrowAllocationOverLimitException ( ( ulong ) lengthInBytes , this . AccumulativeAllocationLimitBytes ) ;
277+ InvalidMemoryOperationException . ThrowAccumulativeAllocationOverLimitException ( lengthInBytes , total , this . AccumulativeAllocationLimitBytes ) ;
264278 }
265279 }
266280
0 commit comments