using System;
namespace FishNet.Utility.Performance
{
///
/// Unity 2022 has a bug where codegen will not compile when referencing a Queue type,
/// while also targeting .Net as the framework API.
/// As a work around this class is used for queues instead.
///
public class BasicQueue
{
///
/// Maximum size of the collection.
///
public int Capacity => Collection.Length;
///
/// Number of elements in the queue.
///
public int Count => _written;
///
/// Collection containing data.
///
private T[] Collection = new T[4];
///
/// Current write index of the collection.
///
public int WriteIndex { get; private set; }
///
/// Buffer for resizing.
///
private T[] _resizeBuffer = new T[0];
///
/// Read position of the next Dequeue.
///
private int _read;
///
/// Length of the queue.
///
private int _written;
///
/// Enqueues an entry.
///
///
public void Enqueue(T data)
{
if (_written == Collection.Length)
Resize();
if (WriteIndex >= Collection.Length)
WriteIndex = 0;
Collection[WriteIndex] = data;
WriteIndex++;
_written++;
}
///
/// Tries to dequeue the next entry.
///
/// Dequeued entry.
/// True if an entry existed to dequeue.
public bool TryDequeue(out T result)
{
if (_written == 0)
{
result = default;
return false;
}
result = Dequeue();
return true;
}
///
/// Dequeues the next entry.
///
///
public T Dequeue()
{
if (_written == 0)
throw new Exception($"Queue of type {typeof(T).Name} is empty.");
T result = Collection[_read];
_written--;
_read++;
if (_read >= Collection.Length)
_read = 0;
return result;
}
///
/// Tries to peek the next entry.
///
/// Peeked entry.
/// True if an entry existed to peek.
public bool TryPeek(out T result)
{
if (_written == 0)
{
result = default;
return false;
}
result = Peek();
return true;
}
///
/// Peeks the next queue entry.
///
///
public T Peek()
{
if (_written == 0)
throw new Exception($"Queue of type {typeof(T).Name} is empty.");
return Collection[_read];
}
///
/// Clears the queue.
///
/// True to make buffer entries default.
public void Clear()
{
_read = 0;
WriteIndex = 0;
_written = 0;
DefaultCollection(Collection);
DefaultCollection(_resizeBuffer);
void DefaultCollection(T[] array)
{
int count = array.Length;
for (int i = 0; i < count; i++)
array[i] = default;
}
}
///
/// Doubles the queue size.
///
private void Resize()
{
int length = _written;
int doubleLength = (length * 2);
int read = _read;
/* Make sure copy array is the same size as current
* and copy contents into it. */
//Ensure large enough to fit contents.
T[] resizeBuffer = _resizeBuffer;
if (resizeBuffer.Length < doubleLength)
Array.Resize(ref resizeBuffer, doubleLength);
//Copy from the read of queue first.
int copyLength = (length - read);
Array.Copy(Collection, read, resizeBuffer, 0, copyLength);
/* If read index was higher than 0
* then copy remaining data as well from 0. */
if (read > 0)
Array.Copy(Collection, 0, resizeBuffer, copyLength, read);
//Set _array to resize.
Collection = resizeBuffer;
//Reset positions.
_read = 0;
WriteIndex = length;
}
///
/// Returns value in actual index as it relates to simulated index.
///
/// Simulated index to return. A value of 0 would return the first simulated index in the collection.
///
public T this[int simulatedIndex]
{
get
{
int offset = (Capacity - _written) + simulatedIndex + WriteIndex;
if (offset >= Capacity)
offset -= Capacity;
return Collection[offset];
}
set
{
int offset = (Capacity - _written) + simulatedIndex + WriteIndex;
if (offset >= Capacity)
offset -= Capacity;
Collection[offset] = value;
}
}
}
}