Copy: handle overlapped copies
[mono.git] / mcs / class / corlib / System / Array.cs
1 //
2 // System.Array.cs
3 //
4 // Authors:
5 //   Joe Shaw (joe@ximian.com)
6 //   Martin Baulig (martin@gnome.org)
7 //   Dietmar Maurer (dietmar@ximian.com)
8 //
9 // (C) 2001 Ximian, Inc.  http://www.ximian.com
10 //
11
12 using System.Collections;
13 using System.Runtime.CompilerServices;
14
15 namespace System
16 {
17
18         [Serializable]
19         public abstract class Array : ICloneable, ICollection, IList, IEnumerable
20         {
21                 // Constructor          
22                 private Array ()
23                 {
24                         /* empty */
25                 }
26                 
27                 // Properties
28                 public int Length 
29                 {
30                         get 
31                         {
32                                 int length = this.GetLength (0);
33
34                                 for (int i = 1; i < this.Rank; i++) {
35                                         length *= this.GetLength (i); 
36                                 }
37                                 
38                                 return length;
39                         }
40                 }
41
42                 public int Rank 
43                 {
44                         get
45                         {
46                                 return this.GetRank ();
47                         }
48                 }
49
50                 // IList interface
51                 public object this [int index] {
52                         get {
53                                 return GetValueImpl (index);
54                         } 
55                         set {
56                                 SetValueImpl (value, index);
57                         }
58                 }
59
60                 int IList.Add (object value) {
61                         throw new NotSupportedException ();
62                 }
63
64                 void IList.Clear () {
65                         Array.Clear (this, this.GetLowerBound(0), this.Length);
66                 }
67
68                 bool IList.Contains (object value) {
69                         int length = this.Length;
70                         for (int i = 0; i < length; i++) {
71                                 if (value.Equals (this.GetValueImpl (i)))
72                                         return true;
73                         }
74                         return false;
75                 }
76
77                 int IList.IndexOf (object value) {
78                         if (this.Rank > 1)
79                                 throw new RankException ();
80
81                         int length = this.Length;
82                         for (int i = 0; i < length; i++) {
83                                 if (value.Equals (this.GetValueImpl (i)))
84                                         // array index may not be zero-based.
85                                         // use lower bound
86                                         return i + this.GetLowerBound (0);
87                         }
88
89                         int retVal;
90                         unchecked {
91                                 // lower bound may be MinValue
92                                 retVal = this.GetLowerBound (0) - 1;
93                         }
94
95                         return retVal;
96                 }
97
98                 void IList.Insert (int index, object value) {
99                         throw new NotSupportedException ();
100                 }
101
102                 void IList.Remove (object value) {
103                         throw new NotSupportedException ();
104                 }
105
106                 void IList.RemoveAt (int index) {
107                         throw new NotSupportedException ();
108                 }
109
110                 // InternalCall Methods
111                 
112                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
113                 private extern int GetRank ();
114
115                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
116                 public extern int GetLength (int dimension);
117
118                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
119                 public extern int GetLowerBound (int dimension);
120
121                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
122                 public extern object GetValue (int[] idxs);
123
124                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
125                 public extern void SetValue (object value, int[] idxs);
126
127                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
128                 internal extern object GetValueImpl (int pos);
129
130                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
131                 internal extern void SetValueImpl (object value, int pos);
132
133                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
134                 internal extern static void FastCopy (Array source, int source_idx, Array dest, int dest_idx, int length);\r
135 \r
136                 [MethodImplAttribute(MethodImplOptions.InternalCall)]\r
137                 internal extern static Array CreateInstanceImpl(Type elementType, int[] lengths, int [] bounds);
138
139                 // Properties
140                 public virtual int Count {
141                         get {
142                                 return Length;
143                         }
144                 }
145
146                 [MonoTODO]
147                 public virtual bool IsSynchronized {
148                         get {
149                                 // FIXME?
150                                 return false;
151                         }
152                 }
153
154                 [MonoTODO]
155                 public virtual object SyncRoot {
156                         get {
157                                 // FIXME
158                                 return null;
159                         }
160                 }
161
162                 public virtual bool IsFixedSize 
163                 {
164                         get {
165                                 return true;
166                         }
167                 }
168
169                 public virtual bool IsReadOnly 
170                 {
171                         get{
172                                 return false;
173                         }
174                 }
175
176                 public virtual IEnumerator GetEnumerator ()
177                 {
178                         return new SimpleEnumerator(this);
179                 }
180
181                 public int GetUpperBound (int dimension)
182                 {
183                         return GetLowerBound (dimension) +
184                                 GetLength (dimension) - 1;
185                 }
186
187                 public object GetValue (int idx)
188                 {
189                         int[] ind = new int [1];
190
191                         ind [0] = idx;
192
193                         return GetValue (ind);
194                 }
195
196                 public object GetValue (int idx1, int idx2)
197                 {
198                         int[] ind = new int [2];
199
200                         ind [0] = idx1;
201                         ind [1] = idx2;
202
203                         return GetValue (ind);
204                 }
205
206                 public object GetValue (int idx1, int idx2, int idx3)
207                 {
208                         int[] ind = new int [3];
209
210                         ind [0] = idx1;
211                         ind [1] = idx2;
212                         ind [2] = idx3;
213
214                         return GetValue (ind);
215                 }
216
217                 // This function is currently unused, but just in case we need it later on ... */
218                 internal int IndexToPos (int[] idxs)
219                 {
220                         if (idxs == null)
221                                 throw new ArgumentNullException ();
222
223                         if ((idxs.Rank != 1) || (idxs.Length != Rank))
224                                 throw new ArgumentException ();
225
226                         if ((idxs [0] < GetLowerBound (0)) || (idxs [0] > GetUpperBound (0)))
227                                 throw new IndexOutOfRangeException();
228
229                         int pos = idxs [0] - GetLowerBound (0);
230                         for (int i = 1; i < Rank; i++) {
231                                 if ((idxs [i] < GetLowerBound (i)) || (idxs [i] > GetUpperBound (i)))
232                                         throw new IndexOutOfRangeException();
233
234                                 pos *= GetLength (i);
235                                 pos += idxs [i] - GetLowerBound (i);
236                         }
237
238                         return pos;
239                 }
240
241                 public void SetValue (object value, int idx)
242                 {
243                         int[] ind = new int [1];
244
245                         ind [0] = idx;
246
247                         SetValue (value, ind);
248                 }
249                 
250                 public void SetValue (object value, int idx1, int idx2)
251                 {
252                         int[] ind = new int [2];
253
254                         ind [0] = idx1;
255                         ind [1] = idx2;
256
257                         SetValue (value, ind);
258                 }
259
260                 public void SetValue (object value, int idx1, int idx2, int idx3)
261                 {
262                         int[] ind = new int [3];
263
264                         ind [0] = idx1;
265                         ind [1] = idx2;
266                         ind [2] = idx3;
267
268                         SetValue (value, ind);
269                 }
270
271                 public static Array CreateInstance(Type elementType, int length)
272                 {
273                         int[] lengths = new int [1];
274                         int[] bounds = null;
275                         
276                         lengths [0] = length;
277                         
278                         return CreateInstanceImpl (elementType, lengths, bounds);
279                 }
280                 
281                 public static Array CreateInstance(Type elementType, int l1, int l2)
282                 {
283                         int[] lengths = new int [2];
284                         int[] bounds = null;
285                         
286                         lengths [0] = l1;
287                         lengths [1] = l2;
288                         
289                         return CreateInstanceImpl (elementType, lengths, bounds);
290                 }
291
292                 public static Array CreateInstance(Type elementType, int l1, int l2, int l3)
293                 {
294                         int[] lengths = new int [3];
295                         int[] bounds = null;
296                         
297                         lengths [0] = l1;
298                         lengths [1] = l2;
299                         lengths [2] = l3;
300                 
301                         return CreateInstanceImpl (elementType, lengths, bounds);
302                 }
303
304                 public static Array CreateInstance(Type elementType, int[] lengths)
305                 {
306                         int[] bounds = null;
307                         
308                         return CreateInstanceImpl (elementType, lengths, bounds);
309                 }
310
311                 public static Array CreateInstance(Type elementType, int[] lengths, int [] bounds)
312                 {
313                         if (bounds == null)
314                                 throw new ArgumentNullException("bounds");
315
316                         return CreateInstanceImpl (elementType, lengths, bounds);
317                 }
318
319                 
320                 public static int BinarySearch (Array array, object value)
321                 {
322                         return BinarySearch (array, array.GetLowerBound (0), array.GetLength (0),
323                                              value, null);
324                 }
325
326                 public static int BinarySearch (Array array, object value, IComparer comparer)
327                 {
328                         return BinarySearch (array, array.GetLowerBound (0), array.GetLength (0),
329                                              value, comparer);
330                 }
331
332                 public static int BinarySearch (Array array, int index, int length, object value)
333                 {
334                         return BinarySearch (array, index, length, value, null);
335                 }
336
337                 public static int BinarySearch (Array array, int index,
338                                                 int length, object value,
339                                                 IComparer comparer)
340                 {
341                         if (array == null)
342                                 throw new ArgumentNullException ("array");
343
344                         if (array.Rank > 1)
345                                 throw new RankException ();
346
347                         if (index < array.GetLowerBound (0) || length < 0)
348                                 throw new ArgumentOutOfRangeException ("index");
349
350                         if (index + length > array.GetUpperBound (0) + 1)
351                                 throw new ArgumentException ("length");
352
353                         // cache this in case we need it
354                         IComparable valueCompare = value as IComparable;
355
356                         int iMin = index;
357                         int iMax = index + length;
358                         int iCmp = 0;
359                         try
360                         {
361                                 // there's a subtle balance here between
362                                 // 1) the while condition
363                                 // 2) the rounding down of the '/ 2'
364                                 // 3) the asymetrical recursion
365                                 // 4) the fact that iMax starts beyond the end of the array
366                                 while (iMin < iMax)
367                                 {
368                                         int iMid = (iMin + iMax) / 2;
369                                         object elt = array.GetValue (iMid);
370
371                                         // this order is from MSDN
372                                         if (comparer != null)
373                                                 iCmp = comparer.Compare (value, elt);
374                                         else
375                                         {
376                                                 IComparable eltCompare = elt as IComparable;
377                                                 if (eltCompare != null)
378                                                         iCmp = -eltCompare.CompareTo (value);
379                                                 else
380                                                         iCmp = valueCompare.CompareTo (elt);
381                                         }
382
383                                         if (iCmp == 0)
384                                                 return iMid;
385                                         else if (iCmp < 0)
386                                                 iMax = iMid;
387                                         else
388                                                 iMin = iMid + 1;        // compensate for the rounding down
389                                 }
390                         }
391                         catch (InvalidCastException e)
392                         {
393                                 throw new ArgumentException ("array", e);
394                         }
395
396                         if (iCmp > 0)
397                                 return ~iMax;
398                         else
399                                 return ~iMin;
400                 }
401
402                 public static void Clear (Array array, int index, int length)
403                 {
404                         if (array == null)
405                                 throw new ArgumentNullException ();
406
407                         if (array.Rank > 1)
408                                 throw new RankException ();
409
410                         if (index < array.GetLowerBound (0) || length < 0 ||
411                                 index + length > array.GetUpperBound (0) + 1)
412                                 throw new ArgumentOutOfRangeException ();
413
414                         for (int i = 0; i < length; i++) 
415                         {
416                                 array.SetValue(null, index + i);
417                         }
418                 }
419
420                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
421                 public virtual extern object Clone ();
422
423                 public static void Copy (Array source, Array dest, int length)
424                 {
425                         Copy (source, source.GetLowerBound (0), dest, dest.GetLowerBound (0), length);                  
426                 }
427
428                 public static void Copy (Array source, int source_idx, Array dest, int dest_idx, int length)
429                 {
430                         if (source == null)
431                                 throw new ArgumentNullException ("null");
432
433                         if (dest == null)
434                                 throw new ArgumentNullException ("dest");
435
436                         if (length < 0)
437                                 throw new ArgumentOutOfRangeException ("length");
438
439                         if (source_idx < 0)
440                                 throw new ArgumentException ("source_idx");
441
442                         if (dest_idx < 0)
443                                 throw new ArgumentException ("dest_idx");
444
445                         int source_pos = source_idx - source.GetLowerBound (0);
446                         int dest_pos = dest_idx - dest.GetLowerBound (0);
447
448
449                         if (source_pos + length > source.Length || dest_pos + length > dest.Length)
450                                 throw new ArgumentException ("length");
451
452                         if (source.Rank != dest.Rank)
453                                 throw new RankException ();
454
455                         Type src_type = source.GetType ().GetElementType ();
456                         Type dst_type = dest.GetType ().GetElementType ();
457
458                         if (src_type == dst_type) {
459                                 FastCopy (source, source_pos, dest, dest_pos, length);
460                                 return;
461                         }
462
463                         if (!Object.ReferenceEquals (source, dest) || source_pos > dest_pos)
464                         {
465                                 for (int i = 0; i < length; i++) 
466                                 {
467                                         Object srcval = source.GetValueImpl (source_pos + i);
468
469                                         try {
470                                                 dest.SetValueImpl (srcval, dest_pos + i);
471                                         } catch {
472                                                 if ((dst_type.IsValueType || dst_type.Equals (typeof (String))) &&
473                                                         (src_type.Equals (typeof (Object))))
474                                                         throw new InvalidCastException ();
475                                                 else
476                                                         throw new ArrayTypeMismatchException (
477                                                                 String.Format ("(Types: source={0};  target={1})", src_type.FullName, dst_type.FullName));
478                                         }
479                                 }
480                         }
481                         else
482                         {
483                                 for (int i = length - 1; i >= 0; i--) 
484                                 {
485                                         Object srcval = source.GetValueImpl (source_pos + i);
486
487                                         try {
488                                                 dest.SetValueImpl (srcval, dest_pos + i);
489                                         } catch {
490                                                 if ((dst_type.IsValueType || dst_type.Equals (typeof (String))) &&
491                                                         (src_type.Equals (typeof (Object))))
492                                                         throw new InvalidCastException ();
493                                                 else
494                                                         throw new ArrayTypeMismatchException (
495                                                                 String.Format ("(Types: source={0};  target={1})", src_type.FullName, dst_type.FullName));
496                                         }
497                                 }
498                         }
499                 }
500                 
501                 public static int IndexOf (Array array, object value)
502                 {
503                         if (array == null)
504                                 throw new ArgumentNullException ();
505         
506                         return IndexOf (array, value, 0, array.Length);
507                 }
508
509                 public static int IndexOf (Array array, object value, int index)
510                 {
511                         if (array == null)
512                                 throw new ArgumentNullException ();
513
514                         return IndexOf (array, value, index, array.Length - index);
515                 }
516                 
517                 public static int IndexOf (Array array, object value, int index, int length)
518                 {
519                         if (array == null)
520                                 throw new ArgumentNullException ();
521         
522                         if (array.Rank > 1)
523                                 throw new RankException ();
524
525                         if (length < 0 || index < array.GetLowerBound (0) ||
526                             index+length-1 > array.GetUpperBound (0))
527                                 throw new ArgumentOutOfRangeException ();
528
529                         for (int i = 0; i < length; i++)
530                         {
531                                 if (array.GetValue(index + i).Equals(value))
532                                         return index + i;
533                         }
534
535                         return array.GetLowerBound (0) - 1;
536                 }
537
538                 public static int LastIndexOf (Array array, object value)
539                 {
540                         if (array == null)
541                                 throw new ArgumentNullException ();
542         
543                         return LastIndexOf (array, value, array.Length-1);
544                 }
545
546                 public static int LastIndexOf (Array array, object value, int index)
547                 {
548                         if (array == null)
549                                 throw new ArgumentNullException ();
550         
551                         return LastIndexOf (array, value, index, index-array.GetLowerBound(0)+1);
552                 }
553                 
554                 public static int LastIndexOf (Array array, object value, int index, int length)
555                 {
556                         if (array == null)
557                                 throw new ArgumentNullException ();
558         
559                         if (array.Rank > 1)
560                                 throw new RankException ();
561
562                         if (length < 0 || index-length+1 < array.GetLowerBound (0) ||
563                             index > array.GetUpperBound (0))
564                                 throw new ArgumentOutOfRangeException ();
565
566                         for (int i = index; i >= index-length+1; i--)
567                         {
568                                 if (array.GetValue(i).Equals(value))
569                                         return i;
570                         }
571
572                         return array.GetLowerBound (0) - 1;
573                 }
574
575                 public static void Reverse (Array array)
576                 {
577                         if (array == null)
578                                 throw new ArgumentNullException ();
579
580                         Reverse (array, array.GetLowerBound (0), array.GetLength (0));
581                 }
582
583                 public static void Reverse (Array array, int index, int length)
584                 {
585                         if (array == null)
586                                 throw new ArgumentNullException ();
587
588                         if (array.Rank > 1)
589                                 throw new RankException ();
590
591                         if (index < array.GetLowerBound (0) || length < 0)
592                                 throw new ArgumentOutOfRangeException ();
593
594                         if (index + length > array.GetUpperBound (0) + 1)
595                                 throw new ArgumentException ();
596
597                         for (int i = 0; i < length/2; i++)
598                         {
599                                 object tmp;
600
601                                 tmp = array.GetValue (index + i);
602                                 array.SetValue(array.GetValue (index + length - i - 1), index + i);
603                                 array.SetValue(tmp, index + length - i - 1);
604                         }
605                 }               
606                 
607                 public static void Sort (Array array)
608                 {
609                         if (array == null)
610                                 throw new ArgumentNullException ();
611
612                         Sort (array, null, array.GetLowerBound (0), array.GetLength (0), null);
613                 }
614
615                 public static void Sort (Array keys, Array items)
616                 {
617                         if (keys == null)
618                                 throw new ArgumentNullException ();
619
620                         Sort (keys, items, keys.GetLowerBound (0), keys.GetLength (0), null);
621                 }
622
623                 public static void Sort (Array array, IComparer comparer)
624                 {
625                         if (array == null)
626                                 throw new ArgumentNullException ();
627
628                         Sort (array, null, array.GetLowerBound (0), array.GetLength (0), comparer);
629                 }
630
631                 public static void Sort (Array array, int index, int length)
632                 {
633                         Sort (array, null, index, length, null);
634                 }
635
636                 public static void Sort (Array keys, Array items, IComparer comparer)
637                 {
638                         if (keys == null)
639                                 throw new ArgumentNullException ();
640
641                         Sort (keys, items, keys.GetLowerBound (0), keys.GetLength (0), comparer);
642                 }
643
644                 public static void Sort (Array keys, Array items, int index, int length)
645                 {
646                         Sort (keys, items, index, length, null);
647                 }
648
649                 public static void Sort (Array array, int index, int length, IComparer comparer)
650                 {
651                         Sort (array, null, index, length, comparer);
652                 }
653
654                 public static void Sort (Array keys, Array items, int index, int length, IComparer comparer)
655                 {
656                         int low0 = index;
657                         int high0 = index + length - 1;
658
659                         qsort (keys, items, index, index + length - 1, comparer);
660                 }
661
662                 private static void qsort (Array keys, Array items, int low0, int high0, IComparer comparer)
663                 {
664                         int pivot;
665                         int low = low0;
666                         int high = high0;
667                         
668                         if (keys == null)
669                                 throw new ArgumentNullException ();
670
671                         if (keys.Rank > 1 || (items != null && items.Rank > 1))
672                                 throw new RankException ();
673
674                         if (low >= high)
675                                 return;
676
677                         pivot = (low + high) / 2;
678
679                         if (compare (keys.GetValue (low), keys.GetValue (pivot), comparer) > 0)
680                                 swap (keys, items, low, pivot);
681                         
682                         if (compare (keys.GetValue (pivot), keys.GetValue (high), comparer) > 0)
683                                 swap (keys, items, pivot, high);
684
685                         while (low < high)
686                         {
687                                 // Move the walls in
688                                 while (low < high && compare (keys.GetValue (low), keys.GetValue (pivot), comparer) < 0)
689                                         low++;
690                                 while (low < high && compare (keys.GetValue (pivot), keys.GetValue (high), comparer) < 0)
691                                         high--;
692
693                                 if (low < high)
694                                 {
695                                         swap (keys, items, low, high);
696                                         low++;
697                                         high--;
698                                 }
699                         }
700
701                         qsort (keys, items, low0, low - 1, comparer);
702                         qsort (keys, items, high + 1, high0, comparer);
703                 }
704
705                 private static void swap (Array keys, Array items, int i, int j)
706                 {
707                         object tmp;
708
709                         tmp = keys.GetValue (i);
710                         keys.SetValue (keys.GetValue (j), i);
711                         keys.SetValue (tmp, j);
712
713                         if (items != null)
714                         {
715                                 tmp = items.GetValue (i);
716                                 items.SetValue (items.GetValue (j), i);
717                                 items.SetValue (tmp, j);
718                         }
719                 }
720
721                 private static int compare (object value1, object value2, IComparer comparer)
722                 {
723                         if (comparer == null)
724                                 return ((IComparable) value1).CompareTo(value2);
725                         else
726                                 return comparer.Compare(value1, value2);
727                 }
728         
729                 public virtual void CopyTo (Array array, int index)
730                 {
731                         if (array == null)
732                                 throw new ArgumentNullException ();
733
734                         // The order of these exception checks may look strange,
735                         // but that's how the microsoft runtime does it.
736                         if (this.Rank > 1)
737                                 throw new RankException ();
738                         if (index + this.GetLength (0) > array.GetLowerBound (0) + array.GetLength (0))
739                                 throw new ArgumentException ();
740                         if (array.Rank > 1)
741                                 throw new RankException ();
742                         if (index < 0)
743                                 throw new ArgumentOutOfRangeException ();
744
745                         Copy (this, this.GetLowerBound(0), array, index, this.GetLength (0));
746                 }
747
748                 internal class SimpleEnumerator : IEnumerator {
749                         Array enumeratee;
750                         int currentpos;
751                         int length;
752
753                         public SimpleEnumerator (Array arrayToEnumerate) {
754                                 this.enumeratee = arrayToEnumerate;
755                                 this.currentpos = -1;
756                                 this.length = arrayToEnumerate.Length;
757                         }
758
759                         public object Current {
760                                 get {
761                                         // Exception messages based on MS implementation
762                                         if (currentpos < 0 ) {
763                                                 throw new InvalidOperationException
764                                                         ("Enumeration has not started");
765                                         }
766                                         if  (currentpos >= length) {
767                                                 throw new InvalidOperationException
768                                                         ("Enumeration has already ended");
769                                         }
770                                         // Current should not increase the position. So no ++ over here.
771                                         return enumeratee.GetValueImpl(currentpos);
772                                 }
773                         }
774
775                         public bool MoveNext() {
776                                 //The docs say Current should throw an exception if last
777                                 //call to MoveNext returned false. This means currentpos
778                                 //should be set to length when returning false.
779                                         if (currentpos < length) {
780                                                 currentpos++;
781                                         }
782                                 if(currentpos < length)
783                                         return true;
784                                 else
785                                         return false;
786                         }
787
788                         public void Reset() {
789                                 currentpos= -1;
790                         }
791                 }
792         }
793 }