summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/Collections
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/src/System/Collections')
-rw-r--r--src/mscorlib/src/System/Collections/ArrayList.cs2635
-rw-r--r--src/mscorlib/src/System/Collections/BitArray.cs524
-rw-r--r--src/mscorlib/src/System/Collections/CollectionBase.cs215
-rw-r--r--src/mscorlib/src/System/Collections/Comparer.cs98
-rw-r--r--src/mscorlib/src/System/Collections/CompatibleComparer.cs67
-rw-r--r--src/mscorlib/src/System/Collections/Concurrent/ConcurrentDictionary.cs2095
-rw-r--r--src/mscorlib/src/System/Collections/Concurrent/ConcurrentQueue.cs960
-rw-r--r--src/mscorlib/src/System/Collections/Concurrent/ConcurrentStack.cs840
-rw-r--r--src/mscorlib/src/System/Collections/Concurrent/IProducerConsumerCollection.cs116
-rw-r--r--src/mscorlib/src/System/Collections/Concurrent/OrderablePartitioner.cs281
-rw-r--r--src/mscorlib/src/System/Collections/Concurrent/Partitioner.cs102
-rw-r--r--src/mscorlib/src/System/Collections/Concurrent/PartitionerStatic.cs1733
-rw-r--r--src/mscorlib/src/System/Collections/DictionaryEntry.cs55
-rw-r--r--src/mscorlib/src/System/Collections/EmptyReadOnlyDictionaryInternal.cs194
-rw-r--r--src/mscorlib/src/System/Collections/Generic/ArraySortHelper.cs1558
-rw-r--r--src/mscorlib/src/System/Collections/Generic/Comparer.cs325
-rw-r--r--src/mscorlib/src/System/Collections/Generic/DebugView.cs129
-rw-r--r--src/mscorlib/src/System/Collections/Generic/Dictionary.cs1187
-rw-r--r--src/mscorlib/src/System/Collections/Generic/EqualityComparer.cs621
-rw-r--r--src/mscorlib/src/System/Collections/Generic/ICollection.cs52
-rw-r--r--src/mscorlib/src/System/Collections/Generic/IComparer.cs30
-rw-r--r--src/mscorlib/src/System/Collections/Generic/IDictionary.cs58
-rw-r--r--src/mscorlib/src/System/Collections/Generic/IEnumerable.cs38
-rw-r--r--src/mscorlib/src/System/Collections/Generic/IEnumerator.cs35
-rw-r--r--src/mscorlib/src/System/Collections/Generic/IEqualityComparer.cs19
-rw-r--r--src/mscorlib/src/System/Collections/Generic/IList.cs54
-rw-r--r--src/mscorlib/src/System/Collections/Generic/IReadOnlyCollection.cs34
-rw-r--r--src/mscorlib/src/System/Collections/Generic/IReadOnlyDictionary.cs29
-rw-r--r--src/mscorlib/src/System/Collections/Generic/IReadOnlyList.cs34
-rw-r--r--src/mscorlib/src/System/Collections/Generic/KeyNotFoundException.cs45
-rw-r--r--src/mscorlib/src/System/Collections/Generic/KeyValuePair.cs56
-rw-r--r--src/mscorlib/src/System/Collections/Generic/List.cs1120
-rw-r--r--src/mscorlib/src/System/Collections/Hashtable.cs1847
-rw-r--r--src/mscorlib/src/System/Collections/ICollection.cs81
-rw-r--r--src/mscorlib/src/System/Collections/IComparer.cs32
-rw-r--r--src/mscorlib/src/System/Collections/IDictionary.cs69
-rw-r--r--src/mscorlib/src/System/Collections/IDictionaryEnumerator.cs79
-rw-r--r--src/mscorlib/src/System/Collections/IEnumerable.cs34
-rw-r--r--src/mscorlib/src/System/Collections/IEnumerator.cs53
-rw-r--r--src/mscorlib/src/System/Collections/IEqualityComparer.cs27
-rw-r--r--src/mscorlib/src/System/Collections/IHashCodeProvider.cs30
-rw-r--r--src/mscorlib/src/System/Collections/IList.cs71
-rw-r--r--src/mscorlib/src/System/Collections/IStructuralComparable.cs11
-rw-r--r--src/mscorlib/src/System/Collections/IStructuralEquatable.cs10
-rw-r--r--src/mscorlib/src/System/Collections/KeyValuePairs.cs40
-rw-r--r--src/mscorlib/src/System/Collections/ListDictionaryInternal.cs429
-rw-r--r--src/mscorlib/src/System/Collections/ObjectModel/Collection.cs328
-rw-r--r--src/mscorlib/src/System/Collections/ObjectModel/KeyedCollection.cs245
-rw-r--r--src/mscorlib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs232
-rw-r--r--src/mscorlib/src/System/Collections/ObjectModel/ReadOnlyDictionary.cs625
-rw-r--r--src/mscorlib/src/System/Collections/SortedList.cs1011
-rw-r--r--src/mscorlib/src/System/Collections/Stack.cs380
-rw-r--r--src/mscorlib/src/System/Collections/StructuralComparisons.cs89
53 files changed, 21062 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Collections/ArrayList.cs b/src/mscorlib/src/System/Collections/ArrayList.cs
new file mode 100644
index 0000000000..94f4dc74e8
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/ArrayList.cs
@@ -0,0 +1,2635 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Implements a dynamically sized List as an array,
+** and provides many convenience methods for treating
+** an array as an IList.
+**
+**
+===========================================================*/
+namespace System.Collections {
+ using System;
+ using System.Runtime;
+ using System.Security;
+ using System.Security.Permissions;
+ using System.Diagnostics;
+ using System.Runtime.CompilerServices;
+ using System.Runtime.Serialization;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+
+ // Implements a variable-size List that uses an array of objects to store the
+ // elements. A ArrayList has a capacity, which is the allocated length
+ // of the internal array. As elements are added to a ArrayList, the capacity
+ // of the ArrayList is automatically increased as required by reallocating the
+ // internal array.
+ //
+#if FEATURE_CORECLR
+ [FriendAccessAllowed]
+#endif
+ [DebuggerTypeProxy(typeof(System.Collections.ArrayList.ArrayListDebugView))]
+ [DebuggerDisplay("Count = {Count}")]
+ [Serializable]
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public class ArrayList : IList, ICloneable
+ {
+ private Object[] _items;
+ [ContractPublicPropertyName("Count")]
+ private int _size;
+ private int _version;
+ [NonSerialized]
+ private Object _syncRoot;
+
+ private const int _defaultCapacity = 4;
+ private static readonly Object[] emptyArray = EmptyArray<Object>.Value;
+
+ // Note: this constructor is a bogus constructor that does nothing
+ // and is for use only with SyncArrayList.
+ internal ArrayList( bool trash )
+ {
+ }
+
+ // Constructs a ArrayList. The list is initially empty and has a capacity
+ // of zero. Upon adding the first element to the list the capacity is
+ // increased to _defaultCapacity, and then increased in multiples of two as required.
+ public ArrayList() {
+ _items = emptyArray;
+ }
+
+ // Constructs a ArrayList with a given initial capacity. The list is
+ // initially empty, but will have room for the given number of elements
+ // before any reallocations are required.
+ //
+ public ArrayList(int capacity) {
+ if (capacity < 0) throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_MustBeNonNegNum", "capacity"));
+ Contract.EndContractBlock();
+
+ if (capacity == 0)
+ _items = emptyArray;
+ else
+ _items = new Object[capacity];
+ }
+
+ // Constructs a ArrayList, copying the contents of the given collection. The
+ // size and capacity of the new list will both be equal to the size of the
+ // given collection.
+ //
+ public ArrayList(ICollection c) {
+ if (c==null)
+ throw new ArgumentNullException("c", Environment.GetResourceString("ArgumentNull_Collection"));
+ Contract.EndContractBlock();
+
+ int count = c.Count;
+ if (count == 0)
+ {
+ _items = emptyArray;
+ }
+ else {
+ _items = new Object[count];
+ AddRange(c);
+ }
+ }
+
+ // Gets and sets the capacity of this list. The capacity is the size of
+ // the internal array used to hold items. When set, the internal
+ // array of the list is reallocated to the given capacity.
+ //
+ public virtual int Capacity {
+ get {
+ Contract.Ensures(Contract.Result<int>() >= Count);
+ return _items.Length;
+ }
+ set {
+ if (value < _size) {
+ throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
+ }
+ Contract.Ensures(Capacity >= 0);
+ Contract.EndContractBlock();
+ // We don't want to update the version number when we change the capacity.
+ // Some existing applications have dependency on this.
+ if (value != _items.Length) {
+ if (value > 0) {
+ Object[] newItems = new Object[value];
+ if (_size > 0) {
+ Array.Copy(_items, 0, newItems, 0, _size);
+ }
+ _items = newItems;
+ }
+ else {
+ _items = new Object[_defaultCapacity];
+ }
+ }
+ }
+ }
+
+ // Read-only property describing how many elements are in the List.
+ public virtual int Count {
+ get {
+ Contract.Ensures(Contract.Result<int>() >= 0);
+ return _size;
+ }
+ }
+
+ public virtual bool IsFixedSize {
+ get { return false; }
+ }
+
+
+ // Is this ArrayList read-only?
+ public virtual bool IsReadOnly {
+ get { return false; }
+ }
+
+ // Is this ArrayList synchronized (thread-safe)?
+ public virtual bool IsSynchronized {
+ get { return false; }
+ }
+
+ // Synchronization root for this object.
+ public virtual Object SyncRoot {
+ get {
+ if( _syncRoot == null) {
+ System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);
+ }
+ return _syncRoot;
+ }
+ }
+
+ // Sets or Gets the element at the given index.
+ //
+ public virtual Object this[int index] {
+ get {
+ if (index < 0 || index >= _size) throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ Contract.EndContractBlock();
+ return _items[index];
+ }
+ set {
+ if (index < 0 || index >= _size) throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ Contract.EndContractBlock();
+ _items[index] = value;
+ _version++;
+ }
+ }
+
+ // Creates a ArrayList wrapper for a particular IList. This does not
+ // copy the contents of the IList, but only wraps the ILIst. So any
+ // changes to the underlying list will affect the ArrayList. This would
+ // be useful if you want to Reverse a subrange of an IList, or want to
+ // use a generic BinarySearch or Sort method without implementing one yourself.
+ // However, since these methods are generic, the performance may not be
+ // nearly as good for some operations as they would be on the IList itself.
+ //
+ public static ArrayList Adapter(IList list) {
+ if (list==null)
+ throw new ArgumentNullException("list");
+ Contract.Ensures(Contract.Result<ArrayList>() != null);
+ Contract.EndContractBlock();
+ return new IListWrapper(list);
+ }
+
+ // Adds the given object to the end of this list. The size of the list is
+ // increased by one. If required, the capacity of the list is doubled
+ // before adding the new element.
+ //
+ public virtual int Add(Object value) {
+ Contract.Ensures(Contract.Result<int>() >= 0);
+ if (_size == _items.Length) EnsureCapacity(_size + 1);
+ _items[_size] = value;
+ _version++;
+ return _size++;
+ }
+
+ // Adds the elements of the given collection to the end of this list. If
+ // required, the capacity of the list is increased to twice the previous
+ // capacity or the new size, whichever is larger.
+ //
+ public virtual void AddRange(ICollection c) {
+ InsertRange(_size, c);
+ }
+
+ // Searches a section of the list for a given element using a binary search
+ // algorithm. Elements of the list are compared to the search value using
+ // the given IComparer interface. If comparer is null, elements of
+ // the list are compared to the search value using the IComparable
+ // interface, which in that case must be implemented by all elements of the
+ // list and the given search value. This method assumes that the given
+ // section of the list is already sorted; if this is not the case, the
+ // result will be incorrect.
+ //
+ // The method returns the index of the given value in the list. If the
+ // list does not contain the given value, the method returns a negative
+ // integer. The bitwise complement operator (~) can be applied to a
+ // negative result to produce the index of the first element (if any) that
+ // is larger than the given search value. This is also the index at which
+ // the search value should be inserted into the list in order for the list
+ // to remain sorted.
+ //
+ // The method uses the Array.BinarySearch method to perform the
+ // search.
+ //
+ public virtual int BinarySearch(int index, int count, Object value, IComparer comparer) {
+ if (index < 0)
+ throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (count < 0)
+ throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (_size - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ Contract.Ensures(Contract.Result<int>() < Count);
+ Contract.Ensures(Contract.Result<int>() < index + count);
+ Contract.EndContractBlock();
+
+ return Array.BinarySearch((Array)_items, index, count, value, comparer);
+ }
+
+ public virtual int BinarySearch(Object value)
+ {
+ Contract.Ensures(Contract.Result<int>() < Count);
+ return BinarySearch(0, Count, value, null);
+ }
+
+ public virtual int BinarySearch(Object value, IComparer comparer)
+ {
+ Contract.Ensures(Contract.Result<int>() < Count);
+ return BinarySearch(0, Count, value, comparer);
+ }
+
+
+ // Clears the contents of ArrayList.
+ public virtual void Clear() {
+ if (_size > 0)
+ {
+ Array.Clear(_items, 0, _size); // Don't need to doc this but we clear the elements so that the gc can reclaim the references.
+ _size = 0;
+ }
+ _version++;
+ }
+
+ // Clones this ArrayList, doing a shallow copy. (A copy is made of all
+ // Object references in the ArrayList, but the Objects pointed to
+ // are not cloned).
+ public virtual Object Clone()
+ {
+ Contract.Ensures(Contract.Result<Object>() != null);
+ ArrayList la = new ArrayList(_size);
+ la._size = _size;
+ la._version = _version;
+ Array.Copy(_items, 0, la._items, 0, _size);
+ return la;
+ }
+
+
+ // Contains returns true if the specified element is in the ArrayList.
+ // It does a linear, O(n) search. Equality is determined by calling
+ // item.Equals().
+ //
+ public virtual bool Contains(Object item) {
+ if (item==null) {
+ for(int i=0; i<_size; i++)
+ if (_items[i]==null)
+ return true;
+ return false;
+ }
+ else {
+ for(int i=0; i<_size; i++)
+ if ( (_items[i] != null) && (_items[i].Equals(item)) )
+ return true;
+ return false;
+ }
+ }
+
+ // Copies this ArrayList into array, which must be of a
+ // compatible array type.
+ //
+ public virtual void CopyTo(Array array) {
+ CopyTo(array, 0);
+ }
+
+ // Copies this ArrayList into array, which must be of a
+ // compatible array type.
+ //
+ public virtual void CopyTo(Array array, int arrayIndex) {
+ if ((array != null) && (array.Rank != 1))
+ throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
+ Contract.EndContractBlock();
+ // Delegate rest of error checking to Array.Copy.
+ Array.Copy(_items, 0, array, arrayIndex, _size);
+ }
+
+ // Copies a section of this list to the given array at the given index.
+ //
+ // The method uses the Array.Copy method to copy the elements.
+ //
+ public virtual void CopyTo(int index, Array array, int arrayIndex, int count) {
+ if (_size - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ if ((array != null) && (array.Rank != 1))
+ throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
+ Contract.EndContractBlock();
+ // Delegate rest of error checking to Array.Copy.
+ Array.Copy(_items, index, array, arrayIndex, count);
+ }
+
+ // Ensures that the capacity of this list is at least the given minimum
+ // value. If the currect capacity of the list is less than min, the
+ // capacity is increased to twice the current capacity or to min,
+ // whichever is larger.
+ private void EnsureCapacity(int min) {
+ if (_items.Length < min) {
+ int newCapacity = _items.Length == 0? _defaultCapacity: _items.Length * 2;
+ // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow.
+ // Note that this check works even when _items.Length overflowed thanks to the (uint) cast
+ if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength;
+ if (newCapacity < min) newCapacity = min;
+ Capacity = newCapacity;
+ }
+ }
+
+ // Returns a list wrapper that is fixed at the current size. Operations
+ // that add or remove items will fail, however, replacing items is allowed.
+ //
+ public static IList FixedSize(IList list) {
+ if (list==null)
+ throw new ArgumentNullException("list");
+ Contract.Ensures(Contract.Result<IList>() != null);
+ Contract.EndContractBlock();
+ return new FixedSizeList(list);
+ }
+
+ // Returns a list wrapper that is fixed at the current size. Operations
+ // that add or remove items will fail, however, replacing items is allowed.
+ //
+ public static ArrayList FixedSize(ArrayList list) {
+ if (list==null)
+ throw new ArgumentNullException("list");
+ Contract.Ensures(Contract.Result<ArrayList>() != null);
+ Contract.EndContractBlock();
+ return new FixedSizeArrayList(list);
+ }
+
+ // Returns an enumerator for this list with the given
+ // permission for removal of elements. If modifications made to the list
+ // while an enumeration is in progress, the MoveNext and
+ // GetObject methods of the enumerator will throw an exception.
+ //
+ public virtual IEnumerator GetEnumerator() {
+ Contract.Ensures(Contract.Result<IEnumerator>() != null);
+ return new ArrayListEnumeratorSimple(this);
+ }
+
+ // Returns an enumerator for a section of this list with the given
+ // permission for removal of elements. If modifications made to the list
+ // while an enumeration is in progress, the MoveNext and
+ // GetObject methods of the enumerator will throw an exception.
+ //
+ public virtual IEnumerator GetEnumerator(int index, int count) {
+ if (index < 0)
+ throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (count < 0)
+ throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (_size - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ Contract.Ensures(Contract.Result<IEnumerator>() != null);
+ Contract.EndContractBlock();
+
+ return new ArrayListEnumerator(this, index, count);
+ }
+
+ // Returns the index of the first occurrence of a given value in a range of
+ // this list. The list is searched forwards from beginning to end.
+ // The elements of the list are compared to the given value using the
+ // Object.Equals method.
+ //
+ // This method uses the Array.IndexOf method to perform the
+ // search.
+ //
+ public virtual int IndexOf(Object value) {
+ Contract.Ensures(Contract.Result<int>() < Count);
+ return Array.IndexOf((Array)_items, value, 0, _size);
+ }
+
+ // Returns the index of the first occurrence of a given value in a range of
+ // this list. The list is searched forwards, starting at index
+ // startIndex and ending at count number of elements. The
+ // elements of the list are compared to the given value using the
+ // Object.Equals method.
+ //
+ // This method uses the Array.IndexOf method to perform the
+ // search.
+ //
+ public virtual int IndexOf(Object value, int startIndex) {
+ if (startIndex > _size)
+ throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ Contract.Ensures(Contract.Result<int>() < Count);
+ Contract.EndContractBlock();
+ return Array.IndexOf((Array)_items, value, startIndex, _size - startIndex);
+ }
+
+ // Returns the index of the first occurrence of a given value in a range of
+ // this list. The list is searched forwards, starting at index
+ // startIndex and upto count number of elements. The
+ // elements of the list are compared to the given value using the
+ // Object.Equals method.
+ //
+ // This method uses the Array.IndexOf method to perform the
+ // search.
+ //
+ public virtual int IndexOf(Object value, int startIndex, int count) {
+ if (startIndex > _size)
+ throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ if (count <0 || startIndex > _size - count) throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Count"));
+ Contract.Ensures(Contract.Result<int>() < Count);
+ Contract.EndContractBlock();
+ return Array.IndexOf((Array)_items, value, startIndex, count);
+ }
+
+ // Inserts an element into this list at a given index. The size of the list
+ // is increased by one. If required, the capacity of the list is doubled
+ // before inserting the new element.
+ //
+ public virtual void Insert(int index, Object value) {
+ // Note that insertions at the end are legal.
+ if (index < 0 || index > _size) throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_ArrayListInsert"));
+ //Contract.Ensures(Count == Contract.OldValue(Count) + 1);
+ Contract.EndContractBlock();
+
+ if (_size == _items.Length) EnsureCapacity(_size + 1);
+ if (index < _size) {
+ Array.Copy(_items, index, _items, index + 1, _size - index);
+ }
+ _items[index] = value;
+ _size++;
+ _version++;
+ }
+
+ // Inserts the elements of the given collection at a given index. If
+ // required, the capacity of the list is increased to twice the previous
+ // capacity or the new size, whichever is larger. Ranges may be added
+ // to the end of the list by setting index to the ArrayList's size.
+ //
+ public virtual void InsertRange(int index, ICollection c) {
+ if (c==null)
+ throw new ArgumentNullException("c", Environment.GetResourceString("ArgumentNull_Collection"));
+ if (index < 0 || index > _size) throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ //Contract.Ensures(Count == Contract.OldValue(Count) + c.Count);
+ Contract.EndContractBlock();
+
+ int count = c.Count;
+ if (count > 0) {
+ EnsureCapacity(_size + count);
+ // shift existing items
+ if (index < _size) {
+ Array.Copy(_items, index, _items, index + count, _size - index);
+ }
+
+ Object[] itemsToInsert = new Object[count];
+ c.CopyTo(itemsToInsert, 0);
+ itemsToInsert.CopyTo(_items, index);
+ _size += count;
+ _version++;
+ }
+ }
+
+ // Returns the index of the last occurrence of a given value in a range of
+ // this list. The list is searched backwards, starting at the end
+ // and ending at the first element in the list. The elements of the list
+ // are compared to the given value using the Object.Equals method.
+ //
+ // This method uses the Array.LastIndexOf method to perform the
+ // search.
+ //
+ public virtual int LastIndexOf(Object value)
+ {
+ Contract.Ensures(Contract.Result<int>() < _size);
+ return LastIndexOf(value, _size - 1, _size);
+ }
+
+ // Returns the index of the last occurrence of a given value in a range of
+ // this list. The list is searched backwards, starting at index
+ // startIndex and ending at the first element in the list. The
+ // elements of the list are compared to the given value using the
+ // Object.Equals method.
+ //
+ // This method uses the Array.LastIndexOf method to perform the
+ // search.
+ //
+ public virtual int LastIndexOf(Object value, int startIndex)
+ {
+ if (startIndex >= _size)
+ throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ Contract.Ensures(Contract.Result<int>() < Count);
+ Contract.EndContractBlock();
+ return LastIndexOf(value, startIndex, startIndex + 1);
+ }
+
+ // Returns the index of the last occurrence of a given value in a range of
+ // this list. The list is searched backwards, starting at index
+ // startIndex and upto count elements. The elements of
+ // the list are compared to the given value using the Object.Equals
+ // method.
+ //
+ // This method uses the Array.LastIndexOf method to perform the
+ // search.
+ //
+ public virtual int LastIndexOf(Object value, int startIndex, int count) {
+ if (Count != 0 && (startIndex < 0 || count < 0))
+ throw new ArgumentOutOfRangeException((startIndex<0 ? "startIndex" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ Contract.Ensures(Contract.Result<int>() < Count);
+ Contract.EndContractBlock();
+
+ if (_size == 0) // Special case for an empty list
+ return -1;
+
+ if (startIndex >= _size || count > startIndex + 1)
+ throw new ArgumentOutOfRangeException((startIndex>=_size ? "startIndex" : "count"), Environment.GetResourceString("ArgumentOutOfRange_BiggerThanCollection"));
+
+ return Array.LastIndexOf((Array)_items, value, startIndex, count);
+ }
+
+ // Returns a read-only IList wrapper for the given IList.
+ //
+#if FEATURE_CORECLR
+ [FriendAccessAllowed]
+#endif
+ public static IList ReadOnly(IList list) {
+ if (list==null)
+ throw new ArgumentNullException("list");
+ Contract.Ensures(Contract.Result<IList>() != null);
+ Contract.EndContractBlock();
+ return new ReadOnlyList(list);
+ }
+
+ // Returns a read-only ArrayList wrapper for the given ArrayList.
+ //
+ public static ArrayList ReadOnly(ArrayList list) {
+ if (list==null)
+ throw new ArgumentNullException("list");
+ Contract.Ensures(Contract.Result<ArrayList>() != null);
+ Contract.EndContractBlock();
+ return new ReadOnlyArrayList(list);
+ }
+
+ // Removes the element at the given index. The size of the list is
+ // decreased by one.
+ //
+ public virtual void Remove(Object obj) {
+ Contract.Ensures(Count >= 0);
+
+ int index = IndexOf(obj);
+ BCLDebug.Correctness(index >= 0 || !(obj is Int32), "You passed an Int32 to Remove that wasn't in the ArrayList." + Environment.NewLine + "Did you mean RemoveAt? int: "+obj+" Count: "+Count);
+ if (index >=0)
+ RemoveAt(index);
+ }
+
+ // Removes the element at the given index. The size of the list is
+ // decreased by one.
+ //
+ public virtual void RemoveAt(int index) {
+ if (index < 0 || index >= _size) throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ Contract.Ensures(Count >= 0);
+ //Contract.Ensures(Count == Contract.OldValue(Count) - 1);
+ Contract.EndContractBlock();
+
+ _size--;
+ if (index < _size) {
+ Array.Copy(_items, index + 1, _items, index, _size - index);
+ }
+ _items[_size] = null;
+ _version++;
+ }
+
+ // Removes a range of elements from this list.
+ //
+ public virtual void RemoveRange(int index, int count) {
+ if (index < 0)
+ throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (count < 0)
+ throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (_size - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ Contract.Ensures(Count >= 0);
+ //Contract.Ensures(Count == Contract.OldValue(Count) - count);
+ Contract.EndContractBlock();
+
+ if (count > 0) {
+ int i = _size;
+ _size -= count;
+ if (index < _size) {
+ Array.Copy(_items, index + count, _items, index, _size - index);
+ }
+ while (i > _size) _items[--i] = null;
+ _version++;
+ }
+ }
+
+ // Returns an IList that contains count copies of value.
+ //
+ public static ArrayList Repeat(Object value, int count) {
+ if (count < 0)
+ throw new ArgumentOutOfRangeException("count",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ Contract.Ensures(Contract.Result<ArrayList>() != null);
+ Contract.EndContractBlock();
+
+ ArrayList list = new ArrayList((count>_defaultCapacity)?count:_defaultCapacity);
+ for(int i=0; i<count; i++)
+ list.Add(value);
+ return list;
+ }
+
+ // Reverses the elements in this list.
+ public virtual void Reverse() {
+ Reverse(0, Count);
+ }
+
+ // Reverses the elements in a range of this list. Following a call to this
+ // method, an element in the range given by index and count
+ // which was previously located at index i will now be located at
+ // index index + (index + count - i - 1).
+ //
+ // This method uses the Array.Reverse method to reverse the
+ // elements.
+ //
+ public virtual void Reverse(int index, int count) {
+ if (index < 0)
+ throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (count < 0)
+ throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (_size - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ Contract.EndContractBlock();
+ Array.Reverse(_items, index, count);
+ _version++;
+ }
+
+ // Sets the elements starting at the given index to the elements of the
+ // given collection.
+ //
+ public virtual void SetRange(int index, ICollection c) {
+ if (c==null) throw new ArgumentNullException("c", Environment.GetResourceString("ArgumentNull_Collection"));
+ Contract.EndContractBlock();
+ int count = c.Count;
+ if (index < 0 || index > _size - count) throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+
+ if (count > 0) {
+ c.CopyTo(_items, index);
+ _version++;
+ }
+ }
+
+ public virtual ArrayList GetRange(int index, int count) {
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (_size - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ Contract.Ensures(Contract.Result<ArrayList>() != null);
+ Contract.EndContractBlock();
+ return new Range(this,index, count);
+ }
+
+ // Sorts the elements in this list. Uses the default comparer and
+ // Array.Sort.
+ public virtual void Sort()
+ {
+ Sort(0, Count, Comparer.Default);
+ }
+
+ // Sorts the elements in this list. Uses Array.Sort with the
+ // provided comparer.
+ public virtual void Sort(IComparer comparer)
+ {
+ Sort(0, Count, comparer);
+ }
+
+ // Sorts the elements in a section of this list. The sort compares the
+ // elements to each other using the given IComparer interface. If
+ // comparer is null, the elements are compared to each other using
+ // the IComparable interface, which in that case must be implemented by all
+ // elements of the list.
+ //
+ // This method uses the Array.Sort method to sort the elements.
+ //
+ public virtual void Sort(int index, int count, IComparer comparer) {
+ if (index < 0)
+ throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (count < 0)
+ throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (_size - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ Contract.EndContractBlock();
+
+ Array.Sort(_items, index, count, comparer);
+ _version++;
+ }
+
+ // Returns a thread-safe wrapper around an IList.
+ //
+ [HostProtection(Synchronization=true)]
+ public static IList Synchronized(IList list) {
+ if (list==null)
+ throw new ArgumentNullException("list");
+ Contract.Ensures(Contract.Result<IList>() != null);
+ Contract.EndContractBlock();
+ return new SyncIList(list);
+ }
+
+ // Returns a thread-safe wrapper around a ArrayList.
+ //
+ [HostProtection(Synchronization=true)]
+ public static ArrayList Synchronized(ArrayList list) {
+ if (list==null)
+ throw new ArgumentNullException("list");
+ Contract.Ensures(Contract.Result<ArrayList>() != null);
+ Contract.EndContractBlock();
+ return new SyncArrayList(list);
+ }
+
+ // ToArray returns a new Object array containing the contents of the ArrayList.
+ // This requires copying the ArrayList, which is an O(n) operation.
+ public virtual Object[] ToArray() {
+ Contract.Ensures(Contract.Result<Object[]>() != null);
+
+ Object[] array = new Object[_size];
+ Array.Copy(_items, 0, array, 0, _size);
+ return array;
+ }
+
+ // ToArray returns a new array of a particular type containing the contents
+ // of the ArrayList. This requires copying the ArrayList and potentially
+ // downcasting all elements. This copy may fail and is an O(n) operation.
+ // Internally, this implementation calls Array.Copy.
+ //
+ [SecuritySafeCritical]
+ public virtual Array ToArray(Type type) {
+ if (type==null)
+ throw new ArgumentNullException("type");
+ Contract.Ensures(Contract.Result<Array>() != null);
+ Contract.EndContractBlock();
+ Array array = Array.UnsafeCreateInstance(type, _size);
+ Array.Copy(_items, 0, array, 0, _size);
+ return array;
+ }
+
+ // Sets the capacity of this list to the size of the list. This method can
+ // be used to minimize a list's memory overhead once it is known that no
+ // new elements will be added to the list. To completely clear a list and
+ // release all memory referenced by the list, execute the following
+ // statements:
+ //
+ // list.Clear();
+ // list.TrimToSize();
+ //
+ public virtual void TrimToSize() {
+ Capacity = _size;
+ }
+
+
+ // This class wraps an IList, exposing it as a ArrayList
+ // Note this requires reimplementing half of ArrayList...
+ [Serializable]
+ private class IListWrapper : ArrayList
+ {
+ private IList _list;
+
+ internal IListWrapper(IList list) {
+ _list = list;
+ _version = 0; // list doesn't not contain a version number
+ }
+
+ public override int Capacity {
+ get { return _list.Count; }
+ set {
+ if (value < Count) throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
+ Contract.EndContractBlock();
+ }
+ }
+
+ public override int Count {
+ get { return _list.Count; }
+ }
+
+ public override bool IsReadOnly {
+ get { return _list.IsReadOnly; }
+ }
+
+ public override bool IsFixedSize {
+ get { return _list.IsFixedSize; }
+ }
+
+
+ public override bool IsSynchronized {
+ get { return _list.IsSynchronized; }
+ }
+
+ public override Object this[int index] {
+ get {
+ return _list[index];
+ }
+ set {
+ _list[index] = value;
+ _version++;
+ }
+ }
+
+ public override Object SyncRoot {
+ get { return _list.SyncRoot; }
+ }
+
+ public override int Add(Object obj) {
+ int i = _list.Add(obj);
+ _version++;
+ return i;
+ }
+
+ public override void AddRange(ICollection c) {
+ InsertRange(Count, c);
+ }
+
+ // Other overloads with automatically work
+ public override int BinarySearch(int index, int count, Object value, IComparer comparer)
+ {
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (this.Count - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ Contract.EndContractBlock();
+ if (comparer == null)
+ comparer = Comparer.Default;
+
+ int lo = index;
+ int hi = index + count - 1;
+ int mid;
+ while (lo <= hi) {
+ mid = (lo+hi)/2;
+ int r = comparer.Compare(value, _list[mid]);
+ if (r == 0)
+ return mid;
+ if (r < 0)
+ hi = mid-1;
+ else
+ lo = mid+1;
+ }
+ // return bitwise complement of the first element greater than value.
+ // Since hi is less than lo now, ~lo is the correct item.
+ return ~lo;
+ }
+
+ public override void Clear() {
+ // If _list is an array, it will support Clear method.
+ // We shouldn't allow clear operation on a FixedSized ArrayList
+ if(_list.IsFixedSize) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection"));
+ }
+
+ _list.Clear();
+ _version++;
+ }
+
+ public override Object Clone() {
+ // This does not do a shallow copy of _list into a ArrayList!
+ // This clones the IListWrapper, creating another wrapper class!
+ return new IListWrapper(_list);
+ }
+
+ public override bool Contains(Object obj) {
+ return _list.Contains(obj);
+ }
+
+ public override void CopyTo(Array array, int index) {
+ _list.CopyTo(array, index);
+ }
+
+ public override void CopyTo(int index, Array array, int arrayIndex, int count) {
+ if (array==null)
+ throw new ArgumentNullException("array");
+ if (index < 0 || arrayIndex < 0)
+ throw new ArgumentOutOfRangeException((index < 0) ? "index" : "arrayIndex", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if( count < 0)
+ throw new ArgumentOutOfRangeException( "count" , Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (array.Length - arrayIndex < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ if (array.Rank != 1)
+ throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
+ Contract.EndContractBlock();
+
+ if (_list.Count - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+
+ for(int i=index; i<index+count; i++)
+ array.SetValue(_list[i], arrayIndex++);
+ }
+
+ public override IEnumerator GetEnumerator() {
+ return _list.GetEnumerator();
+ }
+
+ public override IEnumerator GetEnumerator(int index, int count) {
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ Contract.EndContractBlock();
+ if (_list.Count - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+
+ return new IListWrapperEnumWrapper(this, index, count);
+ }
+
+ public override int IndexOf(Object value) {
+ return _list.IndexOf(value);
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int IndexOf(Object value, int startIndex) {
+ return IndexOf(value, startIndex, _list.Count - startIndex);
+ }
+
+ public override int IndexOf(Object value, int startIndex, int count) {
+ if (startIndex < 0 || startIndex > this.Count) throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ if (count < 0 || startIndex > this.Count - count) throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Count"));
+ Contract.EndContractBlock();
+
+ int endIndex = startIndex + count;
+ if (value == null) {
+ for(int i=startIndex; i<endIndex; i++)
+ if (_list[i] == null)
+ return i;
+ return -1;
+ } else {
+ for(int i=startIndex; i<endIndex; i++)
+ if (_list[i] != null && _list[i].Equals(value))
+ return i;
+ return -1;
+ }
+ }
+
+ public override void Insert(int index, Object obj) {
+ _list.Insert(index, obj);
+ _version++;
+ }
+
+ public override void InsertRange(int index, ICollection c) {
+ if (c==null)
+ throw new ArgumentNullException("c", Environment.GetResourceString("ArgumentNull_Collection"));
+ if (index < 0 || index > this.Count) throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ Contract.EndContractBlock();
+
+ if( c.Count > 0) {
+ ArrayList al = _list as ArrayList;
+ if( al != null) {
+ // We need to special case ArrayList.
+ // When c is a range of _list, we need to handle this in a special way.
+ // See ArrayList.InsertRange for details.
+ al.InsertRange(index, c);
+ }
+ else {
+ IEnumerator en = c.GetEnumerator();
+ while(en.MoveNext()) {
+ _list.Insert(index++, en.Current);
+ }
+ }
+ _version++;
+ }
+ }
+
+ public override int LastIndexOf(Object value) {
+ return LastIndexOf(value,_list.Count - 1, _list.Count);
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int LastIndexOf(Object value, int startIndex) {
+ return LastIndexOf(value, startIndex, startIndex + 1);
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int LastIndexOf(Object value, int startIndex, int count) {
+ if (_list.Count == 0)
+ return -1;
+
+ if (startIndex < 0 || startIndex >= _list.Count) throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ if (count < 0 || count > startIndex + 1) throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Count"));
+
+ int endIndex = startIndex - count + 1;
+ if (value == null) {
+ for(int i=startIndex; i >= endIndex; i--)
+ if (_list[i] == null)
+ return i;
+ return -1;
+ } else {
+ for(int i=startIndex; i >= endIndex; i--)
+ if (_list[i] != null && _list[i].Equals(value))
+ return i;
+ return -1;
+ }
+ }
+
+ public override void Remove(Object value) {
+ int index = IndexOf(value);
+ if (index >=0)
+ RemoveAt(index);
+ }
+
+ public override void RemoveAt(int index) {
+ _list.RemoveAt(index);
+ _version++;
+ }
+
+ public override void RemoveRange(int index, int count) {
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ Contract.EndContractBlock();
+ if (_list.Count - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+
+ if( count > 0) // be consistent with ArrayList
+ _version++;
+
+ while(count > 0) {
+ _list.RemoveAt(index);
+ count--;
+ }
+ }
+
+ public override void Reverse(int index, int count) {
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ Contract.EndContractBlock();
+ if (_list.Count - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+
+ int i = index;
+ int j = index + count - 1;
+ while (i < j)
+ {
+ Object tmp = _list[i];
+ _list[i++] = _list[j];
+ _list[j--] = tmp;
+ }
+ _version++;
+ }
+
+ public override void SetRange(int index, ICollection c) {
+ if (c==null) {
+ throw new ArgumentNullException("c", Environment.GetResourceString("ArgumentNull_Collection"));
+ }
+ Contract.EndContractBlock();
+
+ if (index < 0 || index > _list.Count - c.Count) {
+ throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ }
+
+ if( c.Count > 0) {
+ IEnumerator en = c.GetEnumerator();
+ while(en.MoveNext()) {
+ _list[index++] = en.Current;
+ }
+ _version++;
+ }
+ }
+
+ public override ArrayList GetRange(int index, int count) {
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ Contract.EndContractBlock();
+ if (_list.Count - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ return new Range(this,index, count);
+ }
+
+ public override void Sort(int index, int count, IComparer comparer) {
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ Contract.EndContractBlock();
+ if (_list.Count - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+
+ Object [] array = new Object[count];
+ CopyTo(index, array, 0, count);
+ Array.Sort(array, 0, count, comparer);
+ for(int i=0; i<count; i++)
+ _list[i+index] = array[i];
+
+ _version++;
+ }
+
+
+ public override Object[] ToArray() {
+ Object[] array = new Object[Count];
+ _list.CopyTo(array, 0);
+ return array;
+ }
+
+ [SecuritySafeCritical]
+ public override Array ToArray(Type type)
+ {
+ if (type==null)
+ throw new ArgumentNullException("type");
+ Contract.EndContractBlock();
+ Array array = Array.UnsafeCreateInstance(type, _list.Count);
+ _list.CopyTo(array, 0);
+ return array;
+ }
+
+ public override void TrimToSize()
+ {
+ // Can't really do much here...
+ }
+
+ // This is the enumerator for an IList that's been wrapped in another
+ // class that implements all of ArrayList's methods.
+ [Serializable]
+ private sealed class IListWrapperEnumWrapper : IEnumerator, ICloneable
+ {
+ private IEnumerator _en;
+ private int _remaining;
+ private int _initialStartIndex; // for reset
+ private int _initialCount; // for reset
+ private bool _firstCall; // firstCall to MoveNext
+
+ private IListWrapperEnumWrapper()
+ {
+ }
+
+ internal IListWrapperEnumWrapper(IListWrapper listWrapper, int startIndex, int count)
+ {
+ _en = listWrapper.GetEnumerator();
+ _initialStartIndex = startIndex;
+ _initialCount = count;
+ while(startIndex-- > 0 && _en.MoveNext());
+ _remaining = count;
+ _firstCall = true;
+ }
+
+ public Object Clone() {
+ // We must clone the underlying enumerator, I think.
+ IListWrapperEnumWrapper clone = new IListWrapperEnumWrapper();
+ clone._en = (IEnumerator) ((ICloneable)_en).Clone();
+ clone._initialStartIndex = _initialStartIndex;
+ clone._initialCount = _initialCount;
+ clone._remaining = _remaining;
+ clone._firstCall = _firstCall;
+ return clone;
+ }
+
+ public bool MoveNext() {
+ if (_firstCall) {
+ _firstCall = false;
+ return _remaining-- > 0 && _en.MoveNext();
+ }
+ if (_remaining < 0)
+ return false;
+ bool r = _en.MoveNext();
+ return r && _remaining-- > 0;
+ }
+
+ public Object Current {
+ get {
+ if (_firstCall)
+ throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
+ if (_remaining < 0)
+ throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded));
+ return _en.Current;
+ }
+ }
+
+ public void Reset() {
+ _en.Reset();
+ int startIndex = _initialStartIndex;
+ while(startIndex-- > 0 && _en.MoveNext());
+ _remaining = _initialCount;
+ _firstCall = true;
+ }
+ }
+ }
+
+
+ [Serializable]
+ private class SyncArrayList : ArrayList
+ {
+ private ArrayList _list;
+ private Object _root;
+
+ internal SyncArrayList(ArrayList list)
+ : base( false )
+ {
+ _list = list;
+ _root = list.SyncRoot;
+ }
+
+ public override int Capacity {
+ get {
+ lock(_root) {
+ return _list.Capacity;
+ }
+ }
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ set {
+ lock(_root) {
+ _list.Capacity = value;
+ }
+ }
+ }
+
+ public override int Count {
+ get { lock(_root) { return _list.Count; } }
+ }
+
+ public override bool IsReadOnly {
+ get { return _list.IsReadOnly; }
+ }
+
+ public override bool IsFixedSize {
+ get { return _list.IsFixedSize; }
+ }
+
+
+ public override bool IsSynchronized {
+ get { return true; }
+ }
+
+ public override Object this[int index] {
+ get {
+ lock(_root) {
+ return _list[index];
+ }
+ }
+ set {
+ lock(_root) {
+ _list[index] = value;
+ }
+ }
+ }
+
+ public override Object SyncRoot {
+ get { return _root; }
+ }
+
+ public override int Add(Object value) {
+ lock(_root) {
+ return _list.Add(value);
+ }
+ }
+
+ public override void AddRange(ICollection c) {
+ lock(_root) {
+ _list.AddRange(c);
+ }
+ }
+
+ public override int BinarySearch(Object value) {
+ lock(_root) {
+ return _list.BinarySearch(value);
+ }
+ }
+
+ public override int BinarySearch(Object value, IComparer comparer) {
+ lock(_root) {
+ return _list.BinarySearch(value, comparer);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int BinarySearch(int index, int count, Object value, IComparer comparer) {
+ lock(_root) {
+ return _list.BinarySearch(index, count, value, comparer);
+ }
+ }
+
+ public override void Clear() {
+ lock(_root) {
+ _list.Clear();
+ }
+ }
+
+ public override Object Clone() {
+ lock(_root) {
+ return new SyncArrayList((ArrayList)_list.Clone());
+ }
+ }
+
+ public override bool Contains(Object item) {
+ lock(_root) {
+ return _list.Contains(item);
+ }
+ }
+
+ public override void CopyTo(Array array) {
+ lock(_root) {
+ _list.CopyTo(array);
+ }
+ }
+
+ public override void CopyTo(Array array, int index) {
+ lock(_root) {
+ _list.CopyTo(array, index);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override void CopyTo(int index, Array array, int arrayIndex, int count) {
+ lock(_root) {
+ _list.CopyTo(index, array, arrayIndex, count);
+ }
+ }
+
+ public override IEnumerator GetEnumerator() {
+ lock(_root) {
+ return _list.GetEnumerator();
+ }
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override IEnumerator GetEnumerator(int index, int count) {
+ lock(_root) {
+ return _list.GetEnumerator(index, count);
+ }
+ }
+
+ public override int IndexOf(Object value) {
+ lock(_root) {
+ return _list.IndexOf(value);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int IndexOf(Object value, int startIndex) {
+ lock(_root) {
+ return _list.IndexOf(value, startIndex);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int IndexOf(Object value, int startIndex, int count) {
+ lock(_root) {
+ return _list.IndexOf(value, startIndex, count);
+ }
+ }
+
+ public override void Insert(int index, Object value) {
+ lock(_root) {
+ _list.Insert(index, value);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override void InsertRange(int index, ICollection c) {
+ lock(_root) {
+ _list.InsertRange(index, c);
+ }
+ }
+
+ public override int LastIndexOf(Object value) {
+ lock(_root) {
+ return _list.LastIndexOf(value);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int LastIndexOf(Object value, int startIndex) {
+ lock(_root) {
+ return _list.LastIndexOf(value, startIndex);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int LastIndexOf(Object value, int startIndex, int count) {
+ lock(_root) {
+ return _list.LastIndexOf(value, startIndex, count);
+ }
+ }
+
+ public override void Remove(Object value) {
+ lock(_root) {
+ _list.Remove(value);
+ }
+ }
+
+ public override void RemoveAt(int index) {
+ lock(_root) {
+ _list.RemoveAt(index);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override void RemoveRange(int index, int count) {
+ lock(_root) {
+ _list.RemoveRange(index, count);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override void Reverse(int index, int count) {
+ lock(_root) {
+ _list.Reverse(index, count);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override void SetRange(int index, ICollection c) {
+ lock(_root) {
+ _list.SetRange(index, c);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override ArrayList GetRange(int index, int count) {
+ lock(_root) {
+ return _list.GetRange(index, count);
+ }
+ }
+
+ public override void Sort() {
+ lock(_root) {
+ _list.Sort();
+ }
+ }
+
+ public override void Sort(IComparer comparer) {
+ lock(_root) {
+ _list.Sort(comparer);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override void Sort(int index, int count, IComparer comparer) {
+ lock(_root) {
+ _list.Sort(index, count, comparer);
+ }
+ }
+
+ public override Object[] ToArray() {
+ lock(_root) {
+ return _list.ToArray();
+ }
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override Array ToArray(Type type) {
+ lock(_root) {
+ return _list.ToArray(type);
+ }
+ }
+
+ public override void TrimToSize() {
+ lock(_root) {
+ _list.TrimToSize();
+ }
+ }
+ }
+
+
+ [Serializable]
+ private class SyncIList : IList
+ {
+ private IList _list;
+ private Object _root;
+
+ internal SyncIList(IList list) {
+ _list = list;
+ _root = list.SyncRoot;
+ }
+
+ public virtual int Count {
+ get { lock(_root) { return _list.Count; } }
+ }
+
+ public virtual bool IsReadOnly {
+ get { return _list.IsReadOnly; }
+ }
+
+ public virtual bool IsFixedSize {
+ get { return _list.IsFixedSize; }
+ }
+
+
+ public virtual bool IsSynchronized {
+ get { return true; }
+ }
+
+ public virtual Object this[int index] {
+ get {
+ lock(_root) {
+ return _list[index];
+ }
+ }
+ set {
+ lock(_root) {
+ _list[index] = value;
+ }
+ }
+ }
+
+ public virtual Object SyncRoot {
+ get { return _root; }
+ }
+
+ public virtual int Add(Object value) {
+ lock(_root) {
+ return _list.Add(value);
+ }
+ }
+
+
+ public virtual void Clear() {
+ lock(_root) {
+ _list.Clear();
+ }
+ }
+
+ public virtual bool Contains(Object item) {
+ lock(_root) {
+ return _list.Contains(item);
+ }
+ }
+
+ public virtual void CopyTo(Array array, int index) {
+ lock(_root) {
+ _list.CopyTo(array, index);
+ }
+ }
+
+ public virtual IEnumerator GetEnumerator() {
+ lock(_root) {
+ return _list.GetEnumerator();
+ }
+ }
+
+ public virtual int IndexOf(Object value) {
+ lock(_root) {
+ return _list.IndexOf(value);
+ }
+ }
+
+ public virtual void Insert(int index, Object value) {
+ lock(_root) {
+ _list.Insert(index, value);
+ }
+ }
+
+ public virtual void Remove(Object value) {
+ lock(_root) {
+ _list.Remove(value);
+ }
+ }
+
+ public virtual void RemoveAt(int index) {
+ lock(_root) {
+ _list.RemoveAt(index);
+ }
+ }
+ }
+
+ [Serializable]
+ private class FixedSizeList : IList
+ {
+ private IList _list;
+
+ internal FixedSizeList(IList l) {
+ _list = l;
+ }
+
+ public virtual int Count {
+ get { return _list.Count; }
+ }
+
+ public virtual bool IsReadOnly {
+ get { return _list.IsReadOnly; }
+ }
+
+ public virtual bool IsFixedSize {
+ get { return true; }
+ }
+
+ public virtual bool IsSynchronized {
+ get { return _list.IsSynchronized; }
+ }
+
+ public virtual Object this[int index] {
+ get {
+ return _list[index];
+ }
+ set {
+ _list[index] = value;
+ }
+ }
+
+ public virtual Object SyncRoot {
+ get { return _list.SyncRoot; }
+ }
+
+ public virtual int Add(Object obj) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection"));
+ }
+
+ public virtual void Clear() {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection"));
+ }
+
+ public virtual bool Contains(Object obj) {
+ return _list.Contains(obj);
+ }
+
+ public virtual void CopyTo(Array array, int index) {
+ _list.CopyTo(array, index);
+ }
+
+ public virtual IEnumerator GetEnumerator() {
+ return _list.GetEnumerator();
+ }
+
+ public virtual int IndexOf(Object value) {
+ return _list.IndexOf(value);
+ }
+
+ public virtual void Insert(int index, Object obj) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection"));
+ }
+
+ public virtual void Remove(Object value) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection"));
+ }
+
+ public virtual void RemoveAt(int index) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection"));
+ }
+ }
+
+ [Serializable]
+ private class FixedSizeArrayList : ArrayList
+ {
+ private ArrayList _list;
+
+ internal FixedSizeArrayList(ArrayList l) {
+ _list = l;
+ _version = _list._version;
+ }
+
+ public override int Count {
+ get { return _list.Count; }
+ }
+
+ public override bool IsReadOnly {
+ get { return _list.IsReadOnly; }
+ }
+
+ public override bool IsFixedSize {
+ get { return true; }
+ }
+
+ public override bool IsSynchronized {
+ get { return _list.IsSynchronized; }
+ }
+
+ public override Object this[int index] {
+ get {
+ return _list[index];
+ }
+ set {
+ _list[index] = value;
+ _version = _list._version;
+ }
+ }
+
+ public override Object SyncRoot {
+ get { return _list.SyncRoot; }
+ }
+
+ public override int Add(Object obj) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection"));
+ }
+
+ public override void AddRange(ICollection c) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection"));
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int BinarySearch(int index, int count, Object value, IComparer comparer) {
+ return _list.BinarySearch(index, count, value, comparer);
+ }
+
+ public override int Capacity {
+ get { return _list.Capacity; }
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ set { throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection")); }
+ }
+
+ public override void Clear() {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection"));
+ }
+
+ public override Object Clone() {
+ FixedSizeArrayList arrayList = new FixedSizeArrayList(_list);
+ arrayList._list = (ArrayList)_list.Clone();
+ return arrayList;
+ }
+
+ public override bool Contains(Object obj) {
+ return _list.Contains(obj);
+ }
+
+ public override void CopyTo(Array array, int index) {
+ _list.CopyTo(array, index);
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override void CopyTo(int index, Array array, int arrayIndex, int count) {
+ _list.CopyTo(index, array, arrayIndex, count);
+ }
+
+ public override IEnumerator GetEnumerator() {
+ return _list.GetEnumerator();
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override IEnumerator GetEnumerator(int index, int count) {
+ return _list.GetEnumerator(index, count);
+ }
+
+ public override int IndexOf(Object value) {
+ return _list.IndexOf(value);
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int IndexOf(Object value, int startIndex) {
+ return _list.IndexOf(value, startIndex);
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int IndexOf(Object value, int startIndex, int count) {
+ return _list.IndexOf(value, startIndex, count);
+ }
+
+ public override void Insert(int index, Object obj) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection"));
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override void InsertRange(int index, ICollection c) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection"));
+ }
+
+ public override int LastIndexOf(Object value) {
+ return _list.LastIndexOf(value);
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int LastIndexOf(Object value, int startIndex) {
+ return _list.LastIndexOf(value, startIndex);
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int LastIndexOf(Object value, int startIndex, int count) {
+ return _list.LastIndexOf(value, startIndex, count);
+ }
+
+ public override void Remove(Object value) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection"));
+ }
+
+ public override void RemoveAt(int index) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection"));
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override void RemoveRange(int index, int count) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection"));
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override void SetRange(int index, ICollection c) {
+ _list.SetRange(index, c);
+ _version = _list._version;
+ }
+
+ public override ArrayList GetRange(int index, int count) {
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (Count - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ Contract.EndContractBlock();
+
+ return new Range(this,index, count);
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override void Reverse(int index, int count) {
+ _list.Reverse(index, count);
+ _version = _list._version;
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override void Sort(int index, int count, IComparer comparer) {
+ _list.Sort(index, count, comparer);
+ _version = _list._version;
+ }
+
+ public override Object[] ToArray() {
+ return _list.ToArray();
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override Array ToArray(Type type) {
+ return _list.ToArray(type);
+ }
+
+ public override void TrimToSize() {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection"));
+ }
+ }
+
+ [Serializable]
+ private class ReadOnlyList : IList
+ {
+ private IList _list;
+
+ internal ReadOnlyList(IList l) {
+ _list = l;
+ }
+
+ public virtual int Count {
+ get { return _list.Count; }
+ }
+
+ public virtual bool IsReadOnly {
+ get { return true; }
+ }
+
+ public virtual bool IsFixedSize {
+ get { return true; }
+ }
+
+ public virtual bool IsSynchronized {
+ get { return _list.IsSynchronized; }
+ }
+
+ public virtual Object this[int index] {
+ get {
+ return _list[index];
+ }
+ set {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ReadOnlyCollection"));
+ }
+ }
+
+ public virtual Object SyncRoot {
+ get { return _list.SyncRoot; }
+ }
+
+ public virtual int Add(Object obj) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ReadOnlyCollection"));
+ }
+
+ public virtual void Clear() {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ReadOnlyCollection"));
+ }
+
+ public virtual bool Contains(Object obj) {
+ return _list.Contains(obj);
+ }
+
+ public virtual void CopyTo(Array array, int index) {
+ _list.CopyTo(array, index);
+ }
+
+ public virtual IEnumerator GetEnumerator() {
+ return _list.GetEnumerator();
+ }
+
+ public virtual int IndexOf(Object value) {
+ return _list.IndexOf(value);
+ }
+
+ public virtual void Insert(int index, Object obj) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ReadOnlyCollection"));
+ }
+
+ public virtual void Remove(Object value) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ReadOnlyCollection"));
+ }
+
+ public virtual void RemoveAt(int index) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ReadOnlyCollection"));
+ }
+ }
+
+ [Serializable]
+ private class ReadOnlyArrayList : ArrayList
+ {
+ private ArrayList _list;
+
+ internal ReadOnlyArrayList(ArrayList l) {
+ _list = l;
+ }
+
+ public override int Count {
+ get { return _list.Count; }
+ }
+
+ public override bool IsReadOnly {
+ get { return true; }
+ }
+
+ public override bool IsFixedSize {
+ get { return true; }
+ }
+
+ public override bool IsSynchronized {
+ get { return _list.IsSynchronized; }
+ }
+
+ public override Object this[int index] {
+ get {
+ return _list[index];
+ }
+ set {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ReadOnlyCollection"));
+ }
+ }
+
+ public override Object SyncRoot {
+ get { return _list.SyncRoot; }
+ }
+
+ public override int Add(Object obj) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ReadOnlyCollection"));
+ }
+
+ public override void AddRange(ICollection c) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ReadOnlyCollection"));
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int BinarySearch(int index, int count, Object value, IComparer comparer) {
+ return _list.BinarySearch(index, count, value, comparer);
+ }
+
+
+ public override int Capacity {
+ get { return _list.Capacity; }
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ set { throw new NotSupportedException(Environment.GetResourceString("NotSupported_ReadOnlyCollection")); }
+ }
+
+ public override void Clear() {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ReadOnlyCollection"));
+ }
+
+ public override Object Clone() {
+ ReadOnlyArrayList arrayList = new ReadOnlyArrayList(_list);
+ arrayList._list = (ArrayList)_list.Clone();
+ return arrayList;
+ }
+
+ public override bool Contains(Object obj) {
+ return _list.Contains(obj);
+ }
+
+ public override void CopyTo(Array array, int index) {
+ _list.CopyTo(array, index);
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override void CopyTo(int index, Array array, int arrayIndex, int count) {
+ _list.CopyTo(index, array, arrayIndex, count);
+ }
+
+ public override IEnumerator GetEnumerator() {
+ return _list.GetEnumerator();
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override IEnumerator GetEnumerator(int index, int count) {
+ return _list.GetEnumerator(index, count);
+ }
+
+ public override int IndexOf(Object value) {
+ return _list.IndexOf(value);
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int IndexOf(Object value, int startIndex) {
+ return _list.IndexOf(value, startIndex);
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int IndexOf(Object value, int startIndex, int count) {
+ return _list.IndexOf(value, startIndex, count);
+ }
+
+ public override void Insert(int index, Object obj) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ReadOnlyCollection"));
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override void InsertRange(int index, ICollection c) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ReadOnlyCollection"));
+ }
+
+ public override int LastIndexOf(Object value) {
+ return _list.LastIndexOf(value);
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int LastIndexOf(Object value, int startIndex) {
+ return _list.LastIndexOf(value, startIndex);
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int LastIndexOf(Object value, int startIndex, int count) {
+ return _list.LastIndexOf(value, startIndex, count);
+ }
+
+ public override void Remove(Object value) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ReadOnlyCollection"));
+ }
+
+ public override void RemoveAt(int index) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ReadOnlyCollection"));
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override void RemoveRange(int index, int count) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ReadOnlyCollection"));
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override void SetRange(int index, ICollection c) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ReadOnlyCollection"));
+ }
+
+ public override ArrayList GetRange(int index, int count) {
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (Count - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ Contract.EndContractBlock();
+
+ return new Range(this,index, count);
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override void Reverse(int index, int count) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ReadOnlyCollection"));
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override void Sort(int index, int count, IComparer comparer) {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ReadOnlyCollection"));
+ }
+
+ public override Object[] ToArray() {
+ return _list.ToArray();
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override Array ToArray(Type type) {
+ return _list.ToArray(type);
+ }
+
+ public override void TrimToSize() {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ReadOnlyCollection"));
+ }
+ }
+
+
+ // Implements an enumerator for a ArrayList. The enumerator uses the
+ // internal version number of the list to ensure that no modifications are
+ // made to the list while an enumeration is in progress.
+ [Serializable]
+ private sealed class ArrayListEnumerator : IEnumerator, ICloneable
+ {
+ private ArrayList list;
+ private int index;
+ private int endIndex; // Where to stop.
+ private int version;
+ private Object currentElement;
+ private int startIndex; // Save this for Reset.
+
+ internal ArrayListEnumerator(ArrayList list, int index, int count) {
+ this.list = list;
+ startIndex = index;
+ this.index = index - 1;
+ endIndex = this.index + count; // last valid index
+ version = list._version;
+ currentElement = null;
+ }
+
+ public Object Clone() {
+ return MemberwiseClone();
+ }
+
+ public bool MoveNext() {
+ if (version != list._version) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
+ if (index < endIndex) {
+ currentElement = list[++index];
+ return true;
+ }
+ else {
+ index = endIndex + 1;
+ }
+
+ return false;
+ }
+
+ public Object Current {
+ get {
+ if (index < startIndex)
+ throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
+ else if (index > endIndex) {
+ throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded));
+ }
+ return currentElement;
+ }
+ }
+
+ public void Reset() {
+ if (version != list._version) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
+ index = startIndex - 1;
+ }
+ }
+
+ // Implementation of a generic list subrange. An instance of this class
+ // is returned by the default implementation of List.GetRange.
+ [Serializable]
+ private class Range: ArrayList
+ {
+ private ArrayList _baseList;
+ private int _baseIndex;
+ [ContractPublicPropertyName("Count")]
+ private int _baseSize;
+ private int _baseVersion;
+
+ internal Range(ArrayList list, int index, int count) : base(false) {
+ _baseList = list;
+ _baseIndex = index;
+ _baseSize = count;
+ _baseVersion = list._version;
+ // we also need to update _version field to make Range of Range work
+ _version = list._version;
+ }
+
+ private void InternalUpdateRange()
+ {
+ if (_baseVersion != _baseList._version)
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_UnderlyingArrayListChanged"));
+ }
+
+ private void InternalUpdateVersion() {
+ _baseVersion++;
+ _version++;
+ }
+
+ public override int Add(Object value) {
+ InternalUpdateRange();
+ _baseList.Insert(_baseIndex + _baseSize, value);
+ InternalUpdateVersion();
+ return _baseSize++;
+ }
+
+ public override void AddRange(ICollection c) {
+ if( c == null ) {
+ throw new ArgumentNullException("c");
+ }
+ Contract.EndContractBlock();
+
+ InternalUpdateRange();
+ int count = c.Count;
+ if( count > 0) {
+ _baseList.InsertRange(_baseIndex + _baseSize, c);
+ InternalUpdateVersion();
+ _baseSize += count;
+ }
+ }
+
+ // Other overloads with automatically work
+ public override int BinarySearch(int index, int count, Object value, IComparer comparer) {
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (_baseSize - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ Contract.EndContractBlock();
+ InternalUpdateRange();
+
+ int i = _baseList.BinarySearch(_baseIndex + index, count, value, comparer);
+ if (i >= 0) return i - _baseIndex;
+ return i + _baseIndex;
+ }
+
+ public override int Capacity {
+ get {
+ return _baseList.Capacity;
+ }
+
+ set {
+ if (value < Count) throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
+ Contract.EndContractBlock();
+ }
+ }
+
+
+ public override void Clear() {
+ InternalUpdateRange();
+ if (_baseSize != 0)
+ {
+ _baseList.RemoveRange(_baseIndex, _baseSize);
+ InternalUpdateVersion();
+ _baseSize = 0;
+ }
+ }
+
+ public override Object Clone() {
+ InternalUpdateRange();
+ Range arrayList = new Range(_baseList,_baseIndex,_baseSize);
+ arrayList._baseList = (ArrayList)_baseList.Clone();
+ return arrayList;
+ }
+
+ public override bool Contains(Object item) {
+ InternalUpdateRange();
+ if (item==null) {
+ for(int i=0; i<_baseSize; i++)
+ if (_baseList[_baseIndex + i]==null)
+ return true;
+ return false;
+ }
+ else {
+ for(int i=0; i<_baseSize; i++)
+ if (_baseList[_baseIndex + i] != null && _baseList[_baseIndex + i].Equals(item))
+ return true;
+ return false;
+ }
+ }
+
+ public override void CopyTo(Array array, int index) {
+ if (array==null)
+ throw new ArgumentNullException("array");
+ if (array.Rank != 1)
+ throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
+ if (index < 0)
+ throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (array.Length - index < _baseSize)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ Contract.EndContractBlock();
+
+ InternalUpdateRange();
+ _baseList.CopyTo(_baseIndex, array, index, _baseSize);
+ }
+
+ public override void CopyTo(int index, Array array, int arrayIndex, int count) {
+ if (array==null)
+ throw new ArgumentNullException("array");
+ if (array.Rank != 1)
+ throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (array.Length - arrayIndex < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ if (_baseSize - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ Contract.EndContractBlock();
+
+ InternalUpdateRange();
+ _baseList.CopyTo(_baseIndex + index, array, arrayIndex, count);
+ }
+
+ public override int Count {
+ get {
+ InternalUpdateRange();
+ return _baseSize;
+ }
+ }
+
+ public override bool IsReadOnly {
+ get { return _baseList.IsReadOnly; }
+ }
+
+ public override bool IsFixedSize {
+ get { return _baseList.IsFixedSize; }
+ }
+
+ public override bool IsSynchronized {
+ get { return _baseList.IsSynchronized; }
+ }
+
+ public override IEnumerator GetEnumerator() {
+ return GetEnumerator(0,_baseSize);
+ }
+
+ public override IEnumerator GetEnumerator(int index, int count) {
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (_baseSize - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ Contract.EndContractBlock();
+
+ InternalUpdateRange();
+ return _baseList.GetEnumerator(_baseIndex + index, count);
+ }
+
+ public override ArrayList GetRange(int index, int count) {
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (_baseSize - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ Contract.EndContractBlock();
+
+ InternalUpdateRange();
+ return new Range(this, index, count);
+ }
+
+ public override Object SyncRoot {
+ get {
+ return _baseList.SyncRoot;
+ }
+ }
+
+
+ public override int IndexOf(Object value) {
+ InternalUpdateRange();
+ int i = _baseList.IndexOf(value, _baseIndex, _baseSize);
+ if (i >= 0) return i - _baseIndex;
+ return -1;
+ }
+
+ public override int IndexOf(Object value, int startIndex) {
+ if (startIndex < 0)
+ throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (startIndex > _baseSize)
+ throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ Contract.EndContractBlock();
+
+ InternalUpdateRange();
+ int i = _baseList.IndexOf(value, _baseIndex + startIndex, _baseSize - startIndex);
+ if (i >= 0) return i - _baseIndex;
+ return -1;
+ }
+
+ public override int IndexOf(Object value, int startIndex, int count) {
+ if (startIndex < 0 || startIndex > _baseSize)
+ throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+
+ if (count < 0 || (startIndex > _baseSize - count))
+ throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Count"));
+ Contract.EndContractBlock();
+
+ InternalUpdateRange();
+ int i = _baseList.IndexOf(value, _baseIndex + startIndex, count);
+ if (i >= 0) return i - _baseIndex;
+ return -1;
+ }
+
+ public override void Insert(int index, Object value) {
+ if (index < 0 || index > _baseSize) throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ Contract.EndContractBlock();
+
+ InternalUpdateRange();
+ _baseList.Insert(_baseIndex + index, value);
+ InternalUpdateVersion();
+ _baseSize++;
+ }
+
+ public override void InsertRange(int index, ICollection c) {
+ if (index < 0 || index > _baseSize) throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ if( c == null) {
+ throw new ArgumentNullException("c");
+ }
+ Contract.EndContractBlock();
+
+ InternalUpdateRange();
+ int count = c.Count;
+ if( count > 0) {
+ _baseList.InsertRange(_baseIndex + index, c);
+ _baseSize += count;
+ InternalUpdateVersion();
+ }
+ }
+
+ public override int LastIndexOf(Object value) {
+ InternalUpdateRange();
+ int i = _baseList.LastIndexOf(value, _baseIndex + _baseSize - 1, _baseSize);
+ if (i >= 0) return i - _baseIndex;
+ return -1;
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int LastIndexOf(Object value, int startIndex) {
+ return LastIndexOf(value, startIndex, startIndex + 1);
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int LastIndexOf(Object value, int startIndex, int count) {
+ InternalUpdateRange();
+ if (_baseSize == 0)
+ return -1;
+
+ if (startIndex >= _baseSize)
+ throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ if (startIndex < 0)
+ throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+
+ int i = _baseList.LastIndexOf(value, _baseIndex + startIndex, count);
+ if (i >= 0) return i - _baseIndex;
+ return -1;
+ }
+
+ // Don't need to override Remove
+
+ public override void RemoveAt(int index) {
+ if (index < 0 || index >= _baseSize) throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ Contract.EndContractBlock();
+
+ InternalUpdateRange();
+ _baseList.RemoveAt(_baseIndex + index);
+ InternalUpdateVersion();
+ _baseSize--;
+ }
+
+ public override void RemoveRange(int index, int count) {
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (_baseSize - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ Contract.EndContractBlock();
+
+ InternalUpdateRange();
+ // No need to call _bastList.RemoveRange if count is 0.
+ // In addition, _baseList won't change the vresion number if count is 0.
+ if( count > 0) {
+ _baseList.RemoveRange(_baseIndex + index, count);
+ InternalUpdateVersion();
+ _baseSize -= count;
+ }
+ }
+
+ public override void Reverse(int index, int count) {
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (_baseSize - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ Contract.EndContractBlock();
+
+ InternalUpdateRange();
+ _baseList.Reverse(_baseIndex + index, count);
+ InternalUpdateVersion();
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override void SetRange(int index, ICollection c) {
+ InternalUpdateRange();
+ if (index < 0 || index >= _baseSize) throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ _baseList.SetRange(_baseIndex + index, c);
+ if( c.Count > 0) {
+ InternalUpdateVersion();
+ }
+ }
+
+ public override void Sort(int index, int count, IComparer comparer) {
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (_baseSize - index < count)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ Contract.EndContractBlock();
+
+ InternalUpdateRange();
+ _baseList.Sort(_baseIndex + index, count, comparer);
+ InternalUpdateVersion();
+ }
+
+ public override Object this[int index] {
+ get {
+ InternalUpdateRange();
+ if (index < 0 || index >= _baseSize) throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ return _baseList[_baseIndex + index];
+ }
+ set {
+ InternalUpdateRange();
+ if (index < 0 || index >= _baseSize) throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ _baseList[_baseIndex + index] = value;
+ InternalUpdateVersion();
+ }
+ }
+
+ public override Object[] ToArray() {
+ InternalUpdateRange();
+ Object[] array = new Object[_baseSize];
+ Array.Copy(_baseList._items, _baseIndex, array, 0, _baseSize);
+ return array;
+ }
+
+ [SecuritySafeCritical]
+ public override Array ToArray(Type type) {
+ if (type==null)
+ throw new ArgumentNullException("type");
+ Contract.EndContractBlock();
+
+ InternalUpdateRange();
+ Array array = Array.UnsafeCreateInstance(type, _baseSize);
+ _baseList.CopyTo(_baseIndex, array, 0, _baseSize);
+ return array;
+ }
+
+ public override void TrimToSize() {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_RangeCollection"));
+ }
+ }
+
+ [Serializable]
+ private sealed class ArrayListEnumeratorSimple : IEnumerator, ICloneable {
+ private ArrayList list;
+ private int index;
+ private int version;
+ private Object currentElement;
+ [NonSerialized]
+ private bool isArrayList;
+ // this object is used to indicate enumeration has not started or has terminated
+ static Object dummyObject = new Object();
+
+ internal ArrayListEnumeratorSimple(ArrayList list) {
+ this.list = list;
+ this.index = -1;
+ version = list._version;
+ isArrayList = (list.GetType() == typeof(ArrayList));
+ currentElement = dummyObject;
+ }
+
+ public Object Clone() {
+ return MemberwiseClone();
+ }
+
+ public bool MoveNext() {
+ if (version != list._version) {
+ throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
+ }
+
+ if( isArrayList) { // avoid calling virtual methods if we are operating on ArrayList to improve performance
+ if (index < list._size - 1) {
+ currentElement = list._items[++index];
+ return true;
+ }
+ else {
+ currentElement = dummyObject;
+ index =list._size;
+ return false;
+ }
+ }
+ else {
+ if (index < list.Count - 1) {
+ currentElement = list[++index];
+ return true;
+ }
+ else {
+ index = list.Count;
+ currentElement = dummyObject;
+ return false;
+ }
+ }
+ }
+
+ public Object Current {
+ get {
+ object temp = currentElement;
+ if(dummyObject == temp) { // check if enumeration has not started or has terminated
+ if (index == -1) {
+ throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
+ }
+ else {
+ throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded));
+ }
+ }
+
+ return temp;
+ }
+ }
+
+ public void Reset() {
+ if (version != list._version) {
+ throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
+ }
+
+ currentElement = dummyObject;
+ index = -1;
+ }
+ }
+
+ internal class ArrayListDebugView {
+ private ArrayList arrayList;
+
+ public ArrayListDebugView( ArrayList arrayList) {
+ if( arrayList == null)
+ throw new ArgumentNullException("arrayList");
+
+ this.arrayList = arrayList;
+ }
+
+ [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+ public Object[] Items {
+ get {
+ return arrayList.ToArray();
+ }
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/BitArray.cs b/src/mscorlib/src/System/Collections/BitArray.cs
new file mode 100644
index 0000000000..2f565f83af
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/BitArray.cs
@@ -0,0 +1,524 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+**
+**
+** Purpose: The BitArray class manages a compact array of bit values.
+**
+**
+=============================================================================*/
+namespace System.Collections {
+
+ using System;
+ using System.Security.Permissions;
+ using System.Diagnostics.Contracts;
+ // A vector of bits. Use this to store bits efficiently, without having to do bit
+ // shifting yourself.
+[System.Runtime.InteropServices.ComVisible(true)]
+ [Serializable()] public sealed class BitArray : ICollection, ICloneable {
+ private BitArray() {
+ }
+
+ /*=========================================================================
+ ** Allocates space to hold length bit values. All of the values in the bit
+ ** array are set to false.
+ **
+ ** Exceptions: ArgumentException if length < 0.
+ =========================================================================*/
+ public BitArray(int length)
+ : this(length, false) {
+ }
+
+ /*=========================================================================
+ ** Allocates space to hold length bit values. All of the values in the bit
+ ** array are set to defaultValue.
+ **
+ ** Exceptions: ArgumentOutOfRangeException if length < 0.
+ =========================================================================*/
+ public BitArray(int length, bool defaultValue) {
+ if (length < 0) {
+ throw new ArgumentOutOfRangeException(nameof(length), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ }
+ Contract.EndContractBlock();
+
+ m_array = new int[GetArrayLength(length, BitsPerInt32)];
+ m_length = length;
+
+ int fillValue = defaultValue ? unchecked(((int)0xffffffff)) : 0;
+ for (int i = 0; i < m_array.Length; i++) {
+ m_array[i] = fillValue;
+ }
+
+ _version = 0;
+ }
+
+ /*=========================================================================
+ ** Allocates space to hold the bit values in bytes. bytes[0] represents
+ ** bits 0 - 7, bytes[1] represents bits 8 - 15, etc. The LSB of each byte
+ ** represents the lowest index value; bytes[0] & 1 represents bit 0,
+ ** bytes[0] & 2 represents bit 1, bytes[0] & 4 represents bit 2, etc.
+ **
+ ** Exceptions: ArgumentException if bytes == null.
+ =========================================================================*/
+ public BitArray(byte[] bytes) {
+ if (bytes == null) {
+ throw new ArgumentNullException(nameof(bytes));
+ }
+ Contract.EndContractBlock();
+ // this value is chosen to prevent overflow when computing m_length.
+ // m_length is of type int32 and is exposed as a property, so
+ // type of m_length can't be changed to accommodate.
+ if (bytes.Length > Int32.MaxValue / BitsPerByte) {
+ throw new ArgumentException(Environment.GetResourceString("Argument_ArrayTooLarge", BitsPerByte), nameof(bytes));
+ }
+
+ m_array = new int[GetArrayLength(bytes.Length, BytesPerInt32)];
+ m_length = bytes.Length * BitsPerByte;
+
+ int i = 0;
+ int j = 0;
+ while (bytes.Length - j >= 4) {
+ m_array[i++] = (bytes[j] & 0xff) |
+ ((bytes[j + 1] & 0xff) << 8) |
+ ((bytes[j + 2] & 0xff) << 16) |
+ ((bytes[j + 3] & 0xff) << 24);
+ j += 4;
+ }
+
+ Contract.Assert(bytes.Length - j >= 0, "BitArray byteLength problem");
+ Contract.Assert(bytes.Length - j < 4, "BitArray byteLength problem #2");
+
+ switch (bytes.Length - j) {
+ case 3:
+ m_array[i] = ((bytes[j + 2] & 0xff) << 16);
+ goto case 2;
+ // fall through
+ case 2:
+ m_array[i] |= ((bytes[j + 1] & 0xff) << 8);
+ goto case 1;
+ // fall through
+ case 1:
+ m_array[i] |= (bytes[j] & 0xff);
+ break;
+ }
+
+ _version = 0;
+ }
+
+ public BitArray(bool[] values) {
+ if (values == null) {
+ throw new ArgumentNullException(nameof(values));
+ }
+ Contract.EndContractBlock();
+
+ m_array = new int[GetArrayLength(values.Length, BitsPerInt32)];
+ m_length = values.Length;
+
+ for (int i = 0;i<values.Length;i++) {
+ if (values[i])
+ m_array[i/32] |= (1 << (i%32));
+ }
+
+ _version = 0;
+
+ }
+
+ /*=========================================================================
+ ** Allocates space to hold the bit values in values. values[0] represents
+ ** bits 0 - 31, values[1] represents bits 32 - 63, etc. The LSB of each
+ ** integer represents the lowest index value; values[0] & 1 represents bit
+ ** 0, values[0] & 2 represents bit 1, values[0] & 4 represents bit 2, etc.
+ **
+ ** Exceptions: ArgumentException if values == null.
+ =========================================================================*/
+ public BitArray(int[] values) {
+ if (values == null) {
+ throw new ArgumentNullException(nameof(values));
+ }
+ Contract.EndContractBlock();
+ // this value is chosen to prevent overflow when computing m_length
+ if (values.Length > Int32.MaxValue / BitsPerInt32) {
+ throw new ArgumentException(Environment.GetResourceString("Argument_ArrayTooLarge", BitsPerInt32), nameof(values));
+ }
+
+ m_array = new int[values.Length];
+ m_length = values.Length * BitsPerInt32;
+
+ Array.Copy(values, m_array, values.Length);
+
+ _version = 0;
+ }
+
+ /*=========================================================================
+ ** Allocates a new BitArray with the same length and bit values as bits.
+ **
+ ** Exceptions: ArgumentException if bits == null.
+ =========================================================================*/
+ public BitArray(BitArray bits) {
+ if (bits == null) {
+ throw new ArgumentNullException(nameof(bits));
+ }
+ Contract.EndContractBlock();
+
+ int arrayLength = GetArrayLength(bits.m_length, BitsPerInt32);
+ m_array = new int[arrayLength];
+ m_length = bits.m_length;
+
+ Array.Copy(bits.m_array, m_array, arrayLength);
+
+ _version = bits._version;
+ }
+
+ public bool this[int index] {
+ get {
+ return Get(index);
+ }
+ set {
+ Set(index,value);
+ }
+ }
+
+ /*=========================================================================
+ ** Returns the bit value at position index.
+ **
+ ** Exceptions: ArgumentOutOfRangeException if index < 0 or
+ ** index >= GetLength().
+ =========================================================================*/
+ public bool Get(int index) {
+ if (index < 0 || index >= Length) {
+ throw new ArgumentOutOfRangeException(nameof(index), Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ }
+ Contract.EndContractBlock();
+
+ return (m_array[index / 32] & (1 << (index % 32))) != 0;
+ }
+
+ /*=========================================================================
+ ** Sets the bit value at position index to value.
+ **
+ ** Exceptions: ArgumentOutOfRangeException if index < 0 or
+ ** index >= GetLength().
+ =========================================================================*/
+ public void Set(int index, bool value) {
+ if (index < 0 || index >= Length) {
+ throw new ArgumentOutOfRangeException(nameof(index), Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ }
+ Contract.EndContractBlock();
+
+ if (value) {
+ m_array[index / 32] |= (1 << (index % 32));
+ } else {
+ m_array[index / 32] &= ~(1 << (index % 32));
+ }
+
+ _version++;
+ }
+
+ /*=========================================================================
+ ** Sets all the bit values to value.
+ =========================================================================*/
+ public void SetAll(bool value) {
+ int fillValue = value ? unchecked(((int)0xffffffff)) : 0;
+ int ints = GetArrayLength(m_length, BitsPerInt32);
+ for (int i = 0; i < ints; i++) {
+ m_array[i] = fillValue;
+ }
+
+ _version++;
+ }
+
+ /*=========================================================================
+ ** Returns a reference to the current instance ANDed with value.
+ **
+ ** Exceptions: ArgumentException if value == null or
+ ** value.Length != this.Length.
+ =========================================================================*/
+ public BitArray And(BitArray value) {
+ if (value==null)
+ throw new ArgumentNullException(nameof(value));
+ if (Length != value.Length)
+ throw new ArgumentException(Environment.GetResourceString("Arg_ArrayLengthsDiffer"));
+ Contract.EndContractBlock();
+
+ int ints = GetArrayLength(m_length, BitsPerInt32);
+ for (int i = 0; i < ints; i++) {
+ m_array[i] &= value.m_array[i];
+ }
+
+ _version++;
+ return this;
+ }
+
+ /*=========================================================================
+ ** Returns a reference to the current instance ORed with value.
+ **
+ ** Exceptions: ArgumentException if value == null or
+ ** value.Length != this.Length.
+ =========================================================================*/
+ public BitArray Or(BitArray value) {
+ if (value==null)
+ throw new ArgumentNullException(nameof(value));
+ if (Length != value.Length)
+ throw new ArgumentException(Environment.GetResourceString("Arg_ArrayLengthsDiffer"));
+ Contract.EndContractBlock();
+
+ int ints = GetArrayLength(m_length, BitsPerInt32);
+ for (int i = 0; i < ints; i++) {
+ m_array[i] |= value.m_array[i];
+ }
+
+ _version++;
+ return this;
+ }
+
+ /*=========================================================================
+ ** Returns a reference to the current instance XORed with value.
+ **
+ ** Exceptions: ArgumentException if value == null or
+ ** value.Length != this.Length.
+ =========================================================================*/
+ public BitArray Xor(BitArray value) {
+ if (value==null)
+ throw new ArgumentNullException(nameof(value));
+ if (Length != value.Length)
+ throw new ArgumentException(Environment.GetResourceString("Arg_ArrayLengthsDiffer"));
+ Contract.EndContractBlock();
+
+ int ints = GetArrayLength(m_length, BitsPerInt32);
+ for (int i = 0; i < ints; i++) {
+ m_array[i] ^= value.m_array[i];
+ }
+
+ _version++;
+ return this;
+ }
+
+ /*=========================================================================
+ ** Inverts all the bit values. On/true bit values are converted to
+ ** off/false. Off/false bit values are turned on/true. The current instance
+ ** is updated and returned.
+ =========================================================================*/
+ public BitArray Not() {
+ int ints = GetArrayLength(m_length, BitsPerInt32);
+ for (int i = 0; i < ints; i++) {
+ m_array[i] = ~m_array[i];
+ }
+
+ _version++;
+ return this;
+ }
+
+ public int Length {
+ get {
+ Contract.Ensures(Contract.Result<int>() >= 0);
+ return m_length;
+ }
+ set {
+ if (value < 0) {
+ throw new ArgumentOutOfRangeException(nameof(value), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ }
+ Contract.EndContractBlock();
+
+ int newints = GetArrayLength(value, BitsPerInt32);
+ if (newints > m_array.Length || newints + _ShrinkThreshold < m_array.Length) {
+ // grow or shrink (if wasting more than _ShrinkThreshold ints)
+ int[] newarray = new int[newints];
+ Array.Copy(m_array, newarray, newints > m_array.Length ? m_array.Length : newints);
+ m_array = newarray;
+ }
+
+ if (value > m_length) {
+ // clear high bit values in the last int
+ int last = GetArrayLength(m_length, BitsPerInt32) - 1;
+ int bits = m_length % 32;
+ if (bits > 0) {
+ m_array[last] &= (1 << bits) - 1;
+ }
+
+ // clear remaining int values
+ Array.Clear(m_array, last + 1, newints - last - 1);
+ }
+
+ m_length = value;
+ _version++;
+ }
+ }
+
+ // ICollection implementation
+ public void CopyTo(Array array, int index)
+ {
+ if (array == null)
+ throw new ArgumentNullException(nameof(array));
+
+ if (index < 0)
+ throw new ArgumentOutOfRangeException(nameof(index), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+
+ if (array.Rank != 1)
+ throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"), nameof(array));
+
+ Contract.EndContractBlock();
+
+ if (array is int[])
+ {
+ Array.Copy(m_array, 0, array, index, GetArrayLength(m_length, BitsPerInt32));
+ }
+ else if (array is byte[])
+ {
+ int arrayLength = GetArrayLength(m_length, BitsPerByte);
+ if ((array.Length - index) < arrayLength)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+
+ byte [] b = (byte[])array;
+ for (int i = 0; i < arrayLength; i++)
+ b[index + i] = (byte)((m_array[i/4] >> ((i%4)*8)) & 0x000000FF); // Shift to bring the required byte to LSB, then mask
+ }
+ else if (array is bool[])
+ {
+ if (array.Length - index < m_length)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+
+ bool [] b = (bool[])array;
+ for (int i = 0;i<m_length;i++)
+ b[index + i] = ((m_array[i/32] >> (i%32)) & 0x00000001) != 0;
+ }
+ else
+ throw new ArgumentException(Environment.GetResourceString("Arg_BitArrayTypeUnsupported"), nameof(array));
+ }
+
+ public int Count
+ {
+ get
+ {
+ Contract.Ensures(Contract.Result<int>() >= 0);
+
+ return m_length;
+ }
+ }
+
+ public Object Clone()
+ {
+ Contract.Ensures(Contract.Result<Object>() != null);
+ Contract.Ensures(((BitArray)Contract.Result<Object>()).Length == this.Length);
+
+ return new BitArray(this);
+ }
+
+ public Object SyncRoot
+ {
+ get
+ {
+ if( _syncRoot == null) {
+ System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);
+ }
+ return _syncRoot;
+ }
+ }
+
+ public bool IsReadOnly
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public bool IsSynchronized
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return new BitArrayEnumeratorSimple(this);
+ }
+
+ // XPerY=n means that n Xs can be stored in 1 Y.
+ private const int BitsPerInt32 = 32;
+ private const int BytesPerInt32 = 4;
+ private const int BitsPerByte = 8;
+
+ /// <summary>
+ /// Used for conversion between different representations of bit array.
+ /// Returns (n+(div-1))/div, rearranged to avoid arithmetic overflow.
+ /// For example, in the bit to int case, the straightforward calc would
+ /// be (n+31)/32, but that would cause overflow. So instead it's
+ /// rearranged to ((n-1)/32) + 1, with special casing for 0.
+ ///
+ /// Usage:
+ /// GetArrayLength(77, BitsPerInt32): returns how many ints must be
+ /// allocated to store 77 bits.
+ /// </summary>
+ /// <param name="n"></param>
+ /// <param name="div">use a conversion constant, e.g. BytesPerInt32 to get
+ /// how many ints are required to store n bytes</param>
+ /// <returns></returns>
+ private static int GetArrayLength(int n, int div) {
+ Contract.Assert(div > 0, "GetArrayLength: div arg must be greater than 0");
+ return n > 0 ? (((n - 1) / div) + 1) : 0;
+ }
+
+ [Serializable]
+ private class BitArrayEnumeratorSimple : IEnumerator, ICloneable
+ {
+ private BitArray bitarray;
+ private int index;
+ private int version;
+ private bool currentElement;
+
+ internal BitArrayEnumeratorSimple(BitArray bitarray) {
+ this.bitarray = bitarray;
+ this.index = -1;
+ version = bitarray._version;
+ }
+
+ public Object Clone() {
+ return MemberwiseClone();
+ }
+
+ public virtual bool MoveNext() {
+ if (version != bitarray._version) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
+ if (index < (bitarray.Count-1)) {
+ index++;
+ currentElement = bitarray.Get(index);
+ return true;
+ }
+ else
+ index = bitarray.Count;
+
+ return false;
+ }
+
+ public virtual Object Current {
+ get {
+ if (index == -1)
+ throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
+ if (index >= bitarray.Count)
+ throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded));
+ return currentElement;
+ }
+ }
+
+ public void Reset() {
+ if (version != bitarray._version) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
+ index = -1;
+ }
+ }
+
+ private int[] m_array;
+ private int m_length;
+ private int _version;
+ [NonSerialized]
+ private Object _syncRoot;
+
+ private const int _ShrinkThreshold = 256;
+ }
+
+}
diff --git a/src/mscorlib/src/System/Collections/CollectionBase.cs b/src/mscorlib/src/System/Collections/CollectionBase.cs
new file mode 100644
index 0000000000..1bb08af27a
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/CollectionBase.cs
@@ -0,0 +1,215 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+//
+
+namespace System.Collections {
+ using System;
+ using System.Diagnostics.Contracts;
+
+ // Useful base class for typed read/write collections where items derive from object
+ [Serializable]
+[System.Runtime.InteropServices.ComVisible(true)]
+ public abstract class CollectionBase : IList {
+ ArrayList list;
+
+ protected CollectionBase() {
+ list = new ArrayList();
+ }
+
+ protected CollectionBase(int capacity) {
+ list = new ArrayList(capacity);
+ }
+
+
+ protected ArrayList InnerList {
+ get {
+ if (list == null)
+ list = new ArrayList();
+ return list;
+ }
+ }
+
+ protected IList List {
+ get { return (IList)this; }
+ }
+
+ [System.Runtime.InteropServices.ComVisible(false)]
+ public int Capacity {
+ get {
+ return InnerList.Capacity;
+ }
+ set {
+ InnerList.Capacity = value;
+ }
+ }
+
+
+ public int Count {
+ get {
+ return list == null ? 0 : list.Count;
+ }
+ }
+
+ public void Clear() {
+ OnClear();
+ InnerList.Clear();
+ OnClearComplete();
+ }
+
+ public void RemoveAt(int index) {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ Contract.EndContractBlock();
+ Object temp = InnerList[index];
+ OnValidate(temp);
+ OnRemove(index, temp);
+ InnerList.RemoveAt(index);
+ try {
+ OnRemoveComplete(index, temp);
+ }
+ catch {
+ InnerList.Insert(index, temp);
+ throw;
+ }
+
+ }
+
+ bool IList.IsReadOnly {
+ get { return InnerList.IsReadOnly; }
+ }
+
+ bool IList.IsFixedSize {
+ get { return InnerList.IsFixedSize; }
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return InnerList.IsSynchronized; }
+ }
+
+ Object ICollection.SyncRoot {
+ get { return InnerList.SyncRoot; }
+ }
+
+ void ICollection.CopyTo(Array array, int index) {
+ InnerList.CopyTo(array, index);
+ }
+
+ Object IList.this[int index] {
+ get {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ Contract.EndContractBlock();
+ return InnerList[index];
+ }
+ set {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ Contract.EndContractBlock();
+ OnValidate(value);
+ Object temp = InnerList[index];
+ OnSet(index, temp, value);
+ InnerList[index] = value;
+ try {
+ OnSetComplete(index, temp, value);
+ }
+ catch {
+ InnerList[index] = temp;
+ throw;
+ }
+ }
+ }
+
+ bool IList.Contains(Object value) {
+ return InnerList.Contains(value);
+ }
+
+ int IList.Add(Object value) {
+ OnValidate(value);
+ OnInsert(InnerList.Count, value);
+ int index = InnerList.Add(value);
+ try {
+ OnInsertComplete(index, value);
+ }
+ catch {
+ InnerList.RemoveAt(index);
+ throw;
+ }
+ return index;
+ }
+
+
+ void IList.Remove(Object value) {
+ OnValidate(value);
+ int index = InnerList.IndexOf(value);
+ if (index < 0) throw new ArgumentException(Environment.GetResourceString("Arg_RemoveArgNotFound"));
+ OnRemove(index, value);
+ InnerList.RemoveAt(index);
+ try{
+ OnRemoveComplete(index, value);
+ }
+ catch {
+ InnerList.Insert(index, value);
+ throw;
+ }
+ }
+
+ int IList.IndexOf(Object value) {
+ return InnerList.IndexOf(value);
+ }
+
+ void IList.Insert(int index, Object value) {
+ if (index < 0 || index > Count)
+ throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ Contract.EndContractBlock();
+ OnValidate(value);
+ OnInsert(index, value);
+ InnerList.Insert(index, value);
+ try {
+ OnInsertComplete(index, value);
+ }
+ catch {
+ InnerList.RemoveAt(index);
+ throw;
+ }
+ }
+
+ public IEnumerator GetEnumerator() {
+ return InnerList.GetEnumerator();
+ }
+
+ protected virtual void OnSet(int index, Object oldValue, Object newValue) {
+ }
+
+ protected virtual void OnInsert(int index, Object value) {
+ }
+
+ protected virtual void OnClear() {
+ }
+
+ protected virtual void OnRemove(int index, Object value) {
+ }
+
+ protected virtual void OnValidate(Object value) {
+ if (value == null) throw new ArgumentNullException("value");
+ Contract.EndContractBlock();
+ }
+
+ protected virtual void OnSetComplete(int index, Object oldValue, Object newValue) {
+ }
+
+ protected virtual void OnInsertComplete(int index, Object value) {
+ }
+
+ protected virtual void OnClearComplete() {
+ }
+
+ protected virtual void OnRemoveComplete(int index, Object value) {
+ }
+
+ }
+
+}
diff --git a/src/mscorlib/src/System/Collections/Comparer.cs b/src/mscorlib/src/System/Collections/Comparer.cs
new file mode 100644
index 0000000000..11e26252a8
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Comparer.cs
@@ -0,0 +1,98 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Default IComparer implementation.
+**
+**
+===========================================================*/
+namespace System.Collections {
+
+ using System;
+ using System.Globalization;
+ using System.Runtime.Serialization;
+ using System.Security.Permissions;
+ using System.Diagnostics.Contracts;
+
+ [Serializable]
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public sealed class Comparer : IComparer , ISerializable
+ {
+ private CompareInfo m_compareInfo;
+ public static readonly Comparer Default = new Comparer(CultureInfo.CurrentCulture);
+ public static readonly Comparer DefaultInvariant = new Comparer(CultureInfo.InvariantCulture);
+
+ private const String CompareInfoName = "CompareInfo";
+
+ private Comparer() {
+ m_compareInfo = null;
+ }
+
+ public Comparer(CultureInfo culture) {
+ if (culture==null) {
+ throw new ArgumentNullException("culture");
+ }
+ Contract.EndContractBlock();
+ m_compareInfo = culture.CompareInfo;
+ }
+
+ private Comparer(SerializationInfo info, StreamingContext context) {
+ m_compareInfo = null;
+ SerializationInfoEnumerator enumerator = info.GetEnumerator();
+ while( enumerator.MoveNext()) {
+ switch( enumerator.Name) {
+ case CompareInfoName:
+ m_compareInfo = (CompareInfo) info.GetValue(CompareInfoName, typeof(CompareInfo));
+ break;
+ }
+ }
+ }
+
+ // Compares two Objects by calling CompareTo. If a ==
+ // b,0 is returned. If a implements
+ // IComparable, a.CompareTo(b) is returned. If a
+ // doesn't implement IComparable and b does,
+ // -(b.CompareTo(a)) is returned, otherwise an
+ // exception is thrown.
+ //
+ public int Compare(Object a, Object b) {
+ if (a == b) return 0;
+ if (a == null) return -1;
+ if (b == null) return 1;
+ if (m_compareInfo != null) {
+ String sa = a as String;
+ String sb = b as String;
+ if (sa != null && sb != null)
+ return m_compareInfo.Compare(sa, sb);
+ }
+
+ IComparable ia = a as IComparable;
+ if (ia != null)
+ return ia.CompareTo(b);
+
+ IComparable ib = b as IComparable;
+ if (ib != null)
+ return -ib.CompareTo(a);
+
+ throw new ArgumentException(Environment.GetResourceString("Argument_ImplementIComparable"));
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ public void GetObjectData(SerializationInfo info, StreamingContext context) {
+ if (info==null) {
+ throw new ArgumentNullException("info");
+ }
+ Contract.EndContractBlock();
+
+ if( m_compareInfo != null) {
+ info.AddValue(CompareInfoName, m_compareInfo);
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/CompatibleComparer.cs b/src/mscorlib/src/System/Collections/CompatibleComparer.cs
new file mode 100644
index 0000000000..85e6c3f0f3
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/CompatibleComparer.cs
@@ -0,0 +1,67 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+
+using System.Diagnostics.Contracts;
+
+namespace System.Collections {
+
+ [Serializable]
+ internal class CompatibleComparer: IEqualityComparer {
+ IComparer _comparer;
+#pragma warning disable 618
+ IHashCodeProvider _hcp;
+
+ internal CompatibleComparer(IComparer comparer, IHashCodeProvider hashCodeProvider) {
+ _comparer = comparer;
+ _hcp = hashCodeProvider;
+ }
+#pragma warning restore 618
+
+ public int Compare(Object a, Object b) {
+ if (a == b) return 0;
+ if (a == null) return -1;
+ if (b == null) return 1;
+ if (_comparer != null)
+ return _comparer.Compare(a,b);
+ IComparable ia = a as IComparable;
+ if (ia != null)
+ return ia.CompareTo(b);
+
+ throw new ArgumentException(Environment.GetResourceString("Argument_ImplementIComparable"));
+ }
+
+ public new bool Equals(Object a, Object b) {
+ return Compare(a, b) == 0;
+ }
+
+ public int GetHashCode(Object obj) {
+ if( obj == null) {
+ throw new ArgumentNullException("obj");
+ }
+ Contract.EndContractBlock();
+
+ if (_hcp != null)
+ return _hcp.GetHashCode(obj);
+ return obj.GetHashCode();
+ }
+
+ // These are helpers for the Hashtable to query the IKeyComparer infrastructure.
+ internal IComparer Comparer {
+ get {
+ return _comparer;
+ }
+ }
+
+ // These are helpers for the Hashtable to query the IKeyComparer infrastructure.
+#pragma warning disable 618
+ internal IHashCodeProvider HashCodeProvider {
+ get {
+ return _hcp;
+ }
+ }
+#pragma warning restore 618
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Concurrent/ConcurrentDictionary.cs b/src/mscorlib/src/System/Collections/Concurrent/ConcurrentDictionary.cs
new file mode 100644
index 0000000000..d805dc8be7
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Concurrent/ConcurrentDictionary.cs
@@ -0,0 +1,2095 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+/*============================================================
+**
+**
+**
+** Purpose: A scalable dictionary for concurrent access
+**
+**
+===========================================================*/
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading;
+using System.Security;
+using System.Security.Permissions;
+
+namespace System.Collections.Concurrent
+{
+
+ /// <summary>
+ /// Represents a thread-safe collection of keys and values.
+ /// </summary>
+ /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
+ /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam>
+ /// <remarks>
+ /// All public and protected members of <see cref="ConcurrentDictionary{TKey,TValue}"/> are thread-safe and may be used
+ /// concurrently from multiple threads.
+ /// </remarks>
+#if !FEATURE_CORECLR
+ [Serializable]
+#endif
+ [ComVisible(false)]
+ [DebuggerTypeProxy(typeof(Mscorlib_DictionaryDebugView<,>))]
+ [DebuggerDisplay("Count = {Count}")]
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ public class ConcurrentDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, IReadOnlyDictionary<TKey, TValue>
+ {
+ /// <summary>
+ /// Tables that hold the internal state of the ConcurrentDictionary
+ ///
+ /// Wrapping the three tables in a single object allows us to atomically
+ /// replace all tables at once.
+ /// </summary>
+ private class Tables
+ {
+ internal readonly Node[] m_buckets; // A singly-linked list for each bucket.
+ internal readonly object[] m_locks; // A set of locks, each guarding a section of the table.
+ internal volatile int[] m_countPerLock; // The number of elements guarded by each lock.
+ internal readonly IEqualityComparer<TKey> m_comparer; // Key equality comparer
+
+ internal Tables(Node[] buckets, object[] locks, int[] countPerLock, IEqualityComparer<TKey> comparer)
+ {
+ m_buckets = buckets;
+ m_locks = locks;
+ m_countPerLock = countPerLock;
+ m_comparer = comparer;
+ }
+ }
+#if !FEATURE_CORECLR
+ [NonSerialized]
+#endif
+ private volatile Tables m_tables; // Internal tables of the dictionary
+ // NOTE: this is only used for compat reasons to serialize the comparer.
+ // This should not be accessed from anywhere else outside of the serialization methods.
+ internal IEqualityComparer<TKey> m_comparer;
+#if !FEATURE_CORECLR
+ [NonSerialized]
+#endif
+ private readonly bool m_growLockArray; // Whether to dynamically increase the size of the striped lock
+
+ // How many times we resized becaused of collisions.
+ // This is used to make sure we don't resize the dictionary because of multi-threaded Add() calls
+ // that generate collisions. Whenever a GrowTable() should be the only place that changes this
+#if !FEATURE_CORECLR
+ // The field should be have been marked as NonSerialized but because we shipped it without that attribute in 4.5.1.
+ // we can't add it back without breaking compat. To maximize compat we are going to keep the OptionalField attribute
+ // This will prevent cases where the field was not serialized.
+ [OptionalField]
+#endif
+ private int m_keyRehashCount;
+
+#if !FEATURE_CORECLR
+ [NonSerialized]
+#endif
+ private int m_budget; // The maximum number of elements per lock before a resize operation is triggered
+
+#if !FEATURE_CORECLR // These fields are not used in CoreCLR
+ private KeyValuePair<TKey, TValue>[] m_serializationArray; // Used for custom serialization
+
+ private int m_serializationConcurrencyLevel; // used to save the concurrency level in serialization
+
+ private int m_serializationCapacity; // used to save the capacity in serialization
+#endif
+ // The default concurrency level is DEFAULT_CONCURRENCY_MULTIPLIER * #CPUs. The higher the
+ // DEFAULT_CONCURRENCY_MULTIPLIER, the more concurrent writes can take place without interference
+ // and blocking, but also the more expensive operations that require all locks become (e.g. table
+ // resizing, ToArray, Count, etc). According to brief benchmarks that we ran, 4 seems like a good
+ // compromise.
+ private const int DEFAULT_CONCURRENCY_MULTIPLIER = 4;
+
+ // The default capacity, i.e. the initial # of buckets. When choosing this value, we are making
+ // a trade-off between the size of a very small dictionary, and the number of resizes when
+ // constructing a large dictionary. Also, the capacity should not be divisible by a small prime.
+ private const int DEFAULT_CAPACITY = 31;
+
+ // The maximum size of the striped lock that will not be exceeded when locks are automatically
+ // added as the dictionary grows. However, the user is allowed to exceed this limit by passing
+ // a concurrency level larger than MAX_LOCK_NUMBER into the constructor.
+ private const int MAX_LOCK_NUMBER = 1024;
+
+ // Whether TValue is a type that can be written atomically (i.e., with no danger of torn reads)
+ private static readonly bool s_isValueWriteAtomic = IsValueWriteAtomic();
+
+
+ /// <summary>
+ /// Determines whether type TValue can be written atomically
+ /// </summary>
+ private static bool IsValueWriteAtomic()
+ {
+ Type valueType = typeof(TValue);
+
+ //
+ // Section 12.6.6 of ECMA CLI explains which types can be read and written atomically without
+ // the risk of tearing.
+ //
+ // See http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf
+ //
+ bool isAtomic =
+ (valueType.IsClass)
+ || valueType == typeof(Boolean)
+ || valueType == typeof(Char)
+ || valueType == typeof(Byte)
+ || valueType == typeof(SByte)
+ || valueType == typeof(Int16)
+ || valueType == typeof(UInt16)
+ || valueType == typeof(Int32)
+ || valueType == typeof(UInt32)
+ || valueType == typeof(Single);
+
+ if (!isAtomic && IntPtr.Size == 8)
+ {
+ isAtomic |= valueType == typeof(Double) || valueType == typeof(Int64);
+ }
+
+ return isAtomic;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see
+ /// cref="ConcurrentDictionary{TKey,TValue}"/>
+ /// class that is empty, has the default concurrency level, has the default initial capacity, and
+ /// uses the default comparer for the key type.
+ /// </summary>
+ public ConcurrentDictionary() : this(DefaultConcurrencyLevel, DEFAULT_CAPACITY, true, EqualityComparer<TKey>.Default) { }
+
+ /// <summary>
+ /// Initializes a new instance of the <see
+ /// cref="ConcurrentDictionary{TKey,TValue}"/>
+ /// class that is empty, has the specified concurrency level and capacity, and uses the default
+ /// comparer for the key type.
+ /// </summary>
+ /// <param name="concurrencyLevel">The estimated number of threads that will update the
+ /// <see cref="ConcurrentDictionary{TKey,TValue}"/> concurrently.</param>
+ /// <param name="capacity">The initial number of elements that the <see
+ /// cref="ConcurrentDictionary{TKey,TValue}"/>
+ /// can contain.</param>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="concurrencyLevel"/> is
+ /// less than 1.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"> <paramref name="capacity"/> is less than
+ /// 0.</exception>
+ public ConcurrentDictionary(int concurrencyLevel, int capacity) : this(concurrencyLevel, capacity, false, EqualityComparer<TKey>.Default) { }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/>
+ /// class that contains elements copied from the specified <see
+ /// cref="T:System.Collections.IEnumerable{KeyValuePair{TKey,TValue}}"/>, has the default concurrency
+ /// level, has the default initial capacity, and uses the default comparer for the key type.
+ /// </summary>
+ /// <param name="collection">The <see
+ /// cref="T:System.Collections.IEnumerable{KeyValuePair{TKey,TValue}}"/> whose elements are copied to
+ /// the new
+ /// <see cref="ConcurrentDictionary{TKey,TValue}"/>.</param>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="collection"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ /// <exception cref="T:System.ArgumentException"><paramref name="collection"/> contains one or more
+ /// duplicate keys.</exception>
+ public ConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection) : this(collection, EqualityComparer<TKey>.Default) { }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/>
+ /// class that is empty, has the specified concurrency level and capacity, and uses the specified
+ /// <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/>.
+ /// </summary>
+ /// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/>
+ /// implementation to use when comparing keys.</param>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="comparer"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ public ConcurrentDictionary(IEqualityComparer<TKey> comparer) : this(DefaultConcurrencyLevel, DEFAULT_CAPACITY, true, comparer) { }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/>
+ /// class that contains elements copied from the specified <see
+ /// cref="T:System.Collections.IEnumerable"/>, has the default concurrency level, has the default
+ /// initial capacity, and uses the specified
+ /// <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/>.
+ /// </summary>
+ /// <param name="collection">The <see
+ /// cref="T:System.Collections.IEnumerable{KeyValuePair{TKey,TValue}}"/> whose elements are copied to
+ /// the new
+ /// <see cref="ConcurrentDictionary{TKey,TValue}"/>.</param>
+ /// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/>
+ /// implementation to use when comparing keys.</param>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="collection"/> is a null reference
+ /// (Nothing in Visual Basic). -or-
+ /// <paramref name="comparer"/> is a null reference (Nothing in Visual Basic).
+ /// </exception>
+ public ConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer)
+ : this(comparer)
+ {
+ if (collection == null) throw new ArgumentNullException("collection");
+
+ InitializeFromCollection(collection);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/>
+ /// class that contains elements copied from the specified <see cref="T:System.Collections.IEnumerable"/>,
+ /// has the specified concurrency level, has the specified initial capacity, and uses the specified
+ /// <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/>.
+ /// </summary>
+ /// <param name="concurrencyLevel">The estimated number of threads that will update the
+ /// <see cref="ConcurrentDictionary{TKey,TValue}"/> concurrently.</param>
+ /// <param name="collection">The <see cref="T:System.Collections.IEnumerable{KeyValuePair{TKey,TValue}}"/> whose elements are copied to the new
+ /// <see cref="ConcurrentDictionary{TKey,TValue}"/>.</param>
+ /// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/> implementation to use
+ /// when comparing keys.</param>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// <paramref name="collection"/> is a null reference (Nothing in Visual Basic).
+ /// -or-
+ /// <paramref name="comparer"/> is a null reference (Nothing in Visual Basic).
+ /// </exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// <paramref name="concurrencyLevel"/> is less than 1.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentException"><paramref name="collection"/> contains one or more duplicate keys.</exception>
+ public ConcurrentDictionary(
+ int concurrencyLevel, IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer)
+ : this(concurrencyLevel, DEFAULT_CAPACITY, false, comparer)
+ {
+ if (collection == null) throw new ArgumentNullException("collection");
+ if (comparer == null) throw new ArgumentNullException("comparer");
+
+ InitializeFromCollection(collection);
+ }
+
+ private void InitializeFromCollection(IEnumerable<KeyValuePair<TKey, TValue>> collection)
+ {
+ TValue dummy;
+ foreach (KeyValuePair<TKey, TValue> pair in collection)
+ {
+ if (pair.Key == null) throw new ArgumentNullException("key");
+
+ if (!TryAddInternal(pair.Key, pair.Value, false, false, out dummy))
+ {
+ throw new ArgumentException(GetResource("ConcurrentDictionary_SourceContainsDuplicateKeys"));
+ }
+ }
+
+ if (m_budget == 0)
+ {
+ m_budget = m_tables.m_buckets.Length / m_tables.m_locks.Length;
+ }
+
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/>
+ /// class that is empty, has the specified concurrency level, has the specified initial capacity, and
+ /// uses the specified <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/>.
+ /// </summary>
+ /// <param name="concurrencyLevel">The estimated number of threads that will update the
+ /// <see cref="ConcurrentDictionary{TKey,TValue}"/> concurrently.</param>
+ /// <param name="capacity">The initial number of elements that the <see
+ /// cref="ConcurrentDictionary{TKey,TValue}"/>
+ /// can contain.</param>
+ /// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/>
+ /// implementation to use when comparing keys.</param>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// <paramref name="concurrencyLevel"/> is less than 1. -or-
+ /// <paramref name="capacity"/> is less than 0.
+ /// </exception>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="comparer"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ public ConcurrentDictionary(int concurrencyLevel, int capacity, IEqualityComparer<TKey> comparer)
+ : this(concurrencyLevel, capacity, false, comparer)
+ {
+ }
+
+ internal ConcurrentDictionary(int concurrencyLevel, int capacity, bool growLockArray, IEqualityComparer<TKey> comparer)
+ {
+ if (concurrencyLevel < 1)
+ {
+ throw new ArgumentOutOfRangeException("concurrencyLevel", GetResource("ConcurrentDictionary_ConcurrencyLevelMustBePositive"));
+ }
+ if (capacity < 0)
+ {
+ throw new ArgumentOutOfRangeException("capacity", GetResource("ConcurrentDictionary_CapacityMustNotBeNegative"));
+ }
+ if (comparer == null) throw new ArgumentNullException("comparer");
+
+ // The capacity should be at least as large as the concurrency level. Otherwise, we would have locks that don't guard
+ // any buckets.
+ if (capacity < concurrencyLevel)
+ {
+ capacity = concurrencyLevel;
+ }
+
+ object[] locks = new object[concurrencyLevel];
+ for (int i = 0; i < locks.Length; i++)
+ {
+ locks[i] = new object();
+ }
+
+ int[] countPerLock = new int[locks.Length];
+ Node[] buckets = new Node[capacity];
+ m_tables = new Tables(buckets, locks, countPerLock, comparer);
+
+ m_growLockArray = growLockArray;
+ m_budget = buckets.Length / locks.Length;
+ }
+
+
+ /// <summary>
+ /// Attempts to add the specified key and value to the <see cref="ConcurrentDictionary{TKey,
+ /// TValue}"/>.
+ /// </summary>
+ /// <param name="key">The key of the element to add.</param>
+ /// <param name="value">The value of the element to add. The value can be a null reference (Nothing
+ /// in Visual Basic) for reference types.</param>
+ /// <returns>true if the key/value pair was added to the <see cref="ConcurrentDictionary{TKey,
+ /// TValue}"/>
+ /// successfully; otherwise, false.</returns>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null reference
+ /// (Nothing in Visual Basic).</exception>
+ /// <exception cref="T:System.OverflowException">The <see cref="ConcurrentDictionary{TKey, TValue}"/>
+ /// contains too many elements.</exception>
+ public bool TryAdd(TKey key, TValue value)
+ {
+ if (key == null) throw new ArgumentNullException("key");
+ TValue dummy;
+ return TryAddInternal(key, value, false, true, out dummy);
+ }
+
+ /// <summary>
+ /// Determines whether the <see cref="ConcurrentDictionary{TKey, TValue}"/> contains the specified
+ /// key.
+ /// </summary>
+ /// <param name="key">The key to locate in the <see cref="ConcurrentDictionary{TKey,
+ /// TValue}"/>.</param>
+ /// <returns>true if the <see cref="ConcurrentDictionary{TKey, TValue}"/> contains an element with
+ /// the specified key; otherwise, false.</returns>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ public bool ContainsKey(TKey key)
+ {
+ if (key == null) throw new ArgumentNullException("key");
+
+ TValue throwAwayValue;
+ return TryGetValue(key, out throwAwayValue);
+ }
+
+ /// <summary>
+ /// Attempts to remove and return the the value with the specified key from the
+ /// <see cref="ConcurrentDictionary{TKey, TValue}"/>.
+ /// </summary>
+ /// <param name="key">The key of the element to remove and return.</param>
+ /// <param name="value">When this method returns, <paramref name="value"/> contains the object removed from the
+ /// <see cref="ConcurrentDictionary{TKey,TValue}"/> or the default value of <typeparamref
+ /// name="TValue"/>
+ /// if the operation failed.</param>
+ /// <returns>true if an object was removed successfully; otherwise, false.</returns>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ public bool TryRemove(TKey key, out TValue value)
+ {
+ if (key == null) throw new ArgumentNullException("key");
+
+ return TryRemoveInternal(key, out value, false, default(TValue));
+ }
+
+ /// <summary>
+ /// Removes the specified key from the dictionary if it exists and returns its associated value.
+ /// If matchValue flag is set, the key will be removed only if is associated with a particular
+ /// value.
+ /// </summary>
+ /// <param name="key">The key to search for and remove if it exists.</param>
+ /// <param name="value">The variable into which the removed value, if found, is stored.</param>
+ /// <param name="matchValue">Whether removal of the key is conditional on its value.</param>
+ /// <param name="oldValue">The conditional value to compare against if <paramref name="matchValue"/> is true</param>
+ /// <returns></returns>
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
+ private bool TryRemoveInternal(TKey key, out TValue value, bool matchValue, TValue oldValue)
+ {
+ while (true)
+ {
+ Tables tables = m_tables;
+
+ IEqualityComparer<TKey> comparer = tables.m_comparer;
+
+ int bucketNo, lockNo;
+ GetBucketAndLockNo(comparer.GetHashCode(key), out bucketNo, out lockNo, tables.m_buckets.Length, tables.m_locks.Length);
+
+ lock (tables.m_locks[lockNo])
+ {
+ // If the table just got resized, we may not be holding the right lock, and must retry.
+ // This should be a rare occurence.
+ if (tables != m_tables)
+ {
+ continue;
+ }
+
+ Node prev = null;
+ for (Node curr = tables.m_buckets[bucketNo]; curr != null; curr = curr.m_next)
+ {
+ Assert((prev == null && curr == tables.m_buckets[bucketNo]) || prev.m_next == curr);
+
+ if (comparer.Equals(curr.m_key, key))
+ {
+ if (matchValue)
+ {
+ bool valuesMatch = EqualityComparer<TValue>.Default.Equals(oldValue, curr.m_value);
+ if (!valuesMatch)
+ {
+ value = default(TValue);
+ return false;
+ }
+ }
+
+ if (prev == null)
+ {
+ Volatile.Write<Node>(ref tables.m_buckets[bucketNo], curr.m_next);
+ }
+ else
+ {
+ prev.m_next = curr.m_next;
+ }
+
+ value = curr.m_value;
+ tables.m_countPerLock[lockNo]--;
+ return true;
+ }
+ prev = curr;
+ }
+ }
+
+ value = default(TValue);
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Attempts to get the value associated with the specified key from the <see
+ /// cref="ConcurrentDictionary{TKey,TValue}"/>.
+ /// </summary>
+ /// <param name="key">The key of the value to get.</param>
+ /// <param name="value">When this method returns, <paramref name="value"/> contains the object from
+ /// the
+ /// <see cref="ConcurrentDictionary{TKey,TValue}"/> with the specified key or the default value of
+ /// <typeparamref name="TValue"/>, if the operation failed.</param>
+ /// <returns>true if the key was found in the <see cref="ConcurrentDictionary{TKey,TValue}"/>;
+ /// otherwise, false.</returns>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
+ public bool TryGetValue(TKey key, out TValue value)
+ {
+ if (key == null) throw new ArgumentNullException("key");
+
+ int bucketNo, lockNoUnused;
+
+ // We must capture the m_buckets field in a local variable. It is set to a new table on each table resize.
+ Tables tables = m_tables;
+ IEqualityComparer<TKey> comparer = tables.m_comparer;
+ GetBucketAndLockNo(comparer.GetHashCode(key), out bucketNo, out lockNoUnused, tables.m_buckets.Length, tables.m_locks.Length);
+
+ // We can get away w/out a lock here.
+ // The Volatile.Read ensures that the load of the fields of 'n' doesn't move before the load from buckets[i].
+ Node n = Volatile.Read<Node>(ref tables.m_buckets[bucketNo]);
+
+ while (n != null)
+ {
+ if (comparer.Equals(n.m_key, key))
+ {
+ value = n.m_value;
+ return true;
+ }
+ n = n.m_next;
+ }
+
+ value = default(TValue);
+ return false;
+ }
+
+ /// <summary>
+ /// Compares the existing value for the specified key with a specified value, and if they're equal,
+ /// updates the key with a third value.
+ /// </summary>
+ /// <param name="key">The key whose value is compared with <paramref name="comparisonValue"/> and
+ /// possibly replaced.</param>
+ /// <param name="newValue">The value that replaces the value of the element with <paramref
+ /// name="key"/> if the comparison results in equality.</param>
+ /// <param name="comparisonValue">The value that is compared to the value of the element with
+ /// <paramref name="key"/>.</param>
+ /// <returns>true if the value with <paramref name="key"/> was equal to <paramref
+ /// name="comparisonValue"/> and replaced with <paramref name="newValue"/>; otherwise,
+ /// false.</returns>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null
+ /// reference.</exception>
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
+ public bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue)
+ {
+ if (key == null) throw new ArgumentNullException("key");
+
+ IEqualityComparer<TValue> valueComparer = EqualityComparer<TValue>.Default;
+
+ while (true)
+ {
+ int bucketNo;
+ int lockNo;
+ int hashcode;
+
+ Tables tables = m_tables;
+ IEqualityComparer<TKey> comparer = tables.m_comparer;
+
+ hashcode = comparer.GetHashCode(key);
+ GetBucketAndLockNo(hashcode, out bucketNo, out lockNo, tables.m_buckets.Length, tables.m_locks.Length);
+
+ lock (tables.m_locks[lockNo])
+ {
+ // If the table just got resized, we may not be holding the right lock, and must retry.
+ // This should be a rare occurence.
+ if (tables != m_tables)
+ {
+ continue;
+ }
+
+ // Try to find this key in the bucket
+ Node prev = null;
+ for (Node node = tables.m_buckets[bucketNo]; node != null; node = node.m_next)
+ {
+ Assert((prev == null && node == tables.m_buckets[bucketNo]) || prev.m_next == node);
+ if (comparer.Equals(node.m_key, key))
+ {
+ if (valueComparer.Equals(node.m_value, comparisonValue))
+ {
+ if (s_isValueWriteAtomic)
+ {
+ node.m_value = newValue;
+ }
+ else
+ {
+ Node newNode = new Node(node.m_key, newValue, hashcode, node.m_next);
+
+ if (prev == null)
+ {
+ tables.m_buckets[bucketNo] = newNode;
+ }
+ else
+ {
+ prev.m_next = newNode;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ prev = node;
+ }
+
+ //didn't find the key
+ return false;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Removes all keys and values from the <see cref="ConcurrentDictionary{TKey,TValue}"/>.
+ /// </summary>
+ public void Clear()
+ {
+ int locksAcquired = 0;
+ try
+ {
+ AcquireAllLocks(ref locksAcquired);
+
+ Tables newTables = new Tables(new Node[DEFAULT_CAPACITY], m_tables.m_locks, new int[m_tables.m_countPerLock.Length], m_tables.m_comparer);
+ m_tables = newTables;
+ m_budget = Math.Max(1, newTables.m_buckets.Length / newTables.m_locks.Length);
+ }
+ finally
+ {
+ ReleaseLocks(0, locksAcquired);
+ }
+ }
+
+ /// <summary>
+ /// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection"/> to an array of
+ /// type <see cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/>, starting at the
+ /// specified array index.
+ /// </summary>
+ /// <param name="array">The one-dimensional array of type <see
+ /// cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/>
+ /// that is the destination of the <see
+ /// cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/> elements copied from the <see
+ /// cref="T:System.Collections.ICollection"/>. The array must have zero-based indexing.</param>
+ /// <param name="index">The zero-based index in <paramref name="array"/> at which copying
+ /// begins.</param>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="array"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="index"/> is less than
+ /// 0.</exception>
+ /// <exception cref="T:System.ArgumentException"><paramref name="index"/> is equal to or greater than
+ /// the length of the <paramref name="array"/>. -or- The number of elements in the source <see
+ /// cref="T:System.Collections.ICollection"/>
+ /// is greater than the available space from <paramref name="index"/> to the end of the destination
+ /// <paramref name="array"/>.</exception>
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "ConcurrencyCop just doesn't know about these locks")]
+ void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
+ {
+ if (array == null) throw new ArgumentNullException("array");
+ if (index < 0) throw new ArgumentOutOfRangeException("index", GetResource("ConcurrentDictionary_IndexIsNegative"));
+
+ int locksAcquired = 0;
+ try
+ {
+ AcquireAllLocks(ref locksAcquired);
+
+ int count = 0;
+
+ for (int i = 0; i < m_tables.m_locks.Length && count >= 0; i++)
+ {
+ count += m_tables.m_countPerLock[i];
+ }
+
+ if (array.Length - count < index || count < 0) //"count" itself or "count + index" can overflow
+ {
+ throw new ArgumentException(GetResource("ConcurrentDictionary_ArrayNotLargeEnough"));
+ }
+
+ CopyToPairs(array, index);
+ }
+ finally
+ {
+ ReleaseLocks(0, locksAcquired);
+ }
+ }
+
+ /// <summary>
+ /// Copies the key and value pairs stored in the <see cref="ConcurrentDictionary{TKey,TValue}"/> to a
+ /// new array.
+ /// </summary>
+ /// <returns>A new array containing a snapshot of key and value pairs copied from the <see
+ /// cref="ConcurrentDictionary{TKey,TValue}"/>.</returns>
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "ConcurrencyCop just doesn't know about these locks")]
+ public KeyValuePair<TKey, TValue>[] ToArray()
+ {
+ int locksAcquired = 0;
+ try
+ {
+ AcquireAllLocks(ref locksAcquired);
+ int count = 0;
+ checked
+ {
+ for (int i = 0; i < m_tables.m_locks.Length; i++)
+ {
+ count += m_tables.m_countPerLock[i];
+ }
+ }
+
+ KeyValuePair<TKey, TValue>[] array = new KeyValuePair<TKey, TValue>[count];
+
+ CopyToPairs(array, 0);
+ return array;
+ }
+ finally
+ {
+ ReleaseLocks(0, locksAcquired);
+ }
+ }
+
+ /// <summary>
+ /// Copy dictionary contents to an array - shared implementation between ToArray and CopyTo.
+ ///
+ /// Important: the caller must hold all locks in m_locks before calling CopyToPairs.
+ /// </summary>
+ private void CopyToPairs(KeyValuePair<TKey, TValue>[] array, int index)
+ {
+ Node[] buckets = m_tables.m_buckets;
+ for (int i = 0; i < buckets.Length; i++)
+ {
+ for (Node current = buckets[i]; current != null; current = current.m_next)
+ {
+ array[index] = new KeyValuePair<TKey, TValue>(current.m_key, current.m_value);
+ index++; //this should never flow, CopyToPairs is only called when there's no overflow risk
+ }
+ }
+ }
+
+ /// <summary>
+ /// Copy dictionary contents to an array - shared implementation between ToArray and CopyTo.
+ ///
+ /// Important: the caller must hold all locks in m_locks before calling CopyToEntries.
+ /// </summary>
+ private void CopyToEntries(DictionaryEntry[] array, int index)
+ {
+ Node[] buckets = m_tables.m_buckets;
+ for (int i = 0; i < buckets.Length; i++)
+ {
+ for (Node current = buckets[i]; current != null; current = current.m_next)
+ {
+ array[index] = new DictionaryEntry(current.m_key, current.m_value);
+ index++; //this should never flow, CopyToEntries is only called when there's no overflow risk
+ }
+ }
+ }
+
+ /// <summary>
+ /// Copy dictionary contents to an array - shared implementation between ToArray and CopyTo.
+ ///
+ /// Important: the caller must hold all locks in m_locks before calling CopyToObjects.
+ /// </summary>
+ private void CopyToObjects(object[] array, int index)
+ {
+ Node[] buckets = m_tables.m_buckets;
+ for (int i = 0; i < buckets.Length; i++)
+ {
+ for (Node current = buckets[i]; current != null; current = current.m_next)
+ {
+ array[index] = new KeyValuePair<TKey, TValue>(current.m_key, current.m_value);
+ index++; //this should never flow, CopyToObjects is only called when there's no overflow risk
+ }
+ }
+ }
+
+ /// <summary>Returns an enumerator that iterates through the <see
+ /// cref="ConcurrentDictionary{TKey,TValue}"/>.</summary>
+ /// <returns>An enumerator for the <see cref="ConcurrentDictionary{TKey,TValue}"/>.</returns>
+ /// <remarks>
+ /// The enumerator returned from the dictionary is safe to use concurrently with
+ /// reads and writes to the dictionary, however it does not represent a moment-in-time snapshot
+ /// of the dictionary. The contents exposed through the enumerator may contain modifications
+ /// made to the dictionary after <see cref="GetEnumerator"/> was called.
+ /// </remarks>
+ public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
+ {
+ Node[] buckets = m_tables.m_buckets;
+
+ for (int i = 0; i < buckets.Length; i++)
+ {
+ // The Volatile.Read ensures that the load of the fields of 'current' doesn't move before the load from buckets[i].
+ Node current = Volatile.Read<Node>(ref buckets[i]);
+
+ while (current != null)
+ {
+ yield return new KeyValuePair<TKey, TValue>(current.m_key, current.m_value);
+ current = current.m_next;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Shared internal implementation for inserts and updates.
+ /// If key exists, we always return false; and if updateIfExists == true we force update with value;
+ /// If key doesn't exist, we always add value and return true;
+ /// </summary>
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
+ private bool TryAddInternal(TKey key, TValue value, bool updateIfExists, bool acquireLock, out TValue resultingValue)
+ {
+ while (true)
+ {
+ int bucketNo, lockNo;
+ int hashcode;
+
+ Tables tables = m_tables;
+ IEqualityComparer<TKey> comparer = tables.m_comparer;
+ hashcode = comparer.GetHashCode(key);
+ GetBucketAndLockNo(hashcode, out bucketNo, out lockNo, tables.m_buckets.Length, tables.m_locks.Length);
+
+ bool resizeDesired = false;
+ bool lockTaken = false;
+#if FEATURE_RANDOMIZED_STRING_HASHING
+#if !FEATURE_CORECLR
+ bool resizeDueToCollisions = false;
+#endif // !FEATURE_CORECLR
+#endif
+
+ try
+ {
+ if (acquireLock)
+ Monitor.Enter(tables.m_locks[lockNo], ref lockTaken);
+
+ // If the table just got resized, we may not be holding the right lock, and must retry.
+ // This should be a rare occurence.
+ if (tables != m_tables)
+ {
+ continue;
+ }
+
+#if FEATURE_RANDOMIZED_STRING_HASHING
+#if !FEATURE_CORECLR
+ int collisionCount = 0;
+#endif // !FEATURE_CORECLR
+#endif
+
+ // Try to find this key in the bucket
+ Node prev = null;
+ for (Node node = tables.m_buckets[bucketNo]; node != null; node = node.m_next)
+ {
+ Assert((prev == null && node == tables.m_buckets[bucketNo]) || prev.m_next == node);
+ if (comparer.Equals(node.m_key, key))
+ {
+ // The key was found in the dictionary. If updates are allowed, update the value for that key.
+ // We need to create a new node for the update, in order to support TValue types that cannot
+ // be written atomically, since lock-free reads may be happening concurrently.
+ if (updateIfExists)
+ {
+ if (s_isValueWriteAtomic)
+ {
+ node.m_value = value;
+ }
+ else
+ {
+ Node newNode = new Node(node.m_key, value, hashcode, node.m_next);
+ if (prev == null)
+ {
+ tables.m_buckets[bucketNo] = newNode;
+ }
+ else
+ {
+ prev.m_next = newNode;
+ }
+ }
+ resultingValue = value;
+ }
+ else
+ {
+ resultingValue = node.m_value;
+ }
+ return false;
+ }
+ prev = node;
+
+#if FEATURE_RANDOMIZED_STRING_HASHING
+#if !FEATURE_CORECLR
+ collisionCount++;
+#endif // !FEATURE_CORECLR
+#endif
+ }
+
+#if FEATURE_RANDOMIZED_STRING_HASHING
+#if !FEATURE_CORECLR
+ if(collisionCount > HashHelpers.HashCollisionThreshold && HashHelpers.IsWellKnownEqualityComparer(comparer))
+ {
+ resizeDesired = true;
+ resizeDueToCollisions = true;
+ }
+#endif // !FEATURE_CORECLR
+#endif
+
+ // The key was not found in the bucket. Insert the key-value pair.
+ Volatile.Write<Node>(ref tables.m_buckets[bucketNo], new Node(key, value, hashcode, tables.m_buckets[bucketNo]));
+ checked
+ {
+ tables.m_countPerLock[lockNo]++;
+ }
+
+ //
+ // If the number of elements guarded by this lock has exceeded the budget, resize the bucket table.
+ // It is also possible that GrowTable will increase the budget but won't resize the bucket table.
+ // That happens if the bucket table is found to be poorly utilized due to a bad hash function.
+ //
+ if (tables.m_countPerLock[lockNo] > m_budget)
+ {
+ resizeDesired = true;
+ }
+ }
+ finally
+ {
+ if (lockTaken)
+ Monitor.Exit(tables.m_locks[lockNo]);
+ }
+
+ //
+ // The fact that we got here means that we just performed an insertion. If necessary, we will grow the table.
+ //
+ // Concurrency notes:
+ // - Notice that we are not holding any locks at when calling GrowTable. This is necessary to prevent deadlocks.
+ // - As a result, it is possible that GrowTable will be called unnecessarily. But, GrowTable will obtain lock 0
+ // and then verify that the table we passed to it as the argument is still the current table.
+ //
+ if (resizeDesired)
+ {
+#if FEATURE_RANDOMIZED_STRING_HASHING
+#if !FEATURE_CORECLR
+ if (resizeDueToCollisions)
+ {
+ GrowTable(tables, (IEqualityComparer<TKey>)HashHelpers.GetRandomizedEqualityComparer(comparer), true, m_keyRehashCount);
+ }
+ else
+#endif // !FEATURE_CORECLR
+ {
+ GrowTable(tables, tables.m_comparer, false, m_keyRehashCount);
+ }
+#else
+ GrowTable(tables, tables.m_comparer, false, m_keyRehashCount);
+#endif
+ }
+
+ resultingValue = value;
+ return true;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the value associated with the specified key.
+ /// </summary>
+ /// <param name="key">The key of the value to get or set.</param>
+ /// <value>The value associated with the specified key. If the specified key is not found, a get
+ /// operation throws a
+ /// <see cref="T:Sytem.Collections.Generic.KeyNotFoundException"/>, and a set operation creates a new
+ /// element with the specified key.</value>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ /// <exception cref="T:System.Collections.Generic.KeyNotFoundException">The property is retrieved and
+ /// <paramref name="key"/>
+ /// does not exist in the collection.</exception>
+ public TValue this[TKey key]
+ {
+ get
+ {
+ TValue value;
+ if (!TryGetValue(key, out value))
+ {
+ throw new KeyNotFoundException();
+ }
+ return value;
+ }
+ set
+ {
+ if (key == null) throw new ArgumentNullException("key");
+ TValue dummy;
+ TryAddInternal(key, value, true, true, out dummy);
+ }
+ }
+
+ /// <summary>
+ /// Gets the number of key/value pairs contained in the <see
+ /// cref="ConcurrentDictionary{TKey,TValue}"/>.
+ /// </summary>
+ /// <exception cref="T:System.OverflowException">The dictionary contains too many
+ /// elements.</exception>
+ /// <value>The number of key/value paris contained in the <see
+ /// cref="ConcurrentDictionary{TKey,TValue}"/>.</value>
+ /// <remarks>Count has snapshot semantics and represents the number of items in the <see
+ /// cref="ConcurrentDictionary{TKey,TValue}"/>
+ /// at the moment when Count was accessed.</remarks>
+ public int Count
+ {
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "ConcurrencyCop just doesn't know about these locks")]
+ get
+ {
+ int count = 0;
+
+ int acquiredLocks = 0;
+ try
+ {
+ // Acquire all locks
+ AcquireAllLocks(ref acquiredLocks);
+
+ // Compute the count, we allow overflow
+ for (int i = 0; i < m_tables.m_countPerLock.Length; i++)
+ {
+ count += m_tables.m_countPerLock[i];
+ }
+
+ }
+ finally
+ {
+ // Release locks that have been acquired earlier
+ ReleaseLocks(0, acquiredLocks);
+ }
+
+ return count;
+ }
+ }
+
+ /// <summary>
+ /// Adds a key/value pair to the <see cref="ConcurrentDictionary{TKey,TValue}"/>
+ /// if the key does not already exist.
+ /// </summary>
+ /// <param name="key">The key of the element to add.</param>
+ /// <param name="valueFactory">The function used to generate a value for the key</param>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="valueFactory"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ /// <exception cref="T:System.OverflowException">The dictionary contains too many
+ /// elements.</exception>
+ /// <returns>The value for the key. This will be either the existing value for the key if the
+ /// key is already in the dictionary, or the new value for the key as returned by valueFactory
+ /// if the key was not in the dictionary.</returns>
+ public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)
+ {
+ if (key == null) throw new ArgumentNullException("key");
+ if (valueFactory == null) throw new ArgumentNullException("valueFactory");
+
+ TValue resultingValue;
+ if (TryGetValue(key, out resultingValue))
+ {
+ return resultingValue;
+ }
+ TryAddInternal(key, valueFactory(key), false, true, out resultingValue);
+ return resultingValue;
+ }
+
+ /// <summary>
+ /// Adds a key/value pair to the <see cref="ConcurrentDictionary{TKey,TValue}"/>
+ /// if the key does not already exist.
+ /// </summary>
+ /// <param name="key">The key of the element to add.</param>
+ /// <param name="value">the value to be added, if the key does not already exist</param>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ /// <exception cref="T:System.OverflowException">The dictionary contains too many
+ /// elements.</exception>
+ /// <returns>The value for the key. This will be either the existing value for the key if the
+ /// key is already in the dictionary, or the new value if the key was not in the dictionary.</returns>
+ public TValue GetOrAdd(TKey key, TValue value)
+ {
+ if (key == null) throw new ArgumentNullException("key");
+
+ TValue resultingValue;
+ TryAddInternal(key, value, false, true, out resultingValue);
+ return resultingValue;
+ }
+
+ /// <summary>
+ /// Adds a key/value pair to the <see cref="ConcurrentDictionary{TKey,TValue}"/> if the key does not already
+ /// exist, or updates a key/value pair in the <see cref="ConcurrentDictionary{TKey,TValue}"/> if the key
+ /// already exists.
+ /// </summary>
+ /// <param name="key">The key to be added or whose value should be updated</param>
+ /// <param name="addValueFactory">The function used to generate a value for an absent key</param>
+ /// <param name="updateValueFactory">The function used to generate a new value for an existing key
+ /// based on the key's existing value</param>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="addValueFactory"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="updateValueFactory"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ /// <exception cref="T:System.OverflowException">The dictionary contains too many
+ /// elements.</exception>
+ /// <returns>The new value for the key. This will be either be the result of addValueFactory (if the key was
+ /// absent) or the result of updateValueFactory (if the key was present).</returns>
+ public TValue AddOrUpdate(TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory)
+ {
+ if (key == null) throw new ArgumentNullException("key");
+ if (addValueFactory == null) throw new ArgumentNullException("addValueFactory");
+ if (updateValueFactory == null) throw new ArgumentNullException("updateValueFactory");
+
+ TValue newValue, resultingValue;
+ while (true)
+ {
+ TValue oldValue;
+ if (TryGetValue(key, out oldValue))
+ //key exists, try to update
+ {
+ newValue = updateValueFactory(key, oldValue);
+ if (TryUpdate(key, newValue, oldValue))
+ {
+ return newValue;
+ }
+ }
+ else //try add
+ {
+ newValue = addValueFactory(key);
+ if (TryAddInternal(key, newValue, false, true, out resultingValue))
+ {
+ return resultingValue;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Adds a key/value pair to the <see cref="ConcurrentDictionary{TKey,TValue}"/> if the key does not already
+ /// exist, or updates a key/value pair in the <see cref="ConcurrentDictionary{TKey,TValue}"/> if the key
+ /// already exists.
+ /// </summary>
+ /// <param name="key">The key to be added or whose value should be updated</param>
+ /// <param name="addValue">The value to be added for an absent key</param>
+ /// <param name="updateValueFactory">The function used to generate a new value for an existing key based on
+ /// the key's existing value</param>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="updateValueFactory"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ /// <exception cref="T:System.OverflowException">The dictionary contains too many
+ /// elements.</exception>
+ /// <returns>The new value for the key. This will be either be the result of addValueFactory (if the key was
+ /// absent) or the result of updateValueFactory (if the key was present).</returns>
+ public TValue AddOrUpdate(TKey key, TValue addValue, Func<TKey, TValue, TValue> updateValueFactory)
+ {
+ if (key == null) throw new ArgumentNullException("key");
+ if (updateValueFactory == null) throw new ArgumentNullException("updateValueFactory");
+ TValue newValue, resultingValue;
+ while (true)
+ {
+ TValue oldValue;
+ if (TryGetValue(key, out oldValue))
+ //key exists, try to update
+ {
+ newValue = updateValueFactory(key, oldValue);
+ if (TryUpdate(key, newValue, oldValue))
+ {
+ return newValue;
+ }
+ }
+ else //try add
+ {
+ if (TryAddInternal(key, addValue, false, true, out resultingValue))
+ {
+ return resultingValue;
+ }
+ }
+ }
+ }
+
+
+
+ /// <summary>
+ /// Gets a value that indicates whether the <see cref="ConcurrentDictionary{TKey,TValue}"/> is empty.
+ /// </summary>
+ /// <value>true if the <see cref="ConcurrentDictionary{TKey,TValue}"/> is empty; otherwise,
+ /// false.</value>
+ public bool IsEmpty
+ {
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "ConcurrencyCop just doesn't know about these locks")]
+ get
+ {
+ int acquiredLocks = 0;
+ try
+ {
+ // Acquire all locks
+ AcquireAllLocks(ref acquiredLocks);
+
+ for (int i = 0; i < m_tables.m_countPerLock.Length; i++)
+ {
+ if (m_tables.m_countPerLock[i] != 0)
+ {
+ return false;
+ }
+ }
+ }
+ finally
+ {
+ // Release locks that have been acquired earlier
+ ReleaseLocks(0, acquiredLocks);
+ }
+
+ return true;
+ }
+ }
+
+ #region IDictionary<TKey,TValue> members
+
+ /// <summary>
+ /// Adds the specified key and value to the <see
+ /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>.
+ /// </summary>
+ /// <param name="key">The object to use as the key of the element to add.</param>
+ /// <param name="value">The object to use as the value of the element to add.</param>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ /// <exception cref="T:System.OverflowException">The dictionary contains too many
+ /// elements.</exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// An element with the same key already exists in the <see
+ /// cref="ConcurrentDictionary{TKey,TValue}"/>.</exception>
+ void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
+ {
+ if (!TryAdd(key, value))
+ {
+ throw new ArgumentException(GetResource("ConcurrentDictionary_KeyAlreadyExisted"));
+ }
+ }
+
+ /// <summary>
+ /// Removes the element with the specified key from the <see
+ /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>.
+ /// </summary>
+ /// <param name="key">The key of the element to remove.</param>
+ /// <returns>true if the element is successfully remove; otherwise false. This method also returns
+ /// false if
+ /// <paramref name="key"/> was not found in the original <see
+ /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>.
+ /// </returns>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ bool IDictionary<TKey, TValue>.Remove(TKey key)
+ {
+ TValue throwAwayValue;
+ return TryRemove(key, out throwAwayValue);
+ }
+
+ /// <summary>
+ /// Gets a collection containing the keys in the <see
+ /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.
+ /// </summary>
+ /// <value>An <see cref="T:System.Collections.Generic.ICollection{TKey}"/> containing the keys in the
+ /// <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.</value>
+ public ICollection<TKey> Keys
+ {
+ get { return GetKeys(); }
+ }
+
+ /// <summary>
+ /// Gets an <see cref="T:System.Collections.Generic.IEnumerable{TKey}"/> containing the keys of
+ /// the <see cref="T:System.Collections.Generic.IReadOnlyDictionary{TKey,TValue}"/>.
+ /// </summary>
+ /// <value>An <see cref="T:System.Collections.Generic.IEnumerable{TKey}"/> containing the keys of
+ /// the <see cref="T:System.Collections.Generic.IReadOnlyDictionary{TKey,TValue}"/>.</value>
+ IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys
+ {
+ get { return GetKeys(); }
+ }
+
+ /// <summary>
+ /// Gets a collection containing the values in the <see
+ /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.
+ /// </summary>
+ /// <value>An <see cref="T:System.Collections.Generic.ICollection{TValue}"/> containing the values in
+ /// the
+ /// <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.</value>
+ public ICollection<TValue> Values
+ {
+ get { return GetValues(); }
+ }
+
+ /// <summary>
+ /// Gets an <see cref="T:System.Collections.Generic.IEnumerable{TValue}"/> containing the values
+ /// in the <see cref="T:System.Collections.Generic.IReadOnlyDictionary{TKey,TValue}"/>.
+ /// </summary>
+ /// <value>An <see cref="T:System.Collections.Generic.IEnumerable{TValue}"/> containing the
+ /// values in the <see cref="T:System.Collections.Generic.IReadOnlyDictionary{TKey,TValue}"/>.</value>
+ IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values
+ {
+ get { return GetValues(); }
+ }
+ #endregion
+
+ #region ICollection<KeyValuePair<TKey,TValue>> Members
+
+ /// <summary>
+ /// Adds the specified value to the <see cref="T:System.Collections.Generic.ICollection{TValue}"/>
+ /// with the specified key.
+ /// </summary>
+ /// <param name="keyValuePair">The <see cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/>
+ /// structure representing the key and value to add to the <see
+ /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.</param>
+ /// <exception cref="T:System.ArgumentNullException">The <paramref name="keyValuePair"/> of <paramref
+ /// name="keyValuePair"/> is null.</exception>
+ /// <exception cref="T:System.OverflowException">The <see
+ /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>
+ /// contains too many elements.</exception>
+ /// <exception cref="T:System.ArgumentException">An element with the same key already exists in the
+ /// <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/></exception>
+ void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> keyValuePair)
+ {
+ ((IDictionary<TKey, TValue>)this).Add(keyValuePair.Key, keyValuePair.Value);
+ }
+
+ /// <summary>
+ /// Determines whether the <see cref="T:System.Collections.Generic.ICollection{TKey,TValue}"/>
+ /// contains a specific key and value.
+ /// </summary>
+ /// <param name="keyValuePair">The <see cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/>
+ /// structure to locate in the <see
+ /// cref="T:System.Collections.Generic.ICollection{TValue}"/>.</param>
+ /// <returns>true if the <paramref name="keyValuePair"/> is found in the <see
+ /// cref="T:System.Collections.Generic.ICollection{TKey,TValue}"/>; otherwise, false.</returns>
+ bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> keyValuePair)
+ {
+ TValue value;
+ if (!TryGetValue(keyValuePair.Key, out value))
+ {
+ return false;
+ }
+ return EqualityComparer<TValue>.Default.Equals(value, keyValuePair.Value);
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the dictionary is read-only.
+ /// </summary>
+ /// <value>true if the <see cref="T:System.Collections.Generic.ICollection{TKey,TValue}"/> is
+ /// read-only; otherwise, false. For <see
+ /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>, this property always returns
+ /// false.</value>
+ bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
+ {
+ get { return false; }
+ }
+
+ /// <summary>
+ /// Removes a key and value from the dictionary.
+ /// </summary>
+ /// <param name="keyValuePair">The <see
+ /// cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/>
+ /// structure representing the key and value to remove from the <see
+ /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.</param>
+ /// <returns>true if the key and value represented by <paramref name="keyValuePair"/> is successfully
+ /// found and removed; otherwise, false.</returns>
+ /// <exception cref="T:System.ArgumentNullException">The Key property of <paramref
+ /// name="keyValuePair"/> is a null reference (Nothing in Visual Basic).</exception>
+ bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> keyValuePair)
+ {
+ if (keyValuePair.Key == null) throw new ArgumentNullException(GetResource("ConcurrentDictionary_ItemKeyIsNull"));
+
+ TValue throwAwayValue;
+ return TryRemoveInternal(keyValuePair.Key, out throwAwayValue, true, keyValuePair.Value);
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ /// <summary>Returns an enumerator that iterates through the <see
+ /// cref="ConcurrentDictionary{TKey,TValue}"/>.</summary>
+ /// <returns>An enumerator for the <see cref="ConcurrentDictionary{TKey,TValue}"/>.</returns>
+ /// <remarks>
+ /// The enumerator returned from the dictionary is safe to use concurrently with
+ /// reads and writes to the dictionary, however it does not represent a moment-in-time snapshot
+ /// of the dictionary. The contents exposed through the enumerator may contain modifications
+ /// made to the dictionary after <see cref="GetEnumerator"/> was called.
+ /// </remarks>
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return ((ConcurrentDictionary<TKey, TValue>)this).GetEnumerator();
+ }
+
+ #endregion
+
+ #region IDictionary Members
+
+ /// <summary>
+ /// Adds the specified key and value to the dictionary.
+ /// </summary>
+ /// <param name="key">The object to use as the key.</param>
+ /// <param name="value">The object to use as the value.</param>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ /// <exception cref="T:System.OverflowException">The dictionary contains too many
+ /// elements.</exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// <paramref name="key"/> is of a type that is not assignable to the key type <typeparamref
+ /// name="TKey"/> of the <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>. -or-
+ /// <paramref name="value"/> is of a type that is not assignable to <typeparamref name="TValue"/>,
+ /// the type of values in the <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.
+ /// -or- A value with the same key already exists in the <see
+ /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.
+ /// </exception>
+ void IDictionary.Add(object key, object value)
+ {
+ if (key == null) throw new ArgumentNullException("key");
+ if (!(key is TKey)) throw new ArgumentException(GetResource("ConcurrentDictionary_TypeOfKeyIncorrect"));
+
+ TValue typedValue;
+ try
+ {
+ typedValue = (TValue)value;
+ }
+ catch (InvalidCastException)
+ {
+ throw new ArgumentException(GetResource("ConcurrentDictionary_TypeOfValueIncorrect"));
+ }
+
+ ((IDictionary<TKey, TValue>)this).Add((TKey)key, typedValue);
+ }
+
+ /// <summary>
+ /// Gets whether the <see cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> contains an
+ /// element with the specified key.
+ /// </summary>
+ /// <param name="key">The key to locate in the <see
+ /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>.</param>
+ /// <returns>true if the <see cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> contains
+ /// an element with the specified key; otherwise, false.</returns>
+ /// <exception cref="T:System.ArgumentNullException"> <paramref name="key"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ bool IDictionary.Contains(object key)
+ {
+ if (key == null) throw new ArgumentNullException("key");
+
+ return (key is TKey) && ((ConcurrentDictionary<TKey, TValue>)this).ContainsKey((TKey)key);
+ }
+
+ /// <summary>Provides an <see cref="T:System.Collections.Generics.IDictionaryEnumerator"/> for the
+ /// <see cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>.</summary>
+ /// <returns>An <see cref="T:System.Collections.Generics.IDictionaryEnumerator"/> for the <see
+ /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>.</returns>
+ IDictionaryEnumerator IDictionary.GetEnumerator()
+ {
+ return new DictionaryEnumerator(this);
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the <see
+ /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> has a fixed size.
+ /// </summary>
+ /// <value>true if the <see cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> has a
+ /// fixed size; otherwise, false. For <see
+ /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/>, this property always
+ /// returns false.</value>
+ bool IDictionary.IsFixedSize
+ {
+ get { return false; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the <see
+ /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> is read-only.
+ /// </summary>
+ /// <value>true if the <see cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> is
+ /// read-only; otherwise, false. For <see
+ /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/>, this property always
+ /// returns false.</value>
+ bool IDictionary.IsReadOnly
+ {
+ get { return false; }
+ }
+
+ /// <summary>
+ /// Gets an <see cref="T:System.Collections.ICollection"/> containing the keys of the <see
+ /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>.
+ /// </summary>
+ /// <value>An <see cref="T:System.Collections.ICollection"/> containing the keys of the <see
+ /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>.</value>
+ ICollection IDictionary.Keys
+ {
+ get { return GetKeys(); }
+ }
+
+ /// <summary>
+ /// Removes the element with the specified key from the <see
+ /// cref="T:System.Collections.IDictionary"/>.
+ /// </summary>
+ /// <param name="key">The key of the element to remove.</param>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ void IDictionary.Remove(object key)
+ {
+ if (key == null) throw new ArgumentNullException("key");
+
+ TValue throwAwayValue;
+ if (key is TKey)
+ {
+ this.TryRemove((TKey)key, out throwAwayValue);
+ }
+ }
+
+ /// <summary>
+ /// Gets an <see cref="T:System.Collections.ICollection"/> containing the values in the <see
+ /// cref="T:System.Collections.IDictionary"/>.
+ /// </summary>
+ /// <value>An <see cref="T:System.Collections.ICollection"/> containing the values in the <see
+ /// cref="T:System.Collections.IDictionary"/>.</value>
+ ICollection IDictionary.Values
+ {
+ get { return GetValues(); }
+ }
+
+ /// <summary>
+ /// Gets or sets the value associated with the specified key.
+ /// </summary>
+ /// <param name="key">The key of the value to get or set.</param>
+ /// <value>The value associated with the specified key, or a null reference (Nothing in Visual Basic)
+ /// if <paramref name="key"/> is not in the dictionary or <paramref name="key"/> is of a type that is
+ /// not assignable to the key type <typeparamref name="TKey"/> of the <see
+ /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/>.</value>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ /// <exception cref="T:System.ArgumentException">
+ /// A value is being assigned, and <paramref name="key"/> is of a type that is not assignable to the
+ /// key type <typeparamref name="TKey"/> of the <see
+ /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/>. -or- A value is being
+ /// assigned, and <paramref name="key"/> is of a type that is not assignable to the value type
+ /// <typeparamref name="TValue"/> of the <see
+ /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/>
+ /// </exception>
+ object IDictionary.this[object key]
+ {
+ get
+ {
+ if (key == null) throw new ArgumentNullException("key");
+
+ TValue value;
+ if (key is TKey && this.TryGetValue((TKey)key, out value))
+ {
+ return value;
+ }
+
+ return null;
+ }
+ set
+ {
+ if (key == null) throw new ArgumentNullException("key");
+
+ if (!(key is TKey)) throw new ArgumentException(GetResource("ConcurrentDictionary_TypeOfKeyIncorrect"));
+ if (!(value is TValue)) throw new ArgumentException(GetResource("ConcurrentDictionary_TypeOfValueIncorrect"));
+
+ ((ConcurrentDictionary<TKey, TValue>)this)[(TKey)key] = (TValue)value;
+ }
+ }
+
+ #endregion
+
+ #region ICollection Members
+
+ /// <summary>
+ /// Copies the elements of the <see cref="T:System.Collections.ICollection"/> to an array, starting
+ /// at the specified array index.
+ /// </summary>
+ /// <param name="array">The one-dimensional array that is the destination of the elements copied from
+ /// the <see cref="T:System.Collections.ICollection"/>. The array must have zero-based
+ /// indexing.</param>
+ /// <param name="index">The zero-based index in <paramref name="array"/> at which copying
+ /// begins.</param>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="array"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="index"/> is less than
+ /// 0.</exception>
+ /// <exception cref="T:System.ArgumentException"><paramref name="index"/> is equal to or greater than
+ /// the length of the <paramref name="array"/>. -or- The number of elements in the source <see
+ /// cref="T:System.Collections.ICollection"/>
+ /// is greater than the available space from <paramref name="index"/> to the end of the destination
+ /// <paramref name="array"/>.</exception>
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "ConcurrencyCop just doesn't know about these locks")]
+ void ICollection.CopyTo(Array array, int index)
+ {
+ if (array == null) throw new ArgumentNullException("array");
+ if (index < 0) throw new ArgumentOutOfRangeException("index", GetResource("ConcurrentDictionary_IndexIsNegative"));
+
+ int locksAcquired = 0;
+ try
+ {
+ AcquireAllLocks(ref locksAcquired);
+ Tables tables = m_tables;
+
+ int count = 0;
+
+ for (int i = 0; i < tables.m_locks.Length && count >= 0; i++)
+ {
+ count += tables.m_countPerLock[i];
+ }
+
+ if (array.Length - count < index || count < 0) //"count" itself or "count + index" can overflow
+ {
+ throw new ArgumentException(GetResource("ConcurrentDictionary_ArrayNotLargeEnough"));
+ }
+
+ // To be consistent with the behavior of ICollection.CopyTo() in Dictionary<TKey,TValue>,
+ // we recognize three types of target arrays:
+ // - an array of KeyValuePair<TKey, TValue> structs
+ // - an array of DictionaryEntry structs
+ // - an array of objects
+
+ KeyValuePair<TKey, TValue>[] pairs = array as KeyValuePair<TKey, TValue>[];
+ if (pairs != null)
+ {
+ CopyToPairs(pairs, index);
+ return;
+ }
+
+ DictionaryEntry[] entries = array as DictionaryEntry[];
+ if (entries != null)
+ {
+ CopyToEntries(entries, index);
+ return;
+ }
+
+ object[] objects = array as object[];
+ if (objects != null)
+ {
+ CopyToObjects(objects, index);
+ return;
+ }
+
+ throw new ArgumentException(GetResource("ConcurrentDictionary_ArrayIncorrectType"), "array");
+ }
+ finally
+ {
+ ReleaseLocks(0, locksAcquired);
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether access to the <see cref="T:System.Collections.ICollection"/> is
+ /// synchronized with the SyncRoot.
+ /// </summary>
+ /// <value>true if access to the <see cref="T:System.Collections.ICollection"/> is synchronized
+ /// (thread safe); otherwise, false. For <see
+ /// cref="T:System.Collections.Concurrent.ConcurrentDictionary{TKey,TValue}"/>, this property always
+ /// returns false.</value>
+ bool ICollection.IsSynchronized
+ {
+ get { return false; }
+ }
+
+ /// <summary>
+ /// Gets an object that can be used to synchronize access to the <see
+ /// cref="T:System.Collections.ICollection"/>. This property is not supported.
+ /// </summary>
+ /// <exception cref="T:System.NotSupportedException">The SyncRoot property is not supported.</exception>
+ object ICollection.SyncRoot
+ {
+ get
+ {
+ throw new NotSupportedException(Environment.GetResourceString("ConcurrentCollection_SyncRoot_NotSupported"));
+ }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Replaces the bucket table with a larger one. To prevent multiple threads from resizing the
+ /// table as a result of a race condition, the Tables instance that holds the table of buckets deemed too
+ /// small is passed in as an argument to GrowTable(). GrowTable() obtains a lock, and then checks
+ /// the Tables instance has been replaced in the meantime or not.
+ /// The <paramref name="rehashCount"/> will be used to ensure that we don't do two subsequent resizes
+ /// because of a collision
+ /// </summary>
+ private void GrowTable(Tables tables, IEqualityComparer<TKey> newComparer, bool regenerateHashKeys, int rehashCount)
+ {
+ int locksAcquired = 0;
+ try
+ {
+ // The thread that first obtains m_locks[0] will be the one doing the resize operation
+ AcquireLocks(0, 1, ref locksAcquired);
+
+ if (regenerateHashKeys && rehashCount == m_keyRehashCount)
+ {
+ // This method is called with regenerateHashKeys==true when we detected
+ // more than HashHelpers.HashCollisionThreshold collisions when adding a new element.
+ // In that case we are in the process of switching to another (randomized) comparer
+ // and we have to re-hash all the keys in the table.
+ // We are only going to do this if we did not just rehash the entire table while waiting for the lock
+ tables = m_tables;
+ }
+ else
+ {
+ // If we don't require a regeneration of hash keys we want to make sure we don't do work when
+ // we don't have to
+ if (tables != m_tables)
+ {
+ // We assume that since the table reference is different, it was already resized (or the budget
+ // was adjusted). If we ever decide to do table shrinking, or replace the table for other reasons,
+ // we will have to revisit this logic.
+ return;
+ }
+
+ // Compute the (approx.) total size. Use an Int64 accumulation variable to avoid an overflow.
+ long approxCount = 0;
+ for (int i = 0; i < tables.m_countPerLock.Length; i++)
+ {
+ approxCount += tables.m_countPerLock[i];
+ }
+
+ //
+ // If the bucket array is too empty, double the budget instead of resizing the table
+ //
+ if (approxCount < tables.m_buckets.Length / 4)
+ {
+ m_budget = 2 * m_budget;
+ if (m_budget < 0)
+ {
+ m_budget = int.MaxValue;
+ }
+
+ return;
+ }
+ }
+ // Compute the new table size. We find the smallest integer larger than twice the previous table size, and not divisible by
+ // 2,3,5 or 7. We can consider a different table-sizing policy in the future.
+ int newLength = 0;
+ bool maximizeTableSize = false;
+ try
+ {
+ checked
+ {
+ // Double the size of the buckets table and add one, so that we have an odd integer.
+ newLength = tables.m_buckets.Length * 2 + 1;
+
+ // Now, we only need to check odd integers, and find the first that is not divisible
+ // by 3, 5 or 7.
+ while (newLength % 3 == 0 || newLength % 5 == 0 || newLength % 7 == 0)
+ {
+ newLength += 2;
+ }
+
+ Assert(newLength % 2 != 0);
+
+ if (newLength > Array.MaxArrayLength)
+ {
+ maximizeTableSize = true;
+ }
+ }
+ }
+ catch (OverflowException)
+ {
+ maximizeTableSize = true;
+ }
+
+ if (maximizeTableSize)
+ {
+ newLength = Array.MaxArrayLength;
+
+ // We want to make sure that GrowTable will not be called again, since table is at the maximum size.
+ // To achieve that, we set the budget to int.MaxValue.
+ //
+ // (There is one special case that would allow GrowTable() to be called in the future:
+ // calling Clear() on the ConcurrentDictionary will shrink the table and lower the budget.)
+ m_budget = int.MaxValue;
+ }
+
+ // Now acquire all other locks for the table
+ AcquireLocks(1, tables.m_locks.Length, ref locksAcquired);
+
+ object[] newLocks = tables.m_locks;
+
+ // Add more locks
+ if (m_growLockArray && tables.m_locks.Length < MAX_LOCK_NUMBER)
+ {
+ newLocks = new object[tables.m_locks.Length * 2];
+ Array.Copy(tables.m_locks, newLocks, tables.m_locks.Length);
+
+ for (int i = tables.m_locks.Length; i < newLocks.Length; i++)
+ {
+ newLocks[i] = new object();
+ }
+ }
+
+ Node[] newBuckets = new Node[newLength];
+ int[] newCountPerLock = new int[newLocks.Length];
+
+ // Copy all data into a new table, creating new nodes for all elements
+ for (int i = 0; i < tables.m_buckets.Length; i++)
+ {
+ Node current = tables.m_buckets[i];
+ while (current != null)
+ {
+ Node next = current.m_next;
+ int newBucketNo, newLockNo;
+ int nodeHashCode = current.m_hashcode;
+
+ if (regenerateHashKeys)
+ {
+ // Recompute the hash from the key
+ nodeHashCode = newComparer.GetHashCode(current.m_key);
+ }
+
+ GetBucketAndLockNo(nodeHashCode, out newBucketNo, out newLockNo, newBuckets.Length, newLocks.Length);
+
+ newBuckets[newBucketNo] = new Node(current.m_key, current.m_value, nodeHashCode, newBuckets[newBucketNo]);
+
+ checked
+ {
+ newCountPerLock[newLockNo]++;
+ }
+
+ current = next;
+ }
+ }
+
+ // If this resize regenerated the hashkeys, increment the count
+ if (regenerateHashKeys)
+ {
+ // We use unchecked here because we don't want to throw an exception if
+ // an overflow happens
+ unchecked
+ {
+ m_keyRehashCount++;
+ }
+ }
+
+ // Adjust the budget
+ m_budget = Math.Max(1, newBuckets.Length / newLocks.Length);
+
+ // Replace tables with the new versions
+ m_tables = new Tables(newBuckets, newLocks, newCountPerLock, newComparer);
+ }
+ finally
+ {
+ // Release all locks that we took earlier
+ ReleaseLocks(0, locksAcquired);
+ }
+ }
+
+ /// <summary>
+ /// Computes the bucket and lock number for a particular key.
+ /// </summary>
+ private void GetBucketAndLockNo(
+ int hashcode, out int bucketNo, out int lockNo, int bucketCount, int lockCount)
+ {
+ bucketNo = (hashcode & 0x7fffffff) % bucketCount;
+ lockNo = bucketNo % lockCount;
+
+ Assert(bucketNo >= 0 && bucketNo < bucketCount);
+ Assert(lockNo >= 0 && lockNo < lockCount);
+ }
+
+ /// <summary>
+ /// The number of concurrent writes for which to optimize by default.
+ /// </summary>
+ private static int DefaultConcurrencyLevel
+ {
+
+ get { return DEFAULT_CONCURRENCY_MULTIPLIER * PlatformHelper.ProcessorCount; }
+ }
+
+ /// <summary>
+ /// Acquires all locks for this hash table, and increments locksAcquired by the number
+ /// of locks that were successfully acquired. The locks are acquired in an increasing
+ /// order.
+ /// </summary>
+ private void AcquireAllLocks(ref int locksAcquired)
+ {
+#if !FEATURE_CORECLR
+ if (CDSCollectionETWBCLProvider.Log.IsEnabled())
+ {
+ CDSCollectionETWBCLProvider.Log.ConcurrentDictionary_AcquiringAllLocks(m_tables.m_buckets.Length);
+ }
+#endif //!FEATURE_CORECLR
+
+ // First, acquire lock 0
+ AcquireLocks(0, 1, ref locksAcquired);
+
+ // Now that we have lock 0, the m_locks array will not change (i.e., grow),
+ // and so we can safely read m_locks.Length.
+ AcquireLocks(1, m_tables.m_locks.Length, ref locksAcquired);
+ Assert(locksAcquired == m_tables.m_locks.Length);
+ }
+
+ /// <summary>
+ /// Acquires a contiguous range of locks for this hash table, and increments locksAcquired
+ /// by the number of locks that were successfully acquired. The locks are acquired in an
+ /// increasing order.
+ /// </summary>
+ private void AcquireLocks(int fromInclusive, int toExclusive, ref int locksAcquired)
+ {
+ Assert(fromInclusive <= toExclusive);
+ object[] locks = m_tables.m_locks;
+
+ for (int i = fromInclusive; i < toExclusive; i++)
+ {
+ bool lockTaken = false;
+ try
+ {
+ Monitor.Enter(locks[i], ref lockTaken);
+ }
+ finally
+ {
+ if (lockTaken)
+ {
+ locksAcquired++;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Releases a contiguous range of locks.
+ /// </summary>
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
+ private void ReleaseLocks(int fromInclusive, int toExclusive)
+ {
+ Assert(fromInclusive <= toExclusive);
+
+ for (int i = fromInclusive; i < toExclusive; i++)
+ {
+ Monitor.Exit(m_tables.m_locks[i]);
+ }
+ }
+
+ /// <summary>
+ /// Gets a collection containing the keys in the dictionary.
+ /// </summary>
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "ConcurrencyCop just doesn't know about these locks")]
+ private ReadOnlyCollection<TKey> GetKeys()
+ {
+ int locksAcquired = 0;
+ try
+ {
+ AcquireAllLocks(ref locksAcquired);
+ List<TKey> keys = new List<TKey>();
+
+ for (int i = 0; i < m_tables.m_buckets.Length; i++)
+ {
+ Node current = m_tables.m_buckets[i];
+ while (current != null)
+ {
+ keys.Add(current.m_key);
+ current = current.m_next;
+ }
+ }
+
+ return new ReadOnlyCollection<TKey>(keys);
+ }
+ finally
+ {
+ ReleaseLocks(0, locksAcquired);
+ }
+ }
+
+ /// <summary>
+ /// Gets a collection containing the values in the dictionary.
+ /// </summary>
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "ConcurrencyCop just doesn't know about these locks")]
+ private ReadOnlyCollection<TValue> GetValues()
+ {
+ int locksAcquired = 0;
+ try
+ {
+ AcquireAllLocks(ref locksAcquired);
+ List<TValue> values = new List<TValue>();
+
+ for (int i = 0; i < m_tables.m_buckets.Length; i++)
+ {
+ Node current = m_tables.m_buckets[i];
+ while (current != null)
+ {
+ values.Add(current.m_value);
+ current = current.m_next;
+ }
+ }
+
+ return new ReadOnlyCollection<TValue>(values);
+ }
+ finally
+ {
+ ReleaseLocks(0, locksAcquired);
+ }
+ }
+
+ /// <summary>
+ /// A helper method for asserts.
+ /// </summary>
+ [Conditional("DEBUG")]
+ private void Assert(bool condition)
+ {
+ Contract.Assert(condition);
+ }
+
+ /// <summary>
+ /// A helper function to obtain the string for a particular resource key.
+ /// </summary>
+ /// <param name="key"></param>
+ /// <returns></returns>
+ private string GetResource(string key)
+ {
+ Assert(key != null);
+
+ return Environment.GetResourceString(key);
+ }
+
+ /// <summary>
+ /// A node in a singly-linked list representing a particular hash table bucket.
+ /// </summary>
+ private class Node
+ {
+ internal TKey m_key;
+ internal TValue m_value;
+ internal volatile Node m_next;
+ internal int m_hashcode;
+
+ internal Node(TKey key, TValue value, int hashcode, Node next)
+ {
+ m_key = key;
+ m_value = value;
+ m_next = next;
+ m_hashcode = hashcode;
+ }
+ }
+
+ /// <summary>
+ /// A private class to represent enumeration over the dictionary that implements the
+ /// IDictionaryEnumerator interface.
+ /// </summary>
+ private class DictionaryEnumerator : IDictionaryEnumerator
+ {
+ IEnumerator<KeyValuePair<TKey, TValue>> m_enumerator; // Enumerator over the dictionary.
+
+ internal DictionaryEnumerator(ConcurrentDictionary<TKey, TValue> dictionary)
+ {
+ m_enumerator = dictionary.GetEnumerator();
+ }
+
+ public DictionaryEntry Entry
+ {
+ get { return new DictionaryEntry(m_enumerator.Current.Key, m_enumerator.Current.Value); }
+ }
+
+ public object Key
+ {
+ get { return m_enumerator.Current.Key; }
+ }
+
+ public object Value
+ {
+ get { return m_enumerator.Current.Value; }
+ }
+
+ public object Current
+ {
+ get { return this.Entry; }
+ }
+
+ public bool MoveNext()
+ {
+ return m_enumerator.MoveNext();
+ }
+
+ public void Reset()
+ {
+ m_enumerator.Reset();
+ }
+ }
+
+#if !FEATURE_CORECLR
+ /// <summary>
+ /// Get the data array to be serialized
+ /// </summary>
+ [OnSerializing]
+ private void OnSerializing(StreamingContext context)
+ {
+ Tables tables = m_tables;
+
+ // save the data into the serialization array to be saved
+ m_serializationArray = ToArray();
+ m_serializationConcurrencyLevel = tables.m_locks.Length;
+ m_serializationCapacity = tables.m_buckets.Length;
+ m_comparer = (IEqualityComparer<TKey>)HashHelpers.GetEqualityComparerForSerialization(tables.m_comparer);
+ }
+
+ /// <summary>
+ /// Construct the dictionary from a previously serialized one
+ /// </summary>
+ [OnDeserialized]
+ private void OnDeserialized(StreamingContext context)
+ {
+ KeyValuePair<TKey, TValue>[] array = m_serializationArray;
+
+ var buckets = new Node[m_serializationCapacity];
+ var countPerLock = new int[m_serializationConcurrencyLevel];
+
+ var locks = new object[m_serializationConcurrencyLevel];
+ for (int i = 0; i < locks.Length; i++)
+ {
+ locks[i] = new object();
+ }
+
+ m_tables = new Tables(buckets, locks, countPerLock, m_comparer);
+
+ InitializeFromCollection(array);
+ m_serializationArray = null;
+
+ }
+#endif
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Concurrent/ConcurrentQueue.cs b/src/mscorlib/src/System/Collections/Concurrent/ConcurrentQueue.cs
new file mode 100644
index 0000000000..9164eadad1
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Concurrent/ConcurrentQueue.cs
@@ -0,0 +1,960 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+#pragma warning disable 0420
+
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// A lock-free, concurrent queue primitive, and its associated debugger view type.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Runtime.ConstrainedExecution;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+using System.Security;
+using System.Security.Permissions;
+using System.Threading;
+
+namespace System.Collections.Concurrent
+{
+
+ /// <summary>
+ /// Represents a thread-safe first-in, first-out collection of objects.
+ /// </summary>
+ /// <typeparam name="T">Specifies the type of elements in the queue.</typeparam>
+ /// <remarks>
+ /// All public and protected members of <see cref="ConcurrentQueue{T}"/> are thread-safe and may be used
+ /// concurrently from multiple threads.
+ /// </remarks>
+ [ComVisible(false)]
+ [DebuggerDisplay("Count = {Count}")]
+ [DebuggerTypeProxy(typeof(SystemCollectionsConcurrent_ProducerConsumerCollectionDebugView<>))]
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ [Serializable]
+ public class ConcurrentQueue<T> : IProducerConsumerCollection<T>, IReadOnlyCollection<T>
+ {
+ //fields of ConcurrentQueue
+ [NonSerialized]
+ private volatile Segment m_head;
+
+ [NonSerialized]
+ private volatile Segment m_tail;
+
+ private T[] m_serializationArray; // Used for custom serialization.
+
+ private const int SEGMENT_SIZE = 32;
+
+ //number of snapshot takers, GetEnumerator(), ToList() and ToArray() operations take snapshot.
+ [NonSerialized]
+ internal volatile int m_numSnapshotTakers = 0;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ConcurrentQueue{T}"/> class.
+ /// </summary>
+ public ConcurrentQueue()
+ {
+ m_head = m_tail = new Segment(0, this);
+ }
+
+ /// <summary>
+ /// Initializes the contents of the queue from an existing collection.
+ /// </summary>
+ /// <param name="collection">A collection from which to copy elements.</param>
+ private void InitializeFromCollection(IEnumerable<T> collection)
+ {
+ Segment localTail = new Segment(0, this);//use this local variable to avoid the extra volatile read/write. this is safe because it is only called from ctor
+ m_head = localTail;
+
+ int index = 0;
+ foreach (T element in collection)
+ {
+ Contract.Assert(index >= 0 && index < SEGMENT_SIZE);
+ localTail.UnsafeAdd(element);
+ index++;
+
+ if (index >= SEGMENT_SIZE)
+ {
+ localTail = localTail.UnsafeGrow();
+ index = 0;
+ }
+ }
+
+ m_tail = localTail;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ConcurrentQueue{T}"/>
+ /// class that contains elements copied from the specified collection
+ /// </summary>
+ /// <param name="collection">The collection whose elements are copied to the new <see
+ /// cref="ConcurrentQueue{T}"/>.</param>
+ /// <exception cref="T:System.ArgumentNullException">The <paramref name="collection"/> argument is
+ /// null.</exception>
+ public ConcurrentQueue(IEnumerable<T> collection)
+ {
+ if (collection == null)
+ {
+ throw new ArgumentNullException("collection");
+ }
+
+ InitializeFromCollection(collection);
+ }
+
+ /// <summary>
+ /// Get the data array to be serialized
+ /// </summary>
+ [OnSerializing]
+ private void OnSerializing(StreamingContext context)
+ {
+ // save the data into the serialization array to be saved
+ m_serializationArray = ToArray();
+ }
+
+ /// <summary>
+ /// Construct the queue from a previously seiralized one
+ /// </summary>
+ [OnDeserialized]
+ private void OnDeserialized(StreamingContext context)
+ {
+ Contract.Assert(m_serializationArray != null);
+ InitializeFromCollection(m_serializationArray);
+ m_serializationArray = null;
+ }
+
+ /// <summary>
+ /// Copies the elements of the <see cref="T:System.Collections.ICollection"/> to an <see
+ /// cref="T:System.Array"/>, starting at a particular
+ /// <see cref="T:System.Array"/> index.
+ /// </summary>
+ /// <param name="array">The one-dimensional <see cref="T:System.Array">Array</see> that is the
+ /// destination of the elements copied from the
+ /// <see cref="T:System.Collections.Concurrent.ConcurrentBag"/>. The <see
+ /// cref="T:System.Array">Array</see> must have zero-based indexing.</param>
+ /// <param name="index">The zero-based index in <paramref name="array"/> at which copying
+ /// begins.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="array"/> is a null reference (Nothing in
+ /// Visual Basic).</exception>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than
+ /// zero.</exception>
+ /// <exception cref="ArgumentException">
+ /// <paramref name="array"/> is multidimensional. -or-
+ /// <paramref name="array"/> does not have zero-based indexing. -or-
+ /// <paramref name="index"/> is equal to or greater than the length of the <paramref name="array"/>
+ /// -or- The number of elements in the source <see cref="T:System.Collections.ICollection"/> is
+ /// greater than the available space from <paramref name="index"/> to the end of the destination
+ /// <paramref name="array"/>. -or- The type of the source <see
+ /// cref="T:System.Collections.ICollection"/> cannot be cast automatically to the type of the
+ /// destination <paramref name="array"/>.
+ /// </exception>
+ void ICollection.CopyTo(Array array, int index)
+ {
+ // Validate arguments.
+ if (array == null)
+ {
+ throw new ArgumentNullException("array");
+ }
+
+ // We must be careful not to corrupt the array, so we will first accumulate an
+ // internal list of elements that we will then copy to the array. This requires
+ // some extra allocation, but is necessary since we don't know up front whether
+ // the array is sufficiently large to hold the stack's contents.
+ ((ICollection)ToList()).CopyTo(array, index);
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether access to the <see cref="T:System.Collections.ICollection"/> is
+ /// synchronized with the SyncRoot.
+ /// </summary>
+ /// <value>true if access to the <see cref="T:System.Collections.ICollection"/> is synchronized
+ /// with the SyncRoot; otherwise, false. For <see cref="ConcurrentQueue{T}"/>, this property always
+ /// returns false.</value>
+ bool ICollection.IsSynchronized
+ {
+ // Gets a value indicating whether access to this collection is synchronized. Always returns
+ // false. The reason is subtle. While access is in face thread safe, it's not the case that
+ // locking on the SyncRoot would have prevented concurrent pushes and pops, as this property
+ // would typically indicate; that's because we internally use CAS operations vs. true locks.
+ get { return false; }
+ }
+
+
+ /// <summary>
+ /// Gets an object that can be used to synchronize access to the <see
+ /// cref="T:System.Collections.ICollection"/>. This property is not supported.
+ /// </summary>
+ /// <exception cref="T:System.NotSupportedException">The SyncRoot property is not supported.</exception>
+ object ICollection.SyncRoot
+ {
+ get
+ {
+ throw new NotSupportedException(Environment.GetResourceString("ConcurrentCollection_SyncRoot_NotSupported"));
+ }
+ }
+
+ /// <summary>
+ /// Returns an enumerator that iterates through a collection.
+ /// </summary>
+ /// <returns>An <see cref="T:System.Collections.IEnumerator"/> that can be used to iterate through the collection.</returns>
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return ((IEnumerable<T>)this).GetEnumerator();
+ }
+
+ /// <summary>
+ /// Attempts to add an object to the <see
+ /// cref="T:System.Collections.Concurrent.IProducerConsumerCollection{T}"/>.
+ /// </summary>
+ /// <param name="item">The object to add to the <see
+ /// cref="T:System.Collections.Concurrent.IProducerConsumerCollection{T}"/>. The value can be a null
+ /// reference (Nothing in Visual Basic) for reference types.
+ /// </param>
+ /// <returns>true if the object was added successfully; otherwise, false.</returns>
+ /// <remarks>For <see cref="ConcurrentQueue{T}"/>, this operation will always add the object to the
+ /// end of the <see cref="ConcurrentQueue{T}"/>
+ /// and return true.</remarks>
+ bool IProducerConsumerCollection<T>.TryAdd(T item)
+ {
+ Enqueue(item);
+ return true;
+ }
+
+ /// <summary>
+ /// Attempts to remove and return an object from the <see
+ /// cref="T:System.Collections.Concurrent.IProducerConsumerCollection{T}"/>.
+ /// </summary>
+ /// <param name="item">
+ /// When this method returns, if the operation was successful, <paramref name="item"/> contains the
+ /// object removed. If no object was available to be removed, the value is unspecified.
+ /// </param>
+ /// <returns>true if an element was removed and returned succesfully; otherwise, false.</returns>
+ /// <remarks>For <see cref="ConcurrentQueue{T}"/>, this operation will attempt to remove the object
+ /// from the beginning of the <see cref="ConcurrentQueue{T}"/>.
+ /// </remarks>
+ bool IProducerConsumerCollection<T>.TryTake(out T item)
+ {
+ return TryDequeue(out item);
+ }
+
+ /// <summary>
+ /// Gets a value that indicates whether the <see cref="ConcurrentQueue{T}"/> is empty.
+ /// </summary>
+ /// <value>true if the <see cref="ConcurrentQueue{T}"/> is empty; otherwise, false.</value>
+ /// <remarks>
+ /// For determining whether the collection contains any items, use of this property is recommended
+ /// rather than retrieving the number of items from the <see cref="Count"/> property and comparing it
+ /// to 0. However, as this collection is intended to be accessed concurrently, it may be the case
+ /// that another thread will modify the collection after <see cref="IsEmpty"/> returns, thus invalidating
+ /// the result.
+ /// </remarks>
+ public bool IsEmpty
+ {
+ get
+ {
+ Segment head = m_head;
+ if (!head.IsEmpty)
+ //fast route 1:
+ //if current head is not empty, then queue is not empty
+ return false;
+ else if (head.Next == null)
+ //fast route 2:
+ //if current head is empty and it's the last segment
+ //then queue is empty
+ return true;
+ else
+ //slow route:
+ //current head is empty and it is NOT the last segment,
+ //it means another thread is growing new segment
+ {
+ SpinWait spin = new SpinWait();
+ while (head.IsEmpty)
+ {
+ if (head.Next == null)
+ return true;
+
+ spin.SpinOnce();
+ head = m_head;
+ }
+ return false;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Copies the elements stored in the <see cref="ConcurrentQueue{T}"/> to a new array.
+ /// </summary>
+ /// <returns>A new array containing a snapshot of elements copied from the <see
+ /// cref="ConcurrentQueue{T}"/>.</returns>
+ public T[] ToArray()
+ {
+ return ToList().ToArray();
+ }
+
+ /// <summary>
+ /// Copies the <see cref="ConcurrentQueue{T}"/> elements to a new <see
+ /// cref="T:System.Collections.Generic.List{T}"/>.
+ /// </summary>
+ /// <returns>A new <see cref="T:System.Collections.Generic.List{T}"/> containing a snapshot of
+ /// elements copied from the <see cref="ConcurrentQueue{T}"/>.</returns>
+ private List<T> ToList()
+ {
+ // Increments the number of active snapshot takers. This increment must happen before the snapshot is
+ // taken. At the same time, Decrement must happen after list copying is over. Only in this way, can it
+ // eliminate race condition when Segment.TryRemove() checks whether m_numSnapshotTakers == 0.
+ Interlocked.Increment(ref m_numSnapshotTakers);
+
+ List<T> list = new List<T>();
+ try
+ {
+ //store head and tail positions in buffer,
+ Segment head, tail;
+ int headLow, tailHigh;
+ GetHeadTailPositions(out head, out tail, out headLow, out tailHigh);
+
+ if (head == tail)
+ {
+ head.AddToList(list, headLow, tailHigh);
+ }
+ else
+ {
+ head.AddToList(list, headLow, SEGMENT_SIZE - 1);
+ Segment curr = head.Next;
+ while (curr != tail)
+ {
+ curr.AddToList(list, 0, SEGMENT_SIZE - 1);
+ curr = curr.Next;
+ }
+ //Add tail segment
+ tail.AddToList(list, 0, tailHigh);
+ }
+ }
+ finally
+ {
+ // This Decrement must happen after copying is over.
+ Interlocked.Decrement(ref m_numSnapshotTakers);
+ }
+ return list;
+ }
+
+ /// <summary>
+ /// Store the position of the current head and tail positions.
+ /// </summary>
+ /// <param name="head">return the head segment</param>
+ /// <param name="tail">return the tail segment</param>
+ /// <param name="headLow">return the head offset, value range [0, SEGMENT_SIZE]</param>
+ /// <param name="tailHigh">return the tail offset, value range [-1, SEGMENT_SIZE-1]</param>
+ private void GetHeadTailPositions(out Segment head, out Segment tail,
+ out int headLow, out int tailHigh)
+ {
+ head = m_head;
+ tail = m_tail;
+ headLow = head.Low;
+ tailHigh = tail.High;
+ SpinWait spin = new SpinWait();
+
+ //we loop until the observed values are stable and sensible.
+ //This ensures that any update order by other methods can be tolerated.
+ while (
+ //if head and tail changed, retry
+ head != m_head || tail != m_tail
+ //if low and high pointers, retry
+ || headLow != head.Low || tailHigh != tail.High
+ //if head jumps ahead of tail because of concurrent grow and dequeue, retry
+ || head.m_index > tail.m_index)
+ {
+ spin.SpinOnce();
+ head = m_head;
+ tail = m_tail;
+ headLow = head.Low;
+ tailHigh = tail.High;
+ }
+ }
+
+
+ /// <summary>
+ /// Gets the number of elements contained in the <see cref="ConcurrentQueue{T}"/>.
+ /// </summary>
+ /// <value>The number of elements contained in the <see cref="ConcurrentQueue{T}"/>.</value>
+ /// <remarks>
+ /// For determining whether the collection contains any items, use of the <see cref="IsEmpty"/>
+ /// property is recommended rather than retrieving the number of items from the <see cref="Count"/>
+ /// property and comparing it to 0.
+ /// </remarks>
+ public int Count
+ {
+ get
+ {
+ //store head and tail positions in buffer,
+ Segment head, tail;
+ int headLow, tailHigh;
+ GetHeadTailPositions(out head, out tail, out headLow, out tailHigh);
+
+ if (head == tail)
+ {
+ return tailHigh - headLow + 1;
+ }
+
+ //head segment
+ int count = SEGMENT_SIZE - headLow;
+
+ //middle segment(s), if any, are full.
+ //We don't deal with overflow to be consistent with the behavior of generic types in CLR.
+ count += SEGMENT_SIZE * ((int)(tail.m_index - head.m_index - 1));
+
+ //tail segment
+ count += tailHigh + 1;
+
+ return count;
+ }
+ }
+
+
+ /// <summary>
+ /// Copies the <see cref="ConcurrentQueue{T}"/> elements to an existing one-dimensional <see
+ /// cref="T:System.Array">Array</see>, starting at the specified array index.
+ /// </summary>
+ /// <param name="array">The one-dimensional <see cref="T:System.Array">Array</see> that is the
+ /// destination of the elements copied from the
+ /// <see cref="ConcurrentQueue{T}"/>. The <see cref="T:System.Array">Array</see> must have zero-based
+ /// indexing.</param>
+ /// <param name="index">The zero-based index in <paramref name="array"/> at which copying
+ /// begins.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="array"/> is a null reference (Nothing in
+ /// Visual Basic).</exception>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than
+ /// zero.</exception>
+ /// <exception cref="ArgumentException"><paramref name="index"/> is equal to or greater than the
+ /// length of the <paramref name="array"/>
+ /// -or- The number of elements in the source <see cref="ConcurrentQueue{T}"/> is greater than the
+ /// available space from <paramref name="index"/> to the end of the destination <paramref
+ /// name="array"/>.
+ /// </exception>
+ public void CopyTo(T[] array, int index)
+ {
+ if (array == null)
+ {
+ throw new ArgumentNullException("array");
+ }
+
+ // We must be careful not to corrupt the array, so we will first accumulate an
+ // internal list of elements that we will then copy to the array. This requires
+ // some extra allocation, but is necessary since we don't know up front whether
+ // the array is sufficiently large to hold the stack's contents.
+ ToList().CopyTo(array, index);
+ }
+
+
+ /// <summary>
+ /// Returns an enumerator that iterates through the <see
+ /// cref="ConcurrentQueue{T}"/>.
+ /// </summary>
+ /// <returns>An enumerator for the contents of the <see
+ /// cref="ConcurrentQueue{T}"/>.</returns>
+ /// <remarks>
+ /// The enumeration represents a moment-in-time snapshot of the contents
+ /// of the queue. It does not reflect any updates to the collection after
+ /// <see cref="GetEnumerator"/> was called. The enumerator is safe to use
+ /// concurrently with reads from and writes to the queue.
+ /// </remarks>
+ public IEnumerator<T> GetEnumerator()
+ {
+ // Increments the number of active snapshot takers. This increment must happen before the snapshot is
+ // taken. At the same time, Decrement must happen after the enumeration is over. Only in this way, can it
+ // eliminate race condition when Segment.TryRemove() checks whether m_numSnapshotTakers == 0.
+ Interlocked.Increment(ref m_numSnapshotTakers);
+
+ // Takes a snapshot of the queue.
+ // A design flaw here: if a Thread.Abort() happens, we cannot decrement m_numSnapshotTakers. But we cannot
+ // wrap the following with a try/finally block, otherwise the decrement will happen before the yield return
+ // statements in the GetEnumerator (head, tail, headLow, tailHigh) method.
+ Segment head, tail;
+ int headLow, tailHigh;
+ GetHeadTailPositions(out head, out tail, out headLow, out tailHigh);
+
+ //If we put yield-return here, the iterator will be lazily evaluated. As a result a snapshot of
+ // the queue is not taken when GetEnumerator is initialized but when MoveNext() is first called.
+ // This is inconsistent with existing generic collections. In order to prevent it, we capture the
+ // value of m_head in a buffer and call out to a helper method.
+ //The old way of doing this was to return the ToList().GetEnumerator(), but ToList() was an
+ // unnecessary perfomance hit.
+ return GetEnumerator(head, tail, headLow, tailHigh);
+ }
+
+ /// <summary>
+ /// Helper method of GetEnumerator to seperate out yield return statement, and prevent lazy evaluation.
+ /// </summary>
+ private IEnumerator<T> GetEnumerator(Segment head, Segment tail, int headLow, int tailHigh)
+ {
+ try
+ {
+ SpinWait spin = new SpinWait();
+
+ if (head == tail)
+ {
+ for (int i = headLow; i <= tailHigh; i++)
+ {
+ // If the position is reserved by an Enqueue operation, but the value is not written into,
+ // spin until the value is available.
+ spin.Reset();
+ while (!head.m_state[i].m_value)
+ {
+ spin.SpinOnce();
+ }
+ yield return head.m_array[i];
+ }
+ }
+ else
+ {
+ //iterate on head segment
+ for (int i = headLow; i < SEGMENT_SIZE; i++)
+ {
+ // If the position is reserved by an Enqueue operation, but the value is not written into,
+ // spin until the value is available.
+ spin.Reset();
+ while (!head.m_state[i].m_value)
+ {
+ spin.SpinOnce();
+ }
+ yield return head.m_array[i];
+ }
+ //iterate on middle segments
+ Segment curr = head.Next;
+ while (curr != tail)
+ {
+ for (int i = 0; i < SEGMENT_SIZE; i++)
+ {
+ // If the position is reserved by an Enqueue operation, but the value is not written into,
+ // spin until the value is available.
+ spin.Reset();
+ while (!curr.m_state[i].m_value)
+ {
+ spin.SpinOnce();
+ }
+ yield return curr.m_array[i];
+ }
+ curr = curr.Next;
+ }
+
+ //iterate on tail segment
+ for (int i = 0; i <= tailHigh; i++)
+ {
+ // If the position is reserved by an Enqueue operation, but the value is not written into,
+ // spin until the value is available.
+ spin.Reset();
+ while (!tail.m_state[i].m_value)
+ {
+ spin.SpinOnce();
+ }
+ yield return tail.m_array[i];
+ }
+ }
+ }
+ finally
+ {
+ // This Decrement must happen after the enumeration is over.
+ Interlocked.Decrement(ref m_numSnapshotTakers);
+ }
+ }
+
+ /// <summary>
+ /// Adds an object to the end of the <see cref="ConcurrentQueue{T}"/>.
+ /// </summary>
+ /// <param name="item">The object to add to the end of the <see
+ /// cref="ConcurrentQueue{T}"/>. The value can be a null reference
+ /// (Nothing in Visual Basic) for reference types.
+ /// </param>
+ public void Enqueue(T item)
+ {
+ SpinWait spin = new SpinWait();
+ while (true)
+ {
+ Segment tail = m_tail;
+ if (tail.TryAppend(item))
+ return;
+ spin.SpinOnce();
+ }
+ }
+
+
+ /// <summary>
+ /// Attempts to remove and return the object at the beginning of the <see
+ /// cref="ConcurrentQueue{T}"/>.
+ /// </summary>
+ /// <param name="result">
+ /// When this method returns, if the operation was successful, <paramref name="result"/> contains the
+ /// object removed. If no object was available to be removed, the value is unspecified.
+ /// </param>
+ /// <returns>true if an element was removed and returned from the beggining of the <see
+ /// cref="ConcurrentQueue{T}"/>
+ /// succesfully; otherwise, false.</returns>
+ public bool TryDequeue(out T result)
+ {
+ while (!IsEmpty)
+ {
+ Segment head = m_head;
+ if (head.TryRemove(out result))
+ return true;
+ //since method IsEmpty spins, we don't need to spin in the while loop
+ }
+ result = default(T);
+ return false;
+ }
+
+ /// <summary>
+ /// Attempts to return an object from the beginning of the <see cref="ConcurrentQueue{T}"/>
+ /// without removing it.
+ /// </summary>
+ /// <param name="result">When this method returns, <paramref name="result"/> contains an object from
+ /// the beginning of the <see cref="T:System.Collections.Concurrent.ConccurrentQueue{T}"/> or an
+ /// unspecified value if the operation failed.</param>
+ /// <returns>true if and object was returned successfully; otherwise, false.</returns>
+ public bool TryPeek(out T result)
+ {
+ Interlocked.Increment(ref m_numSnapshotTakers);
+
+ while (!IsEmpty)
+ {
+ Segment head = m_head;
+ if (head.TryPeek(out result))
+ {
+ Interlocked.Decrement(ref m_numSnapshotTakers);
+ return true;
+ }
+ //since method IsEmpty spins, we don't need to spin in the while loop
+ }
+ result = default(T);
+ Interlocked.Decrement(ref m_numSnapshotTakers);
+ return false;
+ }
+
+
+ /// <summary>
+ /// private class for ConcurrentQueue.
+ /// a queue is a linked list of small arrays, each node is called a segment.
+ /// A segment contains an array, a pointer to the next segment, and m_low, m_high indices recording
+ /// the first and last valid elements of the array.
+ /// </summary>
+ private class Segment
+ {
+ //we define two volatile arrays: m_array and m_state. Note that the accesses to the array items
+ //do not get volatile treatment. But we don't need to worry about loading adjacent elements or
+ //store/load on adjacent elements would suffer reordering.
+ // - Two stores: these are at risk, but CLRv2 memory model guarantees store-release hence we are safe.
+ // - Two loads: because one item from two volatile arrays are accessed, the loads of the array references
+ // are sufficient to prevent reordering of the loads of the elements.
+ internal volatile T[] m_array;
+
+ // For each entry in m_array, the corresponding entry in m_state indicates whether this position contains
+ // a valid value. m_state is initially all false.
+ internal volatile VolatileBool[] m_state;
+
+ //pointer to the next segment. null if the current segment is the last segment
+ private volatile Segment m_next;
+
+ //We use this zero based index to track how many segments have been created for the queue, and
+ //to compute how many active segments are there currently.
+ // * The number of currently active segments is : m_tail.m_index - m_head.m_index + 1;
+ // * m_index is incremented with every Segment.Grow operation. We use Int64 type, and we can safely
+ // assume that it never overflows. To overflow, we need to do 2^63 increments, even at a rate of 4
+ // billion (2^32) increments per second, it takes 2^31 seconds, which is about 64 years.
+ internal readonly long m_index;
+
+ //indices of where the first and last valid values
+ // - m_low points to the position of the next element to pop from this segment, range [0, infinity)
+ // m_low >= SEGMENT_SIZE implies the segment is disposable
+ // - m_high points to the position of the latest pushed element, range [-1, infinity)
+ // m_high == -1 implies the segment is new and empty
+ // m_high >= SEGMENT_SIZE-1 means this segment is ready to grow.
+ // and the thread who sets m_high to SEGMENT_SIZE-1 is responsible to grow the segment
+ // - Math.Min(m_low, SEGMENT_SIZE) > Math.Min(m_high, SEGMENT_SIZE-1) implies segment is empty
+ // - initially m_low =0 and m_high=-1;
+ private volatile int m_low;
+ private volatile int m_high;
+
+ private volatile ConcurrentQueue<T> m_source;
+
+ /// <summary>
+ /// Create and initialize a segment with the specified index.
+ /// </summary>
+ internal Segment(long index, ConcurrentQueue<T> source)
+ {
+ m_array = new T[SEGMENT_SIZE];
+ m_state = new VolatileBool[SEGMENT_SIZE]; //all initialized to false
+ m_high = -1;
+ Contract.Assert(index >= 0);
+ m_index = index;
+ m_source = source;
+ }
+
+ /// <summary>
+ /// return the next segment
+ /// </summary>
+ internal Segment Next
+ {
+ get { return m_next; }
+ }
+
+
+ /// <summary>
+ /// return true if the current segment is empty (doesn't have any element available to dequeue,
+ /// false otherwise
+ /// </summary>
+ internal bool IsEmpty
+ {
+ get { return (Low > High); }
+ }
+
+ /// <summary>
+ /// Add an element to the tail of the current segment
+ /// exclusively called by ConcurrentQueue.InitializedFromCollection
+ /// InitializeFromCollection is responsible to guaratee that there is no index overflow,
+ /// and there is no contention
+ /// </summary>
+ /// <param name="value"></param>
+ internal void UnsafeAdd(T value)
+ {
+ Contract.Assert(m_high < SEGMENT_SIZE - 1);
+ m_high++;
+ m_array[m_high] = value;
+ m_state[m_high].m_value = true;
+ }
+
+ /// <summary>
+ /// Create a new segment and append to the current one
+ /// Does not update the m_tail pointer
+ /// exclusively called by ConcurrentQueue.InitializedFromCollection
+ /// InitializeFromCollection is responsible to guaratee that there is no index overflow,
+ /// and there is no contention
+ /// </summary>
+ /// <returns>the reference to the new Segment</returns>
+ internal Segment UnsafeGrow()
+ {
+ Contract.Assert(m_high >= SEGMENT_SIZE - 1);
+ Segment newSegment = new Segment(m_index + 1, m_source); //m_index is Int64, we don't need to worry about overflow
+ m_next = newSegment;
+ return newSegment;
+ }
+
+ /// <summary>
+ /// Create a new segment and append to the current one
+ /// Update the m_tail pointer
+ /// This method is called when there is no contention
+ /// </summary>
+ internal void Grow()
+ {
+ //no CAS is needed, since there is no contention (other threads are blocked, busy waiting)
+ Segment newSegment = new Segment(m_index + 1, m_source); //m_index is Int64, we don't need to worry about overflow
+ m_next = newSegment;
+ Contract.Assert(m_source.m_tail == this);
+ m_source.m_tail = m_next;
+ }
+
+
+ /// <summary>
+ /// Try to append an element at the end of this segment.
+ /// </summary>
+ /// <param name="value">the element to append</param>
+ /// <param name="tail">The tail.</param>
+ /// <returns>true if the element is appended, false if the current segment is full</returns>
+ /// <remarks>if appending the specified element succeeds, and after which the segment is full,
+ /// then grow the segment</remarks>
+ internal bool TryAppend(T value)
+ {
+ //quickly check if m_high is already over the boundary, if so, bail out
+ if (m_high >= SEGMENT_SIZE - 1)
+ {
+ return false;
+ }
+
+ //Now we will use a CAS to increment m_high, and store the result in newhigh.
+ //Depending on how many free spots left in this segment and how many threads are doing this Increment
+ //at this time, the returning "newhigh" can be
+ // 1) < SEGMENT_SIZE - 1 : we took a spot in this segment, and not the last one, just insert the value
+ // 2) == SEGMENT_SIZE - 1 : we took the last spot, insert the value AND grow the segment
+ // 3) > SEGMENT_SIZE - 1 : we failed to reserve a spot in this segment, we return false to
+ // Queue.Enqueue method, telling it to try again in the next segment.
+
+ int newhigh = SEGMENT_SIZE; //initial value set to be over the boundary
+
+ //We need do Interlocked.Increment and value/state update in a finally block to ensure that they run
+ //without interuption. This is to prevent anything from happening between them, and another dequeue
+ //thread maybe spinning forever to wait for m_state[] to be true;
+ try
+ { }
+ finally
+ {
+ newhigh = Interlocked.Increment(ref m_high);
+ if (newhigh <= SEGMENT_SIZE - 1)
+ {
+ m_array[newhigh] = value;
+ m_state[newhigh].m_value = true;
+ }
+
+ //if this thread takes up the last slot in the segment, then this thread is responsible
+ //to grow a new segment. Calling Grow must be in the finally block too for reliability reason:
+ //if thread abort during Grow, other threads will be left busy spinning forever.
+ if (newhigh == SEGMENT_SIZE - 1)
+ {
+ Grow();
+ }
+ }
+
+ //if newhigh <= SEGMENT_SIZE-1, it means the current thread successfully takes up a spot
+ return newhigh <= SEGMENT_SIZE - 1;
+ }
+
+
+ /// <summary>
+ /// try to remove an element from the head of current segment
+ /// </summary>
+ /// <param name="result">The result.</param>
+ /// <param name="head">The head.</param>
+ /// <returns>return false only if the current segment is empty</returns>
+ internal bool TryRemove(out T result)
+ {
+ SpinWait spin = new SpinWait();
+ int lowLocal = Low, highLocal = High;
+ while (lowLocal <= highLocal)
+ {
+ //try to update m_low
+ if (Interlocked.CompareExchange(ref m_low, lowLocal + 1, lowLocal) == lowLocal)
+ {
+ //if the specified value is not available (this spot is taken by a push operation,
+ // but the value is not written into yet), then spin
+ SpinWait spinLocal = new SpinWait();
+ while (!m_state[lowLocal].m_value)
+ {
+ spinLocal.SpinOnce();
+ }
+ result = m_array[lowLocal];
+
+ // If there is no other thread taking snapshot (GetEnumerator(), ToList(), etc), reset the deleted entry to null.
+ // It is ok if after this conditional check m_numSnapshotTakers becomes > 0, because new snapshots won't include
+ // the deleted entry at m_array[lowLocal].
+ if (m_source.m_numSnapshotTakers <= 0)
+ {
+ m_array[lowLocal] = default(T); //release the reference to the object.
+ }
+
+ //if the current thread sets m_low to SEGMENT_SIZE, which means the current segment becomes
+ //disposable, then this thread is responsible to dispose this segment, and reset m_head
+ if (lowLocal + 1 >= SEGMENT_SIZE)
+ {
+ // Invariant: we only dispose the current m_head, not any other segment
+ // In usual situation, disposing a segment is simply seting m_head to m_head.m_next
+ // But there is one special case, where m_head and m_tail points to the same and ONLY
+ //segment of the queue: Another thread A is doing Enqueue and finds that it needs to grow,
+ //while the *current* thread is doing *this* Dequeue operation, and finds that it needs to
+ //dispose the current (and ONLY) segment. Then we need to wait till thread A finishes its
+ //Grow operation, this is the reason of having the following while loop
+ spinLocal = new SpinWait();
+ while (m_next == null)
+ {
+ spinLocal.SpinOnce();
+ }
+ Contract.Assert(m_source.m_head == this);
+ m_source.m_head = m_next;
+ }
+ return true;
+ }
+ else
+ {
+ //CAS failed due to contention: spin briefly and retry
+ spin.SpinOnce();
+ lowLocal = Low; highLocal = High;
+ }
+ }//end of while
+ result = default(T);
+ return false;
+ }
+
+ /// <summary>
+ /// try to peek the current segment
+ /// </summary>
+ /// <param name="result">holds the return value of the element at the head position,
+ /// value set to default(T) if there is no such an element</param>
+ /// <returns>true if there are elements in the current segment, false otherwise</returns>
+ internal bool TryPeek(out T result)
+ {
+ result = default(T);
+ int lowLocal = Low;
+ if (lowLocal > High)
+ return false;
+ SpinWait spin = new SpinWait();
+ while (!m_state[lowLocal].m_value)
+ {
+ spin.SpinOnce();
+ }
+ result = m_array[lowLocal];
+ return true;
+ }
+
+ /// <summary>
+ /// Adds part or all of the current segment into a List.
+ /// </summary>
+ /// <param name="list">the list to which to add</param>
+ /// <param name="start">the start position</param>
+ /// <param name="end">the end position</param>
+ internal void AddToList(List<T> list, int start, int end)
+ {
+ for (int i = start; i <= end; i++)
+ {
+ SpinWait spin = new SpinWait();
+ while (!m_state[i].m_value)
+ {
+ spin.SpinOnce();
+ }
+ list.Add(m_array[i]);
+ }
+ }
+
+ /// <summary>
+ /// return the position of the head of the current segment
+ /// Value range [0, SEGMENT_SIZE], if it's SEGMENT_SIZE, it means this segment is exhausted and thus empty
+ /// </summary>
+ internal int Low
+ {
+ get
+ {
+ return Math.Min(m_low, SEGMENT_SIZE);
+ }
+ }
+
+ /// <summary>
+ /// return the logical position of the tail of the current segment
+ /// Value range [-1, SEGMENT_SIZE-1]. When it's -1, it means this is a new segment and has no elemnet yet
+ /// </summary>
+ internal int High
+ {
+ get
+ {
+ //if m_high > SEGMENT_SIZE, it means it's out of range, we should return
+ //SEGMENT_SIZE-1 as the logical position
+ return Math.Min(m_high, SEGMENT_SIZE - 1);
+ }
+ }
+
+ }
+ }//end of class Segment
+
+ /// <summary>
+ /// A wrapper struct for volatile bool, please note the copy of the struct it self will not be volatile
+ /// for example this statement will not include in volatilness operation volatileBool1 = volatileBool2 the jit will copy the struct and will ignore the volatile
+ /// </summary>
+ struct VolatileBool
+ {
+ public VolatileBool(bool value)
+ {
+ m_value = value;
+ }
+ public volatile bool m_value;
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Concurrent/ConcurrentStack.cs b/src/mscorlib/src/System/Collections/Concurrent/ConcurrentStack.cs
new file mode 100644
index 0000000000..15d4176cff
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Concurrent/ConcurrentStack.cs
@@ -0,0 +1,840 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+#pragma warning disable 0420
+
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// A lock-free, concurrent stack primitive, and its associated debugger view type.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Runtime.ConstrainedExecution;
+using System.Runtime.Serialization;
+using System.Security;
+using System.Security.Permissions;
+using System.Threading;
+
+namespace System.Collections.Concurrent
+{
+ // A stack that uses CAS operations internally to maintain thread-safety in a lock-free
+ // manner. Attempting to push or pop concurrently from the stack will not trigger waiting,
+ // although some optimistic concurrency and retry is used, possibly leading to lack of
+ // fairness and/or livelock. The stack uses spinning and backoff to add some randomization,
+ // in hopes of statistically decreasing the possibility of livelock.
+ //
+ // Note that we currently allocate a new node on every push. This avoids having to worry
+ // about potential ABA issues, since the CLR GC ensures that a memory address cannot be
+ // reused before all references to it have died.
+
+ /// <summary>
+ /// Represents a thread-safe last-in, first-out collection of objects.
+ /// </summary>
+ /// <typeparam name="T">Specifies the type of elements in the stack.</typeparam>
+ /// <remarks>
+ /// All public and protected members of <see cref="ConcurrentStack{T}"/> are thread-safe and may be used
+ /// concurrently from multiple threads.
+ /// </remarks>
+ [DebuggerDisplay("Count = {Count}")]
+ [DebuggerTypeProxy(typeof(SystemCollectionsConcurrent_ProducerConsumerCollectionDebugView<>))]
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+#if !FEATURE_CORECLR
+ [Serializable]
+#endif //!FEATURE_CORECLR
+ public class ConcurrentStack<T> : IProducerConsumerCollection<T>, IReadOnlyCollection<T>
+ {
+ /// <summary>
+ /// A simple (internal) node type used to store elements of concurrent stacks and queues.
+ /// </summary>
+ private class Node
+ {
+ internal readonly T m_value; // Value of the node.
+ internal Node m_next; // Next pointer.
+
+ /// <summary>
+ /// Constructs a new node with the specified value and no next node.
+ /// </summary>
+ /// <param name="value">The value of the node.</param>
+ internal Node(T value)
+ {
+ m_value = value;
+ m_next = null;
+ }
+ }
+
+#if !FEATURE_CORECLR
+ [NonSerialized]
+#endif //!FEATURE_CORECLR
+ private volatile Node m_head; // The stack is a singly linked list, and only remembers the head.
+
+#if !FEATURE_CORECLR
+ private T[] m_serializationArray; // Used for custom serialization.
+#endif //!FEATURE_CORECLR
+
+ private const int BACKOFF_MAX_YIELDS = 8; // Arbitrary number to cap backoff.
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ConcurrentStack{T}"/>
+ /// class.
+ /// </summary>
+ public ConcurrentStack()
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ConcurrentStack{T}"/>
+ /// class that contains elements copied from the specified collection
+ /// </summary>
+ /// <param name="collection">The collection whose elements are copied to the new <see
+ /// cref="ConcurrentStack{T}"/>.</param>
+ /// <exception cref="T:System.ArgumentNullException">The <paramref name="collection"/> argument is
+ /// null.</exception>
+ public ConcurrentStack(IEnumerable<T> collection)
+ {
+ if (collection == null)
+ {
+ throw new ArgumentNullException("collection");
+ }
+ InitializeFromCollection(collection);
+ }
+
+ /// <summary>
+ /// Initializes the contents of the stack from an existing collection.
+ /// </summary>
+ /// <param name="collection">A collection from which to copy elements.</param>
+ private void InitializeFromCollection(IEnumerable<T> collection)
+ {
+ // We just copy the contents of the collection to our stack.
+ Node lastNode = null;
+ foreach (T element in collection)
+ {
+ Node newNode = new Node(element);
+ newNode.m_next = lastNode;
+ lastNode = newNode;
+ }
+
+ m_head = lastNode;
+ }
+
+#if !FEATURE_CORECLR
+ /// <summary>
+ /// Get the data array to be serialized
+ /// </summary>
+ [OnSerializing]
+ private void OnSerializing(StreamingContext context)
+ {
+ // save the data into the serialization array to be saved
+ m_serializationArray = ToArray();
+ }
+
+ /// <summary>
+ /// Construct the stack from a previously seiralized one
+ /// </summary>
+ [OnDeserialized]
+ private void OnDeserialized(StreamingContext context)
+ {
+ Contract.Assert(m_serializationArray != null);
+ // Add the elements to our stack. We need to add them from head-to-tail, to
+ // preserve the original ordering of the stack before serialization.
+ Node prevNode = null;
+ Node head = null;
+ for (int i = 0; i < m_serializationArray.Length; i++)
+ {
+ Node currNode = new Node(m_serializationArray[i]);
+
+ if (prevNode == null)
+ {
+ head = currNode;
+ }
+ else
+ {
+ prevNode.m_next = currNode;
+ }
+
+ prevNode = currNode;
+ }
+
+ m_head = head;
+ m_serializationArray = null;
+ }
+#endif //!FEATURE_CORECLR
+
+
+ /// <summary>
+ /// Gets a value that indicates whether the <see cref="ConcurrentStack{T}"/> is empty.
+ /// </summary>
+ /// <value>true if the <see cref="ConcurrentStack{T}"/> is empty; otherwise, false.</value>
+ /// <remarks>
+ /// For determining whether the collection contains any items, use of this property is recommended
+ /// rather than retrieving the number of items from the <see cref="Count"/> property and comparing it
+ /// to 0. However, as this collection is intended to be accessed concurrently, it may be the case
+ /// that another thread will modify the collection after <see cref="IsEmpty"/> returns, thus invalidating
+ /// the result.
+ /// </remarks>
+ public bool IsEmpty
+ {
+ // Checks whether the stack is empty. Clearly the answer may be out of date even prior to
+ // the function returning (i.e. if another thread concurrently adds to the stack). It does
+ // guarantee, however, that, if another thread does not mutate the stack, a subsequent call
+ // to TryPop will return true -- i.e. it will also read the stack as non-empty.
+ get { return m_head == null; }
+ }
+
+ /// <summary>
+ /// Gets the number of elements contained in the <see cref="ConcurrentStack{T}"/>.
+ /// </summary>
+ /// <value>The number of elements contained in the <see cref="ConcurrentStack{T}"/>.</value>
+ /// <remarks>
+ /// For determining whether the collection contains any items, use of the <see cref="IsEmpty"/>
+ /// property is recommended rather than retrieving the number of items from the <see cref="Count"/>
+ /// property and comparing it to 0.
+ /// </remarks>
+ public int Count
+ {
+ // Counts the number of entries in the stack. This is an O(n) operation. The answer may be out
+ // of date before returning, but guarantees to return a count that was once valid. Conceptually,
+ // the implementation snaps a copy of the list and then counts the entries, though physically
+ // this is not what actually happens.
+ get
+ {
+ int count = 0;
+
+ // Just whip through the list and tally up the number of nodes. We rely on the fact that
+ // node next pointers are immutable after being enqueued for the first time, even as
+ // they are being dequeued. If we ever changed this (e.g. to pool nodes somehow),
+ // we'd need to revisit this implementation.
+
+ for (Node curr = m_head; curr != null; curr = curr.m_next)
+ {
+ count++; //we don't handle overflow, to be consistent with existing generic collection types in CLR
+ }
+
+ return count;
+ }
+ }
+
+
+ /// <summary>
+ /// Gets a value indicating whether access to the <see cref="T:System.Collections.ICollection"/> is
+ /// synchronized with the SyncRoot.
+ /// </summary>
+ /// <value>true if access to the <see cref="T:System.Collections.ICollection"/> is synchronized
+ /// with the SyncRoot; otherwise, false. For <see cref="ConcurrentStack{T}"/>, this property always
+ /// returns false.</value>
+ bool ICollection.IsSynchronized
+ {
+ // Gets a value indicating whether access to this collection is synchronized. Always returns
+ // false. The reason is subtle. While access is in face thread safe, it's not the case that
+ // locking on the SyncRoot would have prevented concurrent pushes and pops, as this property
+ // would typically indicate; that's because we internally use CAS operations vs. true locks.
+ get { return false; }
+ }
+
+ /// <summary>
+ /// Gets an object that can be used to synchronize access to the <see
+ /// cref="T:System.Collections.ICollection"/>. This property is not supported.
+ /// </summary>
+ /// <exception cref="T:System.NotSupportedException">The SyncRoot property is not supported</exception>
+ object ICollection.SyncRoot
+ {
+ get
+ {
+ throw new NotSupportedException(Environment.GetResourceString("ConcurrentCollection_SyncRoot_NotSupported"));
+ }
+ }
+
+ /// <summary>
+ /// Removes all objects from the <see cref="ConcurrentStack{T}"/>.
+ /// </summary>
+ public void Clear()
+ {
+ // Clear the list by setting the head to null. We don't need to use an atomic
+ // operation for this: anybody who is mutating the head by pushing or popping
+ // will need to use an atomic operation to guarantee they serialize and don't
+ // overwrite our setting of the head to null.
+ m_head = null;
+ }
+
+ /// <summary>
+ /// Copies the elements of the <see cref="T:System.Collections.ICollection"/> to an <see
+ /// cref="T:System.Array"/>, starting at a particular
+ /// <see cref="T:System.Array"/> index.
+ /// </summary>
+ /// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of
+ /// the elements copied from the
+ /// <see cref="ConcurrentStack{T}"/>. The <see cref="T:System.Array"/> must
+ /// have zero-based indexing.</param>
+ /// <param name="index">The zero-based index in <paramref name="array"/> at which copying
+ /// begins.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="array"/> is a null reference (Nothing in
+ /// Visual Basic).</exception>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than
+ /// zero.</exception>
+ /// <exception cref="ArgumentException">
+ /// <paramref name="array"/> is multidimensional. -or-
+ /// <paramref name="array"/> does not have zero-based indexing. -or-
+ /// <paramref name="index"/> is equal to or greater than the length of the <paramref name="array"/>
+ /// -or- The number of elements in the source <see cref="T:System.Collections.ICollection"/> is
+ /// greater than the available space from <paramref name="index"/> to the end of the destination
+ /// <paramref name="array"/>. -or- The type of the source <see
+ /// cref="T:System.Collections.ICollection"/> cannot be cast automatically to the type of the
+ /// destination <paramref name="array"/>.
+ /// </exception>
+ void ICollection.CopyTo(Array array, int index)
+ {
+ // Validate arguments.
+ if (array == null)
+ {
+ throw new ArgumentNullException("array");
+ }
+
+ // We must be careful not to corrupt the array, so we will first accumulate an
+ // internal list of elements that we will then copy to the array. This requires
+ // some extra allocation, but is necessary since we don't know up front whether
+ // the array is sufficiently large to hold the stack's contents.
+ ((ICollection)ToList()).CopyTo(array, index);
+ }
+
+ /// <summary>
+ /// Copies the <see cref="ConcurrentStack{T}"/> elements to an existing one-dimensional <see
+ /// cref="T:System.Array"/>, starting at the specified array index.
+ /// </summary>
+ /// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of
+ /// the elements copied from the
+ /// <see cref="ConcurrentStack{T}"/>. The <see cref="T:System.Array"/> must have zero-based
+ /// indexing.</param>
+ /// <param name="index">The zero-based index in <paramref name="array"/> at which copying
+ /// begins.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="array"/> is a null reference (Nothing in
+ /// Visual Basic).</exception>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than
+ /// zero.</exception>
+ /// <exception cref="ArgumentException"><paramref name="index"/> is equal to or greater than the
+ /// length of the <paramref name="array"/>
+ /// -or- The number of elements in the source <see cref="ConcurrentStack{T}"/> is greater than the
+ /// available space from <paramref name="index"/> to the end of the destination <paramref
+ /// name="array"/>.
+ /// </exception>
+ public void CopyTo(T[] array, int index)
+ {
+ if (array == null)
+ {
+ throw new ArgumentNullException("array");
+ }
+
+ // We must be careful not to corrupt the array, so we will first accumulate an
+ // internal list of elements that we will then copy to the array. This requires
+ // some extra allocation, but is necessary since we don't know up front whether
+ // the array is sufficiently large to hold the stack's contents.
+ ToList().CopyTo(array, index);
+ }
+
+
+ /// <summary>
+ /// Inserts an object at the top of the <see cref="ConcurrentStack{T}"/>.
+ /// </summary>
+ /// <param name="item">The object to push onto the <see cref="ConcurrentStack{T}"/>. The value can be
+ /// a null reference (Nothing in Visual Basic) for reference types.
+ /// </param>
+ public void Push(T item)
+ {
+ // Pushes a node onto the front of the stack thread-safely. Internally, this simply
+ // swaps the current head pointer using a (thread safe) CAS operation to accomplish
+ // lock freedom. If the CAS fails, we add some back off to statistically decrease
+ // contention at the head, and then go back around and retry.
+
+ Node newNode = new Node(item);
+ newNode.m_next = m_head;
+ if (Interlocked.CompareExchange(ref m_head, newNode, newNode.m_next) == newNode.m_next)
+ {
+ return;
+ }
+
+ // If we failed, go to the slow path and loop around until we succeed.
+ PushCore(newNode, newNode);
+ }
+
+ /// <summary>
+ /// Inserts multiple objects at the top of the <see cref="ConcurrentStack{T}"/> atomically.
+ /// </summary>
+ /// <param name="items">The objects to push onto the <see cref="ConcurrentStack{T}"/>.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="items"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ /// <remarks>
+ /// When adding multiple items to the stack, using PushRange is a more efficient
+ /// mechanism than using <see cref="Push"/> one item at a time. Additionally, PushRange
+ /// guarantees that all of the elements will be added atomically, meaning that no other threads will
+ /// be able to inject elements between the elements being pushed. Items at lower indices in
+ /// the <paramref name="items"/> array will be pushed before items at higher indices.
+ /// </remarks>
+ public void PushRange(T[] items)
+ {
+ if (items == null)
+ {
+ throw new ArgumentNullException("items");
+ }
+ PushRange(items, 0, items.Length);
+ }
+
+ /// <summary>
+ /// Inserts multiple objects at the top of the <see cref="ConcurrentStack{T}"/> atomically.
+ /// </summary>
+ /// <param name="items">The objects to push onto the <see cref="ConcurrentStack{T}"/>.</param>
+ /// <param name="startIndex">The zero-based offset in <paramref name="items"/> at which to begin
+ /// inserting elements onto the top of the <see cref="ConcurrentStack{T}"/>.</param>
+ /// <param name="count">The number of elements to be inserted onto the top of the <see
+ /// cref="ConcurrentStack{T}"/>.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="items"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="startIndex"/> or <paramref
+ /// name="count"/> is negative. Or <paramref name="startIndex"/> is greater than or equal to the length
+ /// of <paramref name="items"/>.</exception>
+ /// <exception cref="ArgumentException"><paramref name="startIndex"/> + <paramref name="count"/> is
+ /// greater than the length of <paramref name="items"/>.</exception>
+ /// <remarks>
+ /// When adding multiple items to the stack, using PushRange is a more efficient
+ /// mechanism than using <see cref="Push"/> one item at a time. Additionally, PushRange
+ /// guarantees that all of the elements will be added atomically, meaning that no other threads will
+ /// be able to inject elements between the elements being pushed. Items at lower indices in the
+ /// <paramref name="items"/> array will be pushed before items at higher indices.
+ /// </remarks>
+ public void PushRange(T[] items, int startIndex, int count)
+ {
+ ValidatePushPopRangeInput(items, startIndex, count);
+
+ // No op if the count is zero
+ if (count == 0)
+ return;
+
+
+ Node head, tail;
+ head = tail = new Node(items[startIndex]);
+ for (int i = startIndex + 1; i < startIndex + count; i++)
+ {
+ Node node = new Node(items[i]);
+ node.m_next = head;
+ head = node;
+ }
+
+ tail.m_next = m_head;
+ if (Interlocked.CompareExchange(ref m_head, head, tail.m_next) == tail.m_next)
+ {
+ return;
+ }
+
+ // If we failed, go to the slow path and loop around until we succeed.
+ PushCore(head, tail);
+
+ }
+
+
+ /// <summary>
+ /// Push one or many nodes into the stack, if head and tails are equal then push one node to the stack other wise push the list between head
+ /// and tail to the stack
+ /// </summary>
+ /// <param name="head">The head pointer to the new list</param>
+ /// <param name="tail">The tail pointer to the new list</param>
+ private void PushCore(Node head, Node tail)
+ {
+ SpinWait spin = new SpinWait();
+
+ // Keep trying to CAS the exising head with the new node until we succeed.
+ do
+ {
+ spin.SpinOnce();
+ // Reread the head and link our new node.
+ tail.m_next = m_head;
+ }
+ while (Interlocked.CompareExchange(
+ ref m_head, head, tail.m_next) != tail.m_next);
+
+#if !FEATURE_CORECLR
+ if (CDSCollectionETWBCLProvider.Log.IsEnabled())
+ {
+ CDSCollectionETWBCLProvider.Log.ConcurrentStack_FastPushFailed(spin.Count);
+ }
+#endif // !FEATURE_CORECLR
+ }
+
+ /// <summary>
+ /// Local helper function to validate the Pop Push range methods input
+ /// </summary>
+ private void ValidatePushPopRangeInput(T[] items, int startIndex, int count)
+ {
+ if (items == null)
+ {
+ throw new ArgumentNullException("items");
+ }
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ConcurrentStack_PushPopRange_CountOutOfRange"));
+ }
+ int length = items.Length;
+ if (startIndex >= length || startIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ConcurrentStack_PushPopRange_StartOutOfRange"));
+ }
+ if (length - count < startIndex) //instead of (startIndex + count > items.Length) to prevent overflow
+ {
+ throw new ArgumentException(Environment.GetResourceString("ConcurrentStack_PushPopRange_InvalidCount"));
+ }
+ }
+
+ /// <summary>
+ /// Attempts to add an object to the <see
+ /// cref="T:System.Collections.Concurrent.IProducerConsumerCollection{T}"/>.
+ /// </summary>
+ /// <param name="item">The object to add to the <see
+ /// cref="T:System.Collections.Concurrent.IProducerConsumerCollection{T}"/>. The value can be a null
+ /// reference (Nothing in Visual Basic) for reference types.
+ /// </param>
+ /// <returns>true if the object was added successfully; otherwise, false.</returns>
+ /// <remarks>For <see cref="ConcurrentStack{T}"/>, this operation
+ /// will always insert the object onto the top of the <see cref="ConcurrentStack{T}"/>
+ /// and return true.</remarks>
+ bool IProducerConsumerCollection<T>.TryAdd(T item)
+ {
+ Push(item);
+ return true;
+ }
+
+ /// <summary>
+ /// Attempts to return an object from the top of the <see cref="ConcurrentStack{T}"/>
+ /// without removing it.
+ /// </summary>
+ /// <param name="result">When this method returns, <paramref name="result"/> contains an object from
+ /// the top of the <see cref="T:System.Collections.Concurrent.ConccurrentStack{T}"/> or an
+ /// unspecified value if the operation failed.</param>
+ /// <returns>true if and object was returned successfully; otherwise, false.</returns>
+ public bool TryPeek(out T result)
+ {
+ Node head = m_head;
+
+ // If the stack is empty, return false; else return the element and true.
+ if (head == null)
+ {
+ result = default(T);
+ return false;
+ }
+ else
+ {
+ result = head.m_value;
+ return true;
+ }
+ }
+
+ /// <summary>
+ /// Attempts to pop and return the object at the top of the <see cref="ConcurrentStack{T}"/>.
+ /// </summary>
+ /// <param name="result">
+ /// When this method returns, if the operation was successful, <paramref name="result"/> contains the
+ /// object removed. If no object was available to be removed, the value is unspecified.
+ /// </param>
+ /// <returns>true if an element was removed and returned from the top of the <see
+ /// cref="ConcurrentStack{T}"/>
+ /// succesfully; otherwise, false.</returns>
+ public bool TryPop(out T result)
+ {
+ Node head = m_head;
+ //stack is empty
+ if (head == null)
+ {
+ result = default(T);
+ return false;
+ }
+ if (Interlocked.CompareExchange(ref m_head, head.m_next, head) == head)
+ {
+ result = head.m_value;
+ return true;
+ }
+
+ // Fall through to the slow path.
+ return TryPopCore(out result);
+ }
+
+ /// <summary>
+ /// Attempts to pop and return multiple objects from the top of the <see cref="ConcurrentStack{T}"/>
+ /// atomically.
+ /// </summary>
+ /// <param name="items">
+ /// The <see cref="T:System.Array"/> to which objects popped from the top of the <see
+ /// cref="ConcurrentStack{T}"/> will be added.
+ /// </param>
+ /// <returns>The number of objects successfully popped from the top of the <see
+ /// cref="ConcurrentStack{T}"/> and inserted in
+ /// <paramref name="items"/>.</returns>
+ /// <exception cref="ArgumentNullException"><paramref name="items"/> is a null argument (Nothing
+ /// in Visual Basic).</exception>
+ /// <remarks>
+ /// When popping multiple items, if there is little contention on the stack, using
+ /// TryPopRange can be more efficient than using <see cref="TryPop"/>
+ /// once per item to be removed. Nodes fill the <paramref name="items"/>
+ /// with the first node to be popped at the startIndex, the second node to be popped
+ /// at startIndex + 1, and so on.
+ /// </remarks>
+ public int TryPopRange(T[] items)
+ {
+ if (items == null)
+ {
+ throw new ArgumentNullException("items");
+ }
+
+ return TryPopRange(items, 0, items.Length);
+ }
+
+ /// <summary>
+ /// Attempts to pop and return multiple objects from the top of the <see cref="ConcurrentStack{T}"/>
+ /// atomically.
+ /// </summary>
+ /// <param name="items">
+ /// The <see cref="T:System.Array"/> to which objects popped from the top of the <see
+ /// cref="ConcurrentStack{T}"/> will be added.
+ /// </param>
+ /// <param name="startIndex">The zero-based offset in <paramref name="items"/> at which to begin
+ /// inserting elements from the top of the <see cref="ConcurrentStack{T}"/>.</param>
+ /// <param name="count">The number of elements to be popped from top of the <see
+ /// cref="ConcurrentStack{T}"/> and inserted into <paramref name="items"/>.</param>
+ /// <returns>The number of objects successfully popped from the top of
+ /// the <see cref="ConcurrentStack{T}"/> and inserted in <paramref name="items"/>.</returns>
+ /// <exception cref="ArgumentNullException"><paramref name="items"/> is a null reference
+ /// (Nothing in Visual Basic).</exception>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="startIndex"/> or <paramref
+ /// name="count"/> is negative. Or <paramref name="startIndex"/> is greater than or equal to the length
+ /// of <paramref name="items"/>.</exception>
+ /// <exception cref="ArgumentException"><paramref name="startIndex"/> + <paramref name="count"/> is
+ /// greater than the length of <paramref name="items"/>.</exception>
+ /// <remarks>
+ /// When popping multiple items, if there is little contention on the stack, using
+ /// TryPopRange can be more efficient than using <see cref="TryPop"/>
+ /// once per item to be removed. Nodes fill the <paramref name="items"/>
+ /// with the first node to be popped at the startIndex, the second node to be popped
+ /// at startIndex + 1, and so on.
+ /// </remarks>
+ public int TryPopRange(T[] items, int startIndex, int count)
+ {
+ ValidatePushPopRangeInput(items, startIndex, count);
+
+ // No op if the count is zero
+ if (count == 0)
+ return 0;
+
+ Node poppedHead;
+ int nodesCount = TryPopCore(count, out poppedHead);
+ if (nodesCount > 0)
+ {
+ CopyRemovedItems(poppedHead, items, startIndex, nodesCount);
+
+ }
+ return nodesCount;
+
+ }
+
+ /// <summary>
+ /// Local helper function to Pop an item from the stack, slow path
+ /// </summary>
+ /// <param name="result">The popped item</param>
+ /// <returns>True if succeeded, false otherwise</returns>
+ private bool TryPopCore(out T result)
+ {
+ Node poppedNode;
+
+ if (TryPopCore(1, out poppedNode) == 1)
+ {
+ result = poppedNode.m_value;
+ return true;
+ }
+
+ result = default(T);
+ return false;
+
+ }
+
+ /// <summary>
+ /// Slow path helper for TryPop. This method assumes an initial attempt to pop an element
+ /// has already occurred and failed, so it begins spinning right away.
+ /// </summary>
+ /// <param name="count">The number of items to pop.</param>
+ /// <param name="poppedHead">
+ /// When this method returns, if the pop succeeded, contains the removed object. If no object was
+ /// available to be removed, the value is unspecified. This parameter is passed uninitialized.
+ /// </param>
+ /// <returns>True if an element was removed and returned; otherwise, false.</returns>
+ private int TryPopCore(int count, out Node poppedHead)
+ {
+ SpinWait spin = new SpinWait();
+
+ // Try to CAS the head with its current next. We stop when we succeed or
+ // when we notice that the stack is empty, whichever comes first.
+ Node head;
+ Node next;
+ int backoff = 1;
+ Random r = new Random(Environment.TickCount & Int32.MaxValue); // avoid the case where TickCount could return Int32.MinValue
+ while (true)
+ {
+ head = m_head;
+ // Is the stack empty?
+ if (head == null)
+ {
+#if !FEATURE_CORECLR
+ if (count == 1 && CDSCollectionETWBCLProvider.Log.IsEnabled())
+ {
+ CDSCollectionETWBCLProvider.Log.ConcurrentStack_FastPopFailed(spin.Count);
+ }
+#endif //!FEATURE_CORECLR
+ poppedHead = null;
+ return 0;
+ }
+ next = head;
+ int nodesCount = 1;
+ for (; nodesCount < count && next.m_next != null; nodesCount++)
+ {
+ next = next.m_next;
+ }
+
+ // Try to swap the new head. If we succeed, break out of the loop.
+ if (Interlocked.CompareExchange(ref m_head, next.m_next, head) == head)
+ {
+#if !FEATURE_CORECLR
+ if (count == 1 && CDSCollectionETWBCLProvider.Log.IsEnabled())
+ {
+ CDSCollectionETWBCLProvider.Log.ConcurrentStack_FastPopFailed(spin.Count);
+ }
+#endif //!FEATURE_CORECLR
+ // Return the popped Node.
+ poppedHead = head;
+ return nodesCount;
+ }
+
+ // We failed to CAS the new head. Spin briefly and retry.
+ for (int i = 0; i < backoff; i++)
+ {
+ spin.SpinOnce();
+ }
+
+ backoff = spin.NextSpinWillYield ? r.Next(1, BACKOFF_MAX_YIELDS) : backoff * 2;
+ }
+ }
+
+
+ /// <summary>
+ /// Local helper function to copy the poped elements into a given collection
+ /// </summary>
+ /// <param name="head">The head of the list to be copied</param>
+ /// <param name="collection">The collection to place the popped items in</param>
+ /// <param name="startIndex">the beginning of index of where to place the popped items</param>
+ /// <param name="nodesCount">The number of nodes.</param>
+ private void CopyRemovedItems(Node head, T[] collection, int startIndex, int nodesCount)
+ {
+ Node current = head;
+ for (int i = startIndex; i < startIndex + nodesCount; i++)
+ {
+ collection[i] = current.m_value;
+ current = current.m_next;
+ }
+
+ }
+
+ /// <summary>
+ /// Attempts to remove and return an object from the <see
+ /// cref="T:System.Collections.Concurrent.IProducerConsumerCollection{T}"/>.
+ /// </summary>
+ /// <param name="item">
+ /// When this method returns, if the operation was successful, <paramref name="item"/> contains the
+ /// object removed. If no object was available to be removed, the value is unspecified.
+ /// </param>
+ /// <returns>true if an element was removed and returned succesfully; otherwise, false.</returns>
+ /// <remarks>For <see cref="ConcurrentStack{T}"/>, this operation will attempt to pope the object at
+ /// the top of the <see cref="ConcurrentStack{T}"/>.
+ /// </remarks>
+ bool IProducerConsumerCollection<T>.TryTake(out T item)
+ {
+ return TryPop(out item);
+ }
+
+ /// <summary>
+ /// Copies the items stored in the <see cref="ConcurrentStack{T}"/> to a new array.
+ /// </summary>
+ /// <returns>A new array containing a snapshot of elements copied from the <see
+ /// cref="ConcurrentStack{T}"/>.</returns>
+ public T[] ToArray()
+ {
+ return ToList().ToArray();
+ }
+
+ /// <summary>
+ /// Returns an array containing a snapshot of the list's contents, using
+ /// the target list node as the head of a region in the list.
+ /// </summary>
+ /// <returns>An array of the list's contents.</returns>
+ private List<T> ToList()
+ {
+ List<T> list = new List<T>();
+
+ Node curr = m_head;
+ while (curr != null)
+ {
+ list.Add(curr.m_value);
+ curr = curr.m_next;
+ }
+
+ return list;
+ }
+
+ /// <summary>
+ /// Returns an enumerator that iterates through the <see cref="ConcurrentStack{T}"/>.
+ /// </summary>
+ /// <returns>An enumerator for the <see cref="ConcurrentStack{T}"/>.</returns>
+ /// <remarks>
+ /// The enumeration represents a moment-in-time snapshot of the contents
+ /// of the stack. It does not reflect any updates to the collection after
+ /// <see cref="GetEnumerator"/> was called. The enumerator is safe to use
+ /// concurrently with reads from and writes to the stack.
+ /// </remarks>
+ public IEnumerator<T> GetEnumerator()
+ {
+ // Returns an enumerator for the stack. This effectively takes a snapshot
+ // of the stack's contents at the time of the call, i.e. subsequent modifications
+ // (pushes or pops) will not be reflected in the enumerator's contents.
+
+ //If we put yield-return here, the iterator will be lazily evaluated. As a result a snapshot of
+ //the stack is not taken when GetEnumerator is initialized but when MoveNext() is first called.
+ //This is inconsistent with existing generic collections. In order to prevent it, we capture the
+ //value of m_head in a buffer and call out to a helper method
+ return GetEnumerator(m_head);
+ }
+
+ private IEnumerator<T> GetEnumerator(Node head)
+ {
+ Node current = head;
+ while (current != null)
+ {
+ yield return current.m_value;
+ current = current.m_next;
+ }
+ }
+
+ /// <summary>
+ /// Returns an enumerator that iterates through a collection.
+ /// </summary>
+ /// <returns>An <see cref="T:System.Collections.IEnumerator"/> that can be used to iterate through
+ /// the collection.</returns>
+ /// <remarks>
+ /// The enumeration represents a moment-in-time snapshot of the contents of the stack. It does not
+ /// reflect any updates to the collection after
+ /// <see cref="GetEnumerator"/> was called. The enumerator is safe to use concurrently with reads
+ /// from and writes to the stack.
+ /// </remarks>
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return ((IEnumerable<T>)this).GetEnumerator();
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Concurrent/IProducerConsumerCollection.cs b/src/mscorlib/src/System/Collections/Concurrent/IProducerConsumerCollection.cs
new file mode 100644
index 0000000000..a74f69069a
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Concurrent/IProducerConsumerCollection.cs
@@ -0,0 +1,116 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// A common interface for all concurrent collections.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace System.Collections.Concurrent
+{
+
+ /// <summary>
+ /// Defines methods to manipulate thread-safe collections intended for producer/consumer usage.
+ /// </summary>
+ /// <typeparam name="T">Specifies the type of elements in the collection.</typeparam>
+ /// <remarks>
+ /// All implementations of this interface must enable all members of this interface
+ /// to be used concurrently from multiple threads.
+ /// </remarks>
+ public interface IProducerConsumerCollection<T> : IEnumerable<T>, ICollection
+ {
+
+ /// <summary>
+ /// Copies the elements of the <see cref="IProducerConsumerCollection{T}"/> to
+ /// an
+ /// <see cref="T:System.Array"/>, starting at a specified index.
+ /// </summary>
+ /// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of
+ /// the elements copied from the <see cref="IProducerConsumerCollection{T}"/>.
+ /// The array must have zero-based indexing.</param>
+ /// <param name="index">The zero-based index in <paramref name="array"/> at which copying
+ /// begins.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="array"/> is a null reference (Nothing in
+ /// Visual Basic).</exception>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than
+ /// zero.</exception>
+ /// <exception cref="ArgumentException"><paramref name="index"/> is equal to or greater than the
+ /// length of the <paramref name="array"/>
+ /// -or- The number of elements in the source <see cref="ConcurrentQueue{T}"/> is greater than the
+ /// available space from <paramref name="index"/> to the end of the destination <paramref
+ /// name="array"/>.
+ /// </exception>
+ void CopyTo(T[] array, int index);
+
+ /// <summary>
+ /// Attempts to add an object to the <see
+ /// cref="IProducerConsumerCollection{T}"/>.
+ /// </summary>
+ /// <param name="item">The object to add to the <see
+ /// cref="IProducerConsumerCollection{T}"/>.</param>
+ /// <returns>true if the object was added successfully; otherwise, false.</returns>
+ /// <exception cref="T:System.ArgumentException">The <paramref name="item"/> was invalid for this collection.</exception>
+ bool TryAdd(T item);
+
+ /// <summary>
+ /// Attempts to remove and return an object from the <see cref="IProducerConsumerCollection{T}"/>.
+ /// </summary>
+ /// <param name="item">
+ /// When this method returns, if the object was removed and returned successfully, <paramref
+ /// name="item"/> contains the removed object. If no object was available to be removed, the value is
+ /// unspecified.
+ /// </param>
+ /// <returns>true if an object was removed and returned successfully; otherwise, false.</returns>
+ bool TryTake(out T item);
+
+ /// <summary>
+ /// Copies the elements contained in the <see cref="IProducerConsumerCollection{T}"/> to a new array.
+ /// </summary>
+ /// <returns>A new array containing the elements copied from the <see cref="IProducerConsumerCollection{T}"/>.</returns>
+ T[] ToArray();
+
+ }
+
+
+ /// <summary>
+ /// A debugger view of the IProducerConsumerCollection that makes it simple to browse the
+ /// collection's contents at a point in time.
+ /// </summary>
+ /// <typeparam name="T">The type of elements stored within.</typeparam>
+ internal sealed class SystemCollectionsConcurrent_ProducerConsumerCollectionDebugView<T>
+ {
+ private IProducerConsumerCollection<T> m_collection; // The collection being viewed.
+
+ /// <summary>
+ /// Constructs a new debugger view object for the provided collection object.
+ /// </summary>
+ /// <param name="collection">A collection to browse in the debugger.</param>
+ public SystemCollectionsConcurrent_ProducerConsumerCollectionDebugView(IProducerConsumerCollection<T> collection)
+ {
+ if (collection == null)
+ {
+ throw new ArgumentNullException("collection");
+ }
+
+ m_collection = collection;
+ }
+
+ /// <summary>
+ /// Returns a snapshot of the underlying collection's elements.
+ /// </summary>
+ [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+ public T[] Items
+ {
+ get { return m_collection.ToArray(); }
+ }
+
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Concurrent/OrderablePartitioner.cs b/src/mscorlib/src/System/Collections/Concurrent/OrderablePartitioner.cs
new file mode 100644
index 0000000000..02263b7f97
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Concurrent/OrderablePartitioner.cs
@@ -0,0 +1,281 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+//
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Collections.Generic;
+using System.Security.Permissions;
+using System.Threading;
+
+namespace System.Collections.Concurrent
+{
+
+ /// <summary>
+ /// Represents a particular manner of splitting an orderable data source into multiple partitions.
+ /// </summary>
+ /// <typeparam name="TSource">Type of the elements in the collection.</typeparam>
+ /// <remarks>
+ /// <para>
+ /// Each element in each partition has an integer index associated with it, which determines the relative
+ /// order of that element against elements in other partitions.
+ /// </para>
+ /// <para>
+ /// Inheritors of <see cref="OrderablePartitioner{TSource}"/> must adhere to the following rules:
+ /// <ol>
+ /// <li>All indices must be unique, such that there may not be duplicate indices. If all indices are not
+ /// unique, the output ordering may be scrambled.</li>
+ /// <li>All indices must be non-negative. If any indices are negative, consumers of the implementation
+ /// may throw exceptions.</li>
+ /// <li><see cref="GetPartitions"/> and <see cref="GetOrderablePartitions"/> should throw a
+ /// <see cref="T:System.ArgumentOutOfRangeException"/> if the requested partition count is less than or
+ /// equal to zero.</li>
+ /// <li><see cref="GetPartitions"/> and <see cref="GetOrderablePartitions"/> should always return a number
+ /// of enumerables equal to the requested partition count. If the partitioner runs out of data and cannot
+ /// create as many partitions as requested, an empty enumerator should be returned for each of the
+ /// remaining partitions. If this rule is not followed, consumers of the implementation may throw a <see
+ /// cref="T:System.InvalidOperationException"/>.</li>
+ /// <li><see cref="GetPartitions"/>, <see cref="GetOrderablePartitions"/>,
+ /// <see cref="GetDynamicPartitions"/>, and <see cref="GetOrderableDynamicPartitions"/>
+ /// should never return null. If null is returned, a consumer of the implementation may throw a
+ /// <see cref="T:System.InvalidOperationException"/>.</li>
+ /// <li><see cref="GetPartitions"/>, <see cref="GetOrderablePartitions"/>,
+ /// <see cref="GetDynamicPartitions"/>, and <see cref="GetOrderableDynamicPartitions"/>
+ /// should always return partitions that can fully and uniquely enumerate the input data source. All of
+ /// the data and only the data contained in the input source should be enumerated, with no duplication
+ /// that was not already in the input, unless specifically required by the particular partitioner's
+ /// design. If this is not followed, the output ordering may be scrambled.</li>
+ /// <li>If <see cref="KeysOrderedInEachPartition"/> returns true, each partition must return elements
+ /// with increasing key indices.</li>
+ /// <li>If <see cref="KeysOrderedAcrossPartitions"/> returns true, all the keys in partition numbered N
+ /// must be larger than all the keys in partition numbered N-1.</li>
+ /// <li>If <see cref="KeysNormalized"/> returns true, all indices must be monotonically increasing from
+ /// 0, though not necessarily within a single partition.</li>
+ /// </ol>
+ /// </para>
+ /// </remarks>
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ public abstract class OrderablePartitioner<TSource> : Partitioner<TSource>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OrderablePartitioner{TSource}"/> class with the
+ /// specified constraints on the index keys.
+ /// </summary>
+ /// <param name="keysOrderedInEachPartition">
+ /// Indicates whether the elements in each partition are yielded in the order of
+ /// increasing keys.
+ /// </param>
+ /// <param name="keysOrderedAcrossPartitions">
+ /// Indicates whether elements in an earlier partition always come before
+ /// elements in a later partition. If true, each element in partition 0 has a smaller order key than
+ /// any element in partition 1, each element in partition 1 has a smaller order key than any element
+ /// in partition 2, and so on.
+ /// </param>
+ /// <param name="keysNormalized">
+ /// Indicates whether keys are normalized. If true, all order keys are distinct
+ /// integers in the range [0 .. numberOfElements-1]. If false, order keys must still be dictinct, but
+ /// only their relative order is considered, not their absolute values.
+ /// </param>
+ protected OrderablePartitioner(bool keysOrderedInEachPartition, bool keysOrderedAcrossPartitions, bool keysNormalized)
+ {
+ KeysOrderedInEachPartition = keysOrderedInEachPartition;
+ KeysOrderedAcrossPartitions = keysOrderedAcrossPartitions;
+ KeysNormalized = keysNormalized;
+ }
+
+ /// <summary>
+ /// Partitions the underlying collection into the specified number of orderable partitions.
+ /// </summary>
+ /// <remarks>
+ /// Each partition is represented as an enumerator over key-value pairs.
+ /// The value of the pair is the element itself, and the key is an integer which determines
+ /// the relative ordering of this element against other elements in the data source.
+ /// </remarks>
+ /// <param name="partitionCount">The number of partitions to create.</param>
+ /// <returns>A list containing <paramref name="partitionCount"/> enumerators.</returns>
+ public abstract IList<IEnumerator<KeyValuePair<long, TSource>>> GetOrderablePartitions(int partitionCount);
+
+ /// <summary>
+ /// Creates an object that can partition the underlying collection into a variable number of
+ /// partitions.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// The returned object implements the <see
+ /// cref="T:System.Collections.Generic.IEnumerable{TSource}"/> interface. Calling <see
+ /// cref="System.Collections.Generic.IEnumerable{TSource}.GetEnumerator">GetEnumerator</see> on the
+ /// object creates another partition over the sequence.
+ /// </para>
+ /// <para>
+ /// Each partition is represented as an enumerator over key-value pairs. The value in the pair is the element
+ /// itself, and the key is an integer which determines the relative ordering of this element against
+ /// other elements.
+ /// </para>
+ /// <para>
+ /// The <see cref="GetOrderableDynamicPartitions"/> method is only supported if the <see
+ /// cref="System.Collections.Concurrent.Partitioner{TSource}.SupportsDynamicPartitions">SupportsDynamicPartitions</see>
+ /// property returns true.
+ /// </para>
+ /// </remarks>
+ /// <returns>An object that can create partitions over the underlying data source.</returns>
+ /// <exception cref="NotSupportedException">Dynamic partitioning is not supported by this
+ /// partitioner.</exception>
+ public virtual IEnumerable<KeyValuePair<long, TSource>> GetOrderableDynamicPartitions()
+ {
+ throw new NotSupportedException(Environment.GetResourceString("Partitioner_DynamicPartitionsNotSupported"));
+ }
+
+ /// <summary>
+ /// Gets whether elements in each partition are yielded in the order of increasing keys.
+ /// </summary>
+ public bool KeysOrderedInEachPartition { get; private set; }
+
+ /// <summary>
+ /// Gets whether elements in an earlier partition always come before elements in a later partition.
+ /// </summary>
+ /// <remarks>
+ /// If <see cref="KeysOrderedAcrossPartitions"/> returns true, each element in partition 0 has a
+ /// smaller order key than any element in partition 1, each element in partition 1 has a smaller
+ /// order key than any element in partition 2, and so on.
+ /// </remarks>
+ public bool KeysOrderedAcrossPartitions { get; private set; }
+
+ /// <summary>
+ /// Gets whether order keys are normalized.
+ /// </summary>
+ /// <remarks>
+ /// If <see cref="KeysNormalized"/> returns true, all order keys are distinct integers in the range
+ /// [0 .. numberOfElements-1]. If the property returns false, order keys must still be dictinct, but
+ /// only their relative order is considered, not their absolute values.
+ /// </remarks>
+ public bool KeysNormalized { get; private set; }
+
+ /// <summary>
+ /// Partitions the underlying collection into the given number of ordered partitions.
+ /// </summary>
+ /// <remarks>
+ /// The default implementation provides the same behavior as <see cref="GetOrderablePartitions"/> except
+ /// that the returned set of partitions does not provide the keys for the elements.
+ /// </remarks>
+ /// <param name="partitionCount">The number of partitions to create.</param>
+ /// <returns>A list containing <paramref name="partitionCount"/> enumerators.</returns>
+ public override IList<IEnumerator<TSource>> GetPartitions(int partitionCount)
+ {
+ IList<IEnumerator<KeyValuePair<long, TSource>>> orderablePartitions = GetOrderablePartitions(partitionCount);
+
+ if (orderablePartitions.Count != partitionCount)
+ {
+ throw new InvalidOperationException("OrderablePartitioner_GetPartitions_WrongNumberOfPartitions");
+ }
+
+ IEnumerator<TSource>[] partitions = new IEnumerator<TSource>[partitionCount];
+ for (int i = 0; i < partitionCount; i++)
+ {
+ partitions[i] = new EnumeratorDropIndices(orderablePartitions[i]);
+ }
+ return partitions;
+ }
+
+ /// <summary>
+ /// Creates an object that can partition the underlying collection into a variable number of
+ /// partitions.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// The returned object implements the <see
+ /// cref="T:System.Collections.Generic.IEnumerable{TSource}"/> interface. Calling <see
+ /// cref="System.Collections.Generic.IEnumerable{TSource}.GetEnumerator">GetEnumerator</see> on the
+ /// object creates another partition over the sequence.
+ /// </para>
+ /// <para>
+ /// The default implementation provides the same behavior as <see cref="GetOrderableDynamicPartitions"/> except
+ /// that the returned set of partitions does not provide the keys for the elements.
+ /// </para>
+ /// <para>
+ /// The <see cref="GetDynamicPartitions"/> method is only supported if the <see
+ /// cref="System.Collections.Concurrent.Partitioner{TSource}.SupportsDynamicPartitions"/>
+ /// property returns true.
+ /// </para>
+ /// </remarks>
+ /// <returns>An object that can create partitions over the underlying data source.</returns>
+ /// <exception cref="NotSupportedException">Dynamic partitioning is not supported by this
+ /// partitioner.</exception>
+ public override IEnumerable<TSource> GetDynamicPartitions()
+ {
+ IEnumerable<KeyValuePair<long, TSource>> orderablePartitions = GetOrderableDynamicPartitions();
+ return new EnumerableDropIndices(orderablePartitions);
+ }
+
+ /// <summary>
+ /// Converts an enumerable over key-value pairs to an enumerable over values.
+ /// </summary>
+ private class EnumerableDropIndices : IEnumerable<TSource>, IDisposable
+ {
+ private readonly IEnumerable<KeyValuePair<long, TSource>> m_source;
+ public EnumerableDropIndices(IEnumerable<KeyValuePair<long, TSource>> source)
+ {
+ m_source = source;
+ }
+ public IEnumerator<TSource> GetEnumerator()
+ {
+ return new EnumeratorDropIndices(m_source.GetEnumerator());
+ }
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return ((EnumerableDropIndices)this).GetEnumerator();
+ }
+ public void Dispose()
+ {
+ IDisposable d = m_source as IDisposable;
+ if (d != null)
+ {
+ d.Dispose();
+ }
+ }
+ }
+
+ private class EnumeratorDropIndices : IEnumerator<TSource>
+ {
+ private readonly IEnumerator<KeyValuePair<long, TSource>> m_source;
+ public EnumeratorDropIndices(IEnumerator<KeyValuePair<long, TSource>> source)
+ {
+ m_source = source;
+ }
+ public bool MoveNext()
+ {
+ return m_source.MoveNext();
+ }
+ public TSource Current
+ {
+ get
+ {
+ return m_source.Current.Value;
+ }
+ }
+ Object IEnumerator.Current
+ {
+ get
+ {
+ return ((EnumeratorDropIndices)this).Current;
+ }
+ }
+ public void Dispose()
+ {
+ m_source.Dispose();
+ }
+ public void Reset()
+ {
+ m_source.Reset();
+ }
+ }
+
+ }
+
+}
diff --git a/src/mscorlib/src/System/Collections/Concurrent/Partitioner.cs b/src/mscorlib/src/System/Collections/Concurrent/Partitioner.cs
new file mode 100644
index 0000000000..3d54c1471b
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Concurrent/Partitioner.cs
@@ -0,0 +1,102 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// Represents a particular way of splitting a collection into multiple partitions.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Collections.Generic;
+using System.Security.Permissions;
+using System.Threading;
+
+namespace System.Collections.Concurrent
+{
+ /// <summary>
+ /// Represents a particular manner of splitting a data source into multiple partitions.
+ /// </summary>
+ /// <typeparam name="TSource">Type of the elements in the collection.</typeparam>
+ /// <remarks>
+ /// <para>
+ /// Inheritors of <see cref="Partitioner{TSource}"/> must adhere to the following rules:
+ /// <ol>
+ /// <li><see cref="GetPartitions"/> should throw a
+ /// <see cref="T:System.ArgumentOutOfRangeException"/> if the requested partition count is less than or
+ /// equal to zero.</li>
+ /// <li><see cref="GetPartitions"/> should always return a number of enumerables equal to the requested
+ /// partition count. If the partitioner runs out of data and cannot create as many partitions as
+ /// requested, an empty enumerator should be returned for each of the remaining partitions. If this rule
+ /// is not followed, consumers of the implementation may throw a <see
+ /// cref="T:System.InvalidOperationException"/>.</li>
+ /// <li><see cref="GetPartitions"/> and <see cref="GetDynamicPartitions"/>
+ /// should never return null. If null is returned, a consumer of the implementation may throw a
+ /// <see cref="T:System.InvalidOperationException"/>.</li>
+ /// <li><see cref="GetPartitions"/> and <see cref="GetDynamicPartitions"/> should always return
+ /// partitions that can fully and uniquely enumerate the input data source. All of the data and only the
+ /// data contained in the input source should be enumerated, with no duplication that was not already in
+ /// the input, unless specifically required by the particular partitioner's design. If this is not
+ /// followed, the output ordering may be scrambled.</li>
+ /// </ol>
+ /// </para>
+ /// </remarks>
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ public abstract class Partitioner<TSource>
+ {
+ /// <summary>
+ /// Partitions the underlying collection into the given number of partitions.
+ /// </summary>
+ /// <param name="partitionCount">The number of partitions to create.</param>
+ /// <returns>A list containing <paramref name="partitionCount"/> enumerators.</returns>
+ public abstract IList<IEnumerator<TSource>> GetPartitions(int partitionCount);
+
+ /// <summary>
+ /// Gets whether additional partitions can be created dynamically.
+ /// </summary>
+ /// <returns>
+ /// true if the <see cref="Partitioner{TSource}"/> can create partitions dynamically as they are
+ /// requested; false if the <see cref="Partitioner{TSource}"/> can only allocate
+ /// partitions statically.
+ /// </returns>
+ /// <remarks>
+ /// <para>
+ /// If a derived class does not override and implement <see cref="GetDynamicPartitions"/>,
+ /// <see cref="SupportsDynamicPartitions"/> should return false. The value of <see
+ /// cref="SupportsDynamicPartitions"/> should not vary over the lifetime of this instance.
+ /// </para>
+ /// </remarks>
+ public virtual bool SupportsDynamicPartitions
+ {
+ get { return false; }
+ }
+
+ /// <summary>
+ /// Creates an object that can partition the underlying collection into a variable number of
+ /// partitions.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// The returned object implements the <see
+ /// cref="T:System.Collections.Generic.IEnumerable{TSource}"/> interface. Calling <see
+ /// cref="System.Collections.Generic.IEnumerable{TSource}.GetEnumerator">GetEnumerator</see> on the
+ /// object creates another partition over the sequence.
+ /// </para>
+ /// <para>
+ /// The <see cref="GetDynamicPartitions"/> method is only supported if the <see
+ /// cref="SupportsDynamicPartitions"/>
+ /// property returns true.
+ /// </para>
+ /// </remarks>
+ /// <returns>An object that can create partitions over the underlying data source.</returns>
+ /// <exception cref="NotSupportedException">Dynamic partitioning is not supported by this
+ /// partitioner.</exception>
+ public virtual IEnumerable<TSource> GetDynamicPartitions()
+ {
+ throw new NotSupportedException(Environment.GetResourceString("Partitioner_DynamicPartitionsNotSupported"));
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Concurrent/PartitionerStatic.cs b/src/mscorlib/src/System/Collections/Concurrent/PartitionerStatic.cs
new file mode 100644
index 0000000000..2169c6dee7
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Concurrent/PartitionerStatic.cs
@@ -0,0 +1,1733 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+#pragma warning disable 0420
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// A class of default partitioners for Partitioner<TSource>
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Generic;
+using System.Security.Permissions;
+using System.Threading;
+using System.Diagnostics.Contracts;
+using System.Runtime.InteropServices;
+
+namespace System.Collections.Concurrent
+{
+ /// <summary>
+ /// Out-of-the-box partitioners are created with a set of default behaviors.
+ /// For example, by default, some form of buffering and chunking will be employed to achieve
+ /// optimal performance in the common scenario where an IEnumerable<> implementation is fast and
+ /// non-blocking. These behaviors can be overridden via this enumeration.
+ /// </summary>
+ [Flags]
+#if !FEATURE_CORECLR
+ [Serializable]
+#endif
+ public enum EnumerablePartitionerOptions
+ {
+ /// <summary>
+ /// Use the default behavior (i.e., use buffering to achieve optimal performance)
+ /// </summary>
+ None = 0x0,
+
+ /// <summary>
+ /// Creates a partitioner that will take items from the source enumerable one at a time
+ /// and will not use intermediate storage that can be accessed more efficiently by multiple threads.
+ /// This option provides support for low latency (items will be processed as soon as they are available from
+ /// the source) and partial support for dependencies between items (a thread cannot deadlock waiting for an item
+ /// that it, itself, is responsible for processing).
+ /// </summary>
+ NoBuffering = 0x1
+ }
+
+ // The static class Partitioners implements 3 default partitioning strategies:
+ // 1. dynamic load balance partitioning for indexable data source (IList and arrays)
+ // 2. static partitioning for indexable data source (IList and arrays)
+ // 3. dynamic load balance partitioning for enumerables. Enumerables have indexes, which are the natural order
+ // of elements, but enuemrators are not indexable
+ // - data source of type IList/arrays have both dynamic and static partitioning, as 1 and 3.
+ // We assume that the source data of IList/Array is not changing concurrently.
+ // - data source of type IEnumerable can only be partitioned dynamically (load-balance)
+ // - Dynamic partitioning methods 1 and 3 are same, both being dynamic and load-balance. But the
+ // implementation is different for data source of IList/Array vs. IEnumerable:
+ // * When the source collection is IList/Arrays, we use Interlocked on the shared index;
+ // * When the source collection is IEnumerable, we use Monitor to wrap around the access to the source
+ // enumerator.
+
+ /// <summary>
+ /// Provides common partitioning strategies for arrays, lists, and enumerables.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// The static methods on <see cref="Partitioner"/> are all thread-safe and may be used concurrently
+ /// from multiple threads. However, while a created partitioner is in use, the underlying data source
+ /// should not be modified, whether from the same thread that's using a partitioner or from a separate
+ /// thread.
+ /// </para>
+ /// </remarks>
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ public static class Partitioner
+ {
+ /// <summary>
+ /// Creates an orderable partitioner from an <see cref="System.Collections.Generic.IList{T}"/>
+ /// instance.
+ /// </summary>
+ /// <typeparam name="TSource">Type of the elements in source list.</typeparam>
+ /// <param name="list">The list to be partitioned.</param>
+ /// <param name="loadBalance">
+ /// A Boolean value that indicates whether the created partitioner should dynamically
+ /// load balance between partitions rather than statically partition.
+ /// </param>
+ /// <returns>
+ /// An orderable partitioner based on the input list.
+ /// </returns>
+ public static OrderablePartitioner<TSource> Create<TSource>(IList<TSource> list, bool loadBalance)
+ {
+ if (list == null)
+ {
+ throw new ArgumentNullException("list");
+ }
+ if (loadBalance)
+ {
+ return (new DynamicPartitionerForIList<TSource>(list));
+ }
+ else
+ {
+ return (new StaticIndexRangePartitionerForIList<TSource>(list));
+ }
+ }
+
+ /// <summary>
+ /// Creates an orderable partitioner from a <see cref="System.Array"/> instance.
+ /// </summary>
+ /// <typeparam name="TSource">Type of the elements in source array.</typeparam>
+ /// <param name="array">The array to be partitioned.</param>
+ /// <param name="loadBalance">
+ /// A Boolean value that indicates whether the created partitioner should dynamically load balance
+ /// between partitions rather than statically partition.
+ /// </param>
+ /// <returns>
+ /// An orderable partitioner based on the input array.
+ /// </returns>
+ public static OrderablePartitioner<TSource> Create<TSource>(TSource[] array, bool loadBalance)
+ {
+ // This implementation uses 'ldelem' instructions for element retrieval, rather than using a
+ // method call.
+
+ if (array == null)
+ {
+ throw new ArgumentNullException("array");
+ }
+ if (loadBalance)
+ {
+ return (new DynamicPartitionerForArray<TSource>(array));
+ }
+ else
+ {
+ return (new StaticIndexRangePartitionerForArray<TSource>(array));
+ }
+ }
+
+ /// <summary>
+ /// Creates an orderable partitioner from a <see cref="System.Collections.Generic.IEnumerable{TSource}"/> instance.
+ /// </summary>
+ /// <typeparam name="TSource">Type of the elements in source enumerable.</typeparam>
+ /// <param name="source">The enumerable to be partitioned.</param>
+ /// <returns>
+ /// An orderable partitioner based on the input array.
+ /// </returns>
+ /// <remarks>
+ /// The ordering used in the created partitioner is determined by the natural order of the elements
+ /// as retrieved from the source enumerable.
+ /// </remarks>
+ public static OrderablePartitioner<TSource> Create<TSource>(IEnumerable<TSource> source)
+ {
+ return Create<TSource>(source, EnumerablePartitionerOptions.None);
+ }
+
+ /// <summary>
+ /// Creates an orderable partitioner from a <see cref="System.Collections.Generic.IEnumerable{TSource}"/> instance.
+ /// </summary>
+ /// <typeparam name="TSource">Type of the elements in source enumerable.</typeparam>
+ /// <param name="source">The enumerable to be partitioned.</param>
+ /// <param name="partitionerOptions">Options to control the buffering behavior of the partitioner.</param>
+ /// <exception cref="T:System.ArgumentOutOfRangeException">
+ /// The <paramref name="partitionerOptions"/> argument specifies an invalid value for <see
+ /// cref="T:System.Collections.Concurrent.EnumerablePartitionerOptions"/>.
+ /// </exception>
+ /// <returns>
+ /// An orderable partitioner based on the input array.
+ /// </returns>
+ /// <remarks>
+ /// The ordering used in the created partitioner is determined by the natural order of the elements
+ /// as retrieved from the source enumerable.
+ /// </remarks>
+ public static OrderablePartitioner<TSource> Create<TSource>(IEnumerable<TSource> source, EnumerablePartitionerOptions partitionerOptions)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+
+ if ((partitionerOptions & (~EnumerablePartitionerOptions.NoBuffering)) != 0)
+ throw new ArgumentOutOfRangeException("partitionerOptions");
+
+ return (new DynamicPartitionerForIEnumerable<TSource>(source, partitionerOptions));
+ }
+
+ /// <summary>Creates a partitioner that chunks the user-specified range.</summary>
+ /// <param name="fromInclusive">The lower, inclusive bound of the range.</param>
+ /// <param name="toExclusive">The upper, exclusive bound of the range.</param>
+ /// <returns>A partitioner.</returns>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"> The <paramref name="toExclusive"/> argument is
+ /// less than or equal to the <paramref name="fromInclusive"/> argument.</exception>
+ public static OrderablePartitioner<Tuple<long, long>> Create(long fromInclusive, long toExclusive)
+ {
+ // How many chunks do we want to divide the range into? If this is 1, then the
+ // answer is "one chunk per core". Generally, though, you'll achieve better
+ // load balancing on a busy system if you make it higher than 1.
+ int coreOversubscriptionRate = 3;
+
+ if (toExclusive <= fromInclusive) throw new ArgumentOutOfRangeException("toExclusive");
+ long rangeSize = (toExclusive - fromInclusive) /
+ (PlatformHelper.ProcessorCount * coreOversubscriptionRate);
+ if (rangeSize == 0) rangeSize = 1;
+ return Partitioner.Create(CreateRanges(fromInclusive, toExclusive, rangeSize), EnumerablePartitionerOptions.NoBuffering); // chunk one range at a time
+ }
+
+ /// <summary>Creates a partitioner that chunks the user-specified range.</summary>
+ /// <param name="fromInclusive">The lower, inclusive bound of the range.</param>
+ /// <param name="toExclusive">The upper, exclusive bound of the range.</param>
+ /// <param name="rangeSize">The size of each subrange.</param>
+ /// <returns>A partitioner.</returns>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"> The <paramref name="toExclusive"/> argument is
+ /// less than or equal to the <paramref name="fromInclusive"/> argument.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"> The <paramref name="rangeSize"/> argument is
+ /// less than or equal to 0.</exception>
+ public static OrderablePartitioner<Tuple<long, long>> Create(long fromInclusive, long toExclusive, long rangeSize)
+ {
+ if (toExclusive <= fromInclusive) throw new ArgumentOutOfRangeException("toExclusive");
+ if (rangeSize <= 0) throw new ArgumentOutOfRangeException("rangeSize");
+ return Partitioner.Create(CreateRanges(fromInclusive, toExclusive, rangeSize), EnumerablePartitionerOptions.NoBuffering); // chunk one range at a time
+ }
+
+ // Private method to parcel out range tuples.
+ private static IEnumerable<Tuple<long, long>> CreateRanges(long fromInclusive, long toExclusive, long rangeSize)
+ {
+ // Enumerate all of the ranges
+ long from, to;
+ bool shouldQuit = false;
+
+ for (long i = fromInclusive; (i < toExclusive) && !shouldQuit; i += rangeSize)
+ {
+ from = i;
+ try { checked { to = i + rangeSize; } }
+ catch (OverflowException)
+ {
+ to = toExclusive;
+ shouldQuit = true;
+ }
+ if (to > toExclusive) to = toExclusive;
+ yield return new Tuple<long, long>(from, to);
+ }
+ }
+
+ /// <summary>Creates a partitioner that chunks the user-specified range.</summary>
+ /// <param name="fromInclusive">The lower, inclusive bound of the range.</param>
+ /// <param name="toExclusive">The upper, exclusive bound of the range.</param>
+ /// <returns>A partitioner.</returns>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"> The <paramref name="toExclusive"/> argument is
+ /// less than or equal to the <paramref name="fromInclusive"/> argument.</exception>
+ public static OrderablePartitioner<Tuple<int, int>> Create(int fromInclusive, int toExclusive)
+ {
+ // How many chunks do we want to divide the range into? If this is 1, then the
+ // answer is "one chunk per core". Generally, though, you'll achieve better
+ // load balancing on a busy system if you make it higher than 1.
+ int coreOversubscriptionRate = 3;
+
+ if (toExclusive <= fromInclusive) throw new ArgumentOutOfRangeException("toExclusive");
+ int rangeSize = (toExclusive - fromInclusive) /
+ (PlatformHelper.ProcessorCount * coreOversubscriptionRate);
+ if (rangeSize == 0) rangeSize = 1;
+ return Partitioner.Create(CreateRanges(fromInclusive, toExclusive, rangeSize), EnumerablePartitionerOptions.NoBuffering); // chunk one range at a time
+ }
+
+ /// <summary>Creates a partitioner that chunks the user-specified range.</summary>
+ /// <param name="fromInclusive">The lower, inclusive bound of the range.</param>
+ /// <param name="toExclusive">The upper, exclusive bound of the range.</param>
+ /// <param name="rangeSize">The size of each subrange.</param>
+ /// <returns>A partitioner.</returns>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"> The <paramref name="toExclusive"/> argument is
+ /// less than or equal to the <paramref name="fromInclusive"/> argument.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"> The <paramref name="rangeSize"/> argument is
+ /// less than or equal to 0.</exception>
+ public static OrderablePartitioner<Tuple<int, int>> Create(int fromInclusive, int toExclusive, int rangeSize)
+ {
+ if (toExclusive <= fromInclusive) throw new ArgumentOutOfRangeException("toExclusive");
+ if (rangeSize <= 0) throw new ArgumentOutOfRangeException("rangeSize");
+ return Partitioner.Create(CreateRanges(fromInclusive, toExclusive, rangeSize), EnumerablePartitionerOptions.NoBuffering); // chunk one range at a time
+ }
+
+ // Private method to parcel out range tuples.
+ private static IEnumerable<Tuple<int, int>> CreateRanges(int fromInclusive, int toExclusive, int rangeSize)
+ {
+ // Enumerate all of the ranges
+ int from, to;
+ bool shouldQuit = false;
+
+ for (int i = fromInclusive; (i < toExclusive) && !shouldQuit; i += rangeSize)
+ {
+ from = i;
+ try { checked { to = i + rangeSize; } }
+ catch (OverflowException)
+ {
+ to = toExclusive;
+ shouldQuit = true;
+ }
+ if (to > toExclusive) to = toExclusive;
+ yield return new Tuple<int, int>(from, to);
+ }
+ }
+
+ #region DynamicPartitionEnumerator_Abstract class
+ /// <summary>
+ /// DynamicPartitionEnumerator_Abstract defines the enumerator for each partition for the dynamic load-balance
+ /// partitioning algorithm.
+ /// - Partition is an enumerator of KeyValuePairs, each corresponding to an item in the data source:
+ /// the key is the index in the source collection; the value is the item itself.
+ /// - a set of such partitions share a reader over data source. The type of the reader is specified by
+ /// TSourceReader.
+ /// - each partition requests a contiguous chunk of elements at a time from the source data. The chunk
+ /// size is initially 1, and doubles every time until it reaches the maximum chunk size.
+ /// The implementation for GrabNextChunk() method has two versions: one for data source of IndexRange
+ /// types (IList and the array), one for data source of IEnumerable.
+ /// - The method "Reset" is not supported for any partitioning algorithm.
+ /// - The implementation for MoveNext() method is same for all dynanmic partitioners, so we provide it
+ /// in this abstract class.
+ /// </summary>
+ /// <typeparam name="TSource">Type of the elements in the data source</typeparam>
+ /// <typeparam name="TSourceReader">Type of the reader on the data source</typeparam>
+ //TSourceReader is
+ // - IList<TSource>, when source data is IList<TSource>, the shared reader is source data itself
+ // - TSource[], when source data is TSource[], the shared reader is source data itself
+ // - IEnumerator<TSource>, when source data is IEnumerable<TSource>, and the shared reader is an
+ // enumerator of the source data
+ private abstract class DynamicPartitionEnumerator_Abstract<TSource, TSourceReader> : IEnumerator<KeyValuePair<long, TSource>>
+ {
+ //----------------- common fields and constructor for all dynamic partitioners -----------------
+ //--- shared by all dervied class with souce data type: IList, Array, and IEnumerator
+ protected readonly TSourceReader m_sharedReader;
+
+ protected static int s_defaultMaxChunkSize = GetDefaultChunkSize<TSource>();
+
+ //deferred allocating in MoveNext() with initial value 0, to avoid false sharing
+ //we also use the fact that: (m_currentChunkSize==null) means MoveNext is never called on this enumerator
+ protected SharedInt m_currentChunkSize;
+
+ //deferring allocation in MoveNext() with initial value -1, to avoid false sharing
+ protected SharedInt m_localOffset;
+
+ private const int CHUNK_DOUBLING_RATE = 3; // Double the chunk size every this many grabs
+ private int m_doublingCountdown; // Number of grabs remaining until chunk size doubles
+ protected readonly int m_maxChunkSize; // s_defaultMaxChunkSize unless single-chunking is requested by the caller
+
+ // m_sharedIndex shared by this set of partitions, and particularly when m_sharedReader is IEnuerable
+ // it serves as tracking of the natual order of elements in m_sharedReader
+ // the value of this field is passed in from outside (already initialized) by the constructor,
+ protected readonly SharedLong m_sharedIndex;
+
+ protected DynamicPartitionEnumerator_Abstract(TSourceReader sharedReader, SharedLong sharedIndex)
+ : this(sharedReader, sharedIndex, false)
+ {
+ }
+
+ protected DynamicPartitionEnumerator_Abstract(TSourceReader sharedReader, SharedLong sharedIndex, bool useSingleChunking)
+ {
+ m_sharedReader = sharedReader;
+ m_sharedIndex = sharedIndex;
+ m_maxChunkSize = useSingleChunking ? 1 : s_defaultMaxChunkSize;
+ }
+
+ // ---------------- abstract method declarations --------------
+
+ /// <summary>
+ /// Abstract method to request a contiguous chunk of elements from the source collection
+ /// </summary>
+ /// <param name="requestedChunkSize">specified number of elements requested</param>
+ /// <returns>
+ /// true if we successfully reserved at least one element (up to #=requestedChunkSize)
+ /// false if all elements in the source collection have been reserved.
+ /// </returns>
+ //GrabNextChunk does the following:
+ // - grab # of requestedChunkSize elements from source data through shared reader,
+ // - at the time of function returns, m_currentChunkSize is updated with the number of
+ // elements actually got assigned (<=requestedChunkSize).
+ // - GrabNextChunk returns true if at least one element is assigned to this partition;
+ // false if the shared reader already hits the last element of the source data before
+ // we call GrabNextChunk
+ protected abstract bool GrabNextChunk(int requestedChunkSize);
+
+ /// <summary>
+ /// Abstract property, returns whether or not the shared reader has already read the last
+ /// element of the source data
+ /// </summary>
+ protected abstract bool HasNoElementsLeft { get; set; }
+
+ /// <summary>
+ /// Get the current element in the current partition. Property required by IEnumerator interface
+ /// This property is abstract because the implementation is different depending on the type
+ /// of the source data: IList, Array or IEnumerable
+ /// </summary>
+ public abstract KeyValuePair<long, TSource> Current { get; }
+
+ /// <summary>
+ /// Dispose is abstract, and depends on the type of the source data:
+ /// - For source data type IList and Array, the type of the shared reader is just the dataitself.
+ /// We don't do anything in Dispose method for IList and Array.
+ /// - For source data type IEnumerable, the type of the shared reader is an enumerator we created.
+ /// Thus we need to dispose this shared reader enumerator, when there is no more active partitions
+ /// left.
+ /// </summary>
+ public abstract void Dispose();
+
+ /// <summary>
+ /// Reset on partitions is not supported
+ /// </summary>
+ public void Reset()
+ {
+ throw new NotSupportedException();
+ }
+
+
+ /// <summary>
+ /// Get the current element in the current partition. Property required by IEnumerator interface
+ /// </summary>
+ Object IEnumerator.Current
+ {
+ get
+ {
+ return ((DynamicPartitionEnumerator_Abstract<TSource, TSourceReader>)this).Current;
+ }
+ }
+
+ /// <summary>
+ /// Moves to the next element if any.
+ /// Try current chunk first, if the current chunk do not have any elements left, then we
+ /// attempt to grab a chunk from the source collection.
+ /// </summary>
+ /// <returns>
+ /// true if successfully moving to the next position;
+ /// false otherwise, if and only if there is no more elements left in the current chunk
+ /// AND the source collection is exhausted.
+ /// </returns>
+ public bool MoveNext()
+ {
+ //perform deferred allocating of the local variables.
+ if (m_localOffset == null)
+ {
+ Contract.Assert(m_currentChunkSize == null);
+ m_localOffset = new SharedInt(-1);
+ m_currentChunkSize = new SharedInt(0);
+ m_doublingCountdown = CHUNK_DOUBLING_RATE;
+ }
+
+ if (m_localOffset.Value < m_currentChunkSize.Value - 1)
+ //attempt to grab the next element from the local chunk
+ {
+ m_localOffset.Value++;
+ return true;
+ }
+ else
+ //otherwise it means we exhausted the local chunk
+ //grab a new chunk from the source enumerator
+ {
+ // The second part of the || condition is necessary to handle the case when MoveNext() is called
+ // after a previous MoveNext call returned false.
+ Contract.Assert(m_localOffset.Value == m_currentChunkSize.Value - 1 || m_currentChunkSize.Value == 0);
+
+ //set the requested chunk size to a proper value
+ int requestedChunkSize;
+ if (m_currentChunkSize.Value == 0) //first time grabbing from source enumerator
+ {
+ requestedChunkSize = 1;
+ }
+ else if (m_doublingCountdown > 0)
+ {
+ requestedChunkSize = m_currentChunkSize.Value;
+ }
+ else
+ {
+ requestedChunkSize = Math.Min(m_currentChunkSize.Value * 2, m_maxChunkSize);
+ m_doublingCountdown = CHUNK_DOUBLING_RATE; // reset
+ }
+
+ // Decrement your doubling countdown
+ m_doublingCountdown--;
+
+ Contract.Assert(requestedChunkSize > 0 && requestedChunkSize <= m_maxChunkSize);
+ //GrabNextChunk will update the value of m_currentChunkSize
+ if (GrabNextChunk(requestedChunkSize))
+ {
+ Contract.Assert(m_currentChunkSize.Value <= requestedChunkSize && m_currentChunkSize.Value > 0);
+ m_localOffset.Value = 0;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+ }
+ #endregion
+
+ #region Dynamic Partitioner for source data of IEnuemrable<> type
+ /// <summary>
+ /// Inherits from DynamicPartitioners
+ /// Provides customized implementation of GetOrderableDynamicPartitions_Factory method, to return an instance
+ /// of EnumerableOfPartitionsForIEnumerator defined internally
+ /// </summary>
+ /// <typeparam name="TSource">Type of elements in the source data</typeparam>
+ private class DynamicPartitionerForIEnumerable<TSource> : OrderablePartitioner<TSource>
+ {
+ IEnumerable<TSource> m_source;
+ readonly bool m_useSingleChunking;
+
+ //constructor
+ internal DynamicPartitionerForIEnumerable(IEnumerable<TSource> source, EnumerablePartitionerOptions partitionerOptions)
+ : base(true, false, true)
+ {
+ m_source = source;
+ m_useSingleChunking = ((partitionerOptions & EnumerablePartitionerOptions.NoBuffering) != 0);
+ }
+
+ /// <summary>
+ /// Overrides OrderablePartitioner.GetOrderablePartitions.
+ /// Partitions the underlying collection into the given number of orderable partitions.
+ /// </summary>
+ /// <param name="partitionCount">number of partitions requested</param>
+ /// <returns>A list containing <paramref name="partitionCount"/> enumerators.</returns>
+ override public IList<IEnumerator<KeyValuePair<long, TSource>>> GetOrderablePartitions(int partitionCount)
+ {
+ if (partitionCount <= 0)
+ {
+ throw new ArgumentOutOfRangeException("partitionCount");
+ }
+ IEnumerator<KeyValuePair<long, TSource>>[] partitions
+ = new IEnumerator<KeyValuePair<long, TSource>>[partitionCount];
+
+ IEnumerable<KeyValuePair<long, TSource>> partitionEnumerable = new InternalPartitionEnumerable(m_source.GetEnumerator(), m_useSingleChunking, true);
+ for (int i = 0; i < partitionCount; i++)
+ {
+ partitions[i] = partitionEnumerable.GetEnumerator();
+ }
+ return partitions;
+ }
+
+ /// <summary>
+ /// Overrides OrderablePartitioner.GetOrderableDyanmicPartitions
+ /// </summary>
+ /// <returns>a enumerable collection of orderable partitions</returns>
+ override public IEnumerable<KeyValuePair<long, TSource>> GetOrderableDynamicPartitions()
+ {
+ return new InternalPartitionEnumerable(m_source.GetEnumerator(), m_useSingleChunking, false);
+ }
+
+ /// <summary>
+ /// Whether additional partitions can be created dynamically.
+ /// </summary>
+ override public bool SupportsDynamicPartitions
+ {
+ get { return true; }
+ }
+
+ #region Internal classes: InternalPartitionEnumerable, InternalPartitionEnumerator
+ /// <summary>
+ /// Provides customized implementation for source data of IEnumerable
+ /// Different from the counterpart for IList/Array, this enumerable maintains several additional fields
+ /// shared by the partitions it owns, including a boolean "m_hasNoElementsLef", a shared lock, and a
+ /// shared count "m_activePartitionCount" used to track active partitions when they were created statically
+ /// </summary>
+ private class InternalPartitionEnumerable : IEnumerable<KeyValuePair<long, TSource>>, IDisposable
+ {
+ //reader through which we access the source data
+ private readonly IEnumerator<TSource> m_sharedReader;
+ private SharedLong m_sharedIndex;//initial value -1
+
+ private volatile KeyValuePair<long, TSource>[] m_FillBuffer; // intermediate buffer to reduce locking
+ private volatile int m_FillBufferSize; // actual number of elements in m_FillBuffer. Will start
+ // at m_FillBuffer.Length, and might be reduced during the last refill
+ private volatile int m_FillBufferCurrentPosition; //shared value to be accessed by Interlock.Increment only
+ private volatile int m_activeCopiers; //number of active copiers
+
+ //fields shared by all partitions that this Enumerable owns, their allocation is deferred
+ private SharedBool m_hasNoElementsLeft; // no elements left at all.
+ private SharedBool m_sourceDepleted; // no elements left in the enumerator, but there may be elements in the Fill Buffer
+
+ //shared synchronization lock, created by this Enumerable
+ private object m_sharedLock;//deferring allocation by enumerator
+
+ private bool m_disposed;
+
+ // If dynamic partitioning, then m_activePartitionCount == null
+ // If static partitioning, then it keeps track of active partition count
+ private SharedInt m_activePartitionCount;
+
+ // records whether or not the user has requested single-chunking behavior
+ private readonly bool m_useSingleChunking;
+
+ internal InternalPartitionEnumerable(IEnumerator<TSource> sharedReader, bool useSingleChunking, bool isStaticPartitioning)
+ {
+ m_sharedReader = sharedReader;
+ m_sharedIndex = new SharedLong(-1);
+ m_hasNoElementsLeft = new SharedBool(false);
+ m_sourceDepleted = new SharedBool(false);
+ m_sharedLock = new object();
+ m_useSingleChunking = useSingleChunking;
+
+ // Only allocate the fill-buffer if single-chunking is not in effect
+ if (!m_useSingleChunking)
+ {
+ // Time to allocate the fill buffer which is used to reduce the contention on the shared lock.
+ // First pick the buffer size multiplier. We use 4 for when there are more than 4 cores, and just 1 for below. This is based on empirical evidence.
+ int fillBufferMultiplier = (PlatformHelper.ProcessorCount > 4) ? 4 : 1;
+
+ // and allocate the fill buffer using these two numbers
+ m_FillBuffer = new KeyValuePair<long, TSource>[fillBufferMultiplier * Partitioner.GetDefaultChunkSize<TSource>()];
+ }
+
+ if (isStaticPartitioning)
+ {
+ // If this object is created for static partitioning (ie. via GetPartitions(int partitionCount),
+ // GetOrderablePartitions(int partitionCount)), we track the active partitions, in order to dispose
+ // this object when all the partitions have been disposed.
+ m_activePartitionCount = new SharedInt(0);
+ }
+ else
+ {
+ // Otherwise this object is created for dynamic partitioning (ie, via GetDynamicPartitions(),
+ // GetOrderableDynamicPartitions()), we do not need tracking. This object must be disposed
+ // explicitly
+ m_activePartitionCount = null;
+ }
+ }
+
+ public IEnumerator<KeyValuePair<long, TSource>> GetEnumerator()
+ {
+ if (m_disposed)
+ {
+ throw new ObjectDisposedException(Environment.GetResourceString("PartitionerStatic_CanNotCallGetEnumeratorAfterSourceHasBeenDisposed"));
+ }
+ else
+ {
+ return new InternalPartitionEnumerator(m_sharedReader, m_sharedIndex,
+ m_hasNoElementsLeft, m_sharedLock, m_activePartitionCount, this, m_useSingleChunking);
+ }
+ }
+
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return ((InternalPartitionEnumerable)this).GetEnumerator();
+ }
+
+
+ ///////////////////
+ //
+ // Used by GrabChunk_Buffered()
+ private void TryCopyFromFillBuffer(KeyValuePair<long, TSource>[] destArray,
+ int requestedChunkSize,
+ ref int actualNumElementsGrabbed)
+ {
+ actualNumElementsGrabbed = 0;
+
+
+ // making a local defensive copy of the fill buffer reference, just in case it gets nulled out
+ KeyValuePair<long, TSource>[] fillBufferLocalRef = m_FillBuffer;
+ if (fillBufferLocalRef == null) return;
+
+ // first do a quick check, and give up if the current position is at the end
+ // so that we don't do an unncessary pair of Interlocked.Increment / Decrement calls
+ if (m_FillBufferCurrentPosition >= m_FillBufferSize)
+ {
+ return; // no elements in the buffer to copy from
+ }
+
+ // We might have a chance to grab elements from the buffer. We will know for sure
+ // when we do the Interlocked.Add below.
+ // But first we must register as a potential copier in order to make sure
+ // the elements we're copying from don't get overwritten by another thread
+ // that starts refilling the buffer right after our Interlocked.Add.
+ Interlocked.Increment(ref m_activeCopiers);
+
+ int endPos = Interlocked.Add(ref m_FillBufferCurrentPosition, requestedChunkSize);
+ int beginPos = endPos - requestedChunkSize;
+
+ if (beginPos < m_FillBufferSize)
+ {
+ // adjust index and do the actual copy
+ actualNumElementsGrabbed = (endPos < m_FillBufferSize) ? endPos : m_FillBufferSize - beginPos;
+ Array.Copy(fillBufferLocalRef, beginPos, destArray, 0, actualNumElementsGrabbed);
+ }
+
+ // let the record show we are no longer accessing the buffer
+ Interlocked.Decrement(ref m_activeCopiers);
+ }
+
+ /// <summary>
+ /// This is the common entry point for consuming items from the source enumerable
+ /// </summary>
+ /// <returns>
+ /// true if we successfully reserved at least one element
+ /// false if all elements in the source collection have been reserved.
+ /// </returns>
+ internal bool GrabChunk(KeyValuePair<long, TSource>[] destArray, int requestedChunkSize, ref int actualNumElementsGrabbed)
+ {
+ actualNumElementsGrabbed = 0;
+
+ if (m_hasNoElementsLeft.Value)
+ {
+ return false;
+ }
+
+ if (m_useSingleChunking)
+ {
+ return GrabChunk_Single(destArray, requestedChunkSize, ref actualNumElementsGrabbed);
+ }
+ else
+ {
+ return GrabChunk_Buffered(destArray, requestedChunkSize, ref actualNumElementsGrabbed);
+ }
+ }
+
+ /// <summary>
+ /// Version of GrabChunk that grabs a single element at a time from the source enumerable
+ /// </summary>
+ /// <returns>
+ /// true if we successfully reserved an element
+ /// false if all elements in the source collection have been reserved.
+ /// </returns>
+ internal bool GrabChunk_Single(KeyValuePair<long,TSource>[] destArray, int requestedChunkSize, ref int actualNumElementsGrabbed)
+ {
+ Contract.Assert(m_useSingleChunking, "Expected m_useSingleChecking to be true");
+ Contract.Assert(requestedChunkSize == 1, "Got requested chunk size of " + requestedChunkSize + " when single-chunking was on");
+ Contract.Assert(actualNumElementsGrabbed == 0, "Expected actualNumElementsGrabbed == 0, instead it is " + actualNumElementsGrabbed);
+ Contract.Assert(destArray.Length == 1, "Expected destArray to be of length 1, instead its length is " + destArray.Length);
+
+ lock (m_sharedLock)
+ {
+ if (m_hasNoElementsLeft.Value) return false;
+
+ try
+ {
+ if (m_sharedReader.MoveNext())
+ {
+ m_sharedIndex.Value = checked(m_sharedIndex.Value + 1);
+ destArray[0]
+ = new KeyValuePair<long, TSource>(m_sharedIndex.Value,
+ m_sharedReader.Current);
+ actualNumElementsGrabbed = 1;
+ return true;
+ }
+ else
+ {
+ //if MoveNext() return false, we set the flag to inform other partitions
+ m_sourceDepleted.Value = true;
+ m_hasNoElementsLeft.Value = true;
+ return false;
+ }
+ }
+ catch
+ {
+ // On an exception, make sure that no additional items are hereafter enumerated
+ m_sourceDepleted.Value = true;
+ m_hasNoElementsLeft.Value = true;
+ throw;
+ }
+ }
+ }
+
+
+
+ /// <summary>
+ /// Version of GrabChunk that uses buffering scheme to grab items out of source enumerable
+ /// </summary>
+ /// <returns>
+ /// true if we successfully reserved at least one element (up to #=requestedChunkSize)
+ /// false if all elements in the source collection have been reserved.
+ /// </returns>
+ internal bool GrabChunk_Buffered(KeyValuePair<long,TSource>[] destArray, int requestedChunkSize, ref int actualNumElementsGrabbed)
+ {
+ Contract.Assert(requestedChunkSize > 0);
+ Contract.Assert(!m_useSingleChunking, "Did not expect to be in single-chunking mode");
+
+ TryCopyFromFillBuffer(destArray, requestedChunkSize, ref actualNumElementsGrabbed);
+
+ if (actualNumElementsGrabbed == requestedChunkSize)
+ {
+ // that was easy.
+ return true;
+ }
+ else if (m_sourceDepleted.Value)
+ {
+ // looks like we both reached the end of the fill buffer, and the source was depleted previously
+ // this means no more work to do for any other worker
+ m_hasNoElementsLeft.Value = true;
+ m_FillBuffer = null;
+ return (actualNumElementsGrabbed > 0);
+ }
+
+
+ //
+ // now's the time to take the shared lock and enumerate
+ //
+ lock (m_sharedLock)
+ {
+ if (m_sourceDepleted.Value)
+ {
+ return (actualNumElementsGrabbed > 0);
+ }
+
+ try
+ {
+ // we need to make sure all array copiers are finished
+ if (m_activeCopiers > 0)
+ {
+ SpinWait sw = new SpinWait();
+ while( m_activeCopiers > 0) sw.SpinOnce();
+ }
+
+ Contract.Assert(m_sharedIndex != null); //already been allocated in MoveNext() before calling GrabNextChunk
+
+ // Now's the time to actually enumerate the source
+
+ // We first fill up the requested # of elements in the caller's array
+ // continue from the where TryCopyFromFillBuffer() left off
+ for (; actualNumElementsGrabbed < requestedChunkSize; actualNumElementsGrabbed++)
+ {
+ if (m_sharedReader.MoveNext())
+ {
+ m_sharedIndex.Value = checked(m_sharedIndex.Value + 1);
+ destArray[actualNumElementsGrabbed]
+ = new KeyValuePair<long, TSource>(m_sharedIndex.Value,
+ m_sharedReader.Current);
+ }
+ else
+ {
+ //if MoveNext() return false, we set the flag to inform other partitions
+ m_sourceDepleted.Value = true;
+ break;
+ }
+ }
+
+ // taking a local snapshot of m_FillBuffer in case some other thread decides to null out m_FillBuffer
+ // in the entry of this method after observing m_sourceCompleted = true
+ var localFillBufferRef = m_FillBuffer;
+
+ // If the big buffer seems to be depleted, we will also fill that up while we are under the lock
+ // Note that this is the only place that m_FillBufferCurrentPosition can be reset
+ if (m_sourceDepleted.Value == false && localFillBufferRef != null &&
+ m_FillBufferCurrentPosition >= localFillBufferRef.Length)
+ {
+ for (int i = 0; i < localFillBufferRef.Length; i++)
+ {
+ if( m_sharedReader.MoveNext())
+ {
+ m_sharedIndex.Value = checked(m_sharedIndex.Value + 1);
+ localFillBufferRef[i]
+ = new KeyValuePair<long, TSource>(m_sharedIndex.Value,
+ m_sharedReader.Current);
+ }
+ else
+ {
+ // No more elements left in the enumerator.
+ // Record this, so that the next request can skip the lock
+ m_sourceDepleted.Value = true;
+
+ // also record the current count in m_FillBufferSize
+ m_FillBufferSize = i;
+
+ // and exit the for loop so that we don't keep incrementing m_FillBufferSize
+ break;
+ }
+
+ }
+
+ m_FillBufferCurrentPosition = 0;
+ }
+
+
+ }
+ catch
+ {
+ // If an exception occurs, don't let the other enumerators try to enumerate.
+ // NOTE: this could instead throw an InvalidOperationException, but that would be unexpected
+ // and not helpful to the end user. We know the root cause is being communicated already.)
+ m_sourceDepleted.Value = true;
+ m_hasNoElementsLeft.Value = true;
+ throw;
+ }
+ }
+
+ return (actualNumElementsGrabbed > 0);
+ }
+
+ public void Dispose()
+ {
+ if (!m_disposed)
+ {
+ m_disposed = true;
+ m_sharedReader.Dispose();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Inherits from DynamicPartitionEnumerator_Abstract directly
+ /// Provides customized implementation for: GrabNextChunk, HasNoElementsLeft, Current, Dispose
+ /// </summary>
+ private class InternalPartitionEnumerator : DynamicPartitionEnumerator_Abstract<TSource, IEnumerator<TSource>>
+ {
+ //---- fields ----
+ //cached local copy of the current chunk
+ private KeyValuePair<long, TSource>[] m_localList; //defer allocating to avoid false sharing
+
+ // the values of the following two fields are passed in from
+ // outside(already initialized) by the constructor,
+ private readonly SharedBool m_hasNoElementsLeft;
+ private readonly object m_sharedLock;
+ private readonly SharedInt m_activePartitionCount;
+ private InternalPartitionEnumerable m_enumerable;
+
+ //constructor
+ internal InternalPartitionEnumerator(
+ IEnumerator<TSource> sharedReader,
+ SharedLong sharedIndex,
+ SharedBool hasNoElementsLeft,
+ object sharedLock,
+ SharedInt activePartitionCount,
+ InternalPartitionEnumerable enumerable,
+ bool useSingleChunking)
+ : base(sharedReader, sharedIndex, useSingleChunking)
+ {
+ m_hasNoElementsLeft = hasNoElementsLeft;
+ m_sharedLock = sharedLock;
+ m_enumerable = enumerable;
+ m_activePartitionCount = activePartitionCount;
+
+ if (m_activePartitionCount != null)
+ {
+ // If static partitioning, we need to increase the active partition count.
+ Interlocked.Increment(ref m_activePartitionCount.Value);
+ }
+ }
+
+ //overriding methods
+
+ /// <summary>
+ /// Reserves a contiguous range of elements from source data
+ /// </summary>
+ /// <param name="requestedChunkSize">specified number of elements requested</param>
+ /// <returns>
+ /// true if we successfully reserved at least one element (up to #=requestedChunkSize)
+ /// false if all elements in the source collection have been reserved.
+ /// </returns>
+ override protected bool GrabNextChunk(int requestedChunkSize)
+ {
+ Contract.Assert(requestedChunkSize > 0);
+
+ if (HasNoElementsLeft)
+ {
+ return false;
+ }
+
+ // defer allocation to avoid false sharing
+ if (m_localList == null)
+ {
+ m_localList = new KeyValuePair<long, TSource>[m_maxChunkSize];
+ }
+
+ // make the actual call to the enumerable that grabs a chunk
+ return m_enumerable.GrabChunk(m_localList, requestedChunkSize, ref m_currentChunkSize.Value);
+ }
+
+ /// <summary>
+ /// Returns whether or not the shared reader has already read the last
+ /// element of the source data
+ /// </summary>
+ /// <remarks>
+ /// We cannot call m_sharedReader.MoveNext(), to see if it hits the last element
+ /// or not, because we can't undo MoveNext(). Thus we need to maintain a shared
+ /// boolean value m_hasNoElementsLeft across all partitions
+ /// </remarks>
+ override protected bool HasNoElementsLeft
+ {
+ get { return m_hasNoElementsLeft.Value; }
+ set
+ {
+ //we only set it from false to true once
+ //we should never set it back in any circumstances
+ Contract.Assert(value);
+ Contract.Assert(!m_hasNoElementsLeft.Value);
+ m_hasNoElementsLeft.Value = true;
+ }
+ }
+
+ override public KeyValuePair<long, TSource> Current
+ {
+ get
+ {
+ //verify that MoveNext is at least called once before Current is called
+ if (m_currentChunkSize == null)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("PartitionerStatic_CurrentCalledBeforeMoveNext"));
+ }
+ Contract.Assert(m_localList != null);
+ Contract.Assert(m_localOffset.Value >= 0 && m_localOffset.Value < m_currentChunkSize.Value);
+ return (m_localList[m_localOffset.Value]);
+ }
+ }
+
+ override public void Dispose()
+ {
+ // If this is static partitioning, ie. m_activePartitionCount != null, since the current partition
+ // is disposed, we decrement the number of active partitions for the shared reader.
+ if (m_activePartitionCount != null && Interlocked.Decrement(ref m_activePartitionCount.Value) == 0)
+ {
+ // If the number of active partitions becomes 0, we need to dispose the shared
+ // reader we created in the m_enumerable object.
+ m_enumerable.Dispose();
+ }
+ // If this is dynamic partitioning, ie. m_activePartitionCount != null, then m_enumerable needs to
+ // be disposed explicitly by the user, and we do not need to anything here
+ }
+ }
+ #endregion
+
+ }
+ #endregion
+
+ #region Dynamic Partitioner for source data of IndexRange types (IList<> and Array<>)
+ /// <summary>
+ /// Dynamic load-balance partitioner. This class is abstract and to be derived from by
+ /// the customized partitioner classes for IList, Array, and IEnumerable
+ /// </summary>
+ /// <typeparam name="TSource">Type of the elements in the source data</typeparam>
+ /// <typeparam name="TCollection"> Type of the source data collection</typeparam>
+ private abstract class DynamicPartitionerForIndexRange_Abstract<TSource, TCollection> : OrderablePartitioner<TSource>
+ {
+ // TCollection can be: IList<TSource>, TSource[] and IEnumerable<TSource>
+ // Derived classes specify TCollection, and implement the abstract method GetOrderableDynamicPartitions_Factory accordingly
+ TCollection m_data;
+
+ /// <summary>
+ /// Constructs a new orderable partitioner
+ /// </summary>
+ /// <param name="data">source data collection</param>
+ protected DynamicPartitionerForIndexRange_Abstract(TCollection data)
+ : base(true, false, true)
+ {
+ m_data = data;
+ }
+
+ /// <summary>
+ /// Partition the source data and create an enumerable over the resulting partitions.
+ /// </summary>
+ /// <param name="data">the source data collection</param>
+ /// <returns>an enumerable of partitions of </returns>
+ protected abstract IEnumerable<KeyValuePair<long, TSource>> GetOrderableDynamicPartitions_Factory(TCollection data);
+
+ /// <summary>
+ /// Overrides OrderablePartitioner.GetOrderablePartitions.
+ /// Partitions the underlying collection into the given number of orderable partitions.
+ /// </summary>
+ /// <param name="partitionCount">number of partitions requested</param>
+ /// <returns>A list containing <paramref name="partitionCount"/> enumerators.</returns>
+ override public IList<IEnumerator<KeyValuePair<long, TSource>>> GetOrderablePartitions(int partitionCount)
+ {
+ if (partitionCount <= 0)
+ {
+ throw new ArgumentOutOfRangeException("partitionCount");
+ }
+ IEnumerator<KeyValuePair<long, TSource>>[] partitions
+ = new IEnumerator<KeyValuePair<long, TSource>>[partitionCount];
+ IEnumerable<KeyValuePair<long, TSource>> partitionEnumerable = GetOrderableDynamicPartitions_Factory(m_data);
+ for (int i = 0; i < partitionCount; i++)
+ {
+ partitions[i] = partitionEnumerable.GetEnumerator();
+ }
+ return partitions;
+ }
+
+ /// <summary>
+ /// Overrides OrderablePartitioner.GetOrderableDyanmicPartitions
+ /// </summary>
+ /// <returns>a enumerable collection of orderable partitions</returns>
+ override public IEnumerable<KeyValuePair<long, TSource>> GetOrderableDynamicPartitions()
+ {
+ return GetOrderableDynamicPartitions_Factory(m_data);
+ }
+
+ /// <summary>
+ /// Whether additional partitions can be created dynamically.
+ /// </summary>
+ override public bool SupportsDynamicPartitions
+ {
+ get { return true; }
+ }
+
+ }
+
+ /// <summary>
+ /// Defines dynamic partition for source data of IList and Array.
+ /// This class inherits DynamicPartitionEnumerator_Abstract
+ /// - implements GrabNextChunk, HasNoElementsLeft, and Dispose methods for IList and Array
+ /// - Current property still remains abstract, implementation is different for IList and Array
+ /// - introduces another abstract method SourceCount, which returns the number of elements in
+ /// the source data. Implementation differs for IList and Array
+ /// </summary>
+ /// <typeparam name="TSource">Type of the elements in the data source</typeparam>
+ /// <typeparam name="TSourceReader">Type of the reader on the source data</typeparam>
+ private abstract class DynamicPartitionEnumeratorForIndexRange_Abstract<TSource, TSourceReader> : DynamicPartitionEnumerator_Abstract<TSource, TSourceReader>
+ {
+ //fields
+ protected int m_startIndex; //initially zero
+
+ //constructor
+ protected DynamicPartitionEnumeratorForIndexRange_Abstract(TSourceReader sharedReader, SharedLong sharedIndex)
+ : base(sharedReader, sharedIndex)
+ {
+ }
+
+ //abstract methods
+ //the Current property is still abstract, and will be implemented by derived classes
+ //we add another abstract method SourceCount to get the number of elements from the source reader
+
+ /// <summary>
+ /// Get the number of elements from the source reader.
+ /// It calls IList.Count or Array.Length
+ /// </summary>
+ protected abstract int SourceCount { get; }
+
+ //overriding methods
+
+ /// <summary>
+ /// Reserves a contiguous range of elements from source data
+ /// </summary>
+ /// <param name="requestedChunkSize">specified number of elements requested</param>
+ /// <returns>
+ /// true if we successfully reserved at least one element (up to #=requestedChunkSize)
+ /// false if all elements in the source collection have been reserved.
+ /// </returns>
+ override protected bool GrabNextChunk(int requestedChunkSize)
+ {
+ Contract.Assert(requestedChunkSize > 0);
+
+ while (!HasNoElementsLeft)
+ {
+ Contract.Assert(m_sharedIndex != null);
+ // use the new Volatile.Read method because it is cheaper than Interlocked.Read on AMD64 architecture
+ long oldSharedIndex = Volatile.Read(ref m_sharedIndex.Value);
+
+ if (HasNoElementsLeft)
+ {
+ //HasNoElementsLeft situation changed from false to true immediately
+ //and oldSharedIndex becomes stale
+ return false;
+ }
+
+ //there won't be overflow, because the index of IList/array is int, and we
+ //have casted it to long.
+ long newSharedIndex = Math.Min(SourceCount - 1, oldSharedIndex + requestedChunkSize);
+
+
+ //the following CAS, if successful, reserves a chunk of elements [oldSharedIndex+1, newSharedIndex]
+ //inclusive in the source collection
+ if (Interlocked.CompareExchange(ref m_sharedIndex.Value, newSharedIndex, oldSharedIndex)
+ == oldSharedIndex)
+ {
+ //set up local indexes.
+ //m_currentChunkSize is always set to requestedChunkSize when source data had
+ //enough elements of what we requested
+ m_currentChunkSize.Value = (int)(newSharedIndex - oldSharedIndex);
+ m_localOffset.Value = -1;
+ m_startIndex = (int)(oldSharedIndex + 1);
+ return true;
+ }
+ }
+ //didn't get any element, return false;
+ return false;
+ }
+
+ /// <summary>
+ /// Returns whether or not the shared reader has already read the last
+ /// element of the source data
+ /// </summary>
+ override protected bool HasNoElementsLeft
+ {
+ get
+ {
+ Contract.Assert(m_sharedIndex != null);
+ // use the new Volatile.Read method because it is cheaper than Interlocked.Read on AMD64 architecture
+ return Volatile.Read(ref m_sharedIndex.Value) >= SourceCount - 1;
+ }
+ set
+ {
+ Contract.Assert(false);
+ }
+ }
+
+ /// <summary>
+ /// For source data type IList and Array, the type of the shared reader is just the data itself.
+ /// We don't do anything in Dispose method for IList and Array.
+ /// </summary>
+ override public void Dispose()
+ { }
+ }
+
+
+ /// <summary>
+ /// Inherits from DynamicPartitioners
+ /// Provides customized implementation of GetOrderableDynamicPartitions_Factory method, to return an instance
+ /// of EnumerableOfPartitionsForIList defined internally
+ /// </summary>
+ /// <typeparam name="TSource">Type of elements in the source data</typeparam>
+ private class DynamicPartitionerForIList<TSource> : DynamicPartitionerForIndexRange_Abstract<TSource, IList<TSource>>
+ {
+ //constructor
+ internal DynamicPartitionerForIList(IList<TSource> source)
+ : base(source)
+ { }
+
+ //override methods
+ override protected IEnumerable<KeyValuePair<long, TSource>> GetOrderableDynamicPartitions_Factory(IList<TSource> m_data)
+ {
+ //m_data itself serves as shared reader
+ return new InternalPartitionEnumerable(m_data);
+ }
+
+ /// <summary>
+ /// Inherits from PartitionList_Abstract
+ /// Provides customized implementation for source data of IList
+ /// </summary>
+ private class InternalPartitionEnumerable : IEnumerable<KeyValuePair<long, TSource>>
+ {
+ //reader through which we access the source data
+ private readonly IList<TSource> m_sharedReader;
+ private SharedLong m_sharedIndex;
+
+ internal InternalPartitionEnumerable(IList<TSource> sharedReader)
+ {
+ m_sharedReader = sharedReader;
+ m_sharedIndex = new SharedLong(-1);
+ }
+
+ public IEnumerator<KeyValuePair<long, TSource>> GetEnumerator()
+ {
+ return new InternalPartitionEnumerator(m_sharedReader, m_sharedIndex);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return ((InternalPartitionEnumerable)this).GetEnumerator();
+ }
+ }
+
+ /// <summary>
+ /// Inherits from DynamicPartitionEnumeratorForIndexRange_Abstract
+ /// Provides customized implementation of SourceCount property and Current property for IList
+ /// </summary>
+ private class InternalPartitionEnumerator : DynamicPartitionEnumeratorForIndexRange_Abstract<TSource, IList<TSource>>
+ {
+ //constructor
+ internal InternalPartitionEnumerator(IList<TSource> sharedReader, SharedLong sharedIndex)
+ : base(sharedReader, sharedIndex)
+ { }
+
+ //overriding methods
+ override protected int SourceCount
+ {
+ get { return m_sharedReader.Count; }
+ }
+ /// <summary>
+ /// return a KeyValuePair of the current element and its key
+ /// </summary>
+ override public KeyValuePair<long, TSource> Current
+ {
+ get
+ {
+ //verify that MoveNext is at least called once before Current is called
+ if (m_currentChunkSize == null)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("PartitionerStatic_CurrentCalledBeforeMoveNext"));
+ }
+
+ Contract.Assert(m_localOffset.Value >= 0 && m_localOffset.Value < m_currentChunkSize.Value);
+ return new KeyValuePair<long, TSource>(m_startIndex + m_localOffset.Value,
+ m_sharedReader[m_startIndex + m_localOffset.Value]);
+ }
+ }
+ }
+ }
+
+
+
+ /// <summary>
+ /// Inherits from DynamicPartitioners
+ /// Provides customized implementation of GetOrderableDynamicPartitions_Factory method, to return an instance
+ /// of EnumerableOfPartitionsForArray defined internally
+ /// </summary>
+ /// <typeparam name="TSource">Type of elements in the source data</typeparam>
+ private class DynamicPartitionerForArray<TSource> : DynamicPartitionerForIndexRange_Abstract<TSource, TSource[]>
+ {
+ //constructor
+ internal DynamicPartitionerForArray(TSource[] source)
+ : base(source)
+ { }
+
+ //override methods
+ override protected IEnumerable<KeyValuePair<long, TSource>> GetOrderableDynamicPartitions_Factory(TSource[] m_data)
+ {
+ return new InternalPartitionEnumerable(m_data);
+ }
+
+ /// <summary>
+ /// Inherits from PartitionList_Abstract
+ /// Provides customized implementation for source data of Array
+ /// </summary>
+ private class InternalPartitionEnumerable : IEnumerable<KeyValuePair<long, TSource>>
+ {
+ //reader through which we access the source data
+ private readonly TSource[] m_sharedReader;
+ private SharedLong m_sharedIndex;
+
+ internal InternalPartitionEnumerable(TSource[] sharedReader)
+ {
+ m_sharedReader = sharedReader;
+ m_sharedIndex = new SharedLong(-1);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return ((InternalPartitionEnumerable)this).GetEnumerator();
+ }
+
+
+ public IEnumerator<KeyValuePair<long, TSource>> GetEnumerator()
+ {
+ return new InternalPartitionEnumerator(m_sharedReader, m_sharedIndex);
+ }
+ }
+
+ /// <summary>
+ /// Inherits from DynamicPartitionEnumeratorForIndexRange_Abstract
+ /// Provides customized implementation of SourceCount property and Current property for Array
+ /// </summary>
+ private class InternalPartitionEnumerator : DynamicPartitionEnumeratorForIndexRange_Abstract<TSource, TSource[]>
+ {
+ //constructor
+ internal InternalPartitionEnumerator(TSource[] sharedReader, SharedLong sharedIndex)
+ : base(sharedReader, sharedIndex)
+ { }
+
+ //overriding methods
+ override protected int SourceCount
+ {
+ get { return m_sharedReader.Length; }
+ }
+
+ override public KeyValuePair<long, TSource> Current
+ {
+ get
+ {
+ //verify that MoveNext is at least called once before Current is called
+ if (m_currentChunkSize == null)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("PartitionerStatic_CurrentCalledBeforeMoveNext"));
+ }
+
+ Contract.Assert(m_localOffset.Value >= 0 && m_localOffset.Value < m_currentChunkSize.Value);
+ return new KeyValuePair<long, TSource>(m_startIndex + m_localOffset.Value,
+ m_sharedReader[m_startIndex + m_localOffset.Value]);
+ }
+ }
+ }
+ }
+ #endregion
+
+
+ #region Static partitioning for IList and Array, abstract classes
+ /// <summary>
+ /// Static partitioning over IList.
+ /// - dynamic and load-balance
+ /// - Keys are ordered within each partition
+ /// - Keys are ordered across partitions
+ /// - Keys are normalized
+ /// - Number of partitions is fixed once specified, and the elements of the source data are
+ /// distributed to each partition as evenly as possible.
+ /// </summary>
+ /// <typeparam name="TSource">type of the elements</typeparam>
+ /// <typeparam name="TCollection">Type of the source data collection</typeparam>
+ private abstract class StaticIndexRangePartitioner<TSource, TCollection> : OrderablePartitioner<TSource>
+ {
+ protected StaticIndexRangePartitioner()
+ : base(true, true, true)
+ { }
+
+ /// <summary>
+ /// Abstract method to return the number of elements in the source data
+ /// </summary>
+ protected abstract int SourceCount { get; }
+
+ /// <summary>
+ /// Abstract method to create a partition that covers a range over source data,
+ /// starting from "startIndex", ending at "endIndex"
+ /// </summary>
+ /// <param name="startIndex">start index of the current partition on the source data</param>
+ /// <param name="endIndex">end index of the current partition on the source data</param>
+ /// <returns>a partition enumerator over the specified range</returns>
+ // The partitioning algorithm is implemented in GetOrderablePartitions method
+ // This method delegates according to source data type IList/Array
+ protected abstract IEnumerator<KeyValuePair<long, TSource>> CreatePartition(int startIndex, int endIndex);
+
+ /// <summary>
+ /// Overrides OrderablePartitioner.GetOrderablePartitions
+ /// Return a list of partitions, each of which enumerate a fixed part of the source data
+ /// The elements of the source data are distributed to each partition as evenly as possible.
+ /// Specifically, if the total number of elements is N, and number of partitions is x, and N = a*x +b,
+ /// where a is the quotient, and b is the remainder. Then the first b partitions each has a + 1 elements,
+ /// and the last x-b partitions each has a elements.
+ /// For example, if N=10, x =3, then
+ /// partition 0 ranges [0,3],
+ /// partition 1 ranges [4,6],
+ /// partition 2 ranges [7,9].
+ /// This also takes care of the situation of (x&gt;N), the last x-N partitions are empty enumerators.
+ /// An empty enumerator is indicated by
+ /// (m_startIndex == list.Count &amp;&amp; m_endIndex == list.Count -1)
+ /// </summary>
+ /// <param name="partitionCount">specified number of partitions</param>
+ /// <returns>a list of partitions</returns>
+ override public IList<IEnumerator<KeyValuePair<long, TSource>>> GetOrderablePartitions(int partitionCount)
+ {
+ if (partitionCount <= 0)
+ {
+ throw new ArgumentOutOfRangeException("partitionCount");
+ }
+
+ int quotient, remainder;
+ quotient = Math.DivRem(SourceCount, partitionCount, out remainder);
+
+ IEnumerator<KeyValuePair<long, TSource>>[] partitions = new IEnumerator<KeyValuePair<long, TSource>>[partitionCount];
+ int lastEndIndex = -1;
+ for (int i = 0; i < partitionCount; i++)
+ {
+ int startIndex = lastEndIndex + 1;
+
+ if (i < remainder)
+ {
+ lastEndIndex = startIndex + quotient;
+ }
+ else
+ {
+ lastEndIndex = startIndex + quotient - 1;
+ }
+ partitions[i] = CreatePartition(startIndex, lastEndIndex);
+ }
+ return partitions;
+ }
+ }
+
+ /// <summary>
+ /// Static Partition for IList/Array.
+ /// This class implements all methods required by IEnumerator interface, except for the Current property.
+ /// Current Property is different for IList and Array. Arrays calls 'ldelem' instructions for faster element
+ /// retrieval.
+ /// </summary>
+ //We assume the source collection is not being updated concurrently. Otherwise it will break the
+ //static partitioning, since each partition operates on the source collection directly, it does
+ //not have a local cache of the elements assigned to them.
+ private abstract class StaticIndexRangePartition<TSource> : IEnumerator<KeyValuePair<long, TSource>>
+ {
+ //the start and end position in the source collection for the current partition
+ //the partition is empty if and only if
+ // (m_startIndex == m_data.Count && m_endIndex == m_data.Count-1)
+ protected readonly int m_startIndex;
+ protected readonly int m_endIndex;
+
+ //the current index of the current partition while enumerating on the source collection
+ protected volatile int m_offset;
+
+ /// <summary>
+ /// Constructs an instance of StaticIndexRangePartition
+ /// </summary>
+ /// <param name="startIndex">the start index in the source collection for the current partition </param>
+ /// <param name="endIndex">the end index in the source collection for the current partition</param>
+ protected StaticIndexRangePartition(int startIndex, int endIndex)
+ {
+ m_startIndex = startIndex;
+ m_endIndex = endIndex;
+ m_offset = startIndex - 1;
+ }
+
+ /// <summary>
+ /// Current Property is different for IList and Array. Arrays calls 'ldelem' instructions for faster
+ /// element retrieval.
+ /// </summary>
+ public abstract KeyValuePair<long, TSource> Current { get; }
+
+ /// <summary>
+ /// We don't dispose the source for IList and array
+ /// </summary>
+ public void Dispose()
+ { }
+
+ public void Reset()
+ {
+ throw new NotSupportedException();
+ }
+
+ /// <summary>
+ /// Moves to the next item
+ /// Before the first MoveNext is called: m_offset == m_startIndex-1;
+ /// </summary>
+ /// <returns>true if successful, false if there is no item left</returns>
+ public bool MoveNext()
+ {
+ if (m_offset < m_endIndex)
+ {
+ m_offset++;
+ return true;
+ }
+ else
+ {
+ //After we have enumerated over all elements, we set m_offset to m_endIndex +1.
+ //The reason we do this is, for an empty enumerator, we need to tell the Current
+ //property whether MoveNext has been called or not.
+ //For an empty enumerator, it starts with (m_offset == m_startIndex-1 == m_endIndex),
+ //and we don't set a new value to m_offset, then the above condition will always be
+ //true, and the Current property will mistakenly assume MoveNext is never called.
+ m_offset = m_endIndex + 1;
+ return false;
+ }
+ }
+
+ Object IEnumerator.Current
+ {
+ get
+ {
+ return ((StaticIndexRangePartition<TSource>)this).Current;
+ }
+ }
+ }
+ #endregion
+
+ #region Static partitioning for IList
+ /// <summary>
+ /// Inherits from StaticIndexRangePartitioner
+ /// Provides customized implementation of SourceCount and CreatePartition
+ /// </summary>
+ /// <typeparam name="TSource"></typeparam>
+ private class StaticIndexRangePartitionerForIList<TSource> : StaticIndexRangePartitioner<TSource, IList<TSource>>
+ {
+ IList<TSource> m_list;
+ internal StaticIndexRangePartitionerForIList(IList<TSource> list)
+ : base()
+ {
+ Contract.Assert(list != null);
+ m_list = list;
+ }
+ override protected int SourceCount
+ {
+ get { return m_list.Count; }
+ }
+ override protected IEnumerator<KeyValuePair<long, TSource>> CreatePartition(int startIndex, int endIndex)
+ {
+ return new StaticIndexRangePartitionForIList<TSource>(m_list, startIndex, endIndex);
+ }
+ }
+
+ /// <summary>
+ /// Inherits from StaticIndexRangePartition
+ /// Provides customized implementation of Current property
+ /// </summary>
+ /// <typeparam name="TSource"></typeparam>
+ private class StaticIndexRangePartitionForIList<TSource> : StaticIndexRangePartition<TSource>
+ {
+ //the source collection shared by all partitions
+ private volatile IList<TSource> m_list;
+
+ internal StaticIndexRangePartitionForIList(IList<TSource> list, int startIndex, int endIndex)
+ : base(startIndex, endIndex)
+ {
+ Contract.Assert(startIndex >= 0 && endIndex <= list.Count - 1);
+ m_list = list;
+ }
+
+ override public KeyValuePair<long, TSource> Current
+ {
+ get
+ {
+ //verify that MoveNext is at least called once before Current is called
+ if (m_offset < m_startIndex)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("PartitionerStatic_CurrentCalledBeforeMoveNext"));
+ }
+
+ Contract.Assert(m_offset >= m_startIndex && m_offset <= m_endIndex);
+ return (new KeyValuePair<long, TSource>(m_offset, m_list[m_offset]));
+ }
+ }
+ }
+ #endregion
+
+ #region static partitioning for Arrays
+ /// <summary>
+ /// Inherits from StaticIndexRangePartitioner
+ /// Provides customized implementation of SourceCount and CreatePartition for Array
+ /// </summary>
+ private class StaticIndexRangePartitionerForArray<TSource> : StaticIndexRangePartitioner<TSource, TSource[]>
+ {
+ TSource[] m_array;
+ internal StaticIndexRangePartitionerForArray(TSource[] array)
+ : base()
+ {
+ Contract.Assert(array != null);
+ m_array = array;
+ }
+ override protected int SourceCount
+ {
+ get { return m_array.Length; }
+ }
+ override protected IEnumerator<KeyValuePair<long, TSource>> CreatePartition(int startIndex, int endIndex)
+ {
+ return new StaticIndexRangePartitionForArray<TSource>(m_array, startIndex, endIndex);
+ }
+ }
+
+ /// <summary>
+ /// Inherits from StaticIndexRangePartitioner
+ /// Provides customized implementation of SourceCount and CreatePartition
+ /// </summary>
+ private class StaticIndexRangePartitionForArray<TSource> : StaticIndexRangePartition<TSource>
+ {
+ //the source collection shared by all partitions
+ private volatile TSource[] m_array;
+
+ internal StaticIndexRangePartitionForArray(TSource[] array, int startIndex, int endIndex)
+ : base(startIndex, endIndex)
+ {
+ Contract.Assert(startIndex >= 0 && endIndex <= array.Length - 1);
+ m_array = array;
+ }
+
+ override public KeyValuePair<long, TSource> Current
+ {
+ get
+ {
+ //verify that MoveNext is at least called once before Current is called
+ if (m_offset < m_startIndex)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("PartitionerStatic_CurrentCalledBeforeMoveNext"));
+ }
+
+ Contract.Assert(m_offset >= m_startIndex && m_offset <= m_endIndex);
+ return (new KeyValuePair<long, TSource>(m_offset, m_array[m_offset]));
+ }
+ }
+ }
+ #endregion
+
+
+ #region Utility functions
+ /// <summary>
+ /// A very simple primitive that allows us to share a value across multiple threads.
+ /// </summary>
+ /// <typeparam name="TSource"></typeparam>
+ private class SharedInt
+ {
+ internal volatile int Value;
+
+ internal SharedInt(int value)
+ {
+ this.Value = value;
+ }
+
+ }
+
+ /// <summary>
+ /// A very simple primitive that allows us to share a value across multiple threads.
+ /// </summary>
+ private class SharedBool
+ {
+ internal volatile bool Value;
+
+ internal SharedBool(bool value)
+ {
+ this.Value = value;
+ }
+
+ }
+
+ /// <summary>
+ /// A very simple primitive that allows us to share a value across multiple threads.
+ /// </summary>
+ private class SharedLong
+ {
+ internal long Value;
+ internal SharedLong(long value)
+ {
+ this.Value = value;
+ }
+
+ }
+
+ //--------------------
+ // The following part calculates the default chunk size. It is copied from System.Linq.Parallel.Scheduling,
+ // because mscorlib.dll cannot access System.Linq.Parallel.Scheduling
+ //--------------------
+
+ // The number of bytes we want "chunks" to be, when partitioning, etc. We choose 4 cache
+ // lines worth, assuming 128b cache line. Most (popular) architectures use 64b cache lines,
+ // but choosing 128b works for 64b too whereas a multiple of 64b isn't necessarily sufficient
+ // for 128b cache systems. So 128b it is.
+ private const int DEFAULT_BYTES_PER_CHUNK = 128 * 4;
+
+ private static int GetDefaultChunkSize<TSource>()
+ {
+ int chunkSize;
+
+ if (typeof(TSource).IsValueType)
+ {
+#if !FEATURE_CORECLR // Marshal.SizeOf is not supported in CoreCLR
+
+ if (typeof(TSource).StructLayoutAttribute.Value == LayoutKind.Explicit)
+ {
+ chunkSize = Math.Max(1, DEFAULT_BYTES_PER_CHUNK / Marshal.SizeOf(typeof(TSource)));
+ }
+ else
+ {
+ // We choose '128' because this ensures, no matter the actual size of the value type,
+ // the total bytes used will be a multiple of 128. This ensures it's cache aligned.
+ chunkSize = 128;
+ }
+#else
+ chunkSize = 128;
+#endif
+ }
+ else
+ {
+ Contract.Assert((DEFAULT_BYTES_PER_CHUNK % IntPtr.Size) == 0, "bytes per chunk should be a multiple of pointer size");
+ chunkSize = (DEFAULT_BYTES_PER_CHUNK / IntPtr.Size);
+ }
+ return chunkSize;
+ }
+ #endregion
+
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/DictionaryEntry.cs b/src/mscorlib/src/System/Collections/DictionaryEntry.cs
new file mode 100644
index 0000000000..fc1d57fe55
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/DictionaryEntry.cs
@@ -0,0 +1,55 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: DictionaryEntry
+**
+**
+**
+**
+** Purpose: Return Value for IDictionaryEnumerator::GetEntry
+**
+**
+===========================================================*/
+namespace System.Collections {
+
+ using System;
+ // A DictionaryEntry holds a key and a value from a dictionary.
+ // It is returned by IDictionaryEnumerator::GetEntry().
+[System.Runtime.InteropServices.ComVisible(true)]
+ [Serializable]
+ public struct DictionaryEntry
+ {
+ private Object _key;
+ private Object _value;
+
+ // Constructs a new DictionaryEnumerator by setting the Key
+ // and Value fields appropriately.
+ public DictionaryEntry(Object key, Object value) {
+ _key = key;
+ _value = value;
+ }
+
+ public Object Key {
+ get {
+ return _key;
+ }
+
+ set {
+ _key = value;
+ }
+ }
+
+ public Object Value {
+ get {
+ return _value;
+ }
+
+ set {
+ _value = value;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/EmptyReadOnlyDictionaryInternal.cs b/src/mscorlib/src/System/Collections/EmptyReadOnlyDictionaryInternal.cs
new file mode 100644
index 0000000000..6e28493ef8
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/EmptyReadOnlyDictionaryInternal.cs
@@ -0,0 +1,194 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: List for exceptions.
+**
+**
+===========================================================*/
+
+using System.Diagnostics.Contracts;
+
+namespace System.Collections {
+ /// This is a simple implementation of IDictionary that is empty and readonly.
+ [Serializable]
+ internal sealed class EmptyReadOnlyDictionaryInternal: IDictionary {
+
+ // Note that this class must be agile with respect to AppDomains. See its usage in
+ // System.Exception to understand why this is the case.
+
+ public EmptyReadOnlyDictionaryInternal() {
+ }
+
+ // IEnumerable members
+
+ IEnumerator IEnumerable.GetEnumerator() {
+ return new NodeEnumerator();
+ }
+
+ // ICollection members
+
+ public void CopyTo(Array array, int index) {
+ if (array==null)
+ throw new ArgumentNullException("array");
+
+ if (array.Rank != 1)
+ throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
+
+ if (index < 0)
+ throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+
+ if ( array.Length - index < this.Count )
+ throw new ArgumentException( Environment.GetResourceString("ArgumentOutOfRange_Index"), "index");
+ Contract.EndContractBlock();
+
+ // the actual copy is a NOP
+ }
+
+ public int Count {
+ get {
+ return 0;
+ }
+ }
+
+ public Object SyncRoot {
+ get {
+ return this;
+ }
+ }
+
+ public bool IsSynchronized {
+ get {
+ return false;
+ }
+ }
+
+ // IDictionary members
+
+ public Object this[Object key] {
+ get {
+ if (key == null) {
+ throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
+ }
+ Contract.EndContractBlock();
+ return null;
+ }
+ set {
+ if (key == null) {
+ throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
+ }
+
+ if (!key.GetType().IsSerializable)
+ throw new ArgumentException(Environment.GetResourceString("Argument_NotSerializable"), "key");
+
+ if( (value != null) && (!value.GetType().IsSerializable ) )
+ throw new ArgumentException(Environment.GetResourceString("Argument_NotSerializable"), "value");
+ Contract.EndContractBlock();
+
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
+ }
+ }
+
+ public ICollection Keys {
+ get {
+ return EmptyArray<Object>.Value;
+ }
+ }
+
+ public ICollection Values {
+ get {
+ return EmptyArray<Object>.Value;
+ }
+ }
+
+ public bool Contains(Object key) {
+ return false;
+ }
+
+ public void Add(Object key, Object value) {
+ if (key == null) {
+ throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
+ }
+
+ if (!key.GetType().IsSerializable)
+ throw new ArgumentException(Environment.GetResourceString("Argument_NotSerializable"), "key" );
+
+ if( (value != null) && (!value.GetType().IsSerializable) )
+ throw new ArgumentException(Environment.GetResourceString("Argument_NotSerializable"), "value");
+ Contract.EndContractBlock();
+
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
+ }
+
+ public void Clear() {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
+ }
+
+ public bool IsReadOnly {
+ get {
+ return true;
+ }
+ }
+
+ public bool IsFixedSize {
+ get {
+ return true;
+ }
+ }
+
+ public IDictionaryEnumerator GetEnumerator() {
+ return new NodeEnumerator();
+ }
+
+ public void Remove(Object key) {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
+ }
+
+ private sealed class NodeEnumerator : IDictionaryEnumerator {
+
+ public NodeEnumerator() {
+ }
+
+ // IEnumerator members
+
+ public bool MoveNext() {
+ return false;
+ }
+
+ public Object Current {
+ get {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumOpCantHappen"));
+ }
+ }
+
+ public void Reset() {
+ }
+
+ // IDictionaryEnumerator members
+
+ public Object Key {
+ get {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumOpCantHappen"));
+ }
+ }
+
+ public Object Value {
+ get {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumOpCantHappen"));
+ }
+ }
+
+ public DictionaryEntry Entry {
+ get {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumOpCantHappen"));
+ }
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Generic/ArraySortHelper.cs b/src/mscorlib/src/System/Collections/Generic/ArraySortHelper.cs
new file mode 100644
index 0000000000..b2fed9d78f
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Generic/ArraySortHelper.cs
@@ -0,0 +1,1558 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: class to sort arrays
+**
+**
+===========================================================*/
+namespace System.Collections.Generic
+{
+ using System;
+ using System.Globalization;
+ using System.Runtime.CompilerServices;
+ using System.Diagnostics.Contracts;
+ using System.Runtime.Versioning;
+
+ #region ArraySortHelper for single arrays
+
+ internal interface IArraySortHelper<TKey>
+ {
+ void Sort(TKey[] keys, int index, int length, IComparer<TKey> comparer);
+ int BinarySearch(TKey[] keys, int index, int length, TKey value, IComparer<TKey> comparer);
+ }
+
+ internal static class IntrospectiveSortUtilities
+ {
+ // This is the threshold where Introspective sort switches to Insertion sort.
+ // Imperically, 16 seems to speed up most cases without slowing down others, at least for integers.
+ // Large value types may benefit from a smaller number.
+ internal const int IntrosortSizeThreshold = 16;
+
+ internal const int QuickSortDepthThreshold = 32;
+
+ internal static int FloorLog2(int n)
+ {
+ int result = 0;
+ while (n >= 1)
+ {
+ result++;
+ n = n / 2;
+ }
+ return result;
+ }
+
+ internal static void ThrowOrIgnoreBadComparer(Object comparer) {
+ // This is hit when an invarant of QuickSort is violated due to a bad IComparer implementation (for
+ // example, imagine an IComparer that returns 0 when items are equal but -1 all other times).
+ //
+ // We could have thrown this exception on v4, but due to changes in v4.5 around how we partition arrays
+ // there are different sets of input where we would throw this exception. In order to reduce overall risk from
+ // an app compat persective, we're changing to never throw on v4. Instead, we'll return with a partially
+ // sorted array.
+ if(BinaryCompatibility.TargetsAtLeast_Desktop_V4_5) {
+ throw new ArgumentException(Environment.GetResourceString("Arg_BogusIComparer", comparer));
+ }
+ }
+
+ }
+
+ [TypeDependencyAttribute("System.Collections.Generic.GenericArraySortHelper`1")]
+ internal class ArraySortHelper<T>
+ : IArraySortHelper<T>
+ {
+ static volatile IArraySortHelper<T> defaultArraySortHelper;
+
+ public static IArraySortHelper<T> Default
+ {
+ get
+ {
+ IArraySortHelper<T> sorter = defaultArraySortHelper;
+ if (sorter == null)
+ sorter = CreateArraySortHelper();
+
+ return sorter;
+ }
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ private static IArraySortHelper<T> CreateArraySortHelper()
+ {
+ if (typeof(IComparable<T>).IsAssignableFrom(typeof(T)))
+ {
+ defaultArraySortHelper = (IArraySortHelper<T>)RuntimeTypeHandle.Allocate(typeof(GenericArraySortHelper<string>).TypeHandle.Instantiate(new Type[] { typeof(T) }));
+ }
+ else
+ {
+ defaultArraySortHelper = new ArraySortHelper<T>();
+ }
+ return defaultArraySortHelper;
+ }
+
+ #region IArraySortHelper<T> Members
+
+ public void Sort(T[] keys, int index, int length, IComparer<T> comparer)
+ {
+ Contract.Assert(keys != null, "Check the arguments in the caller!");
+ Contract.Assert( index >= 0 && length >= 0 && (keys.Length - index >= length), "Check the arguments in the caller!");
+
+ // Add a try block here to detect IComparers (or their
+ // underlying IComparables, etc) that are bogus.
+ try
+ {
+ if (comparer == null)
+ {
+ comparer = Comparer<T>.Default;
+ }
+
+#if FEATURE_CORECLR
+ // Since QuickSort and IntrospectiveSort produce different sorting sequence for equal keys the upgrade
+ // to IntrospectiveSort was quirked. However since the phone builds always shipped with the new sort aka
+ // IntrospectiveSort and we would want to continue using this sort moving forward CoreCLR always uses the new sort.
+
+ IntrospectiveSort(keys, index, length, comparer);
+#else
+ if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
+ {
+ IntrospectiveSort(keys, index, length, comparer);
+ }
+ else
+ {
+ DepthLimitedQuickSort(keys, index, length + index - 1, comparer, IntrospectiveSortUtilities.QuickSortDepthThreshold);
+ }
+#endif
+ }
+ catch (IndexOutOfRangeException)
+ {
+ IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
+ }
+ catch (Exception e)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
+ }
+ }
+
+ public int BinarySearch(T[] array, int index, int length, T value, IComparer<T> comparer)
+ {
+ try
+ {
+ if (comparer == null)
+ {
+ comparer = Comparer<T>.Default;
+ }
+
+ return InternalBinarySearch(array, index, length, value, comparer);
+ }
+ catch (Exception e)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
+ }
+ }
+
+ #endregion
+
+ internal static int InternalBinarySearch(T[] array, int index, int length, T value, IComparer<T> comparer)
+ {
+ Contract.Requires(array != null, "Check the arguments in the caller!");
+ Contract.Requires(index >= 0 && length >= 0 && (array.Length - index >= length), "Check the arguments in the caller!");
+
+ int lo = index;
+ int hi = index + length - 1;
+ while (lo <= hi)
+ {
+ int i = lo + ((hi - lo) >> 1);
+ int order = comparer.Compare(array[i], value);
+
+ if (order == 0) return i;
+ if (order < 0)
+ {
+ lo = i + 1;
+ }
+ else
+ {
+ hi = i - 1;
+ }
+ }
+
+ return ~lo;
+ }
+
+ private static void SwapIfGreater(T[] keys, IComparer<T> comparer, int a, int b)
+ {
+ if (a != b)
+ {
+ if (comparer.Compare(keys[a], keys[b]) > 0)
+ {
+ T key = keys[a];
+ keys[a] = keys[b];
+ keys[b] = key;
+ }
+ }
+ }
+
+ private static void Swap(T[] a, int i, int j)
+ {
+ if(i != j)
+ {
+ T t = a[i];
+ a[i] = a[j];
+ a[j] = t;
+ }
+ }
+
+ internal static void DepthLimitedQuickSort(T[] keys, int left, int right, IComparer<T> comparer, int depthLimit)
+ {
+ do
+ {
+ if (depthLimit == 0)
+ {
+ Heapsort(keys, left, right, comparer);
+ return;
+ }
+
+ int i = left;
+ int j = right;
+
+ // pre-sort the low, middle (pivot), and high values in place.
+ // this improves performance in the face of already sorted data, or
+ // data that is made up of multiple sorted runs appended together.
+ int middle = i + ((j - i) >> 1);
+ SwapIfGreater(keys, comparer, i, middle); // swap the low with the mid point
+ SwapIfGreater(keys, comparer, i, j); // swap the low with the high
+ SwapIfGreater(keys, comparer, middle, j); // swap the middle with the high
+
+ T x = keys[middle];
+ do
+ {
+ while (comparer.Compare(keys[i], x) < 0) i++;
+ while (comparer.Compare(x, keys[j]) < 0) j--;
+ Contract.Assert(i >= left && j <= right, "(i>=left && j<=right) Sort failed - Is your IComparer bogus?");
+ if (i > j) break;
+ if (i < j)
+ {
+ T key = keys[i];
+ keys[i] = keys[j];
+ keys[j] = key;
+ }
+ i++;
+ j--;
+ } while (i <= j);
+
+ // The next iteration of the while loop is to "recursively" sort the larger half of the array and the
+ // following calls recursively sort the smaller half. So we subtract one from depthLimit here so
+ // both sorts see the new value.
+ depthLimit--;
+
+ if (j - left <= right - i)
+ {
+ if (left < j) DepthLimitedQuickSort(keys, left, j, comparer, depthLimit);
+ left = i;
+ }
+ else
+ {
+ if (i < right) DepthLimitedQuickSort(keys, i, right, comparer, depthLimit);
+ right = j;
+ }
+ } while (left < right);
+ }
+
+ internal static void IntrospectiveSort(T[] keys, int left, int length, IComparer<T> comparer)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(comparer != null);
+ Contract.Requires(left >= 0);
+ Contract.Requires(length >= 0);
+ Contract.Requires(length <= keys.Length);
+ Contract.Requires(length + left <= keys.Length);
+
+ if (length < 2)
+ return;
+
+ IntroSort(keys, left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2(keys.Length), comparer);
+ }
+
+ private static void IntroSort(T[] keys, int lo, int hi, int depthLimit, IComparer<T> comparer)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(comparer != null);
+ Contract.Requires(lo >= 0);
+ Contract.Requires(hi < keys.Length);
+
+ while (hi > lo)
+ {
+ int partitionSize = hi - lo + 1;
+ if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
+ {
+ if (partitionSize == 1)
+ {
+ return;
+ }
+ if (partitionSize == 2)
+ {
+ SwapIfGreater(keys, comparer, lo, hi);
+ return;
+ }
+ if (partitionSize == 3)
+ {
+ SwapIfGreater(keys, comparer, lo, hi-1);
+ SwapIfGreater(keys, comparer, lo, hi);
+ SwapIfGreater(keys, comparer, hi-1, hi);
+ return;
+ }
+
+ InsertionSort(keys, lo, hi, comparer);
+ return;
+ }
+
+ if (depthLimit == 0)
+ {
+ Heapsort(keys, lo, hi, comparer);
+ return;
+ }
+ depthLimit--;
+
+ int p = PickPivotAndPartition(keys, lo, hi, comparer);
+ // Note we've already partitioned around the pivot and do not have to move the pivot again.
+ IntroSort(keys, p + 1, hi, depthLimit, comparer);
+ hi = p - 1;
+ }
+ }
+
+ private static int PickPivotAndPartition(T[] keys, int lo, int hi, IComparer<T> comparer)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(comparer != null);
+ Contract.Requires(lo >= 0);
+ Contract.Requires(hi > lo);
+ Contract.Requires(hi < keys.Length);
+ Contract.Ensures(Contract.Result<int>() >= lo && Contract.Result<int>() <= hi);
+
+ // Compute median-of-three. But also partition them, since we've done the comparison.
+ int middle = lo + ((hi - lo) / 2);
+
+ // Sort lo, mid and hi appropriately, then pick mid as the pivot.
+ SwapIfGreater(keys, comparer, lo, middle); // swap the low with the mid point
+ SwapIfGreater(keys, comparer, lo, hi); // swap the low with the high
+ SwapIfGreater(keys, comparer, middle, hi); // swap the middle with the high
+
+ T pivot = keys[middle];
+ Swap(keys, middle, hi - 1);
+ int left = lo, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
+
+ while (left < right)
+ {
+ while (comparer.Compare(keys[++left], pivot) < 0) ;
+ while (comparer.Compare(pivot, keys[--right]) < 0) ;
+
+ if (left >= right)
+ break;
+
+ Swap(keys, left, right);
+ }
+
+ // Put pivot in the right location.
+ Swap(keys, left, (hi - 1));
+ return left;
+ }
+
+ private static void Heapsort(T[] keys, int lo, int hi, IComparer<T> comparer)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(comparer != null);
+ Contract.Requires(lo >= 0);
+ Contract.Requires(hi > lo);
+ Contract.Requires(hi < keys.Length);
+
+ int n = hi - lo + 1;
+ for (int i = n / 2; i >= 1; i = i - 1)
+ {
+ DownHeap(keys, i, n, lo, comparer);
+ }
+ for (int i = n; i > 1; i = i - 1)
+ {
+ Swap(keys, lo, lo + i - 1);
+ DownHeap(keys, 1, i - 1, lo, comparer);
+ }
+ }
+
+ private static void DownHeap(T[] keys, int i, int n, int lo, IComparer<T> comparer)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(comparer != null);
+ Contract.Requires(lo >= 0);
+ Contract.Requires(lo < keys.Length);
+
+ T d = keys[lo + i - 1];
+ int child;
+ while (i <= n / 2)
+ {
+ child = 2 * i;
+ if (child < n && comparer.Compare(keys[lo + child - 1], keys[lo + child]) < 0)
+ {
+ child++;
+ }
+ if (!(comparer.Compare(d, keys[lo + child - 1]) < 0))
+ break;
+ keys[lo + i - 1] = keys[lo + child - 1];
+ i = child;
+ }
+ keys[lo + i - 1] = d;
+ }
+
+ private static void InsertionSort(T[] keys, int lo, int hi, IComparer<T> comparer)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(lo >= 0);
+ Contract.Requires(hi >= lo);
+ Contract.Requires(hi <= keys.Length);
+
+ int i, j;
+ T t;
+ for (i = lo; i < hi; i++)
+ {
+ j = i;
+ t = keys[i + 1];
+ while (j >= lo && comparer.Compare(t, keys[j]) < 0)
+ {
+ keys[j + 1] = keys[j];
+ j--;
+ }
+ keys[j + 1] = t;
+ }
+ }
+ }
+
+ [Serializable()]
+ internal class GenericArraySortHelper<T>
+ : IArraySortHelper<T>
+ where T : IComparable<T>
+ {
+ // Do not add a constructor to this class because ArraySortHelper<T>.CreateSortHelper will not execute it
+
+ #region IArraySortHelper<T> Members
+
+ public void Sort(T[] keys, int index, int length, IComparer<T> comparer)
+ {
+ Contract.Assert(keys != null, "Check the arguments in the caller!");
+ Contract.Assert(index >= 0 && length >= 0 && (keys.Length - index >= length), "Check the arguments in the caller!");
+
+ try
+ {
+ if (comparer == null || comparer == Comparer<T>.Default) {
+
+#if FEATURE_CORECLR
+ // Since QuickSort and IntrospectiveSort produce different sorting sequence for equal keys the upgrade
+ // to IntrospectiveSort was quirked. However since the phone builds always shipped with the new sort aka
+ // IntrospectiveSort and we would want to continue using this sort moving forward CoreCLR always uses the new sort.
+
+ IntrospectiveSort(keys, index, length);
+#else
+ // call the faster version of our sort algorithm if the user doesn't provide a comparer
+ if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
+ {
+ IntrospectiveSort(keys, index, length);
+ }
+ else
+ {
+ DepthLimitedQuickSort(keys, index, length + index - 1, IntrospectiveSortUtilities.QuickSortDepthThreshold);
+ }
+#endif
+ }
+ else
+ {
+#if FEATURE_CORECLR
+ // Since QuickSort and IntrospectiveSort produce different sorting sequence for equal keys the upgrade
+ // to IntrospectiveSort was quirked. However since the phone builds always shipped with the new sort aka
+ // IntrospectiveSort and we would want to continue using this sort moving forward CoreCLR always uses the new sort.
+
+ ArraySortHelper<T>.IntrospectiveSort(keys, index, length, comparer);
+#else
+ if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
+ {
+ ArraySortHelper<T>.IntrospectiveSort(keys, index, length, comparer);
+ }
+ else
+ {
+ ArraySortHelper<T>.DepthLimitedQuickSort(keys, index, length + index - 1, comparer, IntrospectiveSortUtilities.QuickSortDepthThreshold);
+ }
+#endif
+ }
+ }
+ catch (IndexOutOfRangeException)
+ {
+ IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
+ }
+ catch (Exception e)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
+ }
+ }
+
+ public int BinarySearch(T[] array, int index, int length, T value, IComparer<T> comparer)
+ {
+ Contract.Assert(array != null, "Check the arguments in the caller!");
+ Contract.Assert(index >= 0 && length >= 0 && (array.Length - index >= length), "Check the arguments in the caller!");
+
+ try
+ {
+ if (comparer == null || comparer == Comparer<T>.Default)
+ {
+ return BinarySearch(array, index, length, value);
+ }
+ else
+ {
+ return ArraySortHelper<T>.InternalBinarySearch(array, index, length, value, comparer);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
+ }
+ }
+
+ #endregion
+
+ // This function is called when the user doesn't specify any comparer.
+ // Since T is constrained here, we can call IComparable<T>.CompareTo here.
+ // We can avoid boxing for value type and casting for reference types.
+ private static int BinarySearch(T[] array, int index, int length, T value)
+ {
+ int lo = index;
+ int hi = index + length - 1;
+ while (lo <= hi)
+ {
+ int i = lo + ((hi - lo) >> 1);
+ int order;
+ if (array[i] == null)
+ {
+ order = (value == null) ? 0 : -1;
+ }
+ else
+ {
+ order = array[i].CompareTo(value);
+ }
+
+ if (order == 0)
+ {
+ return i;
+ }
+
+ if (order < 0)
+ {
+ lo = i + 1;
+ }
+ else
+ {
+ hi = i - 1;
+ }
+ }
+
+ return ~lo;
+ }
+
+ private static void SwapIfGreaterWithItems(T[] keys, int a, int b)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(0 <= a && a < keys.Length);
+ Contract.Requires(0 <= b && b < keys.Length);
+
+ if (a != b)
+ {
+ if (keys[a] != null && keys[a].CompareTo(keys[b]) > 0)
+ {
+ T key = keys[a];
+ keys[a] = keys[b];
+ keys[b] = key;
+ }
+ }
+ }
+
+ private static void Swap(T[] a, int i, int j)
+ {
+ if(i!=j)
+ {
+ T t = a[i];
+ a[i] = a[j];
+ a[j] = t;
+ }
+ }
+
+ private static void DepthLimitedQuickSort(T[] keys, int left, int right, int depthLimit)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(0 <= left && left < keys.Length);
+ Contract.Requires(0 <= right && right < keys.Length);
+
+ // The code in this function looks very similar to QuickSort in ArraySortHelper<T> class.
+ // The difference is that T is constrainted to IComparable<T> here.
+ // So the IL code will be different. This function is faster than the one in ArraySortHelper<T>.
+
+ do
+ {
+ if (depthLimit == 0)
+ {
+ Heapsort(keys, left, right);
+ return;
+ }
+
+ int i = left;
+ int j = right;
+
+ // pre-sort the low, middle (pivot), and high values in place.
+ // this improves performance in the face of already sorted data, or
+ // data that is made up of multiple sorted runs appended together.
+ int middle = i + ((j - i) >> 1);
+ SwapIfGreaterWithItems(keys, i, middle); // swap the low with the mid point
+ SwapIfGreaterWithItems(keys, i, j); // swap the low with the high
+ SwapIfGreaterWithItems(keys, middle, j); // swap the middle with the high
+
+ T x = keys[middle];
+ do
+ {
+ if (x == null)
+ {
+ // if x null, the loop to find two elements to be switched can be reduced.
+ while (keys[j] != null) j--;
+ }
+ else
+ {
+ while (x.CompareTo(keys[i]) > 0) i++;
+ while (x.CompareTo(keys[j]) < 0) j--;
+ }
+ Contract.Assert(i >= left && j <= right, "(i>=left && j<=right) Sort failed - Is your IComparer bogus?");
+ if (i > j) break;
+ if (i < j)
+ {
+ T key = keys[i];
+ keys[i] = keys[j];
+ keys[j] = key;
+ }
+ i++;
+ j--;
+ } while (i <= j);
+
+ // The next iteration of the while loop is to "recursively" sort the larger half of the array and the
+ // following calls recursively sort the smaller half. So we subtract one from depthLimit here so
+ // both sorts see the new value.
+ depthLimit--;
+
+ if (j - left <= right - i)
+ {
+ if (left < j) DepthLimitedQuickSort(keys, left, j, depthLimit);
+ left = i;
+ }
+ else
+ {
+ if (i < right) DepthLimitedQuickSort(keys, i, right, depthLimit);
+ right = j;
+ }
+ } while (left < right);
+ }
+
+ internal static void IntrospectiveSort(T[] keys, int left, int length)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(left >= 0);
+ Contract.Requires(length >= 0);
+ Contract.Requires(length <= keys.Length);
+ Contract.Requires(length + left <= keys.Length);
+
+ if (length < 2)
+ return;
+
+ IntroSort(keys, left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2(keys.Length));
+ }
+
+ private static void IntroSort(T[] keys, int lo, int hi, int depthLimit)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(lo >= 0);
+ Contract.Requires(hi < keys.Length);
+
+ while (hi > lo)
+ {
+ int partitionSize = hi - lo + 1;
+ if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
+ {
+ if (partitionSize == 1)
+ {
+ return;
+ }
+ if (partitionSize == 2)
+ {
+ SwapIfGreaterWithItems(keys, lo, hi);
+ return;
+ }
+ if (partitionSize == 3)
+ {
+ SwapIfGreaterWithItems(keys, lo, hi-1);
+ SwapIfGreaterWithItems(keys, lo, hi);
+ SwapIfGreaterWithItems(keys, hi-1, hi);
+ return;
+ }
+
+ InsertionSort(keys, lo, hi);
+ return;
+ }
+
+ if (depthLimit == 0)
+ {
+ Heapsort(keys, lo, hi);
+ return;
+ }
+ depthLimit--;
+
+ int p = PickPivotAndPartition(keys, lo, hi);
+ // Note we've already partitioned around the pivot and do not have to move the pivot again.
+ IntroSort(keys, p + 1, hi, depthLimit);
+ hi = p - 1;
+ }
+ }
+
+ private static int PickPivotAndPartition(T[] keys, int lo, int hi)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(lo >= 0);
+ Contract.Requires(hi > lo);
+ Contract.Requires(hi < keys.Length);
+ Contract.Ensures(Contract.Result<int>() >= lo && Contract.Result<int>() <= hi);
+
+ // Compute median-of-three. But also partition them, since we've done the comparison.
+ int middle = lo + ((hi - lo) / 2);
+
+ // Sort lo, mid and hi appropriately, then pick mid as the pivot.
+ SwapIfGreaterWithItems(keys, lo, middle); // swap the low with the mid point
+ SwapIfGreaterWithItems(keys, lo, hi); // swap the low with the high
+ SwapIfGreaterWithItems(keys, middle, hi); // swap the middle with the high
+
+ T pivot = keys[middle];
+ Swap(keys, middle, hi - 1);
+ int left = lo, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
+
+ while (left < right)
+ {
+ if (pivot == null)
+ {
+ while (left < (hi - 1) && keys[++left] == null) ;
+ while (right > lo && keys[--right] != null) ;
+ }
+ else
+ {
+ while (pivot.CompareTo(keys[++left]) > 0) ;
+ while (pivot.CompareTo(keys[--right]) < 0) ;
+ }
+
+ if (left >= right)
+ break;
+
+ Swap(keys, left, right);
+ }
+
+ // Put pivot in the right location.
+ Swap(keys, left, (hi - 1));
+ return left;
+ }
+
+ private static void Heapsort(T[] keys, int lo, int hi)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(lo >= 0);
+ Contract.Requires(hi > lo);
+ Contract.Requires(hi < keys.Length);
+
+ int n = hi - lo + 1;
+ for (int i = n / 2; i >= 1; i = i - 1)
+ {
+ DownHeap(keys, i, n, lo);
+ }
+ for (int i = n; i > 1; i = i - 1)
+ {
+ Swap(keys, lo, lo + i - 1);
+ DownHeap(keys, 1, i - 1, lo);
+ }
+ }
+
+ private static void DownHeap(T[] keys, int i, int n, int lo)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(lo >= 0);
+ Contract.Requires(lo < keys.Length);
+
+ T d = keys[lo + i - 1];
+ int child;
+ while (i <= n / 2)
+ {
+ child = 2 * i;
+ if (child < n && (keys[lo + child - 1] == null || keys[lo + child - 1].CompareTo(keys[lo + child]) < 0))
+ {
+ child++;
+ }
+ if (keys[lo + child - 1] == null || keys[lo + child - 1].CompareTo(d) < 0)
+ break;
+ keys[lo + i - 1] = keys[lo + child - 1];
+ i = child;
+ }
+ keys[lo + i - 1] = d;
+ }
+
+ private static void InsertionSort(T[] keys, int lo, int hi)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(lo >= 0);
+ Contract.Requires(hi >= lo);
+ Contract.Requires(hi <= keys.Length);
+
+ int i, j;
+ T t;
+ for (i = lo; i < hi; i++)
+ {
+ j = i;
+ t = keys[i + 1];
+ while (j >= lo && (t == null || t.CompareTo(keys[j]) < 0))
+ {
+ keys[j + 1] = keys[j];
+ j--;
+ }
+ keys[j + 1] = t;
+ }
+ }
+ }
+
+ #endregion
+
+ #region ArraySortHelper for paired key and value arrays
+
+ internal interface IArraySortHelper<TKey, TValue>
+ {
+ void Sort(TKey[] keys, TValue[] values, int index, int length, IComparer<TKey> comparer);
+ }
+
+ [TypeDependencyAttribute("System.Collections.Generic.GenericArraySortHelper`2")]
+ internal class ArraySortHelper<TKey, TValue>
+ : IArraySortHelper<TKey, TValue>
+ {
+ static volatile IArraySortHelper<TKey, TValue> defaultArraySortHelper;
+
+ public static IArraySortHelper<TKey, TValue> Default
+ {
+ get
+ {
+ IArraySortHelper<TKey, TValue> sorter = defaultArraySortHelper;
+ if (sorter == null)
+ sorter = CreateArraySortHelper();
+
+ return sorter;
+ }
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ private static IArraySortHelper<TKey, TValue> CreateArraySortHelper()
+ {
+ if (typeof(IComparable<TKey>).IsAssignableFrom(typeof(TKey)))
+ {
+ defaultArraySortHelper = (IArraySortHelper<TKey, TValue>)RuntimeTypeHandle.Allocate(typeof(GenericArraySortHelper<string, string>).TypeHandle.Instantiate(new Type[] { typeof(TKey), typeof(TValue) }));
+ }
+ else
+ {
+ defaultArraySortHelper = new ArraySortHelper<TKey, TValue>();
+ }
+ return defaultArraySortHelper;
+ }
+
+ public void Sort(TKey[] keys, TValue[] values, int index, int length, IComparer<TKey> comparer)
+ {
+ Contract.Assert(keys != null, "Check the arguments in the caller!"); // Precondition on interface method
+ Contract.Assert(index >= 0 && length >= 0 && (keys.Length - index >= length), "Check the arguments in the caller!");
+
+ // Add a try block here to detect IComparers (or their
+ // underlying IComparables, etc) that are bogus.
+ try
+ {
+ if (comparer == null || comparer == Comparer<TKey>.Default)
+ {
+ comparer = Comparer<TKey>.Default;
+ }
+
+#if FEATURE_CORECLR
+ // Since QuickSort and IntrospectiveSort produce different sorting sequence for equal keys the upgrade
+ // to IntrospectiveSort was quirked. However since the phone builds always shipped with the new sort aka
+ // IntrospectiveSort and we would want to continue using this sort moving forward CoreCLR always uses the new sort.
+
+ IntrospectiveSort(keys, values, index, length, comparer);
+#else
+ if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
+ {
+ IntrospectiveSort(keys, values, index, length, comparer);
+ }
+ else
+ {
+ DepthLimitedQuickSort(keys, values, index, length + index - 1, comparer, IntrospectiveSortUtilities.QuickSortDepthThreshold);
+ }
+#endif
+ }
+ catch (IndexOutOfRangeException)
+ {
+ IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
+ }
+ catch (Exception e)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
+ }
+ }
+
+ private static void SwapIfGreaterWithItems(TKey[] keys, TValue[] values, IComparer<TKey> comparer, int a, int b)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(values == null || values.Length >= keys.Length);
+ Contract.Requires(comparer != null);
+ Contract.Requires(0 <= a && a < keys.Length);
+ Contract.Requires(0 <= b && b < keys.Length);
+
+ if (a != b)
+ {
+ if (comparer.Compare(keys[a], keys[b]) > 0)
+ {
+ TKey key = keys[a];
+ keys[a] = keys[b];
+ keys[b] = key;
+ if (values != null)
+ {
+ TValue value = values[a];
+ values[a] = values[b];
+ values[b] = value;
+ }
+ }
+ }
+ }
+
+ private static void Swap(TKey[] keys, TValue[] values, int i, int j)
+ {
+ if(i!=j)
+ {
+ TKey k = keys[i];
+ keys[i] = keys[j];
+ keys[j] = k;
+ if(values != null)
+ {
+ TValue v = values[i];
+ values[i] = values[j];
+ values[j] = v;
+ }
+ }
+ }
+
+ internal static void DepthLimitedQuickSort(TKey[] keys, TValue[] values, int left, int right, IComparer<TKey> comparer, int depthLimit)
+ {
+ do
+ {
+ if (depthLimit == 0)
+ {
+ Heapsort(keys, values, left, right, comparer);
+ return;
+ }
+
+ int i = left;
+ int j = right;
+
+ // pre-sort the low, middle (pivot), and high values in place.
+ // this improves performance in the face of already sorted data, or
+ // data that is made up of multiple sorted runs appended together.
+ int middle = i + ((j - i) >> 1);
+ SwapIfGreaterWithItems(keys, values, comparer, i, middle); // swap the low with the mid point
+ SwapIfGreaterWithItems(keys, values, comparer, i, j); // swap the low with the high
+ SwapIfGreaterWithItems(keys, values, comparer, middle, j); // swap the middle with the high
+
+ TKey x = keys[middle];
+ do
+ {
+ while (comparer.Compare(keys[i], x) < 0) i++;
+ while (comparer.Compare(x, keys[j]) < 0) j--;
+ Contract.Assert(i >= left && j <= right, "(i>=left && j<=right) Sort failed - Is your IComparer bogus?");
+ if (i > j) break;
+ if (i < j)
+ {
+ TKey key = keys[i];
+ keys[i] = keys[j];
+ keys[j] = key;
+ if (values != null)
+ {
+ TValue value = values[i];
+ values[i] = values[j];
+ values[j] = value;
+ }
+ }
+ i++;
+ j--;
+ } while (i <= j);
+
+ // The next iteration of the while loop is to "recursively" sort the larger half of the array and the
+ // following calls recursively sort the smaller half. So we subtract one from depthLimit here so
+ // both sorts see the new value.
+ depthLimit--;
+
+ if (j - left <= right - i)
+ {
+ if (left < j) DepthLimitedQuickSort(keys, values, left, j, comparer, depthLimit);
+ left = i;
+ }
+ else
+ {
+ if (i < right) DepthLimitedQuickSort(keys, values, i, right, comparer, depthLimit);
+ right = j;
+ }
+ } while (left < right);
+ }
+
+ internal static void IntrospectiveSort(TKey[] keys, TValue[] values, int left, int length, IComparer<TKey> comparer)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(values != null);
+ Contract.Requires(comparer != null);
+ Contract.Requires(left >= 0);
+ Contract.Requires(length >= 0);
+ Contract.Requires(length <= keys.Length);
+ Contract.Requires(length + left <= keys.Length);
+ Contract.Requires(length + left <= values.Length);
+
+ if (length < 2)
+ return;
+
+ IntroSort(keys, values, left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2(keys.Length), comparer);
+ }
+
+ private static void IntroSort(TKey[] keys, TValue[] values, int lo, int hi, int depthLimit, IComparer<TKey> comparer)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(values != null);
+ Contract.Requires(comparer != null);
+ Contract.Requires(lo >= 0);
+ Contract.Requires(hi < keys.Length);
+
+ while (hi > lo)
+ {
+ int partitionSize = hi - lo + 1;
+ if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
+ {
+ if (partitionSize == 1)
+ {
+ return;
+ }
+ if (partitionSize == 2)
+ {
+ SwapIfGreaterWithItems(keys, values, comparer, lo, hi);
+ return;
+ }
+ if (partitionSize == 3)
+ {
+ SwapIfGreaterWithItems(keys, values, comparer, lo, hi-1);
+ SwapIfGreaterWithItems(keys, values, comparer, lo, hi);
+ SwapIfGreaterWithItems(keys, values, comparer, hi-1, hi);
+ return;
+ }
+
+ InsertionSort(keys, values, lo, hi, comparer);
+ return;
+ }
+
+ if (depthLimit == 0)
+ {
+ Heapsort(keys, values, lo, hi, comparer);
+ return;
+ }
+ depthLimit--;
+
+ int p = PickPivotAndPartition(keys, values, lo, hi, comparer);
+ // Note we've already partitioned around the pivot and do not have to move the pivot again.
+ IntroSort(keys, values, p + 1, hi, depthLimit, comparer);
+ hi = p - 1;
+ }
+ }
+
+ private static int PickPivotAndPartition(TKey[] keys, TValue[] values, int lo, int hi, IComparer<TKey> comparer)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(values != null);
+ Contract.Requires(comparer != null);
+ Contract.Requires(lo >= 0);
+ Contract.Requires(hi > lo);
+ Contract.Requires(hi < keys.Length);
+ Contract.Ensures(Contract.Result<int>() >= lo && Contract.Result<int>() <= hi);
+
+ // Compute median-of-three. But also partition them, since we've done the comparison.
+ int middle = lo + ((hi - lo) / 2);
+
+ // Sort lo, mid and hi appropriately, then pick mid as the pivot.
+ SwapIfGreaterWithItems(keys, values, comparer, lo, middle); // swap the low with the mid point
+ SwapIfGreaterWithItems(keys, values, comparer, lo, hi); // swap the low with the high
+ SwapIfGreaterWithItems(keys, values, comparer, middle, hi); // swap the middle with the high
+
+ TKey pivot = keys[middle];
+ Swap(keys, values, middle, hi - 1);
+ int left = lo, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
+
+ while (left < right)
+ {
+ while (comparer.Compare(keys[++left], pivot) < 0) ;
+ while (comparer.Compare(pivot, keys[--right]) < 0) ;
+
+ if (left >= right)
+ break;
+
+ Swap(keys, values, left, right);
+ }
+
+ // Put pivot in the right location.
+ Swap(keys, values, left, (hi - 1));
+ return left;
+ }
+
+ private static void Heapsort(TKey[] keys, TValue[] values, int lo, int hi, IComparer<TKey> comparer)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(values != null);
+ Contract.Requires(comparer != null);
+ Contract.Requires(lo >= 0);
+ Contract.Requires(hi > lo);
+ Contract.Requires(hi < keys.Length);
+
+ int n = hi - lo + 1;
+ for (int i = n / 2; i >= 1; i = i - 1)
+ {
+ DownHeap(keys, values, i, n, lo, comparer);
+ }
+ for (int i = n; i > 1; i = i - 1)
+ {
+ Swap(keys, values, lo, lo + i - 1);
+ DownHeap(keys, values, 1, i - 1, lo, comparer);
+ }
+ }
+
+ private static void DownHeap(TKey[] keys, TValue[] values, int i, int n, int lo, IComparer<TKey> comparer)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(comparer != null);
+ Contract.Requires(lo >= 0);
+ Contract.Requires(lo < keys.Length);
+
+ TKey d = keys[lo + i - 1];
+ TValue dValue = (values != null) ? values[lo + i - 1] : default(TValue);
+ int child;
+ while (i <= n / 2)
+ {
+ child = 2 * i;
+ if (child < n && comparer.Compare(keys[lo + child - 1], keys[lo + child]) < 0)
+ {
+ child++;
+ }
+ if (!(comparer.Compare(d, keys[lo + child - 1]) < 0))
+ break;
+ keys[lo + i - 1] = keys[lo + child - 1];
+ if(values != null)
+ values[lo + i - 1] = values[lo + child - 1];
+ i = child;
+ }
+ keys[lo + i - 1] = d;
+ if(values != null)
+ values[lo + i - 1] = dValue;
+ }
+
+ private static void InsertionSort(TKey[] keys, TValue[] values, int lo, int hi, IComparer<TKey> comparer)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(values != null);
+ Contract.Requires(comparer != null);
+ Contract.Requires(lo >= 0);
+ Contract.Requires(hi >= lo);
+ Contract.Requires(hi <= keys.Length);
+
+ int i, j;
+ TKey t;
+ TValue tValue;
+ for (i = lo; i < hi; i++)
+ {
+ j = i;
+ t = keys[i + 1];
+ tValue = (values != null) ? values[i + 1] : default(TValue);
+ while (j >= lo && comparer.Compare(t, keys[j]) < 0)
+ {
+ keys[j + 1] = keys[j];
+ if(values != null)
+ values[j + 1] = values[j];
+ j--;
+ }
+ keys[j + 1] = t;
+ if(values != null)
+ values[j + 1] = tValue;
+ }
+ }
+ }
+
+ internal class GenericArraySortHelper<TKey, TValue>
+ : IArraySortHelper<TKey, TValue>
+ where TKey : IComparable<TKey>
+ {
+ public void Sort(TKey[] keys, TValue[] values, int index, int length, IComparer<TKey> comparer)
+ {
+ Contract.Assert(keys != null, "Check the arguments in the caller!");
+ Contract.Assert( index >= 0 && length >= 0 && (keys.Length - index >= length), "Check the arguments in the caller!");
+
+ // Add a try block here to detect IComparers (or their
+ // underlying IComparables, etc) that are bogus.
+ try
+ {
+ if (comparer == null || comparer == Comparer<TKey>.Default)
+ {
+#if FEATURE_CORECLR
+ // Since QuickSort and IntrospectiveSort produce different sorting sequence for equal keys the upgrade
+ // to IntrospectiveSort was quirked. However since the phone builds always shipped with the new sort aka
+ // IntrospectiveSort and we would want to continue using this sort moving forward CoreCLR always uses the new sort.
+
+ IntrospectiveSort(keys, values, index, length);
+#else
+ // call the faster version of our sort algorithm if the user doesn't provide a comparer
+ if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
+ {
+ IntrospectiveSort(keys, values, index, length);
+ }
+ else
+ {
+ DepthLimitedQuickSort(keys, values, index, length + index - 1, IntrospectiveSortUtilities.QuickSortDepthThreshold);
+ }
+#endif
+ }
+ else
+ {
+#if FEATURE_CORECLR
+ // Since QuickSort and IntrospectiveSort produce different sorting sequence for equal keys the upgrade
+ // to IntrospectiveSort was quirked. However since the phone builds always shipped with the new sort aka
+ // IntrospectiveSort and we would want to continue using this sort moving forward CoreCLR always uses the new sort.
+
+ ArraySortHelper<TKey, TValue>.IntrospectiveSort(keys, values, index, length, comparer);
+#else
+ if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
+ {
+ ArraySortHelper<TKey, TValue>.IntrospectiveSort(keys, values, index, length, comparer);
+ }
+ else
+ {
+ ArraySortHelper<TKey, TValue>.DepthLimitedQuickSort(keys, values, index, length + index - 1, comparer, IntrospectiveSortUtilities.QuickSortDepthThreshold);
+ }
+#endif
+ }
+
+ }
+ catch (IndexOutOfRangeException)
+ {
+ IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
+ }
+ catch (Exception e)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
+ }
+ }
+
+ private static void SwapIfGreaterWithItems(TKey[] keys, TValue[] values, int a, int b)
+ {
+ if (a != b)
+ {
+ if (keys[a] != null && keys[a].CompareTo(keys[b]) > 0)
+ {
+ TKey key = keys[a];
+ keys[a] = keys[b];
+ keys[b] = key;
+ if (values != null)
+ {
+ TValue value = values[a];
+ values[a] = values[b];
+ values[b] = value;
+ }
+ }
+ }
+ }
+
+ private static void Swap(TKey[] keys, TValue[] values, int i, int j)
+ {
+ if(i != j)
+ {
+ TKey k = keys[i];
+ keys[i] = keys[j];
+ keys[j] = k;
+ if(values != null)
+ {
+ TValue v = values[i];
+ values[i] = values[j];
+ values[j] = v;
+ }
+ }
+ }
+
+ private static void DepthLimitedQuickSort(TKey[] keys, TValue[] values, int left, int right, int depthLimit)
+ {
+ // The code in this function looks very similar to QuickSort in ArraySortHelper<T> class.
+ // The difference is that T is constrainted to IComparable<T> here.
+ // So the IL code will be different. This function is faster than the one in ArraySortHelper<T>.
+
+ do
+ {
+ if (depthLimit == 0)
+ {
+ Heapsort(keys, values, left, right);
+ return;
+ }
+
+ int i = left;
+ int j = right;
+
+ // pre-sort the low, middle (pivot), and high values in place.
+ // this improves performance in the face of already sorted data, or
+ // data that is made up of multiple sorted runs appended together.
+ int middle = i + ((j - i) >> 1);
+ SwapIfGreaterWithItems(keys, values, i, middle); // swap the low with the mid point
+ SwapIfGreaterWithItems(keys, values, i, j); // swap the low with the high
+ SwapIfGreaterWithItems(keys, values, middle, j); // swap the middle with the high
+
+ TKey x = keys[middle];
+ do
+ {
+ if (x == null)
+ {
+ // if x null, the loop to find two elements to be switched can be reduced.
+ while (keys[j] != null) j--;
+ }
+ else
+ {
+ while (x.CompareTo(keys[i]) > 0) i++;
+ while (x.CompareTo(keys[j]) < 0) j--;
+ }
+ Contract.Assert(i >= left && j <= right, "(i>=left && j<=right) Sort failed - Is your IComparer bogus?");
+ if (i > j) break;
+ if (i < j)
+ {
+ TKey key = keys[i];
+ keys[i] = keys[j];
+ keys[j] = key;
+ if (values != null)
+ {
+ TValue value = values[i];
+ values[i] = values[j];
+ values[j] = value;
+ }
+ }
+ i++;
+ j--;
+ } while (i <= j);
+
+ // The next iteration of the while loop is to "recursively" sort the larger half of the array and the
+ // following calls recursively sort the smaller half. So we subtract one from depthLimit here so
+ // both sorts see the new value.
+ depthLimit--;
+
+ if (j - left <= right - i)
+ {
+ if (left < j) DepthLimitedQuickSort(keys, values, left, j, depthLimit);
+ left = i;
+ }
+ else
+ {
+ if (i < right) DepthLimitedQuickSort(keys, values, i, right, depthLimit);
+ right = j;
+ }
+ } while (left < right);
+ }
+
+ internal static void IntrospectiveSort(TKey[] keys, TValue[] values, int left, int length)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(values != null);
+ Contract.Requires(left >= 0);
+ Contract.Requires(length >= 0);
+ Contract.Requires(length <= keys.Length);
+ Contract.Requires(length + left <= keys.Length);
+ Contract.Requires(length + left <= values.Length);
+
+ if (length < 2)
+ return;
+
+ IntroSort(keys, values, left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2(keys.Length));
+ }
+
+ private static void IntroSort(TKey[] keys, TValue[] values, int lo, int hi, int depthLimit)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(values != null);
+ Contract.Requires(lo >= 0);
+ Contract.Requires(hi < keys.Length);
+
+ while (hi > lo)
+ {
+ int partitionSize = hi - lo + 1;
+ if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
+ {
+ if (partitionSize == 1)
+ {
+ return;
+ }
+ if (partitionSize == 2)
+ {
+ SwapIfGreaterWithItems(keys, values, lo, hi);
+ return;
+ }
+ if (partitionSize == 3)
+ {
+ SwapIfGreaterWithItems(keys, values, lo, hi-1);
+ SwapIfGreaterWithItems(keys, values, lo, hi);
+ SwapIfGreaterWithItems(keys, values, hi-1, hi);
+ return;
+ }
+
+ InsertionSort(keys, values, lo, hi);
+ return;
+ }
+
+ if (depthLimit == 0)
+ {
+ Heapsort(keys, values, lo, hi);
+ return;
+ }
+ depthLimit--;
+
+ int p = PickPivotAndPartition(keys, values, lo, hi);
+ // Note we've already partitioned around the pivot and do not have to move the pivot again.
+ IntroSort(keys, values, p + 1, hi, depthLimit);
+ hi = p - 1;
+ }
+ }
+
+ private static int PickPivotAndPartition(TKey[] keys, TValue[] values, int lo, int hi)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(values != null);
+ Contract.Requires(lo >= 0);
+ Contract.Requires(hi > lo);
+ Contract.Requires(hi < keys.Length);
+ Contract.Ensures(Contract.Result<int>() >= lo && Contract.Result<int>() <= hi);
+
+ // Compute median-of-three. But also partition them, since we've done the comparison.
+ int middle = lo + ((hi - lo) / 2);
+
+ // Sort lo, mid and hi appropriately, then pick mid as the pivot.
+ SwapIfGreaterWithItems(keys, values, lo, middle); // swap the low with the mid point
+ SwapIfGreaterWithItems(keys, values, lo, hi); // swap the low with the high
+ SwapIfGreaterWithItems(keys, values, middle, hi); // swap the middle with the high
+
+ TKey pivot = keys[middle];
+ Swap(keys, values, middle, hi - 1);
+ int left = lo, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
+
+ while (left < right)
+ {
+ if(pivot == null)
+ {
+ while (left < (hi - 1) && keys[++left] == null) ;
+ while (right > lo && keys[--right] != null);
+ }
+ else
+ {
+ while (pivot.CompareTo(keys[++left]) > 0) ;
+ while (pivot.CompareTo(keys[--right]) < 0) ;
+ }
+
+ if (left >= right)
+ break;
+
+ Swap(keys, values, left, right);
+ }
+
+ // Put pivot in the right location.
+ Swap(keys, values, left, (hi - 1));
+ return left;
+ }
+
+ private static void Heapsort(TKey[] keys, TValue[] values, int lo, int hi)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(values != null);
+ Contract.Requires(lo >= 0);
+ Contract.Requires(hi > lo);
+ Contract.Requires(hi < keys.Length);
+
+ int n = hi - lo + 1;
+ for (int i = n / 2; i >= 1; i = i - 1)
+ {
+ DownHeap(keys, values, i, n, lo);
+ }
+ for (int i = n; i > 1; i = i - 1)
+ {
+ Swap(keys, values, lo, lo + i - 1);
+ DownHeap(keys, values, 1, i - 1, lo);
+ }
+ }
+
+ private static void DownHeap(TKey[] keys, TValue[] values, int i, int n, int lo)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(lo >= 0);
+ Contract.Requires(lo < keys.Length);
+
+ TKey d = keys[lo + i - 1];
+ TValue dValue = (values != null) ? values[lo + i - 1] : default(TValue);
+ int child;
+ while (i <= n / 2)
+ {
+ child = 2 * i;
+ if (child < n && (keys[lo + child - 1] == null || keys[lo + child - 1].CompareTo(keys[lo + child]) < 0))
+ {
+ child++;
+ }
+ if (keys[lo + child - 1] == null || keys[lo + child - 1].CompareTo(d) < 0)
+ break;
+ keys[lo + i - 1] = keys[lo + child - 1];
+ if(values != null)
+ values[lo + i - 1] = values[lo + child - 1];
+ i = child;
+ }
+ keys[lo + i - 1] = d;
+ if(values != null)
+ values[lo + i - 1] = dValue;
+ }
+
+ private static void InsertionSort(TKey[] keys, TValue[] values, int lo, int hi)
+ {
+ Contract.Requires(keys != null);
+ Contract.Requires(values != null);
+ Contract.Requires(lo >= 0);
+ Contract.Requires(hi >= lo);
+ Contract.Requires(hi <= keys.Length);
+
+ int i, j;
+ TKey t;
+ TValue tValue;
+ for (i = lo; i < hi; i++)
+ {
+ j = i;
+ t = keys[i + 1];
+ tValue = (values != null)? values[i + 1] : default(TValue);
+ while (j >= lo && (t == null || t.CompareTo(keys[j]) < 0))
+ {
+ keys[j + 1] = keys[j];
+ if(values != null)
+ values[j + 1] = values[j];
+ j--;
+ }
+ keys[j + 1] = t;
+ if(values != null)
+ values[j + 1] = tValue;
+ }
+ }
+ }
+
+ #endregion
+}
+
+
diff --git a/src/mscorlib/src/System/Collections/Generic/Comparer.cs b/src/mscorlib/src/System/Collections/Generic/Comparer.cs
new file mode 100644
index 0000000000..9f1a8bff6f
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Generic/Comparer.cs
@@ -0,0 +1,325 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+//using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Security;
+using System.Runtime.Serialization;
+
+namespace System.Collections.Generic
+{
+ [Serializable]
+ [TypeDependencyAttribute("System.Collections.Generic.ObjectComparer`1")]
+ public abstract class Comparer<T> : IComparer, IComparer<T>
+ {
+ static readonly Comparer<T> defaultComparer = CreateComparer();
+
+ public static Comparer<T> Default {
+ get {
+ Contract.Ensures(Contract.Result<Comparer<T>>() != null);
+ return defaultComparer;
+ }
+ }
+
+ public static Comparer<T> Create(Comparison<T> comparison)
+ {
+ Contract.Ensures(Contract.Result<Comparer<T>>() != null);
+
+ if (comparison == null)
+ throw new ArgumentNullException("comparison");
+
+ return new ComparisonComparer<T>(comparison);
+ }
+
+ //
+ // Note that logic in this method is replicated in vm\compile.cpp to ensure that NGen
+ // saves the right instantiations
+ //
+ [System.Security.SecuritySafeCritical] // auto-generated
+ private static Comparer<T> CreateComparer()
+ {
+ object result = null;
+ RuntimeType t = (RuntimeType)typeof(T);
+
+ // If T implements IComparable<T> return a GenericComparer<T>
+ if (typeof(IComparable<T>).IsAssignableFrom(t))
+ {
+ result = RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(GenericComparer<int>), t);
+ }
+ else if (default(T) == null)
+ {
+ // If T is a Nullable<U> where U implements IComparable<U> return a NullableComparer<U>
+ if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) {
+ RuntimeType u = (RuntimeType)t.GetGenericArguments()[0];
+ if (typeof(IComparable<>).MakeGenericType(u).IsAssignableFrom(u)) {
+ result = RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(NullableComparer<int>), u);
+ }
+ }
+ }
+ else if (t.IsEnum)
+ {
+ // Explicitly call Enum.GetUnderlyingType here. Although GetTypeCode
+ // ends up doing this anyway, we end up avoiding an unnecessary P/Invoke
+ // and virtual method call.
+ TypeCode underlyingTypeCode = Type.GetTypeCode(Enum.GetUnderlyingType(t));
+
+ // Depending on the enum type, we need to special case the comparers so that we avoid boxing
+ // Specialize differently for signed/unsigned types so we avoid problems with large numbers
+ switch (underlyingTypeCode)
+ {
+ case TypeCode.SByte:
+ case TypeCode.Int16:
+ case TypeCode.Int32:
+ result = RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(Int32EnumComparer<int>), t);
+ break;
+ case TypeCode.Byte:
+ case TypeCode.UInt16:
+ case TypeCode.UInt32:
+ result = RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(UInt32EnumComparer<uint>), t);
+ break;
+ // 64-bit enums: use UnsafeEnumCastLong
+ case TypeCode.Int64:
+ result = RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(Int64EnumComparer<long>), t);
+ break;
+ case TypeCode.UInt64:
+ result = RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(UInt64EnumComparer<ulong>), t);
+ break;
+ }
+ }
+
+ return result != null ?
+ (Comparer<T>)result :
+ new ObjectComparer<T>(); // Fallback to ObjectComparer, which uses boxing
+ }
+
+ public abstract int Compare(T x, T y);
+
+ int IComparer.Compare(object x, object y) {
+ if (x == null) return y == null ? 0 : -1;
+ if (y == null) return 1;
+ if (x is T && y is T) return Compare((T)x, (T)y);
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArgumentForComparison);
+ return 0;
+ }
+ }
+
+ // Note: although there is a lot of shared code in the following
+ // comparers, we do not incorporate it into a base class for perf
+ // reasons. Adding another base class (even one with no fields)
+ // means another generic instantiation, which can be costly esp.
+ // for value types.
+
+ [Serializable]
+ internal sealed class GenericComparer<T> : Comparer<T> where T : IComparable<T>
+ {
+ public override int Compare(T x, T y) {
+ if (x != null) {
+ if (y != null) return x.CompareTo(y);
+ return 1;
+ }
+ if (y != null) return -1;
+ return 0;
+ }
+
+ // Equals method for the comparer itself.
+ public override bool Equals(Object obj) =>
+ obj != null && GetType() == obj.GetType();
+
+ public override int GetHashCode() =>
+ GetType().GetHashCode();
+ }
+
+ [Serializable]
+ internal sealed class NullableComparer<T> : Comparer<T?> where T : struct, IComparable<T>
+ {
+ public override int Compare(Nullable<T> x, Nullable<T> y) {
+ if (x.HasValue) {
+ if (y.HasValue) return x.value.CompareTo(y.value);
+ return 1;
+ }
+ if (y.HasValue) return -1;
+ return 0;
+ }
+
+ // Equals method for the comparer itself.
+ public override bool Equals(Object obj) =>
+ obj != null && GetType() == obj.GetType();
+
+ public override int GetHashCode() =>
+ GetType().GetHashCode();
+ }
+
+ [Serializable]
+ internal sealed class ObjectComparer<T> : Comparer<T>
+ {
+ public override int Compare(T x, T y) {
+ return System.Collections.Comparer.Default.Compare(x, y);
+ }
+
+ // Equals method for the comparer itself.
+ public override bool Equals(Object obj) =>
+ obj != null && GetType() == obj.GetType();
+
+ public override int GetHashCode() =>
+ GetType().GetHashCode();
+ }
+
+ [Serializable]
+ internal sealed class ComparisonComparer<T> : Comparer<T>
+ {
+ private readonly Comparison<T> _comparison;
+
+ public ComparisonComparer(Comparison<T> comparison) {
+ _comparison = comparison;
+ }
+
+ public override int Compare(T x, T y) {
+ return _comparison(x, y);
+ }
+ }
+
+ // Enum comparers (specialized to avoid boxing)
+ // NOTE: Each of these needs to implement ISerializable
+ // and have a SerializationInfo/StreamingContext ctor,
+ // since we want to serialize as ObjectComparer for
+ // back-compat reasons (see below).
+
+ [Serializable]
+ internal sealed class Int32EnumComparer<T> : Comparer<T>, ISerializable where T : struct
+ {
+ public Int32EnumComparer()
+ {
+ Contract.Assert(typeof(T).IsEnum, "This type is only intended to be used to compare enums!");
+ }
+
+ // Used by the serialization engine.
+ private Int32EnumComparer(SerializationInfo info, StreamingContext context) { }
+
+ public override int Compare(T x, T y)
+ {
+ int ix = JitHelpers.UnsafeEnumCast(x);
+ int iy = JitHelpers.UnsafeEnumCast(y);
+ return ix.CompareTo(iy);
+ }
+
+ // Equals method for the comparer itself.
+ public override bool Equals(Object obj) =>
+ obj != null && GetType() == obj.GetType();
+
+ public override int GetHashCode() =>
+ GetType().GetHashCode();
+
+ [SecurityCritical]
+ public void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ // Previously Comparer<T> was not specialized for enums,
+ // and instead fell back to ObjectComparer which uses boxing.
+ // Set the type as ObjectComparer here so code that serializes
+ // Comparer for enums will not break.
+ info.SetType(typeof(ObjectComparer<T>));
+ }
+ }
+
+ [Serializable]
+ internal sealed class UInt32EnumComparer<T> : Comparer<T>, ISerializable where T : struct
+ {
+ public UInt32EnumComparer()
+ {
+ Contract.Assert(typeof(T).IsEnum, "This type is only intended to be used to compare enums!");
+ }
+
+ // Used by the serialization engine.
+ private UInt32EnumComparer(SerializationInfo info, StreamingContext context) { }
+
+ public override int Compare(T x, T y)
+ {
+ uint ix = (uint)JitHelpers.UnsafeEnumCast(x);
+ uint iy = (uint)JitHelpers.UnsafeEnumCast(y);
+ return ix.CompareTo(iy);
+ }
+
+ // Equals method for the comparer itself.
+ public override bool Equals(Object obj) =>
+ obj != null && GetType() == obj.GetType();
+
+ public override int GetHashCode() =>
+ GetType().GetHashCode();
+
+ [SecurityCritical]
+ public void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ info.SetType(typeof(ObjectComparer<T>));
+ }
+ }
+
+ [Serializable]
+ internal sealed class Int64EnumComparer<T> : Comparer<T>, ISerializable where T : struct
+ {
+ public Int64EnumComparer()
+ {
+ Contract.Assert(typeof(T).IsEnum, "This type is only intended to be used to compare enums!");
+ }
+
+ // Used by the serialization engine.
+ private Int64EnumComparer(SerializationInfo info, StreamingContext context) { }
+
+ public override int Compare(T x, T y)
+ {
+ long lx = JitHelpers.UnsafeEnumCastLong(x);
+ long ly = JitHelpers.UnsafeEnumCastLong(y);
+ return lx.CompareTo(ly);
+ }
+
+ // Equals method for the comparer itself.
+ public override bool Equals(Object obj) =>
+ obj != null && GetType() == obj.GetType();
+
+ public override int GetHashCode() =>
+ GetType().GetHashCode();
+
+ [SecurityCritical]
+ public void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ info.SetType(typeof(ObjectComparer<T>));
+ }
+ }
+
+ [Serializable]
+ internal sealed class UInt64EnumComparer<T> : Comparer<T>, ISerializable where T : struct
+ {
+ public UInt64EnumComparer()
+ {
+ Contract.Assert(typeof(T).IsEnum, "This type is only intended to be used to compare enums!");
+ }
+
+ // Used by the serialization engine.
+ private UInt64EnumComparer(SerializationInfo info, StreamingContext context) { }
+
+ public override int Compare(T x, T y)
+ {
+ ulong lx = (ulong)JitHelpers.UnsafeEnumCastLong(x);
+ ulong ly = (ulong)JitHelpers.UnsafeEnumCastLong(y);
+ return lx.CompareTo(ly);
+ }
+
+ // Equals method for the comparer itself.
+ public override bool Equals(Object obj) =>
+ obj != null && GetType() == obj.GetType();
+
+ public override int GetHashCode() =>
+ GetType().GetHashCode();
+
+ [SecurityCritical]
+ public void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ info.SetType(typeof(ObjectComparer<T>));
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Generic/DebugView.cs b/src/mscorlib/src/System/Collections/Generic/DebugView.cs
new file mode 100644
index 0000000000..57b91eff51
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Generic/DebugView.cs
@@ -0,0 +1,129 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: DebugView class for generic collections
+**
+**
+**
+**
+=============================================================================*/
+
+namespace System.Collections.Generic {
+ using System;
+ using System.Collections.ObjectModel;
+ using System.Security.Permissions;
+ using System.Diagnostics;
+ using System.Diagnostics.Contracts;
+
+ //
+ // VS IDE can't differentiate between types with the same name from different
+ // assembly. So we need to use different names for collection debug view for
+ // collections in mscorlib.dll and system.dll.
+ //
+ internal sealed class Mscorlib_CollectionDebugView<T> {
+ private ICollection<T> collection;
+
+ public Mscorlib_CollectionDebugView(ICollection<T> collection) {
+ if (collection == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
+
+ this.collection = collection;
+ }
+
+ [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+ public T[] Items {
+ get {
+ T[] items = new T[collection.Count];
+ collection.CopyTo(items, 0);
+ return items;
+ }
+ }
+ }
+
+ internal sealed class Mscorlib_DictionaryKeyCollectionDebugView<TKey, TValue> {
+ private ICollection<TKey> collection;
+
+ public Mscorlib_DictionaryKeyCollectionDebugView(ICollection<TKey> collection) {
+ if (collection == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
+
+ this.collection = collection;
+ }
+
+ [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+ public TKey[] Items {
+ get {
+ TKey[] items = new TKey[collection.Count];
+ collection.CopyTo(items, 0);
+ return items;
+ }
+ }
+ }
+
+ internal sealed class Mscorlib_DictionaryValueCollectionDebugView<TKey, TValue> {
+ private ICollection<TValue> collection;
+
+ public Mscorlib_DictionaryValueCollectionDebugView(ICollection<TValue> collection) {
+ if (collection == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
+
+ this.collection = collection;
+ }
+
+ [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+ public TValue[] Items {
+ get {
+ TValue[] items = new TValue[collection.Count];
+ collection.CopyTo(items, 0);
+ return items;
+ }
+ }
+ }
+
+ internal sealed class Mscorlib_DictionaryDebugView<K, V> {
+ private IDictionary<K, V> dict;
+
+ public Mscorlib_DictionaryDebugView(IDictionary<K, V> dictionary) {
+ if (dictionary == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);
+
+ this.dict = dictionary;
+ }
+
+ [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+ public KeyValuePair<K, V>[] Items {
+ get {
+ KeyValuePair<K, V>[] items = new KeyValuePair<K, V>[dict.Count];
+ dict.CopyTo(items, 0);
+ return items;
+ }
+ }
+ }
+
+ internal sealed class Mscorlib_KeyedCollectionDebugView<K, T> {
+ private KeyedCollection<K, T> kc;
+
+ public Mscorlib_KeyedCollectionDebugView(KeyedCollection<K, T> keyedCollection) {
+ if (keyedCollection == null) {
+ throw new ArgumentNullException("keyedCollection");
+ }
+ Contract.EndContractBlock();
+
+ kc = keyedCollection;
+ }
+
+ [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+ public T[] Items {
+ get {
+ T[] items = new T[kc.Count];
+ kc.CopyTo(items, 0);
+ return items;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Generic/Dictionary.cs b/src/mscorlib/src/System/Collections/Generic/Dictionary.cs
new file mode 100644
index 0000000000..9cbfff5a57
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Generic/Dictionary.cs
@@ -0,0 +1,1187 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+** Purpose: Generic hash table implementation
+**
+** #DictionaryVersusHashtableThreadSafety
+** Hashtable has multiple reader/single writer (MR/SW) thread safety built into
+** certain methods and properties, whereas Dictionary doesn't. If you're
+** converting framework code that formerly used Hashtable to Dictionary, it's
+** important to consider whether callers may have taken a dependence on MR/SW
+** thread safety. If a reader writer lock is available, then that may be used
+** with a Dictionary to get the same thread safety guarantee.
+**
+** Reader writer locks don't exist in silverlight, so we do the following as a
+** result of removing non-generic collections from silverlight:
+** 1. If the Hashtable was fully synchronized, then we replace it with a
+** Dictionary with full locks around reads/writes (same thread safety
+** guarantee).
+** 2. Otherwise, the Hashtable has the default MR/SW thread safety behavior,
+** so we do one of the following on a case-by-case basis:
+** a. If the race condition can be addressed by rearranging the code and using a temp
+** variable (for example, it's only populated immediately after created)
+** then we address the race condition this way and use Dictionary.
+** b. If there's concern about degrading performance with the increased
+** locking, we ifdef with FEATURE_NONGENERIC_COLLECTIONS so we can at
+** least use Hashtable in the desktop build, but Dictionary with full
+** locks in silverlight builds. Note that this is heavier locking than
+** MR/SW, but this is the only option without rewriting (or adding back)
+** the reader writer lock.
+** c. If there's no performance concern (e.g. debug-only code) we
+** consistently replace Hashtable with Dictionary plus full locks to
+** reduce complexity.
+** d. Most of serialization is dead code in silverlight. Instead of updating
+** those Hashtable occurences in serialization, we carved out references
+** to serialization such that this code doesn't need to build in
+** silverlight.
+===========================================================*/
+namespace System.Collections.Generic {
+
+ using System;
+ using System.Collections;
+ using System.Diagnostics;
+ using System.Diagnostics.Contracts;
+ using System.Runtime.Serialization;
+ using System.Security.Permissions;
+
+ [DebuggerTypeProxy(typeof(Mscorlib_DictionaryDebugView<,>))]
+ [DebuggerDisplay("Count = {Count}")]
+ [Serializable]
+ [System.Runtime.InteropServices.ComVisible(false)]
+ public class Dictionary<TKey,TValue>: IDictionary<TKey,TValue>, IDictionary, IReadOnlyDictionary<TKey, TValue>, ISerializable, IDeserializationCallback {
+
+ private struct Entry {
+ public int hashCode; // Lower 31 bits of hash code, -1 if unused
+ public int next; // Index of next entry, -1 if last
+ public TKey key; // Key of entry
+ public TValue value; // Value of entry
+ }
+
+ private int[] buckets;
+ private Entry[] entries;
+ private int count;
+ private int version;
+ private int freeList;
+ private int freeCount;
+ private IEqualityComparer<TKey> comparer;
+ private KeyCollection keys;
+ private ValueCollection values;
+ private Object _syncRoot;
+
+ // constants for serialization
+ private const String VersionName = "Version";
+ private const String HashSizeName = "HashSize"; // Must save buckets.Length
+ private const String KeyValuePairsName = "KeyValuePairs";
+ private const String ComparerName = "Comparer";
+
+ public Dictionary(): this(0, null) {}
+
+ public Dictionary(int capacity): this(capacity, null) {}
+
+ public Dictionary(IEqualityComparer<TKey> comparer): this(0, comparer) {}
+
+ public Dictionary(int capacity, IEqualityComparer<TKey> comparer) {
+ if (capacity < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity);
+ if (capacity > 0) Initialize(capacity);
+ this.comparer = comparer ?? EqualityComparer<TKey>.Default;
+
+#if FEATURE_RANDOMIZED_STRING_HASHING && FEATURE_CORECLR
+ if (HashHelpers.s_UseRandomizedStringHashing && comparer == EqualityComparer<string>.Default)
+ {
+ this.comparer = (IEqualityComparer<TKey>) NonRandomizedStringEqualityComparer.Default;
+ }
+#endif // FEATURE_RANDOMIZED_STRING_HASHING && FEATURE_CORECLR
+ }
+
+ public Dictionary(IDictionary<TKey,TValue> dictionary): this(dictionary, null) {}
+
+ public Dictionary(IDictionary<TKey,TValue> dictionary, IEqualityComparer<TKey> comparer):
+ this(dictionary != null? dictionary.Count: 0, comparer) {
+
+ if( dictionary == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);
+ }
+
+ // It is likely that the passed-in dictionary is Dictionary<TKey,TValue>. When this is the case,
+ // avoid the enumerator allocation and overhead by looping through the entries array directly.
+ // We only do this when dictionary is Dictionary<TKey,TValue> and not a subclass, to maintain
+ // back-compat with subclasses that may have overridden the enumerator behavior.
+ if (dictionary.GetType() == typeof(Dictionary<TKey,TValue>)) {
+ Dictionary<TKey,TValue> d = (Dictionary<TKey,TValue>)dictionary;
+ int count = d.count;
+ Entry[] entries = d.entries;
+ for (int i = 0; i < count; i++) {
+ if (entries[i].hashCode >= 0) {
+ Add(entries[i].key, entries[i].value);
+ }
+ }
+ return;
+ }
+
+ foreach (KeyValuePair<TKey,TValue> pair in dictionary) {
+ Add(pair.Key, pair.Value);
+ }
+ }
+
+ protected Dictionary(SerializationInfo info, StreamingContext context) {
+ //We can't do anything with the keys and values until the entire graph has been deserialized
+ //and we have a resonable estimate that GetHashCode is not going to fail. For the time being,
+ //we'll just cache this. The graph is not valid until OnDeserialization has been called.
+ HashHelpers.SerializationInfoTable.Add(this, info);
+ }
+
+ public IEqualityComparer<TKey> Comparer {
+ get {
+ return comparer;
+ }
+ }
+
+ public int Count {
+ get { return count - freeCount; }
+ }
+
+ public KeyCollection Keys {
+ get {
+ Contract.Ensures(Contract.Result<KeyCollection>() != null);
+ if (keys == null) keys = new KeyCollection(this);
+ return keys;
+ }
+ }
+
+ ICollection<TKey> IDictionary<TKey, TValue>.Keys {
+ get {
+ if (keys == null) keys = new KeyCollection(this);
+ return keys;
+ }
+ }
+
+ IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys {
+ get {
+ if (keys == null) keys = new KeyCollection(this);
+ return keys;
+ }
+ }
+
+ public ValueCollection Values {
+ get {
+ Contract.Ensures(Contract.Result<ValueCollection>() != null);
+ if (values == null) values = new ValueCollection(this);
+ return values;
+ }
+ }
+
+ ICollection<TValue> IDictionary<TKey, TValue>.Values {
+ get {
+ if (values == null) values = new ValueCollection(this);
+ return values;
+ }
+ }
+
+ IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values {
+ get {
+ if (values == null) values = new ValueCollection(this);
+ return values;
+ }
+ }
+
+ public TValue this[TKey key] {
+ get {
+ int i = FindEntry(key);
+ if (i >= 0) return entries[i].value;
+ ThrowHelper.ThrowKeyNotFoundException();
+ return default(TValue);
+ }
+ set {
+ Insert(key, value, false);
+ }
+ }
+
+ public void Add(TKey key, TValue value) {
+ Insert(key, value, true);
+ }
+
+ void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> keyValuePair) {
+ Add(keyValuePair.Key, keyValuePair.Value);
+ }
+
+ bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> keyValuePair) {
+ int i = FindEntry(keyValuePair.Key);
+ if( i >= 0 && EqualityComparer<TValue>.Default.Equals(entries[i].value, keyValuePair.Value)) {
+ return true;
+ }
+ return false;
+ }
+
+ bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> keyValuePair) {
+ int i = FindEntry(keyValuePair.Key);
+ if( i >= 0 && EqualityComparer<TValue>.Default.Equals(entries[i].value, keyValuePair.Value)) {
+ Remove(keyValuePair.Key);
+ return true;
+ }
+ return false;
+ }
+
+ public void Clear() {
+ if (count > 0) {
+ for (int i = 0; i < buckets.Length; i++) buckets[i] = -1;
+ Array.Clear(entries, 0, count);
+ freeList = -1;
+ count = 0;
+ freeCount = 0;
+ version++;
+ }
+ }
+
+ public bool ContainsKey(TKey key) {
+ return FindEntry(key) >= 0;
+ }
+
+ public bool ContainsValue(TValue value) {
+ if (value == null) {
+ for (int i = 0; i < count; i++) {
+ if (entries[i].hashCode >= 0 && entries[i].value == null) return true;
+ }
+ }
+ else {
+ EqualityComparer<TValue> c = EqualityComparer<TValue>.Default;
+ for (int i = 0; i < count; i++) {
+ if (entries[i].hashCode >= 0 && c.Equals(entries[i].value, value)) return true;
+ }
+ }
+ return false;
+ }
+
+ private void CopyTo(KeyValuePair<TKey,TValue>[] array, int index) {
+ if (array == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ }
+
+ if (index < 0 || index > array.Length ) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
+
+ if (array.Length - index < Count) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
+ }
+
+ int count = this.count;
+ Entry[] entries = this.entries;
+ for (int i = 0; i < count; i++) {
+ if (entries[i].hashCode >= 0) {
+ array[index++] = new KeyValuePair<TKey,TValue>(entries[i].key, entries[i].value);
+ }
+ }
+ }
+
+ public Enumerator GetEnumerator() {
+ return new Enumerator(this, Enumerator.KeyValuePair);
+ }
+
+ IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() {
+ return new Enumerator(this, Enumerator.KeyValuePair);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ public virtual void GetObjectData(SerializationInfo info, StreamingContext context) {
+ if (info==null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info);
+ }
+ info.AddValue(VersionName, version);
+
+#if FEATURE_RANDOMIZED_STRING_HASHING
+ info.AddValue(ComparerName, HashHelpers.GetEqualityComparerForSerialization(comparer), typeof(IEqualityComparer<TKey>));
+#else
+ info.AddValue(ComparerName, comparer, typeof(IEqualityComparer<TKey>));
+#endif
+
+ info.AddValue(HashSizeName, buckets == null ? 0 : buckets.Length); //This is the length of the bucket array.
+ if( buckets != null) {
+ KeyValuePair<TKey, TValue>[] array = new KeyValuePair<TKey, TValue>[Count];
+ CopyTo(array, 0);
+ info.AddValue(KeyValuePairsName, array, typeof(KeyValuePair<TKey, TValue>[]));
+ }
+ }
+
+ private int FindEntry(TKey key) {
+ if( key == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+ }
+
+ if (buckets != null) {
+ int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
+ for (int i = buckets[hashCode % buckets.Length]; i >= 0; i = entries[i].next) {
+ if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i;
+ }
+ }
+ return -1;
+ }
+
+ private void Initialize(int capacity) {
+ int size = HashHelpers.GetPrime(capacity);
+ buckets = new int[size];
+ for (int i = 0; i < buckets.Length; i++) buckets[i] = -1;
+ entries = new Entry[size];
+ freeList = -1;
+ }
+
+ private void Insert(TKey key, TValue value, bool add) {
+
+ if( key == null ) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+ }
+
+ if (buckets == null) Initialize(0);
+ int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
+ int targetBucket = hashCode % buckets.Length;
+
+#if FEATURE_RANDOMIZED_STRING_HASHING
+ int collisionCount = 0;
+#endif
+
+ for (int i = buckets[targetBucket]; i >= 0; i = entries[i].next) {
+ if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) {
+ if (add) {
+#if FEATURE_CORECLR
+ ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(key);
+#else
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate);
+#endif
+ }
+ entries[i].value = value;
+ version++;
+ return;
+ }
+
+#if FEATURE_RANDOMIZED_STRING_HASHING
+ collisionCount++;
+#endif
+ }
+ int index;
+ if (freeCount > 0) {
+ index = freeList;
+ freeList = entries[index].next;
+ freeCount--;
+ }
+ else {
+ if (count == entries.Length)
+ {
+ Resize();
+ targetBucket = hashCode % buckets.Length;
+ }
+ index = count;
+ count++;
+ }
+
+ entries[index].hashCode = hashCode;
+ entries[index].next = buckets[targetBucket];
+ entries[index].key = key;
+ entries[index].value = value;
+ buckets[targetBucket] = index;
+ version++;
+
+#if FEATURE_RANDOMIZED_STRING_HASHING
+
+#if FEATURE_CORECLR
+ // In case we hit the collision threshold we'll need to switch to the comparer which is using randomized string hashing
+ // in this case will be EqualityComparer<string>.Default.
+ // Note, randomized string hashing is turned on by default on coreclr so EqualityComparer<string>.Default will
+ // be using randomized string hashing
+
+ if (collisionCount > HashHelpers.HashCollisionThreshold && comparer == NonRandomizedStringEqualityComparer.Default)
+ {
+ comparer = (IEqualityComparer<TKey>) EqualityComparer<string>.Default;
+ Resize(entries.Length, true);
+ }
+#else
+ if(collisionCount > HashHelpers.HashCollisionThreshold && HashHelpers.IsWellKnownEqualityComparer(comparer))
+ {
+ comparer = (IEqualityComparer<TKey>) HashHelpers.GetRandomizedEqualityComparer(comparer);
+ Resize(entries.Length, true);
+ }
+#endif // FEATURE_CORECLR
+
+#endif
+
+ }
+
+ public virtual void OnDeserialization(Object sender) {
+ SerializationInfo siInfo;
+ HashHelpers.SerializationInfoTable.TryGetValue(this, out siInfo);
+
+ if (siInfo==null) {
+ // It might be necessary to call OnDeserialization from a container if the container object also implements
+ // OnDeserialization. However, remoting will call OnDeserialization again.
+ // We can return immediately if this function is called twice.
+ // Note we set remove the serialization info from the table at the end of this method.
+ return;
+ }
+
+ int realVersion = siInfo.GetInt32(VersionName);
+ int hashsize = siInfo.GetInt32(HashSizeName);
+ comparer = (IEqualityComparer<TKey>)siInfo.GetValue(ComparerName, typeof(IEqualityComparer<TKey>));
+
+ if( hashsize != 0) {
+ buckets = new int[hashsize];
+ for (int i = 0; i < buckets.Length; i++) buckets[i] = -1;
+ entries = new Entry[hashsize];
+ freeList = -1;
+
+ KeyValuePair<TKey, TValue>[] array = (KeyValuePair<TKey, TValue>[])
+ siInfo.GetValue(KeyValuePairsName, typeof(KeyValuePair<TKey, TValue>[]));
+
+ if (array==null) {
+ ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MissingKeys);
+ }
+
+ for (int i=0; i<array.Length; i++) {
+ if ( array[i].Key == null) {
+ ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_NullKey);
+ }
+ Insert(array[i].Key, array[i].Value, true);
+ }
+ }
+ else {
+ buckets = null;
+ }
+
+ version = realVersion;
+ HashHelpers.SerializationInfoTable.Remove(this);
+ }
+
+ private void Resize() {
+ Resize(HashHelpers.ExpandPrime(count), false);
+ }
+
+ private void Resize(int newSize, bool forceNewHashCodes) {
+ Contract.Assert(newSize >= entries.Length);
+ int[] newBuckets = new int[newSize];
+ for (int i = 0; i < newBuckets.Length; i++) newBuckets[i] = -1;
+ Entry[] newEntries = new Entry[newSize];
+ Array.Copy(entries, 0, newEntries, 0, count);
+ if(forceNewHashCodes) {
+ for (int i = 0; i < count; i++) {
+ if(newEntries[i].hashCode != -1) {
+ newEntries[i].hashCode = (comparer.GetHashCode(newEntries[i].key) & 0x7FFFFFFF);
+ }
+ }
+ }
+ for (int i = 0; i < count; i++) {
+ if (newEntries[i].hashCode >= 0) {
+ int bucket = newEntries[i].hashCode % newSize;
+ newEntries[i].next = newBuckets[bucket];
+ newBuckets[bucket] = i;
+ }
+ }
+ buckets = newBuckets;
+ entries = newEntries;
+ }
+
+ public bool Remove(TKey key) {
+ if(key == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+ }
+
+ if (buckets != null) {
+ int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
+ int bucket = hashCode % buckets.Length;
+ int last = -1;
+ for (int i = buckets[bucket]; i >= 0; last = i, i = entries[i].next) {
+ if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) {
+ if (last < 0) {
+ buckets[bucket] = entries[i].next;
+ }
+ else {
+ entries[last].next = entries[i].next;
+ }
+ entries[i].hashCode = -1;
+ entries[i].next = freeList;
+ entries[i].key = default(TKey);
+ entries[i].value = default(TValue);
+ freeList = i;
+ freeCount++;
+ version++;
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public bool TryGetValue(TKey key, out TValue value) {
+ int i = FindEntry(key);
+ if (i >= 0) {
+ value = entries[i].value;
+ return true;
+ }
+ value = default(TValue);
+ return false;
+ }
+
+ // This is a convenience method for the internal callers that were converted from using Hashtable.
+ // Many were combining key doesn't exist and key exists but null value (for non-value types) checks.
+ // This allows them to continue getting that behavior with minimal code delta. This is basically
+ // TryGetValue without the out param
+ internal TValue GetValueOrDefault(TKey key) {
+ int i = FindEntry(key);
+ if (i >= 0) {
+ return entries[i].value;
+ }
+ return default(TValue);
+ }
+
+ bool ICollection<KeyValuePair<TKey,TValue>>.IsReadOnly {
+ get { return false; }
+ }
+
+ void ICollection<KeyValuePair<TKey,TValue>>.CopyTo(KeyValuePair<TKey,TValue>[] array, int index) {
+ CopyTo(array, index);
+ }
+
+ void ICollection.CopyTo(Array array, int index) {
+ if (array == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ }
+
+ if (array.Rank != 1) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
+ }
+
+ if( array.GetLowerBound(0) != 0 ) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound);
+ }
+
+ if (index < 0 || index > array.Length) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
+
+ if (array.Length - index < Count) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
+ }
+
+ KeyValuePair<TKey,TValue>[] pairs = array as KeyValuePair<TKey,TValue>[];
+ if (pairs != null) {
+ CopyTo(pairs, index);
+ }
+ else if( array is DictionaryEntry[]) {
+ DictionaryEntry[] dictEntryArray = array as DictionaryEntry[];
+ Entry[] entries = this.entries;
+ for (int i = 0; i < count; i++) {
+ if (entries[i].hashCode >= 0) {
+ dictEntryArray[index++] = new DictionaryEntry(entries[i].key, entries[i].value);
+ }
+ }
+ }
+ else {
+ object[] objects = array as object[];
+ if (objects == null) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
+ }
+
+ try {
+ int count = this.count;
+ Entry[] entries = this.entries;
+ for (int i = 0; i < count; i++) {
+ if (entries[i].hashCode >= 0) {
+ objects[index++] = new KeyValuePair<TKey,TValue>(entries[i].key, entries[i].value);
+ }
+ }
+ }
+ catch(ArrayTypeMismatchException) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
+ }
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() {
+ return new Enumerator(this, Enumerator.KeyValuePair);
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return false; }
+ }
+
+ object ICollection.SyncRoot {
+ get {
+ if( _syncRoot == null) {
+ System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);
+ }
+ return _syncRoot;
+ }
+ }
+
+ bool IDictionary.IsFixedSize {
+ get { return false; }
+ }
+
+ bool IDictionary.IsReadOnly {
+ get { return false; }
+ }
+
+ ICollection IDictionary.Keys {
+ get { return (ICollection)Keys; }
+ }
+
+ ICollection IDictionary.Values {
+ get { return (ICollection)Values; }
+ }
+
+ object IDictionary.this[object key] {
+ get {
+ if( IsCompatibleKey(key)) {
+ int i = FindEntry((TKey)key);
+ if (i >= 0) {
+ return entries[i].value;
+ }
+ }
+ return null;
+ }
+ set {
+ if (key == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+ }
+ ThrowHelper.IfNullAndNullsAreIllegalThenThrow<TValue>(value, ExceptionArgument.value);
+
+ try {
+ TKey tempKey = (TKey)key;
+ try {
+ this[tempKey] = (TValue)value;
+ }
+ catch (InvalidCastException) {
+ ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(TValue));
+ }
+ }
+ catch (InvalidCastException) {
+ ThrowHelper.ThrowWrongKeyTypeArgumentException(key, typeof(TKey));
+ }
+ }
+ }
+
+ private static bool IsCompatibleKey(object key) {
+ if( key == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+ }
+ return (key is TKey);
+ }
+
+ void IDictionary.Add(object key, object value) {
+ if (key == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+ }
+ ThrowHelper.IfNullAndNullsAreIllegalThenThrow<TValue>(value, ExceptionArgument.value);
+
+ try {
+ TKey tempKey = (TKey)key;
+
+ try {
+ Add(tempKey, (TValue)value);
+ }
+ catch (InvalidCastException) {
+ ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(TValue));
+ }
+ }
+ catch (InvalidCastException) {
+ ThrowHelper.ThrowWrongKeyTypeArgumentException(key, typeof(TKey));
+ }
+ }
+
+ bool IDictionary.Contains(object key) {
+ if(IsCompatibleKey(key)) {
+ return ContainsKey((TKey)key);
+ }
+
+ return false;
+ }
+
+ IDictionaryEnumerator IDictionary.GetEnumerator() {
+ return new Enumerator(this, Enumerator.DictEntry);
+ }
+
+ void IDictionary.Remove(object key) {
+ if(IsCompatibleKey(key)) {
+ Remove((TKey)key);
+ }
+ }
+
+ [Serializable]
+ public struct Enumerator: IEnumerator<KeyValuePair<TKey,TValue>>,
+ IDictionaryEnumerator
+ {
+ private Dictionary<TKey,TValue> dictionary;
+ private int version;
+ private int index;
+ private KeyValuePair<TKey,TValue> current;
+ private int getEnumeratorRetType; // What should Enumerator.Current return?
+
+ internal const int DictEntry = 1;
+ internal const int KeyValuePair = 2;
+
+ internal Enumerator(Dictionary<TKey,TValue> dictionary, int getEnumeratorRetType) {
+ this.dictionary = dictionary;
+ version = dictionary.version;
+ index = 0;
+ this.getEnumeratorRetType = getEnumeratorRetType;
+ current = new KeyValuePair<TKey, TValue>();
+ }
+
+ public bool MoveNext() {
+ if (version != dictionary.version) {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
+ }
+
+ // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends.
+ // dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue
+ while ((uint)index < (uint)dictionary.count) {
+ if (dictionary.entries[index].hashCode >= 0) {
+ current = new KeyValuePair<TKey, TValue>(dictionary.entries[index].key, dictionary.entries[index].value);
+ index++;
+ return true;
+ }
+ index++;
+ }
+
+ index = dictionary.count + 1;
+ current = new KeyValuePair<TKey, TValue>();
+ return false;
+ }
+
+ public KeyValuePair<TKey,TValue> Current {
+ get { return current; }
+ }
+
+ public void Dispose() {
+ }
+
+ object IEnumerator.Current {
+ get {
+ if( index == 0 || (index == dictionary.count + 1)) {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
+ }
+
+ if (getEnumeratorRetType == DictEntry) {
+ return new System.Collections.DictionaryEntry(current.Key, current.Value);
+ } else {
+ return new KeyValuePair<TKey, TValue>(current.Key, current.Value);
+ }
+ }
+ }
+
+ void IEnumerator.Reset() {
+ if (version != dictionary.version) {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
+ }
+
+ index = 0;
+ current = new KeyValuePair<TKey, TValue>();
+ }
+
+ DictionaryEntry IDictionaryEnumerator.Entry {
+ get {
+ if( index == 0 || (index == dictionary.count + 1)) {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
+ }
+
+ return new DictionaryEntry(current.Key, current.Value);
+ }
+ }
+
+ object IDictionaryEnumerator.Key {
+ get {
+ if( index == 0 || (index == dictionary.count + 1)) {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
+ }
+
+ return current.Key;
+ }
+ }
+
+ object IDictionaryEnumerator.Value {
+ get {
+ if( index == 0 || (index == dictionary.count + 1)) {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
+ }
+
+ return current.Value;
+ }
+ }
+ }
+
+ [DebuggerTypeProxy(typeof(Mscorlib_DictionaryKeyCollectionDebugView<,>))]
+ [DebuggerDisplay("Count = {Count}")]
+ [Serializable]
+ public sealed class KeyCollection: ICollection<TKey>, ICollection, IReadOnlyCollection<TKey>
+ {
+ private Dictionary<TKey,TValue> dictionary;
+
+ public KeyCollection(Dictionary<TKey,TValue> dictionary) {
+ if (dictionary == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);
+ }
+ this.dictionary = dictionary;
+ }
+
+ public Enumerator GetEnumerator() {
+ return new Enumerator(dictionary);
+ }
+
+ public void CopyTo(TKey[] array, int index) {
+ if (array == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ }
+
+ if (index < 0 || index > array.Length) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
+
+ if (array.Length - index < dictionary.Count) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
+ }
+
+ int count = dictionary.count;
+ Entry[] entries = dictionary.entries;
+ for (int i = 0; i < count; i++) {
+ if (entries[i].hashCode >= 0) array[index++] = entries[i].key;
+ }
+ }
+
+ public int Count {
+ get { return dictionary.Count; }
+ }
+
+ bool ICollection<TKey>.IsReadOnly {
+ get { return true; }
+ }
+
+ void ICollection<TKey>.Add(TKey item){
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet);
+ }
+
+ void ICollection<TKey>.Clear(){
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet);
+ }
+
+ bool ICollection<TKey>.Contains(TKey item){
+ return dictionary.ContainsKey(item);
+ }
+
+ bool ICollection<TKey>.Remove(TKey item){
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet);
+ return false;
+ }
+
+ IEnumerator<TKey> IEnumerable<TKey>.GetEnumerator() {
+ return new Enumerator(dictionary);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() {
+ return new Enumerator(dictionary);
+ }
+
+ void ICollection.CopyTo(Array array, int index) {
+ if (array==null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ }
+
+ if (array.Rank != 1) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
+ }
+
+ if( array.GetLowerBound(0) != 0 ) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound);
+ }
+
+ if (index < 0 || index > array.Length) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
+
+ if (array.Length - index < dictionary.Count) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
+ }
+
+ TKey[] keys = array as TKey[];
+ if (keys != null) {
+ CopyTo(keys, index);
+ }
+ else {
+ object[] objects = array as object[];
+ if (objects == null) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
+ }
+
+ int count = dictionary.count;
+ Entry[] entries = dictionary.entries;
+ try {
+ for (int i = 0; i < count; i++) {
+ if (entries[i].hashCode >= 0) objects[index++] = entries[i].key;
+ }
+ }
+ catch(ArrayTypeMismatchException) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
+ }
+ }
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return false; }
+ }
+
+ Object ICollection.SyncRoot {
+ get { return ((ICollection)dictionary).SyncRoot; }
+ }
+
+ [Serializable]
+ public struct Enumerator : IEnumerator<TKey>, System.Collections.IEnumerator
+ {
+ private Dictionary<TKey, TValue> dictionary;
+ private int index;
+ private int version;
+ private TKey currentKey;
+
+ internal Enumerator(Dictionary<TKey, TValue> dictionary) {
+ this.dictionary = dictionary;
+ version = dictionary.version;
+ index = 0;
+ currentKey = default(TKey);
+ }
+
+ public void Dispose() {
+ }
+
+ public bool MoveNext() {
+ if (version != dictionary.version) {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
+ }
+
+ while ((uint)index < (uint)dictionary.count) {
+ if (dictionary.entries[index].hashCode >= 0) {
+ currentKey = dictionary.entries[index].key;
+ index++;
+ return true;
+ }
+ index++;
+ }
+
+ index = dictionary.count + 1;
+ currentKey = default(TKey);
+ return false;
+ }
+
+ public TKey Current {
+ get {
+ return currentKey;
+ }
+ }
+
+ Object System.Collections.IEnumerator.Current {
+ get {
+ if( index == 0 || (index == dictionary.count + 1)) {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
+ }
+
+ return currentKey;
+ }
+ }
+
+ void System.Collections.IEnumerator.Reset() {
+ if (version != dictionary.version) {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
+ }
+
+ index = 0;
+ currentKey = default(TKey);
+ }
+ }
+ }
+
+ [DebuggerTypeProxy(typeof(Mscorlib_DictionaryValueCollectionDebugView<,>))]
+ [DebuggerDisplay("Count = {Count}")]
+ [Serializable]
+ public sealed class ValueCollection: ICollection<TValue>, ICollection, IReadOnlyCollection<TValue>
+ {
+ private Dictionary<TKey,TValue> dictionary;
+
+ public ValueCollection(Dictionary<TKey,TValue> dictionary) {
+ if (dictionary == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);
+ }
+ this.dictionary = dictionary;
+ }
+
+ public Enumerator GetEnumerator() {
+ return new Enumerator(dictionary);
+ }
+
+ public void CopyTo(TValue[] array, int index) {
+ if (array == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ }
+
+ if (index < 0 || index > array.Length) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
+
+ if (array.Length - index < dictionary.Count) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
+ }
+
+ int count = dictionary.count;
+ Entry[] entries = dictionary.entries;
+ for (int i = 0; i < count; i++) {
+ if (entries[i].hashCode >= 0) array[index++] = entries[i].value;
+ }
+ }
+
+ public int Count {
+ get { return dictionary.Count; }
+ }
+
+ bool ICollection<TValue>.IsReadOnly {
+ get { return true; }
+ }
+
+ void ICollection<TValue>.Add(TValue item){
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet);
+ }
+
+ bool ICollection<TValue>.Remove(TValue item){
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet);
+ return false;
+ }
+
+ void ICollection<TValue>.Clear(){
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet);
+ }
+
+ bool ICollection<TValue>.Contains(TValue item){
+ return dictionary.ContainsValue(item);
+ }
+
+ IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator() {
+ return new Enumerator(dictionary);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() {
+ return new Enumerator(dictionary);
+ }
+
+ void ICollection.CopyTo(Array array, int index) {
+ if (array == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ }
+
+ if (array.Rank != 1) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
+ }
+
+ if( array.GetLowerBound(0) != 0 ) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound);
+ }
+
+ if (index < 0 || index > array.Length) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
+
+ if (array.Length - index < dictionary.Count)
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
+
+ TValue[] values = array as TValue[];
+ if (values != null) {
+ CopyTo(values, index);
+ }
+ else {
+ object[] objects = array as object[];
+ if (objects == null) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
+ }
+
+ int count = dictionary.count;
+ Entry[] entries = dictionary.entries;
+ try {
+ for (int i = 0; i < count; i++) {
+ if (entries[i].hashCode >= 0) objects[index++] = entries[i].value;
+ }
+ }
+ catch(ArrayTypeMismatchException) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
+ }
+ }
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return false; }
+ }
+
+ Object ICollection.SyncRoot {
+ get { return ((ICollection)dictionary).SyncRoot; }
+ }
+
+ [Serializable]
+ public struct Enumerator : IEnumerator<TValue>, System.Collections.IEnumerator
+ {
+ private Dictionary<TKey, TValue> dictionary;
+ private int index;
+ private int version;
+ private TValue currentValue;
+
+ internal Enumerator(Dictionary<TKey, TValue> dictionary) {
+ this.dictionary = dictionary;
+ version = dictionary.version;
+ index = 0;
+ currentValue = default(TValue);
+ }
+
+ public void Dispose() {
+ }
+
+ public bool MoveNext() {
+ if (version != dictionary.version) {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
+ }
+
+ while ((uint)index < (uint)dictionary.count) {
+ if (dictionary.entries[index].hashCode >= 0) {
+ currentValue = dictionary.entries[index].value;
+ index++;
+ return true;
+ }
+ index++;
+ }
+ index = dictionary.count + 1;
+ currentValue = default(TValue);
+ return false;
+ }
+
+ public TValue Current {
+ get {
+ return currentValue;
+ }
+ }
+
+ Object System.Collections.IEnumerator.Current {
+ get {
+ if( index == 0 || (index == dictionary.count + 1)) {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
+ }
+
+ return currentValue;
+ }
+ }
+
+ void System.Collections.IEnumerator.Reset() {
+ if (version != dictionary.version) {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
+ }
+ index = 0;
+ currentValue = default(TValue);
+ }
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Generic/EqualityComparer.cs b/src/mscorlib/src/System/Collections/Generic/EqualityComparer.cs
new file mode 100644
index 0000000000..b845d64fed
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Generic/EqualityComparer.cs
@@ -0,0 +1,621 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Security;
+using System.Runtime.Serialization;
+
+namespace System.Collections.Generic
+{
+ using System.Globalization;
+ using System.Runtime;
+ using System.Runtime.CompilerServices;
+ using System.Diagnostics.Contracts;
+
+ [Serializable]
+ [TypeDependencyAttribute("System.Collections.Generic.ObjectEqualityComparer`1")]
+ public abstract class EqualityComparer<T> : IEqualityComparer, IEqualityComparer<T>
+ {
+ static readonly EqualityComparer<T> defaultComparer = CreateComparer();
+
+ public static EqualityComparer<T> Default {
+ get {
+ Contract.Ensures(Contract.Result<EqualityComparer<T>>() != null);
+ return defaultComparer;
+ }
+ }
+
+ //
+ // Note that logic in this method is replicated in vm\compile.cpp to ensure that NGen
+ // saves the right instantiations
+ //
+ [System.Security.SecuritySafeCritical] // auto-generated
+ private static EqualityComparer<T> CreateComparer()
+ {
+ Contract.Ensures(Contract.Result<EqualityComparer<T>>() != null);
+
+ object result = null;
+ RuntimeType t = (RuntimeType)typeof(T);
+
+ // Specialize type byte for performance reasons
+ if (t == typeof(byte)) {
+ result = new ByteEqualityComparer();
+ }
+ // If T implements IEquatable<T> return a GenericEqualityComparer<T>
+ else if (typeof(IEquatable<T>).IsAssignableFrom(t))
+ {
+ result = RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(GenericEqualityComparer<int>), t);
+ }
+ else if (default(T) == null) // Reference type/Nullable
+ {
+ // If T is a Nullable<U> where U implements IEquatable<U> return a NullableEqualityComparer<U>
+ if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) {
+ RuntimeType u = (RuntimeType)t.GetGenericArguments()[0];
+ if (typeof(IEquatable<>).MakeGenericType(u).IsAssignableFrom(u)) {
+ result = RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(NullableEqualityComparer<int>), u);
+ }
+ }
+ }
+ // See the METHOD__JIT_HELPERS__UNSAFE_ENUM_CAST and METHOD__JIT_HELPERS__UNSAFE_ENUM_CAST_LONG cases in getILIntrinsicImplementation
+ else if (t.IsEnum) {
+ TypeCode underlyingTypeCode = Type.GetTypeCode(Enum.GetUnderlyingType(t));
+
+ // Depending on the enum type, we need to special case the comparers so that we avoid boxing
+ // Note: We have different comparers for Short and SByte because for those types we need to make sure we call GetHashCode on the actual underlying type as the
+ // implementation of GetHashCode is more complex than for the other types.
+ switch (underlyingTypeCode) {
+ case TypeCode.Int16: // short
+ result = RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(ShortEnumEqualityComparer<short>), t);
+ break;
+ case TypeCode.SByte:
+ result = RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(SByteEnumEqualityComparer<sbyte>), t);
+ break;
+ case TypeCode.Int32:
+ case TypeCode.UInt32:
+ case TypeCode.Byte:
+ case TypeCode.UInt16: //ushort
+ result = RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(EnumEqualityComparer<int>), t);
+ break;
+ case TypeCode.Int64:
+ case TypeCode.UInt64:
+ result = RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(LongEnumEqualityComparer<long>), t);
+ break;
+ }
+ }
+
+ return result != null ?
+ (EqualityComparer<T>)result :
+ new ObjectEqualityComparer<T>(); // Fallback to ObjectEqualityComparer, which uses boxing
+ }
+
+ [Pure]
+ public abstract bool Equals(T x, T y);
+ [Pure]
+ public abstract int GetHashCode(T obj);
+
+ internal virtual int IndexOf(T[] array, T value, int startIndex, int count) {
+ int endIndex = startIndex + count;
+ for (int i = startIndex; i < endIndex; i++) {
+ if (Equals(array[i], value)) return i;
+ }
+ return -1;
+ }
+
+ internal virtual int LastIndexOf(T[] array, T value, int startIndex, int count) {
+ int endIndex = startIndex - count + 1;
+ for (int i = startIndex; i >= endIndex; i--) {
+ if (Equals(array[i], value)) return i;
+ }
+ return -1;
+ }
+
+ int IEqualityComparer.GetHashCode(object obj) {
+ if (obj == null) return 0;
+ if (obj is T) return GetHashCode((T)obj);
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArgumentForComparison);
+ return 0;
+ }
+
+ bool IEqualityComparer.Equals(object x, object y) {
+ if (x == y) return true;
+ if (x == null || y == null) return false;
+ if ((x is T) && (y is T)) return Equals((T)x, (T)y);
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArgumentForComparison);
+ return false;
+ }
+ }
+
+ // The methods in this class look identical to the inherited methods, but the calls
+ // to Equal bind to IEquatable<T>.Equals(T) instead of Object.Equals(Object)
+ [Serializable]
+ internal class GenericEqualityComparer<T>: EqualityComparer<T> where T: IEquatable<T>
+ {
+ [Pure]
+ public override bool Equals(T x, T y) {
+ if (x != null) {
+ if (y != null) return x.Equals(y);
+ return false;
+ }
+ if (y != null) return false;
+ return true;
+ }
+
+ [Pure]
+ public override int GetHashCode(T obj) {
+ if (obj == null) return 0;
+ return obj.GetHashCode();
+ }
+
+ internal override int IndexOf(T[] array, T value, int startIndex, int count) {
+ int endIndex = startIndex + count;
+ if (value == null) {
+ for (int i = startIndex; i < endIndex; i++) {
+ if (array[i] == null) return i;
+ }
+ }
+ else {
+ for (int i = startIndex; i < endIndex; i++) {
+ if (array[i] != null && array[i].Equals(value)) return i;
+ }
+ }
+ return -1;
+ }
+
+ internal override int LastIndexOf(T[] array, T value, int startIndex, int count) {
+ int endIndex = startIndex - count + 1;
+ if (value == null) {
+ for (int i = startIndex; i >= endIndex; i--) {
+ if (array[i] == null) return i;
+ }
+ }
+ else {
+ for (int i = startIndex; i >= endIndex; i--) {
+ if (array[i] != null && array[i].Equals(value)) return i;
+ }
+ }
+ return -1;
+ }
+
+ // Equals method for the comparer itself.
+ public override bool Equals(Object obj){
+ GenericEqualityComparer<T> comparer = obj as GenericEqualityComparer<T>;
+ return comparer != null;
+ }
+
+ public override int GetHashCode() {
+ return this.GetType().Name.GetHashCode();
+ }
+ }
+
+ [Serializable]
+ internal class NullableEqualityComparer<T> : EqualityComparer<Nullable<T>> where T : struct, IEquatable<T>
+ {
+ [Pure]
+ public override bool Equals(Nullable<T> x, Nullable<T> y) {
+ if (x.HasValue) {
+ if (y.HasValue) return x.value.Equals(y.value);
+ return false;
+ }
+ if (y.HasValue) return false;
+ return true;
+ }
+
+ [Pure]
+ public override int GetHashCode(Nullable<T> obj) {
+ return obj.GetHashCode();
+ }
+
+ internal override int IndexOf(Nullable<T>[] array, Nullable<T> value, int startIndex, int count) {
+ int endIndex = startIndex + count;
+ if (!value.HasValue) {
+ for (int i = startIndex; i < endIndex; i++) {
+ if (!array[i].HasValue) return i;
+ }
+ }
+ else {
+ for (int i = startIndex; i < endIndex; i++) {
+ if (array[i].HasValue && array[i].value.Equals(value.value)) return i;
+ }
+ }
+ return -1;
+ }
+
+ internal override int LastIndexOf(Nullable<T>[] array, Nullable<T> value, int startIndex, int count) {
+ int endIndex = startIndex - count + 1;
+ if (!value.HasValue) {
+ for (int i = startIndex; i >= endIndex; i--) {
+ if (!array[i].HasValue) return i;
+ }
+ }
+ else {
+ for (int i = startIndex; i >= endIndex; i--) {
+ if (array[i].HasValue && array[i].value.Equals(value.value)) return i;
+ }
+ }
+ return -1;
+ }
+
+ // Equals method for the comparer itself.
+ public override bool Equals(Object obj){
+ NullableEqualityComparer<T> comparer = obj as NullableEqualityComparer<T>;
+ return comparer != null;
+ }
+
+ public override int GetHashCode() {
+ return this.GetType().Name.GetHashCode();
+ }
+ }
+
+ [Serializable]
+ internal class ObjectEqualityComparer<T>: EqualityComparer<T>
+ {
+ [Pure]
+ public override bool Equals(T x, T y) {
+ if (x != null) {
+ if (y != null) return x.Equals(y);
+ return false;
+ }
+ if (y != null) return false;
+ return true;
+ }
+
+ [Pure]
+ public override int GetHashCode(T obj) {
+ if (obj == null) return 0;
+ return obj.GetHashCode();
+ }
+
+ internal override int IndexOf(T[] array, T value, int startIndex, int count) {
+ int endIndex = startIndex + count;
+ if (value == null) {
+ for (int i = startIndex; i < endIndex; i++) {
+ if (array[i] == null) return i;
+ }
+ }
+ else {
+ for (int i = startIndex; i < endIndex; i++) {
+ if (array[i] != null && array[i].Equals(value)) return i;
+ }
+ }
+ return -1;
+ }
+
+ internal override int LastIndexOf(T[] array, T value, int startIndex, int count) {
+ int endIndex = startIndex - count + 1;
+ if (value == null) {
+ for (int i = startIndex; i >= endIndex; i--) {
+ if (array[i] == null) return i;
+ }
+ }
+ else {
+ for (int i = startIndex; i >= endIndex; i--) {
+ if (array[i] != null && array[i].Equals(value)) return i;
+ }
+ }
+ return -1;
+ }
+
+ // Equals method for the comparer itself.
+ public override bool Equals(Object obj){
+ ObjectEqualityComparer<T> comparer = obj as ObjectEqualityComparer<T>;
+ return comparer != null;
+ }
+
+ public override int GetHashCode() {
+ return this.GetType().Name.GetHashCode();
+ }
+ }
+
+#if FEATURE_CORECLR
+ // NonRandomizedStringEqualityComparer is the comparer used by default with the Dictionary<string,...>
+ // As the randomized string hashing is turned on by default on coreclr, we need to keep the performance not affected
+ // as much as possible in the main stream scenarios like Dictionary<string,>
+ // We use NonRandomizedStringEqualityComparer as default comparer as it doesnt use the randomized string hashing which
+ // keep the perofrmance not affected till we hit collision threshold and then we switch to the comparer which is using
+ // randomized string hashing GenericEqualityComparer<string>
+
+ internal class NonRandomizedStringEqualityComparer : GenericEqualityComparer<string> {
+ static IEqualityComparer<string> s_nonRandomizedComparer;
+
+ internal static new IEqualityComparer<string> Default {
+ get {
+ if (s_nonRandomizedComparer == null) {
+ s_nonRandomizedComparer = new NonRandomizedStringEqualityComparer();
+ }
+ return s_nonRandomizedComparer;
+ }
+ }
+
+ [Pure]
+ public override int GetHashCode(string obj) {
+ if (obj == null) return 0;
+ return obj.GetLegacyNonRandomizedHashCode();
+ }
+ }
+#endif // FEATURE_CORECLR
+
+ // Performance of IndexOf on byte array is very important for some scenarios.
+ // We will call the C runtime function memchr, which is optimized.
+ [Serializable]
+ internal class ByteEqualityComparer: EqualityComparer<byte>
+ {
+ [Pure]
+ public override bool Equals(byte x, byte y) {
+ return x == y;
+ }
+
+ [Pure]
+ public override int GetHashCode(byte b) {
+ return b.GetHashCode();
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ internal unsafe override int IndexOf(byte[] array, byte value, int startIndex, int count) {
+ if (array==null)
+ throw new ArgumentNullException("array");
+ if (startIndex < 0)
+ throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ if (count < 0)
+ throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Count"));
+ if (count > array.Length - startIndex)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ Contract.EndContractBlock();
+ if (count == 0) return -1;
+ fixed (byte* pbytes = array) {
+ return Buffer.IndexOfByte(pbytes, value, startIndex, count);
+ }
+ }
+
+ internal override int LastIndexOf(byte[] array, byte value, int startIndex, int count) {
+ int endIndex = startIndex - count + 1;
+ for (int i = startIndex; i >= endIndex; i--) {
+ if (array[i] == value) return i;
+ }
+ return -1;
+ }
+
+ // Equals method for the comparer itself.
+ public override bool Equals(Object obj){
+ ByteEqualityComparer comparer = obj as ByteEqualityComparer;
+ return comparer != null;
+ }
+
+ public override int GetHashCode() {
+ return this.GetType().Name.GetHashCode();
+ }
+ }
+
+ [Serializable]
+ internal class EnumEqualityComparer<T> : EqualityComparer<T>, ISerializable where T : struct
+ {
+ [Pure]
+ public override bool Equals(T x, T y) {
+ int x_final = System.Runtime.CompilerServices.JitHelpers.UnsafeEnumCast(x);
+ int y_final = System.Runtime.CompilerServices.JitHelpers.UnsafeEnumCast(y);
+ return x_final == y_final;
+ }
+
+ [Pure]
+ public override int GetHashCode(T obj) {
+ int x_final = System.Runtime.CompilerServices.JitHelpers.UnsafeEnumCast(obj);
+ return x_final.GetHashCode();
+ }
+
+ public EnumEqualityComparer() { }
+
+ // This is used by the serialization engine.
+ protected EnumEqualityComparer(SerializationInfo information, StreamingContext context) { }
+
+ [SecurityCritical]
+ public void GetObjectData(SerializationInfo info, StreamingContext context) {
+ // For back-compat we need to serialize the comparers for enums with underlying types other than int as ObjectEqualityComparer
+ if (Type.GetTypeCode(Enum.GetUnderlyingType(typeof(T))) != TypeCode.Int32) {
+ info.SetType(typeof(ObjectEqualityComparer<T>));
+ }
+ }
+
+ // Equals method for the comparer itself.
+ public override bool Equals(Object obj){
+ EnumEqualityComparer<T> comparer = obj as EnumEqualityComparer<T>;
+ return comparer != null;
+ }
+
+ public override int GetHashCode() {
+ return this.GetType().Name.GetHashCode();
+ }
+ }
+
+ [Serializable]
+ internal sealed class SByteEnumEqualityComparer<T> : EnumEqualityComparer<T>, ISerializable where T : struct
+ {
+ public SByteEnumEqualityComparer() { }
+
+ // This is used by the serialization engine.
+ public SByteEnumEqualityComparer(SerializationInfo information, StreamingContext context) { }
+
+ [Pure]
+ public override int GetHashCode(T obj) {
+ int x_final = System.Runtime.CompilerServices.JitHelpers.UnsafeEnumCast(obj);
+ return ((sbyte)x_final).GetHashCode();
+ }
+ }
+
+ [Serializable]
+ internal sealed class ShortEnumEqualityComparer<T> : EnumEqualityComparer<T>, ISerializable where T : struct
+ {
+ public ShortEnumEqualityComparer() { }
+
+ // This is used by the serialization engine.
+ public ShortEnumEqualityComparer(SerializationInfo information, StreamingContext context) { }
+
+ [Pure]
+ public override int GetHashCode(T obj) {
+ int x_final = System.Runtime.CompilerServices.JitHelpers.UnsafeEnumCast(obj);
+ return ((short)x_final).GetHashCode();
+ }
+ }
+
+ [Serializable]
+ internal sealed class LongEnumEqualityComparer<T> : EqualityComparer<T>, ISerializable where T : struct
+ {
+ [Pure]
+ public override bool Equals(T x, T y) {
+ long x_final = System.Runtime.CompilerServices.JitHelpers.UnsafeEnumCastLong(x);
+ long y_final = System.Runtime.CompilerServices.JitHelpers.UnsafeEnumCastLong(y);
+ return x_final == y_final;
+ }
+
+ [Pure]
+ public override int GetHashCode(T obj) {
+ long x_final = System.Runtime.CompilerServices.JitHelpers.UnsafeEnumCastLong(obj);
+ return x_final.GetHashCode();
+ }
+
+ // Equals method for the comparer itself.
+ public override bool Equals(Object obj){
+ LongEnumEqualityComparer<T> comparer = obj as LongEnumEqualityComparer<T>;
+ return comparer != null;
+ }
+
+ public override int GetHashCode() {
+ return this.GetType().Name.GetHashCode();
+ }
+
+ public LongEnumEqualityComparer() { }
+
+ // This is used by the serialization engine.
+ public LongEnumEqualityComparer(SerializationInfo information, StreamingContext context) { }
+
+ [SecurityCritical]
+ public void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ // The LongEnumEqualityComparer does not exist on 4.0 so we need to serialize this comparer as ObjectEqualityComparer
+ // to allow for roundtrip between 4.0 and 4.5.
+ info.SetType(typeof(ObjectEqualityComparer<T>));
+ }
+ }
+
+#if FEATURE_RANDOMIZED_STRING_HASHING
+ // This type is not serializeable by design. It does not exist in previous versions and will be removed
+ // Once we move the framework to using secure hashing by default.
+ internal sealed class RandomizedStringEqualityComparer : IEqualityComparer<String>, IEqualityComparer, IWellKnownStringEqualityComparer
+ {
+ private long _entropy;
+
+ public RandomizedStringEqualityComparer() {
+ _entropy = HashHelpers.GetEntropy();
+ }
+
+ public new bool Equals(object x, object y) {
+ if (x == y) return true;
+ if (x == null || y == null) return false;
+ if ((x is string) && (y is string)) return Equals((string)x, (string)y);
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArgumentForComparison);
+ return false;
+ }
+
+ [Pure]
+ public bool Equals(string x, string y) {
+ if (x != null) {
+ if (y != null) return x.Equals(y);
+ return false;
+ }
+ if (y != null) return false;
+ return true;
+ }
+
+ [Pure]
+ [SecuritySafeCritical]
+ public int GetHashCode(String obj) {
+ if(obj == null) return 0;
+ return String.InternalMarvin32HashString(obj, obj.Length, _entropy);
+ }
+
+ [Pure]
+ [SecuritySafeCritical]
+ public int GetHashCode(Object obj) {
+ if(obj == null) return 0;
+
+ string sObj = obj as string;
+ if(sObj != null) return String.InternalMarvin32HashString(sObj, sObj.Length, _entropy);
+
+ return obj.GetHashCode();
+ }
+
+ // Equals method for the comparer itself.
+ public override bool Equals(Object obj) {
+ RandomizedStringEqualityComparer comparer = obj as RandomizedStringEqualityComparer;
+ return (comparer != null) && (this._entropy == comparer._entropy);
+ }
+
+ public override int GetHashCode() {
+ return (this.GetType().Name.GetHashCode() ^ ((int) (_entropy & 0x7FFFFFFF)));
+ }
+
+
+ IEqualityComparer IWellKnownStringEqualityComparer.GetRandomizedEqualityComparer() {
+ return new RandomizedStringEqualityComparer();
+ }
+
+ // We want to serialize the old comparer.
+ IEqualityComparer IWellKnownStringEqualityComparer.GetEqualityComparerForSerialization() {
+ return EqualityComparer<string>.Default;
+ }
+ }
+
+ // This type is not serializeable by design. It does not exist in previous versions and will be removed
+ // Once we move the framework to using secure hashing by default.
+ internal sealed class RandomizedObjectEqualityComparer : IEqualityComparer, IWellKnownStringEqualityComparer
+ {
+ private long _entropy;
+
+ public RandomizedObjectEqualityComparer() {
+ _entropy = HashHelpers.GetEntropy();
+ }
+
+ [Pure]
+ public new bool Equals(Object x, Object y) {
+ if (x != null) {
+ if (y != null) return x.Equals(y);
+ return false;
+ }
+ if (y != null) return false;
+ return true;
+ }
+
+ [Pure]
+ [SecuritySafeCritical]
+ public int GetHashCode(Object obj) {
+ if(obj == null) return 0;
+
+ string sObj = obj as string;
+ if(sObj != null) return String.InternalMarvin32HashString(sObj, sObj.Length, _entropy);
+
+ return obj.GetHashCode();
+ }
+
+ // Equals method for the comparer itself.
+ public override bool Equals(Object obj){
+ RandomizedObjectEqualityComparer comparer = obj as RandomizedObjectEqualityComparer;
+ return (comparer != null) && (this._entropy == comparer._entropy);
+ }
+
+ public override int GetHashCode() {
+ return (this.GetType().Name.GetHashCode() ^ ((int) (_entropy & 0x7FFFFFFF)));
+ }
+
+ IEqualityComparer IWellKnownStringEqualityComparer.GetRandomizedEqualityComparer() {
+ return new RandomizedObjectEqualityComparer();
+ }
+
+ // We want to serialize the old comparer, which in this case was null.
+ IEqualityComparer IWellKnownStringEqualityComparer.GetEqualityComparerForSerialization() {
+ return null;
+ }
+ }
+#endif
+}
+
diff --git a/src/mscorlib/src/System/Collections/Generic/ICollection.cs b/src/mscorlib/src/System/Collections/Generic/ICollection.cs
new file mode 100644
index 0000000000..741e8cc79b
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Generic/ICollection.cs
@@ -0,0 +1,52 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: ICollection
+**
+**
+**
+**
+** Purpose: Base interface for all generic collections.
+**
+**
+===========================================================*/
+namespace System.Collections.Generic {
+ using System;
+ using System.Runtime.CompilerServices;
+ using System.Diagnostics.Contracts;
+
+ // Base interface for all collections, defining enumerators, size, and
+ // synchronization methods.
+
+ // Note that T[] : IList<T>, and we want to ensure that if you use
+ // IList<YourValueType>, we ensure a YourValueType[] can be used
+ // without jitting. Hence the TypeDependencyAttribute on SZArrayHelper.
+ // This is a special workaround internally though - see VM\compile.cpp.
+ // The same attribute is on IEnumerable<T> and ICollection<T>.
+ [TypeDependencyAttribute("System.SZArrayHelper")]
+ public interface ICollection<T> : IEnumerable<T>
+ {
+ // Number of items in the collections.
+ int Count { get; }
+
+ bool IsReadOnly { get; }
+
+ void Add(T item);
+
+ void Clear();
+
+ bool Contains(T item);
+
+ // CopyTo copies a collection into an Array, starting at a particular
+ // index into the array.
+ //
+ void CopyTo(T[] array, int arrayIndex);
+
+ //void CopyTo(int sourceIndex, T[] destinationArray, int destinationIndex, int count);
+
+ bool Remove(T item);
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Generic/IComparer.cs b/src/mscorlib/src/System/Collections/Generic/IComparer.cs
new file mode 100644
index 0000000000..7b9e97ff0e
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Generic/IComparer.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: IComparer
+**
+**
+**
+**
+** Purpose: Interface for comparing two generic Objects.
+**
+**
+===========================================================*/
+namespace System.Collections.Generic {
+
+ using System;
+ // The generic IComparer interface implements a method that compares
+ // two objects. It is used in conjunction with the Sort and
+ // BinarySearch methods on the Array, List, and SortedList classes.
+ public interface IComparer<in T>
+ {
+ // Compares two objects. An implementation of this method must return a
+ // value less than zero if x is less than y, zero if x is equal to y, or a
+ // value greater than zero if x is greater than y.
+ //
+ int Compare(T x, T y);
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Generic/IDictionary.cs b/src/mscorlib/src/System/Collections/Generic/IDictionary.cs
new file mode 100644
index 0000000000..2a2da944d3
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Generic/IDictionary.cs
@@ -0,0 +1,58 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: IDictionary
+**
+**
+**
+**
+** Purpose: Base interface for all generic dictionaries.
+**
+**
+===========================================================*/
+namespace System.Collections.Generic {
+ using System;
+ using System.Diagnostics.Contracts;
+
+ // An IDictionary is a possibly unordered set of key-value pairs.
+ // Keys can be any non-null object. Values can be any object.
+ // You can look up a value in an IDictionary via the default indexed
+ // property, Items.
+ public interface IDictionary<TKey, TValue> : ICollection<KeyValuePair<TKey, TValue>>
+ {
+ // Interfaces are not serializable
+ // The Item property provides methods to read and edit entries
+ // in the Dictionary.
+ TValue this[TKey key] {
+ get;
+ set;
+ }
+
+ // Returns a collections of the keys in this dictionary.
+ ICollection<TKey> Keys {
+ get;
+ }
+
+ // Returns a collections of the values in this dictionary.
+ ICollection<TValue> Values {
+ get;
+ }
+
+ // Returns whether this dictionary contains a particular key.
+ //
+ bool ContainsKey(TKey key);
+
+ // Adds a key-value pair to the dictionary.
+ //
+ void Add(TKey key, TValue value);
+
+ // Removes a particular key from the dictionary.
+ //
+ bool Remove(TKey key);
+
+ bool TryGetValue(TKey key, out TValue value);
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Generic/IEnumerable.cs b/src/mscorlib/src/System/Collections/Generic/IEnumerable.cs
new file mode 100644
index 0000000000..67f35ce675
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Generic/IEnumerable.cs
@@ -0,0 +1,38 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: IEnumerable
+**
+**
+**
+**
+** Purpose: Interface for providing generic IEnumerators
+**
+**
+===========================================================*/
+namespace System.Collections.Generic {
+ using System;
+ using System.Collections;
+ using System.Runtime.InteropServices;
+ using System.Runtime.CompilerServices;
+ using System.Diagnostics.Contracts;
+
+ // Implement this interface if you need to support foreach semantics.
+
+ // Note that T[] : IList<T>, and we want to ensure that if you use
+ // IList<YourValueType>, we ensure a YourValueType[] can be used
+ // without jitting. Hence the TypeDependencyAttribute on SZArrayHelper.
+ // This is a special workaround internally though - see VM\compile.cpp.
+ // The same attribute is on IList<T> and ICollection<T>.
+ [TypeDependencyAttribute("System.SZArrayHelper")]
+ public interface IEnumerable<out T> : IEnumerable
+ {
+ // Returns an IEnumerator for this enumerable Object. The enumerator provides
+ // a simple way to access all the contents of a collection.
+ /// <include file='doc\IEnumerable.uex' path='docs/doc[@for="IEnumerable.GetEnumerator"]/*' />
+ new IEnumerator<T> GetEnumerator();
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Generic/IEnumerator.cs b/src/mscorlib/src/System/Collections/Generic/IEnumerator.cs
new file mode 100644
index 0000000000..335616757b
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Generic/IEnumerator.cs
@@ -0,0 +1,35 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: IEnumerator
+**
+**
+**
+**
+** Purpose: Base interface for all generic enumerators.
+**
+**
+===========================================================*/
+namespace System.Collections.Generic {
+ using System;
+ using System.Runtime.InteropServices;
+
+ // Base interface for all generic enumerators, providing a simple approach
+ // to iterating over a collection.
+ public interface IEnumerator<out T> : IDisposable, IEnumerator
+ {
+ // Returns the current element of the enumeration. The returned value is
+ // undefined before the first call to MoveNext and following a
+ // call to MoveNext that returned false. Multiple calls to
+ // GetCurrent with no intervening calls to MoveNext
+ // will return the same object.
+ //
+ /// <include file='doc\IEnumerator.uex' path='docs/doc[@for="IEnumerator.Current"]/*' />
+ new T Current {
+ get;
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Generic/IEqualityComparer.cs b/src/mscorlib/src/System/Collections/Generic/IEqualityComparer.cs
new file mode 100644
index 0000000000..b6ac3be006
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Generic/IEqualityComparer.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+
+namespace System.Collections.Generic {
+ using System;
+
+ // The generic IEqualityComparer interface implements methods to if check two objects are equal
+ // and generate Hashcode for an object.
+ // It is use in Dictionary class.
+ public interface IEqualityComparer<in T>
+ {
+ bool Equals(T x, T y);
+ int GetHashCode(T obj);
+ }
+}
+
diff --git a/src/mscorlib/src/System/Collections/Generic/IList.cs b/src/mscorlib/src/System/Collections/Generic/IList.cs
new file mode 100644
index 0000000000..75ca0a9b00
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Generic/IList.cs
@@ -0,0 +1,54 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: IList
+**
+**
+**
+**
+** Purpose: Base interface for all generic lists.
+**
+**
+===========================================================*/
+namespace System.Collections.Generic {
+
+ using System;
+ using System.Collections;
+ using System.Runtime.CompilerServices;
+ using System.Diagnostics.Contracts;
+
+ // An IList is an ordered collection of objects. The exact ordering
+ // is up to the implementation of the list, ranging from a sorted
+ // order to insertion order.
+
+ // Note that T[] : IList<T>, and we want to ensure that if you use
+ // IList<YourValueType>, we ensure a YourValueType[] can be used
+ // without jitting. Hence the TypeDependencyAttribute on SZArrayHelper.
+ // This is a special workaround internally though - see VM\compile.cpp.
+ // The same attribute is on IEnumerable<T> and ICollection<T>.
+ [TypeDependencyAttribute("System.SZArrayHelper")]
+ public interface IList<T> : ICollection<T>
+ {
+ // The Item property provides methods to read and edit entries in the List.
+ T this[int index] {
+ get;
+ set;
+ }
+
+ // Returns the index of a particular item, if it is in the list.
+ // Returns -1 if the item isn't in the list.
+ int IndexOf(T item);
+
+ // Inserts value into the list at position index.
+ // index must be non-negative and less than or equal to the
+ // number of elements in the list. If index equals the number
+ // of items in the list, then value is appended to the end.
+ void Insert(int index, T item);
+
+ // Removes the item at position index.
+ void RemoveAt(int index);
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Generic/IReadOnlyCollection.cs b/src/mscorlib/src/System/Collections/Generic/IReadOnlyCollection.cs
new file mode 100644
index 0000000000..13bc718760
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Generic/IReadOnlyCollection.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: IReadOnlyCollection<T>
+**
+**
+**
+** Purpose: Base interface for read-only generic lists.
+**
+===========================================================*/
+using System;
+using System.Diagnostics.Contracts;
+using System.Runtime.CompilerServices;
+
+namespace System.Collections.Generic
+{
+
+ // Provides a read-only, covariant view of a generic list.
+
+ // Note that T[] : IReadOnlyList<T>, and we want to ensure that if you use
+ // IList<YourValueType>, we ensure a YourValueType[] can be used
+ // without jitting. Hence the TypeDependencyAttribute on SZArrayHelper.
+ // This is a special workaround internally though - see VM\compile.cpp.
+ // The same attribute is on IList<T>, IEnumerable<T>, ICollection<T>, and IReadOnlyList<T>.
+ [TypeDependencyAttribute("System.SZArrayHelper")]
+ // If we ever implement more interfaces on IReadOnlyCollection, we should also update RuntimeTypeCache.PopulateInterfaces() in rttype.cs
+ public interface IReadOnlyCollection<out T> : IEnumerable<T>
+ {
+ int Count { get; }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Generic/IReadOnlyDictionary.cs b/src/mscorlib/src/System/Collections/Generic/IReadOnlyDictionary.cs
new file mode 100644
index 0000000000..3603b9a4ea
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Generic/IReadOnlyDictionary.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: IReadOnlyDictionary<TKey, TValue>
+**
+**
+**
+** Purpose: Base interface for read-only generic dictionaries.
+**
+===========================================================*/
+using System;
+using System.Diagnostics.Contracts;
+
+namespace System.Collections.Generic
+{
+ // Provides a read-only view of a generic dictionary.
+ public interface IReadOnlyDictionary<TKey, TValue> : IReadOnlyCollection<KeyValuePair<TKey, TValue>>
+ {
+ bool ContainsKey(TKey key);
+ bool TryGetValue(TKey key, out TValue value);
+
+ TValue this[TKey key] { get; }
+ IEnumerable<TKey> Keys { get; }
+ IEnumerable<TValue> Values { get; }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Generic/IReadOnlyList.cs b/src/mscorlib/src/System/Collections/Generic/IReadOnlyList.cs
new file mode 100644
index 0000000000..77366f0b2f
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Generic/IReadOnlyList.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: IReadOnlyList<T>
+**
+**
+**
+** Purpose: Base interface for read-only generic lists.
+**
+===========================================================*/
+using System;
+using System.Diagnostics.Contracts;
+using System.Runtime.CompilerServices;
+
+namespace System.Collections.Generic
+{
+
+ // Provides a read-only, covariant view of a generic list.
+
+ // Note that T[] : IReadOnlyList<T>, and we want to ensure that if you use
+ // IList<YourValueType>, we ensure a YourValueType[] can be used
+ // without jitting. Hence the TypeDependencyAttribute on SZArrayHelper.
+ // This is a special workaround internally though - see VM\compile.cpp.
+ // The same attribute is on IList<T>, IEnumerable<T>, ICollection<T> and IReadOnlyCollection<T>.
+ [TypeDependencyAttribute("System.SZArrayHelper")]
+ // If we ever implement more interfaces on IReadOnlyList, we should also update RuntimeTypeCache.PopulateInterfaces() in rttype.cs
+ public interface IReadOnlyList<out T> : IReadOnlyCollection<T>
+ {
+ T this[int index] { get; }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Generic/KeyNotFoundException.cs b/src/mscorlib/src/System/Collections/Generic/KeyNotFoundException.cs
new file mode 100644
index 0000000000..ffcd0405fd
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Generic/KeyNotFoundException.cs
@@ -0,0 +1,45 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+**
+**
+** Purpose: Exception class for Hashtable and Dictionary.
+**
+**
+=============================================================================*/
+
+namespace System.Collections.Generic {
+
+ using System;
+ using System.Runtime.Remoting;
+ using System.Runtime.Serialization;
+
+ [Serializable]
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public class KeyNotFoundException : SystemException, ISerializable {
+
+ public KeyNotFoundException ()
+ : base(Environment.GetResourceString("Arg_KeyNotFound")) {
+ SetErrorCode(System.__HResults.COR_E_KEYNOTFOUND);
+ }
+
+ public KeyNotFoundException(String message)
+ : base(message) {
+ SetErrorCode(System.__HResults.COR_E_KEYNOTFOUND);
+ }
+
+ public KeyNotFoundException(String message, Exception innerException)
+ : base(message, innerException) {
+ SetErrorCode(System.__HResults.COR_E_KEYNOTFOUND);
+ }
+
+
+ protected KeyNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) {
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Generic/KeyValuePair.cs b/src/mscorlib/src/System/Collections/Generic/KeyValuePair.cs
new file mode 100644
index 0000000000..17e1c531f1
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Generic/KeyValuePair.cs
@@ -0,0 +1,56 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: KeyValuePair
+**
+**
+**
+**
+** Purpose: Generic key-value pair for dictionary enumerators.
+**
+**
+===========================================================*/
+namespace System.Collections.Generic {
+
+ using System;
+ using System.Text;
+
+ // A KeyValuePair holds a key and a value from a dictionary.
+ // It is used by the IEnumerable<T> implementation for both IDictionary<TKey, TValue>
+ // and IReadOnlyDictionary<TKey, TValue>.
+ [Serializable]
+ public struct KeyValuePair<TKey, TValue> {
+ private TKey key;
+ private TValue value;
+
+ public KeyValuePair(TKey key, TValue value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public TKey Key {
+ get { return key; }
+ }
+
+ public TValue Value {
+ get { return value; }
+ }
+
+ public override string ToString() {
+ StringBuilder s = StringBuilderCache.Acquire();
+ s.Append('[');
+ if( Key != null) {
+ s.Append(Key.ToString());
+ }
+ s.Append(", ");
+ if( Value != null) {
+ s.Append(Value.ToString());
+ }
+ s.Append(']');
+ return StringBuilderCache.GetStringAndRelease(s);
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Generic/List.cs b/src/mscorlib/src/System/Collections/Generic/List.cs
new file mode 100644
index 0000000000..ae3356d372
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Generic/List.cs
@@ -0,0 +1,1120 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+** Purpose: Implements a generic, dynamically sized list as an
+** array.
+**
+**
+===========================================================*/
+namespace System.Collections.Generic {
+
+ using System;
+ using System.Runtime;
+ using System.Runtime.Versioning;
+ using System.Diagnostics;
+ using System.Diagnostics.Contracts;
+ using System.Collections.ObjectModel;
+ using System.Security.Permissions;
+
+ // Implements a variable-size List that uses an array of objects to store the
+ // elements. A List has a capacity, which is the allocated length
+ // of the internal array. As elements are added to a List, the capacity
+ // of the List is automatically increased as required by reallocating the
+ // internal array.
+ //
+ [DebuggerTypeProxy(typeof(Mscorlib_CollectionDebugView<>))]
+ [DebuggerDisplay("Count = {Count}")]
+ [Serializable]
+ public class List<T> : IList<T>, System.Collections.IList, IReadOnlyList<T>
+ {
+ private const int _defaultCapacity = 4;
+
+ private T[] _items;
+ [ContractPublicPropertyName("Count")]
+ private int _size;
+ private int _version;
+ [NonSerialized]
+ private Object _syncRoot;
+
+ static readonly T[] _emptyArray = new T[0];
+
+ // Constructs a List. The list is initially empty and has a capacity
+ // of zero. Upon adding the first element to the list the capacity is
+ // increased to _defaultCapacity, and then increased in multiples of two
+ // as required.
+ public List() {
+ _items = _emptyArray;
+ }
+
+ // Constructs a List with a given initial capacity. The list is
+ // initially empty, but will have room for the given number of elements
+ // before any reallocations are required.
+ //
+ public List(int capacity) {
+ if (capacity < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ if (capacity == 0)
+ _items = _emptyArray;
+ else
+ _items = new T[capacity];
+ }
+
+ // Constructs a List, copying the contents of the given collection. The
+ // size and capacity of the new list will both be equal to the size of the
+ // given collection.
+ //
+ public List(IEnumerable<T> collection) {
+ if (collection==null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
+ Contract.EndContractBlock();
+
+ ICollection<T> c = collection as ICollection<T>;
+ if( c != null) {
+ int count = c.Count;
+ if (count == 0)
+ {
+ _items = _emptyArray;
+ }
+ else {
+ _items = new T[count];
+ c.CopyTo(_items, 0);
+ _size = count;
+ }
+ }
+ else {
+ _size = 0;
+ _items = _emptyArray;
+ // This enumerable could be empty. Let Add allocate a new array, if needed.
+ // Note it will also go to _defaultCapacity first, not 1, then 2, etc.
+
+ using(IEnumerator<T> en = collection.GetEnumerator()) {
+ while(en.MoveNext()) {
+ Add(en.Current);
+ }
+ }
+ }
+ }
+
+ // Gets and sets the capacity of this list. The capacity is the size of
+ // the internal array used to hold items. When set, the internal
+ // array of the list is reallocated to the given capacity.
+ //
+ public int Capacity {
+ get {
+ Contract.Ensures(Contract.Result<int>() >= 0);
+ return _items.Length;
+ }
+ set {
+ if (value < _size) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity);
+ }
+ Contract.EndContractBlock();
+
+ if (value != _items.Length) {
+ if (value > 0) {
+ T[] newItems = new T[value];
+ if (_size > 0) {
+ Array.Copy(_items, 0, newItems, 0, _size);
+ }
+ _items = newItems;
+ }
+ else {
+ _items = _emptyArray;
+ }
+ }
+ }
+ }
+
+ // Read-only property describing how many elements are in the List.
+ public int Count {
+ get {
+ Contract.Ensures(Contract.Result<int>() >= 0);
+ return _size;
+ }
+ }
+
+ bool System.Collections.IList.IsFixedSize {
+ get { return false; }
+ }
+
+
+ // Is this List read-only?
+ bool ICollection<T>.IsReadOnly {
+ get { return false; }
+ }
+
+ bool System.Collections.IList.IsReadOnly {
+ get { return false; }
+ }
+
+ // Is this List synchronized (thread-safe)?
+ bool System.Collections.ICollection.IsSynchronized {
+ get { return false; }
+ }
+
+ // Synchronization root for this object.
+ Object System.Collections.ICollection.SyncRoot {
+ get {
+ if( _syncRoot == null) {
+ System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);
+ }
+ return _syncRoot;
+ }
+ }
+ // Sets or Gets the element at the given index.
+ //
+ public T this[int index] {
+ get {
+ // Following trick can reduce the range check by one
+ if ((uint) index >= (uint)_size) {
+ ThrowHelper.ThrowArgumentOutOfRange_IndexException();
+ }
+ Contract.EndContractBlock();
+ return _items[index];
+ }
+
+ set {
+ if ((uint) index >= (uint)_size) {
+ ThrowHelper.ThrowArgumentOutOfRange_IndexException();
+ }
+ Contract.EndContractBlock();
+ _items[index] = value;
+ _version++;
+ }
+ }
+
+ private static bool IsCompatibleObject(object value) {
+ // Non-null values are fine. Only accept nulls if T is a class or Nullable<U>.
+ // Note that default(T) is not equal to null for value types except when T is Nullable<U>.
+ return ((value is T) || (value == null && default(T) == null));
+ }
+
+ Object System.Collections.IList.this[int index] {
+ get {
+ return this[index];
+ }
+ set {
+ ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(value, ExceptionArgument.value);
+
+ try {
+ this[index] = (T)value;
+ }
+ catch (InvalidCastException) {
+ ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T));
+ }
+ }
+ }
+
+ // Adds the given object to the end of this list. The size of the list is
+ // increased by one. If required, the capacity of the list is doubled
+ // before adding the new element.
+ //
+ public void Add(T item) {
+ if (_size == _items.Length) EnsureCapacity(_size + 1);
+ _items[_size++] = item;
+ _version++;
+ }
+
+ int System.Collections.IList.Add(Object item)
+ {
+ ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(item, ExceptionArgument.item);
+
+ try {
+ Add((T) item);
+ }
+ catch (InvalidCastException) {
+ ThrowHelper.ThrowWrongValueTypeArgumentException(item, typeof(T));
+ }
+
+ return Count - 1;
+ }
+
+
+ // Adds the elements of the given collection to the end of this list. If
+ // required, the capacity of the list is increased to twice the previous
+ // capacity or the new size, whichever is larger.
+ //
+ public void AddRange(IEnumerable<T> collection) {
+ Contract.Ensures(Count >= Contract.OldValue(Count));
+
+ InsertRange(_size, collection);
+ }
+
+ public ReadOnlyCollection<T> AsReadOnly() {
+ Contract.Ensures(Contract.Result<ReadOnlyCollection<T>>() != null);
+ return new ReadOnlyCollection<T>(this);
+ }
+
+ // Searches a section of the list for a given element using a binary search
+ // algorithm. Elements of the list are compared to the search value using
+ // the given IComparer interface. If comparer is null, elements of
+ // the list are compared to the search value using the IComparable
+ // interface, which in that case must be implemented by all elements of the
+ // list and the given search value. This method assumes that the given
+ // section of the list is already sorted; if this is not the case, the
+ // result will be incorrect.
+ //
+ // The method returns the index of the given value in the list. If the
+ // list does not contain the given value, the method returns a negative
+ // integer. The bitwise complement operator (~) can be applied to a
+ // negative result to produce the index of the first element (if any) that
+ // is larger than the given search value. This is also the index at which
+ // the search value should be inserted into the list in order for the list
+ // to remain sorted.
+ //
+ // The method uses the Array.BinarySearch method to perform the
+ // search.
+ //
+ public int BinarySearch(int index, int count, T item, IComparer<T> comparer) {
+ if (index < 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ if (count < 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ if (_size - index < count)
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
+ Contract.Ensures(Contract.Result<int>() <= index + count);
+ Contract.EndContractBlock();
+
+ return Array.BinarySearch<T>(_items, index, count, item, comparer);
+ }
+
+ public int BinarySearch(T item)
+ {
+ Contract.Ensures(Contract.Result<int>() <= Count);
+ return BinarySearch(0, Count, item, null);
+ }
+
+ public int BinarySearch(T item, IComparer<T> comparer)
+ {
+ Contract.Ensures(Contract.Result<int>() <= Count);
+ return BinarySearch(0, Count, item, comparer);
+ }
+
+
+ // Clears the contents of List.
+ public void Clear() {
+ if (_size > 0)
+ {
+ Array.Clear(_items, 0, _size); // Don't need to doc this but we clear the elements so that the gc can reclaim the references.
+ _size = 0;
+ }
+ _version++;
+ }
+
+ // Contains returns true if the specified element is in the List.
+ // It does a linear, O(n) search. Equality is determined by calling
+ // EqualityComparer<T>.Default.Equals().
+
+ public bool Contains(T item)
+ {
+ // PERF: IndexOf calls Array.IndexOf, which internally
+ // calls EqualityComparer<T>.Default.IndexOf, which
+ // is specialized for different types. This
+ // boosts performance since instead of making a
+ // virtual method call each iteration of the loop,
+ // via EqualityComparer<T>.Default.Equals, we
+ // only make one virtual call to EqualityComparer.IndexOf.
+
+ return _size != 0 && IndexOf(item) != -1;
+ }
+
+ bool System.Collections.IList.Contains(Object item)
+ {
+ if(IsCompatibleObject(item)) {
+ return Contains((T) item);
+ }
+ return false;
+ }
+
+ public List<TOutput> ConvertAll<TOutput>(Converter<T,TOutput> converter) {
+ if( converter == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.converter);
+ }
+
+ Contract.EndContractBlock();
+
+ List<TOutput> list = new List<TOutput>(_size);
+ for( int i = 0; i< _size; i++) {
+ list._items[i] = converter(_items[i]);
+ }
+ list._size = _size;
+ return list;
+ }
+
+ // Copies this List into array, which must be of a
+ // compatible array type.
+ //
+ public void CopyTo(T[] array) {
+ CopyTo(array, 0);
+ }
+
+ // Copies this List into array, which must be of a
+ // compatible array type.
+ //
+ void System.Collections.ICollection.CopyTo(Array array, int arrayIndex) {
+ if ((array != null) && (array.Rank != 1)) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
+ }
+ Contract.EndContractBlock();
+
+ try {
+ // Array.Copy will check for NULL.
+ Array.Copy(_items, 0, array, arrayIndex, _size);
+ }
+ catch(ArrayTypeMismatchException){
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
+ }
+ }
+
+ // Copies a section of this list to the given array at the given index.
+ //
+ // The method uses the Array.Copy method to copy the elements.
+ //
+ public void CopyTo(int index, T[] array, int arrayIndex, int count) {
+ if (_size - index < count) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
+ }
+ Contract.EndContractBlock();
+
+ // Delegate rest of error checking to Array.Copy.
+ Array.Copy(_items, index, array, arrayIndex, count);
+ }
+
+ public void CopyTo(T[] array, int arrayIndex) {
+ // Delegate rest of error checking to Array.Copy.
+ Array.Copy(_items, 0, array, arrayIndex, _size);
+ }
+
+ // Ensures that the capacity of this list is at least the given minimum
+ // value. If the currect capacity of the list is less than min, the
+ // capacity is increased to twice the current capacity or to min,
+ // whichever is larger.
+ private void EnsureCapacity(int min) {
+ if (_items.Length < min) {
+ int newCapacity = _items.Length == 0? _defaultCapacity : _items.Length * 2;
+ // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow.
+ // Note that this check works even when _items.Length overflowed thanks to the (uint) cast
+ if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength;
+ if (newCapacity < min) newCapacity = min;
+ Capacity = newCapacity;
+ }
+ }
+
+ public bool Exists(Predicate<T> match) {
+ return FindIndex(match) != -1;
+ }
+
+ public T Find(Predicate<T> match) {
+ if( match == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
+ }
+ Contract.EndContractBlock();
+
+ for(int i = 0 ; i < _size; i++) {
+ if(match(_items[i])) {
+ return _items[i];
+ }
+ }
+ return default(T);
+ }
+
+ public List<T> FindAll(Predicate<T> match) {
+ if( match == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
+ }
+ Contract.EndContractBlock();
+
+ List<T> list = new List<T>();
+ for(int i = 0 ; i < _size; i++) {
+ if(match(_items[i])) {
+ list.Add(_items[i]);
+ }
+ }
+ return list;
+ }
+
+ public int FindIndex(Predicate<T> match) {
+ Contract.Ensures(Contract.Result<int>() >= -1);
+ Contract.Ensures(Contract.Result<int>() < Count);
+ return FindIndex(0, _size, match);
+ }
+
+ public int FindIndex(int startIndex, Predicate<T> match) {
+ Contract.Ensures(Contract.Result<int>() >= -1);
+ Contract.Ensures(Contract.Result<int>() < startIndex + Count);
+ return FindIndex(startIndex, _size - startIndex, match);
+ }
+
+ public int FindIndex(int startIndex, int count, Predicate<T> match) {
+ if( (uint)startIndex > (uint)_size ) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);
+ }
+
+ if (count < 0 || startIndex > _size - count) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_Count);
+ }
+
+ if( match == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
+ }
+ Contract.Ensures(Contract.Result<int>() >= -1);
+ Contract.Ensures(Contract.Result<int>() < startIndex + count);
+ Contract.EndContractBlock();
+
+ int endIndex = startIndex + count;
+ for( int i = startIndex; i < endIndex; i++) {
+ if( match(_items[i])) return i;
+ }
+ return -1;
+ }
+
+ public T FindLast(Predicate<T> match) {
+ if( match == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
+ }
+ Contract.EndContractBlock();
+
+ for(int i = _size - 1 ; i >= 0; i--) {
+ if(match(_items[i])) {
+ return _items[i];
+ }
+ }
+ return default(T);
+ }
+
+ public int FindLastIndex(Predicate<T> match) {
+ Contract.Ensures(Contract.Result<int>() >= -1);
+ Contract.Ensures(Contract.Result<int>() < Count);
+ return FindLastIndex(_size - 1, _size, match);
+ }
+
+ public int FindLastIndex(int startIndex, Predicate<T> match) {
+ Contract.Ensures(Contract.Result<int>() >= -1);
+ Contract.Ensures(Contract.Result<int>() <= startIndex);
+ return FindLastIndex(startIndex, startIndex + 1, match);
+ }
+
+ public int FindLastIndex(int startIndex, int count, Predicate<T> match) {
+ if( match == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
+ }
+ Contract.Ensures(Contract.Result<int>() >= -1);
+ Contract.Ensures(Contract.Result<int>() <= startIndex);
+ Contract.EndContractBlock();
+
+ if(_size == 0) {
+ // Special case for 0 length List
+ if( startIndex != -1) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);
+ }
+ }
+ else {
+ // Make sure we're not out of range
+ if ( (uint)startIndex >= (uint)_size) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);
+ }
+ }
+
+ // 2nd have of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0.
+ if (count < 0 || startIndex - count + 1 < 0) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_Count);
+ }
+
+ int endIndex = startIndex - count;
+ for( int i = startIndex; i > endIndex; i--) {
+ if( match(_items[i])) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public void ForEach(Action<T> action) {
+ if( action == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.action);
+ }
+ Contract.EndContractBlock();
+
+ int version = _version;
+
+ for(int i = 0 ; i < _size; i++) {
+ if (version != _version && BinaryCompatibility.TargetsAtLeast_Desktop_V4_5) {
+ break;
+ }
+ action(_items[i]);
+ }
+
+ if (version != _version && BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
+ }
+
+ // Returns an enumerator for this list with the given
+ // permission for removal of elements. If modifications made to the list
+ // while an enumeration is in progress, the MoveNext and
+ // GetObject methods of the enumerator will throw an exception.
+ //
+ public Enumerator GetEnumerator() {
+ return new Enumerator(this);
+ }
+
+ /// <internalonly/>
+ IEnumerator<T> IEnumerable<T>.GetEnumerator() {
+ return new Enumerator(this);
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
+ return new Enumerator(this);
+ }
+
+ public List<T> GetRange(int index, int count) {
+ if (index < 0) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
+
+ if (count < 0) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
+
+ if (_size - index < count) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
+ }
+ Contract.Ensures(Contract.Result<List<T>>() != null);
+ Contract.EndContractBlock();
+
+ List<T> list = new List<T>(count);
+ Array.Copy(_items, index, list._items, 0, count);
+ list._size = count;
+ return list;
+ }
+
+
+ // Returns the index of the first occurrence of a given value in a range of
+ // this list. The list is searched forwards from beginning to end.
+ // The elements of the list are compared to the given value using the
+ // Object.Equals method.
+ //
+ // This method uses the Array.IndexOf method to perform the
+ // search.
+ //
+ public int IndexOf(T item) {
+ Contract.Ensures(Contract.Result<int>() >= -1);
+ Contract.Ensures(Contract.Result<int>() < Count);
+ return Array.IndexOf(_items, item, 0, _size);
+ }
+
+ int System.Collections.IList.IndexOf(Object item)
+ {
+ if(IsCompatibleObject(item)) {
+ return IndexOf((T)item);
+ }
+ return -1;
+ }
+
+ // Returns the index of the first occurrence of a given value in a range of
+ // this list. The list is searched forwards, starting at index
+ // index and ending at count number of elements. The
+ // elements of the list are compared to the given value using the
+ // Object.Equals method.
+ //
+ // This method uses the Array.IndexOf method to perform the
+ // search.
+ //
+ public int IndexOf(T item, int index) {
+ if (index > _size)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index);
+ Contract.Ensures(Contract.Result<int>() >= -1);
+ Contract.Ensures(Contract.Result<int>() < Count);
+ Contract.EndContractBlock();
+ return Array.IndexOf(_items, item, index, _size - index);
+ }
+
+ // Returns the index of the first occurrence of a given value in a range of
+ // this list. The list is searched forwards, starting at index
+ // index and upto count number of elements. The
+ // elements of the list are compared to the given value using the
+ // Object.Equals method.
+ //
+ // This method uses the Array.IndexOf method to perform the
+ // search.
+ //
+ public int IndexOf(T item, int index, int count) {
+ if (index > _size)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index);
+
+ if (count <0 || index > _size - count) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_Count);
+ Contract.Ensures(Contract.Result<int>() >= -1);
+ Contract.Ensures(Contract.Result<int>() < Count);
+ Contract.EndContractBlock();
+
+ return Array.IndexOf(_items, item, index, count);
+ }
+
+ // Inserts an element into this list at a given index. The size of the list
+ // is increased by one. If required, the capacity of the list is doubled
+ // before inserting the new element.
+ //
+ public void Insert(int index, T item) {
+ // Note that insertions at the end are legal.
+ if ((uint) index > (uint)_size) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_ListInsert);
+ }
+ Contract.EndContractBlock();
+ if (_size == _items.Length) EnsureCapacity(_size + 1);
+ if (index < _size) {
+ Array.Copy(_items, index, _items, index + 1, _size - index);
+ }
+ _items[index] = item;
+ _size++;
+ _version++;
+ }
+
+ void System.Collections.IList.Insert(int index, Object item)
+ {
+ ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(item, ExceptionArgument.item);
+
+ try {
+ Insert(index, (T) item);
+ }
+ catch (InvalidCastException) {
+ ThrowHelper.ThrowWrongValueTypeArgumentException(item, typeof(T));
+ }
+ }
+
+ // Inserts the elements of the given collection at a given index. If
+ // required, the capacity of the list is increased to twice the previous
+ // capacity or the new size, whichever is larger. Ranges may be added
+ // to the end of the list by setting index to the List's size.
+ //
+ public void InsertRange(int index, IEnumerable<T> collection) {
+ if (collection==null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
+ }
+
+ if ((uint)index > (uint)_size) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index);
+ }
+ Contract.EndContractBlock();
+
+ ICollection<T> c = collection as ICollection<T>;
+ if( c != null ) { // if collection is ICollection<T>
+ int count = c.Count;
+ if (count > 0) {
+ EnsureCapacity(_size + count);
+ if (index < _size) {
+ Array.Copy(_items, index, _items, index + count, _size - index);
+ }
+
+ // If we're inserting a List into itself, we want to be able to deal with that.
+ if (this == c) {
+ // Copy first part of _items to insert location
+ Array.Copy(_items, 0, _items, index, index);
+ // Copy last part of _items back to inserted location
+ Array.Copy(_items, index+count, _items, index*2, _size-index);
+ }
+ else {
+ T[] itemsToInsert = new T[count];
+ c.CopyTo(itemsToInsert, 0);
+ itemsToInsert.CopyTo(_items, index);
+ }
+ _size += count;
+ }
+ }
+ else {
+ using(IEnumerator<T> en = collection.GetEnumerator()) {
+ while(en.MoveNext()) {
+ Insert(index++, en.Current);
+ }
+ }
+ }
+ _version++;
+ }
+
+ // Returns the index of the last occurrence of a given value in a range of
+ // this list. The list is searched backwards, starting at the end
+ // and ending at the first element in the list. The elements of the list
+ // are compared to the given value using the Object.Equals method.
+ //
+ // This method uses the Array.LastIndexOf method to perform the
+ // search.
+ //
+ public int LastIndexOf(T item)
+ {
+ Contract.Ensures(Contract.Result<int>() >= -1);
+ Contract.Ensures(Contract.Result<int>() < Count);
+ if (_size == 0) { // Special case for empty list
+ return -1;
+ }
+ else {
+ return LastIndexOf(item, _size - 1, _size);
+ }
+ }
+
+ // Returns the index of the last occurrence of a given value in a range of
+ // this list. The list is searched backwards, starting at index
+ // index and ending at the first element in the list. The
+ // elements of the list are compared to the given value using the
+ // Object.Equals method.
+ //
+ // This method uses the Array.LastIndexOf method to perform the
+ // search.
+ //
+ public int LastIndexOf(T item, int index)
+ {
+ if (index >= _size)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index);
+ Contract.Ensures(Contract.Result<int>() >= -1);
+ Contract.Ensures(((Count == 0) && (Contract.Result<int>() == -1)) || ((Count > 0) && (Contract.Result<int>() <= index)));
+ Contract.EndContractBlock();
+ return LastIndexOf(item, index, index + 1);
+ }
+
+ // Returns the index of the last occurrence of a given value in a range of
+ // this list. The list is searched backwards, starting at index
+ // index and upto count elements. The elements of
+ // the list are compared to the given value using the Object.Equals
+ // method.
+ //
+ // This method uses the Array.LastIndexOf method to perform the
+ // search.
+ //
+ public int LastIndexOf(T item, int index, int count) {
+ if ((Count != 0) && (index < 0)) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
+
+ if ((Count !=0) && (count < 0)) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ Contract.Ensures(Contract.Result<int>() >= -1);
+ Contract.Ensures(((Count == 0) && (Contract.Result<int>() == -1)) || ((Count > 0) && (Contract.Result<int>() <= index)));
+ Contract.EndContractBlock();
+
+ if (_size == 0) { // Special case for empty list
+ return -1;
+ }
+
+ if (index >= _size) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_BiggerThanCollection);
+ }
+
+ if (count > index + 1) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_BiggerThanCollection);
+ }
+
+ return Array.LastIndexOf(_items, item, index, count);
+ }
+
+ // Removes the element at the given index. The size of the list is
+ // decreased by one.
+ //
+ public bool Remove(T item) {
+ int index = IndexOf(item);
+ if (index >= 0) {
+ RemoveAt(index);
+ return true;
+ }
+
+ return false;
+ }
+
+ void System.Collections.IList.Remove(Object item)
+ {
+ if(IsCompatibleObject(item)) {
+ Remove((T) item);
+ }
+ }
+
+ // This method removes all items which matches the predicate.
+ // The complexity is O(n).
+ public int RemoveAll(Predicate<T> match) {
+ if( match == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
+ }
+ Contract.Ensures(Contract.Result<int>() >= 0);
+ Contract.Ensures(Contract.Result<int>() <= Contract.OldValue(Count));
+ Contract.EndContractBlock();
+
+ int freeIndex = 0; // the first free slot in items array
+
+ // Find the first item which needs to be removed.
+ while( freeIndex < _size && !match(_items[freeIndex])) freeIndex++;
+ if( freeIndex >= _size) return 0;
+
+ int current = freeIndex + 1;
+ while( current < _size) {
+ // Find the first item which needs to be kept.
+ while( current < _size && match(_items[current])) current++;
+
+ if( current < _size) {
+ // copy item to the free slot.
+ _items[freeIndex++] = _items[current++];
+ }
+ }
+
+ Array.Clear(_items, freeIndex, _size - freeIndex);
+ int result = _size - freeIndex;
+ _size = freeIndex;
+ _version++;
+ return result;
+ }
+
+ // Removes the element at the given index. The size of the list is
+ // decreased by one.
+ //
+ public void RemoveAt(int index) {
+ if ((uint)index >= (uint)_size) {
+ ThrowHelper.ThrowArgumentOutOfRange_IndexException();
+ }
+ Contract.EndContractBlock();
+ _size--;
+ if (index < _size) {
+ Array.Copy(_items, index + 1, _items, index, _size - index);
+ }
+ _items[_size] = default(T);
+ _version++;
+ }
+
+ // Removes a range of elements from this list.
+ //
+ public void RemoveRange(int index, int count) {
+ if (index < 0) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
+
+ if (count < 0) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
+
+ if (_size - index < count)
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
+ Contract.EndContractBlock();
+
+ if (count > 0) {
+ int i = _size;
+ _size -= count;
+ if (index < _size) {
+ Array.Copy(_items, index + count, _items, index, _size - index);
+ }
+ Array.Clear(_items, _size, count);
+ _version++;
+ }
+ }
+
+ // Reverses the elements in this list.
+ public void Reverse() {
+ Reverse(0, Count);
+ }
+
+ // Reverses the elements in a range of this list. Following a call to this
+ // method, an element in the range given by index and count
+ // which was previously located at index i will now be located at
+ // index index + (index + count - i - 1).
+ //
+ public void Reverse(int index, int count) {
+ if (index < 0) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
+
+ if (count < 0) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
+
+ if (_size - index < count)
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
+ Contract.EndContractBlock();
+
+ // The non-generic Array.Reverse is not used because it does not perform
+ // well for non-primitive value types.
+ // If/when a generic Array.Reverse<T> becomes available, the below code
+ // can be deleted and replaced with a call to Array.Reverse<T>.
+ int i = index;
+ int j = index + count - 1;
+ T[] array = _items;
+ while (i < j)
+ {
+ T temp = array[i];
+ array[i] = array[j];
+ array[j] = temp;
+ i++;
+ j--;
+ }
+
+ _version++;
+ }
+
+ // Sorts the elements in this list. Uses the default comparer and
+ // Array.Sort.
+ public void Sort()
+ {
+ Sort(0, Count, null);
+ }
+
+ // Sorts the elements in this list. Uses Array.Sort with the
+ // provided comparer.
+ public void Sort(IComparer<T> comparer)
+ {
+ Sort(0, Count, comparer);
+ }
+
+ // Sorts the elements in a section of this list. The sort compares the
+ // elements to each other using the given IComparer interface. If
+ // comparer is null, the elements are compared to each other using
+ // the IComparable interface, which in that case must be implemented by all
+ // elements of the list.
+ //
+ // This method uses the Array.Sort method to sort the elements.
+ //
+ public void Sort(int index, int count, IComparer<T> comparer) {
+ if (index < 0) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
+
+ if (count < 0) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
+
+ if (_size - index < count)
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
+ Contract.EndContractBlock();
+
+ Array.Sort<T>(_items, index, count, comparer);
+ _version++;
+ }
+
+ public void Sort(Comparison<T> comparison) {
+ if( comparison == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparison);
+ }
+ Contract.EndContractBlock();
+
+ if( _size > 0) {
+ IComparer<T> comparer = Comparer<T>.Create(comparison);
+ Array.Sort(_items, 0, _size, comparer);
+ }
+ }
+
+ // ToArray returns an array containing the contents of the List.
+ // This requires copying the List, which is an O(n) operation.
+ public T[] ToArray() {
+ Contract.Ensures(Contract.Result<T[]>() != null);
+ Contract.Ensures(Contract.Result<T[]>().Length == Count);
+
+#if FEATURE_CORECLR
+ if (_size == 0)
+ {
+ return _emptyArray;
+ }
+#endif
+
+ T[] array = new T[_size];
+ Array.Copy(_items, 0, array, 0, _size);
+ return array;
+ }
+
+ // Sets the capacity of this list to the size of the list. This method can
+ // be used to minimize a list's memory overhead once it is known that no
+ // new elements will be added to the list. To completely clear a list and
+ // release all memory referenced by the list, execute the following
+ // statements:
+ //
+ // list.Clear();
+ // list.TrimExcess();
+ //
+ public void TrimExcess() {
+ int threshold = (int)(((double)_items.Length) * 0.9);
+ if( _size < threshold ) {
+ Capacity = _size;
+ }
+ }
+
+ public bool TrueForAll(Predicate<T> match) {
+ if( match == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
+ }
+ Contract.EndContractBlock();
+
+ for(int i = 0 ; i < _size; i++) {
+ if( !match(_items[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ [Serializable]
+ public struct Enumerator : IEnumerator<T>, System.Collections.IEnumerator
+ {
+ private List<T> list;
+ private int index;
+ private int version;
+ private T current;
+
+ internal Enumerator(List<T> list) {
+ this.list = list;
+ index = 0;
+ version = list._version;
+ current = default(T);
+ }
+
+ public void Dispose() {
+ }
+
+ public bool MoveNext() {
+
+ List<T> localList = list;
+
+ if (version == localList._version && ((uint)index < (uint)localList._size))
+ {
+ current = localList._items[index];
+ index++;
+ return true;
+ }
+ return MoveNextRare();
+ }
+
+ private bool MoveNextRare()
+ {
+ if (version != list._version) {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
+ }
+
+ index = list._size + 1;
+ current = default(T);
+ return false;
+ }
+
+ public T Current {
+ get {
+ return current;
+ }
+ }
+
+ Object System.Collections.IEnumerator.Current {
+ get {
+ if( index == 0 || index == list._size + 1) {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
+ }
+ return Current;
+ }
+ }
+
+ void System.Collections.IEnumerator.Reset() {
+ if (version != list._version) {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
+ }
+
+ index = 0;
+ current = default(T);
+ }
+
+ }
+ }
+}
+
diff --git a/src/mscorlib/src/System/Collections/Hashtable.cs b/src/mscorlib/src/System/Collections/Hashtable.cs
new file mode 100644
index 0000000000..262ccedea6
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Hashtable.cs
@@ -0,0 +1,1847 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Hash table implementation
+**
+**
+===========================================================*/
+
+namespace System.Collections {
+ using System;
+ using System.Runtime;
+ using System.Runtime.Serialization;
+ using System.Security.Permissions;
+ using System.Diagnostics;
+ using System.Threading;
+ using System.Runtime.CompilerServices;
+ using System.Runtime.ConstrainedExecution;
+ using System.Diagnostics.Contracts;
+#if !FEATURE_CORECLR
+ using System.Security.Cryptography;
+#endif
+
+ // The Hashtable class represents a dictionary of associated keys and values
+ // with constant lookup time.
+ //
+ // Objects used as keys in a hashtable must implement the GetHashCode
+ // and Equals methods (or they can rely on the default implementations
+ // inherited from Object if key equality is simply reference
+ // equality). Furthermore, the GetHashCode and Equals methods of
+ // a key object must produce the same results given the same parameters for the
+ // entire time the key is present in the hashtable. In practical terms, this
+ // means that key objects should be immutable, at least for the time they are
+ // used as keys in a hashtable.
+ //
+ // When entries are added to a hashtable, they are placed into
+ // buckets based on the hashcode of their keys. Subsequent lookups of
+ // keys will use the hashcode of the keys to only search a particular bucket,
+ // thus substantially reducing the number of key comparisons required to find
+ // an entry. A hashtable's maximum load factor, which can be specified
+ // when the hashtable is instantiated, determines the maximum ratio of
+ // hashtable entries to hashtable buckets. Smaller load factors cause faster
+ // average lookup times at the cost of increased memory consumption. The
+ // default maximum load factor of 1.0 generally provides the best balance
+ // between speed and size. As entries are added to a hashtable, the hashtable's
+ // actual load factor increases, and when the actual load factor reaches the
+ // maximum load factor value, the number of buckets in the hashtable is
+ // automatically increased by approximately a factor of two (to be precise, the
+ // number of hashtable buckets is increased to the smallest prime number that
+ // is larger than twice the current number of hashtable buckets).
+ //
+ // Each object provides their own hash function, accessed by calling
+ // GetHashCode(). However, one can write their own object
+ // implementing IEqualityComparer and pass it to a constructor on
+ // the Hashtable. That hash function (and the equals method on the
+ // IEqualityComparer) would be used for all objects in the table.
+ //
+ // Changes since V1 during Whidbey:
+ // *) Deprecated IHashCodeProvider, use IEqualityComparer instead. This will
+ // allow better performance for objects where equality checking can be
+ // done much faster than establishing an ordering between two objects,
+ // such as an ordinal string equality check.
+ //
+ [DebuggerTypeProxy(typeof(System.Collections.Hashtable.HashtableDebugView))]
+ [DebuggerDisplay("Count = {Count}")]
+ [System.Runtime.InteropServices.ComVisible(true)]
+ [Serializable]
+ public class Hashtable : IDictionary, ISerializable, IDeserializationCallback, ICloneable {
+ /*
+ Implementation Notes:
+ The generic Dictionary was copied from Hashtable's source - any bug
+ fixes here probably need to be made to the generic Dictionary as well.
+
+ This Hashtable uses double hashing. There are hashsize buckets in the
+ table, and each bucket can contain 0 or 1 element. We a bit to mark
+ whether there's been a collision when we inserted multiple elements
+ (ie, an inserted item was hashed at least a second time and we probed
+ this bucket, but it was already in use). Using the collision bit, we
+ can terminate lookups & removes for elements that aren't in the hash
+ table more quickly. We steal the most significant bit from the hash code
+ to store the collision bit.
+
+ Our hash function is of the following form:
+
+ h(key, n) = h1(key) + n*h2(key)
+
+ where n is the number of times we've hit a collided bucket and rehashed
+ (on this particular lookup). Here are our hash functions:
+
+ h1(key) = GetHash(key); // default implementation calls key.GetHashCode();
+ h2(key) = 1 + (((h1(key) >> 5) + 1) % (hashsize - 1));
+
+ The h1 can return any number. h2 must return a number between 1 and
+ hashsize - 1 that is relatively prime to hashsize (not a problem if
+ hashsize is prime). (Knuth's Art of Computer Programming, Vol. 3, p. 528-9)
+ If this is true, then we are guaranteed to visit every bucket in exactly
+ hashsize probes, since the least common multiple of hashsize and h2(key)
+ will be hashsize * h2(key). (This is the first number where adding h2 to
+ h1 mod hashsize will be 0 and we will search the same bucket twice).
+
+ We previously used a different h2(key, n) that was not constant. That is a
+ horrifically bad idea, unless you can prove that series will never produce
+ any identical numbers that overlap when you mod them by hashsize, for all
+ subranges from i to i+hashsize, for all i. It's not worth investigating,
+ since there was no clear benefit from using that hash function, and it was
+ broken.
+
+ For efficiency reasons, we've implemented this by storing h1 and h2 in a
+ temporary, and setting a variable called seed equal to h1. We do a probe,
+ and if we collided, we simply add h2 to seed each time through the loop.
+
+ A good test for h2() is to subclass Hashtable, provide your own implementation
+ of GetHash() that returns a constant, then add many items to the hash table.
+ Make sure Count equals the number of items you inserted.
+
+ Note that when we remove an item from the hash table, we set the key
+ equal to buckets, if there was a collision in this bucket. Otherwise
+ we'd either wipe out the collision bit, or we'd still have an item in
+ the hash table.
+
+ --
+ */
+
+ internal const Int32 HashPrime = 101;
+ private const Int32 InitialSize = 3;
+ private const String LoadFactorName = "LoadFactor";
+ private const String VersionName = "Version";
+ private const String ComparerName = "Comparer";
+ private const String HashCodeProviderName = "HashCodeProvider";
+ private const String HashSizeName = "HashSize"; // Must save buckets.Length
+ private const String KeysName = "Keys";
+ private const String ValuesName = "Values";
+ private const String KeyComparerName = "KeyComparer";
+
+ // Deleted entries have their key set to buckets
+
+ // The hash table data.
+ // This cannot be serialised
+ private struct bucket {
+ public Object key;
+ public Object val;
+ public int hash_coll; // Store hash code; sign bit means there was a collision.
+ }
+
+ private bucket[] buckets;
+
+ // The total number of entries in the hash table.
+ private int count;
+
+ // The total number of collision bits set in the hashtable
+ private int occupancy;
+
+ private int loadsize;
+ private float loadFactor;
+
+ private volatile int version;
+ private volatile bool isWriterInProgress;
+
+ private ICollection keys;
+ private ICollection values;
+
+ private IEqualityComparer _keycomparer;
+ private Object _syncRoot;
+
+ [Obsolete("Please use EqualityComparer property.")]
+ protected IHashCodeProvider hcp
+ {
+ get
+ {
+ if( _keycomparer is CompatibleComparer) {
+ return ((CompatibleComparer)_keycomparer).HashCodeProvider;
+ }
+ else if( _keycomparer == null) {
+ return null;
+ }
+ else {
+ throw new ArgumentException(Environment.GetResourceString("Arg_CannotMixComparisonInfrastructure"));
+ }
+ }
+ set
+ {
+ if (_keycomparer is CompatibleComparer) {
+ CompatibleComparer keyComparer = (CompatibleComparer)_keycomparer;
+ _keycomparer = new CompatibleComparer(keyComparer.Comparer, value);
+ }
+ else if( _keycomparer == null) {
+ _keycomparer = new CompatibleComparer((IComparer)null, value);
+ }
+ else {
+ throw new ArgumentException(Environment.GetResourceString("Arg_CannotMixComparisonInfrastructure"));
+ }
+ }
+ }
+
+
+ [Obsolete("Please use KeyComparer properties.")]
+ protected IComparer comparer
+ {
+ get
+ {
+ if( _keycomparer is CompatibleComparer) {
+ return ((CompatibleComparer)_keycomparer).Comparer;
+ }
+ else if( _keycomparer == null) {
+ return null;
+ }
+ else {
+ throw new ArgumentException(Environment.GetResourceString("Arg_CannotMixComparisonInfrastructure"));
+ }
+ }
+ set
+ {
+ if (_keycomparer is CompatibleComparer) {
+ CompatibleComparer keyComparer = (CompatibleComparer)_keycomparer;
+ _keycomparer = new CompatibleComparer(value, keyComparer.HashCodeProvider);
+ }
+ else if( _keycomparer == null) {
+ _keycomparer = new CompatibleComparer(value, (IHashCodeProvider)null);
+ }
+ else {
+ throw new ArgumentException(Environment.GetResourceString("Arg_CannotMixComparisonInfrastructure"));
+ }
+ }
+ }
+
+ protected IEqualityComparer EqualityComparer
+ {
+ get
+ {
+ return _keycomparer;
+ }
+ }
+
+ // Note: this constructor is a bogus constructor that does nothing
+ // and is for use only with SyncHashtable.
+ internal Hashtable( bool trash )
+ {
+ }
+
+ // Constructs a new hashtable. The hashtable is created with an initial
+ // capacity of zero and a load factor of 1.0.
+ public Hashtable() : this(0, 1.0f) {
+ }
+
+ // Constructs a new hashtable with the given initial capacity and a load
+ // factor of 1.0. The capacity argument serves as an indication of
+ // the number of entries the hashtable will contain. When this number (or
+ // an approximation) is known, specifying it in the constructor can
+ // eliminate a number of resizing operations that would otherwise be
+ // performed when elements are added to the hashtable.
+ //
+ public Hashtable(int capacity) : this(capacity, 1.0f) {
+ }
+
+ // Constructs a new hashtable with the given initial capacity and load
+ // factor. The capacity argument serves as an indication of the
+ // number of entries the hashtable will contain. When this number (or an
+ // approximation) is known, specifying it in the constructor can eliminate
+ // a number of resizing operations that would otherwise be performed when
+ // elements are added to the hashtable. The loadFactor argument
+ // indicates the maximum ratio of hashtable entries to hashtable buckets.
+ // Smaller load factors cause faster average lookup times at the cost of
+ // increased memory consumption. A load factor of 1.0 generally provides
+ // the best balance between speed and size.
+ //
+ public Hashtable(int capacity, float loadFactor) {
+ if (capacity < 0)
+ throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (!(loadFactor >= 0.1f && loadFactor <= 1.0f))
+ throw new ArgumentOutOfRangeException("loadFactor", Environment.GetResourceString("ArgumentOutOfRange_HashtableLoadFactor", .1, 1.0));
+ Contract.EndContractBlock();
+
+ // Based on perf work, .72 is the optimal load factor for this table.
+ this.loadFactor = 0.72f * loadFactor;
+
+ double rawsize = capacity / this.loadFactor;
+ if (rawsize > Int32.MaxValue)
+ throw new ArgumentException(Environment.GetResourceString("Arg_HTCapacityOverflow"));
+
+ // Avoid awfully small sizes
+ int hashsize = (rawsize > InitialSize) ? HashHelpers.GetPrime((int)rawsize) : InitialSize;
+ buckets = new bucket[hashsize];
+
+ loadsize = (int)(this.loadFactor * hashsize);
+ isWriterInProgress = false;
+ // Based on the current algorithm, loadsize must be less than hashsize.
+ Contract.Assert( loadsize < hashsize, "Invalid hashtable loadsize!");
+ }
+
+ // Constructs a new hashtable with the given initial capacity and load
+ // factor. The capacity argument serves as an indication of the
+ // number of entries the hashtable will contain. When this number (or an
+ // approximation) is known, specifying it in the constructor can eliminate
+ // a number of resizing operations that would otherwise be performed when
+ // elements are added to the hashtable. The loadFactor argument
+ // indicates the maximum ratio of hashtable entries to hashtable buckets.
+ // Smaller load factors cause faster average lookup times at the cost of
+ // increased memory consumption. A load factor of 1.0 generally provides
+ // the best balance between speed and size. The hcp argument
+ // is used to specify an Object that will provide hash codes for all
+ // the Objects in the table. Using this, you can in effect override
+ // GetHashCode() on each Object using your own hash function. The
+ // comparer argument will let you specify a custom function for
+ // comparing keys. By specifying user-defined objects for hcp
+ // and comparer, users could make a hash table using strings
+ // as keys do case-insensitive lookups.
+ //
+ [Obsolete("Please use Hashtable(int, float, IEqualityComparer) instead.")]
+ public Hashtable(int capacity, float loadFactor, IHashCodeProvider hcp, IComparer comparer) : this(capacity, loadFactor) {
+ if (hcp == null && comparer == null) {
+ this._keycomparer = null;
+ }
+ else {
+ this._keycomparer = new CompatibleComparer(comparer,hcp);
+ }
+ }
+
+ public Hashtable(int capacity, float loadFactor, IEqualityComparer equalityComparer) : this(capacity, loadFactor) {
+ this._keycomparer = equalityComparer;
+ }
+
+ // Constructs a new hashtable using a custom hash function
+ // and a custom comparison function for keys. This will enable scenarios
+ // such as doing lookups with case-insensitive strings.
+ //
+ [Obsolete("Please use Hashtable(IEqualityComparer) instead.")]
+ public Hashtable(IHashCodeProvider hcp, IComparer comparer) : this(0, 1.0f, hcp, comparer) {
+ }
+
+ public Hashtable(IEqualityComparer equalityComparer) : this(0, 1.0f, equalityComparer) {
+ }
+
+ // Constructs a new hashtable using a custom hash function
+ // and a custom comparison function for keys. This will enable scenarios
+ // such as doing lookups with case-insensitive strings.
+ //
+ [Obsolete("Please use Hashtable(int, IEqualityComparer) instead.")]
+ public Hashtable(int capacity, IHashCodeProvider hcp, IComparer comparer)
+ : this(capacity, 1.0f, hcp, comparer) {
+ }
+
+ public Hashtable(int capacity, IEqualityComparer equalityComparer)
+ : this(capacity, 1.0f, equalityComparer) {
+ }
+
+ // Constructs a new hashtable containing a copy of the entries in the given
+ // dictionary. The hashtable is created with a load factor of 1.0.
+ //
+ public Hashtable(IDictionary d) : this(d, 1.0f) {
+ }
+
+ // Constructs a new hashtable containing a copy of the entries in the given
+ // dictionary. The hashtable is created with the given load factor.
+ //
+ public Hashtable(IDictionary d, float loadFactor)
+ : this(d, loadFactor, (IEqualityComparer)null) {
+ }
+
+ [Obsolete("Please use Hashtable(IDictionary, IEqualityComparer) instead.")]
+ public Hashtable(IDictionary d, IHashCodeProvider hcp, IComparer comparer)
+ : this(d, 1.0f, hcp, comparer) {
+ }
+
+ public Hashtable(IDictionary d, IEqualityComparer equalityComparer)
+ : this(d, 1.0f, equalityComparer) {
+ }
+
+ [Obsolete("Please use Hashtable(IDictionary, float, IEqualityComparer) instead.")]
+ public Hashtable(IDictionary d, float loadFactor, IHashCodeProvider hcp, IComparer comparer)
+ : this((d != null ? d.Count : 0), loadFactor, hcp, comparer) {
+ if (d==null)
+ throw new ArgumentNullException("d", Environment.GetResourceString("ArgumentNull_Dictionary"));
+ Contract.EndContractBlock();
+
+ IDictionaryEnumerator e = d.GetEnumerator();
+ while (e.MoveNext()) Add(e.Key, e.Value);
+ }
+
+ public Hashtable(IDictionary d, float loadFactor, IEqualityComparer equalityComparer)
+ : this((d != null ? d.Count : 0), loadFactor, equalityComparer) {
+ if (d==null)
+ throw new ArgumentNullException("d", Environment.GetResourceString("ArgumentNull_Dictionary"));
+ Contract.EndContractBlock();
+
+ IDictionaryEnumerator e = d.GetEnumerator();
+ while (e.MoveNext()) Add(e.Key, e.Value);
+ }
+
+ protected Hashtable(SerializationInfo info, StreamingContext context) {
+ //We can't do anything with the keys and values until the entire graph has been deserialized
+ //and we have a reasonable estimate that GetHashCode is not going to fail. For the time being,
+ //we'll just cache this. The graph is not valid until OnDeserialization has been called.
+ HashHelpers.SerializationInfoTable.Add(this, info);
+ }
+
+ // ‘InitHash’ is basically an implementation of classic DoubleHashing (see http://en.wikipedia.org/wiki/Double_hashing)
+ //
+ // 1) The only ‘correctness’ requirement is that the ‘increment’ used to probe
+ // a. Be non-zero
+ // b. Be relatively prime to the table size ‘hashSize’. (This is needed to insure you probe all entries in the table before you ‘wrap’ and visit entries already probed)
+ // 2) Because we choose table sizes to be primes, we just need to insure that the increment is 0 < incr < hashSize
+ //
+ // Thus this function would work: Incr = 1 + (seed % (hashSize-1))
+ //
+ // While this works well for ‘uniformly distributed’ keys, in practice, non-uniformity is common.
+ // In particular in practice we can see ‘mostly sequential’ where you get long clusters of keys that ‘pack’.
+ // To avoid bad behavior you want it to be the case that the increment is ‘large’ even for ‘small’ values (because small
+ // values tend to happen more in practice). Thus we multiply ‘seed’ by a number that will make these small values
+ // bigger (and not hurt large values). We picked HashPrime (101) because it was prime, and if ‘hashSize-1’ is not a multiple of HashPrime
+ // (enforced in GetPrime), then incr has the potential of being every value from 1 to hashSize-1. The choice was largely arbitrary.
+ //
+ // Computes the hash function: H(key, i) = h1(key) + i*h2(key, hashSize).
+ // The out parameter seed is h1(key), while the out parameter
+ // incr is h2(key, hashSize). Callers of this function should
+ // add incr each time through a loop.
+ private uint InitHash(Object key, int hashsize, out uint seed, out uint incr) {
+ // Hashcode must be positive. Also, we must not use the sign bit, since
+ // that is used for the collision bit.
+ uint hashcode = (uint) GetHash(key) & 0x7FFFFFFF;
+ seed = (uint) hashcode;
+ // Restriction: incr MUST be between 1 and hashsize - 1, inclusive for
+ // the modular arithmetic to work correctly. This guarantees you'll
+ // visit every bucket in the table exactly once within hashsize
+ // iterations. Violate this and it'll cause obscure bugs forever.
+ // If you change this calculation for h2(key), update putEntry too!
+ incr = (uint)(1 + ((seed * HashPrime) % ((uint)hashsize - 1)));
+ return hashcode;
+ }
+
+ // Adds an entry with the given key and value to this hashtable. An
+ // ArgumentException is thrown if the key is null or if the key is already
+ // present in the hashtable.
+ //
+ public virtual void Add(Object key, Object value) {
+ Insert(key, value, true);
+ }
+
+ // Removes all entries from this hashtable.
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ public virtual void Clear() {
+ Contract.Assert(!isWriterInProgress, "Race condition detected in usages of Hashtable - multiple threads appear to be writing to a Hashtable instance simultaneously! Don't do that - use Hashtable.Synchronized.");
+
+ if (count == 0 && occupancy == 0)
+ return;
+
+#if !FEATURE_CORECLR
+ Thread.BeginCriticalRegion();
+#endif
+ isWriterInProgress = true;
+ for (int i = 0; i < buckets.Length; i++){
+ buckets[i].hash_coll = 0;
+ buckets[i].key = null;
+ buckets[i].val = null;
+ }
+
+ count = 0;
+ occupancy = 0;
+ UpdateVersion();
+ isWriterInProgress = false;
+#if !FEATURE_CORECLR
+ Thread.EndCriticalRegion();
+#endif
+ }
+
+ // Clone returns a virtually identical copy of this hash table. This does
+ // a shallow copy - the Objects in the table aren't cloned, only the references
+ // to those Objects.
+ public virtual Object Clone()
+ {
+ bucket[] lbuckets = buckets;
+ Hashtable ht = new Hashtable(count,_keycomparer);
+ ht.version = version;
+ ht.loadFactor = loadFactor;
+ ht.count = 0;
+
+ int bucket = lbuckets.Length;
+ while (bucket > 0) {
+ bucket--;
+ Object keyv = lbuckets[bucket].key;
+ if ((keyv!= null) && (keyv != lbuckets)) {
+ ht[keyv] = lbuckets[bucket].val;
+ }
+ }
+
+ return ht;
+ }
+
+ // Checks if this hashtable contains the given key.
+ public virtual bool Contains(Object key) {
+ return ContainsKey(key);
+ }
+
+ // Checks if this hashtable contains an entry with the given key. This is
+ // an O(1) operation.
+ //
+ public virtual bool ContainsKey(Object key) {
+ if (key == null) {
+ throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
+ }
+ Contract.EndContractBlock();
+
+ uint seed;
+ uint incr;
+ // Take a snapshot of buckets, in case another thread resizes table
+ bucket[] lbuckets = buckets;
+ uint hashcode = InitHash(key, lbuckets.Length, out seed, out incr);
+ int ntry = 0;
+
+ bucket b;
+ int bucketNumber = (int) (seed % (uint)lbuckets.Length);
+ do {
+ b = lbuckets[bucketNumber];
+ if (b.key == null) {
+ return false;
+ }
+ if (((b.hash_coll & 0x7FFFFFFF) == hashcode) &&
+ KeyEquals (b.key, key))
+ return true;
+ bucketNumber = (int) (((long)bucketNumber + incr)% (uint)lbuckets.Length);
+ } while (b.hash_coll < 0 && ++ntry < lbuckets.Length);
+ return false;
+ }
+
+
+
+ // Checks if this hashtable contains an entry with the given value. The
+ // values of the entries of the hashtable are compared to the given value
+ // using the Object.Equals method. This method performs a linear
+ // search and is thus be substantially slower than the ContainsKey
+ // method.
+ //
+ public virtual bool ContainsValue(Object value) {
+
+ if (value == null) {
+ for (int i = buckets.Length; --i >= 0;) {
+ if (buckets[i].key != null && buckets[i].key != buckets && buckets[i].val == null)
+ return true;
+ }
+ }
+ else {
+ for (int i = buckets.Length; --i >= 0;) {
+ Object val = buckets[i].val;
+ if (val!=null && val.Equals(value)) return true;
+ }
+ }
+ return false;
+ }
+
+ // Copies the keys of this hashtable to a given array starting at a given
+ // index. This method is used by the implementation of the CopyTo method in
+ // the KeyCollection class.
+ private void CopyKeys(Array array, int arrayIndex) {
+ Contract.Requires(array != null);
+ Contract.Requires(array.Rank == 1);
+
+ bucket[] lbuckets = buckets;
+ for (int i = lbuckets.Length; --i >= 0;) {
+ Object keyv = lbuckets[i].key;
+ if ((keyv != null) && (keyv != buckets)){
+ array.SetValue(keyv, arrayIndex++);
+ }
+ }
+ }
+
+ // Copies the keys of this hashtable to a given array starting at a given
+ // index. This method is used by the implementation of the CopyTo method in
+ // the KeyCollection class.
+ private void CopyEntries(Array array, int arrayIndex) {
+ Contract.Requires(array != null);
+ Contract.Requires(array.Rank == 1);
+
+ bucket[] lbuckets = buckets;
+ for (int i = lbuckets.Length; --i >= 0;) {
+ Object keyv = lbuckets[i].key;
+ if ((keyv != null) && (keyv != buckets)){
+ DictionaryEntry entry = new DictionaryEntry(keyv,lbuckets[i].val);
+ array.SetValue(entry, arrayIndex++);
+ }
+ }
+ }
+
+ // Copies the values in this hash table to an array at
+ // a given index. Note that this only copies values, and not keys.
+ public virtual void CopyTo(Array array, int arrayIndex)
+ {
+ if (array == null)
+ throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Array"));
+ if (array.Rank != 1)
+ throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
+ if (arrayIndex < 0)
+ throw new ArgumentOutOfRangeException("arrayIndex", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (array.Length - arrayIndex < Count)
+ throw new ArgumentException(Environment.GetResourceString("Arg_ArrayPlusOffTooSmall"));
+ Contract.EndContractBlock();
+ CopyEntries(array, arrayIndex);
+ }
+
+ // Copies the values in this Hashtable to an KeyValuePairs array.
+ // KeyValuePairs is different from Dictionary Entry in that it has special
+ // debugger attributes on its fields.
+
+ internal virtual KeyValuePairs[] ToKeyValuePairsArray() {
+
+ KeyValuePairs[] array = new KeyValuePairs[count];
+ int index = 0;
+ bucket[] lbuckets = buckets;
+ for (int i = lbuckets.Length; --i >= 0;) {
+ Object keyv = lbuckets[i].key;
+ if ((keyv != null) && (keyv != buckets)){
+ array[index++] = new KeyValuePairs(keyv,lbuckets[i].val);
+ }
+ }
+
+ return array;
+ }
+
+
+ // Copies the values of this hashtable to a given array starting at a given
+ // index. This method is used by the implementation of the CopyTo method in
+ // the ValueCollection class.
+ private void CopyValues(Array array, int arrayIndex) {
+ Contract.Requires(array != null);
+ Contract.Requires(array.Rank == 1);
+
+ bucket[] lbuckets = buckets;
+ for (int i = lbuckets.Length; --i >= 0;) {
+ Object keyv = lbuckets[i].key;
+ if ((keyv != null) && (keyv != buckets)){
+ array.SetValue(lbuckets[i].val, arrayIndex++);
+ }
+ }
+ }
+
+ // Returns the value associated with the given key. If an entry with the
+ // given key is not found, the returned value is null.
+ //
+ public virtual Object this[Object key] {
+ get {
+ if (key == null) {
+ throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
+ }
+ Contract.EndContractBlock();
+
+ uint seed;
+ uint incr;
+
+
+ // Take a snapshot of buckets, in case another thread does a resize
+ bucket[] lbuckets = buckets;
+ uint hashcode = InitHash(key, lbuckets.Length, out seed, out incr);
+ int ntry = 0;
+
+ bucket b;
+ int bucketNumber = (int) (seed % (uint)lbuckets.Length);
+ do
+ {
+ int currentversion;
+
+ // A read operation on hashtable has three steps:
+ // (1) calculate the hash and find the slot number.
+ // (2) compare the hashcode, if equal, go to step 3. Otherwise end.
+ // (3) compare the key, if equal, go to step 4. Otherwise end.
+ // (4) return the value contained in the bucket.
+ // After step 3 and before step 4. A writer can kick in a remove the old item and add a new one
+ // in the same bukcet. So in the reader we need to check if the hash table is modified during above steps.
+ //
+ // Writers (Insert, Remove, Clear) will set 'isWriterInProgress' flag before it starts modifying
+ // the hashtable and will ckear the flag when it is done. When the flag is cleared, the 'version'
+ // will be increased. We will repeat the reading if a writer is in progress or done with the modification
+ // during the read.
+ //
+ // Our memory model guarantee if we pick up the change in bucket from another processor,
+ // we will see the 'isWriterProgress' flag to be true or 'version' is changed in the reader.
+ //
+ int spinCount = 0;
+ do {
+ // this is violate read, following memory accesses can not be moved ahead of it.
+ currentversion = version;
+ b = lbuckets[bucketNumber];
+
+ // The contention between reader and writer shouldn't happen frequently.
+ // But just in case this will burn CPU, yield the control of CPU if we spinned a few times.
+ // 8 is just a random number I pick.
+ if( (++spinCount) % 8 == 0 ) {
+ Thread.Sleep(1); // 1 means we are yeilding control to all threads, including low-priority ones.
+ }
+ } while ( isWriterInProgress || (currentversion != version) );
+
+ if (b.key == null) {
+ return null;
+ }
+ if (((b.hash_coll & 0x7FFFFFFF) == hashcode) &&
+ KeyEquals (b.key, key))
+ return b.val;
+ bucketNumber = (int) (((long)bucketNumber + incr)% (uint)lbuckets.Length);
+ } while (b.hash_coll < 0 && ++ntry < lbuckets.Length);
+ return null;
+ }
+
+ set {
+ Insert(key, value, false);
+ }
+ }
+
+ // Increases the bucket count of this hashtable. This method is called from
+ // the Insert method when the actual load factor of the hashtable reaches
+ // the upper limit specified when the hashtable was constructed. The number
+ // of buckets in the hashtable is increased to the smallest prime number
+ // that is larger than twice the current number of buckets, and the entries
+ // in the hashtable are redistributed into the new buckets using the cached
+ // hashcodes.
+ private void expand() {
+ int rawsize = HashHelpers.ExpandPrime(buckets.Length);
+ rehash(rawsize, false);
+ }
+
+ // We occationally need to rehash the table to clean up the collision bits.
+ private void rehash() {
+ rehash( buckets.Length, false );
+ }
+
+ private void UpdateVersion() {
+ // Version might become negative when version is Int32.MaxValue, but the oddity will be still be correct.
+ // So we don't need to special case this.
+ version++;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ private void rehash( int newsize, bool forceNewHashCode ) {
+
+ // reset occupancy
+ occupancy=0;
+
+ // Don't replace any internal state until we've finished adding to the
+ // new bucket[]. This serves two purposes:
+ // 1) Allow concurrent readers to see valid hashtable contents
+ // at all times
+ // 2) Protect against an OutOfMemoryException while allocating this
+ // new bucket[].
+ bucket[] newBuckets = new bucket[newsize];
+
+ // rehash table into new buckets
+ int nb;
+ for (nb = 0; nb < buckets.Length; nb++){
+ bucket oldb = buckets[nb];
+ if ((oldb.key != null) && (oldb.key != buckets)) {
+ int hashcode = ((forceNewHashCode ? GetHash(oldb.key) : oldb.hash_coll) & 0x7FFFFFFF);
+ putEntry(newBuckets, oldb.key, oldb.val, hashcode);
+ }
+ }
+
+ // New bucket[] is good to go - replace buckets and other internal state.
+#if !FEATURE_CORECLR
+ Thread.BeginCriticalRegion();
+#endif
+ isWriterInProgress = true;
+ buckets = newBuckets;
+ loadsize = (int)(loadFactor * newsize);
+ UpdateVersion();
+ isWriterInProgress = false;
+#if !FEATURE_CORECLR
+ Thread.EndCriticalRegion();
+#endif
+ // minimun size of hashtable is 3 now and maximum loadFactor is 0.72 now.
+ Contract.Assert(loadsize < newsize, "Our current implementaion means this is not possible.");
+ return;
+ }
+
+ // Returns an enumerator for this hashtable.
+ // If modifications made to the hashtable while an enumeration is
+ // in progress, the MoveNext and Current methods of the
+ // enumerator will throw an exception.
+ //
+ IEnumerator IEnumerable.GetEnumerator() {
+ return new HashtableEnumerator(this, HashtableEnumerator.DictEntry);
+ }
+
+ // Returns a dictionary enumerator for this hashtable.
+ // If modifications made to the hashtable while an enumeration is
+ // in progress, the MoveNext and Current methods of the
+ // enumerator will throw an exception.
+ //
+ public virtual IDictionaryEnumerator GetEnumerator() {
+ return new HashtableEnumerator(this, HashtableEnumerator.DictEntry);
+ }
+
+ // Internal method to get the hash code for an Object. This will call
+ // GetHashCode() on each object if you haven't provided an IHashCodeProvider
+ // instance. Otherwise, it calls hcp.GetHashCode(obj).
+ protected virtual int GetHash(Object key)
+ {
+ if (_keycomparer != null)
+ return _keycomparer.GetHashCode(key);
+ return key.GetHashCode();
+ }
+
+ // Is this Hashtable read-only?
+ public virtual bool IsReadOnly {
+ get { return false; }
+ }
+
+ public virtual bool IsFixedSize {
+ get { return false; }
+ }
+
+ // Is this Hashtable synchronized? See SyncRoot property
+ public virtual bool IsSynchronized {
+ get { return false; }
+ }
+
+ // Internal method to compare two keys. If you have provided an IComparer
+ // instance in the constructor, this method will call comparer.Compare(item, key).
+ // Otherwise, it will call item.Equals(key).
+ //
+ protected virtual bool KeyEquals(Object item, Object key)
+ {
+ Contract.Assert(key != null, "key can't be null here!");
+ if( Object.ReferenceEquals(buckets, item)) {
+ return false;
+ }
+
+ if (Object.ReferenceEquals(item,key))
+ return true;
+
+ if (_keycomparer != null)
+ return _keycomparer.Equals(item, key);
+ return item == null ? false : item.Equals(key);
+ }
+
+ // Returns a collection representing the keys of this hashtable. The order
+ // in which the returned collection represents the keys is unspecified, but
+ // it is guaranteed to be buckets = newBuckets; the same order in which a collection returned by
+ // GetValues represents the values of the hashtable.
+ //
+ // The returned collection is live in the sense that any changes
+ // to the hash table are reflected in this collection. It is not
+ // a static copy of all the keys in the hash table.
+ //
+ public virtual ICollection Keys {
+ get {
+ if (keys == null) keys = new KeyCollection(this);
+ return keys;
+ }
+ }
+
+ // Returns a collection representing the values of this hashtable. The
+ // order in which the returned collection represents the values is
+ // unspecified, but it is guaranteed to be the same order in which a
+ // collection returned by GetKeys represents the keys of the
+ // hashtable.
+ //
+ // The returned collection is live in the sense that any changes
+ // to the hash table are reflected in this collection. It is not
+ // a static copy of all the keys in the hash table.
+ //
+ public virtual ICollection Values {
+ get {
+ if (values == null) values = new ValueCollection(this);
+ return values;
+ }
+ }
+
+ // Inserts an entry into this hashtable. This method is called from the Set
+ // and Add methods. If the add parameter is true and the given key already
+ // exists in the hashtable, an exception is thrown.
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ private void Insert (Object key, Object nvalue, bool add) {
+ if (key == null) {
+ throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
+ }
+ Contract.EndContractBlock();
+ if (count >= loadsize) {
+ expand();
+ }
+ else if(occupancy > loadsize && count > 100) {
+ rehash();
+ }
+
+ uint seed;
+ uint incr;
+ // Assume we only have one thread writing concurrently. Modify
+ // buckets to contain new data, as long as we insert in the right order.
+ uint hashcode = InitHash(key, buckets.Length, out seed, out incr);
+ int ntry = 0;
+ int emptySlotNumber = -1; // We use the empty slot number to cache the first empty slot. We chose to reuse slots
+ // create by remove that have the collision bit set over using up new slots.
+ int bucketNumber = (int) (seed % (uint)buckets.Length);
+ do {
+
+ // Set emptySlot number to current bucket if it is the first available bucket that we have seen
+ // that once contained an entry and also has had a collision.
+ // We need to search this entire collision chain because we have to ensure that there are no
+ // duplicate entries in the table.
+ if (emptySlotNumber == -1 && (buckets[bucketNumber].key == buckets) && (buckets[bucketNumber].hash_coll < 0))//(((buckets[bucketNumber].hash_coll & unchecked(0x80000000))!=0)))
+ emptySlotNumber = bucketNumber;
+
+ // Insert the key/value pair into this bucket if this bucket is empty and has never contained an entry
+ // OR
+ // This bucket once contained an entry but there has never been a collision
+ if ((buckets[bucketNumber].key == null) ||
+ (buckets[bucketNumber].key == buckets && ((buckets[bucketNumber].hash_coll & unchecked(0x80000000))==0))) {
+
+ // If we have found an available bucket that has never had a collision, but we've seen an available
+ // bucket in the past that has the collision bit set, use the previous bucket instead
+ if (emptySlotNumber != -1) // Reuse slot
+ bucketNumber = emptySlotNumber;
+
+ // We pretty much have to insert in this order. Don't set hash
+ // code until the value & key are set appropriately.
+#if !FEATURE_CORECLR
+ Thread.BeginCriticalRegion();
+#endif
+ isWriterInProgress = true;
+ buckets[bucketNumber].val = nvalue;
+ buckets[bucketNumber].key = key;
+ buckets[bucketNumber].hash_coll |= (int) hashcode;
+ count++;
+ UpdateVersion();
+ isWriterInProgress = false;
+#if !FEATURE_CORECLR
+ Thread.EndCriticalRegion();
+#endif
+
+#if FEATURE_RANDOMIZED_STRING_HASHING
+#if !FEATURE_CORECLR
+ // coreclr has the randomized string hashing on by default so we don't need to resize at this point
+
+ if(ntry > HashHelpers.HashCollisionThreshold && HashHelpers.IsWellKnownEqualityComparer(_keycomparer))
+ {
+ // PERF: We don't want to rehash if _keycomparer is already a RandomizedObjectEqualityComparer since in some
+ // cases there may not be any strings in the hashtable and we wouldn't get any mixing.
+ if(_keycomparer == null || !(_keycomparer is System.Collections.Generic.RandomizedObjectEqualityComparer))
+ {
+ _keycomparer = HashHelpers.GetRandomizedEqualityComparer(_keycomparer);
+ rehash(buckets.Length, true);
+ }
+ }
+#endif // !FEATURE_CORECLR
+#endif // FEATURE_RANDOMIZED_STRING_HASHING
+ return;
+ }
+
+ // The current bucket is in use
+ // OR
+ // it is available, but has had the collision bit set and we have already found an available bucket
+ if (((buckets[bucketNumber].hash_coll & 0x7FFFFFFF) == hashcode) &&
+ KeyEquals (buckets[bucketNumber].key, key)) {
+ if (add) {
+ throw new ArgumentException(Environment.GetResourceString("Argument_AddingDuplicate__", buckets[bucketNumber].key, key));
+ }
+#if !FEATURE_CORECLR
+ Thread.BeginCriticalRegion();
+#endif
+ isWriterInProgress = true;
+ buckets[bucketNumber].val = nvalue;
+ UpdateVersion();
+ isWriterInProgress = false;
+#if !FEATURE_CORECLR
+ Thread.EndCriticalRegion();
+#endif
+
+#if FEATURE_RANDOMIZED_STRING_HASHING
+#if !FEATURE_CORECLR
+ if(ntry > HashHelpers.HashCollisionThreshold && HashHelpers.IsWellKnownEqualityComparer(_keycomparer))
+ {
+ // PERF: We don't want to rehash if _keycomparer is already a RandomizedObjectEqualityComparer since in some
+ // cases there may not be any strings in the hashtable and we wouldn't get any mixing.
+ if(_keycomparer == null || !(_keycomparer is System.Collections.Generic.RandomizedObjectEqualityComparer))
+ {
+ _keycomparer = HashHelpers.GetRandomizedEqualityComparer(_keycomparer);
+ rehash(buckets.Length, true);
+ }
+ }
+#endif // !FEATURE_CORECLR
+#endif
+ return;
+ }
+
+ // The current bucket is full, and we have therefore collided. We need to set the collision bit
+ // UNLESS
+ // we have remembered an available slot previously.
+ if (emptySlotNumber == -1) {// We don't need to set the collision bit here since we already have an empty slot
+ if( buckets[bucketNumber].hash_coll >= 0 ) {
+ buckets[bucketNumber].hash_coll |= unchecked((int)0x80000000);
+ occupancy++;
+ }
+ }
+
+ bucketNumber = (int) (((long)bucketNumber + incr)% (uint)buckets.Length);
+ } while (++ntry < buckets.Length);
+
+ // This code is here if and only if there were no buckets without a collision bit set in the entire table
+ if (emptySlotNumber != -1)
+ {
+ // We pretty much have to insert in this order. Don't set hash
+ // code until the value & key are set appropriately.
+#if !FEATURE_CORECLR
+ Thread.BeginCriticalRegion();
+#endif
+ isWriterInProgress = true;
+ buckets[emptySlotNumber].val = nvalue;
+ buckets[emptySlotNumber].key = key;
+ buckets[emptySlotNumber].hash_coll |= (int) hashcode;
+ count++;
+ UpdateVersion();
+ isWriterInProgress = false;
+#if !FEATURE_CORECLR
+ Thread.EndCriticalRegion();
+#endif
+
+#if FEATURE_RANDOMIZED_STRING_HASHING
+#if !FEATURE_CORECLR
+ if(buckets.Length > HashHelpers.HashCollisionThreshold && HashHelpers.IsWellKnownEqualityComparer(_keycomparer))
+ {
+ // PERF: We don't want to rehash if _keycomparer is already a RandomizedObjectEqualityComparer since in some
+ // cases there may not be any strings in the hashtable and we wouldn't get any mixing.
+ if(_keycomparer == null || !(_keycomparer is System.Collections.Generic.RandomizedObjectEqualityComparer))
+ {
+ _keycomparer = HashHelpers.GetRandomizedEqualityComparer(_keycomparer);
+ rehash(buckets.Length, true);
+ }
+ }
+#endif // !FEATURE_CORECLR
+#endif
+ return;
+ }
+
+ // If you see this assert, make sure load factor & count are reasonable.
+ // Then verify that our double hash function (h2, described at top of file)
+ // meets the requirements described above. You should never see this assert.
+ Contract.Assert(false, "hash table insert failed! Load factor too high, or our double hashing function is incorrect.");
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HashInsertFailed"));
+ }
+
+ private void putEntry (bucket[] newBuckets, Object key, Object nvalue, int hashcode)
+ {
+ Contract.Assert(hashcode >= 0, "hashcode >= 0"); // make sure collision bit (sign bit) wasn't set.
+
+ uint seed = (uint) hashcode;
+ uint incr = (uint)(1 + ((seed * HashPrime) % ((uint)newBuckets.Length - 1)));
+ int bucketNumber = (int) (seed % (uint)newBuckets.Length);
+ do {
+
+ if ((newBuckets[bucketNumber].key == null) || (newBuckets[bucketNumber].key == buckets)) {
+ newBuckets[bucketNumber].val = nvalue;
+ newBuckets[bucketNumber].key = key;
+ newBuckets[bucketNumber].hash_coll |= hashcode;
+ return;
+ }
+
+ if( newBuckets[bucketNumber].hash_coll >= 0 ) {
+ newBuckets[bucketNumber].hash_coll |= unchecked((int)0x80000000);
+ occupancy++;
+ }
+ bucketNumber = (int) (((long)bucketNumber + incr)% (uint)newBuckets.Length);
+ } while (true);
+ }
+
+ // Removes an entry from this hashtable. If an entry with the specified
+ // key exists in the hashtable, it is removed. An ArgumentException is
+ // thrown if the key is null.
+ //
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public virtual void Remove(Object key) {
+ if (key == null) {
+ throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
+ }
+ Contract.EndContractBlock();
+ Contract.Assert(!isWriterInProgress, "Race condition detected in usages of Hashtable - multiple threads appear to be writing to a Hashtable instance simultaneously! Don't do that - use Hashtable.Synchronized.");
+
+ uint seed;
+ uint incr;
+ // Assuming only one concurrent writer, write directly into buckets.
+ uint hashcode = InitHash(key, buckets.Length, out seed, out incr);
+ int ntry = 0;
+
+ bucket b;
+ int bn = (int) (seed % (uint)buckets.Length); // bucketNumber
+ do {
+ b = buckets[bn];
+ if (((b.hash_coll & 0x7FFFFFFF) == hashcode) &&
+ KeyEquals (b.key, key)) {
+#if !FEATURE_CORECLR
+ Thread.BeginCriticalRegion();
+#endif
+ isWriterInProgress = true;
+ // Clear hash_coll field, then key, then value
+ buckets[bn].hash_coll &= unchecked((int)0x80000000);
+ if (buckets[bn].hash_coll != 0) {
+ buckets[bn].key = buckets;
+ }
+ else {
+ buckets[bn].key = null;
+ }
+ buckets[bn].val = null; // Free object references sooner & simplify ContainsValue.
+ count--;
+ UpdateVersion();
+ isWriterInProgress = false;
+#if !FEATURE_CORECLR
+ Thread.EndCriticalRegion();
+#endif
+ return;
+ }
+ bn = (int) (((long)bn + incr)% (uint)buckets.Length);
+ } while (b.hash_coll < 0 && ++ntry < buckets.Length);
+
+ //throw new ArgumentException(Environment.GetResourceString("Arg_RemoveArgNotFound"));
+ }
+
+ // Returns the object to synchronize on for this hash table.
+ public virtual Object SyncRoot {
+ get {
+ if( _syncRoot == null) {
+ System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);
+ }
+ return _syncRoot;
+ }
+ }
+
+ // Returns the number of associations in this hashtable.
+ //
+ public virtual int Count {
+ get { return count; }
+ }
+
+ // Returns a thread-safe wrapper for a Hashtable.
+ //
+ [HostProtection(Synchronization=true)]
+ public static Hashtable Synchronized(Hashtable table) {
+ if (table==null)
+ throw new ArgumentNullException("table");
+ Contract.EndContractBlock();
+ return new SyncHashtable(table);
+ }
+
+ //
+ // The ISerializable Implementation
+ //
+
+ [System.Security.SecurityCritical]
+ public virtual void GetObjectData(SerializationInfo info, StreamingContext context) {
+ if (info==null) {
+ throw new ArgumentNullException("info");
+ }
+ Contract.EndContractBlock();
+ // This is imperfect - it only works well if all other writes are
+ // also using our synchronized wrapper. But it's still a good idea.
+ lock (SyncRoot) {
+ // This method hasn't been fully tweaked to be safe for a concurrent writer.
+ int oldVersion = version;
+ info.AddValue(LoadFactorName, loadFactor);
+ info.AddValue(VersionName, version);
+
+ //
+ // We need to maintain serialization compatibility with Everett and RTM.
+ // If the comparer is null or a compatible comparer, serialize Hashtable
+ // in a format that can be deserialized on Everett and RTM.
+ //
+ // Also, if the Hashtable is using randomized hashing, serialize the old
+ // view of the _keycomparer so perevious frameworks don't see the new types
+#pragma warning disable 618
+#if FEATURE_RANDOMIZED_STRING_HASHING
+ IEqualityComparer keyComparerForSerilization = (IEqualityComparer) HashHelpers.GetEqualityComparerForSerialization(_keycomparer);
+#else
+ IEqualityComparer keyComparerForSerilization = _keycomparer;
+#endif
+
+ if( keyComparerForSerilization == null) {
+ info.AddValue(ComparerName, null,typeof(IComparer));
+ info.AddValue(HashCodeProviderName, null, typeof(IHashCodeProvider));
+ }
+ else if(keyComparerForSerilization is CompatibleComparer) {
+ CompatibleComparer c = keyComparerForSerilization as CompatibleComparer;
+ info.AddValue(ComparerName, c.Comparer, typeof(IComparer));
+ info.AddValue(HashCodeProviderName, c.HashCodeProvider, typeof(IHashCodeProvider));
+ }
+ else {
+ info.AddValue(KeyComparerName, keyComparerForSerilization, typeof(IEqualityComparer));
+ }
+#pragma warning restore 618
+
+ info.AddValue(HashSizeName, buckets.Length); //This is the length of the bucket array.
+ Object [] serKeys = new Object[count];
+ Object [] serValues = new Object[count];
+ CopyKeys(serKeys, 0);
+ CopyValues(serValues,0);
+ info.AddValue(KeysName, serKeys, typeof(Object[]));
+ info.AddValue(ValuesName, serValues, typeof(Object[]));
+
+ // Explicitly check to see if anyone changed the Hashtable while we
+ // were serializing it. That's a race condition in their code.
+ if (version != oldVersion)
+ throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
+ }
+ }
+
+ //
+ // DeserializationEvent Listener
+ //
+ public virtual void OnDeserialization(Object sender) {
+ if (buckets!=null) {
+ // Somebody had a dependency on this hashtable and fixed us up before the ObjectManager got to it.
+ return;
+ }
+
+ SerializationInfo siInfo;
+ HashHelpers.SerializationInfoTable.TryGetValue(this, out siInfo);
+
+ if (siInfo==null) {
+ throw new SerializationException(Environment.GetResourceString("Serialization_InvalidOnDeser"));
+ }
+
+ int hashsize = 0;
+ IComparer c = null;
+
+#pragma warning disable 618
+ IHashCodeProvider hcp = null;
+#pragma warning restore 618
+
+ Object [] serKeys = null;
+ Object [] serValues = null;
+
+ SerializationInfoEnumerator enumerator = siInfo.GetEnumerator();
+
+ while( enumerator.MoveNext())
+ {
+ switch( enumerator.Name)
+ {
+ case LoadFactorName:
+ loadFactor = siInfo.GetSingle(LoadFactorName);
+ break;
+ case HashSizeName:
+ hashsize = siInfo.GetInt32(HashSizeName);
+ break;
+ case KeyComparerName:
+ _keycomparer = (IEqualityComparer)siInfo.GetValue(KeyComparerName, typeof(IEqualityComparer));
+ break;
+ case ComparerName:
+ c = (IComparer)siInfo.GetValue(ComparerName, typeof(IComparer));
+ break;
+ case HashCodeProviderName:
+#pragma warning disable 618
+ hcp = (IHashCodeProvider)siInfo.GetValue(HashCodeProviderName, typeof(IHashCodeProvider));
+#pragma warning restore 618
+ break;
+ case KeysName:
+ serKeys = (Object[])siInfo.GetValue(KeysName, typeof(Object[]));
+ break;
+ case ValuesName:
+ serValues = (Object[])siInfo.GetValue(ValuesName, typeof(Object[]));
+ break;
+ }
+ }
+
+ loadsize = (int)(loadFactor*hashsize);
+
+ // V1 object doesn't has _keycomparer field.
+ if ( (_keycomparer == null) && ( (c != null) || (hcp != null) ) ){
+ _keycomparer = new CompatibleComparer(c,hcp);
+ }
+
+ buckets = new bucket[hashsize];
+
+ if (serKeys==null) {
+ throw new SerializationException(Environment.GetResourceString("Serialization_MissingKeys"));
+ }
+ if (serValues==null) {
+ throw new SerializationException(Environment.GetResourceString("Serialization_MissingValues"));
+ }
+ if (serKeys.Length!=serValues.Length) {
+ throw new SerializationException(Environment.GetResourceString("Serialization_KeyValueDifferentSizes"));
+ }
+ for (int i=0; i<serKeys.Length; i++) {
+ if (serKeys[i]==null) {
+ throw new SerializationException(Environment.GetResourceString("Serialization_NullKey"));
+ }
+ Insert(serKeys[i], serValues[i], true);
+ }
+
+ version = siInfo.GetInt32(VersionName);
+
+ HashHelpers.SerializationInfoTable.Remove(this);
+ }
+
+
+ // Implements a Collection for the keys of a hashtable. An instance of this
+ // class is created by the GetKeys method of a hashtable.
+ [Serializable]
+ private class KeyCollection : ICollection
+ {
+ private Hashtable _hashtable;
+
+ internal KeyCollection(Hashtable hashtable) {
+ _hashtable = hashtable;
+ }
+
+ public virtual void CopyTo(Array array, int arrayIndex) {
+ if (array==null)
+ throw new ArgumentNullException("array");
+ if (array.Rank != 1)
+ throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
+ if (arrayIndex < 0)
+ throw new ArgumentOutOfRangeException("arrayIndex", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ Contract.EndContractBlock();
+ if (array.Length - arrayIndex < _hashtable.count)
+ throw new ArgumentException(Environment.GetResourceString("Arg_ArrayPlusOffTooSmall"));
+ _hashtable.CopyKeys(array, arrayIndex);
+ }
+
+ public virtual IEnumerator GetEnumerator() {
+ return new HashtableEnumerator(_hashtable, HashtableEnumerator.Keys);
+ }
+
+ public virtual bool IsSynchronized {
+ get { return _hashtable.IsSynchronized; }
+ }
+
+ public virtual Object SyncRoot {
+ get { return _hashtable.SyncRoot; }
+ }
+
+ public virtual int Count {
+ get { return _hashtable.count; }
+ }
+ }
+
+ // Implements a Collection for the values of a hashtable. An instance of
+ // this class is created by the GetValues method of a hashtable.
+ [Serializable]
+ private class ValueCollection : ICollection
+ {
+ private Hashtable _hashtable;
+
+ internal ValueCollection(Hashtable hashtable) {
+ _hashtable = hashtable;
+ }
+
+ public virtual void CopyTo(Array array, int arrayIndex) {
+ if (array==null)
+ throw new ArgumentNullException("array");
+ if (array.Rank != 1)
+ throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
+ if (arrayIndex < 0)
+ throw new ArgumentOutOfRangeException("arrayIndex", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ Contract.EndContractBlock();
+ if (array.Length - arrayIndex < _hashtable.count)
+ throw new ArgumentException(Environment.GetResourceString("Arg_ArrayPlusOffTooSmall"));
+ _hashtable.CopyValues(array, arrayIndex);
+ }
+
+ public virtual IEnumerator GetEnumerator() {
+ return new HashtableEnumerator(_hashtable, HashtableEnumerator.Values);
+ }
+
+ public virtual bool IsSynchronized {
+ get { return _hashtable.IsSynchronized; }
+ }
+
+ public virtual Object SyncRoot {
+ get { return _hashtable.SyncRoot; }
+ }
+
+ public virtual int Count {
+ get { return _hashtable.count; }
+ }
+ }
+
+ // Synchronized wrapper for hashtable
+ [Serializable]
+ private class SyncHashtable : Hashtable, IEnumerable
+ {
+ protected Hashtable _table;
+
+ internal SyncHashtable(Hashtable table) : base(false) {
+ _table = table;
+ }
+
+ internal SyncHashtable(SerializationInfo info, StreamingContext context) : base (info, context) {
+ _table = (Hashtable)info.GetValue("ParentTable", typeof(Hashtable));
+ if (_table==null) {
+ throw new SerializationException(Environment.GetResourceString("Serialization_InsufficientState"));
+ }
+ }
+
+
+ /*================================GetObjectData=================================
+ **Action: Return a serialization info containing a reference to _table. We need
+ ** to implement this because our parent HT does and we don't want to actually
+ ** serialize all of it's values (just a reference to the table, which will then
+ ** be serialized separately.)
+ **Returns: void
+ **Arguments: info -- the SerializationInfo into which to store the data.
+ ** context -- the StreamingContext for the current serialization (ignored)
+ **Exceptions: ArgumentNullException if info is null.
+ ==============================================================================*/
+ [System.Security.SecurityCritical] // auto-generated
+ public override void GetObjectData(SerializationInfo info, StreamingContext context) {
+ if (info==null) {
+ throw new ArgumentNullException("info");
+ }
+ Contract.EndContractBlock();
+ // Our serialization code hasn't been fully tweaked to be safe
+ // for a concurrent writer.
+ lock (_table.SyncRoot) {
+ info.AddValue("ParentTable", _table, typeof(Hashtable));
+ }
+ }
+
+ public override int Count {
+ get { return _table.Count; }
+ }
+
+ public override bool IsReadOnly {
+ get { return _table.IsReadOnly; }
+ }
+
+ public override bool IsFixedSize {
+ get { return _table.IsFixedSize; }
+ }
+
+ public override bool IsSynchronized {
+ get { return true; }
+ }
+
+ public override Object this[Object key] {
+ get {
+ return _table[key];
+ }
+ set {
+ lock(_table.SyncRoot) {
+ _table[key] = value;
+ }
+ }
+ }
+
+ public override Object SyncRoot {
+ get { return _table.SyncRoot; }
+ }
+
+ public override void Add(Object key, Object value) {
+ lock(_table.SyncRoot) {
+ _table.Add(key, value);
+ }
+ }
+
+ public override void Clear() {
+ lock(_table.SyncRoot) {
+ _table.Clear();
+ }
+ }
+
+ public override bool Contains(Object key) {
+ return _table.Contains(key);
+ }
+
+ public override bool ContainsKey(Object key) {
+ if (key == null) {
+ throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
+ }
+ Contract.EndContractBlock();
+ return _table.ContainsKey(key);
+ }
+
+ public override bool ContainsValue(Object key) {
+ lock(_table.SyncRoot) {
+ return _table.ContainsValue(key);
+ }
+ }
+
+ public override void CopyTo(Array array, int arrayIndex) {
+ lock (_table.SyncRoot) {
+ _table.CopyTo(array, arrayIndex);
+ }
+ }
+
+ public override Object Clone() {
+ lock (_table.SyncRoot) {
+ return Hashtable.Synchronized((Hashtable)_table.Clone());
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() {
+ return _table.GetEnumerator();
+ }
+
+ public override IDictionaryEnumerator GetEnumerator() {
+ return _table.GetEnumerator();
+ }
+
+ public override ICollection Keys {
+ get {
+ lock(_table.SyncRoot) {
+ return _table.Keys;
+ }
+ }
+ }
+
+ public override ICollection Values {
+ get {
+ lock(_table.SyncRoot) {
+ return _table.Values;
+ }
+ }
+ }
+
+ public override void Remove(Object key) {
+ lock(_table.SyncRoot) {
+ _table.Remove(key);
+ }
+ }
+
+ /*==============================OnDeserialization===============================
+ **Action: Does nothing. We have to implement this because our parent HT implements it,
+ ** but it doesn't do anything meaningful. The real work will be done when we
+ ** call OnDeserialization on our parent table.
+ **Returns: void
+ **Arguments: None
+ **Exceptions: None
+ ==============================================================================*/
+ public override void OnDeserialization(Object sender) {
+ return;
+ }
+
+ internal override KeyValuePairs[] ToKeyValuePairsArray() {
+ return _table.ToKeyValuePairsArray();
+ }
+
+ }
+
+
+ // Implements an enumerator for a hashtable. The enumerator uses the
+ // internal version number of the hashtabke to ensure that no modifications
+ // are made to the hashtable while an enumeration is in progress.
+ [Serializable]
+ private class HashtableEnumerator : IDictionaryEnumerator, ICloneable
+ {
+ private Hashtable hashtable;
+ private int bucket;
+ private int version;
+ private bool current;
+ private int getObjectRetType; // What should GetObject return?
+ private Object currentKey;
+ private Object currentValue;
+
+ internal const int Keys = 1;
+ internal const int Values = 2;
+ internal const int DictEntry = 3;
+
+ internal HashtableEnumerator(Hashtable hashtable, int getObjRetType) {
+ this.hashtable = hashtable;
+ bucket = hashtable.buckets.Length;
+ version = hashtable.version;
+ current = false;
+ getObjectRetType = getObjRetType;
+ }
+
+ public Object Clone() {
+ return MemberwiseClone();
+ }
+
+ public virtual Object Key {
+ get {
+ if (current == false) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
+ return currentKey;
+ }
+ }
+
+ public virtual bool MoveNext() {
+ if (version != hashtable.version) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
+ while (bucket > 0) {
+ bucket--;
+ Object keyv = hashtable.buckets[bucket].key;
+ if ((keyv!= null) && (keyv != hashtable.buckets)) {
+ currentKey = keyv;
+ currentValue = hashtable.buckets[bucket].val;
+ current = true;
+ return true;
+ }
+ }
+ current = false;
+ return false;
+ }
+
+ public virtual DictionaryEntry Entry {
+ get {
+ if (current == false) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumOpCantHappen));
+ return new DictionaryEntry(currentKey, currentValue);
+ }
+ }
+
+
+ public virtual Object Current {
+ get {
+ if (current == false) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumOpCantHappen));
+
+ if (getObjectRetType==Keys)
+ return currentKey;
+ else if (getObjectRetType==Values)
+ return currentValue;
+ else
+ return new DictionaryEntry(currentKey, currentValue);
+ }
+ }
+
+ public virtual Object Value {
+ get {
+ if (current == false) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumOpCantHappen));
+ return currentValue;
+ }
+ }
+
+ public virtual void Reset() {
+ if (version != hashtable.version) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
+ current = false;
+ bucket = hashtable.buckets.Length;
+ currentKey = null;
+ currentValue = null;
+ }
+ }
+
+ // internal debug view class for hashtable
+ internal class HashtableDebugView {
+ private Hashtable hashtable;
+
+ public HashtableDebugView( Hashtable hashtable) {
+ if( hashtable == null) {
+ throw new ArgumentNullException( "hashtable");
+ }
+ Contract.EndContractBlock();
+
+ this.hashtable = hashtable;
+ }
+
+
+ [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+ public KeyValuePairs[] Items {
+ get {
+ return hashtable.ToKeyValuePairsArray();
+ }
+ }
+ }
+ }
+
+ [FriendAccessAllowed]
+ internal static class HashHelpers
+ {
+
+#if FEATURE_RANDOMIZED_STRING_HASHING
+ public const int HashCollisionThreshold = 100;
+ public static bool s_UseRandomizedStringHashing = String.UseRandomizedHashing();
+#endif
+
+ // Table of prime numbers to use as hash table sizes.
+ // A typical resize algorithm would pick the smallest prime number in this array
+ // that is larger than twice the previous capacity.
+ // Suppose our Hashtable currently has capacity x and enough elements are added
+ // such that a resize needs to occur. Resizing first computes 2x then finds the
+ // first prime in the table greater than 2x, i.e. if primes are ordered
+ // p_1, p_2, ..., p_i, ..., it finds p_n such that p_n-1 < 2x < p_n.
+ // Doubling is important for preserving the asymptotic complexity of the
+ // hashtable operations such as add. Having a prime guarantees that double
+ // hashing does not lead to infinite loops. IE, your hash function will be
+ // h1(key) + i*h2(key), 0 <= i < size. h2 and the size must be relatively prime.
+ public static readonly int[] primes = {
+ 3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919,
+ 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591,
+ 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437,
+ 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263,
+ 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369};
+
+ // Used by Hashtable and Dictionary's SeralizationInfo .ctor's to store the SeralizationInfo
+ // object until OnDeserialization is called.
+ private static ConditionalWeakTable<object, SerializationInfo> s_SerializationInfoTable;
+
+ internal static ConditionalWeakTable<object, SerializationInfo> SerializationInfoTable
+ {
+ get
+ {
+ if(s_SerializationInfoTable == null)
+ {
+ ConditionalWeakTable<object, SerializationInfo> newTable = new ConditionalWeakTable<object, SerializationInfo>();
+ Interlocked.CompareExchange(ref s_SerializationInfoTable, newTable, null);
+ }
+
+ return s_SerializationInfoTable;
+ }
+
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ public static bool IsPrime(int candidate)
+ {
+ if ((candidate & 1) != 0)
+ {
+ int limit = (int)Math.Sqrt (candidate);
+ for (int divisor = 3; divisor <= limit; divisor+=2)
+ {
+ if ((candidate % divisor) == 0)
+ return false;
+ }
+ return true;
+ }
+ return (candidate == 2);
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ public static int GetPrime(int min)
+ {
+ if (min < 0)
+ throw new ArgumentException(Environment.GetResourceString("Arg_HTCapacityOverflow"));
+ Contract.EndContractBlock();
+
+ for (int i = 0; i < primes.Length; i++)
+ {
+ int prime = primes[i];
+ if (prime >= min) return prime;
+ }
+
+ //outside of our predefined table.
+ //compute the hard way.
+ for (int i = (min | 1); i < Int32.MaxValue;i+=2)
+ {
+ if (IsPrime(i) && ((i - 1) % Hashtable.HashPrime != 0))
+ return i;
+ }
+ return min;
+ }
+
+ public static int GetMinPrime()
+ {
+ return primes[0];
+ }
+
+ // Returns size of hashtable to grow to.
+ public static int ExpandPrime(int oldSize)
+ {
+ int newSize = 2 * oldSize;
+
+ // Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow.
+ // Note that this check works even when _items.Length overflowed thanks to the (uint) cast
+ if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize)
+ {
+ Contract.Assert( MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength");
+ return MaxPrimeArrayLength;
+ }
+
+ return GetPrime(newSize);
+ }
+
+
+ // This is the maximum prime smaller than Array.MaxArrayLength
+ public const int MaxPrimeArrayLength = 0x7FEFFFFD;
+
+#if FEATURE_RANDOMIZED_STRING_HASHING
+ public static bool IsWellKnownEqualityComparer(object comparer)
+ {
+ return (comparer == null || comparer == System.Collections.Generic.EqualityComparer<string>.Default || comparer is IWellKnownStringEqualityComparer);
+ }
+
+ public static IEqualityComparer GetRandomizedEqualityComparer(object comparer)
+ {
+ Contract.Assert(comparer == null || comparer == System.Collections.Generic.EqualityComparer<string>.Default || comparer is IWellKnownStringEqualityComparer);
+
+ if(comparer == null) {
+ return new System.Collections.Generic.RandomizedObjectEqualityComparer();
+ }
+
+ if(comparer == System.Collections.Generic.EqualityComparer<string>.Default) {
+ return new System.Collections.Generic.RandomizedStringEqualityComparer();
+ }
+
+ IWellKnownStringEqualityComparer cmp = comparer as IWellKnownStringEqualityComparer;
+
+ if(cmp != null) {
+ return cmp.GetRandomizedEqualityComparer();
+ }
+
+ Contract.Assert(false, "Missing case in GetRandomizedEqualityComparer!");
+
+ return null;
+ }
+
+ public static object GetEqualityComparerForSerialization(object comparer)
+ {
+ if(comparer == null)
+ {
+ return null;
+ }
+
+ IWellKnownStringEqualityComparer cmp = comparer as IWellKnownStringEqualityComparer;
+
+ if(cmp != null)
+ {
+ return cmp.GetEqualityComparerForSerialization();
+ }
+
+ return comparer;
+ }
+
+ private const int bufferSize = 1024;
+#if !FEATURE_CORECLR
+ private static RandomNumberGenerator rng;
+#endif
+ private static byte[] data;
+ private static int currentIndex = bufferSize;
+ private static readonly object lockObj = new Object();
+
+ internal static long GetEntropy()
+ {
+ lock(lockObj) {
+ long ret;
+
+ if(currentIndex == bufferSize)
+ {
+ if(data == null)
+ {
+ data = new byte[bufferSize];
+ Contract.Assert(bufferSize % 8 == 0, "We increment our current index by 8, so our buffer size must be a multiple of 8");
+#if !FEATURE_CORECLR
+ rng = RandomNumberGenerator.Create();
+#endif
+
+ }
+
+#if FEATURE_CORECLR
+ Microsoft.Win32.Win32Native.Random(true, data, data.Length);
+#else
+ rng.GetBytes(data);
+#endif
+ currentIndex = 0;
+ }
+
+ ret = BitConverter.ToInt64(data, currentIndex);
+ currentIndex += 8;
+
+ return ret;
+ }
+ }
+#endif // FEATURE_RANDOMIZED_STRING_HASHING
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/ICollection.cs b/src/mscorlib/src/System/Collections/ICollection.cs
new file mode 100644
index 0000000000..6d4d6e1a93
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/ICollection.cs
@@ -0,0 +1,81 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: ICollection
+**
+**
+**
+**
+** Purpose: Base interface for all collections.
+**
+**
+===========================================================*/
+namespace System.Collections {
+ using System;
+ using System.Diagnostics.Contracts;
+
+ // Base interface for all collections, defining enumerators, size, and
+ // synchronization methods.
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public interface ICollection : IEnumerable
+ {
+ // Interfaces are not serialable
+ // CopyTo copies a collection into an Array, starting at a particular
+ // index into the array.
+ //
+ void CopyTo(Array array, int index);
+
+ // Number of items in the collections.
+ int Count
+ { get; }
+
+
+ // SyncRoot will return an Object to use for synchronization
+ // (thread safety). You can use this object in your code to take a
+ // lock on the collection, even if this collection is a wrapper around
+ // another collection. The intent is to tunnel through to a real
+ // implementation of a collection, and use one of the internal objects
+ // found in that code.
+ //
+ // In the absense of a static Synchronized method on a collection,
+ // the expected usage for SyncRoot would look like this:
+ //
+ // ICollection col = ...
+ // lock (col.SyncRoot) {
+ // // Some operation on the collection, which is now thread safe.
+ // // This may include multiple operations.
+ // }
+ //
+ //
+ // The system-provided collections have a static method called
+ // Synchronized which will create a thread-safe wrapper around the
+ // collection. All access to the collection that you want to be
+ // thread-safe should go through that wrapper collection. However, if
+ // you need to do multiple calls on that collection (such as retrieving
+ // two items, or checking the count then doing something), you should
+ // NOT use our thread-safe wrapper since it only takes a lock for the
+ // duration of a single method call. Instead, use Monitor.Enter/Exit
+ // or your language's equivalent to the C# lock keyword as mentioned
+ // above.
+ //
+ // For collections with no publically available underlying store, the
+ // expected implementation is to simply return the this pointer. Note
+ // that the this pointer may not be sufficient for collections that
+ // wrap other collections; those should return the underlying
+ // collection's SyncRoot property.
+ Object SyncRoot
+ { get; }
+
+ // Is this collection synchronized (i.e., thread-safe)? If you want a
+ // thread-safe collection, you can use SyncRoot as an object to
+ // synchronize your collection with. If you're using one of the
+ // collections in System.Collections, you could call the static
+ // Synchronized method to get a thread-safe wrapper around the
+ // underlying collection.
+ bool IsSynchronized
+ { get; }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/IComparer.cs b/src/mscorlib/src/System/Collections/IComparer.cs
new file mode 100644
index 0000000000..d5a3448934
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/IComparer.cs
@@ -0,0 +1,32 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: IComparer
+**
+**
+**
+**
+** Purpose: Interface for comparing two Objects.
+**
+**
+===========================================================*/
+namespace System.Collections {
+
+ using System;
+ // The IComparer interface implements a method that compares two objects. It is
+ // used in conjunction with the Sort and BinarySearch methods on
+ // the Array and List classes.
+ //
+ // Interfaces are not serializable
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public interface IComparer {
+ // Compares two objects. An implementation of this method must return a
+ // value less than zero if x is less than y, zero if x is equal to y, or a
+ // value greater than zero if x is greater than y.
+ //
+ int Compare(Object x, Object y);
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/IDictionary.cs b/src/mscorlib/src/System/Collections/IDictionary.cs
new file mode 100644
index 0000000000..4da89d6983
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/IDictionary.cs
@@ -0,0 +1,69 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: IDictionary
+**
+**
+**
+**
+** Purpose: Base interface for all dictionaries.
+**
+**
+===========================================================*/
+namespace System.Collections {
+ using System;
+ using System.Diagnostics.Contracts;
+
+ // An IDictionary is a possibly unordered set of key-value pairs.
+ // Keys can be any non-null object. Values can be any object.
+ // You can look up a value in an IDictionary via the default indexed
+ // property, Items.
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public interface IDictionary : ICollection
+ {
+ // Interfaces are not serializable
+ // The Item property provides methods to read and edit entries
+ // in the Dictionary.
+ Object this[Object key] {
+ get;
+ set;
+ }
+
+ // Returns a collections of the keys in this dictionary.
+ ICollection Keys {
+ get;
+ }
+
+ // Returns a collections of the values in this dictionary.
+ ICollection Values {
+ get;
+ }
+
+ // Returns whether this dictionary contains a particular key.
+ //
+ bool Contains(Object key);
+
+ // Adds a key-value pair to the dictionary.
+ //
+ void Add(Object key, Object value);
+
+ // Removes all pairs from the dictionary.
+ void Clear();
+
+ bool IsReadOnly
+ { get; }
+
+ bool IsFixedSize
+ { get; }
+
+ // Returns an IDictionaryEnumerator for this dictionary.
+ new IDictionaryEnumerator GetEnumerator();
+
+ // Removes a particular key from the dictionary.
+ //
+ void Remove(Object key);
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/IDictionaryEnumerator.cs b/src/mscorlib/src/System/Collections/IDictionaryEnumerator.cs
new file mode 100644
index 0000000000..b995a04a0e
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/IDictionaryEnumerator.cs
@@ -0,0 +1,79 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: IDictionaryEnumerator
+**
+**
+**
+**
+** Purpose: Base interface for dictionary enumerators.
+**
+**
+===========================================================*/
+namespace System.Collections {
+ // Interfaces are not serializable
+
+ using System;
+ // This interface represents an enumerator that allows sequential access to the
+ // elements of a dictionary. Upon creation, an enumerator is conceptually
+ // positioned before the first element of the enumeration. The first call to the
+ // MoveNext method brings the first element of the enumeration into view,
+ // and each successive call to MoveNext brings the next element into
+ // view until MoveNext returns false, indicating that there are no more
+ // elements to enumerate. Following each call to MoveNext, the
+ // Key and Value methods are used to obtain the key and
+ // value of the element currently in view. The values returned by calls to
+ // Key and Value are undefined before the first call to
+ // MoveNext and following a call to MoveNext that returned false.
+ // Enumerators are typically used in while loops of the form
+ //
+ // IDictionaryEnumerator e = ...;
+ // while (e.MoveNext()) {
+ // Object key = e.Key;
+ // Object value = e.Value;
+ // ...
+ // }
+ //
+ // The IDictionaryEnumerator interface extends the IEnumerator
+ // inerface and can thus be used as a regular enumerator. The Current
+ // method of an IDictionaryEnumerator returns a DictionaryEntry containing
+ // the current key and value pair. However, the GetEntry method will
+ // return the same DictionaryEntry and avoids boxing the DictionaryEntry (boxing
+ // is somewhat expensive).
+ //
+[System.Runtime.InteropServices.ComVisible(true)]
+ public interface IDictionaryEnumerator : IEnumerator
+ {
+ // Returns the key of the current element of the enumeration. The returned
+ // value is undefined before the first call to GetNext and following
+ // a call to GetNext that returned false. Multiple calls to
+ // GetKey with no intervening calls to GetNext will return
+ // the same object.
+ //
+ Object Key {
+ get;
+ }
+
+ // Returns the value of the current element of the enumeration. The
+ // returned value is undefined before the first call to GetNext and
+ // following a call to GetNext that returned false. Multiple calls
+ // to GetValue with no intervening calls to GetNext will
+ // return the same object.
+ //
+ Object Value {
+ get;
+ }
+
+ // GetBlock will copy dictionary values into the given Array. It will either
+ // fill up the array, or if there aren't enough elements, it will
+ // copy as much as possible into the Array. The number of elements
+ // copied is returned.
+ //
+ DictionaryEntry Entry {
+ get;
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/IEnumerable.cs b/src/mscorlib/src/System/Collections/IEnumerable.cs
new file mode 100644
index 0000000000..5fa63f15d0
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/IEnumerable.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: IEnumerable
+**
+**
+**
+**
+** Purpose: Interface for classes providing IEnumerators
+**
+**
+===========================================================*/
+namespace System.Collections {
+ using System;
+ using System.Diagnostics.Contracts;
+ using System.Runtime.InteropServices;
+
+ // Implement this interface if you need to support VB's foreach semantics.
+ // Also, COM classes that support an enumerator will also implement this interface.
+ [Guid("496B0ABE-CDEE-11d3-88E8-00902754C43A")]
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public interface IEnumerable
+ {
+ // Interfaces are not serializable
+ // Returns an IEnumerator for this enumerable Object. The enumerator provides
+ // a simple way to access all the contents of a collection.
+ [Pure]
+ [DispId(-4)]
+ IEnumerator GetEnumerator();
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/IEnumerator.cs b/src/mscorlib/src/System/Collections/IEnumerator.cs
new file mode 100644
index 0000000000..99509d15ea
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/IEnumerator.cs
@@ -0,0 +1,53 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: IEnumerator
+**
+**
+**
+**
+** Purpose: Base interface for all enumerators.
+**
+**
+===========================================================*/
+namespace System.Collections {
+ using System;
+ using System.Runtime.InteropServices;
+
+ // Base interface for all enumerators, providing a simple approach
+ // to iterating over a collection.
+ [Guid("496B0ABF-CDEE-11d3-88E8-00902754C43A")]
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public interface IEnumerator
+ {
+ // Interfaces are not serializable
+ // Advances the enumerator to the next element of the enumeration and
+ // returns a boolean indicating whether an element is available. Upon
+ // creation, an enumerator is conceptually positioned before the first
+ // element of the enumeration, and the first call to MoveNext
+ // brings the first element of the enumeration into view.
+ //
+ bool MoveNext();
+
+ // Returns the current element of the enumeration. The returned value is
+ // undefined before the first call to MoveNext and following a
+ // call to MoveNext that returned false. Multiple calls to
+ // GetCurrent with no intervening calls to MoveNext
+ // will return the same object.
+ //
+ Object Current {
+ get;
+ }
+
+ // Resets the enumerator to the beginning of the enumeration, starting over.
+ // The preferred behavior for Reset is to return the exact same enumeration.
+ // This means if you modify the underlying collection then call Reset, your
+ // IEnumerator will be invalid, just as it would have been if you had called
+ // MoveNext or Current.
+ //
+ void Reset();
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/IEqualityComparer.cs b/src/mscorlib/src/System/Collections/IEqualityComparer.cs
new file mode 100644
index 0000000000..50c732f47c
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/IEqualityComparer.cs
@@ -0,0 +1,27 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: IEqualityComparer
+**
+**
+**
+**
+** Purpose: A mechanism to expose a simplified infrastructure for
+** Comparing objects in collections.
+**
+**
+===========================================================*/
+namespace System.Collections {
+
+ using System;
+ // An IEqualityComparer is a mechanism to consume custom performant comparison infrastructure
+ // that can be consumed by some of the common collections.
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public interface IEqualityComparer {
+ bool Equals(Object x, Object y);
+ int GetHashCode(Object obj);
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/IHashCodeProvider.cs b/src/mscorlib/src/System/Collections/IHashCodeProvider.cs
new file mode 100644
index 0000000000..aefa15e1e5
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/IHashCodeProvider.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: IHashCodeProvider
+**
+**
+**
+**
+** Purpose: A bunch of strings.
+**
+**
+===========================================================*/
+namespace System.Collections {
+
+ using System;
+ // Provides a mechanism for a hash table user to override the default
+ // GetHashCode() function on Objects, providing their own hash function.
+ [Obsolete("Please use IEqualityComparer instead.")]
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public interface IHashCodeProvider
+ {
+ // Interfaces are not serializable
+ // Returns a hash code for the given object.
+ //
+ int GetHashCode (Object obj);
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/IList.cs b/src/mscorlib/src/System/Collections/IList.cs
new file mode 100644
index 0000000000..8bdfe4c1d0
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/IList.cs
@@ -0,0 +1,71 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: IList
+**
+**
+**
+**
+** Purpose: Base interface for all Lists.
+**
+**
+===========================================================*/
+namespace System.Collections {
+
+ using System;
+ using System.Diagnostics.Contracts;
+
+ // An IList is an ordered collection of objects. The exact ordering
+ // is up to the implementation of the list, ranging from a sorted
+ // order to insertion order.
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public interface IList : ICollection
+ {
+ // The Item property provides methods to read and edit entries in the List.
+ Object this[int index] {
+ get;
+ set;
+ }
+
+ // Adds an item to the list. The exact position in the list is
+ // implementation-dependent, so while ArrayList may always insert
+ // in the last available location, a SortedList most likely would not.
+ // The return value is the position the new element was inserted in.
+ int Add(Object value);
+
+ // Returns whether the list contains a particular item.
+ bool Contains(Object value);
+
+ // Removes all items from the list.
+ void Clear();
+
+ bool IsReadOnly
+ { get; }
+
+
+ bool IsFixedSize
+ {
+ get;
+ }
+
+
+ // Returns the index of a particular item, if it is in the list.
+ // Returns -1 if the item isn't in the list.
+ int IndexOf(Object value);
+
+ // Inserts value into the list at position index.
+ // index must be non-negative and less than or equal to the
+ // number of elements in the list. If index equals the number
+ // of items in the list, then value is appended to the end.
+ void Insert(int index, Object value);
+
+ // Removes an item from the list.
+ void Remove(Object value);
+
+ // Removes the item at position index.
+ void RemoveAt(int index);
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/IStructuralComparable.cs b/src/mscorlib/src/System/Collections/IStructuralComparable.cs
new file mode 100644
index 0000000000..1b6f3aff7a
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/IStructuralComparable.cs
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+using System;
+
+namespace System.Collections {
+
+ public interface IStructuralComparable {
+ Int32 CompareTo(Object other, IComparer comparer);
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/IStructuralEquatable.cs b/src/mscorlib/src/System/Collections/IStructuralEquatable.cs
new file mode 100644
index 0000000000..5a5295fc38
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/IStructuralEquatable.cs
@@ -0,0 +1,10 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+namespace System.Collections {
+
+ public interface IStructuralEquatable {
+ Boolean Equals(Object other, IEqualityComparer comparer);
+ int GetHashCode(IEqualityComparer comparer);
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/KeyValuePairs.cs b/src/mscorlib/src/System/Collections/KeyValuePairs.cs
new file mode 100644
index 0000000000..22bf92c456
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/KeyValuePairs.cs
@@ -0,0 +1,40 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: KeyValuePairs to display items in collection class under debugger
+**
+**
+===========================================================*/
+
+namespace System.Collections {
+ using System.Diagnostics;
+
+ [DebuggerDisplay("{value}", Name = "[{key}]", Type = "" )]
+ internal class KeyValuePairs {
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private object key;
+
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private object value;
+
+ public KeyValuePairs(object key, object value) {
+ this.value = value;
+ this.key = key;
+ }
+
+ public object Key {
+ get { return key; }
+ }
+
+ public object Value {
+ get { return value; }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/ListDictionaryInternal.cs b/src/mscorlib/src/System/Collections/ListDictionaryInternal.cs
new file mode 100644
index 0000000000..944523c475
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/ListDictionaryInternal.cs
@@ -0,0 +1,429 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: List for exceptions.
+**
+**
+===========================================================*/
+using System.Diagnostics.Contracts;
+namespace System.Collections {
+ /// This is a simple implementation of IDictionary using a singly linked list. This
+ /// will be smaller and faster than a Hashtable if the number of elements is 10 or less.
+ /// This should not be used if performance is important for large numbers of elements.
+ [Serializable]
+ internal class ListDictionaryInternal: IDictionary {
+ DictionaryNode head;
+ int version;
+ int count;
+ [NonSerialized]
+ private Object _syncRoot;
+
+ public ListDictionaryInternal() {
+ }
+
+ public Object this[Object key] {
+ get {
+ if (key == null) {
+ throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
+ }
+ Contract.EndContractBlock();
+ DictionaryNode node = head;
+
+ while (node != null) {
+ if ( node.key.Equals(key) ) {
+ return node.value;
+ }
+ node = node.next;
+ }
+ return null;
+ }
+ set {
+ if (key == null) {
+ throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
+ }
+ Contract.EndContractBlock();
+
+#if FEATURE_SERIALIZATION
+ if (!key.GetType().IsSerializable)
+ throw new ArgumentException(Environment.GetResourceString("Argument_NotSerializable"), "key");
+
+ if( (value != null) && (!value.GetType().IsSerializable ) )
+ throw new ArgumentException(Environment.GetResourceString("Argument_NotSerializable"), "value");
+#endif
+
+ version++;
+ DictionaryNode last = null;
+ DictionaryNode node;
+ for (node = head; node != null; node = node.next) {
+ if( node.key.Equals(key) ) {
+ break;
+ }
+ last = node;
+ }
+ if (node != null) {
+ // Found it
+ node.value = value;
+ return;
+ }
+ // Not found, so add a new one
+ DictionaryNode newNode = new DictionaryNode();
+ newNode.key = key;
+ newNode.value = value;
+ if (last != null) {
+ last.next = newNode;
+ }
+ else {
+ head = newNode;
+ }
+ count++;
+ }
+ }
+
+ public int Count {
+ get {
+ return count;
+ }
+ }
+
+ public ICollection Keys {
+ get {
+ return new NodeKeyValueCollection(this, true);
+ }
+ }
+
+ public bool IsReadOnly {
+ get {
+ return false;
+ }
+ }
+
+ public bool IsFixedSize {
+ get {
+ return false;
+ }
+ }
+
+ public bool IsSynchronized {
+ get {
+ return false;
+ }
+ }
+
+ public Object SyncRoot {
+ get {
+ if( _syncRoot == null) {
+ System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);
+ }
+ return _syncRoot;
+ }
+ }
+
+ public ICollection Values {
+ get {
+ return new NodeKeyValueCollection(this, false);
+ }
+ }
+
+ public void Add(Object key, Object value) {
+ if (key == null) {
+ throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
+ }
+ Contract.EndContractBlock();
+
+#if FEATURE_SERIALIZATION
+ if (!key.GetType().IsSerializable)
+ throw new ArgumentException(Environment.GetResourceString("Argument_NotSerializable"), "key" );
+
+ if( (value != null) && (!value.GetType().IsSerializable) )
+ throw new ArgumentException(Environment.GetResourceString("Argument_NotSerializable"), "value");
+#endif
+
+ version++;
+ DictionaryNode last = null;
+ DictionaryNode node;
+ for (node = head; node != null; node = node.next) {
+ if (node.key.Equals(key)) {
+ throw new ArgumentException(Environment.GetResourceString("Argument_AddingDuplicate__", node.key, key));
+ }
+ last = node;
+ }
+ if (node != null) {
+ // Found it
+ node.value = value;
+ return;
+ }
+ // Not found, so add a new one
+ DictionaryNode newNode = new DictionaryNode();
+ newNode.key = key;
+ newNode.value = value;
+ if (last != null) {
+ last.next = newNode;
+ }
+ else {
+ head = newNode;
+ }
+ count++;
+ }
+
+ public void Clear() {
+ count = 0;
+ head = null;
+ version++;
+ }
+
+ public bool Contains(Object key) {
+ if (key == null) {
+ throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
+ }
+ Contract.EndContractBlock();
+ for (DictionaryNode node = head; node != null; node = node.next) {
+ if (node.key.Equals(key)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void CopyTo(Array array, int index) {
+ if (array==null)
+ throw new ArgumentNullException("array");
+
+ if (array.Rank != 1)
+ throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
+
+ if (index < 0)
+ throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+
+ if ( array.Length - index < this.Count )
+ throw new ArgumentException( Environment.GetResourceString("ArgumentOutOfRange_Index"), "index");
+ Contract.EndContractBlock();
+
+ for (DictionaryNode node = head; node != null; node = node.next) {
+ array.SetValue(new DictionaryEntry(node.key, node.value), index);
+ index++;
+ }
+ }
+
+ public IDictionaryEnumerator GetEnumerator() {
+ return new NodeEnumerator(this);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() {
+ return new NodeEnumerator(this);
+ }
+
+ public void Remove(Object key) {
+ if (key == null) {
+ throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
+ }
+ Contract.EndContractBlock();
+ version++;
+ DictionaryNode last = null;
+ DictionaryNode node;
+ for (node = head; node != null; node = node.next) {
+ if (node.key.Equals(key)) {
+ break;
+ }
+ last = node;
+ }
+ if (node == null) {
+ return;
+ }
+ if (node == head) {
+ head = node.next;
+ } else {
+ last.next = node.next;
+ }
+ count--;
+ }
+
+ private class NodeEnumerator : IDictionaryEnumerator {
+ ListDictionaryInternal list;
+ DictionaryNode current;
+ int version;
+ bool start;
+
+
+ public NodeEnumerator(ListDictionaryInternal list) {
+ this.list = list;
+ version = list.version;
+ start = true;
+ current = null;
+ }
+
+ public Object Current {
+ get {
+ return Entry;
+ }
+ }
+
+ public DictionaryEntry Entry {
+ get {
+ if (current == null) {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumOpCantHappen"));
+ }
+ return new DictionaryEntry(current.key, current.value);
+ }
+ }
+
+ public Object Key {
+ get {
+ if (current == null) {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumOpCantHappen"));
+ }
+ return current.key;
+ }
+ }
+
+ public Object Value {
+ get {
+ if (current == null) {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumOpCantHappen"));
+ }
+ return current.value;
+ }
+ }
+
+ public bool MoveNext() {
+ if (version != list.version) {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion"));
+ }
+ if (start) {
+ current = list.head;
+ start = false;
+ }
+ else {
+ if( current != null ) {
+ current = current.next;
+ }
+ }
+ return (current != null);
+ }
+
+ public void Reset() {
+ if (version != list.version) {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion"));
+ }
+ start = true;
+ current = null;
+ }
+
+ }
+
+
+ private class NodeKeyValueCollection : ICollection {
+ ListDictionaryInternal list;
+ bool isKeys;
+
+ public NodeKeyValueCollection(ListDictionaryInternal list, bool isKeys) {
+ this.list = list;
+ this.isKeys = isKeys;
+ }
+
+ void ICollection.CopyTo(Array array, int index) {
+ if (array==null)
+ throw new ArgumentNullException("array");
+ if (array.Rank != 1)
+ throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
+ if (index < 0)
+ throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ Contract.EndContractBlock();
+ if (array.Length - index < list.Count)
+ throw new ArgumentException(Environment.GetResourceString("ArgumentOutOfRange_Index"), "index");
+ for (DictionaryNode node = list.head; node != null; node = node.next) {
+ array.SetValue(isKeys ? node.key : node.value, index);
+ index++;
+ }
+ }
+
+ int ICollection.Count {
+ get {
+ int count = 0;
+ for (DictionaryNode node = list.head; node != null; node = node.next) {
+ count++;
+ }
+ return count;
+ }
+ }
+
+ bool ICollection.IsSynchronized {
+ get {
+ return false;
+ }
+ }
+
+ Object ICollection.SyncRoot {
+ get {
+ return list.SyncRoot;
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() {
+ return new NodeKeyValueEnumerator(list, isKeys);
+ }
+
+
+ private class NodeKeyValueEnumerator: IEnumerator {
+ ListDictionaryInternal list;
+ DictionaryNode current;
+ int version;
+ bool isKeys;
+ bool start;
+
+ public NodeKeyValueEnumerator(ListDictionaryInternal list, bool isKeys) {
+ this.list = list;
+ this.isKeys = isKeys;
+ this.version = list.version;
+ this.start = true;
+ this.current = null;
+ }
+
+ public Object Current {
+ get {
+ if (current == null) {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumOpCantHappen"));
+ }
+ return isKeys ? current.key : current.value;
+ }
+ }
+
+ public bool MoveNext() {
+ if (version != list.version) {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion"));
+ }
+ if (start) {
+ current = list.head;
+ start = false;
+ }
+ else {
+ if( current != null) {
+ current = current.next;
+ }
+ }
+ return (current != null);
+ }
+
+ public void Reset() {
+ if (version != list.version) {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion"));
+ }
+ start = true;
+ current = null;
+ }
+ }
+ }
+
+ [Serializable]
+ private class DictionaryNode {
+ public Object key;
+ public Object value;
+ public DictionaryNode next;
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/ObjectModel/Collection.cs b/src/mscorlib/src/System/Collections/ObjectModel/Collection.cs
new file mode 100644
index 0000000000..54aa7bb09d
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/ObjectModel/Collection.cs
@@ -0,0 +1,328 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+
+namespace System.Collections.ObjectModel
+{
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Runtime;
+
+ [Serializable]
+ [System.Runtime.InteropServices.ComVisible(false)]
+ [DebuggerTypeProxy(typeof(Mscorlib_CollectionDebugView<>))]
+ [DebuggerDisplay("Count = {Count}")]
+ public class Collection<T>: IList<T>, IList, IReadOnlyList<T>
+ {
+ IList<T> items;
+ [NonSerialized]
+ private Object _syncRoot;
+
+ public Collection() {
+ items = new List<T>();
+ }
+
+ public Collection(IList<T> list) {
+ if (list == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.list);
+ }
+ items = list;
+ }
+
+ public int Count {
+ get { return items.Count; }
+ }
+
+ protected IList<T> Items {
+ get { return items; }
+ }
+
+ public T this[int index] {
+ get { return items[index]; }
+ set {
+ if( items.IsReadOnly) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ if (index < 0 || index >= items.Count) {
+ ThrowHelper.ThrowArgumentOutOfRange_IndexException();
+ }
+
+ SetItem(index, value);
+ }
+ }
+
+ public void Add(T item) {
+ if( items.IsReadOnly) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ int index = items.Count;
+ InsertItem(index, item);
+ }
+
+ public void Clear() {
+ if( items.IsReadOnly) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ ClearItems();
+ }
+
+ public void CopyTo(T[] array, int index) {
+ items.CopyTo(array, index);
+ }
+
+ public bool Contains(T item) {
+ return items.Contains(item);
+ }
+
+ public IEnumerator<T> GetEnumerator() {
+ return items.GetEnumerator();
+ }
+
+ public int IndexOf(T item) {
+ return items.IndexOf(item);
+ }
+
+ public void Insert(int index, T item) {
+ if (items.IsReadOnly) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ if (index < 0 || index > items.Count) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_ListInsert);
+ }
+
+ InsertItem(index, item);
+ }
+
+ public bool Remove(T item) {
+ if( items.IsReadOnly) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ int index = items.IndexOf(item);
+ if (index < 0) return false;
+ RemoveItem(index);
+ return true;
+ }
+
+ public void RemoveAt(int index) {
+ if( items.IsReadOnly) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ if (index < 0 || index >= items.Count) {
+ ThrowHelper.ThrowArgumentOutOfRange_IndexException();
+ }
+
+ RemoveItem(index);
+ }
+
+ protected virtual void ClearItems() {
+ items.Clear();
+ }
+
+ protected virtual void InsertItem(int index, T item) {
+ items.Insert(index, item);
+ }
+
+ protected virtual void RemoveItem(int index) {
+ items.RemoveAt(index);
+ }
+
+ protected virtual void SetItem(int index, T item) {
+ items[index] = item;
+ }
+
+ bool ICollection<T>.IsReadOnly {
+ get {
+ return items.IsReadOnly;
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() {
+ return ((IEnumerable)items).GetEnumerator();
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return false; }
+ }
+
+ object ICollection.SyncRoot {
+ get {
+ if( _syncRoot == null) {
+ ICollection c = items as ICollection;
+ if( c != null) {
+ _syncRoot = c.SyncRoot;
+ }
+ else {
+ System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);
+ }
+ }
+ return _syncRoot;
+ }
+ }
+
+ void ICollection.CopyTo(Array array, int index) {
+ if (array == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ }
+
+ if (array.Rank != 1) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
+ }
+
+ if( array.GetLowerBound(0) != 0 ) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound);
+ }
+
+ if (index < 0 ) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
+
+ if (array.Length - index < Count) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
+ }
+
+ T[] tArray = array as T[];
+ if (tArray != null) {
+ items.CopyTo(tArray , index);
+ }
+ else {
+ //
+ // Catch the obvious case assignment will fail.
+ // We can found all possible problems by doing the check though.
+ // For example, if the element type of the Array is derived from T,
+ // we can't figure out if we can successfully copy the element beforehand.
+ //
+ Type targetType = array.GetType().GetElementType();
+ Type sourceType = typeof(T);
+ if(!(targetType.IsAssignableFrom(sourceType) || sourceType.IsAssignableFrom(targetType))) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
+ }
+
+ //
+ // We can't cast array of value type to object[], so we don't support
+ // widening of primitive types here.
+ //
+ object[] objects = array as object[];
+ if( objects == null) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
+ }
+
+ int count = items.Count;
+ try {
+ for (int i = 0; i < count; i++) {
+ objects[index++] = items[i];
+ }
+ }
+ catch(ArrayTypeMismatchException) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
+ }
+ }
+ }
+
+ object IList.this[int index] {
+ get { return items[index]; }
+ set {
+ ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(value, ExceptionArgument.value);
+
+ try {
+ this[index] = (T)value;
+ }
+ catch (InvalidCastException) {
+ ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T));
+ }
+
+ }
+ }
+
+ bool IList.IsReadOnly {
+ get {
+ return items.IsReadOnly;
+ }
+ }
+
+ bool IList.IsFixedSize {
+ get {
+ // There is no IList<T>.IsFixedSize, so we must assume that only
+ // readonly collections are fixed size, if our internal item
+ // collection does not implement IList. Note that Array implements
+ // IList, and therefore T[] and U[] will be fixed-size.
+ IList list = items as IList;
+ if(list != null)
+ {
+ return list.IsFixedSize;
+ }
+ return items.IsReadOnly;
+ }
+ }
+
+ int IList.Add(object value) {
+ if( items.IsReadOnly) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+ ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(value, ExceptionArgument.value);
+
+ try {
+ Add((T)value);
+ }
+ catch (InvalidCastException) {
+ ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T));
+ }
+
+ return this.Count - 1;
+ }
+
+ bool IList.Contains(object value) {
+ if(IsCompatibleObject(value)) {
+ return Contains((T) value);
+ }
+ return false;
+ }
+
+ int IList.IndexOf(object value) {
+ if(IsCompatibleObject(value)) {
+ return IndexOf((T)value);
+ }
+ return -1;
+ }
+
+ void IList.Insert(int index, object value) {
+ if( items.IsReadOnly) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+ ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(value, ExceptionArgument.value);
+
+ try {
+ Insert(index, (T)value);
+ }
+ catch (InvalidCastException) {
+ ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T));
+ }
+
+ }
+
+ void IList.Remove(object value) {
+ if( items.IsReadOnly) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ if(IsCompatibleObject(value)) {
+ Remove((T) value);
+ }
+ }
+
+ private static bool IsCompatibleObject(object value) {
+ // Non-null values are fine. Only accept nulls if T is a class or Nullable<U>.
+ // Note that default(T) is not equal to null for value types except when T is Nullable<U>.
+ return ((value is T) || (value == null && default(T) == null));
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/ObjectModel/KeyedCollection.cs b/src/mscorlib/src/System/Collections/ObjectModel/KeyedCollection.cs
new file mode 100644
index 0000000000..7313d71950
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/ObjectModel/KeyedCollection.cs
@@ -0,0 +1,245 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+
+namespace System.Collections.ObjectModel
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Diagnostics.Contracts;
+
+ [Serializable]
+ [System.Runtime.InteropServices.ComVisible(false)]
+ [DebuggerTypeProxy(typeof(Mscorlib_KeyedCollectionDebugView<,>))]
+ [DebuggerDisplay("Count = {Count}")]
+ public abstract class KeyedCollection<TKey,TItem>: Collection<TItem>
+ {
+ const int defaultThreshold = 0;
+
+ IEqualityComparer<TKey> comparer;
+ Dictionary<TKey,TItem> dict;
+ int keyCount;
+ int threshold;
+
+ protected KeyedCollection(): this(null, defaultThreshold) {}
+
+ protected KeyedCollection(IEqualityComparer<TKey> comparer): this(comparer, defaultThreshold) {}
+
+
+ protected KeyedCollection(IEqualityComparer<TKey> comparer, int dictionaryCreationThreshold)
+ : base(new List<TItem>()) { // Be explicit about the use of List<T> so we can foreach over
+ // Items internally without enumerator allocations.
+ if (comparer == null) {
+ comparer = EqualityComparer<TKey>.Default;
+ }
+
+ if (dictionaryCreationThreshold == -1) {
+ dictionaryCreationThreshold = int.MaxValue;
+ }
+
+ if( dictionaryCreationThreshold < -1) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.dictionaryCreationThreshold, ExceptionResource.ArgumentOutOfRange_InvalidThreshold);
+ }
+
+ this.comparer = comparer;
+ this.threshold = dictionaryCreationThreshold;
+ }
+
+ /// <summary>
+ /// Enables the use of foreach internally without allocations using <see cref="List{T}"/>'s struct enumerator.
+ /// </summary>
+ new private List<TItem> Items {
+ get {
+ Contract.Assert(base.Items is List<TItem>);
+
+ return (List<TItem>)base.Items;
+ }
+ }
+
+ public IEqualityComparer<TKey> Comparer {
+ get {
+ return comparer;
+ }
+ }
+
+ public TItem this[TKey key] {
+ get {
+ if( key == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+ }
+
+ if (dict != null) {
+ return dict[key];
+ }
+
+ foreach (TItem item in Items) {
+ if (comparer.Equals(GetKeyForItem(item), key)) return item;
+ }
+
+ ThrowHelper.ThrowKeyNotFoundException();
+ return default(TItem);
+ }
+ }
+
+ public bool Contains(TKey key) {
+ if( key == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+ }
+
+ if (dict != null) {
+ return dict.ContainsKey(key);
+ }
+
+ foreach (TItem item in Items) {
+ if (comparer.Equals(GetKeyForItem(item), key)) return true;
+ }
+ return false;
+ }
+
+ private bool ContainsItem(TItem item) {
+ TKey key;
+ if( (dict == null) || ((key = GetKeyForItem(item)) == null)) {
+ return Items.Contains(item);
+ }
+
+ TItem itemInDict;
+ bool exist = dict.TryGetValue(key, out itemInDict);
+ if( exist) {
+ return EqualityComparer<TItem>.Default.Equals(itemInDict, item);
+ }
+ return false;
+ }
+
+ public bool Remove(TKey key) {
+ if( key == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+ }
+
+ if (dict != null) {
+ if (dict.ContainsKey(key)) {
+ return Remove(dict[key]);
+ }
+
+ return false;
+ }
+
+ for (int i = 0; i < Items.Count; i++) {
+ if (comparer.Equals(GetKeyForItem(Items[i]), key)) {
+ RemoveItem(i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected IDictionary<TKey,TItem> Dictionary {
+ get { return dict; }
+ }
+
+ protected void ChangeItemKey(TItem item, TKey newKey) {
+ // check if the item exists in the collection
+ if( !ContainsItem(item)) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_ItemNotExist);
+ }
+
+ TKey oldKey = GetKeyForItem(item);
+ if (!comparer.Equals(oldKey, newKey)) {
+ if (newKey != null) {
+ AddKey(newKey, item);
+ }
+
+ if (oldKey != null) {
+ RemoveKey(oldKey);
+ }
+ }
+ }
+
+ protected override void ClearItems() {
+ base.ClearItems();
+ if (dict != null) {
+ dict.Clear();
+ }
+
+ keyCount = 0;
+ }
+
+ protected abstract TKey GetKeyForItem(TItem item);
+
+ protected override void InsertItem(int index, TItem item) {
+ TKey key = GetKeyForItem(item);
+ if (key != null) {
+ AddKey(key, item);
+ }
+ base.InsertItem(index, item);
+ }
+
+ protected override void RemoveItem(int index) {
+ TKey key = GetKeyForItem(Items[index]);
+ if (key != null) {
+ RemoveKey(key);
+ }
+ base.RemoveItem(index);
+ }
+
+ protected override void SetItem(int index, TItem item) {
+ TKey newKey = GetKeyForItem(item);
+ TKey oldKey = GetKeyForItem(Items[index]);
+
+ if (comparer.Equals(oldKey, newKey)) {
+ if (newKey != null && dict != null) {
+ dict[newKey] = item;
+ }
+ }
+ else {
+ if (newKey != null) {
+ AddKey(newKey, item);
+ }
+
+ if (oldKey != null) {
+ RemoveKey(oldKey);
+ }
+ }
+ base.SetItem(index, item);
+ }
+
+ private void AddKey(TKey key, TItem item) {
+ if (dict != null) {
+ dict.Add(key, item);
+ }
+ else if (keyCount == threshold) {
+ CreateDictionary();
+ dict.Add(key, item);
+ }
+ else {
+ if (Contains(key)) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate);
+ }
+
+ keyCount++;
+ }
+ }
+
+ private void CreateDictionary() {
+ dict = new Dictionary<TKey,TItem>(comparer);
+ foreach (TItem item in Items) {
+ TKey key = GetKeyForItem(item);
+ if (key != null) {
+ dict.Add(key, item);
+ }
+ }
+ }
+
+ private void RemoveKey(TKey key) {
+ Contract.Assert(key != null, "key shouldn't be null!");
+ if (dict != null) {
+ dict.Remove(key);
+ }
+ else {
+ keyCount--;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs b/src/mscorlib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs
new file mode 100644
index 0000000000..ec7149e4f5
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs
@@ -0,0 +1,232 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+
+namespace System.Collections.ObjectModel
+{
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Runtime;
+
+ [Serializable]
+ [System.Runtime.InteropServices.ComVisible(false)]
+ [DebuggerTypeProxy(typeof(Mscorlib_CollectionDebugView<>))]
+ [DebuggerDisplay("Count = {Count}")]
+ public class ReadOnlyCollection<T>: IList<T>, IList, IReadOnlyList<T>
+ {
+ IList<T> list;
+ [NonSerialized]
+ private Object _syncRoot;
+
+ public ReadOnlyCollection(IList<T> list) {
+ if (list == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.list);
+ }
+ this.list = list;
+ }
+
+ public int Count {
+ get { return list.Count; }
+ }
+
+ public T this[int index] {
+ get { return list[index]; }
+ }
+
+ public bool Contains(T value) {
+ return list.Contains(value);
+ }
+
+ public void CopyTo(T[] array, int index) {
+ list.CopyTo(array, index);
+ }
+
+ public IEnumerator<T> GetEnumerator() {
+ return list.GetEnumerator();
+ }
+
+ public int IndexOf(T value) {
+ return list.IndexOf(value);
+ }
+
+ protected IList<T> Items {
+ get {
+ return list;
+ }
+ }
+
+ bool ICollection<T>.IsReadOnly {
+ get { return true; }
+ }
+
+ T IList<T>.this[int index] {
+ get { return list[index]; }
+ set {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+ }
+
+ void ICollection<T>.Add(T value) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ void ICollection<T>.Clear() {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ void IList<T>.Insert(int index, T value) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ bool ICollection<T>.Remove(T value) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ return false;
+ }
+
+ void IList<T>.RemoveAt(int index) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() {
+ return ((IEnumerable)list).GetEnumerator();
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return false; }
+ }
+
+ object ICollection.SyncRoot {
+ get {
+ if( _syncRoot == null) {
+ ICollection c = list as ICollection;
+ if( c != null) {
+ _syncRoot = c.SyncRoot;
+ }
+ else {
+ System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);
+ }
+ }
+ return _syncRoot;
+ }
+ }
+
+ void ICollection.CopyTo(Array array, int index) {
+ if (array==null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ }
+
+ if (array.Rank != 1) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
+ }
+
+ if( array.GetLowerBound(0) != 0 ) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound);
+ }
+
+ if (index < 0) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.arrayIndex, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
+
+ if (array.Length - index < Count) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
+ }
+
+ T[] items = array as T[];
+ if (items != null) {
+ list.CopyTo(items, index);
+ }
+ else {
+ //
+ // Catch the obvious case assignment will fail.
+ // We can found all possible problems by doing the check though.
+ // For example, if the element type of the Array is derived from T,
+ // we can't figure out if we can successfully copy the element beforehand.
+ //
+ Type targetType = array.GetType().GetElementType();
+ Type sourceType = typeof(T);
+ if(!(targetType.IsAssignableFrom(sourceType) || sourceType.IsAssignableFrom(targetType))) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
+ }
+
+ //
+ // We can't cast array of value type to object[], so we don't support
+ // widening of primitive types here.
+ //
+ object[] objects = array as object[];
+ if( objects == null) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
+ }
+
+ int count = list.Count;
+ try {
+ for (int i = 0; i < count; i++) {
+ objects[index++] = list[i];
+ }
+ }
+ catch(ArrayTypeMismatchException) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
+ }
+ }
+ }
+
+ bool IList.IsFixedSize {
+ get { return true; }
+ }
+
+ bool IList.IsReadOnly {
+ get { return true; }
+ }
+
+ object IList.this[int index] {
+ get { return list[index]; }
+ set {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+ }
+
+ int IList.Add(object value) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ return -1;
+ }
+
+ void IList.Clear() {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ private static bool IsCompatibleObject(object value) {
+ // Non-null values are fine. Only accept nulls if T is a class or Nullable<U>.
+ // Note that default(T) is not equal to null for value types except when T is Nullable<U>.
+ return ((value is T) || (value == null && default(T) == null));
+ }
+
+ bool IList.Contains(object value) {
+ if(IsCompatibleObject(value)) {
+ return Contains((T) value);
+ }
+ return false;
+ }
+
+ int IList.IndexOf(object value) {
+ if(IsCompatibleObject(value)) {
+ return IndexOf((T)value);
+ }
+ return -1;
+ }
+
+ void IList.Insert(int index, object value) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ void IList.Remove(object value) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ void IList.RemoveAt(int index) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/ObjectModel/ReadOnlyDictionary.cs b/src/mscorlib/src/System/Collections/ObjectModel/ReadOnlyDictionary.cs
new file mode 100644
index 0000000000..11833c2c1b
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/ObjectModel/ReadOnlyDictionary.cs
@@ -0,0 +1,625 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+** Purpose: Read-only wrapper for another generic dictionary.
+**
+===========================================================*/
+
+namespace System.Collections.ObjectModel
+{
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Diagnostics.Contracts;
+
+ [Serializable]
+ [DebuggerTypeProxy(typeof(Mscorlib_DictionaryDebugView<,>))]
+ [DebuggerDisplay("Count = {Count}")]
+ public class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, IReadOnlyDictionary<TKey, TValue>
+ {
+ private readonly IDictionary<TKey, TValue> m_dictionary;
+ [NonSerialized]
+ private Object m_syncRoot;
+ [NonSerialized]
+ private KeyCollection m_keys;
+ [NonSerialized]
+ private ValueCollection m_values;
+
+ public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary) {
+ if (dictionary == null) {
+ throw new ArgumentNullException("dictionary");
+ }
+ Contract.EndContractBlock();
+ m_dictionary = dictionary;
+ }
+
+ protected IDictionary<TKey, TValue> Dictionary {
+ get { return m_dictionary; }
+ }
+
+ public KeyCollection Keys {
+ get {
+ Contract.Ensures(Contract.Result<KeyCollection>() != null);
+ if (m_keys == null) {
+ m_keys = new KeyCollection(m_dictionary.Keys);
+ }
+ return m_keys;
+ }
+ }
+
+ public ValueCollection Values {
+ get {
+ Contract.Ensures(Contract.Result<ValueCollection>() != null);
+ if (m_values == null) {
+ m_values = new ValueCollection(m_dictionary.Values);
+ }
+ return m_values;
+ }
+ }
+
+ #region IDictionary<TKey, TValue> Members
+
+ public bool ContainsKey(TKey key) {
+ return m_dictionary.ContainsKey(key);
+ }
+
+ ICollection<TKey> IDictionary<TKey, TValue>.Keys {
+ get {
+ return Keys;
+ }
+ }
+
+ public bool TryGetValue(TKey key, out TValue value) {
+ return m_dictionary.TryGetValue(key, out value);
+ }
+
+ ICollection<TValue> IDictionary<TKey, TValue>.Values {
+ get {
+ return Values;
+ }
+ }
+
+ public TValue this[TKey key] {
+ get {
+ return m_dictionary[key];
+ }
+ }
+
+ void IDictionary<TKey, TValue>.Add(TKey key, TValue value) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ bool IDictionary<TKey, TValue>.Remove(TKey key) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ return false;
+ }
+
+ TValue IDictionary<TKey, TValue>.this[TKey key] {
+ get {
+ return m_dictionary[key];
+ }
+ set {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+ }
+
+ #endregion
+
+ #region ICollection<KeyValuePair<TKey, TValue>> Members
+
+ public int Count {
+ get { return m_dictionary.Count; }
+ }
+
+ bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) {
+ return m_dictionary.Contains(item);
+ }
+
+ void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
+ m_dictionary.CopyTo(array, arrayIndex);
+ }
+
+ bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly {
+ get { return true; }
+ }
+
+ void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ void ICollection<KeyValuePair<TKey, TValue>>.Clear() {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ return false;
+ }
+
+ #endregion
+
+ #region IEnumerable<KeyValuePair<TKey, TValue>> Members
+
+ public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {
+ return m_dictionary.GetEnumerator();
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
+ return ((IEnumerable)m_dictionary).GetEnumerator();
+ }
+
+ #endregion
+
+ #region IDictionary Members
+
+ private static bool IsCompatibleKey(object key) {
+ if (key == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+ }
+ return key is TKey;
+ }
+
+ void IDictionary.Add(object key, object value) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ void IDictionary.Clear() {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ bool IDictionary.Contains(object key) {
+ return IsCompatibleKey(key) && ContainsKey((TKey)key);
+ }
+
+ IDictionaryEnumerator IDictionary.GetEnumerator() {
+ IDictionary d = m_dictionary as IDictionary;
+ if (d != null) {
+ return d.GetEnumerator();
+ }
+ return new DictionaryEnumerator(m_dictionary);
+ }
+
+ bool IDictionary.IsFixedSize {
+ get { return true; }
+ }
+
+ bool IDictionary.IsReadOnly {
+ get { return true; }
+ }
+
+ ICollection IDictionary.Keys {
+ get {
+ return Keys;
+ }
+ }
+
+ void IDictionary.Remove(object key) {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ ICollection IDictionary.Values {
+ get {
+ return Values;
+ }
+ }
+
+ object IDictionary.this[object key] {
+ get {
+ if (IsCompatibleKey(key)) {
+ return this[(TKey)key];
+ }
+ return null;
+ }
+ set {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+ }
+
+ void ICollection.CopyTo(Array array, int index) {
+ if (array == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ }
+
+ if (array.Rank != 1) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
+ }
+
+ if (array.GetLowerBound(0) != 0) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound);
+ }
+
+ if (index < 0 || index > array.Length) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
+
+ if (array.Length - index < Count) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
+ }
+
+ KeyValuePair<TKey, TValue>[] pairs = array as KeyValuePair<TKey, TValue>[];
+ if (pairs != null) {
+ m_dictionary.CopyTo(pairs, index);
+ }
+ else {
+ DictionaryEntry[] dictEntryArray = array as DictionaryEntry[];
+ if (dictEntryArray != null) {
+ foreach (var item in m_dictionary) {
+ dictEntryArray[index++] = new DictionaryEntry(item.Key, item.Value);
+ }
+ }
+ else {
+ object[] objects = array as object[];
+ if (objects == null) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
+ }
+
+ try {
+ foreach (var item in m_dictionary) {
+ objects[index++] = new KeyValuePair<TKey, TValue>(item.Key, item.Value);
+ }
+ }
+ catch (ArrayTypeMismatchException) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
+ }
+ }
+ }
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return false; }
+ }
+
+ object ICollection.SyncRoot {
+ get {
+ if (m_syncRoot == null) {
+ ICollection c = m_dictionary as ICollection;
+ if (c != null) {
+ m_syncRoot = c.SyncRoot;
+ }
+ else {
+ System.Threading.Interlocked.CompareExchange<Object>(ref m_syncRoot, new Object(), null);
+ }
+ }
+ return m_syncRoot;
+ }
+ }
+
+ [Serializable]
+ private struct DictionaryEnumerator : IDictionaryEnumerator {
+ private readonly IDictionary<TKey, TValue> m_dictionary;
+ private IEnumerator<KeyValuePair<TKey, TValue>> m_enumerator;
+
+ public DictionaryEnumerator(IDictionary<TKey, TValue> dictionary) {
+ m_dictionary = dictionary;
+ m_enumerator = m_dictionary.GetEnumerator();
+ }
+
+ public DictionaryEntry Entry {
+ get { return new DictionaryEntry(m_enumerator.Current.Key, m_enumerator.Current.Value); }
+ }
+
+ public object Key {
+ get { return m_enumerator.Current.Key; }
+ }
+
+ public object Value {
+ get { return m_enumerator.Current.Value; }
+ }
+
+ public object Current {
+ get { return Entry; }
+ }
+
+ public bool MoveNext() {
+ return m_enumerator.MoveNext();
+ }
+
+ public void Reset() {
+ m_enumerator.Reset();
+ }
+ }
+
+ #endregion
+
+ #region IReadOnlyDictionary members
+
+ IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys {
+ get {
+ return Keys;
+ }
+ }
+
+ IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values {
+ get {
+ return Values;
+ }
+ }
+
+ #endregion IReadOnlyDictionary members
+
+ [DebuggerTypeProxy(typeof(Mscorlib_CollectionDebugView<>))]
+ [DebuggerDisplay("Count = {Count}")]
+ [Serializable]
+ public sealed class KeyCollection : ICollection<TKey>, ICollection, IReadOnlyCollection<TKey> {
+ private readonly ICollection<TKey> m_collection;
+ [NonSerialized]
+ private Object m_syncRoot;
+
+ internal KeyCollection(ICollection<TKey> collection)
+ {
+ if (collection == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
+ }
+ m_collection = collection;
+ }
+
+ #region ICollection<T> Members
+
+ void ICollection<TKey>.Add(TKey item)
+ {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ void ICollection<TKey>.Clear()
+ {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ bool ICollection<TKey>.Contains(TKey item)
+ {
+ return m_collection.Contains(item);
+ }
+
+ public void CopyTo(TKey[] array, int arrayIndex)
+ {
+ m_collection.CopyTo(array, arrayIndex);
+ }
+
+ public int Count {
+ get { return m_collection.Count; }
+ }
+
+ bool ICollection<TKey>.IsReadOnly {
+ get { return true; }
+ }
+
+ bool ICollection<TKey>.Remove(TKey item)
+ {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ return false;
+ }
+
+ #endregion
+
+ #region IEnumerable<T> Members
+
+ public IEnumerator<TKey> GetEnumerator()
+ {
+ return m_collection.GetEnumerator();
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
+ return ((IEnumerable)m_collection).GetEnumerator();
+ }
+
+ #endregion
+
+ #region ICollection Members
+
+ void ICollection.CopyTo(Array array, int index) {
+ ReadOnlyDictionaryHelpers.CopyToNonGenericICollectionHelper<TKey>(m_collection, array, index);
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return false; }
+ }
+
+ object ICollection.SyncRoot {
+ get {
+ if (m_syncRoot == null) {
+ ICollection c = m_collection as ICollection;
+ if (c != null) {
+ m_syncRoot = c.SyncRoot;
+ }
+ else {
+ System.Threading.Interlocked.CompareExchange<Object>(ref m_syncRoot, new Object(), null);
+ }
+ }
+ return m_syncRoot;
+ }
+ }
+
+ #endregion
+ }
+
+ [DebuggerTypeProxy(typeof(Mscorlib_CollectionDebugView<>))]
+ [DebuggerDisplay("Count = {Count}")]
+ [Serializable]
+ public sealed class ValueCollection : ICollection<TValue>, ICollection, IReadOnlyCollection<TValue> {
+ private readonly ICollection<TValue> m_collection;
+ [NonSerialized]
+ private Object m_syncRoot;
+
+ internal ValueCollection(ICollection<TValue> collection)
+ {
+ if (collection == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
+ }
+ m_collection = collection;
+ }
+
+ #region ICollection<T> Members
+
+ void ICollection<TValue>.Add(TValue item)
+ {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ void ICollection<TValue>.Clear()
+ {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ }
+
+ bool ICollection<TValue>.Contains(TValue item)
+ {
+ return m_collection.Contains(item);
+ }
+
+ public void CopyTo(TValue[] array, int arrayIndex)
+ {
+ m_collection.CopyTo(array, arrayIndex);
+ }
+
+ public int Count {
+ get { return m_collection.Count; }
+ }
+
+ bool ICollection<TValue>.IsReadOnly {
+ get { return true; }
+ }
+
+ bool ICollection<TValue>.Remove(TValue item)
+ {
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
+ return false;
+ }
+
+ #endregion
+
+ #region IEnumerable<T> Members
+
+ public IEnumerator<TValue> GetEnumerator()
+ {
+ return m_collection.GetEnumerator();
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
+ return ((IEnumerable)m_collection).GetEnumerator();
+ }
+
+ #endregion
+
+ #region ICollection Members
+
+ void ICollection.CopyTo(Array array, int index) {
+ ReadOnlyDictionaryHelpers.CopyToNonGenericICollectionHelper<TValue>(m_collection, array, index);
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return false; }
+ }
+
+ object ICollection.SyncRoot {
+ get {
+ if (m_syncRoot == null) {
+ ICollection c = m_collection as ICollection;
+ if (c != null) {
+ m_syncRoot = c.SyncRoot;
+ }
+ else {
+ System.Threading.Interlocked.CompareExchange<Object>(ref m_syncRoot, new Object(), null);
+ }
+ }
+ return m_syncRoot;
+ }
+ }
+
+ #endregion ICollection Members
+ }
+ }
+
+ // To share code when possible, use a non-generic class to get rid of irrelevant type parameters.
+ internal static class ReadOnlyDictionaryHelpers
+ {
+ #region Helper method for our KeyCollection and ValueCollection
+
+ // Abstracted away to avoid redundant implementations.
+ internal static void CopyToNonGenericICollectionHelper<T>(ICollection<T> collection, Array array, int index)
+ {
+ if (array == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ }
+
+ if (array.Rank != 1) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
+ }
+
+ if (array.GetLowerBound(0) != 0) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound);
+ }
+
+ if (index < 0) {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.arrayIndex, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
+
+ if (array.Length - index < collection.Count) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
+ }
+
+ // Easy out if the ICollection<T> implements the non-generic ICollection
+ ICollection nonGenericCollection = collection as ICollection;
+ if (nonGenericCollection != null) {
+ nonGenericCollection.CopyTo(array, index);
+ return;
+ }
+
+ T[] items = array as T[];
+ if (items != null) {
+ collection.CopyTo(items, index);
+ }
+ else {
+ //
+ // Catch the obvious case assignment will fail.
+ // We can found all possible problems by doing the check though.
+ // For example, if the element type of the Array is derived from T,
+ // we can't figure out if we can successfully copy the element beforehand.
+ //
+ Type targetType = array.GetType().GetElementType();
+ Type sourceType = typeof(T);
+ if (!(targetType.IsAssignableFrom(sourceType) || sourceType.IsAssignableFrom(targetType))) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
+ }
+
+ //
+ // We can't cast array of value type to object[], so we don't support
+ // widening of primitive types here.
+ //
+ object[] objects = array as object[];
+ if (objects == null) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
+ }
+
+ try {
+ foreach (var item in collection) {
+ objects[index++] = item;
+ }
+ }
+ catch (ArrayTypeMismatchException) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
+ }
+ }
+ }
+
+ #endregion Helper method for our KeyCollection and ValueCollection
+ }
+}
+
diff --git a/src/mscorlib/src/System/Collections/SortedList.cs b/src/mscorlib/src/System/Collections/SortedList.cs
new file mode 100644
index 0000000000..8e3926af01
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/SortedList.cs
@@ -0,0 +1,1011 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+** Purpose: A sorted dictionary.
+**
+**
+===========================================================*/
+namespace System.Collections {
+ using System;
+ using System.Security.Permissions;
+ using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+ using System.Globalization;
+
+ // The SortedList class implements a sorted list of keys and values. Entries in
+ // a sorted list are sorted by their keys and are accessible both by key and by
+ // index. The keys of a sorted list can be ordered either according to a
+ // specific IComparer implementation given when the sorted list is
+ // instantiated, or according to the IComparable implementation provided
+ // by the keys themselves. In either case, a sorted list does not allow entries
+ // with duplicate keys.
+ //
+ // A sorted list internally maintains two arrays that store the keys and
+ // values of the entries. The capacity of a sorted list is the allocated
+ // length of these internal arrays. As elements are added to a sorted list, the
+ // capacity of the sorted list is automatically increased as required by
+ // reallocating the internal arrays. The capacity is never automatically
+ // decreased, but users can call either TrimToSize or
+ // Capacity explicitly.
+ //
+ // The GetKeyList and GetValueList methods of a sorted list
+ // provides access to the keys and values of the sorted list in the form of
+ // List implementations. The List objects returned by these
+ // methods are aliases for the underlying sorted list, so modifications
+ // made to those lists are directly reflected in the sorted list, and vice
+ // versa.
+ //
+ // The SortedList class provides a convenient way to create a sorted
+ // copy of another dictionary, such as a Hashtable. For example:
+ //
+ // Hashtable h = new Hashtable();
+ // h.Add(...);
+ // h.Add(...);
+ // ...
+ // SortedList s = new SortedList(h);
+ //
+ // The last line above creates a sorted list that contains a copy of the keys
+ // and values stored in the hashtable. In this particular example, the keys
+ // will be ordered according to the IComparable interface, which they
+ // all must implement. To impose a different ordering, SortedList also
+ // has a constructor that allows a specific IComparer implementation to
+ // be specified.
+ //
+ [DebuggerTypeProxy(typeof(System.Collections.SortedList.SortedListDebugView))]
+ [DebuggerDisplay("Count = {Count}")]
+[System.Runtime.InteropServices.ComVisible(true)]
+#if FEATURE_CORECLR
+ [Obsolete("Non-generic collections have been deprecated. Please use collections in System.Collections.Generic.")]
+#endif
+ [Serializable]
+ public class SortedList : IDictionary, ICloneable
+ {
+ private Object[] keys;
+ private Object[] values;
+ private int _size;
+ private int version;
+ private IComparer comparer;
+ private KeyList keyList;
+ private ValueList valueList;
+ [NonSerialized]
+ private Object _syncRoot;
+
+ private const int _defaultCapacity = 16;
+
+ private static Object[] emptyArray = EmptyArray<Object>.Value;
+
+ // Constructs a new sorted list. The sorted list is initially empty and has
+ // a capacity of zero. Upon adding the first element to the sorted list the
+ // capacity is increased to 16, and then increased in multiples of two as
+ // required. The elements of the sorted list are ordered according to the
+ // IComparable interface, which must be implemented by the keys of
+ // all entries added to the sorted list.
+ public SortedList() {
+ Init();
+ }
+ private void Init()
+ {
+ keys = emptyArray;
+ values = emptyArray;
+ _size = 0;
+ comparer = new Comparer(CultureInfo.CurrentCulture);
+ }
+
+ // Constructs a new sorted list. The sorted list is initially empty and has
+ // a capacity of zero. Upon adding the first element to the sorted list the
+ // capacity is increased to 16, and then increased in multiples of two as
+ // required. The elements of the sorted list are ordered according to the
+ // IComparable interface, which must be implemented by the keys of
+ // all entries added to the sorted list.
+ //
+ public SortedList(int initialCapacity) {
+ if (initialCapacity < 0)
+ throw new ArgumentOutOfRangeException("initialCapacity", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ Contract.EndContractBlock();
+ keys = new Object[initialCapacity];
+ values = new Object[initialCapacity];
+ comparer = new Comparer(CultureInfo.CurrentCulture);
+ }
+
+ // Constructs a new sorted list with a given IComparer
+ // implementation. The sorted list is initially empty and has a capacity of
+ // zero. Upon adding the first element to the sorted list the capacity is
+ // increased to 16, and then increased in multiples of two as required. The
+ // elements of the sorted list are ordered according to the given
+ // IComparer implementation. If comparer is null, the
+ // elements are compared to each other using the IComparable
+ // interface, which in that case must be implemented by the keys of all
+ // entries added to the sorted list.
+ //
+ public SortedList(IComparer comparer)
+ : this() {
+ if (comparer != null) this.comparer = comparer;
+ }
+
+ // Constructs a new sorted list with a given IComparer
+ // implementation and a given initial capacity. The sorted list is
+ // initially empty, but will have room for the given number of elements
+ // before any reallocations are required. The elements of the sorted list
+ // are ordered according to the given IComparer implementation. If
+ // comparer is null, the elements are compared to each other using
+ // the IComparable interface, which in that case must be implemented
+ // by the keys of all entries added to the sorted list.
+ //
+ public SortedList(IComparer comparer, int capacity)
+ : this(comparer) {
+ Capacity = capacity;
+ }
+
+ // Constructs a new sorted list containing a copy of the entries in the
+ // given dictionary. The elements of the sorted list are ordered according
+ // to the IComparable interface, which must be implemented by the
+ // keys of all entries in the the given dictionary as well as keys
+ // subsequently added to the sorted list.
+ //
+ public SortedList(IDictionary d)
+ : this(d, null) {
+ }
+
+ // Constructs a new sorted list containing a copy of the entries in the
+ // given dictionary. The elements of the sorted list are ordered according
+ // to the given IComparer implementation. If comparer is
+ // null, the elements are compared to each other using the
+ // IComparable interface, which in that case must be implemented
+ // by the keys of all entries in the the given dictionary as well as keys
+ // subsequently added to the sorted list.
+ //
+ public SortedList(IDictionary d, IComparer comparer)
+ : this(comparer, (d != null ? d.Count : 0)) {
+ if (d==null)
+ throw new ArgumentNullException("d", Environment.GetResourceString("ArgumentNull_Dictionary"));
+ Contract.EndContractBlock();
+ d.Keys.CopyTo(keys, 0);
+ d.Values.CopyTo(values, 0);
+ Array.Sort(keys, values, comparer);
+ _size = d.Count;
+ }
+
+ // Adds an entry with the given key and value to this sorted list. An
+ // ArgumentException is thrown if the key is already present in the sorted list.
+ //
+ public virtual void Add(Object key, Object value) {
+ if (key == null) throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
+ Contract.EndContractBlock();
+ int i = Array.BinarySearch(keys, 0, _size, key, comparer);
+ if (i >= 0)
+ throw new ArgumentException(Environment.GetResourceString("Argument_AddingDuplicate__", GetKey(i), key));
+ Insert(~i, key, value);
+ }
+
+ // Returns the capacity of this sorted list. The capacity of a sorted list
+ // represents the allocated length of the internal arrays used to store the
+ // keys and values of the list, and thus also indicates the maximum number
+ // of entries the list can contain before a reallocation of the internal
+ // arrays is required.
+ //
+ public virtual int Capacity {
+ get {
+ return keys.Length;
+ }
+ set {
+ if (value < Count) {
+ throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
+ }
+ Contract.EndContractBlock();
+
+ if (value != keys.Length) {
+ if (value > 0) {
+ Object[] newKeys = new Object[value];
+ Object[] newValues = new Object[value];
+ if (_size > 0) {
+ Array.Copy(keys, 0, newKeys, 0, _size);
+ Array.Copy(values, 0, newValues, 0, _size);
+ }
+ keys = newKeys;
+ values = newValues;
+ }
+ else {
+ // size can only be zero here.
+ Contract.Assert( _size == 0, "Size is not zero");
+ keys = emptyArray;
+ values = emptyArray;
+ }
+ }
+ }
+ }
+
+ // Returns the number of entries in this sorted list.
+ //
+ public virtual int Count {
+ get {
+ return _size;
+ }
+ }
+
+ // Returns a collection representing the keys of this sorted list. This
+ // method returns the same object as GetKeyList, but typed as an
+ // ICollection instead of an IList.
+ //
+ public virtual ICollection Keys {
+ get {
+ return GetKeyList();
+ }
+ }
+
+ // Returns a collection representing the values of this sorted list. This
+ // method returns the same object as GetValueList, but typed as an
+ // ICollection instead of an IList.
+ //
+ public virtual ICollection Values {
+ get {
+ return GetValueList();
+ }
+ }
+
+ // Is this SortedList read-only?
+ public virtual bool IsReadOnly {
+ get { return false; }
+ }
+
+ public virtual bool IsFixedSize {
+ get { return false; }
+ }
+
+ // Is this SortedList synchronized (thread-safe)?
+ public virtual bool IsSynchronized {
+ get { return false; }
+ }
+
+ // Synchronization root for this object.
+ public virtual Object SyncRoot {
+ get {
+ if( _syncRoot == null) {
+ System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);
+ }
+ return _syncRoot;
+ }
+ }
+
+ // Removes all entries from this sorted list.
+ public virtual void Clear() {
+ // clear does not change the capacity
+ version++;
+ Array.Clear(keys, 0, _size); // Don't need to doc this but we clear the elements so that the gc can reclaim the references.
+ Array.Clear(values, 0, _size); // Don't need to doc this but we clear the elements so that the gc can reclaim the references.
+ _size = 0;
+
+ }
+
+ // Makes a virtually identical copy of this SortedList. This is a shallow
+ // copy. IE, the Objects in the SortedList are not cloned - we copy the
+ // references to those objects.
+ public virtual Object Clone()
+ {
+ SortedList sl = new SortedList(_size);
+ Array.Copy(keys, 0, sl.keys, 0, _size);
+ Array.Copy(values, 0, sl.values, 0, _size);
+ sl._size = _size;
+ sl.version = version;
+ sl.comparer = comparer;
+ // Don't copy keyList nor valueList.
+ return sl;
+ }
+
+
+ // Checks if this sorted list contains an entry with the given key.
+ //
+ public virtual bool Contains(Object key) {
+ return IndexOfKey(key) >= 0;
+ }
+
+ // Checks if this sorted list contains an entry with the given key.
+ //
+ public virtual bool ContainsKey(Object key) {
+ // Yes, this is a SPEC'ed duplicate of Contains().
+ return IndexOfKey(key) >= 0;
+ }
+
+ // Checks if this sorted list contains an entry with the given value. The
+ // values of the entries of the sorted list are compared to the given value
+ // using the Object.Equals method. This method performs a linear
+ // search and is substantially slower than the Contains
+ // method.
+ //
+ public virtual bool ContainsValue(Object value) {
+ return IndexOfValue(value) >= 0;
+ }
+
+ // Copies the values in this SortedList to an array.
+ public virtual void CopyTo(Array array, int arrayIndex) {
+ if (array == null)
+ throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Array"));
+ if (array.Rank != 1)
+ throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
+ if (arrayIndex < 0)
+ throw new ArgumentOutOfRangeException("arrayIndex", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (array.Length - arrayIndex < Count)
+ throw new ArgumentException(Environment.GetResourceString("Arg_ArrayPlusOffTooSmall"));
+ Contract.EndContractBlock();
+ for (int i = 0; i<Count; i++) {
+ DictionaryEntry entry = new DictionaryEntry(keys[i],values[i]);
+ array.SetValue(entry, i + arrayIndex);
+ }
+ }
+
+ // Copies the values in this SortedList to an KeyValuePairs array.
+ // KeyValuePairs is different from Dictionary Entry in that it has special
+ // debugger attributes on its fields.
+
+ internal virtual KeyValuePairs[] ToKeyValuePairsArray() {
+ KeyValuePairs[] array = new KeyValuePairs[Count];
+ for (int i = 0; i < Count; i++) {
+ array[i] = new KeyValuePairs(keys[i],values[i]);
+ }
+ return array;
+ }
+
+ // Ensures that the capacity of this sorted list is at least the given
+ // minimum value. If the currect capacity of the list is less than
+ // min, the capacity is increased to twice the current capacity or
+ // to min, whichever is larger.
+ private void EnsureCapacity(int min) {
+ int newCapacity = keys.Length == 0? 16: keys.Length * 2;
+ // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow.
+ // Note that this check works even when _items.Length overflowed thanks to the (uint) cast
+ if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength;
+ if (newCapacity < min) newCapacity = min;
+ Capacity = newCapacity;
+ }
+
+ // Returns the value of the entry at the given index.
+ //
+ public virtual Object GetByIndex(int index) {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ Contract.EndContractBlock();
+ return values[index];
+ }
+
+ // Returns an IEnumerator for this sorted list. If modifications
+ // made to the sorted list while an enumeration is in progress,
+ // the MoveNext and Remove methods
+ // of the enumerator will throw an exception.
+ //
+ IEnumerator IEnumerable.GetEnumerator() {
+ return new SortedListEnumerator(this, 0, _size, SortedListEnumerator.DictEntry);
+ }
+
+ // Returns an IDictionaryEnumerator for this sorted list. If modifications
+ // made to the sorted list while an enumeration is in progress,
+ // the MoveNext and Remove methods
+ // of the enumerator will throw an exception.
+ //
+ public virtual IDictionaryEnumerator GetEnumerator() {
+ return new SortedListEnumerator(this, 0, _size, SortedListEnumerator.DictEntry);
+ }
+
+ // Returns the key of the entry at the given index.
+ //
+ public virtual Object GetKey(int index) {
+ if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ Contract.EndContractBlock();
+ return keys[index];
+ }
+
+ // Returns an IList representing the keys of this sorted list. The
+ // returned list is an alias for the keys of this sorted list, so
+ // modifications made to the returned list are directly reflected in the
+ // underlying sorted list, and vice versa. The elements of the returned
+ // list are ordered in the same way as the elements of the sorted list. The
+ // returned list does not support adding, inserting, or modifying elements
+ // (the Add, AddRange, Insert, InsertRange,
+ // Reverse, Set, SetRange, and Sort methods
+ // throw exceptions), but it does allow removal of elements (through the
+ // Remove and RemoveRange methods or through an enumerator).
+ // Null is an invalid key value.
+ //
+ public virtual IList GetKeyList() {
+ if (keyList == null) keyList = new KeyList(this);
+ return keyList;
+ }
+
+ // Returns an IList representing the values of this sorted list. The
+ // returned list is an alias for the values of this sorted list, so
+ // modifications made to the returned list are directly reflected in the
+ // underlying sorted list, and vice versa. The elements of the returned
+ // list are ordered in the same way as the elements of the sorted list. The
+ // returned list does not support adding or inserting elements (the
+ // Add, AddRange, Insert and InsertRange
+ // methods throw exceptions), but it does allow modification and removal of
+ // elements (through the Remove, RemoveRange, Set and
+ // SetRange methods or through an enumerator).
+ //
+ public virtual IList GetValueList() {
+ if (valueList == null) valueList = new ValueList(this);
+ return valueList;
+ }
+
+ // Returns the value associated with the given key. If an entry with the
+ // given key is not found, the returned value is null.
+ //
+ public virtual Object this[Object key] {
+ get {
+ int i = IndexOfKey(key);
+ if (i >= 0) return values[i];
+ return null;
+ }
+ set {
+ if (key == null) throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
+ Contract.EndContractBlock();
+ int i = Array.BinarySearch(keys, 0, _size, key, comparer);
+ if (i >= 0) {
+ values[i] = value;
+ version++;
+ return;
+ }
+ Insert(~i, key, value);
+ }
+ }
+
+ // Returns the index of the entry with a given key in this sorted list. The
+ // key is located through a binary search, and thus the average execution
+ // time of this method is proportional to Log2(size), where
+ // size is the size of this sorted list. The returned value is -1 if
+ // the given key does not occur in this sorted list. Null is an invalid
+ // key value.
+ //
+ public virtual int IndexOfKey(Object key) {
+ if (key == null)
+ throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
+ Contract.EndContractBlock();
+ int ret = Array.BinarySearch(keys, 0, _size, key, comparer);
+ return ret >=0 ? ret : -1;
+ }
+
+ // Returns the index of the first occurrence of an entry with a given value
+ // in this sorted list. The entry is located through a linear search, and
+ // thus the average execution time of this method is proportional to the
+ // size of this sorted list. The elements of the list are compared to the
+ // given value using the Object.Equals method.
+ //
+ public virtual int IndexOfValue(Object value) {
+ return Array.IndexOf(values, value, 0, _size);
+ }
+
+ // Inserts an entry with a given key and value at a given index.
+ private void Insert(int index, Object key, Object value) {
+ if (_size == keys.Length) EnsureCapacity(_size + 1);
+ if (index < _size) {
+ Array.Copy(keys, index, keys, index + 1, _size - index);
+ Array.Copy(values, index, values, index + 1, _size - index);
+ }
+ keys[index] = key;
+ values[index] = value;
+ _size++;
+ version++;
+ }
+
+ // Removes the entry at the given index. The size of the sorted list is
+ // decreased by one.
+ //
+ public virtual void RemoveAt(int index) {
+ if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ Contract.EndContractBlock();
+ _size--;
+ if (index < _size) {
+ Array.Copy(keys, index + 1, keys, index, _size - index);
+ Array.Copy(values, index + 1, values, index, _size - index);
+ }
+ keys[_size] = null;
+ values[_size] = null;
+ version++;
+ }
+
+ // Removes an entry from this sorted list. If an entry with the specified
+ // key exists in the sorted list, it is removed. An ArgumentException is
+ // thrown if the key is null.
+ //
+ public virtual void Remove(Object key) {
+ int i = IndexOfKey(key);
+ if (i >= 0)
+ RemoveAt(i);
+ }
+
+ // Sets the value at an index to a given value. The previous value of
+ // the given entry is overwritten.
+ //
+ public virtual void SetByIndex(int index, Object value) {
+ if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ Contract.EndContractBlock();
+ values[index] = value;
+ version++;
+ }
+
+ // Returns a thread-safe SortedList.
+ //
+ [HostProtection(Synchronization=true)]
+ public static SortedList Synchronized(SortedList list) {
+ if (list==null)
+ throw new ArgumentNullException("list");
+ Contract.EndContractBlock();
+ return new SyncSortedList(list);
+ }
+
+ // Sets the capacity of this sorted list to the size of the sorted list.
+ // This method can be used to minimize a sorted list's memory overhead once
+ // it is known that no new elements will be added to the sorted list. To
+ // completely clear a sorted list and release all memory referenced by the
+ // sorted list, execute the following statements:
+ //
+ // sortedList.Clear();
+ // sortedList.TrimToSize();
+ //
+ public virtual void TrimToSize() {
+ Capacity = _size;
+ }
+
+ [Serializable]
+ private class SyncSortedList : SortedList
+ {
+ private SortedList _list;
+ private Object _root;
+
+
+ internal SyncSortedList(SortedList list) {
+ _list = list;
+ _root = list.SyncRoot;
+ }
+
+ public override int Count {
+ get { lock(_root) { return _list.Count; } }
+ }
+
+ public override Object SyncRoot {
+ get { return _root; }
+ }
+
+ public override bool IsReadOnly {
+ get { return _list.IsReadOnly; }
+ }
+
+ public override bool IsFixedSize {
+ get { return _list.IsFixedSize; }
+ }
+
+
+ public override bool IsSynchronized {
+ get { return true; }
+ }
+
+ public override Object this[Object key] {
+ get {
+ lock(_root) {
+ return _list[key];
+ }
+ }
+ set {
+ lock(_root) {
+ _list[key] = value;
+ }
+ }
+ }
+
+ public override void Add(Object key, Object value) {
+ lock(_root) {
+ _list.Add(key, value);
+ }
+ }
+
+ public override int Capacity {
+ get{ lock(_root) { return _list.Capacity; } }
+ }
+
+ public override void Clear() {
+ lock(_root) {
+ _list.Clear();
+ }
+ }
+
+ public override Object Clone() {
+ lock(_root) {
+ return _list.Clone();
+ }
+ }
+
+ public override bool Contains(Object key) {
+ lock(_root) {
+ return _list.Contains(key);
+ }
+ }
+
+ public override bool ContainsKey(Object key) {
+ lock(_root) {
+ return _list.ContainsKey(key);
+ }
+ }
+
+ public override bool ContainsValue(Object key) {
+ lock(_root) {
+ return _list.ContainsValue(key);
+ }
+ }
+
+ public override void CopyTo(Array array, int index) {
+ lock(_root) {
+ _list.CopyTo(array, index);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override Object GetByIndex(int index) {
+ lock(_root) {
+ return _list.GetByIndex(index);
+ }
+ }
+
+ public override IDictionaryEnumerator GetEnumerator() {
+ lock(_root) {
+ return _list.GetEnumerator();
+ }
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override Object GetKey(int index) {
+ lock(_root) {
+ return _list.GetKey(index);
+ }
+ }
+
+ public override IList GetKeyList() {
+ lock(_root) {
+ return _list.GetKeyList();
+ }
+ }
+
+ public override IList GetValueList() {
+ lock(_root) {
+ return _list.GetValueList();
+ }
+ }
+
+ public override int IndexOfKey(Object key) {
+ if (key == null)
+ throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
+ Contract.EndContractBlock();
+
+ lock(_root) {
+ return _list.IndexOfKey(key);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int IndexOfValue(Object value) {
+ lock(_root) {
+ return _list.IndexOfValue(value);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override void RemoveAt(int index) {
+ lock(_root) {
+ _list.RemoveAt(index);
+ }
+ }
+
+ public override void Remove(Object key) {
+ lock(_root) {
+ _list.Remove(key);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override void SetByIndex(int index, Object value) {
+ lock(_root) {
+ _list.SetByIndex(index, value);
+ }
+ }
+
+ internal override KeyValuePairs[] ToKeyValuePairsArray() {
+ return _list.ToKeyValuePairsArray();
+ }
+
+ public override void TrimToSize() {
+ lock(_root) {
+ _list.TrimToSize();
+ }
+ }
+ }
+
+
+ [Serializable]
+ private class SortedListEnumerator : IDictionaryEnumerator, ICloneable
+ {
+ private SortedList sortedList;
+ private Object key;
+ private Object value;
+ private int index;
+ private int startIndex; // Store for Reset.
+ private int endIndex;
+ private int version;
+ private bool current; // Is the current element valid?
+ private int getObjectRetType; // What should GetObject return?
+
+ internal const int Keys = 1;
+ internal const int Values = 2;
+ internal const int DictEntry = 3;
+
+ internal SortedListEnumerator(SortedList sortedList, int index, int count,
+ int getObjRetType) {
+ this.sortedList = sortedList;
+ this.index = index;
+ startIndex = index;
+ endIndex = index + count;
+ version = sortedList.version;
+ getObjectRetType = getObjRetType;
+ current = false;
+ }
+
+ public Object Clone()
+ {
+ return MemberwiseClone();
+ }
+
+ public virtual Object Key {
+ get {
+ if (version != sortedList.version) throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion"));
+ if (current == false) throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumOpCantHappen"));
+ return key;
+ }
+ }
+
+ public virtual bool MoveNext() {
+ if (version != sortedList.version) throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion"));
+ if (index < endIndex) {
+ key = sortedList.keys[index];
+ value = sortedList.values[index];
+ index++;
+ current = true;
+ return true;
+ }
+ key = null;
+ value = null;
+ current = false;
+ return false;
+ }
+
+ public virtual DictionaryEntry Entry {
+ get {
+ if (version != sortedList.version) throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion"));
+ if (current == false) throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumOpCantHappen"));
+ return new DictionaryEntry(key, value);
+ }
+ }
+
+ public virtual Object Current {
+ get {
+ if (current == false) throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumOpCantHappen"));
+
+ if (getObjectRetType==Keys)
+ return key;
+ else if (getObjectRetType==Values)
+ return value;
+ else
+ return new DictionaryEntry(key, value);
+ }
+ }
+
+ public virtual Object Value {
+ get {
+ if (version != sortedList.version) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
+ if (current == false) throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumOpCantHappen"));
+ return value;
+ }
+ }
+
+ public virtual void Reset() {
+ if (version != sortedList.version) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
+ index = startIndex;
+ current = false;
+ key = null;
+ value = null;
+ }
+ }
+
+ [Serializable]
+ private class KeyList : IList
+ {
+ private SortedList sortedList;
+
+ internal KeyList(SortedList sortedList) {
+ this.sortedList = sortedList;
+ }
+
+ public virtual int Count {
+ get { return sortedList._size; }
+ }
+
+ public virtual bool IsReadOnly {
+ get { return true; }
+ }
+
+ public virtual bool IsFixedSize {
+ get { return true; }
+ }
+
+ public virtual bool IsSynchronized {
+ get { return sortedList.IsSynchronized; }
+ }
+
+ public virtual Object SyncRoot {
+ get { return sortedList.SyncRoot; }
+ }
+
+ public virtual int Add(Object key) {
+ throw new NotSupportedException(Environment.GetResourceString(ResId.NotSupported_SortedListNestedWrite));
+ // return 0; // suppress compiler warning
+ }
+
+ public virtual void Clear() {
+ throw new NotSupportedException(Environment.GetResourceString(ResId.NotSupported_SortedListNestedWrite));
+ }
+
+ public virtual bool Contains(Object key) {
+ return sortedList.Contains(key);
+ }
+
+ public virtual void CopyTo(Array array, int arrayIndex) {
+ if (array != null && array.Rank != 1)
+ throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
+ Contract.EndContractBlock();
+
+ // defer error checking to Array.Copy
+ Array.Copy(sortedList.keys, 0, array, arrayIndex, sortedList.Count);
+ }
+
+ public virtual void Insert(int index, Object value) {
+ throw new NotSupportedException(Environment.GetResourceString(ResId.NotSupported_SortedListNestedWrite));
+ }
+
+ public virtual Object this[int index] {
+ get {
+ return sortedList.GetKey(index);
+ }
+ set {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_KeyCollectionSet"));
+ }
+ }
+
+ public virtual IEnumerator GetEnumerator() {
+ return new SortedListEnumerator(sortedList, 0, sortedList.Count, SortedListEnumerator.Keys);
+ }
+
+ public virtual int IndexOf(Object key) {
+ if (key==null)
+ throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
+ Contract.EndContractBlock();
+
+ int i = Array.BinarySearch(sortedList.keys, 0,
+ sortedList.Count, key, sortedList.comparer);
+ if (i >= 0) return i;
+ return -1;
+ }
+
+ public virtual void Remove(Object key) {
+ throw new NotSupportedException(Environment.GetResourceString(ResId.NotSupported_SortedListNestedWrite));
+ }
+
+ public virtual void RemoveAt(int index) {
+ throw new NotSupportedException(Environment.GetResourceString(ResId.NotSupported_SortedListNestedWrite));
+ }
+ }
+
+ [Serializable]
+ private class ValueList : IList
+ {
+ private SortedList sortedList;
+
+ internal ValueList(SortedList sortedList) {
+ this.sortedList = sortedList;
+ }
+
+ public virtual int Count {
+ get { return sortedList._size; }
+ }
+
+ public virtual bool IsReadOnly {
+ get { return true; }
+ }
+
+ public virtual bool IsFixedSize {
+ get { return true; }
+ }
+
+ public virtual bool IsSynchronized {
+ get { return sortedList.IsSynchronized; }
+ }
+
+ public virtual Object SyncRoot {
+ get { return sortedList.SyncRoot; }
+ }
+
+ public virtual int Add(Object key) {
+ throw new NotSupportedException(Environment.GetResourceString(ResId.NotSupported_SortedListNestedWrite));
+ }
+
+ public virtual void Clear() {
+ throw new NotSupportedException(Environment.GetResourceString(ResId.NotSupported_SortedListNestedWrite));
+ }
+
+ public virtual bool Contains(Object value) {
+ return sortedList.ContainsValue(value);
+ }
+
+ public virtual void CopyTo(Array array, int arrayIndex) {
+ if (array != null && array.Rank != 1)
+ throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
+ Contract.EndContractBlock();
+
+ // defer error checking to Array.Copy
+ Array.Copy(sortedList.values, 0, array, arrayIndex, sortedList.Count);
+ }
+
+ public virtual void Insert(int index, Object value) {
+ throw new NotSupportedException(Environment.GetResourceString(ResId.NotSupported_SortedListNestedWrite));
+ }
+
+ public virtual Object this[int index] {
+ get {
+ return sortedList.GetByIndex(index);
+ }
+ set {
+ throw new NotSupportedException(Environment.GetResourceString(ResId.NotSupported_SortedListNestedWrite));
+ }
+ }
+
+ public virtual IEnumerator GetEnumerator() {
+ return new SortedListEnumerator(sortedList, 0, sortedList.Count, SortedListEnumerator.Values);
+ }
+
+ public virtual int IndexOf(Object value) {
+ return Array.IndexOf(sortedList.values, value, 0, sortedList.Count);
+ }
+
+ public virtual void Remove(Object value) {
+ throw new NotSupportedException(Environment.GetResourceString(ResId.NotSupported_SortedListNestedWrite));
+ }
+
+ public virtual void RemoveAt(int index) {
+ throw new NotSupportedException(Environment.GetResourceString(ResId.NotSupported_SortedListNestedWrite));
+ }
+
+ }
+
+ // internal debug view class for sorted list
+ internal class SortedListDebugView {
+ private SortedList sortedList;
+
+ public SortedListDebugView( SortedList sortedList) {
+ if( sortedList == null) {
+ throw new ArgumentNullException("sortedList");
+ }
+ Contract.EndContractBlock();
+
+ this.sortedList = sortedList;
+ }
+
+ [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+ public KeyValuePairs[] Items {
+ get {
+ return sortedList.ToKeyValuePairsArray();
+ }
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/Stack.cs b/src/mscorlib/src/System/Collections/Stack.cs
new file mode 100644
index 0000000000..0384a4ee81
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/Stack.cs
@@ -0,0 +1,380 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+**
+** Purpose: An array implementation of a stack.
+**
+**
+=============================================================================*/
+namespace System.Collections {
+ using System;
+ using System.Security.Permissions;
+ using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+
+ // A simple stack of objects. Internally it is implemented as an array,
+ // so Push can be O(n). Pop is O(1).
+ [DebuggerTypeProxy(typeof(System.Collections.Stack.StackDebugView))]
+ [DebuggerDisplay("Count = {Count}")]
+[System.Runtime.InteropServices.ComVisible(true)]
+ [Serializable]
+ public class Stack : ICollection, ICloneable {
+ private Object[] _array; // Storage for stack elements
+ [ContractPublicPropertyName("Count")]
+ private int _size; // Number of items in the stack.
+ private int _version; // Used to keep enumerator in sync w/ collection.
+ [NonSerialized]
+ private Object _syncRoot;
+
+ private const int _defaultCapacity = 10;
+
+ public Stack() {
+ _array = new Object[_defaultCapacity];
+ _size = 0;
+ _version = 0;
+ }
+
+ // Create a stack with a specific initial capacity. The initial capacity
+ // must be a non-negative number.
+ public Stack(int initialCapacity) {
+ if (initialCapacity < 0)
+ throw new ArgumentOutOfRangeException("initialCapacity", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ Contract.EndContractBlock();
+ if (initialCapacity < _defaultCapacity)
+ initialCapacity = _defaultCapacity; // Simplify doubling logic in Push.
+ _array = new Object[initialCapacity];
+ _size = 0;
+ _version = 0;
+ }
+
+ // Fills a Stack with the contents of a particular collection. The items are
+ // pushed onto the stack in the same order they are read by the enumerator.
+ //
+ public Stack(ICollection col) : this((col==null ? 32 : col.Count))
+ {
+ if (col==null)
+ throw new ArgumentNullException("col");
+ Contract.EndContractBlock();
+ IEnumerator en = col.GetEnumerator();
+ while(en.MoveNext())
+ Push(en.Current);
+ }
+
+ public virtual int Count {
+ get {
+ Contract.Ensures(Contract.Result<int>() >= 0);
+ return _size;
+ }
+ }
+
+ public virtual bool IsSynchronized {
+ get { return false; }
+ }
+
+ public virtual Object SyncRoot {
+ get {
+ if( _syncRoot == null) {
+ System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);
+ }
+ return _syncRoot;
+ }
+ }
+
+ // Removes all Objects from the Stack.
+ public virtual void Clear() {
+ Array.Clear(_array, 0, _size); // Don't need to doc this but we clear the elements so that the gc can reclaim the references.
+ _size = 0;
+ _version++;
+ }
+
+ public virtual Object Clone() {
+ Contract.Ensures(Contract.Result<Object>() != null);
+
+ Stack s = new Stack(_size);
+ s._size = _size;
+ Array.Copy(_array, 0, s._array, 0, _size);
+ s._version = _version;
+ return s;
+ }
+
+ public virtual bool Contains(Object obj) {
+ int count = _size;
+
+ while (count-- > 0) {
+ if (obj == null) {
+ if (_array[count] == null)
+ return true;
+ }
+ else if (_array[count] != null && _array[count].Equals(obj)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Copies the stack into an array.
+ public virtual void CopyTo(Array array, int index) {
+ if (array==null)
+ throw new ArgumentNullException("array");
+ if (array.Rank != 1)
+ throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
+ if (index < 0)
+ throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ if (array.Length - index < _size)
+ throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
+ Contract.EndContractBlock();
+
+ int i = 0;
+ if (array is Object[]) {
+ Object[] objArray = (Object[]) array;
+ while(i < _size) {
+ objArray[i+index] = _array[_size-i-1];
+ i++;
+ }
+ }
+ else {
+ while(i < _size) {
+ array.SetValue(_array[_size-i-1], i+index);
+ i++;
+ }
+ }
+ }
+
+ // Returns an IEnumerator for this Stack.
+ public virtual IEnumerator GetEnumerator() {
+ Contract.Ensures(Contract.Result<IEnumerator>() != null);
+ return new StackEnumerator(this);
+ }
+
+ // Returns the top object on the stack without removing it. If the stack
+ // is empty, Peek throws an InvalidOperationException.
+ public virtual Object Peek() {
+ if (_size==0)
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EmptyStack"));
+ Contract.EndContractBlock();
+ return _array[_size-1];
+ }
+
+ // Pops an item from the top of the stack. If the stack is empty, Pop
+ // throws an InvalidOperationException.
+ public virtual Object Pop() {
+ if (_size == 0)
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EmptyStack"));
+ //Contract.Ensures(Count == Contract.OldValue(Count) - 1);
+ Contract.EndContractBlock();
+ _version++;
+ Object obj = _array[--_size];
+ _array[_size] = null; // Free memory quicker.
+ return obj;
+ }
+
+ // Pushes an item to the top of the stack.
+ //
+ public virtual void Push(Object obj) {
+ //Contract.Ensures(Count == Contract.OldValue(Count) + 1);
+ if (_size == _array.Length) {
+ Object[] newArray = new Object[2*_array.Length];
+ Array.Copy(_array, 0, newArray, 0, _size);
+ _array = newArray;
+ }
+ _array[_size++] = obj;
+ _version++;
+ }
+
+ // Returns a synchronized Stack.
+ //
+ [HostProtection(Synchronization=true)]
+ public static Stack Synchronized(Stack stack) {
+ if (stack==null)
+ throw new ArgumentNullException("stack");
+ Contract.Ensures(Contract.Result<Stack>() != null);
+ Contract.EndContractBlock();
+ return new SyncStack(stack);
+ }
+
+
+ // Copies the Stack to an array, in the same order Pop would return the items.
+ public virtual Object[] ToArray()
+ {
+ Contract.Ensures(Contract.Result<Object[]>() != null);
+
+ Object[] objArray = new Object[_size];
+ int i = 0;
+ while(i < _size) {
+ objArray[i] = _array[_size-i-1];
+ i++;
+ }
+ return objArray;
+ }
+
+ [Serializable]
+ private class SyncStack : Stack
+ {
+ private Stack _s;
+ private Object _root;
+
+ internal SyncStack(Stack stack) {
+ _s = stack;
+ _root = stack.SyncRoot;
+ }
+
+ public override bool IsSynchronized {
+ get { return true; }
+ }
+
+ public override Object SyncRoot {
+ get {
+ return _root;
+ }
+ }
+
+ public override int Count {
+ get {
+ lock (_root) {
+ return _s.Count;
+ }
+ }
+ }
+
+ public override bool Contains(Object obj) {
+ lock (_root) {
+ return _s.Contains(obj);
+ }
+ }
+
+ public override Object Clone()
+ {
+ lock (_root) {
+ return new SyncStack((Stack)_s.Clone());
+ }
+ }
+
+ public override void Clear() {
+ lock (_root) {
+ _s.Clear();
+ }
+ }
+
+ public override void CopyTo(Array array, int arrayIndex) {
+ lock (_root) {
+ _s.CopyTo(array, arrayIndex);
+ }
+ }
+
+ public override void Push(Object value) {
+ lock (_root) {
+ _s.Push(value);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Thread safety problems with precondition - can't express the precondition as of Dev10.
+ public override Object Pop() {
+ lock (_root) {
+ return _s.Pop();
+ }
+ }
+
+ public override IEnumerator GetEnumerator() {
+ lock (_root) {
+ return _s.GetEnumerator();
+ }
+ }
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Thread safety problems with precondition - can't express the precondition as of Dev10.
+ public override Object Peek() {
+ lock (_root) {
+ return _s.Peek();
+ }
+ }
+
+ public override Object[] ToArray() {
+ lock (_root) {
+ return _s.ToArray();
+ }
+ }
+ }
+
+
+ [Serializable]
+ private class StackEnumerator : IEnumerator, ICloneable
+ {
+ private Stack _stack;
+ private int _index;
+ private int _version;
+ private Object currentElement;
+
+ internal StackEnumerator(Stack stack) {
+ _stack = stack;
+ _version = _stack._version;
+ _index = -2;
+ currentElement = null;
+ }
+
+ public Object Clone()
+ {
+ return MemberwiseClone();
+ }
+
+ public virtual bool MoveNext() {
+ bool retval;
+ if (_version != _stack._version) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
+ if (_index == -2) { // First call to enumerator.
+ _index = _stack._size-1;
+ retval = ( _index >= 0);
+ if (retval)
+ currentElement = _stack._array[_index];
+ return retval;
+ }
+ if (_index == -1) { // End of enumeration.
+ return false;
+ }
+
+ retval = (--_index >= 0);
+ if (retval)
+ currentElement = _stack._array[_index];
+ else
+ currentElement = null;
+ return retval;
+ }
+
+ public virtual Object Current {
+ get {
+ if (_index == -2) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
+ if (_index == -1) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded));
+ return currentElement;
+ }
+ }
+
+ public virtual void Reset() {
+ if (_version != _stack._version) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
+ _index = -2;
+ currentElement = null;
+ }
+ }
+
+ internal class StackDebugView {
+ private Stack stack;
+
+ public StackDebugView( Stack stack) {
+ if( stack == null)
+ throw new ArgumentNullException("stack");
+ Contract.EndContractBlock();
+
+ this.stack = stack;
+ }
+
+ [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+ public Object[] Items {
+ get {
+ return stack.ToArray();
+ }
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Collections/StructuralComparisons.cs b/src/mscorlib/src/System/Collections/StructuralComparisons.cs
new file mode 100644
index 0000000000..685af59c4b
--- /dev/null
+++ b/src/mscorlib/src/System/Collections/StructuralComparisons.cs
@@ -0,0 +1,89 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+
+using System;
+
+namespace System.Collections {
+ public static class StructuralComparisons {
+
+ private static volatile IComparer s_StructuralComparer;
+ private static volatile IEqualityComparer s_StructuralEqualityComparer;
+
+ public static IComparer StructuralComparer {
+ get {
+ IComparer comparer = s_StructuralComparer;
+ if (comparer == null) {
+ comparer = new StructuralComparer();
+ s_StructuralComparer = comparer;
+ }
+ return comparer;
+ }
+ }
+
+ public static IEqualityComparer StructuralEqualityComparer {
+ get {
+ IEqualityComparer comparer = s_StructuralEqualityComparer;
+ if (comparer == null) {
+ comparer = new StructuralEqualityComparer();
+ s_StructuralEqualityComparer = comparer;
+ }
+ return comparer;
+ }
+ }
+ }
+
+ [Serializable]
+ internal class StructuralEqualityComparer : IEqualityComparer {
+ public new bool Equals(Object x, Object y) {
+ if (x != null) {
+
+ IStructuralEquatable seObj = x as IStructuralEquatable;
+
+ if (seObj != null){
+ return seObj.Equals(y, this);
+ }
+
+ if (y != null) {
+ return x.Equals(y);
+ } else {
+ return false;
+ }
+ }
+ if (y != null) return false;
+ return true;
+ }
+
+ public int GetHashCode(Object obj) {
+ if (obj == null) return 0;
+
+ IStructuralEquatable seObj = obj as IStructuralEquatable;
+
+ if (seObj != null) {
+ return seObj.GetHashCode(this);
+ }
+
+ return obj.GetHashCode();
+ }
+ }
+
+ [Serializable]
+ internal class StructuralComparer : IComparer {
+ public int Compare(Object x, Object y) {
+
+ if (x == null) return y == null ? 0 : -1;
+ if (y == null) return 1;
+
+ IStructuralComparable scX = x as IStructuralComparable;
+
+ if (scX != null) {
+ return scX.CompareTo(y, this);
+ }
+
+ return Comparer.Default.Compare(x, y);
+ }
+ }
+
+}