using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; namespace Robust.Shared.Collections; /// /// A fixed-size queue that discards the oldest entry if a new entry is enqueued when the queue is full. /// /// public sealed class OverflowQueue { private readonly T[] _queue; private int _currentIdx = 0; private int _length = 0; /// /// The size of the queue-buffer. /// public int Size => _queue.Length; /// size of the queue-buffer public OverflowQueue(int size) { _queue = new T[size]; } /// /// Enqueues the . Overrides the oldest item if the queue is full. /// /// The item to enqueue public void Enqueue(T item) { _queue[_currentIdx++] = item; if(_length < Size) _length++; if (_currentIdx == Size) { _currentIdx = 0; } } /// /// Removes the item at the head of the queue and returns it. If the queue is empty, this method throws an InvalidOperationException. /// /// The dequeued item. /// Thrown if the queue is empty. public T Dequeue() { if (!TryDequeue(out var item)) { throw new InvalidOperationException($"{nameof(OverflowQueue)} has no more items to dequeue."); } return item; } /// /// Tries to dequeue an item. /// /// The item which got dequeued. Null if the queue was empty /// True if an item was dequeued, false if not. public bool TryDequeue([NotNullWhen(true)] out T? item) { if (_length == 0) { item = default; return false; } item = _queue[GetCurrentIndex()]!; _length--; return true; } private int GetCurrentIndex() { Debug.Assert(_length > 0); var idx = _currentIdx - _length; if (idx < 0) { return idx + Size; } return idx; } /// /// Returns the item at the head of the queue. The object remains in the queue. If the queue is empty, this method throws an InvalidOperationException. /// /// The item at the head of the queue. /// Thrown if the queue is empty. public T Peek() { if (_length == 0) { throw new InvalidOperationException($"{nameof(OverflowQueue)} has no more items to dequeue."); } return _queue[GetCurrentIndex()]; } /// /// Returns true if the queue contains at least one object equal to item. Equality is determined using EqualityComparer.Default.Equals(). /// /// Item to look for. /// True if the queue contains the item, false if not. public bool Contains(T item) { for (int i = 0; i < _length; i++) { var actualIndex = _currentIdx + i; if (actualIndex >= _length) actualIndex -= _length; if (EqualityComparer.Default.Equals(item, _queue[actualIndex])) return true; } return false; } /// /// Returns the queue contents first to last as an array. /// /// The array containing the queue contents. public T[] ToArray() { if (_length == 0) { return Array.Empty(); } var res = new T[_length]; var startIdx = _currentIdx - _length; if (startIdx < 0) { Array.Copy(_queue, startIdx + Size, res, 0, -1 * startIdx); Array.Copy(_queue, 0, res, -1 * startIdx, startIdx + Size); } else { Array.Copy(_queue, startIdx, res, 0, _length); } return res; } }