osu/osu.Game/Rulesets/Difficulty/Utils/ReverseQueue.cs
2022-06-17 16:37:17 +09:00

136 lines
4.2 KiB
C#

// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Collections;
using System.Collections.Generic;
namespace osu.Game.Rulesets.Difficulty.Utils
{
/// <summary>
/// An indexed queue where items are indexed beginning from the most recently enqueued item.
/// Enqueuing an item pushes all existing indexes up by one and inserts the item at index 0.
/// Dequeuing an item removes the item from the highest index and returns it.
/// </summary>
public class ReverseQueue<T> : IEnumerable<T>
{
/// <summary>
/// The number of elements in the <see cref="ReverseQueue{T}"/>.
/// </summary>
public int Count { get; private set; }
private T[] items;
private int capacity;
private int start;
public ReverseQueue(int initialCapacity)
{
if (initialCapacity <= 0)
throw new ArgumentOutOfRangeException(nameof(initialCapacity));
items = new T[initialCapacity];
capacity = initialCapacity;
start = 0;
Count = 0;
}
/// <summary>
/// Retrieves the item at an index in the <see cref="ReverseQueue{T}"/>.
/// </summary>
/// <param name="index">The index of the item to retrieve. The most recently enqueued item is at index 0.</param>
public T this[int index]
{
get
{
if (index < 0 || index > Count - 1)
throw new ArgumentOutOfRangeException(nameof(index));
int reverseIndex = Count - 1 - index;
return items[(start + reverseIndex) % capacity];
}
}
/// <summary>
/// Enqueues an item to this <see cref="ReverseQueue{T}"/>.
/// </summary>
/// <param name="item">The item to enqueue.</param>
public void Enqueue(T item)
{
if (Count == capacity)
{
// Double the buffer size
var buffer = new T[capacity * 2];
// Copy items to new queue
for (int i = 0; i < Count; i++)
{
buffer[i] = items[(start + i) % capacity];
}
// Replace array with new buffer
items = buffer;
capacity *= 2;
start = 0;
}
items[(start + Count) % capacity] = item;
Count++;
}
/// <summary>
/// Dequeues the least recently enqueued item from the <see cref="ReverseQueue{T}"/> and returns it.
/// </summary>
/// <returns>The item dequeued from the <see cref="ReverseQueue{T}"/>.</returns>
public T Dequeue()
{
var item = items[start];
start = (start + 1) % capacity;
Count--;
return item;
}
/// <summary>
/// Clears the <see cref="ReverseQueue{T}"/> of all items.
/// </summary>
public void Clear()
{
start = 0;
Count = 0;
}
/// <summary>
/// Returns an enumerator which enumerates items in the <see cref="ReverseQueue{T}"/> starting from the most recently enqueued item.
/// </summary>
public IEnumerator<T> GetEnumerator() => new Enumerator(this);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public struct Enumerator : IEnumerator<T>
{
private ReverseQueue<T> reverseQueue;
private int currentIndex;
internal Enumerator(ReverseQueue<T> reverseQueue)
{
this.reverseQueue = reverseQueue;
currentIndex = -1; // The first MoveNext() should bring the iterator to 0
}
public bool MoveNext() => ++currentIndex < reverseQueue.Count;
public void Reset() => currentIndex = -1;
public readonly T Current => reverseQueue[currentIndex];
readonly object IEnumerator.Current => Current;
public void Dispose()
{
reverseQueue = null;
}
}
}
}