Files
Misaki.HighPerformance/Misaki.HighPerformance.Test/UnitTest/Buffer/TestStack.cs
Misaki a0deadc363 feat(memory): refactor allocation and add new queue
Refactored memory management by removing safety checks and introducing `MemoryHandle` for centralized tracking. Simplified allocation logic across allocators and enhanced `Dispose` methods for better resource cleanup.

Added `UnsafeChunkedQueue<T>`, a lock-free, dynamically resizing queue with chunk-based memory management, supporting parallel producers and consumers.

Updated unit tests to validate new queue functionality and ensure compatibility with refactored memory logic. Incremented assembly version to 1.6.12.

BREAKING CHANGE: Removed `#if MHP_ENABLE_SAFETY_CHECKS` blocks, altering memory validation behavior.
2026-04-10 14:44:48 +09:00

236 lines
6.3 KiB
C#

using Misaki.HighPerformance.LowLevel.Buffer;
namespace Misaki.HighPerformance.Test.UnitTest.Buffer;
[TestClass]
public unsafe class TestStack
{
[TestMethod]
public void Stack_ScopeAllocateReallocateResetDispose_Works()
{
var stack = new Stack(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.Offset);
stack.Reset();
Assert.AreEqual(0u, (uint)stack.Offset);
}
finally
{
stack.Dispose();
}
Assert.IsNull(stack.Buffer);
}
[TestMethod]
public void Stack_NestedScopes_RewindToPreviousOffsets()
{
var stack = new Stack(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.Offset;
using (stack.CreateScope(handle))
{
var pInner = stack.Allocate(128, 8, AllocationOption.None);
Assert.IsNotNull(pInner);
Assert.IsTrue(stack.Offset > outerOffset);
}
Assert.AreEqual(outerOffset, stack.Offset);
}
Assert.AreEqual(0u, (uint)stack.Offset);
}
finally
{
stack.Dispose();
}
}
[TestMethod]
public void Stack_MultipleAllocations_Works()
{
var stack = new Stack(1024 * 1024);
try
{
using (stack.CreateScope(default))
{
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.Offset);
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 Stack_InvalidAlignment_Throws()
{
var stack = new Stack(128 * 1024);
try
{
using (stack.CreateScope(default))
{
Assert.ThrowsExactly<ArgumentException>(() => stack.Allocate(32, 3, AllocationOption.None));
}
}
finally
{
stack.Dispose();
}
}
[TestMethod]
public void Stack_ZeroSizeAllocation_ReturnsNull()
{
var stack = new Stack(128 * 1024);
try
{
using (stack.CreateScope(default))
{
Assert.IsNull(stack.Allocate(0, 8, AllocationOption.None));
}
}
finally
{
stack.Dispose();
}
}
[TestMethod]
public void Stack_AllocationExceedsCapacity_Throws()
{
var stack = new Stack(1024);
try
{
using (stack.CreateScope(default))
{
Assert.ThrowsExactly<OutOfMemoryException>(() => stack.Allocate(2048, 8, AllocationOption.None));
}
}
finally
{
stack.Dispose();
}
}
[TestMethod]
public void Stack_ReallocateSmallerSize_ReturnsSamePointer()
{
var stack = new Stack(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 Stack_DoubleDispose_IsSafe()
{
var stack = new Stack(128 * 1024);
stack.Dispose();
stack.Dispose(); // Should not throw
}
}