Merge pull request #901 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Dynamic / Utils / CollectionExtensions.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
5  * This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
6  * copy of the license can be found in the License.html file at the root of this distribution. If 
7  * you cannot locate the  Apache License, Version 2.0, please send an email to 
8  * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
9  * by the terms of the Apache License, Version 2.0.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15
16 using System;
17 using System.Collections.Generic;
18 using System.Collections.ObjectModel;
19 using System.Diagnostics;
20
21 namespace Microsoft.Scripting.Utils {
22     internal static class CollectionExtensions {
23         /// <summary>
24         /// Wraps the provided enumerable into a ReadOnlyCollection{T}
25         /// 
26         /// Copies all of the data into a new array, so the data can't be
27         /// changed after creation. The exception is if the enumerable is
28         /// already a ReadOnlyCollection{T}, in which case we just return it.
29         /// </summary>
30         internal static ReadOnlyCollection<T> ToReadOnly<T>(this IEnumerable<T> enumerable) {
31             if (enumerable == null) {
32                 return EmptyReadOnlyCollection<T>.Instance;
33             }
34
35             var roCollection = enumerable as ReadOnlyCollection<T>;
36             if (roCollection != null) {
37                 return roCollection;
38             }
39
40             var collection = enumerable as ICollection<T>;
41             if (collection != null) {
42                 int count = collection.Count;
43                 if (count == 0) {
44                     return EmptyReadOnlyCollection<T>.Instance;
45                 }
46
47                 T[] array = new T[count];
48                 collection.CopyTo(array, 0);
49                 return new ReadOnlyCollection<T>(array);
50             }
51
52             // ToArray trims the excess space and speeds up access
53             return new ReadOnlyCollection<T>(new List<T>(enumerable).ToArray());
54         }
55
56         // We could probably improve the hashing here
57         internal static int ListHashCode<T>(this IEnumerable<T> list) {
58             var cmp = EqualityComparer<T>.Default;
59             int h = 6551;
60             foreach (T t in list) {
61                 h ^= (h << 5) ^ cmp.GetHashCode(t);
62             }
63             return h;
64         }
65
66         internal static bool ListEquals<T>(this ICollection<T> first, ICollection<T> second) {
67             if (first.Count != second.Count) {
68                 return false;
69             }
70             var cmp = EqualityComparer<T>.Default;
71             var f = first.GetEnumerator();
72             var s = second.GetEnumerator();
73             while (f.MoveNext()) {
74                 s.MoveNext();
75
76                 if (!cmp.Equals(f.Current, s.Current)) {
77                     return false;
78                 }
79             }
80             return true;
81         }
82
83         // Name needs to be different so it doesn't conflict with Enumerable.Select
84         internal static U[] Map<T, U>(this ICollection<T> collection, Func<T, U> select) {
85             int count = collection.Count;
86             U[] result = new U[count];
87             count = 0;
88             foreach (T t in collection) {
89                 result[count++] = select(t);
90             }
91             return result;
92         }
93
94         internal static T[] RemoveFirst<T>(this T[] array) {
95             T[] result = new T[array.Length - 1];
96             Array.Copy(array, 1, result, 0, result.Length);
97             return result;
98         }
99
100         internal static T[] RemoveLast<T>(this T[] array) {
101             T[] result = new T[array.Length - 1];
102             Array.Copy(array, 0, result, 0, result.Length);
103             return result;
104         }
105
106         internal static T[] AddFirst<T>(this IList<T> list, T item) {
107             T[] res = new T[list.Count + 1];
108             res[0] = item;
109             list.CopyTo(res, 1);
110             return res;
111         }
112
113         internal static T[] AddLast<T>(this IList<T> list, T item) {
114             T[] res = new T[list.Count + 1];
115             list.CopyTo(res, 0);
116             res[list.Count] = item;
117             return res;
118         }
119
120         internal static T[] RemoveAt<T>(this T[] array, int indexToRemove) {
121             Debug.Assert(array != null);
122             Debug.Assert(indexToRemove >= 0 && indexToRemove < array.Length);
123
124             T[] result = new T[array.Length - 1];
125             if (indexToRemove > 0) {
126                 Array.Copy(array, 0, result, 0, indexToRemove);
127             }
128             int remaining = array.Length - indexToRemove - 1;
129             if (remaining > 0) {
130                 Array.Copy(array, array.Length - remaining, result, result.Length - remaining, remaining);
131             }
132             return result;
133         }
134
135         internal static T[] RotateRight<T>(this T[] array, int count) {
136             Debug.Assert(count >= 0 && count <= array.Length);
137
138             T[] result = new T[array.Length];
139             // The head of the array is shifted, and the tail will be rotated to the head of the resulting array
140             int sizeOfShiftedArray = array.Length - count;
141             Array.Copy(array, 0, result, count, sizeOfShiftedArray);
142             Array.Copy(array, sizeOfShiftedArray, result, 0, count);
143             return result;
144         }
145     }
146
147
148     internal static class EmptyReadOnlyCollection<T> {
149         internal static ReadOnlyCollection<T> Instance = new ReadOnlyCollection<T>(new T[0]);
150     }
151     // TODO: Should we use this everywhere for empty arrays?
152     // my thought is, probably more hassle than its worth
153     internal static class EmptyArray<T> {
154         internal static T[] Instance = new T[0];
155     }
156 }