Skip to content

Commit afc97df

Browse files
Address Copilot feedback
1 parent 323289a commit afc97df

5 files changed

Lines changed: 60 additions & 31 deletions

File tree

src/ImageSharp/Memory/AllocationTrackedMemoryManager{T}.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,14 @@ public abstract class AllocationTrackedMemoryManager<T> : MemoryManager<T>
3535
/// <inheritdoc />
3636
protected sealed override void Dispose(bool disposing)
3737
{
38-
this.DisposeCore(disposing);
39-
this.ReleaseAllocationTracking();
38+
try
39+
{
40+
this.DisposeCore(disposing);
41+
}
42+
finally
43+
{
44+
this.ReleaseAllocationTracking();
45+
}
4046
}
4147

4248
/// <summary>

src/ImageSharp/Memory/Allocators/MemoryAllocator.cs

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -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

src/ImageSharp/Memory/Allocators/UnmanagedMemoryAllocator.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@ protected override AllocationTrackedMemoryManager<T> AllocateCore<T>(int length,
2222
where T : struct
2323
=> AllocateBuffer<T>(length, options);
2424

25-
internal override IMemoryOwner<T> AllocateGroupBuffer<T>(int length, AllocationOptions options = AllocationOptions.None)
26-
where T : struct
27-
=> AllocateBuffer<T>(length, options);
28-
2925
// The pooled allocator uses this internal entry point when it needs a raw unmanaged owner without
3026
// nesting another allocator-level reservation cycle around the fallback allocation.
3127
internal static UnmanagedBuffer<T> AllocateBuffer<T>(int length, AllocationOptions options = AllocationOptions.None)

src/ImageSharp/Memory/InvalidMemoryOperationException.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,9 @@ internal static void ThrowInvalidAlignmentException(long alignment) =>
3939
[DoesNotReturn]
4040
internal static void ThrowAllocationOverLimitException(ulong length, long limit) =>
4141
throw new InvalidMemoryOperationException($"Attempted to allocate a buffer of length={length} that exceeded the limit {limit}.");
42+
43+
[DoesNotReturn]
44+
internal static void ThrowAccumulativeAllocationOverLimitException(long requestedLength, long totalLength, long limit) =>
45+
throw new InvalidMemoryOperationException(
46+
$"Attempted to allocate a buffer of length={requestedLength} that would increase the cumulative allocation size to {totalLength}, exceeding the limit {limit}.");
4247
}

tests/ImageSharp.Tests/Image/ProcessPixelRowsTestBase.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,15 @@ public MockUnmanagedMemoryAllocator(params UnmanagedBuffer<T1>[] buffers)
313313

314314
protected internal override int GetBufferCapacityInBytes() => int.MaxValue;
315315

316-
protected override AllocationTrackedMemoryManager<T> AllocateCore<T>(int length, AllocationOptions options = AllocationOptions.None) =>
317-
this.buffers.Pop() as AllocationTrackedMemoryManager<T>;
316+
protected override AllocationTrackedMemoryManager<T> AllocateCore<T>(int length, AllocationOptions options = AllocationOptions.None)
317+
{
318+
object buffer = this.buffers.Pop();
319+
if (buffer is AllocationTrackedMemoryManager<T> trackedBuffer)
320+
{
321+
return trackedBuffer;
322+
}
323+
324+
throw new InvalidMemoryOperationException("The requested buffer type does not match the mock allocator buffer type.");
325+
}
318326
}
319327
}

0 commit comments

Comments
 (0)