You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
108 lines
3.4 KiB
108 lines
3.4 KiB
using System; |
|
using Unity.Collections; |
|
using Unity.Collections.LowLevel.Unsafe; |
|
using UnityEngine; |
|
|
|
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<FreeNode> m_Buffer; |
|
private readonly int m_NodeNumPerElement; |
|
|
|
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<FreeNode>(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<FreeNode>()); |
|
} |
|
|
|
head.next = node.next; |
|
node.next = -1; |
|
return index; |
|
} |
|
|
|
public ref T ElementAt<T>(int index) where T : unmanaged |
|
{ |
|
ref var node = ref m_Buffer.ElementAt(index * m_NodeNumPerElement); |
|
if (node.next >= 0) |
|
throw new Exception("Index out of range"); |
|
|
|
return ref UnsafeUtility.As<FreeNode, T>(ref m_Buffer.ElementAt(index * m_NodeNumPerElement + 1)); |
|
} |
|
|
|
public void Clear() |
|
{ |
|
m_Buffer.Resize(m_NodeNumPerElement); |
|
m_Buffer.ElementAt(0).next = 0; |
|
} |
|
|
|
internal static int ComputeNodeNumPerElement(int elementSize) |
|
{ |
|
return (elementSize + UnsafeUtility.SizeOf<FreeNode>() - 1) / UnsafeUtility.SizeOf<FreeNode>() + 1; |
|
} |
|
} |
|
} |