diff options
Diffstat (limited to 'src/mscorlib/src/System/Collections/Generic/Dictionary.cs')
-rw-r--r-- | src/mscorlib/src/System/Collections/Generic/Dictionary.cs | 1187 |
1 files changed, 1187 insertions, 0 deletions
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); + } + } + } + } +} |