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