mirror of
https://github.com/DCFApixels/DragonECS.git
synced 2025-09-17 17:34:36 +08:00
333 lines
12 KiB
C#
333 lines
12 KiB
C#
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<T>(int count) where T : unmanaged
|
|
{
|
|
return AllocAndInit_Internal(Marshal.SizeOf<T>() * 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<T>(int count) where T : unmanaged
|
|
{
|
|
return Alloc_Internal(Marshal.SizeOf<T>() * 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<T>(void* target, int oldCount, int newCount) where T : unmanaged
|
|
{
|
|
return ReallocAndInit<T>(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<T>(Handler target, int oldCount, int newCount) where T : unmanaged
|
|
{
|
|
var size = Marshal.SizeOf<T>();
|
|
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<T>(void* target, int newCount) where T : unmanaged
|
|
{
|
|
return Realloc<T>(Handler.FromDataPtr(target), Marshal.SizeOf<T>() * newCount);
|
|
}
|
|
public static Handler Realloc(void* target, int newByteLength)
|
|
{
|
|
return Realloc(Handler.FromDataPtr(target), newByteLength);
|
|
}
|
|
public static Handler Realloc<T>(Handler target, int newCount) where T : unmanaged
|
|
{
|
|
return Realloc_Internal(target, Marshal.SizeOf<T>() * 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<byte> memorySpan = new Span<byte>((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<T>() 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);
|
|
}
|
|
}
|
|
} |