using System.Runtime.InteropServices; namespace Misaki.HighPerformance.LowLevel.Buffer; /// /// A dynamic memory management structure that automatically grows by creating linked arenas /// when more space is needed. /// [StructLayout(LayoutKind.Explicit, Size = 128)] public unsafe struct DynamicArena : IDisposable { [StructLayout(LayoutKind.Sequential)] private struct ArenaNode { public Arena arena; public ArenaNode* next; } [FieldOffset(0)] private ArenaNode* _root; [FieldOffset(8)] private ArenaNode* _current; [FieldOffset(16)] private uint _initialSize; [FieldOffset(20)] private volatile int _nodeCreationLock; /// /// Initializes a new instance of DynamicArena with the specified initial size. /// /// The initial size in bytes for the first arena block. public DynamicArena(uint initialSize) { Initialize(initialSize); } public void Initialize(uint initialSize) { if (_root != null) { return; } _initialSize = initialSize; _root = (ArenaNode*)Malloc(SizeOf()); _root->arena = new Arena(initialSize); _root->next = null; _current = _root; _nodeCreationLock = 0; } private bool TryCreateNewNode(nuint size) { while (Interlocked.CompareExchange(ref _nodeCreationLock, 1, 0) != 0) { Thread.SpinWait(1); } try { var current = _current; if (current->next != null) { // Another thread created a node while we were waiting _current = current->next; return true; } var newNode = (ArenaNode*)Malloc(SizeOf()); try { newNode->arena = new Arena(size); newNode->next = null; // Atomically link the new node current->next = newNode; // Update current pointer _current = newNode; return true; } catch { Free(newNode); return false; } } finally { // Release the spinlock Interlocked.Exchange(ref _nodeCreationLock, 0); } } /// /// Allocates a block of memory with specified size and alignment. Creates a new arena if current one is full. /// /// Size of the memory block to allocate in bytes. /// Alignment requirement for the memory block. /// Pointer to the allocated memory block. /// Thrown if the arena has been disposed. public void* Allocate(nuint size, nuint alignment, AllocationOption allocationOption) { if (_root == null) { return null; } void* result = null; var current = _current; while (current != null) { result = current->arena.Allocate(size, alignment, allocationOption); if (result != null) { return result; } if (current->next == null && !TryCreateNewNode(Math.Max(size, _initialSize))) { return null; } current = current->next; } _current = current; return result; } /// /// Resets all arenas in the chain, optionally clearing their memory. /// /// If true, memory will be cleared during reset. /// Thrown if the arena has been disposed. public void Reset() { var current = _root; while (current != null) { current->arena.Reset(); current = current->next; } _current = _root; } public void Dispose() { if (_root == null) { return; } var current = _root; while (current != null) { var next = current->next; current->arena.Dispose(); Free(current); current = next; } _root = null; _current = null; } }