using DCFApixels.DragonECS.Internal; using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; namespace DCFApixels.DragonECS.Core.Internal { internal unsafe static class MemoryAllocator { private static IdDispenser _idDispenser; private static HandlerDebugInfo[] _debugInfos; //private static ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); static MemoryAllocator() { StaticInit(); } private static void StaticInit() { _idDispenser = new IdDispenser(); _debugInfos = new HandlerDebugInfo[32]; } #region AllocAndInit public static Handler AllocAndInit(int count) where T : unmanaged { return AllocAndInit_Internal(Marshal.SizeOf() * count, typeof(T)); } public static Handler AllocAndInit(int byteLength) { return AllocAndInit_Internal(byteLength, null); } public static Handler AllocAndInit_Internal(int byteLength, Type type) { Handler result = Alloc_Internal(byteLength, type); ClearAllocatedMemory(result.Ptr, 0, byteLength); return result; } #endregion #region Alloc public static Handler Alloc(int count) where T : unmanaged { return Alloc_Internal(Marshal.SizeOf() * count, typeof(T)); } public static Handler Alloc(int byteLength) { return Alloc_Internal(byteLength, null); } public static Handler Alloc_Internal(int byteLength, Type type) { byteLength = byteLength == 0 ? 1 : byteLength; int id = 0; lock (_idDispenser) { if (_debugInfos.Length <= _idDispenser.Count) { Array.Resize(ref _debugInfos, _debugInfos.Length << 1); } id = _idDispenser.UseFree(); } Meta* newHandledPtr = (Meta*)Marshal.AllocHGlobal(byteLength + sizeof(Meta)); newHandledPtr->ID = id; newHandledPtr->ByteLength = byteLength; Handler handler = Handler.FromHandledPtr(newHandledPtr); _debugInfos[id].stackTrace = new StackTrace(); _debugInfos[id].type = type; _debugInfos[id].handler = handler; return handler; } #endregion #region ReallocAndInit public static Handler ReallocAndInit(void* target, int oldCount, int newCount) where T : unmanaged { return ReallocAndInit(Handler.FromDataPtr(target), oldCount, newCount); } public static Handler ReallocAndInit(void* target, int oldByteLength, int newByteLength) { return ReallocAndInit(Handler.FromDataPtr(target), oldByteLength, newByteLength); } public static Handler ReallocAndInit(Handler target, int oldCount, int newCount) where T : unmanaged { var size = Marshal.SizeOf(); return ReallocAndInit_Internal(target, size * oldCount, size * newCount, typeof(T)); } public static Handler ReallocAndInit(Handler target, int oldByteLength, int newByteLength) { return ReallocAndInit_Internal(target, oldByteLength, newByteLength, null); } private static Handler ReallocAndInit_Internal(Handler target, int oldByteLength, int newByteLength, Type newType) { Handler result = Realloc_Internal(target, newByteLength, newType); ClearAllocatedMemory(result.Ptr, oldByteLength, newByteLength - oldByteLength); return result; } #endregion #region Realloc public static Handler Realloc(void* target, int newCount) where T : unmanaged { return Realloc(Handler.FromDataPtr(target), Marshal.SizeOf() * newCount); } public static Handler Realloc(void* target, int newByteLength) { return Realloc(Handler.FromDataPtr(target), newByteLength); } public static Handler Realloc(Handler target, int newCount) where T : unmanaged { return Realloc_Internal(target, Marshal.SizeOf() * newCount, typeof(T)); } public static Handler Realloc(Handler target, int newByteLength) { return Realloc_Internal(target, newByteLength, null); } private static Handler Realloc_Internal(Handler target, int newByteLength, Type newType) { newByteLength = newByteLength == 0 ? 1 : newByteLength; Meta* newHandledPtr = (Meta*)Marshal.ReAllocHGlobal((IntPtr)target.GetHandledPtr(), (IntPtr)newByteLength + sizeof(Meta)); Handler result = Handler.FromHandledPtr(newHandledPtr); _debugInfos[newHandledPtr->ID].stackTrace = new StackTrace(); _debugInfos[newHandledPtr->ID].type = newType; _debugInfos[newHandledPtr->ID].handler = result; return result; } #endregion #region Free public static void Free(ref Handler target) { Free_Internal(target.GetHandledPtr()); target = default; } public static void Free(void* dataPtr) { Free_Internal(((Meta*)dataPtr) - 1); } private static void Free_Internal(Meta* handledPtr) { lock (_idDispenser) { _idDispenser.Release(handledPtr->ID); _debugInfos[handledPtr->ID] = default; } handledPtr->ID = default; handledPtr->ByteLength = default; Marshal.FreeHGlobal((IntPtr)handledPtr); } #endregion #region Other internal static StateDebugInfo GetHandlerInfos_Debug() { StateDebugInfo result = default; result.idDispenser = _idDispenser; result.debugInfos = _debugInfos; return result; } private static unsafe void ClearAllocatedMemory(IntPtr ptr, int startByte, int lengthInBytes) { Span memorySpan = new Span((byte*)ptr + startByte, lengthInBytes); memorySpan.Clear(); } internal struct Meta { public int ID; public int ByteLength; } #if DEBUG [DebuggerDisplay("{handler.DebuggerDisplay()}")] #endif internal struct HandlerDebugInfo { public StackTrace stackTrace; public Type type; public Handler handler; } internal struct StateDebugInfo { public HandlerDebugInfo[] debugInfos; public IdDispenser idDispenser; } //private readonly struct AddLocker : IDisposable //{ // private readonly ReaderWriterLockSlim _readerWriterLockSlim; // public AddLocker(ReaderWriterLockSlim readerWriterLockSlim) // { // _readerWriterLockSlim = readerWriterLockSlim; // readerWriterLockSlim.EnterWriteLock(); // } // public void Dispose() // { // _readerWriterLockSlim.ExitWriteLock(); // } //} //private readonly struct RemoveLocker : IDisposable //{ // private readonly ReaderWriterLockSlim _readerWriterLockSlim; // public RemoveLocker(ReaderWriterLockSlim readerWriterLockSlim) // { // _readerWriterLockSlim = readerWriterLockSlim; // readerWriterLockSlim.EnterReadLock(); // } // public void Dispose() // { // _readerWriterLockSlim.ExitReadLock(); // } //} #endregion #if DEBUG [DebuggerDisplay("{DebuggerDisplay()}")] [DebuggerTypeProxy(typeof(DebuggerProxy))] #endif public readonly struct Handler { public static readonly Handler Empty = new Handler(); internal readonly Meta* Data; // Data[-1] is meta; private Handler(Meta* dataPtr) { Data = dataPtr; } public static Handler FromHandledPtr(void* ptr) { return new Handler(((Meta*)ptr) + 1); } public static Handler FromDataPtr(void* ptr) { return new Handler((Meta*)ptr); } internal Meta* GetHandledPtr() { return Data - 1; } internal int GetID() { return GetHandledPtr()->ID; } internal int GetByteLength() { return GetHandledPtr()->ByteLength; } public bool IsEmpty { get { return Data == null; } } public IntPtr Ptr { get { return (IntPtr)Data; } } public T* As() where T : unmanaged { return (T*)Ptr; } #region Debugger #if DEBUG [RequiresDynamicCode("Calls System.Runtime.InteropServices.Marshal.SizeOf(Type)")] internal unsafe string DebuggerDisplay() { if(Data == null) { return "-"; } Meta meta = GetHandledPtr()[0]; HandlerDebugInfo info = _debugInfos[meta.ID]; if (info.type == null) { return $"Count: {meta.ByteLength} Unknown"; } return $"Count: {meta.ByteLength / Marshal.SizeOf(info.type)} {info.type.Name}"; } [RequiresDynamicCode("Calls System.Runtime.InteropServices.Marshal.SizeOf(Type)")] internal static Array CreateArray_Debug(Type type, int count, byte* data, int byteLength) { var array = Array.CreateInstance(type, count); if (array.Length > 0) { Union union = default; union.array = array; fixed (byte* arrayPtr = union.bytes) { for (int i = 0; i < byteLength; i++) { arrayPtr[i] = data[i]; } } } return array; } [StructLayout(LayoutKind.Explicit)] private unsafe struct Union { [FieldOffset(0)] public Array array; [FieldOffset(0)] public byte[] bytes; } private class DebuggerProxy { private byte* _data; private Type _type; private int _count; public bool IsAlive; public Meta Meta; public HandlerDebugInfo DebugInfo; public Array Data; public HandlerDebugInfo[] OtherHandlersInfo; [RequiresDynamicCode("Calls System.Runtime.InteropServices.Marshal.SizeOf(Type)")] public unsafe DebuggerProxy(Handler handler) { IsAlive = handler.Ptr.ToPointer() != null; if (IsAlive == false) { return; } Meta = handler.GetHandledPtr()[0]; _data = (byte*)handler.Ptr; DebugInfo = _debugInfos[Meta.ID]; if (DebugInfo.type == null) { _type = typeof(byte); } else { _type = DebugInfo.type; } var size = Marshal.SizeOf(_type); _count = Meta.ByteLength / size; Data = CreateArray_Debug(_type, _count, _data, Meta.ByteLength); OtherHandlersInfo = _debugInfos; } } #endif #endregion } } internal static class MemoryAllocatorHandlerExtensions { public static void Dispose(this ref MemoryAllocator.Handler self) { MemoryAllocator.Free(ref self); } } }