2007-10-27 Marek Safar <marek.safar@gmail.com>
[mono.git] / mcs / class / corlib / System / Array.cs
index d1227471b456703ef26574e93297456647ab5597..b0653b0169a5da70c7b59b05bbeca7238b9ac337 100644 (file)
@@ -42,10 +42,12 @@ using System.Runtime.ConstrainedExecution;
 
 namespace System
 {
-
        [Serializable]
-       [MonoTODO ("We are doing way to many double/triple exception checks for the overloaded functions")]
-       [MonoTODO ("Sort overloads parameter checks are VERY inconsistent")]
+#if NET_2_0
+       [ComVisible (true)]
+#endif
+       // FIXME: We are doing way to many double/triple exception checks for the overloaded functions"
+       // FIXME: Sort overloads parameter checks are VERY inconsistent"
        public abstract class Array : ICloneable, ICollection, IList, IEnumerable
        {
                // Constructor
@@ -53,6 +55,200 @@ namespace System
                {
                }
 
+#if NET_2_0
+               // FIXME: they should not be exposed, but there are some
+               // dependent code in the runtime.
+               protected int InternalArray__ICollection_get_Count<T> ()
+               {
+                       return Length;
+               }
+
+               protected IEnumerator<T> InternalArray__IEnumerable_GetEnumerator<T> ()
+               {
+                       return new InternalEnumerator<T> (this);
+               }
+
+               protected void InternalArray__ICollection_Clear<T> ()
+               {
+                       throw new NotSupportedException ("Collection is read-only");
+               }
+
+               protected void InternalArray__ICollection_Add<T> (T item)
+               {
+                       throw new NotSupportedException ("Collection is read-only");
+               }
+
+               protected bool InternalArray__ICollection_Remove<T> (T item)
+               {
+                       throw new NotSupportedException ("Collection is read-only");
+               }
+
+               protected bool InternalArray__ICollection_Contains<T> (T item)
+               {
+                       if (this.Rank > 1)
+                               throw new RankException (Locale.GetText ("Only single dimension arrays are supported."));
+
+                       int length = this.Length;
+                       for (int i = 0; i < length; i++) {
+                               T value;
+                               GetGenericValueImpl (i, out value);
+                               if (item == null){
+                                       if (value == null)
+                                               return true;
+                                       else
+                                               return false;
+                               }
+                               
+                               if (item.Equals (value))
+                                       return true;
+                       }
+
+                       return false;
+               }
+
+               protected void InternalArray__ICollection_CopyTo<T> (T[] array, int index)
+               {
+                       if (array == null)
+                               throw new ArgumentNullException ("array");
+
+                       // The order of these exception checks may look strange,
+                       // but that's how the microsoft runtime does it.
+                       if (this.Rank > 1)
+                               throw new RankException (Locale.GetText ("Only single dimension arrays are supported."));
+                       if (index + this.GetLength (0) > array.GetLowerBound (0) + array.GetLength (0))
+                               throw new ArgumentException ("Destination array was not long " +
+                                       "enough. Check destIndex and length, and the array's " +
+                                       "lower bounds.");
+                       if (array.Rank > 1)
+                               throw new RankException (Locale.GetText ("Only single dimension arrays are supported."));
+                       if (index < 0)
+                               throw new ArgumentOutOfRangeException (
+                                       "index", Locale.GetText ("Value has to be >= 0."));
+
+                       Copy (this, this.GetLowerBound (0), array, index, this.GetLength (0));
+               }
+
+               protected void InternalArray__Insert<T> (int index, T item)
+               {
+                       throw new NotSupportedException ("Collection is read-only");
+               }
+
+               protected void InternalArray__RemoveAt<T> (int index)
+               {
+                       throw new NotSupportedException ("Collection is read-only");
+               }
+
+               protected int InternalArray__IndexOf<T> (T item)
+               {
+                       if (this.Rank > 1)
+                               throw new RankException (Locale.GetText ("Only single dimension arrays are supported."));
+
+                       int length = this.Length;
+                       for (int i = 0; i < length; i++) {
+                               T value;
+                               GetGenericValueImpl (i, out value);
+                               if (item == null){
+                                       if (value == null)
+                                               return i + this.GetLowerBound (0);
+                                       else {
+                                               unchecked {
+                                                       return this.GetLowerBound (0) - 1;
+                                               }
+                                       }
+                               }
+                               if (item.Equals (value))
+                                       // array index may not be zero-based.
+                                       // use lower bound
+                                       return i + this.GetLowerBound (0);
+                       }
+
+                       int retVal;
+                       unchecked {
+                               // lower bound may be MinValue
+                               retVal = this.GetLowerBound (0) - 1;
+                       }
+
+                       return retVal;
+               }
+
+               protected T InternalArray__get_Item<T> (int index)
+               {
+                       if (unchecked ((uint) index) >= unchecked ((uint) Length))
+                               throw new ArgumentOutOfRangeException ("index");
+
+                       T value;
+                       GetGenericValueImpl (index, out value);
+                       return value;
+               }
+
+               protected void InternalArray__set_Item<T> (int index, T item)
+               {
+                       if (unchecked ((uint) index) >= unchecked ((uint) Length))
+                               throw new ArgumentOutOfRangeException ("index");
+
+                       SetGenericValueImpl (index, ref item);
+               }
+
+               // CAUTION! No bounds checking!
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               internal extern void GetGenericValueImpl<T> (int pos, out T value);
+
+               // CAUTION! No bounds checking!
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               internal extern void SetGenericValueImpl<T> (int pos, ref T value);
+
+               internal struct InternalEnumerator<T> : IEnumerator<T>
+               {
+                       const int NOT_STARTED = -2;
+                       
+                       // this MUST be -1, because we depend on it in move next.
+                       // we just decr the size, so, 0 - 1 == FINISHED
+                       const int FINISHED = -1;
+                       
+                       Array array;
+                       int idx;
+
+                       internal InternalEnumerator (Array array)
+                       {
+                               this.array = array;
+                               idx = NOT_STARTED;
+                       }
+
+                       public void Dispose ()
+                       {
+                               idx = NOT_STARTED;
+                       }
+
+                       public bool MoveNext ()
+                       {
+                               if (idx == NOT_STARTED)
+                                       idx = array.Length;
+
+                               return idx != FINISHED && -- idx != FINISHED;
+                       }
+
+                       public T Current {
+                               get {
+                                       if (idx < 0)
+                                               throw new InvalidOperationException ();
+
+                                       return array.InternalArray__get_Item<T> (array.Length - 1 - idx);
+                               }
+                       }
+
+                       void IEnumerator.Reset ()
+                       {
+                               throw new NotImplementedException ();
+                       }
+
+                       object IEnumerator.Current {
+                               get {
+                                       return Current;
+                               }
+                       }
+               }
+#endif
+
                // Properties
                public int Length {
 #if NET_2_0
@@ -166,7 +362,7 @@ namespace System
 
                // InternalCall Methods
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               private extern int GetRank ();
+               extern int GetRank ();
 
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
                public extern int GetLength (int dimension);
@@ -424,7 +620,7 @@ namespace System
                        return CreateInstance (elementType, lengths);
                }
 
-               public static Array CreateInstance (Type elementType, int[] lengths)
+               public static Array CreateInstance (Type elementType, params int[] lengths)
                {
                        if (elementType == null)
                                throw new ArgumentNullException ("elementType");
@@ -644,7 +840,9 @@ namespace System
                        int iCmp = 0;
                        try {
                                while (iMin <= iMax) {
-                                       int iMid = (iMin + iMax) / 2;
+                                       // Be careful with overflow
+                                       // http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html
+                                       int iMid = iMin + ((iMax - iMin) / 2);
                                        object elt = array.GetValueImpl (iMid);
 
                                        iCmp = comparer.Compare (elt, value);
@@ -743,9 +941,19 @@ namespace System
                        int dest_pos = destinationIndex - destinationArray.GetLowerBound (0);
 
                        // re-ordered to avoid possible integer overflow
-                       if (source_pos > sourceArray.Length - length || dest_pos > destinationArray.Length - length)
+                       if (source_pos > sourceArray.Length - length)
                                throw new ArgumentException ("length");
 
+                       if (dest_pos > destinationArray.Length - length) {
+                               string msg = "Destination array was not long enough. Check " +
+                                       "destIndex and length, and the array's lower bounds";
+#if NET_2_0
+                               throw new ArgumentException (msg, string.Empty);
+#else
+                               throw new ArgumentException (msg);
+#endif
+                       }
+
                        if (sourceArray.Rank != destinationArray.Rank)
                                throw new RankException (Locale.GetText ("Arrays must be of same size."));
 
@@ -873,7 +1081,6 @@ namespace System
                        return array.GetLowerBound (0) - 1;
                }
 
-               [MonoTODO]
                public void Initialize()
                {
                        //FIXME: We would like to find a compiler that uses
@@ -951,7 +1158,9 @@ namespace System
                        if (array is double[])
                                return new Swapper (array.double_swapper);
 
-                       return new Swapper (array.obj_swapper);
+                       // gmcs refuses to compile this
+                       //return new Swapper (array.generic_swapper<T>);
+                       return new Swapper (array.slow_swapper);
                }
 #endif
 
@@ -1180,6 +1389,15 @@ namespace System
                        array [j] = val;
                }
 
+#if NET_2_0
+               void generic_swapper<T> (int i, int j) {
+                       T[] array = this as T[];
+                       T val = array [i];
+                       array [i] = array [j];
+                       array [j] = val;
+               }
+#endif
+
                static int new_gap (int gap)
                {
                        gap = (gap * 10) / 13;
@@ -1270,7 +1488,9 @@ namespace System
                        int low = low0;
                        int high = high0;
 
-                       object objPivot = keys.GetValueImpl ((low + high) / 2);
+                       // Be careful with overflows
+                       int mid = low + ((high - low) / 2);
+                       object objPivot = keys.GetValueImpl (mid);
 
                        while (low <= high) {
                                // Move the walls in
@@ -1320,7 +1540,7 @@ namespace System
                }
        
 #if NET_2_0
-               [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
+               [ReliabilityContractAttribute (Consistency.MayCorruptInstance, Cer.MayFail)]
                public static void Sort<T> (T [] array)
                {
                        if (array == null)
@@ -1329,7 +1549,7 @@ namespace System
                        Sort<T, T> (array, null, 0, array.Length, null);
                }
 
-               [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
+               [ReliabilityContractAttribute (Consistency.MayCorruptInstance, Cer.MayFail)]
                public static void Sort<TKey, TValue> (TKey [] keys, TValue [] items)
                {
                        if (keys == null)
@@ -1338,7 +1558,7 @@ namespace System
                        Sort<TKey, TValue> (keys, items, 0, keys.Length, null);
                }
 
-               [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
+               [ReliabilityContractAttribute (Consistency.MayCorruptInstance, Cer.MayFail)]
                public static void Sort<T> (T [] array, IComparer<T> comparer)
                {
                        if (array == null)
@@ -1347,7 +1567,7 @@ namespace System
                        Sort<T, T> (array, null, 0, array.Length, comparer);
                }
 
-               [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
+               [ReliabilityContractAttribute (Consistency.MayCorruptInstance, Cer.MayFail)]
                public static void Sort<TKey, TValue> (TKey [] keys, TValue [] items, IComparer<TKey> comparer)
                {
                        if (keys == null)
@@ -1356,7 +1576,7 @@ namespace System
                        Sort<TKey, TValue> (keys, items, 0, keys.Length, comparer);
                }
 
-               [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
+               [ReliabilityContractAttribute (Consistency.MayCorruptInstance, Cer.MayFail)]
                public static void Sort<T> (T [] array, int index, int length)
                {
                        if (array == null)
@@ -1365,13 +1585,13 @@ namespace System
                        Sort<T, T> (array, null, index, length, null);
                }
 
-               [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
+               [ReliabilityContractAttribute (Consistency.MayCorruptInstance, Cer.MayFail)]
                public static void Sort<TKey, TValue> (TKey [] keys, TValue [] items, int index, int length)
                {
                        Sort<TKey, TValue> (keys, items, index, length, null);
                }
 
-               [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
+               [ReliabilityContractAttribute (Consistency.MayCorruptInstance, Cer.MayFail)]
                public static void Sort<T> (T [] array, int index, int length, IComparer<T> comparer)
                {
                        if (array == null)
@@ -1380,7 +1600,7 @@ namespace System
                        Sort<T, T> (array, null, index, length, comparer);
                }
 
-               [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
+               [ReliabilityContractAttribute (Consistency.MayCorruptInstance, Cer.MayFail)]
                public static void Sort<TKey, TValue> (TKey [] keys, TValue [] items, int index, int length, IComparer<TKey> comparer)
                {
                        if (keys == null)
@@ -1439,15 +1659,20 @@ namespace System
                {
                        if (array == null)
                                throw new ArgumentNullException ("array");
+                       Sort<T> (array, array.Length, comparison);
+               }
+
+               internal static void Sort<T> (T [] array, int length, Comparison<T> comparison)
+               {
                        if (comparison == null)
                                throw new ArgumentNullException ("comparison");
 
-                       if (array.Length <= 1)
+                       if (length <= 1 || array.Length <= 1)
                                return;
                        
                        try {
                                int low0 = 0;
-                               int high0 = array.Length - 1;
+                               int high0 = length - 1;
                                qsort<T> (array, low0, high0, comparison);
                        }
                        catch (Exception e) {
@@ -1463,7 +1688,9 @@ namespace System
                        int low = low0;
                        int high = high0;
 
-                       K keyPivot = keys [(low + high) / 2];
+                       // Be careful with overflows
+                       int mid = low + ((high - low) / 2);
+                       K keyPivot = keys [mid];
 
                        while (low <= high) {
                                // Move the walls in
@@ -1489,7 +1716,9 @@ namespace System
 
                private static int compare<T> (T value1, T value2, IComparer<T> comparer)
                {
-                       if (value1 == null)
+                       if (comparer != null)
+                               return comparer.Compare (value1, value2);
+                       else if (value1 == null)
                                return value2 == null ? 0 : -1;
                        else if (value2 == null)
                                return 1;
@@ -1497,8 +1726,6 @@ namespace System
                                return ((IComparable<T>) value1).CompareTo (value2);
                        else if (value1 is IComparable)
                                return ((IComparable) value1).CompareTo (value2);
-                       else if (comparer != null)
-                               return comparer.Compare (value1, value2);
 
                        string msg = Locale.GetText ("No IComparable or IComparable<T> interface found for type '{0}'.");
                        throw new InvalidOperationException (String.Format (msg, typeof (T)));
@@ -1512,7 +1739,9 @@ namespace System
                        int low = low0;
                        int high = high0;
 
-                       T keyPivot = array [(low + high) / 2];
+                       // Be careful with overflows
+                       int mid = low + ((high - low) / 2);
+                       T keyPivot = array [mid];
 
                        while (low <= high) {
                                // Move the walls in
@@ -1572,7 +1801,9 @@ namespace System
                        if (this.Rank > 1)
                                throw new RankException (Locale.GetText ("Only single dimension arrays are supported."));
                        if (index + this.GetLength (0) > array.GetLowerBound (0) + array.GetLength (0))
-                               throw new ArgumentException ();
+                               throw new ArgumentException ("Destination array was not long " +
+                                       "enough. Check destIndex and length, and the array's " +
+                                       "lower bounds.");
                        if (array.Rank > 1)
                                throw new RankException (Locale.GetText ("Only single dimension arrays are supported."));
                        if (index < 0)
@@ -1827,7 +2058,8 @@ namespace System
                        int iCmp = 0;
                        try {
                                while (iMin <= iMax) {
-                                       int iMid = (iMin + iMax) / 2;
+                                       // Be careful with overflows
+                                       int iMid = iMin + ((iMax - iMin) / 2);
                                        iCmp = comparer.Compare (value, array [iMid]);
 
                                        if (iCmp == 0)
@@ -1849,7 +2081,7 @@ namespace System
                        if (array == null)
                                throw new ArgumentNullException ("array");
        
-                       return IndexOf (array, value, 0, array.Length);
+                       return IndexOf<T> (array, value, 0, array.Length);
                }
 
                public static int IndexOf<T> (T [] array, T value, int startIndex)
@@ -1857,7 +2089,7 @@ namespace System
                        if (array == null)
                                throw new ArgumentNullException ("array");
 
-                       return IndexOf (array, value, startIndex, array.Length - startIndex);
+                       return IndexOf<T> (array, value, startIndex, array.Length - startIndex);
                }
 
                public static int IndexOf<T> (T [] array, T value, int startIndex, int count)
@@ -1870,8 +2102,9 @@ namespace System
                                throw new ArgumentOutOfRangeException ();
 
                        int max = startIndex + count;
+                       EqualityComparer<T> equalityComparer = EqualityComparer<T>.Default;
                        for (int i = startIndex; i < max; i++) {
-                               if (Object.Equals (value, array [i]))
+                               if (equalityComparer.Equals (value, array [i]))
                                        return i;
                        }
 
@@ -1883,7 +2116,7 @@ namespace System
                        if (array == null)
                                throw new ArgumentNullException ("array");
 
-                       return LastIndexOf (array, value, array.Length - 1);
+                       return LastIndexOf<T> (array, value, array.Length - 1);
                }
 
                public static int LastIndexOf<T> (T [] array, T value, int startIndex)
@@ -1891,7 +2124,7 @@ namespace System
                        if (array == null)
                                throw new ArgumentNullException ("array");
 
-                       return LastIndexOf (array, value, startIndex, startIndex + 1);
+                       return LastIndexOf<T> (array, value, startIndex, startIndex + 1);
                }
 
                public static int LastIndexOf<T> (T [] array, T value, int startIndex, int count)
@@ -1902,8 +2135,9 @@ namespace System
                        if (count < 0 || startIndex > array.Length || startIndex - count + 1 < 0)
                                throw new ArgumentOutOfRangeException ();
 
+                       EqualityComparer<T> equalityComparer = EqualityComparer<T>.Default;
                        for (int i = startIndex; i >= startIndex - count + 1; i--) {
-                               if (Object.Equals (value, array [i]))
+                               if (equalityComparer.Equals (value, array [i]))
                                        return i;
                        }
 
@@ -1991,188 +2225,6 @@ namespace System
 #endif 
 
 #if NET_2_0
-               //
-               // This is used internally by the runtime to represent arrays; see #74953.
-               //
-               // Note that you normally can't derive a class from System.Array (CS0644),
-               // but GMCS makes an exception here for all classes which are nested inside
-               // System.Array.
-               //
-               internal class InternalArray<T> : Array, IList<T>
-               {
-                       new public IEnumerator<T> GetEnumerator ()
-                       {
-                               return new InternalEnumerator (this);
-                       }
-
-                       public int Count {
-                               get {
-                                       return Length;
-                               }
-                       }
-
-                       bool ICollection<T>.IsReadOnly {
-                               get {
-                                       return true;
-                               }
-                       }
-
-                       void ICollection<T>.Clear ()
-                       {
-                               throw new NotSupportedException ("Collection is read-only");
-                       }
-
-                       void ICollection<T>.Add (T item)
-                       {
-                               throw new NotSupportedException ("Collection is read-only");
-                       }
-
-                       bool ICollection<T>.Remove (T item)
-                       {
-                               throw new NotSupportedException ("Collection is read-only");
-                       }
-
-                       public bool Contains (T item)
-                       {
-                               if (this.Rank > 1)
-                                       throw new RankException (Locale.GetText ("Only single dimension arrays are supported."));
-
-                               int length = this.Length;
-                               for (int i = 0; i < length; i++) {
-                                       T value;
-                                       GetGenericValueImpl (i, out value);
-                                       if (item.Equals (value))
-                                               return true;
-                               }
-
-                               return false;
-                       }
-
-                       public void CopyTo (T[] array, int index)
-                       {
-                               if (array == null)
-                                       throw new ArgumentNullException ("array");
-
-                               // The order of these exception checks may look strange,
-                               // but that's how the microsoft runtime does it.
-                               if (this.Rank > 1)
-                                       throw new RankException (Locale.GetText ("Only single dimension arrays are supported."));
-                               if (index + this.GetLength (0) > array.GetLowerBound (0) + array.GetLength (0))
-                                       throw new ArgumentException ();
-                               if (array.Rank > 1)
-                                       throw new RankException (Locale.GetText ("Only single dimension arrays are supported."));
-                               if (index < 0)
-                                       throw new ArgumentOutOfRangeException (
-                                               "index", Locale.GetText ("Value has to be >= 0."));
-
-                               Copy (this, this.GetLowerBound (0), array, index, this.GetLength (0));
-                       }
-
-                       new public T this [int index] {
-                               get {
-                                       if (unchecked ((uint) index) >= unchecked ((uint) Length))
-                                               throw new ArgumentOutOfRangeException ("index");
-
-                                       T value;
-                                       GetGenericValueImpl (index, out value);
-                                       return value;
-                               }
-
-                               set {
-                                       throw new NotSupportedException ("Collection is read-only");
-                               }
-                       }
-
-                       void IList<T>.Insert (int index, T item)
-                       {
-                               throw new NotSupportedException ("Collection is read-only");
-                       }
-
-                       void IList<T>.RemoveAt (int index)
-                       {
-                               throw new NotSupportedException ("Collection is read-only");
-                       }
-
-                       public int IndexOf (T item)
-                       {
-                               if (this.Rank > 1)
-                                       throw new RankException (Locale.GetText ("Only single dimension arrays are supported."));
-
-                               int length = this.Length;
-                               for (int i = 0; i < length; i++) {
-                                       T value;
-                                       GetGenericValueImpl (i, out value);
-                                       if (item.Equals (value))
-                                               // array index may not be zero-based.
-                                               // use lower bound
-                                               return i + this.GetLowerBound (0);
-                               }
-
-                               int retVal;
-                               unchecked {
-                                       // lower bound may be MinValue
-                                       retVal = this.GetLowerBound (0) - 1;
-                               }
-
-                               return retVal;
-                       }
-
-                       // CAUTION! No bounds checking!
-                       [MethodImplAttribute (MethodImplOptions.InternalCall)]
-                       protected extern void GetGenericValueImpl (int pos, out T value);
-
-                       internal struct InternalEnumerator : IEnumerator<T>
-                       {
-                               const int NOT_STARTED = -2;
-                       
-                               // this MUST be -1, because we depend on it in move next.
-                               // we just decr the size, so, 0 - 1 == FINISHED
-                               const int FINISHED = -1;
-                       
-                               InternalArray<T> array;
-                               int idx;
-
-                               internal InternalEnumerator (InternalArray<T> array)
-                               {
-                                       this.array = array;
-                                       idx = NOT_STARTED;
-                               }
-
-                               public void Dispose ()
-                               {
-                                       idx = NOT_STARTED;
-                               }
-
-                               public bool MoveNext ()
-                               {
-                                       if (idx == NOT_STARTED)
-                                               idx = array.Length;
-
-                                       return idx != FINISHED && -- idx != FINISHED;
-                               }
-
-                               public T Current {
-                                       get {
-                                               if (idx < 0)
-                                                       throw new InvalidOperationException ();
-
-                                               return array [array.Length - 1 - idx];
-                                       }
-                               }
-
-                               void IEnumerator.Reset ()
-                               {
-                                       throw new NotImplementedException ();
-                               }
-
-                               object IEnumerator.Current {
-                                       get {
-                                               return Current;
-                                       }
-                               }
-                       }
-               }
-
                class ArrayReadOnlyList<T> : IList<T>
                {
                        T [] array;