Refactor memory allocation system to use generic MemoryPool<TAllocator, TOpts> for arena, stack, and free list allocators, replacing custom allocator structs. Introduce MemoryBlock as a safer, more robust replacement for UnTypedArray. Improve thread safety, safety checks, and documentation. Reorder and clarify Allocator enum. Add comprehensive unit tests for all allocators and pointer assertion utilities. Update project to enable safety checks in Debug builds. Remove obsolete interfaces and ensure consistent deallocation with MemoryUtility.Free. BREAKING CHANGE: Custom allocator structs are removed and replaced with MemoryPool-based abstraction. UnTypedArray is replaced by MemoryBlock. Allocator enum order and semantics are changed. Public API changes may require code updates.
268 lines
7.3 KiB
C#
268 lines
7.3 KiB
C#
using Misaki.HighPerformance.LowLevel.Buffer;
|
|
|
|
namespace Misaki.HighPerformance.Test.UnitTest.Buffer;
|
|
|
|
[TestClass]
|
|
public unsafe class TestVirtualStack
|
|
{
|
|
[TestMethod]
|
|
public void VirtualStack_ScopeAllocateReallocateResetDispose_Works()
|
|
{
|
|
var stack = new VirtualStack(256 * 1024);
|
|
try
|
|
{
|
|
var handle = default(AllocationHandle);
|
|
using (stack.CreateScope(handle))
|
|
{
|
|
var p1 = (byte*)stack.Allocate(32, 8, AllocationOption.Clear);
|
|
Assert.IsNotNull(p1);
|
|
for (var i = 0; i < 32; i++)
|
|
{
|
|
Assert.AreEqual(0, p1[i]);
|
|
}
|
|
|
|
for (var i = 0; i < 32; i++)
|
|
{
|
|
p1[i] = 0x11;
|
|
}
|
|
|
|
var grown = (byte*)stack.Reallocate(p1, 32, 96, 8, AllocationOption.Clear);
|
|
Assert.AreEqual((nint)p1, (nint)grown);
|
|
for (var i = 0; i < 32; i++)
|
|
{
|
|
Assert.AreEqual(0x11, grown[i]);
|
|
}
|
|
|
|
for (var i = 32; i < 96; i++)
|
|
{
|
|
Assert.AreEqual(0, grown[i]);
|
|
}
|
|
}
|
|
|
|
Assert.AreEqual(0u, (uint)stack.Allocated);
|
|
|
|
stack.Reset();
|
|
Assert.AreEqual(0u, (uint)stack.Allocated);
|
|
}
|
|
finally
|
|
{
|
|
stack.Dispose();
|
|
}
|
|
|
|
Assert.IsTrue(stack.Buffer == null);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void VirtualStack_NestedScopes_RewindToPreviousOffsets()
|
|
{
|
|
var stack = new VirtualStack(128 * 1024);
|
|
try
|
|
{
|
|
var handle = default(AllocationHandle);
|
|
using (stack.CreateScope(handle))
|
|
{
|
|
var pOuter = stack.Allocate(32, 8, AllocationOption.None);
|
|
Assert.IsNotNull(pOuter);
|
|
var outerOffset = stack.Allocated;
|
|
|
|
using (stack.CreateScope(handle))
|
|
{
|
|
var pInner = stack.Allocate(128, 8, AllocationOption.None);
|
|
Assert.IsNotNull(pInner);
|
|
Assert.IsTrue(stack.Allocated > outerOffset);
|
|
}
|
|
|
|
Assert.AreEqual(outerOffset, stack.Allocated);
|
|
}
|
|
|
|
Assert.AreEqual(0u, (uint)stack.Allocated);
|
|
}
|
|
finally
|
|
{
|
|
stack.Dispose();
|
|
}
|
|
}
|
|
|
|
[TestMethod]
|
|
public void VirtualStack_MultipleAllocations_Works()
|
|
{
|
|
var stack = new VirtualStack(1024 * 1024);
|
|
try
|
|
{
|
|
var handle = default(AllocationHandle);
|
|
using (stack.CreateScope(handle))
|
|
{
|
|
var p1 = (byte*)stack.Allocate(32, 8, AllocationOption.Clear);
|
|
Assert.IsNotNull(p1);
|
|
for (var i = 0; i < 32; i++)
|
|
{
|
|
Assert.AreEqual(0, p1[i]);
|
|
}
|
|
|
|
for (var i = 0; i < 32; i++)
|
|
{
|
|
p1[i] = 0x42;
|
|
}
|
|
|
|
var same = (byte*)stack.Reallocate(p1, 32, 64, 8, AllocationOption.Clear);
|
|
Assert.AreEqual((nint)p1, (nint)same);
|
|
for (var i = 0; i < 32; i++)
|
|
{
|
|
Assert.AreEqual(0x42, same[i]);
|
|
}
|
|
|
|
for (var i = 32; i < 64; i++)
|
|
{
|
|
Assert.AreEqual(0, same[i]);
|
|
}
|
|
|
|
var p2 = (byte*)stack.Allocate(16, 8, AllocationOption.None);
|
|
Assert.IsNotNull(p2);
|
|
|
|
for (var i = 0; i < 64; i++)
|
|
{
|
|
same[i] = (byte)(255 - i);
|
|
}
|
|
|
|
var moved = (byte*)stack.Reallocate(same, 64, 96, 8, AllocationOption.None);
|
|
Assert.IsNotNull(moved);
|
|
for (var i = 0; i < 64; i++)
|
|
{
|
|
Assert.AreEqual((byte)(255 - i), moved[i]);
|
|
}
|
|
|
|
stack.Reset();
|
|
Assert.AreEqual(0u, (uint)stack.Allocated);
|
|
var firstAfterReset = (byte*)stack.Allocate(8, 8, AllocationOption.None);
|
|
Assert.AreEqual((nint)stack.Buffer, (nint)firstAfterReset);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
stack.Dispose();
|
|
}
|
|
|
|
Assert.IsNull(stack.Buffer);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void VirtualStack_AllocationFailsOutsideScope()
|
|
{
|
|
var stack = new VirtualStack(128 * 1024);
|
|
try
|
|
{
|
|
Assert.ThrowsExactly<InvalidOperationException>(() => stack.Allocate(32, 8, AllocationOption.Clear));
|
|
|
|
stack.Dispose();
|
|
Assert.IsTrue(stack.Buffer == null);
|
|
}
|
|
finally
|
|
{
|
|
stack.Dispose();
|
|
}
|
|
}
|
|
|
|
[TestMethod]
|
|
public void VirtualStack_InvalidAlignment_Throws()
|
|
{
|
|
var stack = new VirtualStack(128 * 1024);
|
|
try
|
|
{
|
|
using (stack.CreateScope(default))
|
|
{
|
|
Assert.ThrowsExactly<ArgumentException>(() => stack.Allocate(32, 3, AllocationOption.None));
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
stack.Dispose();
|
|
}
|
|
}
|
|
|
|
[TestMethod]
|
|
public void VirtualStack_ZeroSizeAllocation_ReturnsNull()
|
|
{
|
|
var stack = new VirtualStack(128 * 1024);
|
|
try
|
|
{
|
|
using (stack.CreateScope(default))
|
|
{
|
|
Assert.IsNull(stack.Allocate(0, 8, AllocationOption.None));
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
stack.Dispose();
|
|
}
|
|
}
|
|
|
|
[TestMethod]
|
|
public void VirtualStack_AllocationExceedsReservedCapacity_ReturnsNull()
|
|
{
|
|
var stack = new VirtualStack(1024);
|
|
try
|
|
{
|
|
using (stack.CreateScope(default))
|
|
{
|
|
Assert.IsNull(stack.Allocate(128 * 1024, 8, AllocationOption.None));
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
stack.Dispose();
|
|
}
|
|
}
|
|
|
|
[TestMethod]
|
|
public void VirtualStack_ReallocateSmallerSize_ReturnsSamePointer()
|
|
{
|
|
var stack = new VirtualStack(128 * 1024);
|
|
try
|
|
{
|
|
using (stack.CreateScope(default))
|
|
{
|
|
var p = (byte*)stack.Allocate(64, 8);
|
|
Assert.IsNotNull(p);
|
|
|
|
for (var i = 0; i < 32; i++)
|
|
{
|
|
p[i] = (byte)(i + 1);
|
|
}
|
|
|
|
var shrunk = (byte*)stack.Reallocate(p, 64, 32, 8, AllocationOption.None);
|
|
Assert.AreEqual((nint)p, (nint)shrunk);
|
|
for (var i = 0; i < 32; i++)
|
|
{
|
|
Assert.AreEqual((byte)(i + 1), shrunk[i]);
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
stack.Dispose();
|
|
}
|
|
}
|
|
|
|
[TestMethod]
|
|
public void VirtualStack_AllocationRequiresCommitment()
|
|
{
|
|
var stack = new VirtualStack(128 * 1024);
|
|
try
|
|
{
|
|
var handle = default(AllocationHandle);
|
|
using (stack.CreateScope(handle))
|
|
{
|
|
Assert.AreEqual(0u, (uint)stack.Committed);
|
|
|
|
var p = (byte*)stack.Allocate(64, 8);
|
|
Assert.IsNotNull(p);
|
|
|
|
Assert.IsTrue(stack.Committed >= 64);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
stack.Dispose();
|
|
}
|
|
}
|
|
} |