using System;
using System.Threading;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
namespace Unity.Networking.Transport.Utilities.LowLevel.Unsafe
{
internal unsafe struct UnsafeAtomicFreeList : IDisposable
{
// used count
// free list size
// free indices...
[NativeDisableUnsafePtrRestriction]
private int* m_Buffer;
private int m_Length;
private Allocator m_Allocator;
public int Capacity => m_Length;
public int InUse => m_Buffer[0] - m_Buffer[1];
public bool IsCreated => m_Buffer != null;
///
/// Initializes a new instance of the AtomicFreeList struct.
///
/// The number of elements the free list can store.
/// The used to allocate the memory.
public UnsafeAtomicFreeList(int capacity, Allocator allocator)
{
m_Allocator = allocator;
m_Length = capacity;
var size = UnsafeUtility.SizeOf() * (capacity + 2);
m_Buffer = (int*)UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator);
UnsafeUtility.MemClear(m_Buffer, size);
}
public void Dispose()
{
if (IsCreated)
UnsafeUtility.Free(m_Buffer, m_Allocator);
}
///
/// Inserts an item on top of the stack.
///
/// The item to push onto the stack.
public unsafe void Push(int item)
{
int* buffer = m_Buffer;
int idx = Interlocked.Increment(ref buffer[1]) - 1;
while (Interlocked.CompareExchange(ref buffer[idx + 2], item + 1, 0) != 0)
{
}
}
///
/// Remove and return a value from the top of the stack
///
///
/// The removed value from the top of the stack.
public unsafe int Pop()
{
int* buffer = m_Buffer;
int idx = buffer[1] - 1;
while (idx >= 0 && Interlocked.CompareExchange(ref buffer[1], idx, idx + 1) != idx + 1)
idx = buffer[1] - 1;
if (idx >= 0)
{
int val = 0;
while (val == 0)
{
val = Interlocked.Exchange(ref buffer[2 + idx], 0);
}
return val - 1;
}
idx = Interlocked.Increment(ref buffer[0]) - 1;
if (idx >= Capacity)
{
Interlocked.Decrement(ref buffer[0]);
return -1;
}
return idx;
}
}
}