|
|
|
|
|
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 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 IndexOutOfRangeException();
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|