// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Buffers; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Tests.Memory.Allocators; public class SimpleGcMemoryAllocatorTests { public class BufferTests : BufferTestSuite { public BufferTests() : base(new SimpleGcMemoryAllocator()) { } } protected SimpleGcMemoryAllocator MemoryAllocator { get; } = new(); public static TheoryData InvalidLengths { get; set; } = new() { { -1 }, { (1 << 30) + 1 } }; [Theory] [MemberData(nameof(InvalidLengths))] public void Allocate_IncorrectAmount_ThrowsCorrect_InvalidMemoryOperationException(int length) => Assert.Throws( () => this.MemoryAllocator.Allocate(length)); [Fact] public unsafe void Allocate_MemoryIsPinnableMultipleTimes() { SimpleGcMemoryAllocator allocator = this.MemoryAllocator; using IMemoryOwner memoryOwner = allocator.Allocate(100); using (MemoryHandle pin = memoryOwner.Memory.Pin()) { Assert.NotEqual(IntPtr.Zero, (IntPtr)pin.Pointer); } using (MemoryHandle pin = memoryOwner.Memory.Pin()) { Assert.NotEqual(IntPtr.Zero, (IntPtr)pin.Pointer); } } [Fact] public void Allocate_AccumulativeLimit_ReleasesOnOwnerDispose() { SimpleGcMemoryAllocator allocator = new(new MemoryAllocatorOptions { AccumulativeAllocationLimitMegabytes = 1 }); const int oneMb = 1 << 20; // Reserve the full limit with a single owner. IMemoryOwner b0 = allocator.Allocate(oneMb); // Additional allocation should exceed the limit while the owner is live. Assert.Throws(() => allocator.Allocate(1)); // Disposing the owner releases the reservation. b0.Dispose(); // Allocation should succeed after the reservation is released. allocator.Allocate(oneMb).Dispose(); } [Fact] public void AllocateGroup_AccumulativeLimit_ReleasesOnGroupDispose() { SimpleGcMemoryAllocator allocator = new(new MemoryAllocatorOptions { AccumulativeAllocationLimitMegabytes = 1 }); const int oneMb = 1 << 20; // Reserve the full limit with a single group. MemoryGroup g0 = allocator.AllocateGroup(oneMb, 1024); // Additional allocation should exceed the limit while the group is live. Assert.Throws(() => allocator.AllocateGroup(1, 1024)); // Disposing the group releases the reservation. g0.Dispose(); // Allocation should succeed after the reservation is released. allocator.AllocateGroup(oneMb, 1024).Dispose(); } [StructLayout(LayoutKind.Explicit, Size = 512)] private struct BigStruct { } }