// 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 } /************************************************************************************************************************/ } }