diff --git a/src/mscorlib/src/System/Collections/Generic/Dictionary.cs b/src/mscorlib/src/System/Collections/Generic/Dictionary.cs index 1ba146679e5f..67309e15aa25 100644 --- a/src/mscorlib/src/System/Collections/Generic/Dictionary.cs +++ b/src/mscorlib/src/System/Collections/Generic/Dictionary.cs @@ -63,15 +63,19 @@ private struct 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 comparer; - private KeyCollection keys; - private ValueCollection values; + private static Entry s_nullEntry; + + private int[] _buckets; + private Entry[] _entries; + private int _count; + private int _version; + private int _freeList; + private int _freeCount; + + private IEqualityComparer _customComparer; + + private KeyCollection _keys; + private ValueCollection _values; private Object _syncRoot; // constants for serialization @@ -90,18 +94,24 @@ public Dictionary(int capacity, IEqualityComparer comparer) { if (capacity < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity); if (capacity > 0) Initialize(capacity); - this.comparer = comparer ?? EqualityComparer.Default; + _customComparer = comparer; - if (this.comparer == EqualityComparer.Default) + // String has a more nuanced comparer as its GetHashCode is randomised for security + if (typeof(TKey) == typeof(string)) { - this.comparer = (IEqualityComparer)NonRandomizedStringEqualityComparer.Default; + // If TKey is a string, we move off the default comparer to a non-randomized comparer + // Later if collisions become too high we will move back onto the default randomized comparer + if (comparer == null || ReferenceEquals(comparer, EqualityComparer.Default)) + { + _customComparer = (IEqualityComparer)NonRandomizedStringEqualityComparer.Default; + } } } public Dictionary(IDictionary dictionary) : this(dictionary, null) { } public Dictionary(IDictionary dictionary, IEqualityComparer comparer) : - this(dictionary != null ? dictionary.Count : 0, comparer) + this(dictionary?.Count ?? 0, comparer) { if (dictionary == null) { @@ -115,13 +125,14 @@ public Dictionary(IDictionary dictionary, IEqualityComparer if (dictionary.GetType() == typeof(Dictionary)) { Dictionary d = (Dictionary)dictionary; - int count = d.count; - Entry[] entries = d.entries; + int count = d._count; + Entry[] entries = d._entries; for (int i = 0; i < count; i++) { - if (entries[i].hashCode >= 0) + ref Entry entry = ref entries[i]; + if (entry.hashCode >= 0) { - Add(entries[i].key, entries[i].value); + Add(entry.key, entry.value); } } return; @@ -159,81 +170,45 @@ protected Dictionary(SerializationInfo info, StreamingContext context) HashHelpers.SerializationInfoTable.Add(this, info); } - public IEqualityComparer Comparer - { - get - { - return comparer; - } - } + public IEqualityComparer Comparer => _customComparer ?? EqualityComparer.Default; - public int Count - { - get { return count - freeCount; } - } + public int Count => _count - _freeCount; public KeyCollection Keys { get { Contract.Ensures(Contract.Result() != null); - if (keys == null) keys = new KeyCollection(this); - return keys; + return _keys ?? (_keys = new KeyCollection(this)); } } - ICollection IDictionary.Keys - { - get - { - if (keys == null) keys = new KeyCollection(this); - return keys; - } - } + ICollection IDictionary.Keys => _keys ?? (_keys = new KeyCollection(this)); - IEnumerable IReadOnlyDictionary.Keys - { - get - { - if (keys == null) keys = new KeyCollection(this); - return keys; - } - } + IEnumerable IReadOnlyDictionary.Keys => _keys ?? (_keys = new KeyCollection(this)); public ValueCollection Values { get { Contract.Ensures(Contract.Result() != null); - if (values == null) values = new ValueCollection(this); - return values; + return _values ?? (_values = new ValueCollection(this)); } } - ICollection IDictionary.Values - { - get - { - if (values == null) values = new ValueCollection(this); - return values; - } - } + ICollection IDictionary.Values => _values ?? (_values = new ValueCollection(this)); - IEnumerable IReadOnlyDictionary.Values - { - get - { - if (values == null) values = new ValueCollection(this); - return values; - } - } + IEnumerable IReadOnlyDictionary.Values => _values ?? (_values = new ValueCollection(this)); public TValue this[TKey key] { get { - int i = FindEntry(key); - if (i >= 0) return entries[i].value; + ref Entry entry = ref FindEntry(key, out bool found); + if (found) + { + return entry.value; + } ThrowHelper.ThrowKeyNotFoundException(); return default(TValue); } @@ -257,18 +232,14 @@ void ICollection>.Add(KeyValuePair keyV bool ICollection>.Contains(KeyValuePair keyValuePair) { - int i = FindEntry(keyValuePair.Key); - if (i >= 0 && EqualityComparer.Default.Equals(entries[i].value, keyValuePair.Value)) - { - return true; - } - return false; + ref Entry entry = ref FindEntry(keyValuePair.Key, out bool found); + return found && EqualityComparer.Default.Equals(entry.value, keyValuePair.Value); } bool ICollection>.Remove(KeyValuePair keyValuePair) { - int i = FindEntry(keyValuePair.Key); - if (i >= 0 && EqualityComparer.Default.Equals(entries[i].value, keyValuePair.Value)) + ref Entry entry = ref FindEntry(keyValuePair.Key, out bool found); + if (found && EqualityComparer.Default.Equals(entry.value, keyValuePair.Value)) { Remove(keyValuePair.Key); return true; @@ -278,29 +249,38 @@ bool ICollection>.Remove(KeyValuePair k public void Clear() { + int count = _count; 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++; + int[] buckets = _buckets; + 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; + FindEntry(key, out bool found); + return found; } public bool ContainsValue(TValue value) { + Entry[] entries = _entries; + int count = _count; if (value == null) { for (int i = 0; i < count; i++) { - if (entries[i].hashCode >= 0 && entries[i].value == null) return true; + ref Entry entry = ref entries[i]; + if (entry.hashCode >= 0 && entry.value == null) return true; } } else @@ -308,7 +288,8 @@ public bool ContainsValue(TValue value) EqualityComparer c = EqualityComparer.Default; for (int i = 0; i < count; i++) { - if (entries[i].hashCode >= 0 && c.Equals(entries[i].value, value)) return true; + ref Entry entry = ref entries[i]; + if (entry.hashCode >= 0 && c.Equals(entry.value, value)) return true; } } return false; @@ -331,26 +312,21 @@ private void CopyTo(KeyValuePair[] array, int index) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); } - int count = this.count; - Entry[] entries = this.entries; + int count = this._count; + Entry[] entries = this._entries; for (int i = 0; i < count; i++) { - if (entries[i].hashCode >= 0) + ref Entry entry = ref entries[i]; + if (entry.hashCode >= 0) { - array[index++] = new KeyValuePair(entries[i].key, entries[i].value); + array[index++] = new KeyValuePair(entry.key, entry.value); } } } - public Enumerator GetEnumerator() - { - return new Enumerator(this, Enumerator.KeyValuePair); - } + public Enumerator GetEnumerator() => new Enumerator(this, Enumerator.KeyValuePair); - IEnumerator> IEnumerable>.GetEnumerator() - { - return new Enumerator(this, Enumerator.KeyValuePair); - } + IEnumerator> IEnumerable>.GetEnumerator() => new Enumerator(this, Enumerator.KeyValuePair); public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { @@ -358,10 +334,10 @@ public virtual void GetObjectData(SerializationInfo info, StreamingContext conte { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info); } - info.AddValue(VersionName, version); - info.AddValue(ComparerName, comparer, typeof(IEqualityComparer)); - info.AddValue(HashSizeName, buckets == null ? 0 : buckets.Length); //This is the length of the bucket array. - if (buckets != null) + info.AddValue(VersionName, _version); + info.AddValue(ComparerName, _customComparer ?? EqualityComparer.Default, typeof(IEqualityComparer)); + info.AddValue(HashSizeName, _buckets == null ? 0 : _buckets.Length); //This is the length of the bucket array. + if (_buckets != null) { KeyValuePair[] array = new KeyValuePair[Count]; CopyTo(array, 0); @@ -369,33 +345,99 @@ public virtual void GetObjectData(SerializationInfo info, StreamingContext conte } } - private int FindEntry(TKey key) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ref Entry FindEntry(TKey key, out bool found) { if (key == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); } + IEqualityComparer comparer = _customComparer; + if (comparer == null) + { + return ref FindEntryDefaultComparer(key, out found); + } + else + { + return ref FindEntryCustomComparer(key, comparer, out found); + } + } + + private ref Entry FindEntryDefaultComparer(TKey key, out bool found) + { + Debug.Assert(key != null); + + found = true; + int[] buckets = _buckets; + if (buckets != null) + { + // Keys are never null + int hashCode = key.GetHashCode() & 0x7FFFFFFF; + + Entry[] entries = _entries; + for (int i = buckets[(uint)hashCode % (uint)buckets.Length]; i >= 0; i = entries[i].next) + { + ref Entry entry = ref entries[i]; + + // If class use equals, if struct specialize equality to avoid boxing + if (entry.hashCode == hashCode && (default(TKey) == null ? entry.key.Equals(key) : StructKeyEquals(entry.key, key))) + { + return ref entry; + } + } + } + + found = false; + return ref NotFound; + } + + private ref Entry FindEntryCustomComparer(TKey key, IEqualityComparer comparer, out bool found) + { + Debug.Assert(key != null); + + found = true; + int[] buckets = _buckets; if (buckets != null) { int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; - for (int i = buckets[hashCode % buckets.Length]; i >= 0; i = entries[i].next) + Entry[] entries = _entries; + for (int i = buckets[(uint)hashCode % (uint)buckets.Length]; i >= 0; i = entries[i].next) { - if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i; + ref Entry entry = ref entries[i]; + if (entry.hashCode == hashCode && comparer.Equals(entry.key, key)) + { + return ref entry; + } } } - return -1; + + found = false; + return ref NotFound; + } + + private ref Entry NotFound + { + [MethodImpl(MethodImplOptions.NoInlining)] + get => ref s_nullEntry; } - private void Initialize(int capacity) + private int[] 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; + int[] buckets = new int[size]; + for (int i = 0; i < buckets.Length; i++) + { + buckets[i] = -1; + } + _entries = new Entry[size]; + _freeList = -1; + + _buckets = buckets; + return buckets; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool TryInsert(TKey key, TValue value, InsertionBehavior behavior) { if (key == null) @@ -403,19 +445,99 @@ private bool TryInsert(TKey key, TValue value, InsertionBehavior behavior) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); } - if (buckets == null) Initialize(0); - int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; - int targetBucket = hashCode % buckets.Length; + IEqualityComparer customComparer = _customComparer; + if (customComparer == null) + { + return TryInsertDefaultComparer(key, value, behavior); + } + else + { + return TryInsertCustomComparer(key, value, behavior, customComparer); + } + } + + private bool TryInsertDefaultComparer(TKey key, TValue value, InsertionBehavior behavior) + { + Debug.Assert(key != null); + + int[] buckets = _buckets ?? Initialize(0); + // Keys are never null + int hashCode = key.GetHashCode() & 0x7FFFFFFF; + + Entry[] entries = _entries; + for (int i = buckets[(uint)hashCode % (uint)buckets.Length]; i >= 0; i = entries[i].next) + { + ref Entry candidateEntry = ref entries[i]; + + // If class use equals, if struct specialize equality to avoid boxing + if (candidateEntry.hashCode == hashCode && (default(TKey) == null ? candidateEntry.key.Equals(key) : StructKeyEquals(candidateEntry.key, key))) + { + if (behavior == InsertionBehavior.OverwriteExisting) + { + candidateEntry.value = value; + _version++; + return true; + } + + if (behavior == InsertionBehavior.ThrowOnExisting) + { + ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(key); + } + + return false; + } + } + int index; + if (_freeCount > 0) + { + index = _freeList; + _freeList = entries[index].next; + _freeCount--; + } + else + { + if (_count == entries.Length) + { + Resize(); + // Update local cached items + buckets = _buckets; + entries = _entries; + } + index = _count; + _count++; + } + + ref int bucket = ref buckets[(uint)hashCode % (uint)buckets.Length]; + ref Entry entry = ref entries[index]; + entry.hashCode = hashCode; + entry.next = bucket; + entry.key = key; + entry.value = value; + bucket = index; + _version++; + + return true; + } + + private bool TryInsertCustomComparer(TKey key, TValue value, InsertionBehavior behavior, IEqualityComparer customComparer) + { + Debug.Assert(key != null); + + int[] buckets = _buckets ?? Initialize(0); + int hashCode = customComparer.GetHashCode(key) & 0x7FFFFFFF; + // Count collisions to see if we need to move to randomized hashing for string keys int collisionCount = 0; - for (int i = buckets[targetBucket]; i >= 0; i = entries[i].next) + Entry[] entries = _entries; + for (int i = buckets[(uint)hashCode % (uint)buckets.Length]; i >= 0; i = entries[i].next) { - if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) + ref Entry candidateEntry = ref entries[i]; + if (candidateEntry.hashCode == hashCode && customComparer.Equals(candidateEntry.key, key)) { if (behavior == InsertionBehavior.OverwriteExisting) { - entries[i].value = value; - version++; + candidateEntry.value = value; + _version++; return true; } @@ -430,37 +552,41 @@ private bool TryInsert(TKey key, TValue value, InsertionBehavior behavior) collisionCount++; } int index; - if (freeCount > 0) + if (_freeCount > 0) { - index = freeList; - freeList = entries[index].next; - freeCount--; + index = _freeList; + _freeList = entries[index].next; + _freeCount--; } else { - if (count == entries.Length) + if (_count == entries.Length) { Resize(); - targetBucket = hashCode % buckets.Length; + // Update local cached items + buckets = _buckets; + entries = _entries; } - index = count; - count++; + index = _count; + _count++; } - entries[index].hashCode = hashCode; - entries[index].next = buckets[targetBucket]; - entries[index].key = key; - entries[index].value = value; - buckets[targetBucket] = index; - version++; + ref int bucket = ref buckets[(uint)hashCode % (uint)buckets.Length]; + ref Entry entry = ref entries[index]; + entry.hashCode = hashCode; + entry.next = bucket; + entry.key = key; + entry.value = value; + bucket = index; + _version++; // If we hit the collision threshold we'll need to switch to the comparer which is using randomized string hashing // i.e. EqualityComparer.Default. - if (collisionCount > HashHelpers.HashCollisionThreshold && comparer == NonRandomizedStringEqualityComparer.Default) + if (collisionCount > HashHelpers.HashCollisionThreshold && ReferenceEquals(_customComparer, NonRandomizedStringEqualityComparer.Default)) { - comparer = (IEqualityComparer)EqualityComparer.Default; - Resize(entries.Length, true); + _customComparer = null; // Use default comparer + Resize(_entries.Length, true); } return true; @@ -482,14 +608,18 @@ public virtual void OnDeserialization(Object sender) int realVersion = siInfo.GetInt32(VersionName); int hashsize = siInfo.GetInt32(HashSizeName); - comparer = (IEqualityComparer)siInfo.GetValue(ComparerName, typeof(IEqualityComparer)); + _customComparer = (IEqualityComparer)siInfo.GetValue(ComparerName, typeof(IEqualityComparer)); if (hashsize != 0) { - buckets = new int[hashsize]; - for (int i = 0; i < buckets.Length; i++) buckets[i] = -1; - entries = new Entry[hashsize]; - freeList = -1; + int[] buckets = new int[hashsize]; + for (int i = 0; i < buckets.Length; i++) + { + buckets[i] = -1; + } + _buckets = buckets; + _entries = new Entry[hashsize]; + _freeList = -1; KeyValuePair[] array = (KeyValuePair[]) siInfo.GetValue(KeyValuePairsName, typeof(KeyValuePair[])); @@ -510,80 +640,154 @@ public virtual void OnDeserialization(Object sender) } else { - buckets = null; + _buckets = null; } - version = realVersion; + _version = realVersion; HashHelpers.SerializationInfoTable.Remove(this); } - private void Resize() + private void Resize() => Resize(HashHelpers.ExpandPrime(_count), false); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Resize(int newSize, bool forceNewHashCodes) { - Resize(HashHelpers.ExpandPrime(count), false); + Debug.Assert(newSize >= _entries.Length); + + IEqualityComparer customComparer = _customComparer; + if (customComparer == null) + { + ResizeDefaultComparer(newSize, forceNewHashCodes); + } + else + { + ResizeCustomComparer(newSize, forceNewHashCodes, customComparer); + } } - private void Resize(int newSize, bool forceNewHashCodes) + private void ResizeDefaultComparer(int newSize, bool forceNewHashCodes) { - Debug.Assert(newSize >= entries.Length); + Debug.Assert(newSize >= _entries.Length); + int[] newBuckets = new int[newSize]; - for (int i = 0; i < newBuckets.Length; i++) newBuckets[i] = -1; + for (int i = 0; i < newBuckets.Length; i++) + { + newBuckets[i] = -1; + } + + int count = _count; Entry[] newEntries = new Entry[newSize]; - Array.Copy(entries, 0, newEntries, 0, count); - if (forceNewHashCodes) + Array.Copy(_entries, 0, newEntries, 0, count); + for (int i = 0; i < count; i++) { - for (int i = 0; i < count; i++) + ref Entry entry = ref newEntries[i]; + + int hashCode = entry.hashCode; + if (forceNewHashCodes && hashCode >= 0) { - if (newEntries[i].hashCode != -1) - { - newEntries[i].hashCode = (comparer.GetHashCode(newEntries[i].key) & 0x7FFFFFFF); - } + // Keys are never null + hashCode = entry.key.GetHashCode() & 0x7FFFFFFF; + entry.hashCode = hashCode; + } + if (hashCode >= 0) + { + ref int newBucket = ref newBuckets[(uint)hashCode % (uint)newBuckets.Length]; + entry.next = newBucket; + newBucket = i; } } + _buckets = newBuckets; + _entries = newEntries; + } + + private void ResizeCustomComparer(int newSize, bool forceNewHashCodes, IEqualityComparer customComparer) + { + Debug.Assert(newSize >= _entries.Length); + + int[] newBuckets = new int[newSize]; + for (int i = 0; i < newBuckets.Length; i++) + { + newBuckets[i] = -1; + } + + int count = _count; + Entry[] newEntries = new Entry[newSize]; + Array.Copy(_entries, 0, newEntries, 0, count); for (int i = 0; i < count; i++) { - if (newEntries[i].hashCode >= 0) + ref Entry entry = ref newEntries[i]; + + int hashCode = entry.hashCode; + if (forceNewHashCodes && hashCode >= 0) + { + hashCode = customComparer.GetHashCode(entry.key) & 0x7FFFFFFF; + entry.hashCode = hashCode; + } + if (hashCode >= 0) { - int bucket = newEntries[i].hashCode % newSize; - newEntries[i].next = newBuckets[bucket]; - newBuckets[bucket] = i; + ref int newBucket = ref newBuckets[(uint)hashCode % (uint)newBuckets.Length]; + entry.next = newBucket; + newBucket = i; } } - buckets = newBuckets; - entries = newEntries; + _buckets = newBuckets; + _entries = newEntries; } - // The overload Remove(TKey key, out TValue value) is a copy of this method with one additional - // statement to copy the value for entry being removed into the output parameter. - // Code has been intentionally duplicated for performance reasons. - public bool Remove(TKey key) + public bool Remove(TKey key) => Remove(key, out _); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Remove(TKey key, out TValue value) { if (key == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); } + IEqualityComparer customComparer = _customComparer; + if (customComparer == null) + { + return RemoveDefaultComparer(key, out value); + } + else + { + return RemoveCustomComparer(key, out value, customComparer); + } + } + + private bool RemoveDefaultComparer(TKey key, out TValue value) + { + Debug.Assert(key != null); + + int[] buckets = _buckets; if (buckets != null) { - int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; - int bucket = hashCode % buckets.Length; + // Keys are never null + int hashCode = key.GetHashCode() & 0x7FFFFFFF; int last = -1; - int i = buckets[bucket]; + ref int bucket = ref buckets[(uint)hashCode % (uint)buckets.Length]; + Entry[] entries = _entries; + int i = bucket; while (i >= 0) { ref Entry entry = ref entries[i]; - if (entry.hashCode == hashCode && comparer.Equals(entry.key, key)) + // If class use equals, if struct specialize equality to avoid boxing + if (entry.hashCode == hashCode && (default(TKey) == null ? entry.key.Equals(key) : StructKeyEquals(entry.key, key))) { if (last < 0) { - buckets[bucket] = entry.next; + bucket = entry.next; } else { entries[last].next = entry.next; } + + value = entry.value; + entry.hashCode = -1; - entry.next = freeList; + entry.next = _freeList; if (RuntimeHelpers.IsReferenceOrContainsReferences()) { @@ -593,9 +797,9 @@ public bool Remove(TKey key) { entry.value = default(TValue); } - freeList = i; - freeCount++; - version++; + _freeList = i; + _freeCount++; + _version++; return true; } @@ -603,34 +807,31 @@ public bool Remove(TKey key) i = entry.next; } } + value = default(TValue); return false; } - // This overload is a copy of the overload Remove(TKey key) with one additional - // statement to copy the value for entry being removed into the output parameter. - // Code has been intentionally duplicated for performance reasons. - public bool Remove(TKey key, out TValue value) + private bool RemoveCustomComparer(TKey key, out TValue value, IEqualityComparer customComparer) { - if (key == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); - } + Debug.Assert(key != null); + int[] buckets = _buckets; if (buckets != null) { - int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; - int bucket = hashCode % buckets.Length; + int hashCode = customComparer.GetHashCode(key) & 0x7FFFFFFF; int last = -1; - int i = buckets[bucket]; + ref int bucket = ref buckets[(uint)hashCode % (uint)buckets.Length]; + Entry[] entries = _entries; + int i = bucket; while (i >= 0) { ref Entry entry = ref entries[i]; - if (entry.hashCode == hashCode && comparer.Equals(entry.key, key)) + if (entry.hashCode == hashCode && customComparer.Equals(entry.key, key)) { if (last < 0) { - buckets[bucket] = entry.next; + bucket = entry.next; } else { @@ -640,7 +841,7 @@ public bool Remove(TKey key, out TValue value) value = entry.value; entry.hashCode = -1; - entry.next = freeList; + entry.next = _freeList; if (RuntimeHelpers.IsReferenceOrContainsReferences()) { @@ -650,9 +851,9 @@ public bool Remove(TKey key, out TValue value) { entry.value = default(TValue); } - freeList = i; - freeCount++; - version++; + _freeList = i; + _freeCount++; + _version++; return true; } @@ -666,26 +867,81 @@ public bool Remove(TKey key, out TValue value) 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; + ref Entry entry = ref FindEntry(key, out bool found); + value = found ? entry.value : default(TValue); + return found; } + public bool TryAdd(TKey key, TValue value) => TryInsert(key, value, InsertionBehavior.None); - bool ICollection>.IsReadOnly + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool StructKeyEquals(TKey key0, TKey key1) { - get { return false; } + Debug.Assert(default(TKey) != null); +#if EQUALITYCOMPARER_INTRINSIC + return EqualityComparer.Default.Equals(key0, key1); +#else + // Specialize equality for each of the BCL ValueType primitives + // cast via object to the type and use them directly + // the Jit will elide the boxing. + if (typeof(TKey) == typeof(byte)) + { + return (byte)(object)key0 == (byte)(object)key1; + } + else if (typeof(TKey) == typeof(sbyte)) + { + return (sbyte)(object)key0 == (sbyte)(object)key1; + } + else if (typeof(TKey) == typeof(ushort)) + { + return (ushort)(object)key0 == (ushort)(object)key1; + } + else if (typeof(TKey) == typeof(short)) + { + return (short)(object)key0 == (short)(object)key1; + } + else if (typeof(TKey) == typeof(char)) + { + return (char)(object)key0 == (char)(object)key1; + } + else if (typeof(TKey) == typeof(uint)) + { + return (uint)(object)key0 == (uint)(object)key1; + } + else if (typeof(TKey) == typeof(int)) + { + return (int)(object)key0 == (int)(object)key1; + } + else if (typeof(TKey) == typeof(ulong)) + { + return (ulong)(object)key0 == (ulong)(object)key1; + } + else if (typeof(TKey) == typeof(long)) + { + return (long)(object)key0 == (long)(object)key1; + } + else if (typeof(TKey) == typeof(IntPtr)) + { + return (IntPtr)(object)key0 == (IntPtr)(object)key1; + } + else if (typeof(TKey) == typeof(UIntPtr)) + { + return (UIntPtr)(object)key0 == (UIntPtr)(object)key1; + } + else if (typeof(TKey) == typeof(Guid)) + { + return (Guid)(object)key0 == (Guid)(object)key1; + } + else + { + return EqualityComparer.Default.Equals(key0, key1); + } +#endif } - void ICollection>.CopyTo(KeyValuePair[] array, int index) - { - CopyTo(array, index); - } + bool ICollection>.IsReadOnly => false; + + void ICollection>.CopyTo(KeyValuePair[] array, int index) => CopyTo(array, index); void ICollection.CopyTo(Array array, int index) { @@ -722,8 +978,8 @@ void ICollection.CopyTo(Array array, int index) else if (array is DictionaryEntry[]) { DictionaryEntry[] dictEntryArray = array as DictionaryEntry[]; - Entry[] entries = this.entries; - for (int i = 0; i < count; i++) + Entry[] entries = this._entries; + for (int i = 0; i < _count; i++) { if (entries[i].hashCode >= 0) { @@ -741,8 +997,8 @@ void ICollection.CopyTo(Array array, int index) try { - int count = this.count; - Entry[] entries = this.entries; + int count = this._count; + Entry[] entries = this._entries; for (int i = 0; i < count; i++) { if (entries[i].hashCode >= 0) @@ -758,15 +1014,9 @@ void ICollection.CopyTo(Array array, int index) } } - IEnumerator IEnumerable.GetEnumerator() - { - return new Enumerator(this, Enumerator.KeyValuePair); - } + IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this, Enumerator.KeyValuePair); - bool ICollection.IsSynchronized - { - get { return false; } - } + bool ICollection.IsSynchronized => false; object ICollection.SyncRoot { @@ -780,25 +1030,13 @@ object ICollection.SyncRoot } } - bool IDictionary.IsFixedSize - { - get { return false; } - } + bool IDictionary.IsFixedSize => false; - bool IDictionary.IsReadOnly - { - get { return false; } - } + bool IDictionary.IsReadOnly => false; - ICollection IDictionary.Keys - { - get { return (ICollection)Keys; } - } + ICollection IDictionary.Keys => (ICollection)Keys; - ICollection IDictionary.Values - { - get { return (ICollection)Values; } - } + ICollection IDictionary.Values => (ICollection)Values; object IDictionary.this[object key] { @@ -806,10 +1044,10 @@ object IDictionary.this[object key] { if (IsCompatibleKey(key)) { - int i = FindEntry((TKey)key); - if (i >= 0) + ref Entry entry = ref FindEntry((TKey)key, out bool found); + if (found) { - return entries[i].value; + return entry.value; } } return null; @@ -877,20 +1115,9 @@ void IDictionary.Add(object key, object value) } } - bool IDictionary.Contains(object key) - { - if (IsCompatibleKey(key)) - { - return ContainsKey((TKey)key); - } + bool IDictionary.Contains(object key) => IsCompatibleKey(key) && ContainsKey((TKey)key); - return false; - } - - IDictionaryEnumerator IDictionary.GetEnumerator() - { - return new Enumerator(this, Enumerator.DictEntry); - } + IDictionaryEnumerator IDictionary.GetEnumerator() => new Enumerator(this, Enumerator.DictEntry); void IDictionary.Remove(object key) { @@ -915,7 +1142,7 @@ public struct Enumerator : IEnumerator>, internal Enumerator(Dictionary dictionary, int getEnumeratorRetType) { this.dictionary = dictionary; - version = dictionary.version; + version = dictionary._version; index = 0; this.getEnumeratorRetType = getEnumeratorRetType; current = new KeyValuePair(); @@ -923,16 +1150,16 @@ internal Enumerator(Dictionary dictionary, int getEnumeratorRetTyp public bool MoveNext() { - if (version != dictionary.version) + if (version != dictionary._version) { ThrowHelper.ThrowInvalidOperationException_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) + while ((uint)index < (uint)dictionary._count) { - ref Entry entry = ref dictionary.entries[index++]; + ref Entry entry = ref dictionary._entries[index++]; if (entry.hashCode >= 0) { @@ -941,15 +1168,12 @@ public bool MoveNext() } } - index = dictionary.count + 1; + index = dictionary._count + 1; current = new KeyValuePair(); return false; } - public KeyValuePair Current - { - get { return current; } - } + public KeyValuePair Current => current; public void Dispose() { @@ -959,7 +1183,7 @@ object IEnumerator.Current { get { - if (index == 0 || (index == dictionary.count + 1)) + if (index == 0 || (index == dictionary._count + 1)) { ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); } @@ -977,7 +1201,7 @@ object IEnumerator.Current void IEnumerator.Reset() { - if (version != dictionary.version) + if (version != dictionary._version) { ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); } @@ -990,7 +1214,7 @@ DictionaryEntry IDictionaryEnumerator.Entry { get { - if (index == 0 || (index == dictionary.count + 1)) + if (index == 0 || (index == dictionary._count + 1)) { ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); } @@ -1003,7 +1227,7 @@ object IDictionaryEnumerator.Key { get { - if (index == 0 || (index == dictionary.count + 1)) + if (index == 0 || (index == dictionary._count + 1)) { ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); } @@ -1016,7 +1240,7 @@ object IDictionaryEnumerator.Value { get { - if (index == 0 || (index == dictionary.count + 1)) + if (index == 0 || (index == dictionary._count + 1)) { ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); } @@ -1041,10 +1265,7 @@ public KeyCollection(Dictionary dictionary) this.dictionary = dictionary; } - public Enumerator GetEnumerator() - { - return new Enumerator(dictionary); - } + public Enumerator GetEnumerator() => new Enumerator(dictionary); public void CopyTo(TKey[] array, int index) { @@ -1063,38 +1284,23 @@ public void CopyTo(TKey[] array, int index) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); } - int count = dictionary.count; - Entry[] entries = dictionary.entries; + 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; } - } + public int Count => dictionary.Count; - bool ICollection.IsReadOnly - { - get { return true; } - } + bool ICollection.IsReadOnly => true; - void ICollection.Add(TKey item) - { - ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet); - } + void ICollection.Add(TKey item) => ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet); - void ICollection.Clear() - { - ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet); - } + void ICollection.Clear() => ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet); - bool ICollection.Contains(TKey item) - { - return dictionary.ContainsKey(item); - } + bool ICollection.Contains(TKey item) => dictionary.ContainsKey(item); bool ICollection.Remove(TKey item) { @@ -1102,15 +1308,9 @@ bool ICollection.Remove(TKey item) return false; } - IEnumerator IEnumerable.GetEnumerator() - { - return new Enumerator(dictionary); - } + IEnumerator IEnumerable.GetEnumerator() => new Enumerator(dictionary); - IEnumerator IEnumerable.GetEnumerator() - { - return new Enumerator(dictionary); - } + IEnumerator IEnumerable.GetEnumerator() => new Enumerator(dictionary); void ICollection.CopyTo(Array array, int index) { @@ -1152,8 +1352,8 @@ void ICollection.CopyTo(Array array, int index) ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); } - int count = dictionary.count; - Entry[] entries = dictionary.entries; + int count = dictionary._count; + Entry[] entries = dictionary._entries; try { for (int i = 0; i < count; i++) @@ -1168,15 +1368,9 @@ void ICollection.CopyTo(Array array, int index) } } - bool ICollection.IsSynchronized - { - get { return false; } - } + bool ICollection.IsSynchronized => false; - Object ICollection.SyncRoot - { - get { return ((ICollection)dictionary).SyncRoot; } - } + Object ICollection.SyncRoot => ((ICollection)dictionary).SyncRoot; public struct Enumerator : IEnumerator, System.Collections.IEnumerator { @@ -1188,7 +1382,7 @@ public struct Enumerator : IEnumerator, System.Collections.IEnumerator internal Enumerator(Dictionary dictionary) { this.dictionary = dictionary; - version = dictionary.version; + version = dictionary._version; index = 0; currentKey = default(TKey); } @@ -1199,14 +1393,14 @@ public void Dispose() public bool MoveNext() { - if (version != dictionary.version) + if (version != dictionary._version) { ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); } - while ((uint)index < (uint)dictionary.count) + while ((uint)index < (uint)dictionary._count) { - ref Entry entry = ref dictionary.entries[index++]; + ref Entry entry = ref dictionary._entries[index++]; if (entry.hashCode >= 0) { @@ -1215,24 +1409,18 @@ public bool MoveNext() } } - index = dictionary.count + 1; + index = dictionary._count + 1; currentKey = default(TKey); return false; } - public TKey Current - { - get - { - return currentKey; - } - } + public TKey Current => currentKey; Object System.Collections.IEnumerator.Current { get { - if (index == 0 || (index == dictionary.count + 1)) + if (index == 0 || (index == dictionary._count + 1)) { ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); } @@ -1243,7 +1431,7 @@ Object System.Collections.IEnumerator.Current void System.Collections.IEnumerator.Reset() { - if (version != dictionary.version) + if (version != dictionary._version) { ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); } @@ -1269,10 +1457,7 @@ public ValueCollection(Dictionary dictionary) this.dictionary = dictionary; } - public Enumerator GetEnumerator() - { - return new Enumerator(dictionary); - } + public Enumerator GetEnumerator() => new Enumerator(dictionary); public void CopyTo(TValue[] array, int index) { @@ -1291,28 +1476,19 @@ public void CopyTo(TValue[] array, int index) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); } - int count = dictionary.count; - Entry[] entries = dictionary.entries; + 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; } - } + public int Count => dictionary.Count; - bool ICollection.IsReadOnly - { - get { return true; } - } + bool ICollection.IsReadOnly => true; - void ICollection.Add(TValue item) - { - ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet); - } + void ICollection.Add(TValue item) => ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet); bool ICollection.Remove(TValue item) { @@ -1320,25 +1496,13 @@ bool ICollection.Remove(TValue item) return false; } - void ICollection.Clear() - { - ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet); - } + void ICollection.Clear() => ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet); - bool ICollection.Contains(TValue item) - { - return dictionary.ContainsValue(item); - } + bool ICollection.Contains(TValue item) => dictionary.ContainsValue(item); - IEnumerator IEnumerable.GetEnumerator() - { - return new Enumerator(dictionary); - } + IEnumerator IEnumerable.GetEnumerator() => new Enumerator(dictionary); - IEnumerator IEnumerable.GetEnumerator() - { - return new Enumerator(dictionary); - } + IEnumerator IEnumerable.GetEnumerator() => new Enumerator(dictionary); void ICollection.CopyTo(Array array, int index) { @@ -1378,8 +1542,8 @@ void ICollection.CopyTo(Array array, int index) ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); } - int count = dictionary.count; - Entry[] entries = dictionary.entries; + int count = dictionary._count; + Entry[] entries = dictionary._entries; try { for (int i = 0; i < count; i++) @@ -1394,15 +1558,9 @@ void ICollection.CopyTo(Array array, int index) } } - bool ICollection.IsSynchronized - { - get { return false; } - } + bool ICollection.IsSynchronized => false; - Object ICollection.SyncRoot - { - get { return ((ICollection)dictionary).SyncRoot; } - } + Object ICollection.SyncRoot => ((ICollection)dictionary).SyncRoot; public struct Enumerator : IEnumerator, System.Collections.IEnumerator { @@ -1414,7 +1572,7 @@ public struct Enumerator : IEnumerator, System.Collections.IEnumerator internal Enumerator(Dictionary dictionary) { this.dictionary = dictionary; - version = dictionary.version; + version = dictionary._version; index = 0; currentValue = default(TValue); } @@ -1425,14 +1583,14 @@ public void Dispose() public bool MoveNext() { - if (version != dictionary.version) + if (version != dictionary._version) { ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); } - while ((uint)index < (uint)dictionary.count) + while ((uint)index < (uint)dictionary._count) { - ref Entry entry = ref dictionary.entries[index++]; + ref Entry entry = ref dictionary._entries[index++]; if (entry.hashCode >= 0) { @@ -1440,24 +1598,18 @@ public bool MoveNext() return true; } } - index = dictionary.count + 1; + index = dictionary._count + 1; currentValue = default(TValue); return false; } - public TValue Current - { - get - { - return currentValue; - } - } + public TValue Current => currentValue; Object System.Collections.IEnumerator.Current { get { - if (index == 0 || (index == dictionary.count + 1)) + if (index == 0 || (index == dictionary._count + 1)) { ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); } @@ -1468,7 +1620,7 @@ Object System.Collections.IEnumerator.Current void System.Collections.IEnumerator.Reset() { - if (version != dictionary.version) + if (version != dictionary._version) { ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); }