From 3dde27a1e5001d31d0942ff24526060577c9addc Mon Sep 17 00:00:00 2001 From: DCFApixels <99481254+DCFApixels@users.noreply.github.com> Date: Sat, 17 May 2025 14:55:31 +0800 Subject: [PATCH] add MemoryAllocator/fix leaks --- src/Collections/EcsGroup.cs | 130 +++++++++---- src/EcsMask.cs | 21 +- src/EcsWorld.cs | 4 +- src/Internal/ArrayUtility.cs | 71 ++++--- src/Internal/IdDispenser.cs | 1 + src/Internal/MemoryAllocator.cs | 333 ++++++++++++++++++++++++++++++++ 6 files changed, 481 insertions(+), 79 deletions(-) create mode 100644 src/Internal/MemoryAllocator.cs diff --git a/src/Collections/EcsGroup.cs b/src/Collections/EcsGroup.cs index 150cc47..6a53a65 100644 --- a/src/Collections/EcsGroup.cs +++ b/src/Collections/EcsGroup.cs @@ -1,6 +1,7 @@ #if DISABLE_DEBUG #undef DEBUG #endif +using DCFApixels.DragonECS.Core.Internal; using DCFApixels.DragonECS.Core.Unchecked; using DCFApixels.DragonECS.Internal; using System; @@ -188,7 +189,7 @@ namespace DCFApixels.DragonECS private List> _groups = new List>(); private Stack _groupsPool = new Stack(64); - private int*[] _groupSparsePagePool = new int*[64]; + private MemoryAllocator.Handler[] _groupSparsePagePool = new MemoryAllocator.Handler[64]; private int _groupSparsePagePoolCount = 0; #region Pages @@ -196,35 +197,44 @@ namespace DCFApixels.DragonECS { if (_groupSparsePagePoolCount <= 0) { - var newPage = UnmanagedArrayUtility.NewAndInit(EcsGroup.PAGE_SIZE); - return newPage; + return MemoryAllocator.AllocAndInit(EcsGroup.PAGE_SIZE).As(); } var takedPage = _groupSparsePagePool[--_groupSparsePagePoolCount]; - _groupSparsePagePool[_groupSparsePagePoolCount] = null; - return takedPage; + _groupSparsePagePool[_groupSparsePagePoolCount] = MemoryAllocator.Handler.Empty; + return takedPage.As(); } internal void ReturnPage(int* page) { +#if DEBUG && DRAGONECS_DEEP_DEBUG + var h = MemoryAllocator.Handler.FromDataPtr(page); + if (h.GetID() == 0 || page == null) + { + Throw.DeepDebugException(); + } +#endif + if (_groupSparsePagePoolCount >= _groupSparsePagePool.Length) { var old = _groupSparsePagePool; - _groupSparsePagePool = new int*[_groupSparsePagePoolCount << 1]; + _groupSparsePagePool = new MemoryAllocator.Handler[_groupSparsePagePoolCount << 1]; for (int j = 0; j < old.Length; j++) { _groupSparsePagePool[j] = old[j]; } } - _groupSparsePagePool[_groupSparsePagePoolCount++] = page; + _groupSparsePagePool[_groupSparsePagePoolCount++] = MemoryAllocator.Handler.FromDataPtr(page); } - private void DisposeGroupPages() + private void DisposeGroups() { - foreach (var page in _groupSparsePagePool) + for (int i = 0; i < _groupSparsePagePoolCount; i++) { - if (page != null) + ref var page = ref _groupSparsePagePool[i]; + if (page.IsEmpty == false) { - UnmanagedArrayUtility.Free(page); + MemoryAllocator.Free(ref page); } } + _groupSparsePagePoolCount = 0; } #endregion @@ -277,12 +287,13 @@ namespace DCFApixels.DragonECS private EcsWorld _source; private int[] _dense; // 0 индекс для нулевой записи private PageSlot* _sparsePages; //Старший бит занят временной маркировкой в операциях над множествами + private MemoryAllocator.Handler _sparsePagesHandler; //Старший бит занят временной маркировкой в операциях над множествами private int _sparsePagesCount; private int _totalCapacity; private int _count = 0; internal bool _isReleased = true; - internal static readonly int* _nullPage = UnmanagedArrayUtility.NewAndInit(PageSlot.SIZE); + internal static readonly int* _nullPage = MemoryAllocator.AllocAndInit(PageSlot.SIZE).As(); internal static readonly long _nullPagePtrFake = (long)_nullPage; #region Properties @@ -362,12 +373,31 @@ namespace DCFApixels.DragonECS _dense = new int[denseCapacity]; _totalCapacity = world.Capacity; _sparsePagesCount = CalcSparseSize(_totalCapacity); - _sparsePages = UnmanagedArrayUtility.New(_sparsePagesCount); + _sparsePagesHandler = MemoryAllocator.Alloc(_sparsePagesCount); + _sparsePages = _sparsePagesHandler.As(); for (int i = 0; i < _sparsePagesCount; i++) { _sparsePages[i] = PageSlot.Empty; } } + ~EcsGroup() + { + lock (this) + { + for (int i = 0; i < _sparsePagesCount; i++) + { + ref PageSlot page = ref _sparsePages[i]; + if (page.Indexes != _nullPage) + { + MemoryAllocator.Free(page.Indexes); + page = default; + page.Indexes = _nullPage; + } + page.IndexesXOR = 0; + page.Count = 0; + } + } + } public void Dispose() { _source.ReleaseGroup(this); @@ -518,24 +548,27 @@ namespace DCFApixels.DragonECS public void Clear() { if (_count == 0) { return; } - for (int i = 0; i < _sparsePagesCount; i++) - { - ref PageSlot page = ref _sparsePages[i]; - if (page.Indexes != _nullPage) - { - //TODO тут надо оптимизировать отчисткой не всего а по dense списку - for (int j = 0; j < PageSlot.SIZE; j++) - { - page.Indexes[j] = 0; - } - _source.ReturnPage(page.Indexes); - page.Indexes = _nullPage; - } - page.IndexesXOR = 0; - page.Count = 0; - } - _count = 0; + if (_source.IsDestroyed == false) + { + for (int i = 0; i < _sparsePagesCount; i++) + { + ref PageSlot page = ref _sparsePages[i]; + if (page.Indexes != _nullPage) + { + //TODO тут надо оптимизировать отчисткой не всего а по dense списку + for (int j = 0; j < PageSlot.SIZE; j++) + { + page.Indexes[j] = 0; + } + _source.ReturnPage(page.Indexes); + page.Indexes = _nullPage; + } + page.IndexesXOR = 0; + page.Count = 0; + } + _count = 0; + } } #endregion @@ -1550,7 +1583,9 @@ namespace DCFApixels.DragonECS _totalCapacity = newSize; var oldPagesCount = _sparsePagesCount; _sparsePagesCount = CalcSparseSize(_totalCapacity); - _sparsePages = UnmanagedArrayUtility.Resize(_sparsePages, _sparsePagesCount); + _sparsePagesHandler = MemoryAllocator.Realloc(_sparsePagesHandler, _sparsePagesCount); + _sparsePages = _sparsePagesHandler.As(); + //_sparsePages = UnmanagedArrayUtility.Resize(_sparsePages, _sparsePagesCount); for (int i = oldPagesCount; i < _sparsePagesCount; i++) { _sparsePages[i] = PageSlot.Empty; @@ -1634,24 +1669,35 @@ namespace DCFApixels.DragonECS private PageSlot _page; public int[] Indexes; public IntPtr IndexesPtr; - public bool IsNullPage - { - get { return IndexesPtr == (IntPtr)_nullPagePtrFake; } - } + public bool IsNullPage; public int IndexesXOR; public sbyte Count; public DebuggerProxy(PageSlot page) { - _page = page; - Indexes = new int[SIZE]; - for (int i = 0; i < SIZE; i++) + //if (page.Indexes == null) { return; } + //try { - Indexes[i] = page.Indexes[i]; + _page = page; + Indexes = new int[SIZE]; + for (int i = 0; i < SIZE; i++) + { + Indexes[i] = page.Indexes[i]; + } + IndexesPtr = (IntPtr)page.Indexes; + IndexesXOR = page.IndexesXOR; + Count = page.Count; + IsNullPage = IndexesPtr == (IntPtr)_nullPagePtrFake; } - IndexesPtr = (IntPtr)page.Indexes; - IndexesXOR = page.IndexesXOR; - Count = page.Count; + //catch (Exception) + //{ + // _page = default; + // Indexes = null; + // IndexesPtr = default; + // IndexesXOR = default; + // Count = default; + // IsNullPage = default; + //} } } } diff --git a/src/EcsMask.cs b/src/EcsMask.cs index 4579813..d52e027 100644 --- a/src/EcsMask.cs +++ b/src/EcsMask.cs @@ -2,6 +2,7 @@ #undef DEBUG #endif using DCFApixels.DragonECS.Core; +using DCFApixels.DragonECS.Core.Internal; using DCFApixels.DragonECS.Internal; using System; using System.Collections.Generic; @@ -505,6 +506,9 @@ namespace DCFApixels.DragonECS /// slised _sortIncChunckBuffer private readonly UnsafeArray _sortExcChunckBuffer; + private MemoryAllocator.Handler _bufferHandler; + private MemoryAllocator.Handler _chunckBufferHandler; + private readonly bool _isSingleIncPoolWithEntityStorage; private readonly bool _isHasAnyEntityStorage; private readonly MaskType _maskType; @@ -522,14 +526,21 @@ namespace DCFApixels.DragonECS World = source; Mask = mask; - var sortBuffer = new UnsafeArray(mask._incs.Length + mask._excs.Length); - var sortChunckBuffer = new UnsafeArray(mask._incChunckMasks.Length + mask._excChunckMasks.Length); + int bufferLength = mask._incs.Length + mask._excs.Length; + int chunckBufferLength = mask._incChunckMasks.Length + mask._excChunckMasks.Length; + _bufferHandler = MemoryAllocator.AllocAndInit(bufferLength); + _chunckBufferHandler = MemoryAllocator.AllocAndInit(chunckBufferLength); + var sortBuffer = UnsafeArray.Manual(_bufferHandler.As(), bufferLength); + var sortChunckBuffer = UnsafeArray.Manual(_chunckBufferHandler.As(), chunckBufferLength); _sortIncBuffer = sortBuffer.Slice(0, mask._incs.Length); _sortIncBuffer.CopyFromArray_Unchecked(mask._incs); _sortExcBuffer = sortBuffer.Slice(mask._incs.Length, mask._excs.Length); _sortExcBuffer.CopyFromArray_Unchecked(mask._excs); + //EcsDebug.PrintError(_sortIncBuffer.ToArray()); + //EcsDebug.PrintError(new Span(_bufferHandler.GetPtrAs(), _sortIncBuffer.Length).ToArray()); + _sortIncChunckBuffer = sortChunckBuffer.Slice(0, mask._incChunckMasks.Length); _sortIncChunckBuffer.CopyFromArray_Unchecked(mask._incChunckMasks); _sortExcChunckBuffer = sortChunckBuffer.Slice(mask._incChunckMasks.Length, mask._excChunckMasks.Length); @@ -565,8 +576,10 @@ namespace DCFApixels.DragonECS } private void Cleanup(bool disposing) { - _sortIncBuffer.ReadonlyDispose(); // использует общую памяять с _sortExcBuffer; - _sortIncChunckBuffer.ReadonlyDispose(); // использует общую памяять с _sortExcChunckBuffer; + _bufferHandler.Dispose(); + _chunckBufferHandler.Dispose(); + //_sortIncBuffer.ReadonlyDispose(); // использует общую памяять с _sortExcBuffer; + //_sortIncChunckBuffer.ReadonlyDispose(); // использует общую памяять с _sortExcChunckBuffer; } #endregion diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 025f6a3..a06ded2 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -226,6 +226,7 @@ namespace DCFApixels.DragonECS #endif return; } + _isDestroyed = true; _listeners.InvokeOnWorldDestroy(); _entityDispenser = null; _pools = null; @@ -233,10 +234,9 @@ namespace DCFApixels.DragonECS _worlds[ID] = null; ReleaseData(ID); _worldIdDispenser.Release(ID); - _isDestroyed = true; _poolTypeCode_2_CmpTypeIDs = null; _cmpTypeCode_2_CmpTypeIDs = null; - DisposeGroupPages(); + DisposeGroups(); foreach (var item in _executorCoures) { diff --git a/src/Internal/ArrayUtility.cs b/src/Internal/ArrayUtility.cs index c761f77..a0cfa68 100644 --- a/src/Internal/ArrayUtility.cs +++ b/src/Internal/ArrayUtility.cs @@ -1,6 +1,7 @@ #if DISABLE_DEBUG #undef DEBUG #endif +using DCFApixels.DragonECS.Core.Internal; using System; using System.Collections; using System.Collections.Generic; @@ -275,50 +276,56 @@ namespace DCFApixels.DragonECS.Internal public static T* New(int capacity) where T : unmanaged { //Console.WriteLine($"{typeof(T).Name} - {Marshal.SizeOf()} - {capacity} - {Marshal.SizeOf() * capacity}"); - return (T*)Marshal.AllocHGlobal(Marshal.SizeOf() * capacity).ToPointer(); + //return (T*)Marshal.AllocHGlobal(Marshal.SizeOf() * capacity).ToPointer(); + return MemoryAllocator.Alloc(capacity).As(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void New(out T* ptr, int capacity) where T : unmanaged { - ptr = (T*)Marshal.AllocHGlobal(Marshal.SizeOf() * capacity).ToPointer(); + //ptr = (T*)Marshal.AllocHGlobal(Marshal.SizeOf() * capacity).ToPointer(); + ptr = MemoryAllocator.Alloc(capacity).As(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T* NewAndInit(int capacity) where T : unmanaged { - int newSize = MetaCache.Size * capacity; - byte* newPointer = (byte*)Marshal.AllocHGlobal(newSize).ToPointer(); - - for (int i = 0; i < newSize; i++) - { - *(newPointer + i) = 0; - } - - return (T*)newPointer; + //int newSize = MetaCache.Size * capacity; + //byte* newPointer = (byte*)Marshal.AllocHGlobal(newSize).ToPointer(); + // + //for (int i = 0; i < newSize; i++) + //{ + // *(newPointer + i) = 0; + //} + // + //return (T*)newPointer; + return MemoryAllocator.AllocAndInit(capacity).As(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void NewAndInit(out T* ptr, int capacity) where T : unmanaged { - int newSize = MetaCache.Size * capacity; - byte* newPointer = (byte*)Marshal.AllocHGlobal(newSize).ToPointer(); - - for (int i = 0; i < newSize; i++) - { - *(newPointer + i) = 0; - } - - ptr = (T*)newPointer; + //int newSize = MetaCache.Size * capacity; + //byte* newPointer = (byte*)Marshal.AllocHGlobal(newSize).ToPointer(); + // + //for (int i = 0; i < newSize; i++) + //{ + // *(newPointer + i) = 0; + //} + // + //ptr = (T*)newPointer; + ptr = MemoryAllocator.AllocAndInit(capacity).As(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Free(void* pointer) { - Marshal.FreeHGlobal(new IntPtr(pointer)); + //Marshal.FreeHGlobal(new IntPtr(pointer)); + MemoryAllocator.Free(dataPtr: pointer); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Free(ref T* pointer, ref int length) where T : unmanaged { - Marshal.FreeHGlobal(new IntPtr(pointer)); + //Marshal.FreeHGlobal(new IntPtr(pointer)); + MemoryAllocator.Free(dataPtr: pointer); pointer = null; length = 0; } @@ -337,19 +344,21 @@ namespace DCFApixels.DragonECS.Internal [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T* Resize(void* oldPointer, int newCount) where T : unmanaged { - return (T*)(Marshal.ReAllocHGlobal( - new IntPtr(oldPointer), - new IntPtr(MetaCache.Size * newCount))).ToPointer(); + //return (T*)(Marshal.ReAllocHGlobal( + // new IntPtr(oldPointer), + // new IntPtr(MetaCache.Size * newCount))).ToPointer(); + return MemoryAllocator.Realloc(MemoryAllocator.Handler.FromDataPtr(oldPointer), newCount).As(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T* ResizeAndInit(void* oldPointer, int oldSize, int newSize) where T : unmanaged { - int sizeT = MetaCache.Size; - T* result = (T*)Marshal.ReAllocHGlobal( - new IntPtr(oldPointer), - new IntPtr(sizeT * newSize)).ToPointer(); - Init((byte*)result, sizeT * oldSize, sizeT * newSize); - return result; + //int sizeT = MetaCache.Size; + //T* result = (T*)Marshal.ReAllocHGlobal( + // new IntPtr(oldPointer), + // new IntPtr(sizeT * newSize)).ToPointer(); + //Init((byte*)result, sizeT * oldSize, sizeT * newSize); + //return result; + return MemoryAllocator.ReallocAndInit(MemoryAllocator.Handler.FromDataPtr(oldPointer), oldSize, newSize).As(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void Init(byte* pointer, int startByteIndex, int endByteIndex) diff --git a/src/Internal/IdDispenser.cs b/src/Internal/IdDispenser.cs index 47e993b..c4661bf 100644 --- a/src/Internal/IdDispenser.cs +++ b/src/Internal/IdDispenser.cs @@ -16,6 +16,7 @@ namespace DCFApixels.DragonECS.Internal #endif [Serializable] [DebuggerTypeProxy(typeof(DebuggerProxy))] + [DebuggerDisplay("Count: {Count}")] internal class IdDispenser : IEnumerable, IReadOnlyCollection { private const int MIN_SIZE = 4; diff --git a/src/Internal/MemoryAllocator.cs b/src/Internal/MemoryAllocator.cs new file mode 100644 index 0000000..43c1c8c --- /dev/null +++ b/src/Internal/MemoryAllocator.cs @@ -0,0 +1,333 @@ +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); + } + } +} \ No newline at end of file