2006-04-16 Jonathan Chambers <joncham@gmail.com>
[mono.git] / mcs / class / corlib / System.Collections.Generic / List.cs
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
33 #if NET_2_0
34
35 using System.Collections.ObjectModel;
36 using System.Runtime.InteropServices;
37
38 namespace System.Collections.Generic {
39         [Serializable]
40         public class List <T> : IList <T>, IList, ICollection {
41                 T [] _items;
42                 int _size;
43                 int _version;
44                 
45                 static readonly T [] EmptyArray = new T [0]; 
46                 const int DefaultCapacity = 4;
47                 
48                 public List ()
49                 {
50                         _items = EmptyArray;
51                 }
52                 
53                 public List (IEnumerable <T> collection)
54                 {
55                         CheckCollection (collection);
56
57                         // initialize to needed size (if determinable)
58                         ICollection <T> c = collection as ICollection <T>;
59                         if (c == null)
60                         {
61                                 _items = EmptyArray;
62                                 AddEnumerable (collection);
63                         }
64                         else
65                         {
66                                 _items = new T [c.Count];
67                                 AddCollection (c);
68                         }
69                 }
70                 
71                 public List (int capacity)
72                 {
73                         if (capacity < 0)
74                                 throw new ArgumentOutOfRangeException ("capacity");
75                         _items = new T [capacity];
76                 }
77                 
78                 internal List (T [] data, int size)
79                 {
80                         _items = data;
81                         _size = size;
82                 }
83                 public void Add (T item)
84                 {
85                         // If we check to see if we need to grow before trying to grow
86                         // we can speed things up by 25%
87                         if (_size == _items.Length)
88                                 GrowIfNeeded (1);
89                         _items [_size ++] = item;
90                         _version++;
91                 }
92                 
93                 void GrowIfNeeded (int newCount)
94                 {
95                         int minimumSize = _size + newCount;
96                         if (minimumSize > _items.Length)
97                                 Capacity = Math.Max (Math.Max (Capacity * 2, DefaultCapacity), minimumSize);
98                 }
99                 
100                 void CheckRange (int idx, int count)
101                 {
102                         if (idx < 0)
103                                 throw new ArgumentOutOfRangeException ("index");
104                         
105                         if (count < 0)
106                                 throw new ArgumentOutOfRangeException ("count");
107
108                         if ((uint) idx + (uint) count > (uint) _size)
109                                 throw new ArgumentException ("index and count exceed length of list");
110                 }
111                 
112                 void AddCollection (ICollection <T> collection)
113                 {
114                         int collectionCount = collection.Count;
115                         GrowIfNeeded (collectionCount);                  
116                         collection.CopyTo (_items, _size);
117                         _size += collectionCount;
118                 }
119                 void AddEnumerable (IEnumerable <T> enumerable)
120                 {
121                         foreach (T t in enumerable)
122                         {
123                                 Add (t);
124                         }
125                 }
126
127                 public void AddRange (IEnumerable <T> collection)
128                 {
129                         CheckCollection (collection);
130                         
131                         ICollection <T> c = collection as ICollection <T>;
132                         if (c != null)
133                                 AddCollection (c);
134                         else
135                                 AddEnumerable (collection);
136                         _version++;
137                 }
138                 
139                 public ReadOnlyCollection <T> AsReadOnly ()
140                 {
141                         return new ReadOnlyCollection <T> (this);
142                 }
143                 
144                 public int BinarySearch (T item)
145                 {
146                         return Array.BinarySearch <T> (_items, 0, _size, item);
147                 }
148                 
149                 public int BinarySearch (T item, IComparer <T> comparer)
150                 {
151                         return Array.BinarySearch <T> (_items, 0, _size, item, comparer);
152                 }
153                 
154                 public int BinarySearch (int index, int count, T item, IComparer <T> comparer)
155                 {
156                         CheckRange (index, count);
157                         return Array.BinarySearch <T> (_items, index, count, item, comparer);
158                 }
159                 
160                 public void Clear ()
161                 {
162                         Array.Clear (_items, 0, _items.Length);
163                         _size = 0;
164                         _version++;
165                 }
166                 
167                 public bool Contains (T item)
168                 {
169                         return Array.IndexOf<T>(_items, item, 0, _size) != -1;
170                 }
171                 
172                 public List <TOutput> ConvertAll <TOutput> (Converter <T, TOutput> converter)
173                 {
174                         if (converter == null)
175                                 throw new ArgumentNullException ("converter");
176                         List <TOutput> u = new List <TOutput> (_size);
177                         for (int i = 0; i < _size; i++)
178                                 u._items[i] = converter(_items[i]);
179
180                         u._size = _size;
181                         return u;
182                 }
183                 
184                 public void CopyTo (T [] array)
185                 {
186                         Array.Copy (_items, 0, array, 0, _size);
187                 }
188                 
189                 public void CopyTo (T [] array, int arrayIndex)
190                 {
191                         Array.Copy (_items, 0, array, arrayIndex, _size);
192                 }
193                 
194                 public void CopyTo (int index, T [] array, int arrayIndex, int count)
195                 {
196                         CheckRange (index, count);
197                         Array.Copy (_items, index, array, arrayIndex, count);
198                 }
199
200                 public bool Exists (Predicate <T> match)
201                 {
202                         CheckMatch(match);
203                         return GetIndex(0, _size, match) != -1;
204                 }
205                 
206                 public T Find (Predicate <T> match)
207                 {
208                         CheckMatch(match);
209                         int i = GetIndex(0, _size, match);
210                         return (i != -1) ? _items [i] : default (T);
211                 }
212                 void CheckMatch (Predicate <T> match)
213                 {
214                         if (match == null)
215                                 throw new ArgumentNullException ("match");
216                 }
217                 
218                 public List <T> FindAll (Predicate <T> match)
219                 {
220                         this.CheckMatch (match);
221                         if (this._size <= 0x10000) // <= 8 * 1024 * 8 (8k in stack)
222                                 return this.FindAllStackBits (match);
223                         else 
224                                 return this.FindAllList (match);
225                 }
226                 
227                 private List <T> FindAllStackBits (Predicate <T> match)
228                 {
229                         unsafe
230                         {
231                                 uint *bits = stackalloc uint [(this._size / 32) + 1];
232                                 uint *ptr = bits;
233                                 int found = 0;
234                                 uint bitmask = 0x80000000;
235                                 
236                                 for (int i = 0; i < this._size; i++)
237                                 {
238                                         if (match (this._items [i]))
239                                         {
240                                                 (*ptr) = (*ptr) | bitmask;
241                                                 found++;
242                                         }
243                                         
244                                         bitmask = bitmask >> 1;
245                                         if (bitmask == 0)
246                                         {
247                                                 ptr++;
248                                                 bitmask = 0x80000000;
249                                         }
250                                 }
251                                 
252                                 T [] results = new T [found];
253                                 bitmask = 0x80000000;
254                                 ptr = bits;
255                                 int j = 0;
256                                 for (int i = 0; i < this._size && j < found; i++)
257                                 {
258                                         if (((*ptr) & bitmask) == bitmask)
259                                                 results [j++] = this._items [i];
260                                         
261                                         bitmask = bitmask >> 1;
262                                         if (bitmask == 0)
263                                         {
264                                                 ptr++;
265                                                 bitmask = 0x80000000;
266                                         }
267                                 }
268                                 
269                                 return new List <T> (results, found);
270                         }
271                 }
272                 
273                 private List <T> FindAllList (Predicate <T> match)
274                 {
275                         List <T> results = new List <T> ();
276                         for (int i = 0; i < this._size; i++)
277                                 if (match (this._items [i]))
278                                         results.Add (this._items [i]);
279                         
280                         return results;
281                 }
282                 
283                 public int FindIndex (Predicate <T> match)
284                 {
285                         CheckMatch (match);
286                         return GetIndex (0, _size, match);
287                 }
288                 
289                 public int FindIndex (int startIndex, Predicate <T> match)
290                 {
291                         CheckMatch (match);
292                         CheckIndex (startIndex);
293                         return GetIndex (startIndex, _size - startIndex, match);
294                 }
295                 public int FindIndex (int startIndex, int count, Predicate <T> match)
296                 {
297                         CheckMatch (match);
298                         CheckRange (startIndex, count);
299                         return GetIndex (startIndex, count, match);
300                 }
301                 int GetIndex (int startIndex, int count, Predicate <T> match)
302                 {
303                         int end = startIndex + count;
304                         for (int i = startIndex; i < end; i ++)
305                                 if (match (_items [i]))
306                                         return i;
307                                 
308                         return -1;
309                 }
310                 
311                 public T FindLast (Predicate <T> match)
312                 {
313                         CheckMatch (match);
314                         int i = GetLastIndex (0, _size, match);
315                         return i == -1 ? default (T) : this [i];
316                 }
317                 
318                 public int FindLastIndex (Predicate <T> match)
319                 {
320                         CheckMatch (match);
321                         return GetLastIndex (0, _size, match);
322                 }
323                 
324                 public int FindLastIndex (int startIndex, Predicate <T> match)
325                 {
326                         CheckMatch (match);
327                         CheckIndex (startIndex);
328                         return GetLastIndex (0, startIndex + 1, match);
329                 }
330                 
331                 public int FindLastIndex (int startIndex, int count, Predicate <T> match)
332                 {
333                         CheckMatch (match);
334                         int start = startIndex - count + 1;
335                         CheckRange (start, count);
336                         return GetLastIndex (start, count, match);
337                 }
338
339                 int GetLastIndex (int startIndex, int count, Predicate <T> match)
340                 {
341                         // unlike FindLastIndex, takes regular params for search range
342                         for (int i = startIndex + count; i != startIndex;)
343                                 if (match (_items [--i]))
344                                         return i;
345                         return -1;      
346                 }
347                 
348                 public void ForEach (Action <T> action)
349                 {
350                         if (action == null)
351                                 throw new ArgumentNullException ("action");
352                         for(int i=0; i < _size; i++)
353                                 action(_items[i]);
354                 }
355                 
356                 public Enumerator GetEnumerator ()
357                 {
358                         return new Enumerator (this);
359                 }
360                 
361                 public List <T> GetRange (int index, int count)
362                 {
363                         CheckRange (index, count);
364                         T [] tmpArray = new T [count];
365                         Array.Copy (_items, index, tmpArray, 0, count);
366                         return new List <T> (tmpArray, count);
367                 }
368                 
369                 public int IndexOf (T item)
370                 {
371                         return Array.IndexOf<T> (_items, item, 0, _size);
372                 }
373                 
374                 public int IndexOf (T item, int index)
375                 {
376                         CheckIndex (index);
377                         return Array.IndexOf<T> (_items, item, index, _size - index);
378                 }
379                 
380                 public int IndexOf (T item, int index, int count)
381                 {
382                         if (index < 0)
383                                 throw new ArgumentOutOfRangeException ("index");
384                         
385                         if (count < 0)
386                                 throw new ArgumentOutOfRangeException ("count");
387
388                         if ((uint) index + (uint) count > (uint) _size)
389                                 throw new ArgumentOutOfRangeException ("index and count exceed length of list");
390
391                         return Array.IndexOf<T> (_items, item, index, count);
392                 }
393                 
394                 void Shift (int start, int delta)
395                 {
396                         if (delta < 0)
397                                 start -= delta;
398                         
399                         if (start < _size)
400                                 Array.Copy (_items, start, _items, start + delta, _size - start);
401                         
402                         _size += delta;
403                 }
404
405                 void CheckIndex (int index)
406                 {
407                         if (index < 0 || (uint) index > (uint) _size)
408                                 throw new ArgumentOutOfRangeException ("index");
409                 }
410                 
411                 public void Insert (int index, T item)
412                 {
413                         CheckIndex (index);                     
414                         if (_size == _items.Length)
415                                 GrowIfNeeded (1);
416                         Shift (index, 1);
417                         _items[index] = item;
418                         _version++;
419                 }
420
421                 void CheckCollection (IEnumerable <T> collection)
422                 {
423                         if (collection == null)
424                                 throw new ArgumentNullException ("collection");
425                 }
426                 
427                 public void InsertRange (int index, IEnumerable <T> collection)
428                 {
429                         CheckCollection (collection);
430                         CheckIndex (index);
431                         if (collection == this) {
432                                 T[] buffer = new T[_size];
433                                 CopyTo (buffer, 0);
434                                 GrowIfNeeded (_size);
435                                 Shift (index, buffer.Length);
436                                 Array.Copy (buffer, 0, _items, index, buffer.Length);
437                         } else {
438                                 ICollection <T> c = collection as ICollection <T>;
439                                 if (c != null)
440                                         InsertCollection (index, c);
441                                 else
442                                         InsertEnumeration (index, collection);
443                         }
444                         _version++;
445                 }
446
447                 void InsertCollection (int index, ICollection <T> collection)
448                 {
449                         int collectionCount = collection.Count;
450                         GrowIfNeeded (collectionCount);
451                         
452                         Shift (index, collectionCount);
453                         collection.CopyTo (_items, index);
454                 }
455                 void InsertEnumeration (int index, IEnumerable <T> enumerable)
456                 {
457                         foreach (T t in enumerable)
458                                 Insert (index++, t);            
459                 }
460
461                 public int LastIndexOf (T item)
462                 {
463                         return Array.LastIndexOf<T> (_items, item, _size - 1, _size);
464                 }
465                 
466                 public int LastIndexOf (T item, int index)
467                 {
468                         CheckIndex (index);
469                         return Array.LastIndexOf<T> (_items, item, index, index + 1);
470                 }
471                 
472                 public int LastIndexOf (T item, int index, int count)
473                 {
474                         if (index < 0)
475                                 throw new ArgumentOutOfRangeException ("index", index, "index is negative");
476
477                         if (count < 0)
478                                 throw new ArgumentOutOfRangeException ("count", count, "count is negative");
479
480                         if (index - count + 1 < 0)
481                                 throw new ArgumentOutOfRangeException ("cound", count, "count is too large");
482
483                         return Array.LastIndexOf<T> (_items, item, index, count);
484                 }
485                 
486                 public bool Remove (T item)
487                 {
488                         int loc = IndexOf (item);
489                         if (loc != -1)
490                                 RemoveAt (loc);
491                         
492                         return loc != -1;
493                 }
494                 
495                 public int RemoveAll (Predicate <T> match)
496                 {
497                         CheckMatch(match);
498                         int i = 0;
499                         int j = 0;
500
501                         // Find the first item to remove
502                         for (i = 0; i < _size; i++)
503                                 if (match(_items[i]))
504                                         break;
505
506                         if (i == _size)
507                                 return 0;
508
509                         _version++;
510
511                         // Remove any additional items
512                         for (j = i + 1; j < _size; j++)
513                         {
514                                 if (!match(_items[j]))
515                                         _items[i++] = _items[j];
516                         }
517
518                         _size = i;
519                         return (j - i);
520                 }
521                 
522                 public void RemoveAt (int index)
523                 {
524                         if (index < 0 || (uint)index >= (uint)_size)
525                                 throw new ArgumentOutOfRangeException("index");
526                         Shift (index, -1);
527                         Array.Clear (_items, _size, 1);
528                         _version++;
529                 }
530                 
531                 public void RemoveRange (int index, int count)
532                 {
533                         CheckRange (index, count);
534                         if (count > 0) {
535                                 Shift (index, -count);
536                                 Array.Clear (_items, _size, count);
537                                 _version++;
538                         }
539                 }
540                 
541                 public void Reverse ()
542                 {
543                         Array.Reverse (_items, 0, _size);
544                         _version++;
545                 }
546                 public void Reverse (int index, int count)
547                 {
548                         CheckRange (index, count);
549                         Array.Reverse (_items, index, count);
550                         _version++;
551                 }
552                 
553                 public void Sort ()
554                 {
555                         Array.Sort<T> (_items, 0, _size, Comparer <T>.Default);
556                         _version++;
557                 }
558                 public void Sort (IComparer <T> comparer)
559                 {
560                         Array.Sort<T> (_items, 0, _size, comparer);
561                         _version++;
562                 }
563
564                 public void Sort (Comparison <T> comparison)
565                 {
566                         Array.Sort<T> (_items, _size, comparison);
567                         _version++;
568                 }
569                 
570                 public void Sort (int index, int count, IComparer <T> comparer)
571                 {
572                         CheckRange (index, count);
573                         Array.Sort<T> (_items, index, count, comparer);
574                         _version++;
575                 }
576
577                 public T [] ToArray ()
578                 {
579                         T [] t = new T [_size];
580                         Array.Copy (_items, t, _size);
581                         
582                         return t;
583                 }
584                 
585                 public void TrimExcess ()
586                 {
587                         Capacity = _size;
588                 }
589                 
590                 public bool TrueForAll (Predicate <T> match)
591                 {
592                         CheckMatch (match);
593
594                         for (int i = 0; i < _size; i++)
595                                 if (!match(_items[i]))
596                                         return false;
597                                 
598                         return true;
599                 }
600                 
601                 public int Capacity {
602                         get { 
603                                 return _items.Length;
604                         }
605                         set {
606                                 if ((uint) value < (uint) _size)
607                                         throw new ArgumentOutOfRangeException ();
608                                 
609                                 Array.Resize (ref _items, value);
610                         }
611                 }
612                 
613                 public int Count {
614                         get { return _size; }
615                 }
616                 
617                 public T this [int index] {
618                         get {
619                                 if ((uint) index >= (uint) _size)
620                                         throw new ArgumentOutOfRangeException ("index");
621                                 return _items [index];
622                         }
623                         set {
624                                 CheckIndex (index);
625                                 if ((uint) index == (uint) _size)
626                                         throw new ArgumentOutOfRangeException ("index");
627                                 _items [index] = value;
628                         }
629                 }
630                 
631 #region Interface implementations.
632                 IEnumerator <T> IEnumerable <T>.GetEnumerator ()
633                 {
634                         return GetEnumerator ();
635                 }
636                 
637                 void ICollection.CopyTo (Array array, int arrayIndex)
638                 {
639                         Array.Copy (_items, 0, array, arrayIndex, _size);
640                 }
641                 
642                 IEnumerator IEnumerable.GetEnumerator ()
643                 {
644                         return GetEnumerator ();
645                 }
646                 
647                 int IList.Add (object item)
648                 {
649                         try {
650                                 Add ((T) item);
651                         } catch (InvalidCastException) {
652                                 throw new ArgumentException("item");
653                         }
654                         return _size - 1;
655                 }
656                 
657                 bool IList.Contains (object item)
658                 {
659                         try {
660                                 return Contains ((T) item);
661                         } catch (InvalidCastException) {
662                                 return false;
663                         }
664                 }
665                 
666                 int IList.IndexOf (object item)
667                 {
668                         try {
669                                 return IndexOf((T) item);
670                         } catch (InvalidCastException) {
671                                 return -1;
672                         }
673                 }
674                 
675                 void IList.Insert (int index, object item)
676                 {
677                         // We need to check this first because, even if the
678                         // item is null or not the correct type, we need to
679                         // return an ArgumentOutOfRange exception if the
680                         // index is out of range
681                         CheckIndex (index);
682                         try {
683                                 Insert (index, (T) item);
684                         } catch (InvalidCastException) {
685                                 throw new ArgumentException("item");
686                         }
687                 }
688                 
689                 void IList.Remove (object item)
690                 {
691                         try {
692                                 Remove ((T) item);
693                         } catch (InvalidCastException) {
694                                 // Swallow the exception--if we
695                                 // can't cast to the correct type
696                                 // then we've already "succeeded"
697                                 // in removing the item from the
698                                 // List.
699                         }
700                 }
701                 
702                 bool ICollection <T>.IsReadOnly {
703                         get { return false; }
704                 }
705                 bool ICollection.IsSynchronized {
706                         get { return false; }
707                 }
708                 
709                 object ICollection.SyncRoot {
710                         get { return this; }
711                 }
712                 bool IList.IsFixedSize {
713                         get { return false; }
714                 }
715                 
716                 bool IList.IsReadOnly {
717                         get { return false; }
718                 }
719                 
720                 object IList.this [int index] {
721                         get { return this [index]; }
722                         set { this [index] = (T) value; }
723                 }
724 #endregion
725                                 
726                 [Serializable]
727                 public struct Enumerator : IEnumerator <T>, IDisposable {
728                         const int NOT_STARTED = -2;
729                         
730                         // this MUST be -1, because we depend on it in move next.
731                         // we just decr the size, so, 0 - 1 == FINISHED
732                         const int FINISHED = -1;
733                         
734                         List <T> l;
735                         int idx;
736                         int ver;
737                         
738                         internal Enumerator (List <T> l)
739                         {
740                                 this.l = l;
741                                 idx = NOT_STARTED;
742                                 ver = l._version;
743                         }
744                         
745                         // for some fucked up reason, MSFT added a useless dispose to this class
746                         // It means that in foreach, we must still do a try/finally. Broken, very
747                         // broken.
748                         public void Dispose ()
749                         {
750                                 idx = NOT_STARTED;
751                         }
752                         
753                         public bool MoveNext ()
754                         {
755                                 if (ver != l._version)
756                                         throw new InvalidOperationException ("Collection was modified;"
757                                                 + "enumeration operation may not execute.");
758                                 
759                                 if (idx == NOT_STARTED)
760                                         idx = l._size;
761                                 
762                                 return idx != FINISHED && -- idx != FINISHED;
763                         }
764                         
765                         public T Current {
766                                 get {
767                                         if (idx < 0)
768                                                 throw new InvalidOperationException ();
769                                         
770                                         return l._items [l._size - 1 - idx];
771                                 }
772                         }
773                         
774                         void IEnumerator.Reset ()
775                         {
776                                 if (ver != l._version)
777                                         throw new InvalidOperationException ("Collection was modified;"
778                                                 + "enumeration operation may not execute.");
779                                 
780                                 idx = NOT_STARTED;
781                         }
782                         
783                         object IEnumerator.Current {
784                                 get { return Current; }
785                         }
786                 }
787         }
788 }
789 #endif