using System.Collections; using System.Collections.Generic; using LanLib.UpdateManager.Extensions; namespace LanLib.UpdateManager.Internal { /// Generic list with O(1) insertion and removal. /// /// /// Adding duplicate values and removing items that are not present are both no-op. /// /// Items may be added and removed while the collection is being enumerated without raising exceptions. /// This collection guarantees that all items are enumerated, even if insertions or removals happen during enumeration. /// /// /// All enumerators reference the same list index, so avoid having more than one enumerator at the same time. /// Creating a new enumerator resets this shared index. /// /// /// public class FastRemoveList : IReadOnlyList { private readonly List _list = new List(); private readonly Dictionary _indexMap = new Dictionary(); private int _loopIndex; /// Get the number of items contained in the . public int Count => _list.Count; /// Item registered at if index is valid, otherwise. public T this[int index] => index >= 0 && index < Count ? _list[index] : default; /// Add to the end of the list. /// if value was not already present in the list and was successfully added, otherwise. /// /// Adding a value while the collection is being enumerated is permitted and won't raise exceptions. /// This operation is O(1). /// public bool Add(T value) { if (_indexMap.ContainsKey(value)) { return false; } _list.Add(value); _indexMap.Add(value, _list.Count - 1); return true; } /// Remove from the list, if found. /// if value was present in the list and was successfully removed, otherwise. /// /// Removing a value while the collection is being enumerated is permitted and won't raise exceptions. /// This operation is O(1). /// public bool Remove(T value) { if (!_indexMap.TryGetValue(value, out int indexToRemove)) { return false; } _indexMap.Remove(value); // If removing the object that was just enumerated, make sure the // new object swapped back to this index gets enumerated as well if (indexToRemove == _loopIndex) { _loopIndex--; } // If removing an object that was already enumerated while the loop is // still running, swap it with current loop index to make sure we // still enumerate the last element that will be swapped back later else if (indexToRemove < _loopIndex) { _list.Swap(_loopIndex, indexToRemove, out T swappedValue); _indexMap[swappedValue] = indexToRemove; indexToRemove = _loopIndex; _loopIndex--; } _list.RemoveAtSwapBack(indexToRemove, out T swappedBack); if (swappedBack != null) { _indexMap[swappedBack] = indexToRemove; } return true; } /// Removes all values from the . public void Clear() { _list.Clear(); _indexMap.Clear(); } /// Returns an enumerator that iterates through the . public Enumerator GetEnumerator() { return new Enumerator(this); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public struct Enumerator : IEnumerator { private FastRemoveList _list; public Enumerator(FastRemoveList list) { _list = list; Reset(); } public T Current => _list[_list._loopIndex]; object IEnumerator.Current => Current; public void Dispose() { } public bool MoveNext() { if (_list._loopIndex < _list.Count - 1) { _list._loopIndex++; return true; } else { return false; } } public void Reset() { _list._loopIndex = -1; } } } }