ExposedList.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. //
  2. // System.Collections.Generic.List
  3. //
  4. // Authors:
  5. // Ben Maurer (bmaurer@ximian.com)
  6. // Martin Baulig (martin@ximian.com)
  7. // Carlos Alberto Cortez (calberto.cortez@gmail.com)
  8. // David Waite (mass@akuma.org)
  9. //
  10. // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
  11. // Copyright (C) 2005 David Waite
  12. //
  13. // Permission is hereby granted, free of charge, to any person obtaining
  14. // a copy of this software and associated documentation files (the
  15. // "Software"), to deal in the Software without restriction, including
  16. // without limitation the rights to use, copy, modify, merge, publish,
  17. // distribute, sublicense, and/or sell copies of the Software, and to
  18. // permit persons to whom the Software is furnished to do so, subject to
  19. // the following conditions:
  20. //
  21. // The above copyright notice and this permission notice shall be
  22. // included in all copies or substantial portions of the Software.
  23. //
  24. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  25. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  26. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  27. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  28. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  29. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  30. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  31. //
  32. using System;
  33. using System.Collections;
  34. using System.Collections.Generic;
  35. using System.Diagnostics;
  36. namespace Spine {
  37. [DebuggerDisplay("Count={Count}")]
  38. public class ExposedList<T> : IEnumerable<T> {
  39. public T[] Items;
  40. public int Count;
  41. private const int DefaultCapacity = 4;
  42. private static readonly T[] EmptyArray = new T[0];
  43. private int version;
  44. public ExposedList () {
  45. Items = EmptyArray;
  46. }
  47. public ExposedList (IEnumerable<T> collection) {
  48. CheckCollection(collection);
  49. // initialize to needed size (if determinable)
  50. ICollection<T> c = collection as ICollection<T>;
  51. if (c == null) {
  52. Items = EmptyArray;
  53. AddEnumerable(collection);
  54. } else {
  55. Items = new T[c.Count];
  56. AddCollection(c);
  57. }
  58. }
  59. public ExposedList (int capacity) {
  60. if (capacity < 0)
  61. throw new ArgumentOutOfRangeException("capacity");
  62. Items = new T[capacity];
  63. }
  64. internal ExposedList (T[] data, int size) {
  65. Items = data;
  66. Count = size;
  67. }
  68. public void Add (T item) {
  69. // If we check to see if we need to grow before trying to grow
  70. // we can speed things up by 25%
  71. if (Count == Items.Length)
  72. GrowIfNeeded(1);
  73. Items[Count++] = item;
  74. version++;
  75. }
  76. public void GrowIfNeeded (int addedCount) {
  77. int minimumSize = Count + addedCount;
  78. if (minimumSize > Items.Length)
  79. Capacity = Math.Max(Math.Max(Capacity * 2, DefaultCapacity), minimumSize);
  80. }
  81. public ExposedList<T> Resize (int newSize) {
  82. int itemsLength = Items.Length;
  83. var oldItems = Items;
  84. if (newSize > itemsLength) {
  85. Array.Resize(ref Items, newSize);
  86. // var newItems = new T[newSize];
  87. // Array.Copy(oldItems, newItems, Count);
  88. // Items = newItems;
  89. } else if (newSize < itemsLength) {
  90. // Allow nulling of T reference type to allow GC.
  91. for (int i = newSize; i < itemsLength; i++)
  92. oldItems[i] = default(T);
  93. }
  94. Count = newSize;
  95. return this;
  96. }
  97. public void EnsureCapacity (int min) {
  98. if (Items.Length < min) {
  99. int newCapacity = Items.Length == 0 ? DefaultCapacity : Items.Length * 2;
  100. //if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength;
  101. if (newCapacity < min) newCapacity = min;
  102. Capacity = newCapacity;
  103. }
  104. }
  105. private void CheckRange (int index, int count) {
  106. if (index < 0)
  107. throw new ArgumentOutOfRangeException("index");
  108. if (count < 0)
  109. throw new ArgumentOutOfRangeException("count");
  110. if ((uint)index + (uint)count > (uint)Count)
  111. throw new ArgumentException("index and count exceed length of list");
  112. }
  113. private void AddCollection (ICollection<T> collection) {
  114. int collectionCount = collection.Count;
  115. if (collectionCount == 0)
  116. return;
  117. GrowIfNeeded(collectionCount);
  118. collection.CopyTo(Items, Count);
  119. Count += collectionCount;
  120. }
  121. private void AddEnumerable (IEnumerable<T> enumerable) {
  122. foreach (T t in enumerable) {
  123. Add(t);
  124. }
  125. }
  126. // Additional overload provided because ExposedList<T> only implements IEnumerable<T>,
  127. // leading to sub-optimal behavior: It grows multiple times as it assumes not
  128. // to know the final size ahead of insertion.
  129. public void AddRange (ExposedList<T> list) {
  130. CheckCollection(list);
  131. int collectionCount = list.Count;
  132. if (collectionCount == 0)
  133. return;
  134. GrowIfNeeded(collectionCount);
  135. list.CopyTo(Items, Count);
  136. Count += collectionCount;
  137. version++;
  138. }
  139. public void AddRange (IEnumerable<T> collection) {
  140. CheckCollection(collection);
  141. ICollection<T> c = collection as ICollection<T>;
  142. if (c != null)
  143. AddCollection(c);
  144. else
  145. AddEnumerable(collection);
  146. version++;
  147. }
  148. public int BinarySearch (T item) {
  149. return Array.BinarySearch<T>(Items, 0, Count, item);
  150. }
  151. public int BinarySearch (T item, IComparer<T> comparer) {
  152. return Array.BinarySearch<T>(Items, 0, Count, item, comparer);
  153. }
  154. public int BinarySearch (int index, int count, T item, IComparer<T> comparer) {
  155. CheckRange(index, count);
  156. return Array.BinarySearch<T>(Items, index, count, item, comparer);
  157. }
  158. public void Clear (bool clearArray = true) {
  159. if (clearArray)
  160. Array.Clear(Items, 0, Items.Length);
  161. Count = 0;
  162. version++;
  163. }
  164. public bool Contains (T item) {
  165. return Array.IndexOf<T>(Items, item, 0, Count) != -1;
  166. }
  167. public ExposedList<TOutput> ConvertAll<TOutput> (Converter<T, TOutput> converter) {
  168. if (converter == null)
  169. throw new ArgumentNullException("converter");
  170. ExposedList<TOutput> u = new ExposedList<TOutput>(Count);
  171. for (int i = 0; i < Count; i++)
  172. u.Items[i] = converter(Items[i]);
  173. u.Count = Count;
  174. return u;
  175. }
  176. public void CopyTo (T[] array) {
  177. Array.Copy(Items, 0, array, 0, Count);
  178. }
  179. public void CopyTo (T[] array, int arrayIndex) {
  180. Array.Copy(Items, 0, array, arrayIndex, Count);
  181. }
  182. public void CopyTo (int index, T[] array, int arrayIndex, int count) {
  183. CheckRange(index, count);
  184. Array.Copy(Items, index, array, arrayIndex, count);
  185. }
  186. public bool Exists (Predicate<T> match) {
  187. CheckMatch(match);
  188. return GetIndex(0, Count, match) != -1;
  189. }
  190. public T Find (Predicate<T> match) {
  191. CheckMatch(match);
  192. int i = GetIndex(0, Count, match);
  193. return (i != -1) ? Items[i] : default(T);
  194. }
  195. private static void CheckMatch (Predicate<T> match) {
  196. if (match == null)
  197. throw new ArgumentNullException("match");
  198. }
  199. public ExposedList<T> FindAll (Predicate<T> match) {
  200. CheckMatch(match);
  201. return FindAllList(match);
  202. }
  203. private ExposedList<T> FindAllList (Predicate<T> match) {
  204. ExposedList<T> results = new ExposedList<T>();
  205. for (int i = 0; i < Count; i++)
  206. if (match(Items[i]))
  207. results.Add(Items[i]);
  208. return results;
  209. }
  210. public int FindIndex (Predicate<T> match) {
  211. CheckMatch(match);
  212. return GetIndex(0, Count, match);
  213. }
  214. public int FindIndex (int startIndex, Predicate<T> match) {
  215. CheckMatch(match);
  216. CheckIndex(startIndex);
  217. return GetIndex(startIndex, Count - startIndex, match);
  218. }
  219. public int FindIndex (int startIndex, int count, Predicate<T> match) {
  220. CheckMatch(match);
  221. CheckRange(startIndex, count);
  222. return GetIndex(startIndex, count, match);
  223. }
  224. private int GetIndex (int startIndex, int count, Predicate<T> match) {
  225. int end = startIndex + count;
  226. for (int i = startIndex; i < end; i++)
  227. if (match(Items[i]))
  228. return i;
  229. return -1;
  230. }
  231. public T FindLast (Predicate<T> match) {
  232. CheckMatch(match);
  233. int i = GetLastIndex(0, Count, match);
  234. return i == -1 ? default(T) : Items[i];
  235. }
  236. public int FindLastIndex (Predicate<T> match) {
  237. CheckMatch(match);
  238. return GetLastIndex(0, Count, match);
  239. }
  240. public int FindLastIndex (int startIndex, Predicate<T> match) {
  241. CheckMatch(match);
  242. CheckIndex(startIndex);
  243. return GetLastIndex(0, startIndex + 1, match);
  244. }
  245. public int FindLastIndex (int startIndex, int count, Predicate<T> match) {
  246. CheckMatch(match);
  247. int start = startIndex - count + 1;
  248. CheckRange(start, count);
  249. return GetLastIndex(start, count, match);
  250. }
  251. private int GetLastIndex (int startIndex, int count, Predicate<T> match) {
  252. // unlike FindLastIndex, takes regular params for search range
  253. for (int i = startIndex + count; i != startIndex; )
  254. if (match(Items[--i]))
  255. return i;
  256. return -1;
  257. }
  258. public void ForEach (Action<T> action) {
  259. if (action == null)
  260. throw new ArgumentNullException("action");
  261. for (int i = 0; i < Count; i++)
  262. action(Items[i]);
  263. }
  264. public Enumerator GetEnumerator () {
  265. return new Enumerator(this);
  266. }
  267. public ExposedList<T> GetRange (int index, int count) {
  268. CheckRange(index, count);
  269. T[] tmpArray = new T[count];
  270. Array.Copy(Items, index, tmpArray, 0, count);
  271. return new ExposedList<T>(tmpArray, count);
  272. }
  273. public int IndexOf (T item) {
  274. return Array.IndexOf<T>(Items, item, 0, Count);
  275. }
  276. public int IndexOf (T item, int index) {
  277. CheckIndex(index);
  278. return Array.IndexOf<T>(Items, item, index, Count - index);
  279. }
  280. public int IndexOf (T item, int index, int count) {
  281. if (index < 0)
  282. throw new ArgumentOutOfRangeException("index");
  283. if (count < 0)
  284. throw new ArgumentOutOfRangeException("count");
  285. if ((uint)index + (uint)count > (uint)Count)
  286. throw new ArgumentOutOfRangeException("index and count exceed length of list");
  287. return Array.IndexOf<T>(Items, item, index, count);
  288. }
  289. private void Shift (int start, int delta) {
  290. if (delta < 0)
  291. start -= delta;
  292. if (start < Count)
  293. Array.Copy(Items, start, Items, start + delta, Count - start);
  294. Count += delta;
  295. if (delta < 0)
  296. Array.Clear(Items, Count, -delta);
  297. }
  298. private void CheckIndex (int index) {
  299. if (index < 0 || (uint)index > (uint)Count)
  300. throw new ArgumentOutOfRangeException("index");
  301. }
  302. public void Insert (int index, T item) {
  303. CheckIndex(index);
  304. if (Count == Items.Length)
  305. GrowIfNeeded(1);
  306. Shift(index, 1);
  307. Items[index] = item;
  308. version++;
  309. }
  310. private void CheckCollection (IEnumerable<T> collection) {
  311. if (collection == null)
  312. throw new ArgumentNullException("collection");
  313. }
  314. public void InsertRange (int index, IEnumerable<T> collection) {
  315. CheckCollection(collection);
  316. CheckIndex(index);
  317. if (collection == this) {
  318. T[] buffer = new T[Count];
  319. CopyTo(buffer, 0);
  320. GrowIfNeeded(Count);
  321. Shift(index, buffer.Length);
  322. Array.Copy(buffer, 0, Items, index, buffer.Length);
  323. } else {
  324. ICollection<T> c = collection as ICollection<T>;
  325. if (c != null)
  326. InsertCollection(index, c);
  327. else
  328. InsertEnumeration(index, collection);
  329. }
  330. version++;
  331. }
  332. private void InsertCollection (int index, ICollection<T> collection) {
  333. int collectionCount = collection.Count;
  334. GrowIfNeeded(collectionCount);
  335. Shift(index, collectionCount);
  336. collection.CopyTo(Items, index);
  337. }
  338. private void InsertEnumeration (int index, IEnumerable<T> enumerable) {
  339. foreach (T t in enumerable)
  340. Insert(index++, t);
  341. }
  342. public int LastIndexOf (T item) {
  343. return Array.LastIndexOf<T>(Items, item, Count - 1, Count);
  344. }
  345. public int LastIndexOf (T item, int index) {
  346. CheckIndex(index);
  347. return Array.LastIndexOf<T>(Items, item, index, index + 1);
  348. }
  349. public int LastIndexOf (T item, int index, int count) {
  350. if (index < 0)
  351. throw new ArgumentOutOfRangeException("index", index, "index is negative");
  352. if (count < 0)
  353. throw new ArgumentOutOfRangeException("count", count, "count is negative");
  354. if (index - count + 1 < 0)
  355. throw new ArgumentOutOfRangeException("count", count, "count is too large");
  356. return Array.LastIndexOf<T>(Items, item, index, count);
  357. }
  358. public bool Remove (T item) {
  359. int loc = IndexOf(item);
  360. if (loc != -1)
  361. RemoveAt(loc);
  362. return loc != -1;
  363. }
  364. public int RemoveAll (Predicate<T> match) {
  365. CheckMatch(match);
  366. int i = 0;
  367. int j = 0;
  368. // Find the first item to remove
  369. for (i = 0; i < Count; i++)
  370. if (match(Items[i]))
  371. break;
  372. if (i == Count)
  373. return 0;
  374. version++;
  375. // Remove any additional items
  376. for (j = i + 1; j < Count; j++) {
  377. if (!match(Items[j]))
  378. Items[i++] = Items[j];
  379. }
  380. if (j - i > 0)
  381. Array.Clear(Items, i, j - i);
  382. Count = i;
  383. return (j - i);
  384. }
  385. public void RemoveAt (int index) {
  386. if (index < 0 || (uint)index >= (uint)Count)
  387. throw new ArgumentOutOfRangeException("index");
  388. Shift(index, -1);
  389. Array.Clear(Items, Count, 1);
  390. version++;
  391. }
  392. // Spine Added Method
  393. // Based on Stack<T>.Pop(); https://referencesource.microsoft.com/#mscorlib/system/collections/stack.cs
  394. /// <summary>Pops the last item of the list. If the list is empty, Pop throws an InvalidOperationException.</summary>
  395. public T Pop () {
  396. if (Count == 0)
  397. throw new InvalidOperationException("List is empty. Nothing to pop.");
  398. int i = Count - 1;
  399. T item = Items[i];
  400. Items[i] = default(T);
  401. Count--;
  402. version++;
  403. return item;
  404. }
  405. public void RemoveRange (int index, int count) {
  406. CheckRange(index, count);
  407. if (count > 0) {
  408. Shift(index, -count);
  409. Array.Clear(Items, Count, count);
  410. version++;
  411. }
  412. }
  413. public void Reverse () {
  414. Array.Reverse(Items, 0, Count);
  415. version++;
  416. }
  417. public void Reverse (int index, int count) {
  418. CheckRange(index, count);
  419. Array.Reverse(Items, index, count);
  420. version++;
  421. }
  422. public void Sort () {
  423. Array.Sort<T>(Items, 0, Count, Comparer<T>.Default);
  424. version++;
  425. }
  426. public void Sort (IComparer<T> comparer) {
  427. Array.Sort<T>(Items, 0, Count, comparer);
  428. version++;
  429. }
  430. public void Sort (Comparison<T> comparison) {
  431. Array.Sort<T>(Items, comparison);
  432. version++;
  433. }
  434. public void Sort (int index, int count, IComparer<T> comparer) {
  435. CheckRange(index, count);
  436. Array.Sort<T>(Items, index, count, comparer);
  437. version++;
  438. }
  439. public T[] ToArray () {
  440. T[] t = new T[Count];
  441. Array.Copy(Items, t, Count);
  442. return t;
  443. }
  444. public void TrimExcess () {
  445. Capacity = Count;
  446. }
  447. public bool TrueForAll (Predicate<T> match) {
  448. CheckMatch(match);
  449. for (int i = 0; i < Count; i++)
  450. if (!match(Items[i]))
  451. return false;
  452. return true;
  453. }
  454. public int Capacity {
  455. get {
  456. return Items.Length;
  457. }
  458. set {
  459. if ((uint)value < (uint)Count)
  460. throw new ArgumentOutOfRangeException();
  461. Array.Resize(ref Items, value);
  462. }
  463. }
  464. #region Interface implementations.
  465. IEnumerator<T> IEnumerable<T>.GetEnumerator () {
  466. return GetEnumerator();
  467. }
  468. IEnumerator IEnumerable.GetEnumerator () {
  469. return GetEnumerator();
  470. }
  471. #endregion
  472. public struct Enumerator : IEnumerator<T>, IDisposable {
  473. private ExposedList<T> l;
  474. private int next;
  475. private int ver;
  476. private T current;
  477. internal Enumerator (ExposedList<T> l)
  478. : this() {
  479. this.l = l;
  480. ver = l.version;
  481. }
  482. public void Dispose () {
  483. l = null;
  484. }
  485. private void VerifyState () {
  486. if (l == null)
  487. throw new ObjectDisposedException(GetType().FullName);
  488. if (ver != l.version)
  489. throw new InvalidOperationException(
  490. "Collection was modified; enumeration operation may not execute.");
  491. }
  492. public bool MoveNext () {
  493. VerifyState();
  494. if (next < 0)
  495. return false;
  496. if (next < l.Count) {
  497. current = l.Items[next++];
  498. return true;
  499. }
  500. next = -1;
  501. return false;
  502. }
  503. public T Current {
  504. get {
  505. return current;
  506. }
  507. }
  508. void IEnumerator.Reset () {
  509. VerifyState();
  510. next = 0;
  511. }
  512. object IEnumerator.Current {
  513. get {
  514. VerifyState();
  515. if (next <= 0)
  516. throw new InvalidOperationException();
  517. return current;
  518. }
  519. }
  520. }
  521. }
  522. }