using ILRuntime.Runtime.Intepreter; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ILRuntime.Runtime.Stack { unsafe struct MemoryBlockInfo { public StackObject* RequestAddress; public StackObject* StartAddress; public int Size; public int ManagedIndex; public int ManagedCount; } public unsafe struct StackObjectAllocation { public StackObject* Address; public int ManagedIndex; } public unsafe delegate void StackObjectAllocateCallback(int size, out StackObject* ptr, out int managedIdx); public unsafe class StackObjectAllocator { MemoryBlockInfo[] freeBlocks; StackObjectAllocateCallback allocCallback; public StackObjectAllocator(StackObjectAllocateCallback cb) { allocCallback = cb; freeBlocks = new MemoryBlockInfo[8]; } public void Clear() { for(int i = 0; i < freeBlocks.Length; i++) { if (freeBlocks[i].StartAddress == null) break; freeBlocks[i] = default(MemoryBlockInfo); } } void ExpandFreeList() { int expandSize = Math.Min(freeBlocks.Length, 32); MemoryBlockInfo[] newArr = new MemoryBlockInfo[freeBlocks.Length + expandSize]; freeBlocks.CopyTo(newArr, 0); freeBlocks = newArr; } void FreeBlock(int idx) { freeBlocks[idx].RequestAddress = null; var cnt = freeBlocks.Length; int freeSize = 0; int freeManaged = 0; int freeBlock = 0; for (int i = idx-1; i >= 0; i--) { if (freeBlocks[i].RequestAddress == null) { idx = i; } else break; } for (int i = idx + 1; i < cnt; i++) { if (freeBlocks[i].StartAddress == null) break; if (freeBlocks[i].RequestAddress == null) { freeSize += freeBlocks[i].Size; freeManaged += freeBlocks[i].ManagedCount; freeBlock++; } else break; } if (freeBlock > 0) { freeBlocks[idx].Size += freeSize; freeBlocks[idx].ManagedCount += freeManaged; int tail = idx + freeBlock + 1; if (tail < freeBlocks.Length) { Array.Copy(freeBlocks, tail, freeBlocks, idx + 1, cnt - tail); } for (int i = cnt - freeBlock; i < cnt; i++) { freeBlocks[i] = default(MemoryBlockInfo); } } } public void FreeBefore(StackObject* ptr) { int firstHit = -1; var cnt = freeBlocks.Length; for (int i = 0; i < cnt; i++) { if (freeBlocks[i].StartAddress == null) break; if (freeBlocks[i].StartAddress <= ptr) { freeBlocks[i] = default(MemoryBlockInfo); if (firstHit < 0) firstHit = i; } } if (firstHit >= 0) { int validIdx = 0; for (int i = firstHit; i < cnt; i++) { if (freeBlocks[i].StartAddress != null) { if (validIdx != i) { freeBlocks[validIdx++] = freeBlocks[i]; } } } } } public void Free(StackObject* ptr) { var cnt = freeBlocks.Length; for (int i = 0; i < cnt; i++) { if (freeBlocks[i].StartAddress == null) break; if (freeBlocks[i].RequestAddress == ptr) { FreeBlock(i); break; } } } public void RegisterAllocation(StackObject* ptr, StackObject* src, int size, int managedIndex, int managedCount) { int emptyIndex = -1; int cnt = freeBlocks.Length; for (int i = 0; i < cnt; i++) { if (freeBlocks[i].StartAddress == null) { emptyIndex = i; break; } } if (emptyIndex == -1) { emptyIndex = freeBlocks.Length; ExpandFreeList(); } StackObject* dst; int mIdx; allocCallback(size, out dst, out mIdx); if (dst != src) throw new NotSupportedException(); freeBlocks[emptyIndex] = new MemoryBlockInfo() { StartAddress = dst, RequestAddress = ptr, Size = size, ManagedCount = managedCount, ManagedIndex = managedIndex != int.MaxValue ? managedIndex : mIdx }; } public bool AllocExisting(StackObject* ptr, int size, int managedSize, out StackObjectAllocation alloc) { int cnt = freeBlocks.Length; for (int i = 0; i < cnt; i++) { if (freeBlocks[i].StartAddress == null) { break; } if (freeBlocks[i].RequestAddress == ptr || freeBlocks[i].RequestAddress == null) { if (freeBlocks[i].Size >= size && freeBlocks[i].ManagedCount >= managedSize) { freeBlocks[i].RequestAddress = ptr; alloc = new StackObjectAllocation() { Address = freeBlocks[i].StartAddress, ManagedIndex = freeBlocks[i].ManagedIndex }; return true; } } } alloc = new StackObjectAllocation(); return false; } public StackObjectAllocation Alloc(StackObject* ptr, int size, int managedSize) { int found = -1; int emptyIndex = -1; StackObjectAllocation alloc; int cnt = freeBlocks.Length; for (int i = 0; i < cnt; i++) { if (freeBlocks[i].StartAddress == null) { emptyIndex = i; break; } if (freeBlocks[i].RequestAddress == ptr) { if (freeBlocks[i].Size >= size && freeBlocks[i].ManagedCount >= managedSize) { return new StackObjectAllocation() { Address = freeBlocks[i].StartAddress, ManagedIndex = freeBlocks[i].ManagedIndex }; } //freeBlocks[i].RequestAddress = null; //freeIndex = i; FreeBlock(i); } } for (int i = 0; i < cnt; i++) { if (freeBlocks[i].StartAddress == null) break; if (freeBlocks[i].RequestAddress == null) { if (freeBlocks[i].Size >= size && freeBlocks[i].ManagedCount >= managedSize) { found = i; break; } } } if (found >= 0) { freeBlocks[found].RequestAddress = ptr; return new StackObjectAllocation() { Address = freeBlocks[found].StartAddress, ManagedIndex = freeBlocks[found].ManagedIndex }; } else { if (emptyIndex == -1) { emptyIndex = freeBlocks.Length; ExpandFreeList(); } allocCallback(size, out alloc.Address, out alloc.ManagedIndex); freeBlocks[emptyIndex] = new MemoryBlockInfo() { StartAddress = alloc.Address, RequestAddress = ptr, Size = size, ManagedCount = managedSize, ManagedIndex = alloc.ManagedIndex }; } return alloc; } } }