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

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;
}
}
}