Merge pull request #4540 from kumpera/android-changes-part1
[mono.git] / mcs / class / corlib / corert / Array.Portable.cs
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 using System.Runtime;
6 using System.Threading;
7 using System.Collections;
8 using System.Diagnostics;
9 using System.Collections.Generic;
10 using System.Collections.ObjectModel;
11 using System.Runtime.InteropServices;
12 using System.Runtime.CompilerServices;
13 using System.Diagnostics.Contracts;
14 #if MONO
15 using System.Diagnostics.Private;
16 #endif
17
18 namespace System
19 {
20     public abstract partial class Array : ICollection, IEnumerable, IList, IStructuralComparable, IStructuralEquatable, ICloneable
21     {
22         public static ReadOnlyCollection<T> AsReadOnly<T>(T[] array)
23         {
24             if (array == null)
25             {
26                 throw new ArgumentNullException(nameof(array));
27             }
28
29             // T[] implements IList<T>.
30             return new ReadOnlyCollection<T>(array);
31         }
32
33         public static void Resize<T>(ref T[] array, int newSize)
34         {
35             if (newSize < 0)
36                 throw new ArgumentOutOfRangeException(nameof(newSize), SR.ArgumentOutOfRange_NeedNonNegNum);
37
38             T[] larray = array;
39             if (larray == null)
40             {
41                 array = new T[newSize];
42                 return;
43             }
44
45             if (larray.Length != newSize)
46             {
47                 T[] newArray = new T[newSize];
48                 Copy(larray, 0, newArray, 0, larray.Length > newSize ? newSize : larray.Length);
49                 array = newArray;
50             }
51         }
52
53         // Number of elements in the Array.
54         int ICollection.Count
55         { get { return Length; } }
56
57         // Is this Array read-only?
58         bool IList.IsReadOnly
59         { get { return false; } }
60
61         Object IList.this[int index]
62         {
63             get
64             {
65                 return GetValue(index);
66             }
67
68             set
69             {
70                 SetValue(value, index);
71             }
72         }
73
74         int IList.Add(Object value)
75         {
76             throw new NotSupportedException(SR.NotSupported_FixedSizeCollection);
77         }
78
79         bool IList.Contains(Object value)
80         {
81             return Array.IndexOf(this, value) >= 0;
82         }
83
84         void IList.Clear()
85         {
86             Array.Clear(this, 0, this.Length);
87         }
88
89         void IList.Insert(int index, Object value)
90         {
91             throw new NotSupportedException(SR.NotSupported_FixedSizeCollection);
92         }
93
94         void IList.Remove(Object value)
95         {
96             throw new NotSupportedException(SR.NotSupported_FixedSizeCollection);
97         }
98
99         void IList.RemoveAt(int index)
100         {
101             throw new NotSupportedException(SR.NotSupported_FixedSizeCollection);
102         }
103
104         // CopyTo copies a collection into an Array, starting at a particular
105         // index into the array.
106         // 
107         // This method is to support the ICollection interface, and calls
108         // Array.Copy internally.  If you aren't using ICollection explicitly,
109         // call Array.Copy to avoid an extra indirection.
110         // 
111         public void CopyTo(Array array, int index)
112         {
113             // Note: Array.Copy throws a RankException and we want a consistent ArgumentException for all the IList CopyTo methods.
114             if (array != null && array.Rank != 1)
115                 throw new ArgumentException(SR.Arg_RankMultiDimNotSupported);
116
117             Array.Copy(this, 0, array, index, Length);
118         }
119
120         // Make a new array which is a deep copy of the original array.
121         // 
122         public Object Clone()
123         {
124             return MemberwiseClone();
125         }
126
127         Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer)
128         {
129             if (other == null)
130             {
131                 return 1;
132             }
133
134             Array o = other as Array;
135
136             if (o == null || this.Length != o.Length)
137             {
138                 throw new ArgumentException(SR.ArgumentException_OtherNotArrayOfCorrectLength, nameof(other));
139             }
140
141             int i = 0;
142             int c = 0;
143
144             while (i < o.Length && c == 0)
145             {
146                 object left = GetValue(i);
147                 object right = o.GetValue(i);
148
149                 c = comparer.Compare(left, right);
150                 i++;
151             }
152
153             return c;
154         }
155
156         Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer)
157         {
158             if (other == null)
159             {
160                 return false;
161             }
162
163             if (Object.ReferenceEquals(this, other))
164             {
165                 return true;
166             }
167
168             Array o = other as Array;
169
170             if (o == null || o.Length != this.Length)
171             {
172                 return false;
173             }
174
175             int i = 0;
176             while (i < o.Length)
177             {
178                 object left = GetValue(i);
179                 object right = o.GetValue(i);
180
181                 if (!comparer.Equals(left, right))
182                 {
183                     return false;
184                 }
185                 i++;
186             }
187
188             return true;
189         }
190
191         // From System.Web.Util.HashCodeCombiner
192         internal static int CombineHashCodes(int h1, int h2)
193         {
194             return (((h1 << 5) + h1) ^ h2);
195         }
196
197         int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
198         {
199             if (comparer == null)
200                 throw new ArgumentNullException(nameof(comparer));
201
202             int ret = 0;
203
204             for (int i = (this.Length >= 8 ? this.Length - 8 : 0); i < this.Length; i++)
205             {
206                 ret = CombineHashCodes(ret, comparer.GetHashCode(GetValue(i)));
207             }
208
209             return ret;
210         }
211
212         // Searches an array for a given element using a binary search algorithm.
213         // Elements of the array are compared to the search value using the
214         // IComparable interface, which must be implemented by all elements
215         // of the array and the given search value. This method assumes that the
216         // array is already sorted according to the IComparable interface;
217         // if this is not the case, the result will be incorrect.
218         //
219         // The method returns the index of the given value in the array. If the
220         // array does not contain the given value, the method returns a negative
221         // integer. The bitwise complement operator (~) can be applied to a
222         // negative result to produce the index of the first element (if any) that
223         // is larger than the given search value.
224         // 
225         public static int BinarySearch(Array array, Object value)
226         {
227             if (array == null)
228                 throw new ArgumentNullException(nameof(array));
229             return BinarySearch(array, 0, array.Length, value, null);
230         }
231
232         public static TOutput[] ConvertAll<TInput, TOutput>(TInput[] array, Converter<TInput, TOutput> converter)
233         {
234             if (array == null)
235                 throw new ArgumentNullException(nameof(array));
236
237             if (converter == null)
238                 throw new ArgumentNullException(nameof(converter));
239
240             Contract.Ensures(Contract.Result<TOutput[]>() != null);
241             Contract.Ensures(Contract.Result<TOutput[]>().Length == array.Length);
242             Contract.EndContractBlock();
243
244             TOutput[] newArray = new TOutput[array.Length];
245             for (int i = 0; i < array.Length; i++)
246             {
247                 newArray[i] = converter(array[i]);
248             }
249             return newArray;
250         }
251
252         public static void Copy(Array sourceArray, Array destinationArray, long length)
253         {
254             if (length > Int32.MaxValue || length < Int32.MinValue)
255                 throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_HugeArrayNotSupported);
256
257             Array.Copy(sourceArray, destinationArray, (int)length);
258         }
259
260         public static void Copy(Array sourceArray, long sourceIndex, Array destinationArray, long destinationIndex, long length)
261         {
262             if (sourceIndex > Int32.MaxValue || sourceIndex < Int32.MinValue)
263                 throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_HugeArrayNotSupported);
264             if (destinationIndex > Int32.MaxValue || destinationIndex < Int32.MinValue)
265                 throw new ArgumentOutOfRangeException(nameof(destinationIndex), SR.ArgumentOutOfRange_HugeArrayNotSupported);
266             if (length > Int32.MaxValue || length < Int32.MinValue)
267                 throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_HugeArrayNotSupported);
268
269             Array.Copy(sourceArray, (int)sourceIndex, destinationArray, (int)destinationIndex, (int)length);
270         }
271
272         public void CopyTo(Array array, long index)
273         {
274             if (index > Int32.MaxValue || index < Int32.MinValue)
275                 throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_HugeArrayNotSupported);
276             Contract.EndContractBlock();
277
278             this.CopyTo(array, (int)index);
279         }
280
281         public static void ForEach<T>(T[] array, Action<T> action)
282         {
283             if (array == null)
284                 throw new ArgumentNullException(nameof(array));
285
286             if (action == null)
287                 throw new ArgumentNullException(nameof(action));
288
289             Contract.EndContractBlock();
290
291             for (int i = 0; i < array.Length; i++)
292             {
293                 action(array[i]);
294             }
295         }
296
297         public long LongLength
298         {
299             get
300             {
301                 long ret = GetLength(0);
302
303                 for (int i = 1; i < Rank; ++i)
304                 {
305                     ret = ret * GetLength(i);
306                 }
307
308                 return ret;
309             }
310         }
311
312         public long GetLongLength(int dimension)
313         {
314             // This method does throw an IndexOutOfRangeException for compat if dimension < 0 or >= Rank
315             // by calling GetUpperBound
316             return GetLength(dimension);
317         }
318
319         public Object GetValue(long index)
320         {
321             if (index > Int32.MaxValue || index < Int32.MinValue)
322                 throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_HugeArrayNotSupported);
323             Contract.EndContractBlock();
324
325             return this.GetValue((int)index);
326         }
327
328         public Object GetValue(long index1, long index2)
329         {
330             if (index1 > Int32.MaxValue || index1 < Int32.MinValue)
331                 throw new ArgumentOutOfRangeException(nameof(index1), SR.ArgumentOutOfRange_HugeArrayNotSupported);
332             if (index2 > Int32.MaxValue || index2 < Int32.MinValue)
333                 throw new ArgumentOutOfRangeException(nameof(index2), SR.ArgumentOutOfRange_HugeArrayNotSupported);
334             Contract.EndContractBlock();
335
336             return this.GetValue((int)index1, (int)index2);
337         }
338
339         public Object GetValue(long index1, long index2, long index3)
340         {
341             if (index1 > Int32.MaxValue || index1 < Int32.MinValue)
342                 throw new ArgumentOutOfRangeException(nameof(index1), SR.ArgumentOutOfRange_HugeArrayNotSupported);
343             if (index2 > Int32.MaxValue || index2 < Int32.MinValue)
344                 throw new ArgumentOutOfRangeException(nameof(index2), SR.ArgumentOutOfRange_HugeArrayNotSupported);
345             if (index3 > Int32.MaxValue || index3 < Int32.MinValue)
346                 throw new ArgumentOutOfRangeException(nameof(index3), SR.ArgumentOutOfRange_HugeArrayNotSupported);
347             Contract.EndContractBlock();
348
349             return this.GetValue((int)index1, (int)index2, (int)index3);
350         }
351
352         public Object GetValue(params long[] indices)
353         {
354             if (indices == null)
355                 throw new ArgumentNullException(nameof(indices));
356             if (Rank != indices.Length)
357                 throw new ArgumentException(SR.Arg_RankIndices);
358             Contract.EndContractBlock();
359
360             int[] intIndices = new int[indices.Length];
361
362             for (int i = 0; i < indices.Length; ++i)
363             {
364                 long index = indices[i];
365                 if (index > Int32.MaxValue || index < Int32.MinValue)
366                     throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_HugeArrayNotSupported);
367                 intIndices[i] = (int)index;
368             }
369
370             return this.GetValue(intIndices);
371         }
372
373         public void Initialize()
374         {
375             // Project N port note: On the desktop, this api is a nop unless the array element type is a value type with
376             // an explicit nullary constructor. Such a type cannot be expressed in C# so Project N does not support this.
377             // The ILC toolchain fails the build if it encounters such a type.
378             return;
379         }        
380
381         public bool IsFixedSize { get { return true; } }
382
383         // Is this Array synchronized (i.e., thread-safe)?  If you want a synchronized
384         // collection, you can use SyncRoot as an object to synchronize your 
385         // collection with.  You could also call GetSynchronized() 
386         // to get a synchronized wrapper around the Array.
387         public bool IsSynchronized { get { return false; } }
388
389         // Returns an object appropriate for synchronizing access to this 
390         // Array.
391         public Object SyncRoot { get { return this; } }
392
393         // Searches a section of an array for a given element using a binary search
394         // algorithm. Elements of the array are compared to the search value using
395         // the IComparable interface, which must be implemented by all
396         // elements of the array and the given search value. This method assumes
397         // that the array is already sorted according to the IComparable
398         // interface; if this is not the case, the result will be incorrect.
399         //
400         // The method returns the index of the given value in the array. If the
401         // array does not contain the given value, the method returns a negative
402         // integer. The bitwise complement operator (~) can be applied to a
403         // negative result to produce the index of the first element (if any) that
404         // is larger than the given search value.
405         // 
406         public static int BinarySearch(Array array, int index, int length, Object value)
407         {
408             return BinarySearch(array, index, length, value, null);
409         }
410
411         // Searches an array for a given element using a binary search algorithm.
412         // Elements of the array are compared to the search value using the given
413         // IComparer interface. If comparer is null, elements of the
414         // array are compared to the search value using the IComparable
415         // interface, which in that case must be implemented by all elements of the
416         // array and the given search value. This method assumes that the array is
417         // already sorted; if this is not the case, the result will be incorrect.
418         // 
419         // The method returns the index of the given value in the array. If the
420         // array does not contain the given value, the method returns a negative
421         // integer. The bitwise complement operator (~) can be applied to a
422         // negative result to produce the index of the first element (if any) that
423         // is larger than the given search value.
424         // 
425         public static int BinarySearch(Array array, Object value, IComparer comparer)
426         {
427             if (array == null)
428                 throw new ArgumentNullException(nameof(array));
429             return BinarySearch(array, 0, array.Length, value, comparer);
430         }
431
432         // Searches a section of an array for a given element using a binary search
433         // algorithm. Elements of the array are compared to the search value using
434         // the given IComparer interface. If comparer is null,
435         // elements of the array are compared to the search value using the
436         // IComparable interface, which in that case must be implemented by
437         // all elements of the array and the given search value. This method
438         // assumes that the array is already sorted; if this is not the case, the
439         // result will be incorrect.
440         // 
441         // The method returns the index of the given value in the array. If the
442         // array does not contain the given value, the method returns a negative
443         // integer. The bitwise complement operator (~) can be applied to a
444         // negative result to produce the index of the first element (if any) that
445         // is larger than the given search value.
446         // 
447         public static int BinarySearch(Array array, int index, int length, Object value, IComparer comparer)
448         {
449             if (array == null)
450                 throw new ArgumentNullException(nameof(array));
451
452             if (index < 0 || length < 0)
453                 throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(length)), SR.ArgumentOutOfRange_NeedNonNegNum);
454             if (array.Length - index < length)
455                 throw new ArgumentException(SR.Argument_InvalidOffLen);
456             if (array.Rank != 1)
457                 throw new RankException(SR.Rank_MultiDimNotSupported);
458
459             if (comparer == null) comparer = LowLevelComparer.Default;
460
461             int lo = index;
462             int hi = index + length - 1;
463             Object[] objArray = array as Object[];
464             if (objArray != null)
465             {
466                 while (lo <= hi)
467                 {
468                     // i might overflow if lo and hi are both large positive numbers. 
469                     int i = GetMedian(lo, hi);
470
471                     int c;
472                     try
473                     {
474                         c = comparer.Compare(objArray[i], value);
475                     }
476                     catch (Exception e)
477                     {
478                         throw new InvalidOperationException(SR.InvalidOperation_IComparerFailed, e);
479                     }
480                     if (c == 0) return i;
481                     if (c < 0)
482                     {
483                         lo = i + 1;
484                     }
485                     else
486                     {
487                         hi = i - 1;
488                     }
489                 }
490             }
491             else
492             {
493                 while (lo <= hi)
494                 {
495                     int i = GetMedian(lo, hi);
496
497                     int c;
498                     try
499                     {
500                         c = comparer.Compare(array.GetValue(i), value);
501                     }
502                     catch (Exception e)
503                     {
504                         throw new InvalidOperationException(SR.InvalidOperation_IComparerFailed, e);
505                     }
506                     if (c == 0) return i;
507                     if (c < 0)
508                     {
509                         lo = i + 1;
510                     }
511                     else
512                     {
513                         hi = i - 1;
514                     }
515                 }
516             }
517             return ~lo;
518         }
519
520         private static int GetMedian(int low, int hi)
521         {
522             // Note both may be negative, if we are dealing with arrays w/ negative lower bounds.
523             return low + ((hi - low) >> 1);
524         }
525
526         public static int BinarySearch<T>(T[] array, T value)
527         {
528             if (array == null)
529                 throw new ArgumentNullException(nameof(array));
530             return BinarySearch<T>(array, 0, array.Length, value, null);
531         }
532
533         public static int BinarySearch<T>(T[] array, T value, System.Collections.Generic.IComparer<T> comparer)
534         {
535             if (array == null)
536                 throw new ArgumentNullException(nameof(array));
537             return BinarySearch<T>(array, 0, array.Length, value, comparer);
538         }
539
540         public static int BinarySearch<T>(T[] array, int index, int length, T value)
541         {
542             return BinarySearch<T>(array, index, length, value, null);
543         }
544
545         public static int BinarySearch<T>(T[] array, int index, int length, T value, System.Collections.Generic.IComparer<T> comparer)
546         {
547             if (array == null)
548                 throw new ArgumentNullException(nameof(array));
549             if (index < 0 || length < 0)
550                 throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(length)), SR.ArgumentOutOfRange_NeedNonNegNum);
551             if (array.Length - index < length)
552                 throw new ArgumentException(SR.Argument_InvalidOffLen);
553
554             return ArraySortHelper<T>.BinarySearch(array, index, length, value, comparer);
555         }
556
557         // Returns the index of the first occurrence of a given value in an array.
558         // The array is searched forwards, and the elements of the array are
559         // compared to the given value using the Object.Equals method.
560         // 
561         public static int IndexOf(Array array, Object value)
562         {
563             if (array == null)
564             {
565                 throw new ArgumentNullException(nameof(array));
566             }
567
568             return IndexOf(array, value, 0, array.Length);
569         }
570
571         // Returns the index of the first occurrence of a given value in a range of
572         // an array. The array is searched forwards, starting at index
573         // startIndex and ending at the last element of the array. The
574         // elements of the array are compared to the given value using the
575         // Object.Equals method.
576         // 
577         public static int IndexOf(Array array, Object value, int startIndex)
578         {
579             if (array == null)
580             {
581                 throw new ArgumentNullException(nameof(array));
582             }
583
584             return IndexOf(array, value, startIndex, array.Length - startIndex);
585         }
586
587         // Returns the index of the first occurrence of a given value in a range of
588         // an array. The array is searched forwards, starting at index
589         // startIndex and upto count elements. The
590         // elements of the array are compared to the given value using the
591         // Object.Equals method.
592         // 
593         public static int IndexOf(Array array, Object value, int startIndex, int count)
594         {
595             if (array == null)
596                 throw new ArgumentNullException(nameof(array));
597             if (array.Rank != 1)
598                 throw new RankException(SR.Rank_MultiDimNotSupported);
599             if (startIndex < 0 || startIndex > array.Length)
600                 throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
601             if (count < 0 || count > array.Length - startIndex)
602                 throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count);
603
604             Object[] objArray = array as Object[];
605             int endIndex = startIndex + count;
606             if (objArray != null)
607             {
608                 if (value == null)
609                 {
610                     for (int i = startIndex; i < endIndex; i++)
611                     {
612                         if (objArray[i] == null) return i;
613                     }
614                 }
615                 else
616                 {
617                     for (int i = startIndex; i < endIndex; i++)
618                     {
619                         Object obj = objArray[i];
620                         if (obj != null && obj.Equals(value)) return i;
621                     }
622                 }
623             }
624             else
625             {
626                 for (int i = startIndex; i < endIndex; i++)
627                 {
628                     Object obj = array.GetValue(i);
629                     if (obj == null)
630                     {
631                         if (value == null) return i;
632                     }
633                     else
634                     {
635                         if (obj.Equals(value)) return i;
636                     }
637                 }
638             }
639             return -1;
640         }
641
642         /// <summary>
643         /// This version is called from Array<T>.IndexOf and Contains<T>, so it's in every unique array instance due to array interface implementation.
644         /// Do not call into IndexOf<T>(Array array, Object value, int startIndex, int count) for size and space reasons.
645         /// Otherwise there will be two IndexOf methods for each unique array instance, and extra parameter checking which are not needed for the common case.
646         /// </summary>
647         public static int IndexOf<T>(T[] array, T value)
648         {
649             if (array == null)
650             {
651                 throw new ArgumentNullException(nameof(array));
652             }
653
654             // See comment above Array.GetComparerForReferenceTypesOnly for details
655             EqualityComparer<T> comparer = GetComparerForReferenceTypesOnly<T>();
656
657             if (comparer != null)
658             {
659                 for (int i = 0; i < array.Length; i++)
660                 {
661                     if (comparer.Equals(array[i], value))
662                         return i;
663                 }
664             }
665             else
666             {
667                 for (int i = 0; i < array.Length; i++)
668                 {
669                     if (StructOnlyEquals<T>(array[i], value))
670                         return i;
671                 }
672             }
673
674             return -1;
675         }
676
677         public static int IndexOf<T>(T[] array, T value, int startIndex)
678         {
679             if (array == null)
680             {
681                 throw new ArgumentNullException(nameof(array));
682             }
683
684             return IndexOf(array, value, startIndex, array.Length - startIndex);
685         }
686
687         public static int IndexOf<T>(T[] array, T value, int startIndex, int count)
688         {
689             if (array == null)
690             {
691                 throw new ArgumentNullException(nameof(array));
692             }
693
694             if (startIndex < 0 || startIndex > array.Length)
695             {
696                 throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
697             }
698
699             if (count < 0 || count > array.Length - startIndex)
700             {
701                 throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count);
702             }
703
704             int endIndex = startIndex + count;
705
706             // See comment above Array.GetComparerForReferenceTypesOnly for details
707             EqualityComparer<T> comparer = GetComparerForReferenceTypesOnly<T>();
708
709             if (comparer != null)
710             {
711                 for (int i = startIndex; i < endIndex; i++)
712                 {
713                     if (comparer.Equals(array[i], value))
714                         return i;
715                 }
716             }
717             else
718             {
719                 for (int i = startIndex; i < endIndex; i++)
720                 {
721                     if (StructOnlyEquals<T>(array[i], value))
722                         return i;
723                 }
724             }
725
726             return -1;
727         }
728
729         public static int LastIndexOf(Array array, Object value)
730         {
731             if (array == null)
732                 throw new ArgumentNullException(nameof(array));
733
734             return LastIndexOf(array, value, array.Length - 1, array.Length);
735         }
736
737         // Returns the index of the last occurrence of a given value in a range of
738         // an array. The array is searched backwards, starting at index
739         // startIndex and ending at index 0. The elements of the array are
740         // compared to the given value using the Object.Equals method.
741         // 
742         public static int LastIndexOf(Array array, Object value, int startIndex)
743         {
744             if (array == null)
745                 throw new ArgumentNullException(nameof(array));
746
747             return LastIndexOf(array, value, startIndex, startIndex + 1);
748         }
749
750         // Returns the index of the last occurrence of a given value in a range of
751         // an array. The array is searched backwards, starting at index
752         // startIndex and counting uptocount elements. The elements of
753         // the array are compared to the given value using the Object.Equals
754         // method.
755         // 
756         public static int LastIndexOf(Array array, Object value, int startIndex, int count)
757         {
758             if (array == null)
759                 throw new ArgumentNullException(nameof(array));
760
761             if (array.Length == 0)
762             {
763                 return -1;
764             }
765
766             if (startIndex < 0 || startIndex >= array.Length)
767                 throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
768             if (count < 0)
769                 throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count);
770             if (count > startIndex + 1)
771                 throw new ArgumentOutOfRangeException("endIndex", SR.ArgumentOutOfRange_EndIndexStartIndex);
772             if (array.Rank != 1)
773                 throw new RankException(SR.Rank_MultiDimNotSupported);
774
775             Object[] objArray = array as Object[];
776             int endIndex = startIndex - count + 1;
777             if (objArray != null)
778             {
779                 if (value == null)
780                 {
781                     for (int i = startIndex; i >= endIndex; i--)
782                     {
783                         if (objArray[i] == null) return i;
784                     }
785                 }
786                 else
787                 {
788                     for (int i = startIndex; i >= endIndex; i--)
789                     {
790                         Object obj = objArray[i];
791                         if (obj != null && obj.Equals(value)) return i;
792                     }
793                 }
794             }
795             else
796             {
797                 for (int i = startIndex; i >= endIndex; i--)
798                 {
799                     Object obj = array.GetValue(i);
800                     if (obj == null)
801                     {
802                         if (value == null) return i;
803                     }
804                     else
805                     {
806                         if (obj.Equals(value)) return i;
807                     }
808                 }
809             }
810             return -1;  // Return lb-1 for arrays with negative lower bounds.
811         }
812
813         public static int LastIndexOf<T>(T[] array, T value)
814         {
815             if (array == null)
816             {
817                 throw new ArgumentNullException(nameof(array));
818             }
819
820             return LastIndexOf(array, value, array.Length - 1, array.Length);
821         }
822
823         public static int LastIndexOf<T>(T[] array, T value, int startIndex)
824         {
825             if (array == null)
826             {
827                 throw new ArgumentNullException(nameof(array));
828             }
829
830             // if array is empty and startIndex is 0, we need to pass 0 as count
831             return LastIndexOf(array, value, startIndex, (array.Length == 0) ? 0 : (startIndex + 1));
832         }
833
834         public static int LastIndexOf<T>(T[] array, T value, int startIndex, int count)
835         {
836             if (array == null)
837             {
838                 throw new ArgumentNullException(nameof(array));
839             }
840
841             if (array.Length == 0)
842             {
843                 //
844                 // Special case for 0 length List
845                 // accept -1 and 0 as valid startIndex for compablility reason.
846                 //
847                 if (startIndex != -1 && startIndex != 0)
848                 {
849                     throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
850                 }
851
852                 // only 0 is a valid value for count if array is empty
853                 if (count != 0)
854                 {
855                     throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count);
856                 }
857                 return -1;
858             }
859
860             // Make sure we're not out of range            
861             if (startIndex < 0 || startIndex >= array.Length)
862             {
863                 throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
864             }
865
866             // 2nd have of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0.
867             if (count < 0 || startIndex - count + 1 < 0)
868             {
869                 throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count);
870             }
871
872             // See comment above Array.GetComparerForReferenceTypesOnly for details
873             EqualityComparer<T> comparer = GetComparerForReferenceTypesOnly<T>();
874
875             int endIndex = startIndex - count + 1;
876             if (comparer != null)
877             {
878                 for (int i = startIndex; i >= endIndex; i--)
879                 {
880                     if (comparer.Equals(array[i], value))
881                         return i;
882                 }
883             }
884             else
885             {
886                 for (int i = startIndex; i >= endIndex; i--)
887                 {
888                     if (StructOnlyEquals<T>(array[i], value))
889                         return i;
890                 }
891             }
892
893             return -1;
894         }
895
896         // Reverses all elements of the given array. Following a call to this
897         // method, an element previously located at index i will now be
898         // located at index length - i - 1, where length is the
899         // length of the array.
900         // 
901         public static void Reverse(Array array)
902         {
903             if (array == null)
904                 throw new ArgumentNullException(nameof(array));
905
906             Reverse(array, 0, array.Length);
907         }
908
909         // Reverses the elements in a range of an array. Following a call to this
910         // method, an element in the range given by index and count
911         // which was previously located at index i will now be located at
912         // index index + (index + count - i - 1).
913         // Reliability note: This may fail because it may have to box objects.
914         // 
915         public static void Reverse(Array array, int index, int length)
916         {
917             if (array == null)
918                 throw new ArgumentNullException(nameof(array));
919             int lowerBound = array.GetLowerBound(0);
920             if (index < lowerBound || length < 0)
921                 throw new ArgumentOutOfRangeException((index < lowerBound ? nameof(index) : nameof(length)), SR.ArgumentOutOfRange_NeedNonNegNum);
922             if (array.Length - (index - lowerBound) < length)
923                 throw new ArgumentException(SR.Argument_InvalidOffLen);
924             if (array.Rank != 1)
925                 throw new RankException(SR.Rank_MultiDimNotSupported);
926
927             int i = index;
928             int j = index + length - 1;
929             Object[] objArray = array as Object[];
930             if (objArray != null)
931             {
932                 while (i < j)
933                 {
934                     Object temp = objArray[i];
935                     objArray[i] = objArray[j];
936                     objArray[j] = temp;
937                     i++;
938                     j--;
939                 }
940             }
941             else
942             {
943                 while (i < j)
944                 {
945                     Object temp = array.GetValue(i);
946                     array.SetValue(array.GetValue(j), i);
947                     array.SetValue(temp, j);
948                     i++;
949                     j--;
950                 }
951             }
952         }
953
954         public static void Reverse<T>(T[] array)
955         {
956             if (array == null)
957                 throw new ArgumentNullException(nameof(array));
958
959             Reverse(array, 0, array.Length);
960         }
961
962         public static void Reverse<T>(T[] array, int index, int length)
963         {
964             if (array == null)
965                 throw new ArgumentNullException(nameof(array));
966             if (index < 0 || length < 0)
967                 throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(length)), SR.ArgumentOutOfRange_NeedNonNegNum);
968             if (array.Length - index < length)
969                 throw new ArgumentException(SR.Argument_InvalidOffLen);
970
971             int i = index;
972             int j = index + length - 1;
973             while (i < j)
974             {
975                 T temp = array[i];
976                 array[i] = array[j];
977                 array[j] = temp;
978                 i++;
979                 j--;
980             }
981         }
982
983         // Sorts the elements of an array. The sort compares the elements to each
984         // other using the IComparable interface, which must be implemented
985         // by all elements of the array.
986         // 
987         public static void Sort(Array array)
988         {
989             if (array == null)
990                 throw new ArgumentNullException(nameof(array));
991
992             Sort(array, null, 0, array.Length, null);
993         }
994
995         // Sorts the elements in a section of an array. The sort compares the
996         // elements to each other using the IComparable interface, which
997         // must be implemented by all elements in the given section of the array.
998         // 
999         public static void Sort(Array array, int index, int length)
1000         {
1001             Sort(array, null, index, length, null);
1002         }
1003
1004         // Sorts the elements of an array. The sort compares the elements to each
1005         // other using the given IComparer interface. If comparer is
1006         // null, the elements are compared to each other using the
1007         // IComparable interface, which in that case must be implemented by
1008         // all elements of the array.
1009         // 
1010         public static void Sort(Array array, IComparer comparer)
1011         {
1012             if (array == null)
1013                 throw new ArgumentNullException(nameof(array));
1014
1015             Sort(array, null, 0, array.Length, comparer);
1016         }
1017
1018         // Sorts the elements in a section of an array. The sort compares the
1019         // elements to each other using the given IComparer interface. If
1020         // comparer is null, the elements are compared to each other using
1021         // the IComparable interface, which in that case must be implemented
1022         // by all elements in the given section of the array.
1023         // 
1024         public static void Sort(Array array, int index, int length, IComparer comparer)
1025         {
1026             Sort(array, null, index, length, comparer);
1027         }
1028
1029         public static void Sort(Array keys, Array items)
1030         {
1031             if (keys == null)
1032             {
1033                 throw new ArgumentNullException(nameof(keys));
1034             }
1035
1036             Sort(keys, items, keys.GetLowerBound(0), keys.Length, null);
1037         }
1038
1039         public static void Sort(Array keys, Array items, IComparer comparer)
1040         {
1041             if (keys == null)
1042             {
1043                 throw new ArgumentNullException(nameof(keys));
1044             }
1045
1046             Sort(keys, items, keys.GetLowerBound(0), keys.Length, comparer);
1047         }
1048
1049         public static void Sort(Array keys, Array items, int index, int length)
1050         {
1051             Sort(keys, items, index, length, null);
1052         }
1053
1054         public static void Sort(Array keys, Array items, int index, int length, IComparer comparer)
1055         {
1056             if (keys == null)
1057                 throw new ArgumentNullException(nameof(keys));
1058             if (keys.Rank != 1 || (items != null && items.Rank != 1))
1059                 throw new RankException(SR.Rank_MultiDimNotSupported);
1060             int keysLowerBound = keys.GetLowerBound(0);
1061             if (items != null && keysLowerBound != items.GetLowerBound(0))
1062                 throw new ArgumentException(SR.Arg_LowerBoundsMustMatch);
1063             if (index < keysLowerBound || length < 0)
1064                 throw new ArgumentOutOfRangeException((length < 0 ? nameof(length) : nameof(index)), SR.ArgumentOutOfRange_NeedNonNegNum);
1065             if (keys.Length - (index - keysLowerBound) < length || (items != null && (index - keysLowerBound) > items.Length - length))
1066                 throw new ArgumentException(SR.Argument_InvalidOffLen);
1067
1068             if (length > 1)
1069             {
1070 #if CORERT
1071                 IComparer<Object> comparerT = new ComparerAsComparerT(comparer);
1072                 Object[] objKeys = keys as Object[];
1073                 Object[] objItems = items as Object[];
1074
1075                 // Unfortunately, on Project N, we don't have the ability to specialize ArraySortHelper<> on demand
1076                 // for value types. Rather than incur a boxing cost on every compare and every swap (and maintain a separate introsort algorithm
1077                 // just for this), box them all, sort them as an Object[] array and unbox them back.
1078
1079                 // Check if either of the arrays need to be copied.
1080                 if (objKeys == null)
1081                 {
1082                     objKeys = new Object[index + length];
1083                     Array.CopyImplValueTypeArrayToReferenceArray(keys, index, objKeys, index, length, reliable: false);
1084                 }
1085                 if (objItems == null && items != null)
1086                 {
1087                     objItems = new Object[index + length];
1088                     Array.CopyImplValueTypeArrayToReferenceArray(items, index, objItems, index, length, reliable: false);
1089                 }
1090
1091                 Sort<Object, Object>(objKeys, objItems, index, length, comparerT);
1092
1093                 // If either array was copied, copy it back into the original
1094                 if (objKeys != keys)
1095                 {
1096                     Array.CopyImplReferenceArrayToValueTypeArray(objKeys, index, keys, index, length, reliable: false);
1097                 }
1098                 if (objItems != items)
1099                 {
1100                     Array.CopyImplReferenceArrayToValueTypeArray(objItems, index, items, index, length, reliable: false);
1101                 }
1102 #else
1103                 Object[] objKeys = keys as Object[];
1104                 Object[] objItems = null;
1105                 if (objKeys != null)
1106                     objItems = items as Object[];
1107                 if (objKeys != null && (items == null || objItems != null))
1108                 {
1109                     SorterObjectArray sorter = new SorterObjectArray(objKeys, objItems, comparer);
1110                     sorter.Sort(index, length);
1111                 }
1112                 else
1113                 {
1114                         SorterGenericArray sorter = new SorterGenericArray(keys, items, comparer);
1115                         sorter.Sort(index, length);
1116                 }
1117 #endif                
1118             }
1119         }
1120
1121         // Wraps an IComparer inside an IComparer<Object>.
1122         private sealed class ComparerAsComparerT : IComparer<Object>
1123         {
1124             public ComparerAsComparerT(IComparer comparer)
1125             {
1126                 _comparer = (comparer == null) ? LowLevelComparer.Default : comparer;
1127             }
1128
1129             public int Compare(Object x, Object y)
1130             {
1131                 return _comparer.Compare(x, y);
1132             }
1133
1134             private IComparer _comparer;
1135         }
1136
1137         public static void Sort<T>(T[] array)
1138         {
1139             if (array == null)
1140                 throw new ArgumentNullException(nameof(array));
1141
1142             Sort<T>(array, 0, array.Length, null);
1143         }
1144
1145         public static void Sort<T>(T[] array, int index, int length)
1146         {
1147             Sort<T>(array, index, length, null);
1148         }
1149
1150         public static void Sort<T>(T[] array, System.Collections.Generic.IComparer<T> comparer)
1151         {
1152             if (array == null)
1153                 throw new ArgumentNullException(nameof(array));
1154             Sort<T>(array, 0, array.Length, comparer);
1155         }
1156
1157         public static void Sort<T>(T[] array, int index, int length, System.Collections.Generic.IComparer<T> comparer)
1158         {
1159             if (array == null)
1160                 throw new ArgumentNullException(nameof(array));
1161             if (index < 0 || length < 0)
1162                 throw new ArgumentOutOfRangeException((length < 0 ? nameof(length) : nameof(index)), SR.ArgumentOutOfRange_NeedNonNegNum);
1163             if (array.Length - index < length)
1164                 throw new ArgumentException(SR.Argument_InvalidOffLen);
1165
1166             if (length > 1)
1167                 ArraySortHelper<T>.Sort(array, index, length, comparer);
1168         }
1169
1170         public static void Sort<T>(T[] array, Comparison<T> comparison)
1171         {
1172             if (array == null)
1173             {
1174                 throw new ArgumentNullException(nameof(array));
1175             }
1176
1177             if (comparison == null)
1178             {
1179                 throw new ArgumentNullException(nameof(comparison));
1180             }
1181
1182             ArraySortHelper<T>.Sort(array, 0, array.Length, comparison);
1183         }
1184
1185         public static void Sort<TKey, TValue>(TKey[] keys, TValue[] items)
1186         {
1187             if (keys == null)
1188                 throw new ArgumentNullException(nameof(keys));
1189             Contract.EndContractBlock();
1190             Sort<TKey, TValue>(keys, items, 0, keys.Length, null);
1191         }
1192
1193         public static void Sort<TKey, TValue>(TKey[] keys, TValue[] items, int index, int length)
1194         {
1195             Sort<TKey, TValue>(keys, items, index, length, null);
1196         }
1197
1198         public static void Sort<TKey, TValue>(TKey[] keys, TValue[] items, IComparer<TKey> comparer)
1199         {
1200             if (keys == null)
1201                 throw new ArgumentNullException(nameof(keys));
1202             Contract.EndContractBlock();
1203             Sort<TKey, TValue>(keys, items, 0, keys.Length, comparer);
1204         }
1205
1206         public static void Sort<TKey, TValue>(TKey[] keys, TValue[] items, int index, int length, IComparer<TKey> comparer)
1207         {
1208             if (keys == null)
1209                 throw new ArgumentNullException(nameof(keys));
1210             if (index < 0 || length < 0)
1211                 throw new ArgumentOutOfRangeException((length < 0 ? nameof(length) : nameof(index)), SR.ArgumentOutOfRange_NeedNonNegNum);
1212             if (keys.Length - index < length || (items != null && index > items.Length - length))
1213                 throw new ArgumentException(SR.Argument_InvalidOffLen);
1214             Contract.EndContractBlock();
1215
1216             if (length > 1)
1217             {
1218                 if (items == null)
1219                 {
1220                     Sort<TKey>(keys, index, length, comparer);
1221                     return;
1222                 }
1223
1224                 ArraySortHelper<TKey, TValue>.Default.Sort(keys, items, index, length, comparer);
1225             }
1226         }
1227
1228         public static T[] Empty<T>()
1229         {
1230             return EmptyArray<T>.Value;
1231         }
1232
1233         public static bool Exists<T>(T[] array, Predicate<T> match)
1234         {
1235             return Array.FindIndex(array, match) != -1;
1236         }
1237
1238         public static void Fill<T>(T[] array, T value)
1239         {
1240             if (array == null)
1241             {
1242                 throw new ArgumentNullException(nameof(array));
1243             }
1244
1245             for (int i = 0; i < array.Length; i++)
1246             {
1247                 array[i] = value;
1248             }
1249         }
1250
1251         public static void Fill<T>(T[] array, T value, int startIndex, int count)
1252         {
1253             if (array == null)
1254             {
1255                 throw new ArgumentNullException(nameof(array));
1256             }
1257
1258             if (startIndex < 0 || startIndex > array.Length)
1259             {
1260                 throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
1261             }
1262
1263             if (count < 0 || startIndex > array.Length - count)
1264             {
1265                 throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count);
1266             }
1267
1268             for (int i = startIndex; i < startIndex + count; i++)
1269             {
1270                 array[i] = value;
1271             }
1272         }
1273
1274         public static T Find<T>(T[] array, Predicate<T> match)
1275         {
1276             if (array == null)
1277             {
1278                 throw new ArgumentNullException(nameof(array));
1279             }
1280
1281             if (match == null)
1282             {
1283                 throw new ArgumentNullException(nameof(match));
1284             }
1285
1286             for (int i = 0; i < array.Length; i++)
1287             {
1288                 if (match(array[i]))
1289                 {
1290                     return array[i];
1291                 }
1292             }
1293             return default(T);
1294         }
1295
1296         public static int FindIndex<T>(T[] array, Predicate<T> match)
1297         {
1298             if (array == null)
1299             {
1300                 throw new ArgumentNullException(nameof(array));
1301             }
1302
1303             return FindIndex(array, 0, array.Length, match);
1304         }
1305
1306         public static int FindIndex<T>(T[] array, int startIndex, Predicate<T> match)
1307         {
1308             if (array == null)
1309             {
1310                 throw new ArgumentNullException(nameof(array));
1311             }
1312
1313             return FindIndex(array, startIndex, array.Length - startIndex, match);
1314         }
1315
1316         public static int FindIndex<T>(T[] array, int startIndex, int count, Predicate<T> match)
1317         {
1318             if (array == null)
1319             {
1320                 throw new ArgumentNullException(nameof(array));
1321             }
1322
1323             if (startIndex < 0 || startIndex > array.Length)
1324             {
1325                 throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
1326             }
1327
1328             if (count < 0 || startIndex > array.Length - count)
1329             {
1330                 throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count);
1331             }
1332
1333             if (match == null)
1334             {
1335                 throw new ArgumentNullException(nameof(match));
1336             }
1337
1338             int endIndex = startIndex + count;
1339             for (int i = startIndex; i < endIndex; i++)
1340             {
1341                 if (match(array[i])) return i;
1342             }
1343             return -1;
1344         }
1345
1346         public static T FindLast<T>(T[] array, Predicate<T> match)
1347         {
1348             if (array == null)
1349             {
1350                 throw new ArgumentNullException(nameof(array));
1351             }
1352
1353             if (match == null)
1354             {
1355                 throw new ArgumentNullException(nameof(match));
1356             }
1357
1358             for (int i = array.Length - 1; i >= 0; i--)
1359             {
1360                 if (match(array[i]))
1361                 {
1362                     return array[i];
1363                 }
1364             }
1365             return default(T);
1366         }
1367
1368         public static int FindLastIndex<T>(T[] array, Predicate<T> match)
1369         {
1370             if (array == null)
1371             {
1372                 throw new ArgumentNullException(nameof(array));
1373             }
1374
1375             return FindLastIndex(array, array.Length - 1, array.Length, match);
1376         }
1377
1378         public static int FindLastIndex<T>(T[] array, int startIndex, Predicate<T> match)
1379         {
1380             if (array == null)
1381             {
1382                 throw new ArgumentNullException(nameof(array));
1383             }
1384
1385             return FindLastIndex(array, startIndex, startIndex + 1, match);
1386         }
1387
1388         public static int FindLastIndex<T>(T[] array, int startIndex, int count, Predicate<T> match)
1389         {
1390             if (array == null)
1391             {
1392                 throw new ArgumentNullException(nameof(array));
1393             }
1394
1395             if (match == null)
1396             {
1397                 throw new ArgumentNullException(nameof(match));
1398             }
1399
1400             if (array.Length == 0)
1401             {
1402                 // Special case for 0 length List
1403                 if (startIndex != -1)
1404                 {
1405                     throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
1406                 }
1407             }
1408             else
1409             {
1410                 // Make sure we're not out of range            
1411                 if (startIndex < 0 || startIndex >= array.Length)
1412                 {
1413                     throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
1414                 }
1415             }
1416
1417             // 2nd have of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0.
1418             if (count < 0 || startIndex - count + 1 < 0)
1419             {
1420                 throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count);
1421             }
1422
1423             int endIndex = startIndex - count;
1424             for (int i = startIndex; i > endIndex; i--)
1425             {
1426                 if (match(array[i]))
1427                 {
1428                     return i;
1429                 }
1430             }
1431             return -1;
1432         }
1433
1434         public static bool TrueForAll<T>(T[] array, Predicate<T> match)
1435         {
1436             if (array == null)
1437             {
1438                 throw new ArgumentNullException(nameof(array));
1439             }
1440
1441             if (match == null)
1442             {
1443                 throw new ArgumentNullException(nameof(match));
1444             }
1445
1446             for (int i = 0; i < array.Length; i++)
1447             {
1448                 if (!match(array[i]))
1449                 {
1450                     return false;
1451                 }
1452             }
1453             return true;
1454         }
1455
1456         public IEnumerator GetEnumerator()
1457         {
1458             return new ArrayEnumerator(this);
1459         }
1460
1461         // These functions look odd, as they are part of a complex series of compiler intrinsics
1462         // designed to produce very high quality code for equality comparison cases without utilizing
1463         // reflection like other platforms. The major complication is that the specification of
1464         // IndexOf is that it is supposed to use IEquatable<T> if possible, but that requirement
1465         // cannot be expressed in IL directly due to the lack of constraints.
1466         // Instead, specialization at call time is used within the compiler. 
1467         // 
1468         // General Approach
1469         // - Perform fancy redirection for Array.GetComparerForReferenceTypesOnly<T>(). If T is a reference 
1470         //   type or UniversalCanon, have this redirect to EqualityComparer<T>.get_Default, Otherwise, use 
1471         //   the function as is. (will return null in that case)
1472         // - Change the contents of the IndexOf functions to have a pair of loops. One for if 
1473         //   GetComparerForReferenceTypesOnly returns null, and one for when it does not. 
1474         //   - If it does not return null, call the EqualityComparer<T> code.
1475         //   - If it does return null, use a special function StructOnlyEquals<T>(). 
1476         //     - Calls to that function result in calls to a pair of helper function in 
1477         //       EqualityComparerHelpers (StructOnlyEqualsIEquatable, or StructOnlyEqualsNullable) 
1478         //       depending on whether or not they are the right function to call.
1479         // - The end result is that in optimized builds, we have the same single function compiled size 
1480         //   characteristics that the old EqualsOnlyComparer<T>.Equals function had, but we maintain 
1481         //   correctness as well.
1482         private static EqualityComparer<T> GetComparerForReferenceTypesOnly<T>()
1483         {
1484 #if !CORERT
1485                 return System.Runtime.CompilerServices.RuntimeHelpers.IsReferenceOrContainsReferences<T> () ? EqualityComparer<T>.Default : null;
1486 #else
1487             return EqualityComparer<T>.Default;
1488 #endif
1489         }
1490
1491         private static bool StructOnlyEquals<T>(T left, T right)
1492         {
1493             return left.Equals(right);
1494         }
1495
1496         private sealed class ArrayEnumerator : IEnumerator, ICloneable
1497         {
1498             private Array _array;
1499             private int _index;
1500             private int _endIndex; // cache array length, since it's a little slow.
1501
1502             internal ArrayEnumerator(Array array)
1503             {
1504                 _array = array;
1505                 _index = -1;
1506                 _endIndex = array.Length;
1507             }
1508
1509             public bool MoveNext()
1510             {
1511                 if (_index < _endIndex)
1512                 {
1513                     _index++;
1514                     return (_index < _endIndex);
1515                 }
1516                 return false;
1517             }
1518
1519             public Object Current
1520             {
1521                 get
1522                 {
1523                     if (_index < 0) throw new InvalidOperationException(SR.InvalidOperation_EnumNotStarted);
1524                     if (_index >= _endIndex) throw new InvalidOperationException(SR.InvalidOperation_EnumEnded);
1525                     return _array.GetValueWithFlattenedIndex_NoErrorCheck(_index);
1526                 }
1527             }
1528
1529             public void Reset()
1530             {
1531                 _index = -1;
1532             }
1533
1534             public object Clone()
1535             {
1536                 return MemberwiseClone();
1537             }
1538         }
1539     }
1540 }