// // System.Collections.Generic.List // // Authors: // Ben Maurer (bmaurer@ximian.com) // Martin Baulig (martin@ximian.com) // Carlos Alberto Cortez (calberto.cortez@gmail.com) // David Waite (mass@akuma.org) // // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) // Copyright (C) 2005 David Waite // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; namespace Spine { [Serializable] [DebuggerDisplay("Count={Count}")] public class ExposedList : IEnumerable { public T[] Items; public int Count; private const int DefaultCapacity = 4; private static readonly T[] EmptyArray = new T[0]; private int _version; public ExposedList() { Items = EmptyArray; } public ExposedList(IEnumerable collection) { CheckCollection(collection); // initialize to needed size (if determinable) ICollection c = collection as ICollection; if (c == null) { Items = EmptyArray; AddEnumerable(collection); } else { Items = new T[c.Count]; AddCollection(c); } } public ExposedList(int capacity) { if (capacity < 0) throw new ArgumentOutOfRangeException("capacity"); Items = new T[capacity]; } internal ExposedList(T[] data, int size) { Items = data; Count = size; } public void Add(T item) { // If we check to see if we need to grow before trying to grow // we can speed things up by 25% if (Count == Items.Length) GrowIfNeeded(1); Items[Count ++] = item; _version++; } private void GrowIfNeeded(int newCount) { int minimumSize = Count + newCount; if (minimumSize > Items.Length) Capacity = Math.Max(Math.Max(Capacity * 2, DefaultCapacity), minimumSize); } private void CheckRange(int idx, int count) { if (idx < 0) throw new ArgumentOutOfRangeException("index"); if (count < 0) throw new ArgumentOutOfRangeException("count"); if ((uint) idx + (uint) count > (uint) Count) throw new ArgumentException("index and count exceed length of list"); } private void AddCollection(ICollection collection) { int collectionCount = collection.Count; if (collectionCount == 0) return; GrowIfNeeded(collectionCount); collection.CopyTo(Items, Count); Count += collectionCount; } private void AddEnumerable(IEnumerable enumerable) { foreach (T t in enumerable) { Add(t); } } public void AddRange(IEnumerable collection) { CheckCollection(collection); ICollection c = collection as ICollection; if (c != null) AddCollection(c); else AddEnumerable(collection); _version++; } public int BinarySearch(T item) { return Array.BinarySearch(Items, 0, Count, item); } public int BinarySearch(T item, IComparer comparer) { return Array.BinarySearch(Items, 0, Count, item, comparer); } public int BinarySearch(int index, int count, T item, IComparer comparer) { CheckRange(index, count); return Array.BinarySearch(Items, index, count, item, comparer); } public void Clear() { Array.Clear(Items, 0, Items.Length); Count = 0; _version++; } public bool Contains(T item) { return Array.IndexOf(Items, item, 0, Count) != -1; } public ExposedList ConvertAll(Converter converter) { if (converter == null) throw new ArgumentNullException("converter"); ExposedList u = new ExposedList(Count); for (int i = 0; i < Count; i++) u.Items[i] = converter(Items[i]); u.Count = Count; return u; } public void CopyTo(T[] array) { Array.Copy(Items, 0, array, 0, Count); } public void CopyTo(T[] array, int arrayIndex) { Array.Copy(Items, 0, array, arrayIndex, Count); } public void CopyTo(int index, T[] array, int arrayIndex, int count) { CheckRange(index, count); Array.Copy(Items, index, array, arrayIndex, count); } public bool Exists(Predicate match) { CheckMatch(match); return GetIndex(0, Count, match) != -1; } public T Find(Predicate match) { CheckMatch(match); int i = GetIndex(0, Count, match); return (i != -1) ? Items[i] : default (T); } private static void CheckMatch(Predicate match) { if (match == null) throw new ArgumentNullException("match"); } public ExposedList FindAll(Predicate match) { CheckMatch(match); return FindAllList(match); } private ExposedList FindAllList(Predicate match) { ExposedList results = new ExposedList(); for (int i = 0; i < Count; i++) if (match(Items[i])) results.Add(Items[i]); return results; } public int FindIndex(Predicate match) { CheckMatch(match); return GetIndex(0, Count, match); } public int FindIndex(int startIndex, Predicate match) { CheckMatch(match); CheckIndex(startIndex); return GetIndex(startIndex, Count - startIndex, match); } public int FindIndex(int startIndex, int count, Predicate match) { CheckMatch(match); CheckRange(startIndex, count); return GetIndex(startIndex, count, match); } private int GetIndex(int startIndex, int count, Predicate match) { int end = startIndex + count; for (int i = startIndex; i < end; i ++) if (match(Items[i])) return i; return -1; } public T FindLast(Predicate match) { CheckMatch(match); int i = GetLastIndex(0, Count, match); return i == -1 ? default (T) : Items[i]; } public int FindLastIndex(Predicate match) { CheckMatch(match); return GetLastIndex(0, Count, match); } public int FindLastIndex(int startIndex, Predicate match) { CheckMatch(match); CheckIndex(startIndex); return GetLastIndex(0, startIndex + 1, match); } public int FindLastIndex(int startIndex, int count, Predicate match) { CheckMatch(match); int start = startIndex - count + 1; CheckRange(start, count); return GetLastIndex(start, count, match); } private int GetLastIndex(int startIndex, int count, Predicate match) { // unlike FindLastIndex, takes regular params for search range for (int i = startIndex + count; i != startIndex;) if (match(Items[--i])) return i; return -1; } public void ForEach(Action action) { if (action == null) throw new ArgumentNullException("action"); for (int i = 0; i < Count; i++) action(Items[i]); } public Enumerator GetEnumerator() { return new Enumerator(this); } public ExposedList GetRange(int index, int count) { CheckRange(index, count); T[] tmpArray = new T[count]; Array.Copy(Items, index, tmpArray, 0, count); return new ExposedList(tmpArray, count); } public int IndexOf(T item) { return Array.IndexOf(Items, item, 0, Count); } public int IndexOf(T item, int index) { CheckIndex(index); return Array.IndexOf(Items, item, index, Count - index); } public int IndexOf(T item, int index, int count) { if (index < 0) throw new ArgumentOutOfRangeException("index"); if (count < 0) throw new ArgumentOutOfRangeException("count"); if ((uint) index + (uint) count > (uint) Count) throw new ArgumentOutOfRangeException("index and count exceed length of list"); return Array.IndexOf(Items, item, index, count); } private void Shift(int start, int delta) { if (delta < 0) start -= delta; if (start < Count) Array.Copy(Items, start, Items, start + delta, Count - start); Count += delta; if (delta < 0) Array.Clear(Items, Count, -delta); } private void CheckIndex(int index) { if (index < 0 || (uint) index > (uint) Count) throw new ArgumentOutOfRangeException("index"); } public void Insert(int index, T item) { CheckIndex(index); if (Count == Items.Length) GrowIfNeeded(1); Shift(index, 1); Items[index] = item; _version++; } private void CheckCollection(IEnumerable collection) { if (collection == null) throw new ArgumentNullException("collection"); } public void InsertRange(int index, IEnumerable collection) { CheckCollection(collection); CheckIndex(index); if (collection == this) { T[] buffer = new T[Count]; CopyTo(buffer, 0); GrowIfNeeded(Count); Shift(index, buffer.Length); Array.Copy(buffer, 0, Items, index, buffer.Length); } else { ICollection c = collection as ICollection; if (c != null) InsertCollection(index, c); else InsertEnumeration(index, collection); } _version++; } private void InsertCollection(int index, ICollection collection) { int collectionCount = collection.Count; GrowIfNeeded(collectionCount); Shift(index, collectionCount); collection.CopyTo(Items, index); } private void InsertEnumeration(int index, IEnumerable enumerable) { foreach (T t in enumerable) Insert(index++, t); } public int LastIndexOf(T item) { return Array.LastIndexOf(Items, item, Count - 1, Count); } public int LastIndexOf(T item, int index) { CheckIndex(index); return Array.LastIndexOf(Items, item, index, index + 1); } public int LastIndexOf(T item, int index, int count) { if (index < 0) throw new ArgumentOutOfRangeException("index", index, "index is negative"); if (count < 0) throw new ArgumentOutOfRangeException("count", count, "count is negative"); if (index - count + 1 < 0) throw new ArgumentOutOfRangeException("count", count, "count is too large"); return Array.LastIndexOf(Items, item, index, count); } public bool Remove(T item) { int loc = IndexOf(item); if (loc != -1) RemoveAt(loc); return loc != -1; } public int RemoveAll(Predicate match) { CheckMatch(match); int i = 0; int j = 0; // Find the first item to remove for (i = 0; i < Count; i++) if (match(Items[i])) break; if (i == Count) return 0; _version++; // Remove any additional items for (j = i + 1; j < Count; j++) { if (!match(Items[j])) Items[i++] = Items[j]; } if (j - i > 0) Array.Clear(Items, i, j - i); Count = i; return (j - i); } public void RemoveAt(int index) { if (index < 0 || (uint) index >= (uint) Count) throw new ArgumentOutOfRangeException("index"); Shift(index, -1); Array.Clear(Items, Count, 1); _version++; } public void RemoveRange(int index, int count) { CheckRange(index, count); if (count > 0) { Shift(index, -count); Array.Clear(Items, Count, count); _version++; } } public void Reverse() { Array.Reverse(Items, 0, Count); _version++; } public void Reverse(int index, int count) { CheckRange(index, count); Array.Reverse(Items, index, count); _version++; } public void Sort() { Array.Sort(Items, 0, Count, Comparer.Default); _version++; } public void Sort(IComparer comparer) { Array.Sort(Items, 0, Count, comparer); _version++; } public void Sort(Comparison comparison) { Array.Sort(Items, comparison); _version++; } public void Sort(int index, int count, IComparer comparer) { CheckRange(index, count); Array.Sort(Items, index, count, comparer); _version++; } public T[] ToArray() { T[] t = new T[Count]; Array.Copy(Items, t, Count); return t; } public void TrimExcess() { Capacity = Count; } public bool TrueForAll(Predicate match) { CheckMatch(match); for (int i = 0; i < Count; i++) if (!match(Items[i])) return false; return true; } public int Capacity { get { return Items.Length; } set { if ((uint) value < (uint) Count) throw new ArgumentOutOfRangeException(); Array.Resize(ref Items, value); } } #region Interface implementations. IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion [Serializable] public struct Enumerator : IEnumerator, IDisposable { private ExposedList l; private int next; private int ver; private T current; internal Enumerator(ExposedList l) : this() { this.l = l; ver = l._version; } public void Dispose() { l = null; } private void VerifyState() { if (l == null) throw new ObjectDisposedException(GetType().FullName); if (ver != l._version) throw new InvalidOperationException( "Collection was modified; enumeration operation may not execute."); } public bool MoveNext() { VerifyState(); if (next < 0) return false; if (next < l.Count) { current = l.Items[next++]; return true; } next = -1; return false; } public T Current { get { return current; } } void IEnumerator.Reset() { VerifyState(); next = 0; } object IEnumerator.Current { get { VerifyState(); if (next <= 0) throw new InvalidOperationException(); return current; } } } } }