using System; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using UnityEngine; using UnityEngine.Assertions; namespace GameCore.LowLevel { public unsafe struct UnsafeDataContainer : IDisposable { private struct FreeNode { public int next; public int _padding; } public enum AllocOptions { None = 0, ClearNew = 1, ClearAll = 2, } public readonly int elementSize; private UnsafeList m_Buffer; private readonly int m_NodeNumPerElement; public int Length => m_Buffer.Length; public bool IsCreated => m_Buffer.IsCreated; public UnsafeDataContainer(int elementSize, AllocatorManager.AllocatorHandle allocator) { if (elementSize <= 0) throw new ArgumentException(nameof(elementSize)); this.elementSize = elementSize; m_NodeNumPerElement = ComputeNodeNumPerElement(elementSize); m_Buffer = new UnsafeList(0, allocator); m_Buffer.Resize(m_NodeNumPerElement, NativeArrayOptions.ClearMemory); } public void Dispose() { m_Buffer.Dispose(); } public bool HasValue(int index) { var node = m_Buffer[index * m_NodeNumPerElement]; return node.next < 0; } public bool Free(int index) { ref var node = ref m_Buffer.ElementAt(index * m_NodeNumPerElement); if (node.next >= 0) return false; ref var head = ref m_Buffer.ElementAt(0); node.next = head.next; head.next = index; return true; } public int Alloc(AllocOptions options = AllocOptions.None) { ref var head = ref m_Buffer.ElementAt(0); var index = head.next; if (index == 0) { index = m_Buffer.Length / m_NodeNumPerElement; m_Buffer.Resize(m_Buffer.Length + m_NodeNumPerElement, options == AllocOptions.None ? NativeArrayOptions.UninitializedMemory : NativeArrayOptions.ClearMemory); m_Buffer.ElementAt(index * m_NodeNumPerElement).next = -1; return index; } ref var node = ref m_Buffer.ElementAt(index * m_NodeNumPerElement); if (options == AllocOptions.ClearAll) { var ptr = m_Buffer.Ptr + index * m_NodeNumPerElement; UnsafeUtility.MemClear(ptr, m_NodeNumPerElement * UnsafeUtility.SizeOf()); } head.next = node.next; node.next = -1; return index; } public ref T ElementAt(int index) where T : unmanaged { ref var node = ref m_Buffer.ElementAt(index * m_NodeNumPerElement); if (node.next >= 0) throw new IndexOutOfRangeException(); return ref UnsafeUtility.As(ref m_Buffer.ElementAt(index * m_NodeNumPerElement + 1)); } public void Clear() { m_Buffer.Resize(m_NodeNumPerElement); m_Buffer.ElementAt(0).next = 0; } public void CopyFrom(UnsafeDataContainer other) { if (elementSize != other.elementSize) { throw new InvalidOperationException(); } m_Buffer.CopyFrom(other.m_Buffer); } internal static int ComputeNodeNumPerElement(int elementSize) { return (elementSize + UnsafeUtility.SizeOf() - 1) / UnsafeUtility.SizeOf() + 1; } } }