// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2023 Kybernetik //
using System;
using System.Collections;
using System.Collections.Generic;
namespace Animancer
{
///
/// An for any doesn't bother checking if the target has been
/// modified. This gives it good performance but also makes it slightly less safe to use.
///
///
/// This struct also implements so it can be used in foreach statements and
/// to allow the target collection to be modified without breaking the enumerator (though
/// doing so is still somewhat dangerous so use with care).
///
///
/// var numbers = new int[] { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, };
/// var count = 4;
/// foreach (var number in new FastEnumerator<int>(numbers, count))
/// {
/// Debug.Log(number);
/// }
///
/// // Log Output:
/// // 9
/// // 8
/// // 7
/// // 6
///
public struct FastEnumerator : IList, IEnumerator
{
/************************************************************************************************************************/
/// The target .
private readonly IList List;
/************************************************************************************************************************/
private int _Count;
/// []
/// The number of items in the (which can be less than the
/// of the ).
///
public int Count
{
get => _Count;
set
{
AssertCount(value);
_Count = value;
}
}
/************************************************************************************************************************/
private int _Index;
/// The position of the item in the .
public int Index
{
get => _Index;
set
{
AssertIndex(value);
_Index = value;
}
}
/************************************************************************************************************************/
/// The item at the current in the .
public T Current
{
get
{
AssertCount(_Count);
AssertIndex(_Index);
return List[_Index];
}
set
{
AssertCount(_Count);
AssertIndex(_Index);
List[_Index] = value;
}
}
/// The item at the current in the .
object IEnumerator.Current => Current;
/************************************************************************************************************************/
/// Creates a new .
///
/// The `list` is null. Use the default instead.
///
public FastEnumerator(IList list)
: this(list, list.Count)
{ }
/// Creates a new .
///
/// The `list` is null. Use the default instead.
///
public FastEnumerator(IList list, int count)
{
List = list;
_Count = count;
_Index = -1;
AssertCount(count);
}
/************************************************************************************************************************/
/// Moves to the next item in the and returns true if there is one.
/// At the end of the the is set to .
public bool MoveNext()
{
_Index++;
if ((uint)_Index < (uint)_Count)
{
return true;
}
else
{
_Index = int.MinValue;
return false;
}
}
/************************************************************************************************************************/
/// Moves to the previous item in the and returns true if there is one.
/// At the end of the the is set to -1.
public bool MovePrevious()
{
if (_Index > 0)
{
_Index--;
return true;
}
else
{
_Index = -1;
return false;
}
}
/************************************************************************************************************************/
/// [] Reverts this enumerator to the start of the .
public void Reset()
{
_Index = -1;
}
/************************************************************************************************************************/
///
void IDisposable.Dispose() { }
/************************************************************************************************************************/
// IEnumerator.
/************************************************************************************************************************/
/// Returns this.
public FastEnumerator GetEnumerator() => this;
///
IEnumerator IEnumerable.GetEnumerator() => this;
///
IEnumerator IEnumerable.GetEnumerator() => this;
/************************************************************************************************************************/
// IList.
/************************************************************************************************************************/
/// [] Returns the first index of the `item` in the .
public int IndexOf(T item) => List.IndexOf(item);
/// [] The item at the specified `index` in the .
public T this[int index]
{
get
{
AssertIndex(index);
return List[index];
}
set
{
AssertIndex(index);
List[index] = value;
}
}
/// [] Inserts the `item` at the specified `index` in the .
public void Insert(int index, T item)
{
AssertIndex(index);
List.Insert(index, item);
if (_Index >= index)
_Index++;
_Count++;
}
/// [] Removes the item at the specified `index` from the .
public void RemoveAt(int index)
{
AssertIndex(index);
List.RemoveAt(index);
if (_Index >= index)
_Index--;
_Count--;
}
/************************************************************************************************************************/
// ICollection.
/************************************************************************************************************************/
/// [] Is the read-only?
public bool IsReadOnly => List.IsReadOnly;
/// [] Does the contain the `item`?
public bool Contains(T item) => List.Contains(item);
/// [] Adds the `item` to the end of the .
public void Add(T item)
{
List.Add(item);
_Count++;
}
/// [] Removes the `item` from the and returns true if successful.
public bool Remove(T item)
{
var index = List.IndexOf(item);
if (index >= 0)
{
RemoveAt(index);
return true;
}
else return false;
}
/// [] Removes everything from the .
public void Clear()
{
List.Clear();
_Index = -1;
_Count = 0;
}
/// [] Copies the contents of the into the `array`.
public void CopyTo(T[] array, int arrayIndex)
{
for (int i = 0; i < _Count; i++)
array[arrayIndex + i] = List[i];
}
/************************************************************************************************************************/
/// [Assert-Only] Throws an exception unless 0 <= `index` < .
///
[System.Diagnostics.Conditional(Strings.Assertions)]
private void AssertIndex(int index)
{
#if UNITY_ASSERTIONS
if ((uint)index > (uint)_Count)
throw new ArgumentOutOfRangeException(nameof(index),
$"{nameof(FastEnumerator)}.{nameof(Index)}" +
$" must be within 0 <= {nameof(Index)} ({index}) < {nameof(Count)} ({_Count}).");
#endif
}
/************************************************************************************************************************/
/// [Assert-Only] Throws an exception unless 0 < `count` <= .
///
[System.Diagnostics.Conditional(Strings.Assertions)]
private void AssertCount(int count)
{
#if UNITY_ASSERTIONS
if (List == null)
{
if (count != 0)
throw new ArgumentOutOfRangeException(nameof(count),
$"Must be within 0 since the {nameof(List)} is null.");
}
else
{
if ((uint)count > (uint)List.Count)
throw new ArgumentOutOfRangeException(nameof(count),
$"Must be within 0 <= {nameof(count)} ({count}) < {nameof(List)}.{nameof(List.Count)} ({List.Count}).");
}
#endif
}
/************************************************************************************************************************/
}
}